001/* 002 * Copyright (c) 1994, 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 java.util.*; 028 029import java.util.function.BiConsumer; 030import java.util.function.Consumer; 031import java.util.function.Predicate; 032import java.util.function.Function; 033 034import Torello.Java.Additional.Tuple2; 035 036import Torello.JavaDoc.Annotations.LinkJavaSource; 037import Torello.JavaDoc.Annotations.IntoHTMLTable; 038 039/** 040 * Immutable Wrapper for <CODE>java.util.Hashtable</CODE>, found in the "Java Collections 041 * Framework". 042 * 043 * <EMBED CLASS=globalDefs DATA-JDK=Hashtable DATA-ENDING=table> 044 * <EMBED CLASS='external-html' DATA-FILE-ID=DATA_CLASS> 045 * <EMBED CLASS='external-html' DATA-FILE-ID=RO_SYNCHRONIZED> 046 * 047 * @param <K> the type of keys maintained by this map 048 * @param <V> the type of mapped values 049 */ 050@Torello.JavaDoc.Annotations.JDHeaderBackgroundImg(EmbedTagFileID="JDHBI_MAIN") 051@SuppressWarnings("unchecked") 052public class ReadOnlyHashtable<K, V> 053 implements ReadOnlyDictionary<K, V>, ReadOnlyMap<K, V>, Cloneable, java.io.Serializable 054{ 055 // ******************************************************************************************** 056 // ******************************************************************************************** 057 // Protected & Private Fields, Methods, Statics 058 // ******************************************************************************************** 059 // ******************************************************************************************** 060 061 062 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */ 063 protected static final long serialVersionUID = 1; 064 065 // Minor Optimization where new Hashtable's that have no contents always re-use this static 066 // instance. Since this instance is completely empty, the Raw-Types things is irrelevant. 067 068 @SuppressWarnings("rawtypes") 069 private static final Hashtable EMPTY_HASH_TABLE = new Hashtable(0, 0.75f); 070 071 // Singleton & Empty ReadOnlyHashtable, Uses the "Supplier Constructor" 072 @SuppressWarnings("rawtypes") 073 private static final ReadOnlyHashtable EMPTY_READONLY_HASH_TABLE = 074 new ReadOnlyHashtable(EMPTY_HASH_TABLE); 075 076 // The actual Hashtable used by this instance. 077 private final Hashtable<K, V> hashTable; 078 079 // TRUE => This was built using the class ROHashtableBuilder 080 // FALSE => This was built using the clone() of a standard java.util.Hashtable constructor 081 082 private final boolean fromBuilderOrHashtable; 083 084 // Mimics the C++ Keyword/Concept of "Friend Class". Is "Friends With" ROHashtableBuilder 085 static class AccessBadge { private AccessBadge() { } } 086 private static final AccessBadge friendClassBadge = new AccessBadge(); 087 088 089 // ******************************************************************************************** 090 // ******************************************************************************************** 091 // Builder, Acess-Badge Constructor - and Static "Empty" getter (which is used by the builder) 092 // ******************************************************************************************** 093 // ******************************************************************************************** 094 095 096 /** 097 * Returns an empty, <B STYLE='color: red;'>singleton</B>, instance of 098 * {@code ReadOnlyHashtable}. 099 * 100 * @param <X> Returned {@link ReadOnlyMap}'s Key-Type. 101 * @param <Y> Returned {@link ReadOnlyMap}'s Value-Type. 102 * 103 * @return An empty map. Since this map is both empty & read-only, a raw-type singleton 104 * will suffice for all operations offered by this clas. 105 * 106 * <EMBED CLASS='external-html' DATA-FILE-ID=EMPTY_SYNCHRONIZED> 107 */ 108 public static <X, Y> ReadOnlyHashtable<X, Y> emptyROHT() 109 { return (ReadOnlyHashtable<X, Y>) EMPTY_READONLY_HASH_TABLE; } 110 111 // Used by the **Builder** 112 // To all the readers out there following along: The "AccessBadge" thing is just a slightly 113 // wimpier substitute for the C++ keyword / concept 'friend' or "Friend Class". It means this 114 // constructor is (for all intents and purposes) a private-constructor, except for the class 115 // ROHashtableBuilder 116 // 117 // This is the Constructor used by the Builder. It has a "Zero-Size" Optimization 118 119 ReadOnlyHashtable(ROHashtableBuilder<K, V> rohtb, ROHashtableBuilder.AccessBadge badge) 120 { 121 Objects.requireNonNull(badge, "Access Badge is null. Requires Friend-Class Badge"); 122 123 this.fromBuilderOrHashtable = true; 124 this.hashTable = rohtb; 125 } 126 127 // SPECIAL CASE: ReadOnlyProperties inherits ReadOnlyHashtable 128 // NOTE: ROPropertiesBuilder **DOES NOT** inherit ROHashtableBuilder 129 // 130 // The parent class of ReadOnlyProperties (a.k.a. this class) is completely unused, and the 131 // private fields are not important. For inheritance purposes, though, this is the parent 132 // class, and as such, it has to be initialized with something! 133 134 ReadOnlyHashtable(ReadOnlyProperties.AccessBadge badge) 135 { 136 this.fromBuilderOrHashtable = true; // This is unused 137 this.hashTable = null; // This is unused 138 } 139 140 141 // ******************************************************************************************** 142 // ******************************************************************************************** 143 // Modified-Original Constructors 144 // ******************************************************************************************** 145 // ******************************************************************************************** 146 147 148 /** 149 * Copies the contents of parameter {@code 'map'}, and saves saves it, thereby guaranteeing 150 * {@code 'this'} instance is Read-Only and fully-shielded from outside modification. 151 * 152 * @param map The {@code map} to be copied into {@code 'this'} instance internal and private 153 * {@code 'hashTable'} field. 154 */ 155 public ReadOnlyHashtable(Map<K, V> map) 156 { 157 this.fromBuilderOrHashtable = false; 158 159 this.hashTable = (map.size() == 0) 160 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 161 : new Hashtable<>(map); 162 } 163 164 /** 165 * If only a small amount of processing needs to be done on the contents of some Java 166 * Map, and using an entire Builder-Class seems disproportionately complex - <I>this 167 * constructor can convert any Java {@code Map} into a {@code ReadOnlyHashtable}, using 168 * a simple {@code 'mapTranslator'}</I>. 169 * 170 * @param <X> The Key-Type of the User-Provided {@code Map}. 171 * @param <Y> The Value-Type of the User-Provided {@code Map}. 172 * 173 * @param refHolder This must a non-null instance of {@link Tuple2}. The provided 174 * {@code Consumer} is just that, a {@code 'Consumer'} rather than a {@code 'Function'}, since 175 * the results of each translation must be assigned to the values inside this tuple in order 176 * for them to be inserted into this {@code ReadOnlyHashtable}. 177 * 178 * @param map Any Java {@code Map}. 179 * 180 * @param mapTranslator A consumer for mapping the iterated elements of Map-Types {@code 'X'} 181 * and {@code 'Y'}, into the actual {@code Hashtable's} Key-Type {@code 'K'}, and Value-Type 182 * {@code 'V'}. The results of this translation must be placed into the fields inside 183 * {@code 'refHolder'}. 184 * 185 * <BR /><BR />If this parameter is passed null, this method will throw a 186 * {@code NullPointerException}. 187 * 188 * @param filter An optional filter that can be used to prevent & prohibit any chosen 189 * elements from input {@code 'map'} from being inserted into {@code 'this'} 190 * {@code ReadOnlyHashtable}. 191 * 192 * <BR /><BR />This parameter may be passed null, and if it is, it will be silently ignored, 193 * and all entries present inside {@code 'map'} will be processed and inserted into 194 * {@code 'this'} 195 * 196 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 197 * 198 * @throws NullPointerException if either parameter {@code 'i'} or parameter 199 * {@code 'mapTranslator'} is passed null. 200 */ 201 public <X, Y> ReadOnlyHashtable( 202 Tuple2<K, V> refHolder, 203 Map<X, Y> map, 204 Consumer<Map.Entry<X, Y>> mapTranslator, 205 Predicate<Map.Entry<X, Y>> filter, 206 Float loadFactor 207 ) 208 { 209 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 210 Objects.requireNonNull(mapTranslator, ROHelpers.NULL_MSG + "'mapTranslator'"); 211 212 fromBuilderOrHashtable = false; 213 214 Hashtable<K, V> hashTable = (loadFactor != null) 215 ? new Hashtable<>(map.size(), loadFactor) 216 : new Hashtable<>(map.size()); 217 218 if (filter == null) 219 220 for (Map.Entry<X, Y> entry : map.entrySet()) 221 { 222 mapTranslator.accept(entry); 223 hashTable.put(refHolder.a, refHolder.b); 224 } 225 226 else for (Map.Entry<X, Y> entry : map.entrySet()) 227 { 228 if (! filter.test(entry)) continue; 229 mapTranslator.accept(entry); 230 hashTable.put(refHolder.a, refHolder.b); 231 } 232 233 // Empty Optimization (throw away, completely, the reference, use static-constant) 234 this.hashTable = (hashTable.size() == 0) 235 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 236 : hashTable; 237 } 238 239 240 // ******************************************************************************************** 241 // ******************************************************************************************** 242 // New Stuff, January 2024 243 // ******************************************************************************************** 244 // ******************************************************************************************** 245 246 247 /** 248 * Constructs an instance of {@code ReadOnlyHashtable} that contains the keys present in 249 * parameter {@code 'keys'}, and values generated by {@code 'valueMapper'} - using each of the 250 * {@code 'keys'} as input. 251 * 252 * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT> 253 * 254 * @param keys Any Java {@code Iterable} instance. 255 * @param valueMapper A user provided function to compute a map value, based on a map key. 256 * @param filter <EMBED CLASS='external-html' DATA-FILE-ID=MAP_ITERABLE_FILTER> 257 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 258 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY> 259 * 260 * @throws NullPointerException if either {@code 'keys'} or {@code 'valueMapper'} are passed 261 * null. 262 */ 263 public ReadOnlyHashtable( 264 Iterable<? extends K> keys, 265 Function<? super K, ? extends V> valueMapper, 266 Predicate<? super K> filter, 267 Float loadFactor, 268 Integer sizeIfKnown 269 ) 270 { 271 Objects.requireNonNull(keys, ROHelpers.NULL_MSG + "'keys'"); 272 Objects.requireNonNull(valueMapper, ROHelpers.NULL_MSG + "'valueMapper'"); 273 274 fromBuilderOrHashtable = false; 275 276 Hashtable<K, V> hashTable = new Hashtable<>( 277 ((sizeIfKnown == null) ? 16 : sizeIfKnown), 278 ((loadFactor == null) ? 0.75f : loadFactor) 279 ); 280 281 if (filter == null) 282 for (K key : keys) 283 hashTable.put(key, valueMapper.apply(key)); 284 285 else 286 for (K key : keys) 287 if (filter.test(key)) 288 hashTable.put(key, valueMapper.apply(key)); 289 290 // Empty Optimization (throw away, completely, the reference, use static-constant) 291 this.hashTable = (hashTable.size() == 0) 292 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 293 : hashTable; 294 } 295 296 /** 297 * Constructs an instance of {@code ReadOnlyHashtable} that has been populated by the Key-Value 298 * Pairs left in {@code 'refHolder'} by each invocation of the {@code Runnable} parameter 299 * {@code 'computeNextEntry'}. Key-Value Pairs are inserted until an invocation of the 300 * {@code Runnable} leaves null in {@code refHolder.a} and {@code refHolder.b} 301 * 302 * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT> 303 * 304 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 305 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=COMPUTE_NEXT_RUN> 306 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 307 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY> 308 * 309 * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'} are 310 * passed null. 311 */ 312 public ReadOnlyHashtable( 313 Tuple2<K, V> refHolder, 314 Runnable computeNextEntry, 315 Float loadFactor, 316 Integer sizeIfKnown 317 ) 318 { 319 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 320 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 321 322 fromBuilderOrHashtable = false; 323 324 Hashtable<K, V> hashTable = new Hashtable<>( 325 ((sizeIfKnown == null) ? 16 : sizeIfKnown), 326 ((loadFactor == null) ? 0.75f : loadFactor) 327 ); 328 329 do 330 { 331 computeNextEntry.run(); 332 if ((refHolder.a == null) && (refHolder.b == null)) break; 333 hashTable.put(refHolder.a, refHolder.b); 334 } 335 while (true); 336 337 // Empty Optimization (throw away, completely, the reference, use static-constant) 338 this.hashTable = (hashTable.size() == 0) 339 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 340 : hashTable; 341 } 342 343 /** 344 * Populates an instance of {@code ReadOnlyHashtable} by iterating the input {@code 'source'} 345 * iterable, and passing each value returned by that {@code Iterator} to the 346 * {@code 'computeNextEntry'} Java {@code Consumer}. 347 * 348 * <BR /><BR />It is the programmer's responsibility to properly place each Key-Value Pair 349 * that is intending to be inserted into the final {@code Map} instance into the 350 * {@code 'refHolder'} instance. After each invocation of {@code 'computeNextEntry'}, this 351 * constructor's logic will retrieve the values within {@code 'refHolder.a'} and 352 * {@code 'refHolder.b'} and insert them into this instance internal {@code Hashtable}. 353 * 354 * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT> 355 * 356 * @param <X> The type of the elements inside {@code 'source'} 357 * @param source Any Java {@code Iterable} instance. 358 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 359 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=COMPUTE_NEXT_CONS> 360 * @param filter May be used to filter out some of the elements of {@code 'source'} 361 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 362 * @param sizeIfKnown <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY> 363 * 364 * @throws NullPointerException if either {@code 'refHolder'}, {@code 'computeNextEntry'} or 365 * {@code 'source'} are passed null. 366 */ 367 public <X> ReadOnlyHashtable( 368 Iterable<X> source, 369 Tuple2<K, V> refHolder, 370 Consumer<? super X> computeNextEntry, 371 Predicate<? super X> filter, 372 Float loadFactor, 373 Integer sizeIfKnown 374 ) 375 { 376 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 377 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 378 379 fromBuilderOrHashtable = false; 380 381 Hashtable<K, V> hashTable = new Hashtable<>( 382 ((sizeIfKnown == null) ? 16 : sizeIfKnown), 383 ((loadFactor == null) ? 0.75f : loadFactor) 384 ); 385 386 X x; // temp var 387 Iterator<X> iter = source.iterator(); 388 389 if (filter == null) 390 391 while (iter.hasNext()) 392 { 393 computeNextEntry.accept(iter.next()); 394 hashTable.put(refHolder.a, refHolder.b); 395 } 396 397 else 398 399 while (iter.hasNext()) 400 if (filter.test(x = iter.next())) 401 { 402 computeNextEntry.accept(x); 403 hashTable.put(refHolder.a, refHolder.b); 404 } 405 406 // Empty Optimization (throw away, completely, the reference, use static-constant) 407 this.hashTable = (hashTable.size() == 0) 408 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 409 : hashTable; 410 } 411 412 413 // ******************************************************************************************** 414 // ******************************************************************************************** 415 // Array Constructors, March 2024 416 // ******************************************************************************************** 417 // ******************************************************************************************** 418 419 420 /** 421 * Retrieves elements from the VarArgs Generic-Array parameter {@code 'elements'}, and 422 * subsequently invokes the {@code 'computeNextEntry'} processor to populate this 423 * {@code ReadOnlyHashtable}. 424 * 425 * @param <X> The type of array parameter {@code 'elements'} 426 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 427 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=ARR_COMPUTE_NEXT_CONS> 428 * @param filter <EMBED CLASS='external-html' DATA-FILE-ID=MAP_ARRAY_FILTER> 429 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 430 * @param elements Any Generic VarArgs-Array 431 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CCEX> 432 * 433 * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'} 434 * are passed null 435 */ 436 public <X> ReadOnlyHashtable( 437 Tuple2<K, V> refHolder, 438 Consumer<? super X> computeNextEntry, 439 Predicate<? super X> filter, 440 Float loadFactor, 441 X... elements 442 ) 443 { 444 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 445 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 446 447 this.fromBuilderOrHashtable = false; 448 449 Hashtable<K, V> hashTable = 450 new Hashtable<>(elements.length, ((loadFactor == null) ? 0.75f : loadFactor)); 451 452 if (filter == null) for (X e : elements) 453 { 454 computeNextEntry.accept(e); 455 hashTable.put(refHolder.a, refHolder.b); 456 } 457 458 else for (X x : elements) if (filter.test(x)) 459 { 460 computeNextEntry.accept(x); 461 hashTable.put(refHolder.a, refHolder.b); 462 } 463 464 // Empty Optimization (throw away, completely, the reference, use static-constant) 465 this.hashTable = (hashTable.size() == 0) 466 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 467 : hashTable; 468 } 469 470 /** 471 * Retrieves elements from the Java Primitive-Array parameter {@code 'primitiveArray'}, and 472 * subsequently invokes the {@code 'computeNextEntry'} processor to populate this 473 * {@code ReadOnlyHashtable}. 474 * 475 * @param filter <EMBED CLASS='external-html' DATA-FILE-ID=PRED_FILT_PRIM> 476 * @param computeNextEntry <EMBED CLASS='external-html' DATA-FILE-ID=ARR_COMPUTE_NEXT_CONS> 477 * @param refHolder <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER> 478 * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR> 479 * @param primitiveArray Any Java Primitive-Array 480 * @throws ClassCastException <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CCEX> 481 * 482 * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'} 483 * are passed null 484 */ 485 @LinkJavaSource(handle="ROHelperPrimitiveArrays", name="buildROMap") 486 public <X> ReadOnlyHashtable( 487 Predicate<?> filter, 488 Consumer<?> computeNextEntry, 489 Tuple2<K, V> refHolder, 490 Float loadFactor, 491 Object primitiveArray 492 ) 493 { 494 Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'"); 495 Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'"); 496 497 this.fromBuilderOrHashtable = false; 498 499 Hashtable<K, V> hashTable = ROHelperPrimitiveArrays.buildROMap( 500 primitiveArray, 501 (int arrayLen) -> new Hashtable<>(arrayLen, ((loadFactor == null) ? 0.75f : loadFactor)), 502 filter, 503 refHolder, 504 computeNextEntry 505 ); 506 507 // Empty Optimization (throw away, completely, the reference, use static-constant) 508 this.hashTable = (hashTable.size() == 0) 509 ? ((Hashtable<K, V>) EMPTY_HASH_TABLE) 510 : hashTable; 511 } 512 513 514 // ******************************************************************************************** 515 // ******************************************************************************************** 516 // Convert to java.util Types 517 // ******************************************************************************************** 518 // ******************************************************************************************** 519 520 521 /** 522 * Clone's {@code 'this'} instance internal {@code Hashtable<K, V>} field, and returns it. 523 * <EMBED CLASS='external-html' DATA-TYPE=Hashtable DATA-FILE-ID=CLONE_TO> 524 * 525 * @return An independent, mutable copy of {@code 'this'} instance internal 526 * {@code Hashtable<K, V>} data-structure. 527 */ 528 public Hashtable<K, V> cloneToHashtableOLD() 529 { 530 if (! fromBuilderOrHashtable) return (Hashtable<K, V>) this.hashTable.clone(); 531 532 Hashtable<K, V> ret = new Hashtable<K, V>(); 533 534 for (Map.Entry<K, V> e : 535 ((ROHashtableBuilder<K, V>) this.hashTable)._entrySet(friendClassBadge)) 536 537 ret.put(e.getKey(), e.getValue()); 538 539 return ret; 540 } 541 542 543 // ******************************************************************************************** 544 // ******************************************************************************************** 545 // Original JDK Methods, java.util.Hashtable 546 // ******************************************************************************************** 547 // ******************************************************************************************** 548 549 550 /** 551 * Returns the number of keys in this {@code Hashtable}. 552 * @return the number of keys in this {@code Hashtable}. 553 */ 554 public int size() 555 { return this.hashTable.size(); } 556 557 /** 558 * Tests if this {@code Hashtable} maps no keys to values. 559 * 560 * @return {@code TRUE} if this {@code Hashtable} maps no keys to values; {@code FALSE} 561 * otherwise. 562 */ 563 public boolean isEmpty() 564 { return this.hashTable.isEmpty(); } 565 566 /** 567 * Returns an enumeration of the keys in this {@code Hashtable}. Use the Enumeration methods 568 * on the returned object to fetch the keys sequentially. 569 * 570 * @return an enumeration of the keys in this {@code Hashtable}. 571 * 572 * @see #elements() 573 * @see #keySet() 574 * @see ReadOnlyMap 575 */ 576 public Enumeration<K> keys() 577 { return this.hashTable.keys(); } 578 579 /** 580 * Returns an enumeration of the values in this {@code Hashtable}. Use the Enumeration methods 581 * on the returned object to fetch the elements sequentially. 582 * 583 * @return an enumeration of the values in this {@code Hashtable}. 584 * 585 * @see #keys() 586 * @see #values() 587 * @see ReadOnlyMap 588 */ 589 public Enumeration<V> elements() 590 { return this.hashTable.elements(); } 591 592 /** 593 * Tests if some key maps into the specified value in this {@code Hashtable}. This operation 594 * is more expensive than the {@link #containsKey containsKey} method. 595 * 596 * <BR /><BR />Note that this method is identical in functionality to 597 * {@link #containsValue containsValue}, (which is part of the {@link ReadOnlyMap} interface. 598 * 599 * @param value a value to search for 600 * 601 * @return {@code TRUE} if and only if some key maps to the {@code value} argument in this 602 * {@code Hashtable} as determined by the {@code equals} method; {@code false} otherwise. 603 * 604 * @throws NullPointerException if the value is {@code null} 605 */ 606 public boolean contains(Object value) 607 { return this.hashTable.contains(value); } 608 609 /** 610 * Returns {@code TRUE} if this {@code Hashtable} maps one or more keys to this value. 611 * 612 * <BR /><BR />Note that this method is identical in functionality to {@link #contains}. 613 * 614 * @param value value whose presence in this {@code Hashtable} is to be tested 615 * @return {@code TRUE} if this map maps one or more keys to the specified value 616 * @throws NullPointerException if the value is {@code null} 617 */ 618 public boolean containsValue(Object value) 619 { return this.hashTable.contains(value); } 620 621 /** 622 * Tests if the specified object is a key in this {@code Hashtable}. 623 * 624 * @param key possible key 625 * 626 * @return {@code TRUE} if and only if the specified object is a key in this {@code Hashtable}, 627 * as determined by the {@code equals} method; {@code false} otherwise. 628 * 629 * @throws NullPointerException if the key is {@code null} 630 * 631 * @see #contains(Object) 632 */ 633 public boolean containsKey(Object key) 634 { return this.hashTable.containsKey(key); } 635 636 /** 637 * Returns the value to which the specified key is mapped, or {@code null} if this map contains 638 * no mapping for the key. 639 * 640 * <BR /><BR />More formally, if this map contains a mapping from a key {@code k} to a value 641 * {@code v} such that {@code (key.equals(k))}, then this method returns {@code v}; otherwise 642 * it returns {@code null}. (There can be at most one such mapping.) 643 * 644 * @param key the key whose associated value is to be returned 645 * 646 * @return the value to which the specified key is mapped, or {@code null} if this map contains 647 * no mapping for the key 648 * 649 * @throws NullPointerException if the specified key is null 650 */ 651 public V get(Object key) 652 { return this.hashTable.get(key); } 653 654 655 // ******************************************************************************************** 656 // ******************************************************************************************** 657 // Views 658 // ******************************************************************************************** 659 // ******************************************************************************************** 660 661 662 /** 663 * Returns a {@link ReadOnlySet} view of the keys contained in this map. The set is backed by 664 * the map, so changes to the map are reflected in the set, and vice-versa. 665 */ 666 @LinkJavaSource(handle="JavaHTMLReadOnlySet") 667 public ReadOnlySet<K> keySet() 668 { 669 return new JavaHTMLReadOnlySet<>( 670 fromBuilderOrHashtable 671 ? ((ROHashtableBuilder<K, V>) this.hashTable)._keySet(friendClassBadge) 672 : this.hashTable.keySet() 673 ); 674 } 675 676 /** Returns a {@link ReadOnlySet} view of the mappings contained in this map. */ 677 @LinkJavaSource(handle="ROHelperEntrySet") 678 public ReadOnlySet<ReadOnlyMap.Entry<K, V>> entrySet() 679 { 680 return ROHelperEntrySet.toReadOnlyEntrySet( 681 fromBuilderOrHashtable 682 ? ((ROHashtableBuilder<K, V>) this.hashTable)._entrySet(friendClassBadge) 683 : this.hashTable.entrySet() 684 ); 685 } 686 687 /** Returns a {@link ReadOnlyCollection} view of the values contained in this map. */ 688 @LinkJavaSource(handle="JavaHTMLReadOnlyCollection") 689 public ReadOnlyCollection<V> values() 690 { 691 return new JavaHTMLReadOnlyCollection<>( 692 fromBuilderOrHashtable 693 ? ((ROHashtableBuilder<K, V>) this.hashTable)._values(friendClassBadge) 694 : this.hashTable.values() 695 ); 696 } 697 698 699 // ******************************************************************************************** 700 // ******************************************************************************************** 701 // Comparison and hashing 702 // ******************************************************************************************** 703 // ******************************************************************************************** 704 705 706 public V getOrDefault(Object key, V defaultValue) 707 { return this.hashTable.getOrDefault(key, defaultValue); } 708 709 public void forEach(BiConsumer<? super K, ? super V> action) 710 { this.hashTable.forEach(action); } 711 712 713 // ******************************************************************************************** 714 // ******************************************************************************************** 715 // Convert to java.util Types 716 // ******************************************************************************************** 717 // ******************************************************************************************** 718 719 720 /** 721 * Clone's {@code 'this'} instance internal {@code Hashtable<K, V>} field, and returns it. 722 * <EMBED CLASS='external-html' DATA-TYPE=Hashtable DATA-FILE-ID=CLONE_TO> 723 * 724 * @return An independent, mutable copy of {@code 'this'} instance' internal 725 * {@code Hashtable<K, V>} data-structure. 726 */ 727 public Hashtable<K, V> cloneToHashtable() 728 { 729 return fromBuilderOrHashtable 730 ? new Hashtable<K, V>(this.hashTable) 731 : (Hashtable<K, V>) this.hashTable.clone(); 732 } 733 734 /** 735 * <BR>Same: Identical to the method {@link #cloneToHashtable()}. 736 * <BR>Returns: Has Return-Type {@code Map<K, V>}, instead. 737 * <BR>Implements: Parent-Class {@link ReadOnlyMap#cloneToMap} 738 */ 739 @IntoHTMLTable(title="Converts this ReadOnlyHashtable to a java.util.Hashtable") 740 public Map<K, V> cloneToMap() { return cloneToHashtable(); } 741 742 // Documented in the Implemented-Interface ReadOnlyMap 743 public Map<K, V> wrapToImmutableMap() 744 { return Collections.unmodifiableMap(this.hashTable); } 745 746 747 // ******************************************************************************************** 748 // ******************************************************************************************** 749 // java.lang.Object 750 // ******************************************************************************************** 751 // ******************************************************************************************** 752 753 754 /** 755 * Returns a string representation of this {@code Hashtable} object in the form of a set of 756 * entries, enclosed in braces and separated by the ASCII characters {@code " , "} (comma and 757 * space). Each entry is rendered as the key, an equals sign {@code '='}, and the associated 758 * element, where the {@code toString} method is used to convert the key and element to 759 * {@code String's}. 760 * 761 * @return a {@code String} representation of this {@code Hashtable} 762 */ 763 @LinkJavaSource(handle="ROHelperEntrySet") 764 public String toString() 765 { 766 return ROHelperEntrySet.toString( 767 this.hashTable, // if the map contains itself, it is needed for printing purposes 768 fromBuilderOrHashtable 769 ? ((ROHashtableBuilder<K, V>) this.hashTable)._entrySet(friendClassBadge) 770 : this.hashTable.entrySet() 771 ); 772 } 773 774 /** 775 * Compares the specified Object with this Map for equality, as per the definition in the 776 * class {@code java.util.Hashtable}. 777 * 778 * @param o object to be compared for equality with this {@code ReadOnlyHashtable}. 779 * @return {@code TRUE} if the specified Object is equal to this Map 780 */ 781 @LinkJavaSource(handle="ROHelperEquals", name="roMapEq") 782 public boolean equals(Object o) 783 { return ROHelperEquals.roMapEq(this, o); } 784 785 /** 786 * Returns the hash code value for this Map as per the definition in the class 787 * {@code java.util.Hashtable}. 788 */ 789 public int hashCode() 790 { return this.hashTable.hashCode(); } 791}