f5d218f553feea282052fb8beb8f57f551339015
[hc2vpp.git] /
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
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:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package io.fd.honeycomb.v3po.translate.util.write.registry;
18
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;
28 import java.util.Map;
29 import java.util.Set;
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;
38
39 /**
40  * Builder for {@link FlatWriterRegistry} allowing users to specify inter-writer relationships.
41  */
42 @NotThreadSafe
43 public final class FlatWriterRegistryBuilder implements ModifiableWriterRegistry, WriterRegistryBuilder, AutoCloseable {
44
45     private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistryBuilder.class);
46
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<>();
51
52     /**
53      * AddWriter without any special relationship to any other type.
54      */
55     @Override
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);
64         return this;
65     }
66
67     /**
68      * AddWriter without any special relationship to any other type.
69      */
70     @Override
71     public FlatWriterRegistryBuilder addSubtreeWriter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
72                                                       @Nonnull final Writer<? extends DataObject> writer) {
73         addWriter(SubtreeWriter.createForWriter(handledChildren, writer));
74         return this;
75     }
76
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));
80     }
81
82     /**
83      * Add writer with relationship: to be executed before writer handling relatedType.
84      */
85     @Override
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);
95         return this;
96     }
97
98     @Override
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);
103     }
104
105     @Override
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);
118         return this;
119     }
120
121     @Override
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);
126     }
127
128     /**
129      * Add writer with relationship: to be executed after writer handling relatedType.
130      */
131     @Override
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);
142         return this;
143     }
144
145     @Override
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);
150     }
151
152     @Override
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);
166         return this;
167     }
168
169     @Override
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);
174     }
175
176
177     private void addEdge(final InstanceIdentifier<?> targetType,
178                          final InstanceIdentifier<?> relatedType) {
179         try {
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);
184         }
185     }
186
187     /**
188      * Create FlatWriterRegistry with writers ordered according to submitted relationships.
189      */
190     @Override
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);
200     }
201
202     @VisibleForTesting
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));
211                     }
212                 });
213         return builder.build();
214     }
215
216     @Override
217     public void close() throws Exception {
218         writersMap.clear();
219         writersRelations.removeAllEdges(writersRelations.edgeSet());
220         writersRelations.removeAllVertices(writersRelations.vertexSet());
221     }
222
223     // Represents edges in graph
224     private static final class WriterRelation {}
225 }