--- /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.adapter.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.dom.api.DOMDataBroker;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This abstract class forwards DOM Data Broker and provides de-serialization of DOM data to Binding data.
+ */
+@Beta
+public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBroker>, AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedDataBroker.class);
+
+ // The Broker to whom we do all forwarding
+ private final DOMDataBroker domDataBroker;
+
+ private final BindingToNormalizedNodeCodec codec;
+
+ protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker, final BindingToNormalizedNodeCodec codec) {
+ this.domDataBroker = domDataBroker;
+ this.codec = codec;
+ }
+
+ protected BindingToNormalizedNodeCodec getCodec() {
+ return codec;
+ }
+
+ @Override
+ public DOMDataBroker getDelegate() {
+ return domDataBroker;
+ }
+
+ protected Map<InstanceIdentifier<? extends TreeNode>, TreeNode> toBinding(
+ final InstanceIdentifier<? extends TreeNode> path,
+ final Map<YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized) {
+ final Map<InstanceIdentifier<? extends TreeNode>, TreeNode> newMap = new HashMap<>();
+
+ for (final Map.Entry<YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> entry : normalized.entrySet()) {
+ try {
+ final Optional<Entry<InstanceIdentifier<? extends TreeNode>, TreeNode>> potential =
+ getCodec().toBinding(entry);
+ if (potential.isPresent()) {
+ final Entry<InstanceIdentifier<? extends TreeNode>, TreeNode> binding = potential.get();
+ newMap.put(binding.getKey(), binding.getValue());
+ }
+ } catch (final DeserializationException e) {
+ LOG.warn("Failed to transform {}, omitting it", entry, e);
+ }
+ }
+ return newMap;
+ }
+
+ protected Set<InstanceIdentifier<?>> toBinding(final InstanceIdentifier<?> path,
+ final Set<YangInstanceIdentifier> normalized) {
+ final Set<InstanceIdentifier<?>> hashSet = new HashSet<>();
+ for (final YangInstanceIdentifier normalizedPath : normalized) {
+ try {
+ final Optional<InstanceIdentifier<? extends TreeNode>> potential =
+ getCodec().toBinding(normalizedPath);
+ if (potential.isPresent()) {
+ final InstanceIdentifier<? extends TreeNode> binding = potential.get();
+ hashSet.add(binding);
+ } else if (normalizedPath
+ .getLastPathArgument() instanceof YangInstanceIdentifier.AugmentationIdentifier) {
+ hashSet.add(path);
+ }
+ } catch (final DeserializationException e) {
+ LOG.warn("Failed to transform {}, omitting it", normalizedPath, e);
+ }
+ }
+ return hashSet;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Optional<TreeNode> toBindingData(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> data) {
+ if (path.isWildcarded()) {
+ return Optional.absent();
+ }
+ return (Optional<TreeNode>) getCodec().deserializeFunction(path)
+ .apply(Optional.of(data));
+ }
+
+ @Override
+ public void close() {
+ // Intentional NOOP
+ }
+}
--- /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.adapter.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import javax.annotation.Nonnull;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.common.api.AsyncTransaction;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.common.api.ReadFailedException;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Abstract class for forwards transaction and codec for serialize/deserialize DOM and Binding data.
+ *
+ * @param <T>
+ * - type of asynchronous transaction
+ */
+@Beta
+public abstract class AbstractForwardedTransaction<
+ T extends AsyncTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>>>
+ implements Delegator<T>, Identifiable<Object> {
+
+ private final T delegate;
+ private final BindingToNormalizedNodeCodec codec;
+
+ public AbstractForwardedTransaction(final T delegateTx, final BindingToNormalizedNodeCodec codec) {
+ this.delegate = Preconditions.checkNotNull(delegateTx, "Delegate must not be null");
+ this.codec = Preconditions.checkNotNull(codec, "Codec must not be null");
+ }
+
+ @Nonnull
+ @Override
+ public final Object getIdentifier() {
+ return delegate.getIdentifier();
+ }
+
+ @Override
+ public final T getDelegate() {
+ return delegate;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected final <S extends AsyncTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>>> S
+ getDelegateChecked(final Class<S> txType) {
+ Preconditions.checkState(txType.isInstance(delegate));
+ return (S) delegate;
+ }
+
+ protected final BindingToNormalizedNodeCodec getCodec() {
+ return codec;
+ }
+
+ protected final <D extends TreeNode> CheckedFuture<Optional<D>, ReadFailedException> doRead(
+ final DOMDataTreeReadTransaction readTx, final LogicalDatastoreType store,
+ final InstanceIdentifier<D> path) {
+ Preconditions.checkArgument(!path.isWildcarded(), "Invalid read of wildcarded path %s", path);
+
+ return MappingCheckedFuture
+ .create(Futures.transform(readTx.read(store, codec.toYangInstanceIdentifierBlocking(path)),
+ codec.deserializeFunction(path)), ReadFailedException.MAPPER);
+ }
+}
--- /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.adapter.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Map.Entry;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.binding.javav2.spec.base.IdentifiableItem;
+import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Abstract Base Transaction for transactions which are backed by {@link DOMDataTreeWriteTransaction}.
+ */
+@Beta
+public abstract class AbstractWriteTransaction<T extends DOMDataTreeWriteTransaction>
+ extends AbstractForwardedTransaction<T> {
+
+ protected AbstractWriteTransaction(final T delegate, final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ /**
+ * Put Binding data to specific datastore via
+ * {@link DOMDataTreeWriteTransaction#put(LogicalDatastoreType, YangInstanceIdentifier, NormalizedNode)}.
+ *
+ * @param store
+ * - specific {@link LogicalDatastoreType}
+ * @param path
+ * - path to data
+ * @param data
+ * - data to be written to specific path
+ * @param createParents
+ * - option to create parent of data to be write
+ */
+ public final <U extends TreeNode> void put(final LogicalDatastoreType store, final InstanceIdentifier<U> path,
+ final U data, final boolean createParents) {
+ Preconditions.checkArgument(!path.isWildcarded(), "Cannot put data into wildcarded path %s", path);
+
+ final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec().toNormalizedNode(path, data);
+ if (createParents) {
+ ensureParentsByMerge(store, normalized.getKey(), path);
+ } else {
+ ensureListParentIfNeeded(store, path, normalized);
+ }
+
+ getDelegate().put(store, normalized.getKey(), normalized.getValue());
+ }
+
+ /**
+ * Merge Binding data with existing data on specific path via
+ * {@link DOMDataTreeWriteTransaction#merge(LogicalDatastoreType, YangInstanceIdentifier, NormalizedNode)}.
+ *
+ * @param store
+ * - specific {@link LogicalDatastoreType}
+ * @param path
+ * - path to data
+ * @param data
+ * - data to be merged
+ * @param createParents
+ * - option to create parent of data to be merged
+ */
+ public final <U extends TreeNode> void merge(final LogicalDatastoreType store, final InstanceIdentifier<U> path,
+ final U data, final boolean createParents) {
+ Preconditions.checkArgument(!path.isWildcarded(), "Cannot merge data into wildcarded path %s", path);
+
+ final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec().toNormalizedNode(path, data);
+ if (createParents) {
+ ensureParentsByMerge(store, normalized.getKey(), path);
+ } else {
+ ensureListParentIfNeeded(store, path, normalized);
+ }
+
+ getDelegate().merge(store, normalized.getKey(), normalized.getValue());
+ }
+
+ /**
+ * Ensures list parent if item is list, otherwise noop.
+ *
+ * <p>
+ * One of properties of binding specification is that it is impossible to represent list as a whole and
+ * thus it is impossible to write empty variation of MapNode without creating parent node, with empty
+ * list.
+ *
+ * <p>
+ * This actually makes writes such as
+ *
+ * <pre>
+ * put("Nodes", new NodesBuilder().build());
+ * put("Nodes/Node[key]", new NodeBuilder().setKey("key").build());
+ * </pre>
+ *
+ * <p>
+ * To result in three DOM operations:
+ *
+ * <pre>
+ * put("/nodes", domNodes);
+ * merge("/nodes/node", domNodeList);
+ * put("/nodes/node/node[key]", domNode);
+ * </pre>
+ *
+ * <p>
+ * In order to allow that to be inserted if necessary, if we know item is list item, we will try to merge
+ * empty MapNode or OrderedNodeMap to ensure list exists.
+ *
+ * @param store
+ * - Data Store type
+ * @param path
+ * - Path to data (Binding Aware)
+ * @param normalized
+ * - Normalized version of data to be written
+ */
+ private void ensureListParentIfNeeded(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
+ final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized) {
+ if (IdentifiableItem.class.isAssignableFrom(path.getTargetType())) {
+ final YangInstanceIdentifier parentMapPath = normalized.getKey().getParent();
+ Preconditions.checkArgument(parentMapPath != null, "Map path %s does not have a parent", path);
+
+ final NormalizedNode<?, ?> emptyParent = getCodec().getDefaultNodeFor(parentMapPath);
+ getDelegate().merge(store, parentMapPath, emptyParent);
+ }
+ }
+
+ /**
+ * Subclasses of this class are required to implement creation of parent nodes based on behavior of their
+ * underlying transaction.
+ *
+ * @param store
+ * - an instance of LogicalDatastoreType
+ * @param domPath
+ * - an instance of YangInstanceIdentifier
+ * @param path
+ * - an instance of InstanceIdentifier
+ */
+ protected final void ensureParentsByMerge(final LogicalDatastoreType store, final YangInstanceIdentifier domPath,
+ final InstanceIdentifier<?> path) {
+ final YangInstanceIdentifier parentPath = domPath.getParent();
+ if (parentPath != null) {
+ final NormalizedNode<?, ?> parentNode = getCodec().instanceIdentifierToNode(parentPath);
+ getDelegate().merge(store, YangInstanceIdentifier.create(parentNode.getIdentifier()), parentNode);
+ }
+ }
+
+ protected final void doDelete(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+ Preconditions.checkArgument(!path.isWildcarded(), "Cannot delete wildcarded path %s", path);
+
+ final YangInstanceIdentifier normalized = getCodec().toYangInstanceIdentifierBlocking(path);
+ getDelegate().delete(store, normalized);
+ }
+
+ protected final CheckedFuture<Void, TransactionCommitFailedException> doSubmit() {
+ return getDelegate().submit();
+ }
+
+ protected final boolean doCancel() {
+ return getDelegate().cancel();
+ }
+}
+
--- /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.adapter.spi.builder;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+import java.util.Set;
+import org.opendaylight.yangtools.concepts.Builder;
+
+/**
+ * Class for building instances of delegates of specific type.
+ *
+ * @param <T>
+ * - builded specific object type
+ * @param <D>
+ * - delegates type
+ */
+@Beta
+public abstract class AdapterBuilder<T, D> implements Builder<T> {
+
+ private final ClassToInstanceMap<D> delegates = MutableClassToInstanceMap.create();
+
+ protected abstract T createInstance(ClassToInstanceMap<D> delegates);
+
+ /**
+ * Get required delegates.
+ *
+ * @return set of delegates
+ */
+ public abstract Set<? extends Class<? extends D>> getRequiredDelegates();
+
+ /**
+ * Add delegate to set of delegates.
+ *
+ * @param type
+ * - type of delegate
+ * @param impl
+ * - implementation of delegate
+ */
+ public final <V extends D> void addDelegate(final Class<V> type, final D impl) {
+ delegates.put(type, impl);
+ }
+
+ @Override
+ public final T build() {
+ checkAllRequiredServices();
+ return createInstance(ImmutableClassToInstanceMap.copyOf(delegates));
+ }
+
+ private void checkAllRequiredServices() {
+ for (final Class<? extends D> type : getRequiredDelegates()) {
+ Preconditions.checkState(delegates.get(type) != null, "Requires service %s is not defined.", type);
+ }
+ }
+}
--- /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.adapter.spi.builder;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import org.opendaylight.mdsal.binding.javav2.api.BindingService;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.dom.api.DOMService;
+
+/**
+ * Binding DOM adapter builder instance.
+ *
+ * @param <T>
+ * - Binding Service type
+ */
+@Beta
+public abstract class BindingDOMAdapterBuilder<T extends BindingService> extends AdapterBuilder<T, DOMService> {
+
+ private BindingToNormalizedNodeCodec codec;
+
+ protected abstract T createInstance(BindingToNormalizedNodeCodec codec, ClassToInstanceMap<DOMService> delegates);
+
+ @Override
+ protected final T createInstance(final ClassToInstanceMap<DOMService> delegates) {
+ Preconditions.checkState(codec != null);
+ return createInstance(codec, delegates);
+ }
+
+ /**
+ * Set codec for builder.
+ *
+ * @param codec
+ * - binding normalized node codec
+ */
+ public void setCodec(final BindingToNormalizedNodeCodec codec) {
+ this.codec = codec;
+ }
+
+ /**
+ * Factory for creating of new builder.
+ *
+ * @param <T>
+ * - Binding Service type
+ */
+ public interface Factory<T extends BindingService> {
+
+ /**
+ * Prepare new builder for service.
+ *
+ * @return adapter builder
+ */
+ BindingDOMAdapterBuilder<T> newBuilder();
+ }
+}
--- /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.adapter.spi.loader;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheLoader;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.AdapterBuilder;
+
+/**
+ * Class for loading specific delegate type.
+ *
+ * @param <T>
+ * - built specific object type
+ * @param <D>
+ * - delegates type
+ */
+@Beta
+public abstract class AdapterLoader<T, D> extends CacheLoader<Class<? extends T>, Optional<T>> {
+
+ @Nullable
+ protected abstract D getDelegate(Class<? extends D> reqDeleg);
+
+ @Nonnull
+ protected abstract AdapterBuilder<? extends T, D> createBuilder(Class<? extends T> key);
+
+ @Nonnull
+ @Override
+ public Optional<T> load(@Nonnull final Class<? extends T> key) {
+
+ final AdapterBuilder<? extends T, D> builder = createBuilder(key);
+ for (final Class<? extends D> reqDeleg : builder.getRequiredDelegates()) {
+ final D deleg = getDelegate(reqDeleg);
+ if (deleg != null) {
+ builder.addDelegate(reqDeleg, deleg);
+ } else {
+ return Optional.absent();
+ }
+ }
+ return Optional.of(builder.build());
+ }
+}
\ 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.adapter.spi.loader;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.mdsal.binding.javav2.api.BindingService;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.AdapterBuilder;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder;
+import org.opendaylight.mdsal.binding.javav2.dom.adapter.spi.builder.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.mdsal.dom.api.DOMService;
+
+/**
+ * Loader for factory of services.
+ */
+@Beta
+public abstract class BindingDOMAdapterLoader extends AdapterLoader<BindingService, DOMService> {
+
+ // TODO add all factory of services
+ @SuppressWarnings("checkstyle:GenericWhitespace")
+ private static final Map<Class<?>, BindingDOMAdapterBuilder.Factory<?>> FACTORIES =
+ ImmutableMap.<Class<?>, BindingDOMAdapterBuilder.Factory<?>> builder().build();
+
+ private final BindingToNormalizedNodeCodec codec;
+
+ public BindingDOMAdapterLoader(final BindingToNormalizedNodeCodec codec) {
+ this.codec = codec;
+ }
+
+ @Nonnull
+ @Override
+ protected final AdapterBuilder<? extends BindingService, DOMService>
+ createBuilder(final Class<? extends BindingService> key) {
+ final Factory<?> factory = FACTORIES.get(key);
+ Preconditions.checkArgument(factory != null, "Unsupported service type %s", key);
+ final BindingDOMAdapterBuilder<?> builder = factory.newBuilder();
+ builder.setCodec(codec);
+ return builder;
+ }
+}
\ No newline at end of file