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