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 */ 025 026package Torello.Java.ReadOnly; 027 028import java.util.*; 029 030import java.util.function.BiConsumer; 031import java.io.Serializable; 032 033/** 034 * Immutable variant of Java Collections Framework interface 035 * <CODE>java.util.Map<K, V></CODE>. 036 * 037 * <EMBED CLASS='external-html' DATA-JDK=ReadOnlyMap DATA-FILE-ID=INTERFACES> 038 * 039 * @param <K> the type of keys maintained by this map 040 * @param <V> the type of mapped values 041 */ 042@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="JDHBI_INTERFACE") 043public interface ReadOnlyMap<K, V> 044{ 045 // ******************************************************************************************** 046 // ******************************************************************************************** 047 // Query Operations 048 // ******************************************************************************************** 049 // ******************************************************************************************** 050 051 052 /** 053 * Returns the number of key-value mappings in this map. If the map contains more than 054 * {@code Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}. 055 * 056 * @return the number of key-value mappings in this map 057 */ 058 int size(); 059 060 /** 061 * Returns {@code TRUE} if this map contains no key-value mappings. 062 * @return {@code TRUE} if this map contains no key-value mappings 063 */ 064 boolean isEmpty(); 065 066 /** 067 * Returns {@code TRUE} if this map contains a mapping for the specified key. More formally, 068 * returns {@code TRUE} if and only if this map contains a mapping for a key {@code k} such 069 * that {@code Objects.equals(key, k)}. (There can be at most one such mapping.) 070 * 071 * @param key key whose presence in this map is to be tested 072 * @return {@code TRUE} if this map contains a mapping for the specified key 073 * 074 * @throws ClassCastException if the key is of an inappropriate type for this map 075 * (<A HREF='ReadOnlyCollection.html#optional-restrictions'>optional-restrictions</A>) 076 * 077 * @throws NullPointerException if the specified key is null and this map does not permit null 078 * keys (<A HREF='ReadOnlyCollection.html#optional-restrictions'>optional-restrictions</A>) 079 */ 080 boolean containsKey(Object key); 081 082 /** 083 * Returns {@code TRUE} if this map maps one or more keys to the specified value. More 084 * formally, returns {@code TRUE} if and only if this map contains at least one mapping to a 085 * value {@code v} such that {@code Objects.equals(value, v)}. This operation will probably 086 * require time linear in the map size for most implementations of the {@code Map} interface. 087 * 088 * @param value value whose presence in this map is to be tested 089 * @return {@code TRUE} if this map maps one or more keys to the specified value 090 * 091 * @throws ClassCastException if the value is of an inappropriate type for 092 * this map (<A HREF='ReadOnlyCollection.html#optional-restrictions'>optional-restrictions</A>) 093 * 094 * @throws NullPointerException if the specified value is null and this map does not permit 095 * null values 096 * (<A HREF='ReadOnlyCollection.html#optional-restrictions'>optional-restrictions</A>) 097 */ 098 boolean containsValue(Object value); 099 100 /** 101 * Returns the value to which the specified key is mapped, or {@code null} if this map contains 102 * no mapping for the key. 103 * 104 * <BR /><BR />More formally, if this map contains a mapping from a key {@code k} to a value 105 * {@code v} such that {@code Objects.equals(key, k)}, then this method returns {@code v}; 106 * otherwise it returns {@code null}. (There can be at most one such mapping.) 107 * 108 * <BR /><BR />If this map permits null values, then a return value of {@code null} does not 109 * <i>necessarily</i> indicate that the map contains no mapping for the key; it's also possible 110 * that the map explicitly maps the key to {@code null}. The {@link #containsKey containsKey} 111 * operation may be used to distinguish these two cases. 112 * 113 * @param key the key whose associated value is to be returned 114 * 115 * @return the value to which the specified key is mapped, or {@code null} if this map contains 116 * no mapping for the key 117 * 118 * @throws ClassCastException if the key is of an inappropriate type for this map 119 * (<A HREF='ReadOnlyCollection.html#optional-restrictions'>optional-restrictions</A>) 120 * 121 * @throws NullPointerException if the specified key is null and this map does not permit null 122 * keys (<A HREF='ReadOnlyCollection.html#optional-restrictions'>optional-restrictions</A>) 123 */ 124 V get(Object key); 125 126 127 // ******************************************************************************************** 128 // ******************************************************************************************** 129 // Views 130 // ******************************************************************************************** 131 // ******************************************************************************************** 132 133 134 /** 135 * Returns a {@link ReadOnlySet} view of the keys contained in this map. The set is backed by 136 * the map, so changes to the map are reflected in the set, and vice-versa. 137 * 138 * @return a set view of the keys contained in this map 139 */ 140 ReadOnlySet<K> keySet(); 141 142 /** 143 * Returns a {@link ReadOnlyCollection} view of the values contained in this map. 144 * @return a collection view of the values contained in this map 145 */ 146 ReadOnlyCollection<V> values(); 147 148 /** 149 * Returns a {@link ReadOnlySet} view of the mappings contained in this map. 150 * @return a set view of the mappings contained in this map 151 */ 152 ReadOnlySet<ReadOnlyMap.Entry<K, V>> entrySet(); 153 154 /** 155 * A map entry (key-value pair). 156 * 157 * <BR /><BR />An Entry may also be obtained from a map's entry-set view by other means, for 158 * example, using the 159 * {@link ReadOnlySet#parallelStream parallelStream}, 160 * {@link ReadOnlySet#stream stream}, 161 * {@link ReadOnlySet#spliterator spliterator} 162 * methods, any of the 163 * {@link ReadOnlySet#toArray toArray} 164 * overloads, or by copying the entry-set view into another collection. 165 * 166 * <BR /><BR />In addition, an Entry may be obtained directly from a map, for example via calls 167 * to methods directly on the {@link ReadOnlyNavigableMap} interface. 168 * 169 * @param <K> the type of the key 170 * @param <V> the type of the value 171 * 172 * @see ReadOnlyMap#entrySet() 173 */ 174 interface Entry<K, V> 175 { 176 /** 177 * Returns the key corresponding to this entry. 178 * @return the key corresponding to this entry 179 */ 180 K getKey(); 181 182 /** 183 * Returns the value corresponding to this entry. 184 * @return the value corresponding to this entry 185 */ 186 V getValue(); 187 188 /** 189 * Compares the specified object with this entry for equality. 190 * Returns {@code TRUE} if the given object is also a map entry and 191 * the two entries represent the same mapping. More formally, two 192 * entries {@code e1} and {@code e2} represent the same mapping 193 * if 194 * 195 * <BR /><DIV CLASS=SNIP>{@code 196 * (e1.getKey()==null 197 * ? e2.getKey()==null 198 * : e1.getKey().equals(e2.getKey())) 199 * && 200 * (e1.getValue()==null 201 * ? e2.getValue()==null 202 * : e1.getValue().equals(e2.getValue())) 203 * }</DIV> 204 * 205 * <BR /><BR />This ensures that the {@code equals} method works properly across different 206 * implementations of the {@code ReadOnlyMap.Entry} interface. 207 * 208 * @param o object to be compared for equality with this map entry 209 * @return {@code TRUE} if the specified object is equal to this map entry 210 */ 211 boolean equals(Object o); 212 213 /** 214 * Returns the hash code value for this map entry. The hash code of a map entry {@code e} 215 * is defined to be: 216 * 217 * <BR /><DIV CLASS=SNIP>{@code 218 * (e.getKey()==null ? 0 : e.getKey().hashCode()) ^ 219 * (e.getValue()==null ? 0 : e.getValue().hashCode()) 220 * }</DIV> 221 * 222 * <BR /><BR />This ensures that {@code e1.equals(e2)} implies that 223 * {@code e1.hashCode()==e2.hashCode()} for any two Entries {@code e1} and {@code e2}, as 224 * required by the general contract of {@code Object.hashCode}. 225 * 226 * @return the hash code value for this map entry 227 * @see #equals(Object) 228 */ 229 int hashCode(); 230 231 /** 232 * Returns a comparator that compares {@link ReadOnlyMap.Entry} in natural order on key. 233 * 234 * <BR /><BR />The returned comparator is serializable and throws 235 * {@code NullPointerException} when comparing an entry with a null key. 236 * 237 * @param <K> the {@link Comparable} type of then map keys 238 * @param <V> the type of the map values 239 * @return a comparator that compares {@link ReadOnlyMap.Entry} in natural order on key. 240 */ 241 public static <K extends Comparable<? super K>, V> 242 Comparator<ReadOnlyMap.Entry<K, V>> 243 comparingByKey() 244 { 245 return (Comparator<ReadOnlyMap.Entry<K, V>> & Serializable) 246 (c1, c2) -> c1.getKey().compareTo(c2.getKey()); 247 } 248 249 /** 250 * Returns a comparator that compares {@link ReadOnlyMap.Entry} in natural order on value. 251 * 252 * <BR /><BR />The returned comparator is serializable and throws 253 * {@code NullPointerException} when comparing an entry with null values. 254 * 255 * @param <K> the type of the map keys 256 * @param <V> the {@code Comparable} type of the map values 257 * @return a comparator that compares {@link ReadOnlyMap.Entry} in natural order on value. 258 * @see Comparable 259 */ 260 public static <K, V extends Comparable<? super V>> 261 Comparator<ReadOnlyMap.Entry<K, V>> 262 comparingByValue() 263 { 264 return (Comparator<ReadOnlyMap.Entry<K, V>> & Serializable) 265 (c1, c2) -> c1.getValue().compareTo(c2.getValue()); 266 } 267 268 /** 269 * Returns a comparator that compares {@link ReadOnlyMap.Entry} by key using the given 270 * {@code Comparator}. 271 * 272 * <BR /><BR />The returned comparator is serializable if the specified comparator is also 273 * serializable. 274 * 275 * @param <K> the type of the map keys 276 * @param <V> the type of the map values 277 * @param cmp the key {@code Comparator} 278 * @return a comparator that compares {@link ReadOnlyMap.Entry} by the key. 279 */ 280 public static <K, V> 281 Comparator<ReadOnlyMap.Entry<K, V>> 282 comparingByKey(Comparator<? super K> cmp) 283 { 284 Objects.requireNonNull(cmp); 285 return (Comparator<ReadOnlyMap.Entry<K, V>> & Serializable) 286 (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey()); 287 } 288 289 /** 290 * Returns a comparator that compares {@link ReadOnlyMap.Entry} by value using the given 291 * {@code Comparator}. 292 * 293 * <BR /><BR />The returned comparator is serializable if the specified comparator is also 294 * serializable. 295 * 296 * @param <K> the type of the map keys 297 * @param <V> the type of the map values 298 * @param cmp the value {@link Comparator} 299 * @return a comparator that compares {@link ReadOnlyMap.Entry} by the value. 300 */ 301 public static <K, V> 302 Comparator<ReadOnlyMap.Entry<K, V>> 303 comparingByValue(Comparator<? super V> cmp) 304 { 305 Objects.requireNonNull(cmp); 306 return (Comparator<ReadOnlyMap.Entry<K, V>> & Serializable) 307 (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue()); 308 } 309 } 310 311 312 // ******************************************************************************************** 313 // ******************************************************************************************** 314 // Change The Type, Clone the Internal Data-Structure 315 // ******************************************************************************************** 316 // ******************************************************************************************** 317 318 319 /** 320 * Clones the internal {@code java.util.Map} representation, and returns it. This is the 321 * Top-Level Interface-Variant of this method. It returns the Interface-Implementation 322 * {@code java.util.Map}. 323 * 324 * <BR /><BR /><B CLASS=JDDescLabel>Alternate Variant:</B> 325 * 326 * <BR />The four classes which implement the interface {@code ReadOnlyMap} 327 * (which are {@link ReadOnlyHashMap}, {@link ReadOnlyTreeMap}, {@link ReadOnlyHashtable} and 328 * {@link ReadOnlyProperties}), for convenience, also implement an identical "Wrap To" Method 329 * which returns the exact type of the internal {@code Map} representation (which are 330 * {@code java.util.HashMap, java.util.TreeMap, java.util.Hashtable} and 331 * {@code java.util.Properties}) - rather than the Interface-Type {@code java.util.Map}. 332 * 333 * @return <UL CLASS=JDUL> 334 * 335 * <LI> For the sub-class {@link ReadOnlyHashMap}, this will return a cloned instance of the 336 * internal {@code java.util.HashMap}. 337 * <BR /><BR /> 338 * </LI> 339 * 340 * <LI> For the sub-class {@link ReadOnlyTreeMap}, this will return a cloned instance of the 341 * internal {@code java.util.TreeMap}. 342 * <BR /><BR /> 343 * </LI> 344 * 345 * <LI> For the sub-class {@link ReadOnlyHashtable}, this will return a cloned instance of the 346 * internal {@code java.util.Hashtable}. 347 * <BR /><BR /> 348 * </LI> 349 * 350 * <LI> For the sub-class {@link ReadOnlyProperties}, this will return a cloned instance of the 351 * internal {@code java.util.Properties}. 352 * </LI> 353 * 354 * </UL> 355 */ 356 Map<K, V> cloneToMap(); 357 358 /** 359 * Invokes {@code java.util.Collections.unmodifiableMap} on the internal {@code Map} 360 * 361 * <EMBED CLASS='external-html' DATA-JDK=Map DATA-RET_TYPE=Map 362 * DATA-FILE-ID=WRAP_TO_IMMUTABLE> 363 * 364 * @return A {@code Set} which adheres to the JDK interface {@code java.util.Set}, but throws 365 * an {@code UnsupportedOperationException} if a user attempts to invoke a Mutator-Method on 366 * the returned instance. 367 */ 368 Map<K, V> wrapToImmutableMap(); 369 370 371 // ******************************************************************************************** 372 // ******************************************************************************************** 373 // Comparison and hashing 374 // ******************************************************************************************** 375 // ******************************************************************************************** 376 377 378 /** 379 * Compares the specified object with this map for equality. Returns {@code TRUE} if the given 380 * object is also a map and the two maps represent the same mappings. More formally, two maps 381 * {@code m1} and {@code m2} represent the same mappings if 382 * {@code m1.entrySet().equals(m2.entrySet())}. This ensures that the {@code equals} method 383 * works properly across different implementations of the {@code Map} interface. 384 * 385 * @param o object to be compared for equality with this map 386 * @return {@code TRUE} if the specified object is equal to this map 387 */ 388 boolean equals(Object o); 389 390 /** 391 * Returns the hash code value for this map. The hash code of a map is defined to be the sum 392 * of the hash codes of each entry in the map's {@code entrySet()} view. This ensures that 393 * {@code m1.equals(m2)} implies that {@code m1.hashCode()==m2.hashCode()} for any two maps 394 * {@code m1} and {@code m2}, as required by the general contract of {@code Object.hashCode}. 395 * 396 * @return the hash code value for this map 397 * @see ReadOnlyMap.Entry#hashCode() 398 * @see #equals(Object) 399 */ 400 int hashCode(); 401 402 403 // ******************************************************************************************** 404 // ******************************************************************************************** 405 // Defaultable methods 406 // ******************************************************************************************** 407 // ******************************************************************************************** 408 409 410 /** 411 * Returns the value to which the specified key is mapped, or {@code defaultValue} if this map 412 * contains no mapping for the key. 413 * 414 * @param key the key whose associated value is to be returned 415 * @param defaultValue the default mapping of the key 416 * 417 * @return the value to which the specified key is mapped, or {@code defaultValue} if this map 418 * contains no mapping for the key 419 * 420 * @throws ClassCastException if the key is of an inappropriate type for this map 421 * (<A HREF='ReadOnlyCollection.html#optional-restrictions'>optional-restrictions</A>) 422 * 423 * @throws NullPointerException if the specified key is null and this map does not permit null 424 * keys 425 * (<A HREF='ReadOnlyCollection.html#optional-restrictions'>optional-restrictions</A>) 426 */ 427 default V getOrDefault(Object key, V defaultValue) 428 { 429 V v; 430 return (((v = get(key)) != null) || containsKey(key)) 431 ? v 432 : defaultValue; 433 } 434 435 /** 436 * Performs the given action for each entry in this map until all entries have been processed 437 * or the action throws an exception. Unless otherwise specified by the implementing class, 438 * actions are performed in the order of entry set iteration (if an iteration order is 439 * specified.) Exceptions thrown by the action are relayed to the caller. 440 * 441 * <BR /><BR />The default implementation is equivalent to, for this {@code map}: 442 * 443 * <BR /><DIV CLASS=SNIP>{@code 444 * for (ReadOnlyMap.Entry<K, V> entry : map.entrySet()) 445 * action.accept(entry.getKey(), entry.getValue()); 446 * }</DIV> 447 * 448 * <BR /><BR />The default implementation makes no guarantees about synchronization or 449 * atomicity properties of this method. Any implementation providing atomicity guarantees must 450 * override this method and document its concurrency properties. 451 * 452 * @param action The action to be performed for each entry 453 * @throws NullPointerException if the specified action is null 454 */ 455 default void forEach(BiConsumer<? super K, ? super V> action) 456 { 457 Objects.requireNonNull(action); 458 459 for (ReadOnlyMap.Entry<K, V> entry : entrySet()) 460 { 461 K k; 462 V v; 463 464 try 465 { 466 k = entry.getKey(); 467 v = entry.getValue(); 468 } 469 470 catch (IllegalStateException ise) 471 { 472 // this usually means the entry is no longer in the map. 473 // throw new ConcurrentModificationException(ise); 474 475 throw new Torello.Java.UnreachableError(); 476 } 477 478 action.accept(k, v); 479 } 480 } 481 482 /** 483 * Copies the contents of {@code 'this'} ReadOnlyMap into the user-provided {@code 'map'}. 484 * Utilizes the {@code java.util.Map} method {@code put(K key, V value)}. 485 * 486 * @param map Any instance of java.util.Map 487 * @see #forEach(BiConsumer) 488 */ 489 default void copyIntoMap(Map<? super K, ? super V> map) 490 { this.forEach((K k, V v) -> map.put(k, v)); } 491 492 493 // ******************************************************************************************** 494 // ******************************************************************************************** 495 // MAP-KEYS: contains - using Var-Args Arrays 496 // ******************************************************************************************** 497 // ******************************************************************************************** 498 499 500 /** 501 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 502 * <B STYLE='color: red;'>contains a matching key for every one of the elements</B> in 503 * Var-Args Parameter {@code 'elements'} 504 * 505 * @param elements a list of elements 506 * 507 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'keySet()'} contains 508 * every element in {@code 'elements'} 509 */ 510 default boolean containsKeyAND(Object... elements) 511 { 512 ReadOnlySet<K> keys = this.keySet(); 513 for (Object elem : elements) if (! keys.contains(elem)) return false; 514 return true; 515 } 516 517 /** 518 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 519 * <B STYLE='color: red;'>does not contain any key that matches any of the elements</B> in 520 * Var-Args Parameter {@code 'elements'} 521 * 522 * @param elements a list of elements 523 * 524 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'keySet()'} contains 525 * none of the elements in {@code 'elements'} 526 */ 527 default boolean containsKeyNAND(Object... elements) 528 { 529 ReadOnlySet<K> keys = this.keySet(); 530 for (Object elem : elements) if (keys.contains(elem)) return false; 531 return true; 532 } 533 534 /** 535 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 536 * <B STYLE='color: red;'>contains at least one key that matches at least one element</B> in 537 * Var-Args Parameter {@code 'elements'} 538 * 539 * @param elements a list of elements 540 * 541 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'keySet()'} contains 542 * one or more of the elements in {@code 'elements'} 543 */ 544 default boolean containsKeyOR(Object... elements) 545 { 546 ReadOnlySet<K> keys = this.keySet(); 547 for (Object elem : elements) if (keys.contains(elem)) return true; 548 return false; 549 } 550 551 /** 552 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 553 * <B STYLE='color: red;'>contains exactly one key that matches an element</B> in Var-Args 554 * Parameter {@code 'elements'} 555 * 556 * @param elements a list of elements 557 * 558 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'keySet()'} contains 559 * precisely one element that is also in {@code 'elements'} 560 */ 561 default boolean containsKeyXOR(Object... elements) 562 { 563 ReadOnlySet<K> keys = this.keySet(); 564 boolean found = false; 565 566 for (Object elem : elements) 567 568 if (keys.contains(elem)) 569 { 570 if (found) return false; 571 else found = true; 572 } 573 574 return found; 575 } 576 577 578 // ******************************************************************************************** 579 // ******************************************************************************************** 580 // MAP-KEYS: contains - using java.lang.Iterable 581 // ******************************************************************************************** 582 // ******************************************************************************************** 583 584 585 /** 586 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 587 * <B STYLE='color: red;'>contains a matching key for every one of the elements</B> in 588 * {@code Iterable} parameter {@code 'i'}. 589 * 590 * @param i any Java {@code Iterable} 591 * 592 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'keySet()'} contains 593 * every element in {@code 'i'} 594 */ 595 default boolean containsKeyAND(Iterable<?> i) 596 { 597 ReadOnlySet<K> keys = this.keySet(); 598 for (Object o: i) if (! keys.contains(o)) return false; 599 return true; 600 } 601 602 /** 603 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 604 * <B STYLE='color: red;'>does not contain any key that matches any of the elements</B> in 605 * {@code Iterable} parameter {@code 'i'} 606 * 607 * @param i any Java {@code Iterable} 608 * 609 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'keySet()'} contains 610 * none of the elements in {@code 'i'} 611 */ 612 default boolean containsKeyNAND(Iterable<?> i) 613 { 614 ReadOnlySet<K> keys = this.keySet(); 615 for (Object o: i) if (keys.contains(o)) return false; 616 return true; 617 } 618 619 /** 620 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 621 * <B STYLE='color: red;'>contains at least one key that matches at least one element</B> in 622 * {@code Iterable} parameter {@code 'i'} 623 * 624 * @param i any Java {@code Iterable} 625 * 626 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'keySet()'} contains 627 * one or more of the elements in {@code 'i'} 628 */ 629 default boolean containsKeyOR(Iterable<?> i) 630 { 631 ReadOnlySet<K> keys = this.keySet(); 632 for (Object o: i) if (keys.contains(o)) return true; 633 return false; 634 } 635 636 /** 637 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 638 * <B STYLE='color: red;'>contains exactly one key that matches an element</B> in 639 * {@code Iterable} parameter {@code 'i'} 640 * 641 * @param i any Java {@code Iterable} 642 * 643 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'keySet()'} contains 644 * precisely one element that is also in {@code 'i'} 645 */ 646 default boolean containsKeyXOR(Iterable<?> i) 647 { 648 ReadOnlySet<K> keys = this.keySet(); 649 boolean found = false; 650 651 for (Object o: i) 652 653 if (keys.contains(o)) 654 { 655 if (found) return false; 656 else found = true; 657 } 658 659 return found; 660 } 661 662 663 // ******************************************************************************************** 664 // ******************************************************************************************** 665 // MAP-VALUES: contains - using Var-Args Arrays 666 // ******************************************************************************************** 667 // ******************************************************************************************** 668 669 670 /** 671 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 672 * <B STYLE='color: red;'>contains a matching value for every one of the elements</B> in 673 * Var-Args Parameter {@code 'elements'} 674 * 675 * @param elements a list of elements 676 * 677 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'values()'} contains 678 * every element in {@code 'elements'} 679 */ 680 default boolean containsValueAND(Object... elements) 681 { 682 ReadOnlyCollection<V> values = this.values(); 683 for (Object elem : elements) if (! values.contains(elem)) return false; 684 return true; 685 } 686 687 /** 688 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 689 * <B STYLE='color: red;'>does not contain any value that matches any of the elements</B> in 690 * Var-Args Parameter {@code 'elements'} 691 * 692 * @param elements a list of elements 693 * 694 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'values()'} contains 695 * none of the elements in {@code 'elements'} 696 */ 697 default boolean containsValueNAND(Object... elements) 698 { 699 ReadOnlyCollection<V> values = this.values(); 700 for (Object elem : elements) if (values.contains(elem)) return false; 701 return true; 702 } 703 704 /** 705 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 706 * <B STYLE='color: red;'>contains at least one value that matches at least one element</B> in 707 * Var-Args Parameter {@code 'elements'} 708 * 709 * @param elements a list of elements 710 * 711 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'values()'} contains 712 * one or more of the elements in {@code 'elements'} 713 */ 714 default boolean containsValueOR(Object... elements) 715 { 716 ReadOnlyCollection<V> values = this.values(); 717 for (Object elem : elements) if (values.contains(elem)) return true; 718 return false; 719 } 720 721 /** 722 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 723 * <B STYLE='color: red;'>contains exactly one value that matches an element</B> in Var-Args 724 * Parameter {@code 'elements'} 725 * 726 * @param elements a list of elements 727 * 728 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'values()'} contains 729 * precisely one element that is also in {@code 'elements'} 730 */ 731 default boolean containsValueXOR(Object... elements) 732 { 733 ReadOnlyCollection<V> values = this.values(); 734 boolean found = false; 735 736 for (Object elem : elements) 737 738 if (values.contains(elem)) 739 { 740 if (found) return false; 741 else found = true; 742 } 743 744 return found; 745 } 746 747 748 // ******************************************************************************************** 749 // ******************************************************************************************** 750 // MAP-VALUES: contains - using java.lang.Iterable 751 // ******************************************************************************************** 752 // ******************************************************************************************** 753 754 755 /** 756 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 757 * <B STYLE='color: red;'>contains a matching value for every one of the elements</B> in 758 * {@code Iterable} parameter {@code 'i'} 759 * 760 * @param i any Java {@code Iterable} 761 * 762 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'values()'} contains 763 * every element in {@code 'i'} 764 */ 765 default boolean containsValueAND(Iterable<?> i) 766 { 767 ReadOnlyCollection<V> values = this.values(); 768 for (Object o: i) if (! values.contains(o)) return false; 769 return true; 770 } 771 772 /** 773 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 774 * <B STYLE='color: red;'>does not contain any value that matches any of the elements</B> in 775 * {@code Iterable} parameter {@code 'i'} 776 * 777 * @param i any Java {@code Iterable} 778 * 779 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'values()'} contains 780 * none of the elements in {@code 'i'} 781 */ 782 default boolean containsValueNAND(Iterable<?> i) 783 { 784 ReadOnlyCollection<V> values = this.values(); 785 for (Object o: i) if (values.contains(o)) return false; 786 return true; 787 } 788 789 /** 790 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 791 * <B STYLE='color: red;'>contains at least one value that matches at least one element</B> in 792 * {@code Iterable} parameter {@code 'i'} 793 * 794 * @param i any Java {@code Iterable} 795 * 796 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'values()'} contains 797 * one or more of the elements in {@code 'i'} 798 */ 799 default boolean containsValueOR(Iterable<?> i) 800 { 801 ReadOnlyCollection<V> values = this.values(); 802 for (Object o: i) if (values.contains(o)) return true; 803 return false; 804 } 805 806 /** 807 * Checks to ensure that {@code 'this'} instance of {@code ReadOnlyMap} 808 * <B STYLE='color: red;'>contains exactly one value that matches an element</B> in 809 * {@code Iterable} parameter {@code 'i'} 810 * 811 * @param i any Java {@code Iterable} 812 * 813 * @return {@code TRUE} If and only if {@code 'this'} instance' {@code 'values()'} contains 814 * precisely one element that is also in {@code 'i'} 815 */ 816 default boolean containsValueXOR(Iterable<?> i) 817 { 818 ReadOnlyCollection<V> values = this.values(); 819 boolean found = false; 820 821 for (Object o: i) 822 823 if (values.contains(o)) 824 { 825 if (found) return false; 826 else found = true; 827 } 828 829 return found; 830 } 831 832 833 // ******************************************************************************************** 834 // ******************************************************************************************** 835 // "of" ... 836 // ******************************************************************************************** 837 // ******************************************************************************************** 838 839 840 /** 841 * Returns an unmodifiable map containing zero mappings. 842 * 843 * @param <K> the {@code Map}'s key type 844 * @param <V> the {@code Map}'s value type 845 * @return an empty {@code Map} 846 */ 847 @SuppressWarnings("unchecked") 848 static <K, V> ReadOnlyMap<K, V> of() 849 { return new JavaHTMLReadOnlyMap<>(Map.of()); } 850 851 /** 852 * Returns an unmodifiable map containing a single mapping. 853 * 854 * @param <K> the {@code Map}'s key type 855 * @param <V> the {@code Map}'s value type 856 * @param k1 the mapping's key 857 * @param v1 the mapping's value 858 * 859 * @return a {@link ReadOnlyMap} containing the specified mapping 860 * @throws NullPointerException if the key or the value is {@code null} 861 */ 862 static <K, V> ReadOnlyMap<K, V> of(K k1, V v1) 863 { return new JavaHTMLReadOnlyMap<>(Map.of(k1, v1)); } 864 865 /** 866 * Returns an unmodifiable map containing two mappings. 867 * 868 * @param <K> the {@code Map}'s key type 869 * @param <V> the {@code Map}'s value type 870 * @param k1 the first mapping's key 871 * @param v1 the first mapping's value 872 * @param k2 the second mapping's key 873 * @param v2 the second mapping's value 874 * 875 * @return a {@link ReadOnlyMap} containing the specified mappings 876 * @throws IllegalArgumentException if the keys are duplicates 877 * @throws NullPointerException if any key or value is {@code null} 878 */ 879 static <K, V> ReadOnlyMap<K, V> of(K k1, V v1, K k2, V v2) 880 { return new JavaHTMLReadOnlyMap<>(Map.of(k1, v1, k2, v2)); } 881 882 /** 883 * Returns an unmodifiable map containing three mappings. 884 * 885 * @param <K> the {@code Map}'s key type 886 * @param <V> the {@code Map}'s value type 887 * @param k1 the first mapping's key 888 * @param v1 the first mapping's value 889 * @param k2 the second mapping's key 890 * @param v2 the second mapping's value 891 * @param k3 the third mapping's key 892 * @param v3 the third mapping's value 893 * 894 * @return a {@link ReadOnlyMap} containing the specified mappings 895 * @throws IllegalArgumentException if there are any duplicate keys 896 * @throws NullPointerException if any key or value is {@code null} 897 */ 898 static <K, V> ReadOnlyMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) 899 { return new JavaHTMLReadOnlyMap<>(Map.of(k1, v1, k2, v2, k3, v3)); } 900 901 /** 902 * Returns an unmodifiable map containing four mappings. 903 * 904 * @param <K> the {@code Map}'s key type 905 * @param <V> the {@code Map}'s value type 906 * @param k1 the first mapping's key 907 * @param v1 the first mapping's value 908 * @param k2 the second mapping's key 909 * @param v2 the second mapping's value 910 * @param k3 the third mapping's key 911 * @param v3 the third mapping's value 912 * @param k4 the fourth mapping's key 913 * @param v4 the fourth mapping's value 914 * 915 * @return a {@link ReadOnlyMap} containing the specified mappings 916 * @throws IllegalArgumentException if there are any duplicate keys 917 * @throws NullPointerException if any key or value is {@code null} 918 */ 919 static <K, V> ReadOnlyMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) 920 { return new JavaHTMLReadOnlyMap<>(Map.of(k1, v1, k2, v2, k3, v3, k4, v4)); } 921 922 /** 923 * Returns an unmodifiable map containing five mappings. 924 * 925 * @param <K> the {@code Map}'s key type 926 * @param <V> the {@code Map}'s value type 927 * @param k1 the first mapping's key 928 * @param v1 the first mapping's value 929 * @param k2 the second mapping's key 930 * @param v2 the second mapping's value 931 * @param k3 the third mapping's key 932 * @param v3 the third mapping's value 933 * @param k4 the fourth mapping's key 934 * @param v4 the fourth mapping's value 935 * @param k5 the fifth mapping's key 936 * @param v5 the fifth mapping's value 937 * 938 * @return a {@link ReadOnlyMap} containing the specified mappings 939 * @throws IllegalArgumentException if there are any duplicate keys 940 * @throws NullPointerException if any key or value is {@code null} 941 * 942 */ 943 static <K, V> ReadOnlyMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) 944 { return new JavaHTMLReadOnlyMap<>(Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5)); } 945 946 /** 947 * Returns an unmodifiable map containing six mappings. 948 * 949 * @param <K> the {@code Map}'s key type 950 * @param <V> the {@code Map}'s value type 951 * @param k1 the first mapping's key 952 * @param v1 the first mapping's value 953 * @param k2 the second mapping's key 954 * @param v2 the second mapping's value 955 * @param k3 the third mapping's key 956 * @param v3 the third mapping's value 957 * @param k4 the fourth mapping's key 958 * @param v4 the fourth mapping's value 959 * @param k5 the fifth mapping's key 960 * @param v5 the fifth mapping's value 961 * @param k6 the sixth mapping's key 962 * @param v6 the sixth mapping's value 963 * 964 * @return a {@link ReadOnlyMap} containing the specified mappings 965 * @throws IllegalArgumentException if there are any duplicate keys 966 * @throws NullPointerException if any key or value is {@code null} 967 */ 968 static <K, V> ReadOnlyMap<K, V> of( 969 K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, 970 K k6, V v6 971 ) 972 { 973 return new JavaHTMLReadOnlyMap<> 974 (Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6)); 975 } 976 977 /** 978 * Returns an unmodifiable map containing seven mappings. 979 * 980 * @param <K> the {@code Map}'s key type 981 * @param <V> the {@code Map}'s value type 982 * @param k1 the first mapping's key 983 * @param v1 the first mapping's value 984 * @param k2 the second mapping's key 985 * @param v2 the second mapping's value 986 * @param k3 the third mapping's key 987 * @param v3 the third mapping's value 988 * @param k4 the fourth mapping's key 989 * @param v4 the fourth mapping's value 990 * @param k5 the fifth mapping's key 991 * @param v5 the fifth mapping's value 992 * @param k6 the sixth mapping's key 993 * @param v6 the sixth mapping's value 994 * @param k7 the seventh mapping's key 995 * @param v7 the seventh mapping's value 996 * 997 * @return a {@link ReadOnlyMap} containing the specified mappings 998 * @throws IllegalArgumentException if there are any duplicate keys 999 * @throws NullPointerException if any key or value is {@code null} 1000 */ 1001 static <K, V> ReadOnlyMap<K, V> of( 1002 K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, 1003 K k6, V v6, K k7, V v7 1004 ) 1005 { 1006 return new JavaHTMLReadOnlyMap<> 1007 (Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7)); 1008 } 1009 1010 /** 1011 * Returns an unmodifiable map containing eight mappings. 1012 * 1013 * @param <K> the {@code Map}'s key type 1014 * @param <V> the {@code Map}'s value type 1015 * @param k1 the first mapping's key 1016 * @param v1 the first mapping's value 1017 * @param k2 the second mapping's key 1018 * @param v2 the second mapping's value 1019 * @param k3 the third mapping's key 1020 * @param v3 the third mapping's value 1021 * @param k4 the fourth mapping's key 1022 * @param v4 the fourth mapping's value 1023 * @param k5 the fifth mapping's key 1024 * @param v5 the fifth mapping's value 1025 * @param k6 the sixth mapping's key 1026 * @param v6 the sixth mapping's value 1027 * @param k7 the seventh mapping's key 1028 * @param v7 the seventh mapping's value 1029 * @param k8 the eighth mapping's key 1030 * @param v8 the eighth mapping's value 1031 * 1032 * @return a {@link ReadOnlyMap} containing the specified mappings 1033 * @throws IllegalArgumentException if there are any duplicate keys 1034 * @throws NullPointerException if any key or value is {@code null} 1035 */ 1036 static <K, V> ReadOnlyMap<K, V> of( 1037 K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, 1038 K k6, V v6, K k7, V v7, K k8, V v8 1039 ) 1040 { 1041 return new JavaHTMLReadOnlyMap<> 1042 (Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8)); 1043 } 1044 1045 /** 1046 * Returns an unmodifiable map containing nine mappings. 1047 * 1048 * @param <K> the {@code Map}'s key type 1049 * @param <V> the {@code Map}'s value type 1050 * @param k1 the first mapping's key 1051 * @param v1 the first mapping's value 1052 * @param k2 the second mapping's key 1053 * @param v2 the second mapping's value 1054 * @param k3 the third mapping's key 1055 * @param v3 the third mapping's value 1056 * @param k4 the fourth mapping's key 1057 * @param v4 the fourth mapping's value 1058 * @param k5 the fifth mapping's key 1059 * @param v5 the fifth mapping's value 1060 * @param k6 the sixth mapping's key 1061 * @param v6 the sixth mapping's value 1062 * @param k7 the seventh mapping's key 1063 * @param v7 the seventh mapping's value 1064 * @param k8 the eighth mapping's key 1065 * @param v8 the eighth mapping's value 1066 * @param k9 the ninth mapping's key 1067 * @param v9 the ninth mapping's value 1068 * 1069 * @return a {@link ReadOnlyMap} containing the specified mappings 1070 * @throws IllegalArgumentException if there are any duplicate keys 1071 * @throws NullPointerException if any key or value is {@code null} 1072 */ 1073 static <K, V> ReadOnlyMap<K, V> of( 1074 K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, 1075 K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9 1076 ) 1077 { 1078 return new JavaHTMLReadOnlyMap<> 1079 (Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9)); 1080 } 1081 1082 /** 1083 * Returns an unmodifiable map containing ten mappings. 1084 * 1085 * @param <K> the {@code Map}'s key type 1086 * @param <V> the {@code Map}'s value type 1087 * @param k1 the first mapping's key 1088 * @param v1 the first mapping's value 1089 * @param k2 the second mapping's key 1090 * @param v2 the second mapping's value 1091 * @param k3 the third mapping's key 1092 * @param v3 the third mapping's value 1093 * @param k4 the fourth mapping's key 1094 * @param v4 the fourth mapping's value 1095 * @param k5 the fifth mapping's key 1096 * @param v5 the fifth mapping's value 1097 * @param k6 the sixth mapping's key 1098 * @param v6 the sixth mapping's value 1099 * @param k7 the seventh mapping's key 1100 * @param v7 the seventh mapping's value 1101 * @param k8 the eighth mapping's key 1102 * @param v8 the eighth mapping's value 1103 * @param k9 the ninth mapping's key 1104 * @param v9 the ninth mapping's value 1105 * @param k10 the tenth mapping's key 1106 * @param v10 the tenth mapping's value 1107 * 1108 * @return a {@link ReadOnlyMap} containing the specified mappings 1109 * @throws IllegalArgumentException if there are any duplicate keys 1110 * @throws NullPointerException if any key or value is {@code null} 1111 */ 1112 static <K, V> ReadOnlyMap<K, V> of( 1113 K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, 1114 K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10 1115 ) 1116 { 1117 return new JavaHTMLReadOnlyMap<>( 1118 Map.of( 1119 k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, 1120 k7, v7, k8, v8, k9, v9, k10, v10 1121 )); 1122 } 1123 1124 /** 1125 * Returns an unmodifiable map containing keys and values extracted from the given entries. 1126 * The entries themselves are not stored in the map. 1127 * 1128 * <BR /><BR />It is convenient to create the map entries using the 1129 * {@link ReadOnlyMap#entry ReadOnlyMap.entry()} method. For example, 1130 * 1131 * <DIV CLASS=EXAMPLE>{@code 1132 * import static Torello.Java.ReadOnly.ReadOnlyMap.entry; 1133 * 1134 * ReadOnlyMap<Integer,String> map = ReadOnlyMap.ofEntries( 1135 * entry(1, "a"), 1136 * entry(2, "b"), 1137 * entry(3, "c"), 1138 * ... 1139 * entry(26, "z")); 1140 * }</DIV> 1141 * 1142 * @param <K> the {@link ReadOnlyMap}'s key type 1143 * @param <V> the {@link ReadOnlyMap}'s value type 1144 * 1145 * @param entries {@code ReadOnlyMap.Entry}s containing the keys and values from which the map 1146 * is populated 1147 * 1148 * @return a {@link ReadOnlyMap} containing the specified mappings 1149 * @throws IllegalArgumentException if there are any duplicate keys 1150 * 1151 * @throws NullPointerException if any entry, key, or value is {@code null}, or if the 1152 * {@code entries} array is {@code null} 1153 * 1154 * @see ReadOnlyMap#entry Map.entry() 1155 */ 1156 @SafeVarargs 1157 @SuppressWarnings("varargs") 1158 static <K, V> 1159 ReadOnlyMap<K, V> 1160 ofEntries 1161 (ReadOnlyMap.Entry<? extends K, ? extends V>... entries) 1162 { 1163 // *** Java-HTML: This arbitrarily uses TreeMap 1164 ROTreeMapBuilder<K, V> b = new ROTreeMapBuilder<>(); 1165 1166 for (ReadOnlyMap.Entry<? extends K, ? extends V> e : entries) 1167 b.put(e.getKey(), e.getValue()); 1168 1169 return b.build(); 1170 } 1171 1172 /** 1173 * Returns an unmodifiable {@link Entry} containing the given key and value. 1174 * These entries are suitable for populating {@code ReadOnlyMap} instances using the 1175 * {@link ReadOnlyMap#ofEntries ReadOnlyMap.ofEntries()} method. 1176 * 1177 * @param <K> the key's type 1178 * @param <V> the value's type 1179 * @param k the key 1180 * @param v the value 1181 * @return an {@code Entry} containing the specified key and value 1182 * @throws NullPointerException if the key or value is {@code null} 1183 * 1184 * @see ReadOnlyMap#ofEntries Map.ofEntries() 1185 */ 1186 static <K, V> ReadOnlyMap.Entry<K, V> entry(K k, V v) 1187 { 1188 // KeyValueHolder checks for nulls 1189 // return new KeyValueHolder<>(k, v); 1190 return new EntryImpl<>(Objects.requireNonNull(k), Objects.requireNonNull(v)); 1191 } 1192}