From e3c31cee916480b2d9d169c1f5afb1c42efaabe1 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Fri, 29 Jul 2016 16:27:12 +0200 Subject: HONEYCOMB-130: Rename infra packages(remove vpp/v3po) Change-Id: Ic5b90e397e3743623d01b206bc60bc5c7df6b981 Signed-off-by: Maros Marsalek --- .../util/read/registry/CompositeReader.java | 202 +++++++++++++++++ .../read/registry/CompositeReaderRegistry.java | 117 ++++++++++ .../registry/CompositeReaderRegistryBuilder.java | 109 +++++++++ .../util/read/registry/SubtreeReader.java | 250 +++++++++++++++++++++ .../util/read/registry/TypeHierarchy.java | 101 +++++++++ 5 files changed, 779 insertions(+) create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReader.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistry.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilder.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReader.java create mode 100644 infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchy.java (limited to 'infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry') diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReader.java new file mode 100644 index 000000000..91a195f44 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReader.java @@ -0,0 +1,202 @@ +/* + * 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.translate.util.read.registry; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import io.fd.honeycomb.translate.read.ListReader; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.util.read.AbstractGenericReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class CompositeReader> extends AbstractGenericReader { + + private static final Logger LOG = LoggerFactory.getLogger(CompositeReader.class); + + private final Reader delegate; + private final ImmutableMap, Reader>> childReaders; + + private CompositeReader(final Reader reader, + final ImmutableMap, Reader>> childReaders) { + super(reader.getManagedDataObjectType()); + this.delegate = reader; + this.childReaders = childReaders; + } + + @VisibleForTesting + ImmutableMap, Reader>> getChildReaders() { + return childReaders; + } + + @SuppressWarnings("unchecked") + public static InstanceIdentifier appendTypeToId( + final InstanceIdentifier parentId, final InstanceIdentifier type) { + final InstanceIdentifier.PathArgument t = new InstanceIdentifier.Item<>(type.getTargetType()); + return (InstanceIdentifier) InstanceIdentifier.create(Iterables.concat( + parentId.getPathArguments(), Collections.singleton(t))); + } + + @Nonnull + @Override + public Optional read(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + if (shouldReadCurrent(id)) { + LOG.trace("{}: Reading current: {}", this, id); + return readCurrent((InstanceIdentifier) id, ctx); + } else if (shouldDelegateToChild(id)) { + LOG.trace("{}: Reading child: {}", this, id); + return readSubtree(id, ctx); + } else { + // Fallback + LOG.trace("{}: Delegating read: {}", this, id); + return delegate.read(id, ctx); + } + } + + private boolean shouldReadCurrent(@Nonnull final InstanceIdentifier id) { + return id.getTargetType().equals(getManagedDataObjectType().getTargetType()); + } + + private boolean shouldDelegateToChild(@Nonnull final InstanceIdentifier id) { + return childReaders.containsKey(RWUtils.getNextId(id, getManagedDataObjectType()).getType()); + } + + private Optional readSubtree(final InstanceIdentifier id, + final ReadContext ctx) throws ReadFailedException { + final InstanceIdentifier.PathArgument nextId = RWUtils.getNextId(id, getManagedDataObjectType()); + final Reader> nextReader = childReaders.get(nextId.getType()); + checkArgument(nextReader != null, "Unable to read: %s. No delegate present, available readers at next level: %s", + id, childReaders.keySet()); + return nextReader.read(id, ctx); + } + + @SuppressWarnings("unchecked") + private void readChildren(final InstanceIdentifier id, @Nonnull final ReadContext ctx, final B builder) + throws ReadFailedException { + LOG.debug("{}: Reading children: {}", this, childReaders.keySet()); + for (Reader child : childReaders.values()) { + final InstanceIdentifier childId = appendTypeToId(id, child.getManagedDataObjectType()); + + LOG.debug("{}: Reading child from: {}", this, child); + if (child instanceof ListReader) { + final List list = ((ListReader) child).readList(childId, ctx); + ((ListReader) child).merge(builder, list); + } else { + final Optional read = child.read(childId, ctx); + if (read.isPresent()) { + child.merge(builder, read.get()); + } + } + } + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + delegate.readCurrentAttributes(id, builder, ctx); + readChildren(id, ctx, builder); + } + + @Nonnull + @Override + public B getBuilder(final InstanceIdentifier id) { + return delegate.getBuilder(id); + } + + @Override + public void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue) { + delegate.merge(parentBuilder, readValue); + } + + /** + * Wrap a Reader as a Composite Reader. + */ + static > Reader createForReader( + @Nonnull final Reader reader, + @Nonnull final ImmutableMap, Reader>> childReaders) { + + return (reader instanceof ListReader) + ? new CompositeListReader<>((ListReader) reader, childReaders) + : new CompositeReader<>(reader, childReaders); + } + + private static class CompositeListReader, B extends Builder, K extends Identifier> + extends CompositeReader + implements ListReader { + + private final ListReader delegate; + + private CompositeListReader(final ListReader reader, + final ImmutableMap, Reader>> childReaders) { + super(reader, childReaders); + this.delegate = reader; + } + + @Nonnull + @Override + public List readList(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext ctx) + throws ReadFailedException { + LOG.trace("{}: Reading all list entries", this); + final List allIds = delegate.getAllIds(id, ctx); + LOG.debug("{}: Reading list entries for: {}", this, allIds); + + // Override read list in order to perform readCurrent + readChildren here + final ArrayList allEntries = new ArrayList<>(allIds.size()); + for (K key : allIds) { + final InstanceIdentifier.IdentifiableItem currentBdItem = RWUtils.getCurrentIdItem(id, key); + final InstanceIdentifier keyedId = RWUtils.replaceLastInId(id, currentBdItem); + final Optional read = readCurrent(keyedId, ctx); + if (read.isPresent()) { + final DataObject singleItem = read.get(); + checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(singleItem.getClass())); + allEntries.add(getManagedDataObjectType().getTargetType().cast(singleItem)); + } + } + return allEntries; + } + + @Override + public void merge(@Nonnull final Builder builder, @Nonnull final List readData) { + delegate.merge(builder, readData); + } + + @Override + public List getAllIds(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + return delegate.getAllIds(id, ctx); + } + } + +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistry.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistry.java new file mode 100644 index 000000000..ec3ce0605 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistry.java @@ -0,0 +1,117 @@ +/* + * 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.translate.util.read.registry; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.Multimap; +import io.fd.honeycomb.translate.read.ListReader; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.read.registry.ReaderRegistry; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 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 CompositeReaderRegistry implements ReaderRegistry { + + private static final Logger LOG = LoggerFactory.getLogger(CompositeReaderRegistry.class); + + private final Map, Reader>> rootReaders; + + /** + * Create new {@link CompositeReaderRegistry}. + * + * @param rootReaders List of delegate readers + */ + public CompositeReaderRegistry(@Nonnull final List>> rootReaders) { + this.rootReaders = RWUtils.uniqueLinkedIndex(checkNotNull(rootReaders), RWUtils.MANAGER_CLASS_FUNCTION); + } + + @VisibleForTesting + Map, Reader>> getRootReaders() { + return rootReaders; + } + + @Override + @Nonnull + public Multimap, ? extends DataObject> readAll( + @Nonnull final ReadContext ctx) throws ReadFailedException { + + LOG.debug("Reading from all delegates: {}", this); + LOG.trace("Reading from all delegates: {}", rootReaders.values()); + + final Multimap, DataObject> objects = LinkedListMultimap.create(); + for (Reader> rootReader : rootReaders.values()) { + LOG.debug("Reading from delegate: {}", rootReader); + + if (rootReader instanceof ListReader) { + final List listEntries = + ((ListReader) rootReader).readList(rootReader.getManagedDataObjectType(), ctx); + if (!listEntries.isEmpty()) { + objects.putAll(rootReader.getManagedDataObjectType(), listEntries); + } + } else { + final Optional read = rootReader.read(rootReader.getManagedDataObjectType(), ctx); + if (read.isPresent()) { + objects.putAll(rootReader.getManagedDataObjectType(), Collections.singletonList(read.get())); + } + } + } + + return objects; + } + + @Nonnull + @Override + public Optional read(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + final InstanceIdentifier.PathArgument first = checkNotNull( + Iterables.getFirst(id.getPathArguments(), null), "Empty id"); + final Reader> reader = rootReaders.get(first.getType()); + checkNotNull(reader, + "Unable to read %s. Missing reader. Current readers for: %s", id, rootReaders.keySet()); + LOG.debug("Reading from delegate: {}", reader); + return reader.read(id, ctx); + } + + @Override + public String toString() { + return getClass().getSimpleName() + + rootReaders.keySet().stream().map(Class::getSimpleName).collect(Collectors.toList()); + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilder.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilder.java new file mode 100644 index 000000000..3e21bfca3 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/CompositeReaderRegistryBuilder.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read.registry; + +import com.google.common.collect.ImmutableMap; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.honeycomb.translate.read.registry.ReaderRegistryBuilder; +import io.fd.honeycomb.translate.util.read.ReflexiveReader; +import io.fd.honeycomb.translate.read.registry.ReaderRegistry; +import io.fd.honeycomb.translate.util.AbstractSubtreeManagerRegistryBuilderBuilder; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@NotThreadSafe +public final class CompositeReaderRegistryBuilder + extends AbstractSubtreeManagerRegistryBuilderBuilder>, ReaderRegistry> + implements ModifiableReaderRegistryBuilder, ReaderRegistryBuilder { + + private static final Logger LOG = LoggerFactory.getLogger(CompositeReaderRegistryBuilder.class); + + @Override + protected Reader> getSubtreeHandler(@Nonnull final Set> handledChildren, + @Nonnull final Reader> reader) { + return SubtreeReader.createForReader(handledChildren, reader); + } + + @Override + public void addStructuralReader(@Nonnull InstanceIdentifier id, + @Nonnull Class> builderType) { + add(new ReflexiveReader<>(id, builderType)); + } + + /** + * Create {@link CompositeReaderRegistry} with Readers ordered according to submitted relationships. + *

+ * Note: The ordering only applies between nodes on the same level, inter-level and inter-subtree relationships are + * ignored. + */ + @Override + public ReaderRegistry build() { + ImmutableMap, Reader>> mappedReaders = + getMappedHandlers(); + LOG.debug("Building Reader registry with Readers: {}", + mappedReaders.keySet().stream() + .map(InstanceIdentifier::getTargetType) + .map(Class::getSimpleName) + .collect(Collectors.joining(", "))); + + LOG.trace("Building Reader registry with Readers: {}", mappedReaders); + final List> readerOrder = new ArrayList<>(mappedReaders.keySet()); + + // Wrap readers into composite readers recursively, collect roots and create registry + final TypeHierarchy typeHierarchy = TypeHierarchy.create(mappedReaders.keySet()); + final List>> orderedRootReaders = + typeHierarchy.getRoots().stream() + .map(rootId -> toCompositeReader(rootId, mappedReaders, typeHierarchy)) + .collect(Collectors.toList()); + + // We are violating the ordering from mappedReaders, since we are forming a composite structure + // but at least order root writers + orderedRootReaders.sort((reader1, reader2) -> readerOrder.indexOf(reader1.getManagedDataObjectType()) + - readerOrder.indexOf(reader2.getManagedDataObjectType())); + + return new CompositeReaderRegistry(orderedRootReaders); + } + + private Reader> toCompositeReader( + final InstanceIdentifier instanceIdentifier, + final ImmutableMap, Reader>> mappedReaders, + final TypeHierarchy typeHierarchy) { + + // Order child readers according to the mappedReadersCollection + final ImmutableMap.Builder, Reader>> childReadersMapB = ImmutableMap.builder(); + for (InstanceIdentifier childId : mappedReaders.keySet()) { + if (typeHierarchy.getDirectChildren(instanceIdentifier).contains(childId)) { + childReadersMapB.put(childId.getTargetType(), toCompositeReader(childId, mappedReaders, typeHierarchy)); + } + } + + final ImmutableMap, Reader>> childReadersMap = childReadersMapB.build(); + return childReadersMap.isEmpty() + ? mappedReaders.get(instanceIdentifier) + : CompositeReader.createForReader(mappedReaders.get(instanceIdentifier), childReadersMap); + } +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReader.java new file mode 100644 index 000000000..720bd0b11 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/SubtreeReader.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read.registry; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import io.fd.honeycomb.translate.read.ListReader; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.util.ReflectionUtils; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Simple Reader delegate for subtree Readers (Readers handling also children nodes) providing a list of all the + * children nodes being handled. + */ +class SubtreeReader> implements Reader { + + private static final Logger LOG = LoggerFactory.getLogger(SubtreeReader.class); + + private final Reader delegate; + private final Set> handledChildTypes = new HashSet<>(); + + private SubtreeReader(final Reader delegate, Set> handledTypes) { + this.delegate = delegate; + for (InstanceIdentifier handledType : handledTypes) { + // Iid has to start with Reader's handled root type + checkArgument(delegate.getManagedDataObjectType().getTargetType().equals( + handledType.getPathArguments().iterator().next().getType()), + "Handled node from subtree has to be identified by an instance identifier starting from: %s." + + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType); + checkArgument(Iterables.size(handledType.getPathArguments()) > 1, + "Handled node from subtree identifier too short: %s", handledType); + handledChildTypes.add(InstanceIdentifier.create(Iterables.concat( + getManagedDataObjectType().getPathArguments(), Iterables.skip(handledType.getPathArguments(), 1)))); + } + } + + /** + * Return set of types also handled by this Reader. All of the types are children of the type managed by this Reader + * excluding the type of this Reader. + */ + Set> getHandledChildTypes() { + return handledChildTypes; + } + + @Override + @Nonnull + public Optional read( + @Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + final InstanceIdentifier wildcarded = RWUtils.makeIidWildcarded(id); + + // Reading entire subtree and filtering if is current reader responsible + if (getHandledChildTypes().contains(wildcarded)) { + LOG.debug("{}: Subtree node managed by this writer requested: {}. Reading current and filtering", this, id); + // If there's no dedicated reader, use read current + final InstanceIdentifier currentId = RWUtils.cutId(id, getManagedDataObjectType()); + final Optional current = delegate.read(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; + + // Fallback solution, try delegate, maybe it can read the ID + } else { + return delegate.read(id, ctx); + } + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, @Nonnull final B builder, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + delegate.readCurrentAttributes(id, builder, ctx); + } + + @Nonnull + @Override + public B getBuilder(final InstanceIdentifier id) { + return delegate.getBuilder(id); + } + + @Override + public void merge(@Nonnull final Builder parentBuilder, @Nonnull final D readValue) { + delegate.merge(parentBuilder, readValue); + } + + @Nonnull + private static Optional filterSubtree(@Nonnull final DataObject parent, + @Nonnull final InstanceIdentifier absolutPath, + @Nonnull final Class managedType) { + final InstanceIdentifier.PathArgument nextId = + RWUtils.getNextId(absolutPath, InstanceIdentifier.create(parent.getClass())); + + final Optional nextParent = findNextParent(parent, nextId, managedType); + + if (Iterables.getLast(absolutPath.getPathArguments()).equals(nextId)) { + return nextParent; // we found the dataObject identified by absolutePath + } else if (nextParent.isPresent()) { + return filterSubtree(nextParent.get(), absolutPath, nextId.getType()); + } else { + return nextParent; // we can't go further, return Optional.absent() + } + } + + private static Optional findNextParent(@Nonnull final DataObject parent, + @Nonnull final InstanceIdentifier.PathArgument nextId, + @Nonnull final Class managedType) { + // TODO is there a better way than reflection ? e.g. convert into NN and filter out with a utility + Optional method = 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(); + // TODO replace with stream().filter().findFirst() when we switch to using java's Optional instead of Guava's + // because now we would have to do awkward Optional transformation since findFirstReturns guava's optional + return Iterables.tryFind(invoke, new Predicate() { + + @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 + @Nonnull + public InstanceIdentifier getManagedDataObjectType() { + return delegate.getManagedDataObjectType(); + } + + /** + * Wrap a Reader as a subtree Reader. + */ + static > Reader createForReader(@Nonnull final Set> handledChildren, + @Nonnull final Reader reader) { + return (reader instanceof ListReader) + ? new SubtreeListReader<>((ListReader) reader, handledChildren) + : new SubtreeReader<>(reader, handledChildren); + } + + private static final class SubtreeListReader, B extends Builder, K extends Identifier> + extends SubtreeReader implements ListReader { + + private final ListReader delegate; + + private SubtreeListReader(final ListReader delegate, + final Set> handledTypes) { + super(delegate, handledTypes); + this.delegate = delegate; + } + + @Nonnull + @Override + public List readList(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext ctx) + throws ReadFailedException { + return delegate.readList(id, ctx); + } + + @Override + public void merge(@Nonnull final Builder builder, @Nonnull final List readData) { + delegate.merge(builder, readData); + } + + @Override + public List getAllIds(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + return delegate.getAllIds(id, ctx); + } + } + +} diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchy.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchy.java new file mode 100644 index 000000000..a30663221 --- /dev/null +++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/translate/util/read/registry/TypeHierarchy.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.util.read.registry; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.Iterables; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.jgrapht.experimental.dag.DirectedAcyclicGraph; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +final class TypeHierarchy { + private final DirectedAcyclicGraph, Parent> hierarchy; + + private TypeHierarchy(@Nonnull final DirectedAcyclicGraph, Parent> hierarchy) { + this.hierarchy = hierarchy; + } + + Set> getAllChildren(InstanceIdentifier id) { + final HashSet> instanceIdentifiers = new HashSet<>(); + for (InstanceIdentifier childId : getDirectChildren(id)) { + instanceIdentifiers.add(childId); + instanceIdentifiers.addAll(getAllChildren(childId)); + } + return instanceIdentifiers; + } + + Set> getDirectChildren(InstanceIdentifier id) { + checkArgument(hierarchy.vertexSet().contains(id), + "Unknown reader: %s. Known readers: %s", id, hierarchy.vertexSet()); + + return hierarchy.outgoingEdgesOf(id).stream() + .map(hierarchy::getEdgeTarget) + .collect(Collectors.toSet()); + } + + Set> getRoots() { + return hierarchy.vertexSet().stream() + .filter(vertex -> hierarchy.incomingEdgesOf(vertex).size() == 0) + .collect(Collectors.toSet()); + } + + /** + * Create reader hierarchy from a flat set of instance identifiers. + * + * @param allIds Set of unkeyed instance identifiers + */ + static TypeHierarchy create(@Nonnull Set> allIds) { + final DirectedAcyclicGraph, Parent> + readersHierarchy = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new Parent()); + + for (InstanceIdentifier allId : allIds) { + checkArgument(!Iterables.isEmpty(allId.getPathArguments()), "Empty ID detected"); + + if (Iterables.size(allId.getPathArguments()) == 1) { + readersHierarchy.addVertex(allId); + } + + List pathArgs = new LinkedList<>(); + pathArgs.add(allId.getPathArguments().iterator().next()); + + for (InstanceIdentifier.PathArgument pathArgument : Iterables.skip(allId.getPathArguments(), 1)) { + final InstanceIdentifier previous = InstanceIdentifier.create(pathArgs); + pathArgs.add(pathArgument); + final InstanceIdentifier current = InstanceIdentifier.create(pathArgs); + + readersHierarchy.addVertex(previous); + readersHierarchy.addVertex(current); + + try { + readersHierarchy.addDagEdge(previous, current); + } catch (DirectedAcyclicGraph.CycleFoundException e) { + throw new IllegalArgumentException("Loop in hierarchy detected", e); + } + } + } + + return new TypeHierarchy(readersHierarchy); + } + + private static final class Parent{} +} -- cgit 1.2.3-korg