From 92e4fd194891f567aad81f36352aaf9c80df7838 Mon Sep 17 00:00:00 2001 From: Martin Vitez Date: Tue, 17 Jun 2014 13:12:13 +0200 Subject: [PATCH] BUG-1196: fixed bug in choice case codec. 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 --- .../generator/impl/BindingGeneratorImpl.xtend | 24 +- .../binding/generator/impl/CodecMapping.java | 19 +- .../impl/LazyGeneratedCodecRegistry.java | 466 +++++++----------- .../binding/generator/impl/ModuleContext.java | 21 +- .../generator/impl/TransformerGenerator.xtend | 21 +- common/parent/pom.xml | 6 + integration-test/bug1196-test-model/pom.xml | 86 ++++ .../src/main/yang/network-topology-pcep.yang | 32 ++ .../src/main/yang/network-topology-unix.yang | 25 + .../yang/network-topology@2013-10-21.yang | 23 + .../main/yang/odl-pcep-ietf-stateful07.yang | 38 ++ .../src/main/yang/pcep-types.yang | 19 + integration-test/pom.xml | 1 + restconf/restconf-util/pom.xml | 5 + .../yangtools/restconf/utils/Bug1196Test.java | 128 +++++ .../test/resources/topology-bug1196-linux.xml | 76 +++ .../test/resources/topology-bug1196-unix.xml | 76 +++ 17 files changed, 730 insertions(+), 336 deletions(-) create mode 100644 integration-test/bug1196-test-model/pom.xml create mode 100644 integration-test/bug1196-test-model/src/main/yang/network-topology-pcep.yang create mode 100644 integration-test/bug1196-test-model/src/main/yang/network-topology-unix.yang create mode 100644 integration-test/bug1196-test-model/src/main/yang/network-topology@2013-10-21.yang create mode 100644 integration-test/bug1196-test-model/src/main/yang/odl-pcep-ietf-stateful07.yang create mode 100644 integration-test/bug1196-test-model/src/main/yang/pcep-types.yang create mode 100644 restconf/restconf-util/src/test/java/org/opendaylight/yangtools/restconf/utils/Bug1196Test.java create mode 100644 restconf/restconf-util/src/test/resources/topology-bug1196-linux.xml create mode 100644 restconf/restconf-util/src/test/resources/topology-bug1196-unix.xml diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.xtend b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.xtend index 8a41bef08f..ba8259e9b3 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.xtend +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.xtend @@ -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 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 childNodes = dataNodeCase.childNodes; - if (childNodes !== null) { - resolveDataSchemaNodes(module, basePackageName, caseTypeBuilder, childOfType, childNodes); - } - } else { - val ChoiceCaseNode node = targetNode.getCaseNodeByName(caseNode.getQName().getLocalName()); - val Set 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 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); } } diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/CodecMapping.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/CodecMapping.java index 9a4ce78541..2ef288f797 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/CodecMapping.java +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/CodecMapping.java @@ -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> 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> dataCodec, BindingCodec augmentableCodec) { Field instanceIdField; diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java index e62f28e646..c251ba1820 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java @@ -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, DataContainerCodec> containerCodecs = Collections @@ -109,8 +107,7 @@ class LazyGeneratedCodecRegistry implements // @SuppressWarnings("rawtypes") private static final Map> typeToClass = new ConcurrentHashMap<>(); - @SuppressWarnings("rawtypes") - private static final ConcurrentMap typeToCaseCodecs = new ConcurrentHashMap<>(); + private static final ConcurrentMap caseTypeToCaseSchema = new ConcurrentHashMap<>(); private static final Map pathToType = new ConcurrentHashMap<>(); private static final Map, Type> pathToInstantiatedType = new ConcurrentHashMap<>(); @@ -124,7 +121,6 @@ class LazyGeneratedCodecRegistry implements // . 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 newCodec = generator.caseCodecFor(caseClass, caseCodec.getSchema()); + Preconditions.checkState(caseSchema != null, "Case schema is not available for %s", caseClass.getName()); + Class 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, 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 cases, final SchemaContext module) { - for (Entry 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, 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 implements ChoiceCaseCodec, // Delegator, LocationAwareBindingCodec, ValueWithQName> { - private boolean augmenting; - private boolean uses; - private BindingCodec delegate; - - private Set validNames; - private Set validQNames; - private ChoiceCaseNode schema; - private Set> applicableLocations; + private final BindingCodec delegate; + private final ChoiceCaseNode schema; + private final Map, 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 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(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> cases = Collections .synchronizedMap(new WeakHashMap>()); - private final CaseCompositeNodeMapFacade CompositeToCase; - public PublicChoiceCodecImpl(final BindingCodec, 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, Object> getDelegate() { return delegate; @@ -886,163 +802,141 @@ class LazyGeneratedCodecRegistry implements // } - @SuppressWarnings("unused") - private class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec> { + class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec> { + 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 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>> 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 { - - final Map> choiceCases; - - public CaseCompositeNodeMapFacade(final Map> 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> entry : choiceCases.entrySet()) { - ChoiceCaseCodecImpl codec = entry.getValue(); - if (codec.isAcceptable((CompositeNode) key)) { - return codec.getDelegate(); + if (input instanceof CompositeNode) { + List>> codecs = new ArrayList<>(getImplementations().entrySet()); + for (Entry> 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 - * Key type - */ - @SuppressWarnings("rawtypes") - private abstract static class MapFacadeBase implements Map> { - - @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 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> 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 tryToLoadImplementation(final Type potential) { + try { + @SuppressWarnings("unchecked") + Class clazz = (Class) 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, Object> put(final T key, final BindingCodec value) { - throw notModifiable(); + protected ChoiceCaseCodecImpl tryToLoadImplementation(final Class inputType) { + ChoiceCaseCodecImpl codec = getCaseCodecFor(inputType); + addImplementation(codec); + return codec; } @Override - public void putAll(final Map> m) { - throw notModifiable(); + protected void tryToLoadImplementations() { + Type type = referencedType(choiceType); + Collection 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 newChoice = findInstantiatedChoice(ctxNode, choiceName); + tryToLoadImplementations(); + if (newChoice.isPresent()) { + for (@SuppressWarnings("rawtypes") + Entry> codec : getImplementations().entrySet()) { + ChoiceCaseCodecImpl caseCodec = codec.getValue(); + Optional instantiatedSchema = findInstantiatedCase(newChoice.get(), + caseCodec.getSchema()); + if (instantiatedSchema.isPresent()) { + caseCodec.adaptForPath(augTarget, instantiatedSchema.get()); + } + } + } } - @Override - public boolean isEmpty() { - return true; - } + private Optional findInstantiatedChoice(final DataNodeContainer ctxNode, final QName choiceName) { + DataSchemaNode potential = ctxNode.getDataChildByName(choiceName); + if (potential == null) { + potential = ctxNode.getDataChildByName(choiceName.getLocalName()); + } - @Override - public Set keySet() { - return Collections.emptySet(); - } + if (potential instanceof ChoiceNode) { + return Optional.of((ChoiceNode) potential); + } - @Override - public Set>> entrySet() { - return Collections.emptySet(); + return Optional.absent(); } - @Override - public boolean containsValue(final Object value) { - return false; + private Optional 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 { - - 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> implements AugmentationCodec, Delegator, LocationAwareBindingCodec, ValueWithQName> { diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/ModuleContext.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/ModuleContext.java index 48b6947f9a..acb939d149 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/ModuleContext.java +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/ModuleContext.java @@ -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 genTOs = new ArrayList(); @@ -39,6 +38,7 @@ public final class ModuleContext { private final BiMap typeToAugmentation = HashBiMap.create(); private final Multimap choiceToCases = HashMultimap.create(); + private final BiMap caseTypeToSchema = HashBiMap.create(); private final Multimap 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 getCaseTypeToSchemas() { + return caseTypeToSchema; + } + } diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/TransformerGenerator.xtend b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/TransformerGenerator.xtend index 8821411022..792b0b9cc3 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/TransformerGenerator.xtend +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/TransformerGenerator.xtend @@ -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); } ''' ] diff --git a/common/parent/pom.xml b/common/parent/pom.xml index 6b528d9833..b54e3020e5 100644 --- a/common/parent/pom.xml +++ b/common/parent/pom.xml @@ -441,6 +441,12 @@ 0.6.2-SNAPSHOT test + + org.opendaylight.yangtools + bug1196-test-model + 0.6.2-SNAPSHOT + test + org.opendaylight.yangtools diff --git a/integration-test/bug1196-test-model/pom.xml b/integration-test/bug1196-test-model/pom.xml new file mode 100644 index 0000000000..8059b5c463 --- /dev/null +++ b/integration-test/bug1196-test-model/pom.xml @@ -0,0 +1,86 @@ + + + + + + + org.opendaylight.yangtools + yangtools-parent + 0.6.2-SNAPSHOT + /../../common/parent/pom.xml + + + 4.0.0 + bug1196-test-model + ${project.artifactId} + ${project.artifactId} + + + + + maven-jar-plugin + + + org.opendaylight.yangtools + yang-maven-plugin + + + org.opendaylight.yangtools + yang-binding + 0.6.2-SNAPSHOT + + + org.opendaylight.yangtools + yang-common + 0.6.2-SNAPSHOT + + + org.opendaylight.yangtools.model + yang-ext + 2013.09.07.4-SNAPSHOT + + + org.opendaylight.yangtools.model + ietf-inet-types + 2010.09.24.4-SNAPSHOT + + + + + org.apache.felix + maven-bundle-plugin + true + + + org.opendaylight.yangtools.model.${project.artifactId} + + + + + + + + + org.opendaylight.yangtools + yang-binding + + + org.opendaylight.yangtools + yang-common + + + org.opendaylight.yangtools.model + yang-ext + 2013.09.07.4-SNAPSHOT + + + org.opendaylight.yangtools.model + ietf-inet-types + + + + 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 index 0000000000..29d202dace --- /dev/null +++ b/integration-test/bug1196-test-model/src/main/yang/network-topology-pcep.yang @@ -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 index 0000000000..d061f47471 --- /dev/null +++ b/integration-test/bug1196-test-model/src/main/yang/network-topology-unix.yang @@ -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 index 0000000000..630b780319 --- /dev/null +++ b/integration-test/bug1196-test-model/src/main/yang/network-topology@2013-10-21.yang @@ -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 index 0000000000..525040cdba --- /dev/null +++ b/integration-test/bug1196-test-model/src/main/yang/odl-pcep-ietf-stateful07.yang @@ -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 index 0000000000..a05c2eaed8 --- /dev/null +++ b/integration-test/bug1196-test-model/src/main/yang/pcep-types.yang @@ -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 { + } + } + } + +} + diff --git a/integration-test/pom.xml b/integration-test/pom.xml index 2b9a20c908..77dd63be37 100644 --- a/integration-test/pom.xml +++ b/integration-test/pom.xml @@ -15,6 +15,7 @@ bug527-test-model + bug1196-test-model bundle-test test-models regression-test-model diff --git a/restconf/restconf-util/pom.xml b/restconf/restconf-util/pom.xml index 99c08eb515..b21db84147 100644 --- a/restconf/restconf-util/pom.xml +++ b/restconf/restconf-util/pom.xml @@ -96,5 +96,10 @@ 0.6.2-SNAPSHOT test + + org.opendaylight.yangtools + bug1196-test-model + test + 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 index 0000000000..ac5d868d39 --- /dev/null +++ b/restconf/restconf-util/src/test/java/org/opendaylight/yangtools/restconf/utils/Bug1196Test.java @@ -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 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 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 domPcc = mappingService.toDataDom(new SimpleEntry,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 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 domPcc = mappingService.toDataDom(new SimpleEntry,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 index 0000000000..2582c9ce07 --- /dev/null +++ b/restconf/restconf-util/src/test/resources/topology-bug1196-linux.xml @@ -0,0 +1,76 @@ + + + true + example-pcep-topology + + + + synchronized + + + true + false + true + + + + update-tunel + + + AAAAAA== + false + false + + + false + false + + false + + 195.20.160.40/32 + + + + false + + 201.20.160.43/32 + + + + + 0 + 7 + false + 0 + 0 + false + false + 7 + + + + + + dXBkYXRlLXR1bmVs + + + 9 + + 5 + + + + true + false + false + true + false + 40 + false + + + 39.39.39.39 + + pcc://39.39.39.39 + + 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 index 0000000000..28186c4d93 --- /dev/null +++ b/restconf/restconf-util/src/test/resources/topology-bug1196-unix.xml @@ -0,0 +1,76 @@ + + + true + example-pcep-topology + + + + synchronized + + + true + false + true + + + + update-tunel + + + AAAAAA== + false + false + + + false + false + + false + + 195.20.160.40/32 + + + + false + + 201.20.160.43/32 + + + + + 0 + 7 + false + 0 + 0 + false + false + 7 + + + + + + dXBkYXRlLXR1bmVs + + + 9 + + 5 + + + + true + false + false + true + false + 40 + false + + + 39.39.39.39 + + pcc://39.39.39.39 + + -- 2.36.6