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