summaryrefslogtreecommitdiffstats
path: root/v3po/translate-utils/src/main/java/io
diff options
context:
space:
mode:
authorMaros Marsalek <mmarsale@cisco.com>2016-07-13 11:52:51 +0200
committerMaros Marsalek <mmarsale@cisco.com>2016-07-21 14:18:59 +0200
commit4e6b846c342b2c9e9443e3d3472685e5acb32fa3 (patch)
treedf8990c71077f8561b94c6055dfe3e2b59fa32e3 /v3po/translate-utils/src/main/java/io
parent758f3cc154d28df97b3995344e0c67190c50a035 (diff)
HONEYCOMB-122 Update reader registry to share similar APIs as writer
+ Extract common registry builder base code (Reader registry is not flat, so there is not full control over ordering as with writers but it is sufficient) + Split CompositeReader into CompositeReader, SubtreeReader and GenericReader + No need to build composite structure in ReaderFactories (registry does that internally) + Keep only ReaderCustomizer + ListReaderCustomizer, no root reader (same for writers) Change-Id: Ic4e5bc96ad47a6cbcada4efcc2209db5c16d2a6c Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'v3po/translate-utils/src/main/java/io')
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java213
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java35
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/TransactionMappingContext.java (renamed from v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionMappingContext.java)4
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java90
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java39
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/CloseableReader.java60
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/KeepaliveReaderWrapper.java (renamed from v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/KeepaliveReaderWrapper.java)34
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java4
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveAugmentReaderCustomizer.java59
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveChildReaderCustomizer.java57
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java57
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java103
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveRootReaderCustomizer.java42
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java192
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistry.java (renamed from v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/DelegatingReaderRegistry.java)33
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java109
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java250
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java101
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java137
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriter.java63
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java25
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java18
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java182
23 files changed, 1342 insertions, 565 deletions
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java
new file mode 100644
index 000000000..23a66337f
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.ModifiableSubtreeManagerRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.SubtreeManager;
+import io.fd.honeycomb.v3po.translate.SubtreeManagerRegistryBuilder;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public abstract class AbstractSubtreeManagerRegistryBuilderBuilder<S extends SubtreeManager<? extends DataObject>, R>
+ implements ModifiableSubtreeManagerRegistryBuilder<S>, SubtreeManagerRegistryBuilder<R>, AutoCloseable {
+
+ // Using directed acyclic graph to represent the ordering relationships between writers
+ private final DirectedAcyclicGraph<InstanceIdentifier<?>, Order>
+ handlersRelations = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new Order());
+ private final Map<InstanceIdentifier<?>, S> handlersMap = new HashMap<>();
+
+ /**
+ * Add handler without any special relationship to any other type.
+ */
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> add(@Nonnull final S handler) {
+ // Make IID wildcarded just in case
+ // + the way InstanceIdentifier.create + equals work for Identifiable items is unexpected, meaning updates would
+ // not be matched to writers in registry
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
+ checkWriterNotPresentYet(targetType);
+ handlersRelations.addVertex(targetType);
+ handlersMap.put(targetType, handler);
+ return this;
+ }
+
+ /**
+ * Add handler without any special relationship to any other type.
+ */
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAdd(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler) {
+ add(getSubtreeHandler(handledChildren, handler));
+ return this;
+ }
+
+ private void checkWriterNotPresentYet(final InstanceIdentifier<?> targetType) {
+ Preconditions.checkArgument(!handlersMap.containsKey(targetType),
+ "Writer for type: %s already present: %s", targetType, handlersMap.get(targetType));
+ }
+
+ /**
+ * Add handler with relationship: to be executed before handler handling relatedType.
+ */
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addBefore(@Nonnull final S handler,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
+ final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
+ checkWriterNotPresentYet(targetType);
+ handlersRelations.addVertex(targetType);
+ handlersRelations.addVertex(wildcardedRelatedType);
+ addEdge(targetType, wildcardedRelatedType);
+ handlersMap.put(targetType, handler);
+ return this;
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addBefore(@Nonnull final S handler,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
+ checkWriterNotPresentYet(targetType);
+ handlersRelations.addVertex(targetType);
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(handlersRelations::addVertex);
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(type -> addEdge(targetType, type));
+ handlersMap.put(targetType, handler);
+ return this;
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddBefore(
+ @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ return addBefore(getSubtreeHandler(handledChildren, handler), relatedType);
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddBefore(
+ @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ return addBefore(getSubtreeHandler(handledChildren, handler), relatedTypes);
+ }
+
+ protected abstract S getSubtreeHandler(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler);
+
+ /**
+ * Add handler with relationship: to be executed after handler handling relatedType.
+ */
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addAfter(@Nonnull final S handler,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
+ final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
+ checkWriterNotPresentYet(targetType);
+ handlersRelations.addVertex(targetType);
+ handlersRelations.addVertex(wildcardedRelatedType);
+ // set edge to indicate before relationship, just reversed
+ addEdge(wildcardedRelatedType, targetType);
+ handlersMap.put(targetType, handler);
+ return this;
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addAfter(@Nonnull final S handler,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
+ checkWriterNotPresentYet(targetType);
+ handlersRelations.addVertex(targetType);
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(handlersRelations::addVertex);
+ // set edge to indicate before relationship, just reversed
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(type -> addEdge(type, targetType));
+ handlersMap.put(targetType, handler);
+ return this;
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddAfter(
+ @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ return addAfter(getSubtreeHandler(handledChildren, handler), relatedType);
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddAfter(
+ @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ return addAfter(getSubtreeHandler(handledChildren, handler), relatedTypes);
+ }
+
+
+ private void addEdge(final InstanceIdentifier<?> targetType,
+ final InstanceIdentifier<?> relatedType) {
+ try {
+ handlersRelations.addDagEdge(targetType, relatedType);
+ } catch (DirectedAcyclicGraph.CycleFoundException e) {
+ throw new IllegalArgumentException(String.format(
+ "Unable to add writer with relation: %s -> %s. Loop detected", targetType, relatedType), e);
+ }
+ }
+
+ protected ImmutableMap<InstanceIdentifier<?>, S> getMappedHandlers() {
+ final ImmutableMap.Builder<InstanceIdentifier<?>, S> builder = ImmutableMap.builder();
+ // Iterate writer types according to their relationships from graph
+ handlersRelations.iterator()
+ .forEachRemaining(writerType -> {
+ // There might be types stored just for relationship sake, no real writer, ignoring those
+ if (handlersMap.containsKey(writerType)) {
+ builder.put(writerType, handlersMap.get(writerType));
+ }
+ });
+
+ // TODO we could optimize subtree handlers, if there is a dedicated handler for a node managed by a subtree
+ // handler, recreate the subtree handler with a subset of handled child nodes
+ // This way it is not necessary to change the configuration of subtree writer, just to add a dedicated child
+ // writer. This will be needed if we ever switch to annotations for reader/writer hierarchy initialization
+
+ return builder.build();
+ }
+
+ @Override
+ public void close() throws Exception {
+ handlersMap.clear();
+ // Wrap sets into another set to avoid concurrent modification ex in graph
+ handlersRelations.removeAllEdges(Sets.newHashSet(handlersRelations.edgeSet()));
+ handlersRelations.removeAllVertices(Sets.newHashSet(handlersRelations.vertexSet()));
+ }
+
+ // Represents edges in graph
+ private class Order {}
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java
index 55ae9ecf0..ba9d8e16f 100644
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java
@@ -22,7 +22,6 @@ import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import io.fd.honeycomb.v3po.translate.SubtreeManager;
-import io.fd.honeycomb.v3po.translate.read.ChildReader;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -32,7 +31,6 @@ import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.ChildOf;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.Identifiable;
import org.opendaylight.yangtools.yang.binding.Identifier;
@@ -75,24 +73,6 @@ public final class RWUtils {
return Iterables.get(pathArguments, i + 1);
}
- public static <T> List<ChildReader<? extends ChildOf<T>>> emptyChildReaderList() {
- return Collections.emptyList();
- }
-
- public static <T> List<ChildReader<? extends Augmentation<T>>> emptyAugReaderList() {
- return Collections.emptyList();
- }
-
- public static <T> List<ChildReader<? extends Augmentation<T>>> singletonAugReaderList(
- ChildReader<? extends Augmentation<T>> item) {
- return Collections.<ChildReader<? extends Augmentation<T>>>singletonList(item);
- }
-
- public static <T> List<ChildReader<? extends ChildOf<T>>> singletonChildReaderList(
- ChildReader<? extends ChildOf<T>> item) {
- return Collections.<ChildReader<? extends ChildOf<T>>>singletonList(item);
- }
-
/**
* Replace last item in ID with a provided IdentifiableItem of the same type
*/
@@ -170,25 +150,16 @@ public final class RWUtils {
}
};
- @SuppressWarnings("unchecked")
- public static <D extends DataObject> InstanceIdentifier<D> appendTypeToId(
- final InstanceIdentifier<? extends DataObject> parentId, final InstanceIdentifier<D> type) {
- Preconditions.checkArgument(!parentId.contains(type),
- "Unexpected InstanceIdentifier %s, already contains %s", parentId, type);
- final InstanceIdentifier.PathArgument t = Iterables.getOnlyElement(type.getPathArguments());
- return (InstanceIdentifier<D>) InstanceIdentifier.create(Iterables.concat(
- parentId.getPathArguments(), Collections.singleton(t)));
- }
-
/**
* Transform a keyed instance identifier into a wildcarded one.
*/
- public static InstanceIdentifier<?> makeIidWildcarded(final InstanceIdentifier<?> id) {
+ @SuppressWarnings("unchecked")
+ public static <D extends DataObject> InstanceIdentifier<D> makeIidWildcarded(final InstanceIdentifier<D> id) {
final List<InstanceIdentifier.PathArgument> transformedPathArguments =
StreamSupport.stream(id.getPathArguments().spliterator(), false)
.map(RWUtils::cleanPathArgumentFromKeys)
.collect(Collectors.toList());
- return InstanceIdentifier.create(transformedPathArguments);
+ return (InstanceIdentifier<D>) InstanceIdentifier.create(transformedPathArguments);
}
private static InstanceIdentifier.PathArgument cleanPathArgumentFromKeys(final InstanceIdentifier.PathArgument pathArgument) {
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionMappingContext.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/TransactionMappingContext.java
index 60f4282f5..6abc3b1eb 100644
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionMappingContext.java
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/TransactionMappingContext.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package io.fd.honeycomb.v3po.translate.util.write;
+package io.fd.honeycomb.v3po.translate.util;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.CheckedFuture;
@@ -28,7 +28,7 @@ import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
/**
- * Binding Transaction backed mapping context
+ * Binding Transaction backed mapping context.
*/
public class TransactionMappingContext implements MappingContext {
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java
new file mode 100644
index 000000000..9bfbc2450
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+public abstract class AbstractGenericReader<D extends DataObject, B extends Builder<D>> implements Reader<D, B> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractGenericReader.class);
+
+ private final InstanceIdentifier<D> instanceIdentifier;
+
+ protected AbstractGenericReader(final InstanceIdentifier<D> managedDataObjectType) {
+ this.instanceIdentifier = RWUtils.makeIidWildcarded(managedDataObjectType);
+ }
+
+ @Nonnull
+ @Override
+ public final InstanceIdentifier<D> getManagedDataObjectType() {
+ return instanceIdentifier;
+ }
+
+ /**
+ * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present.
+ *
+ */
+ protected Optional<D> readCurrent(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ LOG.debug("{}: Reading current: {}", this, id);
+ final B builder = getBuilder(id);
+ // Cache empty value to determine if anything has changed later TODO cache in a field
+ final D emptyValue = builder.build();
+
+ LOG.trace("{}: Reading current attributes", this);
+ readCurrentAttributes(id, builder, ctx);
+
+ // Need to check whether anything was filled in to determine if data is present or not.
+ final D built = builder.build();
+ final Optional<D> read = built.equals(emptyValue)
+ ? Optional.absent()
+ : Optional.of(built);
+
+ LOG.debug("{}: Current node read successfully. Result: {}", this, read);
+ return read;
+ }
+
+ @Nonnull
+ @Override
+ @SuppressWarnings("unchecked")
+ public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ LOG.trace("{}: Reading : {}", this, id);
+ checkArgument(id.getTargetType().equals(getManagedDataObjectType().getTargetType()));
+ return readCurrent((InstanceIdentifier<D>) id, ctx);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Reader[%s]", getManagedDataObjectType().getTargetType().getSimpleName());
+ }
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java
index 58695cf8b..68aa3956e 100644
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java
@@ -25,23 +25,29 @@ import javax.annotation.Nonnull;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.Builder;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
/**
- * Simple DataBroker backed reader allowing to delegate reads to different brokers
+ * Simple DataBroker backed reader allowing to delegate reads to different brokers.
*/
-public final class BindingBrokerReader<D extends DataObject> implements Reader<D> {
+public final class BindingBrokerReader<D extends DataObject, B extends Builder<D>>
+ implements Reader<D, B>, AutoCloseable {
private final InstanceIdentifier<D> instanceIdentifier;
private final DataBroker dataBroker;
private final LogicalDatastoreType datastoreType;
+ private final ReflexiveReaderCustomizer<D, B> reflexiveReaderCustomizer;
- public BindingBrokerReader(final Class<D> managedDataObjectType, final DataBroker dataBroker,
- final LogicalDatastoreType datastoreType) {
+ public BindingBrokerReader(final InstanceIdentifier<D> instanceIdentifier,
+ final DataBroker dataBroker,
+ final LogicalDatastoreType datastoreType,
+ final Class<B> builderClass) {
+ this.reflexiveReaderCustomizer = new ReflexiveReaderCustomizer<>(instanceIdentifier.getTargetType(), builderClass);
+ this.instanceIdentifier = instanceIdentifier;
this.dataBroker = dataBroker;
this.datastoreType = datastoreType;
- this.instanceIdentifier = InstanceIdentifier.create(managedDataObjectType);
}
@Nonnull
@@ -59,9 +65,32 @@ public final class BindingBrokerReader<D extends DataObject> implements Reader<D
}
}
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final D readValue) {
+ reflexiveReaderCustomizer.merge(parentBuilder, readValue);
+ }
+
+ @Nonnull
+ @Override
+ public B getBuilder(final InstanceIdentifier<D> id) {
+ return reflexiveReaderCustomizer.getBuilder(id);
+ }
+
+ @Override
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final B builder,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
@Nonnull
@Override
public InstanceIdentifier<D> getManagedDataObjectType() {
return instanceIdentifier;
}
+
+ @Override
+ public void close() throws Exception {
+ // Noop
+ }
}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/CloseableReader.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/CloseableReader.java
deleted file mode 100644
index 5492062e3..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/CloseableReader.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.util.read;
-
-import com.google.common.base.Optional;
-import io.fd.honeycomb.v3po.translate.read.ReadContext;
-import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
-import io.fd.honeycomb.v3po.translate.read.Reader;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * Closeable wrapper for a reader
- */
-public final class CloseableReader<D extends DataObject> implements Reader<D>, AutoCloseable {
-
- private Reader<D> compositeRootReader;
-
- public CloseableReader(@Nonnull final Reader<D> compositeRootReader) {
- this.compositeRootReader = compositeRootReader;
- }
-
- @Nonnull
- @Override
- public Optional<? extends DataObject> read(@Nonnull InstanceIdentifier<? extends DataObject> id,
- @Nonnull ReadContext ctx) throws ReadFailedException {
- return compositeRootReader.read(id, ctx);
- }
-
- @Nonnull
- @Override
- public InstanceIdentifier<D> getManagedDataObjectType() {
- return compositeRootReader.getManagedDataObjectType();
- }
-
- @Override
- public String toString() {
- return compositeRootReader.toString();
- }
-
- @Override
- public void close() throws Exception {
- //NOOP
- }
-}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/KeepaliveReaderWrapper.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/KeepaliveReaderWrapper.java
index d59111faa..d782bcc7f 100644
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/KeepaliveReaderWrapper.java
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/KeepaliveReaderWrapper.java
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package io.fd.honeycomb.v3po.translate.util;
+package io.fd.honeycomb.v3po.translate.util.read;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import io.fd.honeycomb.v3po.translate.MappingContext;
import io.fd.honeycomb.v3po.translate.ModificationCache;
-import io.fd.honeycomb.v3po.translate.read.ChildReader;
import io.fd.honeycomb.v3po.translate.read.ReadContext;
import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
import java.io.Closeable;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@@ -39,13 +39,13 @@ import org.slf4j.LoggerFactory;
* Reader wrapper that periodically invokes a read to determine whether reads are still fully functional.
* In case a specific error occurs, Keep-alive failure listener gets notified.
*/
-public final class KeepaliveReaderWrapper<D extends DataObject> implements ChildReader<D>, Runnable, Closeable {
+public final class KeepaliveReaderWrapper<D extends DataObject, B extends Builder<D>> implements Reader<D, B>, Runnable, Closeable {
private static final Logger LOG = LoggerFactory.getLogger(KeepaliveReaderWrapper.class);
private static final NoopReadContext CTX = new NoopReadContext();
- private final ChildReader<D> delegate;
+ private final Reader<D, B> delegate;
private final Class<? extends Exception> exceptionType;
private final KeepaliveFailureListener failureListener;
private final ScheduledFuture<?> scheduledFuture;
@@ -59,7 +59,7 @@ public final class KeepaliveReaderWrapper<D extends DataObject> implements Child
* @param delayInSeconds number of seconds to wait between keepalive calls
* @param failureListener listener to be called whenever a keepalive failure is detected
*/
- public KeepaliveReaderWrapper(@Nonnull final ChildReader<D> delegate,
+ public KeepaliveReaderWrapper(@Nonnull final Reader<D, B> delegate,
@Nonnull final ScheduledExecutorService executor,
@Nonnull final Class<? extends Exception> exception,
@Nonnegative final int delayInSeconds,
@@ -73,17 +73,25 @@ public final class KeepaliveReaderWrapper<D extends DataObject> implements Child
}
@Nonnull
- @Override
- public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier id,
@Nonnull final ReadContext ctx) throws ReadFailedException {
return delegate.read(id, ctx);
}
- @Override
- public void read(@Nonnull final InstanceIdentifier<? extends DataObject> id,
- @Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final ReadContext ctx)
- throws ReadFailedException {
- delegate.read(id, parentBuilder, ctx);
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final B builder,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ delegate.readCurrentAttributes(id, builder, ctx);
+ }
+
+ @Nonnull
+ public B getBuilder(final InstanceIdentifier<D> id) {
+ return delegate.getBuilder(id);
+ }
+
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder,
+ @Nonnull final D readValue) {
+ delegate.merge(parentBuilder, readValue);
}
@Nonnull
@@ -99,7 +107,7 @@ public final class KeepaliveReaderWrapper<D extends DataObject> implements Child
final Optional<? extends DataObject> read = read(delegate.getManagedDataObjectType(), CTX);
LOG.debug("Keepalive executed successfully with data: {}", read);
} catch (Exception e) {
- if(exceptionType.isAssignableFrom(e.getClass())) {
+ if (exceptionType.isAssignableFrom(e.getClass())) {
LOG.warn("Keepalive failed. Notifying listener", e);
failureListener.onKeepaliveFailure();
}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java
index cf494efbe..a4de9febb 100644
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java
@@ -18,13 +18,13 @@ package io.fd.honeycomb.v3po.translate.util.read;
import io.fd.honeycomb.v3po.translate.read.ReadContext;
import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
-import io.fd.honeycomb.v3po.translate.spi.read.RootReaderCustomizer;
+import io.fd.honeycomb.v3po.translate.spi.read.ReaderCustomizer;
import org.opendaylight.yangtools.concepts.Builder;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
public abstract class NoopReaderCustomizer<C extends DataObject, B extends Builder<C>> implements
- RootReaderCustomizer<C, B> {
+ ReaderCustomizer<C, B> {
@Override
public void readCurrentAttributes(InstanceIdentifier<C> id, final B builder, final ReadContext context) throws
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveAugmentReaderCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveAugmentReaderCustomizer.java
deleted file mode 100644
index 38107edff..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveAugmentReaderCustomizer.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.util.read;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.Lists;
-import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer;
-import io.fd.honeycomb.v3po.translate.util.ReflectionUtils;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import org.opendaylight.yangtools.concepts.Builder;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-
-/**
- * Might be slow !
- */
-public class ReflexiveAugmentReaderCustomizer<C extends DataObject, B extends Builder<C>>
- extends ReflexiveRootReaderCustomizer<C, B>
- implements ChildReaderCustomizer<C,B> {
-
- private final Class<C> augType;
-
- public ReflexiveAugmentReaderCustomizer(final Class<B> builderClass, final Class<C> augType) {
- super(builderClass);
- this.augType = augType;
- }
-
- @Override
- public void merge(final Builder<? extends DataObject> parentBuilder, final C readValue) {
- final Optional<Method> method =
- ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "addAugmentation",
- Lists.newArrayList(Class.class, Augmentation.class), parentBuilder.getClass());
-
- checkArgument(method.isPresent(), "Not possible to add augmentations to builder: %s", parentBuilder);
- try {
- method.get().invoke(parentBuilder, augType, readValue);
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e);
- }
- }
-
-}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveChildReaderCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveChildReaderCustomizer.java
deleted file mode 100644
index 3d5f9f4e8..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveChildReaderCustomizer.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.util.read;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import io.fd.honeycomb.v3po.translate.util.ReflectionUtils;
-import io.fd.honeycomb.v3po.translate.spi.read.ChildReaderCustomizer;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Collections;
-import org.opendaylight.yangtools.concepts.Builder;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-
-/**
- * Might be slow !
- */
-public class ReflexiveChildReaderCustomizer<C extends DataObject, B extends Builder<C>>
- extends ReflexiveRootReaderCustomizer<C, B>
- implements ChildReaderCustomizer<C,B> {
-
- public ReflexiveChildReaderCustomizer(final Class<B> builderClass) {
- super(builderClass);
- }
-
- // TODO Could be just a default implementation in interface (making this a mixin)
-
- @Override
- public void merge(final Builder<? extends DataObject> parentBuilder, final C readValue) {
- final Optional<Method> method =
- ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "set",
- Collections.<Class<?>>singletonList(readValue.getClass()), parentBuilder.getClass());
-
- Preconditions.checkArgument(method.isPresent(), "Unable to set %s to %s", readValue, parentBuilder);
-
- try {
- method.get().invoke(parentBuilder, readValue);
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e);
- }
- }
-
-}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java
new file mode 100644
index 000000000..51725e728
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read;
+
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Reader that performs no read operation on its own, just fills in the hierarchy.
+ * <p/>
+ * Might be slow due to reflection !
+ */
+public final class ReflexiveReader<C extends DataObject, B extends Builder<C>> extends AbstractGenericReader<C, B> {
+
+ private final ReflexiveReaderCustomizer<C, B> customizer;
+
+ public ReflexiveReader(final InstanceIdentifier<C> identifier, final Class<B> builderClass) {
+ super(identifier);
+ this.customizer = new ReflexiveReaderCustomizer<>(identifier.getTargetType(), builderClass);
+ }
+
+ @Override
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder,
+ @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ customizer.readCurrentAttributes(id, builder, ctx);
+ }
+
+ @Nonnull
+ @Override
+ public B getBuilder(final InstanceIdentifier<C> id) {
+ return customizer.getBuilder(id);
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final C readValue) {
+ customizer.merge(parentBuilder, readValue);
+ }
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java
new file mode 100644
index 000000000..f9efca3dc
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import io.fd.honeycomb.v3po.translate.util.ReflectionUtils;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Might be slow !
+ */
+final class ReflexiveReaderCustomizer<C extends DataObject, B extends Builder<C>> extends NoopReaderCustomizer<C, B> {
+
+ private final Class<C> typeClass;
+ private final Class<B> builderClass;
+
+ public ReflexiveReaderCustomizer(final Class<C> typeClass, final Class<B> builderClass) {
+ this.typeClass = typeClass;
+ this.builderClass = builderClass;
+ }
+
+ protected Class<C> getTypeClass() {
+ return typeClass;
+ }
+
+ protected Class<B> getBuilderClass() {
+ return builderClass;
+ }
+
+ @Nonnull
+ @Override
+ public B getBuilder(@Nonnull InstanceIdentifier<C> id) {
+ try {
+ return builderClass.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new IllegalStateException("Unable to instantiate " + builderClass, e);
+ }
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final C readValue) {
+ if (Augmentation.class.isAssignableFrom(typeClass)) {
+ mergeAugmentation(parentBuilder, (Class<? extends Augmentation<?>>) typeClass, readValue);
+ } else {
+ mergeRegular(parentBuilder, readValue);
+ }
+ }
+
+ private static void mergeRegular(@Nonnull final Builder<? extends DataObject> parentBuilder,
+ @Nonnull final DataObject readValue) {
+ final Optional<Method> method =
+ ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "set",
+ Collections.singletonList(readValue.getClass()), parentBuilder.getClass());
+
+ checkArgument(method.isPresent(), "Unable to set %s to %s", readValue, parentBuilder);
+
+ try {
+ method.get().invoke(parentBuilder, readValue);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e);
+ }
+ }
+
+ private static void mergeAugmentation(@Nonnull final Builder<? extends DataObject> parentBuilder,
+ @Nonnull final Class<? extends Augmentation<?>> typeClass,
+ @Nonnull final DataObject readValue) {
+ final Optional<Method> method =
+ ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "addAugmentation",
+ Lists.newArrayList(Class.class, Augmentation.class), parentBuilder.getClass());
+
+ checkArgument(method.isPresent(), "Not possible to add augmentations to builder: %s", parentBuilder);
+ try {
+ method.get().invoke(parentBuilder, typeClass, readValue);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e);
+ }
+ }
+
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveRootReaderCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveRootReaderCustomizer.java
deleted file mode 100644
index 029f359bb..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveRootReaderCustomizer.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.util.read;
-
-import org.opendaylight.yangtools.concepts.Builder;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * Might be slow !
- */
-public class ReflexiveRootReaderCustomizer<C extends DataObject, B extends Builder<C>> extends NoopReaderCustomizer<C, B> {
-
- private final Class<B> builderClass;
-
- public ReflexiveRootReaderCustomizer(final Class<B> builderClass) {
- this.builderClass = builderClass;
- }
-
- @Override
- public B getBuilder(InstanceIdentifier<C> id) {
- try {
- return builderClass.newInstance();
- } catch (InstantiationException | IllegalAccessException e) {
- throw new IllegalStateException("Unable to instantiate " + builderClass, e);
- }
- }
-}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java
new file mode 100644
index 000000000..64ecaf095
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import io.fd.honeycomb.v3po.translate.read.ListReader;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.util.read.AbstractGenericReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class CompositeReader<D extends DataObject, B extends Builder<D>> extends AbstractGenericReader<D, B> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CompositeReader.class);
+
+ private final Reader<D, B> delegate;
+ private final ImmutableMap<Class<?>, Reader<? extends DataObject, ? extends Builder<?>>> childReaders;
+
+ private CompositeReader(final Reader<D, B> reader,
+ final ImmutableMap<Class<?>, Reader<? extends DataObject, ? extends Builder<?>>> childReaders) {
+ super(reader.getManagedDataObjectType());
+ this.delegate = reader;
+ this.childReaders = childReaders;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <D extends DataObject> InstanceIdentifier<D> appendTypeToId(
+ final InstanceIdentifier<? extends DataObject> parentId, final InstanceIdentifier<D> type) {
+ final InstanceIdentifier.PathArgument t = new InstanceIdentifier.Item<>(type.getTargetType());
+ return (InstanceIdentifier<D>) InstanceIdentifier.create(Iterables.concat(
+ parentId.getPathArguments(), Collections.singleton(t)));
+ }
+
+ @Nonnull
+ @Override
+ public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ if (shouldReadCurrent(id)) {
+ return readCurrent((InstanceIdentifier<D>) id, ctx);
+ } else if (shouldDelegateToChild(id)) {
+ return readSubtree(id, ctx);
+ } else {
+ // Fallback
+ return delegate.read(id, ctx);
+ }
+ }
+
+ private boolean shouldReadCurrent(@Nonnull final InstanceIdentifier<? extends DataObject> id) {
+ return id.getTargetType().equals(getManagedDataObjectType().getTargetType());
+ }
+
+ private boolean shouldDelegateToChild(@Nonnull final InstanceIdentifier<? extends DataObject> id) {
+ return childReaders.containsKey(RWUtils.getNextId(id, getManagedDataObjectType()).getType());
+ }
+
+ private Optional<? extends DataObject> readSubtree(final InstanceIdentifier<? extends DataObject> id,
+ final ReadContext ctx) throws ReadFailedException {
+ final InstanceIdentifier.PathArgument nextId = RWUtils.getNextId(id, getManagedDataObjectType());
+ final Reader<?, ? extends Builder<?>> nextReader = childReaders.get(nextId.getType());
+ checkArgument(nextReader != null, "Unable to read: %s. No delegate present, available readers at next level: %s",
+ id, childReaders.keySet());
+ return nextReader.read(id, ctx);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void readChildren(final InstanceIdentifier<D> id, @Nonnull final ReadContext ctx, final B builder)
+ throws ReadFailedException {
+ for (Reader child : childReaders.values()) {
+ LOG.debug("{}: Reading child node from: {}", this, child);
+ final InstanceIdentifier childId = appendTypeToId(id, child.getManagedDataObjectType());
+
+ if (child instanceof ListReader) {
+ final List<? extends DataObject> list = ((ListReader) child).readList(childId, ctx);
+ ((ListReader) child).merge(builder, list);
+ } else {
+ final Optional<? extends DataObject> read = child.read(childId, ctx);
+ if (read.isPresent()) {
+ child.merge(builder, read.get());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final B builder,
+ @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ delegate.readCurrentAttributes(id, builder, ctx);
+ readChildren(id, ctx, builder);
+ }
+
+ @Nonnull
+ @Override
+ public B getBuilder(final InstanceIdentifier<D> id) {
+ return delegate.getBuilder(id);
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final D readValue) {
+ delegate.merge(parentBuilder, readValue);
+ }
+
+ /**
+ * Wrap a Reader as a Composite Reader.
+ */
+ static <D extends DataObject, B extends Builder<D>> Reader<D, B> createForReader(
+ @Nonnull final Reader<D, B> reader,
+ @Nonnull final ImmutableMap<Class<?>, Reader<?, ? extends Builder<?>>> childReaders) {
+
+ return (reader instanceof ListReader)
+ ? new CompositeListReader<>((ListReader) reader, childReaders)
+ : new CompositeReader<>(reader, childReaders);
+ }
+
+ private static class CompositeListReader<D extends DataObject & Identifiable<K>, B extends Builder<D>, K extends Identifier<D>>
+ extends CompositeReader<D, B>
+ implements ListReader<D, K, B> {
+
+ private final ListReader<D, K, B> delegate;
+
+ private CompositeListReader(final ListReader<D, K, B> reader,
+ final ImmutableMap<Class<?>, Reader<? extends DataObject, ? extends Builder<?>>> childReaders) {
+ super(reader, childReaders);
+ this.delegate = reader;
+ }
+
+ @Nonnull
+ @Override
+ public List<D> readList(@Nonnull final InstanceIdentifier<D> id, @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ LOG.trace("{}: Reading all list entries", this);
+ final List<K> allIds = delegate.getAllIds(id, ctx);
+ LOG.debug("{}: Reading list entries for: {}", this, allIds);
+
+ // Override read list in order to perform readCurrent + readChildren here
+ final ArrayList<D> allEntries = new ArrayList<>(allIds.size());
+ for (K key : allIds) {
+ final InstanceIdentifier.IdentifiableItem<D, K> currentBdItem = RWUtils.getCurrentIdItem(id, key);
+ final InstanceIdentifier<D> keyedId = RWUtils.replaceLastInId(id, currentBdItem);
+ final Optional<D> read = readCurrent(keyedId, ctx);
+ if (read.isPresent()) {
+ final DataObject singleItem = read.get();
+ checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(singleItem.getClass()));
+ allEntries.add(getManagedDataObjectType().getTargetType().cast(singleItem));
+ }
+ }
+ return allEntries;
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<D> readData) {
+ delegate.merge(builder, readData);
+ }
+
+ @Override
+ public List<K> getAllIds(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ return delegate.getAllIds(id, ctx);
+ }
+ }
+
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/DelegatingReaderRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistry.java
index 387f3cc7c..0a948c7a8 100644
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/DelegatingReaderRegistry.java
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistry.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package io.fd.honeycomb.v3po.translate.util.read;
+package io.fd.honeycomb.v3po.translate.util.read.registry;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -25,13 +25,14 @@ import com.google.common.collect.Multimap;
import io.fd.honeycomb.v3po.translate.read.ListReader;
import io.fd.honeycomb.v3po.translate.read.ReadContext;
import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
-import io.fd.honeycomb.v3po.translate.read.ReaderRegistry;
-import io.fd.honeycomb.v3po.translate.util.RWUtils;
import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
@@ -40,21 +41,21 @@ import org.slf4j.LoggerFactory;
/**
* Simple reader registry able to perform and aggregated read (ROOT read) on top of all provided readers. Also able to
* delegate a specific read to one of the delegate readers.
- *
+ * <p/>
* This could serve as a utility to hold & hide all available readers in upper layers.
*/
-public final class DelegatingReaderRegistry implements ReaderRegistry {
+public final class CompositeReaderRegistry implements ReaderRegistry {
- private static final Logger LOG = LoggerFactory.getLogger(DelegatingReaderRegistry.class);
+ private static final Logger LOG = LoggerFactory.getLogger(CompositeReaderRegistry.class);
- private final Map<Class<? extends DataObject>, Reader<? extends DataObject>> rootReaders;
+ private final Map<Class<? extends DataObject>, Reader<? extends DataObject, ? extends Builder<?>>> rootReaders;
/**
- * Create new {@link DelegatingReaderRegistry}
+ * Create new {@link CompositeReaderRegistry}.
*
* @param rootReaders List of delegate readers
*/
- public DelegatingReaderRegistry(@Nonnull final List<Reader<? extends DataObject>> rootReaders) {
+ public CompositeReaderRegistry(@Nonnull final List<Reader<? extends DataObject, ? extends Builder<?>>> rootReaders) {
this.rootReaders = RWUtils.uniqueLinkedIndex(checkNotNull(rootReaders), RWUtils.MANAGER_CLASS_FUNCTION);
}
@@ -67,7 +68,7 @@ public final class DelegatingReaderRegistry implements ReaderRegistry {
LOG.trace("Reading from all delegates: {}", rootReaders.values());
final Multimap<InstanceIdentifier<? extends DataObject>, DataObject> objects = LinkedListMultimap.create();
- for (Reader<? extends DataObject> rootReader : rootReaders.values()) {
+ for (Reader<? extends DataObject, ? extends Builder<?>> rootReader : rootReaders.values()) {
LOG.debug("Reading from delegate: {}", rootReader);
if (rootReader instanceof ListReader) {
@@ -94,20 +95,10 @@ public final class DelegatingReaderRegistry implements ReaderRegistry {
throws ReadFailedException {
final InstanceIdentifier.PathArgument first = checkNotNull(
Iterables.getFirst(id.getPathArguments(), null), "Empty id");
- final Reader<? extends DataObject> reader = rootReaders.get(first.getType());
+ final Reader<? extends DataObject, ? extends Builder<?>> reader = rootReaders.get(first.getType());
checkNotNull(reader,
"Unable to read %s. Missing reader. Current readers for: %s", id, rootReaders.keySet());
LOG.debug("Reading from delegate: {}", reader);
return reader.read(id, ctx);
}
-
- /**
- * @throws UnsupportedOperationException This getter is not supported for reader registry since it does not manage a
- * specific node type
- */
- @Nonnull
- @Override
- public InstanceIdentifier<DataObject> getManagedDataObjectType() {
- throw new UnsupportedOperationException("Root registry has no type");
- }
}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java
new file mode 100644
index 000000000..3adda713d
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import com.google.common.collect.ImmutableMap;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.read.registry.ModifiableReaderRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry;
+import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.util.AbstractSubtreeManagerRegistryBuilderBuilder;
+import io.fd.honeycomb.v3po.translate.util.read.ReflexiveReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NotThreadSafe
+public final class CompositeReaderRegistryBuilder
+ extends AbstractSubtreeManagerRegistryBuilderBuilder<Reader<? extends DataObject, ? extends Builder<?>>, ReaderRegistry>
+ implements ModifiableReaderRegistryBuilder, ReaderRegistryBuilder {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CompositeReaderRegistryBuilder.class);
+
+ @Override
+ protected Reader<? extends DataObject, ? extends Builder<?>> getSubtreeHandler(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Reader<? extends DataObject, ? extends Builder<?>> reader) {
+ return SubtreeReader.createForReader(handledChildren, reader);
+ }
+
+ @Override
+ public <D extends DataObject> void addStructuralReader(@Nonnull InstanceIdentifier<D> id,
+ @Nonnull Class<? extends Builder<D>> builderType) {
+ add(new ReflexiveReader<>(id, builderType));
+ }
+
+ /**
+ * Create {@link CompositeReaderRegistry} with Readers ordered according to submitted relationships.
+ * <p/>
+ * Note: The ordering only applies between nodes on the same level, inter-level and inter-subtree relationships are
+ * ignored.
+ */
+ @Override
+ public ReaderRegistry build() {
+ ImmutableMap<InstanceIdentifier<?>, Reader<? extends DataObject, ? extends Builder<?>>> mappedReaders =
+ getMappedHandlers();
+ LOG.debug("Building Reader registry with Readers: {}",
+ mappedReaders.keySet().stream()
+ .map(InstanceIdentifier::getTargetType)
+ .map(Class::getSimpleName)
+ .collect(Collectors.joining(", ")));
+
+ LOG.trace("Building Reader registry with Readers: {}", mappedReaders);
+ final List<InstanceIdentifier<?>> readerOrder = new ArrayList<>(mappedReaders.keySet());
+
+ // Wrap readers into composite readers recursively, collect roots and create registry
+ final TypeHierarchy typeHierarchy = TypeHierarchy.create(mappedReaders.keySet());
+ final List<Reader<? extends DataObject, ? extends Builder<?>>> orderedRootReaders =
+ typeHierarchy.getRoots().stream()
+ .map(rootId -> toCompositeReader(rootId, mappedReaders, typeHierarchy))
+ .collect(Collectors.toList());
+
+ // We are violating the ordering from mappedReaders, since we are forming a composite structure
+ // but at least order root writers
+ orderedRootReaders.sort((reader1, reader2) -> readerOrder.indexOf(reader1.getManagedDataObjectType())
+ - readerOrder.indexOf(reader2.getManagedDataObjectType()));
+
+ return new CompositeReaderRegistry(orderedRootReaders);
+ }
+
+ private Reader<? extends DataObject, ? extends Builder<?>> toCompositeReader(
+ final InstanceIdentifier<?> instanceIdentifier,
+ final ImmutableMap<InstanceIdentifier<?>, Reader<? extends DataObject, ? extends Builder<?>>> mappedReaders,
+ final TypeHierarchy typeHierarchy) {
+
+ // Order child readers according to the mappedReadersCollection
+ final ImmutableMap.Builder<Class<?>, Reader<?, ? extends Builder<?>>> childReadersMapB = ImmutableMap.builder();
+ for (InstanceIdentifier<?> childId : mappedReaders.keySet()) {
+ if (typeHierarchy.getDirectChildren(instanceIdentifier).contains(childId)) {
+ childReadersMapB.put(childId.getTargetType(), toCompositeReader(childId, mappedReaders, typeHierarchy));
+ }
+ }
+
+ final ImmutableMap<Class<?>, Reader<?, ? extends Builder<?>>> childReadersMap = childReadersMapB.build();
+ return childReadersMap.isEmpty()
+ ? mappedReaders.get(instanceIdentifier)
+ : CompositeReader.createForReader(mappedReaders.get(instanceIdentifier), childReadersMap);
+ }
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java
new file mode 100644
index 000000000..98fcac673
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import io.fd.honeycomb.v3po.translate.read.ListReader;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.util.ReflectionUtils;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple Reader delegate for subtree Readers (Readers handling also children nodes) providing a list of all the
+ * children nodes being handled.
+ */
+class SubtreeReader<D extends DataObject, B extends Builder<D>> implements Reader<D, B> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SubtreeReader.class);
+
+ private final Reader<D, B> delegate;
+ private final Set<InstanceIdentifier<?>> handledChildTypes = new HashSet<>();
+
+ private SubtreeReader(final Reader<D, B> delegate, Set<InstanceIdentifier<?>> handledTypes) {
+ this.delegate = delegate;
+ for (InstanceIdentifier<?> handledType : handledTypes) {
+ // Iid has to start with Reader's handled root type
+ checkArgument(delegate.getManagedDataObjectType().getTargetType().equals(
+ handledType.getPathArguments().iterator().next().getType()),
+ "Handled node from subtree has to be identified by an instance identifier starting from: %s."
+ + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType);
+ checkArgument(Iterables.size(handledType.getPathArguments()) > 1,
+ "Handled node from subtree identifier too short: %s", handledType);
+ handledChildTypes.add(InstanceIdentifier.create(Iterables.concat(
+ getManagedDataObjectType().getPathArguments(), Iterables.skip(handledType.getPathArguments(), 1))));
+ }
+ }
+
+ /**
+ * Return set of types also handled by this Reader. All of the types are children of the type managed by this Reader
+ * excluding the type of this Reader.
+ */
+ Set<InstanceIdentifier<?>> getHandledChildTypes() {
+ return handledChildTypes;
+ }
+
+ @Override
+ @Nonnull
+ public Optional<? extends DataObject> read(
+ @Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ final InstanceIdentifier<?> wildcarded = RWUtils.makeIidWildcarded(id);
+
+ // Reading entire subtree and filtering if is current reader responsible
+ if (getHandledChildTypes().contains(wildcarded)) {
+ LOG.debug("{}: Subtree node managed by this writer requested: {}. Reading current and filtering", this, id);
+ // If there's no dedicated reader, use read current
+ final InstanceIdentifier<D> currentId = RWUtils.cutId(id, getManagedDataObjectType());
+ final Optional<? extends DataObject> current = read(currentId, ctx);
+ // then perform post-reading filtering (return only requested sub-node)
+ final Optional<? extends DataObject> readSubtree = current.isPresent()
+ ? filterSubtree(current.get(), id, getManagedDataObjectType().getTargetType())
+ : current;
+
+ LOG.debug("{}: Subtree: {} read successfully. Result: {}", this, id, readSubtree);
+ return readSubtree;
+
+ // Fallback solution, try delegate, maybe it can read the ID
+ } else {
+ return delegate.read(id, ctx);
+ }
+ }
+
+ @Override
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final B builder,
+ @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ delegate.readCurrentAttributes(id, builder, ctx);
+ }
+
+ @Nonnull
+ @Override
+ public B getBuilder(final InstanceIdentifier<D> id) {
+ return delegate.getBuilder(id);
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final D readValue) {
+ delegate.merge(parentBuilder, readValue);
+ }
+
+ @Nonnull
+ private static Optional<? extends DataObject> filterSubtree(@Nonnull final DataObject parent,
+ @Nonnull final InstanceIdentifier<? extends DataObject> absolutPath,
+ @Nonnull final Class<?> managedType) {
+ final InstanceIdentifier.PathArgument nextId =
+ RWUtils.getNextId(absolutPath, InstanceIdentifier.create(parent.getClass()));
+
+ final Optional<? extends DataObject> nextParent = findNextParent(parent, nextId, managedType);
+
+ if (Iterables.getLast(absolutPath.getPathArguments()).equals(nextId)) {
+ return nextParent; // we found the dataObject identified by absolutePath
+ } else if (nextParent.isPresent()) {
+ return filterSubtree(nextParent.get(), absolutPath, nextId.getType());
+ } else {
+ return nextParent; // we can't go further, return Optional.absent()
+ }
+ }
+
+ private static Optional<? extends DataObject> findNextParent(@Nonnull final DataObject parent,
+ @Nonnull final InstanceIdentifier.PathArgument nextId,
+ @Nonnull final Class<?> managedType) {
+ // TODO is there a better way than reflection ? e.g. convert into NN and filter out with a utility
+ Optional<Method> method = ReflectionUtils.findMethodReflex(managedType, "get",
+ Collections.emptyList(), nextId.getType());
+
+ if (method.isPresent()) {
+ return Optional.fromNullable(filterSingle(parent, nextId, method.get()));
+ } else {
+ // List child nodes
+ method = ReflectionUtils.findMethodReflex(managedType,
+ "get" + nextId.getType().getSimpleName(), Collections.emptyList(), List.class);
+
+ if (method.isPresent()) {
+ return filterList(parent, nextId, method.get());
+ } else {
+ throw new IllegalStateException(
+ "Unable to filter " + nextId + " from " + parent + " getters not found using reflexion");
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Optional<? extends DataObject> filterList(final DataObject parent,
+ final InstanceIdentifier.PathArgument nextId,
+ final Method method) {
+ final List<? extends DataObject> invoke = (List<? extends DataObject>) invoke(method, nextId, parent);
+
+ checkArgument(nextId instanceof InstanceIdentifier.IdentifiableItem<?, ?>,
+ "Unable to perform wildcarded read for %s", nextId);
+ final Identifier key = ((InstanceIdentifier.IdentifiableItem) nextId).getKey();
+ // TODO replace with stream().filter().findFirst() when we switch to using java's Optional instead of Guava's
+ // because now we would have to do awkward Optional transformation since findFirstReturns guava's optional
+ return Iterables.tryFind(invoke, new Predicate<DataObject>() {
+
+ @Override
+ public boolean apply(@Nullable final DataObject input) {
+ final Optional<Method> keyGetter = ReflectionUtils.findMethodReflex(nextId.getType(), "get",
+ Collections.emptyList(), key.getClass());
+ final Object actualKey;
+ actualKey = invoke(keyGetter.get(), nextId, input);
+ return key.equals(actualKey);
+ }
+ });
+ }
+
+ private static DataObject filterSingle(final DataObject parent,
+ final InstanceIdentifier.PathArgument nextId, final Method method) {
+ return nextId.getType().cast(invoke(method, nextId, parent));
+ }
+
+ private static Object invoke(final Method method,
+ final InstanceIdentifier.PathArgument nextId, final DataObject parent) {
+ try {
+ return method.invoke(parent);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalArgumentException("Unable to get " + nextId + " from " + parent, e);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public InstanceIdentifier<D> getManagedDataObjectType() {
+ return delegate.getManagedDataObjectType();
+ }
+
+ /**
+ * Wrap a Reader as a subtree Reader.
+ */
+ static <D extends DataObject, B extends Builder<D>> Reader<D, B> createForReader(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Reader<D, B> reader) {
+ return (reader instanceof ListReader)
+ ? new SubtreeListReader<>((ListReader) reader, handledChildren)
+ : new SubtreeReader<>(reader, handledChildren);
+ }
+
+ private static final class SubtreeListReader<D extends DataObject & Identifiable<K>, B extends Builder<D>, K extends Identifier<D>>
+ extends SubtreeReader<D, B> implements ListReader<D, K, B> {
+
+ private final ListReader<D, K, B> delegate;
+
+ private SubtreeListReader(final ListReader<D, K, B> delegate,
+ final Set<InstanceIdentifier<?>> handledTypes) {
+ super(delegate, handledTypes);
+ this.delegate = delegate;
+ }
+
+ @Nonnull
+ @Override
+ public List<D> readList(@Nonnull final InstanceIdentifier<D> id, @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ return delegate.readList(id, ctx);
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<D> readData) {
+ delegate.merge(builder, readData);
+ }
+
+ @Override
+ public List<K> getAllIds(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ return delegate.getAllIds(id, ctx);
+ }
+ }
+
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java
new file mode 100644
index 000000000..005e3bc8d
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.Iterables;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class TypeHierarchy {
+ private final DirectedAcyclicGraph<InstanceIdentifier<?>, Parent> hierarchy;
+
+ private TypeHierarchy(@Nonnull final DirectedAcyclicGraph<InstanceIdentifier<?>, Parent> hierarchy) {
+ this.hierarchy = hierarchy;
+ }
+
+ Set<InstanceIdentifier<?>> getAllChildren(InstanceIdentifier<?> id) {
+ final HashSet<InstanceIdentifier<?>> instanceIdentifiers = new HashSet<>();
+ for (InstanceIdentifier<?> childId : getDirectChildren(id)) {
+ instanceIdentifiers.add(childId);
+ instanceIdentifiers.addAll(getAllChildren(childId));
+ }
+ return instanceIdentifiers;
+ }
+
+ Set<InstanceIdentifier<?>> getDirectChildren(InstanceIdentifier<?> id) {
+ checkArgument(hierarchy.vertexSet().contains(id),
+ "Unknown reader: %s. Known readers: %s", id, hierarchy.vertexSet());
+
+ return hierarchy.outgoingEdgesOf(id).stream()
+ .map(hierarchy::getEdgeTarget)
+ .collect(Collectors.toSet());
+ }
+
+ Set<InstanceIdentifier<?>> getRoots() {
+ return hierarchy.vertexSet().stream()
+ .filter(vertex -> hierarchy.incomingEdgesOf(vertex).size() == 0)
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Create reader hierarchy from a flat set of instance identifiers.
+ *
+ * @param allIds Set of unkeyed instance identifiers
+ */
+ static TypeHierarchy create(@Nonnull Set<InstanceIdentifier<?>> allIds) {
+ final DirectedAcyclicGraph<InstanceIdentifier<?>, Parent>
+ readersHierarchy = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new Parent());
+
+ for (InstanceIdentifier<?> allId : allIds) {
+ checkArgument(!Iterables.isEmpty(allId.getPathArguments()), "Empty ID detected");
+
+ if (Iterables.size(allId.getPathArguments()) == 1) {
+ readersHierarchy.addVertex(allId);
+ }
+
+ List<InstanceIdentifier.PathArgument> pathArgs = new LinkedList<>();
+ pathArgs.add(allId.getPathArguments().iterator().next());
+
+ for (InstanceIdentifier.PathArgument pathArgument : Iterables.skip(allId.getPathArguments(), 1)) {
+ final InstanceIdentifier<?> previous = InstanceIdentifier.create(pathArgs);
+ pathArgs.add(pathArgument);
+ final InstanceIdentifier<?> current = InstanceIdentifier.create(pathArgs);
+
+ readersHierarchy.addVertex(previous);
+ readersHierarchy.addVertex(current);
+
+ try {
+ readersHierarchy.addDagEdge(previous, current);
+ } catch (DirectedAcyclicGraph.CycleFoundException e) {
+ throw new IllegalArgumentException("Loop in hierarchy detected", e);
+ }
+ }
+ }
+
+ return new TypeHierarchy(readersHierarchy);
+ }
+
+ private static final class Parent{}
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java
new file mode 100644
index 000000000..44b36edae
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractGenericWriter<D extends DataObject> implements Writer<D> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractGenericWriter.class);
+
+ private final InstanceIdentifier<D> instanceIdentifier;
+
+ protected AbstractGenericWriter(final InstanceIdentifier<D> type) {
+ this.instanceIdentifier = RWUtils.makeIidWildcarded(type);
+ }
+
+ protected void writeCurrent(final InstanceIdentifier<D> id, final D data, final WriteContext ctx)
+ throws WriteFailedException {
+ LOG.debug("{}: Writing current: {} data: {}", this, id, data);
+ writeCurrentAttributes(id, data, ctx);
+ LOG.debug("{}: Current node written successfully", this);
+ }
+
+ protected void updateCurrent(final InstanceIdentifier<D> id, final D dataBefore, final D dataAfter,
+ final WriteContext ctx) throws WriteFailedException {
+ LOG.debug("{}: Updating current: {} dataBefore: {}, datAfter: {}", this, id, dataBefore, dataAfter);
+
+ if (dataBefore.equals(dataAfter)) {
+ LOG.debug("{}: Skipping current(no update): {}", this, id);
+ // No change, ignore
+ return;
+ }
+ updateCurrentAttributes(id, dataBefore, dataAfter, ctx);
+ LOG.debug("{}: Current node updated successfully", this);
+ }
+
+ protected void deleteCurrent(final InstanceIdentifier<D> id, final D dataBefore, final WriteContext ctx)
+ throws WriteFailedException {
+ LOG.debug("{}: Deleting current: {} dataBefore: {}", this, id, dataBefore);
+ deleteCurrentAttributes(id, dataBefore, ctx);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ LOG.debug("{}: Updating : {}", this, id);
+ LOG.trace("{}: Updating : {}, from: {} to: {}", this, id, dataBefore, dataAfter);
+
+ checkArgument(idPointsToCurrent(id), "Cannot handle data: %s. Only: %s can be handled by writer: %s",
+ id, getManagedDataObjectType(), this);
+
+ if (isWrite(dataBefore, dataAfter)) {
+ writeCurrent((InstanceIdentifier<D>) id, castToManaged(dataAfter), ctx);
+ } else if (isDelete(dataBefore, dataAfter)) {
+ deleteCurrent((InstanceIdentifier<D>) id, castToManaged(dataBefore), ctx);
+ } else {
+ checkArgument(dataBefore != null && dataAfter != null, "No data to process");
+ updateCurrent((InstanceIdentifier<D>) id, castToManaged(dataBefore), castToManaged(dataAfter), ctx);
+ }
+ }
+
+ private void checkDataType(@Nonnull final DataObject dataAfter) {
+ checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(dataAfter.getClass()));
+ }
+
+ private D castToManaged(final DataObject data) {
+ checkDataType(data);
+ return getManagedDataObjectType().getTargetType().cast(data);
+ }
+
+ private static boolean isWrite(final DataObject dataBefore,
+ final DataObject dataAfter) {
+ return dataBefore == null && dataAfter != null;
+ }
+
+ private static boolean isDelete(final DataObject dataBefore,
+ final DataObject dataAfter) {
+ return dataAfter == null && dataBefore != null;
+ }
+
+ private boolean idPointsToCurrent(final @Nonnull InstanceIdentifier<? extends DataObject> id) {
+ return id.getTargetType().equals(getManagedDataObjectType().getTargetType());
+ }
+
+ protected abstract void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final D data,
+ @Nonnull final WriteContext ctx) throws WriteFailedException;
+
+ protected abstract void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final D dataBefore,
+ @Nonnull final WriteContext ctx) throws WriteFailedException;
+
+ protected abstract void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final D dataBefore,
+ @Nonnull final D dataAfter,
+ @Nonnull final WriteContext ctx) throws WriteFailedException;
+
+ @Nonnull
+ @Override
+ public InstanceIdentifier<D> getManagedDataObjectType() {
+ return instanceIdentifier;
+ }
+
+
+ @Override
+ public String toString() {
+ return String.format("Writer[%s]", getManagedDataObjectType().getTargetType().getSimpleName());
+ }
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriter.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriter.java
deleted file mode 100644
index 3442f8370..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriter.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.fd.honeycomb.v3po.translate.util.write;
-
-import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
-import io.fd.honeycomb.v3po.translate.write.Writer;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * Closeable wrapper for a writer
- */
-public final class CloseableWriter<D extends DataObject> implements Writer<D>, AutoCloseable {
-
- private Writer<D> vppCompositeRootWriter;
-
- public CloseableWriter(
- final Writer<D> vppCompositeRootWriter) {
- this.vppCompositeRootWriter = vppCompositeRootWriter;
- }
-
- @Override
- public void update(
- @Nonnull final InstanceIdentifier<? extends DataObject> id,
- @Nullable final DataObject dataBefore,
- @Nullable final DataObject dataAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- vppCompositeRootWriter.update(id, dataBefore, dataAfter, ctx);
- }
-
- @Nonnull
- @Override
- public InstanceIdentifier<D> getManagedDataObjectType() {
- return vppCompositeRootWriter.getManagedDataObjectType();
- }
-
- @Override
- public String toString() {
- return vppCompositeRootWriter.toString();
- }
-
- @Override
- public void close() throws Exception {
-
- }
-}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java
index 236ad8917..7c45fcd82 100644
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java
@@ -18,14 +18,8 @@ package io.fd.honeycomb.v3po.translate.util.write;
import io.fd.honeycomb.v3po.translate.TranslationException;
import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
-import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry;
import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Empty registry that does not perform any changes. Can be used in data layer, if we want to disable passing data to
@@ -33,29 +27,12 @@ import org.slf4j.LoggerFactory;
*/
public class NoopWriterRegistry implements WriterRegistry, AutoCloseable {
- private static final Logger LOG = LoggerFactory.getLogger(NoopWriterRegistry.class);
-
- @Override
- public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
- @Nullable final DataObject dataBefore, @Nullable final DataObject dataAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- LOG.trace("NoopWriterRegistry.update id={}, dataBefore{}, dataAfter={], ctx={}", id, dataBefore, dataAfter,
- ctx);
- // NOOP
- }
-
@Override
public void update(@Nonnull final DataObjectUpdates updates,
@Nonnull final WriteContext ctx) throws TranslationException {
// NOOP
}
- @Nonnull
- @Override
- public InstanceIdentifier<DataObject> getManagedDataObjectType() {
- throw new UnsupportedOperationException("Noop registry has no type");
- }
-
@Override
public void close() throws Exception {
// NOOP
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java
index 4abad23a6..7433de813 100644
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java
@@ -21,7 +21,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.base.Optional;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
@@ -32,7 +31,7 @@ import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
import io.fd.honeycomb.v3po.translate.write.WriteContext;
import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
import io.fd.honeycomb.v3po.translate.write.Writer;
-import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -91,15 +90,6 @@ final class FlatWriterRegistry implements WriterRegistry {
}
@Override
- public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
- @Nullable final DataObject dataBefore,
- @Nullable final DataObject dataAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- singleUpdate(ImmutableMultimap.of(
- RWUtils.makeIidWildcarded(id), DataObjectUpdate.create(id, dataBefore, dataAfter)), ctx);
- }
-
- @Override
public void update(@Nonnull final DataObjectUpdates updates,
@Nonnull final WriteContext ctx) throws TranslationException {
if (updates.isEmpty()) {
@@ -265,12 +255,6 @@ final class FlatWriterRegistry implements WriterRegistry {
return writers.get(singleType);
}
- @Nonnull
- @Override
- public InstanceIdentifier<DataObject> getManagedDataObjectType() {
- throw new UnsupportedOperationException("Registry has no managed type");
- }
-
// FIXME unit test
private final class ReverterImpl implements Reverter {
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java
index f5d218f55..bfac2eedd 100644
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java
@@ -17,20 +17,16 @@
package io.fd.honeycomb.v3po.translate.util.write.registry;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
-import io.fd.honeycomb.v3po.translate.util.RWUtils;
-import io.fd.honeycomb.v3po.translate.write.ModifiableWriterRegistry;
+import io.fd.honeycomb.v3po.translate.util.AbstractSubtreeManagerRegistryBuilderBuilder;
import io.fd.honeycomb.v3po.translate.write.Writer;
-import io.fd.honeycomb.v3po.translate.write.WriterRegistryBuilder;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
+import io.fd.honeycomb.v3po.translate.write.registry.ModifiableWriterRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistryBuilder;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;
-import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
@@ -40,156 +36,24 @@ import org.slf4j.LoggerFactory;
* Builder for {@link FlatWriterRegistry} allowing users to specify inter-writer relationships.
*/
@NotThreadSafe
-public final class FlatWriterRegistryBuilder implements ModifiableWriterRegistry, WriterRegistryBuilder, AutoCloseable {
+public final class FlatWriterRegistryBuilder
+ extends AbstractSubtreeManagerRegistryBuilderBuilder<Writer<? extends DataObject>, WriterRegistry>
+ implements ModifiableWriterRegistryBuilder, WriterRegistryBuilder {
private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistryBuilder.class);
- // Using directed acyclic graph to represent the ordering relationships between writers
- private final DirectedAcyclicGraph<InstanceIdentifier<?>, WriterRelation>
- writersRelations = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new WriterRelation());
- private final Map<InstanceIdentifier<?>, Writer<?>> writersMap = new HashMap<>();
-
- /**
- * AddWriter without any special relationship to any other type.
- */
- @Override
- public FlatWriterRegistryBuilder addWriter(@Nonnull final Writer<? extends DataObject> writer) {
- // Make IID wildcarded just in case
- // + the way InstanceIdentifier.create + equals work for Identifiable items is unexpected, meaning updates would
- // not be matched to writers in registry
- final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
- checkWriterNotPresentYet(targetType);
- writersRelations.addVertex(targetType);
- writersMap.put(targetType, writer);
- return this;
- }
-
- /**
- * AddWriter without any special relationship to any other type.
- */
- @Override
- public FlatWriterRegistryBuilder addSubtreeWriter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
- @Nonnull final Writer<? extends DataObject> writer) {
- addWriter(SubtreeWriter.createForWriter(handledChildren, writer));
- return this;
- }
-
- private void checkWriterNotPresentYet(final InstanceIdentifier<?> targetType) {
- Preconditions.checkArgument(!writersMap.containsKey(targetType),
- "Writer for type: %s already present: %s", targetType, writersMap.get(targetType));
- }
-
- /**
- * Add writer with relationship: to be executed before writer handling relatedType.
- */
- @Override
- public FlatWriterRegistryBuilder addWriterBefore(@Nonnull final Writer<? extends DataObject> writer,
- @Nonnull final InstanceIdentifier<?> relatedType) {
- final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
- final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
- checkWriterNotPresentYet(targetType);
- writersRelations.addVertex(targetType);
- writersRelations.addVertex(wildcardedRelatedType);
- addEdge(targetType, wildcardedRelatedType);
- writersMap.put(targetType, writer);
- return this;
- }
-
- @Override
- public FlatWriterRegistryBuilder addSubtreeWriterBefore(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
- @Nonnull final Writer<? extends DataObject> writer,
- @Nonnull final InstanceIdentifier<?> relatedType) {
- return addWriterBefore(SubtreeWriter.createForWriter(handledChildren, writer), relatedType);
- }
-
- @Override
- public FlatWriterRegistryBuilder addWriterBefore(@Nonnull final Writer<? extends DataObject> writer,
- @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
- final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
- checkWriterNotPresentYet(targetType);
- writersRelations.addVertex(targetType);
- relatedTypes.stream()
- .map(RWUtils::makeIidWildcarded)
- .forEach(writersRelations::addVertex);
- relatedTypes.stream()
- .map(RWUtils::makeIidWildcarded)
- .forEach(type -> addEdge(targetType, type));
- writersMap.put(targetType, writer);
- return this;
- }
-
- @Override
- public FlatWriterRegistryBuilder addSubtreeWriterBefore(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
- @Nonnull final Writer<? extends DataObject> writer,
- @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
- return addWriterBefore(SubtreeWriter.createForWriter(handledChildren, writer), relatedTypes);
- }
-
- /**
- * Add writer with relationship: to be executed after writer handling relatedType.
- */
- @Override
- public FlatWriterRegistryBuilder addWriterAfter(@Nonnull final Writer<? extends DataObject> writer,
- @Nonnull final InstanceIdentifier<?> relatedType) {
- final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
- final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
- checkWriterNotPresentYet(targetType);
- writersRelations.addVertex(targetType);
- writersRelations.addVertex(wildcardedRelatedType);
- // set edge to indicate before relationship, just reversed
- addEdge(wildcardedRelatedType, targetType);
- writersMap.put(targetType, writer);
- return this;
- }
-
- @Override
- public FlatWriterRegistryBuilder addSubtreeWriterAfter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
- @Nonnull final Writer<? extends DataObject> writer,
- @Nonnull final InstanceIdentifier<?> relatedType) {
- return addWriterAfter(SubtreeWriter.createForWriter(handledChildren, writer), relatedType);
- }
-
- @Override
- public FlatWriterRegistryBuilder addWriterAfter(@Nonnull final Writer<? extends DataObject> writer,
- @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
- final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
- checkWriterNotPresentYet(targetType);
- writersRelations.addVertex(targetType);
- relatedTypes.stream()
- .map(RWUtils::makeIidWildcarded)
- .forEach(writersRelations::addVertex);
- // set edge to indicate before relationship, just reversed
- relatedTypes.stream()
- .map(RWUtils::makeIidWildcarded)
- .forEach(type -> addEdge(type, targetType));
- writersMap.put(targetType, writer);
- return this;
- }
-
@Override
- public FlatWriterRegistryBuilder addSubtreeWriterAfter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
- @Nonnull final Writer<? extends DataObject> writer,
- @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
- return addWriterAfter(SubtreeWriter.createForWriter(handledChildren, writer), relatedTypes);
- }
-
-
- private void addEdge(final InstanceIdentifier<?> targetType,
- final InstanceIdentifier<?> relatedType) {
- try {
- writersRelations.addDagEdge(targetType, relatedType);
- } catch (DirectedAcyclicGraph.CycleFoundException e) {
- throw new IllegalArgumentException(String.format(
- "Unable to add writer with relation: %s -> %s. Loop detected", targetType, relatedType), e);
- }
+ protected Writer<? extends DataObject> getSubtreeHandler(final @Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ final @Nonnull Writer<? extends DataObject> writer) {
+ return SubtreeWriter.createForWriter(handledChildren, writer);
}
/**
* Create FlatWriterRegistry with writers ordered according to submitted relationships.
*/
@Override
- public FlatWriterRegistry build() {
- final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters = getMappedWriters();
+ public WriterRegistry build() {
+ final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters = getMappedHandlers();
LOG.debug("Building writer registry with writers: {}",
mappedWriters.keySet().stream()
.map(InstanceIdentifier::getTargetType)
@@ -200,26 +64,8 @@ public final class FlatWriterRegistryBuilder implements ModifiableWriterRegistry
}
@VisibleForTesting
- ImmutableMap<InstanceIdentifier<?>, Writer<?>> getMappedWriters() {
- final ImmutableMap.Builder<InstanceIdentifier<?>, Writer<?>> builder = ImmutableMap.builder();
- // Iterate writer types according to their relationships from graph
- writersRelations.iterator()
- .forEachRemaining(writerType -> {
- // There might be types stored just for relationship sake, no real writer, ignoring those
- if (writersMap.containsKey(writerType)) {
- builder.put(writerType, writersMap.get(writerType));
- }
- });
- return builder.build();
- }
-
@Override
- public void close() throws Exception {
- writersMap.clear();
- writersRelations.removeAllEdges(writersRelations.edgeSet());
- writersRelations.removeAllVertices(writersRelations.vertexSet());
+ protected ImmutableMap<InstanceIdentifier<?>, Writer<? extends DataObject>> getMappedHandlers() {
+ return super.getMappedHandlers();
}
-
- // Represents edges in graph
- private static final class WriterRelation {}
}