Skip to content

Commit 9e7ae7a

Browse files
author
David Waltermire
committed
Refactored the result type used in MetapathExpression to avoid the need for a case statement. This should result in cleaner code and better performance overall.
1 parent a5fc87c commit 9e7ae7a

File tree

1 file changed

+69
-68
lines changed

1 file changed

+69
-68
lines changed

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java

Lines changed: 69 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import gov.nist.secauto.metaschema.core.metapath.function.library.FnData;
1919
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
2020
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
21-
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDecimalItem;
2221
import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem;
2322
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
2423

@@ -47,40 +46,91 @@
4746
"PMD.CouplingBetweenObjects" // necessary since this class aggregates functionality
4847
})
4948
public 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

Comments
 (0)