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}