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 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;
27 * Represents unique path to every schema node inside the schema node identifier namespace. This concept is defined
28 * in <a href="https://www.rfc-editor.org/rfc/rfc7950#section-6.5">RFC7950</a>.
30 public abstract sealed class SchemaNodeIdentifier implements Immutable {
32 * An absolute schema node identifier.
34 public abstract static sealed class Absolute extends SchemaNodeIdentifier {
35 private static final Interner<@NonNull Absolute> INTERNER = Interners.newWeakInterner();
38 * Create an absolute schema node identifier composed of a single node identifier.
40 * @param nodeIdentifier Single node identifier
41 * @return An absolute schema node identifier
42 * @throws NullPointerException if {@code nodeIdentifier} is null
44 public static @NonNull Absolute of(final QName nodeIdentifier) {
45 return new AbsoluteSingle(nodeIdentifier);
49 * Create an absolute schema node identifier composed of multiple node identifiers.
51 * @param nodeIdentifiers Node identifiers
52 * @return An absolute schema node identifier
53 * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
54 * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
56 public static @NonNull Absolute of(final QName... nodeIdentifiers) {
57 return of(Arrays.asList(nodeIdentifiers));
61 * Create an absolute schema node identifier composed of multiple node identifiers.
63 * @param nodeIdentifiers Node identifiers
64 * @return An absolute schema node identifier
65 * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
66 * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
68 public static @NonNull Absolute of(final Collection<QName> nodeIdentifiers) {
69 final var qnames = checkQNames(nodeIdentifiers);
70 return qnames.size() == 1 ? of(qnames.get(0)) : new AbsoluteMultiple(qnames);
74 * Return an interned reference to an equivalent object.
76 * @return An interned reference, or this object if it was previously interned.
78 public final @NonNull Absolute intern() {
79 return INTERNER.intern(this);
83 final String className() {
89 * A descendant schema node identifier.
91 public abstract static sealed class Descendant extends SchemaNodeIdentifier {
93 * Create a descendant schema node identifier composed of a single node identifier.
95 * @param nodeIdentifier Single node identifier
96 * @return A descendant schema node identifier
97 * @throws NullPointerException if {@code nodeIdentifier} is null
99 public static @NonNull Descendant of(final QName nodeIdentifier) {
100 return new DescendantSingle(nodeIdentifier);
104 * Create a descendant schema node identifier composed of multiple node identifiers.
106 * @param nodeIdentifiers Node identifiers
107 * @return A descendant schema node identifier
108 * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
109 * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
111 public static @NonNull Descendant of(final QName... nodeIdentifiers) {
112 return of(Arrays.asList(nodeIdentifiers));
116 * Create a descendant schema node identifier composed of multiple node identifiers.
118 * @param nodeIdentifiers Node identifiers
119 * @return A descendant schema node identifier
120 * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
121 * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
123 public static @NonNull Descendant of(final Collection<QName> nodeIdentifiers) {
124 final var qnames = checkQNames(nodeIdentifiers);
125 return qnames.size() == 1 ? of(qnames.get(0)) : new DescandantMultiple(qnames);
129 final String className() {
134 private static final class AbsoluteSingle extends Absolute {
135 private final @NonNull QName qname;
137 AbsoluteSingle(final QName qname) {
138 this.qname = requireNonNull(qname);
142 public ImmutableList<QName> getNodeIdentifiers() {
143 return ImmutableList.of(qname);
147 public QName firstNodeIdentifier() {
152 public QName lastNodeIdentifier() {
157 Object pathObject() {
162 private static final class AbsoluteMultiple extends Absolute {
163 private final @NonNull ImmutableList<QName> qnames;
165 AbsoluteMultiple(final ImmutableList<QName> qnames) {
166 this.qnames = requireNonNull(qnames);
170 public ImmutableList<QName> getNodeIdentifiers() {
175 Object pathObject() {
180 private static final class DescendantSingle extends Descendant {
181 private final @NonNull QName qname;
183 DescendantSingle(final QName qname) {
184 this.qname = requireNonNull(qname);
188 public ImmutableList<QName> getNodeIdentifiers() {
189 return ImmutableList.of(qname);
193 public QName firstNodeIdentifier() {
198 public QName lastNodeIdentifier() {
203 Object pathObject() {
208 private static final class DescandantMultiple extends Descendant {
209 private final @NonNull ImmutableList<QName> qnames;
211 DescandantMultiple(final ImmutableList<QName> qnames) {
212 this.qnames = requireNonNull(qnames);
216 public ImmutableList<QName> getNodeIdentifiers() {
221 Object pathObject() {
227 private volatile int hash;
230 * Return the non-empty sequence of node identifiers which constitute this schema node identifier.
232 * @return Non-empty sequence of node identifiers
234 public abstract @NonNull List<QName> getNodeIdentifiers();
237 * Return the first node identifier. This method is equivalent to {@code getNodeIdentifiers().get(0)}, but is
238 * potentially more efficient.
240 * @return The first node identifier
242 public @NonNull QName firstNodeIdentifier() {
243 return getNodeIdentifiers().get(0);
247 * Return the last node identifier. This method is equivalent to {@code getNodeIdentifiers().get(size - 1)}, but
248 * is potentially more efficient.
250 * @return The last node identifier
252 public @NonNull QName lastNodeIdentifier() {
253 final var local = getNodeIdentifiers();
254 return local.get(local.size() - 1);
258 public final int hashCode() {
260 return (local = hash) != 0 ? local : (hash = pathObject().hashCode());
264 public final boolean equals(final Object obj) {
265 return this == obj || obj != null && getClass() == obj.getClass()
266 && pathObject().equals(((SchemaNodeIdentifier) obj).pathObject());
270 public final String toString() {
271 return MoreObjects.toStringHelper(className()).add("qnames", toStringQNames()).toString();
274 abstract @NonNull Object pathObject();
276 abstract @NonNull String className();
278 private List<?> toStringQNames() {
279 final var ids = getNodeIdentifiers();
280 return ids.size() < 2 ? ids : simplifyQNames(ids);
283 private static ImmutableList<QName> checkQNames(final Collection<QName> qnames) {
284 final var ret = ImmutableList.copyOf(qnames);
285 checkArgument(!ret.isEmpty(), "SchemaNodeIdentifier has to have at least one node identifier");
289 private static List<?> simplifyQNames(final List<QName> qnames) {
290 final var ret = new ArrayList<>(qnames.size());
292 QNameModule prev = null;
293 for (var qname : qnames) {
294 final var module = qname.getModule();
295 ret.add(module.equals(prev) ? qname.getLocalName() : qname);