Fixup SchemaNodeIdentifier design
[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 java.util.Arrays;
16 import java.util.Collection;
17 import java.util.List;
18 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.yangtools.concepts.Immutable;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
23
24 /**
25  * Represents unique path to the every schema node inside the schema node identifier namespace. This concept is defined
26  * in <a href="https://tools.ietf.org/html/rfc7950#section-6.5">RFC7950</a>.
27  */
28 public abstract class SchemaNodeIdentifier implements Immutable {
29     /**
30      * An absolute schema node identifier.
31      */
32     public static final class Absolute extends SchemaNodeIdentifier {
33         Absolute(final QName qname) {
34             super(qname);
35         }
36
37         Absolute(final Collection<QName> qnames) {
38             super(qnames);
39         }
40
41         public static Absolute of(final QName nodeIdentifier) {
42             return new Absolute(nodeIdentifier);
43         }
44
45         public static Absolute of(final QName... nodeIdentifiers) {
46             return new Absolute(Arrays.asList(nodeIdentifiers));
47         }
48
49         public static Absolute of(final Collection<QName> nodeIdentifiers) {
50             return new Absolute(ImmutableList.copyOf(nodeIdentifiers));
51         }
52
53         @Override
54         SchemaPath implicitSchemaPathParent() {
55             return SchemaPath.ROOT;
56         }
57     }
58
59     /**
60      * A descendant schema node identifier.
61      */
62     public static final class Descendant extends SchemaNodeIdentifier {
63         Descendant(final QName qname) {
64             super(qname);
65         }
66
67         Descendant(final Collection<QName> qnames) {
68             super(qnames);
69         }
70
71         public static Descendant of(final QName nodeIdentifier) {
72             return new Descendant(nodeIdentifier);
73         }
74
75         public static Descendant of(final QName... nodeIdentifiers) {
76             return new Descendant(Arrays.asList(nodeIdentifiers));
77         }
78
79         public static Descendant of(final Collection<QName> nodeIdentifiers) {
80             return new Descendant(nodeIdentifiers);
81         }
82
83         @Override
84         SchemaPath implicitSchemaPathParent() {
85             return SchemaPath.SAME;
86         }
87     }
88
89     private static final AtomicReferenceFieldUpdater<SchemaNodeIdentifier, SchemaPath> SCHEMAPATH_UPDATER =
90             AtomicReferenceFieldUpdater.newUpdater(SchemaNodeIdentifier.class, SchemaPath.class, "schemaPath");
91
92     private final @NonNull Object qnames;
93
94     // Cached SchemaPath.
95     private volatile SchemaPath schemaPath;
96     // Cached hashCode
97     private volatile int hash;
98
99     SchemaNodeIdentifier(final QName qname) {
100         this.qnames = requireNonNull(qname);
101     }
102
103     SchemaNodeIdentifier(final Collection<QName> qnames) {
104         final ImmutableList<QName> tmp = ImmutableList.copyOf(qnames);
105         checkArgument(!tmp.isEmpty());
106         this.qnames = tmp.size() == 1 ? tmp.get(0) : tmp;
107     }
108
109     public @NonNull List<QName> getNodeIdentifiers() {
110         return qnames instanceof QName ? ImmutableList.of((QName) qnames) : (ImmutableList<QName>) qnames;
111     }
112
113     /**
114      * Create the {@link SchemaPath} equivalent of this identifier.
115      *
116      * @return SchemaPath equivalent.
117      */
118     public final @NonNull SchemaPath asSchemaPath() {
119         final SchemaPath ret = schemaPath;
120         return ret != null ? ret : loadSchemaPath();
121     }
122
123     private SchemaPath loadSchemaPath() {
124         final SchemaPath newPath = implicitSchemaPathParent().createChild(getNodeIdentifiers());
125         return SCHEMAPATH_UPDATER.compareAndSet(this, null, newPath) ? newPath : schemaPath;
126     }
127
128     abstract SchemaPath implicitSchemaPathParent();
129
130     @Override
131     public final int hashCode() {
132         final int local;
133         return (local = hash) != 0 ? local : (hash = qnames.hashCode());
134     }
135
136     @Override
137     public final boolean equals(final Object obj) {
138         if (this == obj) {
139             return true;
140         }
141         if (obj == null || getClass() != obj.getClass()) {
142             return false;
143         }
144         return qnames.equals(((SchemaNodeIdentifier) obj).qnames);
145     }
146
147     @Override
148     public final String toString() {
149         return MoreObjects.toStringHelper(this).add("qnames", qnames).toString();
150     }
151 }