Add SchemaNodeIdentifier.Absolute.intern()
[yangtools.git] / yang / yang-model-api / src / main / java / org / opendaylight / yangtools / yang / model / api / stmt / SchemaNodeIdentifier.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.model.api.stmt;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
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;
27
28 /**
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>.
31  */
32 public abstract class SchemaNodeIdentifier implements Immutable {
33     /**
34      * An absolute schema node identifier.
35      */
36     public static final class Absolute extends SchemaNodeIdentifier {
37         private static final Interner<Absolute> INTERNER = Interners.newWeakInterner();
38
39         Absolute(final QName qname) {
40             super(qname);
41         }
42
43         Absolute(final Collection<QName> qnames) {
44             super(qnames);
45         }
46
47         public static @NonNull Absolute of(final QName nodeIdentifier) {
48             return new Absolute(nodeIdentifier);
49         }
50
51         public static @NonNull Absolute of(final QName... nodeIdentifiers) {
52             return new Absolute(Arrays.asList(nodeIdentifiers));
53         }
54
55         public static @NonNull Absolute of(final Collection<QName> nodeIdentifiers) {
56             return new Absolute(nodeIdentifiers);
57         }
58
59         public @NonNull Absolute intern() {
60             return INTERNER.intern(this);
61         }
62
63         @Override
64         SchemaPath implicitSchemaPathParent() {
65             return SchemaPath.ROOT;
66         }
67     }
68
69     /**
70      * A descendant schema node identifier.
71      */
72     public static final class Descendant extends SchemaNodeIdentifier {
73         Descendant(final QName qname) {
74             super(qname);
75         }
76
77         Descendant(final Collection<QName> qnames) {
78             super(qnames);
79         }
80
81         public static @NonNull Descendant of(final QName nodeIdentifier) {
82             return new Descendant(nodeIdentifier);
83         }
84
85         public static @NonNull Descendant of(final QName... nodeIdentifiers) {
86             return new Descendant(Arrays.asList(nodeIdentifiers));
87         }
88
89         public static @NonNull Descendant of(final Collection<QName> nodeIdentifiers) {
90             return new Descendant(nodeIdentifiers);
91         }
92
93         @Override
94         SchemaPath implicitSchemaPathParent() {
95             return SchemaPath.SAME;
96         }
97     }
98
99     private static final AtomicReferenceFieldUpdater<SchemaNodeIdentifier, SchemaPath> SCHEMAPATH_UPDATER =
100             AtomicReferenceFieldUpdater.newUpdater(SchemaNodeIdentifier.class, SchemaPath.class, "schemaPath");
101
102     private final @NonNull Object qnames;
103
104     // Cached SchemaPath.
105     private volatile SchemaPath schemaPath;
106     // Cached hashCode
107     private volatile int hash;
108
109     SchemaNodeIdentifier(final QName qname) {
110         this.qnames = requireNonNull(qname);
111     }
112
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;
117     }
118
119     public @NonNull List<QName> getNodeIdentifiers() {
120         return qnames instanceof QName ? ImmutableList.of((QName) qnames) : (ImmutableList<QName>) qnames;
121     }
122
123     /**
124      * Create the {@link SchemaPath} equivalent of this identifier.
125      *
126      * @return SchemaPath equivalent.
127      */
128     public final @NonNull SchemaPath asSchemaPath() {
129         final SchemaPath ret = schemaPath;
130         return ret != null ? ret : loadSchemaPath();
131     }
132
133     private SchemaPath loadSchemaPath() {
134         final SchemaPath newPath = implicitSchemaPathParent().createChild(getNodeIdentifiers());
135         return SCHEMAPATH_UPDATER.compareAndSet(this, null, newPath) ? newPath : schemaPath;
136     }
137
138     abstract SchemaPath implicitSchemaPathParent();
139
140     @Override
141     public final int hashCode() {
142         final int local;
143         return (local = hash) != 0 ? local : (hash = qnames.hashCode());
144     }
145
146     @Override
147     public final boolean equals(final Object obj) {
148         if (this == obj) {
149             return true;
150         }
151         if (obj == null || getClass() != obj.getClass()) {
152             return false;
153         }
154         return qnames.equals(((SchemaNodeIdentifier) obj).qnames);
155     }
156
157     @Override
158     public final String toString() {
159         return MoreObjects.toStringHelper(this).add("qnames", toStringQNames()).toString();
160     }
161
162     private List<?> toStringQNames() {
163         final List<QName> ids = getNodeIdentifiers();
164         return ids.size() < 2 ? ids : simplifyQNames(ids);
165     }
166
167     private static List<?> simplifyQNames(final List<QName> qnames) {
168         final List<Object> ret = new ArrayList<>(qnames.size());
169
170         QNameModule prev = null;
171         for (QName qname : qnames) {
172             final QNameModule module = qname.getModule();
173             ret.add(module.equals(prev) ? qname.getLocalName() : qname);
174             prev = module;
175         }
176
177         return ret;
178     }
179 }