1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.collection.AbstractCollection;
13 
14 import hunt.collection.Collection;
15 import hunt.Exceptions;
16 import hunt.Functions;
17 import hunt.Object;
18 
19 import std.array;
20 import std.conv;
21 import std.range;
22 
23 import hunt.logging;
24 
25 /**
26  * This class provides a skeletal implementation of the {@code Collection}
27  * interface, to minimize the effort required to implement this interface. <p>
28  *
29  * To implement an unmodifiable collection, the programmer needs only to
30  * extend this class and provide implementations for the {@code iterator} and
31  * {@code size} methods.  (The iterator returned by the {@code iterator}
32  * method must implement {@code hasNext} and {@code next}.)<p>
33  *
34  * To implement a modifiable collection, the programmer must additionally
35  * override this class's {@code add} method (which otherwise throws an
36  * {@code UnsupportedOperationException}), and the iterator returned by the
37  * {@code iterator} method must additionally implement its {@code remove}
38  * method.<p>
39  *
40  * The programmer should generally provide a void (no argument) and
41  * {@code Collection} constructor, as per the recommendation in the
42  * {@code Collection} interface specification.<p>
43  *
44  * The documentation for each non-abstract method in this class describes its
45  * implementation in detail.  Each of these methods may be overridden if
46  * the collection being implemented admits a more efficient implementation.<p>
47  *
48  * This class is a member of the
49  * <a href="{@docRoot}/java/util/package-summary.html#CollectionsFramework">
50  * Java Collections Framework</a>.
51  *
52  * @author  Josh Bloch
53  * @author  Neal Gafter
54  * @see Collection
55  */
56 abstract class AbstractCollection(E) : Collection!E {
57     /**
58      * Sole constructor.  (For invocation by subclass constructors, typically
59      * implicit.)
60      */
61     protected this() {
62     }
63 
64     // Query Operations
65 
66     /**
67      * Returns an iterator over the elements contained in this collection.
68      *
69      * @return an iterator over the elements contained in this collection
70      */
71     InputRange!E iterator() {
72         throw new NotImplementedException();
73     }
74 
75     abstract int size();
76 
77     /**
78      * {@inheritDoc}
79      *
80      * <p>This implementation returns <tt>size() == 0</tt>.
81      */
82     bool isEmpty() {
83         return size() == 0;
84     }
85 
86     /**
87      * {@inheritDoc}
88      *
89      * <p>This implementation iterates over the elements in the collection,
90      * checking each element in turn for equality with the specified element.
91      *
92      * @throws ClassCastException   {@inheritDoc}
93      * @throws NullPointerException {@inheritDoc}
94      */
95     bool contains(E o) {
96         // Iterator<E> it = iterator();
97         // if (o==null) {
98         //     while (it.hasNext())
99         //         if (it.next()==null)
100         //             return true;
101         // } else {
102         //     while (it.hasNext())
103         //         if (o.equals(it.next()))
104         //             return true;
105         // }
106         // return false;
107 
108         throw new NotImplementedException();
109     }
110 
111     bool add(E e) {
112         throw new NotImplementedException();
113     }
114 
115     bool remove(E e) {
116         throw new NotImplementedException();
117     }
118 
119     // E get(int index) { throw new UnsupportedOperationException(); }
120 
121     // Bulk Operations
122 
123     /**
124      * {@inheritDoc}
125      *
126      * <p>This implementation iterates over the specified collection,
127      * checking each element returned by the iterator in turn to see
128      * if it's contained in this collection.  If all elements are so
129      * contained <tt>true</tt> is returned, otherwise <tt>false</tt>.
130      *
131      * @throws ClassCastException            {@inheritDoc}
132      * @throws NullPointerException          {@inheritDoc}
133      * @see #contains(Object)
134      */
135     bool containsAll(Collection!E c) {
136         foreach (E e; c)
137             if (!contains(e))
138                 return false;
139         return true;
140     }
141 
142     /**
143      * {@inheritDoc}
144      *
145      * <p>This implementation iterates over the specified collection, and adds
146      * each object returned by the iterator to this collection, in turn.
147      *
148      * <p>Note that this implementation will throw an
149      * <tt>UnsupportedOperationException</tt> unless <tt>add</tt> is
150      * overridden (assuming the specified collection is non-empty).
151      *
152      * @throws UnsupportedOperationException {@inheritDoc}
153      * @throws ClassCastException            {@inheritDoc}
154      * @throws NullPointerException          {@inheritDoc}
155      * @throws IllegalArgumentException      {@inheritDoc}
156      * @throws IllegalStateException         {@inheritDoc}
157      *
158      * @see #add(Object)
159      */
160     bool addAll(Collection!E c) {
161         bool modified = false;
162         foreach (E e; c) {
163             if (add(e))
164                 modified = true;
165         }
166         return modified;
167     }
168 
169     bool addAll(E[] c) {
170         bool modified = false;
171         foreach (E e; c) {
172             if (add(e))
173                 modified = true;
174         }
175         return modified;
176     }
177 
178     /**
179      * {@inheritDoc}
180      *
181      * <p>This implementation iterates over this collection, checking each
182      * element returned by the iterator in turn to see if it's contained
183      * in the specified collection.  If it's so contained, it's removed from
184      * this collection with the iterator's <tt>remove</tt> method.
185      *
186      * <p>Note that this implementation will throw an
187      * <tt>UnsupportedOperationException</tt> if the iterator returned by the
188      * <tt>iterator</tt> method does not implement the <tt>remove</tt> method
189      * and this collection contains one or more elements in common with the
190      * specified collection.
191      *
192      * @throws UnsupportedOperationException {@inheritDoc}
193      * @throws ClassCastException            {@inheritDoc}
194      * @throws NullPointerException          {@inheritDoc}
195      *
196      * @see #remove(Object)
197      * @see #contains(Object)
198      */
199     bool removeAll(Collection!E c) {
200         assert(c !is null);
201         bool modified = false;
202         foreach (E k; c) {
203             if (this.contains(k)) {
204                 this.remove(k);
205                 modified = true;
206             }
207         }
208 
209         return modified;
210     }
211 
212     bool removeIf(Predicate!E filter) {
213         assert(filter !is null);
214         E[] items;
215         foreach (E item; this) {
216             if (filter(item))
217                 items ~= item;
218         }
219 
220         foreach (E item; items) {
221             remove(item);
222         }
223 
224         return items.length > 0;
225     }
226 
227     /**
228      * {@inheritDoc}
229      *
230      * <p>This implementation iterates over this collection, checking each
231      * element returned by the iterator in turn to see if it's contained
232      * in the specified collection.  If it's not so contained, it's removed
233      * from this collection with the iterator's <tt>remove</tt> method.
234      *
235      * <p>Note that this implementation will throw an
236      * <tt>UnsupportedOperationException</tt> if the iterator returned by the
237      * <tt>iterator</tt> method does not implement the <tt>remove</tt> method
238      * and this collection contains one or more elements not present in the
239      * specified collection.
240      *
241      * @throws UnsupportedOperationException {@inheritDoc}
242      * @throws ClassCastException            {@inheritDoc}
243      * @throws NullPointerException          {@inheritDoc}
244      *
245      * @see #remove(Object)
246      * @see #contains(Object)
247      */
248     bool retainAll(Collection!E c) {
249         assert(c !is null);
250         bool modified = false;
251 
252         // InputRange!E it = iterator();
253         // while (!it.empty) {
254         //     E current = it.front();
255         //     it.popFront();
256 
257         //     if (!c.contains(current)) {
258         //         // it.remove();
259         //         this.remove(current);
260         //         modified = true;
261         //     }
262         // }
263         import std.container.slist;
264 
265         SList!E list;
266 
267         foreach (E k; this) {
268             if (!c.contains(k)) {
269                 // this.remove(k);
270                 list.insert(k);
271             }
272         }
273 
274         modified = !list.empty();
275         foreach (E e; list)
276             this.remove(e);
277 
278         return modified;
279     }
280 
281     void clear() {
282         throw new NotImplementedException();
283     }
284 
285     int opApply(scope int delegate(ref E) dg) {
286         throw new NotImplementedException();
287         // return 0;
288     }
289 
290     // int opApply(scope int delegate(MapEntry!(E) entry) dg) {
291     //     throw new NotImplementedException();
292     // }
293 
294     /**
295      * {@inheritDoc}
296      *
297      * <p>This implementation returns an array containing all the elements
298      * returned by this collection's iterator, in the same order, stored in
299      * consecutive elements of the array, starting with index {@code 0}.
300      * The length of the returned array is equal to the number of elements
301      * returned by the iterator, even if the size of this collection changes
302      * during iteration, as might happen if the collection permits
303      * concurrent modification during iteration.  The {@code size} method is
304      * called only as an optimization hint; the correct result is returned
305      * even if the iterator returns a different number of elements.
306      *
307      * <p>This method is equivalent to:
308      *
309      *  <pre> {@code
310      * List<E> list = new ArrayList<E>(size());
311      * for (E e : this)
312      *     list.add(e);
313      * return list.toArray();
314      * }</pre>
315      */
316     E[] toArray() {
317         int s = size();
318         if (s == 0)
319             return [];
320 
321         E[] r = new E[s];
322         int i = 0;
323         foreach (E e; this) {
324             r[i++] = e;
325         }
326         return r;
327     }
328 
329     override bool opEquals(IObject o) {
330         return opEquals(cast(Object) o);
331     }
332 
333     override bool opEquals(Object o) {
334         return super.opEquals(o);
335     }
336 
337     override size_t toHash() @trusted nothrow {
338         return super.toHash();
339     }
340 
341     //  string conversion
342 
343     /**
344      * Returns a string representation of this collection.  The string
345      * representation consists of a list of the collection's elements in the
346      * order they are returned by its iterator, enclosed in square brackets
347      * ({@code "[]"}).  Adjacent elements are separated by the characters
348      * {@code ", "} (comma and space).  Elements are converted to strings as
349      * by {@link string#valueOf(Object)}.
350      *
351      * @return a string representation of this collection
352      */
353     override string toString() {
354         if (size() == 0)
355             return "[]";
356 
357         Appender!string sb;
358         sb.put("[");
359         bool isFirst = true;
360         foreach (E e; this) {
361             if (!isFirst)
362                 sb.put(", ");
363             static if (is(E == class))
364                 sb.put(e is this ? "(this Collection)" : e.toString());
365             else
366                 sb.put(e.to!string());
367             isFirst = false;
368         }
369         sb.put(']');
370         return sb.data;
371     }
372 }