*/
package org.opendaylight.yangtools.yang.data.codec.gson;
-import static com.google.common.base.Verify.verifyNotNull;
-
import com.google.common.annotations.Beta;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.yang.data.util.codec.CodecCache;
import org.opendaylight.yangtools.yang.data.util.codec.LazyCodecCache;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition;
-import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
/**
* Factory for creating JSON equivalents of codecs. Each instance of this object is bound to
abstract JSONCodec<?> wrapDecimalCodec(DecimalStringCodec decimalCodec);
abstract JSONCodec<?> wrapIntegerCodec(AbstractIntegerStringCodec<?, ?> integerCodec);
-
- final JSONCodec<?> codecFor(final TypedDataSchemaNode currentNode) {
- return codecFor(currentNode, type -> verifyNotNull(
- SchemaContextUtil.getBaseTypeForLeafRef(type, getEffectiveModelContext(), currentNode),
- "Unable to find base type for leafref node %s type %s.", currentNode, type));
- }
}
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.util.codec.CodecCache;
import org.opendaylight.yangtools.yang.data.util.codec.LazyCodecCache;
import org.opendaylight.yangtools.yang.data.util.codec.NoopCodecCache;
import org.opendaylight.yangtools.yang.data.util.codec.PrecomputedCodecCache;
import org.opendaylight.yangtools.yang.data.util.codec.SharedCodecCache;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final Stopwatch sw = Stopwatch.createStarted();
final LazyCodecCache<JSONCodec<?>> lazyCache = new LazyCodecCache<>();
final JSONCodecFactory lazy = factorySupplier.apply(key, lazyCache);
- final int visitedLeaves = requestCodecsForChildren(lazy, key);
+ final SchemaInferenceStack stack = SchemaInferenceStack.of(key);
+
+ int visitedLeaves = 0;
+ for (ModuleEffectiveStatement module : key.getModuleStatements().values()) {
+ visitedLeaves += codecsForChildren(lazy, stack, module);
+ stack.clear();
+ }
sw.stop();
final PrecomputedCodecCache<JSONCodec<?>> cache = lazyCache.toPrecomputed();
return factorySupplier.apply(key, cache);
}
- private static int requestCodecsForChildren(final JSONCodecFactory lazy, final DataNodeContainer parent) {
+ private static int codecsForChildren(final JSONCodecFactory lazy, final SchemaInferenceStack stack,
+ final DataTreeAwareEffectiveStatement<?, ?> parent) {
int ret = 0;
- for (DataSchemaNode child : parent.getChildNodes()) {
- if (child instanceof TypedDataSchemaNode) {
- lazy.codecFor((TypedDataSchemaNode) child);
+ final Map<QName, DataTreeEffectiveStatement<?>> dataTree =
+ parent.getAll(DataTreeAwareEffectiveStatement.Namespace.class);
+ for (DataTreeEffectiveStatement<?> child : dataTree.values()) {
+ if (child instanceof DataTreeAwareEffectiveStatement) {
+ stack.enterDataTree(child.argument());
+ ret += codecsForChildren(lazy, stack, (DataTreeAwareEffectiveStatement<?, ?>) child);
+ stack.exit();
+ } else if (child instanceof TypedDataSchemaNode) {
+ lazy.codecFor((TypedDataSchemaNode) child, stack);
++ret;
- } else if (child instanceof DataNodeContainer) {
- ret += requestCodecsForChildren(lazy, (DataNodeContainer) child);
}
}
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.util.LeafrefResolver;
abstract class JSONInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec
implements JSONCodec<YangInstanceIdentifier> {
}
@Override
- protected final Object deserializeKeyValue(final DataSchemaNode schemaNode, final String value) {
+ protected final Object deserializeKeyValue(final DataSchemaNode schemaNode, final LeafrefResolver resolver,
+ final String value) {
requireNonNull(schemaNode, "schemaNode cannot be null");
checkArgument(schemaNode instanceof LeafSchemaNode, "schemaNode must be of type LeafSchemaNode");
- final JSONCodec<?> objectJSONCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode);
+ final JSONCodec<?> objectJSONCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode, resolver);
return objectJSONCodec.parseValue(null, value);
}
public void scalarValue(final Object value) throws IOException {
final Object current = tracker.getParent();
if (current instanceof TypedDataSchemaNode) {
- writeValue(value, codecs.codecFor((TypedDataSchemaNode) current));
+ writeValue(value, codecs.codecFor((TypedDataSchemaNode) current, tracker));
} else if (current instanceof AnydataSchemaNode) {
writeAnydataValue(value);
} else {
import javax.xml.transform.dom.DOMSource;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.XMLNamespace;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.util.AbstractNodeDataWithSchema;
import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
private final JSONCodecFactory codecs;
private final DataSchemaNode parentNode;
+ private final SchemaInferenceStack stack;
+
// TODO: consider class specialization to remove this field
private final boolean lenient;
private JsonParserStream(final NormalizedNodeStreamWriter writer, final JSONCodecFactory codecs,
- final DataSchemaNode parentNode, final boolean lenient) {
+ final SchemaInferenceStack stack, final boolean lenient) {
this.writer = requireNonNull(writer);
this.codecs = requireNonNull(codecs);
- this.parentNode = parentNode;
+ this.stack = requireNonNull(stack);
this.lenient = lenient;
+
+ if (!stack.isEmpty()) {
+ final EffectiveStatement<?, ?> parent = stack.currentStatement();
+ if (parent instanceof DataSchemaNode) {
+ parentNode = (DataSchemaNode) parent;
+ } else if (parent instanceof OperationDefinition) {
+ parentNode = OperationAsContainer.of((OperationDefinition) parent);
+ } else {
+ throw new IllegalArgumentException("Illegal parent node " + parent);
+ }
+ } else {
+ parentNode = stack.getEffectiveModelContext();
+ }
}
/**
*/
public static @NonNull JsonParserStream create(final @NonNull NormalizedNodeStreamWriter writer,
final @NonNull JSONCodecFactory codecFactory) {
- return new JsonParserStream(writer, codecFactory, codecFactory.getEffectiveModelContext(), false);
+ return new JsonParserStream(writer, codecFactory,
+ SchemaInferenceStack.of(codecFactory.getEffectiveModelContext()), false);
}
/**
* @throws NullPointerException if any of the arguments are null
*/
public static @NonNull JsonParserStream create(final @NonNull NormalizedNodeStreamWriter writer,
- final @NonNull JSONCodecFactory codecFactory, final @NonNull SchemaNode parentNode) {
- return new JsonParserStream(writer, codecFactory, validateParent(parentNode), false);
+ final @NonNull JSONCodecFactory codecFactory, final @NonNull EffectiveStatementInference parentNode) {
+ return new JsonParserStream(writer, codecFactory, SchemaInferenceStack.ofInference(parentNode), false);
}
/**
*/
public static @NonNull JsonParserStream createLenient(final @NonNull NormalizedNodeStreamWriter writer,
final @NonNull JSONCodecFactory codecFactory) {
- return new JsonParserStream(writer, codecFactory, codecFactory.getEffectiveModelContext(), true);
+ return new JsonParserStream(writer, codecFactory,
+ SchemaInferenceStack.of(codecFactory.getEffectiveModelContext()), true);
}
/**
* @throws NullPointerException if any of the arguments are null
*/
public static @NonNull JsonParserStream createLenient(final @NonNull NormalizedNodeStreamWriter writer,
- final @NonNull JSONCodecFactory codecFactory, final @NonNull SchemaNode parentNode) {
- return new JsonParserStream(writer, codecFactory, validateParent(parentNode), true);
+ final @NonNull JSONCodecFactory codecFactory, final @NonNull EffectiveStatementInference parentNode) {
+ return new JsonParserStream(writer, codecFactory, SchemaInferenceStack.ofInference(parentNode), true);
}
public JsonParserStream parse(final JsonReader reader) {
"Schema for node with name %s and namespace %s does not exist at %s",
localName, getCurrentNamespace(), parentSchema);
-
+ final QName qname = childDataSchemaNodes.peekLast().getQName();
final AbstractNodeDataWithSchema<?> newChild = ((CompositeNodeDataWithSchema<?>) parent)
.addChild(childDataSchemaNodes, ChildReusePolicy.NOOP);
if (newChild instanceof AnyXmlNodeDataWithSchema) {
readAnyXmlValue(in, (AnyXmlNodeDataWithSchema) newChild, jsonElementName);
} else {
+ stack.enterDataTree(qname);
read(in, newChild);
+ stack.exit();
}
removeNamespace();
}
private Object translateValueByType(final String value, final DataSchemaNode node) {
checkArgument(node instanceof TypedDataSchemaNode);
- return codecs.codecFor((TypedDataSchemaNode) node).parseValue(null, value);
+ return codecs.codecFor((TypedDataSchemaNode) node, stack).parseValue(null, value);
}
private void removeNamespace() {
return namespaces.peek();
}
- private static DataSchemaNode validateParent(final SchemaNode parent) {
- if (parent instanceof DataSchemaNode) {
- return (DataSchemaNode) parent;
- } else if (parent instanceof OperationDefinition) {
- return OperationAsContainer.of((OperationDefinition) parent);
- } else {
- throw new IllegalArgumentException("Illegal parent node " + requireNonNull(parent));
- }
- }
-
@Override
public void flush() throws IOException {
writer.flush();
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
/**
* Each test tests whether json input is correctly transformed to normalized node structure.
final NormalizedNodeResult result = new NormalizedNodeResult();
final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
- final SchemaNode parentNode = schemaContext.findDataChildByName(CONT_1).get();
- final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory, parentNode);
+ final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory,
+ Inference.ofDataTreePath(schemaContext, CONT_1));
jsonParser.parse(new JsonReader(new StringReader(inputJson)));
final NormalizedNode transformedInput = result.getResult();
assertNotNull(transformedInput);
final NormalizedNodeResult result = new NormalizedNodeResult();
final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
- final SchemaNode parentNode = schemaContext.findDataChildByName(CONT_1).get();
- final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory, parentNode);
+ final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory,
+ Inference.ofDataTreePath(schemaContext, CONT_1));
jsonParser.parse(new JsonReader(new StringReader(inputJson)));
final NormalizedNode transformedInput = result.getResult();
assertNotNull(transformedInput);
final NormalizedNodeResult result = new NormalizedNodeResult();
final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
- final SchemaNode parentNode = schemaContext.findDataChildByName(CONT_1).get();
- final QName augmentChoice1QName = QName.create(parentNode.getQName(), "augment-choice1");
+ final QName augmentChoice1QName = QName.create(CONT_1, "augment-choice1");
final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
final QName containerQName = QName.create(augmentChoice1QName, "case11-choice-case-container");
final QName leafQName = QName.create(augmentChoice1QName, "case11-choice-case-leaf");
final NodeIdentifier containerId = new NodeIdentifier(containerQName);
final NormalizedNode cont1Normalized =
- containerBuilder().withNodeIdentifier(new NodeIdentifier(parentNode.getQName()))
+ containerBuilder().withNodeIdentifier(new NodeIdentifier(CONT_1))
.withChild(augmentationBuilder().withNodeIdentifier(aug1Id)
.withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id)
.withChild(augmentationBuilder().withNodeIdentifier(aug2Id)
typedef ref1-2 {
type leafref {
- path "/root/l1";
+ path "/bar:root/bar:l1";
}
}
typedef ref2-2 {
type leafref {
- path "/root/l2";
+ path "/bar:root/bar:l2";
}
}
typedef ref3-2 {
type leafref {
- path "/root/l3";
+ path "/bar:root/bar:l3";
}
}
typedef leafreftype {
type leafref {
- path "/cont/lf3";
+ path "/auglfrfmo:cont/auglfrfmo:lf3";
}
}
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
final class SchemaAwareXMLStreamNormalizedNodeStreamWriter
extends XMLStreamNormalizedNodeStreamWriter<TypedDataSchemaNode> implements EffectiveModelContextProvider {
@Override
String encodeValue(final ValueWriter xmlWriter, final Object value, final TypedDataSchemaNode schemaNode)
throws XMLStreamException {
- return streamUtils.encodeValue(xmlWriter, schemaNode, schemaNode.getType(), value,
+ return streamUtils.encodeValue(xmlWriter, resolveType(schemaNode.getType()), value,
schemaNode.getQName().getModule());
}
final Optional<AnnotationSchemaNode> optAnnotation =
AnnotationSchemaNode.find(streamUtils.getEffectiveModelContext(), qname);
if (optAnnotation.isPresent()) {
- final AnnotationSchemaNode schema = optAnnotation.get();
- return streamUtils.encodeValue(xmlWriter, schema, schema.getType(), value, qname.getModule());
+ return streamUtils.encodeValue(xmlWriter, resolveType(optAnnotation.get().getType()), value,
+ qname.getModule());
}
checkArgument(!qname.getRevision().isPresent(), "Failed to find bound annotation %s", qname);
void startAnydata(final NodeIdentifier name) {
tracker.startAnydataNode(name);
}
+
+ private @NonNull TypeDefinition<?> resolveType(final @NonNull TypeDefinition<?> type) throws XMLStreamException {
+ if (type instanceof LeafrefTypeDefinition) {
+ try {
+ return tracker.resolveLeafref((LeafrefTypeDefinition) type);
+ } catch (IllegalArgumentException e) {
+ throw new XMLStreamException("Cannot resolved type " + type, e);
+ }
+ }
+ return type;
+ }
}
*/
package org.opendaylight.yangtools.yang.data.codec.xml;
-import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
import java.util.Map.Entry;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
-import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
final class SchemaAwareXMLStreamWriterUtils extends XMLStreamWriterUtils implements EffectiveModelContextProvider {
private final @NonNull EffectiveModelContext schemaContext;
this.schemaContext = requireNonNull(schemaContext);
}
- @Override
- TypeDefinition<?> getBaseTypeForLeafRef(final SchemaNode schemaNode, final LeafrefTypeDefinition type) {
- final TypeDefinition<?> ret = SchemaContextUtil.getBaseTypeForLeafRef(type, schemaContext, schemaNode);
- return verifyNotNull(ret, "Unable to find base type for leafref node '%s'.", schemaNode);
- }
-
@Override
String encodeInstanceIdentifier(final ValueWriter writer, final YangInstanceIdentifier value)
throws XMLStreamException {
package org.opendaylight.yangtools.yang.data.codec.xml;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
final class SchemalessXMLStreamWriterUtils extends XMLStreamWriterUtils {
static final SchemalessXMLStreamWriterUtils INSTANCE = new SchemalessXMLStreamWriterUtils();
// Hidden on purpose
}
- @Override
- TypeDefinition<?> getBaseTypeForLeafRef(final SchemaNode schemaNode, final LeafrefTypeDefinition type) {
- return type;
- }
-
@Override
String encodeInstanceIdentifier(final ValueWriter writer, final YangInstanceIdentifier value) {
throw new UnsupportedOperationException("Schema context not present in " + this + ", cannot serialize "
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*/
private static final Set<QName> IDENTITYREF_WARNED = ConcurrentHashMap.newKeySet();
- /**
- * Encode a value into a String in the context of a XML stream writer. This method assumes the start and end of
- * element is emitted by the caller.
- *
- * @param writer XML Stream writer
- * @param schemaNode Schema node that describes the value
- * @param type Schema type definition
- * @param value data value
- * @param parent module QName owning the leaf definition
- * @return String characters to be written
- * @throws XMLStreamException if an encoding problem occurs
- */
- String encodeValue(final @NonNull ValueWriter writer,final @NonNull SchemaNode schemaNode,
- final TypeDefinition<?> type, final @NonNull Object value, final QNameModule parent)
- throws XMLStreamException {
- return type instanceof LeafrefTypeDefinition
- ? encodeValue(writer, getBaseTypeForLeafRef(schemaNode, (LeafrefTypeDefinition) type), value, parent)
- : encodeValue(writer, type, value, parent);
- }
-
/**
* Write a value into a XML stream writer. This method assumes the start and end of element is
* emitted by the caller.
* @return String characters to be written
* @throws XMLStreamException if an encoding problem occurs
*/
- private String encodeValue(final @NonNull ValueWriter writer, final @NonNull TypeDefinition<?> type,
+ String encodeValue(final @NonNull ValueWriter writer, final @NonNull TypeDefinition<?> type,
final @NonNull Object value, final QNameModule parent) throws XMLStreamException {
if (type instanceof IdentityrefTypeDefinition) {
return encode(writer, (IdentityrefTypeDefinition) type, value, parent);
return value.toString();
}
- abstract @NonNull TypeDefinition<?> getBaseTypeForLeafRef(SchemaNode schemaNode,
- @NonNull LeafrefTypeDefinition type);
-
abstract String encodeInstanceIdentifier(@NonNull ValueWriter writer, YangInstanceIdentifier value)
throws XMLStreamException;
}
package org.opendaylight.yangtools.yang.data.codec.xml;
-import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
import org.opendaylight.yangtools.yang.data.util.codec.AbstractCodecFactory;
import org.opendaylight.yangtools.yang.data.util.codec.SharedCodecCache;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.TypeAware;
import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition;
-import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
/**
* A thread-safe factory for instantiating {@link XmlCodec}s.
protected XmlCodec<?> unknownCodec(final UnknownTypeDefinition type) {
return NullXmlCodec.INSTANCE;
}
-
- <T extends SchemaNode & TypeAware> XmlCodec<?> codecFor(final T currentNode) {
- return codecFor(currentNode, type -> verifyNotNull(
- SchemaContextUtil.getBaseTypeForLeafRef(type, getEffectiveModelContext(), currentNode),
- "Unable to find base type for leafref node %s type %s.", currentNode, type));
- }
}
codecs.getEffectiveModelContext(), qname);
if (optAnnotation.isPresent()) {
final AnnotationSchemaNode schema = optAnnotation.get();
- final Object value = codecs.codecFor(schema).parseValue(in.getNamespaceContext(), attrValue);
+ final Object value = codecs.codecFor(schema, stack)
+ .parseValue(in.getNamespaceContext(), attrValue);
attributes.put(schema.getQName(), value);
continue;
}
checkArgument(node instanceof TypedDataSchemaNode);
checkArgument(value instanceof String);
- return codecs.codecFor((TypedDataSchemaNode) node).parseValue(namespaceCtx, (String) value);
+ return codecs.codecFor((TypedDataSchemaNode) node, stack).parseValue(namespaceCtx, (String) value);
}
private static AbstractNodeDataWithSchema<?> newEntryNode(final AbstractNodeDataWithSchema<?> parent) {
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.util.LeafrefResolver;
final class XmlStringInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec
implements XmlCodec<YangInstanceIdentifier> {
}
@Override
- protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final String value) {
+ protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final LeafrefResolver resolver,
+ final String value) {
requireNonNull(schemaNode, "schemaNode cannot be null");
checkArgument(schemaNode instanceof LeafSchemaNode, "schemaNode must be of type LeafSchemaNode");
- final XmlCodec<?> objectXmlCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode);
+ final XmlCodec<?> objectXmlCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode, resolver);
return objectXmlCodec.parseValue(getNamespaceContext(), value);
}
package org.opendaylight.yangtools.yang.data.codec.xml;
import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.net.URI;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.stream.XMLStreamException;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.common.XMLNamespace;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
-import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
public class XmlStreamUtilsTest {
void accept(XMLStreamWriter writer) throws XMLStreamException;
}
- private static SchemaContext schemaContext;
+ private static EffectiveModelContext schemaContext;
private static Module leafRefModule;
@BeforeClass
*/
@Test
public void testLeafRefRelativeChaining() {
- getTargetNodeForLeafRef("leafname3", StringTypeDefinition.class);
+ getTargetNodeForLeafRef(StringTypeDefinition.class, "cont3", "leafname3");
}
@Test
public void testLeafRefRelative() {
- getTargetNodeForLeafRef("pointToStringLeaf", StringTypeDefinition.class);
+ getTargetNodeForLeafRef(StringTypeDefinition.class, "pointToStringLeaf");
}
@Test
public void testLeafRefAbsoluteWithSameTarget() {
- getTargetNodeForLeafRef("absname", InstanceIdentifierTypeDefinition.class);
+ getTargetNodeForLeafRef(InstanceIdentifierTypeDefinition.class, "absname");
}
/**
@Ignore
@Test
public void testLeafRefWithDoublePointInPath() {
- getTargetNodeForLeafRef("lf-with-double-point-inside", StringTypeDefinition.class);
+ getTargetNodeForLeafRef(StringTypeDefinition.class, "lf-with-double-point-inside");
}
@Test
public void testLeafRefRelativeAndAbsoluteWithSameTarget() {
- final TypeDefinition<?> targetNodeForAbsname = getTargetNodeForLeafRef("absname",
- InstanceIdentifierTypeDefinition.class);
- final TypeDefinition<?> targetNodeForRelname = getTargetNodeForLeafRef("relname",
- InstanceIdentifierTypeDefinition.class);
- assertEquals(targetNodeForAbsname, targetNodeForRelname);
+ assertSame(getTargetNodeForLeafRef(InstanceIdentifierTypeDefinition.class, "absname"),
+ getTargetNodeForLeafRef(InstanceIdentifierTypeDefinition.class, "relname"));
}
- private TypeDefinition<?> getTargetNodeForLeafRef(final String nodeName, final Class<?> clas) {
- final LeafSchemaNode schemaNode = findSchemaNodeWithLeafrefType(leafRefModule, nodeName);
- assertNotNull(schemaNode);
- final LeafrefTypeDefinition leafrefTypedef = findLeafrefType(schemaNode);
- assertNotNull(leafrefTypedef);
- final TypeDefinition<?> targetBaseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypedef, schemaContext,
- schemaNode);
- assertTrue("Wrong class found.", clas.isInstance(targetBaseType));
- return targetBaseType;
- }
-
- private static Map<String, String> mapPrefixed(final Iterable<Map.Entry<URI, String>> prefixes) {
- final Map<String, String> mappedPrefixes = new HashMap<>();
- for (final Map.Entry<URI, String> prefix : prefixes) {
- mappedPrefixes.put(prefix.getKey().toString(), prefix.getValue());
+ private static TypeDefinition<?> getTargetNodeForLeafRef(final Class<?> clas, final String... names) {
+ final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext);
+ stack.enterDataTree(QName.create(leafRefModule.getQNameModule(), "cont2"));
+ for (String name : names) {
+ stack.enterDataTree(QName.create(leafRefModule.getQNameModule(), name));
}
- return mappedPrefixes;
- }
- private static QName getAttrQName(final String namespace, final String revision, final String localName,
- final Optional<String> prefix) {
- if (prefix.isPresent()) {
- final QName moduleQName = QName.create(namespace, revision, "module");
- final QNameModule module = QNameModule.create(moduleQName.getNamespace(), moduleQName.getRevision());
- return QName.create(module, localName);
- }
- return QName.create(namespace, revision, localName);
- }
+ final EffectiveStatement<?, ?> leaf = stack.currentStatement();
+ assertThat(leaf, instanceOf(LeafSchemaNode.class));
+ final TypeDefinition<? extends TypeDefinition<?>> type = ((TypedDataSchemaNode) leaf).getType();
+ assertThat(type, instanceOf(LeafrefTypeDefinition.class));
- private LeafSchemaNode findSchemaNodeWithLeafrefType(final DataNodeContainer module, final String nodeName) {
- for (final DataSchemaNode childNode : module.getChildNodes()) {
- if (childNode instanceof DataNodeContainer) {
- LeafSchemaNode leafrefFromRecursion = findSchemaNodeWithLeafrefType((DataNodeContainer) childNode,
- nodeName);
- if (leafrefFromRecursion != null) {
- return leafrefFromRecursion;
- }
- } else if (childNode.getQName().getLocalName().equals(nodeName) && childNode instanceof LeafSchemaNode) {
- final TypeDefinition<?> leafSchemaNodeType = ((LeafSchemaNode) childNode).getType();
- if (leafSchemaNodeType instanceof LeafrefTypeDefinition) {
- return (LeafSchemaNode) childNode;
- }
- }
- }
- return null;
- }
-
- private static LeafrefTypeDefinition findLeafrefType(final LeafSchemaNode schemaNode) {
- final TypeDefinition<?> type = schemaNode.getType();
- if (type instanceof LeafrefTypeDefinition) {
- return (LeafrefTypeDefinition) type;
- }
- return null;
+ final TypeDefinition<?> resolved = stack.resolveLeafref((LeafrefTypeDefinition) type);
+ assertThat(resolved, instanceOf(clas));
+ return resolved;
}
}
requires transitive org.opendaylight.yangtools.yang.model.util;
requires com.google.common;
+ requires org.opendaylight.yangtools.concepts;
+ requires org.opendaylight.yangtools.yang.model.api;
requires org.opendaylight.yangtools.yang.model.spi;
requires org.opendaylight.yangtools.util;
requires org.slf4j;
// Annotations
requires static org.eclipse.jdt.annotation;
- requires org.opendaylight.yangtools.concepts;
}
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.codec.InstanceIdentifierCodec;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.LeafrefResolver;
/**
* Abstract utility class for representations which encode {@link YangInstanceIdentifier} as a
*/
protected abstract @NonNull DataSchemaContextTree getDataContextTree();
- protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final String value) {
+ protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final LeafrefResolver resolver,
+ final String value) {
return value;
}
}
@Override
+ @Deprecated
public boolean isAugmenting() {
return false;
}
/*
* Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * Copyright (c) 2021 PANTHEON.tech, s.r.o.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.concepts.Mutable;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ActionEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
+import org.opendaylight.yangtools.yang.model.util.LeafrefResolver;
import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
import org.slf4j.Logger;
* Utility class for tracking schema state underlying a {@link NormalizedNode} structure.
*/
@Beta
-public final class NormalizedNodeStreamWriterStack implements Mutable {
+public final class NormalizedNodeStreamWriterStack implements LeafrefResolver {
private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodeStreamWriterStack.class);
private final Deque<WithStatus> schemaStack = new ArrayDeque<>();
private final DataNodeContainer root;
private NormalizedNodeStreamWriterStack(final EffectiveModelContext context) {
- root = requireNonNull(context);
dataTree = SchemaInferenceStack.of(context);
+ root = requireNonNull(context);
}
private NormalizedNodeStreamWriterStack(final SchemaInferenceStack dataTree) {
return new NormalizedNodeStreamWriterStack(stack);
}
- /**
- * Return a copy of this tracker's state as an {@link SchemaInferenceStack}.
- *
- * @return A SchemaInferenceStack
- */
- public @NonNull SchemaInferenceStack toSchemaInferenceStack() {
- return dataTree.copy();
+ @Override
+ public TypeDefinition<?> resolveLeafref(final LeafrefTypeDefinition type) {
+ return dataTree.resolveLeafref(type);
}
public Object getParent() {
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.concepts.Builder;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
/**
* Iterator which lazily parses {@link PathArgument} from string representation.
private final List<PathArgument> product = new ArrayList<>();
private final AbstractStringInstanceIdentifierCodec codec;
+ private final SchemaInferenceStack stack;
private final String data;
private DataSchemaContextNode<?> current;
XpathStringParsingPathArgumentBuilder(final AbstractStringInstanceIdentifierCodec codec, final String data) {
this.codec = requireNonNull(codec);
this.data = requireNonNull(data);
- this.current = codec.getDataContextTree().getRoot();
this.offset = 0;
+
+ final DataSchemaContextTree tree = codec.getDataContextTree();
+ this.stack = SchemaInferenceStack.of(tree.getEffectiveModelContext());
+ this.current = tree.getRoot();
}
@Override
private DataSchemaContextNode<?> nextContextNode(final QName name) {
current = current.getChild(name);
- checkValid(current != null, "%s is not correct schema node identifier.",name);
+ checkValid(current != null, "%s is not correct schema node identifier.", name);
while (current.isMixin()) {
product.add(current.getIdentifier());
current = current.getChild(name);
}
+ stack.enterDataTree(name);
return current;
}
* @return PathArgument representing node selection with predictes
*/
private PathArgument computeIdentifierWithPredicate(final QName name) {
- DataSchemaContextNode<?> currentNode = nextContextNode(name);
+ final DataSchemaContextNode<?> currentNode = nextContextNode(name);
checkValid(currentNode.isKeyedEntry(), "Entry %s does not allow specifying predicates.", name);
- ImmutableMap.Builder<QName,Object> keyValues = ImmutableMap.builder();
+ ImmutableMap.Builder<QName, Object> keyValues = ImmutableMap.builder();
while (!allCharactersConsumed() && PRECONDITION_START == currentChar()) {
skipCurrentChar();
skipWhitespaces();
}
final DataSchemaContextNode<?> keyNode = currentNode.getChild(key);
checkValid(keyNode != null, "%s is not correct schema node identifier.", key);
- final Object value = codec.deserializeKeyValue(keyNode.getDataSchemaNode(), keyValue);
+ final Object value = codec.deserializeKeyValue(keyNode.getDataSchemaNode(),
+ type -> resolveLeafref(key, type), keyValue);
keyValues.put(key, value);
}
return NodeIdentifierWithPredicates.of(name, keyValues.build());
}
+ private @NonNull TypeDefinition<?> resolveLeafref(final QName qname, final LeafrefTypeDefinition type) {
+ final SchemaInferenceStack tmp = stack.copy();
+ tmp.enterDataTree(qname);
+ return tmp.resolveLeafref(type);
+ }
private PathArgument computeIdentifier(final QName name) {
DataSchemaContextNode<?> currentNode = nextContextNode(name);
import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
-import com.google.common.annotations.Beta;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition;
import org.opendaylight.yangtools.yang.model.spi.AbstractEffectiveModelContextProvider;
+import org.opendaylight.yangtools.yang.model.util.LeafrefResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*/
public abstract class AbstractCodecFactory<T extends TypeAwareCodec<?, ?, ?>>
extends AbstractEffectiveModelContextProvider {
- /**
- * Helper interface aiding resolution of leafref chains.
- */
- @Beta
- @FunctionalInterface
- public interface LeafrefResolver {
- /**
- * Resolve specified {@link LeafrefTypeDefinition}.
- *
- * @param type leafref definition
- * @return Resolved type
- */
- @NonNull TypeDefinition<?> resolveLeafref(@NonNull LeafrefTypeDefinition type);
- }
-
private static final Logger LOG = LoggerFactory.getLogger(AbstractCodecFactory.class);
private final @NonNull CodecCache<T> cache;
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-model-spi</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-xpath-api</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-xpath-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
requires transitive org.opendaylight.yangtools.yang.model.api;
requires transitive org.opendaylight.yangtools.yang.repo.api;
+ requires transitive org.opendaylight.yangtools.yang.xpath.api;
requires com.google.common;
requires org.opendaylight.yangtools.yang.common;
requires org.opendaylight.yangtools.yang.model.spi;
--- /dev/null
+/*
+ * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.util;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+
+/**
+ * Helper interface aiding resolution of leafref chains.
+ */
+@Beta
+@FunctionalInterface
+public interface LeafrefResolver {
+ /**
+ * Resolve specified {@link LeafrefTypeDefinition} until a non-{@code leafref} type is found.
+ *
+ * @param type leafref definition
+ * @return Resolved type
+ * @throws NullPointerException if {@code type} is null
+ * @throws IllegalArgumentException if the type definition cannot be resolved
+ */
+ @NonNull TypeDefinition<?> resolveLeafref(@NonNull LeafrefTypeDefinition type);
+}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (c) 2019 PANTHEON.tech, s.r.o. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.yangtools.yang.model.util;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.yangtools.yang.model.spi.AbstractPathExpression;
-
-/**
- * A simple XPathExpression implementation.
- *
- * @deprecated Users are advised to supply their own implementation of PathExpression.
- */
-@Deprecated(forRemoval = true)
-@NonNullByDefault
-public final class PathExpressionImpl extends AbstractPathExpression {
- private final @Nullable Steps steps;
- private final boolean absolute;
-
- @SuppressFBWarnings(value = "NP_STORE_INTO_NONNULL_FIELD", justification = "Non-grok on SpotBugs part")
- public PathExpressionImpl(final String xpath, final boolean absolute) {
- super(xpath);
- this.absolute = absolute;
- this.steps = null;
- }
-
- public PathExpressionImpl(final String xpath, final Steps steps) {
- super(xpath);
- this.steps = requireNonNull(steps);
- this.absolute = steps instanceof LocationPathSteps
- && ((LocationPathSteps) steps).getLocationPath().isAbsolute();
- }
-
- @Override
- public boolean isAbsolute() {
- return absolute;
- }
-
- @Override
- public Steps getSteps() {
- final Steps loc = steps;
- if (loc == null) {
- throw new UnsupportedOperationException("Steps have not been provided");
- }
- return loc;
- }
-
- @Override
- protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
- return super.addToStringAttributes(helper.add("absolute", absolute).add("steps", steps));
- }
-}
package org.opendaylight.yangtools.yang.model.util;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.yangtools.yang.common.AbstractQName;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerLike;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.ModuleImport;
import org.opendaylight.yangtools.yang.model.api.ModuleLike;
import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
-import org.opendaylight.yangtools.yang.model.api.PathExpression;
-import org.opendaylight.yangtools.yang.model.api.PathExpression.DerefSteps;
-import org.opendaylight.yangtools.yang.model.api.PathExpression.LocationPathSteps;
-import org.opendaylight.yangtools.yang.model.api.PathExpression.Steps;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.api.Submodule;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
-import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
-import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStep;
-import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
-import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
-import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*/
public final class SchemaContextUtil {
private static final Logger LOG = LoggerFactory.getLogger(SchemaContextUtil.class);
- private static final Splitter COLON_SPLITTER = Splitter.on(':');
- private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings();
- private static final Pattern GROUPS_PATTERN = Pattern.compile("\\[(.*?)\\]");
+
private SchemaContextUtil() {
// Hidden on purpose
return findNodeInSchemaContext(context, prefixedPath);
}
- /**
- * Attempt to find a DataSchemaNode based on its path from root, similar to
- * {@link #findDataSchemaNode(SchemaContext, Module, PathExpression)} without requiring an expression.
- *
- * @param context Schema Context
- * @param path Path to search for
- * @return SchemaNode from the end of the Schema Path or {@code null} if the Node is not present.
- * @throws NullPointerException if a any argument is null or if the path contains a null element
- */
- @Beta
- public static SchemaNode findDataSchemaNode(final SchemaContext context, final List<QName> path) {
- return findTargetNode(context, null, YangLocationPath.absolute(
- path.stream().map(YangXPathAxis.CHILD::asStep).collect(Collectors.toList())));
- }
-
- /**
- * Attempt to find a DataSchemaNode based on its path from root, similar to
- * {@link #findDataSchemaNode(SchemaContext, Module, PathExpression)} without requiring an expression.
- *
- * @param context Schema Context
- * @param path Path to search for
- * @return SchemaNode from the end of the Schema Path or {@code null} if the Node is not present.
- * @throws NullPointerException if a any argument is null or if the path contains a null element
- */
- @Beta
- public static SchemaNode findDataSchemaNode(final SchemaContext context, final QName... path) {
- return findDataSchemaNode(context, Arrays.asList(path));
- }
-
- /**
- * Method attempts to find DataSchemaNode inside of provided Schema Context
- * and Yang Module accordingly to Non-conditional Revision Aware XPath. The
- * specified Module MUST be present in Schema Context otherwise the
- * operation would fail and return <code>null</code>. <br>
- * The Revision Aware XPath MUST be specified WITHOUT the conditional
- * statement (i.e. without [cond]) in path, because in this state the Schema
- * Context is completely unaware of data state and will be not able to
- * properly resolve XPath. If the XPath contains condition the method will
- * return IllegalArgumentException. <br>
- * If the Revision Aware XPath is correct and desired Data Schema Node is
- * present in Yang module or in depending module in Schema Context the
- * method will return specified Data Schema Node, otherwise the operation
- * will fail and method will return <code>null</code>.
- *
- * @param context
- * Schema Context
- * @param module
- * Yang Module
- * @param nonCondXPath
- * Non Conditional Revision Aware XPath
- * @return Returns Data Schema Node for specified Schema Context for given
- * Non-conditional Revision Aware XPath, or <code>null</code> if the
- * DataSchemaNode is not present in Schema Context.
- * @throws NullPointerException if any of the arguments is null
- * @deprecated Use {@link #findDataTreeSchemaNode(SchemaContext, QNameModule, YangLocationPath)} or
- * {@link #findDataTreeSchemaNode(SchemaContext, QNameModule, PathExpression)} instead.
- */
- // FIXME: This entire method is ill-defined, as the resolution process depends on where the XPath is defined --
- // notably RPCs, actions and notifications modify the data tree temporarily. See sections 6.4.1 and 9.9.2
- // of RFC7950.
- //
- // Most notably we need to understand whether the XPath is being resolved in the data tree, or as part of
- // a notification/action/RPC, as then the SchemaContext grows tentative nodes ... which could be addressed
- // via a derived SchemaContext (i.e. this class would have to have a
- //
- // SchemaContext notificationSchemaContext(SchemaContext delegate, NotificationDefinition notif)
- //
- // which would then be passed in to a method similar to this one. In static contexts, like MD-SAL codegen,
- // that feels like an overkill.
- @Deprecated
- public static SchemaNode findDataSchemaNode(final SchemaContext context, final Module module,
- final PathExpression nonCondXPath) {
- requireNonNull(context, "context");
- requireNonNull(module, "module");
-
- final String strXPath = nonCondXPath.getOriginalString();
- checkArgument(strXPath.indexOf('[') == -1, "Revision Aware XPath may not contain a condition");
- if (nonCondXPath.isAbsolute()) {
- return findTargetNode(context, xpathToQNamePath(context, module, strXPath));
- }
- return null;
- }
-
- @Beta
- public static SchemaNode findDataTreeSchemaNode(final SchemaContext ctx, final QNameModule localModule,
- final YangLocationPath absPath) {
- checkArgument(absPath.isAbsolute(), "Unsupported relative path %s", absPath);
- return findTargetNode(ctx, localModule, absPath);
- }
-
- @Beta
- public static SchemaNode findDataTreeSchemaNode(final SchemaContext ctx, final QNameModule localModule,
- final PathExpression absPath) {
- final Steps pathSteps = absPath.getSteps();
- if (pathSteps instanceof LocationPathSteps) {
- return findDataTreeSchemaNode(ctx, localModule, ((LocationPathSteps) pathSteps).getLocationPath());
- }
-
- // We would need a reference schema node and no, we do not want to use SchemaContext in its SchemaNode capacity
- checkArgument(!(pathSteps instanceof DerefSteps), "No reference node for steps %s", pathSteps);
-
- // We are missing proper API alignment, if this ever triggers
- throw new IllegalStateException("Unsupported path " + pathSteps);
- }
-
- /**
- * Method attempts to find DataSchemaNode inside of provided Schema Context
- * and Yang Module accordingly to Non-conditional relative Revision Aware
- * XPath. The specified Module MUST be present in Schema Context otherwise
- * the operation would fail and return <code>null</code>. <br>
- * The relative Revision Aware XPath MUST be specified WITHOUT the
- * conditional statement (i.e. without [cond]) in path, because in this
- * state the Schema Context is completely unaware of data state and will be
- * not able to properly resolve XPath. If the XPath contains condition the
- * method will return IllegalArgumentException. <br>
- * The Actual Schema Node MUST be specified correctly because from this
- * Schema Node will search starts. If the Actual Schema Node is not correct
- * the operation will simply fail, because it will be unable to find desired
- * DataSchemaNode. <br>
- * If the Revision Aware XPath doesn't have flag
- * <code>isAbsolute == false</code> the method will throw
- * IllegalArgumentException. <br>
- * If the relative Revision Aware XPath is correct and desired Data Schema
- * Node is present in Yang module or in depending module in Schema Context
- * the method will return specified Data Schema Node, otherwise the
- * operation will fail and method will return <code>null</code>.
- *
- * @param context
- * Schema Context
- * @param module
- * Yang Module
- * @param actualSchemaNode
- * Actual Schema Node
- * @param relativeXPath
- * Relative Non Conditional Revision Aware XPath
- * @return DataSchemaNode if is present in specified Schema Context for
- * given relative Revision Aware XPath, otherwise will return
- * <code>null</code>.
- * @throws NullPointerException if any argument is null
- */
- // FIXME: This entire method is ill-defined, as the resolution process depends on where the XPath is defined --
- // notably RPCs, actions and notifications modify the data tree temporarily. See sections 6.4.1 and 9.9.2
- // of RFC7950.
- //
- // Most notably we need to understand whether the XPath is being resolved in the data tree, or as part of
- // a notification/action/RPC, as then the SchemaContext grows tentative nodes ... which could be addressed
- // via a derived SchemaContext (i.e. this class would have to have a
- //
- // SchemaContext notificationSchemaContext(SchemaContext delegate, NotificationDefinition notif)
- //
- // which would then be passed in to a method similar to this one. In static contexts, like MD-SAL codegen,
- // that feels like an overkill.
- // FIXME: YANGTOOLS-1052: this is a static analysis util, move it to a dedicated class
- public static SchemaNode findDataSchemaNodeForRelativeXPath(final SchemaContext context, final Module module,
- final SchemaNode actualSchemaNode, final PathExpression relativeXPath) {
- checkState(!relativeXPath.isAbsolute(), "Revision Aware XPath MUST be relative i.e. MUST contains ../, "
- + "for non relative Revision Aware XPath use findDataSchemaNode method");
- return resolveRelativeXPath(context, module, removePredicatesFromXpath(relativeXPath.getOriginalString()),
- actualSchemaNode);
- }
-
- private static String removePredicatesFromXpath(final String xpath) {
- return GROUPS_PATTERN.matcher(xpath).replaceAll("");
- }
-
/**
* Returns parent Yang Module for specified Schema Context in which Schema
* Node is declared. If the Schema Node is not present in Schema Context the
return null;
}
- /**
- * Transforms string representation of XPath to Queue of QNames. The XPath
- * is split by "/" and for each part of XPath is assigned correct module in
- * Schema Path. <br>
- * If Schema Context, Parent Module or XPath string contains
- * <code>null</code> values, the method will throws IllegalArgumentException
- *
- * @param context
- * Schema Context
- * @param parentModule
- * Parent Module
- * @param xpath
- * XPath String
- * @return return a list of QName
- */
- private static List<QName> xpathToQNamePath(final SchemaContext context, final Module parentModule,
- final String xpath) {
- final List<QName> path = new ArrayList<>();
- for (final String pathComponent : SLASH_SPLITTER.split(xpath)) {
- path.add(stringPathPartToQName(context, parentModule, pathComponent));
- }
- return path;
- }
-
- /**
- * Transforms part of Prefixed Path as java String to QName. <br>
- * If the string contains module prefix separated by ":" (i.e.
- * mod:container) this module is provided from from Parent Module list of
- * imports. If the Prefixed module is present in Schema Context the QName
- * can be constructed. <br>
- * If the Prefixed Path Part does not contains prefix the Parent's Module
- * namespace is taken for construction of QName. <br>
- *
- * @param context Schema Context
- * @param parentModule Parent Module
- * @param prefixedPathPart Prefixed Path Part string
- * @return QName from prefixed Path Part String.
- * @throws NullPointerException if any arguments are null
- */
- private static QName stringPathPartToQName(final SchemaContext context, final Module parentModule,
- final String prefixedPathPart) {
- requireNonNull(context, "context");
-
- if (prefixedPathPart.indexOf(':') != -1) {
- final Iterator<String> prefixedName = COLON_SPLITTER.split(prefixedPathPart).iterator();
- final String modulePrefix = prefixedName.next();
-
- final Module module = resolveModuleForPrefix(context, parentModule, modulePrefix);
- checkArgument(module != null, "Failed to resolve xpath: no module found for prefix %s in module %s",
- modulePrefix, parentModule.getName());
-
- return QName.create(module.getQNameModule(), prefixedName.next());
- }
-
- return QName.create(parentModule.getQNameModule(), prefixedPathPart);
- }
-
- /**
- * Method will attempt to resolve and provide Module reference for specified module prefix. Each Yang module could
- * contains multiple imports which MUST be associated with corresponding module prefix. The method simply looks into
- * module imports and returns the module that is bounded with specified prefix. If the prefix is not present
- * in module or the prefixed module is not present in specified Schema Context, the method will return {@code null}.
- * <br>
- * If String prefix is the same as prefix of the specified Module the reference to this module is returned.<br>
- *
- * @param context Schema Context
- * @param module Yang Module
- * @param prefix Module Prefix
- * @return Module for given prefix in specified Schema Context if is present, otherwise returns <code>null</code>
- * @throws NullPointerException if any arguments are null
- */
- private static Module resolveModuleForPrefix(final SchemaContext context, final Module module,
- final String prefix) {
- requireNonNull(context, "context");
-
- if (prefix.equals(module.getPrefix())) {
- return module;
- }
-
- for (final ModuleImport mi : module.getImports()) {
- if (prefix.equals(mi.getPrefix())) {
- return context.findModule(mi.getModuleName(), mi.getRevision()).orElse(null);
- }
- }
- return null;
- }
-
- /**
- * Resolve a relative XPath into a set of QNames.
- *
- * @param context
- * Schema Context
- * @param module
- * Yang Module
- * @param pathStr
- * xPath of leafref
- * @param actualSchemaNode
- * actual schema node
- * @return target schema node
- * @throws IllegalArgumentException if any arguments are null
- */
- private static @Nullable SchemaNode resolveRelativeXPath(final SchemaContext context, final Module module,
- final String pathStr, final SchemaNode actualSchemaNode) {
- checkState(actualSchemaNode.getPath() != null, "Schema Path reference for Leafref cannot be NULL");
-
- return pathStr.startsWith("deref(") ? resolveDerefPath(context, module, actualSchemaNode, pathStr)
- : findTargetNode(context, resolveRelativePath(context, module, actualSchemaNode,
- doSplitXPath(pathStr)));
- }
-
- private static Iterable<QName> resolveRelativePath(final SchemaContext context, final Module module,
- final SchemaNode actualSchemaNode, final List<String> steps) {
- // Find out how many "parent" components there are and trim them
- final int colCount = normalizeXPath(steps);
- final List<String> xpaths = colCount == 0 ? steps : steps.subList(colCount, steps.size());
-
- final List<QName> walkablePath = createWalkablePath(actualSchemaNode.getPath().getPathFromRoot(),
- context, colCount);
-
- if (walkablePath.size() - colCount >= 0) {
- return Iterables.concat(Iterables.limit(walkablePath, walkablePath.size() - colCount),
- Iterables.transform(xpaths, input -> stringPathPartToQName(context, module, input)));
- }
- return Iterables.concat(walkablePath,
- Iterables.transform(xpaths, input -> stringPathPartToQName(context, module, input)));
- }
-
- /**
- * Return List of qNames that are walkable using xPath. When getting a path from schema node it will return path
- * with parents like CaseSchemaNode and ChoiceSchemaNode as well if they are parents of the node. We need to get
- * rid of these in order to find the node that xPath is pointing to. Also we can not remove any node beyond the
- * amount of "../" because we will not be able to find the correct schema node from schema context
- *
- * @param schemaNodePath list of qNames as a path to the leaf of type leafref
- * @param context create schema context
- * @param colCount amount of "../" in the xPath expression
- * @return list of QNames as a path where we should be able to find referenced node
- */
- private static List<QName> createWalkablePath(final Iterable<QName> schemaNodePath, final SchemaContext context,
- final int colCount) {
- final List<Integer> indexToRemove = new ArrayList<>();
- List<QName> schemaNodePathRet = Lists.newArrayList(schemaNodePath);
- for (int j = 0, i = schemaNodePathRet.size() - 1; i >= 0 && j != colCount; i--, j++) {
- final SchemaNode nodeIn = findTargetNode(context, schemaNodePathRet);
- if (nodeIn instanceof CaseSchemaNode || nodeIn instanceof ChoiceSchemaNode) {
- indexToRemove.add(i);
- j--;
- }
- schemaNodePathRet.remove(i);
- }
- schemaNodePathRet = Lists.newArrayList(schemaNodePath);
- for (int i : indexToRemove) {
- schemaNodePathRet.remove(i);
- }
- return schemaNodePathRet;
- }
-
- private static SchemaNode resolveDerefPath(final SchemaContext context, final Module module,
- final SchemaNode actualSchemaNode, final String xpath) {
- final int paren = xpath.indexOf(')', 6);
- checkArgument(paren != -1, "Cannot find matching parentheses in %s", xpath);
-
- final String derefArg = xpath.substring(6, paren).strip();
- // Look up the node which we need to reference
- final SchemaNode derefTarget = findTargetNode(context, resolveRelativePath(context, module, actualSchemaNode,
- doSplitXPath(derefArg)));
- checkArgument(derefTarget != null, "Cannot find deref(%s) target node %s in context of %s", derefArg,
- actualSchemaNode);
- checkArgument(derefTarget instanceof TypedDataSchemaNode, "deref(%s) resolved to non-typed %s", derefArg,
- derefTarget);
-
- // We have a deref() target, decide what to do about it
- final TypeDefinition<?> targetType = ((TypedDataSchemaNode) derefTarget).getType();
- if (targetType instanceof InstanceIdentifierTypeDefinition) {
- // Static inference breaks down, we cannot determine where this points to
- // FIXME: dedicated exception, users can recover from it, derive from IAE
- throw new UnsupportedOperationException("Cannot infer instance-identifier reference " + targetType);
- }
-
- // deref() is define only for instance-identifier and leafref types, handle the latter
- checkArgument(targetType instanceof LeafrefTypeDefinition, "Illegal target type %s", targetType);
-
- final PathExpression targetPath = ((LeafrefTypeDefinition) targetType).getPathStatement();
- LOG.debug("Derefencing path {}", targetPath);
-
- final SchemaNode deref = targetPath.isAbsolute()
- ? findTargetNode(context, actualSchemaNode.getQName().getModule(),
- ((LocationPathSteps) targetPath.getSteps()).getLocationPath())
- : findDataSchemaNodeForRelativeXPath(context, module, actualSchemaNode, targetPath);
- if (deref == null) {
- LOG.debug("Path {} could not be derefenced", targetPath);
- return null;
- }
-
- checkArgument(deref instanceof LeafSchemaNode, "Unexpected %s reference in %s", deref, targetPath);
-
- final List<String> qnames = doSplitXPath(xpath.substring(paren + 1).stripLeading());
- return findTargetNode(context, resolveRelativePath(context, module, deref, qnames));
- }
-
- private static @Nullable SchemaNode findTargetNode(final SchemaContext context, final QNameModule localNamespace,
- final YangLocationPath path) {
- final Deque<QName> ret = new ArrayDeque<>();
- for (Step step : path.getSteps()) {
- if (step instanceof AxisStep) {
- // We only support parent axis steps
- final YangXPathAxis axis = ((AxisStep) step).getAxis();
- checkState(axis == YangXPathAxis.PARENT, "Unexpected axis %s", axis);
- ret.removeLast();
- continue;
- }
-
- // This has to be a QNameStep
- checkState(step instanceof QNameStep, "Unhandled step %s in %s", step, path);
- ret.addLast(resolve(((QNameStep) step).getQName(), localNamespace));
- }
-
- return findTargetNode(context, ret);
- }
-
- private static @Nullable SchemaNode findTargetNode(final SchemaContext context, final Iterable<QName> qnamePath) {
- // We do not have enough information about resolution context, hence cannot account for actions, RPCs
- // and notifications. We therefore attempt to make a best estimate, but this can still fail.
- final Optional<DataSchemaNode> pureData = context.findDataTreeChild(qnamePath);
- return pureData.isPresent() ? pureData.get() : findNodeInSchemaContext(context, qnamePath);
- }
-
- private static QName resolve(final AbstractQName toResolve, final QNameModule localNamespace) {
- if (toResolve instanceof QName) {
- return (QName) toResolve;
- } else if (toResolve instanceof UnqualifiedQName) {
- return ((UnqualifiedQName) toResolve).bindTo(localNamespace);
- } else {
- throw new IllegalStateException("Unhandled step " + toResolve);
- }
- }
-
- @VisibleForTesting
- static int normalizeXPath(final List<String> xpath) {
- LOG.trace("Normalize {}", xpath);
-
- // We need to make multiple passes here, as the leading XPaths as we can have "../abc/../../def", which really
- // is "../../def"
- while (true) {
- // Next up: count leading ".." components
- int leadingParents = 0;
- while (true) {
- if (leadingParents == xpath.size()) {
- return leadingParents;
- }
- if (!"..".equals(xpath.get(leadingParents))) {
- break;
- }
-
- ++leadingParents;
- }
-
- // Now let's see if there there is a '..' in the rest
- final int dots = findDots(xpath, leadingParents + 1);
- if (dots == -1) {
- return leadingParents;
- }
-
- xpath.remove(dots - 1);
- xpath.remove(dots - 1);
- LOG.trace("Next iteration {}", xpath);
- }
- }
-
- private static int findDots(final List<String> xpath, final int startIndex) {
- for (int i = startIndex; i < xpath.size(); ++i) {
- if ("..".equals(xpath.get(i))) {
- return i;
- }
- }
-
- return -1;
- }
-
- private static List<String> doSplitXPath(final String xpath) {
- final List<String> ret = new ArrayList<>();
- for (String str : SLASH_SPLITTER.split(xpath)) {
- ret.add(str);
- }
- return ret;
- }
-
- /**
- * Extracts the base type of node on which schema node points to. If target node is again of type
- * LeafrefTypeDefinition, methods will be call recursively until it reach concrete type definition.
- *
- * @param typeDefinition
- * type of node which will be extracted
- * @param schemaContext
- * Schema Context
- * @param schema
- * Schema Node
- * @return recursively found type definition this leafref is pointing to or null if the xpath is incorrect (null
- * is there to preserve backwards compatibility)
- */
- public static TypeDefinition<?> getBaseTypeForLeafRef(final LeafrefTypeDefinition typeDefinition,
- final SchemaContext schemaContext, final SchemaNode schema) {
- final PathExpression pathStatement = typeDefinition.getPathStatement();
- final String pathStr = stripConditionsFromXPathString(pathStatement);
-
- final DataSchemaNode dataSchemaNode;
- if (pathStatement.isAbsolute()) {
- SchemaNode baseSchema = schema;
- while (baseSchema instanceof DerivableSchemaNode) {
- final Optional<? extends SchemaNode> basePotential = ((DerivableSchemaNode) baseSchema).getOriginal();
- if (basePotential.isPresent()) {
- baseSchema = basePotential.get();
- } else {
- break;
- }
- }
-
- final Module parentModule = findParentModuleOfReferencingType(schemaContext, baseSchema);
- dataSchemaNode = (DataSchemaNode) findTargetNode(schemaContext,
- xpathToQNamePath(schemaContext, parentModule, pathStr));
- } else {
- Module parentModule = findParentModule(schemaContext, schema);
- dataSchemaNode = (DataSchemaNode) resolveRelativeXPath(schemaContext, parentModule, pathStr, schema);
- }
-
- // FIXME this is just to preserve backwards compatibility since yangtools do not mind wrong leafref xpaths
- // and current expected behaviour for such cases is to just use pure string
- // This should throw an exception about incorrect XPath in leafref
- if (dataSchemaNode == null) {
- return null;
- }
-
- final TypeDefinition<?> targetTypeDefinition = typeDefinition(dataSchemaNode);
-
- if (targetTypeDefinition instanceof LeafrefTypeDefinition) {
- return getBaseTypeForLeafRef((LeafrefTypeDefinition) targetTypeDefinition, schemaContext, dataSchemaNode);
- }
-
- return targetTypeDefinition;
- }
-
- /**
- * Returns base type for {@code typeDefinition} which belongs to module specified via {@code qname}. This handle
- * the case when leafref type isn't specified as type substatement of leaf or leaf-list but is defined in other
- * module as typedef which is then imported to referenced module.
- *
- * <p>
- * Because {@code typeDefinition} is definied via typedef statement, only absolute path is meaningful.
- */
- public static TypeDefinition<?> getBaseTypeForLeafRef(final LeafrefTypeDefinition typeDefinition,
- final SchemaContext schemaContext, final QName qname) {
- final PathExpression pathStatement = typeDefinition.getPathStatement();
- if (!pathStatement.isAbsolute()) {
- return null;
- }
-
- final Optional<Module> parentModule = schemaContext.findModule(qname.getModule());
- checkArgument(parentModule.isPresent(), "Failed to find parent module for %s", qname);
-
- final DataSchemaNode dataSchemaNode = (DataSchemaNode) findTargetNode(schemaContext,
- xpathToQNamePath(schemaContext, parentModule.get(), stripConditionsFromXPathString(pathStatement)));
- final TypeDefinition<?> targetTypeDefinition = typeDefinition(dataSchemaNode);
- if (targetTypeDefinition instanceof LeafrefTypeDefinition) {
- return getBaseTypeForLeafRef((LeafrefTypeDefinition) targetTypeDefinition, schemaContext, dataSchemaNode);
- }
-
- return targetTypeDefinition;
- }
-
- private static Module findParentModuleOfReferencingType(final SchemaContext schemaContext,
- final SchemaNode schemaNode) {
- checkArgument(schemaContext != null, "Schema Context reference cannot be NULL!");
- checkArgument(schemaNode instanceof TypedDataSchemaNode, "Unsupported node %s", schemaNode);
-
- TypeDefinition<?> nodeType = ((TypedDataSchemaNode) schemaNode).getType();
- if (nodeType.getBaseType() != null) {
- while (nodeType.getBaseType() != null) {
- nodeType = nodeType.getBaseType();
- }
-
- return schemaContext.findModule(nodeType.getQName().getModule()).orElse(null);
- }
-
- return findParentModule(schemaContext, schemaNode);
- }
-
- private static final Pattern STRIP_PATTERN = Pattern.compile("\\[[^\\[\\]]*\\]");
-
- /**
- * Removes conditions from xPath pointed to target node.
- *
- * @param pathStatement
- * xPath to target node
- * @return string representation of xPath without conditions
- */
- @VisibleForTesting
- static String stripConditionsFromXPathString(final PathExpression pathStatement) {
- return STRIP_PATTERN.matcher(pathStatement.getOriginalString()).replaceAll("");
- }
-
- /**
- * Gets the base type of DataSchemaNode value.
- *
- * @param node
- * a node representing DataSchemaNode
- * @return concrete type definition of node value
- */
- private static TypeDefinition<?> typeDefinition(final DataSchemaNode node) {
- checkArgument(node instanceof TypedDataSchemaNode, "Unhandled parameter type %s", node);
-
- TypeDefinition<?> current = ((TypedDataSchemaNode) node).getType();
- // TODO: don't we have a utility for this?
- TypeDefinition<?> base = current.getBaseType();
- while (base != null) {
- current = base;
- base = current.getBaseType();
- }
- return current;
- }
}
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider;
import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.PathExpression.DerefSteps;
+import org.opendaylight.yangtools.yang.model.api.PathExpression.LocationPathSteps;
+import org.opendaylight.yangtools.yang.model.api.PathExpression.Steps;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.api.SchemaTreeInference;
+import org.opendaylight.yangtools.yang.model.api.TypeAware;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.spi.AbstractEffectiveStatementInference;
import org.opendaylight.yangtools.yang.model.spi.DefaultSchemaTreeInference;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
/**
* A state tracking utility for walking {@link EffectiveModelContext}'s contents along schema/grouping namespaces. This
* This class is designed for single-threaded uses and does not make any guarantees around concurrent access.
*/
@Beta
-public final class SchemaInferenceStack implements Mutable, EffectiveModelContextProvider {
+public final class SchemaInferenceStack implements Mutable, EffectiveModelContextProvider, LeafrefResolver {
/**
* Semantic binding of {@link EffectiveStatementInference} produced by {@link SchemaInferenceStack}. Sequence of
* {@link #statementPath()} is implementation-specific.
return (DataTreeEffectiveStatement<?>) child;
}
+
+ @Override
+ public TypeDefinition<?> resolveLeafref(final LeafrefTypeDefinition type) {
+ final SchemaInferenceStack tmp = copy();
+
+ LeafrefTypeDefinition current = type;
+ while (true) {
+ final EffectiveStatement<?, ?> resolved = tmp.resolvePathExpression(current.getPathStatement());
+ checkState(resolved instanceof TypeAware, "Unexpected result %s resultion of %s", resolved, type);
+ final TypeDefinition<?> result = ((TypedDataSchemaNode) resolved).getType();
+ if (result instanceof LeafrefTypeDefinition) {
+ checkArgument(result != type, "Resolution of %s loops back onto itself via %s", type, current);
+ current = (LeafrefTypeDefinition) result;
+ } else {
+ return result;
+ }
+ }
+ }
+
+ /**
+ * Resolve a {@link PathExpression}.
+ *
+ * <p>
+ * Note if this method throws, this stack may be in an undefined state.
+ *
+ * @param path Requested path
+ * @return Resolved schema tree child
+ * @throws NullPointerException if {@code path} is null
+ * @throws IllegalArgumentException if the target node cannot be found
+ * @throws VerifyException if path expression is invalid
+ */
+ public @NonNull EffectiveStatement<?, ?> resolvePathExpression(final PathExpression path) {
+ final Steps steps = path.getSteps();
+ if (steps instanceof LocationPathSteps) {
+ return resolveLocationPath(((LocationPathSteps) steps).getLocationPath());
+ } else if (steps instanceof DerefSteps) {
+ return resolveDeref((DerefSteps) steps);
+ } else {
+ throw new VerifyException("Unhandled steps " + steps);
+ }
+ }
+
+ private @NonNull EffectiveStatement<?, ?> resolveDeref(final DerefSteps deref) {
+ final EffectiveStatement<?, ?> leafRefSchemaNode = currentStatement();
+ final YangLocationPath.Relative derefArg = deref.getDerefArgument();
+ final EffectiveStatement<?, ?> derefStmt = resolveLocationPath(derefArg);
+ checkArgument(derefStmt != null, "Cannot find deref(%s) target node %s in context of %s",
+ derefArg, leafRefSchemaNode);
+ checkArgument(derefStmt instanceof TypedDataSchemaNode, "deref(%s) resolved to non-typed %s", derefArg,
+ derefStmt);
+
+ // We have a deref() target, decide what to do about it
+ final TypeDefinition<?> targetType = ((TypedDataSchemaNode) derefStmt).getType();
+ if (targetType instanceof InstanceIdentifierTypeDefinition) {
+ // Static inference breaks down, we cannot determine where this points to
+ // FIXME: dedicated exception, users can recover from it, derive from IAE
+ throw new UnsupportedOperationException("Cannot infer instance-identifier reference " + targetType);
+ }
+
+ // deref() is defined only for instance-identifier and leafref types, handle the latter
+ checkArgument(targetType instanceof LeafrefTypeDefinition, "Illegal target type %s", targetType);
+
+ final PathExpression dereferencedLeafRefPath = ((LeafrefTypeDefinition) targetType).getPathStatement();
+ EffectiveStatement<?, ?> derefNode = resolvePathExpression(dereferencedLeafRefPath);
+ checkArgument(derefStmt != null, "Can not find target node of dereferenced node %s", derefStmt);
+ checkArgument(derefNode instanceof LeafSchemaNode, "Unexpected %s reference in %s", deref,
+ dereferencedLeafRefPath);
+ return resolveLocationPath(deref.getRelativePath());
+ }
+
+ private @NonNull EffectiveStatement<?, ?> resolveLocationPath(final YangLocationPath path) {
+ // get the default namespace before we clear and loose our deque
+ final QNameModule defaultNamespace = deque.isEmpty() ? null : ((QName) deque.peek().argument()).getModule();
+ if (path.isAbsolute()) {
+ clear();
+ }
+
+ EffectiveStatement<?, ?> current = null;
+ for (Step step : path.getSteps()) {
+ final YangXPathAxis axis = step.getAxis();
+ switch (axis) {
+ case PARENT:
+ verify(step instanceof AxisStep, "Unexpected parent step %s", step);
+ try {
+ current = exitToDataTree();
+ } catch (IllegalStateException e) {
+ throw new IllegalArgumentException("Illegal parent access in " + path, e);
+ }
+ break;
+ case CHILD:
+ verify(step instanceof QNameStep, "Unexpected child step %s", step);
+ current = enterChild((QNameStep) step, defaultNamespace);
+ break;
+ default:
+ throw new VerifyException("Unexpected step " + step);
+ }
+ }
+
+ return verifyNotNull(current);
+ }
+
+ private @NonNull EffectiveStatement<?, ?> enterChild(final QNameStep step, final QNameModule defaultNamespace) {
+ final AbstractQName toResolve = step.getQName();
+ final QName qname;
+ if (toResolve instanceof QName) {
+ qname = (QName) toResolve;
+ } else if (toResolve instanceof UnqualifiedQName) {
+ checkArgument(defaultNamespace != null, "Can not find target module of step %s", step);
+ qname = ((UnqualifiedQName) toResolve).bindTo(defaultNamespace);
+ } else {
+ throw new VerifyException("Unexpected child step QName " + toResolve);
+ }
+ return enterDataTree(qname);
+ }
+
/**
* Return an {@link Inference} equivalent of current state.
*
+++ /dev/null
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.yangtools.yang.model.util;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-import org.opendaylight.yangtools.yang.model.api.PathExpression;
-
-public class Bug4969Test {
- @Test
- public void testRegex() {
- PathExpression xpath = new PathExpressionImpl(
- "nd:network[nd:network-id=current()/../network-ref]/nd:node[nd:node-id=current()/../node-ref]"
- + "/termination-point/tp-id", true);
- assertEquals("nd:network/nd:node/termination-point/tp-id",
- SchemaContextUtil.stripConditionsFromXPathString(xpath));
- }
-}
*/
package org.opendaylight.yangtools.yang.model.util;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.isA;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
public void testGrpOuterId() {
final LeafSchemaNode leaf = (LeafSchemaNode) grp.findDataChildByName(QName.create(FOO, "outer-id")).get();
// Cannot be found as the reference goes outside of the grouping
- assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
- ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ final SchemaInferenceStack stack = SchemaInferenceStack.of(context);
+ stack.enterGrouping(grp.getQName());
+ stack.enterSchemaTree(QName.create(FOO, "outer-id"));
+ assertThrowsInvalidPath(stack, leaf);
}
@Test
public void testFooOuterId() {
final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName(QName.create(FOO, "outer-id")).get();
- final SchemaNode found = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
- ((LeafrefTypeDefinition) leaf.getType()).getPathStatement());
+ final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context, foo.getQName(), bar.getQName());
+ stack.enterSchemaTree(QName.create(FOO, "outer-id"));
+ final SchemaNode found = (SchemaNode) stack.resolvePathExpression(((LeafrefTypeDefinition) leaf.getType())
+ .getPathStatement());
assertThat(found, isA(LeafSchemaNode.class));
- assertEquals(SchemaPath.create(true, FOO, QName.create(FOO, "id")), found.getPath());
+ assertEquals(QName.create(FOO, "id"), found.getQName());
}
@Test
public void testGrpOuterIndirectProp() {
final LeafSchemaNode leaf = (LeafSchemaNode) grp.findDataChildByName(
QName.create(FOO, "outer-indirect-prop")).get();
+ final SchemaInferenceStack stack = SchemaInferenceStack.of(context);
+ stack.enterGrouping(grp.getQName());
+ stack.enterSchemaTree(QName.create(FOO, "outer-indirect-prop"));
// Cannot resolve deref outer-id
- assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
- ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ assertThrowsInvalidPath(stack, leaf);
}
@Test
public void testFooOuterIndirectProp() {
final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName(
QName.create(FOO, "outer-indirect-prop")).get();
- final SchemaNode found = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
- ((LeafrefTypeDefinition) leaf.getType()).getPathStatement());
+ final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context, foo.getQName(), bar.getQName());
+ stack.enterSchemaTree(QName.create(FOO, "outer-indirect-prop"));
+ final SchemaNode found = (SchemaNode) stack.resolvePathExpression(((LeafrefTypeDefinition) leaf.getType())
+ .getPathStatement());
assertThat(found, isA(LeafSchemaNode.class));
assertEquals(QName.create(FOO, "prop"), found.getQName());
@Test
public void testGrpIndirect() {
final LeafSchemaNode leaf = (LeafSchemaNode) grp.findDataChildByName(QName.create(FOO, "indirect")).get();
- final SchemaNode found = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
- ((LeafrefTypeDefinition) leaf.getType()).getPathStatement());
+ final SchemaInferenceStack stack = SchemaInferenceStack.of(context);
+ stack.enterGrouping(grp.getQName());
+ stack.enterSchemaTree(QName.create(FOO, "indirect"));
+ final SchemaNode found = (SchemaNode) stack.resolvePathExpression(((LeafrefTypeDefinition) leaf.getType())
+ .getPathStatement());
assertThat(found, isA(LeafSchemaNode.class));
assertEquals(QName.create(FOO, "prop"), found.getQName());
@Test
public void testFooIndirect() {
final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName(QName.create(FOO, "indirect")).get();
- final SchemaNode found = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
- ((LeafrefTypeDefinition) leaf.getType()).getPathStatement());
+ final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context, foo.getQName(), bar.getQName());
+ stack.enterSchemaTree(QName.create(FOO, "indirect"));
+ final SchemaNode found = (SchemaNode) stack.resolvePathExpression(((LeafrefTypeDefinition) leaf.getType())
+ .getPathStatement());
assertThat(found, isA(LeafSchemaNode.class));
assertEquals(QName.create(FOO, "prop"), found.getQName());
public void testGrpDerefNonExistent() {
final LeafSchemaNode leaf = (LeafSchemaNode) grp.findDataChildByName(
QName.create(FOO, "deref-non-existent")).get();
- assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
- ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ final SchemaInferenceStack stack = SchemaInferenceStack.of(context);
+ stack.enterGrouping(grp.getQName());
+ stack.enterSchemaTree(QName.create(FOO, "deref-non-existent"));
+ assertThrowsMissingXyzzy(stack, leaf);
}
@Test
public void testFooDerefNonExistent() {
final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName(
QName.create(FOO, "deref-non-existent")).get();
- assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
- ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context, foo.getQName(), bar.getQName());
+ stack.enterSchemaTree(QName.create(FOO, "deref-non-existent"));
+ assertThrowsMissingXyzzy(stack, leaf);
}
@Test
public void testGrpNonExistentDeref() {
final LeafSchemaNode leaf = (LeafSchemaNode) grp.findDataChildByName(
QName.create(FOO, "non-existent-deref")).get();
- assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
- ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ final SchemaInferenceStack stack = SchemaInferenceStack.of(context);
+ stack.enterGrouping(grp.getQName());
+ stack.enterSchemaTree(QName.create(FOO, "non-existent-deref"));
+ assertThrowsMissingXyzzy(stack, leaf);
}
@Test
public void testFooNonExistentDeref() {
final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName(
QName.create(FOO, "non-existent-deref")).get();
- assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
- ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context, foo.getQName(), bar.getQName());
+ stack.enterSchemaTree(QName.create(FOO, "non-existent-deref"));
+ assertThrowsMissingXyzzy(stack, leaf);
}
@Test
public void testNonExistentRelativeXpath() {
final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName(
QName.create(FOO, "indirect-with-current")).get();
- assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
- ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context,
+ foo.getQName(), bar.getQName(), QName.create(FOO, "indirect-with-current"));
+ assertThrowsMissingChild(stack, leaf, "(leafrefs)n");
+ }
+
+ private static void assertThrowsInvalidPath(final SchemaInferenceStack stack, final LeafSchemaNode leaf) {
+ final IllegalArgumentException ex = assertThrowsIAE(stack, leaf);
+ assertThat(ex.getMessage(), startsWith("Illegal parent access in "));
+ final Throwable cause = ex.getCause();
+ assertThat(cause, instanceOf(IllegalStateException.class));
+ assertEquals("Unexpected current GroupingEffectiveStatementImpl[qname=(leafrefs)grp]", cause.getMessage());
+ }
+
+ private static void assertThrowsMissingXyzzy(final SchemaInferenceStack stack, final LeafSchemaNode leaf) {
+ assertThrowsMissingChild(stack, leaf, "(leafrefs)xyzzy");
+ }
+
+ private static void assertThrowsMissingChild(final SchemaInferenceStack stack, final LeafSchemaNode leaf,
+ final String childName) {
+ assertEquals("Data tree child " + childName + " not present", assertThrowsIAE(stack, leaf).getMessage());
+ }
+
+ private static IllegalArgumentException assertThrowsIAE(final SchemaInferenceStack stack,
+ final LeafSchemaNode leaf) {
+ return assertThrows(IllegalArgumentException.class,
+ () -> stack.resolvePathExpression(((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
}
}
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.PathExpression.LocationPathSteps;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
public class SchemaContextUtilIntegrationTest {
- private static SchemaContext context;
+ private static EffectiveModelContext context;
private static Module myModule;
@BeforeClass
final Module importedModule = context.findModule(XMLNamespace.of("uri:imported-module"),
Revision.of("2014-10-07")).get();
- final SchemaNode testNode = ((ContainerSchemaNode) importedModule.getDataChildByName(QName.create(
- importedModule.getQNameModule(), "my-imported-container"))).getDataChildByName(QName.create(
- importedModule.getQNameModule(), "my-imported-leaf"));
+ final QName myImportedContainer = QName.create(importedModule.getQNameModule(), "my-imported-container");
+ final QName myImportedLeaf = QName.create(importedModule.getQNameModule(), "my-imported-leaf");
- final PathExpression xpath = new PathExpressionImpl("imp:my-imported-container/imp:my-imported-leaf", true);
+ final SchemaNode testNode = ((ContainerSchemaNode) importedModule.getDataChildByName(myImportedContainer))
+ .getDataChildByName(myImportedLeaf);
- final SchemaNode foundNode = SchemaContextUtil.findDataSchemaNode(context, myModule, xpath);
- assertNotNull(foundNode);
- assertEquals(testNode, foundNode);
+ final PathExpression expr = mock(PathExpression.class);
+ doReturn(true).when(expr).isAbsolute();
+ doReturn(new LocationPathSteps(YangLocationPath.absolute(
+ YangXPathAxis.CHILD.asStep(myImportedContainer), YangXPathAxis.CHILD.asStep(myImportedLeaf))))
+ .when(expr).getSteps();
+
+ assertEquals(testNode, SchemaInferenceStack.of(context).resolvePathExpression(expr));
}
@Test
public void findDataSchemaNodeTest2() {
- final GroupingDefinition grouping = getGroupingByName(myModule, "my-grouping");
- final SchemaNode testNode = grouping.getDataChildByName(QName.create(myModule.getQNameModule(),
- "my-leaf-in-gouping2"));
-
- final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", true);
+ final QName myLeafInGrouping2 = QName.create(myModule.getQNameModule(), "my-leaf-in-gouping2");
+ final PathExpression expr = mock(PathExpression.class);
+ doReturn(true).when(expr).isAbsolute();
+ doReturn(new LocationPathSteps(YangLocationPath.relative(YangXPathAxis.CHILD.asStep(myLeafInGrouping2))))
+ .when(expr).getSteps();
- final SchemaNode foundNode = SchemaContextUtil.findDataSchemaNode(context, myModule, xpath);
-
- assertNotNull(foundNode);
- assertEquals(testNode, foundNode);
+ final GroupingDefinition grouping = getGroupingByName(myModule, "my-grouping");
+ final SchemaInferenceStack stack = SchemaInferenceStack.of(context);
+ assertSame(grouping, stack.enterGrouping(grouping.getQName()));
+ assertEquals(grouping.getDataChildByName(myLeafInGrouping2), stack.resolvePathExpression(expr));
}
@Test
*/
package org.opendaylight.yangtools.yang.model.util;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
import java.util.Optional;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.common.XMLNamespace;
import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.PathExpression;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.opendaylight.yangtools.yang.model.ri.type.BaseTypes;
@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class SchemaContextUtilTest {
public SchemaNode schemaNode;
@Test
- @Ignore
- // FIXME: YANGTOOLS-1127: rewrite this test in terms of a real PathExpression
public void testFindDummyData() {
doReturn(Optional.empty()).when(mockSchemaContext).findModule(any(QNameModule.class));
- doReturn(Optional.empty()).when(mockSchemaContext).findDataTreeChild(any(Iterable.class));
-
- doReturn("test").when(mockModule).getName();
- doReturn("test").when(mockModule).getPrefix();
- doReturn(QNameModule.create(NAMESPACE)).when(mockModule).getQNameModule();
QName qname = QName.create("namespace", "localname");
SchemaPath schemaPath = SchemaPath.create(Collections.singletonList(qname), true);
assertNull("Should be null. Module TestQName not found",
SchemaContextUtil.findDataSchemaNode(mockSchemaContext, schemaPath));
- PathExpression xpath = new PathExpressionImpl("/test:bookstore/test:book/test:title", true);
- assertNull("Should be null. Module bookstore not found",
- SchemaContextUtil.findDataSchemaNode(mockSchemaContext, mockModule, xpath));
-
-
- final PathExpression xPath = new PathExpressionImpl("/bookstore/book/title", true);
- assertEquals("Should be null. Module bookstore not found", null,
- SchemaContextUtil.findDataSchemaNode(mockSchemaContext, mockModule, xPath));
-
- SchemaNode int32node = BaseTypes.int32Type();
- PathExpression xpathRelative = new PathExpressionImpl("../prefix", false);
- assertNull("Should be null, Module prefix not found",
- SchemaContextUtil.findDataSchemaNodeForRelativeXPath(
- mockSchemaContext, mockModule, int32node, xpathRelative));
-
assertNull("Should be null. Module TestQName not found",
SchemaContextUtil.findNodeInSchemaContext(mockSchemaContext, Collections.singleton(qname)));
-
- assertNull("Should be null.", SchemaContextUtil.findParentModule(mockSchemaContext, int32node));
- }
-
- @Test
- public void findDataSchemaNodeFromXPathIllegalArgumentTest() {
- assertThrows(NullPointerException.class,
- () -> SchemaContextUtil.findDataSchemaNode(mock(SchemaContext.class), mock(Module.class), null));
- }
-
- @Test
- public void findDataSchemaNodeFromXPathIllegalArgumentTest2() {
- final SchemaContext mockContext = mock(SchemaContext.class);
- final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", true);
-
- assertThrows(NullPointerException.class, () -> SchemaContextUtil.findDataSchemaNode(mockContext, null, xpath));
- }
-
- @Test
- public void findDataSchemaNodeFromXPathIllegalArgumentTest3() {
- final Module module = mock(Module.class);
- final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", true);
-
- assertThrows(NullPointerException.class, () -> SchemaContextUtil.findDataSchemaNode(null, module, xpath));
- }
-
- @Test
- public void findDataSchemaNodeFromXPathIllegalArgumentTest4() {
- final SchemaContext mockContext = mock(SchemaContext.class);
- final Module module = mock(Module.class);
- final PathExpression xpath = new PathExpressionImpl("my:my-grouping[@con='NULL']/my:my-leaf-in-gouping2", true);
-
- assertThrows(IllegalArgumentException.class,
- () -> SchemaContextUtil.findDataSchemaNode(mockContext, module, xpath));
}
@Test
assertThrows(NullPointerException.class, () -> SchemaContextUtil.findDataSchemaNode(null,
SchemaPath.create(true, QName.create(XMLNamespace.of("uri:my-module"), Revision.of("2014-10-07"), "foo"))));
}
-
- @Test
- public void findDataSchemaNodeFromXPathNullTest2() {
- final SchemaContext mockContext = mock(SchemaContext.class);
- final Module module = mock(Module.class);
- final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", false);
-
- assertNull(SchemaContextUtil.findDataSchemaNode(mockContext, module, xpath));
- }
-
- @Test
- public void testNormalizeXPath() {
- assertNormalizedPath(0, ImmutableList.of(""), "");
- assertNormalizedPath(0, ImmutableList.of("a"), "a");
- assertNormalizedPath(0, ImmutableList.of("a", "b"), "a b");
- assertNormalizedPath(1, ImmutableList.of("..", "b"), ".. b");
- assertNormalizedPath(0, ImmutableList.of(), "a ..");
- assertNormalizedPath(0, ImmutableList.of("b"), "a .. b");
- assertNormalizedPath(2, ImmutableList.of("..", "..", "a", "c"), ".. .. a b .. c");
- assertNormalizedPath(3, ImmutableList.of("..", "..", "..", "b"), ".. .. a .. .. b");
- }
-
- private static void assertNormalizedPath(final int expectedLead, final List<String> expectedList,
- final String input) {
- final List<String> list = new ArrayList<>(SPACE_SPLITTER.splitToList(input));
- assertEquals(expectedLead, SchemaContextUtil.normalizeXPath(list));
- assertEquals(expectedList, list);
- }
}
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
final TypeDefinition<?> typeNodeType = secondaryType.getType();
assertThat(typeNodeType, isA(LeafrefTypeDefinition.class));
- final SchemaNode found = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, secondaryType,
- ((LeafrefTypeDefinition) typeNodeType).getPathStatement());
+ final SchemaInferenceStack stack = SchemaInferenceStack.of(context);
+ stack.enterGrouping(QName.create(module.getQNameModule(), "grp"));
+ stack.enterSchemaTree(QName.create(module.getQNameModule(), "secondary"));
+ stack.enterSchemaTree(secondaryType.getQName());
+ final EffectiveStatement<?, ?> found = stack.resolvePathExpression(((LeafrefTypeDefinition) typeNodeType)
+ .getPathStatement());
assertSame(primaryType, found);
}
}
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.isA;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.XMLNamespace;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.PathExpression;
import org.opendaylight.yangtools.yang.model.api.PathExpression.LocationPathSteps;
import org.opendaylight.yangtools.yang.model.api.PathExpression.Steps;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
}
@Test
- public void testFindDataSchemaNode() {
- final SchemaNode found = SchemaContextUtil.findDataTreeSchemaNode(context, CONT.getModule(), path);
- assertThat(found, isA(LeafSchemaNode.class));
- assertEquals(SchemaPath.create(true, QName.create("imported", "root"), QName.create("imported", "leaf1")),
- found.getPath());
+ public void testFindDataSchemaNodeAbsolutePathImportedModule() {
+ final EffectiveStatement<?, ?> foundStmt = SchemaInferenceStack.ofDataTreePath(context, CONT, LEAF1)
+ .resolvePathExpression(path);
+ assertThat(foundStmt, isA(LeafSchemaNode.class));
+ assertEquals(QName.create(XMLNamespace.of("imported"), "leaf1"), ((LeafSchemaNode) foundStmt).getQName());
+
+ // since this is absolute path with prefixes stack should be able to resolve it from any state
+ final EffectiveStatement<?, ?> foundStmtSecond = SchemaInferenceStack.of(context).resolvePathExpression(path);
+ assertSame(foundStmt, foundStmtSecond);
}
}
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.PathExpression;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
final EffectiveModelContext context = YangParserTestUtils.parseYangResource("/yt1100.yang");
final Module module = context.findModule("yt1100").orElseThrow();
final QNameModule qnm = module.getQNameModule();
- final DataSchemaNode leaf = module.findDataTreeChild(
- QName.create(qnm, "foo"), QName.create(qnm, "scheduler-node"), QName.create(qnm, "child-scheduler-nodes"),
- QName.create(qnm, "name")).orElseThrow();
+ final QName foo = QName.create(qnm, "foo");
+ final QName schedulerNode = QName.create(qnm, "scheduler-node");
+ final QName childSchedulerNodes = QName.create(qnm, "child-scheduler-nodes");
+ final QName name = QName.create(qnm, "name");
+ final DataSchemaNode leaf = module.findDataTreeChild(foo, schedulerNode, childSchedulerNodes, name)
+ .orElseThrow();
assertThat(leaf, instanceOf(LeafSchemaNode.class));
final TypeDefinition<?> type = ((LeafSchemaNode) leaf).getType();
assertThat(type, instanceOf(LeafrefTypeDefinition.class));
final PathExpression leafref = ((LeafrefTypeDefinition) type).getPathStatement();
- final SchemaNode ref = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf, leafref);
- assertThat(ref, instanceOf(LeafSchemaNode.class));
- final LeafSchemaNode targetLeaf = (LeafSchemaNode) ref;
+ final EffectiveStatement<?, ?> resolvedLeafRef = SchemaInferenceStack.ofDataTreePath(
+ context, foo, schedulerNode,childSchedulerNodes, name).resolvePathExpression(leafref);
+ assertThat(resolvedLeafRef, instanceOf(LeafSchemaNode.class));
+ final LeafSchemaNode targetLeaf = (LeafSchemaNode) resolvedLeafRef;
assertEquals(QName.create(qnm, "name"), targetLeaf.getQName());
assertThat(targetLeaf.getType(), instanceOf(StringTypeDefinition.class));
}
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
private static final String REV = "2016-03-01";
@Test
- public void test() throws Exception {
- SchemaContext context = YangParserTestUtils.parseYangResource("/yt588.yang");
+ public void test() {
+ EffectiveModelContext context = YangParserTestUtils.parseYangResource("/yt588.yang");
QName root = QName.create(NS, REV, "root");
QName leafRef2 = QName.create(NS, REV, "leaf-ref-2");
assertThat(leafRefNode.getType(), isA(LeafrefTypeDefinition.class));
assertThat(leafRefNode2.getType(), isA(LeafrefTypeDefinition.class));
- TypeDefinition<?> baseTypeForLeafRef = SchemaContextUtil.getBaseTypeForLeafRef(
- (LeafrefTypeDefinition) leafRefNode.getType(), context, leafRefNode);
- TypeDefinition<?> baseTypeForLeafRef2 = SchemaContextUtil.getBaseTypeForLeafRef(
- (LeafrefTypeDefinition) leafRefNode2.getType(), context, leafRefNode2);
+ EffectiveStatement<?, ?> found = SchemaInferenceStack.ofDataTreePath(context, root, conGrp, leafRef)
+ .resolvePathExpression(((LeafrefTypeDefinition) leafRefNode.getType()).getPathStatement());
+ assertThat(((TypedDataSchemaNode)found).getType(), isA(BinaryTypeDefinition.class));
- assertThat(baseTypeForLeafRef, isA(BinaryTypeDefinition.class));
- assertThat(baseTypeForLeafRef2, isA(Int16TypeDefinition.class));
+ found = SchemaInferenceStack.ofDataTreePath(context, root, leafRef2)
+ .resolvePathExpression(((LeafrefTypeDefinition) leafRefNode2.getType()).getPathStatement());
+ assertThat(((TypedDataSchemaNode)found).getType(), isA(Int16TypeDefinition.class));
}
}
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
@Test
public void correctTest4() throws Exception {
- final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5335/correct/case-4");
+ final EffectiveModelContext context = StmtTestUtils.parseYangSources("/bugs/bug5335/correct/case-4");
final DataSchemaNode mandatoryLeaf = context.findDataTreeChild(ROOT, NON_PRESENCE_CONTAINER_F, MANDATORY_LEAF_F)
.orElse(null);
assertThat(mandatoryLeaf, instanceOf(LeafSchemaNode.class));
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.yangtools.yang.stmt;
import static org.junit.Assert.assertEquals;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
public class RpcStmtTest {
-
@Test
public void rpcTest() throws ReactorException {
- final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild()
+ final EffectiveModelContext result = RFC7950Reactors.defaultReactor().newBuild()
.addSource(sourceForResource("/model/baz.yang"))
.addSource(sourceForResource("/model/bar.yang"))
.addSource(sourceForResource("/rpc-stmt-test/foo.yang"))