* {@link XMLStreamWriter}, resulting in a RFC 6020 XML encoding.
*/
public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
- private static final XmlStreamUtils UTILS = XmlStreamUtils.create(XmlUtils.DEFAULT_XML_CODEC_PROVIDER);
private final XMLStreamWriter writer;
private final SchemaTracker tracker;
+ private final XmlStreamUtils streamUtils;
private XMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) {
this.writer = Preconditions.checkNotNull(writer);
this.tracker = SchemaTracker.create(context, path);
+ this.streamUtils = XmlStreamUtils.create(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, context);
}
/**
try {
writeStartElement(qname);
if (value != null) {
- UTILS.writeValue(writer, type, value);
+ streamUtils.writeValue(writer, type, value);
+ }
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new IOException("Failed to emit element", e);
+ }
+ }
+
+ private void writeElement(final QName qname, final SchemaNode schemaNode, final Object value) throws IOException {
+ try {
+ writeStartElement(qname);
+ if (value != null) {
+ streamUtils.writeValue(writer, schemaNode, value);
}
writer.writeEndElement();
} catch (XMLStreamException e) {
@Override
public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
final LeafSchemaNode schema = tracker.leafNode(name);
-
- writeElement(schema.getQName(), schema.getType(), value);
+ writeElement(schema.getQName(), schema, value);
}
@Override
@Override
public void leafSetEntryNode(final Object value) throws IOException {
final LeafListSchemaNode schema = tracker.leafSetEntryNode();
- writeElement(schema.getQName(), schema.getType(), value);
+ writeElement(schema.getQName(), schema, value);
}
@Override
try {
writeStartElement(qname);
if (value != null) {
- UTILS.writeValue(writer, (Node<?>)value, schema);
+ streamUtils.writeValue(writer, (Node<?>)value, schema);
}
writer.writeEndElement();
} catch (XMLStreamException e) {
import static com.google.common.base.Preconditions.checkState;
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
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.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+
public class XmlDocumentUtils {
private static class ElementWithSchemaContext {
Element element;
private static Object resolveValueFromSchemaType(final Element xmlElement, final DataSchemaNode schema, final TypeDefinition<?> type,
final XmlCodecProvider codecProvider,final SchemaContext schemaCtx) {
- final TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(type);
+
+ TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(type);
+ if (baseType instanceof LeafrefTypeDefinition) {
+ final LeafrefTypeDefinition leafrefTypeDefinition = (LeafrefTypeDefinition) baseType;
+ baseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypeDefinition, schemaCtx, schema);
+ }
+
final String text = xmlElement.getTextContent();
final Object value;
} else if (baseType instanceof IdentityrefTypeDefinition) {
value = InstanceIdentifierForXmlCodec.toIdentity(text, xmlElement, schemaCtx);
} else {
- final TypeDefinitionAwareCodec<?, ?> codec = codecProvider.codecFor(type);
+ final TypeDefinitionAwareCodec<?, ?> codec = codecProvider.codecFor(baseType);
if (codec == null) {
LOG.info("No codec for schema {}, falling back to text", schema);
value = text;
import java.util.Set;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Internal equivalent of {@link Collections}' unmodifiable Map. It does not retain
* keySet/entrySet references, thus lowering the memory overhead.
*/
final class UnmodifiableChildrenMap implements Map<PathArgument, DataContainerChild<? extends PathArgument, ?>>, Serializable {
- private static final Logger LOG = LoggerFactory.getLogger(UnmodifiableChildrenMap.class);
private static final long serialVersionUID = 1L;
private final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> delegate;
private transient Collection<DataContainerChild<? extends PathArgument, ?>> values;
@Override
public Set<Entry<PathArgument, DataContainerChild<? extends PathArgument, ?>>> entrySet() {
- LOG.warn("Invocation of inefficient entrySet()", new Throwable().fillInStackTrace());
+ /*
+ * Okay, this is not as efficient as it could be -- we could save ourselves the
+ * map instantiation. The cost of that would be re-implementation of a read-only
+ * Map.Entry to ensure our delegate is never modified.
+ *
+ * Let's skip that and use whatever the JRE gives us instead.
+ */
return Collections.unmodifiableMap(delegate).entrySet();
}
-
@Override
public boolean equals(final Object o) {
return this == o || delegate.equals(o);
package org.opendaylight.yangtools.yang.data.impl.codec.xml;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.net.URI;
+import java.net.URISyntaxException;
import java.util.AbstractMap;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+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.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.model.util.StringType;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
import org.w3c.dom.Document;
public class XmlStreamUtilsTest {
public static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
+ private static SchemaContext schemaContext;
+ private static Module leafRefModule;
+
+ @BeforeClass
+ public static void initialize() throws URISyntaxException {
+ final YangParserImpl yangParser = new YangParserImpl();
+ final File file = new File(XmlStreamUtils.class.getResource("/leafref-test.yang").toURI());
+ schemaContext = yangParser.parseFiles(Arrays.asList(file));
+ assertNotNull(schemaContext);
+ assertEquals(1,schemaContext.getModules().size());
+ leafRefModule = schemaContext.getModules().iterator().next();
+ assertNotNull(leafRefModule);
+ }
+
+
@Test
public void testWriteAttribute() throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
assertTrue("Xml differs: " + diff.toString(), identical);
}
+ /**
+ * One leafref reference to other leafref via relative references
+ */
+ @Test
+ public void testLeafRefRelativeChaining() {
+ getTargetNodeForLeafRef("leafname3",StringType.class);
+ }
+
+ @Test
+ public void testLeafRefRelative() {
+ getTargetNodeForLeafRef("pointToStringLeaf",StringType.class);
+ }
+
+ @Test
+ public void testLeafRefAbsoluteWithSameTarget() {
+ getTargetNodeForLeafRef("absname",InstanceIdentifierType.class);
+ }
+
+ /**
+ * Tests relative path with double point inside path (e. g. "../../lf:interface/../lf:cont2/lf:stringleaf")
+ */
+ @Ignore //ignored because this isn't implemented
+ @Test
+ public void testLeafRefWithDoublePointInPath() {
+ getTargetNodeForLeafRef("lf-with-double-point-inside",StringType.class);
+ }
+
+ @Test
+ public void testLeafRefRelativeAndAbsoluteWithSameTarget() {
+ final TypeDefinition<?> targetNodeForAbsname = getTargetNodeForLeafRef("absname",InstanceIdentifierType.class);
+ final TypeDefinition<?> targetNodeForRelname = getTargetNodeForLeafRef("relname",InstanceIdentifierType.class);
+ assertEquals(targetNodeForAbsname, targetNodeForRelname);
+ }
+
+ 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);
+ assertEquals("Wrong class found.", clas, targetBaseType.getClass());
+ return targetBaseType;
+ }
+
@Test
public void testEmptyNodeWithAttribute() throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
return QName.create(namespace, revision, localName);
}
}
-}
+
+ 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 LeafrefTypeDefinition findLeafrefType(final LeafSchemaNode schemaNode) {
+ final TypeDefinition<?> type = schemaNode.getType();
+ if (type instanceof LeafrefTypeDefinition) {
+ return (LeafrefTypeDefinition)type;
+ }
+ return null;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 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
+ */
+module leafref-test {
+ yang-version 1;
+ namespace "urn:opendaylight:yangtools:leafref:test";
+ prefix "lt";
+
+ revision 2014-11-04 {
+ description "Test deserialization value of leafref type.";
+ }
+
+ container interface {
+ leaf simpleValue {
+ type instance-identifier;
+ }
+ }
+
+ container cont2 {
+ container cont3 {
+ leaf leafname3 {
+ type leafref {
+ path "../../pointToStringLeaf";
+ }
+ }
+ }
+ leaf pointToStringLeaf {
+ type leafref {
+ path "../stringleaf";
+ }
+ }
+ leaf stringleaf {
+ type string;
+ }
+ leaf absname {
+ type leafref {
+ path "/lt:interface/lt:simpleValue";
+ }
+ }
+ leaf relname {
+ type leafref {
+ path "../../lt:interface/lt:simpleValue";
+ }
+ }
+
+ leaf lf-with-double-point-inside {
+ type leafref {
+ path "../../lt:interface/../lt:cont2/lt:stringleaf";
+ }
+ }
+ }
+}
\ No newline at end of file
Preconditions.checkArgument(module != null, "Failed to resolve xpath: no module found for prefix %s in module %s",
modulePrefix, parentModule.getName());
- // FIXME: Module should have a QNameModule handle
- return QName.create(module.getNamespace(), module.getRevision(), prefixedName.next());
+ return QName.create(module.getQNameModule(), prefixedName.next());
} else {
return QName.create(parentModule.getNamespace(), parentModule.getRevision(), prefixedPathPart);
}
* Yang Module
* @param relativeXPath
* Non conditional Revision Aware Relative XPath
- * @param leafrefSchemaPath
- * Schema Path for Leafref
+ * @param actualSchemaNode
+ * actual schema node
* @return list of QName
*/
private static Iterable<QName> resolveRelativeXPath(final SchemaContext context, final Module module,
- final RevisionAwareXPath relativeXPath, final SchemaNode leafrefParentNode) {
+ final RevisionAwareXPath relativeXPath, final SchemaNode actualSchemaNode) {
Preconditions.checkArgument(context != null, "Schema Context reference cannot be NULL");
Preconditions.checkArgument(module != null, "Module reference cannot be NULL");
Preconditions.checkArgument(relativeXPath != null, "Non Conditional Revision Aware XPath cannot be NULL");
Preconditions.checkState(!relativeXPath.isAbsolute(),
"Revision Aware XPath MUST be relative i.e. MUST contains ../, "
+ "for non relative Revision Aware XPath use findDataSchemaNode method");
- Preconditions.checkState(leafrefParentNode.getPath() != null,
+ Preconditions.checkState(actualSchemaNode.getPath() != null,
"Schema Path reference for Leafref cannot be NULL");
final Iterable<String> xpaths = SLASH_SPLITTER.split(relativeXPath.toString());
// Find out how many "parent" components there are
// FIXME: is .contains() the right check here?
+ // FIXME: case ../../node1/node2/../node3/../node4
int colCount = 0;
for (Iterator<String> it = xpaths.iterator(); it.hasNext() && it.next().contains(".."); ) {
++colCount;
}
- final Iterable<QName> parent = leafrefParentNode.getPath().getPathFromRoot();
- return Iterables.concat(Iterables.limit(parent, Iterables.size(parent) - colCount),
+ final Iterable<QName> schemaNodePath = actualSchemaNode.getPath().getPathFromRoot();
+
+ if (Iterables.size(schemaNodePath) - colCount >= 0) {
+ return Iterables.concat(Iterables.limit(schemaNodePath, Iterables.size(schemaNodePath) - colCount),
+ Iterables.transform(Iterables.skip(xpaths, colCount), new Function<String, QName>() {
+ @Override
+ public QName apply(final String input) {
+ return stringPathPartToQName(context, module, input);
+ }
+ }));
+ }
+ return Iterables.concat(schemaNodePath,
Iterables.transform(Iterables.skip(xpaths, colCount), new Function<String, QName>() {
@Override
public QName apply(final String input) {
* Schema Context
* @param schema
* Schema Node
- * @return
+ * @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) {
RevisionAwareXPath pathStatement = typeDefinition.getPathStatement();
if(pathStatement.isAbsolute()) {
dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNode(schemaContext, parentModule, pathStatement);
} else {
- SchemaPath parentSchemaPath = schema.getPath().getParent();
- SchemaNode parentSchemaNode = SchemaContextUtil.findDataSchemaNode(schemaContext, parentSchemaPath);
- dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext, parentModule, parentSchemaNode, pathStatement);
+ dataSchemaNode = (DataSchemaNode) SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext, parentModule, schema, pathStatement);
+ }
+
+ // 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);
return repository.getSchemaSource(input, ASTSchemaSource.class);
}
};
- private final Cache<Collection<SourceIdentifier>, SchemaContext> cache = CacheBuilder.newBuilder().softValues().build();
+ private final Cache<Collection<SourceIdentifier>, SchemaContext> cache = CacheBuilder.newBuilder().weakValues().build();
private final AsyncFunction<List<ASTSchemaSource>, SchemaContext> assembleSources = new AsyncFunction<List<ASTSchemaSource>, SchemaContext>() {
@Override