Initial implementation of VPP writers
authorMaros Marsalek <[email protected]>
Tue, 22 Mar 2016 14:09:23 +0000 (15:09 +0100)
committerMarek Gradzki <[email protected]>
Thu, 31 Mar 2016 14:27:42 +0000 (14:27 +0000)
Composite, recursive and extensible writers

Change-Id: I1fbd1d49af44343ab655e31d17ba51dd0f8ca268
Signed-off-by: Maros Marsalek <[email protected]>
12 files changed:
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/VppRWUtils.java
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/AbstractCompositeVppWriter.java [new file with mode: 0644]
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/CompositeChildVppWriter.java [new file with mode: 0644]
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/CompositeListVppWriter.java [new file with mode: 0644]
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/CompositeRootVppWriter.java [new file with mode: 0644]
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/spi/ChildVppWriterCustomizer.java [new file with mode: 0644]
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/spi/ListVppWriterCustomizer.java [new file with mode: 0644]
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/spi/RootVppWriterCustomizer.java [new file with mode: 0644]
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistry.java [new file with mode: 0644]
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/NoopWriterCustomizer.java [new file with mode: 0644]
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/ReflexiveChildWriterCustomizer.java [new file with mode: 0644]
v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/TransactionWriteContext.java [new file with mode: 0644]

index fe0f1f2..d11910b 100644 (file)
@@ -23,6 +23,7 @@ import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 import io.fd.honeycomb.v3po.impl.trans.SubtreeManager;
 import io.fd.honeycomb.v3po.impl.trans.r.ChildVppReader;
+import io.fd.honeycomb.v3po.impl.trans.w.ChildVppWriter;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -61,10 +62,18 @@ public final class VppRWUtils {
         return Collections.emptyList();
     }
 
+    public static <T> List<ChildVppWriter<? extends ChildOf<T>>> emptyChildWriterList() {
+        return Collections.emptyList();
+    }
+
     public static <T> List<ChildVppReader<? extends Augmentation<T>>> emptyAugReaderList() {
         return Collections.emptyList();
     }
 
+    public static <T> List<ChildVppWriter<? extends Augmentation<T>>> emptyAugWriterList() {
+        return Collections.emptyList();
+    }
+
     public static <T> List<ChildVppReader<? extends Augmentation<T>>> singletonAugReaderList(
         ChildVppReader<? extends Augmentation<T>> item) {
         return Collections.<ChildVppReader<? extends Augmentation<T>>>singletonList(item);
@@ -75,6 +84,11 @@ public final class VppRWUtils {
         return Collections.<ChildVppReader<? extends ChildOf<T>>>singletonList(item);
     }
 
+    public static <T> List<ChildVppWriter<? extends ChildOf<T>>> singletonChildWriterList(
+        ChildVppWriter<? extends ChildOf<T>> item) {
+        return Collections.<ChildVppWriter<? extends ChildOf<T>>>singletonList(item);
+    }
+
     /**
      * Replace last item in ID with a provided IdentifiableItem of the same type
      */
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/AbstractCompositeVppWriter.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/AbstractCompositeVppWriter.java
new file mode 100644 (file)
index 0000000..44690bd
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * 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.impl.trans.w.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils;
+import io.fd.honeycomb.v3po.impl.trans.w.ChildVppWriter;
+import io.fd.honeycomb.v3po.impl.trans.w.VppWriter;
+import io.fd.honeycomb.v3po.impl.trans.w.WriteContext;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractCompositeVppWriter<D extends DataObject> implements VppWriter<D> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractCompositeVppWriter.class);
+
+    public static final Function<DataObject, Object> INDEX_FUNCTION = new Function<DataObject, Object>() {
+        @Override
+        public Object apply(final DataObject input) {
+            return input instanceof Identifiable<?>
+                ? ((Identifiable<?>) input).getKey()
+                : input;
+        }
+    };
+
+    private final Map<Class<? extends DataObject>, ChildVppWriter<? extends ChildOf<D>>> childReaders;
+    private final Map<Class<? extends DataObject>, ChildVppWriter<? extends Augmentation<D>>> augReaders;
+    private final InstanceIdentifier<D> instanceIdentifier;
+
+    public AbstractCompositeVppWriter(final Class<D> type,
+                                      final List<ChildVppWriter<? extends ChildOf<D>>> childReaders,
+                                      final List<ChildVppWriter<? extends Augmentation<D>>> augReaders) {
+        this.instanceIdentifier = InstanceIdentifier.create(type);
+        this.childReaders = VppRWUtils.uniqueLinkedIndex(childReaders, VppRWUtils.MANAGER_CLASS_FUNCTION);
+        this.augReaders = VppRWUtils.uniqueLinkedIndex(augReaders, VppRWUtils.MANAGER_CLASS_AUG_FUNCTION);
+    }
+
+    protected void writeCurrent(final InstanceIdentifier<D> id, final D data, final WriteContext ctx) {
+        LOG.debug("{}: Writing current: {} data: {}", this, id, data);
+
+        LOG.trace("{}: Writing current attributes", this);
+        writeCurrentAttributes(id, data, ctx);
+
+        for (ChildVppWriter<? extends ChildOf<D>> child : childReaders.values()) {
+            LOG.debug("{}: Writing child in: {}", this, child);
+            child.writeChild(id, data, ctx);
+        }
+
+        for (ChildVppWriter<? extends Augmentation<D>> child : augReaders.values()) {
+            LOG.debug("{}: Writing augment in: {}", this, child);
+            child.writeChild(id, data, ctx);
+        }
+
+        LOG.debug("{}: Current node written successfully", this);
+    }
+
+    protected void updateCurrent(final InstanceIdentifier<D> id, final D dataBefore, final D dataAfter,
+                                 final WriteContext ctx) {
+        LOG.debug("{}: Updating current: {} dataBefore: {}, datAfter: {}", this, id, dataBefore, dataAfter);
+
+        if(dataBefore.equals(dataAfter)) {
+            LOG.debug("{}: Skipping current(no update): {}", this, id);
+            // No change, ignore
+            return;
+        }
+
+        LOG.trace("{}: Updating current attributes", this);
+        updateCurrentAttributes(id, dataBefore, dataAfter, ctx);
+
+        for (ChildVppWriter<? extends ChildOf<D>> child : childReaders.values()) {
+            LOG.debug("{}: Updating child in: {}", this, child);
+            child.updateChild(id, dataBefore, dataAfter, ctx);
+        }
+
+        for (ChildVppWriter<? extends Augmentation<D>> child : augReaders.values()) {
+            LOG.debug("{}: Updating augment in: {}", this, child);
+            child.updateChild(id, dataBefore, dataAfter, ctx);
+        }
+
+        LOG.debug("{}: Current node updated successfully", this);
+    }
+
+    protected void deleteCurrent(final InstanceIdentifier<D> id, final D dataBefore, final WriteContext ctx) {
+        LOG.debug("{}: Deleting current: {} dataBefore: {}", this, id, dataBefore);
+
+        // delete in reversed order
+        for (ChildVppWriter<? extends Augmentation<D>> child : reverseCollection(augReaders.values())) {
+            LOG.debug("{}: Deleting augment in: {}", this, child);
+            child.deleteChild(id, dataBefore, ctx);
+        }
+
+        for (ChildVppWriter<? extends ChildOf<D>> child : reverseCollection(childReaders.values())) {
+            LOG.debug("{}: Deleting child in: {}", this, child);
+            child.deleteChild(id, dataBefore, ctx);
+        }
+
+        LOG.trace("{}: Deleting current attributes", this);
+        deleteCurrentAttributes(id, dataBefore, ctx);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+                       @Nonnull final List<? extends DataObject> dataBefore,
+                       @Nonnull final List<? extends DataObject> dataAfter,
+                       @Nonnull final WriteContext ctx) {
+        LOG.debug("{}: Updating : {}", this, id);
+        LOG.trace("{}: Updating : {}, from: {} to: {}", this, id, dataBefore, dataAfter);
+
+        if (idPointsToCurrent(id)) {
+            if(isWrite(dataBefore, dataAfter)) {
+                writeAll((InstanceIdentifier<D>) id, dataAfter, ctx);
+            } else if(isDelete(dataBefore, dataAfter)) {
+                deleteAll((InstanceIdentifier<D>) id, dataBefore, ctx);
+            } else {
+                checkArgument(!dataBefore.isEmpty() && !dataAfter.isEmpty(), "No data to process");
+                updateAll((InstanceIdentifier<D>) id, dataBefore, dataAfter, ctx);
+            }
+        } else {
+            if (isWrite(dataBefore, dataAfter)) {
+                writeSubtree(id, dataAfter, ctx);
+            } else if (isDelete(dataBefore, dataAfter)) {
+                deleteSubtree(id, dataBefore, ctx);
+            } else {
+                checkArgument(!dataBefore.isEmpty() && !dataAfter.isEmpty(), "No data to process");
+                updateSubtree(id, dataBefore, dataAfter, ctx);
+            }
+        }
+    }
+
+    protected void updateAll(final @Nonnull InstanceIdentifier<D> id,
+                             final @Nonnull List<? extends DataObject> dataBefore,
+                             final @Nonnull List<? extends DataObject> dataAfter, final WriteContext ctx) {
+        LOG.trace("{}: Updating all : {}", this, id);
+
+        final Map<Object, ? extends DataObject> indexedAfter = indexData(dataAfter);
+        final Map<Object, ? extends DataObject> indexedBefore = indexData(dataBefore);
+
+        for (Map.Entry<Object, ? extends DataObject> after : indexedAfter.entrySet()) {
+            final DataObject before = indexedBefore.get(after.getKey());
+            if(before == null) {
+                writeCurrent(id, castToManaged(after.getValue()), ctx);
+            } else {
+                updateCurrent(id, castToManaged(before), castToManaged(after.getValue()), ctx);
+            }
+        }
+
+        // Delete the rest in dataBefore
+        for (Object deletedNodeKey : Sets.difference(indexedBefore.keySet(), indexedAfter.keySet())) {
+            final DataObject deleted = indexedBefore.get(deletedNodeKey);
+            deleteCurrent(id, castToManaged(deleted), ctx);
+        }
+    }
+
+    private static Map<Object, ? extends DataObject> indexData(final List<? extends DataObject> data) {
+        return Maps.uniqueIndex(data, INDEX_FUNCTION);
+    }
+
+    protected void deleteAll(final @Nonnull InstanceIdentifier<D> id,
+                             final @Nonnull List<? extends DataObject> dataBefore, final WriteContext ctx) {
+        LOG.trace("{}: Deleting all : {}", this, id);
+        for (DataObject singleValue : dataBefore) {
+            checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(singleValue.getClass()));
+            deleteCurrent(id, castToManaged(singleValue), ctx);
+        }
+    }
+
+    private D castToManaged(final DataObject data) {
+        checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(data.getClass()));
+        return getManagedDataObjectType().getTargetType().cast(data);
+    }
+
+    protected void writeAll(final @Nonnull InstanceIdentifier<D> id,
+                            final @Nonnull List<? extends DataObject> dataAfter, final WriteContext ctx) {
+        LOG.trace("{}: Writing all : {}", this, id);
+        for (DataObject singleValue : dataAfter) {
+            checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(singleValue.getClass()));
+            writeCurrent(id, castToManaged(singleValue), ctx);
+        }
+    }
+
+    private static boolean isWrite(final List<? extends DataObject> dataBefore,
+                                    final List<? extends DataObject> dataAfter) {
+        return dataBefore.isEmpty() && !dataAfter.isEmpty();
+    }
+
+    private static boolean isDelete(final List<? extends DataObject> dataBefore,
+                                    final List<? extends DataObject> dataAfter) {
+        return dataAfter.isEmpty() && !dataBefore.isEmpty();
+    }
+
+    private void writeSubtree(final InstanceIdentifier<? extends DataObject> id,
+                              final List<? extends DataObject> dataAfter, final WriteContext ctx) {
+        LOG.debug("{}: Writing subtree: {}", this, id);
+        final VppWriter<? extends ChildOf<D>> vppWriter = getNextWriter(id);
+
+        if (vppWriter != null) {
+            LOG.debug("{}: Writing subtree: {} in: {}", this, id, vppWriter);
+            vppWriter.update(id, Collections.<DataObject>emptyList(), dataAfter, ctx);
+        } else {
+            // If there's no dedicated writer, use write current
+            // But we need current data after to do so
+            final InstanceIdentifier<D> currentId = VppRWUtils.cutId(id, getManagedDataObjectType());
+            List<? extends DataObject> currentDataAfter = ctx.readAfter(currentId);
+            LOG.debug("{}: Dedicated subtree writer missing for: {}. Writing current.", this,
+                VppRWUtils.getNextId(id, getManagedDataObjectType()).getType(), currentDataAfter);
+            writeAll(currentId, currentDataAfter, ctx);
+        }
+    }
+
+    private boolean idPointsToCurrent(final @Nonnull InstanceIdentifier<? extends DataObject> id) {
+        return id.getTargetType().equals(getManagedDataObjectType().getTargetType());
+    }
+
+    @SuppressWarnings("unchecked")
+    private void deleteSubtree(final InstanceIdentifier<? extends DataObject> id,
+                               final List<? extends DataObject> dataBefore, final WriteContext ctx) {
+        LOG.debug("{}: Deleting subtree: {}", this, id);
+        final VppWriter<? extends ChildOf<D>> vppWriter = getNextWriter(id);
+
+        if (vppWriter != null) {
+            LOG.debug("{}: Deleting subtree: {} in: {}", this, id, vppWriter);
+            vppWriter.update(id, dataBefore, Collections.<DataObject>emptyList(), ctx);
+        } else {
+            updateSubtreeFromCurrent(id, ctx);
+        }
+    }
+
+    private void updateSubtreeFromCurrent(final InstanceIdentifier<? extends DataObject> id, final WriteContext ctx) {
+        final InstanceIdentifier<D> currentId = VppRWUtils.cutId(id, getManagedDataObjectType());
+        List<? extends DataObject> currentDataBefore = ctx.readBefore(currentId);
+        List<? extends DataObject> currentDataAfter = ctx.readAfter(currentId);
+        LOG.debug("{}: Dedicated subtree writer missing for: {}. Updating current without subtree", this,
+            VppRWUtils.getNextId(id, getManagedDataObjectType()).getType(), currentDataAfter);
+        updateAll((InstanceIdentifier<D>) id, currentDataBefore, currentDataAfter, ctx);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void updateSubtree(final InstanceIdentifier<? extends DataObject> id,
+                               final List<? extends DataObject> dataBefore,
+                               final List<? extends DataObject> dataAfter,
+                               final WriteContext ctx) {
+        LOG.debug("{}: Updating subtree: {}", this, id);
+        final VppWriter<? extends ChildOf<D>> vppWriter = getNextWriter(id);
+
+        if (vppWriter != null) {
+            LOG.debug("{}: Updating subtree: {} in: {}", this, id, vppWriter);
+            vppWriter.update(id, dataBefore, dataAfter, ctx);
+        } else {
+            updateSubtreeFromCurrent(id, ctx);
+        }
+    }
+
+    private VppWriter<? extends ChildOf<D>> getNextWriter(final InstanceIdentifier<? extends DataObject> id) {
+        final Class<? extends DataObject> next = VppRWUtils.getNextId(id, getManagedDataObjectType()).getType();
+        return childReaders.get(next);
+    }
+
+    private static <T> List<T> reverseCollection(final Collection<T> original) {
+        // TODO find a better reverse mechanism (probably a different collection for child readers is necessary)
+        final ArrayList<T> list = Lists.newArrayList(original);
+        Collections.reverse(list);
+        return list;
+    }
+
+    protected abstract void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+                                                   @Nonnull final D data,
+                                                   @Nonnull final WriteContext ctx);
+
+    protected abstract void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+                                                    @Nonnull final D dataBefore,
+                                                    @Nonnull final WriteContext ctx);
+
+    protected abstract void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+                                                    @Nonnull final D dataBefore,
+                                                    @Nonnull final D dataAfter,
+                                                    @Nonnull final WriteContext ctx);
+
+    @Nonnull
+    @Override
+    public InstanceIdentifier<D> getManagedDataObjectType() {
+        return instanceIdentifier;
+    }
+
+
+    @Override
+    public String toString() {
+        return String.format("Writer[%s]", getManagedDataObjectType().getTargetType().getSimpleName());
+    }
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/CompositeChildVppWriter.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/CompositeChildVppWriter.java
new file mode 100644 (file)
index 0000000..919bbaa
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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.impl.trans.w.impl;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils;
+import io.fd.honeycomb.v3po.impl.trans.w.ChildVppWriter;
+import io.fd.honeycomb.v3po.impl.trans.w.WriteContext;
+import io.fd.honeycomb.v3po.impl.trans.w.impl.spi.ChildVppWriterCustomizer;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class CompositeChildVppWriter<D extends DataObject> extends AbstractCompositeVppWriter<D>
+    implements ChildVppWriter<D> {
+
+    private final ChildVppWriterCustomizer<D> customizer;
+
+    public CompositeChildVppWriter(@Nonnull final Class<D> type,
+                                   @Nonnull final List<ChildVppWriter<? extends ChildOf<D>>> childWriters,
+                                   @Nonnull final List<ChildVppWriter<? extends Augmentation<D>>> augReaders,
+                                   @Nonnull final ChildVppWriterCustomizer<D> customizer) {
+        super(type, childWriters, augReaders);
+        this.customizer = customizer;
+    }
+
+    public CompositeChildVppWriter(@Nonnull final Class<D> type,
+                                   @Nonnull final List<ChildVppWriter<? extends ChildOf<D>>> childWriters,
+                                   @Nonnull final ChildVppWriterCustomizer<D> customizer) {
+        this(type, childWriters, VppRWUtils.<D>emptyAugWriterList(), customizer);
+    }
+
+    public CompositeChildVppWriter(@Nonnull final Class<D> type,
+                                   @Nonnull final ChildVppWriterCustomizer<D> customizer) {
+        this(type, VppRWUtils.<D>emptyChildWriterList(), VppRWUtils.<D>emptyAugWriterList(), customizer);
+    }
+
+    @Override
+    protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D data,
+                                          @Nonnull final WriteContext ctx) {
+        customizer.writeCurrentAttributes(id, data, ctx.getContext());
+    }
+
+    @Override
+    protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+                                           @Nonnull WriteContext ctx) {
+        customizer.deleteCurrentAttributes(id, dataBefore, ctx.getContext());
+    }
+
+    @Override
+    protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+                                           @Nonnull final D dataAfter, @Nonnull WriteContext ctx) {
+        customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx.getContext());
+    }
+
+    @Override
+    public void writeChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
+                           @Nonnull final DataObject parentData, @Nonnull WriteContext ctx) {
+        final InstanceIdentifier<D> currentId = VppRWUtils.appendTypeToId(parentId, getManagedDataObjectType());
+        final Optional<D> currentData = customizer.extract(currentId, parentData);
+        if(currentData.isPresent()) {
+            writeCurrent(currentId, currentData.get(), ctx);
+        }
+    }
+
+    @Override
+    public void deleteChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
+                            @Nonnull final DataObject parentData,
+                            @Nonnull final WriteContext ctx) {
+        final InstanceIdentifier<D> currentId = VppRWUtils.appendTypeToId(parentId, getManagedDataObjectType());
+        final Optional<D> currentData = customizer.extract(currentId, parentData);
+        if(currentData.isPresent()) {
+            deleteCurrent(currentId, currentData.get(), ctx);
+        }
+    }
+
+    @Override
+    public void updateChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
+                            @Nonnull final DataObject parentDataBefore, @Nonnull final DataObject parentDataAfter,
+                            @Nonnull final WriteContext ctx) {
+        final InstanceIdentifier<D> currentId = VppRWUtils.appendTypeToId(parentId, getManagedDataObjectType());
+        final Optional<D> before = customizer.extract(currentId, parentDataBefore);
+        final Optional<D> after = customizer.extract(currentId, parentDataAfter);
+        if(before.isPresent() && after.isPresent()) {
+            updateCurrent(currentId, before.get(), after.get(), ctx);
+        }
+    }
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/CompositeListVppWriter.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/CompositeListVppWriter.java
new file mode 100644 (file)
index 0000000..8914d37
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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.impl.trans.w.impl;
+
+import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils;
+import io.fd.honeycomb.v3po.impl.trans.w.ChildVppWriter;
+import io.fd.honeycomb.v3po.impl.trans.w.WriteContext;
+import io.fd.honeycomb.v3po.impl.trans.w.impl.spi.ListVppWriterCustomizer;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class CompositeListVppWriter<D extends DataObject & Identifiable<K>, K extends Identifier<D>> extends AbstractCompositeVppWriter<D>
+    implements ChildVppWriter<D> {
+
+    private final ListVppWriterCustomizer<D, K> customizer;
+
+    public CompositeListVppWriter(@Nonnull final Class<D> type,
+                                  @Nonnull final List<ChildVppWriter<? extends ChildOf<D>>> childReaders,
+                                  @Nonnull final List<ChildVppWriter<? extends Augmentation<D>>> augReaders,
+                                  @Nonnull final ListVppWriterCustomizer<D, K> customizer) {
+        super(type, childReaders, augReaders);
+        this.customizer = customizer;
+    }
+
+    public CompositeListVppWriter(@Nonnull final Class<D> type,
+                                  @Nonnull final List<ChildVppWriter<? extends ChildOf<D>>> childReaders,
+                                  @Nonnull final ListVppWriterCustomizer<D, K> customizer) {
+        this(type, childReaders, VppRWUtils.<D>emptyAugWriterList(), customizer);
+    }
+
+    public CompositeListVppWriter(@Nonnull final Class<D> type,
+                                  @Nonnull final ListVppWriterCustomizer<D, K> customizer) {
+        this(type, VppRWUtils.<D>emptyChildWriterList(), VppRWUtils.<D>emptyAugWriterList(), customizer);
+
+    }
+
+    @Override
+    protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D data,
+                                          @Nonnull final WriteContext ctx) {
+        customizer.writeCurrentAttributes(id, data, ctx.getContext());
+    }
+
+    @Override
+    protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+                                           @Nonnull final WriteContext ctx) {
+        customizer.deleteCurrentAttributes(id, dataBefore, ctx.getContext());
+    }
+
+    @Override
+    protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+                                           @Nonnull final D dataAfter, @Nonnull final WriteContext ctx) {
+        customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx.getContext());
+    }
+
+    @Override
+    public void writeChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
+                           @Nonnull final DataObject parentData,
+                           @Nonnull final WriteContext ctx) {
+        final InstanceIdentifier<D> currentId = VppRWUtils.appendTypeToId(parentId, getManagedDataObjectType());
+        final List<D> currentData = customizer.extract(currentId, parentData);
+        writeAll(currentId, currentData, ctx);
+    }
+
+    @Override
+    public void deleteChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
+                            @Nonnull final DataObject parentDataBefore,
+                            @Nonnull final WriteContext ctx) {
+        final InstanceIdentifier<D> currentId = VppRWUtils.appendTypeToId(parentId, getManagedDataObjectType());
+        final List<D> dataBefore = customizer.extract(currentId, parentDataBefore);
+        deleteAll(currentId, dataBefore, ctx);
+    }
+
+    @Override
+    public void updateChild(@Nonnull final InstanceIdentifier<? extends DataObject> parentId,
+                            @Nonnull final DataObject parentDataBefore, @Nonnull final DataObject parentDataAfter,
+                            @Nonnull final WriteContext ctx) {
+        final InstanceIdentifier<D> currentId = VppRWUtils.appendTypeToId(parentId, getManagedDataObjectType());
+        final List<D> dataBefore = customizer.extract(currentId, parentDataBefore);
+        final List<D> dataAfter = customizer.extract(currentId, parentDataAfter);
+        updateAll(currentId, dataBefore, dataAfter, ctx);
+    }
+
+    @Override
+    protected void writeCurrent(final InstanceIdentifier<D> id, final D data, final WriteContext ctx) {
+        // Make sure the key is present
+        if(isWildcarded(id)) {
+            super.writeCurrent(getSpecificId(id, data), data, ctx);
+        } else {
+            super.writeCurrent(id, data, ctx);
+        }
+    }
+
+    @Override
+    protected void updateCurrent(final InstanceIdentifier<D> id, final D dataBefore, final D dataAfter,
+                                 final WriteContext ctx) {
+        // Make sure the key is present
+        if(isWildcarded(id)) {
+            super.updateCurrent(getSpecificId(id, dataBefore), dataBefore, dataAfter, ctx);
+        } else {
+            super.updateCurrent(id, dataBefore, dataAfter, ctx);
+        }
+    }
+
+    @Override
+    protected void deleteCurrent(final InstanceIdentifier<D> id, final D dataBefore, final WriteContext ctx) {
+        // Make sure the key is present
+        if(isWildcarded(id)) {
+            super.deleteCurrent(getSpecificId(id, dataBefore), dataBefore, ctx);
+        } else {
+            super.deleteCurrent(id, dataBefore, ctx);
+        }
+    }
+
+    private boolean isWildcarded(final InstanceIdentifier<D> id) {
+        return id.firstIdentifierOf(getManagedDataObjectType().getTargetType()).isWildcarded();
+    }
+
+    private InstanceIdentifier<D> getSpecificId(final InstanceIdentifier<D> currentId, final D current) {
+        return VppRWUtils.replaceLastInId(currentId,
+            new InstanceIdentifier.IdentifiableItem<>(currentId.getTargetType(), current.getKey()));
+    }
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/CompositeRootVppWriter.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/CompositeRootVppWriter.java
new file mode 100644 (file)
index 0000000..6be5651
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.impl.trans.w.impl;
+
+import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils;
+import io.fd.honeycomb.v3po.impl.trans.w.ChildVppWriter;
+import io.fd.honeycomb.v3po.impl.trans.w.WriteContext;
+import io.fd.honeycomb.v3po.impl.trans.w.impl.spi.RootVppWriterCustomizer;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class CompositeRootVppWriter<D extends DataObject> extends AbstractCompositeVppWriter<D> {
+
+    private final RootVppWriterCustomizer<D> customizer;
+
+    public CompositeRootVppWriter(@Nonnull final Class<D> type,
+                                  @Nonnull final List<ChildVppWriter<? extends ChildOf<D>>> childReaders,
+                                  @Nonnull final List<ChildVppWriter<? extends Augmentation<D>>> augReaders,
+                                  @Nonnull final RootVppWriterCustomizer<D> customizer) {
+        super(type, childReaders, augReaders);
+        this.customizer = customizer;
+    }
+
+    public CompositeRootVppWriter(@Nonnull final Class<D> type,
+                                  @Nonnull final List<ChildVppWriter<? extends ChildOf<D>>> childReaders,
+                                  @Nonnull final RootVppWriterCustomizer<D> customizer) {
+        this(type, childReaders, VppRWUtils.<D>emptyAugWriterList(), customizer);
+    }
+
+    public CompositeRootVppWriter(@Nonnull final Class<D> type,
+                                  @Nonnull final RootVppWriterCustomizer<D> customizer) {
+        this(type, VppRWUtils.<D>emptyChildWriterList(), VppRWUtils.<D>emptyAugWriterList(), customizer);
+    }
+
+    @Override
+    protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D data,
+                                          @Nonnull final WriteContext ctx) {
+        customizer.writeCurrentAttributes(id, data, ctx.getContext());
+    }
+
+    @Override
+    protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+                                           @Nonnull final WriteContext ctx) {
+        customizer.deleteCurrentAttributes(id, dataBefore, ctx.getContext());
+    }
+
+    @Override
+    protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+                                           @Nonnull final D dataBefore,
+                                           @Nonnull final D dataAfter,
+                                           @Nonnull final WriteContext ctx) {
+        customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx.getContext());
+    }
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/spi/ChildVppWriterCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/spi/ChildVppWriterCustomizer.java
new file mode 100644 (file)
index 0000000..5ea5042
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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.impl.trans.w.impl.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Beta
+public interface ChildVppWriterCustomizer<D extends DataObject> extends RootVppWriterCustomizer<D> {
+
+    @Nonnull
+    Optional<D> extract(@Nonnull final InstanceIdentifier<D> currentId, @Nonnull final DataObject parentData);
+
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/spi/ListVppWriterCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/spi/ListVppWriterCustomizer.java
new file mode 100644 (file)
index 0000000..edd8de9
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.impl.trans.w.impl.spi;
+
+import com.google.common.annotations.Beta;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Beta
+public interface ListVppWriterCustomizer<C extends DataObject & Identifiable<K>, K extends Identifier<C>> extends RootVppWriterCustomizer<C> {
+
+    @Nonnull
+    List<C> extract(@Nonnull final InstanceIdentifier<C> currentId, @Nonnull final DataObject parentData);
+
+}
\ No newline at end of file
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/spi/RootVppWriterCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/impl/spi/RootVppWriterCustomizer.java
new file mode 100644 (file)
index 0000000..6a2d0c2
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.impl.trans.w.impl.spi;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.impl.trans.util.Context;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Beta
+public interface RootVppWriterCustomizer<D extends DataObject> {
+
+    void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+                                @Nonnull final D dataAfter,
+                                @Nonnull final Context writeContext);
+
+    void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+                                 @Nonnull final D dataBefore,
+                                 @Nonnull final D dataAfter,
+                                 @Nonnull final Context writeContext);
+
+    void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+                                 @Nonnull final D dataBefore,
+                                 @Nonnull final Context writeContext);
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/DelegatingWriterRegistry.java
new file mode 100644 (file)
index 0000000..6df9606
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.impl.trans.w.util;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.Iterables;
+import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils;
+import io.fd.honeycomb.v3po.impl.trans.w.VppWriter;
+import io.fd.honeycomb.v3po.impl.trans.w.WriteContext;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Simple reader registry able to perform and aggregated read (ROOT read) on top of all
+ * provided readers. Also able to delegate a specific read to one of the delegate readers.
+ *
+ * This could serve as a utility to hold & hide all available readers in upper layers.
+ */
+public final class DelegatingWriterRegistry implements VppWriter<DataObject> {
+
+    private final Map<Class<? extends DataObject>, VppWriter<? extends DataObject>> rootReaders;
+
+    /**
+     * Create new {@link DelegatingWriterRegistry}
+     *
+     * @param rootReaders List of delegate readers
+     */
+    public DelegatingWriterRegistry(@Nonnull final List<VppWriter<? extends DataObject>> rootReaders) {
+        this.rootReaders = VppRWUtils.uniqueLinkedIndex(checkNotNull(rootReaders), VppRWUtils.MANAGER_CLASS_FUNCTION);
+    }
+
+    /**
+     * @throws UnsupportedOperationException This getter is not supported for reader registry since it does not manage a
+     *                                       specific node type
+     */
+    @Nonnull
+    @Override
+    public InstanceIdentifier<DataObject> getManagedDataObjectType() {
+        throw new UnsupportedOperationException("Root registry has no type");
+    }
+
+    @Override
+    public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+                       @Nonnull final List<? extends DataObject> dataBefore,
+                       @Nonnull final List<? extends DataObject> dataAfter,
+                       @Nonnull final WriteContext ctx) {
+        final InstanceIdentifier.PathArgument first = checkNotNull(
+            Iterables.getFirst(id.getPathArguments(), null), "Empty id");
+        final VppWriter<? extends DataObject> vppWriter = rootReaders.get(first.getType());
+        checkNotNull(vppWriter,
+            "Unable to write %s. Missing writer. Current writers for: %s", id, rootReaders.keySet());
+        vppWriter.update(id, dataBefore, dataAfter, ctx);
+    }
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/NoopWriterCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/NoopWriterCustomizer.java
new file mode 100644 (file)
index 0000000..4f02a1d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.impl.trans.w.util;
+
+import io.fd.honeycomb.v3po.impl.trans.util.Context;
+import io.fd.honeycomb.v3po.impl.trans.w.impl.spi.RootVppWriterCustomizer;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class NoopWriterCustomizer<D extends DataObject> implements RootVppWriterCustomizer<D> {
+
+    @Override
+    public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataAfter,
+                                       @Nonnull final Context ctx) {
+
+    }
+
+    @Override
+    public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+                                        @Nonnull final D dataAfter,
+                                        @Nonnull final Context ctx) {
+
+    }
+
+    @Override
+    public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+                                        @Nonnull final Context ctx) {
+
+    }
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/ReflexiveChildWriterCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/ReflexiveChildWriterCustomizer.java
new file mode 100644 (file)
index 0000000..9d324a0
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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.impl.trans.w.util;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import io.fd.honeycomb.v3po.impl.trans.util.ReflectionUtils;
+import io.fd.honeycomb.v3po.impl.trans.w.impl.spi.ChildVppWriterCustomizer;
+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
+    ChildVppWriterCustomizer<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.of((C) method.get().invoke(parentData))
+                : Optional.<C>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/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/TransactionWriteContext.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/w/util/TransactionWriteContext.java
new file mode 100644 (file)
index 0000000..3a1fd2f
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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.impl.trans.w.util;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.impl.trans.util.Context;
+import io.fd.honeycomb.v3po.impl.trans.w.WriteContext;
+import java.util.Collections;
+import java.util.List;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class TransactionWriteContext implements WriteContext, AutoCloseable {
+
+    private final ReadOnlyTransaction beforeTx;
+    private final ReadOnlyTransaction afterTx;
+    private final Context ctx;
+
+    public TransactionWriteContext(final ReadOnlyTransaction beforeTx, final ReadOnlyTransaction afterTx,
+                                   final Context ctx) {
+        super();
+        this.beforeTx = beforeTx;
+        this.afterTx = afterTx;
+        this.ctx = ctx;
+    }
+
+    public TransactionWriteContext(final ReadOnlyTransaction beforeTx,
+                                   final ReadOnlyTransaction afterTx) {
+        this(beforeTx, afterTx, new Context());
+    }
+
+    @Override
+    public List<? extends DataObject> readBefore(final InstanceIdentifier<? extends DataObject> currentId) {
+        return read(currentId, beforeTx);
+    }
+
+    private List<? extends DataObject> read(final InstanceIdentifier<? extends DataObject> currentId,
+                                            final ReadOnlyTransaction tx) {
+        // FIXME how to read all for list (using wildcarded ID) ?
+
+        final CheckedFuture<? extends Optional<? extends DataObject>, ReadFailedException> read =
+            tx.read(LogicalDatastoreType.CONFIGURATION, currentId);
+        try {
+            final Optional<? extends DataObject> optional = read.checkedGet();
+            return optional.isPresent()
+                ? Collections.singletonList(optional.get())
+                : Collections.<DataObject>emptyList();
+        } catch (ReadFailedException e) {
+            throw new IllegalStateException("Unable to perform read", e);
+        }
+    }
+
+    @Override
+    public List<? extends DataObject> readAfter(final InstanceIdentifier<? extends DataObject> currentId) {
+        return read(currentId, afterTx);
+    }
+
+    @Override
+    public Context getContext() {
+        return ctx;
+    }
+
+    /**
+     * Does not close the transactions
+     */
+    @Override
+    public void close() throws Exception {
+        ctx.close();
+    }
+}