/* * 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.InitFailedException; import io.fd.honeycomb.translate.read.InitListReader; import io.fd.honeycomb.translate.read.Initializer; 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.read.AbstractGenericReader; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.binding.api.DataBroker; 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 implements Initializer { 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); // Dont set empty lists if (!list.isEmpty()) { ((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); } @SuppressWarnings("unchecked") @Override public void init(final DataBroker broker, final InstanceIdentifier id, final ReadContext ctx) throws InitFailedException { if (delegate instanceof Initializer) { LOG.trace("{}: Initializing current: {}", this, id); ((Initializer) delegate).init(broker, id, ctx); } for (Reader child : childReaders.values()) { final InstanceIdentifier childId = appendTypeToId(id, child.getManagedDataObjectType()); if (child instanceof Initializer) { LOG.trace("{}: Initializing child: {}", this, childId); ((Initializer) child).init(broker, childId, ctx); } } } private static class CompositeListReader, B extends Builder, K extends Identifier> extends CompositeReader implements InitListReader { 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 init(final DataBroker broker, final InstanceIdentifier id, final ReadContext ctx) throws InitFailedException { try { final List allIds = delegate.getAllIds(id, ctx); for (K key : allIds) { super.init(broker, RWUtils.replaceLastInId(id, RWUtils.getCurrentIdItem(id, key)), ctx); } } catch (ReadFailedException e) { throw new InitFailedException(id, e); } } @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); } } }