Merge "Bug 1260: Implemented Binding Caching Codec"
authorRobert Varga <nite@hq.sk>
Mon, 13 Apr 2015 09:18:48 +0000 (09:18 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 13 Apr 2015 09:18:50 +0000 (09:18 +0000)
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/LazyDataObject.java
code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AugmentationSubstitutionTest.java
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BuilderTemplate.xtend
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AugmentationHolder.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/AugmentationFieldGetter.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/Bug2964Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/leafref-test.yang

index 074a20a790d602416ade8b4246d52c92a4c7888d..8302aed0e6348d283b45d974bf8c2c105ba77edb 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
+import org.opendaylight.yangtools.yang.binding.AugmentationHolder;
+
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
@@ -116,7 +118,7 @@ abstract class DataObjectCodecContext<D extends DataObject,T extends DataNodeCon
         byBindingArgClassBuilder.putAll(byStreamClass);
         this.byBindingArgClass = ImmutableMap.copyOf(byBindingArgClassBuilder);
 
-        final Class<?> proxyClass = Proxy.getProxyClass(getBindingClass().getClassLoader(),  new Class[] { getBindingClass() });
+        final Class<?> proxyClass = Proxy.getProxyClass(getBindingClass().getClassLoader(),  new Class[] { getBindingClass(), AugmentationHolder.class });
         try {
             proxyConstructor = LOOKUP.findConstructor(proxyClass, CONSTRUCTOR_TYPE).asType(DATAOBJECT_TYPE);
         } catch (NoSuchMethodException | IllegalAccessException e) {
@@ -161,7 +163,7 @@ abstract class DataObjectCodecContext<D extends DataObject,T extends DataNodeCon
     @SuppressWarnings("unchecked")
     @Override
     public <DV extends DataObject> Optional<DataContainerCodecContext<DV, ?>> possibleStreamChild(
-            Class<DV> childClass) {
+            final Class<DV> childClass) {
         final DataContainerCodecPrototype<?> childProto = byStreamClass.get(childClass);
         if(childProto != null) {
             return Optional.<DataContainerCodecContext<DV,?>>of((DataContainerCodecContext<DV,?>) childProto.get());
@@ -310,13 +312,13 @@ abstract class DataObjectCodecContext<D extends DataObject,T extends DataNodeCon
     }
 
     @Override
-    public InstanceIdentifier.PathArgument deserializePathArgument(YangInstanceIdentifier.PathArgument arg) {
+    public InstanceIdentifier.PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
         Preconditions.checkArgument(getDomPathArgument().equals(arg));
         return bindingArg();
     }
 
     @Override
-    public YangInstanceIdentifier.PathArgument serializePathArgument(InstanceIdentifier.PathArgument arg) {
+    public YangInstanceIdentifier.PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) {
         Preconditions.checkArgument(bindingArg().equals(arg));
         return getDomPathArgument();
     }
index b92acca00784e382c8db2aba813c8701e800b54d..f1b578b9e873bcc38cf27aab456c945cfcba0dea 100644 (file)
@@ -37,6 +37,7 @@ class LazyDataObject<D extends DataObject> implements InvocationHandler, Augment
     private static final String EQUALS = "equals";
     private static final String GET_AUGMENTATION = "getAugmentation";
     private static final String HASHCODE = "hashCode";
+    private static final String AUGMENTATIONS = "augmentations";
     private static final Object NULL_VALUE = new Object();
 
     private final ConcurrentHashMap<Method, Object> cachedData = new ConcurrentHashMap<>();
@@ -62,6 +63,8 @@ class LazyDataObject<D extends DataObject> implements InvocationHandler, Augment
                 return bindingToString();
             } else if (HASHCODE.equals(name)) {
                 return bindingHashCode();
+            } else if (AUGMENTATIONS.equals(name)) {
+                return getAugmentationsImpl();
             }
             return getBindingData(method);
         } else if (GET_AUGMENTATION.equals(method.getName())) {
index 6dc53e5f8f522614859498069a79dd5dd496f488..32006821d47eeb16310fe3cd18d0f3190abb41a5 100644 (file)
@@ -7,6 +7,10 @@
  */
 package org.opendaylight.yangtools.binding.data.codec.test;
 
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.base.Optional;
+import java.util.Collections;
 import javassist.ClassPool;
 import org.junit.Test;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.RpcComplexUsesAugment;
@@ -27,10 +31,6 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
-import java.util.Collections;
-
-import static org.junit.Assert.assertEquals;
-
 
 public class AugmentationSubstitutionTest extends AbstractBindingRuntimeTest {
 
@@ -48,26 +48,39 @@ public class AugmentationSubstitutionTest extends AbstractBindingRuntimeTest {
     @Override
     public void setup() {
         super.setup();
-        JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault());
+        final JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault());
         registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils));
         registry.onBindingRuntimeContextUpdated(getRuntimeContext());
     }
 
     @Test
     public void augmentationInGroupingSubstituted() {
-        TopLevelList baRpc = new TopLevelListBuilder()
+        final TopLevelList baRpc = new TopLevelListBuilder()
             .setKey(TOP_FOO_KEY)
             .addAugmentation(RpcComplexUsesAugment.class, new RpcComplexUsesAugmentBuilder(createComplexData()).build())
             .build();
-        TopLevelList baTree = new TopLevelListBuilder()
+        final TopLevelList baTree = new TopLevelListBuilder()
             .setKey(TOP_FOO_KEY)
             .addAugmentation(TreeComplexUsesAugment.class, new TreeComplexUsesAugmentBuilder(createComplexData()).build())
             .build();
-        NormalizedNode<?, ?> domTreeEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baTree).getValue();
-        NormalizedNode<?, ?> domRpcEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baRpc).getValue();
+        final NormalizedNode<?, ?> domTreeEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baTree).getValue();
+        final NormalizedNode<?, ?> domRpcEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baRpc).getValue();
         assertEquals(domTreeEntry, domRpcEntry);
     }
 
+    @Test
+    public void copyBuilderWithAugmenationsTest() {
+        final TopLevelList manuallyConstructed = new TopLevelListBuilder()
+            .setKey(TOP_FOO_KEY)
+            .addAugmentation(TreeComplexUsesAugment.class, new TreeComplexUsesAugmentBuilder(createComplexData()).build())
+            .build();
+        final NormalizedNode<?, ?> domTreeEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, manuallyConstructed).getValue();
+        final TopLevelList deserialized = registry.deserializeFunction(BA_TOP_LEVEL_LIST).apply(Optional.<NormalizedNode<?, ?>>of(domTreeEntry)).get();
+        assertEquals(manuallyConstructed, deserialized);
+        final TopLevelList copiedFromDeserialized = new TopLevelListBuilder(deserialized).build();
+        assertEquals(manuallyConstructed, copiedFromDeserialized);
+    }
+
     private RpcComplexUsesAugment createComplexData() {
         return new RpcComplexUsesAugmentBuilder()
         .setContainerWithUses(new ContainerWithUsesBuilder()
index 15e93756576ab94104cd4d33549e8b486937e6f9..710f1022104491baf8853ad89d4a2806be45a2eb 100644 (file)
@@ -32,6 +32,7 @@ import org.opendaylight.yangtools.yang.binding.Augmentable
 import org.opendaylight.yangtools.yang.binding.DataObject
 import org.opendaylight.yangtools.yang.binding.Identifiable
 import org.opendaylight.yangtools.concepts.Builder
+import org.opendaylight.yangtools.yang.binding.AugmentationHolder
 
 /**
  * Template for generating JAVA builder classes.
@@ -556,25 +557,28 @@ class BuilderTemplate extends BaseTemplate {
                 this.«field.fieldName» = base.«field.getterMethodName»();
             «ENDFOR»
             «IF augmentField != null»
-                «IF !impl»if (base instanceof «type.name»«IMPL») {«ENDIF»
-                    «IF !impl»«type.name»«IMPL» _impl = («type.name»«IMPL») base;«ENDIF»
-                    «val prop = if (impl) "base" else "_impl"»
-                    «IF impl»
-                        switch («prop».«augmentField.name».size()) {
-                        case 0:
-                            this.«augmentField.name» = «Collections.importedName».emptyMap();
-                            break;
-                            case 1:
-                                final «Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = «prop».«augmentField.name».entrySet().iterator().next();
-                                this.«augmentField.name» = «Collections.importedName».<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());
-                            break;
-                        default :
-                            this.«augmentField.name» = new «HashMap.importedName»<>(«prop».«augmentField.name»);
-                        }
-                    «ELSE»
-                        this.«augmentField.name» = new «HashMap.importedName»<>(«prop».«augmentField.name»);
-                    «ENDIF»
-                «IF !impl»}«ENDIF»
+                «IF impl»
+                    switch (base.«augmentField.name».size()) {
+                    case 0:
+                        this.«augmentField.name» = «Collections.importedName».emptyMap();
+                        break;
+                        case 1:
+                            final «Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = base.«augmentField.name».entrySet().iterator().next();
+                            this.«augmentField.name» = «Collections.importedName».<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());
+                        break;
+                    default :
+                        this.«augmentField.name» = new «HashMap.importedName»<>(base.«augmentField.name»);
+                    }
+                «ELSE»
+                    if (base instanceof «type.name»«IMPL») {
+                        «type.name»«IMPL» impl = («type.name»«IMPL») base;
+                        this.«augmentField.name» = new «HashMap.importedName»<>(impl.«augmentField.name»);
+                    } else if (base instanceof «AugmentationHolder.importedName») {
+                        @SuppressWarnings("unchecked")
+                        «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base;
+                        this.«augmentField.name» = new «HashMap.importedName»<>(casted.augmentations());
+                    }
+                «ENDIF»
             «ENDIF»
         }
     '''
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AugmentationHolder.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AugmentationHolder.java
new file mode 100644 (file)
index 0000000..472c3c2
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.binding;
+
+import java.util.Map;
+
+/**
+ *
+ * Augmentable (extensible) object which could carry additional data defined by
+ * third-party extension, without introducing conflict between various
+ * extension.
+ *
+ *
+ * @author Tony Tkacik
+ * @param <T>
+ *            Base class which should is target
+ *            for augmentations.
+ */
+public interface AugmentationHolder<T> {
+
+    /**
+     * Returns map of all augmentations.
+     *
+     * @return map of all augmentations.
+     */
+    Map<Class<? extends Augmentation<T>>,Augmentation<T>> augmentations();
+}
index 8178d8f1e1923580d490028cd6222159b1e69ace..2e8a33a272b7f542ba95a731679833f73914e777 100644 (file)
@@ -1,12 +1,14 @@
 /*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ * 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
+ * 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.binding.util;
 
+import org.opendaylight.yangtools.yang.binding.AugmentationHolder;
+
 import com.google.common.base.Preconditions;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
@@ -30,19 +32,31 @@ abstract class AugmentationFieldGetter {
         }
     };
 
-   /**
-    *
-    * Retrieves augmentations from supplied object
-    *
-    * @param input Input Data object, from which augmentations should be extracted
-    * @return Map of Augmentation class to augmentation
-    */
-   protected abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input);
+    private static final AugmentationFieldGetter AUGMENTATION_HOLDER_GETTER = new AugmentationFieldGetter() {
 
-    private static final LoadingCache<Class<?>, AugmentationFieldGetter> AUGMENTATION_GETTERS =
-            CacheBuilder.newBuilder().weakKeys().softValues().build(new AugmentationGetterLoader());
+        @Override
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        protected Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
+            return (Map) ((AugmentationHolder<?>) input).augmentations();
+        }
+    };
+
+    /**
+     *
+     * Retrieves augmentations from supplied object
+     *
+     * @param input Input Data object, from which augmentations should be extracted
+     * @return Map of Augmentation class to augmentation
+     */
+    protected abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input);
+
+    private static final LoadingCache<Class<?>, AugmentationFieldGetter> AUGMENTATION_GETTERS = CacheBuilder
+            .newBuilder().weakKeys().softValues().build(new AugmentationGetterLoader());
 
     public static AugmentationFieldGetter getGetter(final Class<? extends Object> clz) {
+        if(AugmentationHolder.class.isAssignableFrom(clz)) {
+            return AUGMENTATION_HOLDER_GETTER;
+        }
         return AUGMENTATION_GETTERS.getUnchecked(clz);
     }
 
@@ -81,5 +95,4 @@ abstract class AugmentationFieldGetter {
         }
     }
 
-
 }
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/Bug2964Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/Bug2964Test.java
new file mode 100644 (file)
index 0000000..3cf1b9b
--- /dev/null
@@ -0,0 +1,103 @@
+package org.opendaylight.yangtools.yang.data.impl.codec.xml;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.Lists;
+import com.google.common.io.ByteSource;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+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.parser.impl.YangParserImpl;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class Bug2964Test {
+
+    public static final String XML_CONTENT = "<cont2 xmlns=\"urn:opendaylight:yangtools:leafref:test\">\n" +
+            "<point-to-identityrefleaf>test-identity</point-to-identityrefleaf>\n" +
+            "</cont2>";
+
+    private static final DocumentBuilderFactory BUILDERFACTORY;
+
+    private static final String NAMESPACE = "urn:opendaylight:yangtools:leafref:test";
+    private static final String TEST_IDENTITY = "test-identity";
+    private static final String CONT_2 = "cont2";
+    private static final String IDENTITY_LEAFREF = "point-to-identityrefleaf";
+
+    static {
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        factory.setCoalescing(true);
+        factory.setIgnoringElementContentWhitespace(true);
+        factory.setIgnoringComments(true);
+        BUILDERFACTORY = factory;
+    }
+
+    private SchemaContext schema;
+
+    @Before
+    public void setUp() throws Exception {
+        final ByteSource byteSource = new ByteSource() {
+            @Override
+            public InputStream openStream() throws IOException {
+                return Bug2964Test.this.getClass().getResourceAsStream("/leafref-test.yang");
+            }
+        };
+        schema = new YangParserImpl().parseSources(Lists.newArrayList(byteSource));
+    }
+
+    public static Document readXmlToDocument(final String xmlContent) throws SAXException, IOException {
+        return readXmlToDocument(new ByteArrayInputStream(xmlContent.getBytes(Charsets.UTF_8)));
+    }
+
+    @Test
+    public void testLeafrefIdentityRefDeserialization() throws Exception {
+        final URI namespaceUri = new URI(NAMESPACE);
+
+        final Document document = readXmlToDocument(XML_CONTENT);
+        final Element identityLeafRefElement = (Element) document.getDocumentElement().getFirstChild().getNextSibling();
+
+        final Module leafrefModule = schema.findModuleByNamespaceAndRevision(
+                namespaceUri, null);
+        final ContainerSchemaNode cont2 = (ContainerSchemaNode) leafrefModule.getDataChildByName(CONT_2);
+        final DataSchemaNode identityLeafRefSchema = cont2.getDataChildByName(IDENTITY_LEAFREF);
+        final Object parsedValue = DomUtils.parseXmlValue(identityLeafRefElement, DomUtils.defaultValueCodecProvider(),
+                identityLeafRefSchema, ((LeafSchemaNode) identityLeafRefSchema).getType(), schema);
+
+        assertThat(parsedValue, instanceOf(QName.class));
+        final QName parsedQName = (QName) parsedValue;
+        assertEquals(namespaceUri, parsedQName.getNamespace());
+        assertEquals(TEST_IDENTITY, parsedQName.getLocalName());
+    }
+
+    public static Document readXmlToDocument(final InputStream xmlContent) throws SAXException, IOException {
+        final DocumentBuilder dBuilder;
+        try {
+            dBuilder = BUILDERFACTORY.newDocumentBuilder();
+        } catch (final ParserConfigurationException e) {
+            throw new IllegalStateException("Failed to parse XML document", e);
+        }
+        final Document doc = dBuilder.parse(xmlContent);
+
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+}
index 3d31c8900c92527643d64c1f3aabaf29a16a7d12..6851300c18270e1b48af290b0fe8486da074ed21 100644 (file)
@@ -14,6 +14,13 @@ module leafref-test {
         description "Test deserialization value of leafref type.";
     }
 
+    identity test-identity-base {
+    }
+
+    identity test-identity {
+      base lt:test-identity-base;
+    }
+
     container interface {
         leaf simpleValue {
             type instance-identifier;
@@ -33,6 +40,19 @@ module leafref-test {
                 path "../stringleaf";
             }
         }
+
+        leaf point-to-identityrefleaf {
+            type leafref {
+                path "../identityrefleaf";
+            }
+        }
+
+        leaf identityrefleaf {
+            type identityref {
+              base lt:test-identity-base;
+            }
+        }
+
         leaf stringleaf {
             type string;
         }
@@ -53,4 +73,4 @@ module leafref-test {
             }
         }
     }
-}
\ No newline at end of file
+}