Bump upstreams for 2022.09 Chlorine
[openflowplugin.git] / extension / openflowplugin-extension-api / src / main / java / org / opendaylight / openflowplugin / extension / api / AugmentationGroupingResolver.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.openflowplugin.extension.api;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import java.util.HashSet;
15 import java.util.Optional;
16 import java.util.Set;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.opendaylight.yangtools.concepts.Immutable;
19 import org.opendaylight.yangtools.yang.binding.Augmentable;
20 import org.opendaylight.yangtools.yang.binding.Augmentation;
21 import org.opendaylight.yangtools.yang.binding.DataObject;
22
23 /**
24  * Resolver providing a bridge between a grouping and its various instantiations via augment. This is useful for
25  * extracting the grouping's content from a DataObject's augmentations without knowing from which instantiation it
26  * comes from.
27  *
28  * <p>
29  * Typical use case is, given a base grouping module:
30  * <pre>
31  *     module foo;
32  *
33  *     grouping foo {
34  *         container augmentable {
35  *
36  *         }
37  *     }
38  * </pre>
39  * and a module independent of it:
40  * <pre>
41  *     module bar;
42  *
43  *     container bar {
44  *         uses foo:foo;
45  *     }
46  * </pre>
47  * and
48  * <pre>
49  *     module baz;
50  *
51  *     container baz {
52  *         uses foo:foo;
53  *     }
54  * </pre>
55  * an external module can perform:
56  * <pre>
57  *     module xyzzy;
58  *
59  *     import bar { prefix bar; }
60  *     import baz { prefix baz; }
61  *
62  *     grouping something {
63  *         // ...
64  *     }
65  *
66  *     augment /bar:bar/bar:augmentable {
67  *         uses something;
68  *     }
69  *
70  *     augment /baz:baz/baz:augmentable {
71  *         uses something;
72  *     }
73  * </pre>
74  * The augmentations of {@code bar} and {@code baz} instantiations of {@code grouping foo} have an equivalent
75  * augmentation introduced by {@code xyzzy}. This equivalence is not expressed is generated code, in that it is
76  * not apparent given {@code bar} or {@code baz} there is an augmentation which provides {@code something}.
77  *
78  * <p>
79  * This class provides the static knowledge to ask for the contents of {@code something} given an instance of
80  * {@code augmentable}, without knowing which augmentation introduces it.
81  *
82  * @param <G> Grouping type
83  * @param <T> Augmentable type
84  */
85 @Beta
86 public final class AugmentationGroupingResolver<G extends DataObject, T extends Augmentable<T>> implements Immutable {
87     private final Class<? extends Augmentation<T>>[] augmentations;
88     private final Class<G> grouping;
89
90     AugmentationGroupingResolver(final Class<G> grouping, final Class<? extends Augmentation<T>>[] augmentations) {
91         this.grouping = requireNonNull(grouping);
92         this.augmentations = requireNonNull(augmentations);
93     }
94
95     public @NonNull Optional<G> findExtension(final T data) {
96         requireNonNull(data);
97
98         for (Class<? extends Augmentation<T>> cls : augmentations) {
99             final Augmentation<T> potential = data.augmentation(cls);
100             if (potential != null) {
101                 return Optional.of(grouping.cast(potential));
102             }
103         }
104         return Optional.empty();
105     }
106
107     public static <G extends DataObject, T extends Augmentable<T>> @NonNull Builder<G, T> builder(
108             final Class<G> groupingClass) {
109         return new Builder<>(groupingClass);
110     }
111
112     @SuppressWarnings("unchecked")
113     public static <T extends Augmentable<T>> @NonNull Factory<T> factory(final Class<T> augmentableClass,
114             final Set<Class<? extends Augmentation<T>>> augmentationClasses) {
115         // Defensive copy via .clone() to guard against evil Set implementations
116         final Class<?>[] array = augmentationClasses.toArray(new Class<?>[0]).clone();
117
118         // Defensive check of all array elements
119         for (Class<?> clazz : array) {
120             checkArgument(Augmentation.class.isAssignableFrom(clazz), "Class %s is not an Augmentation", clazz);
121         }
122
123         return new Factory<>((Class<? extends Augmentation<T>>[]) array);
124     }
125
126     public static final class Builder<G extends DataObject, T extends Augmentable<T>> {
127         private final Set<Class<? extends Augmentation<T>>> augmentations = new HashSet<>();
128         private final Class<G> grouping;
129
130         Builder(final Class<G> groupingClass) {
131             grouping = requireNonNull(groupingClass);
132         }
133
134         public <X extends Augmentation<T>> @NonNull Builder<G, T> addAugmentationClass(
135                 final Class<X> augmentationClass) {
136             checkAssignable(grouping, augmentationClass);
137             augmentations.add(augmentationClass);
138             return this;
139         }
140
141         @SuppressWarnings("unchecked")
142         public @NonNull AugmentationGroupingResolver<G, T> build() {
143             return new AugmentationGroupingResolver<>(grouping,
144                     (Class<? extends Augmentation<T>>[]) augmentations.toArray(new Class<?>[0]));
145         }
146     }
147
148     public static final class Factory<T extends Augmentable<T>> implements Immutable {
149         private final Class<? extends Augmentation<T>>[] augmentations;
150
151         Factory(final Class<? extends Augmentation<T>>[] augmentations) {
152             this.augmentations = requireNonNull(augmentations);
153         }
154
155         public <G extends DataObject> @NonNull AugmentationGroupingResolver<G, T> createResolver(
156                 final Class<G> groupingClass) {
157             for (Class<? extends Augmentation<T>> cls : augmentations) {
158                 checkAssignable(groupingClass, cls);
159             }
160
161             return new AugmentationGroupingResolver<>(groupingClass, augmentations);
162         }
163     }
164
165     static void checkAssignable(final Class<?> groupingClass, final Class<?> augmentationClass) {
166         checkArgument(groupingClass.isAssignableFrom(augmentationClass), "%s is not compatible with grouping %s",
167             augmentationClass, groupingClass);
168     }
169 }