--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.codec.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Thrown when codec was used with data which are not modeled
+ * and available in schema used by codec.
+ */
+public class MissingSchemaException extends IllegalArgumentException {
+
+ private static final long serialVersionUID = 1L;
+
+ protected MissingSchemaException(final String msg) {
+ super(msg);
+ }
+
+ protected MissingSchemaException(final String msg, final Throwable cause) {
+ super(msg, cause);
+ }
+
+ private static MissingSchemaException create(final String format, final Object... args) {
+ return new MissingSchemaException(String.format(format, args));
+ }
+
+ public static void checkModulePresent(final SchemaContext schemaContext, final QName name) {
+ if(schemaContext.findModuleByNamespaceAndRevision(name.getNamespace(), name.getRevision()) == null) {
+ throw MissingSchemaException.create("Module %s is not present in current schema context.",name.getModule());
+ }
+ }
+
+ public static void checkModulePresent(final SchemaContext schemaContext, final YangInstanceIdentifier.PathArgument
+ child) {
+ checkModulePresent(schemaContext, extractName(child));
+ }
+
+ private static QName extractName(final PathArgument child) {
+ if(child instanceof YangInstanceIdentifier.AugmentationIdentifier) {
+ final Set<QName> children = ((YangInstanceIdentifier.AugmentationIdentifier) child).getPossibleChildNames();
+ Preconditions.checkArgument(!children.isEmpty(),"Augmentation without childs must not be used in data");
+ return children.iterator().next();
+ }
+ return child.getNodeType();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.codec.impl.context.base;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableCollection;
+import java.io.IOException;
+import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.api.codecs.BindingNormalizedNodeCachingCodec;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.MissingSchemaException;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingStreamEventWriter;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializer;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+@Beta
+abstract class DataContainerCodecContext<D extends TreeNode, T> extends NodeCodecContext<D> {
+
+ private final DataContainerCodecPrototype<T> prototype;
+ private volatile TreeNodeSerializer eventStreamSerializer;
+
+ protected DataContainerCodecContext(final DataContainerCodecPrototype<T> prototype) {
+ this.prototype = prototype;
+ }
+
+ @Nonnull
+ @Override
+ public final T getSchema() {
+ return prototype.getSchema();
+ }
+
+ protected final QNameModule namespace() {
+ return prototype.getNamespace();
+ }
+
+ protected final CodecContextFactory factory() {
+ return prototype.getFactory();
+ }
+
+ @Override
+ protected YangInstanceIdentifier.PathArgument getDomPathArgument() {
+ return prototype.getYangArg();
+ }
+
+ /**
+ * Returns nested node context using supplied YANG Instance Identifier
+ *
+ * @param arg Yang Instance Identifier Argument
+ * @return Context of child
+ * @throws IllegalArgumentException If supplied argument does not represent valid child.
+ */
+ @Nonnull
+ @Override
+ public abstract NodeCodecContext<?> yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg);
+
+ /**
+ * Returns nested node context using supplied Binding Instance Identifier
+ * and adds YANG instance identifiers to supplied list.
+ *
+ * @param arg Binding Instance Identifier Argument
+ * @return Context of child or null if supplied {@code arg} does not represent valid child.
+ * @throws IllegalArgumentException If supplied argument does not represent valid child.
+ */
+ @SuppressWarnings("unchecked")
+ @Nonnull
+ @Override
+ public DataContainerCodecContext<?,?> bindingPathArgumentChild(@Nonnull final TreeArgument<?> arg,
+ final List<PathArgument> builder) {
+ final DataContainerCodecContext<?,?> child = streamChild((Class<? extends TreeNode>) arg.getType());
+ if (builder != null) {
+ child.addYangPathArgument(arg,builder);
+ }
+ return child;
+ }
+
+ /**
+ * Returns de-serialized Binding Path Argument from YANG instance identifier.
+ *
+ * @param domArg input path argument
+ * @return returns binding path argument
+ */
+ @SuppressWarnings("rawtypes")
+ protected TreeArgument getBindingPathArgument(final YangInstanceIdentifier.PathArgument domArg) {
+ return bindingArg();
+ }
+
+ @SuppressWarnings("rawtypes")
+ protected final TreeArgument bindingArg() {
+ return prototype.getBindingArg();
+ }
+
+ @Nonnull
+ @SuppressWarnings("unchecked")
+ @Override
+ public final Class<D> getBindingClass() {
+ return Class.class.cast(prototype.getBindingClass());
+ }
+
+ /**
+ * Returns child context as if it was walked by
+ * {@link BindingStreamEventWriter}. This means that to enter case, one
+ * must issue getChild(ChoiceClass).getChild(CaseClass).
+ *
+ * @param childClass input child class
+ * @return Context of child node or null, if supplied class is not subtree child
+ * @throws IllegalArgumentException If supplied child class is not valid in specified context.
+ */
+ @Nonnull
+ @Override
+ public abstract <DV extends TreeNode> DataContainerCodecContext<DV,?> streamChild(@Nonnull final Class<DV> childClass) throws IllegalArgumentException;
+
+ /**
+ * Returns child context as if it was walked by
+ * {@link BindingStreamEventWriter}. This means that to enter case, one
+ * must issue getChild(ChoiceClass).getChild(CaseClass).
+ *
+ * @param childClass input child class
+ * @return Context of child or Optional absent is supplied class is not applicable in context.
+ */
+ @Override
+ public abstract <DV extends TreeNode> Optional<DataContainerCodecContext<DV, ?>> possibleStreamChild(@Nonnull
+ final Class<DV> childClass);
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [" + prototype.getBindingClass() + "]";
+ }
+
+ @Nonnull
+ @Override
+ public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
+ @Nonnull final ImmutableCollection<Class<? extends TreeNode>> cacheSpecifier) {
+
+ //TODO: implement in cache-related patches to come
+ throw new NotImplementedException();
+ }
+
+ BindingStreamEventWriter createWriter(final NormalizedNodeStreamWriter domWriter) {
+
+ //TODO: streamWriter to come
+ throw new NotImplementedException();
+ }
+
+ @Nonnull
+ protected final <V> V childNonNull(@Nullable final V nullable, final YangInstanceIdentifier.PathArgument child,
+ final String message, final Object... args) {
+ if (nullable != null) {
+ return nullable;
+ }
+ MissingSchemaException.checkModulePresent(factory().getRuntimeContext().getSchemaContext(), child);
+ throw IncorrectNestingException.create(message, args);
+ }
+
+ @Nonnull
+ protected final <V> V childNonNull(@Nullable final V nullable, final QName child, final String message,
+ final Object... args) {
+ if (nullable != null) {
+ return nullable;
+ }
+ MissingSchemaException.checkModulePresent(factory().getRuntimeContext().getSchemaContext(), child);
+ throw IncorrectNestingException.create(message, args);
+ }
+
+ @Nonnull
+ protected final <V> V childNonNull(@Nullable final V nullable, final Class<?> childClass, final String message,
+ final Object... args) {
+ if (nullable != null) {
+ return nullable;
+ }
+ MissingSchemaForClassException.check(factory().getRuntimeContext(), childClass);
+ MissingClassInLoadingStrategyException.check(factory().getRuntimeContext().getStrategy(), childClass);
+ throw IncorrectNestingException.create(message, args);
+ }
+
+ TreeNodeSerializer eventStreamSerializer() {
+ if(eventStreamSerializer == null) {
+ eventStreamSerializer = factory().getEventStreamSerializer(getBindingClass());
+ }
+ return eventStreamSerializer;
+ }
+
+ @Nonnull
+ @Override
+ public NormalizedNode<?, ?> serialize(@Nonnull final D data) {
+ final NormalizedNodeResult result = new NormalizedNodeResult();
+ // We create DOM stream writer which produces normalized nodes
+ final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+ writeAsNormalizedNode(data, domWriter);
+ return result.getResult();
+ }
+
+ @Override
+ public void writeAsNormalizedNode(final D data, final NormalizedNodeStreamWriter writer) {
+ try {
+ eventStreamSerializer().serialize(data, createWriter(writer));
+ } catch (final IOException e) {
+ throw new IllegalStateException("Failed to serialize Binding DTO",e);
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.codec.impl.context.base;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.NodeCodecContext.CodecContextFactory;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Item;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeRoot;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+@Beta
+final class DataContainerCodecPrototype<T> implements NodeContextSupplier {
+
+ private final T schema;
+ private final QNameModule namespace;
+ private final CodecContextFactory factory;
+ private final Class<?> bindingClass;
+ private final Item<?> bindingArg;
+ private final YangInstanceIdentifier.PathArgument yangArg;
+ private volatile DataContainerCodecContext<?,T> instance = null;
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private DataContainerCodecPrototype(final Class<?> cls, final YangInstanceIdentifier.PathArgument arg, final T nodeSchema,
+ final CodecContextFactory factory) {
+ this.bindingClass = Preconditions.checkNotNull(cls);
+ this.yangArg = Preconditions.checkNotNull(arg);
+ this.schema = Preconditions.checkNotNull(nodeSchema);
+ this.factory = Preconditions.checkNotNull(factory);
+
+ this.bindingArg = new Item(bindingClass);
+
+ if (arg instanceof AugmentationIdentifier) {
+ this.namespace = Iterables.getFirst(((AugmentationIdentifier) arg).getPossibleChildNames(), null).getModule();
+ } else {
+ this.namespace = arg.getNodeType().getModule();
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ static <T extends DataSchemaNode> DataContainerCodecPrototype<T> from(final Class<?> cls, final T schema,
+ final CodecContextFactory factory) {
+ return new DataContainerCodecPrototype(cls, NodeIdentifier.create(schema.getQName()), schema, factory);
+ }
+
+ static DataContainerCodecPrototype<SchemaContext> rootPrototype(final CodecContextFactory factory) {
+ final SchemaContext schema = factory.getRuntimeContext().getSchemaContext();
+ final NodeIdentifier arg = NodeIdentifier.create(schema.getQName());
+ return new DataContainerCodecPrototype<>(TreeRoot.class, arg, schema, factory);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ static DataContainerCodecPrototype<?> from(final Class<?> augClass, final AugmentationIdentifier arg,
+ final AugmentationSchema schema, final CodecContextFactory factory) {
+ return new DataContainerCodecPrototype(augClass, arg, schema, factory);
+ }
+
+ static DataContainerCodecPrototype<NotificationDefinition> from(final Class<?> augClass, final NotificationDefinition schema, final CodecContextFactory factory) {
+ final PathArgument arg = NodeIdentifier.create(schema.getQName());
+ return new DataContainerCodecPrototype<>(augClass, arg, schema, factory);
+ }
+
+ protected T getSchema() {
+ return schema;
+ }
+
+ protected QNameModule getNamespace() {
+ return namespace;
+ }
+
+ protected CodecContextFactory getFactory() {
+ return factory;
+ }
+
+ protected Class<?> getBindingClass() {
+ return bindingClass;
+ }
+
+ protected Item<?> getBindingArg() {
+ return bindingArg;
+ }
+
+ protected YangInstanceIdentifier.PathArgument getYangArg() {
+ return yangArg;
+ }
+
+ @Nonnull
+ @Override
+ public DataContainerCodecContext<?,T> get() {
+ DataContainerCodecContext<?,T> tmp = instance;
+ if (tmp == null) {
+ synchronized (this) {
+ tmp = instance;
+ if (tmp == null) {
+ tmp = createInstance();
+ instance = tmp;
+ }
+ }
+ }
+
+ return tmp;
+ }
+
+ @GuardedBy("this")
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ protected DataContainerCodecContext<?, T> createInstance() {
+ //TODO - implement it
+ throw new NotImplementedException();
+ }
+
+ boolean isChoice() {
+ return schema instanceof ChoiceSchemaNode;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.codec.impl.context.base;
+
+import com.google.common.annotations.Beta;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Thrown where incorrect nesting of data structures was detected
+ * and was caused by user.
+ */
+@Beta
+class IncorrectNestingException extends IllegalArgumentException {
+
+ private static final long serialVersionUID = 1L;
+
+ protected IncorrectNestingException(final String message) {
+ super(message);
+ }
+
+ public static IncorrectNestingException create(final String message, final Object... args) {
+ return new IncorrectNestingException(String.format(message, args));
+ }
+
+ public static void check(final boolean check, final String message, final Object... args) {
+ if (!check) {
+ throw IncorrectNestingException.create(message, args);
+ }
+ }
+
+ @Nonnull
+ public static <V> V checkNonNull(@Nullable final V nullable, final String message, final Object... args) {
+ if (nullable != null) {
+ return nullable;
+ }
+ throw IncorrectNestingException.create(message, args);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.codec.impl.context.base;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.api.AugmentationReader;
+import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentable;
+import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+class LazyTreeNode<D extends TreeNode> implements InvocationHandler, AugmentationReader {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LazyTreeNode.class);
+ private static final String GET_IMPLEMENTED_INTERFACE = "implementedInterface";
+ private static final String TO_STRING = "toString";
+ 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<>();
+ private final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data;
+ private final TreeNodeCodecContext<D,?> context;
+
+ private volatile ImmutableMap<Class<? extends Augmentation<?>>, Augmentation<?>> cachedAugmentations = null;
+ private volatile Integer cachedHashcode = null;
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ LazyTreeNode(final TreeNodeCodecContext<D,?> ctx, final NormalizedNodeContainer data) {
+ this.context = Preconditions.checkNotNull(ctx, "Context must not be null");
+ this.data = Preconditions.checkNotNull(data, "Data must not be null");
+ }
+
+ @Override
+ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+ if (method.getParameterTypes().length == 0) {
+ final String name = method.getName();
+ if (GET_IMPLEMENTED_INTERFACE.equals(name)) {
+ return context.getBindingClass();
+ } else if (TO_STRING.equals(name)) {
+ 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())) {
+ return getAugmentationImpl((Class<?>) args[0]);
+ } else if (EQUALS.equals(method.getName())) {
+ return bindingEquals(args[0]);
+ }
+ throw new UnsupportedOperationException("Unsupported method " + method);
+ }
+
+ private boolean bindingEquals(final Object other) {
+ if (other == null) {
+ return false;
+ }
+ if (!context.getBindingClass().isAssignableFrom(other.getClass())) {
+ return false;
+ }
+ try {
+ for (final Method m : context.getHashCodeAndEqualsMethods()) {
+ final Object thisValue = getBindingData(m);
+ final Object otherValue = m.invoke(other);
+ /*
+ * added for valid byte array comparison, when list key type is binary
+ * deepEquals is not used since it does excessive amount of instanceof calls.
+ */
+ if (thisValue instanceof byte[] && otherValue instanceof byte[]) {
+ if (!Arrays.equals((byte[]) thisValue, (byte[]) otherValue)) {
+ return false;
+ }
+ } else if (!Objects.equals(thisValue, otherValue)){
+ return false;
+ }
+ }
+
+ if (Augmentable.class.isAssignableFrom(context.getBindingClass())) {
+ if (!getAugmentationsImpl().equals(getAllAugmentations(other))) {
+ return false;
+ }
+ }
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ LOG.warn("Can not determine equality of {} and {}", this, other, e);
+ return false;
+ }
+ return true;
+ }
+
+ private static Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentations(final Object dataObject) {
+ if (dataObject instanceof AugmentationReader) {
+ return ((AugmentationReader) dataObject).getAugmentations(dataObject);
+ } else if (dataObject instanceof Augmentable<?>){
+ return BindingReflections.getAugmentations((Augmentable<?>) dataObject);
+ }
+
+ throw new IllegalArgumentException("Unable to get all augmentations from " + dataObject);
+ }
+
+ private Integer bindingHashCode() {
+ final Integer ret = cachedHashcode;
+ if (ret != null) {
+ return ret;
+ }
+
+ final int prime = 31;
+ int result = 1;
+ for (final Method m : context.getHashCodeAndEqualsMethods()) {
+ final Object value = getBindingData(m);
+ result = prime * result + Objects.hashCode(value);
+ }
+ if (Augmentable.class.isAssignableFrom(context.getBindingClass())) {
+ result = prime * result + (getAugmentationsImpl().hashCode());
+ }
+ cachedHashcode = result;
+ return result;
+ }
+
+ private Object getBindingData(final Method method) {
+ Object cached = cachedData.get(method);
+ if (cached == null) {
+ final Object readedValue = context.getBindingChildValue(method, data);
+ if (readedValue == null) {
+ cached = NULL_VALUE;
+ } else {
+ cached = readedValue;
+ }
+ cachedData.putIfAbsent(method, cached);
+ }
+
+ return cached == NULL_VALUE ? null : cached;
+ }
+
+ private Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentationsImpl() {
+ ImmutableMap<Class<? extends Augmentation<?>>, Augmentation<?>> ret = cachedAugmentations;
+ if (ret == null) {
+ synchronized (this) {
+ ret = cachedAugmentations;
+ if (ret == null) {
+ ret = ImmutableMap.copyOf(context.getAllAugmentationsFrom(data));
+ cachedAugmentations = ret;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ @Override
+ public Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object obj) {
+ Preconditions.checkArgument(this == Proxy.getInvocationHandler(obj),
+ "Supplied object is not associated with this proxy handler");
+
+ return getAugmentationsImpl();
+ }
+
+ private Object getAugmentationImpl(final Class<?> cls) {
+ final ImmutableMap<Class<? extends Augmentation<?>>, Augmentation<?>> aug = cachedAugmentations;
+ if (aug != null) {
+ return aug.get(cls);
+ }
+ Preconditions.checkNotNull(cls,"Supplied augmentation must not be null.");
+
+ @SuppressWarnings({"unchecked","rawtypes"})
+ final Optional<DataContainerCodecContext<?,?>> augCtx= context.possibleStreamChild((Class) cls);
+ if(augCtx.isPresent()) {
+ final Optional<NormalizedNode<?, ?>> augData = data.getChild(augCtx.get().getDomPathArgument());
+ if (augData.isPresent()) {
+ return augCtx.get().deserialize(augData.get());
+ }
+ }
+ return null;
+ }
+
+ public String bindingToString() {
+ final ToStringHelper helper = MoreObjects.toStringHelper(context.getBindingClass()).omitNullValues();
+
+ for (final Method m :context.getHashCodeAndEqualsMethods()) {
+ helper.add(m.getName(), getBindingData(m));
+ }
+ if (Augmentable.class.isAssignableFrom(context.getBindingClass())) {
+ helper.add("augmentations", getAugmentationsImpl());
+ }
+ return helper.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.context, this.data);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final LazyTreeNode<?> other = (LazyTreeNode<?>) obj;
+ return Objects.equals(context, other.context) && Objects.equals(data, other.data);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.codec.impl.context.base;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableCollection;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.api.codecs.BindingNormalizedNodeCachingCodec;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.api.codecs.BindingTreeNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+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.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+
+@Beta
+final class LeafNodeCodecContext<D extends TreeNode> extends NodeCodecContext<D> implements NodeContextSupplier {
+
+ private final YangInstanceIdentifier.PathArgument yangIdentifier;
+ private final Codec<Object, Object> valueCodec;
+ private final Method getter;
+ private final DataSchemaNode schema;
+ private final Object defaultObject;
+
+ LeafNodeCodecContext(final DataSchemaNode schema, final Codec<Object, Object> codec, final Method getter,
+ final SchemaContext schemaContext) {
+ this.yangIdentifier = new YangInstanceIdentifier.NodeIdentifier(schema.getQName());
+ this.valueCodec = Preconditions.checkNotNull(codec);
+ this.getter = Preconditions.checkNotNull(getter);
+ this.schema = Preconditions.checkNotNull(schema);
+
+ this.defaultObject = createDefaultObject(schema, valueCodec, schemaContext);
+ }
+
+ private static Object createDefaultObject(final DataSchemaNode schema, final Codec<Object, Object> codec,
+ final SchemaContext schemaContext) {
+ if (schema instanceof LeafSchemaNode) {
+ Object defaultValue = ((LeafSchemaNode) schema).getDefault();
+ TypeDefinition<?> type = ((LeafSchemaNode) schema).getType();
+ if (defaultValue != null) {
+ if (type instanceof IdentityrefTypeDefinition) {
+ return qnameDomValueFromString(codec, schema, (String) defaultValue, schemaContext);
+ }
+ return domValueFromString(codec, type, defaultValue);
+ }
+ else {
+ while (type.getBaseType() != null && type.getDefaultValue() == null) {
+ type = type.getBaseType();
+ }
+
+ defaultValue = type.getDefaultValue();
+ if (defaultValue != null) {
+ if (type instanceof IdentityrefTypeDefinition) {
+ return qnameDomValueFromString(codec, schema, (String) defaultValue, schemaContext);
+ }
+ return domValueFromString(codec, type, defaultValue);
+ }
+ }
+ }
+ return null;
+ }
+
+ private static Object qnameDomValueFromString(final Codec<Object, Object> codec, final DataSchemaNode schema,
+ final String defaultValue, final SchemaContext schemaContext) {
+ final int prefixEndIndex = defaultValue.indexOf(':');
+ if (prefixEndIndex != -1) {
+ final String defaultValuePrefix = defaultValue.substring(0, prefixEndIndex);
+
+ final Module module = schemaContext.findModuleByNamespaceAndRevision(schema.getQName().getNamespace(),
+ schema.getQName().getRevision());
+
+ if (module.getPrefix().equals(defaultValuePrefix)) {
+ return codec
+ .deserialize(QName.create(module.getQNameModule(), defaultValue.substring(prefixEndIndex + 1)));
+ } else {
+ final Set<ModuleImport> imports = module.getImports();
+ for (final ModuleImport moduleImport : imports) {
+ if (moduleImport.getPrefix().equals(defaultValuePrefix)) {
+ final Module importedModule = schemaContext.findModuleByName(moduleImport.getModuleName(),
+ moduleImport.getRevision());
+ return codec.deserialize(QName.create(importedModule.getQNameModule(),
+ defaultValue.substring(prefixEndIndex + 1)));
+ }
+ }
+ return null;
+ }
+ }
+
+ return codec.deserialize(QName.create(schema.getQName(), defaultValue));
+ }
+
+ private static Object domValueFromString(final Codec<Object, Object> codec, final TypeDefinition<?> type,
+ final Object defaultValue) {
+ final TypeDefinitionAwareCodec<?, ?> typeDefAwareCodec = TypeDefinitionAwareCodec.from(type);
+ if (typeDefAwareCodec != null) {
+ final Object castedDefaultValue = typeDefAwareCodec.deserialize((String) defaultValue);
+ return codec.deserialize(castedDefaultValue);
+ }
+ // FIXME: BUG-4647 Refactor / redesign this to throw hard error,
+ // once BUG-4638 is fixed and will provide proper getDefaultValue implementation.
+ return null;
+ }
+
+ @Override
+ protected YangInstanceIdentifier.PathArgument getDomPathArgument() {
+ return yangIdentifier;
+ }
+
+ protected Codec<Object, Object> getValueCodec() {
+ return valueCodec;
+ }
+
+ @Nonnull
+ @Override
+ public D deserialize(@Nonnull final NormalizedNode<?, ?> normalizedNode) {
+ throw new UnsupportedOperationException("Leaf can not be deserialized to TreeNode");
+ }
+
+ @Nonnull
+ @Override
+ public NodeCodecContext<?> get() {
+ return this;
+ }
+
+ final Method getGetter() {
+ return getter;
+ }
+
+ @Nonnull
+ @Override
+ public BindingTreeNodeCodec<?> bindingPathArgumentChild(@Nonnull final TreeArgument<?> arg,
+ final List<YangInstanceIdentifier.PathArgument> builder) {
+ throw new IllegalArgumentException("Leaf does not have children");
+ }
+
+ @Nonnull
+ @Override
+ public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
+ @Nonnull final ImmutableCollection<Class<? extends TreeNode>> cacheSpecifier) {
+ throw new UnsupportedOperationException("Leaves does not support caching codec.");
+ }
+
+ @Nonnull
+ @Override
+ public Class<D> getBindingClass() {
+ throw new UnsupportedOperationException("Leaf does not have DataObject representation");
+ }
+
+ @Nonnull
+ @Override
+ public NormalizedNode<?, ?> serialize(@Nonnull final D data) {
+ throw new UnsupportedOperationException("Separate serialization of leaf node is not supported.");
+ }
+
+ @Override
+ public void writeAsNormalizedNode(final D data, final NormalizedNodeStreamWriter writer) {
+ throw new UnsupportedOperationException("Separate serialization of leaf node is not supported.");
+ }
+
+ @Nonnull
+ @Override
+ public <E extends TreeNode> BindingTreeNodeCodec<E> streamChild(@Nonnull final Class<E> childClass) {
+ throw new IllegalArgumentException("Leaf does not have children");
+ }
+
+ @Override
+ public <E extends TreeNode> Optional<? extends BindingTreeNodeCodec<E>> possibleStreamChild(
+ @Nonnull final Class<E> childClass) {
+ throw new IllegalArgumentException("Leaf does not have children");
+ }
+
+ @Nonnull
+ @Override
+ public BindingTreeNodeCodec<?> yangPathArgumentChild(final YangInstanceIdentifier.PathArgument child) {
+ throw new IllegalArgumentException("Leaf does not have children");
+ }
+
+ @Override
+ protected Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
+ if (normalizedNode instanceof LeafNode<?>) {
+ return valueCodec.deserialize(normalizedNode.getValue());
+ }
+ if (normalizedNode instanceof LeafSetNode<?>) {
+ @SuppressWarnings("unchecked")
+ final Collection<LeafSetEntryNode<Object>> domValues = ((LeafSetNode<Object>) normalizedNode).getValue();
+ final List<Object> result = new ArrayList<>(domValues.size());
+ for (final LeafSetEntryNode<Object> valueNode : domValues) {
+ result.add(valueCodec.deserialize(valueNode.getValue()));
+ }
+ return result;
+ }
+ return null;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ public TreeArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
+ Preconditions.checkArgument(getDomPathArgument().equals(arg));
+ return null;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public YangInstanceIdentifier.PathArgument serializePathArgument(final TreeArgument arg) {
+ return getDomPathArgument();
+ }
+
+ @Nonnull
+ @Override
+ public DataSchemaNode getSchema() {
+ return schema;
+ }
+
+ /**
+ * Return the default value object.
+ *
+ * @return The default value object, or null if the default value is not defined.
+ */
+ @Nullable
+ Object defaultObject() {
+ return defaultObject;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.codec.impl.context.base;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.MissingSchemaException;
+import org.opendaylight.mdsal.binding.javav2.generator.api.ClassLoadingStrategy;
+
+/**
+ * Thrown when user schema for supplied binding class is available in present schema context, but
+ * binding class itself is not known to codecs because backing class loading strategy did not
+ * provided it.
+ */
+@Beta
+public class MissingClassInLoadingStrategyException extends MissingSchemaException {
+
+ private static final long serialVersionUID = 1L;
+
+ protected MissingClassInLoadingStrategyException(final String msg, final Throwable cause) {
+ super(msg, cause);
+ }
+
+ public static void check(final ClassLoadingStrategy strategy, final Class<?> childClass) {
+ try {
+ strategy.loadClass(childClass.getName());
+ } catch (final ClassNotFoundException e) {
+ final String message =
+ String.format("User supplied class %s is not available in %s.", childClass.getName(), strategy);
+ throw new MissingClassInLoadingStrategyException(message, e);
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.codec.impl.context.base;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.MissingSchemaException;
+import org.opendaylight.mdsal.binding.javav2.runtime.context.BindingRuntimeContext;
+import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
+
+/**
+ * Thrown when Java Binding class was used in data for which codec does not
+ * have schema.
+ *
+ * By serialization / deserialization of this exception {@link #getBindingClass()}
+ * will return null.
+ */
+@Beta
+public class MissingSchemaForClassException extends MissingSchemaException {
+
+ private static final long serialVersionUID = 1L;
+
+ private final transient Class<?> bindingClass;
+
+ private MissingSchemaForClassException(final Class<?> clz) {
+ super(String.format("Schema is not available for %s", clz));
+ this.bindingClass = Preconditions.checkNotNull(clz);
+ }
+
+ static MissingSchemaForClassException forClass(final Class<?> clz) {
+ return new MissingSchemaForClassException(clz);
+ }
+
+ public Class<?> getBindingClass() {
+ return bindingClass;
+ }
+
+ public static void check(final BindingRuntimeContext runtimeContext, final Class<?> bindingClass) {
+ final Object schema;
+ if (Augmentation.class.isAssignableFrom(bindingClass)) {
+ schema = runtimeContext.getAugmentationDefinition(bindingClass);
+ } else {
+ schema = runtimeContext.getSchemaDefinition(bindingClass);
+ }
+ if (schema == null) {
+ throw MissingSchemaForClassException.forClass(bindingClass);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.codec.impl.context.base;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import java.util.List;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.api.codecs.BindingTreeNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.runtime.context.BindingRuntimeContext;
+import org.opendaylight.mdsal.binding.javav2.spec.base.IdentifiableItem;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializer;
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+/**
+ *
+ * Location specific context for schema nodes, which contains codec specific
+ * information to properly serialize / deserialize from Java YANG Binding data
+ * to NormalizedNode data.
+ *
+ * Two core subtypes of codec context are available:
+ * <ul>
+ * <li>{@link LeafNodeCodecContext} - Context for nodes, which does not contain
+ * any nested YANG modeled substructures.</li>
+ * <li>{@link TreeNodeCodecContext} - Context for nodes, which does contain
+ * nested YANG modeled substructures. This context nodes contains context
+ * for children nodes.</li>
+ * </ul>
+ *
+ */
+@Beta
+abstract class NodeCodecContext<D extends TreeNode> implements BindingTreeNodeCodec<D>{
+ /**
+ * Returns Yang Instance Identifier Path Argument of current node
+ *
+ * @return DOM Path Argument of node
+ */
+ protected abstract YangInstanceIdentifier.PathArgument getDomPathArgument();
+
+ /**
+ *
+ * Immutable factory, which provides access to runtime context,
+ * create leaf nodes and provides path argument codecs.
+ * <p>
+ * During lifetime of factory all calls for same arguments to method must return
+ * equal result (not necessary same instance of result).
+ *
+ */
+ protected interface CodecContextFactory {
+ /**
+ * Returns immutable runtime context associated with this factory.
+ * @return runtime context
+ */
+ BindingRuntimeContext getRuntimeContext();
+
+ /**
+ * Returns leaf nodes for supplied data container and parent class.
+ *
+ * @param type Binding type for which leaves should be loaded.
+ * @param schema Instantiated schema of binding type.
+ * @return Map of local name to leaf node context.
+ */
+ ImmutableMap<String, LeafNodeCodecContext<?>> getLeafNodes(Class<?> type, DataNodeContainer schema);
+
+ /**
+ * Returns Path argument codec for list item
+ *
+ * @param type Type of list item
+ * @param schema Schema of list item
+ * @return Path argument codec for supplied list item.
+ */
+ Codec<NodeIdentifierWithPredicates, IdentifiableItem<?, ?>> getPathArgumentCodec(Class<?> type,
+ ListSchemaNode schema);
+
+ TreeNodeSerializer getEventStreamSerializer(Class<?> type);
+ }
+
+ /**
+ *
+ * Serializes supplied Binding Path Argument
+ * and adds all necessary YANG instance identifiers to supplied list.
+ *
+ * @param arg Binding Path Argument
+ * @param builder DOM Path argument.
+ */
+ @SuppressWarnings("rawtypes")
+ protected void addYangPathArgument(final TreeArgument arg,
+ final List<YangInstanceIdentifier.PathArgument> builder) {
+ if (builder != null) {
+ builder.add(getDomPathArgument());
+ }
+ }
+
+ protected abstract Object deserializeObject(NormalizedNode<?, ?> normalizedNode);
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.codec.impl.context.base;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Supplier;
+import javax.annotation.Nonnull;
+
+/**
+ * Type capture of an entity producing NodeCodecContexts.
+ */
+@Beta
+interface NodeContextSupplier extends Supplier<NodeCodecContext<?>> {
+
+ @Override
+ @Nonnull
+ NodeCodecContext<?> get();
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.javav2.dom.codec.impl.context.base;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
+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 java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.mdsal.binding.javav2.generator.api.ClassLoadingStrategy;
+import org.opendaylight.mdsal.binding.javav2.model.api.Type;
+import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentable;
+import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
+import org.opendaylight.mdsal.binding.javav2.spec.structural.AugmentationHolder;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+abstract class TreeNodeCodecContext<D extends TreeNode, T extends DataNodeContainer>
+ extends DataContainerCodecContext<D, T> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TreeNodeCodecContext.class);
+ private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, InvocationHandler.class);
+ private static final MethodType TREENODE_TYPE = MethodType.methodType(TreeNode.class, InvocationHandler.class);
+ private static final Comparator<Method> METHOD_BY_ALPHABET = Comparator.comparing(Method::getName);
+
+ private final ImmutableMap<String, LeafNodeCodecContext<?>> leafChild;
+ private final ImmutableMap<YangInstanceIdentifier.PathArgument, NodeContextSupplier> byYang;
+ private final ImmutableSortedMap<Method, NodeContextSupplier> byMethod;
+ private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
+ private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
+ private final ImmutableMap<AugmentationIdentifier, Type> possibleAugmentations;
+ private final MethodHandle proxyConstructor;
+
+ private final ConcurrentMap<PathArgument, DataContainerCodecPrototype<?>> byYangAugmented =
+ new ConcurrentHashMap<>();
+ private final ConcurrentMap<Class<?>, DataContainerCodecPrototype<?>> byStreamAugmented = new ConcurrentHashMap<>();
+
+
+ protected TreeNodeCodecContext(final DataContainerCodecPrototype<T> prototype) {
+ super(prototype);
+
+ this.leafChild = factory().getLeafNodes(getBindingClass(), getSchema());
+
+ final Map<Class<?>, Method> clsToMethod = BindingReflections.getChildrenClassToMethod(getBindingClass());
+
+ final Map<YangInstanceIdentifier.PathArgument, NodeContextSupplier> byYangBuilder = new HashMap<>();
+ final SortedMap<Method, NodeContextSupplier> byMethodBuilder = new TreeMap<>(METHOD_BY_ALPHABET);
+ final Map<Class<?>, DataContainerCodecPrototype<?>> byStreamClassBuilder = new HashMap<>();
+ final Map<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClassBuilder = new HashMap<>();
+
+ // Adds leaves to mapping
+ for (final LeafNodeCodecContext<?> leaf : leafChild.values()) {
+ byMethodBuilder.put(leaf.getGetter(), leaf);
+ byYangBuilder.put(leaf.getDomPathArgument(), leaf);
+ }
+
+ for (final Entry<Class<?>, Method> childDataObj : clsToMethod.entrySet()) {
+ final DataContainerCodecPrototype<?> childProto = loadChildPrototype(childDataObj.getKey());
+ byMethodBuilder.put(childDataObj.getValue(), childProto);
+ byStreamClassBuilder.put(childProto.getBindingClass(), childProto);
+ byYangBuilder.put(childProto.getYangArg(), childProto);
+ //TODO: get cases in consideration - finish in patches to come
+ //if (childProto.isChoice()) {
+ }
+ this.byMethod = ImmutableSortedMap.copyOfSorted(byMethodBuilder);
+ this.byYang = ImmutableMap.copyOf(byYangBuilder);
+ this.byStreamClass = ImmutableMap.copyOf(byStreamClassBuilder);
+ byBindingArgClassBuilder.putAll(byStreamClass);
+ this.byBindingArgClass = ImmutableMap.copyOf(byBindingArgClassBuilder);
+
+
+ if (Augmentable.class.isAssignableFrom(getBindingClass())) {
+ this.possibleAugmentations = factory().getRuntimeContext().getAvailableAugmentationTypes(getSchema());
+ } else {
+ this.possibleAugmentations = ImmutableMap.of();
+ }
+ reloadAllAugmentations();
+
+ final Class<?> proxyClass = Proxy.getProxyClass(getBindingClass().getClassLoader(), new Class[] { getBindingClass(), AugmentationHolder.class });
+ try {
+ proxyConstructor = MethodHandles.publicLookup().findConstructor(proxyClass, CONSTRUCTOR_TYPE)
+ .asType(TREENODE_TYPE);
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new IllegalStateException("Failed to find contructor for class " + proxyClass);
+ }
+ }
+
+ private void reloadAllAugmentations() {
+ for (final Entry<AugmentationIdentifier, Type> augment : possibleAugmentations.entrySet()) {
+ final DataContainerCodecPrototype<?> augProto = getAugmentationPrototype(augment.getValue());
+ if (augProto != null) {
+ byYangAugmented.putIfAbsent(augProto.getYangArg(), augProto);
+ byStreamAugmented.putIfAbsent(augProto.getBindingClass(), augProto);
+ }
+ }
+ }
+
+ @Nonnull
+ @SuppressWarnings("unchecked")
+ @Override
+ public <DV extends TreeNode> DataContainerCodecContext<DV, ?> streamChild(@Nonnull final Class<DV> childClass) {
+ final DataContainerCodecPrototype<?> childProto = streamChildPrototype(childClass);
+ return (DataContainerCodecContext<DV, ?>) childNonNull(childProto, childClass, " Child %s is not valid child.",
+ childClass).get();
+ }
+
+ private final DataContainerCodecPrototype<?> streamChildPrototype(final Class<?> childClass) {
+ final DataContainerCodecPrototype<?> childProto = byStreamClass.get(childClass);
+ if (childProto != null) {
+ return childProto;
+ }
+ if (Augmentation.class.isAssignableFrom(childClass)) {
+ return augmentationByClass(childClass);
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <DV extends TreeNode> Optional<DataContainerCodecContext<DV, ?>> possibleStreamChild(
+ @Nonnull final Class<DV> childClass) {
+ final DataContainerCodecPrototype<?> childProto = streamChildPrototype(childClass);
+ if (childProto != null) {
+ return Optional.of((DataContainerCodecContext<DV,?>) childProto.get());
+ }
+ return Optional.absent();
+ }
+
+ @Nonnull
+ @Override
+ public DataContainerCodecContext<?,?> bindingPathArgumentChild(final TreeArgument<?> arg,
+ final List<PathArgument> builder) {
+
+ final Class<? extends TreeNode> argType = (Class<? extends TreeNode>) arg.getType();
+ DataContainerCodecPrototype<?> ctxProto = byBindingArgClass.get(argType);
+ if (ctxProto == null && Augmentation.class.isAssignableFrom(argType)) {
+ ctxProto = augmentationByClass(argType);
+ }
+ final DataContainerCodecContext<?, ?> context =
+ childNonNull(ctxProto, argType, "Class %s is not valid child of %s", argType, getBindingClass()).get();
+ //TODO: get cases in consideration - finish in patches to come
+// if (context instanceof ChoiceNodeCodecContext) {
+ context.addYangPathArgument(arg, builder);
+ return context;
+ }
+
+ @Nonnull
+ @SuppressWarnings("unchecked")
+ @Override
+ public NodeCodecContext<D> yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg) {
+ final NodeContextSupplier childSupplier;
+ if (arg instanceof NodeIdentifierWithPredicates) {
+ childSupplier = byYang.get(new NodeIdentifier(arg.getNodeType()));
+ } else if (arg instanceof AugmentationIdentifier) {
+ childSupplier = yangAugmentationChild((AugmentationIdentifier) arg);
+ } else {
+ childSupplier = byYang.get(arg);
+ }
+
+ return (NodeCodecContext<D>) childNonNull(childSupplier, arg,
+ "Argument %s is not valid child of %s", arg, getSchema()).get();
+ }
+
+ protected final LeafNodeCodecContext<?> getLeafChild(final String name) {
+ final LeafNodeCodecContext<?> value = leafChild.get(name);
+ return IncorrectNestingException.checkNonNull(value, "Leaf %s is not valid for %s", name, getBindingClass());
+ }
+
+ private DataContainerCodecPrototype<?> loadChildPrototype(final Class<?> childClass) {
+ final DataSchemaNode origDef = factory().getRuntimeContext().getSchemaDefinition(childClass);
+ // Direct instantiation or use in same module in which grouping
+ // was defined.
+ DataSchemaNode sameName;
+ try {
+ sameName = getSchema().getDataChildByName(origDef.getQName());
+ } catch (final IllegalArgumentException e) {
+ sameName = null;
+ }
+ final DataSchemaNode childSchema;
+ if (sameName != null) {
+ // Exactly same schema node
+ if (origDef.equals(sameName)) {
+ childSchema = sameName;
+ // We check if instantiated node was added via uses
+ // statement and is instantiation of same grouping
+ } else if (origDef.equals(SchemaNodeUtils.getRootOriginalIfPossible(sameName))) {
+ childSchema = sameName;
+ } else {
+ // Node has same name, but clearly is different
+ childSchema = null;
+ }
+ } else {
+ // We are looking for instantiation via uses in other module
+ final QName instantiedName = QName.create(namespace(), origDef.getQName().getLocalName());
+ final DataSchemaNode potential = getSchema().getDataChildByName(instantiedName);
+ // We check if it is really instantiated from same
+ // definition as class was derived
+ if (potential != null && origDef.equals(SchemaNodeUtils.getRootOriginalIfPossible(potential))) {
+ childSchema = potential;
+ } else {
+ childSchema = null;
+ }
+ }
+ final DataSchemaNode nonNullChild =
+ childNonNull(childSchema, childClass, "Node %s does not have child named %s", getSchema(), childClass);
+ return DataContainerCodecPrototype.from(childClass, nonNullChild, factory());
+ }
+
+ private DataContainerCodecPrototype<?> yangAugmentationChild(final AugmentationIdentifier arg) {
+ final DataContainerCodecPrototype<?> firstTry = byYangAugmented.get(arg);
+ if (firstTry != null) {
+ return firstTry;
+ }
+ if (possibleAugmentations.containsKey(arg)) {
+ reloadAllAugmentations();
+ return byYangAugmented.get(arg);
+ }
+ return null;
+ }
+
+ @Nullable
+ private DataContainerCodecPrototype<?> augmentationByClass(@Nonnull final Class<?> childClass) {
+ final DataContainerCodecPrototype<?> firstTry = augmentationByClassOrEquivalentClass(childClass);
+ if (firstTry != null) {
+ return firstTry;
+ }
+ reloadAllAugmentations();
+ return augmentationByClassOrEquivalentClass(childClass);
+ }
+
+ @Nullable
+ private final DataContainerCodecPrototype<?> augmentationByClassOrEquivalentClass(@Nonnull final Class<?> childClass) {
+ final DataContainerCodecPrototype<?> childProto = byStreamAugmented.get(childClass);
+ if (childProto != null) {
+ return childProto;
+ }
+
+ /*
+ * It is potentially mismatched valid augmentation - we look up equivalent augmentation
+ * using reflection and walk all stream child and compare augmenations classes if they are
+ * equivalent.
+ *
+ * FIXME: Cache mapping of mismatched augmentation to real one, to speed up lookup.
+ */
+ @SuppressWarnings("rawtypes")
+ final Class<?> augTarget = BindingReflections.findAugmentationTarget((Class) childClass);
+ if ((getBindingClass().equals(augTarget))) {
+ for (final DataContainerCodecPrototype<?> realChild : byStreamAugmented.values()) {
+ if (Augmentation.class.isAssignableFrom(realChild.getBindingClass())
+ && BindingReflections.isSubstitutionFor(childClass, realChild.getBindingClass())) {
+ return realChild;
+ }
+ }
+ }
+ return null;
+ }
+
+ private DataContainerCodecPrototype<?> getAugmentationPrototype(final Type value) {
+ final ClassLoadingStrategy loader = factory().getRuntimeContext().getStrategy();
+ @SuppressWarnings("rawtypes")
+ final Class augClass;
+ try {
+ augClass = loader.loadClass(value);
+ } catch (final ClassNotFoundException e) {
+ LOG.debug("Failed to load augmentation prototype for {}. Will be retried when needed.", value, e);
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ final Entry<AugmentationIdentifier, AugmentationSchema> augSchema = factory().getRuntimeContext()
+ .getResolvedAugmentationSchema(getSchema(), augClass);
+ return DataContainerCodecPrototype.from(augClass, augSchema.getKey(), augSchema.getValue(), factory());
+ }
+
+ @SuppressWarnings("rawtypes")
+ Object getBindingChildValue(final Method method, final NormalizedNodeContainer domData) {
+ final NodeCodecContext<?> childContext = byMethod.get(method).get();
+ @SuppressWarnings("unchecked")
+ final Optional<NormalizedNode<?, ?>> domChild = domData.getChild(childContext.getDomPathArgument());
+ if (domChild.isPresent()) {
+ return childContext.deserializeObject(domChild.get());
+ } else if (childContext instanceof LeafNodeCodecContext) {
+ return ((LeafNodeCodecContext)childContext).defaultObject();
+ } else {
+ return null;
+ }
+ }
+
+ protected final D createBindingProxy(final NormalizedNodeContainer<?, ?, ?> node) {
+ try {
+ return (D) proxyConstructor.invokeExact((InvocationHandler) new LazyTreeNode<>(this, node));
+ } catch (final Throwable e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAllAugmentationsFrom(
+ final NormalizedNodeContainer<?, PathArgument, NormalizedNode<?, ?>> data) {
+
+ @SuppressWarnings("rawtypes")
+ final Map map = new HashMap<>();
+
+ for (final NormalizedNode<?, ?> childValue : data.getValue()) {
+ if (childValue instanceof AugmentationNode) {
+ final AugmentationNode augDomNode = (AugmentationNode) childValue;
+ final DataContainerCodecPrototype<?> codecProto = yangAugmentationChild(augDomNode.getIdentifier());
+ if (codecProto != null) {
+ final DataContainerCodecContext<?, ?> codec = codecProto.get();
+ map.put(codec.getBindingClass(), codec.deserializeObject(augDomNode));
+ }
+ }
+ }
+ for (final DataContainerCodecPrototype<?> value : byStreamAugmented.values()) {
+ final Optional<NormalizedNode<?, ?>> augData = data.getChild(value.getYangArg());
+ if (augData.isPresent()) {
+ map.put(value.getBindingClass(), value.get().deserializeObject(augData.get()));
+ }
+ }
+ return map;
+ }
+
+ public Collection<Method> getHashCodeAndEqualsMethods() {
+ return byMethod.keySet();
+ }
+
+ @Override
+ public TreeArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
+ Preconditions.checkArgument(getDomPathArgument().equals(arg));
+ return bindingArg();
+ }
+
+ @Override
+ public YangInstanceIdentifier.PathArgument serializePathArgument(final TreeArgument arg) {
+ Preconditions.checkArgument(bindingArg().equals(arg));
+ return getDomPathArgument();
+ }
+
+}
\ No newline at end of file