001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2022 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.Constable; 020import java.lang.constant.ConstantDesc; 021import java.lang.constant.DynamicConstantDesc; 022import java.lang.constant.MethodHandleDesc; 023import java.lang.constant.MethodTypeDesc; 024 025import java.util.Optional; 026 027import static java.lang.constant.ConstantDescs.BSM_INVOKE; 028import static java.lang.constant.ConstantDescs.CD_Object; 029import static java.lang.constant.ConstantDescs.DEFAULT_NAME; 030import static java.lang.constant.ConstantDescs.NULL; 031 032import static java.lang.constant.DirectMethodHandleDesc.Kind.INTERFACE_STATIC; 033 034import static org.microbean.qualifier.ConstantDescs.CD_Qualified; 035import static org.microbean.qualifier.ConstantDescs.CD_Qualifiers; 036 037/** 038 * A {@link Constable} pairing of a {@link Qualifiers} and an {@link 039 * Object} that is qualified by them. 040 * 041 * @param <V> the type borne by the values of the {@link Qualifiers} 042 * in this {@link Qualified} 043 * 044 * @param <T> the type of the object that is qualified; note that if it 045 * does not extend {@link Constable} then a {@link Qualified} bearing 046 * it will return an {@linkplain Optional#empty() empty 047 * <code>Optional</code>} from its {@link #describeConstable()} method 048 * 049 * @author <a href="https://about.me/lairdnelson" 050 * target="_parent">Laird Nelson</a> 051 * 052 * @see Qualifiers 053 * 054 * @see Constable 055 */ 056public interface Qualified<V, T> extends Constable { 057 058 059 /* 060 * Instance methods. 061 */ 062 063 064 /** 065 * Returns this {@link Qualified}'s {@link Qualifiers}. 066 * 067 * @return this {@link Qualified}'s {@link Qualifiers} 068 * 069 * @nullability Implementations of this method must not return 070 * {@code null}. 071 * 072 * @idempotency Implementations of this method must be idempotent 073 * and deterministic. 074 * 075 * @threadsafety Implementations of this method must be safe for 076 * concurrent use by multiple threads. 077 */ 078 public Qualifiers<V> qualifiers(); 079 080 /** 081 * Returns this {@link Qualified}'s qualified object. 082 * 083 * @return this {@link Qualified}'s qualified object, which may be 084 * {@code null} 085 * 086 * @nullability Implementations of this method may return {@code null}. 087 * 088 * @idempotency Implementations of this method must be idempotent 089 * and deterministic. 090 * 091 * @threadsafety Implementations of this method must be safe for 092 * concurrent use by multiple threads. 093 */ 094 public T qualified(); 095 096 /** 097 * Returns an {@link Optional} housing a {@link ConstantDesc} 098 * describing this {@link Qualified}, if this {@link Qualified} is 099 * capable of being represented as a <a 100 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic 101 * constant</a>, or an {@linkplain Optional#isEmpty() empty} {@link 102 * Optional} if not. 103 * 104 * @return an {@link Optional} housing a {@link ConstantDesc} 105 * describing this {@link Qualified}, if this {@link Qualified} is 106 * capable of being represented as a <a 107 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic 108 * constant</a>, or an {@linkplain Optional#isEmpty() empty} {@link 109 * Optional} if not 110 * 111 * @nullability This method does not, and its overrides must not, 112 * return {@code null}. 113 * 114 * @idempotency This method is, and its overrides must be, 115 * idempotent and deterministic. 116 * 117 * @threadsafety This method is, and its overrides must be, safe for 118 * concurrent use by multiple threads. 119 * 120 * @see <a 121 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">Dynamically-computed 122 * constants</a> 123 */ 124 @Override // Constable 125 public default Optional<? extends ConstantDesc> describeConstable() { 126 final T qualified = this.qualified(); 127 final ConstantDesc qualifiedCd; 128 if (qualified == null) { 129 qualifiedCd = NULL; 130 } else if (qualified instanceof Constable c) { 131 qualifiedCd = c.describeConstable().orElse(null); 132 } else if (qualified instanceof ConstantDesc cd) { 133 qualifiedCd = cd; 134 } else { 135 return Optional.empty(); 136 } 137 final ConstantDesc qualifiersCd = this.qualifiers().describeConstable().orElse(null); 138 if (qualifiersCd == null) { 139 return Optional.empty(); 140 } 141 // Call Qualified.of(qualifiers, qualified) to rehydrate. 142 return 143 Optional.of(DynamicConstantDesc.ofNamed(BSM_INVOKE, 144 DEFAULT_NAME, 145 CD_Qualified, 146 MethodHandleDesc.ofMethod(INTERFACE_STATIC, 147 CD_Qualified, 148 "of", 149 MethodTypeDesc.of(CD_Qualified, 150 CD_Qualifiers, 151 CD_Object)), 152 qualifiersCd, 153 qualifiedCd)); 154 } 155 156 157 /* 158 * Static methods. 159 */ 160 161 162 /** 163 * Returns a {@link Qualified}, which may or may not be newly 164 * created, representing the supplied qualified object. 165 * 166 * @param <V> the type of the {@link Qualified}'s {@link 167 * Qualifiers}' {@linkplain Binding#value() value} and {@linkplain 168 * Binding#attributes() attribute values} 169 * 170 * @param <T> the type of the qualified object 171 * 172 * @param qualified the qualified object; may be {@code null} 173 * 174 * @return a {@link Qualified} 175 * 176 * @nullability This method never returns {@code null}. 177 * 178 * @idempotency This method is neither idempotent nor deterministic. 179 * 180 * @threadsafety This method is safe for concurrent use by multiple 181 * threads. 182 * 183 * @see #of(Qualifiers, Object) 184 */ 185 public static <V, T> Qualified<V, T> of(final T qualified) { 186 return Record.of(qualified); 187 } 188 189 /** 190 * Returns a {@link Qualified}, which may or may not be newly 191 * created, representing the supplied {@link Qualifiers} and 192 * qualified object. 193 * 194 * @param <V> the type of the {@link Qualified}'s {@link 195 * Qualifiers}' {@linkplain Binding#value() value} and {@linkplain 196 * Binding#attributes() attribute values} 197 * 198 * @param <T> the type of the qualified object 199 * 200 * @param qualifiers the {@link Qualifiers}; may be {@code null} 201 * 202 * @param qualified the qualified object; may be {@code null} 203 * 204 * @return a {@link Qualified} 205 * 206 * @nullability This method never returns {@code null}. 207 * 208 * @idempotency This method is neither idempotent nor deterministic. 209 * 210 * @threadsafety This method is safe for concurrent use by multiple 211 * threads. 212 */ 213 // Called by #describeConstable(). 214 public static <V, T> Qualified<V, T> of(final Qualifiers<V> qualifiers, final T qualified) { 215 return Record.of(qualifiers, qualified); 216 } 217 218 219 /* 220 * Inner and nested classes. 221 */ 222 223 224 /** 225 * A {@link Qualified} {@link java.lang.Record}. 226 * 227 * @param <V> the type borne by the values of the {@link Qualifiers} 228 * in this {@link Qualified.Record} 229 * 230 * @param <T> the type of the object that is qualified; note that if 231 * it does not extend {@link Constable} then a {@link Record} 232 * bearing it will return an {@linkplain Optional#empty() empty 233 * <code>Optional</code>} from its {@link #describeConstable()} 234 * method 235 * 236 * @param qualifiers the {@link Qualifiers}; may be {@code null} in 237 * which case the return value of {@link Qualifiers#of()} will be 238 * used instead 239 * 240 * @param qualified the object being qualified; may be {@code null} 241 * 242 * @author <a href="https://about.me/lairdnelson" 243 * target="_parent">Laird Nelson</a> 244 */ 245 public static final record Record<V, T>(Qualifiers<V> qualifiers, T qualified) 246 implements Qualified<V, T>, Constable { 247 248 249 /* 250 * Constructors. 251 */ 252 253 254 /** 255 * Creates a new {@link Record}. 256 * 257 * @param qualified the object being qualified; may be {@code 258 * null} 259 * 260 * @see #Record(Qualifiers, Object) 261 */ 262 public Record(final T qualified) { 263 this(Qualifiers.of(), qualified); 264 } 265 266 /** 267 * Creates a new {@link Record}. 268 * 269 * @param qualifiers the {@link Qualifiers}; may be {@code null} 270 * in which case the return value of {@link Qualifiers#of()} will 271 * be used instead 272 * 273 * @param qualified the object being qualified; may be {@code 274 * null} 275 */ 276 public Record { 277 if (qualifiers == null) { 278 qualifiers = Qualifiers.of(); 279 } 280 } 281 282 283 /* 284 * Instance methods. 285 */ 286 287 288 /** 289 * Returns an {@link Optional} housing a {@link ConstantDesc} 290 * describing this {@link Record}, if this {@link Record} is 291 * capable of being represented as a <a 292 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic 293 * constant</a>, or an {@linkplain Optional#isEmpty() empty} 294 * {@link Optional} if not. 295 * 296 * @return an {@link Optional} housing a {@link ConstantDesc} 297 * describing this {@link Record}, if this {@link Record} is 298 * capable of being represented as a <a 299 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">dynamic 300 * constant</a>, or an {@linkplain Optional#isEmpty() empty} 301 * {@link Optional} if not 302 * 303 * @nullability This method does not, and its overrides must not, 304 * return {@code null}. 305 * 306 * @idempotency This method is, and its overrides must be, 307 * idempotent and deterministic. 308 * 309 * @threadsafety This method is, and its overrides must be, safe 310 * for concurrent use by multiple threads. 311 * 312 * @see <a 313 * href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/invoke/package-summary.html#condycon">Dynamically-computed 314 * constants</a> 315 */ 316 @Override // Constable 317 public final Optional<? extends ConstantDesc> describeConstable() { 318 return Qualified.super.describeConstable(); 319 } 320 321 322 /* 323 * Static methods. 324 */ 325 326 327 /** 328 * Returns a {@link Record}, which may or may not be newly 329 * created, representing the qualified object. 330 * 331 * @param <V> the type of the {@link Record}'s {@link Qualifiers}' 332 * {@linkplain Binding#value() value} and {@linkplain 333 * Binding#attributes() attribute values} 334 * 335 * @param <T> the type of the qualified object 336 * 337 * @param qualified the qualified object; may be {@code null} 338 * 339 * @return a {@link Record} 340 * 341 * @nullability This method never returns {@code null}. 342 * 343 * @idempotency This method is neither idempotent nor deterministic. 344 * 345 * @threadsafety This method is safe for concurrent use by multiple 346 * threads. 347 * 348 * @see #of(Qualifiers, Object) 349 */ 350 public static final <V, T> Record<V, T> of(final T qualified) { 351 return of(Qualifiers.<V>of(), qualified); 352 } 353 354 /** 355 * Returns a {@link Record}, which may or may not be newly 356 * created, representing the supplied {@link Qualifiers} and 357 * qualified object. 358 * 359 * @param <V> the type of the {@link Record}'s {@link Qualifiers}' 360 * {@linkplain Binding#value() value} and {@linkplain 361 * Binding#attributes() attribute values} 362 * 363 * @param <T> the type of the qualified object 364 * 365 * @param qualifiers the {@link Qualifiers}; may be {@code null} 366 * 367 * @param qualified the qualified object; may be {@code null} 368 * 369 * @return a {@link Record} 370 * 371 * @nullability This method never returns {@code null}. 372 * 373 * @idempotency This method is neither idempotent nor deterministic. 374 * 375 * @threadsafety This method is safe for concurrent use by multiple 376 * threads. 377 */ 378 public static final <V, T> Record<V, T> of(final Qualifiers<V> qualifiers, final T qualified) { 379 return new Record<>(qualifiers, qualified); 380 } 381 382 383 } 384 385}