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 org.eclipse.jdt.annotation.NonNull;
23 import org.opendaylight.yangtools.concepts.Immutable;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.common.QNameModule;
28 * Represents unique path to every schema node inside the schema node identifier namespace. This concept is defined
29 * in <a href="https://tools.ietf.org/html/rfc7950#section-6.5">RFC7950</a>.
31 public abstract class SchemaNodeIdentifier implements Immutable {
33 * An absolute schema node identifier.
35 public abstract static class Absolute extends SchemaNodeIdentifier {
36 private static final Interner<Absolute> INTERNER = Interners.newWeakInterner();
43 * Create an absolute schema node identifier composed of a single node identifier.
45 * @param nodeIdentifier Single node identifier
46 * @return An absolute schema node identifier
47 * @throws NullPointerException if {@code nodeIdentifier} is null
49 public static @NonNull Absolute of(final QName nodeIdentifier) {
50 return new AbsoluteSingle(nodeIdentifier);
54 * Create an absolute schema node identifier composed of multiple node identifiers.
56 * @param nodeIdentifiers Node identifiers
57 * @return An absolute schema node identifier
58 * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
59 * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
61 public static @NonNull Absolute of(final QName... nodeIdentifiers) {
62 return of(Arrays.asList(nodeIdentifiers));
66 * Create an absolute schema node identifier composed of multiple node identifiers.
68 * @param nodeIdentifiers Node identifiers
69 * @return An absolute schema node identifier
70 * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
71 * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
73 public static @NonNull Absolute of(final Collection<QName> nodeIdentifiers) {
74 final ImmutableList<QName> qnames = checkQNames(nodeIdentifiers);
75 return qnames.size() == 1 ? of(qnames.get(0)) : new AbsoluteMultiple(qnames);
79 * Return an interned reference to an equivalent object.
81 * @return An interned reference, or this object if it was previously interned.
83 public final @NonNull Absolute intern() {
84 return INTERNER.intern(this);
88 final String className() {
94 * A descendant schema node identifier.
96 public abstract static class Descendant extends SchemaNodeIdentifier {
102 * Create a descendant schema node identifier composed of a single node identifier.
104 * @param nodeIdentifier Single node identifier
105 * @return A descendant schema node identifier
106 * @throws NullPointerException if {@code nodeIdentifier} is null
108 public static @NonNull Descendant of(final QName nodeIdentifier) {
109 return new DescendantSingle(nodeIdentifier);
113 * Create a descendant schema node identifier composed of multiple node identifiers.
115 * @param nodeIdentifiers Node identifiers
116 * @return A descendant schema node identifier
117 * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
118 * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
120 public static @NonNull Descendant of(final QName... nodeIdentifiers) {
121 return of(Arrays.asList(nodeIdentifiers));
125 * Create a descendant schema node identifier composed of multiple node identifiers.
127 * @param nodeIdentifiers Node identifiers
128 * @return A descendant schema node identifier
129 * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
130 * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
132 public static @NonNull Descendant of(final Collection<QName> nodeIdentifiers) {
133 final ImmutableList<QName> qnames = checkQNames(nodeIdentifiers);
134 return qnames.size() == 1 ? of(qnames.get(0)) : new DescandantMultiple(qnames);
138 final String className() {
143 private static final class AbsoluteSingle extends Absolute {
144 private final @NonNull QName qname;
146 AbsoluteSingle(final QName qname) {
147 this.qname = requireNonNull(qname);
151 public ImmutableList<QName> getNodeIdentifiers() {
152 return ImmutableList.of(qname);
156 public QName firstNodeIdentifier() {
161 public QName lastNodeIdentifier() {
166 Object pathObject() {
171 private static final class AbsoluteMultiple extends Absolute {
172 private final @NonNull ImmutableList<QName> qnames;
174 AbsoluteMultiple(final ImmutableList<QName> qnames) {
175 this.qnames = requireNonNull(qnames);
179 public ImmutableList<QName> getNodeIdentifiers() {
184 Object pathObject() {
189 private static final class DescendantSingle extends Descendant {
190 private final @NonNull QName qname;
192 DescendantSingle(final QName qname) {
193 this.qname = requireNonNull(qname);
197 public ImmutableList<QName> getNodeIdentifiers() {
198 return ImmutableList.of(qname);
202 public QName firstNodeIdentifier() {
207 public QName lastNodeIdentifier() {
212 Object pathObject() {
217 private static final class DescandantMultiple extends Descendant {
218 private final @NonNull ImmutableList<QName> qnames;
220 DescandantMultiple(final ImmutableList<QName> qnames) {
221 this.qnames = requireNonNull(qnames);
225 public ImmutableList<QName> getNodeIdentifiers() {
230 Object pathObject() {
236 private volatile int hash;
238 SchemaNodeIdentifier() {
243 * Return the non-empty sequence of node identifiers which constitute this schema node identifier.
245 * @return Non-empty sequence of node identifiers
247 public abstract @NonNull List<QName> getNodeIdentifiers();
250 * Return the first node identifier. This method is equivalent to {@code getNodeIdentifiers().get(0)}, but is
251 * potentially more efficient.
253 * @return The first node identifier
255 public @NonNull QName firstNodeIdentifier() {
256 return getNodeIdentifiers().get(0);
260 * Return the last node identifier. This method is equivalent to {@code getNodeIdentifiers().get(size - 1)}, but
261 * is potentially more efficient.
263 * @return The last node identifier
265 public @NonNull QName lastNodeIdentifier() {
266 final List<QName> local = getNodeIdentifiers();
267 return local.get(local.size() - 1);
271 public final int hashCode() {
273 return (local = hash) != 0 ? local : (hash = pathObject().hashCode());
277 public final boolean equals(final Object obj) {
278 return this == obj || obj != null && getClass() == obj.getClass()
279 && pathObject().equals(((SchemaNodeIdentifier) obj).pathObject());
283 public final String toString() {
284 return MoreObjects.toStringHelper(className()).add("qnames", toStringQNames()).toString();
287 abstract @NonNull Object pathObject();
289 abstract @NonNull String className();
291 private List<?> toStringQNames() {
292 final List<QName> ids = getNodeIdentifiers();
293 return ids.size() < 2 ? ids : simplifyQNames(ids);
296 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
297 justification = "https://github.com/spotbugs/spotbugs/issues/811")
298 private static ImmutableList<QName> checkQNames(final Collection<QName> qnames) {
299 final ImmutableList<QName> ret = ImmutableList.copyOf(qnames);
300 checkArgument(!ret.isEmpty(), "SchemaNodeIdentifier has to have at least one node identifier");
304 private static List<?> simplifyQNames(final List<QName> qnames) {
305 final List<Object> ret = new ArrayList<>(qnames.size());
307 QNameModule prev = null;
308 for (QName qname : qnames) {
309 final QNameModule module = qname.getModule();
310 ret.add(module.equals(prev) ? qname.getLocalName() : qname);