2 * Copyright (c) 2016 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package io.fd.honeycomb.v3po.translate.util.read.registry;
19 import static com.google.common.base.Preconditions.checkArgument;
21 import com.google.common.base.Optional;
22 import com.google.common.base.Predicate;
23 import com.google.common.collect.Iterables;
24 import io.fd.honeycomb.v3po.translate.read.ListReader;
25 import io.fd.honeycomb.v3po.translate.read.ReadContext;
26 import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
27 import io.fd.honeycomb.v3po.translate.read.Reader;
28 import io.fd.honeycomb.v3po.translate.util.RWUtils;
29 import io.fd.honeycomb.v3po.translate.util.ReflectionUtils;
30 import java.lang.reflect.InvocationTargetException;
31 import java.lang.reflect.Method;
32 import java.util.Collections;
33 import java.util.HashSet;
34 import java.util.List;
36 import javax.annotation.Nonnull;
37 import javax.annotation.Nullable;
38 import org.opendaylight.yangtools.concepts.Builder;
39 import org.opendaylight.yangtools.yang.binding.DataObject;
40 import org.opendaylight.yangtools.yang.binding.Identifiable;
41 import org.opendaylight.yangtools.yang.binding.Identifier;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * Simple Reader delegate for subtree Readers (Readers handling also children nodes) providing a list of all the
48 * children nodes being handled.
50 class SubtreeReader<D extends DataObject, B extends Builder<D>> implements Reader<D, B> {
52 private static final Logger LOG = LoggerFactory.getLogger(SubtreeReader.class);
54 private final Reader<D, B> delegate;
55 private final Set<InstanceIdentifier<?>> handledChildTypes = new HashSet<>();
57 private SubtreeReader(final Reader<D, B> delegate, Set<InstanceIdentifier<?>> handledTypes) {
58 this.delegate = delegate;
59 for (InstanceIdentifier<?> handledType : handledTypes) {
60 // Iid has to start with Reader's handled root type
61 checkArgument(delegate.getManagedDataObjectType().getTargetType().equals(
62 handledType.getPathArguments().iterator().next().getType()),
63 "Handled node from subtree has to be identified by an instance identifier starting from: %s."
64 + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType);
65 checkArgument(Iterables.size(handledType.getPathArguments()) > 1,
66 "Handled node from subtree identifier too short: %s", handledType);
67 handledChildTypes.add(InstanceIdentifier.create(Iterables.concat(
68 getManagedDataObjectType().getPathArguments(), Iterables.skip(handledType.getPathArguments(), 1))));
73 * Return set of types also handled by this Reader. All of the types are children of the type managed by this Reader
74 * excluding the type of this Reader.
76 Set<InstanceIdentifier<?>> getHandledChildTypes() {
77 return handledChildTypes;
82 public Optional<? extends DataObject> read(
83 @Nonnull final InstanceIdentifier<? extends DataObject> id,
84 @Nonnull final ReadContext ctx) throws ReadFailedException {
85 final InstanceIdentifier<?> wildcarded = RWUtils.makeIidWildcarded(id);
87 // Reading entire subtree and filtering if is current reader responsible
88 if (getHandledChildTypes().contains(wildcarded)) {
89 LOG.debug("{}: Subtree node managed by this writer requested: {}. Reading current and filtering", this, id);
90 // If there's no dedicated reader, use read current
91 final InstanceIdentifier<D> currentId = RWUtils.cutId(id, getManagedDataObjectType());
92 final Optional<? extends DataObject> current = read(currentId, ctx);
93 // then perform post-reading filtering (return only requested sub-node)
94 final Optional<? extends DataObject> readSubtree = current.isPresent()
95 ? filterSubtree(current.get(), id, getManagedDataObjectType().getTargetType())
98 LOG.debug("{}: Subtree: {} read successfully. Result: {}", this, id, readSubtree);
101 // Fallback solution, try delegate, maybe it can read the ID
103 return delegate.read(id, ctx);
108 public void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final B builder,
109 @Nonnull final ReadContext ctx)
110 throws ReadFailedException {
111 delegate.readCurrentAttributes(id, builder, ctx);
116 public B getBuilder(final InstanceIdentifier<D> id) {
117 return delegate.getBuilder(id);
121 public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final D readValue) {
122 delegate.merge(parentBuilder, readValue);
126 private static Optional<? extends DataObject> filterSubtree(@Nonnull final DataObject parent,
127 @Nonnull final InstanceIdentifier<? extends DataObject> absolutPath,
128 @Nonnull final Class<?> managedType) {
129 final InstanceIdentifier.PathArgument nextId =
130 RWUtils.getNextId(absolutPath, InstanceIdentifier.create(parent.getClass()));
132 final Optional<? extends DataObject> nextParent = findNextParent(parent, nextId, managedType);
134 if (Iterables.getLast(absolutPath.getPathArguments()).equals(nextId)) {
135 return nextParent; // we found the dataObject identified by absolutePath
136 } else if (nextParent.isPresent()) {
137 return filterSubtree(nextParent.get(), absolutPath, nextId.getType());
139 return nextParent; // we can't go further, return Optional.absent()
143 private static Optional<? extends DataObject> findNextParent(@Nonnull final DataObject parent,
144 @Nonnull final InstanceIdentifier.PathArgument nextId,
145 @Nonnull final Class<?> managedType) {
146 // TODO is there a better way than reflection ? e.g. convert into NN and filter out with a utility
147 Optional<Method> method = ReflectionUtils.findMethodReflex(managedType, "get",
148 Collections.emptyList(), nextId.getType());
150 if (method.isPresent()) {
151 return Optional.fromNullable(filterSingle(parent, nextId, method.get()));
154 method = ReflectionUtils.findMethodReflex(managedType,
155 "get" + nextId.getType().getSimpleName(), Collections.emptyList(), List.class);
157 if (method.isPresent()) {
158 return filterList(parent, nextId, method.get());
160 throw new IllegalStateException(
161 "Unable to filter " + nextId + " from " + parent + " getters not found using reflexion");
166 @SuppressWarnings("unchecked")
167 private static Optional<? extends DataObject> filterList(final DataObject parent,
168 final InstanceIdentifier.PathArgument nextId,
169 final Method method) {
170 final List<? extends DataObject> invoke = (List<? extends DataObject>) invoke(method, nextId, parent);
172 checkArgument(nextId instanceof InstanceIdentifier.IdentifiableItem<?, ?>,
173 "Unable to perform wildcarded read for %s", nextId);
174 final Identifier key = ((InstanceIdentifier.IdentifiableItem) nextId).getKey();
175 // TODO replace with stream().filter().findFirst() when we switch to using java's Optional instead of Guava's
176 // because now we would have to do awkward Optional transformation since findFirstReturns guava's optional
177 return Iterables.tryFind(invoke, new Predicate<DataObject>() {
180 public boolean apply(@Nullable final DataObject input) {
181 final Optional<Method> keyGetter = ReflectionUtils.findMethodReflex(nextId.getType(), "get",
182 Collections.emptyList(), key.getClass());
183 final Object actualKey;
184 actualKey = invoke(keyGetter.get(), nextId, input);
185 return key.equals(actualKey);
190 private static DataObject filterSingle(final DataObject parent,
191 final InstanceIdentifier.PathArgument nextId, final Method method) {
192 return nextId.getType().cast(invoke(method, nextId, parent));
195 private static Object invoke(final Method method,
196 final InstanceIdentifier.PathArgument nextId, final DataObject parent) {
198 return method.invoke(parent);
199 } catch (IllegalAccessException | InvocationTargetException e) {
200 throw new IllegalArgumentException("Unable to get " + nextId + " from " + parent, e);
206 public InstanceIdentifier<D> getManagedDataObjectType() {
207 return delegate.getManagedDataObjectType();
211 * Wrap a Reader as a subtree Reader.
213 static <D extends DataObject, B extends Builder<D>> Reader<D, B> createForReader(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
214 @Nonnull final Reader<D, B> reader) {
215 return (reader instanceof ListReader)
216 ? new SubtreeListReader<>((ListReader) reader, handledChildren)
217 : new SubtreeReader<>(reader, handledChildren);
220 private static final class SubtreeListReader<D extends DataObject & Identifiable<K>, B extends Builder<D>, K extends Identifier<D>>
221 extends SubtreeReader<D, B> implements ListReader<D, K, B> {
223 private final ListReader<D, K, B> delegate;
225 private SubtreeListReader(final ListReader<D, K, B> delegate,
226 final Set<InstanceIdentifier<?>> handledTypes) {
227 super(delegate, handledTypes);
228 this.delegate = delegate;
233 public List<D> readList(@Nonnull final InstanceIdentifier<D> id, @Nonnull final ReadContext ctx)
234 throws ReadFailedException {
235 return delegate.readList(id, ctx);
239 public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<D> readData) {
240 delegate.merge(builder, readData);
244 public List<K> getAllIds(@Nonnull final InstanceIdentifier<D> id,
245 @Nonnull final ReadContext ctx) throws ReadFailedException {
246 return delegate.getAllIds(id, ctx);