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.write.registry;
19 import com.google.common.annotations.VisibleForTesting;
20 import com.google.common.base.Preconditions;
21 import com.google.common.collect.ImmutableMap;
22 import io.fd.honeycomb.v3po.translate.util.RWUtils;
23 import io.fd.honeycomb.v3po.translate.write.ModifiableWriterRegistry;
24 import io.fd.honeycomb.v3po.translate.write.Writer;
25 import io.fd.honeycomb.v3po.translate.write.WriterRegistryBuilder;
26 import java.util.Collection;
27 import java.util.HashMap;
30 import java.util.stream.Collectors;
31 import javax.annotation.Nonnull;
32 import javax.annotation.concurrent.NotThreadSafe;
33 import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
34 import org.opendaylight.yangtools.yang.binding.DataObject;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * Builder for {@link FlatWriterRegistry} allowing users to specify inter-writer relationships.
43 public final class FlatWriterRegistryBuilder implements ModifiableWriterRegistry, WriterRegistryBuilder, AutoCloseable {
45 private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistryBuilder.class);
47 // Using directed acyclic graph to represent the ordering relationships between writers
48 private final DirectedAcyclicGraph<InstanceIdentifier<?>, WriterRelation>
49 writersRelations = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new WriterRelation());
50 private final Map<InstanceIdentifier<?>, Writer<?>> writersMap = new HashMap<>();
53 * AddWriter without any special relationship to any other type.
56 public FlatWriterRegistryBuilder addWriter(@Nonnull final Writer<? extends DataObject> writer) {
57 // Make IID wildcarded just in case
58 // + the way InstanceIdentifier.create + equals work for Identifiable items is unexpected, meaning updates would
59 // not be matched to writers in registry
60 final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
61 checkWriterNotPresentYet(targetType);
62 writersRelations.addVertex(targetType);
63 writersMap.put(targetType, writer);
68 * AddWriter without any special relationship to any other type.
71 public FlatWriterRegistryBuilder addSubtreeWriter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
72 @Nonnull final Writer<? extends DataObject> writer) {
73 addWriter(SubtreeWriter.createForWriter(handledChildren, writer));
77 private void checkWriterNotPresentYet(final InstanceIdentifier<?> targetType) {
78 Preconditions.checkArgument(!writersMap.containsKey(targetType),
79 "Writer for type: %s already present: %s", targetType, writersMap.get(targetType));
83 * Add writer with relationship: to be executed before writer handling relatedType.
86 public FlatWriterRegistryBuilder addWriterBefore(@Nonnull final Writer<? extends DataObject> writer,
87 @Nonnull final InstanceIdentifier<?> relatedType) {
88 final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
89 final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
90 checkWriterNotPresentYet(targetType);
91 writersRelations.addVertex(targetType);
92 writersRelations.addVertex(wildcardedRelatedType);
93 addEdge(targetType, wildcardedRelatedType);
94 writersMap.put(targetType, writer);
99 public FlatWriterRegistryBuilder addSubtreeWriterBefore(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
100 @Nonnull final Writer<? extends DataObject> writer,
101 @Nonnull final InstanceIdentifier<?> relatedType) {
102 return addWriterBefore(SubtreeWriter.createForWriter(handledChildren, writer), relatedType);
106 public FlatWriterRegistryBuilder addWriterBefore(@Nonnull final Writer<? extends DataObject> writer,
107 @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
108 final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
109 checkWriterNotPresentYet(targetType);
110 writersRelations.addVertex(targetType);
111 relatedTypes.stream()
112 .map(RWUtils::makeIidWildcarded)
113 .forEach(writersRelations::addVertex);
114 relatedTypes.stream()
115 .map(RWUtils::makeIidWildcarded)
116 .forEach(type -> addEdge(targetType, type));
117 writersMap.put(targetType, writer);
122 public FlatWriterRegistryBuilder addSubtreeWriterBefore(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
123 @Nonnull final Writer<? extends DataObject> writer,
124 @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
125 return addWriterBefore(SubtreeWriter.createForWriter(handledChildren, writer), relatedTypes);
129 * Add writer with relationship: to be executed after writer handling relatedType.
132 public FlatWriterRegistryBuilder addWriterAfter(@Nonnull final Writer<? extends DataObject> writer,
133 @Nonnull final InstanceIdentifier<?> relatedType) {
134 final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
135 final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
136 checkWriterNotPresentYet(targetType);
137 writersRelations.addVertex(targetType);
138 writersRelations.addVertex(wildcardedRelatedType);
139 // set edge to indicate before relationship, just reversed
140 addEdge(wildcardedRelatedType, targetType);
141 writersMap.put(targetType, writer);
146 public FlatWriterRegistryBuilder addSubtreeWriterAfter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
147 @Nonnull final Writer<? extends DataObject> writer,
148 @Nonnull final InstanceIdentifier<?> relatedType) {
149 return addWriterAfter(SubtreeWriter.createForWriter(handledChildren, writer), relatedType);
153 public FlatWriterRegistryBuilder addWriterAfter(@Nonnull final Writer<? extends DataObject> writer,
154 @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
155 final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType());
156 checkWriterNotPresentYet(targetType);
157 writersRelations.addVertex(targetType);
158 relatedTypes.stream()
159 .map(RWUtils::makeIidWildcarded)
160 .forEach(writersRelations::addVertex);
161 // set edge to indicate before relationship, just reversed
162 relatedTypes.stream()
163 .map(RWUtils::makeIidWildcarded)
164 .forEach(type -> addEdge(type, targetType));
165 writersMap.put(targetType, writer);
170 public FlatWriterRegistryBuilder addSubtreeWriterAfter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
171 @Nonnull final Writer<? extends DataObject> writer,
172 @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
173 return addWriterAfter(SubtreeWriter.createForWriter(handledChildren, writer), relatedTypes);
177 private void addEdge(final InstanceIdentifier<?> targetType,
178 final InstanceIdentifier<?> relatedType) {
180 writersRelations.addDagEdge(targetType, relatedType);
181 } catch (DirectedAcyclicGraph.CycleFoundException e) {
182 throw new IllegalArgumentException(String.format(
183 "Unable to add writer with relation: %s -> %s. Loop detected", targetType, relatedType), e);
188 * Create FlatWriterRegistry with writers ordered according to submitted relationships.
191 public FlatWriterRegistry build() {
192 final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters = getMappedWriters();
193 LOG.debug("Building writer registry with writers: {}",
194 mappedWriters.keySet().stream()
195 .map(InstanceIdentifier::getTargetType)
196 .map(Class::getSimpleName)
197 .collect(Collectors.joining(", ")));
198 LOG.trace("Building writer registry with writers: {}", mappedWriters);
199 return new FlatWriterRegistry(mappedWriters);
203 ImmutableMap<InstanceIdentifier<?>, Writer<?>> getMappedWriters() {
204 final ImmutableMap.Builder<InstanceIdentifier<?>, Writer<?>> builder = ImmutableMap.builder();
205 // Iterate writer types according to their relationships from graph
206 writersRelations.iterator()
207 .forEachRemaining(writerType -> {
208 // There might be types stored just for relationship sake, no real writer, ignoring those
209 if (writersMap.containsKey(writerType)) {
210 builder.put(writerType, writersMap.get(writerType));
213 return builder.build();
217 public void close() throws Exception {
219 writersRelations.removeAllEdges(writersRelations.edgeSet());
220 writersRelations.removeAllVertices(writersRelations.vertexSet());
223 // Represents edges in graph
224 private static final class WriterRelation {}