summaryrefslogtreecommitdiff
path: root/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java
blob: f92da6b45db6f3307ff49a766206465282e2e6b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package io.dico.dicore;

/**
 * A chainable object
 * <p>
 * It is not possible to declare another upper bound for type parameter Subtype.
 * However, it is required that it also extends the type parameter Element.
 *
 * @param <Subtype> the interface that is chainable
 * @param <Element> the element of the chain, this is a supertype of the subtype
 */
@SuppressWarnings("unchecked")
public interface InterfaceChain<Element, Subtype extends InterfaceChain<Element, Subtype>> {
    
    /**
     * returns the empty InterfaceChain instance.
     *
     * @return the empty InterfaceChain instance
     */
    Subtype getEmptyInstance();
    
    /**
     * returns a InterfaceChain with the last added element detached.
     * <p>
     * if this InterfaceChain is the empty instance, the empty instance is returned.
     *
     * @return a InterfaceChain with the last added element detached
     * @implNote for the purpose of lambdas, the default implementation also returns the empty instance.
     */
    default Subtype withoutLastNode() {
        return getEmptyInstance();
    }
    
    /**
     * returns the element that was inserted as the last element
     * <p>
     * For instance, calling this method on the result of calling {@link InterfaceChain#withElement(Object)}
     * would return the given element.
     *
     * @return the element that was inserted as the last element
     * @implNote for the purpose of lambdas, the default implementation returns this object,
     * which is required to implement the Element type parameter.
     */
    default Element getDelegateOfLastNode() {
        //noinspection unchecked
        return (Element) this;
    }
    
    /**
     * @return The number of elements chained from this InterfaceChain.
     * @implNote for the purpose of lambdas, the default implementation returns 1.
     */
    default int getElementCount() {
        return 1;
    }
    
    /**
     * Get a new InterfaceChain that includes the given Element.
     * <p>
     * The default implementation of the Subtype should look like this:
     * <pre> {@code
     * if (element == null) {
     *     return this;
     * }
     *
     * int count = getElementCount() + 1;
     * return new Subtype() {
     *     \@Override
     *     public void exampleElementMethod() {
     *         try {
     *             Subtype.this.exampleElementMethod();
     *         } finally {
     *             element.exampleElementMethod();
     *         }
     *     }
     *
     *     \@Override
     *     public Subtype withoutLastNode() {
     *         return Subtype.this;
     *     }
     *
     *     \@Override
     *     public Element getDelegateOfLastNode() {
     *         return element;
     *     }
     *
     *     \@Override
     *     public int getElementCount() {
     *         return count;
     *     }
     * };
     * }
     * </pre>
     *
     * @param element A new element to insert at the end of this InterfaceChain.
     * @return a new InterfaceChain that includes the given Element.
     */
    Subtype withElement(Element element);
    
    /**
     * Append each of the elements to this InterfaceChain
     *
     * @param elements the elements to append
     * @return a new InterfaceChain with the elements appended
     */
    default Subtype withElements(Element... elements) {
        Subtype result = (Subtype) this;
        for (Element element : elements) {
            result = result.withElement(element);
        }
        return result;
    }
    
    /*
    Example Subtypes implementation
    
        public class Subtypes {
            
            private Subtypes() {
            
            }
            
            private static final Subtype empty = new Subtype() {
                @Override
                public void exampleElementMethod() {
                
                }
            
                @Override
                public Subtype withElement(Element other) {
                    return Subtypes.singleton(other);
                }
            
                @Override
                public int getElementCount() {
                    return 0;
                }
            
                @Override
                public Element getDelegateOfLastNode() {
                    return null;
                }
            };
            
            public static Subtype empty() {
                return empty;
            }
            
            public static Subtype singleton(Element element) {
                if (element instanceof Subtype) {
                    return (Subtype) element;
                }
                if (element == null) {
                    return empty();
                }
                return new Subtype() {
                    @Override
                    public void exampleElementMethod() {
                        element.exampleElementMethod();
                    }
                    
                    @Override
                    public Element getDelegateOfLastNode() {
                        return element;
                    }
                };
            }
            
        }

     */
    
}