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