Merge branch 'master' of ../controller
[yangtools.git] / common / concepts / src / main / java / org / opendaylight / yangtools / concepts / ObjectExtensions.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, 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.yangtools.concepts;
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 com.google.common.collect.ClassToInstanceMap;
15 import com.google.common.collect.ImmutableSet;
16 import com.google.common.collect.Iterators;
17 import java.util.AbstractList;
18 import java.util.AbstractMap;
19 import java.util.AbstractSet;
20 import java.util.Collection;
21 import java.util.Iterator;
22 import java.util.Map;
23 import java.util.Set;
24 import org.eclipse.jdt.annotation.NonNull;
25
26 /**
27  * Utility {@link ClassToInstanceMap} implementation for implementing {@link ExtensibleObject#getExtensions()} method
28  * by objects which are themselves implementing the extension.
29  *
30  * @param <O> Type of extensible object
31  * @param <E> Extension marker interface
32  * @author Robert Varga
33  */
34 @Beta
35 public final class ObjectExtensions<O extends ExtensibleObject<O, E>, E extends ObjectExtension<O, E>>
36         extends AbstractMap<Class<? extends E>, E> implements ClassToInstanceMap<E> {
37     private final class EntrySet extends AbstractSet<Entry<Class<? extends E>, E>> {
38         @Override
39         public Iterator<Entry<Class<? extends E>, E>> iterator() {
40             return Iterators.transform(extensions.iterator(), ext -> new SimpleImmutableEntry<>(ext, ext.cast(object)));
41         }
42
43         @Override
44         public int size() {
45             return extensions.size();
46         }
47     }
48
49     private static final class Values<E> extends AbstractList<E> {
50         private final @NonNull E instance;
51         private final int size;
52
53         @SuppressWarnings("unchecked")
54         Values(final @NonNull Object instance, final int size) {
55             this.instance = (E) instance;
56             this.size = size;
57         }
58
59         @Override
60         public E get(final int index) {
61             if (index < 0 || index >= size) {
62                 throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
63             }
64             return instance;
65         }
66
67         @Override
68         public int size() {
69             return size;
70         }
71     }
72
73     public static final class Factory<T, O extends ExtensibleObject<O, E>, E extends ObjectExtension<O, E>> {
74         private final @NonNull ImmutableSet<Class<? extends E>> extensions;
75
76         Factory(final ImmutableSet<Class<? extends E>> extensions) {
77             this.extensions = requireNonNull(extensions);
78         }
79
80         public @NonNull ClassToInstanceMap<E> newInstance(final T object) {
81             return new ObjectExtensions<>(extensions, object);
82         }
83     }
84
85     private final @NonNull ImmutableSet<Class<? extends E>> extensions;
86     private final @NonNull Object object;
87
88     ObjectExtensions(final ImmutableSet<Class<? extends E>> extensions, final Object object) {
89         this.extensions = requireNonNull(extensions);
90         this.object = requireNonNull(object);
91     }
92
93     @SafeVarargs
94     public static <T, O extends ExtensibleObject<O, E>, E extends ObjectExtension<O, E>> @NonNull Factory<T, O, E>
95             factory(final Class<T> objClass, final Class<? extends E>... extensions) {
96         final ImmutableSet<Class<? extends E>> set = ImmutableSet.copyOf(extensions);
97         for (Class<? extends E> extension : set) {
98             checkArgument(extension.isAssignableFrom(objClass), "%s is not a valid extension %s", objClass, extension);
99         }
100         return new Factory<>(set);
101     }
102
103     @Override
104     public int size() {
105         return extensions.size();
106     }
107
108     @Override
109     public boolean isEmpty() {
110         return extensions.isEmpty();
111     }
112
113     @Override
114     public boolean containsKey(final Object key) {
115         return extensions.contains(key);
116     }
117
118     @Override
119     public boolean containsValue(final Object value) {
120         return object.equals(value);
121     }
122
123     @Override
124     public E get(final Object key) {
125         return containsKey(key) ? ((Class<? extends E>) key).cast(object) : null;
126     }
127
128     @Override
129     public E put(final Class<? extends E> key, final E value) {
130         throw new UnsupportedOperationException();
131     }
132
133     @Override
134     public E remove(final Object key) {
135         throw new UnsupportedOperationException();
136     }
137
138     @Override
139     @SuppressWarnings("checkstyle:parameterName")
140     public void putAll(final Map<? extends Class<? extends E>, ? extends E> m) {
141         throw new UnsupportedOperationException();
142     }
143
144     @Override
145     public void clear() {
146         throw new UnsupportedOperationException();
147     }
148
149     @Override
150     public Set<Class<? extends E>> keySet() {
151         return extensions;
152     }
153
154     @Override
155     public Collection<E> values() {
156         return new Values<>(object, extensions.size());
157     }
158
159     @Override
160     public Set<Entry<Class<? extends E>, E>> entrySet() {
161         return new EntrySet();
162     }
163
164     @Override
165     public <T extends E> T getInstance(final Class<T> type) {
166         return extensions.contains(requireNonNull(type)) ? type.cast(object) : null;
167     }
168
169     @Override
170     public <T extends E> T putInstance(final Class<T> type, final T value) {
171         throw new UnsupportedOperationException();
172     }
173 }