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.collect.ImmutableList;
27 import com.google.common.collect.ImmutableMap;
28 import com.google.common.collect.ImmutableSet;
30 public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable, Serializable {
32 private static final long serialVersionUID = 8467409862384206193L;
33 private final List<PathArgument> path;
35 private transient String toStringCache = null;
36 private transient Integer hashCodeCache = null;
38 public List<PathArgument> getPath() {
42 public InstanceIdentifier(final List<? extends PathArgument> path) {
43 this.path = ImmutableList.copyOf(path);
46 private InstanceIdentifier(final NodeIdentifier nodeIdentifier) {
47 this.path = ImmutableList.<PathArgument> of(nodeIdentifier);
51 public int hashCode() {
53 * The hashCodeCache is safe, since the object contract requires
54 * immutability of the object and all objects referenced from this
57 * Used lists, maps are immutable. Path Arguments (elements) are also
58 * immutable, since the PathArgument contract requires immutability.
60 * The cache is thread-safe - if multiple computations occurs at the
61 * same time, cache will be overwritten with same result.
63 if (hashCodeCache == null) {
66 result = prime * result + ((path == null) ? 0 : path.hashCode());
67 hashCodeCache = result;
73 public boolean equals(final Object obj) {
80 if (getClass() != obj.getClass()) {
83 InstanceIdentifier other = (InstanceIdentifier) obj;
84 if (this.hashCode() != obj.hashCode()) {
88 if (other.path != null) {
91 } else if (!path.equals(other.path)) {
97 public InstanceIdentifier node(final QName name) {
98 return node(new NodeIdentifier(name));
101 public InstanceIdentifier node(final PathArgument arg) {
102 return new InstanceIdentifier(ImmutableList.<PathArgument>builder().addAll(path).add(arg).build());
106 * Get the relative path from an ancestor. This method attempts to perform the reverse
107 * of concatenating a base (ancestor) and a path.
109 * @param ancestor Ancestor against which the relative path should be calculated
110 * @return This object's relative path from parent, or Optional.absent() if the
111 * specified parent is not in fact an ancestor of this object.
113 public Optional<InstanceIdentifier> relativeTo(final InstanceIdentifier ancestor) {
114 if (ancestor.contains(this)) {
115 final int common = ancestor.path.size();
116 return Optional.of(new InstanceIdentifier(path.subList(common, path.size())));
118 return Optional.absent();
122 // Static factories & helpers
124 public static InstanceIdentifier of(final QName name) {
125 return new InstanceIdentifier(new NodeIdentifier(name));
128 static public InstanceIdentifierBuilder builder() {
129 return new BuilderImpl();
132 static public InstanceIdentifierBuilder builder(final InstanceIdentifier origin) {
133 return new BuilderImpl(origin.getPath());
136 public static InstanceIdentifierBuilder builder(final QName node) {
137 return builder().node(node);
140 public interface PathArgument extends Immutable, Serializable {
143 * If applicable returns uniqee QName of data node as defined in YANG
146 * This method may return null, if the corresponding schema node, does
147 * not have QName associated, such as in cases of augmentations.
155 public interface InstanceIdentifierBuilder extends Builder<InstanceIdentifier> {
156 InstanceIdentifierBuilder node(QName nodeType);
158 InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
160 InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
163 InstanceIdentifier getIdentifier();
165 InstanceIdentifier build();
169 * Simple path argument identifying a {@link ContainerNode} or {@link LeafNode} leaf
173 public static final class NodeIdentifier implements PathArgument {
178 private static final long serialVersionUID = -2255888212390871347L;
180 private final QName nodeType;
182 public NodeIdentifier(final QName node) {
183 this.nodeType = node;
187 public QName getNodeType() {
192 public int hashCode() {
193 final int prime = 31;
195 result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
200 public boolean equals(final Object obj) {
207 if (getClass() != obj.getClass()) {
210 NodeIdentifier other = (NodeIdentifier) obj;
211 if (nodeType == null) {
212 if (other.nodeType != null) {
215 } else if (!nodeType.equals(other.nodeType)) {
222 public String toString() {
223 return nodeType.toString();
229 * Composite path argument identifying a {@link MapEntryNode} leaf
233 public static final class NodeIdentifierWithPredicates implements PathArgument {
238 private static final long serialVersionUID = -4787195606494761540L;
240 private final QName nodeType;
241 private final Map<QName, Object> keyValues;
243 public NodeIdentifierWithPredicates(final QName node, final Map<QName, Object> keyValues) {
244 this.nodeType = node;
245 this.keyValues = ImmutableMap.copyOf(keyValues);
248 public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
249 this.nodeType = node;
250 this.keyValues = ImmutableMap.of(key, value);
254 public QName getNodeType() {
258 public Map<QName, Object> getKeyValues() {
263 public int hashCode() {
264 final int prime = 31;
266 result = prime * result + ((keyValues == null) ? 0 : keyValues.hashCode());
267 result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
272 public boolean equals(final Object obj) {
279 if (getClass() != obj.getClass()) {
282 NodeIdentifierWithPredicates other = (NodeIdentifierWithPredicates) obj;
283 if (keyValues == null) {
284 if (other.keyValues != null) {
287 } else if (!keyValues.equals(other.keyValues)) {
290 if (nodeType == null) {
291 if (other.nodeType != null) {
294 } else if (!nodeType.equals(other.nodeType)) {
301 public String toString() {
302 return nodeType + "[" + keyValues + "]";
307 * Simple path argument identifying a {@link LeafSetEntryNode} leaf
311 public static final class NodeWithValue implements PathArgument {
315 * Composite path argument identifying a {@link AugmentationNode} leaf
319 private static final long serialVersionUID = -3637456085341738431L;
321 private final QName nodeType;
322 private final Object value;
324 public NodeWithValue(final QName node, final Object value) {
325 this.nodeType = node;
330 public QName getNodeType() {
334 public Object getValue() {
339 public int hashCode() {
340 final int prime = 31;
342 result = prime * result + ((value == null) ? 0 : value.hashCode());
343 result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
348 public boolean equals(final Object obj) {
355 if (getClass() != obj.getClass()) {
358 NodeWithValue other = (NodeWithValue) obj;
360 if (other.value != null) {
363 } else if (!value.equals(other.value)) {
366 if (nodeType == null) {
367 if (other.nodeType != null) {
370 } else if (!nodeType.equals(other.nodeType)) {
377 public String toString() {
378 return nodeType + "[" + value + "]";
384 public static final class AugmentationIdentifier implements PathArgument {
386 private static final long serialVersionUID = -8122335594681936939L;
387 private final ImmutableSet<QName> childNames;
390 public QName getNodeType() {
391 // This should rather throw exception than return always null
392 throw new UnsupportedOperationException("Augmentation node has no QName");
395 public AugmentationIdentifier(final Set<QName> childNames) {
396 this.childNames = ImmutableSet.copyOf(childNames);
400 * Augmentation node has no QName
403 public AugmentationIdentifier(final QName nodeType, final Set<QName> childNames) {
407 public Set<QName> getPossibleChildNames() {
412 public String toString() {
413 final StringBuffer sb = new StringBuffer("AugmentationIdentifier{");
414 sb.append("childNames=").append(childNames);
416 return sb.toString();
420 public boolean equals(final Object o) {
424 if (!(o instanceof AugmentationIdentifier)) {
428 AugmentationIdentifier that = (AugmentationIdentifier) o;
430 if (!childNames.equals(that.childNames)) {
438 public int hashCode() {
439 return childNames.hashCode();
443 private static class BuilderImpl implements InstanceIdentifierBuilder {
445 private final ImmutableList.Builder<PathArgument> path;
447 public BuilderImpl() {
448 path = ImmutableList.<PathArgument> builder();
451 public BuilderImpl(final List<? extends PathArgument> prefix) {
452 path = ImmutableList.<PathArgument> builder();
457 public InstanceIdentifierBuilder node(final QName nodeType) {
458 path.add(new NodeIdentifier(nodeType));
463 public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final QName key, final Object value) {
464 path.add(new NodeIdentifierWithPredicates(nodeType, key, value));
469 public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final Map<QName, Object> keyValues) {
470 path.add(new NodeIdentifierWithPredicates(nodeType, keyValues));
476 public InstanceIdentifier toInstance() {
481 public InstanceIdentifier build() {
482 return new InstanceIdentifier(path.build());
487 public InstanceIdentifier getIdentifier() {
493 public boolean contains(final InstanceIdentifier other) {
495 throw new IllegalArgumentException("other should not be null");
497 final int localSize = this.path.size();
498 final List<PathArgument> otherPath = other.getPath();
499 if (localSize > other.path.size()) {
502 for (int i = 0; i < localSize; i++) {
503 if (!path.get(i).equals(otherPath.get(i))) {
511 public String toString() {
513 * The toStringCache is safe, since the object contract requires
514 * immutability of the object and all objects referenced from this
517 * Used lists, maps are immutable. Path Arguments (elements) are also
518 * immutable, since the PathArgument contract requires immutability.
520 * The cache is thread-safe - if multiple computations occurs at the
521 * same time, cache will be overwritten with same result.
523 if (toStringCache != null) {
524 return toStringCache;
527 final StringBuilder builder = new StringBuilder('/');
528 boolean first = true;
529 for (PathArgument argument : path) {
535 builder.append(argument.toString());
538 toStringCache = builder.toString();
539 return toStringCache;