23a66337fffffd2b7f2d4727108e0c7b14e21a40
[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;
18
19 import com.google.common.base.Preconditions;
20 import com.google.common.collect.ImmutableMap;
21 import com.google.common.collect.Sets;
22 import io.fd.honeycomb.v3po.translate.ModifiableSubtreeManagerRegistryBuilder;
23 import io.fd.honeycomb.v3po.translate.SubtreeManager;
24 import io.fd.honeycomb.v3po.translate.SubtreeManagerRegistryBuilder;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.Set;
29 import javax.annotation.Nonnull;
30 import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
31 import org.opendaylight.yangtools.yang.binding.DataObject;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33
34 public abstract class AbstractSubtreeManagerRegistryBuilderBuilder<S extends SubtreeManager<? extends DataObject>, R>
35         implements ModifiableSubtreeManagerRegistryBuilder<S>, SubtreeManagerRegistryBuilder<R>, AutoCloseable {
36
37     // Using directed acyclic graph to represent the ordering relationships between writers
38     private final DirectedAcyclicGraph<InstanceIdentifier<?>, Order>
39             handlersRelations = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new Order());
40     private final Map<InstanceIdentifier<?>, S> handlersMap = new HashMap<>();
41
42     /**
43      * Add handler without any special relationship to any other type.
44      */
45     @Override
46     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> add(@Nonnull final S handler) {
47         // Make IID wildcarded just in case
48         // + the way InstanceIdentifier.create + equals work for Identifiable items is unexpected, meaning updates would
49         // not be matched to writers in registry
50         final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
51         checkWriterNotPresentYet(targetType);
52         handlersRelations.addVertex(targetType);
53         handlersMap.put(targetType, handler);
54         return this;
55     }
56
57     /**
58      * Add handler without any special relationship to any other type.
59      */
60     @Override
61     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAdd(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
62                                                                          @Nonnull final S handler) {
63         add(getSubtreeHandler(handledChildren, handler));
64         return this;
65     }
66
67     private void checkWriterNotPresentYet(final InstanceIdentifier<?> targetType) {
68         Preconditions.checkArgument(!handlersMap.containsKey(targetType),
69                 "Writer for type: %s already present: %s", targetType, handlersMap.get(targetType));
70     }
71
72     /**
73      * Add handler with relationship: to be executed before handler handling relatedType.
74      */
75     @Override
76     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addBefore(@Nonnull final S handler,
77                                                                         @Nonnull final InstanceIdentifier<?> relatedType) {
78         final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
79         final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
80         checkWriterNotPresentYet(targetType);
81         handlersRelations.addVertex(targetType);
82         handlersRelations.addVertex(wildcardedRelatedType);
83         addEdge(targetType, wildcardedRelatedType);
84         handlersMap.put(targetType, handler);
85         return this;
86     }
87
88     @Override
89     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addBefore(@Nonnull final S handler,
90                                                                         @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
91         final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
92         checkWriterNotPresentYet(targetType);
93         handlersRelations.addVertex(targetType);
94         relatedTypes.stream()
95                 .map(RWUtils::makeIidWildcarded)
96                 .forEach(handlersRelations::addVertex);
97         relatedTypes.stream()
98                 .map(RWUtils::makeIidWildcarded)
99                 .forEach(type -> addEdge(targetType, type));
100         handlersMap.put(targetType, handler);
101         return this;
102     }
103
104     @Override
105     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddBefore(
106             @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
107             @Nonnull final S handler,
108             @Nonnull final InstanceIdentifier<?> relatedType) {
109         return addBefore(getSubtreeHandler(handledChildren, handler), relatedType);
110     }
111
112     @Override
113     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddBefore(
114             @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
115             @Nonnull final S handler,
116             @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
117         return addBefore(getSubtreeHandler(handledChildren, handler), relatedTypes);
118     }
119
120     protected abstract S getSubtreeHandler(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
121                                            @Nonnull final S handler);
122
123     /**
124      * Add handler with relationship: to be executed after handler handling relatedType.
125      */
126     @Override
127     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addAfter(@Nonnull final S handler,
128                                                                        @Nonnull final InstanceIdentifier<?> relatedType) {
129         final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
130         final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
131         checkWriterNotPresentYet(targetType);
132         handlersRelations.addVertex(targetType);
133         handlersRelations.addVertex(wildcardedRelatedType);
134         // set edge to indicate before relationship, just reversed
135         addEdge(wildcardedRelatedType, targetType);
136         handlersMap.put(targetType, handler);
137         return this;
138     }
139
140     @Override
141     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addAfter(@Nonnull final S handler,
142                                                                        @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
143         final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
144         checkWriterNotPresentYet(targetType);
145         handlersRelations.addVertex(targetType);
146         relatedTypes.stream()
147                 .map(RWUtils::makeIidWildcarded)
148                 .forEach(handlersRelations::addVertex);
149         // set edge to indicate before relationship, just reversed
150         relatedTypes.stream()
151                 .map(RWUtils::makeIidWildcarded)
152                 .forEach(type -> addEdge(type, targetType));
153         handlersMap.put(targetType, handler);
154         return this;
155     }
156
157     @Override
158     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddAfter(
159             @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
160             @Nonnull final S handler,
161             @Nonnull final InstanceIdentifier<?> relatedType) {
162         return addAfter(getSubtreeHandler(handledChildren, handler), relatedType);
163     }
164
165     @Override
166     public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddAfter(
167             @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
168             @Nonnull final S handler,
169             @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
170         return addAfter(getSubtreeHandler(handledChildren, handler), relatedTypes);
171     }
172
173
174     private void addEdge(final InstanceIdentifier<?> targetType,
175                          final InstanceIdentifier<?> relatedType) {
176         try {
177             handlersRelations.addDagEdge(targetType, relatedType);
178         } catch (DirectedAcyclicGraph.CycleFoundException e) {
179             throw new IllegalArgumentException(String.format(
180                     "Unable to add writer with relation: %s -> %s. Loop detected", targetType, relatedType), e);
181         }
182     }
183
184     protected ImmutableMap<InstanceIdentifier<?>, S> getMappedHandlers() {
185         final ImmutableMap.Builder<InstanceIdentifier<?>, S> builder = ImmutableMap.builder();
186         // Iterate writer types according to their relationships from graph
187         handlersRelations.iterator()
188                 .forEachRemaining(writerType -> {
189                     // There might be types stored just for relationship sake, no real writer, ignoring those
190                     if (handlersMap.containsKey(writerType)) {
191                         builder.put(writerType, handlersMap.get(writerType));
192                     }
193                 });
194
195         // TODO we could optimize subtree handlers, if there is a dedicated handler for a node managed by a subtree
196         // handler, recreate the subtree handler with a subset of handled child nodes
197         // This way it is not necessary to change the configuration of subtree writer, just to add a dedicated child
198         // writer. This will be needed if we ever switch to annotations for reader/writer hierarchy initialization
199
200         return builder.build();
201     }
202
203     @Override
204     public void close() throws Exception {
205         handlersMap.clear();
206         // Wrap sets into another set to avoid concurrent modification ex in graph
207         handlersRelations.removeAllEdges(Sets.newHashSet(handlersRelations.edgeSet()));
208         handlersRelations.removeAllVertices(Sets.newHashSet(handlersRelations.vertexSet()));
209     }
210
211     // Represents edges in graph
212     private class Order {}
213 }