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}