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;
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;
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;
34 public abstract class AbstractSubtreeManagerRegistryBuilderBuilder<S extends SubtreeManager<? extends DataObject>, R>
35 implements ModifiableSubtreeManagerRegistryBuilder<S>, SubtreeManagerRegistryBuilder<R>, AutoCloseable {
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<>();
43 * Add handler without any special relationship to any other type.
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);
58 * Add handler without any special relationship to any other type.
61 public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAdd(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
62 @Nonnull final S handler) {
63 add(getSubtreeHandler(handledChildren, handler));
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));
73 * Add handler with relationship: to be executed before handler handling relatedType.
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);
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);
95 .map(RWUtils::makeIidWildcarded)
96 .forEach(handlersRelations::addVertex);
98 .map(RWUtils::makeIidWildcarded)
99 .forEach(type -> addEdge(targetType, type));
100 handlersMap.put(targetType, handler);
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);
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);
120 protected abstract S getSubtreeHandler(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
121 @Nonnull final S handler);
124 * Add handler with relationship: to be executed after handler handling relatedType.
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);
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);
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);
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);
174 private void addEdge(final InstanceIdentifier<?> targetType,
175 final InstanceIdentifier<?> relatedType) {
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);
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));
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
200 return builder.build();
204 public void close() throws Exception {
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()));
211 // Represents edges in graph
212 private class Order {}