2 * Copyright (c) 2014 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.data.api;
10 import java.io.Serializable;
11 import java.util.List;
15 import org.opendaylight.yangtools.concepts.Builder;
16 import org.opendaylight.yangtools.concepts.Immutable;
17 import org.opendaylight.yangtools.concepts.Path;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
25 import com.google.common.base.Optional;
26 import com.google.common.base.Preconditions;
27 import com.google.common.collect.ImmutableList;
28 import com.google.common.collect.ImmutableMap;
29 import com.google.common.collect.ImmutableSet;
31 public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable, Serializable {
33 private static final long serialVersionUID = 8467409862384206193L;
34 private final List<PathArgument> path;
36 private transient String toStringCache = null;
37 private transient Integer hashCodeCache = null;
39 public List<PathArgument> getPath() {
43 public InstanceIdentifier(final List<? extends PathArgument> path) {
44 this.path = ImmutableList.copyOf(path);
47 private InstanceIdentifier(final NodeIdentifier nodeIdentifier) {
48 this.path = ImmutableList.<PathArgument> of(nodeIdentifier);
52 public int hashCode() {
54 * The hashCodeCache is safe, since the object contract requires
55 * immutability of the object and all objects referenced from this
58 * Used lists, maps are immutable. Path Arguments (elements) are also
59 * immutable, since the PathArgument contract requires immutability.
61 * The cache is thread-safe - if multiple computations occurs at the
62 * same time, cache will be overwritten with same result.
64 if (hashCodeCache == null) {
67 result = prime * result + ((path == null) ? 0 : path.hashCode());
68 hashCodeCache = result;
74 public boolean equals(final Object obj) {
81 if (getClass() != obj.getClass()) {
84 InstanceIdentifier other = (InstanceIdentifier) obj;
85 if (this.hashCode() != obj.hashCode()) {
89 if (other.path != null) {
92 } else if (!path.equals(other.path)) {
98 public InstanceIdentifier node(final QName name) {
99 return node(new NodeIdentifier(name));
102 public InstanceIdentifier node(final PathArgument arg) {
103 return new InstanceIdentifier(ImmutableList.<PathArgument>builder().addAll(path).add(arg).build());
107 * Get the relative path from an ancestor. This method attempts to perform the reverse
108 * of concatenating a base (ancestor) and a path.
110 * @param ancestor Ancestor against which the relative path should be calculated
111 * @return This object's relative path from parent, or Optional.absent() if the
112 * specified parent is not in fact an ancestor of this object.
114 public Optional<InstanceIdentifier> relativeTo(final InstanceIdentifier ancestor) {
115 if (ancestor.contains(this)) {
116 final int common = ancestor.path.size();
117 return Optional.of(new InstanceIdentifier(path.subList(common, path.size())));
119 return Optional.absent();
123 // Static factories & helpers
125 public static InstanceIdentifier of(final QName name) {
126 return new InstanceIdentifier(new NodeIdentifier(name));
129 static public InstanceIdentifierBuilder builder() {
130 return new BuilderImpl();
133 static public InstanceIdentifierBuilder builder(final InstanceIdentifier origin) {
134 return new BuilderImpl(origin.getPath());
137 public static InstanceIdentifierBuilder builder(final QName node) {
138 return builder().node(node);
141 public interface PathArgument extends Immutable, Serializable {
144 * If applicable returns uniqee QName of data node as defined in YANG
147 * This method may return null, if the corresponding schema node, does
148 * not have QName associated, such as in cases of augmentations.
156 public interface InstanceIdentifierBuilder extends Builder<InstanceIdentifier> {
157 InstanceIdentifierBuilder node(QName nodeType);
159 InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
161 InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
164 InstanceIdentifier getIdentifier();
166 InstanceIdentifier build();
170 * Simple path argument identifying a {@link ContainerNode} or {@link LeafNode} leaf
173 public static final class NodeIdentifier implements PathArgument, Comparable<NodeIdentifier> {
174 private static final long serialVersionUID = -2255888212390871347L;
175 private final QName nodeType;
177 public NodeIdentifier(final QName node) {
178 this.nodeType = Preconditions.checkNotNull(node);
182 public QName getNodeType() {
187 public int hashCode() {
188 return 31 + nodeType.hashCode();
192 public boolean equals(final Object obj) {
196 if (!(obj instanceof NodeIdentifier)) {
199 final NodeIdentifier other = (NodeIdentifier) obj;
200 return nodeType.equals(other.nodeType);
204 public String toString() {
205 return nodeType.toString();
209 public int compareTo(final NodeIdentifier o) {
210 return nodeType.compareTo(o.nodeType);
215 * Composite path argument identifying a {@link MapEntryNode} leaf
218 public static final class NodeIdentifierWithPredicates implements PathArgument {
219 private static final long serialVersionUID = -4787195606494761540L;
221 private final QName nodeType;
222 private final Map<QName, Object> keyValues;
224 public NodeIdentifierWithPredicates(final QName node, final Map<QName, Object> keyValues) {
225 this.nodeType = Preconditions.checkNotNull(node);
226 this.keyValues = ImmutableMap.copyOf(keyValues);
229 public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
230 this.nodeType = node;
231 this.keyValues = ImmutableMap.of(key, value);
235 public QName getNodeType() {
239 public Map<QName, Object> getKeyValues() {
244 public int hashCode() {
245 final int prime = 31;
247 result = prime * result + ((keyValues == null) ? 0 : keyValues.hashCode());
248 result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
253 public boolean equals(final Object obj) {
260 if (getClass() != obj.getClass()) {
263 NodeIdentifierWithPredicates other = (NodeIdentifierWithPredicates) obj;
264 if (keyValues == null) {
265 if (other.keyValues != null) {
268 } else if (!keyValues.equals(other.keyValues)) {
271 if (nodeType == null) {
272 if (other.nodeType != null) {
275 } else if (!nodeType.equals(other.nodeType)) {
282 public String toString() {
283 return nodeType + "[" + keyValues + "]";
288 * Simple path argument identifying a {@link LeafSetEntryNode} leaf
291 public static final class NodeWithValue implements PathArgument {
292 private static final long serialVersionUID = -3637456085341738431L;
294 private final QName nodeType;
295 private final Object value;
297 public NodeWithValue(final QName node, final Object value) {
298 this.nodeType = Preconditions.checkNotNull(node);
303 public QName getNodeType() {
307 public Object getValue() {
312 public int hashCode() {
313 final int prime = 31;
315 result = prime * result + ((value == null) ? 0 : value.hashCode());
316 result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
321 public boolean equals(final Object obj) {
328 if (getClass() != obj.getClass()) {
331 NodeWithValue other = (NodeWithValue) obj;
333 if (other.value != null) {
336 } else if (!value.equals(other.value)) {
339 if (nodeType == null) {
340 if (other.nodeType != null) {
343 } else if (!nodeType.equals(other.nodeType)) {
350 public String toString() {
351 return nodeType + "[" + value + "]";
357 * Composite path argument identifying a {@link AugmentationNode} leaf
360 public static final class AugmentationIdentifier implements PathArgument {
361 private static final long serialVersionUID = -8122335594681936939L;
362 private final ImmutableSet<QName> childNames;
365 public QName getNodeType() {
366 // This should rather throw exception than return always null
367 throw new UnsupportedOperationException("Augmentation node has no QName");
370 public AugmentationIdentifier(final Set<QName> childNames) {
371 this.childNames = ImmutableSet.copyOf(childNames);
375 * Augmentation node has no QName
378 public AugmentationIdentifier(final QName nodeType, final Set<QName> childNames) {
382 public Set<QName> getPossibleChildNames() {
387 public String toString() {
388 final StringBuffer sb = new StringBuffer("AugmentationIdentifier{");
389 sb.append("childNames=").append(childNames);
391 return sb.toString();
395 public boolean equals(final Object o) {
399 if (!(o instanceof AugmentationIdentifier)) {
403 AugmentationIdentifier that = (AugmentationIdentifier) o;
405 if (!childNames.equals(that.childNames)) {
413 public int hashCode() {
414 return childNames.hashCode();
418 private static class BuilderImpl implements InstanceIdentifierBuilder {
420 private final ImmutableList.Builder<PathArgument> path;
422 public BuilderImpl() {
423 path = ImmutableList.<PathArgument> builder();
426 public BuilderImpl(final List<? extends PathArgument> prefix) {
427 path = ImmutableList.<PathArgument> builder();
432 public InstanceIdentifierBuilder node(final QName nodeType) {
433 path.add(new NodeIdentifier(nodeType));
438 public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final QName key, final Object value) {
439 path.add(new NodeIdentifierWithPredicates(nodeType, key, value));
444 public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final Map<QName, Object> keyValues) {
445 path.add(new NodeIdentifierWithPredicates(nodeType, keyValues));
451 public InstanceIdentifier toInstance() {
456 public InstanceIdentifier build() {
457 return new InstanceIdentifier(path.build());
462 public InstanceIdentifier getIdentifier() {
468 public boolean contains(final InstanceIdentifier other) {
470 throw new IllegalArgumentException("other should not be null");
472 final int localSize = this.path.size();
473 final List<PathArgument> otherPath = other.getPath();
474 if (localSize > other.path.size()) {
477 for (int i = 0; i < localSize; i++) {
478 if (!path.get(i).equals(otherPath.get(i))) {
486 public String toString() {
488 * The toStringCache is safe, since the object contract requires
489 * immutability of the object and all objects referenced from this
492 * Used lists, maps are immutable. Path Arguments (elements) are also
493 * immutable, since the PathArgument contract requires immutability.
495 * The cache is thread-safe - if multiple computations occurs at the
496 * same time, cache will be overwritten with same result.
498 if (toStringCache != null) {
499 return toStringCache;
502 final StringBuilder builder = new StringBuilder('/');
503 boolean first = true;
504 for (PathArgument argument : path) {
510 builder.append(argument.toString());
513 toStringCache = builder.toString();
514 return toStringCache;