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