2 * Copyright (c) 2015 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.yangtools.yang.model.api.stmt;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
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;
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>.
33 public abstract class SchemaNodeIdentifier implements Immutable {
35 * An absolute schema node identifier.
37 public abstract static class Absolute extends SchemaNodeIdentifier {
38 private static final Interner<Absolute> INTERNER = Interners.newWeakInterner();
45 * Create an absolute schema node identifier composed of a single node identifier.
47 * @param nodeIdentifier Single node identifier
48 * @return An absolute schema node identifier
49 * @throws NullPointerException if {@code nodeIdentifier} is null
51 public static @NonNull Absolute of(final QName nodeIdentifier) {
52 return new AbsoluteSingle(nodeIdentifier);
56 * Create an absolute schema node identifier composed of multiple node identifiers.
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
63 public static @NonNull Absolute of(final QName... nodeIdentifiers) {
64 return of(Arrays.asList(nodeIdentifiers));
68 * Create an absolute schema node identifier composed of multiple node identifiers.
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
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);
81 * Return an interned reference to an equivalent object.
83 * @return An interned reference, or this object if it was previously interned.
85 public final @NonNull Absolute intern() {
86 return INTERNER.intern(this);
90 @Deprecated(since = "7.0.9", forRemoval = true)
91 final SchemaPath implicitSchemaPathParent() {
92 return SchemaPath.ROOT;
96 final String className() {
102 * A descendant schema node identifier.
104 public abstract static class Descendant extends SchemaNodeIdentifier {
110 * Create a descendant schema node identifier composed of a single node identifier.
112 * @param nodeIdentifier Single node identifier
113 * @return A descendant schema node identifier
114 * @throws NullPointerException if {@code nodeIdentifier} is null
116 public static @NonNull Descendant of(final QName nodeIdentifier) {
117 return new DescendantSingle(nodeIdentifier);
121 * Create a descendant schema node identifier composed of multiple node identifiers.
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
128 public static @NonNull Descendant of(final QName... nodeIdentifiers) {
129 return of(Arrays.asList(nodeIdentifiers));
133 * Create a descendant schema node identifier composed of multiple node identifiers.
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
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);
146 @Deprecated(since = "7.0.9", forRemoval = true)
147 final SchemaPath implicitSchemaPathParent() {
148 return SchemaPath.SAME;
152 final String className() {
157 private static final class AbsoluteSingle extends Absolute {
158 private final @NonNull QName qname;
160 AbsoluteSingle(final QName qname) {
161 this.qname = requireNonNull(qname);
165 public ImmutableList<QName> getNodeIdentifiers() {
166 return ImmutableList.of(qname);
170 public QName firstNodeIdentifier() {
175 public QName lastNodeIdentifier() {
180 Object pathObject() {
185 private static final class AbsoluteMultiple extends Absolute {
186 private final @NonNull ImmutableList<QName> qnames;
188 AbsoluteMultiple(final ImmutableList<QName> qnames) {
189 this.qnames = requireNonNull(qnames);
193 public ImmutableList<QName> getNodeIdentifiers() {
198 Object pathObject() {
203 private static final class DescendantSingle extends Descendant {
204 private final @NonNull QName qname;
206 DescendantSingle(final QName qname) {
207 this.qname = requireNonNull(qname);
211 public ImmutableList<QName> getNodeIdentifiers() {
212 return ImmutableList.of(qname);
216 public QName firstNodeIdentifier() {
221 public QName lastNodeIdentifier() {
226 Object pathObject() {
231 private static final class DescandantMultiple extends Descendant {
232 private final @NonNull ImmutableList<QName> qnames;
234 DescandantMultiple(final ImmutableList<QName> qnames) {
235 this.qnames = requireNonNull(qnames);
239 public ImmutableList<QName> getNodeIdentifiers() {
244 Object pathObject() {
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");
253 // Cached SchemaPath.
254 @Deprecated(since = "7.0.9", forRemoval = true)
255 private volatile SchemaPath schemaPath;
257 private volatile int hash;
259 SchemaNodeIdentifier() {
264 * Return the non-empty sequence of node identifiers which constitute this schema node identifier.
266 * @return Non-empty sequence of node identifiers
268 public abstract @NonNull List<QName> getNodeIdentifiers();
271 * Return the first node identifier. This method is equivalent to {@code getNodeIdentifiers().get(0)}, but is
272 * potentially more efficient.
274 * @return The first node identifier
276 public @NonNull QName firstNodeIdentifier() {
277 return getNodeIdentifiers().get(0);
281 * Return the last node identifier. This method is equivalent to {@code getNodeIdentifiers().get(size - 1)}, but
282 * is potentially more efficient.
284 * @return The last node identifier
286 public @NonNull QName lastNodeIdentifier() {
287 final List<QName> local = getNodeIdentifiers();
288 return local.get(local.size() - 1);
292 * Create the {@link SchemaPath} equivalent of this identifier.
294 * @return SchemaPath equivalent.
295 * @deprecated This method is scheduled for removal along with {@link SchemaPath}. This method performs memoization,
296 * which should not be needed most of the time. If you need memoization, you probably can do better, as
297 * you probably want to attach more state. If you just need a SchemaPath, use
298 * {@link SchemaPath#of(SchemaNodeIdentifier)} instead.
300 @Deprecated(since = "7.0.9", forRemoval = true)
301 public final @NonNull SchemaPath asSchemaPath() {
302 final SchemaPath ret = schemaPath;
303 return ret != null ? ret : loadSchemaPath();
307 public final int hashCode() {
309 return (local = hash) != 0 ? local : (hash = pathObject().hashCode());
313 public final boolean equals(final Object obj) {
314 return this == obj || obj != null && getClass() == obj.getClass()
315 && pathObject().equals(((SchemaNodeIdentifier) obj).pathObject());
319 public final String toString() {
320 return MoreObjects.toStringHelper(className()).add("qnames", toStringQNames()).toString();
323 @Deprecated(since = "7.0.9", forRemoval = true)
324 abstract @NonNull SchemaPath implicitSchemaPathParent();
326 abstract @NonNull Object pathObject();
328 abstract @NonNull String className();
330 @Deprecated(since = "7.0.9", forRemoval = true)
331 private @NonNull SchemaPath loadSchemaPath() {
332 final SchemaPath newPath = implicitSchemaPathParent().createChild(getNodeIdentifiers());
333 return SCHEMAPATH_UPDATER.compareAndSet(this, null, newPath) ? newPath : schemaPath;
336 private List<?> toStringQNames() {
337 final List<QName> ids = getNodeIdentifiers();
338 return ids.size() < 2 ? ids : simplifyQNames(ids);
341 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
342 justification = "https://github.com/spotbugs/spotbugs/issues/811")
343 private static ImmutableList<QName> checkQNames(final Collection<QName> qnames) {
344 final ImmutableList<QName> ret = ImmutableList.copyOf(qnames);
345 checkArgument(!ret.isEmpty(), "SchemaNodeIdentifier has to have at least one node identifier");
349 private static List<?> simplifyQNames(final List<QName> qnames) {
350 final List<Object> ret = new ArrayList<>(qnames.size());
352 QNameModule prev = null;
353 for (QName qname : qnames) {
354 final QNameModule module = qname.getModule();
355 ret.add(module.equals(prev) ? qname.getLocalName() : qname);