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}