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.collect.ImmutableList;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.collect.ImmutableSet;
29 public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable, Serializable {
31 private static final long serialVersionUID = 8467409862384206193L;
32 private final List<PathArgument> path;
34 private transient String toStringCache = null;
35 private transient Integer hashCodeCache = null;
37 public List<PathArgument> getPath() {
41 public InstanceIdentifier(final List<? extends PathArgument> path) {
42 this.path = ImmutableList.copyOf(path);
45 private InstanceIdentifier(final NodeIdentifier nodeIdentifier) {
46 this.path = ImmutableList.<PathArgument> of(nodeIdentifier);
50 public int hashCode() {
52 * The hashCodeCache is safe, since the object contract requires
53 * immutability of the object and all objects referenced from this
56 * Used lists, maps are immutable. Path Arguments (elements) are also
57 * immutable, since the PathArgument contract requires immutability.
59 * The cache is thread-safe - if multiple computations occurs at the
60 * same time, cache will be overwritten with same result.
62 if (hashCodeCache == null) {
65 result = prime * result + ((path == null) ? 0 : path.hashCode());
66 hashCodeCache = result;
72 public boolean equals(final Object obj) {
79 if (getClass() != obj.getClass()) {
82 InstanceIdentifier other = (InstanceIdentifier) obj;
83 if (this.hashCode() != obj.hashCode()) {
87 if (other.path != null) {
90 } else if (!path.equals(other.path)) {
96 public InstanceIdentifier node(final QName name) {
97 return node(new NodeIdentifier(name));
100 public InstanceIdentifier node(final PathArgument arg) {
101 return new InstanceIdentifier(ImmutableList.<PathArgument>builder().addAll(path).add(arg).build());
104 // Static factories & helpers
106 public static InstanceIdentifier of(final QName name) {
107 return new InstanceIdentifier(new NodeIdentifier(name));
110 static public InstanceIdentifierBuilder builder() {
111 return new BuilderImpl();
114 static public InstanceIdentifierBuilder builder(final InstanceIdentifier origin) {
115 return new BuilderImpl(origin.getPath());
118 public static InstanceIdentifierBuilder builder(final QName node) {
119 return builder().node(node);
122 public interface PathArgument extends Immutable, Serializable {
125 * If applicable returns uniqee QName of data node as defined in YANG
128 * This method may return null, if the corresponding schema node, does
129 * not have QName associated, such as in cases of augmentations.
137 public interface InstanceIdentifierBuilder extends Builder<InstanceIdentifier> {
138 InstanceIdentifierBuilder node(QName nodeType);
140 InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
142 InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
145 InstanceIdentifier getIdentifier();
147 InstanceIdentifier build();
151 * Simple path argument identifying a {@link ContainerNode} or {@link LeafNode} leaf
155 public static final class NodeIdentifier implements PathArgument {
160 private static final long serialVersionUID = -2255888212390871347L;
162 private final QName nodeType;
164 public NodeIdentifier(final QName node) {
165 this.nodeType = node;
169 public QName getNodeType() {
174 public int hashCode() {
175 final int prime = 31;
177 result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
182 public boolean equals(final Object obj) {
189 if (getClass() != obj.getClass()) {
192 NodeIdentifier other = (NodeIdentifier) obj;
193 if (nodeType == null) {
194 if (other.nodeType != null) {
197 } else if (!nodeType.equals(other.nodeType)) {
204 public String toString() {
205 return nodeType.toString();
211 * Composite path argument identifying a {@link MapEntryNode} leaf
215 public static final class NodeIdentifierWithPredicates implements PathArgument {
220 private static final long serialVersionUID = -4787195606494761540L;
222 private final QName nodeType;
223 private final Map<QName, Object> keyValues;
225 public NodeIdentifierWithPredicates(final QName node, final Map<QName, Object> keyValues) {
226 this.nodeType = node;
227 this.keyValues = ImmutableMap.copyOf(keyValues);
230 public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
231 this.nodeType = node;
232 this.keyValues = ImmutableMap.of(key, value);
236 public QName getNodeType() {
240 public Map<QName, Object> getKeyValues() {
245 public int hashCode() {
246 final int prime = 31;
248 result = prime * result + ((keyValues == null) ? 0 : keyValues.hashCode());
249 result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
254 public boolean equals(final Object obj) {
261 if (getClass() != obj.getClass()) {
264 NodeIdentifierWithPredicates other = (NodeIdentifierWithPredicates) obj;
265 if (keyValues == null) {
266 if (other.keyValues != null) {
269 } else if (!keyValues.equals(other.keyValues)) {
272 if (nodeType == null) {
273 if (other.nodeType != null) {
276 } else if (!nodeType.equals(other.nodeType)) {
283 public String toString() {
284 return nodeType + "[" + keyValues + "]";
289 * Simple path argument identifying a {@link LeafSetEntryNode} leaf
293 public static final class NodeWithValue implements PathArgument {
297 * Composite path argument identifying a {@link AugmentationNode} leaf
301 private static final long serialVersionUID = -3637456085341738431L;
303 private final QName nodeType;
304 private final Object value;
306 public NodeWithValue(final QName node, final Object value) {
307 this.nodeType = node;
312 public QName getNodeType() {
316 public Object getValue() {
321 public int hashCode() {
322 final int prime = 31;
324 result = prime * result + ((value == null) ? 0 : value.hashCode());
325 result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
330 public boolean equals(final Object obj) {
337 if (getClass() != obj.getClass()) {
340 NodeWithValue other = (NodeWithValue) obj;
342 if (other.value != null) {
345 } else if (!value.equals(other.value)) {
348 if (nodeType == null) {
349 if (other.nodeType != null) {
352 } else if (!nodeType.equals(other.nodeType)) {
359 public String toString() {
360 return nodeType + "[" + value + "]";
366 public static final class AugmentationIdentifier implements PathArgument {
368 private static final long serialVersionUID = -8122335594681936939L;
369 private final ImmutableSet<QName> childNames;
372 public QName getNodeType() {
373 // This should rather throw exception than return always null
374 throw new UnsupportedOperationException("Augmentation node has no QName");
377 public AugmentationIdentifier(final Set<QName> childNames) {
378 this.childNames = ImmutableSet.copyOf(childNames);
382 * Augmentation node has no QName
385 public AugmentationIdentifier(final QName nodeType, final Set<QName> childNames) {
389 public Set<QName> getPossibleChildNames() {
394 public String toString() {
395 final StringBuffer sb = new StringBuffer("AugmentationIdentifier{");
396 sb.append("childNames=").append(childNames);
398 return sb.toString();
402 public boolean equals(final Object o) {
406 if (!(o instanceof AugmentationIdentifier)) {
410 AugmentationIdentifier that = (AugmentationIdentifier) o;
412 if (!childNames.equals(that.childNames)) {
420 public int hashCode() {
421 return childNames.hashCode();
425 private static class BuilderImpl implements InstanceIdentifierBuilder {
427 private final ImmutableList.Builder<PathArgument> path;
429 public BuilderImpl() {
430 path = ImmutableList.<PathArgument> builder();
433 public BuilderImpl(final List<? extends PathArgument> prefix) {
434 path = ImmutableList.<PathArgument> builder();
439 public InstanceIdentifierBuilder node(final QName nodeType) {
440 path.add(new NodeIdentifier(nodeType));
445 public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final QName key, final Object value) {
446 path.add(new NodeIdentifierWithPredicates(nodeType, key, value));
451 public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final Map<QName, Object> keyValues) {
452 path.add(new NodeIdentifierWithPredicates(nodeType, keyValues));
458 public InstanceIdentifier toInstance() {
463 public InstanceIdentifier build() {
464 return new InstanceIdentifier(path.build());
469 public InstanceIdentifier getIdentifier() {
475 public boolean contains(final InstanceIdentifier other) {
477 throw new IllegalArgumentException("other should not be null");
479 final int localSize = this.path.size();
480 final List<PathArgument> otherPath = other.getPath();
481 if (localSize > other.path.size()) {
484 for (int i = 0; i < localSize; i++) {
485 if (!path.get(i).equals(otherPath.get(i))) {
493 public String toString() {
495 * The toStringCache is safe, since the object contract requires
496 * immutability of the object and all objects referenced from this
499 * Used lists, maps are immutable. Path Arguments (elements) are also
500 * immutable, since the PathArgument contract requires immutability.
502 * The cache is thread-safe - if multiple computations occurs at the
503 * same time, cache will be overwritten with same result.
505 if (toStringCache != null) {
506 return toStringCache;
508 StringBuilder builder = new StringBuilder();
509 for (PathArgument argument : path) {
511 builder.append(argument.toString());
513 toStringCache = builder.toString();
514 return toStringCache;