From e1743c8eccee7d5ea8ad2c247d2575e8fd219fe4 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Tue, 12 Apr 2016 10:13:18 +0200 Subject: HONEYCOMB-9: Remove references to VPP from translation layer Change-Id: I281db366a112edc08203e8cb392a212708d4552a Signed-off-by: Maros Marsalek --- .../impl/read/AbstractCompositeReader.java | 232 +++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/AbstractCompositeReader.java (limited to 'v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/AbstractCompositeReader.java') diff --git a/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/AbstractCompositeReader.java b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/AbstractCompositeReader.java new file mode 100644 index 000000000..f9fa0eeaa --- /dev/null +++ b/v3po/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/AbstractCompositeReader.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.translate.impl.read; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.Beta; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import io.fd.honeycomb.v3po.translate.util.ReflectionUtils; +import io.fd.honeycomb.v3po.translate.util.RWUtils; +import io.fd.honeycomb.v3po.translate.read.ChildReader; +import io.fd.honeycomb.v3po.translate.read.ReadContext; +import io.fd.honeycomb.v3po.translate.read.ReadFailedException; +import io.fd.honeycomb.v3po.translate.read.Reader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.concepts.Builder; +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.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Beta +abstract class AbstractCompositeReader> implements Reader { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractCompositeReader.class); + + private final Map, ChildReader>> childReaders; + private final Map, ChildReader>> augReaders; + private final InstanceIdentifier instanceIdentifier; + + AbstractCompositeReader(final Class managedDataObjectType, + final List>> childReaders, + final List>> augReaders) { + this.childReaders = RWUtils.uniqueLinkedIndex(childReaders, RWUtils.MANAGER_CLASS_FUNCTION); + this.augReaders = RWUtils.uniqueLinkedIndex(augReaders, RWUtils.MANAGER_CLASS_AUG_FUNCTION); + this.instanceIdentifier = InstanceIdentifier.create(managedDataObjectType); + } + + @Nonnull + @Override + public final InstanceIdentifier getManagedDataObjectType() { + return instanceIdentifier; + } + + /** + * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. + * + */ + protected Optional readCurrent(final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + LOG.debug("{}: Reading current: {}", this, id); + final B builder = getBuilder(id); + // Cache empty value to determine if anything has changed later TODO cache in a field + final D emptyValue = builder.build(); + + LOG.trace("{}: Reading current attributes", this); + readCurrentAttributes(id, builder, ctx); + + // TODO expect exceptions from reader + for (ChildReader> child : childReaders.values()) { + LOG.debug("{}: Reading child from: {}", this, child); + child.read(id, builder, ctx); + } + + for (ChildReader> child : augReaders.values()) { + LOG.debug("{}: Reading augment from: {}", this, child); + child.read(id, builder, ctx); + } + + // Need to check whether anything was filled in to determine if data is present or not. + final D built = builder.build(); + final Optional read = built.equals(emptyValue) + ? Optional.absent() + : Optional.of(built); + + LOG.debug("{}: Current node read successfully. Result: {}", this, read); + return read; + } + + @Nonnull + @Override + @SuppressWarnings("unchecked") + public Optional read(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + LOG.trace("{}: Reading : {}", this, id); + if (id.getTargetType().equals(getManagedDataObjectType().getTargetType())) { + return readCurrent((InstanceIdentifier) id, ctx); + } else { + return readSubtree(id, ctx); + } + } + + private Optional readSubtree(final InstanceIdentifier id, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + LOG.debug("{}: Reading subtree: {}", this, id); + final Class next = RWUtils.getNextId(id, getManagedDataObjectType()).getType(); + final ChildReader> reader = childReaders.get(next); + + if (reader != null) { + LOG.debug("{}: Reading subtree: {} from: {}", this, id, reader); + return reader.read(id, ctx); + } else { + LOG.debug("{}: Dedicated subtree reader missing for: {}. Reading current and filtering", this, next); + // If there's no dedicated reader, use read current + final InstanceIdentifier currentId = RWUtils.cutId(id, getManagedDataObjectType()); + final Optional current = readCurrent(currentId, ctx); + // then perform post-reading filtering (return only requested sub-node) + final Optional readSubtree = current.isPresent() + ? filterSubtree(current.get(), id, getManagedDataObjectType().getTargetType()) + : current; + + LOG.debug("{}: Subtree: {} read successfully. Result: {}", this, id, readSubtree); + return readSubtree; + } + } + + /** + * Fill in current node's attributes + * + * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. + * @param builder Builder object for current node where the read attributes must be placed + * @param ctx Current read context + */ + protected abstract void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, + @Nonnull final ReadContext ctx) throws ReadFailedException; + + /** + * Return new instance of a builder object for current node + * + * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. + * @return Builder object for current node type + */ + protected abstract B getBuilder(InstanceIdentifier id); + + // TODO move filtering out of here into a dedicated Filter ifc + @Nonnull + private static Optional filterSubtree(@Nonnull final DataObject parent, + @Nonnull final InstanceIdentifier absolutPath, + @Nonnull final Class managedType) { + // TODO is there a better way than reflection ? e.g. convert into NN and filter out with a utility + // FIXME this needs to be recursive. right now it expects only 1 additional element in ID + test + + final InstanceIdentifier.PathArgument nextId = + RWUtils.getNextId(absolutPath, InstanceIdentifier.create(parent.getClass())); + + Optional method = ReflectionUtils.findMethodReflex(managedType, "get", + Collections.>emptyList(), nextId.getType()); + + if (method.isPresent()) { + return Optional.fromNullable(filterSingle(parent, nextId, method.get())); + } else { + // List child nodes + method = ReflectionUtils.findMethodReflex(managedType, + "get" + nextId.getType().getSimpleName(), Collections.>emptyList(), List.class); + + if (method.isPresent()) { + return filterList(parent, nextId, method.get()); + } else { + throw new IllegalStateException( + "Unable to filter " + nextId + " from " + parent + " getters not found using reflexion"); + } + } + } + + @SuppressWarnings("unchecked") + private static Optional filterList(final DataObject parent, + final InstanceIdentifier.PathArgument nextId, + final Method method) { + final List invoke = (List) invoke(method, nextId, parent); + + checkArgument(nextId instanceof InstanceIdentifier.IdentifiableItem, + "Unable to perform wildcarded read for %s", nextId); + final Identifier key = ((InstanceIdentifier.IdentifiableItem) nextId).getKey(); + return Iterables.tryFind(invoke, new Predicate() { + @Override + public boolean apply(@Nullable final DataObject input) { + final Optional keyGetter = + ReflectionUtils.findMethodReflex(nextId.getType(), "get", + Collections.>emptyList(), key.getClass()); + final Object actualKey; + actualKey = invoke(keyGetter.get(), nextId, input); + return key.equals(actualKey); + } + }); + } + + private static DataObject filterSingle(final DataObject parent, + final InstanceIdentifier.PathArgument nextId, final Method method) { + return nextId.getType().cast(invoke(method, nextId, parent)); + } + + private static Object invoke(final Method method, + final InstanceIdentifier.PathArgument nextId, final DataObject parent) { + try { + return method.invoke(parent); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalArgumentException("Unable to get " + nextId + " from " + parent, e); + } + } + + @Override + public String toString() { + return String.format("Reader[%s]", getManagedDataObjectType().getTargetType().getSimpleName()); + } +} -- cgit 1.2.3-korg