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