Skip to content

Commit

Permalink
Feature/anonymous functions (#266)
Browse files Browse the repository at this point in the history
* Hooked up the data type specific cast methods using the data type service as the source of data types. This provides for long-term scalability as new data types are added.
* Completed testing eqname and varref cases for the arrow operator. Resolves #68.
* Inline functions are now working. Resolves #242.
* Refactored Metapath expression to use an interface instead of the implementation for returned instances. Updated all uses to use IMetaschemaExpression. Changed uses of evaluateAs using the ResultType.SEQUENCE to just use evaluate. Removed ResultType.SEQUENCE.
* Applied suggestions from code review
  • Loading branch information
david-waltermire authored Dec 2, 2024
1 parent 801b310 commit dff71d3
Show file tree
Hide file tree
Showing 279 changed files with 2,023 additions and 1,331 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
Expand Down Expand Up @@ -216,6 +217,19 @@ public IItemType getItemTypeByItemClass(Class<? extends IItem> clazz) {
return itemTypeByItemClass.get(clazz);
}

/**
* Get the collection of all registered data type adapters provided by this
* service.
* <p>
* The returned collection is unmodifiable.
*
* @return the data type adapters
*/
@NonNull
public Collection<? extends IDataTypeAdapter<?>> getDataTypes() {
return ObjectUtils.notNull(atomicTypeByAdapterClass.values());
}

/**
* Lookup a specific {@link IDataTypeAdapter} by its adapter class.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
import gov.nist.secauto.metaschema.core.configuration.DefaultConfiguration;
import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration;
import gov.nist.secauto.metaschema.core.metapath.function.DefaultFunction.CallingContext;
import gov.nist.secauto.metaschema.core.metapath.function.CalledContext;
import gov.nist.secauto.metaschema.core.metapath.function.IFunction.FunctionProperty;
import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
import gov.nist.secauto.metaschema.core.model.IUriResolver;
import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
Expand Down Expand Up @@ -75,7 +76,7 @@ private static class SharedState {
@NonNull
private final Map<URI, IDocumentNodeItem> availableDocuments;
@NonNull
private final Map<CallingContext, ISequence<?>> functionResultCache;
private final Map<CalledContext, ISequence<?>> functionResultCache;
@Nullable
private CachingLoader documentLoader;
@NonNull
Expand All @@ -92,7 +93,7 @@ public SharedState(@NonNull StaticContext staticContext) {
this.functionResultCache = ObjectUtils.notNull(Caffeine.newBuilder()
.maximumSize(5000)
.expireAfterAccess(10, TimeUnit.MINUTES)
.<CallingContext, ISequence<?>>build().asMap());
.<CalledContext, ISequence<?>>build().asMap());
this.configuration = new DefaultConfiguration<>();
this.configuration.enableFeature(MetapathEvaluationFeature.METAPATH_EVALUATE_PREDICATES);
}
Expand Down Expand Up @@ -194,7 +195,7 @@ public void setDocumentLoader(@NonNull IDocumentLoader documentLoader) {
* @return the cached result sequence for the function call
*/
@Nullable
public ISequence<?> getCachedResult(@NonNull CallingContext callingContext) {
public ISequence<?> getCachedResult(@NonNull CalledContext callingContext) {
return sharedState.functionResultCache.get(callingContext);
}

Expand All @@ -208,7 +209,7 @@ public ISequence<?> getCachedResult(@NonNull CallingContext callingContext) {
* @param result
* the function call result
*/
public void cacheResult(@NonNull CallingContext callingContext, @NonNull ISequence<?> result) {
public void cacheResult(@NonNull CalledContext callingContext, @NonNull ISequence<?> result) {
ISequence<?> old = sharedState.functionResultCache.put(callingContext, result);
assert old == null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
/*
* SPDX-FileCopyrightText: none
* SPDX-License-Identifier: CC0-1.0
*/

package gov.nist.secauto.metaschema.core.metapath;

import gov.nist.secauto.metaschema.core.metapath.MetapathExpression.ConversionFunction;
import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
import gov.nist.secauto.metaschema.core.metapath.function.library.FnBoolean;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem;
import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException;
import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import java.math.BigDecimal;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

public interface IMetapathExpression {

/**
* Identifies the expected type for a Metapath evaluation result.
*/
enum ResultType {
/**
* The result is expected to be a {@link BigDecimal} value.
*/
NUMBER(BigDecimal.class, sequence -> {
INumericItem numeric = FunctionUtils.toNumeric(sequence, true);
return numeric == null ? null : numeric.asDecimal();
}),
/**
* The result is expected to be a {@link String} value.
*/
STRING(String.class, sequence -> {
IAnyAtomicItem item = ISequence.of(sequence.atomize()).getFirstItem(true);
return item == null ? "" : item.asString();
}),
/**
* The result is expected to be a {@link Boolean} value.
*/
BOOLEAN(Boolean.class, sequence -> FnBoolean.fnBoolean(sequence).toBoolean()),
/**
* The result is expected to be an {@link IItem} value.
*/
ITEM(IItem.class, sequence -> sequence.getFirstItem(true));

@NonNull
private final Class<?> clazz;
private final ConversionFunction converter;

ResultType(@NonNull Class<?> clazz, @NonNull ConversionFunction converter) {
this.clazz = clazz;
this.converter = converter;
}

/**
* Get the expected class for the result type.
*
* @return the expected class
*
*/
@NonNull
public Class<?> expectedClass() {
return clazz;
}

/**
* Convert the provided sequence to the expected type.
*
* @param <T>
* the Java type of the expected return value
* @param sequence
* the Metapath result sequence to convert
* @return the converted sequence as the expected type
* @throws TypeMetapathException
* if the provided sequence is incompatible with the expected result
* type
*/
@Nullable
public <T> T convert(@NonNull ISequence<?> sequence) {
try {
return ObjectUtils.asNullableType(converter.convert(sequence));
} catch (ClassCastException ex) {
throw new InvalidTypeMetapathException(null,
String.format("Unable to cast to expected result type '%s' using expected type '%s'.",
name(),
expectedClass().getName()),
ex);
}
}
}

/**
* Get the Metapath expression identifying the current context node.
*
* @return the context expression
*/
@NonNull
static IMetapathExpression contextNode() {
return MetapathExpression.CONTEXT_NODE;
}

/**
* Compile a Metapath expression string.
*
* @param path
* the metapath expression
* @return the compiled expression object
* @throws MetapathException
* if an error occurred while compiling the Metapath expression
*/
@NonNull
static IMetapathExpression compile(@NonNull String path) {
return MetapathExpression.compile(path, StaticContext.instance());
}

/**
* Compiles a Metapath expression string using the provided static context.
*
* @param path
* the metapath expression
* @param staticContext
* the static evaluation context
* @return the compiled expression object
* @throws MetapathException
* if an error occurred while compiling the Metapath expression
*/
@NonNull
static IMetapathExpression compile(@NonNull String path, @NonNull StaticContext staticContext) {
return MetapathExpression.compile(path, staticContext);
}

/**
* Get the original Metapath expression as a string.
*
* @return the expression
*/
@NonNull
String getPath();

/**
* Get the static context used to compile this Metapath.
*
* @return the static context
*/
@NonNull
StaticContext getStaticContext();

/**
* Evaluate this Metapath expression without a specific focus. The required
* result type will be determined by the {@code resultType} argument.
*
* @param <T>
* the expected result type
* @param resultType
* the type of result to produce
* @return the converted result
* @throws TypeMetapathException
* if the provided sequence is incompatible with the requested result
* type
* @throws MetapathException
* if an error occurred during evaluation
* @see ResultType#convert(ISequence)
*/
@Nullable
default <T> T evaluateAs(@NonNull ResultType resultType) {
return evaluateAs(null, resultType);
}

/**
* Evaluate this Metapath expression using the provided {@code focus} as the
* initial evaluation context. The required result type will be determined by
* the {@code resultType} argument.
*
* @param <T>
* the expected result type
* @param focus
* the focus of the expression
* @param resultType
* the type of result to produce
* @return the converted result
* @throws TypeMetapathException
* if the provided sequence is incompatible with the requested result
* type
* @throws MetapathException
* if an error occurred during evaluation
* @see ResultType#convert(ISequence)
*/
@Nullable
default <T> T evaluateAs(
@Nullable IItem focus,
@NonNull ResultType resultType) {
ISequence<?> result = evaluate(focus);
return resultType.convert(result);
}

/**
* Evaluate this Metapath expression using the provided {@code focus} as the
* initial evaluation context. The specific result type will be determined by
* the {@code resultType} argument.
* <p>
* This variant allow for reuse of a provided {@code dynamicContext}.
*
* @param <T>
* the expected result type
* @param focus
* the outer focus of the expression
* @param resultType
* the type of result to produce
* @param dynamicContext
* the dynamic context to use for evaluation
* @return the converted result
* @throws TypeMetapathException
* if the provided sequence is incompatible with the requested result
* type
* @throws MetapathException
* if an error occurred during evaluation
* @see ResultType#convert(ISequence)
*/
@Nullable
default <T> T evaluateAs(
@Nullable IItem focus,
@NonNull ResultType resultType,
@NonNull DynamicContext dynamicContext) {
ISequence<?> result = evaluate(focus, dynamicContext);
return resultType.convert(result);
}

/**
* Evaluate this Metapath expression without a specific focus.
*
* @param <T>
* the type of items contained in the resulting sequence
* @return a sequence of Metapath items representing the result of the
* evaluation
* @throws MetapathException
* if an error occurred during evaluation
*/
@NonNull
default <T extends IItem> ISequence<T> evaluate() {
return evaluate((IItem) null);
}

/**
* Evaluate this Metapath expression using the provided {@code focus} as the
* initial evaluation context.
*
* @param <T>
* the type of items contained in the resulting sequence
* @param focus
* the outer focus of the expression
* @return a sequence of Metapath items representing the result of the
* evaluation
* @throws MetapathException
* if an error occurred during evaluation
*/
@NonNull
default <T extends IItem> ISequence<T> evaluate(
@Nullable IItem focus) {
return evaluate(focus, new DynamicContext(getStaticContext()));
}

/**
* Evaluate this Metapath expression using the provided {@code focus} as the
* initial evaluation context.
* <p>
* This variant allow for reuse of a provided {@code dynamicContext}.
*
* @param <T>
* the type of items contained in the resulting sequence
* @param focus
* the outer focus of the expression
* @param dynamicContext
* the dynamic context to use for evaluation
* @return a sequence of Metapath items representing the result of the
* evaluation
* @throws MetapathException
* if an error occurred during evaluation
*/
@NonNull
<T extends IItem> ISequence<T> evaluate(
@Nullable IItem focus,
@NonNull DynamicContext dynamicContext);
}

This file was deleted.

Loading

0 comments on commit dff71d3

Please sign in to comment.