001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2025 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
012 * specific language governing permissions and limitations under the License.
013 */
014package org.microbean.assign;
015
016import java.util.ArrayList;
017import java.util.Collection;
018import java.util.Collections;
019import java.util.List;
020
021import org.microbean.attributes.Attributes;
022
023/**
024 * A utility class for working with commonly-used <dfn>qualifiers</dfn>.
025 *
026 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
027 *
028 * @see Attributes
029 */
030public final class Qualifiers {
031
032  private static final Attributes QUALIFIER = Attributes.of("Qualifier");
033
034  private static final List<Attributes> QUALIFIERS = List.of(QUALIFIER);
035
036  private static final Attributes ANY_QUALIFIER = Attributes.of("Any", QUALIFIERS);
037
038  private static final List<Attributes> ANY_QUALIFIERS = List.of(ANY_QUALIFIER);
039
040  private static final Attributes DEFAULT_QUALIFIER = Attributes.of("Default", QUALIFIERS);
041
042  private static final List<Attributes> DEFAULT_QUALIFIERS = List.of(DEFAULT_QUALIFIER);
043
044  private static final List<Attributes> ANY_AND_DEFAULT_QUALIFIERS = List.of(ANY_QUALIFIER, DEFAULT_QUALIFIER);
045
046  private static final Attributes PRIMORDIAL_QUALIFIER = Attributes.of("Primordial", QUALIFIERS);
047
048  private static final List<Attributes> PRIMORDIAL_QUALIFIERS = List.of(PRIMORDIAL_QUALIFIER);
049
050  private Qualifiers() {
051    super();
052  }
053
054  /**
055   * Returns an unmodifiable {@link List} consisting solely of the unattributed <dfn>any qualifier</dfn> and the
056   * <dfn>default qualifier</dfn>.
057   *
058   * @return an unmodifiable {@link List} consisting solely of the unattributed any qualifier and the default qualifier;
059   * never {@code null}
060   *
061   * @see #anyQualifier()
062   *
063   * @see #defaultQualifier()
064   */
065  public static final List<Attributes> anyAndDefaultQualifiers() {
066    return ANY_AND_DEFAULT_QUALIFIERS;
067  }
068
069  /**
070   * Returns the unattributed <dfn>any qualifier</dfn>.
071   *
072   * @return the <dfn>any qualifier</dfn>; never {@code null}
073   *
074   * @see #anyQualifiers()
075   */
076  public static final Attributes anyQualifier() {
077    return ANY_QUALIFIER;
078  }
079
080  /**
081   * Returns {@code true} if and only if the supplied {@link Attributes} {@linkplain Attributes#equals(Object) is equal
082   * to} the unattributed {@linkplain #anyQualifier() any qualifier}.
083   *
084   * @param a an {@link Attributes}; must not be {@code null}
085   *
086   * @return {@code true} if and only if the supplied {@link Attributes} {@linkplain Attributes#equals(Object) is equal
087   * to} the unattributed {@linkplain #anyQualifier() any qualifier}
088   *
089   * @exception NullPointerException if {@code a} is {@code null}
090   */
091  public static final boolean anyQualifier(final Attributes a) {
092    return ANY_QUALIFIER == a || anyQualifier().equals(a) && qualifier(a);
093  }
094
095  /**
096   * Returns an immutable {@link List} consisting solely of the unattributed <dfn>any qualifier</dfn>.
097   *
098   * @return an immutable {@link List}; never {@code null}
099   *
100   * @see #anyQualifier()
101   */
102  public static final List<Attributes> anyQualifiers() {
103    return ANY_QUALIFIERS;
104  }
105
106  /**
107   * Returns the <dfn>default qualifier</dfn>.
108   *
109   * @return the <dfn>default qualifier</dfn>; never {@code null}
110   *
111   * @see #defaultQualifiers()
112   */
113  public static final Attributes defaultQualifier() {
114    return DEFAULT_QUALIFIER;
115  }
116
117  /**
118   * Returns {@code true} if and only if the supplied {@link Attributes} {@linkplain
119   * Attributes#equals(Object) is equal to} the {@linkplain #defaultQualifier() default qualifier}.
120   *
121   * @param a an {@link Attributes}; must not be {@code null}
122   *
123   * @return {@code true} if and only if the supplied {@link Attributes} {@linkplain
124   * Attributes#equals(Object) is equal to} the {@linkplain #defaultQualifier() default qualifier}
125   *
126   * @exception NullPointerException if {@code a} is {@code null}
127   */
128  public static final boolean defaultQualifier(final Attributes a) {
129    return DEFAULT_QUALIFIER == a || defaultQualifier().equals(a) && qualifier(a);
130  }
131
132  /**
133   * Returns an immutable {@link List} consisting solely of the <dfn>default qualifier</dfn>.
134   *
135   * @return an immutable {@link List}; never {@code null}
136   *
137   * @see #defaultQualifier()
138   */
139  public static final List<Attributes> defaultQualifiers() {
140    return DEFAULT_QUALIFIERS;
141  }
142
143  /**
144   * Returns an {@link Attributes} that is {@linkplain Attributes#equals(Object) equal to} the supplied {@link
145   * Attributes}.
146   *
147   * <p>The returned {@link Attributes} may be the supplied {@link Attributes} or a different instance.</p>
148   *
149   * @param a an {@link Attributes}; must not be {@code null}
150   *
151   * @return an {@link Attributes} that is {@linkplain Attributes#equals(Object) equal to} the supplied {@link
152   * Attributes}; never {@code null}
153   *
154   * @exception NullPointerException if {@code a} is {@code null}
155   */
156  public static final Attributes normalize(final Attributes a) {
157    return switch (a) {
158    case null -> throw new NullPointerException("a");
159    case Attributes q when defaultQualifier(q) -> defaultQualifier();
160    case Attributes q when QUALIFIER.equals(q) -> qualifier();
161    default -> a;
162    };
163  }
164
165  /**
166   * Returns an immutable {@link List} of {@link Attributes}s that is {@linkplain List#equals(Object) equal to} the
167   * supplied {@link List}.
168   *
169   * <p>The returned {@link List} may be the supplied {@link List} or a different instance.</p>
170   *
171   * @param list a {@link List} of {@link Attributes}s; must not be {@code null}
172   *
173   * @return an immutable {@link List} of {@link Attributes}s that is {@linkplain List#equals(Object) equal to} the
174   * supplied {@link List}; never {@code null}
175   *
176   * @exception NullPointerException if {@code list} is {@code null}
177   */
178  public static final List<Attributes> normalize(final List<Attributes> list) {
179    return switch (list.size()) {
180    case 0 -> List.of();
181    case 1 -> list.equals(defaultQualifiers()) ? defaultQualifiers() : List.copyOf(list);
182    default -> {
183      final List<Attributes> l = new ArrayList<>(list.size());
184      for (final Attributes a : list) {
185        l.add(normalize(a));
186      }
187      yield Collections.unmodifiableList(l);
188    }
189    };
190  }
191
192  /**
193   * Returns the <dfn>primordial qualifier</dfn>.
194   *
195   * @return the <dfn>primordial qualifier</dfn>; never {@code null}
196   *
197   * @see #primordialQualifiers()
198   */
199  public static final Attributes primordialQualifier() {
200    return PRIMORDIAL_QUALIFIER;
201  }
202
203  /**
204   * Returns {@code true} if and only if the supplied {@link Attributes} {@linkplain
205   * Attributes#equals(Object) is equal to} the {@linkplain #primordialQualifier() primordial qualifier}.
206   *
207   * @param a an {@link Attributes}; must not be {@code null}
208   *
209   * @return {@code true} if and only if the supplied {@link Attributes} {@linkplain
210   * Attributes#equals(Object) is equal to} the {@linkplain #primordialQualifier() primordial qualifier}
211   *
212   * @exception NullPointerException if {@code a} is {@code null}
213   */
214  public static final boolean primordialQualifier(final Attributes a) {
215    return PRIMORDIAL_QUALIFIER == a || primordialQualifier().equals(a) && qualifier(a);
216  }
217
218  /**
219   * Returns an immutable {@link List} consisting solely of the <dfn>primordial qualifier</dfn>.
220   *
221   * @return an immutable {@link List}; never {@code null}
222   *
223   * @see #primordialQualifier()
224   */
225  public static final List<Attributes> primordialQualifiers() {
226    return PRIMORDIAL_QUALIFIERS;
227  }
228
229  /**
230   * Returns the <dfn>qualifier</dfn> (meta-) qualifier.
231   *
232   * @return the <dfn>qualifier</dfn> (meta-) qualifier; never {@code null}
233   */
234  public static final Attributes qualifier() {
235    return QUALIFIER;
236  }
237
238  /**
239   * Returns {@code true} if and only if the supplied {@link Attributes} is an {@link Attributes} that can be used to
240   * designate other {@link Attributes} as qualifiers.
241   *
242   * @param q an {@link Attributes}; must not be {@code null}
243   *
244   * @return {@code true} if and only if the supplied {@link Attributes} is an {@link Attributes} that can be used to
245   * designate other {@link Attributes} as qualifiers
246   *
247   * @exception NullPointerException if {@code q} is {@code null}
248   */
249  public static final boolean qualifier(final Attributes q) {
250    return q.attributes().contains(qualifier());
251  }
252
253  /**
254   * Returns an immutable {@link List} consisting solely of the <dfn>qualifier</dfn> (meta-) qualifier.
255   *
256   * @return an immutable {@link List}; never {@code null}
257   *
258   * @see #qualifier()
259   */
260  public static final List<Attributes> qualifiers() {
261    return QUALIFIERS;
262  }
263
264  /**
265   * Returns an unmodifiable {@link List} consisting only of those {@link Attributes} in the supplied {@link
266   * Collection} that {@linkplain #qualifier(Attributes) are qualifiers}.
267   *
268   * @param c a {@link Collection} of {@link Attributes}s; must not be {@code null}
269   *
270   * @return an unmodifiable {@link List} consisting only of those {@link Attributes}s in the supplied {@link
271   * Collection} that {@linkplain #qualifier(Attributes) are qualifiers}; never {@code null}
272   *
273   * @exception NullPointerException if {@code c} is {@code null}
274   */
275  public static final List<Attributes> qualifiers(final Collection<? extends Attributes> c) {
276    return switch (c) {
277    case Collection<?> c0 when c0.isEmpty() -> List.of();
278    case Collection<?> c0 when c0.equals(defaultQualifiers()) -> defaultQualifiers();
279    case Collection<?> c0 when c0.equals(anyAndDefaultQualifiers()) -> anyAndDefaultQualifiers();
280    default ->{
281      final ArrayList<Attributes> list = new ArrayList<>(c.size());
282      for (final Attributes a : c) {
283        if (qualifier(a)) {
284          list.add(normalize(a));
285        }
286      }
287      list.trimToSize();
288      yield Collections.unmodifiableList(list);
289    }
290    };
291  }
292
293}