1818import gov .nist .secauto .metaschema .core .metapath .function .library .FnData ;
1919import gov .nist .secauto .metaschema .core .metapath .item .IItem ;
2020import gov .nist .secauto .metaschema .core .metapath .item .atomic .IAnyAtomicItem ;
21- import gov .nist .secauto .metaschema .core .metapath .item .atomic .IDecimalItem ;
2221import gov .nist .secauto .metaschema .core .metapath .item .atomic .INumericItem ;
2322import gov .nist .secauto .metaschema .core .util .ObjectUtils ;
2423
4746 "PMD.CouplingBetweenObjects" // necessary since this class aggregates functionality
4847})
4948public class MetapathExpression {
49+
5050 /**
5151 * Identifies the expected type for a Metapath evaluation result.
5252 */
5353 public enum ResultType {
5454 /**
5555 * The result is expected to be a {@link BigDecimal} value.
5656 */
57- NUMBER ,
57+ NUMBER (BigDecimal .class , sequence -> {
58+ INumericItem numeric = FunctionUtils .toNumeric (sequence , true );
59+ return numeric == null ? null : numeric .asDecimal ();
60+ }),
5861 /**
5962 * The result is expected to be a {@link String} value.
6063 */
61- STRING ,
64+ STRING (String .class , sequence -> {
65+ IAnyAtomicItem item = FnData .fnData (sequence ).getFirstItem (true );
66+ return item == null ? "" : item .asString ();
67+ }),
6268 /**
6369 * The result is expected to be a {@link Boolean} value.
6470 */
65- BOOLEAN ,
71+ BOOLEAN ( Boolean . class , sequence -> FnBoolean . fnBoolean ( sequence ). toBoolean ()) ,
6672 /**
6773 * The result is expected to be an {@link ISequence} value.
6874 */
69- SEQUENCE ,
75+ SEQUENCE ( ISequence . class , sequence -> sequence ) ,
7076 /**
7177 * The result is expected to be an {@link IItem} value.
7278 */
73- ITEM ;
74- }
79+ ITEM (IItem .class , sequence -> sequence .getFirstItem (true ));
7580
76- private static final Logger LOGGER = LogManager .getLogger (MetapathExpression .class );
81+ @ NonNull
82+ private final Class <?> clazz ;
83+ private final ConversionFunction converter ;
84+
85+ ResultType (@ NonNull Class <?> clazz , @ NonNull ConversionFunction converter ) {
86+ this .clazz = clazz ;
87+ this .converter = converter ;
88+ }
89+
90+ /**
91+ * Get the expected class for the result type.
92+ *
93+ * @return the expected class
94+ *
95+ */
96+ @ NonNull
97+ public Class <?> expectedClass () {
98+ return clazz ;
99+ }
100+
101+ /**
102+ * Convert the provided sequence to the expected type.
103+ *
104+ * @param <T>
105+ * the Java type of the expected return value
106+ * @param sequence
107+ * the Metapath result sequence to convert
108+ * @return the converted sequence as the expected type
109+ * @throws TypeMetapathException
110+ * if the provided sequence is incompatible with the expected result
111+ * type
112+ */
113+ @ Nullable
114+ public <T > T convert (@ NonNull ISequence <?> sequence ) {
115+ try {
116+ return ObjectUtils .asNullableType (converter .convert (sequence ));
117+ } catch (ClassCastException ex ) {
118+ throw new InvalidTypeMetapathException (null ,
119+ String .format ("Unable to cast to expected result type '%s' using expected type '%s'." ,
120+ name (),
121+ expectedClass ().getName ()),
122+ ex );
123+ }
124+ }
125+ }
77126
78127 /**
79128 * The Metapath expression identifying the current context node.
80129 */
81130 @ NonNull
82131 public static final MetapathExpression CONTEXT_NODE
83132 = new MetapathExpression ("." , ContextItem .instance (), StaticContext .instance ());
133+ private static final Logger LOGGER = LogManager .getLogger (MetapathExpression .class );
84134
85135 @ NonNull
86136 private final String path ;
@@ -236,7 +286,7 @@ public String toString() {
236286 * type
237287 * @throws MetapathException
238288 * if an error occurred during evaluation
239- * @see #toResultType (ISequence, ResultType )
289+ * @see ResultType#convert (ISequence)
240290 */
241291 @ Nullable
242292 public <T > T evaluateAs (@ NonNull ResultType resultType ) {
@@ -260,14 +310,14 @@ public <T> T evaluateAs(@NonNull ResultType resultType) {
260310 * type
261311 * @throws MetapathException
262312 * if an error occurred during evaluation
263- * @see #toResultType (ISequence, ResultType )
313+ * @see ResultType#convert (ISequence)
264314 */
265315 @ Nullable
266316 public <T > T evaluateAs (
267317 @ Nullable IItem focus ,
268318 @ NonNull ResultType resultType ) {
269319 ISequence <?> result = evaluate (focus );
270- return toResultType (result , resultType );
320+ return resultType . convert (result );
271321 }
272322
273323 /**
@@ -291,70 +341,15 @@ public <T> T evaluateAs(
291341 * type
292342 * @throws MetapathException
293343 * if an error occurred during evaluation
294- * @see #toResultType (ISequence, ResultType )
344+ * @see ResultType#convert (ISequence)
295345 */
296346 @ Nullable
297347 public <T > T evaluateAs (
298348 @ Nullable IItem focus ,
299349 @ NonNull ResultType resultType ,
300350 @ NonNull DynamicContext dynamicContext ) {
301351 ISequence <?> result = evaluate (focus , dynamicContext );
302- return toResultType (result , resultType );
303- }
304-
305- /**
306- * Converts the provided {@code sequence} to the requested {@code resultType}.
307- * <p>
308- * The {@code resultType} determines the returned result, which is derived from
309- * the evaluation result sequence, as follows:
310- * <ul>
311- * <li>BOOLEAN - the effective boolean result is produced using
312- * {@link FnBoolean#fnBoolean(ISequence)}.</li>
313- * <li>NODE - the first result item in the sequence is returned.</li>
314- * <li>NUMBER - the sequence is cast to a number using
315- * {@link IDecimalItem#cast(IAnyAtomicItem)}.</li>
316- * <li>SEQUENCE - the evaluation result sequence.</li>
317- * <li>STRING - the string value of the first result item in the sequence.</li>
318- * </ul>
319- *
320- * @param <T>
321- * the requested return value
322- * @param sequence
323- * the sequence to convert
324- * @param resultType
325- * the type of result to produce
326- * @return the converted result
327- * @throws TypeMetapathException
328- * if the provided sequence is incompatible with the requested result
329- * type
330- */
331- @ SuppressWarnings ({ "PMD.NullAssignment" , "PMD.CyclomaticComplexity" }) // for readability
332- @ Nullable
333- protected <T > T toResultType (@ NonNull ISequence <?> sequence , @ NonNull ResultType resultType ) {
334- Object result ;
335- switch (resultType ) {
336- case BOOLEAN :
337- result = FnBoolean .fnBoolean (sequence ).toBoolean ();
338- break ;
339- case ITEM :
340- result = sequence .getFirstItem (true );
341- break ;
342- case NUMBER :
343- INumericItem numeric = FunctionUtils .toNumeric (sequence , true );
344- result = numeric == null ? null : numeric .asDecimal ();
345- break ;
346- case SEQUENCE :
347- result = sequence ;
348- break ;
349- case STRING :
350- IAnyAtomicItem item = FnData .fnData (sequence ).getFirstItem (true );
351- result = item == null ? "" : item .asString ();
352- break ;
353- default :
354- throw new InvalidTypeMetapathException (null , String .format ("unsupported result type '%s'" , resultType .name ()));
355- }
356-
357- return ObjectUtils .asNullableType (result );
352+ return resultType .convert (result );
358353 }
359354
360355 /**
@@ -424,4 +419,10 @@ public <T extends IItem> ISequence<T> evaluate(
424419 ex );
425420 }
426421 }
422+
423+ @ FunctionalInterface
424+ interface ConversionFunction {
425+ @ Nullable
426+ Object convert (@ NonNull ISequence <?> sequence );
427+ }
427428}
0 commit comments