Merge "Bug 1677 - Parser: ListSchemaNodeBuilder keys needs to be a LinkedHashSet"
authorTony Tkacik <ttkacik@cisco.com>
Thu, 20 Nov 2014 11:37:05 +0000 (11:37 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 20 Nov 2014 11:37:05 +0000 (11:37 +0000)
25 files changed:
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/RuntimeGeneratedMappingServiceImpl.java
model/ietf/ietf-topology-isis/src/main/yang/isis-topology@2013-07-12.yang
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/InstanceIdentifier.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XMLStreamNormalizedNodeStreamWriter.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlDocumentUtils.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/Builders.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriter.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/AbstractImmutableDataContainerNodeBuilder.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetEntryNodeBuilder.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableLeafSetNodeBuilder.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableMapNodeBuilder.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedMapNodeBuilder.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableOrderedMapNodeSchemaAwareBuilder.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/ImmutableUnkeyedListNodeBuilder.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableDataContainerNode.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueAttrNode.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/UnmodifiableChildrenMap.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodeContainerModificationStrategy.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/TreeNodeUtils.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlStreamUtilsTest.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueAttrNodeTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/leafref-test.yang [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java
yang/yang-parser-impl/src/main/antlr/YangLexer.g4
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java

index 29a532228dd54bfc6421c34035af5f91f923cafb..c0fef76f26c058ee8a26b9a08ce5c5b919a27d5c 100644 (file)
@@ -13,26 +13,7 @@ import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
-
-import java.net.URI;
-import java.util.AbstractMap.SimpleEntry;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutionException;
-
 import javassist.ClassPool;
-
-import javax.annotation.concurrent.GuardedBy;
-
 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
 import org.opendaylight.yangtools.binding.generator.util.Types;
@@ -75,6 +56,22 @@ import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.concurrent.GuardedBy;
+import java.net.URI;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+
 public class RuntimeGeneratedMappingServiceImpl implements BindingIndependentMappingService, SchemaContextListener,
 SchemaLock, AutoCloseable, SchemaContextHolder, TypeResolver {
 
@@ -159,6 +156,7 @@ SchemaLock, AutoCloseable, SchemaContextHolder, TypeResolver {
                 Type serviceClass = new ReferencedTypeImpl(namespace, BindingMapping.getClassName(module.getName())
                         + "Service");
                 serviceTypeToRpc.put(serviceClass, rpcs);
+                updatePromisedSchemas(serviceClass);
             }
 
             Map<SchemaPath, Type> typedefs = context.getTypedefs();
@@ -337,8 +335,10 @@ SchemaLock, AutoCloseable, SchemaContextHolder, TypeResolver {
         Set<QName> serviceRef = serviceTypeToRpc.get(new ReferencedTypeImpl(service.getPackage().getName(), service
                 .getSimpleName()));
         if (serviceRef == null) {
-            serviceRef = Collections.emptySet();
-        }
+            waitForSchema(service);
+            serviceRef = serviceTypeToRpc.get(new ReferencedTypeImpl(service.getPackage().getName(), service
+                    .getSimpleName()));
+            }
         return serviceRef;
     }
 
index a24b67c5a94f61ab1d7625daefaef9be56fefd75..23e9dbc1c5a74df1c751ed433e2547880496d96f 100644 (file)
@@ -40,7 +40,7 @@ module isis-topology {
        typedef iso-net-id {
         description "ISO NET ID. RFC 1237";
         type string {
-            pattern '[0-9a-fA-F]{2}((.[0-9a-fA-F]{4}){6}*)';
+            pattern '[0-9a-fA-F]{2}((.[0-9a-fA-F]{4}){6})';
         }
     }
 
index 9840338bb9442bd82db429ef1a2379db6a45d2ee..934c90d9ae77aea68079ad3b0a91eb6489dda1bc 100644 (file)
@@ -479,8 +479,13 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
      *
      * @param id instance identifier
      * @return key associated with the last component
+     * @throws IllegalArgumentException if the supplied identifier type cannot have a key.
+     * @throws NullPointerException if id is null.
      */
     public static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K keyOf(final InstanceIdentifier<N> id) {
+        Preconditions.checkNotNull(id);
+        Preconditions.checkArgument(id instanceof KeyedInstanceIdentifier, "%s does not have a key", id);
+
         @SuppressWarnings("unchecked")
         final K ret = ((KeyedInstanceIdentifier<N, K>)id).getKey();
         return ret;
index 97f676ae69bd62636764d138ea74e2ee4a678fb9..7f2c3019d35af9356115f33ff3ecc3da77e7a20f 100644 (file)
@@ -36,14 +36,15 @@ import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
  * {@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);
     }
 
     /**
@@ -82,7 +83,19 @@ public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNode
         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) {
@@ -110,8 +123,7 @@ public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNode
     @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
@@ -122,7 +134,7 @@ public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNode
     @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
@@ -173,7 +185,7 @@ public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNode
         try {
             writeStartElement(qname);
             if (value != null) {
-                UTILS.writeValue(writer, (Node<?>)value, schema);
+                streamUtils.writeValue(writer, (Node<?>)value, schema);
             }
             writer.writeEndElement();
         } catch (XMLStreamException e) {
index eb76a4c56dedc328082c9e18f47fcdfcf28ffda3..3fcb5784f7d44a55a123d9909ca1f466fc428325 100644 (file)
@@ -9,12 +9,6 @@ package org.opendaylight.yangtools.yang.data.impl.codec.xml;
 
 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;
@@ -56,7 +50,9 @@ 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.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;
@@ -64,6 +60,13 @@ import org.w3c.dom.Document;
 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;
@@ -267,7 +270,13 @@ public class XmlDocumentUtils {
 
     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;
 
@@ -276,7 +285,7 @@ public class XmlDocumentUtils {
         } 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;
index 2ee7ee174e31a65b9370f89665958198698cfb03..c2081f37de9875f78e6c4ba2495f9c1bfadf3b50 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.yangtools.yang.data.impl.schema;
 
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
index fe3d1841a2bd8b67041aa66b7cddaa40120cb4e9..3ac6f32b0871a27a5982e77c25af8c83eb8ae642 100644 (file)
@@ -8,27 +8,38 @@
 package org.opendaylight.yangtools.yang.data.impl.schema;
 
 import com.google.common.base.Preconditions;
-
 import java.io.IOException;
 import java.util.ArrayDeque;
 import java.util.Deque;
 import java.util.List;
-
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
 
 /**
@@ -83,11 +94,10 @@ public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStream
      * <p>
      * This method is useful for clients, which knows there will be one
      * top level node written, but does not know which type of {@link NormalizedNode}
-     * will be writen.
-     *
+     * will be written.
      *
      * @param result {@link NormalizedNodeResult} object which will hold result value.
-     * @return {@link NormalizedNodeStreamWriter} whcih will write item to supplied result holder.
+     * @return {@link NormalizedNodeStreamWriter} which will write item to supplied result holder.
      */
     public static final NormalizedNodeStreamWriter from(final NormalizedNodeResult result) {
         return new ImmutableNormalizedNodeStreamWriter(new NormalizedNodeResultBuilder(result));
@@ -127,9 +137,10 @@ public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStream
     }
 
     @Override
-    public void startLeafSet(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
         checkDataNodeContainer();
-        ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = Builders.leafSetBuilder();
+        ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = UNKNOWN_SIZE == childSizeHint ?
+                ImmutableLeafSetNodeBuilder.create() : ImmutableLeafSetNodeBuilder.create(childSizeHint);
         builder.withNodeIdentifier(name);
         enter(builder);
     }
@@ -145,53 +156,73 @@ public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStream
     @Override
     public void anyxmlNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException {
         checkDataNodeContainer();
-
-
     }
 
     @Override
-    public void startContainerNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+    public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
         checkDataNodeContainer();
-        enter(Builders.containerBuilder().withNodeIdentifier(name));
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder = UNKNOWN_SIZE == childSizeHint ?
+                ImmutableContainerNodeBuilder.create() : ImmutableContainerNodeBuilder.create(childSizeHint);
+        enter(builder.withNodeIdentifier(name));
     }
 
     @Override
-    public void startUnkeyedList(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+    public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
         checkDataNodeContainer();
-        enter(Builders.unkeyedListBuilder().withNodeIdentifier(name));
+
+        final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = UNKNOWN_SIZE == childSizeHint ?
+                ImmutableUnkeyedListNodeBuilder.create() : ImmutableUnkeyedListNodeBuilder.create(childSizeHint);
+        enter(builder.withNodeIdentifier(name));
     }
 
     @Override
-    public void startUnkeyedListItem(final NodeIdentifier name,final int childSizeHint) throws IllegalStateException {
+    public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IllegalStateException {
         Preconditions.checkArgument(getCurrent() instanceof ImmutableUnkeyedListNodeBuilder);
-        enter(Builders.unkeyedListEntryBuilder().withNodeIdentifier(name));
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> builder = UNKNOWN_SIZE == childSizeHint ?
+                ImmutableUnkeyedListEntryNodeBuilder.create() : ImmutableUnkeyedListEntryNodeBuilder.create(childSizeHint);
+        enter(builder.withNodeIdentifier(name));
     }
 
     @Override
-    public void startMapNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+    public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
         checkDataNodeContainer();
-        enter(Builders.mapBuilder().withNodeIdentifier(name));
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> builder = UNKNOWN_SIZE == childSizeHint ?
+                ImmutableMapNodeBuilder.create() : ImmutableMapNodeBuilder.create(childSizeHint);
+        enter(builder.withNodeIdentifier(name));
     }
 
     @Override
-    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier,final int childSizeHint) throws IllegalArgumentException {
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) throws IllegalArgumentException {
         if(!(getCurrent() instanceof NormalizedNodeResultBuilder)) {
             Preconditions.checkArgument(getCurrent() instanceof ImmutableMapNodeBuilder || getCurrent() instanceof ImmutableOrderedMapNodeBuilder);
         }
-        enter(Builders.mapEntryBuilder().withNodeIdentifier(identifier));
+
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = UNKNOWN_SIZE == childSizeHint ?
+                ImmutableMapEntryNodeBuilder.create() : ImmutableMapEntryNodeBuilder.create(childSizeHint);
+        enter(builder.withNodeIdentifier(identifier));
     }
 
     @Override
-    public void startOrderedMapNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+    public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
         checkDataNodeContainer();
-        enter(Builders.orderedMapBuilder().withNodeIdentifier(name));
+
+        final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> builder = UNKNOWN_SIZE == childSizeHint ?
+                ImmutableOrderedMapNodeBuilder.create() : ImmutableOrderedMapNodeBuilder.create(childSizeHint);
+        enter(builder.withNodeIdentifier(name));
     }
 
     @Override
-    public void startChoiceNode(final NodeIdentifier name,final int childSizeHint) throws IllegalArgumentException {
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
         checkDataNodeContainer();
-        enter(Builders.choiceBuilder().withNodeIdentifier(name));
+
+        final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> builder = UNKNOWN_SIZE == childSizeHint ?
+                ImmutableChoiceNodeBuilder.create() : ImmutableChoiceNodeBuilder.create(childSizeHint);
+        enter(builder.withNodeIdentifier(name));
     }
+
     @Override
     public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException {
         checkDataNodeContainer();
index bb271684e967494043c1d13a7ff4f88d64856168..cb50fefc8df70e7da24a5d83e48c1dec08774646 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.yangtools.yang.data.impl.schema.builder.impl;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
@@ -20,7 +19,7 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNo
 import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
 
 abstract class AbstractImmutableDataContainerNodeBuilder<I extends YangInstanceIdentifier.PathArgument, R extends DataContainerNode<I>> implements DataContainerNodeBuilder<I, R> {
-
+    private static final int DEFAULT_CAPACITY = 4;
     private Map<YangInstanceIdentifier.PathArgument, DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> value;
     private I nodeIdentifier;
 
@@ -33,17 +32,28 @@ abstract class AbstractImmutableDataContainerNodeBuilder<I extends YangInstanceI
     private boolean dirty;
 
     protected AbstractImmutableDataContainerNodeBuilder() {
-        this.value = new HashMap<>();
+        this.value = new HashMap<>(DEFAULT_CAPACITY);
         this.dirty = false;
     }
 
     protected AbstractImmutableDataContainerNodeBuilder(final int sizeHint) {
-        this.value = new HashMap<>(sizeHint);
+        this.value = new HashMap<>(DEFAULT_CAPACITY);
         this.dirty = false;
     }
 
     protected AbstractImmutableDataContainerNodeBuilder(final AbstractImmutableDataContainerNode<I> node) {
         this.nodeIdentifier = node.getIdentifier();
+
+        /*
+         * FIXME: BUG-2402: this call is not what we actually want. We are the
+         *        only user of getChildren(), and we really want this to be a
+         *        zero-copy operation if we happen to not modify the children.
+         *        If we do, we want to perform an efficient copy-on-write before
+         *        we make the change.
+         *
+         *        With this interface we end up creating a lot of short-lived
+         *        objects in case we modify the map -- see checkDirty().
+         */
         this.value = node.getChildren();
         this.dirty = true;
     }
@@ -63,6 +73,13 @@ abstract class AbstractImmutableDataContainerNodeBuilder<I extends YangInstanceI
 
     private void checkDirty() {
         if (dirty) {
+            /*
+             * FIXME: BUG-2402: This is the second part of the above. Note
+             *        that value here is usually a read-only view. Invocation
+             *        of this constructor will force instantiation of a wrapper
+             *        Map.Entry object, just to make sure this read path does
+             *        not modify the map.
+             */
             value = new HashMap<>(value);
             dirty = false;
         }
index 219f58c522165d174f7dc662f3fcc5371939cdf6..40c7a654d3fd8f33f5000ea24dd8839bffb64d7a 100644 (file)
@@ -7,15 +7,14 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.builder.impl;
 
+import com.google.common.base.Preconditions;
 import java.util.Map;
-
+import java.util.Objects;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedValueAttrNode;
 
-import com.google.common.base.Preconditions;
-
 public class ImmutableLeafSetEntryNodeBuilder<T> extends AbstractImmutableNormalizedNodeBuilder<YangInstanceIdentifier.NodeWithValue, T, LeafSetEntryNode<T>> {
 
     public static <T> ImmutableLeafSetEntryNodeBuilder<T> create() {
@@ -31,7 +30,7 @@ public class ImmutableLeafSetEntryNodeBuilder<T> extends AbstractImmutableNormal
 
         ImmutableLeafSetEntryNode(final YangInstanceIdentifier.NodeWithValue nodeIdentifier, final T value, final Map<QName, String> attributes) {
             super(nodeIdentifier, value, attributes);
-            Preconditions.checkArgument(nodeIdentifier.getValue().equals(value),
+            Preconditions.checkArgument(Objects.deepEquals(nodeIdentifier.getValue(), value),
                     "Node identifier contains different value: %s than value itself: %s", nodeIdentifier, value);
         }
     }
index dd5048103c7a32f5ff3e9a62fe012ee27e4f354b..71b4bbc7606433dd53ee85ecbf709f21a99939dc 100644 (file)
@@ -7,11 +7,12 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.builder.impl;
 
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.util.MapAdaptor;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -25,16 +26,17 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNo
 import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedValueNode;
 
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
-
 public class ImmutableLeafSetNodeBuilder<T> implements ListNodeBuilder<T, LeafSetEntryNode<T>> {
-
+    private static final int DEFAULT_CAPACITY = 4;
     private final Map<YangInstanceIdentifier.NodeWithValue, LeafSetEntryNode<T>> value;
     private YangInstanceIdentifier.NodeIdentifier nodeIdentifier;
 
     protected ImmutableLeafSetNodeBuilder() {
-        value = new HashMap<>();
+        value = new HashMap<>(DEFAULT_CAPACITY);
+    }
+
+    protected ImmutableLeafSetNodeBuilder(final int sizeHint) {
+        value = new HashMap<>(sizeHint * 4 / 3);
     }
 
     protected ImmutableLeafSetNodeBuilder(final ImmutableLeafSetNode<T> node) {
@@ -46,6 +48,10 @@ public class ImmutableLeafSetNodeBuilder<T> implements ListNodeBuilder<T, LeafSe
         return new ImmutableLeafSetNodeBuilder<>();
     }
 
+    public static <T> ListNodeBuilder<T, LeafSetEntryNode<T>> create(final int sizeHint) {
+        return new ImmutableLeafSetNodeBuilder<>(sizeHint);
+    }
+
     public static <T> ListNodeBuilder<T, LeafSetEntryNode<T>> create(final LeafSetNode<T> node) {
         if (!(node instanceof ImmutableLeafSetNode<?>)) {
             throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
index 98028944ffe4146df5a67cf729f9ba2050a77d7a..95d9aa2b81a1945168c78e209dd570fde77ac644 100644 (file)
@@ -7,10 +7,11 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.builder.impl;
 
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.util.MapAdaptor;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -22,17 +23,17 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNo
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedNode;
 
-import com.google.common.base.Optional;
-import com.google.common.collect.Iterables;
-
-public class ImmutableMapNodeBuilder
-        implements CollectionNodeBuilder<MapEntryNode, MapNode> {
-
+public class ImmutableMapNodeBuilder implements CollectionNodeBuilder<MapEntryNode, MapNode> {
+    private static final int DEFAULT_CAPACITY = 4;
     private final Map<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> value;
     private YangInstanceIdentifier.NodeIdentifier nodeIdentifier;
 
     protected ImmutableMapNodeBuilder() {
-        this.value = new HashMap<>();
+        this.value = new HashMap<>(DEFAULT_CAPACITY);
+    }
+
+    protected ImmutableMapNodeBuilder(final int sizeHint) {
+        this.value = new HashMap<>(DEFAULT_CAPACITY);
     }
 
     protected ImmutableMapNodeBuilder(final ImmutableMapNode node) {
@@ -44,6 +45,10 @@ public class ImmutableMapNodeBuilder
         return new ImmutableMapNodeBuilder();
     }
 
+    public static CollectionNodeBuilder<MapEntryNode, MapNode> create(final int sizeHint) {
+        return new ImmutableMapNodeBuilder(sizeHint);
+    }
+
     public static CollectionNodeBuilder<MapEntryNode, MapNode> create(final MapNode node) {
         if (!(node instanceof ImmutableMapNode)) {
             throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
@@ -113,10 +118,10 @@ public class ImmutableMapNodeBuilder
             return Optional.fromNullable(children.get(child));
         }
 
-               @Override
-               public Iterable<MapEntryNode> getValue() {
-                       return Iterables.unmodifiableIterable(children.values());
-               }
+        @Override
+        public Iterable<MapEntryNode> getValue() {
+            return Iterables.unmodifiableIterable(children.values());
+        }
 
         @Override
         protected int valueHashCode() {
index e7d68d992894f7a87b39039b12cf58545f847723..523ad51817a5b27af9ffa6865f33a0c9d17074da 100644 (file)
@@ -9,6 +9,9 @@ package org.opendaylight.yangtools.yang.data.impl.schema.builder.impl;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.Iterables;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -20,19 +23,19 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNo
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedNode;
 
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-public class ImmutableOrderedMapNodeBuilder
-        implements CollectionNodeBuilder<MapEntryNode, OrderedMapNode> {
-
+public class ImmutableOrderedMapNodeBuilder implements CollectionNodeBuilder<MapEntryNode, OrderedMapNode> {
+    private static final int DEFAULT_CAPACITY = 4;
     private Map<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> value;
     private YangInstanceIdentifier.NodeIdentifier nodeIdentifier;
-    private boolean dirty = false;
+    private boolean dirty;
 
     protected ImmutableOrderedMapNodeBuilder() {
-        this.value = new LinkedHashMap<>();
+        this.value = new LinkedHashMap<>(DEFAULT_CAPACITY);
+        this.dirty = false;
+    }
+
+    protected ImmutableOrderedMapNodeBuilder(final int sizeHint) {
+        this.value = new LinkedHashMap<>(DEFAULT_CAPACITY);
         this.dirty = false;
     }
 
@@ -46,6 +49,10 @@ public class ImmutableOrderedMapNodeBuilder
         return new ImmutableOrderedMapNodeBuilder();
     }
 
+    public static CollectionNodeBuilder<MapEntryNode, OrderedMapNode> create(final int sizeHint) {
+        return new ImmutableOrderedMapNodeBuilder(sizeHint);
+    }
+
     public static CollectionNodeBuilder<MapEntryNode, OrderedMapNode> create(final MapNode node) {
         if (!(node instanceof ImmutableOrderedMapNode)) {
             throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
@@ -145,9 +152,9 @@ public class ImmutableOrderedMapNodeBuilder
             return children.size();
         }
 
-               @Override
-               public Iterable<MapEntryNode> getValue() {
-                       return Iterables.unmodifiableIterable(children.values());
-               }
+        @Override
+        public Iterable<MapEntryNode> getValue() {
+            return Iterables.unmodifiableIterable(children.values());
+        }
     }
 }
index 25d6dab768bd54b2143507c076216710079f882d..7787f3f18f2536f3f3949b142f179d49cedafcef 100644 (file)
@@ -17,8 +17,6 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNo
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataValidationException;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
-import com.google.common.base.Preconditions;
-
 public class ImmutableOrderedMapNodeSchemaAwareBuilder extends ImmutableOrderedMapNodeBuilder {
 
     private final ListSchemaNode schema;
index 66f81cadff5f75bc1b23cc9cbf915395830fda13..9eab2ba09816bd45d76faca4344d4b06273767f8 100644 (file)
@@ -7,9 +7,11 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.builder.impl;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -21,14 +23,10 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNo
 import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableNormalizedValueNode;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-
 public class ImmutableUnkeyedListNodeBuilder implements CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> {
-
     private List<UnkeyedListEntryNode> value;
     private YangInstanceIdentifier.NodeIdentifier nodeIdentifier;
-    private boolean dirty = false;
+    private boolean dirty;
 
     protected ImmutableUnkeyedListNodeBuilder() {
         this.value = new LinkedList<>();
@@ -46,6 +44,10 @@ public class ImmutableUnkeyedListNodeBuilder implements CollectionNodeBuilder<Un
         return new ImmutableUnkeyedListNodeBuilder();
     }
 
+    public static CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> create(final int sizeHint) {
+        return new ImmutableUnkeyedListNodeBuilder();
+    }
+
     public static CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> create(final UnkeyedListNode node) {
         if (!(node instanceof ImmutableUnkeyedListNode)) {
             throw new UnsupportedOperationException(String.format("Cannot initialize from class %s", node.getClass()));
@@ -95,7 +97,11 @@ public class ImmutableUnkeyedListNodeBuilder implements CollectionNodeBuilder<Un
     @Override
     public UnkeyedListNode build() {
         dirty = true;
-        return new ImmutableUnkeyedListNode(nodeIdentifier, ImmutableList.copyOf(value));
+        if (value.isEmpty()) {
+            return new EmptyImmutableUnkeyedListNode(nodeIdentifier);
+        } else {
+            return new ImmutableUnkeyedListNode(nodeIdentifier, ImmutableList.copyOf(value));
+        }
     }
 
     @Override
@@ -109,6 +115,37 @@ public class ImmutableUnkeyedListNodeBuilder implements CollectionNodeBuilder<Un
         return withoutChild(key);
     }
 
+    protected static final class EmptyImmutableUnkeyedListNode extends AbstractImmutableNormalizedNode<YangInstanceIdentifier.NodeIdentifier, Iterable<UnkeyedListEntryNode>> implements Immutable, UnkeyedListNode {
+        protected EmptyImmutableUnkeyedListNode(final NodeIdentifier nodeIdentifier) {
+            super(nodeIdentifier);
+        }
+
+        @Override
+        public Iterable<UnkeyedListEntryNode> getValue() {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public UnkeyedListEntryNode getChild(final int position) {
+            return null;
+        }
+
+        @Override
+        public int getSize() {
+            return 0;
+        }
+
+        @Override
+        protected boolean valueEquals(final AbstractImmutableNormalizedNode<?, ?> other) {
+            return Collections.EMPTY_LIST.equals(other.getValue());
+        }
+
+        @Override
+        protected int valueHashCode() {
+            return Collections.EMPTY_LIST.hashCode();
+        }
+    }
+
     protected static final class ImmutableUnkeyedListNode extends
             AbstractImmutableNormalizedValueNode<YangInstanceIdentifier.NodeIdentifier, Iterable<UnkeyedListEntryNode>>
             implements Immutable, UnkeyedListNode {
index 01bfe32b2811d9afaf93040f6c8c112af899bdf7..b6ac22617abcb44feb63367c750a764c5ef9a23c 100644 (file)
@@ -8,49 +8,20 @@
 package org.opendaylight.yangtools.yang.data.impl.schema.nodes;
 
 import com.google.common.base.Optional;
-
-import java.util.Collections;
 import java.util.Map;
-
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public abstract class AbstractImmutableDataContainerNode<K extends PathArgument> extends AbstractImmutableNormalizedNode<K, Iterable<DataContainerChild<? extends PathArgument, ?>>> implements Immutable, DataContainerNode<K> {
-    private static final Logger LOG = LoggerFactory.getLogger(AbstractImmutableDataContainerNode.class);
     private final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children;
 
     public AbstractImmutableDataContainerNode(
             final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children, final K nodeIdentifier) {
         super(nodeIdentifier);
 
-        /*
-         * There is a code path where AbstractImmutableDataContainerNodeBuilder can reflect
-         * the collection acquired via getChildren() back to us. This is typically the case
-         * in the datastore where transactions cancel each other out, leaving an unmodified
-         * node. In that case we want to skip wrapping the map again (and again and again).
-         *
-         * In a perfect world, Collection.unmodifiableMap() would be doing the instanceof
-         * check which would stop the proliferation. Unfortunately this not the case and the
-         * 'unmodifiable' trait is not exposed by anything we can query. Furthermore the API
-         * contract there is sufficiently vague so an implementation may actually return a
-         * different implementation based on input map -- for example
-         * Collections.unmodifiableMap(Collections.emptyMap()) returning the same thing as
-         * Collections.emptyMap().
-         *
-         * This means that we have to perform the instantiation here (as opposed to once at
-         * class load time) and then compare the classes.
-         */
-        final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> pub = Collections.unmodifiableMap(children);
-        if (children.getClass().equals(pub.getClass())) {
-            LOG.trace("Reusing already-unmodifiable children {}", children);
-            this.children = children;
-        } else {
-            this.children = pub;
-        }
+        this.children = UnmodifiableChildrenMap.create(children);
     }
 
     @Override
@@ -68,6 +39,15 @@ public abstract class AbstractImmutableDataContainerNode<K extends PathArgument>
         return children.hashCode();
     }
 
+    /**
+     * DO NOT USE THIS METHOD.
+     *
+     * This is an implementation-internal API and no outside users should use it. If you do,
+     * you are asking for trouble, as the returned object is not guaranteed to conform to
+     * java.util.Map interface.
+     *
+     * @return An unmodifiable view if this node's children.
+     */
     public final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> getChildren() {
         return children;
     }
index e90db03c6440ae77cee001360d6586335f0c2a2a..52eb7650f12ac363ff5027a5fe29e4a3e88805b1 100644 (file)
@@ -10,7 +10,10 @@ package org.opendaylight.yangtools.yang.data.impl.schema.nodes;
 import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableMap;
+
+import java.util.Arrays;
 import java.util.Map;
+
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -53,17 +56,20 @@ public abstract class AbstractImmutableNormalizedValueAttrNode<K extends YangIns
 
     @Override
     protected boolean valueEquals(final AbstractImmutableNormalizedNode<?, ?> other) {
-        // We can not call directly getValue.equals because of Empty Type Definition leaves
-        // which allways have NULL value
-        if (!Objects.equal(getValue(), other.getValue())) {
+        // We can not call directly getValue.equals because of Empty Type
+        // Definition leaves which allways have NULL value
+
+        if (!java.util.Objects.deepEquals(getValue(), other.getValue())) {
             return false;
         }
 
-// FIXME: are attributes part of hashCode/equals?
-//        final Set<Entry<QName, String>> tas = getAttributes().entrySet();
-//        final Set<Entry<QName, String>> oas = container.getAttributes().entrySet();
-//
-//        return tas.containsAll(oas) && oas.containsAll(tas);
+        // FIXME: are attributes part of hashCode/equals?
+        // final Set<Entry<QName, String>> tas = getAttributes().entrySet();
+        // final Set<Entry<QName, String>> oas =
+        // container.getAttributes().entrySet();
+        //
+        // return tas.containsAll(oas) && oas.containsAll(tas);
         return true;
     }
+
 }
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/UnmodifiableChildrenMap.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/UnmodifiableChildrenMap.java
new file mode 100644 (file)
index 0000000..98598aa
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.nodes;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+
+/**
+ * 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 long serialVersionUID = 1L;
+    private final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> delegate;
+    private transient Collection<DataContainerChild<? extends PathArgument, ?>> values;
+
+    private UnmodifiableChildrenMap(final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> delegate) {
+        this.delegate = Preconditions.checkNotNull(delegate);
+    }
+
+    /**
+     * Create an unmodifiable view of a particular map. Does not perform unnecessary
+     * encapsulation if the map is known to be already unmodifiable.
+     *
+     * @param map Backing map
+     * @return Unmodifiable view
+     */
+    static Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> create(final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> map) {
+        if (map instanceof UnmodifiableChildrenMap) {
+            return map;
+        }
+        if (map instanceof ImmutableMap) {
+            return map;
+        }
+        if (map.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        return new UnmodifiableChildrenMap(map);
+    }
+
+    @Override
+    public int size() {
+        return delegate.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return delegate.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(final Object key) {
+        return delegate.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(final Object value) {
+        return delegate.containsValue(value);
+    }
+
+    @Override
+    public DataContainerChild<? extends PathArgument, ?> get(final Object key) {
+        return delegate.get(key);
+    }
+
+    @Override
+    public DataContainerChild<? extends PathArgument, ?> put(final PathArgument key,
+            final DataContainerChild<? extends PathArgument, ?> value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DataContainerChild<? extends PathArgument, ?> remove(final Object key) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void putAll(final Map<? extends PathArgument, ? extends DataContainerChild<? extends PathArgument, ?>> m) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Set<PathArgument> keySet() {
+        return Collections.unmodifiableSet(delegate.keySet());
+    }
+
+    @Override
+    public Collection<DataContainerChild<? extends PathArgument, ?>> values() {
+        if (values == null) {
+            values = Collections.unmodifiableCollection(delegate.values());
+        }
+        return values;
+    }
+
+    @Override
+    public Set<Entry<PathArgument, DataContainerChild<? extends PathArgument, ?>>> entrySet() {
+        /*
+         * 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);
+    }
+
+    @Override
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return delegate.toString();
+    }
+}
index 7ca902092c6bd9d852b0006fb5aee9ad458a531b..52aa03eed2be09bd1d526d5f13e56980b0481a12 100644 (file)
@@ -7,9 +7,11 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.tree;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
+import java.util.Map;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
@@ -18,7 +20,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.MutableTreeNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
@@ -37,10 +38,6 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
-import java.util.Map;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
 abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
 
     private final Class<? extends NormalizedNode<?, ?>> nodeClass;
index f51c0c63fdecc65c56a42634adbb6a079b25988b..491594d1c4649c7370f74b5be036fb1bb60a14e6 100644 (file)
@@ -12,11 +12,9 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Iterables;
-
 import java.util.AbstractMap.SimpleEntry;
 import java.util.Iterator;
 import java.util.Map;
-
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode;
@@ -85,7 +83,7 @@ public final class TreeNodeUtils {
             nesting++;
         }
         if(current.isPresent()) {
-            final YangInstanceIdentifier currentPath = YangInstanceIdentifier.create(path.getPath().subList(0, nesting));
+            final YangInstanceIdentifier currentPath = YangInstanceIdentifier.create(Iterables.limit(path.getPathArguments(), nesting));
             return new SimpleEntry<YangInstanceIdentifier,T>(currentPath,current.get());
         }
 
@@ -96,8 +94,7 @@ public final class TreeNodeUtils {
          * present. At any rate we check state just to be on the safe side.
          */
         Preconditions.checkState(nesting > 0);
-        final YangInstanceIdentifier parentPath = YangInstanceIdentifier.create(path.getPath().subList(0, nesting - 1));
-
+        final YangInstanceIdentifier parentPath = YangInstanceIdentifier.create(Iterables.limit(path.getPathArguments(), nesting - 1));
         return new SimpleEntry<YangInstanceIdentifier,T>(parentPath,parent.get());
     }
 
index 6832748bdaf49656883a864a016b7b60d670e1f2..c4787d5ba2dc4cad26a01e3695da3861e8502d38 100644 (file)
@@ -9,33 +9,40 @@
 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.Lists;
 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.api.SimpleNode;
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
-import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
-import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
+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;
 
@@ -43,6 +50,21 @@ 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();
@@ -80,6 +102,50 @@ public class XmlStreamUtilsTest {
         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();
@@ -126,4 +192,29 @@ public class XmlStreamUtilsTest {
             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
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueAttrNodeTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueAttrNodeTest.java
new file mode 100644 (file)
index 0000000..778bf25
--- /dev/null
@@ -0,0 +1,329 @@
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.nodes;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+public class AbstractImmutableNormalizedValueAttrNodeTest {
+
+    private static QName ROOT_QNAME = QName.create("urn:test", "2014-03-13",
+            "root");
+    private static QName LEAF_QNAME = QName.create(ROOT_QNAME, "my-leaf");
+    private static QName SAME_LEAF_QNAME = QName.create(ROOT_QNAME, "my-leaf");
+    private static QName OTHER_LEAF_QNAME = QName.create(ROOT_QNAME,
+            "my-other-leaf");
+
+    @Test
+    public void equalsByteTest() {
+
+        byte[] valueNull = null;
+        byte[] equalValueNull = null;
+
+        LeafNode<byte[]> leafNodeNull = ImmutableNodes.leafNode(LEAF_QNAME,
+                valueNull);
+        LeafNode<byte[]> equalLeafNodeNull = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValueNull);
+
+        assertTrue(leafNodeNull.equals(leafNodeNull));
+        assertTrue(leafNodeNull.equals(equalLeafNodeNull));
+        assertTrue(equalLeafNodeNull.equals(leafNodeNull));
+
+        byte[] value = "test".getBytes();
+        byte[] equalValue = "test".getBytes();
+
+        LeafNode<byte[]> leafNode = ImmutableNodes.leafNode(LEAF_QNAME, value);
+        LeafNode<byte[]> equalLeafNode = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue);
+
+        assertTrue(leafNode.equals(leafNode));
+        assertTrue(leafNode.equals(equalLeafNode));
+        assertTrue(equalLeafNode.equals(leafNode));
+
+        Byte[] value2 = new Byte[] { new Byte("1"), new Byte("2") };
+        Byte[] equalValue2 = new Byte[] { new Byte("1"), new Byte("2") };
+
+        LeafNode<Byte[]> leafNode2 = ImmutableNodes
+                .leafNode(LEAF_QNAME, value2);
+        LeafNode<Byte[]> equalLeafNode2 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue2);
+
+        assertTrue(leafNode2.equals(leafNode2));
+        assertTrue(leafNode2.equals(equalLeafNode2));
+        assertTrue(equalLeafNode2.equals(leafNode2));
+
+        byte[][] value3 = new byte[][] { "test".getBytes(), "test2".getBytes() };
+        byte[][] equalValue3 = new byte[][] { "test".getBytes(),
+                "test2".getBytes() };
+
+        LeafNode<byte[][]> leafNode3 = ImmutableNodes.leafNode(LEAF_QNAME,
+                value3);
+        LeafNode<byte[][]> equalLeafNode3 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue3);
+
+        assertTrue(leafNode3.equals(leafNode3));
+        assertTrue(leafNode3.equals(equalLeafNode3));
+        assertTrue(equalLeafNode3.equals(leafNode3));
+
+        Byte[][] value4 = new Byte[][] {
+                new Byte[] { new Byte("1"), new Byte("2") },
+                new Byte[] { new Byte("3"), new Byte("4") } };
+        Byte[][] equalValue4 = new Byte[][] {
+                new Byte[] { new Byte("1"), new Byte("2") },
+                new Byte[] { new Byte("3"), new Byte("4") } };
+
+        LeafNode<Byte[][]> leafNode4 = ImmutableNodes.leafNode(LEAF_QNAME,
+                value4);
+        LeafNode<Byte[][]> equalLeafNode4 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue4);
+
+        assertTrue(leafNode4.equals(leafNode4));
+        assertTrue(leafNode4.equals(equalLeafNode4));
+        assertTrue(equalLeafNode4.equals(leafNode4));
+
+        Byte value6 = new Byte("1");
+        Byte equalValue6 = new Byte("1");
+
+        LeafNode<Byte> leafNode6 = ImmutableNodes.leafNode(LEAF_QNAME, value6);
+        LeafNode<Byte> equalLeafNode6 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue6);
+
+        assertTrue(leafNode6.equals(leafNode6));
+        assertTrue(leafNode6.equals(equalLeafNode6));
+        assertTrue(equalLeafNode6.equals(leafNode6));
+
+        String value5 = new String("test");
+        String equalValue5 = new String("test");
+
+        LeafNode<String> leafNode5 = ImmutableNodes
+                .leafNode(LEAF_QNAME, value5);
+        LeafNode<String> equalLeafNode5 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue5);
+
+        assertTrue(leafNode5.equals(leafNode5));
+        assertTrue(leafNode5.equals(equalLeafNode5));
+        assertTrue(equalLeafNode5.equals(leafNode5));
+
+    }
+
+    @Test
+    public void notEqualByteTest() {
+
+        byte[] value = "test".getBytes();
+        byte[] equalValue = "test".getBytes();
+
+        LeafNode<byte[]> leafNode = ImmutableNodes.leafNode(LEAF_QNAME, value);
+        LeafNode<byte[]> otherLeafNode = ImmutableNodes.leafNode(
+                OTHER_LEAF_QNAME, equalValue);
+
+        assertFalse(leafNode.equals(null));
+        assertFalse(leafNode.equals(new Object()));
+        assertFalse(leafNode.equals(otherLeafNode));
+        assertFalse(otherLeafNode.equals(leafNode));
+
+        byte[] value1 = "test".getBytes();
+        byte[] otherValue1 = "test1".getBytes();
+
+        LeafNode<byte[]> leafNode1 = ImmutableNodes
+                .leafNode(LEAF_QNAME, value1);
+        LeafNode<byte[]> otherLeafNode1 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, otherValue1);
+
+        assertFalse(leafNode1.equals(otherLeafNode1));
+        assertFalse(otherLeafNode1.equals(leafNode1));
+
+        Byte[] value2 = new Byte[] { new Byte("1"), new Byte("1") };
+        Byte[] otherValue2 = new Byte[] { new Byte("1"), new Byte("2") };
+
+        LeafNode<Byte[]> leafNode2 = ImmutableNodes
+                .leafNode(LEAF_QNAME, value2);
+        LeafNode<Byte[]> otherLeafNode2 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, otherValue2);
+
+        assertFalse(leafNode2.equals(otherLeafNode2));
+        assertFalse(otherLeafNode2.equals(leafNode2));
+
+        byte[][] value3 = new byte[][] { "test".getBytes(), "test2".getBytes() };
+        byte[][] otherValue3 = new byte[][] { "test".getBytes(),
+                "test3".getBytes() };
+
+        LeafNode<byte[][]> leafNode3 = ImmutableNodes.leafNode(LEAF_QNAME,
+                value3);
+        LeafNode<byte[][]> otherLeafNode3 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, otherValue3);
+
+        assertFalse(leafNode3.equals(otherLeafNode3));
+        assertFalse(otherLeafNode3.equals(leafNode3));
+
+        Byte[][] value4 = new Byte[][] {
+                new Byte[] { new Byte("1"), new Byte("2") },
+                new Byte[] { new Byte("3"), new Byte("4") } };
+        Byte[][] otherValue4 = new Byte[][] {
+                new Byte[] { new Byte("1"), new Byte("2") },
+                new Byte[] { new Byte("3"), new Byte("5") } };
+
+        LeafNode<Byte[][]> leafNode4 = ImmutableNodes.leafNode(LEAF_QNAME,
+                value4);
+        LeafNode<Byte[][]> otherLeafNode4 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, otherValue4);
+
+        assertFalse(leafNode4.equals(otherLeafNode4));
+        assertFalse(otherLeafNode4.equals(leafNode4));
+
+        Byte value6 = new Byte("1");
+        Byte otherValue6 = new Byte("2");
+
+        LeafNode<Byte> leafNode6 = ImmutableNodes.leafNode(LEAF_QNAME, value6);
+        LeafNode<Byte> otherLeafNode6 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, otherValue6);
+
+        assertFalse(leafNode6.equals(otherLeafNode6));
+        assertFalse(otherLeafNode6.equals(leafNode6));
+
+        String value5 = new String("test");
+        String otherValue5 = new String("test2");
+
+        LeafNode<String> leafNode5 = ImmutableNodes
+                .leafNode(LEAF_QNAME, value5);
+        LeafNode<String> otherLeafNode5 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, otherValue5);
+
+        assertFalse(leafNode5.equals(otherLeafNode5));
+        assertFalse(otherLeafNode5.equals(leafNode5));
+        assertFalse(leafNode5.equals(leafNode));
+        assertFalse(leafNode5.equals(leafNode1));
+        assertFalse(leafNode5.equals(leafNode2));
+        assertFalse(leafNode5.equals(leafNode3));
+        assertFalse(leafNode5.equals(leafNode4));
+        assertFalse(leafNode5.equals(leafNode6));
+        assertFalse(leafNode.equals(leafNode5));
+        assertFalse(leafNode1.equals(leafNode5));
+        assertFalse(leafNode2.equals(leafNode5));
+        assertFalse(leafNode3.equals(leafNode5));
+        assertFalse(leafNode4.equals(leafNode5));
+        assertFalse(leafNode6.equals(leafNode5));
+
+        byte[] valueNull = null;
+
+        LeafNode<byte[]> leafNodeNull = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, valueNull);
+        assertFalse(leafNodeNull.equals(leafNode));
+        assertFalse(leafNode.equals(leafNodeNull));
+
+        byte[] byteValue = new byte[] { new Byte("1").byteValue(),
+                new Byte("1").byteValue() };
+
+        LeafNode<byte[]> byteLeafNode = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, byteValue);
+        assertFalse(byteLeafNode.equals(leafNode2));
+        assertFalse(leafNode2.equals(byteLeafNode));
+
+    }
+
+    @Test
+    public void equalsOtherTypesTest() {
+
+        char[] valueChar = "test".toCharArray();
+        char[] equalValueChar = "test".toCharArray();
+
+        LeafNode<char[]> leafNodeChar = ImmutableNodes.leafNode(LEAF_QNAME,
+                valueChar);
+        LeafNode<char[]> equalLeafNodeChar = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValueChar);
+
+        assertTrue(leafNodeChar.equals(leafNodeChar));
+        assertTrue(leafNodeChar.equals(equalLeafNodeChar));
+        assertTrue(equalLeafNodeChar.equals(leafNodeChar));
+
+        boolean[] value = new boolean[] { true, false };
+        boolean[] equalValue = new boolean[] { true, false };
+
+        LeafNode<boolean[]> leafNode = ImmutableNodes.leafNode(LEAF_QNAME,
+                value);
+        LeafNode<boolean[]> equalLeafNode = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue);
+
+        assertTrue(leafNode.equals(leafNode));
+        assertTrue(leafNode.equals(equalLeafNode));
+        assertTrue(equalLeafNode.equals(leafNode));
+
+        int[] value2 = new int[] { 1, 2 };
+        int[] equalValue2 = new int[] { 1, 2 };
+
+        LeafNode<int[]> leafNode2 = ImmutableNodes.leafNode(LEAF_QNAME, value2);
+        LeafNode<int[]> equalLeafNode2 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue2);
+
+        assertTrue(leafNode2.equals(leafNode2));
+        assertTrue(leafNode2.equals(equalLeafNode2));
+        assertTrue(equalLeafNode2.equals(leafNode2));
+
+        short[] value3 = new short[] { 1, 2 };
+        short[] equalValue3 = new short[] { 1, 2 };
+
+        LeafNode<short[]> leafNode3 = ImmutableNodes.leafNode(LEAF_QNAME,
+                value3);
+        LeafNode<short[]> equalLeafNode3 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue3);
+
+        assertTrue(leafNode3.equals(leafNode3));
+        assertTrue(leafNode3.equals(equalLeafNode3));
+        assertTrue(equalLeafNode3.equals(leafNode3));
+
+        long[] value4 = new long[] { 1, 2 };
+        long[] equalValue4 = new long[] { 1, 2 };
+
+        LeafNode<long[]> leafNode4 = ImmutableNodes
+                .leafNode(LEAF_QNAME, value4);
+        LeafNode<long[]> equalLeafNode4 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue4);
+
+        assertTrue(leafNode4.equals(leafNode4));
+        assertTrue(leafNode4.equals(equalLeafNode4));
+        assertTrue(equalLeafNode4.equals(leafNode4));
+
+        double[] value6 = new double[] { 1, 2 };
+        double[] equalValue6 = new double[] { 1, 2 };
+
+        LeafNode<double[]> leafNode6 = ImmutableNodes.leafNode(LEAF_QNAME,
+                value6);
+        LeafNode<double[]> equalLeafNode6 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue6);
+
+        assertTrue(leafNode6.equals(leafNode6));
+        assertTrue(leafNode6.equals(equalLeafNode6));
+        assertTrue(equalLeafNode6.equals(leafNode6));
+
+        float[] value5 = new float[] { 1, 2 };
+        float[] equalValue5 = new float[] { 1, 2 };
+
+        LeafNode<float[]> leafNode5 = ImmutableNodes.leafNode(LEAF_QNAME,
+                value5);
+        LeafNode<float[]> equalLeafNode5 = ImmutableNodes.leafNode(
+                SAME_LEAF_QNAME, equalValue5);
+
+        assertTrue(leafNode5.equals(leafNode5));
+        assertTrue(leafNode5.equals(equalLeafNode5));
+        assertTrue(equalLeafNode5.equals(leafNode5));
+
+        assertFalse(leafNode.equals(leafNode5));
+        assertFalse(leafNode2.equals(leafNode5));
+        assertFalse(leafNode3.equals(leafNode5));
+        assertFalse(leafNode4.equals(leafNode5));
+        assertFalse(leafNodeChar.equals(leafNode5));
+        assertFalse(leafNode6.equals(leafNode5));
+
+        assertFalse(leafNode5.equals(leafNode));
+        assertFalse(leafNode5.equals(leafNode2));
+        assertFalse(leafNode5.equals(leafNode3));
+        assertFalse(leafNode5.equals(leafNode4));
+        assertFalse(leafNode5.equals(leafNodeChar));
+        assertFalse(leafNode5.equals(leafNode6));
+    }
+
+}
diff --git a/yang/yang-data-impl/src/test/resources/leafref-test.yang b/yang/yang-data-impl/src/test/resources/leafref-test.yang
new file mode 100644 (file)
index 0000000..3d31c89
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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
index 2fecd7f84904cf543095a7854d017313ac02af95..132edfc347bdb7dd2405aefd755cfc832a118bd9 100644 (file)
@@ -513,8 +513,7 @@ public final class SchemaContextUtil {
             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);
         }
@@ -571,32 +570,43 @@ public final class SchemaContextUtil {
      *            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) {
@@ -615,7 +625,7 @@ public final class SchemaContextUtil {
      *            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();
@@ -627,9 +637,14 @@ public final class SchemaContextUtil {
         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);
index ac9358c72eb55494f9affb955ff918747f8126d8..48d75a0feec6ac8e1afa9962fe051b2fdb2c099a 100644 (file)
@@ -105,7 +105,9 @@ fragment HEX : [0-9a-fA-F] ;
           
 END_IDENTIFIER_SEMICOLON : ';' -> type(SEMICOLON),popMode;
 END_IDENTIFIER_LEFT_BRACE : '{' ->type(LEFT_BRACE), popMode;
+
+START_INNER_BLOCK_COMMENT : '/*' ->pushMode(BLOCK_COMMENT_MODE), skip ;
+
 fragment SUB_STRING : ('"' (ESC | ~["])*'"') | ('\'' (ESC | ~['])*'\'') ;
 
 STRING: ((~( '\r' | '\n' | '\t' | ' ' | ';' | '{' | '"' | '\'')~( '\r' | '\n' | '\t' | ' ' | ';' | '{' )* ) | SUB_STRING ) ->popMode;// IDENTIFIER ;
index e6e357dc0e9664f21595bf54066caea591071500..ebe602d5a72c42a06fec63899b8d63a0d8b9d2ce 100644 (file)
@@ -63,7 +63,7 @@ final class SharedSchemaContextFactory implements SchemaContextFactory {
             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