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.ConsoleLogger;
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  * @since 1.2
56  */
57 abstract class AbstractCollection(E) : Collection!E {
58     /**
59      * Sole constructor.  (For invocation by subclass constructors, typically
60      * implicit.)
61      */
62     protected this() {
63     }
64 
65     // Query Operations
66 
67     /**
68      * Returns an iterator over the elements contained in this collection.
69      *
70      * @return an iterator over the elements contained in this collection
71      */
72     InputRange!E iterator() {
73         throw new NotImplementedException();
74     }
75 
76     abstract int size();
77 
78     /**
79      * {@inheritDoc}
80      *
81      * <p>This implementation returns <tt>size() == 0</tt>.
82      */
83     bool isEmpty() {
84         return size() == 0;
85     }
86 
87     /**
88      * {@inheritDoc}
89      *
90      * <p>This implementation iterates over the elements in the collection,
91      * checking each element in turn for equality with the specified element.
92      *
93      * @throws ClassCastException   {@inheritDoc}
94      * @throws NullPointerException {@inheritDoc}
95      */
96     bool contains(E o) {
97         // Iterator<E> it = iterator();
98         // if (o==null) {
99         //     while (it.hasNext())
100         //         if (it.next()==null)
101         //             return true;
102         // } else {
103         //     while (it.hasNext())
104         //         if (o.equals(it.next()))
105         //             return true;
106         // }
107         // return false;
108 
109         throw new NotImplementedException();
110     }
111 
112     bool add(E e) {
113         throw new NotImplementedException();
114     }
115 
116     bool remove(E e) {
117         throw new NotImplementedException();
118     }
119 
120     // E get(int index) { throw new UnsupportedOperationException(); }
121 
122     // Bulk Operations
123 
124     /**
125      * {@inheritDoc}
126      *
127      * <p>This implementation iterates over the specified collection,
128      * checking each element returned by the iterator in turn to see
129      * if it's contained in this collection.  If all elements are so
130      * contained <tt>true</tt> is returned, otherwise <tt>false</tt>.
131      *
132      * @throws ClassCastException            {@inheritDoc}
133      * @throws NullPointerException          {@inheritDoc}
134      * @see #contains(Object)
135      */
136     bool containsAll(Collection!E c) {
137         foreach (E e; c)
138             if (!contains(e))
139                 return false;
140         return true;
141     }
142 
143     /**
144      * {@inheritDoc}
145      *
146      * <p>This implementation iterates over the specified collection, and adds
147      * each object returned by the iterator to this collection, in turn.
148      *
149      * <p>Note that this implementation will throw an
150      * <tt>UnsupportedOperationException</tt> unless <tt>add</tt> is
151      * overridden (assuming the specified collection is non-empty).
152      *
153      * @throws UnsupportedOperationException {@inheritDoc}
154      * @throws ClassCastException            {@inheritDoc}
155      * @throws NullPointerException          {@inheritDoc}
156      * @throws IllegalArgumentException      {@inheritDoc}
157      * @throws IllegalStateException         {@inheritDoc}
158      *
159      * @see #add(Object)
160      */
161     bool addAll(Collection!E c) {
162         bool modified = false;
163         foreach (E e; c) {
164             if (add(e))
165                 modified = true;
166         }
167         return modified;
168     }
169 
170     bool addAll(E[] c) {
171         bool modified = false;
172         foreach (E e; c) {
173             if (add(e))
174                 modified = true;
175         }
176         return modified;
177     }
178 
179     /**
180      * {@inheritDoc}
181      *
182      * <p>This implementation iterates over this collection, checking each
183      * element returned by the iterator in turn to see if it's contained
184      * in the specified collection.  If it's so contained, it's removed from
185      * this collection with the iterator's <tt>remove</tt> method.
186      *
187      * <p>Note that this implementation will throw an
188      * <tt>UnsupportedOperationException</tt> if the iterator returned by the
189      * <tt>iterator</tt> method does not implement the <tt>remove</tt> method
190      * and this collection contains one or more elements in common with the
191      * specified collection.
192      *
193      * @throws UnsupportedOperationException {@inheritDoc}
194      * @throws ClassCastException            {@inheritDoc}
195      * @throws NullPointerException          {@inheritDoc}
196      *
197      * @see #remove(Object)
198      * @see #contains(Object)
199      */
200     bool removeAll(Collection!E c) {
201         assert(c !is null);
202         bool modified = false;
203         foreach (E k; c) {
204             if (this.contains(k)) {
205                 this.remove(k);
206                 modified = true;
207             }
208         }
209 
210         return modified;
211     }
212 
213     bool removeIf(Predicate!E filter) {
214         assert(filter !is null);
215         E[] items;
216         foreach (E item; this) {
217             if (filter(item))
218                 items ~= item;
219         }
220 
221         foreach (E item; items) {
222             remove(item);
223         }
224 
225         return items.length > 0;
226     }
227 
228     /**
229      * {@inheritDoc}
230      *
231      * <p>This implementation iterates over this collection, checking each
232      * element returned by the iterator in turn to see if it's contained
233      * in the specified collection.  If it's not so contained, it's removed
234      * from this collection with the iterator's <tt>remove</tt> method.
235      *
236      * <p>Note that this implementation will throw an
237      * <tt>UnsupportedOperationException</tt> if the iterator returned by the
238      * <tt>iterator</tt> method does not implement the <tt>remove</tt> method
239      * and this collection contains one or more elements not present in the
240      * specified collection.
241      *
242      * @throws UnsupportedOperationException {@inheritDoc}
243      * @throws ClassCastException            {@inheritDoc}
244      * @throws NullPointerException          {@inheritDoc}
245      *
246      * @see #remove(Object)
247      * @see #contains(Object)
248      */
249     bool retainAll(Collection!E c) {
250         assert(c !is null);
251         bool modified = false;
252 
253         // InputRange!E it = iterator();
254         // while (!it.empty) {
255         //     E current = it.front();
256         //     it.popFront();
257 
258         //     if (!c.contains(current)) {
259         //         // it.remove();
260         //         this.remove(current);
261         //         modified = true;
262         //     }
263         // }
264         import std.container.slist;
265 
266         SList!E list;
267 
268         foreach (E k; this) {
269             if (!c.contains(k)) {
270                 // this.remove(k);
271                 list.insert(k);
272             }
273         }
274 
275         modified = !list.empty();
276         foreach (E e; list)
277             this.remove(e);
278 
279         return modified;
280     }
281 
282     void clear() {
283         throw new NotImplementedException();
284     }
285 
286     int opApply(scope int delegate(ref E) dg) {
287         throw new NotImplementedException();
288         // return 0;
289     }
290 
291     // int opApply(scope int delegate(MapEntry!(E) entry) dg) {
292     //     throw new NotImplementedException();
293     // }
294 
295     /**
296      * {@inheritDoc}
297      *
298      * <p>This implementation returns an array containing all the elements
299      * returned by this collection's iterator, in the same order, stored in
300      * consecutive elements of the array, starting with index {@code 0}.
301      * The length of the returned array is equal to the number of elements
302      * returned by the iterator, even if the size of this collection changes
303      * during iteration, as might happen if the collection permits
304      * concurrent modification during iteration.  The {@code size} method is
305      * called only as an optimization hint; the correct result is returned
306      * even if the iterator returns a different number of elements.
307      *
308      * <p>This method is equivalent to:
309      *
310      *  <pre> {@code
311      * List<E> list = new ArrayList<E>(size());
312      * for (E e : this)
313      *     list.add(e);
314      * return list.toArray();
315      * }</pre>
316      */
317     E[] toArray() {
318         int s = size();
319         if (s == 0)
320             return [];
321 
322         E[] r = new E[s];
323         int i = 0;
324         foreach (E e; this) {
325             r[i++] = e;
326         }
327         return r;
328     }
329 
330     override bool opEquals(IObject o) {
331         return opEquals(cast(Object) o);
332     }
333 
334     override bool opEquals(Object o) {
335         return super.opEquals(o);
336     }
337 
338     override size_t toHash() @trusted nothrow {
339         return super.toHash();
340     }
341 
342     //  string conversion
343 
344     /**
345      * Returns a string representation of this collection.  The string
346      * representation consists of a list of the collection's elements in the
347      * order they are returned by its iterator, enclosed in square brackets
348      * ({@code "[]"}).  Adjacent elements are separated by the characters
349      * {@code ", "} (comma and space).  Elements are converted to strings as
350      * by {@link string#valueOf(Object)}.
351      *
352      * @return a string representation of this collection
353      */
354     override string toString() {
355         if (size() == 0)
356             return "[]";
357 
358         Appender!string sb;
359         sb.put("[");
360         bool isFirst = true;
361         foreach (E e; this) {
362             if (!isFirst)
363                 sb.put(", ");
364             static if (is(E == class))
365                 sb.put(e is this ? "(this Collection)" : e.toString());
366             else
367                 sb.put(e.to!string());
368             isFirst = false;
369         }
370         sb.put(']');
371         return sb.data;
372     }
373 }