001/*
002 * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025package Torello.Java.ReadOnly;
026
027import Torello.Java.Additional.RemoveUnsupportedIterator;
028
029import java.util.*;
030
031import java.util.function.Predicate;
032
033/**
034 * A Copy of Java's {@code HashSet} class; used for building a {@link ReadOnlyHashSet}.  Maintains
035 * <I>an internal and inaccessible {@code HashSet<E>} instance</I>. 
036 * 
037 * <EMBED CLASS=globalDefs DATA-JDK=HashSet>
038 * <EMBED CLASS='external-html' DATA-A_AN=a DATA-FILE-ID=BUILDERS>
039 * 
040 * @param <E> the type of elements maintained by this set
041 * @see ReadOnlyHashSet
042 */
043@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="JDHBI_BUILDER")
044public final class ROHashSetBuilder<E>
045    extends HashSet<E>
046    implements Cloneable, java.io.Serializable
047{
048    // ********************************************************************************************
049    // ********************************************************************************************
050    // Fields & Builder
051    // ********************************************************************************************
052    // ********************************************************************************************
053
054
055    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */
056    protected static final long serialVersionUID = 1;
057
058    // Exception messages need this
059    private static final String ROHS = "HashSet";
060
061    private boolean built = false;
062
063    // Mimics the C++ Concept of "Friend Class".  This badge is utilized by the "build()" method
064    // directly below this inner-class declaration.  It is the only place where this declaration is
065    // actually used anywhere.  This prohibits access to the constructor that is used to anybody
066    // else
067
068    static class AccessBadge { private AccessBadge() { } }
069    private static final AccessBadge friendClassBadge = new AccessBadge();
070
071    /**
072     * Simply transfers {@code 'this'} instance' internal {@code HashSet} to the 
073     * {@code ReadOnlyHashSet} Wrapper-Class.
074     * 
075     * @return a newly constructed {@code ReadOnlyHashSet} "Wrapper-Class", shielding the
076     * internal {@code 'hashSet'} private-field from any modification.
077     */
078    public ReadOnlyHashSet<E> build()
079    {
080        this.built = true;
081
082        return (size() == 0)
083            ? ReadOnlyHashSet.emptyROHS()
084            : new ReadOnlyHashSet<E>(this, friendClassBadge);
085    }
086
087
088    // ********************************************************************************************
089    // ********************************************************************************************
090    // Modified Constructors
091    // ********************************************************************************************
092    // ********************************************************************************************
093
094
095    /**
096     * Constructs a new, empty {@code ROHashSetBuilder}, the underlying {@code HashMap} instance
097     * has default initial capacity (16) and load factor (0.75).
098     */
099    public ROHashSetBuilder()
100    { super(); }
101
102    /**
103     * Constructs a new {@code ROHashSetBuilder} containing the elements in the specified
104     * collection.  The underlying {@code HashMap} is created with default load factor (0.75) and
105     * an initial capacity sufficient to contain the elements in the specified collection.
106     *
107     * @param c the collection whose elements are to be placed into this set
108     * @throws NullPointerException if the specified collection is null
109     */
110    public ROHashSetBuilder(Collection<? extends E> c)
111    { super(c); }
112
113    /**
114     * Constructs a new, empty {@code ROHashSetBuilder}; the backing {@code HashMap} instance has
115     * the specified initial capacity and the specified load factor.
116     *
117     * <BR /><BR />To create a {@code ROHashSetBuilder} with an initial capacity that accommodates
118     * an expected number of elements, use {@link #newROHashSetBuilder(int) newROHashSetBuilder}.
119     *
120     * @param initialCapacity the initial capacity of the hash map
121     * @param loadFactor the load factor of the hash map
122     * 
123     * @throws IllegalArgumentException if the initial capacity is less than zero, or if the load
124     * factor is nonpositive
125     */
126    public ROHashSetBuilder(int initialCapacity, float loadFactor)
127    { super(initialCapacity, loadFactor); }
128
129    /**
130     * Constructs a new, empty {@code ROHashSetBuilder}; the backing {@code HashMap} instance has
131     * the specified initial capacity and default load factor (0.75).
132     *
133     * <BR /><BR />To create a {@code ReadOnlyHashSet} with an initial capacity that accommodates
134     * an expected number of elements, use {@link #newROHashSetBuilder(int) newROHashSetBuilder}.
135     *
136     * @param initialCapacity the initial capacity of the hash table
137     * @throws IllegalArgumentException if the initial capacity is less than zero
138     */
139    public ROHashSetBuilder(int initialCapacity)
140    { super(initialCapacity); }
141
142    /**
143     * Creates a new, empty {@code ROHashSetBuilder} suitable for the expected number of elements.
144     * The returned set uses the default load factor of 0.75, and its initial capacity is generally
145     * large enough so that the expected number of elements can be added without resizing the set.
146     * 
147     * @param <T> the type of elements maintained by the new set builder
148     * @param numElements the expected number of elements
149     * @return the newly created builder
150     * @throws IllegalArgumentException if numElements is negative
151     */
152    public static <T> ROHashSetBuilder<T> newROHashSetBuilder(int numElements)
153    {
154        if (numElements < 0)
155            throw new IllegalArgumentException("Negative number of elements: " + numElements);
156
157        return new ROHashSetBuilder<>(calculateHashMapCapacity(numElements));
158    }
159
160    // Copied from JDK-21, java.util.HashMap
161    // Calculate initial capacity for HashMap based classes, from expected size and default load
162    // factor (0.75).
163
164    private static int calculateHashMapCapacity(int numMappings)
165    { return (int) Math.ceil(numMappings / (double) /* DEFAULT_LOAD_FACTOR */ 0.75f); }
166
167
168    // ********************************************************************************************
169    // ********************************************************************************************
170    // Original JDK Methods
171    // ********************************************************************************************
172    // ********************************************************************************************
173
174
175    public RemoveUnsupportedIterator<E> iterator()
176    { return new RemoveUnsupportedIterator<>(super.iterator()); }
177
178    /**
179     * Adds the specified element to this set if it is not already present.  More formally, adds
180     * the specified element {@code e} to this set if this set contains no element {@code e2} such
181     * that {@code Objects.equals(e, e2)}.  If this set already contains the element, the call
182     * leaves the set unchanged and returns {@code FALSE}.
183     * 
184     * <EMBED DATA-TYPE=HashSet CLASS='external-html' DATA-FILE-ID=MUTATOR>
185     *
186     * @param e element to be added to this set
187     * @return {@code TRUE} if this set did not already contain the specified element
188     */
189    public boolean add(E e)
190    {
191        if (this.built) throw new AttemptedModificationException(ROHS);
192        return super.add(e);
193    }
194
195    /**
196     * Removes the specified element from this set if it is present.  More formally, removes an
197     * element {@code e} such that {@code Objects.equals(o, e)}, if this set contains such an
198     * element.  Returns {@code TRUE} if this set contained the element (or equivalently, if this
199     * set changed as a result of the call).  (This set will not contain the element once the call
200     * returns.)
201     * 
202     * <EMBED DATA-TYPE=HashSet CLASS='external-html' DATA-FILE-ID=MUTATOR>
203     *
204     * @param o object to be removed from this set, if present
205     * @return {@code TRUE} if the set contained the specified element
206     */
207    public boolean remove(Object o)
208    {
209        if (this.built) throw new AttemptedModificationException(ROHS);
210        return super.remove(o);
211    }
212
213    /**
214     * Removes all of the elements from this set.  The set will be empty after this call returns.
215     * <EMBED DATA-TYPE=HashSet CLASS='external-html' DATA-FILE-ID=MUTATOR>
216     */
217    public void clear()
218    {
219        if (this.built) throw new AttemptedModificationException(ROHS);
220        super.clear();
221    }
222
223
224    // ********************************************************************************************
225    // ********************************************************************************************
226    // Methods inherited from class java.util.HashSet
227    // ********************************************************************************************
228    // ********************************************************************************************
229
230
231    // contains, isEmpty, size, spliterator
232    // 
233    // Introspection Only: contains, isEmpty, size, spliterator
234    // Mutator or Potential Mutator Methods: NONE
235    // 
236    // *** OTHER INHERITED METHODS ***
237    // 
238    // Methods inherited from class java.util.AbstractSet:
239    // hashCode, removeAll
240    // 
241    // Methods inherited from class java.util.AbstractCollection:
242    // addAll, containsAll, retainAll, toArray, toArray, toString
243    // 
244    // Methods inherited from interface java.util.Collection: 
245    // parallelStream, removeIf, stream, toArray
246    // 
247    // Methods inherited from interface java.lang.Iterable:
248    // forEach
249    // 
250    // Methods inherited from interface java.util.Set:
251    // addAll, containsAll, hashCode, removeAll, retainAll, toArray, toArray
252
253    @Override
254    public boolean removeAll(Collection<?> c)
255    {
256        if (this.built) throw new AttemptedModificationException(ROHS);
257        return super.removeAll(c);
258    }
259
260    @Override
261    public boolean addAll(Collection<? extends E> c)
262    {
263        if (this.built) throw new AttemptedModificationException(ROHS);
264        return super.addAll(c);
265    }
266
267    @Override
268    public boolean retainAll(Collection<?> c)
269    {
270        if (this.built) throw new AttemptedModificationException(ROHS);
271        return super.retainAll(c);
272    }
273
274    @Override
275    public boolean removeIf(Predicate<? super E> filter)
276    {
277        if (this.built) throw new AttemptedModificationException(ROHS);
278        return super.removeIf(filter);
279    }
280
281
282    // ********************************************************************************************
283    // ********************************************************************************************
284    // java.lang.Object & Cloneable
285    // ********************************************************************************************
286    // ********************************************************************************************
287
288
289    /**
290     * Compares the specified Object with this Builder for equality, as per the definition in the
291     * original class {@code java.util.HashSet}.  Ignores the private field {@code 'built'}.  If
292     * {@code 'this'} has not been built, but {@code 'o'} has been, that fact is ignored during the
293     * equality-comparison.
294     *
295     * @param  o object to be compared for equality with this {@code ROHashSetBuilder} instance
296     * @return true if the specified Object is equal to this Builder
297     */
298    public boolean equals(Object o)
299    {
300        if (this == o) return true;
301        if (! (o instanceof ROHashSetBuilder)) return false;
302        return super.equals((HashSet) o);
303    }
304
305    /**
306     * Clones this instance' of {@code ROHashSetBuilder}.
307     * 
308     * <BR /><BR />The clone that's returned has had it's internal {@code 'built'} boolean-flag set
309     * {@code FALSE}
310     * 
311     * @return a clone of this builder
312     */
313    public synchronized ROHashSetBuilder<E> clone()
314    { return new ROHashSetBuilder<>(this); }
315
316    private ROHashSetBuilder(ROHashSetBuilder<E> rohsb)
317    { super(rohsb); this.built=false; }
318}