001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2022–2023 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 014 * implied. See the License for the specific language governing 015 * permissions and limitations under the License. 016 */ 017package org.microbean.qualifier; 018 019import java.lang.constant.ClassDesc; 020import java.lang.constant.Constable; 021import java.lang.constant.ConstantDesc; 022import java.lang.constant.DynamicConstantDesc; 023import java.lang.constant.MethodHandleDesc; 024 025import java.util.HashSet; 026import java.util.Iterator; 027import java.util.Objects; 028import java.util.Optional; 029import java.util.Set; 030import java.util.SortedSet; 031import java.util.Spliterator; 032import java.util.TreeSet; 033 034import java.util.stream.Stream; 035 036import org.microbean.constant.Constables; 037 038import static java.lang.constant.ConstantDescs.BSM_INVOKE; 039 040import static java.util.Collections.emptySortedSet; 041import static java.util.Collections.unmodifiableSortedSet; 042 043import static org.microbean.constant.ConstantDescs.CD_Iterable; 044 045/** 046 * An abstract, immutable {@linkplain Iterable iterable} collection of 047 * {@link Binding} instances. 048 * 049 * @param <V> the type of a {@link Binding}'s {@linkplain 050 * Binding#attributes() attribute values} 051 * 052 * @param <B> The concrete subtype of this class 053 * 054 * @author <a href="https://about.me/lairdnelson" 055 * target="_parent">Laird Nelson</a> 056 */ 057public abstract class Bindings<V, B extends Binding<V, B>> implements Constable, Iterable<B> { 058 059 060 /* 061 * Instance fields. 062 */ 063 064 065 private final SortedSet<B> bindings; 066 067 068 /* 069 * Constructors. 070 */ 071 072 073 /** 074 * Creates a new {@link Bindings}. 075 * 076 * <p>Duplicate {@link Binding} instances contained by the supplied 077 * {@link Iterable} will be discarded.</p> 078 * 079 * @param bindings the {@link Binding} instances that will be 080 * contained by this {@link Bindings} 081 */ 082 protected Bindings(final Iterable<? extends B> bindings) { 083 super(); 084 if (bindings == null) { 085 this.bindings = emptySortedSet(); 086 } else { 087 final Iterator<? extends B> i = bindings.iterator(); 088 if (i.hasNext()) { 089 final SortedSet<B> newBindings = new TreeSet<>(); 090 newBindings.add(i.next()); 091 while (i.hasNext()) { 092 newBindings.add(i.next()); 093 } 094 this.bindings = unmodifiableSortedSet(newBindings); 095 } else { 096 this.bindings = emptySortedSet(); 097 } 098 } 099 } 100 101 102 /* 103 * Instance methods. 104 */ 105 106 107 /** 108 * Returns {@code true} if this {@link Bindings} is logically empty. 109 * 110 * @return {@code true} if this {@link Bindings} is logically empty 111 * 112 * @idempotency This method is idempotent and deterministic. 113 * 114 * @threadsafety This method is safe for concurrent use by multiple 115 * threads. 116 * 117 * @see #size() 118 */ 119 public final boolean isEmpty() { 120 return this.bindings.isEmpty(); 121 } 122 123 /** 124 * Returns {@code 0} or a positive integer describing the number of 125 * entries contained by this {@link Bindings}. 126 * 127 * @return the size of this {@link Bindings} 128 * 129 * @idempotency This method is idempotent and deterministic. 130 * 131 * @threadsafety This method is safe for concurrent use by multiple 132 * threads. 133 */ 134 public final int size() { 135 return this.bindings.size(); 136 } 137 138 /** 139 * Returns the sole {@link Binding} {@linkplain Binding#value() 140 * value} whose {@linkplain Binding#name() name} {@linkplain 141 * String#equals(Object) is equal to} the supplied {@code name}, or 142 * {@code null} if either there is no such {@link Binding} or there 143 * are several {@link Binding}s with the supplied {@code name}. 144 * 145 * @param name the name; may be {@code null} in which case {@code 146 * false} will be returned 147 * 148 * @return the sole {@link Binding} {@linkplain Binding#value() 149 * value} whose {@linkplain Binding#name() name} {@linkplain 150 * String#equals(Object) is equal to} the supplied {@code name}, or 151 * {@code null} if either there is no such {@link Binding} or there 152 * are several {@link Binding}s with the supplied {@code name}. 153 * 154 * @nullability This method may return {@code null}. 155 * 156 * @idempotency This method is idempotent and deterministic. 157 * 158 * @threadsafety This method is safe for concurrent use by multiple 159 * threads. 160 * 161 * @see #unique(String) 162 */ 163 public final V uniqueValue(final String name) { 164 final B b = this.unique(name); 165 return b == null ? null : b.value(); 166 } 167 168 /** 169 * Returns the sole {@link Binding} instance whose {@linkplain 170 * Binding#name() name} {@linkplain String#equals(Object) is equal 171 * to} the supplied {@code name}, or {@code null} if either there is 172 * no such {@link Binding} or there are several {@link Binding}s 173 * with the supplied {@code name}. 174 * 175 * @param name the name; may be {@code null} in which case {@code 176 * false} will be returned 177 * 178 * @return the sole {@link Binding} instance whose {@linkplain 179 * Binding#name() name} {@linkplain String#equals(Object) is equal 180 * to} the supplied {@code name}, or {@code null} if either there is 181 * no such {@link Binding} or there are several {@link Binding}s 182 * with the supplied {@code name}. 183 * 184 * @nullability This method may return {@code null}. 185 * 186 * @idempotency This method is idempotent and deterministic. 187 * 188 * @threadsafety This method is safe for concurrent use by multiple 189 * threads. 190 */ 191 public final B unique(final String name) { 192 B returnValue = null; 193 if (name != null) { 194 for (final B b : this) { 195 if (b.name().equals(name)) { 196 if (returnValue == null) { 197 returnValue = b; 198 } else { 199 return null; 200 } 201 } 202 } 203 } 204 return returnValue; 205 } 206 207 /** 208 * Returns {@code true} if and only if the supplied {@link Object} 209 * is contained by this {@link Bindings}. 210 * 211 * <p>If the {@link Object} is a {@link Binding}, the containment 212 * check is performed via an equality check. If the {@link Object} 213 * is a {@link String}, then this method will return {@code true} if 214 * there is a {@link Binding} contained by this {@link Bindings} 215 * whose {@linkplain Binding#name() name} {@linkplain 216 * String#equals(Object) is equal to} the supplied {@link 217 * String}.</p> 218 * 219 * <p>There may be many {@link Binding} instances contained by this 220 * {@link Bindings} whose {@linkplain Binding#name() names} are 221 * {@linkplain String#equals(Object) equal}.</p> 222 * 223 * @param o the {@link Object} to test; {@code true} return values 224 * are possible only when this {@link Object} is either a {@link 225 * Binding} or a {@link String} 226 * 227 * @return {@code true} if and only if the supplied {@link Object} 228 * is contained by this {@link Bindings} 229 * 230 * @idempotency This method is idempotent and deterministic. 231 * 232 * @threadsafety This method is safe for concurrent use by multiple 233 * threads. 234 * 235 * @see #containsUnique(String) 236 */ 237 public final boolean contains(final Object o) { 238 if (o == null || o instanceof Binding<?, ?>) { 239 return this.bindings.contains(o); 240 } 241 for (final B b : this) { 242 if (b.name().equals(o)) { 243 return true; 244 } 245 } 246 return false; 247 } 248 249 /** 250 * Returns {@code true} if and only if there is exactly one {@link 251 * Binding} contained by this {@link Bindings} whose {@linkplain 252 * Binding#name() name} {@linkplain String#equals(Object) is equal 253 * to} the supplied {@code name}. 254 * 255 * @param name the name to test; may be {@code null} in which case 256 * {@code false} will be returned 257 * 258 * @return {@code true} if and only if there is exactly one {@link 259 * Binding} contained by this {@link Bindings} whose {@linkplain 260 * Binding#name() name} {@linkplain String#equals(Object) is equal 261 * to} the supplied {@code name} 262 * 263 * @idempotency This method is idempotent and deterministic. 264 * 265 * @threadsafety This method is safe for concurrent use by multiple 266 * threads. 267 */ 268 public final boolean containsUnique(final String name) { 269 return this.unique(name) != null; 270 } 271 272 /** 273 * Returns a {@link Stream} of this {@link Bindings}' {@linkplain 274 * Binding entries}. 275 * 276 * @return a {@link Stream} of this {@link Bindings}' {@linkplain 277 * Binding entries} 278 * 279 * @nullability This method never returns {@code null}. 280 * 281 * @idempotency This method is idempotent and deterministic. 282 * 283 * @threadsafety This method is safe for concurrent use by multiple 284 * threads. 285 */ 286 public final Stream<B> stream() { 287 return this.bindings.stream(); 288 } 289 290 /** 291 * Returns a non-{@code null}, immutable {@link Iterator} of {@link 292 * Binding} instances contained by this {@link Bindings}. 293 * 294 * @return a non-{@code null}, immutable {@link Iterator} of {@link 295 * Binding} instances contained by this {@link Bindings} 296 * 297 * @nullability This method never returns {@code null}. 298 * 299 * @idempotency This method is idempotent and deterministic. 300 * 301 * @threadsafety This method is safe for concurrent use by multiple 302 * threads. 303 */ 304 @Override // Iterable<B> 305 public final Iterator<B> iterator() { 306 return this.bindings.iterator(); 307 } 308 309 /** 310 * Returns a non-{@code null}, immutable {@link Spliterator} of 311 * {@link Binding} instances contained by this {@link Bindings}. 312 * 313 * @return a non-{@code null}, immutable {@link Spliterator} of 314 * {@link Binding} instances contained by this {@link Bindings} 315 * 316 * @nullability This method never returns {@code null}. 317 * 318 * @idempotency This method is idempotent and deterministic. 319 * 320 * @threadsafety This method is safe for concurrent use by multiple 321 * threads. 322 */ 323 @Override // Iterable<B> 324 public final Spliterator<B> spliterator() { 325 return this.bindings.spliterator(); 326 } 327 328 /** 329 * Returns the number of entries this {@link Bindings} has in common 330 * with the supplied {@link Iterable}. 331 * 332 * <p>The number returned will be {@code 0} or greater.</p> 333 * 334 * @param other the {@link Iterable}; may be {@code null} in which 335 * case {@code 0} will be returned 336 * 337 * @return the number of entries this {@link Bindings} has in common 338 * with the supplied {@link Iterable} 339 * 340 * @idempotency This method is idempotent and deterministic. 341 * 342 * @threadsafety This method is safe for concurrent use by multiple 343 * threads. 344 */ 345 public final int intersectionSize(final Iterable<?> other) { 346 if (other == null) { 347 return 0; 348 } else if (this == other) { 349 return this.size(); 350 } else { 351 final Iterator<?> i = other.iterator(); 352 if (i.hasNext()) { 353 int count = this.bindings.contains(i.next()) ? 1 : 0; 354 while (i.hasNext()) { 355 if (this.bindings.contains(i.next())) { 356 ++count; 357 } 358 } 359 return count; 360 } else { 361 return 0; 362 } 363 } 364 } 365 366 /** 367 * Returns the size of the <em>symmetric difference</em> between 368 * this {@link Bindings} and the supplied {@link Iterable}. 369 * 370 * <p>The number returned is always {@code 0} or greater.</p> 371 * 372 * <p>The size of the symmetric difference between this {@link 373 * Bindings} instance and the supplied {@link Iterable} is the 374 * number of entries that are in one of these two objects but not in 375 * the other.</p> 376 * 377 * @param other an {@link Iterable}; may be {@code null} in which 378 * case the result of an invocation of this {@link Bindings}' 379 * {@link #size()} method will be returned 380 * 381 * @return the size of the <em>symmetric difference</em> between 382 * this {@link Bindings} and the supplied {@link Iterable}; always 383 * {@code 0} or greater 384 * 385 * @idempotency This method is idempotent and deterministic. 386 * 387 * @threadsafety This method is safe for concurrent use by multiple 388 * threads. 389 */ 390 public final int symmetricDifferenceSize(final Iterable<?> other) { 391 if (other == null) { 392 return this.size(); 393 } else if (this == other) { 394 return 0; 395 } else { 396 final Iterator<?> i = other.iterator(); 397 if (i.hasNext()) { 398 final Set<? super Object> symmetricDifference = new HashSet<>(this.bindings); 399 Object b = i.next(); 400 if (!symmetricDifference.add(b)) { 401 symmetricDifference.remove(b); 402 } 403 while (i.hasNext()) { 404 b = i.next(); 405 if (!symmetricDifference.add(b)) { 406 symmetricDifference.remove(b); 407 } 408 } 409 return symmetricDifference.size(); 410 } else { 411 return this.size(); 412 } 413 } 414 } 415 416 /** 417 * Returns an {@link Optional} housing a {@link ConstantDesc} 418 * describing this {@link Bindings}, if this {@link Bindings} is 419 * capable of being represented as a <a 420 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic 421 * constant</a>, or an {@linkplain Optional#isEmpty() empty} {@link 422 * Optional} if not. 423 * 424 * @return an {@link Optional} housing a {@link ConstantDesc} 425 * describing this {@link Binding}, if this {@link Bindings} is 426 * capable of being represented as a <a 427 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic 428 * constant</a>, or an {@linkplain Optional#isEmpty() empty} {@link 429 * Optional} if not 430 * 431 * @nullability This method never returns {@code null}. 432 * 433 * @idempotency This method is idempotent and deterministic. 434 * 435 * @threadsafety This method is safe for concurrent use by multiple 436 * threads. 437 * 438 * @see #describeConstructor() 439 * 440 * @see <a 441 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">Dynamically-computed 442 * constants</a> 443 */ 444 @Override // Constable 445 public final Optional<? extends ConstantDesc> describeConstable() { 446 final MethodHandleDesc constructor = this.describeConstructor(); 447 if (constructor == null) { 448 return Optional.empty(); 449 } 450 final ConstantDesc bindingsCd = Constables.describeConstable(this.bindings).orElse(null); 451 if (bindingsCd == null) { 452 return Optional.empty(); 453 } 454 return 455 Optional.of(DynamicConstantDesc.of(BSM_INVOKE, constructor, bindingsCd)); 456 } 457 458 /** 459 * Returns a {@link MethodHandleDesc} describing the constructor or 460 * {@code static} method that will be used to create a <a 461 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic 462 * constant</a> representing this {@link Bindings}. 463 * 464 * @return a {@link MethodHandleDesc} describing the constructor or 465 * {@code static} method that will be used to create a <a 466 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic 467 * constant</a> representing this {@link Bindings} 468 * 469 * @nullability This method does not, and its overrides must not, 470 * return {@code null}. 471 * 472 * @idempotency This method is, and its overrides must be, 473 * idempotent and deterministic. 474 * 475 * @threadsafety This method is, and its overrides must be, safe for 476 * concurrent use by multiple threads. 477 */ 478 protected MethodHandleDesc describeConstructor() { 479 return 480 MethodHandleDesc.ofConstructor(this.getClass().describeConstable().orElseThrow(), 481 new ClassDesc[] { CD_Iterable }); 482 } 483 484 /** 485 * Returns a hashcode for this {@link Bindings}. 486 * 487 * @return a hashcode for this {@link Bindings} 488 * 489 * @idempotency This method is, and its overrides must be, 490 * idempotent and deterministic. 491 * 492 * @threadsafety This method is, and its overrides must be, safe for 493 * concurrent use by multiple threads. 494 * 495 * @see #equals(Object) 496 */ 497 @Override // Object 498 public final int hashCode() { 499 return this.bindings.hashCode(); 500 } 501 502 /** 503 * Returns {@code true} if and only if this {@link Bindings} is 504 * equal to the supplied {@link Object}. 505 * 506 * <p>The supplied {@link Object} is considered to be equal to this 507 * {@link Bindings} if and only if:</p> 508 * 509 * <ul> 510 * 511 * <li>Its {@linkplain #getClass() class} is identical to this 512 * {@link Bindings}' {@linkplain #getClass() class}, and</li> 513 * 514 * <li>Each of the {@link Binding} instances it contains is 515 * {@linkplain Binding#equals(Object) equal to} the corresponding 516 * {@link Binding} instance contained by this {@link Bindings}</li> 517 * 518 * </ul> 519 * 520 * @param other the {@link Object} to test; may be {@code null} in 521 * which case {@code false} will be returned 522 * 523 * @return {@code true} if the supplied {@link Object} is equal to 524 * this {@link Binding} 525 * 526 * @idempotency This method is, and its overrides must be, 527 * idempotent and deterministic. 528 * 529 * @threadsafety This method is, and its overrides must be, safe for 530 * concurrent use by multiple threads. 531 * 532 * @see #hashCode() 533 * 534 * @see Binding#equals(Object) 535 */ 536 @Override // Object 537 public final boolean equals(final Object other) { 538 if (other == this) { 539 return true; 540 } else if (other != null && other.getClass() == this.getClass()) { 541 final Bindings<?, ?> her = (Bindings<?, ?>)other; 542 return 543 Objects.equals(this.bindings, her.bindings); 544 } else { 545 return false; 546 } 547 } 548 549 /** 550 * Returns a {@link String} representation of this {@link Bindings}. 551 * 552 * <p>The format of the returned {@link String} is deliberately 553 * undefined and may change between versions of this class without 554 * prior notice.</p> 555 * 556 * @return a {@link String} representation of this {@link Bindings} 557 * 558 * @nullability This method does not, and its overrides must not, 559 * return {@code null}. 560 * 561 * @idempotency This method is, and its overrides must be, 562 * idempotent and deterministic. 563 * 564 * @threadsafety This method is, and its overrides must be, safe for 565 * concurrent use by multiple threads. 566 */ 567 @Override // Object 568 public String toString() { 569 return this.bindings.toString(); 570 } 571 572}