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://tools.ietf.org/html/rfc7950#section-6.5">RFC7950</a>.
30 public abstract class SchemaNodeIdentifier implements Immutable {
32 * An absolute schema node identifier.
34 public abstract static class Absolute extends SchemaNodeIdentifier {
35 private static final Interner<Absolute> INTERNER = Interners.newWeakInterner();
42 * Create an absolute schema node identifier composed of a single node identifier.
44 * @param nodeIdentifier Single node identifier
45 * @return An absolute schema node identifier
46 * @throws NullPointerException if {@code nodeIdentifier} is null
48 public static @NonNull Absolute of(final QName nodeIdentifier) {
49 return new AbsoluteSingle(nodeIdentifier);
53 * Create an absolute schema node identifier composed of multiple node identifiers.
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
60 public static @NonNull Absolute of(final QName... nodeIdentifiers) {
61 return of(Arrays.asList(nodeIdentifiers));
65 * Create an absolute schema node identifier composed of multiple node identifiers.
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
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);
78 * Return an interned reference to an equivalent object.
80 * @return An interned reference, or this object if it was previously interned.
82 public final @NonNull Absolute intern() {
83 return INTERNER.intern(this);
87 final String className() {
93 * A descendant schema node identifier.
95 public abstract static class Descendant extends SchemaNodeIdentifier {
101 * Create a descendant schema node identifier composed of a single node identifier.
103 * @param nodeIdentifier Single node identifier
104 * @return A descendant schema node identifier
105 * @throws NullPointerException if {@code nodeIdentifier} is null
107 public static @NonNull Descendant of(final QName nodeIdentifier) {
108 return new DescendantSingle(nodeIdentifier);
112 * Create a descendant schema node identifier composed of multiple node identifiers.
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
119 public static @NonNull Descendant of(final QName... nodeIdentifiers) {
120 return of(Arrays.asList(nodeIdentifiers));
124 * Create a descendant schema node identifier composed of multiple node identifiers.
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
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);
137 final String className() {
142 private static final class AbsoluteSingle extends Absolute {
143 private final @NonNull QName qname;
145 AbsoluteSingle(final QName qname) {
146 this.qname = requireNonNull(qname);
150 public ImmutableList<QName> getNodeIdentifiers() {
151 return ImmutableList.of(qname);
155 public QName firstNodeIdentifier() {
160 public QName lastNodeIdentifier() {
165 Object pathObject() {
170 private static final class AbsoluteMultiple extends Absolute {
171 private final @NonNull ImmutableList<QName> qnames;
173 AbsoluteMultiple(final ImmutableList<QName> qnames) {
174 this.qnames = requireNonNull(qnames);
178 public ImmutableList<QName> getNodeIdentifiers() {
183 Object pathObject() {
188 private static final class DescendantSingle extends Descendant {
189 private final @NonNull QName qname;
191 DescendantSingle(final QName qname) {
192 this.qname = requireNonNull(qname);
196 public ImmutableList<QName> getNodeIdentifiers() {
197 return ImmutableList.of(qname);
201 public QName firstNodeIdentifier() {
206 public QName lastNodeIdentifier() {
211 Object pathObject() {
216 private static final class DescandantMultiple extends Descendant {
217 private final @NonNull ImmutableList<QName> qnames;
219 DescandantMultiple(final ImmutableList<QName> qnames) {
220 this.qnames = requireNonNull(qnames);
224 public ImmutableList<QName> getNodeIdentifiers() {
229 Object pathObject() {
235 private volatile int hash;
237 SchemaNodeIdentifier() {
242 * Return the non-empty sequence of node identifiers which constitute this schema node identifier.
244 * @return Non-empty sequence of node identifiers
246 public abstract @NonNull List<QName> getNodeIdentifiers();
249 * Return the first node identifier. This method is equivalent to {@code getNodeIdentifiers().get(0)}, but is
250 * potentially more efficient.
252 * @return The first node identifier
254 public @NonNull QName firstNodeIdentifier() {
255 return getNodeIdentifiers().get(0);
259 * Return the last node identifier. This method is equivalent to {@code getNodeIdentifiers().get(size - 1)}, but
260 * is potentially more efficient.
262 * @return The last node identifier
264 public @NonNull QName lastNodeIdentifier() {
265 final List<QName> local = getNodeIdentifiers();
266 return local.get(local.size() - 1);
270 public final int hashCode() {
272 return (local = hash) != 0 ? local : (hash = pathObject().hashCode());
276 public final boolean equals(final Object obj) {
277 return this == obj || obj != null && getClass() == obj.getClass()
278 && pathObject().equals(((SchemaNodeIdentifier) obj).pathObject());
282 public final String toString() {
283 return MoreObjects.toStringHelper(className()).add("qnames", toStringQNames()).toString();
286 abstract @NonNull Object pathObject();
288 abstract @NonNull String className();
290 private List<?> toStringQNames() {
291 final List<QName> ids = getNodeIdentifiers();
292 return ids.size() < 2 ? ids : simplifyQNames(ids);
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");
301 private static List<?> simplifyQNames(final List<QName> qnames) {
302 final List<Object> ret = new ArrayList<>(qnames.size());
304 QNameModule prev = null;
305 for (QName qname : qnames) {
306 final QNameModule module = qname.getModule();
307 ret.add(module.equals(prev) ? qname.getLocalName() : qname);