Bug 2374 - YANG Binding: Added support for AugmentationHolder interface
authorTony Tkacik <ttkacik@cisco.com>
Tue, 31 Mar 2015 12:57:44 +0000 (14:57 +0200)
committerTony Tkacik <ttkacik@cisco.com>
Tue, 31 Mar 2015 12:57:44 +0000 (14:57 +0200)
Binding specification v1 was originally designed and implemented
in only one implementation of interfaces in mind, which were generated one,
but during Helium additional implementation was introduced, but
specification and generated copy builder constructor did not accounted for
that change. Added additional interface which is implemented by LazyDataObject
that allow copy of augmenations.

Updated code generator to allow for that change and implementation of
LazyDataObject to support new interface contract.

Change-Id: I15aec38259f7f5e95301368d0264dfcf0571e2ba
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
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

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 {
         }
     }
 
-
 }