MapConstructor.java
/*
* SPDX-FileCopyrightText: none
* SPDX-License-Identifier: CC0-1.0
*/
package gov.nist.secauto.metaschema.core.metapath.cst;
import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ICollectionValue;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException;
import gov.nist.secauto.metaschema.core.metapath.function.library.FnData;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem;
import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import edu.umd.cs.findbugs.annotations.NonNull;
/**
* An implementation of the
* <a href="https://www.w3.org/TR/xpath-31/#id-map-constructors">Map
* Constructor</a> supporting the creation of a Metapath {@link IMapItem}.
*/
public class MapConstructor implements IExpression {
@NonNull
private final List<MapConstructor.Entry> entries;
/**
* Construct a new map constructor expression that uses the provided entry
* expressions to initialize the map entries.
*
* @param entries
* the expressions used to produce the map entries
*/
public MapConstructor(@NonNull List<MapConstructor.Entry> entries) {
this.entries = entries;
}
@Override
public List<MapConstructor.Entry> getChildren() {
return entries;
}
@Override
public ISequence<? extends IItem> accept(DynamicContext dynamicContext, ISequence<?> focus) {
return IMapItem.ofCollection(
ObjectUtils.notNull(getChildren().stream()
.map(item -> {
IExpression keyExpression = item.getKeyExpression();
IAnyAtomicItem key = FnData.fnData(keyExpression.accept(dynamicContext, focus))
.getFirstItem(true);
if (key == null) {
throw new InvalidTypeMetapathException(null, String.format(
"The expression '%s' did not result in a single key atomic value.", keyExpression.toASTString()));
}
ICollectionValue value = item.getValueExpression().accept(dynamicContext, focus).toCollectionValue();
return IMapItem.entry(key, value);
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))))
.asSequence();
}
@Override
public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
return visitor.visitMapConstructor(this, context);
}
/**
* A map entry expression used to produce an entry in a {@link IMapItem}.
*/
public static class Entry implements IExpression {
@NonNull
private final IExpression keyExpression;
@NonNull
private final IExpression valueExpression;
/**
* Construct a new map entry expression using the provided ket and value
* expressions.
*
* @param keyExpression
* the expression used to get the map entry key
* @param valueExpression
* the expression used to get the map entry value
*/
public Entry(@NonNull IExpression keyExpression, @NonNull IExpression valueExpression) {
this.keyExpression = keyExpression;
this.valueExpression = valueExpression;
}
/**
* Get the map entry key expression.
*
* @return the key expression
*/
@NonNull
public IExpression getKeyExpression() {
return keyExpression;
}
/**
* Get the map entry value expression.
*
* @return the value expression
*/
@NonNull
public IExpression getValueExpression() {
return valueExpression;
}
@SuppressWarnings("null")
@Override
public List<? extends IExpression> getChildren() {
return List.of(keyExpression, valueExpression);
}
@Override
public ISequence<? extends IItem> accept(DynamicContext dynamicContext, ISequence<?> focus) {
throw new UnsupportedOperationException("handled by the map constructor");
}
@Override
public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
return visitor.visitMapConstructorEntry(this, context);
}
}
}