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