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.AbstractSet;
13 
14 
15 import hunt.collection.AbstractCollection;
16 import hunt.collection.Collection;
17 import hunt.collection.Set;
18 
19 import hunt.Exceptions;
20 import hunt.Functions;
21 import hunt.Object;
22 
23 
24 /**
25  * This class provides a skeletal implementation of the <tt>Set</tt>
26  * interface to minimize the effort required to implement this
27  * interface. <p>
28  *
29  * The process of implementing a set by extending this class is identical
30  * to that of implementing a Collection by extending AbstractCollection,
31  * except that all of the methods and constructors in subclasses of this
32  * class must obey the additional constraints imposed by the <tt>Set</tt>
33  * interface (for instance, the add method must not permit addition of
34  * multiple instances of an object to a set).<p>
35  *
36  * Note that this class does not override any of the implementations from
37  * the <tt>AbstractCollection</tt> class.  It merely adds implementations
38  * for <tt>equals</tt> and <tt>hashCode</tt>.<p>
39  *
40  * This class is a member of the
41  * <a href="{@docRoot}/../technotes/guides/collections/index.html">
42  * Java Collections Framework</a>.
43  *
44  * @param !(E) the type of elements maintained by this set
45  *
46  * @see Collection
47  * @see AbstractCollection
48  * @see Set
49  */
50 abstract class AbstractSet(E) : AbstractCollection!E, Set!E {
51     /**
52      * Sole constructor.  (For invocation by subclass constructors, typically
53      * implicit.)
54      */
55     protected this() {
56     }
57 
58     // Comparison and hashing
59 
60     /**
61      * Compares the specified object with this set for equality.  Returns
62      * <tt>true</tt> if the given object is also a set, the two sets have
63      * the same size, and every member of the given set is contained in
64      * this set.  This ensures that the <tt>equals</tt> method works
65      * properly across different implementations of the <tt>Set</tt>
66      * interface.<p>
67      *
68      * This implementation first checks if the specified object is this
69      * set; if so it returns <tt>true</tt>.  Then, it checks if the
70      * specified object is a set whose size is identical to the size of
71      * this set; if not, it returns false.  If so, it returns
72      * <tt>containsAll((Collection) o)</tt>.
73      *
74      * @param o object to be compared for equality with this set
75      * @return <tt>true</tt> if the specified object is equal to this set
76      */
77     override bool opEquals(Object o) {
78         if(o is null) return false;
79         if (o is this) return true;
80 
81         Collection!E c = cast(Collection!E) o;
82         if(c is null) return false;
83         if (c.size() != size())
84             return false;
85 
86         try {
87             return containsAll(c);
88         } catch (Exception) {
89             return false;
90         }
91     }
92 
93     override bool opEquals(IObject o) {
94         return opEquals(cast(Object) o);
95     }
96     /**
97      * Returns the hash code value for this set.  The hash code of a set is
98      * defined to be the sum of the hash codes of the elements in the set,
99      * where the hash code of a <tt>null</tt> element is defined to be zero.
100      * This ensures that <tt>s1.equals(s2)</tt> implies that
101      * <tt>s1.toHash()==s2.toHash()</tt> for any two sets <tt>s1</tt>
102      * and <tt>s2</tt>, as required by the general contract of
103      * {@link Object#hashCode}.
104      *
105      * <p>This implementation iterates over the set, calling the
106      * <tt>hashCode</tt> method on each element in the set, and adding up
107      * the results.
108      *
109      * @return the hash code value for this set
110      * @see Object#equals(Object)
111      * @see Set#equals(Object)
112      */
113     override size_t toHash() @trusted nothrow {
114         try {
115             size_t h = 0;
116             foreach(E item; this)
117                 h += hashOf(item);
118             return h;
119         }
120         catch(Exception) {
121             return 0;
122         }
123     }
124 
125     override string toString() {
126         return super.toString();
127     }    
128 
129     /**
130      * Removes from this set all of its elements that are contained in the
131      * specified collection (optional operation).  If the specified
132      * collection is also a set, this operation effectively modifies this
133      * set so that its value is the <i>asymmetric set difference</i> of
134      * the two sets.
135      *
136      * <p>This implementation determines which is the smaller of this set
137      * and the specified collection, by invoking the <tt>size</tt>
138      * method on each.  If this set has fewer elements, then the
139      * implementation iterates over this set, checking each element
140      * returned by the iterator in turn to see if it is contained in
141      * the specified collection.  If it is so contained, it is removed
142      * from this set with the iterator's <tt>remove</tt> method.  If
143      * the specified collection has fewer elements, then the
144      * implementation iterates over the specified collection, removing
145      * from this set each element returned by the iterator, using this
146      * set's <tt>remove</tt> method.
147      *
148      * <p>Note that this implementation will throw an
149      * <tt>UnsupportedOperationException</tt> if the iterator returned by the
150      * <tt>iterator</tt> method does not implement the <tt>remove</tt> method.
151      *
152      * @param  c collection containing elements to be removed from this set
153      * @return <tt>true</tt> if this set changed as a result of the call
154      * @throws UnsupportedOperationException if the <tt>removeAll</tt> operation
155      *         is not supported by this set
156      * @throws ClassCastException if the class of an element of this set
157      *         is incompatible with the specified collection
158      * (<a href="Collection.html#optional-restrictions">optional</a>)
159      * @throws NullPointerException if this set contains a null element and the
160      *         specified collection does not permit null elements
161      * (<a href="Collection.html#optional-restrictions">optional</a>),
162      *         or if the specified collection is null
163      * @see #remove(Object)
164      * @see #contains(Object)
165      */
166     override bool removeAll(Collection!E c) {
167         assert(c !is null);
168 
169         bool modified = false;
170         if (size() > c.size()) {
171             foreach(E k; c)        {
172                 if(this.contains(k)) {
173                     this.remove(k); modified = true;
174                 }
175             }
176         } else {
177             foreach(E k; this) {
178                 if(c.contains(k))  {
179                     this.remove(k);
180                     modified = true;
181                 }
182             }            
183         }
184         return modified;
185     }
186 
187 }
188 
189 
190 /**
191 */
192 class EmptySet(E) : AbstractSet!(E) {
193 
194     // Iterator!(E) iterator() { return emptyIterator(); }
195 
196     override int size() {return 0;}
197     override bool isEmpty() {return true;}
198     override void clear() {}
199 
200     override bool contains(E obj) {return false;}
201     override bool containsAll(Collection!E c) { return c.isEmpty(); }
202 
203     override E[] toArray() { return []; }
204 
205     override bool removeIf(Predicate!E filter) {
206         assert(filter !is null);
207         return false;
208     }
209 
210     override size_t toHash() @trusted nothrow {
211         return 0;
212     }
213 }