1 /*
2 * SPDX-FileCopyrightText: none
3 * SPDX-License-Identifier: CC0-1.0
4 */
5
6 package gov.nist.secauto.metaschema.core.metapath.function;
7
8 import gov.nist.secauto.metaschema.core.metapath.item.IItem;
9 import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
10 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
11 import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem;
12 import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException;
13 import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException;
14 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
15
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.stream.Collectors;
22 import java.util.stream.Stream;
23
24 import edu.umd.cs.findbugs.annotations.NonNull;
25 import edu.umd.cs.findbugs.annotations.Nullable;
26
27 /**
28 * A collection of utility functions for use in implementing Metapath functions.
29 * <p>
30 * This class is thread-safe as all methods are stateless and the internal
31 * constant is immutable.
32 */
33 // FIXME: Remove these methods in favor of direct calls to methods on the item
34 // types
35 public final class FunctionUtils {
36 private FunctionUtils() {
37 // disable
38 }
39
40 /**
41 * Gets the first item of the provided sequence as a {@link INumericItem} value.
42 * If the sequence is empty, then a {@code null} value is returned.
43 *
44 * @param sequence
45 * a Metapath sequence containing the value to convert
46 * @param requireSingleton
47 * if {@code true} then a {@link TypeMetapathException} is thrown if
48 * the sequence contains more than one item
49 * @return the numeric item value, or {@code null} if the result is an empty
50 * sequence
51 * @throws TypeMetapathException
52 * if the sequence contains more than one item, or the item cannot be
53 * cast to a numeric value
54 *
55 */
56 @Deprecated(since = "3.0.0", forRemoval = true)
57 @Nullable
58 public static INumericItem toNumeric(@NonNull ISequence<?> sequence, boolean requireSingleton) {
59 IItem item = sequence.getFirstItem(requireSingleton);
60 return item == null ? null : toNumeric(item);
61 }
62
63 /**
64 * Gets the provided item value as a {@link INumericItem} value.
65 *
66 * @param item
67 * the value to convert
68 * @return the numeric item value
69 * @throws TypeMetapathException
70 * if the sequence contains more than one item, or the item cannot be
71 * cast to a numeric value
72 */
73 @Deprecated(since = "3.0.0", forRemoval = true)
74 @NonNull
75 public static INumericItem toNumeric(@NonNull IItem item) {
76 // atomize
77 IAnyAtomicItem atomicItem = ISequence.getFirstItem(item.atomize(), true);
78 if (atomicItem == null) {
79 throw new InvalidTypeMetapathException(item, "Unable to cast null item");
80 }
81 return toNumeric(atomicItem);
82 }
83
84 /**
85 * Gets the provided item value as a {@link INumericItem} value.
86 *
87 * @param item
88 * the value to convert
89 * @return the numeric item value
90 * @throws TypeMetapathException
91 * if the item cannot be cast to a numeric value
92 */
93 @Deprecated(since = "3.0.0", forRemoval = true)
94 @NonNull
95 public static INumericItem toNumeric(@NonNull IAnyAtomicItem item) {
96 return castToNumeric(item);
97 }
98
99 /**
100 * Gets the provided item value as a {@link INumericItem} value. If the item is
101 * {@code null}, then a {@code null} value is returned.
102 *
103 * @param item
104 * the value to convert
105 * @return the numeric item value
106 * @throws TypeMetapathException
107 * if the item cannot be cast to a numeric value
108 * @deprecated Use {@link #castToNumeric(IAnyAtomicItem)} with null checking
109 * instead
110 */
111 @Deprecated(since = "3.0.0", forRemoval = true)
112 @Nullable
113 public static INumericItem toNumericOrNull(@Nullable IAnyAtomicItem item) {
114 return item == null ? null : castToNumeric(item);
115 }
116
117 /**
118 * Casts the provided item value to a {@link INumericItem} value.
119 * <p>
120 * This method wraps {@link INumericItem#cast(IAnyAtomicItem)} and converts
121 * {@link InvalidValueForCastFunctionException} to
122 * {@link InvalidTypeMetapathException} for consistent exception handling.
123 *
124 * @param item
125 * the value to cast
126 * @return the numeric item value
127 * @throws InvalidTypeMetapathException
128 * if the item cannot be cast to a numeric value
129 */
130 @NonNull
131 public static INumericItem castToNumeric(@NonNull IAnyAtomicItem item) {
132 try {
133 return INumericItem.cast(item);
134 } catch (InvalidValueForCastFunctionException ex) {
135 throw new InvalidTypeMetapathException(item, ex.getLocalizedMessage(), ex);
136 }
137 }
138
139 /**
140 * Casts the provided {@code item} as the result type, if the item is not
141 * {@code null}.
142 *
143 * @param <TYPE>
144 * the Java type to cast to
145 * @param item
146 * the value to cast
147 * @return the item cast to the required type or {@code null} if the item is
148 * {@code null}
149 * @throws ClassCastException
150 * if the item's type is not compatible with the requested type
151 */
152 @SuppressWarnings("unchecked")
153 @Nullable
154 public static <TYPE extends IItem> TYPE asTypeOrNull(@Nullable IItem item) {
155 return (TYPE) item;
156 }
157
158 /**
159 * Casts the provided {@code item} as the result type.
160 *
161 * @param <TYPE>
162 * the Java type to cast to
163 * @param item
164 * the value to cast
165 * @return the item cast to the required type
166 * @throws ClassCastException
167 * if the item's type is not compatible with the requested type
168 */
169 @SuppressWarnings("unchecked")
170 @NonNull
171 public static <TYPE extends IItem> TYPE asType(@NonNull IItem item) {
172 return (TYPE) item;
173 }
174
175 /**
176 * Casts the provided {@code item} as the result sequence type.
177 *
178 * @param <TYPE>
179 * the Java type to cast to
180 * @param sequence
181 * the values to cast
182 * @return the sequence cast to the required type
183 * @throws ClassCastException
184 * if the sequence's type is not compatible with the requested type
185 */
186 @SuppressWarnings("unchecked")
187 @NonNull
188 public static <TYPE extends IItem> ISequence<TYPE> asType(@NonNull ISequence<?> sequence) {
189 return (ISequence<TYPE>) sequence;
190 }
191
192 /**
193 * Casts the provided {@code item} as the result type.
194 *
195 * @param <TYPE>
196 * the Java type to cast to
197 * @param clazz
198 * the Java class instance for the requested type
199 * @param item
200 * the value to cast
201 * @return the item cast to the required type
202 * @throws InvalidTypeMetapathException
203 * if the provided item is {@code null} or if the item's type is not
204 * assignment compatible to the requested type
205 */
206 @Deprecated(since = "3.0.0", forRemoval = true)
207 @NonNull
208 public static <TYPE extends IItem> TYPE requireType(Class<TYPE> clazz, IItem item) {
209 if (item == null) {
210 throw new InvalidTypeMetapathException(
211 null,
212 String.format("Expected non-null type '%s', but the node was null.",
213 clazz.getName()));
214 }
215 if (!clazz.isInstance(item)) {
216 throw new InvalidTypeMetapathException(
217 item,
218 String.format("Expected type '%s', but the node was type '%s'.",
219 clazz.getName(),
220 item.getClass().getName()));
221 }
222 return asType(item);
223 }
224
225 /**
226 * Casts the provided {@code item} as the result type, if the item is not
227 * {@code null}.
228 *
229 * @param <TYPE>
230 * the Java type to cast to
231 * @param clazz
232 * the Java class instance for the requested type
233 * @param item
234 * the value to cast
235 * @return the item cast to the required type or {@code null} if the item is
236 * {@code null}
237 * @throws InvalidTypeMetapathException
238 * if the provided item is {@code null} or if the item's type is not
239 * assignment compatible to the requested type
240 */
241 @Deprecated(since = "3.0.0", forRemoval = true)
242 @Nullable
243 public static <TYPE extends IItem> TYPE requireTypeOrNull(Class<TYPE> clazz, @Nullable IItem item) {
244 if (item == null || clazz.isInstance(item)) {
245 return asTypeOrNull(item);
246 }
247 throw new InvalidTypeMetapathException(
248 item,
249 String.format("Expected type '%s', but the node was type '%s'.",
250 clazz.getName(),
251 item.getClass().getName()));
252 }
253
254 /**
255 * Get a stream of item data types for the stream of items.
256 *
257 * @param items
258 * the Metapath items to get the data types for
259 * @return a stream of data type classes
260 */
261 @Deprecated(since = "3.0.0", forRemoval = true)
262 @NonNull
263 public static Stream<Class<?>> getTypes(@NonNull Stream<? extends IItem> items) {
264 return ObjectUtils.notNull(items.map(Object::getClass));
265 }
266
267 /**
268 * Generate a list of Metapath item Java type classes from a list of Metapath
269 * items.
270 *
271 * @param <T>
272 * the base Java types of the items
273 * @param items
274 * the items to get Java type class for
275 * @return a list of corresponding Java type classes for the provided items
276 */
277 @Deprecated(since = "3.0.0", forRemoval = true)
278 @SuppressWarnings("unchecked")
279 @NonNull
280 public static <T extends IItem> List<Class<? extends T>> getTypes(@NonNull List<T> items) {
281 return ObjectUtils.notNull(items.stream()
282 .map(item -> (Class<? extends T>) item.getClass())
283 .collect(Collectors.toList()));
284 }
285
286 /**
287 * Count the occurrences of the provided data type item {@code classes} used in
288 * the set of provided {@code items}.
289 *
290 * @param <T>
291 * the class type
292 * @param classes
293 * the Metapath item classes to count
294 * @param items
295 * the Metapath items to analyze
296 * @return a mapping of Metapath item class to count
297 */
298 @Deprecated(since = "3.0.0", forRemoval = true)
299 @NonNull
300 public static <T extends IItem> Map<Class<? extends T>, Integer> countTypes(
301 @NonNull Set<Class<? extends T>> classes,
302 @NonNull Collection<? extends T> items) {
303 Map<Class<? extends T>, Integer> retval = new HashMap<>(); // NOPMD
304 for (T item : items) {
305 Class<?> itemClass = item.getClass();
306 for (Class<? extends T> clazz : classes) {
307 if (clazz.isAssignableFrom(itemClass)) {
308 retval.compute(clazz, (cl, current) -> current == null ? 1 : current + 1);
309 }
310 }
311 }
312 return retval;
313 }
314 }