Bump odlparent to 10.0.0
[yangtools.git] / model / yang-model-api / src / main / java / org / opendaylight / yangtools / yang / model / api / stmt / SchemaNodeIdentifier.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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.yang.model.api.stmt;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.MoreObjects;
14 import com.google.common.collect.ImmutableList;
15 import com.google.common.collect.Interner;
16 import com.google.common.collect.Interners;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Collection;
20 import java.util.List;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.opendaylight.yangtools.concepts.Immutable;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.common.QNameModule;
25
26 /**
27  * Represents unique path to every schema node inside the schema node identifier namespace. This concept is defined
28  * in <a href="https://tools.ietf.org/html/rfc7950#section-6.5">RFC7950</a>.
29  */
30 public abstract class SchemaNodeIdentifier implements Immutable {
31     /**
32      * An absolute schema node identifier.
33      */
34     public abstract static class Absolute extends SchemaNodeIdentifier {
35         private static final Interner<Absolute> INTERNER = Interners.newWeakInterner();
36
37         Absolute() {
38             // Hidden on purpose
39         }
40
41         /**
42          * Create an absolute schema node identifier composed of a single node identifier.
43          *
44          * @param nodeIdentifier Single node identifier
45          * @return An absolute schema node identifier
46          * @throws NullPointerException if {@code nodeIdentifier} is null
47          */
48         public static @NonNull Absolute of(final QName nodeIdentifier) {
49             return new AbsoluteSingle(nodeIdentifier);
50         }
51
52         /**
53          * Create an absolute schema node identifier composed of multiple node identifiers.
54          *
55          * @param nodeIdentifiers Node identifiers
56          * @return An absolute schema node identifier
57          * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
58          * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
59          */
60         public static @NonNull Absolute of(final QName... nodeIdentifiers) {
61             return of(Arrays.asList(nodeIdentifiers));
62         }
63
64         /**
65          * Create an absolute schema node identifier composed of multiple node identifiers.
66          *
67          * @param nodeIdentifiers Node identifiers
68          * @return An absolute schema node identifier
69          * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
70          * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
71          */
72         public static @NonNull Absolute of(final Collection<QName> nodeIdentifiers) {
73             final ImmutableList<QName> qnames = checkQNames(nodeIdentifiers);
74             return qnames.size() == 1 ? of(qnames.get(0)) : new AbsoluteMultiple(qnames);
75         }
76
77         /**
78          * Return an interned reference to an equivalent object.
79          *
80          * @return An interned reference, or this object if it was previously interned.
81          */
82         public final @NonNull Absolute intern() {
83             return INTERNER.intern(this);
84         }
85
86         @Override
87         final String className() {
88             return "Absolute";
89         }
90     }
91
92     /**
93      * A descendant schema node identifier.
94      */
95     public abstract static class Descendant extends SchemaNodeIdentifier {
96         Descendant() {
97             // Hidden on purpose
98         }
99
100         /**
101          * Create a descendant schema node identifier composed of a single node identifier.
102          *
103          * @param nodeIdentifier Single node identifier
104          * @return A descendant schema node identifier
105          * @throws NullPointerException if {@code nodeIdentifier} is null
106          */
107         public static @NonNull Descendant of(final QName nodeIdentifier) {
108             return new DescendantSingle(nodeIdentifier);
109         }
110
111         /**
112          * Create a descendant schema node identifier composed of multiple node identifiers.
113          *
114          * @param nodeIdentifiers Node identifiers
115          * @return A descendant schema node identifier
116          * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
117          * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
118          */
119         public static @NonNull Descendant of(final QName... nodeIdentifiers) {
120             return of(Arrays.asList(nodeIdentifiers));
121         }
122
123         /**
124          * Create a descendant schema node identifier composed of multiple node identifiers.
125          *
126          * @param nodeIdentifiers Node identifiers
127          * @return A descendant schema node identifier
128          * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
129          * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
130          */
131         public static @NonNull Descendant of(final Collection<QName> nodeIdentifiers) {
132             final ImmutableList<QName> qnames = checkQNames(nodeIdentifiers);
133             return qnames.size() == 1 ? of(qnames.get(0)) : new DescandantMultiple(qnames);
134         }
135
136         @Override
137         final String className() {
138             return "Descendant";
139         }
140     }
141
142     private static final class AbsoluteSingle extends Absolute {
143         private final @NonNull QName qname;
144
145         AbsoluteSingle(final QName qname) {
146             this.qname = requireNonNull(qname);
147         }
148
149         @Override
150         public ImmutableList<QName> getNodeIdentifiers() {
151             return ImmutableList.of(qname);
152         }
153
154         @Override
155         public QName firstNodeIdentifier() {
156             return qname;
157         }
158
159         @Override
160         public QName lastNodeIdentifier() {
161             return qname;
162         }
163
164         @Override
165         Object pathObject() {
166             return qname;
167         }
168     }
169
170     private static final class AbsoluteMultiple extends Absolute {
171         private final @NonNull ImmutableList<QName> qnames;
172
173         AbsoluteMultiple(final ImmutableList<QName> qnames) {
174             this.qnames = requireNonNull(qnames);
175         }
176
177         @Override
178         public ImmutableList<QName> getNodeIdentifiers() {
179             return qnames;
180         }
181
182         @Override
183         Object pathObject() {
184             return qnames;
185         }
186     }
187
188     private static final class DescendantSingle extends Descendant {
189         private final @NonNull QName qname;
190
191         DescendantSingle(final QName qname) {
192             this.qname = requireNonNull(qname);
193         }
194
195         @Override
196         public ImmutableList<QName> getNodeIdentifiers() {
197             return ImmutableList.of(qname);
198         }
199
200         @Override
201         public QName firstNodeIdentifier() {
202             return qname;
203         }
204
205         @Override
206         public QName lastNodeIdentifier() {
207             return qname;
208         }
209
210         @Override
211         Object pathObject() {
212             return qname;
213         }
214     }
215
216     private static final class DescandantMultiple extends Descendant {
217         private final @NonNull ImmutableList<QName> qnames;
218
219         DescandantMultiple(final ImmutableList<QName> qnames) {
220             this.qnames = requireNonNull(qnames);
221         }
222
223         @Override
224         public ImmutableList<QName> getNodeIdentifiers() {
225             return qnames;
226         }
227
228         @Override
229         Object pathObject() {
230             return qnames;
231         }
232     }
233
234     // Cached hashCode
235     private volatile int hash;
236
237     SchemaNodeIdentifier() {
238         // Hidden on purpose
239     }
240
241     /**
242      * Return the non-empty sequence of node identifiers which constitute this schema node identifier.
243      *
244      * @return Non-empty sequence of node identifiers
245      */
246     public abstract @NonNull List<QName> getNodeIdentifiers();
247
248     /**
249      * Return the first node identifier. This method is equivalent to {@code getNodeIdentifiers().get(0)}, but is
250      * potentially more efficient.
251      *
252      * @return The first node identifier
253      */
254     public @NonNull QName firstNodeIdentifier() {
255         return getNodeIdentifiers().get(0);
256     }
257
258     /**
259      * Return the last node identifier. This method is equivalent to {@code getNodeIdentifiers().get(size - 1)}, but
260      * is potentially more efficient.
261      *
262      * @return The last node identifier
263      */
264     public @NonNull QName lastNodeIdentifier() {
265         final List<QName> local = getNodeIdentifiers();
266         return local.get(local.size() - 1);
267     }
268
269     @Override
270     public final int hashCode() {
271         final int local;
272         return (local = hash) != 0 ? local : (hash = pathObject().hashCode());
273     }
274
275     @Override
276     public final boolean equals(final Object obj) {
277         return this == obj || obj != null && getClass() == obj.getClass()
278                 && pathObject().equals(((SchemaNodeIdentifier) obj).pathObject());
279     }
280
281     @Override
282     public final String toString() {
283         return MoreObjects.toStringHelper(className()).add("qnames", toStringQNames()).toString();
284     }
285
286     abstract @NonNull Object pathObject();
287
288     abstract @NonNull String className();
289
290     private List<?> toStringQNames() {
291         final List<QName> ids = getNodeIdentifiers();
292         return ids.size() < 2 ? ids : simplifyQNames(ids);
293     }
294
295     private static ImmutableList<QName> checkQNames(final Collection<QName> qnames) {
296         final ImmutableList<QName> ret = ImmutableList.copyOf(qnames);
297         checkArgument(!ret.isEmpty(), "SchemaNodeIdentifier has to have at least one node identifier");
298         return ret;
299     }
300
301     private static List<?> simplifyQNames(final List<QName> qnames) {
302         final List<Object> ret = new ArrayList<>(qnames.size());
303
304         QNameModule prev = null;
305         for (QName qname : qnames) {
306             final QNameModule module = qname.getModule();
307             ret.add(module.equals(prev) ? qname.getLocalName() : qname);
308             prev = module;
309         }
310
311         return ret;
312     }
313 }