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 java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
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;
26 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
29 * Represents unique path to the every schema node inside the schema node identifier namespace. This concept is defined
30 * in <a href="https://tools.ietf.org/html/rfc7950#section-6.5">RFC7950</a>.
32 public abstract class SchemaNodeIdentifier implements Immutable {
34 * An absolute schema node identifier.
36 public static final class Absolute extends SchemaNodeIdentifier {
37 private static final Interner<Absolute> INTERNER = Interners.newWeakInterner();
39 Absolute(final QName qname) {
43 Absolute(final Collection<QName> qnames) {
47 public static @NonNull Absolute of(final QName nodeIdentifier) {
48 return new Absolute(nodeIdentifier);
51 public static @NonNull Absolute of(final QName... nodeIdentifiers) {
52 return new Absolute(Arrays.asList(nodeIdentifiers));
55 public static @NonNull Absolute of(final Collection<QName> nodeIdentifiers) {
56 return new Absolute(nodeIdentifiers);
59 public @NonNull Absolute intern() {
60 return INTERNER.intern(this);
64 SchemaPath implicitSchemaPathParent() {
65 return SchemaPath.ROOT;
70 * A descendant schema node identifier.
72 public static final class Descendant extends SchemaNodeIdentifier {
73 Descendant(final QName qname) {
77 Descendant(final Collection<QName> qnames) {
81 public static @NonNull Descendant of(final QName nodeIdentifier) {
82 return new Descendant(nodeIdentifier);
85 public static @NonNull Descendant of(final QName... nodeIdentifiers) {
86 return new Descendant(Arrays.asList(nodeIdentifiers));
89 public static @NonNull Descendant of(final Collection<QName> nodeIdentifiers) {
90 return new Descendant(nodeIdentifiers);
94 SchemaPath implicitSchemaPathParent() {
95 return SchemaPath.SAME;
99 private static final AtomicReferenceFieldUpdater<SchemaNodeIdentifier, SchemaPath> SCHEMAPATH_UPDATER =
100 AtomicReferenceFieldUpdater.newUpdater(SchemaNodeIdentifier.class, SchemaPath.class, "schemaPath");
102 private final @NonNull Object qnames;
104 // Cached SchemaPath.
105 private volatile SchemaPath schemaPath;
107 private volatile int hash;
109 SchemaNodeIdentifier(final QName qname) {
110 this.qnames = requireNonNull(qname);
113 SchemaNodeIdentifier(final Collection<QName> qnames) {
114 final ImmutableList<QName> tmp = ImmutableList.copyOf(qnames);
115 checkArgument(!tmp.isEmpty(), "SchemaNodeIdentifier has to have at least one node identifier");
116 this.qnames = tmp.size() == 1 ? tmp.get(0) : tmp;
119 public @NonNull List<QName> getNodeIdentifiers() {
120 return qnames instanceof QName ? ImmutableList.of((QName) qnames) : (ImmutableList<QName>) qnames;
124 * Create the {@link SchemaPath} equivalent of this identifier.
126 * @return SchemaPath equivalent.
128 public final @NonNull SchemaPath asSchemaPath() {
129 final SchemaPath ret = schemaPath;
130 return ret != null ? ret : loadSchemaPath();
133 private SchemaPath loadSchemaPath() {
134 final SchemaPath newPath = implicitSchemaPathParent().createChild(getNodeIdentifiers());
135 return SCHEMAPATH_UPDATER.compareAndSet(this, null, newPath) ? newPath : schemaPath;
138 abstract SchemaPath implicitSchemaPathParent();
141 public final int hashCode() {
143 return (local = hash) != 0 ? local : (hash = qnames.hashCode());
147 public final boolean equals(final Object obj) {
151 if (obj == null || getClass() != obj.getClass()) {
154 return qnames.equals(((SchemaNodeIdentifier) obj).qnames);
158 public final String toString() {
159 return MoreObjects.toStringHelper(this).add("qnames", toStringQNames()).toString();
162 private List<?> toStringQNames() {
163 final List<QName> ids = getNodeIdentifiers();
164 return ids.size() < 2 ? ids : simplifyQNames(ids);
167 private static List<?> simplifyQNames(final List<QName> qnames) {
168 final List<Object> ret = new ArrayList<>(qnames.size());
170 QNameModule prev = null;
171 for (QName qname : qnames) {
172 final QNameModule module = qname.getModule();
173 ret.add(module.equals(prev) ? qname.getLocalName() : qname);