2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.neutronvpn;
10 import com.google.common.base.Predicates;
11 import com.google.common.collect.Maps;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.LinkedList;
17 import java.util.List;
19 import java.util.Map.Entry;
20 import java.util.Queue;
22 import java.util.function.Predicate;
23 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
24 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
25 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
26 import org.opendaylight.yangtools.yang.binding.ChildOf;
27 import org.opendaylight.yangtools.yang.binding.DataObject;
28 import org.opendaylight.yangtools.yang.binding.Identifiable;
29 import org.opendaylight.yangtools.yang.binding.Identifier;
30 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
31 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
33 public class ChangeUtils {
35 private ChangeUtils() { }
37 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBefore() {
38 return input -> input != null && input.getDataBefore() != null;
41 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataBeforeAndDataAfter() {
42 return input -> input != null && input.getDataBefore() != null && input.getDataAfter() != null;
45 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasNoDataBefore() {
46 return input -> input != null && input.getDataBefore() == null;
49 private static <T extends DataObject> Predicate<DataObjectModification<T>> hasDataAfterAndMatchesFilter(
50 final Predicate<DataObjectModification<T>> filter) {
51 return input -> input != null && input.getDataAfter() != null && filter.test(input);
54 private static <T extends DataObject> Predicate<DataObjectModification<T>> matchesEverything() {
58 private static <T extends DataObject> Predicate<DataObjectModification<T>> modificationIsDeletion() {
59 return input -> input != null && input.getModificationType() == DataObjectModification
60 .ModificationType.DELETE;
63 private static <T extends DataObject> Predicate<DataObjectModification<T>>
64 modificationIsDeletionAndHasDataBefore() {
65 return input -> input != null && input.getModificationType() == DataObjectModification
66 .ModificationType.DELETE && input.getDataBefore() != null;
69 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractCreated(
70 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,Class<T> klazz) {
71 return extract(changes.getCreatedData(),klazz);
75 * Extract all the instances of {@code clazz} which were created in the given set of modifications.
77 * @param changes The changes to process.
78 * @param clazz The class we're interested in.
79 * @param <T> The type of changes we're interested in.
80 * @param <U> The type of changes to process.
81 * @return The created instances, mapped by instance identifier.
83 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreated(
84 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
85 return extractCreatedOrUpdated(changes, clazz, hasNoDataBefore());
88 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractUpdated(
89 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
90 return extract(changes.getUpdatedData(),klazz);
94 * Extract all the instances of {@code clazz} which were updated in the given set of modifications.
96 * @param changes The changes to process.
97 * @param clazz The class we're interested in.
98 * @param <T> The type of changes we're interested in.
99 * @param <U> The type of changes to process.
100 * @return The updated instances, mapped by instance identifier.
102 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractUpdated(
103 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
104 return extractCreatedOrUpdated(changes, clazz, hasDataBeforeAndDataAfter());
108 * Extract all the instance of {@code clazz} which were created or updated in the given set of modifications, and
109 * which satisfy the given filter.
111 * @param changes The changes to process.
112 * @param clazz The class we're interested in.
113 * @param filter The filter the changes must satisfy.
114 * @param <T> The type of changes we're interested in.
115 * @param <U> The type of changes to process.
116 * @return The created or updated instances which satisfy the filter, mapped by instance identifier.
118 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdated(
119 Collection<DataTreeModification<U>> changes, Class<T> clazz,
120 Predicate<DataObjectModification<T>> filter) {
121 Map<InstanceIdentifier<T>, T> result = new HashMap<>();
122 for (Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry : extractDataObjectModifications(changes,
123 clazz, hasDataAfterAndMatchesFilter(filter)).entrySet()) {
124 result.put(entry.getKey(), entry.getValue().getDataAfter());
129 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractCreatedOrUpdated(
130 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
131 Map<InstanceIdentifier<T>,T> result = extractUpdated(changes,klazz);
132 result.putAll(extractCreated(changes,klazz));
137 * Extract all the instances of {@code clazz} which were created or updated in the given set of modifications.
139 * @param changes The changes to process.
140 * @param clazz The class we're interested in.
141 * @param <T> The type of changes we're interested in.
142 * @param <U> The type of changes to process.
143 * @return The created or updated instances, mapped by instance identifier.
145 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdated(
146 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
147 return extractCreatedOrUpdated(changes, clazz, matchesEverything());
150 public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractCreatedOrUpdatedOrRemoved(
151 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
153 Map<InstanceIdentifier<T>,T> result = extractCreatedOrUpdated(changes,klazz);
154 result.putAll(extractRemovedObjects(changes, klazz));
159 * Extract all the instances of {@code clazz} which were created, updated, or removed in the given set of
160 * modifications. For instances which were created or updated, the new instances are returned; for instances
161 * which were removed, the old instances are returned.
163 * @param changes The changes to process.
164 * @param clazz The class we're interested in.
165 * @param <T> The type of changes we're interested in.
166 * @param <U> The type of changes to process.
167 * @return The created, updated or removed instances, mapped by instance identifier.
169 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T>
170 extractCreatedOrUpdatedOrRemoved(
171 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
172 Map<InstanceIdentifier<T>, T> result = extractCreatedOrUpdated(changes, clazz);
173 result.putAll(extractRemovedObjects(changes, clazz));
177 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extractOriginal(
178 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
179 return extract(changes.getOriginalData(),klazz);
183 * Extract the original instances of class {@code clazz} in the given set of modifications.
185 * @param changes The changes to process.
186 * @param clazz The class we're interested in.
187 * @param <T> The type of changes we're interested in.
188 * @param <U> The type of changes to process.
189 * @return The original instances, mapped by instance identifier.
191 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractOriginal(
192 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
193 Map<InstanceIdentifier<T>, T> result = new HashMap<>();
194 for (Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry :
195 extractDataObjectModifications(changes, clazz, hasDataBefore()).entrySet()) {
196 result.put(entry.getKey(), entry.getValue().getDataBefore());
201 public static <T extends DataObject> Set<InstanceIdentifier<T>> extractRemoved(
202 AsyncDataChangeEvent<InstanceIdentifier<?>,DataObject> changes,Class<T> klazz) {
203 Set<InstanceIdentifier<T>> result = new HashSet<>();
204 if (changes != null && changes.getRemovedPaths() != null) {
205 for (InstanceIdentifier<?> iid : changes.getRemovedPaths()) {
206 if (iid.getTargetType().equals(klazz)) {
207 // Actually checked above
208 @SuppressWarnings("unchecked")
209 InstanceIdentifier<T> iidn = (InstanceIdentifier<T>)iid;
218 * Extract the instance identifier of removed instances of {@code clazz} from the given set of modifications.
220 * @param changes The changes to process.
221 * @param clazz The class we're interested in.
222 * @param <T> The type of changes we're interested in.
223 * @param <U> The type of changes to process.
224 * @return The instance identifiers of removed instances.
226 public static <T extends DataObject, U extends DataObject> Set<InstanceIdentifier<T>> extractRemoved(
227 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
228 return extractDataObjectModifications(changes, clazz, modificationIsDeletion()).keySet();
232 * Extract all the modifications affecting instances of {@code clazz} which are present in the given set of
233 * modifications and satisfy the given filter.
235 * @param changes The changes to process.
236 * @param clazz The class we're interested in.
237 * @param filter The filter the changes must satisfy.
238 * @param <T> The type of changes we're interested in.
239 * @param <U> The type of changes to process.
240 * @return The modifications, mapped by instance identifier.
242 private static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, DataObjectModification<T>>
243 extractDataObjectModifications(Collection<DataTreeModification<U>> changes, Class<T> clazz,
244 Predicate<DataObjectModification<T>> filter) {
245 List<DataObjectModification<? extends DataObject>> dataObjectModifications = new ArrayList<>();
246 List<InstanceIdentifier<? extends DataObject>> paths = new ArrayList<>();
247 if (changes != null) {
248 for (DataTreeModification<? extends DataObject> change : changes) {
249 dataObjectModifications.add(change.getRootNode());
250 paths.add(change.getRootPath().getRootIdentifier());
253 return extractDataObjectModifications(dataObjectModifications, paths, clazz, filter);
257 * Extract all the modifications affecting instances of {@code clazz} which are present in the given set of
258 * modifications and satisfy the given filter.
260 * @param changes The changes to process.
261 * @param paths The paths of the changes.
262 * @param clazz The class we're interested in.
263 * @param filter The filter the changes must satisfy.
264 * @param <T> The type of changes we're interested in.
265 * @return The modifications, mapped by instance identifier.
267 private static <T extends DataObject> Map<InstanceIdentifier<T>, DataObjectModification<T>>
268 extractDataObjectModifications(
269 Collection<DataObjectModification<? extends DataObject>> changes,
270 Collection<InstanceIdentifier<? extends DataObject>> paths, Class<T> clazz,
271 Predicate<DataObjectModification<T>> filter) {
272 Map<InstanceIdentifier<T>, DataObjectModification<T>> result = new HashMap<>();
273 Queue<DataObjectModification<? extends DataObject>> remainingChanges = new LinkedList<>(changes);
274 Queue<InstanceIdentifier<? extends DataObject>> remainingPaths = new LinkedList<>(paths);
275 while (!remainingChanges.isEmpty()) {
276 DataObjectModification<? extends DataObject> change = remainingChanges.remove();
277 InstanceIdentifier<? extends DataObject> path = remainingPaths.remove();
278 // Is the change relevant?
279 if (clazz.isAssignableFrom(change.getDataType()) && filter.test((DataObjectModification<T>) change)) {
280 result.put((InstanceIdentifier<T>) path, (DataObjectModification<T>) change);
282 // Add any children to the queue
283 for (DataObjectModification<? extends DataObject> child : change.getModifiedChildren()) {
284 remainingChanges.add(child);
285 remainingPaths.add(extendPath(path, child));
292 * Extends the given instance identifier path to include the given child. Augmentations are treated in the same way
293 * as children; keyed children are handled correctly.
295 * @param path The current path.
296 * @param child The child modification to include.
297 * @return The extended path.
299 private static <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>, T extends DataObject>
300 InstanceIdentifier<? extends DataObject> extendPath(
301 InstanceIdentifier path,
302 DataObjectModification child) {
303 Class<N> item = (Class<N>) child.getDataType();
304 if (child.getIdentifier() instanceof InstanceIdentifier.IdentifiableItem) {
305 K key = (K) ((InstanceIdentifier.IdentifiableItem) child.getIdentifier()).getKey();
306 KeyedInstanceIdentifier<N, K> extendedPath = path.child(item, key);
309 InstanceIdentifier<N> extendedPath = path.child(item);
314 public static <T extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
315 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes,
317 Set<InstanceIdentifier<T>> iids = extractRemoved(changes, klazz);
318 return Maps.filterKeys(extractOriginal(changes, klazz),Predicates.in(iids));
322 * Extract the removed instances of {@code clazz} from the given set of modifications.
324 * @param changes The changes to process.
325 * @param clazz The class we're interested in.
326 * @param <T> The type of changes we're interested in.
327 * @param <U> The type of changes to process.
328 * @return The removed instances, keyed by instance identifier.
330 public static <T extends DataObject, U extends DataObject> Map<InstanceIdentifier<T>, T> extractRemovedObjects(
331 Collection<DataTreeModification<U>> changes, Class<T> clazz) {
332 Map<InstanceIdentifier<T>, T> result = new HashMap<>();
333 for (Entry<InstanceIdentifier<T>, DataObjectModification<T>> entry :
334 extractDataObjectModifications(changes, clazz, modificationIsDeletionAndHasDataBefore()).entrySet()) {
335 result.put(entry.getKey(), entry.getValue().getDataBefore());
340 public static <T extends DataObject> Map<InstanceIdentifier<T>,T> extract(
341 Map<InstanceIdentifier<?>, DataObject> changes, Class<T> klazz) {
342 Map<InstanceIdentifier<T>,T> result = new HashMap<>();
343 if (changes != null) {
344 for (Entry<InstanceIdentifier<?>, DataObject> created : changes.entrySet()) {
345 if (klazz.isInstance(created.getValue())) {
346 @SuppressWarnings("unchecked")
347 T value = (T) created.getValue();
348 Class<?> type = created.getKey().getTargetType();
349 if (type.equals(klazz)) {
350 // Actually checked above
351 @SuppressWarnings("unchecked")
352 InstanceIdentifier<T> iid = (InstanceIdentifier<T>) created.getKey();
353 result.put(iid, value);