--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.codec.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.binding.OpaqueObject;
+
+@Beta
+public interface BindingOpaqueObjectCodecTreeNode<T extends OpaqueObject<T>> extends BindingObjectCodecTreeNode<T>,
+ BindingNormalizedNodeCodec<T> {
+
+}
* 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.mdsal.binding.dom.codec.gen.impl;
+import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import java.util.HashMap;
if (!schemaChild.isAugmenting()) {
final String getter = BindingSchemaMapping.getGetterMethodName(schemaChild);
final Type childType = getterToType.get(getter);
- if (childType == null) {
- // FIXME AnyXml nodes are ignored, since their type cannot be found in generated bindnig
- // Bug-706 https://bugs.opendaylight.org/show_bug.cgi?id=706
- if (schemaChild instanceof AnyXmlSchemaNode) {
- LOG.warn("Node {} will be ignored. AnyXml is not yet supported from binding aware code."
- + "Binding Independent code can be used to serialize anyXml nodes.",
- schemaChild.getPath());
- continue;
- }
-
- throw new IllegalStateException(
- String.format("Unable to find type for child node %s. Expected child nodes: %s",
- schemaChild.getPath(), getterToType));
- }
+ checkState(childType != null, "Unable to find type for child node %s. Expected child nodes: %s",
+ schemaChild.getPath(), getterToType);
emitChild(sb, getter, childType, schemaChild);
}
}
return invoke(STREAM, "leafNode", escape(localName), value);
}
- protected static final CharSequence startLeafSet(final String localName,final CharSequence expected) {
+ protected static final CharSequence startLeafSet(final String localName, final CharSequence expected) {
return invoke(STREAM, "startLeafSet", escape(localName), expected);
}
return invoke(STREAM, "startAugmentationNode", key);
}
- protected static final CharSequence startChoiceNode(final CharSequence localName,final CharSequence expected) {
+ protected static final CharSequence startChoiceNode(final CharSequence localName, final CharSequence expected) {
return invoke(STREAM, "startChoiceNode", localName, expected);
}
- protected static final CharSequence startCaseNode(final CharSequence localName,final CharSequence expected) {
+ protected static final CharSequence startCaseNode(final CharSequence localName, final CharSequence expected) {
return invoke(STREAM, "startCase", localName, expected);
}
- protected static final CharSequence anyxmlNode(final String name, final String value)
+ protected static final CharSequence anyxmlNode(final String localName, final CharSequence value)
throws IllegalArgumentException {
- return invoke(STREAM, "anyxmlNode", escape(name), name);
+ return invoke(STREAM, "anyxmlNode", escape(localName), value);
}
protected static final CharSequence endNode() {
import org.opendaylight.yangtools.yang.binding.Identifier;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.OpaqueObject;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
final DataNodeContainer childSchema) {
final Map<String, DataSchemaNode> getterToLeafSchema = new HashMap<>();
for (final DataSchemaNode leaf : childSchema.getChildNodes()) {
- if (leaf instanceof TypedDataSchemaNode) {
- getterToLeafSchema.put(BindingSchemaMapping.getGetterMethodName((TypedDataSchemaNode) leaf), leaf);
+ // FIXME: support AnyDataSchemaNode, too
+ if (leaf instanceof TypedDataSchemaNode || leaf instanceof AnyXmlSchemaNode) {
+ getterToLeafSchema.put(BindingSchemaMapping.getGetterMethodName(leaf), leaf);
}
}
return getLeafNodesUsingReflection(parentClass, getterToLeafSchema);
for (final Method method : parentClass.getMethods()) {
if (method.getParameterCount() == 0) {
final DataSchemaNode schema = getterToLeafSchema.get(method.getName());
- if (!(schema instanceof TypedDataSchemaNode)) {
- // We do not have schema for leaf, so we will ignore it (e.g. getClass).
- continue;
- }
final ValueNodeCodecContext valueNode;
if (schema instanceof LeafSchemaNode) {
final LeafListSchemaNode leafListSchema = (LeafListSchemaNode) schema;
final Codec<Object, Object> codec = getCodec(valueType, leafListSchema.getType());
valueNode = new LeafSetNodeCodecContext(leafListSchema, codec, method);
+ } else if (schema instanceof AnyXmlSchemaNode) {
+ final Class<?> valueType = method.getReturnType();
+ verify(OpaqueObject.class.isAssignableFrom(valueType), "Illegal value type %s", valueType);
+ valueNode = new OpaqueNodeCodecContext.AnyXml<>((AnyXmlSchemaNode) schema, method,
+ valueType.asSubclass(OpaqueObject.class));
} else {
- throw new IllegalStateException("Unhandled typed schema " + schema);
+ verify(schema == null, "Unhandled schema %s for method %s", schema, method);
+ // We do not have schema for leaf, so we will ignore it (e.g. getClass).
+ continue;
}
leaves.put(schema.getQName().getLocalName(), valueNode);
}
@Override
+ @SuppressFBWarnings("BC_UNCONFIRMED_CAST")
public ContainerNode toNormalizedNodeRpcData(final DataContainer data) {
// FIXME: Should the cast to DataObject be necessary?
return serializeDataObject((DataObject) data, this::newRpcWriter);
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
+import org.opendaylight.yangtools.yang.binding.OpaqueObject;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
for (final Entry<Class<?>, Method> childDataObj : clsToMethod.entrySet()) {
final Method method = childDataObj.getValue();
verify(!method.isDefault(), "Unexpected default method %s in %s", method, bindingClass);
- final DataContainerCodecPrototype<?> childProto = loadChildPrototype(childDataObj.getKey());
+
+ final Class<?> retClass = childDataObj.getKey();
+ if (OpaqueObject.class.isAssignableFrom(retClass)) {
+ // Filter OpaqueObjects, they are not containers
+ continue;
+ }
+
+ final DataContainerCodecPrototype<?> childProto = loadChildPrototype(retClass);
tmpMethodToSupplier.put(method, childProto);
byStreamClassBuilder.put(childProto.getBindingClass(), childProto);
byYangBuilder.put(childProto.getYangArg(), childProto);
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.codec.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import org.opendaylight.yangtools.yang.binding.AbstractOpaqueData;
+import org.opendaylight.yangtools.yang.binding.OpaqueData;
+import org.opendaylight.yangtools.yang.data.api.schema.ForeignDataNode;
+
+/**
+ * An {@link OpaqueData} implementation backed by {@link ForeignDataNode}.
+ *
+ * @param <T> Object model type
+ */
+final class ForeignOpaqueData<T> extends AbstractOpaqueData<T> {
+ private final ForeignDataNode<?, T> domData;
+
+ ForeignOpaqueData(final ForeignDataNode<?, T> domData) {
+ this.domData = requireNonNull(domData);
+ }
+
+ @Override
+ public Class<T> getObjectModel() {
+ return domData.getValueObjectModel();
+ }
+
+ @Override
+ public T getData() {
+ return domData.getValue();
+ }
+
+ ForeignDataNode<?, T> domData() {
+ return domData;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.codec.impl;
+
+import static java.util.Objects.requireNonNull;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
+import org.opendaylight.yangtools.yang.binding.AbstractOpaqueObject;
+import org.opendaylight.yangtools.yang.binding.OpaqueData;
+import org.opendaylight.yangtools.yang.binding.OpaqueObject;
+
+/**
+ * Proxy support for an OpaqueObject.
+ *
+ * @param <T> OpaqueObject type
+ */
+// FIXME: MDSAL-442: this class has no run-time dependencies on other generated types, hence is a prime candidate for
+// becoming a prototype instantiated in a separate ClassLoader (see MDSAL-401) and not being a proxy at all.
+final class ForeignOpaqueObject<T extends OpaqueObject<T>> extends AbstractOpaqueObject<T>
+ implements InvocationHandler {
+ private final @NonNull Class<T> implementedInterface;
+ private final @NonNull ForeignOpaqueData<?> value;
+
+ ForeignOpaqueObject(final Class<T> implementedInterface, final ForeignOpaqueData<?> value) {
+ this.implementedInterface = requireNonNull(implementedInterface);
+ this.value = requireNonNull(value);
+ }
+
+ @Override
+ public Class<T> implementedInterface() {
+ return implementedInterface;
+ }
+
+ @Override
+ public OpaqueData<?> getValue() {
+ return value;
+ }
+
+ @Override
+ public Object invoke(final Object proxy, final Method method, final Object[] args) {
+ switch (method.getName()) {
+ case "equals":
+ return equals(args[0]);
+ case "hashCode":
+ return hashCode();
+ case "getValue":
+ return getValue();
+ case "toString":
+ return toString();
+ case BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME:
+ return implementedInterface;
+ default:
+ throw new NoSuchMethodError("Unknown method " + method);
+ }
+ }
+}
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
-class LeafNodeCodecContext extends ValueNodeCodecContext {
+class LeafNodeCodecContext extends ValueNodeCodecContext.WithCodec {
static final class OfTypeObject<T extends TypeObject> extends LeafNodeCodecContext
implements BindingTypeObjectCodecTreeNode<T> {
private final @NonNull Class<T> bindingClass;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-final class LeafSetNodeCodecContext extends ValueNodeCodecContext {
+final class LeafSetNodeCodecContext extends ValueNodeCodecContext.WithCodec {
LeafSetNodeCodecContext(final LeafListSchemaNode schema, final Codec<Object, Object> codec,
final Method getter) {
// FIXME: add support for defaults
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.codec.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import javax.xml.transform.dom.DOMSource;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingOpaqueObjectCodecTreeNode;
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.binding.OpaqueData;
+import org.opendaylight.yangtools.yang.binding.OpaqueObject;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ForeignDataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class OpaqueNodeCodecContext<T extends OpaqueObject<T>> extends ValueNodeCodecContext
+ implements BindingOpaqueObjectCodecTreeNode<T> {
+ static final class AnyXml<T extends OpaqueObject<T>> extends OpaqueNodeCodecContext<T> {
+ AnyXml(final AnyXmlSchemaNode schema, final Method getter, final Class<T> bindingClass) {
+ super(schema, getter, bindingClass);
+ }
+
+ @Override
+ ForeignDataNode<?, ?> serializedData(final OpaqueData<?> opaqueData) {
+ final Class<?> model = opaqueData.getObjectModel();
+ verify(DOMSource.class.isAssignableFrom(model), "Cannot just yet support object model %s", model);
+ return Builders.anyXmlBuilder().withNodeIdentifier(getDomPathArgument())
+ .withValue((DOMSource) opaqueData.getData()).build();
+ }
+ }
+
+ private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, InvocationHandler.class);
+ private static final MethodType OPAQUEOBJECT_TYPE = MethodType.methodType(OpaqueObject.class,
+ ForeignOpaqueObject.class);
+
+ private final Codec<Object, Object> valueCodec = new Codec<Object, Object>() {
+ @Override
+ public Object serialize(final Object input) {
+ checkArgument(bindingClass.isInstance(input), "Unexpected input %s", input);
+ // FIXME: this works for DOMSource only, for generalization we need to pass down the object model, too
+ final OpaqueData<?> opaqueData = bindingClass.cast(input).getValue();
+ final Object data = opaqueData.getData();
+ checkArgument(data instanceof DOMSource, "Unexpected data %s", data);
+ return data;
+ }
+
+ @Override
+ public Object deserialize(final Object input) {
+ checkArgument(input instanceof NormalizedNode, "Unexpected input %s", input);
+ return OpaqueNodeCodecContext.this.deserializeObject((NormalizedNode<?, ?>) input);
+ }
+ };
+
+ private final MethodHandle proxyConstructor;
+ private final @NonNull Class<T> bindingClass;
+
+ OpaqueNodeCodecContext(final DataSchemaNode schema, final Method getter, final Class<T> bindingClass) {
+ super(schema, getter, null);
+ this.bindingClass = requireNonNull(bindingClass);
+
+ final Class<?> proxyClass = Proxy.getProxyClass(bindingClass.getClassLoader(), bindingClass);
+ try {
+ proxyConstructor = MethodHandles.publicLookup().findConstructor(proxyClass, CONSTRUCTOR_TYPE)
+ .asType(OPAQUEOBJECT_TYPE);
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new IllegalStateException("Failed to find contructor for class " + proxyClass, e);
+ }
+ }
+
+ @Override
+ public final Class<T> getBindingClass() {
+ return bindingClass;
+ }
+
+ @Override
+ public final T deserialize(final NormalizedNode<?, ?> data) {
+ checkArgument(data instanceof ForeignDataNode, "Unexpected value %s", data);
+ final ForeignDataNode<?, ?> foreignData = (ForeignDataNode<?, ?>) data;
+ // Streaming cannot support anything but DOMSource-based AnyxmlNodes.
+ verify(foreignData instanceof AnyXmlNode, "Variable node %s not supported yet", foreignData);
+
+ final ForeignOpaqueData<?> opaqueData = new ForeignOpaqueData<>(foreignData);
+ return bindingClass.cast(createBindingProxy(new ForeignOpaqueObject<>(bindingClass, opaqueData)));
+ }
+
+ @Override
+ public final ForeignDataNode<?, ?> serialize(final T data) {
+ final OpaqueData<?> opaqueData = data.getValue();
+ return opaqueData instanceof ForeignOpaqueData ? ((ForeignOpaqueData<?>) opaqueData).domData()
+ : serializedData(opaqueData);
+ }
+
+ @Override
+ protected final @NonNull Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
+ return deserialize(normalizedNode);
+ }
+
+ @Override
+ Codec<Object, Object> getValueCodec() {
+ return valueCodec;
+ }
+
+ abstract @NonNull ForeignDataNode<?, ?> serializedData(OpaqueData<?> opaqueData);
+
+ @SuppressWarnings("checkstyle:illegalCatch")
+ private OpaqueObject<?> createBindingProxy(final ForeignOpaqueObject<?> handler) {
+ try {
+ return (OpaqueObject<?>) proxyConstructor.invokeExact(handler);
+ } catch (final Throwable e) {
+ Throwables.throwIfUnchecked(e);
+ throw new IllegalStateException(e);
+ }
+ }
+}
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.concepts.Codec;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
/**
* Abstract base class for atomic nodes. These are nodes which are not decomposed in the Binding Specification, such
* as LeafNodes and LeafSetNodes.
*/
abstract class ValueNodeCodecContext extends NodeCodecContext implements NodeContextSupplier {
+ abstract static class WithCodec extends ValueNodeCodecContext {
+ private final @NonNull Codec<Object, Object> valueCodec;
+
+ WithCodec(final DataSchemaNode schema, final Codec<Object, Object> codec, final Method getter,
+ final Object defaultObject) {
+ super(schema, getter, defaultObject);
+ this.valueCodec = requireNonNull(codec);
+ }
+
+ @Override
+ final Codec<Object, Object> getValueCodec() {
+ return valueCodec;
+ }
+ }
+
private final @NonNull NodeIdentifier yangIdentifier;
- private final @NonNull Codec<Object, Object> valueCodec;
private final @NonNull Method getter;
- private final @NonNull TypedDataSchemaNode schema;
+ private final @NonNull DataSchemaNode schema;
private final Object defaultObject;
- ValueNodeCodecContext(final TypedDataSchemaNode schema, final Codec<Object, Object> codec,
- final Method getter, final Object defaultObject) {
+ ValueNodeCodecContext(final DataSchemaNode schema, final Method getter, final Object defaultObject) {
this.yangIdentifier = NodeIdentifier.create(schema.getQName());
- this.valueCodec = requireNonNull(codec);
this.getter = requireNonNull(getter);
this.schema = requireNonNull(schema);
this.defaultObject = defaultObject;
return getter;
}
- final Codec<Object, Object> getValueCodec() {
- return valueCodec;
- }
+ abstract Codec<Object, Object> getValueCodec();
@Override
- public final TypedDataSchemaNode getSchema() {
+ public final DataSchemaNode getSchema() {
return schema;
}
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.codec.test;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+
+import java.util.Map.Entry;
+import javax.xml.transform.dom.DOMSource;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.mdsal437.norev.Cont;
+import org.opendaylight.yang.gen.v1.mdsal437.norev.ContBuilder;
+import org.opendaylight.yang.gen.v1.mdsal437.norev.cont.ContAny;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.binding.AbstractOpaqueData;
+import org.opendaylight.yangtools.yang.binding.AbstractOpaqueObject;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.OpaqueData;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class AnyxmlLeafTest extends AbstractBindingCodecTest {
+ private static final NodeIdentifier CONT_NODE_ID = new NodeIdentifier(Cont.QNAME);
+
+ private DOMSource domSource;
+ private ContainerNode cont;
+
+ @Override
+ public void before() {
+ super.before();
+
+ final Document doc = UntrustedXML.newDocumentBuilder().newDocument();
+ final Element element = doc.createElement("foo");
+ domSource = new DOMSource(element);
+
+ cont = Builders.containerBuilder()
+ .withNodeIdentifier(CONT_NODE_ID)
+ .withChild(Builders.anyXmlBuilder()
+ .withNodeIdentifier(new NodeIdentifier(ContAny.QNAME))
+ .withValue(domSource)
+ .build())
+ .build();
+ }
+
+ @Test
+ public void testAnyxmlToBinding() {
+ final Entry<InstanceIdentifier<?>, DataObject> entry = registry.fromNormalizedNode(
+ YangInstanceIdentifier.create(CONT_NODE_ID), cont);
+ assertEquals(InstanceIdentifier.create(Cont.class), entry.getKey());
+ final DataObject ldo = entry.getValue();
+ assertThat(ldo, instanceOf(Cont.class));
+
+ // So no... GrpAny should be null ..
+ final Cont contValue = (Cont) ldo;
+ assertNull(contValue.getGrpAny());
+
+ // ContAny is interesting
+ final ContAny anyCont = contValue.getContAny();
+ assertNotNull(anyCont);
+ assertEquals(ContAny.class, anyCont.implementedInterface());
+
+ final OpaqueData<?> value = anyCont.getValue();
+ assertNotNull(value);
+ assertEquals(DOMSource.class, value.getObjectModel());
+ assertSame(domSource, value.getData());
+
+ // Stable hashCode
+ final int hashOne = anyCont.hashCode();
+ final int hashTwo = anyCont.hashCode();
+ assertEquals(hashOne, hashTwo);
+
+ // Basic equality
+ assertNotEquals(anyCont, null);
+ assertEquals(anyCont, anyCont);
+ assertEquals(new FakeCont(), anyCont);
+ assertEquals(anyCont, new FakeCont());
+ assertNotEquals(anyCont, new TestNormalizedNodeCont());
+ assertNotEquals(new TestNormalizedNodeCont(), anyCont);
+ }
+
+ @Test
+ public void testAnyxmlFromBinding() {
+ final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry = registry.toNormalizedNode(
+ InstanceIdentifier.create(Cont.class), new ContBuilder().setContAny(new FakeCont()).build());
+ assertEquals(YangInstanceIdentifier.create(CONT_NODE_ID), entry.getKey());
+ assertEquals(cont, entry.getValue());
+ }
+
+ private final class FakeData extends AbstractOpaqueData<DOMSource> {
+ @Override
+ public Class<DOMSource> getObjectModel() {
+ return DOMSource.class;
+ }
+
+ @Override
+ public DOMSource getData() {
+ return domSource;
+ }
+ }
+
+ private abstract static class AbstractTestCont extends AbstractOpaqueObject<ContAny> implements ContAny {
+
+ }
+
+ private final class FakeCont extends AbstractTestCont {
+ @Override
+ public OpaqueData<?> getValue() {
+ return new FakeData();
+ }
+ }
+
+ private final class TestNormalizedNodeCont extends AbstractTestCont {
+ @Override
+ public OpaqueData<?> getValue() {
+ return new AbstractOpaqueData<NormalizedNode>() {
+
+ @Override
+ public Class<NormalizedNode> getObjectModel() {
+ return NormalizedNode.class;
+ }
+
+ @Override
+ public NormalizedNode getData() {
+ return cont;
+ }
+ };
+ }
+ }
+}