HONEYCOMB-359 - Wildcarded writers 34/8434/18
authorJan Srnicek <jsrnicek@cisco.com>
Fri, 3 Nov 2017 12:33:53 +0000 (13:33 +0100)
committerMarek Gradzki <mgradzki@cisco.com>
Fri, 3 Nov 2017 12:58:01 +0000 (12:58 +0000)
Adds option to specify subtree writer that can handle whole subtree
of nodes without having whole subtree specified. Its checking
if node is children at runtime, rather than having pre-computed tree

Change-Id: Ic46f2bd6de84f0dd14865825399f5a90a1f80859
Signed-off-by: Jan Srnicek <jsrnicek@cisco.com>
16 files changed:
infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/HoneycombWriteInfraTest.java
infra/minimal-distribution-core/src/main/java/io/fd/honeycomb/infra/distro/schema/YangModulesProvider.java
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/ApplicationRibWriterFactory.java
infra/northbound/bgp/src/main/java/io/fd/honeycomb/infra/bgp/neighbors/BgpPeerWriterFactory.java
infra/translate-api/src/main/java/io/fd/honeycomb/translate/ModifiableSubtreeManagerRegistryBuilder.java
infra/translate-api/src/main/java/io/fd/honeycomb/translate/write/Writer.java
infra/translate-impl/pom.xml
infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/registry/CompositeReaderRegistryBuilder.java
infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistry.java
infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryBuilder.java
infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/SubtreeWriter.java
infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryTest.java
infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/WildcardedSubtreeWriterTest.java [new file with mode: 0644]
infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java
infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/AbstractGenericWriter.java
infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/write/BindingBrokerWriter.java

index a740e01..54b93c7 100644 (file)
@@ -22,6 +22,7 @@ import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -29,6 +30,7 @@ import static org.mockito.Mockito.when;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import io.fd.honeycomb.data.DataModification;
+import io.fd.honeycomb.test.model.Ids;
 import io.fd.honeycomb.translate.impl.write.registry.FlatWriterRegistryBuilder;
 import io.fd.honeycomb.translate.util.YangDAG;
 import io.fd.honeycomb.translate.write.WriteContext;
@@ -40,6 +42,8 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import org.junit.Test;
 import org.mockito.InOrder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment;
@@ -75,7 +79,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
 import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
-import io.fd.honeycomb.test.model.Ids;
 
 /**
  * Testing honeycomb writes from data tree up to mocked writers.
@@ -103,6 +106,31 @@ public class HoneycombWriteInfraTest extends AbstractInfraTest {
     private static <D extends DataObject> Writer<D> mockWriter(final InstanceIdentifier<D> id) {
         final Writer<D> mock = (Writer<D>) mock(Writer.class);
         when(mock.getManagedDataObjectType()).thenReturn(id);
+        //TODO - HONEYCOMB-412 - to call default impl of canProcess()
+        when(mock.canProcess(any())).thenAnswer(invocationOnMock -> {
+            final Writer writer = Writer.class.cast(invocationOnMock.getMock());
+            final Writer delegatingWriter = new Writer() {
+                @Nonnull
+                @Override
+                public InstanceIdentifier getManagedDataObjectType() {
+                    return writer.getManagedDataObjectType();
+                }
+
+                @Override
+                public boolean supportsDirectUpdate() {
+                    return writer.supportsDirectUpdate();
+                }
+
+                @Override
+                public void processModification(@Nonnull final InstanceIdentifier id,
+                                                @Nullable final DataObject dataBefore,
+                                                @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx)
+                        throws WriteFailedException {
+                    writer.processModification(id, dataBefore, dataAfter, ctx);
+                }
+            };
+            return delegatingWriter.canProcess(InstanceIdentifier.class.cast(invocationOnMock.getArguments()[0]));
+        });
         return mock;
     }
 
@@ -226,8 +254,19 @@ public class HoneycombWriteInfraTest extends AbstractInfraTest {
 
         for (Writer<?> orderedWriter : orderedWriters) {
             verify(orderedWriter).getManagedDataObjectType();
-            verifyNoMoreInteractions(orderedWriter);
+            //TODO - HONEYCOMB-412
+            //verifyNoMoreInteractions(orderedWriter);
         }
+
+        verify(complexAugmentContainerWriter, times(1)).processModification(any(), any(), any(), any());
+        verify(c3Writer, times(1)).processModification(any(), any(), any(), any());
+        verify(simpleAugmentWriter, times(1)).processModification(any(), any(), any(), any());
+        verify(simpleContainerWriter, times(1)).processModification(any(), any(), any(), any());
+        verify(containerWithChoiceWriter, times(1)).processModification(any(), any(), any(), any());
+        verify(containerFromGroupingWriter, times(1)).processModification(any(), any(), any(), any());
+        verify(nestedListWriter, times(2)).processModification(any(), any(), any(), any());
+        verify(listInContainerWriter, times(2)).processModification(any(), any(), any(), any());
+        verify(containerInListWriter, times(2)).processModification(any(), any(), any(), any());
     }
 
     private Writer<?>[] getOrderedWriters() {
@@ -316,8 +355,19 @@ public class HoneycombWriteInfraTest extends AbstractInfraTest {
 
         for (Writer<?> orderedWriter : orderedWriters) {
             verify(orderedWriter).getManagedDataObjectType();
-            verifyNoMoreInteractions(orderedWriter);
+            //TODO - HONEYCOMB-412
+            // verifyNoMoreInteractions(orderedWriter);
         }
+
+        verify(complexAugmentContainerWriter, times(2)).processModification(any(), any(), any(), any());
+        verify(c3Writer, times(2)).processModification(any(), any(), any(), any());
+        verify(simpleAugmentWriter, times(2)).processModification(any(), any(), any(), any());
+        verify(simpleContainerWriter, times(2)).processModification(any(), any(), any(), any());
+        verify(containerWithChoiceWriter, times(2)).processModification(any(), any(), any(), any());
+        verify(containerFromGroupingWriter, times(2)).processModification(any(), any(), any(), any());
+        verify(nestedListWriter, times(4)).processModification(any(), any(), any(), any());
+        verify(listInContainerWriter, times(4)).processModification(any(), any(), any(), any());
+        verify(containerInListWriter, times(4)).processModification(any(), any(), any(), any());
     }
 
     private void writeContainerWithList(final DataModification dataModification) {
index b60df1a..5acc1ea 100644 (file)
@@ -56,10 +56,10 @@ public class YangModulesProvider implements Provider<YangModulesProvider.YangMod
                 .collect(Collectors.toSet()));
     }
 
-    public static class YangModules {
+    static class YangModules {
         private final Set<Class<? extends YangModelBindingProvider>> yangBindings;
 
-        public YangModules(final Set<Class<? extends YangModelBindingProvider>> yangBindings) {
+        YangModules(final Set<Class<? extends YangModelBindingProvider>> yangBindings) {
             this.yangBindings = yangBindings;
         }
 
index 02f1788..00a12e7 100644 (file)
@@ -45,14 +45,6 @@ final class ApplicationRibWriterFactory implements WriterFactory {
             InstanceIdentifier.create(ApplicationRib.class);
     private static final InstanceIdentifier<Tables> TABLES_IID = AR_IID.child(Tables.class);
 
-
-    // TODO (HONEYCOMB-359):
-    // BGP models are huge, we need some kind of wildcarded subtree writer, that works for whole subtree.
-    // 1) we can either move checking handledTypes to writers (getHandledTypes, isAffected, writer.getHandedTypes, ...)
-    // but then precondition check in flatWriterRegistry might be slower (we need to check if we have all writers
-    // in order to avoid unnecessary reverts).
-    //
-    // 2) alternative is to compute all child nodes during initialization (might introduce some footprint penalty).
     @Override
     public void init(final ModifiableWriterRegistryBuilder registry) {
         registry.subtreeAdd(ImmutableSet.of(TABLES_IID), new BindingBrokerWriter<>(InstanceIdentifier.create(ApplicationRib.class), dataBroker)
index 8b7510e..848b4d5 100644 (file)
 
 package io.fd.honeycomb.infra.bgp.neighbors;
 
-import com.google.common.collect.Sets;
 import com.google.inject.Inject;
 import io.fd.honeycomb.infra.bgp.BgpConfiguration;
 import io.fd.honeycomb.translate.impl.write.GenericListWriter;
 import io.fd.honeycomb.translate.write.WriterFactory;
 import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
-import javax.annotation.Nonnull;
 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
 import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
 import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.multiprotocol.rev151009.bgp.common.afi.safi.list.AfiSafi;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.AfiSafis;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.Config;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.Timers;
-import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.Transport;
 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbors.Neighbor;
 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.Bgp;
 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.top.bgp.Neighbors;
@@ -38,25 +31,24 @@ import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.re
 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.NetworkInstance;
 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.Protocols;
 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.network.instance.rev151018.network.instance.top.network.instances.network.instance.protocols.Protocol;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.AfiSafi1;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Config1;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Config2;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.openconfig.extensions.rev160614.Protocol1;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
+import javax.annotation.Nonnull;
+
 /**
  * Initializes writer for Bgp Neighbors ({@link Neighbor} node) and all its parents required by HC infra.
  */
 public final class BgpPeerWriterFactory implements WriterFactory {
     private static final InstanceIdentifier<NetworkInstance> NETWORK_INSTANCE_ID =
-        InstanceIdentifier.create(NetworkInstances.class)
-            .child(NetworkInstance.class);
+            InstanceIdentifier.create(NetworkInstances.class)
+                    .child(NetworkInstance.class);
 
     private static final InstanceIdentifier<Protocol> PROTOCOL_ID =
-        NETWORK_INSTANCE_ID.child(Protocols.class).child(Protocol.class);
+            NETWORK_INSTANCE_ID.child(Protocols.class).child(Protocol.class);
 
     private static final InstanceIdentifier<Neighbor> NEIGHBOR_ID =
-        PROTOCOL_ID.augmentation(Protocol1.class).child(Bgp.class).child(Neighbors.class).child(Neighbor.class);
+            PROTOCOL_ID.augmentation(Protocol1.class).child(Bgp.class).child(Neighbors.class).child(Neighbor.class);
 
     @Inject
     private BgpConfiguration configuration;
@@ -72,39 +64,18 @@ public final class BgpPeerWriterFactory implements WriterFactory {
         // NetworkInstances
         //  NetworkInstance =
         registry.add(new GenericListWriter<>(NETWORK_INSTANCE_ID,
-            new NetworkInstanceCustomizer(configuration.bgpNetworkInstanceName)));
+                new NetworkInstanceCustomizer(configuration.bgpNetworkInstanceName)));
 
         //   Protocols
         //    Protocol =
         registry.add(
-            new GenericListWriter<>(PROTOCOL_ID, new ProtocolCustomizer(configuration.bgpProtocolInstanceName.get())));
+                new GenericListWriter<>(PROTOCOL_ID, new ProtocolCustomizer(configuration.bgpProtocolInstanceName.get())));
 
         //     Protocol1 augmentation (from bgp-openconfig-extensions)
         //      Bgp
         //       Neighbors
         //        Neighbor=
-        final InstanceIdentifier<Neighbor> neighbor = InstanceIdentifier.create(Neighbor.class);
-        registry.subtreeAdd(
-            // TODO (HONEYCOMB-359): there might be more subnodes that needs to be handled
-            Sets.newHashSet(
-                neighbor.child(Config.class),
-                neighbor.child(Config.class).augmentation(Config2.class),
-                neighbor.child(AfiSafis.class),
-                neighbor.child(AfiSafis.class).child(AfiSafi.class),
-                neighbor.child(AfiSafis.class).child(AfiSafi.class).augmentation(AfiSafi1.class),
-                neighbor.child(Timers.class),
-                neighbor.child(Timers.class).child(
-                    org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.timers.Config.class),
-                neighbor.child(Transport.class),
-                neighbor.child(Transport.class).child(
-                    org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.transport.Config.class),
-                neighbor.child(Transport.class).child(
-                    org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group.transport.Config.class)
-                    .augmentation(Config1.class)
-            ),
-            new GenericListWriter<>(
-                NEIGHBOR_ID,
-                new NeighborCustomizer(globalRib, peerRegistry, tableTypeRegistry)));
+        registry.wildcardedSubtreeAdd(new GenericListWriter<>(NEIGHBOR_ID, new NeighborCustomizer(globalRib, peerRegistry, tableTypeRegistry)));
     }
 }
 
index 4e6b2d8..ad9cd2b 100644 (file)
 
 package io.fd.honeycomb.translate;
 
-import java.util.Collection;
-import java.util.Set;
-import javax.annotation.Nonnull;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
+import javax.annotation.Nonnull;
+import java.util.Collection;
+import java.util.Set;
+
 /**
  * Registry builder where {@link SubtreeManager}s can be added with or without relationships between them.
  * The relationships express what the order of execution should be.
@@ -41,6 +42,11 @@ public interface ModifiableSubtreeManagerRegistryBuilder<S extends SubtreeManage
     ModifiableSubtreeManagerRegistryBuilder<S> subtreeAdd(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
                                                           @Nonnull S handler);
 
+    /**
+     * Add a handler responsible for writing all complex nodes within a subtree its responsible for.
+     */
+    ModifiableSubtreeManagerRegistryBuilder<S> wildcardedSubtreeAdd(@Nonnull S handler);
+
     /**
      * Add a handler and make sure it will be executed before handler identifier by relatedType is executed.
      */
@@ -50,6 +56,12 @@ public interface ModifiableSubtreeManagerRegistryBuilder<S extends SubtreeManage
     ModifiableSubtreeManagerRegistryBuilder<S> addBefore(@Nonnull S handler,
                                                          @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
 
+    ModifiableSubtreeManagerRegistryBuilder<S> wildcardedSubtreeAddBefore(@Nonnull S handler,
+                                                                          @Nonnull InstanceIdentifier<?> relatedType);
+
+    ModifiableSubtreeManagerRegistryBuilder<S> wildcardedSubtreeAddBefore(@Nonnull S handler,
+                                                                          @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
+
     ModifiableSubtreeManagerRegistryBuilder<S> subtreeAddBefore(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
                                                                 @Nonnull S handler,
                                                                 @Nonnull InstanceIdentifier<?> relatedType);
@@ -67,6 +79,12 @@ public interface ModifiableSubtreeManagerRegistryBuilder<S extends SubtreeManage
     ModifiableSubtreeManagerRegistryBuilder<S> addAfter(@Nonnull S handler,
                                                         @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
 
+    ModifiableSubtreeManagerRegistryBuilder<S> wildcardedSubtreeAddAfter(@Nonnull S handler,
+                                                                         @Nonnull InstanceIdentifier<?> relatedType);
+
+    ModifiableSubtreeManagerRegistryBuilder<S> wildcardedSubtreeAddAfter(@Nonnull S handler,
+                                                                         @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
+
     ModifiableSubtreeManagerRegistryBuilder<S> subtreeAddAfter(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
                                                                @Nonnull S handler,
                                                                @Nonnull InstanceIdentifier<?> relatedType);
index 1a16b72..18573e5 100644 (file)
@@ -47,8 +47,17 @@ public interface Writer<D extends DataObject> extends SubtreeManager<D> {
                              @Nonnull final WriteContext ctx) throws WriteFailedException;
 
     /**
-     * Indicates whether there is direct support for updating nodes handled by this writer,
+     * Indicates whether there is direct support for updating nodes handled by writer,
      * or they must be broken up to individual deletes and creates.
      */
     boolean supportsDirectUpdate();
+
+    /**
+     * Returns true if node identified by this identifier can be processes by this writer
+     *
+     * @param instanceIdentifier identifier to be checked
+     */
+    default boolean canProcess(@Nonnull final InstanceIdentifier<? extends DataObject> instanceIdentifier) {
+        return getManagedDataObjectType().equals(instanceIdentifier);
+    }
 }
index c11893a..aa2a48b 100644 (file)
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>io.fd.honeycomb.it</groupId>
+            <artifactId>honeycomb-test-model</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
 
         <dependency>
             <groupId>junit</groupId>
             <artifactId>hamcrest-all</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.7.25</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
index f8290f1..4f7e1e1 100644 (file)
@@ -16,8 +16,6 @@
 
 package io.fd.honeycomb.translate.impl.read.registry;
 
-import static com.google.common.base.Preconditions.checkArgument;
-
 import com.google.common.collect.ImmutableMap;
 import io.fd.honeycomb.translate.impl.read.GenericListReader;
 import io.fd.honeycomb.translate.impl.read.GenericReader;
@@ -29,12 +27,6 @@ import io.fd.honeycomb.translate.read.registry.ReaderRegistry;
 import io.fd.honeycomb.translate.read.registry.ReaderRegistryBuilder;
 import io.fd.honeycomb.translate.util.AbstractSubtreeManagerRegistryBuilderBuilder;
 import io.fd.honeycomb.translate.util.YangDAG;
-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.Identifiable;
@@ -43,6 +35,15 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.NotThreadSafe;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
 @NotThreadSafe
 public final class CompositeReaderRegistryBuilder
         extends AbstractSubtreeManagerRegistryBuilderBuilder<Reader<? extends DataObject, ? extends Builder<?>>, ReaderRegistry>
@@ -62,6 +63,11 @@ public final class CompositeReaderRegistryBuilder
                 : SubtreeReader.createForReader(handledChildren, reader);
     }
 
+    @Override
+    protected Reader<? extends DataObject, ? extends Builder<?>> getWildcardedSubtreeHandler(@Nonnull Reader<? extends DataObject, ? extends Builder<?>> handler) {
+        throw new UnsupportedOperationException("Wildcarded readers are not supported");
+    }
+
     @Override
     public <D extends DataObject> void addStructuralReader(@Nonnull InstanceIdentifier<D> id,
                                                            @Nonnull Class<? extends Builder<D>> builderType) {
index 146ddb9..c3bc1ee 100644 (file)
@@ -21,7 +21,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
@@ -32,12 +31,15 @@ import io.fd.honeycomb.translate.write.WriteContext;
 import io.fd.honeycomb.translate.write.Writer;
 import io.fd.honeycomb.translate.write.registry.UpdateFailedException;
 import io.fd.honeycomb.translate.write.registry.WriterRegistry;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 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;
@@ -54,39 +56,23 @@ 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;
+    private final Map<InstanceIdentifier<?>, Writer<?>> writersById;
+    private final Set<? extends 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
+     * @param writersById 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();
+    FlatWriterRegistry(@Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writersById) {
+        this.writersById = writersById;
+        this.writersOrderReversed = Sets.newLinkedHashSet(Lists.reverse(Lists.newArrayList(writersById.keySet())));
+        this.writersOrder = writersById.keySet();
+        this.writers = writersById.entrySet().stream().map(Map.Entry::getValue).collect(Collectors.toSet());
     }
 
     @Override
@@ -145,9 +131,9 @@ final class FlatWriterRegistry implements WriterRegistry {
 
         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);
+            checkArgument(writer != null, "Unable to process update. Missing writers for: %s",
+                    singleType);
             singleTypeUpdates = getParentDataObjectUpdate(ctx, updates, writer);
         }
 
@@ -168,9 +154,9 @@ final class FlatWriterRegistry implements WriterRegistry {
 
     @Nullable
     private Writer<?> getSubtreeWriterResponsible(final InstanceIdentifier<?> singleType) {
-        return writers.values().stream()
+        return writersById.values().stream()
                 .filter(w -> w instanceof SubtreeWriter)
-                .filter(w -> ((SubtreeWriter<?>) w).getHandledChildTypes().contains(singleType))
+                .filter(w -> w.canProcess(singleType))
                 .findFirst()
                 .orElse(null);
     }
@@ -249,10 +235,23 @@ final class FlatWriterRegistry implements WriterRegistry {
 
     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);
+
+        List<InstanceIdentifier<?>> noWriterNodes = new ArrayList<>();
+        for (InstanceIdentifier<?> id : updates.keySet()) {
+            // either there is direct writer for the iid
+            if (writersById.containsKey(id)) {
+                continue;
+            } else {
+                // or subtree one
+                if (writers.stream().anyMatch(o -> o.canProcess(id))) {
+                    continue;
+                }
+            }
+            noWriterNodes.add(id);
+        }
+
+        if (!noWriterNodes.isEmpty()) {
+            throw new IllegalArgumentException("Unable to process update. Missing writers for: " + noWriterNodes);
         }
     }
 
@@ -269,7 +268,7 @@ final class FlatWriterRegistry implements WriterRegistry {
 
     @Nullable
     private Writer<?> getWriter(@Nonnull final InstanceIdentifier<?> singleType) {
-        return writers.get(singleType);
+        return writersById.get(singleType);
     }
 
 }
index 0c6d0a1..4360606 100644 (file)
@@ -24,15 +24,16 @@ import io.fd.honeycomb.translate.write.Writer;
 import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
 import io.fd.honeycomb.translate.write.registry.WriterRegistry;
 import io.fd.honeycomb.translate.write.registry.WriterRegistryBuilder;
-import java.util.Set;
-import java.util.stream.Collectors;
-import javax.annotation.Nonnull;
-import javax.annotation.concurrent.NotThreadSafe;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.NotThreadSafe;
+import java.util.Set;
+import java.util.stream.Collectors;
+
 /**
  * Builder for {@link FlatWriterRegistry} allowing users to specify inter-writer relationships.
  */
@@ -53,6 +54,11 @@ public final class FlatWriterRegistryBuilder
         return SubtreeWriter.createForWriter(handledChildren, writer);
     }
 
+    @Override
+    protected Writer<? extends DataObject> getWildcardedSubtreeHandler(@Nonnull Writer<? extends DataObject> handler) {
+        return SubtreeWriter.createWildcardedForWriter(handler);
+    }
+
     /**
      * Create FlatWriterRegistry with writers ordered according to submitted relationships.
      */
index b2a571b..a1a5f3f 100644 (file)
@@ -37,15 +37,16 @@ final class SubtreeWriter<D extends DataObject> implements Writer<D> {
 
     private final Writer<D> delegate;
     private final Set<InstanceIdentifier<?>> handledChildTypes = new HashSet<>();
+    private boolean isWildcarded = false;
 
-    private SubtreeWriter(final Writer<D> delegate, Set<InstanceIdentifier<?>> handledTypes) {
+    private SubtreeWriter(final Writer<D> delegate, final 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);
+                            + "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(
@@ -53,6 +54,11 @@ final class SubtreeWriter<D extends DataObject> implements Writer<D> {
         }
     }
 
+    private SubtreeWriter(final Writer<D> delegate) {
+        this.delegate = delegate;
+        this.isWildcarded = true;
+    }
+
     /**
      * 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.
@@ -74,6 +80,20 @@ final class SubtreeWriter<D extends DataObject> implements Writer<D> {
         return delegate.supportsDirectUpdate();
     }
 
+    @Override
+    public boolean canProcess(@Nonnull InstanceIdentifier<?> instanceIdentifier) {
+        if (isWildcarded) {
+            final Class<D> parent = delegate.getManagedDataObjectType().getTargetType();
+            for (InstanceIdentifier.PathArgument pathArgument : instanceIdentifier.getPathArguments()) {
+                if (pathArgument.getType().equals(parent)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        return handledChildTypes.contains(instanceIdentifier);
+    }
+
     @Override
     @Nonnull
     public InstanceIdentifier<D> getManagedDataObjectType() {
@@ -87,4 +107,11 @@ final class SubtreeWriter<D extends DataObject> implements Writer<D> {
                                      @Nonnull final Writer<? extends DataObject> writer) {
         return new SubtreeWriter<>(writer, handledChildren);
     }
+
+    /**
+     * Wrap a writer as a subtree writer.
+     */
+    static Writer<?> createWildcardedForWriter(@Nonnull final Writer<? extends DataObject> writer) {
+        return new SubtreeWriter<>(writer);
+    }
 }
index 1514369..01852d1 100644 (file)
@@ -25,6 +25,7 @@ import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
@@ -39,15 +40,19 @@ import io.fd.honeycomb.translate.util.DataObjects.DataObject1;
 import io.fd.honeycomb.translate.util.DataObjects.DataObject2;
 import io.fd.honeycomb.translate.write.DataObjectUpdate;
 import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
 import io.fd.honeycomb.translate.write.Writer;
 import io.fd.honeycomb.translate.write.registry.UpdateFailedException;
 import io.fd.honeycomb.translate.write.registry.WriterRegistry;
 import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
@@ -72,6 +77,18 @@ public class FlatWriterRegistryTest {
         when(writer1.getManagedDataObjectType()).thenReturn(DataObject1.IID);
         when(writer2.getManagedDataObjectType()).thenReturn(DataObject2.IID);
         when(writer3.getManagedDataObjectType()).thenReturn(DataObjects.DataObject3.IID);
+        when(writer4.getManagedDataObjectType()).thenReturn(DataObjects.DataObject1ChildK.IID);
+        // TODO - HONEYCOMB-412 - thenCallRealMethod doest work with default methods
+        // https://stackoverflow.com/questions/27663252/can-you-make-mockito-1-10-17-work-with-default-methods-in-interfaces
+        when(writer1.canProcess(any())).thenAnswer(answerWithImpl());
+        when(writer2.canProcess(any())).thenAnswer(answerWithImpl());
+        when(writer3.canProcess(any())).thenAnswer(answerWithImpl());
+        when(writer4.canProcess(any())).thenAnswer(answerWithImpl());
+    }
+
+    private static Answer<Object> answerWithImpl() {
+        return invocationOnMock -> new CheckedMockWriter(Writer.class.cast(invocationOnMock.getMock())).canProcess(
+                InstanceIdentifier.class.cast(invocationOnMock.getArguments()[0]));
     }
 
     @Test
@@ -112,8 +129,12 @@ public class FlatWriterRegistryTest {
         inOrder.verify(writer1).processModification(iid, dataObject, dataObject, ctx);
         inOrder.verify(writer2).processModification(iid2, dataObject2, dataObject2, ctx);
 
-        verifyNoMoreInteractions(writer1);
-        verifyNoMoreInteractions(writer2);
+        // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
+        // we are really interested just in invocations of processModification(),so adding specific verify to check that
+        verify(writer1,times(1)).processModification(any(),any(),any(),any());
+        verify(writer2,times(1)).processModification(any(),any(),any(),any());
+        //verifyNoMoreInteractions(writer1);
+        //verifyNoMoreInteractions(writer2);
     }
 
     @Test
@@ -136,8 +157,12 @@ public class FlatWriterRegistryTest {
         inOrder.verify(writer2).processModification(iid2, dataObject2, null, ctx);
         inOrder.verify(writer1).processModification(iid, dataObject, null, ctx);
 
-        verifyNoMoreInteractions(writer1);
-        verifyNoMoreInteractions(writer2);
+        // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
+        // we are really interested just in invocations of processModification(),so adding specific verify to check that
+        verify(writer1,times(1)).processModification(any(),any(),any(),any());
+        verify(writer2,times(1)).processModification(any(),any(),any(),any());
+        //verifyNoMoreInteractions(writer1);
+        //verifyNoMoreInteractions(writer2);
     }
 
     @Test
@@ -170,8 +195,12 @@ public class FlatWriterRegistryTest {
         inOrder.verify(writer1).processModification(iid, dataObject, dataObject, ctx);
         inOrder.verify(writer2).processModification(iid2, dataObject2, dataObject2, ctx);
 
-        verifyNoMoreInteractions(writer1);
-        verifyNoMoreInteractions(writer2);
+        // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
+        // we are really interested just in invocations of processModification(),so adding specific verify to check that
+        verify(writer1,times(2)).processModification(any(),any(),any(),any());
+        verify(writer2,times(2)).processModification(any(),any(),any(),any());
+        //verifyNoMoreInteractions(writer1);
+        //verifyNoMoreInteractions(writer2);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -298,4 +327,35 @@ public class FlatWriterRegistryTest {
                                                                       final InstanceIdentifier<D> iid) {
         return DataObjectUpdate.create(iid, mock(type), mock(type));
     }
+
+    //TODO - HONEYCOMB-412 - remove after
+    /**
+     * Used to utilize default implementation of canProcess()
+     * */
+    static class CheckedMockWriter implements Writer{
+
+        private final Writer mockedWriter;
+
+        CheckedMockWriter(final Writer mockedWriter) {
+            this.mockedWriter = mockedWriter;
+        }
+
+        @Override
+        public void processModification(@Nonnull final InstanceIdentifier id, @Nullable final DataObject dataBefore,
+                                        @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx)
+                throws WriteFailedException {
+            mockedWriter.processModification(id,dataBefore,dataAfter,ctx);
+        }
+
+        @Override
+        public boolean supportsDirectUpdate() {
+            return mockedWriter.supportsDirectUpdate();
+        }
+
+        @Nonnull
+        @Override
+        public InstanceIdentifier getManagedDataObjectType() {
+            return mockedWriter.getManagedDataObjectType();
+        }
+    }
 }
\ No newline at end of file
diff --git a/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/WildcardedSubtreeWriterTest.java b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/WildcardedSubtreeWriterTest.java
new file mode 100644 (file)
index 0000000..7006eea
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2017 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.translate.impl.write.registry;
+
+import io.fd.honeycomb.translate.write.Writer;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.AugTarget;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.FromAugmentAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.FromAugmentListAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.SimpleNestedAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.aug.target.FromAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.aug.target.from.augment.FromAugmentEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoice;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class WildcardedSubtreeWriterTest {
+
+    private static final InstanceIdentifier<ContainerWithList> C_WITH_LIST = InstanceIdentifier.create(ContainerWithList.class);
+    private static final InstanceIdentifier<ContainerWithChoice> C_WITH_CHOICE = InstanceIdentifier.create(ContainerWithChoice.class);
+    private static final InstanceIdentifier<AugTarget> C_AUG = InstanceIdentifier.create(AugTarget.class);
+
+    private static final InstanceIdentifier<ListInContainer> L_IN_CONTAINER = C_WITH_LIST.child(ListInContainer.class);
+    private static final InstanceIdentifier<ContainerInList> C_IN_LIST = L_IN_CONTAINER.child(ContainerInList.class);
+
+    private static final InstanceIdentifier<NestedList> N_LIST = C_IN_LIST.child(NestedList.class);
+
+    private Writer subtreeContainerWithList;
+    private Writer subtreeContainerWithChoice;
+    private Writer subtreeAugTarget;
+
+
+    @Before
+    public void init() {
+        MockitoAnnotations.initMocks(this);
+        Writer<ContainerWithList> writerContainerWithList = mock(Writer.class);
+        Writer<ContainerWithChoice> writerContainerWithChoice = mock(Writer.class);
+        Writer<AugTarget> writerAugTarget = mock(Writer.class);
+        when(writerContainerWithList.getManagedDataObjectType()).thenReturn(C_WITH_LIST);
+        when(writerContainerWithChoice.getManagedDataObjectType()).thenReturn(C_WITH_CHOICE);
+        when(writerAugTarget.getManagedDataObjectType()).thenReturn(C_AUG);
+        subtreeContainerWithList = SubtreeWriter.createWildcardedForWriter(writerContainerWithList);
+        subtreeContainerWithChoice = SubtreeWriter.createWildcardedForWriter(writerContainerWithChoice);
+        subtreeAugTarget = SubtreeWriter.createWildcardedForWriter(writerAugTarget);
+    }
+
+    @Test
+    public void testParent() {
+        assertTrue(subtreeContainerWithList.canProcess(C_WITH_LIST));
+        assertFalse(subtreeContainerWithList.canProcess(C_WITH_CHOICE));
+
+        assertTrue(subtreeContainerWithChoice.canProcess(C_WITH_CHOICE));
+        assertFalse(subtreeContainerWithChoice.canProcess(C_WITH_LIST));
+
+        assertTrue(subtreeAugTarget.canProcess(C_AUG));
+        assertFalse(subtreeAugTarget.canProcess(C_WITH_LIST));
+    }
+
+    @Test
+    public void testDirectChild() {
+        assertTrue(subtreeContainerWithList.canProcess(L_IN_CONTAINER));
+        assertFalse(subtreeContainerWithList.canProcess(C_WITH_CHOICE.child(C3.class)));
+
+        assertTrue(subtreeContainerWithChoice.canProcess(C_WITH_CHOICE.child(C3.class)));
+        assertFalse(subtreeContainerWithChoice.canProcess(L_IN_CONTAINER));
+    }
+
+    @Test
+    public void testIndirectChild() {
+        assertTrue(subtreeContainerWithList.canProcess(C_IN_LIST));
+        assertTrue(subtreeContainerWithList.canProcess(N_LIST));
+    }
+
+    @Test
+    public void testAugDirectChild() {
+        assertTrue(subtreeAugTarget.canProcess(C_AUG.augmentation(FromAugmentAugment.class).child(FromAugment.class)));
+        assertFalse(subtreeContainerWithList.canProcess(C_AUG.augmentation(FromAugmentAugment.class).child(FromAugment.class)));
+    }
+
+    @Test
+    public void testAugIndirectChild() {
+        assertTrue(subtreeAugTarget.canProcess(C_AUG.augmentation(FromAugmentAugment.class)
+                .child(FromAugment.class)
+                .augmentation(SimpleNestedAugment.class)));
+        assertFalse(subtreeContainerWithList.canProcess(C_AUG.augmentation(FromAugmentAugment.class)
+                .child(FromAugment.class)
+                .augmentation(FromAugmentListAugment.class)
+                .child(FromAugmentEntry.class)));
+    }
+}
index fe2f117..181d717 100644 (file)
@@ -21,13 +21,14 @@ import com.google.common.collect.ImmutableMap;
 import io.fd.honeycomb.translate.ModifiableSubtreeManagerRegistryBuilder;
 import io.fd.honeycomb.translate.SubtreeManager;
 import io.fd.honeycomb.translate.SubtreeManagerRegistryBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import javax.annotation.Nonnull;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
-import javax.annotation.Nonnull;
-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> {
@@ -64,6 +65,12 @@ public abstract class AbstractSubtreeManagerRegistryBuilderBuilder<S extends Sub
         return this;
     }
 
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<S> wildcardedSubtreeAdd(@Nonnull S handler) {
+        add(getWildcardedSubtreeHandler(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));
@@ -109,6 +116,12 @@ public abstract class AbstractSubtreeManagerRegistryBuilderBuilder<S extends Sub
         return addBefore(getSubtreeHandler(handledChildren, handler), relatedType);
     }
 
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<S> wildcardedSubtreeAddBefore(@Nonnull final S handler,
+                                                                                 @Nonnull final InstanceIdentifier<?> relatedType) {
+        return addBefore(getWildcardedSubtreeHandler(handler), relatedType);
+    }
+
     @Override
     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddBefore(
             @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
@@ -117,9 +130,17 @@ public abstract class AbstractSubtreeManagerRegistryBuilderBuilder<S extends Sub
         return addBefore(getSubtreeHandler(handledChildren, handler), relatedTypes);
     }
 
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<S> wildcardedSubtreeAddBefore(@Nonnull final S handler,
+                                                                                 @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+        return addBefore(getWildcardedSubtreeHandler(handler), relatedTypes);
+    }
+
     protected abstract S getSubtreeHandler(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
                                            @Nonnull final S handler);
 
+    protected abstract S getWildcardedSubtreeHandler(@Nonnull final S handler);
+
     /**
      * Add handler with relationship: to be executed after handler handling relatedType.
      */
@@ -162,6 +183,12 @@ public abstract class AbstractSubtreeManagerRegistryBuilderBuilder<S extends Sub
         return addAfter(getSubtreeHandler(handledChildren, handler), relatedType);
     }
 
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<S> wildcardedSubtreeAddAfter(@Nonnull final S handler,
+                                                                                @Nonnull final InstanceIdentifier<?> relatedType) {
+        return addAfter(getWildcardedSubtreeHandler(handler), relatedType);
+    }
+
     @Override
     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddAfter(
             @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
@@ -170,6 +197,11 @@ public abstract class AbstractSubtreeManagerRegistryBuilderBuilder<S extends Sub
         return addAfter(getSubtreeHandler(handledChildren, handler), relatedTypes);
     }
 
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<S> wildcardedSubtreeAddAfter(@Nonnull S handler, @Nonnull Collection<InstanceIdentifier<?>> relatedTypes) {
+        return addAfter(getWildcardedSubtreeHandler(handler), relatedTypes);
+    }
+
     protected ImmutableMap<InstanceIdentifier<?>, S> getMappedHandlers() {
         final ImmutableMap.Builder<InstanceIdentifier<?>, S> builder = ImmutableMap.builder();
         // Iterate writer types according to their relationships from graph
index e2ea115..4f1b696 100644 (file)
 
 package io.fd.honeycomb.translate.util.write;
 
-import static com.google.common.base.Preconditions.checkArgument;
-
 import io.fd.honeycomb.translate.util.RWUtils;
 import io.fd.honeycomb.translate.write.WriteContext;
 import io.fd.honeycomb.translate.write.WriteFailedException;
 import io.fd.honeycomb.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;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
 public abstract class AbstractGenericWriter<D extends DataObject> implements Writer<D> {
 
     private static final Logger LOG = LoggerFactory.getLogger(AbstractGenericWriter.class);
index 7b68376..60a81a7 100644 (file)
 
 package io.fd.honeycomb.translate.util.write;
 
-import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
-
 import com.google.common.util.concurrent.CheckedFuture;
 import io.fd.honeycomb.translate.write.WriteContext;
 import io.fd.honeycomb.translate.write.WriteFailedException;
 import io.fd.honeycomb.translate.write.Writer;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+
 /**
  * Simple DataBroker backed writer allowing to delegate writes to different brokers.
  */