summaryrefslogtreecommitdiffstats
path: root/v3po/translate-utils/src/main/java/io/fd/honeycomb
diff options
context:
space:
mode:
Diffstat (limited to 'v3po/translate-utils/src/main/java/io/fd/honeycomb')
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java37
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriterRegistry.java65
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java182
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java49
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java24
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveAugmentWriterCustomizer.java50
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java59
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java334
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java225
-rw-r--r--v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java85
10 files changed, 675 insertions, 435 deletions
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 b712e159c..55ae9ecf0 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
@@ -23,13 +23,13 @@ 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 io.fd.honeycomb.v3po.translate.write.ChildWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collector;
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;
@@ -79,18 +79,10 @@ public final class RWUtils {
return Collections.emptyList();
}
- public static <T> List<ChildWriter<? extends ChildOf<T>>> emptyChildWriterList() {
- return Collections.emptyList();
- }
-
public static <T> List<ChildReader<? extends Augmentation<T>>> emptyAugReaderList() {
return Collections.emptyList();
}
- public static <T> List<ChildWriter<? extends Augmentation<T>>> emptyAugWriterList() {
- 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);
@@ -101,16 +93,6 @@ public final class RWUtils {
return Collections.<ChildReader<? extends ChildOf<T>>>singletonList(item);
}
- public static <T> List<ChildWriter<? extends ChildOf<T>>> singletonChildWriterList(
- ChildWriter<? extends ChildOf<T>> item) {
- return Collections.<ChildWriter<? extends ChildOf<T>>>singletonList(item);
- }
-
- public static <T> List<ChildWriter<? extends Augmentation<T>>> singletonAugWriterList(
- ChildWriter<? extends Augmentation<T>> item) {
- return Collections.<ChildWriter<? extends Augmentation<T>>>singletonList(item);
- }
-
/**
* Replace last item in ID with a provided IdentifiableItem of the same type
*/
@@ -197,4 +179,21 @@ public final class RWUtils {
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) {
+ final List<InstanceIdentifier.PathArgument> transformedPathArguments =
+ StreamSupport.stream(id.getPathArguments().spliterator(), false)
+ .map(RWUtils::cleanPathArgumentFromKeys)
+ .collect(Collectors.toList());
+ return InstanceIdentifier.create(transformedPathArguments);
+ }
+
+ private static InstanceIdentifier.PathArgument cleanPathArgumentFromKeys(final InstanceIdentifier.PathArgument pathArgument) {
+ return pathArgument instanceof InstanceIdentifier.IdentifiableItem<?, ?>
+ ? new InstanceIdentifier.Item<>(pathArgument.getType())
+ : pathArgument;
+ }
}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriterRegistry.java
deleted file mode 100644
index cd53a4f8b..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriterRegistry.java
+++ /dev/null
@@ -1,65 +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.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 java.util.Map;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * WriterRegistry wrapper providing AutoCloseable interface.
- */
-public final class CloseableWriterRegistry implements WriterRegistry, AutoCloseable {
- private final WriterRegistry writerRegistry;
-
- public CloseableWriterRegistry( final WriterRegistry writerRegistry) {
- this.writerRegistry = writerRegistry;
- }
-
- @Override
- public void update(
- @Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesBefore,
- @Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesAfter,
- @Nonnull final WriteContext ctx) throws TranslationException {
- writerRegistry.update(nodesBefore, nodesAfter, ctx);
- }
-
- @Override
- public void update(
- @Nonnull final InstanceIdentifier<? extends DataObject> id,
- @Nullable final DataObject dataBefore, @Nullable final DataObject dataAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- writerRegistry.update(id, dataBefore, dataAfter, ctx);
- }
-
- @Nonnull
- @Override
- public InstanceIdentifier<DataObject> getManagedDataObjectType() {
- return writerRegistry.getManagedDataObjectType();
- }
-
- @Override
- public void close() throws Exception {
- // NOOP
- }
-} \ No newline at end of file
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java
deleted file mode 100644
index 061d3fa4a..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java
+++ /dev/null
@@ -1,182 +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 static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-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 io.fd.honeycomb.v3po.translate.write.WriterRegistry;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-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;
-
-/**
- * Simple writer registry able to perform and aggregated write (ROOT write) on top of all provided writers. Also able to
- * delegate a specific write to one of the delegate writers.
- *
- * This could serve as a utility to hold & hide all available writers in upper layers.
- */
-public final class DelegatingWriterRegistry implements WriterRegistry {
-
- private static final Logger LOG = LoggerFactory.getLogger(DelegatingWriterRegistry.class);
-
- private final Map<Class<? extends DataObject>, Writer<? extends DataObject>> rootWriters;
-
- /**
- * Create new {@link DelegatingWriterRegistry}
- *
- * @param rootWriters List of delegate writers
- */
- public DelegatingWriterRegistry(@Nonnull final List<Writer<? extends DataObject>> rootWriters) {
- this.rootWriters = RWUtils.uniqueLinkedIndex(checkNotNull(rootWriters), RWUtils.MANAGER_CLASS_FUNCTION);
- }
-
- /**
- * @throws UnsupportedOperationException This getter is not supported for writer registry since it does not manage a
- * specific node type
- */
- @Nonnull
- @Override
- public InstanceIdentifier<DataObject> getManagedDataObjectType() {
- throw new UnsupportedOperationException("Root registry has no type");
- }
-
- @Override
- public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
- @Nullable final DataObject dataBefore,
- @Nullable final DataObject dataAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
- final InstanceIdentifier.PathArgument first = checkNotNull(
- Iterables.getFirst(id.getPathArguments(), null), "Empty id");
- final Writer<? extends DataObject> writer = rootWriters.get(first.getType());
- checkNotNull(writer,
- "Unable to write %s. Missing writer. Current writers for: %s", id, rootWriters.keySet());
- writer.update(id, dataBefore, dataAfter, ctx);
- }
-
- @Override
- public void update(@Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesBefore,
- @Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesAfter,
- @Nonnull final WriteContext ctx) throws WriteFailedException {
-
- Multimap<InstanceIdentifier<?>, InstanceIdentifier<?>> rootIdToNestedIds = HashMultimap.create();
- try {
- checkAllWritersPresent(nodesBefore, rootIdToNestedIds);
- checkAllWritersPresent(nodesAfter, rootIdToNestedIds);
- } catch (IllegalArgumentException e) {
- LOG.warn("Unable to process update", e);
- throw e;
- }
-
- final List<InstanceIdentifier<?>> processedNodes = Lists.newArrayList();
-
- for (Map.Entry<Class<? extends DataObject>, Writer<? extends DataObject>> rootWriterEntry : rootWriters
- .entrySet()) {
-
- final InstanceIdentifier<? extends DataObject> id = rootWriterEntry.getValue().getManagedDataObjectType();
- // FIXME !! this is not ideal, we are not handling nested updates in expected order
- // Root writers are invoked in order they were registered, but nested updates are not, since they are
- // iterated here.
- //
- for (InstanceIdentifier<?> specificInstanceIdentifier : rootIdToNestedIds.get(id)) {
- final DataObject dataBefore = nodesBefore.get(specificInstanceIdentifier);
- final DataObject dataAfter = nodesAfter.get(specificInstanceIdentifier);
-
- // No change to current writer
- if (dataBefore == null && dataAfter == null) {
- continue;
- }
-
- try {
- LOG.debug("ChangesProcessor.applyChanges() processing dataBefore={}, dataAfter={}", dataBefore, dataAfter);
- update(specificInstanceIdentifier, dataBefore, dataAfter, ctx);
- processedNodes.add(id);
- } catch (Exception e) {
- LOG.error("Error while processing data change of: {} (before={}, after={})", id, dataBefore, dataAfter, e);
- throw new BulkUpdateException(id, new ReverterImpl(this, processedNodes, nodesBefore, nodesAfter, ctx), e);
- }
- }
- }
- }
-
- private void checkAllWritersPresent(final @Nonnull Map<InstanceIdentifier<?>, DataObject> nodesBefore,
- final @Nonnull Multimap<InstanceIdentifier<?>, InstanceIdentifier<?>> rootIdToNestedIds) {
- for (final InstanceIdentifier<?> changeId : nodesBefore.keySet()) {
- final InstanceIdentifier.PathArgument first = Iterables.getFirst(changeId.getPathArguments(), null);
- checkNotNull(first, "Empty identifier detected");
- final InstanceIdentifier<? extends DataObject> rootId = InstanceIdentifier.create(first.getType());
- checkArgument(rootWriters.keySet().contains(first.getType()),
- "Unable to handle change. Missing dedicated writer for: %s", first.getType());
- rootIdToNestedIds.put(rootId, changeId);
- }
- }
-
- private static final class ReverterImpl implements Reverter {
- private final WriterRegistry delegatingWriterRegistry;
- private final List<InstanceIdentifier<?>> processedNodes;
- private final Map<InstanceIdentifier<?>, DataObject> nodesBefore;
- private final Map<InstanceIdentifier<?>, DataObject> nodesAfter;
- private final WriteContext ctx;
-
- ReverterImpl(final WriterRegistry delegatingWriterRegistry,
- final List<InstanceIdentifier<?>> processedNodes,
- final Map<InstanceIdentifier<?>, DataObject> nodesBefore,
- final Map<InstanceIdentifier<?>, DataObject> nodesAfter, final WriteContext ctx) {
- this.delegatingWriterRegistry = delegatingWriterRegistry;
- this.processedNodes = processedNodes;
- this.nodesBefore = nodesBefore;
- this.nodesAfter = nodesAfter;
- this.ctx = ctx;
- }
-
- @Override
- public void revert() throws RevertFailedException {
- final LinkedList<InstanceIdentifier<?>> notReverted = new LinkedList<>(processedNodes);
-
- while (notReverted.size() > 0) {
- final InstanceIdentifier<?> node = notReverted.peekLast();
- LOG.debug("ChangesProcessor.revertChanges() processing node={}", node);
-
- final DataObject dataBefore = nodesBefore.get(node);
- final DataObject dataAfter = nodesAfter.get(node);
-
- // revert a change by invoking writer with reordered arguments
- try {
- delegatingWriterRegistry.update(node, dataAfter, dataBefore, ctx);
- notReverted.removeLast(); // change was successfully reverted
- } catch (Exception e) {
- throw new RevertFailedException(notReverted, e);
- }
-
- }
- }
- }
-}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java
deleted file mode 100644
index 9f9c9f53e..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java
+++ /dev/null
@@ -1,49 +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.spi.write.RootWriterCustomizer;
-import io.fd.honeycomb.v3po.translate.write.WriteContext;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * Customizer not performing any changes on current level. Suitable for nodes that don't have any leaves and all of
- * its child nodes are managed by dedicated writers
- */
-public class NoopWriterCustomizer<D extends DataObject> implements RootWriterCustomizer<D> {
-
- @Override
- public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataAfter,
- @Nonnull final WriteContext ctx) {
-
- }
-
- @Override
- public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
- @Nonnull final D dataAfter,
- @Nonnull final WriteContext ctx) {
-
- }
-
- @Override
- public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
- @Nonnull final WriteContext ctx) {
-
- }
-}
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 866854212..236ad8917 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
@@ -20,7 +20,6 @@ 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 java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -32,19 +31,11 @@ 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
* translation layer.
*/
-public class NoopWriterRegistry implements WriterRegistry {
+public class NoopWriterRegistry implements WriterRegistry, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(NoopWriterRegistry.class);
@Override
- public void update(@Nonnull final Map<InstanceIdentifier<?>, DataObject> dataBefore,
- @Nonnull final Map<InstanceIdentifier<?>, DataObject> dataAfter, @Nonnull final WriteContext ctx)
- throws TranslationException {
- LOG.trace("NoopWriterRegistry.update dataBefore{}, dataAfter={], ctx={}", dataBefore, dataAfter, ctx);
- // NOOP
- }
-
- @Override
public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
@Nullable final DataObject dataBefore, @Nullable final DataObject dataAfter,
@Nonnull final WriteContext ctx) throws WriteFailedException {
@@ -53,9 +44,20 @@ public class NoopWriterRegistry implements WriterRegistry {
// 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("Root registry has no type");
+ 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/ReflexiveAugmentWriterCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveAugmentWriterCustomizer.java
deleted file mode 100644
index 6d29214b3..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveAugmentWriterCustomizer.java
+++ /dev/null
@@ -1,50 +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 static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.common.base.Optional;
-import io.fd.honeycomb.v3po.translate.spi.write.ChildWriterCustomizer;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.binding.Augmentable;
-import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * Might be slow !
- */
-public class ReflexiveAugmentWriterCustomizer<C extends DataObject> extends NoopWriterCustomizer<C> implements
- ChildWriterCustomizer<C> {
-
- @Nonnull
- @Override
- @SuppressWarnings("unchecked")
- public Optional<C> extract(@Nonnull final InstanceIdentifier<C> currentId, @Nonnull final DataObject parentData) {
- checkArgument(parentData instanceof Augmentable<?>, "Not augmnatable parent object: %s", parentData);
- final Class<C> currentType = currentId.getTargetType();
- final Augmentation<?> augmentation = ((Augmentable) parentData).getAugmentation(currentType);
- if(augmentation == null) {
- return Optional.absent();
- } else {
- checkState(currentType.isAssignableFrom(augmentation.getClass()));
- return Optional.of(currentType.cast(augmentation));
- }
- }
-}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java
deleted file mode 100644
index 79cdf62c3..000000000
--- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.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.write;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
-import io.fd.honeycomb.v3po.translate.util.ReflectionUtils;
-import io.fd.honeycomb.v3po.translate.spi.write.ChildWriterCustomizer;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Collections;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * Might be slow !
- */
-public class ReflexiveChildWriterCustomizer<C extends DataObject> extends NoopWriterCustomizer<C> implements
- ChildWriterCustomizer<C> {
-
- @Nonnull
- @Override
- @SuppressWarnings("unchecked")
- public Optional<C> extract(@Nonnull final InstanceIdentifier<C> currentId, @Nonnull final DataObject parentData) {
- final Class<C> currentType = currentId.getTargetType();
- final Optional<Method> method = ReflectionUtils.findMethodReflex(getParentType(currentId),
- "get" + currentType.getSimpleName(), Collections.<Class<?>>emptyList(), currentType);
-
- Preconditions.checkArgument(method.isPresent(), "Unable to get %s from %s", currentType, parentData);
-
- try {
- return method.isPresent()
- ? Optional.fromNullable((C) method.get().invoke(parentData))
- : Optional.absent();
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new IllegalArgumentException("Unable to get " + currentType + " from " + parentData, e);
- }
- }
-
- private Class<? extends DataObject> getParentType(final @Nonnull InstanceIdentifier<C> currentId) {
- return Iterables.get(currentId.getPathArguments(), Iterables.size(currentId.getPathArguments()) - 2).getType();
- }
-}
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
new file mode 100644
index 000000000..79d8eb88d
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java
@@ -0,0 +1,334 @@
+/*
+ * 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.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+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;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+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 java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Flat writer registry, delegating updates to writers in the order writers were submitted.
+ */
+@ThreadSafe
+final class FlatWriterRegistry implements WriterRegistry {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistry.class);
+
+ // All types handled by writers directly or as children
+ private final ImmutableSet<InstanceIdentifier<?>> handledTypes;
+
+ private final Set<InstanceIdentifier<?>> writersOrderReversed;
+ private final Set<InstanceIdentifier<?>> writersOrder;
+ private final Map<InstanceIdentifier<?>, Writer<?>> writers;
+
+ /**
+ * Create flat registry instance.
+ *
+ * @param writers immutable, ordered map of writers to use to process updates. Order of the writers has to be
+ * one in which create and update operations should be handled. Deletes will be handled in reversed
+ * order. All deletes are handled before handling all the updates.
+ */
+ FlatWriterRegistry(@Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writers) {
+ this.writers = writers;
+ this.writersOrderReversed = Sets.newLinkedHashSet(Lists.reverse(Lists.newArrayList(writers.keySet())));
+ this.writersOrder = writers.keySet();
+ this.handledTypes = getAllHandledTypes(writers);
+ }
+
+ private static ImmutableSet<InstanceIdentifier<?>> getAllHandledTypes(
+ @Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writers) {
+ final ImmutableSet.Builder<InstanceIdentifier<?>> handledTypesBuilder = ImmutableSet.builder();
+ for (Map.Entry<InstanceIdentifier<?>, Writer<?>> writerEntry : writers.entrySet()) {
+ final InstanceIdentifier<?> writerType = writerEntry.getKey();
+ final Writer<?> writer = writerEntry.getValue();
+ handledTypesBuilder.add(writerType);
+ if (writer instanceof SubtreeWriter) {
+ handledTypesBuilder.addAll(((SubtreeWriter<?>) writer).getHandledChildTypes());
+ }
+ }
+ return handledTypesBuilder.build();
+ }
+
+ @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()) {
+ return;
+ }
+
+ // Optimization
+ if (updates.containsOnlySingleType()) {
+ // First process delete
+ singleUpdate(updates.getDeletes(), ctx);
+ // Next is update
+ singleUpdate(updates.getUpdates(), ctx);
+ } else {
+ // First process deletes
+ bulkUpdate(updates.getDeletes(), ctx, true, writersOrderReversed);
+ // Next are updates
+ bulkUpdate(updates.getUpdates(), ctx, true, writersOrder);
+ }
+
+ LOG.debug("Update successful for types: {}", updates.getTypeIntersection());
+ LOG.trace("Update successful for: {}", updates);
+ }
+
+ private void singleUpdate(@Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ if (updates.isEmpty()) {
+ return;
+ }
+
+ final InstanceIdentifier<?> singleType = updates.keySet().iterator().next();
+ LOG.debug("Performing single type update for: {}", singleType);
+ Collection<? extends DataObjectUpdate> singleTypeUpdates = updates.get(singleType);
+ Writer<?> writer = getWriter(singleType);
+
+ if (writer == null) {
+ // This node must be handled by a subtree writer, find it and call it or else fail
+ checkArgument(handledTypes.contains(singleType), "Unable to process update. Missing writers for: %s",
+ singleType);
+ writer = getSubtreeWriterResponsible(singleType);
+ singleTypeUpdates = getParentDataObjectUpdate(ctx, updates, writer);
+ }
+
+ LOG.trace("Performing single type update with writer: {}", writer);
+ for (DataObjectUpdate singleUpdate : singleTypeUpdates) {
+ writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx);
+ }
+ }
+
+ private Writer<?> getSubtreeWriterResponsible(final InstanceIdentifier<?> singleType) {
+ final Writer<?> writer;// This is slow ( minor TODO-perf )
+ writer = writers.values().stream()
+ .filter(w -> w instanceof SubtreeWriter)
+ .filter(w -> ((SubtreeWriter<?>) w).getHandledChildTypes().contains(singleType))
+ .findFirst()
+ .get();
+ return writer;
+ }
+
+ private Collection<DataObjectUpdate> getParentDataObjectUpdate(final WriteContext ctx,
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ final Writer<?> writer) {
+ // Now read data for subtree reader root, but first keyed ID is needed and that ID can be cut from updates
+ InstanceIdentifier<?> firstAffectedChildId = ((SubtreeWriter<?>) writer).getHandledChildTypes().stream()
+ .filter(updates::containsKey)
+ .map(unkeyedId -> updates.get(unkeyedId))
+ .flatMap(doUpdates -> doUpdates.stream())
+ .map(DataObjectUpdate::getId)
+ .findFirst()
+ .get();
+
+ final InstanceIdentifier<?> parentKeyedId =
+ RWUtils.cutId(firstAffectedChildId, writer.getManagedDataObjectType());
+
+ final Optional<? extends DataObject> parentBefore = ctx.readBefore(parentKeyedId);
+ final Optional<? extends DataObject> parentAfter = ctx.readAfter(parentKeyedId);
+ return Collections.singleton(
+ DataObjectUpdate.create(parentKeyedId, parentBefore.orNull(), parentAfter.orNull()));
+ }
+
+ private void bulkUpdate(@Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ @Nonnull final WriteContext ctx,
+ final boolean attemptRevert,
+ @Nonnull final Set<InstanceIdentifier<?>> writersOrder) throws BulkUpdateException {
+ if (updates.isEmpty()) {
+ return;
+ }
+
+ LOG.debug("Performing bulk update with revert attempt: {} for: {}", attemptRevert, updates.keySet());
+
+ // Check that all updates can be handled
+ checkAllTypesCanBeHandled(updates);
+
+ // Capture all changes successfully processed in case revert is needed
+ final Set<InstanceIdentifier<?>> processedNodes = new HashSet<>();
+
+ // Iterate over all writers and call update if there are any related updates
+ for (InstanceIdentifier<?> writerType : writersOrder) {
+ Collection<? extends DataObjectUpdate> writersData = updates.get(writerType);
+ final Writer<?> writer = getWriter(writerType);
+
+ if (writersData.isEmpty()) {
+ // If there are no data for current writer, but it is a SubtreeWriter and there are updates to
+ // its children, still invoke it with its root data
+ if (writer instanceof SubtreeWriter<?> && isAffected(((SubtreeWriter<?>) writer), updates)) {
+ // Provide parent data for SubtreeWriter for further processing
+ writersData = getParentDataObjectUpdate(ctx, updates, writer);
+ } else {
+ // Skipping unaffected writer
+ // Alternative to this would be modification sort according to the order of writers
+ continue;
+ }
+ }
+
+ LOG.debug("Performing update for: {}", writerType);
+ LOG.trace("Performing update with writer: {}", writer);
+
+ for (DataObjectUpdate singleUpdate : writersData) {
+ try {
+ writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx);
+ processedNodes.add(singleUpdate.getId());
+ LOG.trace("Update successful for type: {}", writerType);
+ LOG.debug("Update successful for: {}", singleUpdate);
+ } catch (Exception e) {
+ LOG.error("Error while processing data change of: {} (updates={})", writerType, writersData, e);
+
+ final Reverter reverter = attemptRevert
+ ? new ReverterImpl(processedNodes, updates, writersOrder, ctx)
+ : () -> {}; // NOOP reverter
+
+ // Find out which changes left unprocessed
+ final Set<InstanceIdentifier<?>> unprocessedChanges = updates.values().stream()
+ .map(DataObjectUpdate::getId)
+ .filter(id -> !processedNodes.contains(id))
+ .collect(Collectors.toSet());
+ throw new BulkUpdateException(unprocessedChanges, reverter, e);
+ }
+ }
+ }
+ }
+
+ private void checkAllTypesCanBeHandled(
+ @Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates) {
+ if (!handledTypes.containsAll(updates.keySet())) {
+ final Sets.SetView<InstanceIdentifier<?>> missingWriters = Sets.difference(updates.keySet(), handledTypes);
+ LOG.warn("Unable to process update. Missing writers for: {}", missingWriters);
+ throw new IllegalArgumentException("Unable to process update. Missing writers for: " + missingWriters);
+ }
+ }
+
+ /**
+ * Check whether {@link SubtreeWriter} is affected by the updates.
+ *
+ * @return true if there are any updates to SubtreeWriter's child nodes (those marked by SubtreeWriter
+ * as being taken care of)
+ * */
+ private static boolean isAffected(final SubtreeWriter<?> writer,
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates) {
+ return !Sets.intersection(writer.getHandledChildTypes(), updates.keySet()).isEmpty();
+ }
+
+ private Writer<?> getWriter(@Nonnull final InstanceIdentifier<?> singleType) {
+ final Writer<?> writer = writers.get(singleType);
+ checkNotNull(writer,
+ "Unable to write %s. Missing writer. Current writers for: %s", singleType, writers.keySet());
+ return writer;
+ }
+
+ @Nonnull
+ @Override
+ public InstanceIdentifier<DataObject> getManagedDataObjectType() {
+ throw new UnsupportedOperationException("Registry has no managed type");
+ }
+
+ // FIXME unit test
+ private final class ReverterImpl implements Reverter {
+
+ private final Collection<InstanceIdentifier<?>> processedNodes;
+ private final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates;
+ private final Set<InstanceIdentifier<?>> revertDeleteOrder;
+ private final WriteContext ctx;
+
+ ReverterImpl(final Collection<InstanceIdentifier<?>> processedNodes,
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ final Set<InstanceIdentifier<?>> writersOrderOriginal,
+ final WriteContext ctx) {
+ this.processedNodes = processedNodes;
+ this.updates = updates;
+ // Use opposite ordering when executing revert
+ this.revertDeleteOrder = writersOrderOriginal == FlatWriterRegistry.this.writersOrder
+ ? FlatWriterRegistry.this.writersOrderReversed
+ : FlatWriterRegistry.this.writersOrder;
+ this.ctx = ctx;
+ }
+
+ @Override
+ public void revert() throws RevertFailedException {
+ Multimap<InstanceIdentifier<?>, DataObjectUpdate> updatesToRevert =
+ filterAndRevertProcessed(updates, processedNodes);
+
+ LOG.info("Attempting revert for changes: {}", updatesToRevert);
+ try {
+ // Perform reversed bulk update without revert attempt
+ bulkUpdate(updatesToRevert, ctx, true, revertDeleteOrder);
+ LOG.info("Revert successful");
+ } catch (BulkUpdateException e) {
+ LOG.error("Revert failed", e);
+ throw new RevertFailedException(e.getFailedIds(), e);
+ }
+ }
+
+ /**
+ * Create new updates map, but only keep already processed changes. Switching before and after data for each
+ * update.
+ */
+ private Multimap<InstanceIdentifier<?>, DataObjectUpdate> filterAndRevertProcessed(
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ final Collection<InstanceIdentifier<?>> processedNodes) {
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> filtered = HashMultimap.create();
+ for (InstanceIdentifier<?> processedNode : processedNodes) {
+ final InstanceIdentifier<?> wildcardedIid = RWUtils.makeIidWildcarded(processedNode);
+ if (updates.containsKey(wildcardedIid)) {
+ updates.get(wildcardedIid).stream()
+ .filter(dataObjectUpdate -> processedNode.contains(dataObjectUpdate.getId()))
+ .forEach(dataObjectUpdate -> filtered.put(processedNode, dataObjectUpdate.reverse()));
+ }
+ }
+ return filtered;
+ }
+ }
+
+}
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
new file mode 100644
index 000000000..f5d218f55
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java
@@ -0,0 +1,225 @@
+/*
+ * 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.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.write.Writer;
+import io.fd.honeycomb.v3po.translate.write.WriterRegistryBuilder;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+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;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Builder for {@link FlatWriterRegistry} allowing users to specify inter-writer relationships.
+ */
+@NotThreadSafe
+public final class FlatWriterRegistryBuilder implements ModifiableWriterRegistry, WriterRegistryBuilder, AutoCloseable {
+
+ 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);
+ }
+ }
+
+ /**
+ * Create FlatWriterRegistry with writers ordered according to submitted relationships.
+ */
+ @Override
+ public FlatWriterRegistry build() {
+ final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters = getMappedWriters();
+ LOG.debug("Building writer registry with writers: {}",
+ mappedWriters.keySet().stream()
+ .map(InstanceIdentifier::getTargetType)
+ .map(Class::getSimpleName)
+ .collect(Collectors.joining(", ")));
+ LOG.trace("Building writer registry with writers: {}", mappedWriters);
+ return new FlatWriterRegistry(mappedWriters);
+ }
+
+ @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());
+ }
+
+ // Represents edges in graph
+ private static final class WriterRelation {}
+}
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java
new file mode 100644
index 000000000..e395b29da
--- /dev/null
+++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java
@@ -0,0 +1,85 @@
+/*
+ * 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.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.Iterables;
+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 java.util.HashSet;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Simple writer delegate for subtree writers (writers handling also children nodes) providing a list of all the
+ * children nodes being handled.
+ */
+final class SubtreeWriter<D extends DataObject> implements Writer<D> {
+
+ private final Writer<D> delegate;
+ private final Set<InstanceIdentifier<?>> handledChildTypes = new HashSet<>();
+
+ private SubtreeWriter(final Writer<D> delegate, Set<InstanceIdentifier<?>> handledTypes) {
+ this.delegate = delegate;
+ for (InstanceIdentifier<?> handledType : handledTypes) {
+ // Iid has to start with writer'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 writer. All of the types are children of the type managed by this
+ * writer excluding the type of this writer.
+ */
+ Set<InstanceIdentifier<?>> getHandledChildTypes() {
+ return handledChildTypes;
+ }
+
+ @Override
+ public void update(
+ @Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx) throws WriteFailedException {
+ delegate.update(id, dataBefore, dataAfter, ctx);
+ }
+
+ @Override
+ @Nonnull
+ public InstanceIdentifier<D> getManagedDataObjectType() {
+ return delegate.getManagedDataObjectType();
+ }
+
+ /**
+ * Wrap a writer as a subtree writer.
+ */
+ static Writer<?> createForWriter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Writer<? extends DataObject> writer) {
+ return new SubtreeWriter<>(writer, handledChildren);
+ }
+}