BUG-1196: fixed bug in choice case codec. 71/8071/4
authorMartin Vitez <mvitez@cisco.com>
Tue, 17 Jun 2014 11:12:13 +0000 (13:12 +0200)
committerTony Tkacik <ttkacik@cisco.com>
Tue, 17 Jun 2014 18:50:17 +0000 (20:50 +0200)
Implemented lazy generation of choice case codecs by
using DispatchChoiceCodecImpl - delegator for de/serialization
of case instances in
PublicChoiceCodecImpl. This codec is created for each interface
generated from yang choice. It contains information about all
implementations of choice-cases in context and delegate
de/serialization of concrete case instance to correct case
codec based on given InstanceIdentifier.

ChoiceCaseCodecImpl instances are created when concrete case
is encountered.

By using DispatchChoiceCodecImpl we don't need to hold
COMPOSITE_TO_CASE and CLASS_TO_CASE_MAP variables in runtime
generated codec which were
used to serialization and deserialization before.

Added test which test serialization / deserialization.

Change-Id: I52eb78f66e91695758b825b109639d039860f997
Signed-off-by: Martin Vitez <mvitez@cisco.com>
17 files changed:
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.xtend
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/CodecMapping.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/ModuleContext.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/TransformerGenerator.xtend
common/parent/pom.xml
integration-test/bug1196-test-model/pom.xml [new file with mode: 0644]
integration-test/bug1196-test-model/src/main/yang/network-topology-pcep.yang [new file with mode: 0644]
integration-test/bug1196-test-model/src/main/yang/network-topology-unix.yang [new file with mode: 0644]
integration-test/bug1196-test-model/src/main/yang/network-topology@2013-10-21.yang [new file with mode: 0644]
integration-test/bug1196-test-model/src/main/yang/odl-pcep-ietf-stateful07.yang [new file with mode: 0644]
integration-test/bug1196-test-model/src/main/yang/pcep-types.yang [new file with mode: 0644]
integration-test/pom.xml
restconf/restconf-util/pom.xml
restconf/restconf-util/src/test/java/org/opendaylight/yangtools/restconf/utils/Bug1196Test.java [new file with mode: 0644]
restconf/restconf-util/src/test/resources/topology-bug1196-linux.xml [new file with mode: 0644]
restconf/restconf-util/src/test/resources/topology-bug1196-unix.xml [new file with mode: 0644]

index 8a41bef08f88a24889e712412508735e47227f87..ba8259e9b30627c064d1e041312963e4acafb427 100644 (file)
@@ -1148,6 +1148,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                 val caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode)
                 caseTypeBuilder.addImplementsType(refChoiceType)
                 genCtx.get(module).addCaseType(caseNode.path, caseTypeBuilder)
+                genCtx.get(module).addChoiceToCaseMapping(refChoiceType, caseTypeBuilder,caseNode)
                 val Set<DataSchemaNode> caseChildNodes = caseNode.childNodes
                 if (caseChildNodes !== null) {
                     var Object parentNode = null
@@ -1250,21 +1251,18 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     throw new IllegalArgumentException("Failed to find parent type of choice " + targetNode);
                 }
 
-                if (caseNode instanceof DataNodeContainer) {
-                    val DataNodeContainer dataNodeCase = caseNode as DataNodeContainer;
-                    val Set<DataSchemaNode> childNodes = dataNodeCase.childNodes;
-                    if (childNodes !== null) {
-                        resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType, childNodes);
-                    }
-                } else {
-                    val ChoiceCaseNode node = targetNode.getCaseNodeByName(caseNode.getQName().getLocalName());
-                    val Set<DataSchemaNode> childNodes = node.childNodes;
-                    if (childNodes !== null) {
-                        resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType, childNodes);
-                    }
+                var ChoiceCaseNode node = null;
+                if (caseNode instanceof ChoiceCaseNode) {
+                    node = caseNode as ChoiceCaseNode;
+                }  else {
+                    node = targetNode.getCaseNodeByName(caseNode.getQName().getLocalName());
+                }
+                val Set<DataSchemaNode> childNodes = node.childNodes;
+                if (childNodes !== null) {
+                    resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType, childNodes);
                 }
-
                 genCtx.get(module).addCaseType(caseNode.path, caseTypeBuilder)
+                genCtx.get(module).addChoiceToCaseMapping(targetType, caseTypeBuilder,node);
             }
         }
 
index 9a4ce78541f13ed7461bf66153a99a51b2fad3a2..2ef288f79703c09936b77fb486535995db5580b9 100644 (file)
@@ -9,11 +9,10 @@ package org.opendaylight.yangtools.sal.binding.generator.impl;
 
 import java.lang.reflect.Field;
 import java.util.Map;
-
-import org.opendaylight.yangtools.yang.data.impl.codec.IdentityCodec;
-import org.opendaylight.yangtools.yang.data.impl.codec.InstanceIdentifierCodec;
 import org.opendaylight.yangtools.yang.binding.BindingCodec;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.IdentityCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.InstanceIdentifierCodec;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -27,6 +26,7 @@ public class CodecMapping {
     public static final String CLASS_TO_CASE_MAP = "CLASS_TO_CASE";
     public static final String COMPOSITE_TO_CASE = "COMPOSITE_TO_CASE";
     public static final String AUGMENTATION_CODEC = "AUGMENTATION_CODEC";
+    public static final String DISPATCH_CODEC = "DISPATCH_CODEC";
 
     public static void setIdentifierCodec(Class<?> obj,InstanceIdentifierCodec codec) {
         Field instanceIdField;
@@ -82,6 +82,19 @@ public class CodecMapping {
         }
     }
 
+    public static void setDispatchCodec(Class<? extends BindingCodec<?,?>> codec,
+            BindingCodec<?, ?> dispatchCodec) {
+        Field instanceIdField;
+        try {
+            instanceIdField = codec.getField(DISPATCH_CODEC);
+            instanceIdField.set(null, dispatchCodec);
+        } catch (NoSuchFieldException e) {
+            LOG.debug("BUG: dispatch codec is not needed for {}",codec.getName(),e);
+        } catch (SecurityException | IllegalAccessException e) {
+            LOG.error("Dispatch codec could not be set for {}",codec.getName(),e);
+        }
+    }
+
     public static void setAugmentationCodec(Class<? extends BindingCodec<?,?>> dataCodec,
             BindingCodec<?,?> augmentableCodec) {
             Field instanceIdField;
index e62f28e6469f2ad1f9a2af9e319b7ca92650ec4a..c251ba18203add8324274bf6a6a8e63563eb263e 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.yangtools.sal.binding.generator.impl;
 
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
+import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -18,7 +19,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Objects;
 import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
@@ -33,7 +33,6 @@ import org.opendaylight.yangtools.sal.binding.generator.util.CodeGenerationExcep
 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
-import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
@@ -69,7 +68,6 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -78,6 +76,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
 
@@ -87,7 +86,6 @@ class LazyGeneratedCodecRegistry implements //
         GeneratorListener {
 
     private static final Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class);
-    private static final LateMixinCodec NOT_READY_CODEC = new LateMixinCodec();
 
     // Concrete class to codecs
     private static final Map<Class<?>, DataContainerCodec<?>> containerCodecs = Collections
@@ -109,8 +107,7 @@ class LazyGeneratedCodecRegistry implements //
     @SuppressWarnings("rawtypes")
     private static final Map<Type, WeakReference<Class>> typeToClass = new ConcurrentHashMap<>();
 
-    @SuppressWarnings("rawtypes")
-    private static final ConcurrentMap<Type, ChoiceCaseCodecImpl> typeToCaseCodecs = new ConcurrentHashMap<>();
+    private static final ConcurrentMap<Type, ChoiceCaseNode> caseTypeToCaseSchema = new ConcurrentHashMap<>();
 
     private static final Map<SchemaPath, Type> pathToType = new ConcurrentHashMap<>();
     private static final Map<List<QName>, Type> pathToInstantiatedType = new ConcurrentHashMap<>();
@@ -124,7 +121,6 @@ class LazyGeneratedCodecRegistry implements //
             .<Type, Type> create());
 
     private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
-    private final CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade();
     private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec();
     private final ClassLoadingStrategy classLoadingStrategy;
     private final AbstractTransformerGenerator generator;
@@ -392,15 +388,12 @@ class LazyGeneratedCodecRegistry implements //
             return potential;
         }
         ConcreteType typeref = Types.typeForClass(caseClass);
-        ChoiceCaseCodecImpl caseCodec = typeToCaseCodecs.get(typeref);
+        ChoiceCaseNode caseSchema = caseTypeToCaseSchema.get(typeref);
 
-        Preconditions.checkState(caseCodec != null, "Case Codec was not created proactivelly for %s",
-                caseClass.getName());
-        Preconditions.checkState(caseCodec.getSchema() != null, "Case schema is not available for %s",
-                caseClass.getName());
-        Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseCodec.getSchema());
+        Preconditions.checkState(caseSchema != null, "Case schema is not available for %s", caseClass.getName());
+        Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseSchema);
         BindingCodec newInstance = newInstanceOf(newCodec);
-        caseCodec.setDelegate(newInstance);
+        ChoiceCaseCodecImpl caseCodec = new ChoiceCaseCodecImpl(caseClass, caseSchema, newInstance);
         caseCodecs.put(caseClass, caseCodec);
 
         for (Entry<Class<?>, PublicChoiceCodecImpl<?>> choice : choiceCodecs.entrySet()) {
@@ -437,29 +430,8 @@ class LazyGeneratedCodecRegistry implements //
         synchronized (choiceToCases) {
             choiceToCases.putAll(context.getChoiceToCases());
         }
-        captureCases(context.getCases(), schemaContext);
-    }
-
-    private void captureCases(final Map<SchemaPath, GeneratedTypeBuilder> cases, final SchemaContext module) {
-        for (Entry<SchemaPath, GeneratedTypeBuilder> caseNode : cases.entrySet()) {
-            ReferencedTypeImpl typeref = new ReferencedTypeImpl(caseNode.getValue().getPackageName(), caseNode
-                    .getValue().getName());
-
-            pathToType.put(caseNode.getKey(), caseNode.getValue());
-
-            ChoiceCaseNode node = (ChoiceCaseNode) SchemaContextUtil.findDataSchemaNode(module, caseNode.getKey());
-
-            if (node == null) {
-                LOG.warn("Failed to find YANG SchemaNode for {}, with path {} was not found in context.",
-                        typeref.getFullyQualifiedName(), caseNode.getKey());
-                @SuppressWarnings("rawtypes")
-                ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl();
-                typeToCaseCodecs.putIfAbsent(typeref, value);
-                continue;
-            }
-            @SuppressWarnings("rawtypes")
-            ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl(node);
-            typeToCaseCodecs.putIfAbsent(typeref, value);
+        synchronized (caseTypeToCaseSchema) {
+            caseTypeToCaseSchema.putAll(context.getCaseTypeToSchemas());
         }
     }
 
@@ -476,43 +448,9 @@ class LazyGeneratedCodecRegistry implements //
         Preconditions.checkState(oldCodec == null);
         BindingCodec<Map<QName, Object>, Object> delegate = newInstanceOf(choiceCodec);
         PublicChoiceCodecImpl<?> newCodec = new PublicChoiceCodecImpl(delegate);
+        DispatchChoiceCodecImpl dispatchCodec = new DispatchChoiceCodecImpl(choiceClass);
         choiceCodecs.put(choiceClass, newCodec);
-        CodecMapping.setClassToCaseMap(choiceCodec, classToCaseRawCodec);
-        CodecMapping.setCompositeNodeToCaseMap(choiceCodec, newCodec.getCompositeToCase());
-
-        tryToCreateCasesCodecs(schema);
-
-    }
-
-    @Deprecated
-    private void tryToCreateCasesCodecs(final ChoiceNode schema) {
-        for (ChoiceCaseNode choiceCase : schema.getCases()) {
-            ChoiceCaseNode caseNode = choiceCase;
-            if (caseNode.isAddedByUses()) {
-                DataSchemaNode origCaseNode = SchemaContextUtil.findOriginal(caseNode, currentSchema);
-                if (origCaseNode instanceof ChoiceCaseNode) {
-                    caseNode = (ChoiceCaseNode) origCaseNode;
-                }
-            }
-            SchemaPath path = caseNode.getPath();
-
-            Type type;
-            if (path != null && (type = pathToType.get(path)) != null) {
-                ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
-                @SuppressWarnings("rawtypes")
-                ChoiceCaseCodecImpl partialCodec = typeToCaseCodecs.get(typeref);
-                if (partialCodec.getSchema() == null) {
-                    partialCodec.setSchema(caseNode);
-                }
-                try {
-                    Class<?> caseClass = classLoadingStrategy.loadClass(type.getFullyQualifiedName());
-                    getCaseCodecFor(caseClass);
-                } catch (ClassNotFoundException e) {
-                    LOG.trace("Could not proactivelly create case codec for {}", type, e);
-                }
-            }
-        }
-
+        CodecMapping.setDispatchCodec(choiceCodec, dispatchCodec);
     }
 
     @Override
@@ -734,41 +672,21 @@ class LazyGeneratedCodecRegistry implements //
     @SuppressWarnings("rawtypes")
     private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
             Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
-        private boolean augmenting;
-        private boolean uses;
-        private BindingCodec delegate;
-
-        private Set<String> validNames;
-        private Set<QName> validQNames;
-        private ChoiceCaseNode schema;
-        private Set<InstanceIdentifier<?>> applicableLocations;
+        private final BindingCodec delegate;
+        private final ChoiceCaseNode schema;
+        private final Map<InstanceIdentifier<?>, ChoiceCaseNode> instantiatedLocations;
+        private final Class<?> dataType;
 
         @Override
         public boolean isApplicable(final InstanceIdentifier location) {
-            return applicableLocations.contains(location);
+            return instantiatedLocations.containsKey(location);
         }
 
-        public void setSchema(final ChoiceCaseNode caseNode) {
+        public ChoiceCaseCodecImpl(final Class<?> caseClass, final ChoiceCaseNode caseNode, final BindingCodec newInstance) {
+            this.delegate = newInstance;
+            this.dataType = caseClass;
             this.schema = caseNode;
-            validNames = new HashSet<>();
-            validQNames = new HashSet<>();
-            for (DataSchemaNode node : caseNode.getChildNodes()) {
-                QName qname = node.getQName();
-                validQNames.add(qname);
-                validNames.add(qname.getLocalName());
-            }
-            augmenting = caseNode.isAugmenting();
-            uses = caseNode.isAddedByUses();
-            applicableLocations = new HashSet<>();
-        }
-
-        public ChoiceCaseCodecImpl() {
-            this.delegate = NOT_READY_CODEC;
-        }
-
-        public ChoiceCaseCodecImpl(final ChoiceCaseNode caseNode) {
-            this.delegate = NOT_READY_CODEC;
-            setSchema(caseNode);
+            instantiatedLocations = new HashMap<>();
         }
 
         @Override
@@ -778,7 +696,19 @@ class LazyGeneratedCodecRegistry implements //
 
         @Override
         public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
-            throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
+            if (input == null) {
+                return null;
+            }
+            QName qname = input.getNodeType();
+            synchronized (instantiatedLocations) {
+                ChoiceCaseNode instantiation = instantiatedLocations.get(bindingIdentifier);
+                if (instantiation != null) {
+                    qname = instantiatedLocations.get(bindingIdentifier).getQName();
+                }
+            }
+            @SuppressWarnings("unchecked")
+            T value = (T) getDelegate().deserialize(new SimpleEntry(qname, input), bindingIdentifier);
+            return new ValueWithQName<T>(qname, value);
         }
 
         @Override
@@ -791,56 +721,49 @@ class LazyGeneratedCodecRegistry implements //
             return delegate;
         }
 
-        public void setDelegate(final BindingCodec delegate) {
-            this.delegate = delegate;
-        }
-
         public ChoiceCaseNode getSchema() {
             return schema;
         }
 
         @Override
+        @Deprecated
         public boolean isAcceptable(final Node<?> input) {
-            if (input instanceof CompositeNode) {
-                if (augmenting && !uses) {
-                    return checkAugmenting((CompositeNode) input);
-                } else {
-                    return checkLocal((CompositeNode) input);
-                }
-            }
-            return false;
+            return checkAgainstSchema(schema, input);
         }
 
-        @SuppressWarnings("deprecation")
-        private boolean checkLocal(final CompositeNode input) {
-            QName parent = input.getNodeType();
-            for (Node<?> childNode : input.getChildren()) {
-                QName child = childNode.getNodeType();
-                if (!Objects.equals(parent.getNamespace(), child.getNamespace())
-                        || !Objects.equals(parent.getRevision(), child.getRevision())) {
-                    continue;
-                }
-                if (validNames.contains(child.getLocalName())) {
-                    return true;
+        private static boolean checkAgainstSchema(final ChoiceCaseNode schema, final Node<?> node) {
+            if (node instanceof CompositeNode) {
+                CompositeNode input = (CompositeNode) node;
+                for (Node<?> childNode : input.getValue()) {
+                    QName child = childNode.getNodeType();
+                    if (schema.getDataChildByName(child) != null) {
+                        return true;
+                    }
                 }
             }
             return false;
         }
 
-        @SuppressWarnings("deprecation")
-        private boolean checkAugmenting(final CompositeNode input) {
-            for (Node<?> child : input.getChildren()) {
-                if (validQNames.contains(child.getNodeType())) {
-                    return true;
-                }
+        @Override
+        public Class<?> getDataType() {
+            return dataType;
+        }
+
+        public void adaptForPath(final InstanceIdentifier<?> augTarget, final ChoiceCaseNode choiceCaseNode) {
+            synchronized (instantiatedLocations) {
+                instantiatedLocations.put(augTarget, choiceCaseNode);
             }
-            return false;
         }
 
-        @Override
-        public Class<?> getDataType() {
-            // TODO Auto-generated method stub
-            throw new UnsupportedOperationException("Not implemented Yet.");
+        public boolean isAcceptable(final InstanceIdentifier path, final CompositeNode input) {
+            ChoiceCaseNode instantiatedSchema = null;
+            synchronized (instantiatedLocations) {
+                instantiatedSchema = instantiatedLocations.get(path);
+            }
+            if (instantiatedSchema == null) {
+                return false;
+            }
+            return checkAgainstSchema(instantiatedSchema, input);
         }
     }
 
@@ -853,11 +776,8 @@ class LazyGeneratedCodecRegistry implements //
         private final Map<Class, ChoiceCaseCodecImpl<?>> cases = Collections
                 .synchronizedMap(new WeakHashMap<Class, ChoiceCaseCodecImpl<?>>());
 
-        private final CaseCompositeNodeMapFacade CompositeToCase;
-
         public PublicChoiceCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
             this.delegate = delegate;
-            this.CompositeToCase = new CaseCompositeNodeMapFacade(cases);
         }
 
         @Override
@@ -875,10 +795,6 @@ class LazyGeneratedCodecRegistry implements //
             throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
         }
 
-        public CaseCompositeNodeMapFacade getCompositeToCase() {
-            return CompositeToCase;
-        }
-
         @Override
         public BindingCodec<Map<QName, Object>, Object> getDelegate() {
             return delegate;
@@ -886,163 +802,141 @@ class LazyGeneratedCodecRegistry implements //
 
     }
 
-    @SuppressWarnings("unused")
-    private class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec<ChoiceCaseCodecImpl<?>> {
+    class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec<ChoiceCaseCodecImpl<?>> {
+        private final Class<?> choiceType;
+        private final QName choiceName;
 
-        @Override
-        public Object deserialize(final Object input,
-                @SuppressWarnings("rawtypes") final InstanceIdentifier bindingIdentifier) {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        @Override
-        public Object serialize(final Object input) {
-            // TODO Auto-generated method stub
-            return null;
+        private DispatchChoiceCodecImpl(final Class<?> type) {
+            choiceType = type;
+            choiceName = BindingReflections.findQName(type);
         }
 
         @Override
-        protected ChoiceCaseCodecImpl<?> tryToLoadImplementation(final Class<? extends DataContainer> inputType) {
-            return getCaseCodecFor(inputType);
-        }
-
-        @Override
-        protected void tryToLoadImplementations() {
-            // TODO Auto-generated method stub
-
-        }
-
-        @Override
-        protected void adaptForPathImpl(final InstanceIdentifier<?> path, final DataNodeContainer ctx) {
-            // TODO Auto-generated method stub
-
-        }
-    }
-
-    @SuppressWarnings("rawtypes")
-    private class CaseClassMapFacade extends MapFacadeBase {
-
-        @Override
-        public Set<Entry<Class, BindingCodec<Object, Object>>> entrySet() {
-            return Collections.emptySet();
-        }
-
-        @Override
-        public BindingCodec get(final Object key) {
-            if (key instanceof Class) {
-                Class cls = (Class) key;
-                // bindingClassEncountered(cls);
-                ChoiceCaseCodecImpl caseCodec = getCaseCodecFor(cls);
-                return caseCodec.getDelegate();
-            }
-            return null;
-        }
-    }
-
-    @SuppressWarnings("rawtypes")
-    private static class CaseCompositeNodeMapFacade extends MapFacadeBase<CompositeNode> {
-
-        final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases;
-
-        public CaseCompositeNodeMapFacade(final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases) {
-            this.choiceCases = choiceCases;
-        }
+        public Object deserialize(final Object input, @SuppressWarnings("rawtypes") final InstanceIdentifier path) {
+            adaptForPath(path);
 
-        @Override
-        public BindingCodec get(final Object key) {
-            if (!(key instanceof CompositeNode)) {
-                return null;
-            }
-            for (Entry<Class, ChoiceCaseCodecImpl<?>> entry : choiceCases.entrySet()) {
-                ChoiceCaseCodecImpl<?> codec = entry.getValue();
-                if (codec.isAcceptable((CompositeNode) key)) {
-                    return codec.getDelegate();
+            if (input instanceof CompositeNode) {
+                List<Entry<Class, ChoiceCaseCodecImpl<?>>> codecs = new ArrayList<>(getImplementations().entrySet());
+                for (Entry<Class, ChoiceCaseCodecImpl<?>> codec : codecs) {
+                    ChoiceCaseCodecImpl<?> caseCodec = codec.getValue();
+                    if (caseCodec.isAcceptable(path, (CompositeNode) input)) {
+                        ValueWithQName<?> value = caseCodec.deserialize((CompositeNode) input, path);
+                        if (value != null) {
+                            return value.getValue();
+                        }
+                        return null;
+                    }
                 }
             }
             return null;
         }
 
-    }
-
-    /**
-     * This map is used as only facade for
-     * {@link org.opendaylight.yangtools.yang.binding.BindingCodec} in different
-     * classloaders to retrieve codec dynamicly based on provided key.
-     *
-     * @param <T>
-     *            Key type
-     */
-    @SuppressWarnings("rawtypes")
-    private abstract static class MapFacadeBase<T> implements Map<T, BindingCodec<?, ?>> {
-
-        @Override
-        public boolean containsKey(final Object key) {
-            return get(key) != null;
-        }
-
-        @Override
-        public void clear() {
-            throw notModifiable();
-        }
-
-        @Override
-        public boolean equals(final Object obj) {
-            return super.equals(obj);
-        }
-
-        @Override
-        public BindingCodec remove(final Object key) {
-            return null;
-        }
-
+        @SuppressWarnings("unchecked")
         @Override
-        public int size() {
-            return 0;
+        public Object serialize(final Object input) {
+            Preconditions.checkArgument(input instanceof Map.Entry<?, ?>, "Input must be QName, Value");
+            @SuppressWarnings("rawtypes")
+            Object inputValue = ((Map.Entry) input).getValue();
+            Preconditions.checkArgument(inputValue instanceof DataObject);
+            Class<? extends DataContainer> inputType = ((DataObject) inputValue).getImplementedInterface();
+            ChoiceCaseCodecImpl<?> codec = tryToLoadImplementation(inputType);
+            Preconditions.checkState(codec != null, "Unable to get codec for %s", inputType);
+            if(isAugmenting(codec.getSchema())) {
+                // If choice is augmenting we use QName which defined this augmentation
+                return codec.getDelegate().serialize(new ValueWithQName<>(codec.getSchema().getQName(), inputValue));
+            }
+            return codec.getDelegate().serialize(input);
         }
 
-        @Override
-        public Collection<BindingCodec<?, ?>> values() {
-            return Collections.emptySet();
+        private boolean isAugmenting(final ChoiceCaseNode schema) {
+            if(schema.isAugmenting()) {
+                return true;
+            }
+            QName parentQName = Iterables.get(schema.getPath().getPathTowardsRoot(), 2); // choice QName
+            if(!parentQName.getNamespace().equals(schema.getQName().getNamespace())) {
+                return true;
+            }
+            return false;
         }
 
-        private UnsupportedOperationException notModifiable() {
-            return new UnsupportedOperationException("Not externally modifiable.");
+        @SuppressWarnings("rawtypes")
+        protected Optional<ChoiceCaseCodecImpl> tryToLoadImplementation(final Type potential) {
+            try {
+                @SuppressWarnings("unchecked")
+                Class<? extends DataContainer> clazz = (Class<? extends DataContainer>) classLoadingStrategy
+                        .loadClass(potential);
+                ChoiceCaseCodecImpl codec = tryToLoadImplementation(clazz);
+                addImplementation(codec);
+                return Optional.of(codec);
+            } catch (ClassNotFoundException e) {
+                LOG.warn("Failed to find class for choice {}", potential, e);
+            }
+            return Optional.absent();
         }
 
         @Override
-        public BindingCodec<Map<QName, Object>, Object> put(final T key, final BindingCodec<?, ?> value) {
-            throw notModifiable();
+        protected ChoiceCaseCodecImpl<?> tryToLoadImplementation(final Class<? extends DataContainer> inputType) {
+            ChoiceCaseCodecImpl<?> codec = getCaseCodecFor(inputType);
+            addImplementation(codec);
+            return codec;
         }
 
         @Override
-        public void putAll(final Map<? extends T, ? extends BindingCodec<?, ?>> m) {
-            throw notModifiable();
+        protected void tryToLoadImplementations() {
+            Type type = referencedType(choiceType);
+            Collection<Type> potentialCases;
+            synchronized (choiceToCases) {
+                potentialCases = choiceToCases.get(type);
+            }
+            for (Type potential : potentialCases) {
+                try {
+                    tryToLoadImplementation(potential);
+                } catch (CodeGenerationException e) {
+                    LOG.warn("Failed to proactively generate choice code for {}", type, e);
+                }
+            }
         }
 
         @Override
-        public int hashCode() {
-            return super.hashCode();
+        protected void adaptForPathImpl(final InstanceIdentifier<?> augTarget, final DataNodeContainer ctxNode) {
+            Optional<ChoiceNode> newChoice = findInstantiatedChoice(ctxNode, choiceName);
+            tryToLoadImplementations();
+            if (newChoice.isPresent()) {
+                for (@SuppressWarnings("rawtypes")
+                Entry<Class, ChoiceCaseCodecImpl<?>> codec : getImplementations().entrySet()) {
+                    ChoiceCaseCodecImpl<?> caseCodec = codec.getValue();
+                    Optional<ChoiceCaseNode> instantiatedSchema = findInstantiatedCase(newChoice.get(),
+                            caseCodec.getSchema());
+                    if (instantiatedSchema.isPresent()) {
+                        caseCodec.adaptForPath(augTarget, instantiatedSchema.get());
+                    }
+                }
+            }
         }
 
-        @Override
-        public boolean isEmpty() {
-            return true;
-        }
+        private Optional<ChoiceNode> findInstantiatedChoice(final DataNodeContainer ctxNode, final QName choiceName) {
+            DataSchemaNode potential = ctxNode.getDataChildByName(choiceName);
+            if (potential == null) {
+                potential = ctxNode.getDataChildByName(choiceName.getLocalName());
+            }
 
-        @Override
-        public Set<T> keySet() {
-            return Collections.emptySet();
-        }
+            if (potential instanceof ChoiceNode) {
+                return Optional.of((ChoiceNode) potential);
+            }
 
-        @Override
-        public Set<Entry<T, BindingCodec<?, ?>>> entrySet() {
-            return Collections.emptySet();
+            return Optional.absent();
         }
 
-        @Override
-        public boolean containsValue(final Object value) {
-            return false;
+        private Optional<ChoiceCaseNode> findInstantiatedCase(final ChoiceNode newChoice, final ChoiceCaseNode schema) {
+            ChoiceCaseNode potential = newChoice.getCaseNodeByName(schema.getQName());
+            if (potential != null) {
+                return Optional.of(potential);
+            }
+            // FIXME: Probably requires more extensive check
+            // e.g. we have one choice and two augmentations from different
+            // modules using same local name
+            // but different namespace / contents
+            return Optional.fromNullable(newChoice.getCaseNodeByName(schema.getQName().getLocalName()));
         }
     }
 
@@ -1224,36 +1118,6 @@ class LazyGeneratedCodecRegistry implements //
         }
     }
 
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private static class LateMixinCodec implements BindingCodec, Delegator<BindingCodec> {
-
-        private BindingCodec delegate;
-
-        @Override
-        public BindingCodec getDelegate() {
-            if (delegate == null) {
-                throw new IllegalStateException("Codec not initialized yet.");
-            }
-            return delegate;
-        }
-
-        @Override
-        public Object deserialize(final Object input) {
-            return getDelegate().deserialize(input);
-        }
-
-        @Override
-        public Object deserialize(final Object input, final InstanceIdentifier bindingIdentifier) {
-            return getDelegate().deserialize(input, bindingIdentifier);
-        }
-
-        @Override
-        public Object serialize(final Object input) {
-            return getDelegate().serialize(input);
-        }
-
-    }
-
     @SuppressWarnings("rawtypes")
     private static class AugmentationCodecWrapper<T extends Augmentation<?>> implements AugmentationCodec<T>,
             Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
index 48b6947f9a0627b7cab2862561c2115788bf1d05..acb939d14962f0df818af2950bb1e8b5a1c35ede 100644 (file)
@@ -7,25 +7,24 @@
  */
 package org.opendaylight.yangtools.sal.binding.generator.impl;
 
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-
 public final class ModuleContext {
     private GeneratedTypeBuilder moduleNode;
     private final List<GeneratedTOBuilder> genTOs = new ArrayList<GeneratedTOBuilder>();
@@ -39,6 +38,7 @@ public final class ModuleContext {
     private final BiMap<Type,AugmentationSchema> typeToAugmentation = HashBiMap.create();
 
     private final Multimap<Type, Type> choiceToCases = HashMultimap.create();
+    private final BiMap<Type,ChoiceCaseNode> caseTypeToSchema = HashBiMap.create();
 
     private final Multimap<Type, Type> augmentableToAugmentations = HashMultimap.create();
 
@@ -178,4 +178,13 @@ public final class ModuleContext {
         augmentableToAugmentations.put(target,augmentation);
     }
 
+    public void addChoiceToCaseMapping(Type choiceType, Type caseType, ChoiceCaseNode schema) {
+        choiceToCases.put(choiceType, caseType);
+        caseTypeToSchema.put(caseType, schema);
+    }
+
+    public BiMap<Type, ChoiceCaseNode> getCaseTypeToSchemas() {
+        return caseTypeToSchema;
+    }
+
 }
index 8821411022763d504a06e3fd9f1078005b898ee0..792b0b9cc3d1cc470d4d463d7dfb7a79e2b30102 100644 (file)
@@ -625,8 +625,7 @@ class TransformerGenerator extends AbstractTransformerGenerator {
                 //staticQNameField(inputType);
                 staticField(it, INSTANCE_IDENTIFIER_CODEC, BindingCodec)
                 staticField(it, IDENTITYREF_CODEC, BindingCodec)
-                staticField(it, CLASS_TO_CASE_MAP, Map)
-                staticField(it, COMPOSITE_TO_CASE, Map)
+                staticField(it, DISPATCH_CODEC, BindingCodec)
                 //staticField(it,QNAME_TO_CASE_MAP,BindingCodec)
                 implementsType(BINDING_CODEC)
                 method(List, "toDomStatic", #[QName, Object]) [
@@ -636,14 +635,11 @@ class TransformerGenerator extends AbstractTransformerGenerator {
                             if($2 == null) {
                                 return null;
                             }
-                            Â«DataObject.name» _baValue = («DataObject.name») $2;
-                            Class _baClass = _baValue.getImplementedInterface();
-                            Â«BINDING_CODEC.name» _codec =  Â«CLASS_TO_CASE_MAP».get(_baClass);
-                            if(_codec == null) {
-                                return null;
+                            if («DISPATCH_CODEC» == null) {
+                                throw new Â«IllegalStateException.name»("Implementation of codec was not initialized.");
                             }
-                            java.util.Map.Entry _input = new Â«SimpleEntry.name»($1,_baValue);
-                            Object _ret =  _codec.serialize(_input);
+                            java.util.Map.Entry _input = new Â«SimpleEntry.name»($1,$2);
+                            Object _ret =  Â«DISPATCH_CODEC».serialize(_input);
                             ////System.out.println("«typeSpec.name»#toDomStatic: " + _ret);
                             return («List.name») _ret;
                         }
@@ -658,11 +654,10 @@ class TransformerGenerator extends AbstractTransformerGenerator {
                     modifiers = PUBLIC + FINAL + STATIC
                     bodyChecked = '''
                         {
-                            Â«BINDING_CODEC.name» _codec = («BINDING_CODEC.name») Â«COMPOSITE_TO_CASE».get($2);
-                            if(_codec != null) {
-                                return _codec.deserialize(new Â«SimpleEntry.name»($1,$2),$3);
+                            if («DISPATCH_CODEC» == null) {
+                                throw new Â«IllegalStateException.name»("Implementation of codec was not initialized.");
                             }
-                            return null;
+                            return Â«DISPATCH_CODEC».deserialize($2,$3);
                         }
                     '''
                 ]
index 6b528d983361d7180b7c2a7c7aa725609e33d486..b54e3020e5de9edcc517657bf2d1491787e41881 100644 (file)
                 <version>0.6.2-SNAPSHOT</version>
                 <scope>test</scope>
             </dependency>
+            <dependency>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>bug1196-test-model</artifactId>
+                <version>0.6.2-SNAPSHOT</version>
+                <scope>test</scope>
+            </dependency>
 
             <dependency>
                 <groupId>org.opendaylight.yangtools</groupId>
diff --git a/integration-test/bug1196-test-model/pom.xml b/integration-test/bug1196-test-model/pom.xml
new file mode 100644 (file)
index 0000000..8059b5c
--- /dev/null
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- Copyright (c) 2013 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 -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>yangtools-parent</artifactId>
+        <version>0.6.2-SNAPSHOT</version>
+        <relativePath>/../../common/parent/pom.xml</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>bug1196-test-model</artifactId>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-binding</artifactId>
+                        <version>0.6.2-SNAPSHOT</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-common</artifactId>
+                        <version>0.6.2-SNAPSHOT</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools.model</groupId>
+                        <artifactId>yang-ext</artifactId>
+                        <version>2013.09.07.4-SNAPSHOT</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools.model</groupId>
+                        <artifactId>ietf-inet-types</artifactId>
+                        <version>2010.09.24.4-SNAPSHOT</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-Name>org.opendaylight.yangtools.model.${project.artifactId}</Bundle-Name>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-binding</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>yang-ext</artifactId>
+            <version>2013.09.07.4-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>ietf-inet-types</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/integration-test/bug1196-test-model/src/main/yang/network-topology-pcep.yang b/integration-test/bug1196-test-model/src/main/yang/network-topology-pcep.yang
new file mode 100644 (file)
index 0000000..29d202d
--- /dev/null
@@ -0,0 +1,32 @@
+module network-topology-pcep {
+    // vi: set et smarttab sw=4 tabstop=4:
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:topology:pcep";
+    prefix "pn";
+
+    import network-topology { prefix nt; revision-date 2013-10-21; }
+
+    revision "2013-10-24" {
+    }
+
+
+    grouping pcep-client-attributes {
+        container path-computation-client {
+            config false;
+            list reported-lsp {
+                leaf name {
+                    type string;
+                }
+                key name;
+            }
+        }
+    }
+
+    augment "/nt:network-topology/nt:topology/nt:node" {
+        when "../../nt:topology-types/topology-pcep";
+
+        uses pcep-client-attributes;
+    }
+
+}
+
diff --git a/integration-test/bug1196-test-model/src/main/yang/network-topology-unix.yang b/integration-test/bug1196-test-model/src/main/yang/network-topology-unix.yang
new file mode 100644 (file)
index 0000000..d061f47
--- /dev/null
@@ -0,0 +1,25 @@
+module network-topology-unix {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:network:topology-unix";
+    prefix "unix";
+
+    import network-topology { prefix nt; revision-date 2013-10-21; }
+    import network-topology-pcep { prefix topo; revision-date 2013-10-24; }
+    import odl-pcep-ietf-stateful07 { prefix stateful; revision-date 2013-12-22; }
+
+    revision "2013-12-22" {
+    }
+
+
+    augment "/nt:network-topology/nt:topology/nt:node/topo:path-computation-client/topo:reported-lsp/stateful:lsp/stateful:tlvs/stateful:vs-tlv/stateful:vendor-payload" {
+        case unix {
+            container unix-sub-tlvs {
+                leaf unix-value {
+                    type uint8;
+                }
+            }
+        }
+    }
+
+}
+
diff --git a/integration-test/bug1196-test-model/src/main/yang/network-topology@2013-10-21.yang b/integration-test/bug1196-test-model/src/main/yang/network-topology@2013-10-21.yang
new file mode 100644 (file)
index 0000000..630b780
--- /dev/null
@@ -0,0 +1,23 @@
+module network-topology  {
+    yang-version 1;
+    namespace "urn:TBD:params:xml:ns:yang:network-topology";
+    prefix "nt";
+
+
+    revision 2013-10-21 {
+    }
+
+
+
+    container network-topology {
+        list topology {
+            key "topology-id";
+            leaf topology-id {
+                type string;
+            }
+            list node {
+            }
+        }
+    }
+}
+
diff --git a/integration-test/bug1196-test-model/src/main/yang/odl-pcep-ietf-stateful07.yang b/integration-test/bug1196-test-model/src/main/yang/odl-pcep-ietf-stateful07.yang
new file mode 100644 (file)
index 0000000..525040c
--- /dev/null
@@ -0,0 +1,38 @@
+module odl-pcep-ietf-stateful07 {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:pcep:ietf:stateful";
+    prefix "stateful";
+
+    import network-topology { prefix nt; revision-date 2013-10-21; }
+    import network-topology-pcep { prefix topo; revision-date 2013-10-24; }
+    import pcep-types { prefix pcep; revision-date 2013-10-05; }
+    import yang-ext { prefix ext; revision-date 2013-07-09; }
+
+    revision "2013-12-22" {
+    }
+
+
+    grouping lsp-object {
+        container lsp {
+            container "tlvs" {
+                uses pcep:vs-tlv;
+            }
+        }
+    }
+
+    augment "/nt:network-topology/nt:topology/nt:node/topo:path-computation-client/topo:reported-lsp" {
+        uses lsp-object;
+    }
+
+    augment "/nt:network-topology/nt:topology/nt:node/topo:path-computation-client/topo:reported-lsp/stateful:lsp/stateful:tlvs/stateful:vs-tlv/stateful:vendor-payload" {
+        case linux {
+            container linux-sub-tlvs {
+                leaf linux-value {
+                    type uint8;
+                }
+            }
+        }
+    }
+
+}
+
diff --git a/integration-test/bug1196-test-model/src/main/yang/pcep-types.yang b/integration-test/bug1196-test-model/src/main/yang/pcep-types.yang
new file mode 100644 (file)
index 0000000..a05c2ea
--- /dev/null
@@ -0,0 +1,19 @@
+module pcep-types {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:pcep:types";
+    prefix "pcep-t";
+
+    revision "2013-10-05" {
+    }
+
+
+    grouping vs-tlv {
+        description "Vendor-specific TLV.";
+        container vs-tlv {
+                choice vendor-payload {
+                }
+        }
+    }
+
+}
+
index 2b9a20c90874456f7ca3e49190bdf48111e8dde7..77dd63be37a71195f7b98db72df02558cb2b7187 100644 (file)
@@ -15,6 +15,7 @@
 
     <modules>
         <module>bug527-test-model</module>
+        <module>bug1196-test-model</module>
         <module>bundle-test</module>
         <module>test-models</module>
         <module>regression-test-model</module>
index 99c08eb51511cbf3c24d57d81a6a35a8fe84e499..b21db8414775da7966276cfcb9e568375fb75dc0 100644 (file)
             <version>0.6.2-SNAPSHOT</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>bug1196-test-model</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/restconf/restconf-util/src/test/java/org/opendaylight/yangtools/restconf/utils/Bug1196Test.java b/restconf/restconf-util/src/test/java/org/opendaylight/yangtools/restconf/utils/Bug1196Test.java
new file mode 100644 (file)
index 0000000..ac5d868
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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.restconf.utils;
+
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+
+import java.io.InputStream;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Map.Entry;
+
+import javassist.ClassPool;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.network.topology.unix.rev131222.network.topology.topology.node.path.computation.client.reported.lsp.lsp.tlvs.vs.tlv.vendor.payload.unix.UnixSubTlvs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.ReportedLsp1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.object.Lsp;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.object.lsp.Tlvs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.network.topology.topology.node.path.computation.client.reported.lsp.lsp.tlvs.vs.tlv.vendor.payload.linux.LinuxSubTlvs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.vs.tlv.VsTlv;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.Node1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.PathComputationClient;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.path.computation.client.ReportedLsp;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+public class Bug1196Test {
+
+    private static final InstanceIdentifier<PathComputationClient> PATH_TO_CLIENT = InstanceIdentifier.builder(NetworkTopology.class)
+            .child(Topology.class)
+            .child(Node.class)
+            .augmentation(Node1.class)
+            .child(PathComputationClient.class)
+            .build();
+    private RuntimeGeneratedMappingServiceImpl mappingService;
+
+    @Before
+    public void setup() {
+        this.mappingService = new RuntimeGeneratedMappingServiceImpl(new ClassPool());
+
+        final ModuleInfoBackedContext moduleInfo = ModuleInfoBackedContext.create();
+        moduleInfo.addModuleInfos(BindingReflections.loadModuleInfos());
+        this.mappingService.onGlobalContextUpdated(moduleInfo.tryToCreateSchemaContext().get());
+    }
+
+    @Test
+    public void testXmlDataToDataObjectLinuxCase() {
+        final InstanceIdentifier<Topology> instanceIdentifier = InstanceIdentifier.builder(NetworkTopology.class)
+                .child(Topology.class, new TopologyKey(new TopologyId("example-pcep-topology"))).toInstance();
+        final InputStream is = this.getClass().getClassLoader().getResourceAsStream("topology-bug1196-linux.xml");
+        final DataSchemaNode dataSchema = RestconfUtils.toRestconfIdentifier(instanceIdentifier, this.mappingService,
+                this.mappingService.getSchemaContext()).getValue();
+        Topology topology = (Topology) RestconfUtils.dataObjectFromInputStream(instanceIdentifier, is,
+                this.mappingService.getSchemaContext(), this.mappingService, dataSchema);
+        assertNotNull(topology);
+        assertNotNull(topology.getNode());
+        assertEquals(1, topology.getNode().size());
+        Node node = topology.getNode().get(0);
+        Node1 node1 = node.getAugmentation(Node1.class);
+        assertNotNull(node1);
+        final PathComputationClient pcc = node1.getPathComputationClient();
+        final Lsp lsp = pcc.getReportedLsp().get(0).getAugmentation(ReportedLsp1.class).getLsp();
+        org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.object.lsp.Tlvs tlvs = lsp.getTlvs();
+        assertNotNull(tlvs);
+        VsTlv vsTlv = tlvs.getVsTlv();
+        assertNotNull(vsTlv.getVendorPayload());
+
+        Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> domPcc = mappingService.toDataDom(new SimpleEntry<InstanceIdentifier<?>,DataObject>(PATH_TO_CLIENT,pcc));
+        CompositeNode domPccValue = domPcc.getValue();
+        assertNotNull(domPccValue);
+        CompositeNode domPccTlvs = getFirstReportedLspVsTlvs(domPccValue);
+        assertNotNull(domPccTlvs);
+        assertNotNull(domPccTlvs.getFirstCompositeByName(LinuxSubTlvs.QNAME));
+
+    }
+
+    private CompositeNode getFirstReportedLspVsTlvs(final CompositeNode domPccValue) {
+        return domPccValue.getFirstCompositeByName(ReportedLsp.QNAME).getFirstCompositeByName(Lsp.QNAME).getFirstCompositeByName(Tlvs.QNAME).getFirstCompositeByName(QName.create(Tlvs.QNAME,VsTlv.QNAME.getLocalName()));
+    }
+
+    @Test
+    public void testXmlDataToDataObjectUnixCase() {
+        final InstanceIdentifier<Topology> instanceIdentifier = InstanceIdentifier.builder(NetworkTopology.class)
+                .child(Topology.class, new TopologyKey(new TopologyId("example-pcep-topology"))).toInstance();
+        final InputStream is = this.getClass().getClassLoader().getResourceAsStream("topology-bug1196-unix.xml");
+        final DataSchemaNode dataSchema = RestconfUtils.toRestconfIdentifier(instanceIdentifier, this.mappingService,
+                this.mappingService.getSchemaContext()).getValue();
+        Topology topology = (Topology) RestconfUtils.dataObjectFromInputStream(instanceIdentifier, is,
+                this.mappingService.getSchemaContext(), this.mappingService, dataSchema);
+        assertNotNull(topology);
+        assertNotNull(topology.getNode());
+        assertEquals(1, topology.getNode().size());
+        Node node = topology.getNode().get(0);
+        Node1 node1 = node.getAugmentation(Node1.class);
+        assertNotNull(node1);
+        final PathComputationClient pcc = node1.getPathComputationClient();
+        final Lsp lsp = pcc.getReportedLsp().get(0).getAugmentation(ReportedLsp1.class).getLsp();
+        org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.object.lsp.Tlvs tlvs = lsp.getTlvs();
+        assertNotNull(tlvs);
+        VsTlv vsTlv = tlvs.getVsTlv();
+        assertNotNull(vsTlv.getVendorPayload());
+
+        Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> domPcc = mappingService.toDataDom(new SimpleEntry<InstanceIdentifier<?>,DataObject>(PATH_TO_CLIENT,pcc));
+        CompositeNode domPccValue = domPcc.getValue();
+        assertNotNull(domPccValue);
+        CompositeNode domPccTlvs = getFirstReportedLspVsTlvs(domPccValue);
+        assertNotNull(domPccTlvs);
+        assertNotNull(domPccTlvs.getFirstCompositeByName(UnixSubTlvs.QNAME));
+    }
+
+}
diff --git a/restconf/restconf-util/src/test/resources/topology-bug1196-linux.xml b/restconf/restconf-util/src/test/resources/topology-bug1196-linux.xml
new file mode 100644 (file)
index 0000000..2582c9c
--- /dev/null
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<topology xmlns="urn:TBD:params:xml:ns:yang:network-topology">
+    <server-provided>true</server-provided>
+    <topology-id>example-pcep-topology</topology-id>
+    <topology-types/>
+    <node>
+     <path-computation-client xmlns="urn:opendaylight:params:xml:ns:yang:topology:pcep">
+      <state-sync>synchronized</state-sync>
+       <stateful-tlv>
+        <stateful>
+         <lsp-update-capability>true</lsp-update-capability>
+         <include-db-version>false</include-db-version>
+         <initiation>true</initiation>
+        </stateful>
+       </stateful-tlv>
+       <reported-lsp>
+        <name>update-tunel</name>
+        <path>
+          <bandwidth>
+           <bandwidth>AAAAAA==</bandwidth>
+           <ignore>false</ignore>
+           <processing-rule>false</processing-rule>
+          </bandwidth>
+          <ero>
+            <ignore>false</ignore>
+            <processing-rule>false</processing-rule>
+            <subobject>
+             <loose>false</loose>
+             <ip-prefix>
+              <ip-prefix>195.20.160.40/32</ip-prefix>
+             </ip-prefix>
+          </subobject>
+          <subobject>
+           <loose>false</loose>
+           <ip-prefix>
+            <ip-prefix>201.20.160.43/32</ip-prefix>
+           </ip-prefix>
+          </subobject>
+         </ero>
+         <lspa>
+          <exclude-any>0</exclude-any>
+          <hold-priority>7</hold-priority>
+          <ignore>false</ignore>
+          <include-all>0</include-all>
+          <include-any>0</include-any>
+          <local-protection-desired>false</local-protection-desired>
+          <processing-rule>false</processing-rule>
+          <setup-priority>7</setup-priority>
+         </lspa>
+        </path>
+        <lsp xmlns="urn:opendaylight:params:xml:ns:yang:pcep:ietf:stateful">
+         <tlvs>
+          <symbolic-path-name>
+           <path-name>dXBkYXRlLXR1bmVs</path-name>
+          </symbolic-path-name>
+            <vs-tlv>
+                 <enterprise-number>9</enterprise-number>
+                 <linux-sub-tlvs>
+                    <linux-value>5</linux-value>
+                 </linux-sub-tlvs>
+            </vs-tlv>
+         </tlvs>
+         <delegate>true</delegate>
+         <processing-rule>false</processing-rule>
+         <ignore>false</ignore>
+         <operational>true</operational>
+         <sync>false</sync>
+         <plsp-id>40</plsp-id>
+         <remove>false</remove>
+        </lsp>
+      </reported-lsp>
+      <ip-address>39.39.39.39</ip-address>
+     </path-computation-client>
+     <node-id>pcc://39.39.39.39</node-id>
+    </node>
+</topology>
diff --git a/restconf/restconf-util/src/test/resources/topology-bug1196-unix.xml b/restconf/restconf-util/src/test/resources/topology-bug1196-unix.xml
new file mode 100644 (file)
index 0000000..28186c4
--- /dev/null
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<topology xmlns="urn:TBD:params:xml:ns:yang:network-topology">
+    <server-provided>true</server-provided>
+    <topology-id>example-pcep-topology</topology-id>
+    <topology-types/>
+    <node>
+     <path-computation-client xmlns="urn:opendaylight:params:xml:ns:yang:topology:pcep">
+      <state-sync>synchronized</state-sync>
+       <stateful-tlv>
+        <stateful>
+         <lsp-update-capability>true</lsp-update-capability>
+         <include-db-version>false</include-db-version>
+         <initiation>true</initiation>
+        </stateful>
+       </stateful-tlv>
+       <reported-lsp>
+        <name>update-tunel</name>
+        <path>
+          <bandwidth>
+           <bandwidth>AAAAAA==</bandwidth>
+           <ignore>false</ignore>
+           <processing-rule>false</processing-rule>
+          </bandwidth>
+          <ero>
+            <ignore>false</ignore>
+            <processing-rule>false</processing-rule>
+            <subobject>
+             <loose>false</loose>
+             <ip-prefix>
+              <ip-prefix>195.20.160.40/32</ip-prefix>
+             </ip-prefix>
+          </subobject>
+          <subobject>
+           <loose>false</loose>
+           <ip-prefix>
+            <ip-prefix>201.20.160.43/32</ip-prefix>
+           </ip-prefix>
+          </subobject>
+         </ero>
+         <lspa>
+          <exclude-any>0</exclude-any>
+          <hold-priority>7</hold-priority>
+          <ignore>false</ignore>
+          <include-all>0</include-all>
+          <include-any>0</include-any>
+          <local-protection-desired>false</local-protection-desired>
+          <processing-rule>false</processing-rule>
+          <setup-priority>7</setup-priority>
+         </lspa>
+        </path>
+        <lsp xmlns="urn:opendaylight:params:xml:ns:yang:pcep:ietf:stateful">
+         <tlvs>
+          <symbolic-path-name>
+           <path-name>dXBkYXRlLXR1bmVs</path-name>
+          </symbolic-path-name>
+            <vs-tlv>
+                 <enterprise-number>9</enterprise-number>
+                 <unix-sub-tlvs xmlns="urn:opendaylight:params:xml:ns:yang:network:topology-unix">
+                    <unix-value>5</unix-value>
+                 </unix-sub-tlvs>
+            </vs-tlv>
+         </tlvs>
+         <delegate>true</delegate>
+         <processing-rule>false</processing-rule>
+         <ignore>false</ignore>
+         <operational>true</operational>
+         <sync>false</sync>
+         <plsp-id>40</plsp-id>
+         <remove>false</remove>
+        </lsp>
+      </reported-lsp>
+      <ip-address>39.39.39.39</ip-address>
+     </path-computation-client>
+     <node-id>pcc://39.39.39.39</node-id>
+    </node>
+</topology>