* This interface itself is used as common parent for actual
* path arguments types and should not be implemented by user code.
*
*
* Path arguments SHOULD contain only minimum of information
* required to uniquely identify node on particular subtree level.
*
*
, Immutable, Serializable {
/**
* If applicable returns unique QName of data node as defined in YANG
* Schema.
*
*
* This method may return null, if the corresponding schema node, does
* not have QName associated, such as in cases of augmentations.
*
* @return Node type
*/
QName getNodeType();
/**
* Return the string representation of this object for use in context
* provided by a previous object. This method can be implemented in
* terms of {@link #toString()}, but implementations are encourage to
* reuse any context already emitted by the previous object.
*
* @param previous Previous path argument
* @return String representation
*/
@NonNull String toRelativeString(PathArgument previous);
}
private abstract static class AbstractPathArgument implements PathArgument {
private static final long serialVersionUID = -4546547994250849340L;
private final @NonNull QName nodeType;
private transient volatile int hashValue;
protected AbstractPathArgument(final QName nodeType) {
this.nodeType = requireNonNull(nodeType);
}
@Override
public final @NonNull QName getNodeType() {
return nodeType;
}
@Override
@SuppressWarnings("checkstyle:parameterName")
public int compareTo(final PathArgument o) {
return nodeType.compareTo(o.getNodeType());
}
protected int hashCodeImpl() {
return 31 + getNodeType().hashCode();
}
@Override
public final int hashCode() {
int local;
return (local = hashValue) != 0 ? local : (hashValue = hashCodeImpl());
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null || this.getClass() != obj.getClass()) {
return false;
}
return getNodeType().equals(((AbstractPathArgument)obj).getNodeType());
}
@Override
public String toString() {
return getNodeType().toString();
}
@Override
public String toRelativeString(final PathArgument previous) {
if (previous instanceof AbstractPathArgument) {
final QNameModule mod = previous.getNodeType().getModule();
if (getNodeType().getModule().equals(mod)) {
return getNodeType().getLocalName();
}
}
return getNodeType().toString();
}
abstract Object writeReplace();
}
/**
* Simple path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.ContainerNode} or
* {@link org.opendaylight.yangtools.yang.data.api.schema.LeafNode} leaf in particular subtree.
*/
public static final class NodeIdentifier extends AbstractPathArgument {
private static final long serialVersionUID = -2255888212390871347L;
private static final LoadingCache CACHE = CacheBuilder.newBuilder().weakValues()
.build(new CacheLoader() {
@Override
public NodeIdentifier load(final QName key) {
return new NodeIdentifier(key);
}
});
public NodeIdentifier(final QName node) {
super(node);
}
/**
* Return a NodeIdentifier for a particular QName. Unlike the constructor, this factory method uses a global
* instance cache, resulting in object reuse for equal inputs.
*
* @param node Node's QName
* @return A {@link NodeIdentifier}
*/
public static @NonNull NodeIdentifier create(final QName node) {
return CACHE.getUnchecked(node);
}
@Override
Object writeReplace() {
return new NIv1(this);
}
}
/**
* Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode} leaf
* overall data tree.
*/
public static final class NodeIdentifierWithPredicates extends AbstractPathArgument {
private static final long serialVersionUID = -4787195606494761540L;
private final @NonNull Map keyValues;
// Exposed for NIPv1
NodeIdentifierWithPredicates(final Map keyValues, final QName node) {
super(node);
this.keyValues = requireNonNull(keyValues);
}
public static @NonNull NodeIdentifierWithPredicates of(final QName node) {
return new NodeIdentifierWithPredicates(node);
}
@Deprecated
public NodeIdentifierWithPredicates(final QName node) {
super(node);
this.keyValues = ImmutableMap.of();
}
public static @NonNull NodeIdentifierWithPredicates of(final QName node, final Map keyValues) {
return new NodeIdentifierWithPredicates(node, keyValues);
}
@Deprecated
public NodeIdentifierWithPredicates(final QName node, final Map keyValues) {
super(node);
// Retains ImmutableMap for empty maps. For larger sizes uses a shared key set.
this.keyValues = ImmutableOffsetMap.unorderedCopyOf(keyValues);
}
public static @NonNull NodeIdentifierWithPredicates of(final QName node,
final ImmutableOffsetMap keyValues) {
return new NodeIdentifierWithPredicates(node, keyValues);
}
@Deprecated
public NodeIdentifierWithPredicates(final QName node, final ImmutableOffsetMap keyValues) {
this(keyValues, node);
}
public static @NonNull NodeIdentifierWithPredicates of(final QName node,
final SharedSingletonMap keyValues) {
return new NodeIdentifierWithPredicates(node, keyValues);
}
@Deprecated
public NodeIdentifierWithPredicates(final QName node, final SharedSingletonMap keyValues) {
this(keyValues, node);
}
public static @NonNull NodeIdentifierWithPredicates of(final QName node, final QName key, final Object value) {
return of(node, SharedSingletonMap.unorderedOf(key, value));
}
@Deprecated
public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
this(node, SharedSingletonMap.unorderedOf(key, value));
}
public @NonNull Map getKeyValues() {
return keyValues;
}
@Override
protected int hashCodeImpl() {
final int prime = 31;
int result = super.hashCodeImpl();
result = prime * result;
for (Entry entry : keyValues.entrySet()) {
result += Objects.hashCode(entry.getKey()) + YangInstanceIdentifier.hashCode(entry.getValue());
}
return result;
}
@Override
@SuppressWarnings("checkstyle:equalsHashCode")
public boolean equals(final Object obj) {
if (!super.equals(obj)) {
return false;
}
final Map otherKeyValues = ((NodeIdentifierWithPredicates) obj).keyValues;
// TODO: benchmark to see if just calling equals() on the two maps is not faster
if (keyValues == otherKeyValues) {
return true;
}
if (keyValues.size() != otherKeyValues.size()) {
return false;
}
for (Entry entry : keyValues.entrySet()) {
if (!otherKeyValues.containsKey(entry.getKey())
|| !Objects.deepEquals(entry.getValue(), otherKeyValues.get(entry.getKey()))) {
return false;
}
}
return true;
}
@Override
public String toString() {
return super.toString() + '[' + keyValues + ']';
}
@Override
public String toRelativeString(final PathArgument previous) {
return super.toRelativeString(previous) + '[' + keyValues + ']';
}
@Override
Object writeReplace() {
return new NIPv1(this);
}
}
/**
* Simple path argument identifying a {@link LeafSetEntryNode} leaf
* overall data tree.
*/
public static final class NodeWithValue extends AbstractPathArgument {
private static final long serialVersionUID = -3637456085341738431L;
private final T value;
public NodeWithValue(final QName node, final T value) {
super(node);
this.value = value;
}
public T getValue() {
return value;
}
@Override
protected int hashCodeImpl() {
final int prime = 31;
int result = super.hashCodeImpl();
result = prime * result + YangInstanceIdentifier.hashCode(value);
return result;
}
@Override
@SuppressWarnings("checkstyle:equalsHashCode")
public boolean equals(final Object obj) {
if (!super.equals(obj)) {
return false;
}
final NodeWithValue> other = (NodeWithValue>) obj;
return Objects.deepEquals(value, other.value);
}
@Override
public String toString() {
return super.toString() + '[' + value + ']';
}
@Override
public String toRelativeString(final PathArgument previous) {
return super.toRelativeString(previous) + '[' + value + ']';
}
@Override
Object writeReplace() {
return new NIVv1(this);
}
}
/**
* Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode}
* node in particular subtree.
*
*
* Augmentation is uniquely identified by set of all possible child nodes.
* This is possible
* to identify instance of augmentation,
* since RFC6020 states that augment
that augment
* statement must not add multiple nodes from same namespace
* / module to the target node.
*
* @see RFC6020
*/
public static final class AugmentationIdentifier implements PathArgument {
private static final long serialVersionUID = -8122335594681936939L;
private static final LoadingCache, AugmentationIdentifier> CACHE = CacheBuilder.newBuilder()
.weakValues().build(new CacheLoader, AugmentationIdentifier>() {
@Override
public AugmentationIdentifier load(final ImmutableSet key) {
return new AugmentationIdentifier(key);
}
});
private final @NonNull ImmutableSet childNames;
@Override
public QName getNodeType() {
// This should rather throw exception than return always null
throw new UnsupportedOperationException("Augmentation node has no QName");
}
/**
* Construct new augmentation identifier using supplied set of possible
* child nodes.
*
* @param childNames
* Set of possible child nodes.
*/
public AugmentationIdentifier(final ImmutableSet childNames) {
this.childNames = requireNonNull(childNames);
}
/**
* Construct new augmentation identifier using supplied set of possible
* child nodes.
*
* @param childNames
* Set of possible child nodes.
*/
public AugmentationIdentifier(final Set childNames) {
this.childNames = ImmutableSet.copyOf(childNames);
}
/**
* Return an AugmentationIdentifier for a particular set of QNames. Unlike the constructor, this factory method
* uses a global instance cache, resulting in object reuse for equal inputs.
*
* @param childNames Set of possible child nodes
* @return An {@link AugmentationIdentifier}
*/
public static @NonNull AugmentationIdentifier create(final ImmutableSet childNames) {
return CACHE.getUnchecked(childNames);
}
/**
* Return an AugmentationIdentifier for a particular set of QNames. Unlike the constructor, this factory method
* uses a global instance cache, resulting in object reuse for equal inputs.
*
* @param childNames Set of possible child nodes
* @return An {@link AugmentationIdentifier}
*/
public static @NonNull AugmentationIdentifier create(final Set childNames) {
final AugmentationIdentifier existing = CACHE.getIfPresent(childNames);
return existing != null ? existing : create(ImmutableSet.copyOf(childNames));
}
/**
* Returns set of all possible child nodes.
*
* @return set of all possible child nodes.
*/
public @NonNull Set getPossibleChildNames() {
return childNames;
}
@Override
public String toString() {
return "AugmentationIdentifier{" + "childNames=" + childNames + '}';
}
@Override
public String toRelativeString(final PathArgument previous) {
return toString();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AugmentationIdentifier)) {
return false;
}
AugmentationIdentifier that = (AugmentationIdentifier) obj;
return childNames.equals(that.childNames);
}
@Override
public int hashCode() {
return childNames.hashCode();
}
@Override
@SuppressWarnings("checkstyle:parameterName")
public int compareTo(final PathArgument o) {
if (!(o instanceof AugmentationIdentifier)) {
return -1;
}
AugmentationIdentifier other = (AugmentationIdentifier) o;
Set otherChildNames = other.getPossibleChildNames();
int thisSize = childNames.size();
int otherSize = otherChildNames.size();
if (thisSize == otherSize) {
// Quick Set-based comparison
if (childNames.equals(otherChildNames)) {
return 0;
}
// We already know the sets are not equal, but have equal size, hence the sets differ in their elements,
// but potentially share a common set of elements. The most consistent way of comparing them is using
// total ordering defined by QName's compareTo. Hence convert both sets to lists ordered
// by QName.compareTo() and decide on the first differing element.
final List diff = new ArrayList<>(Sets.symmetricDifference(childNames, otherChildNames));
verify(!diff.isEmpty(), "Augmentation identifiers %s and %s report no difference", this, o);
diff.sort(QName::compareTo);
return childNames.contains(diff.get(0)) ? -1 : 1;
} else if (thisSize < otherSize) {
return 1;
} else {
return -1;
}
}
private Object writeReplace() {
return new AIv1(this);
}
}
/**
* Fluent Builder of Instance Identifier instances.
*/
public interface InstanceIdentifierBuilder extends Builder {
/**
* Adds a {@link PathArgument} to path arguments of resulting instance identifier.
*
* @param arg A {@link PathArgument} to be added
* @return this builder
*/
@NonNull InstanceIdentifierBuilder node(PathArgument arg);
/**
* Adds {@link NodeIdentifier} with supplied QName to path arguments of resulting instance identifier.
*
* @param nodeType QName of {@link NodeIdentifier} which will be added
* @return this builder
*/
@NonNull InstanceIdentifierBuilder node(QName nodeType);
/**
* Adds {@link NodeIdentifierWithPredicates} with supplied QName and key values to path arguments of resulting
* instance identifier.
*
* @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
* @param keyValues Map of key components and their respective values for {@link NodeIdentifierWithPredicates}
* @return this builder
*/
@NonNull InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map keyValues);
/**
* Adds {@link NodeIdentifierWithPredicates} with supplied QName and key, value.
*
* @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
* @param key QName of key which will be added
* @param value value of key which will be added
* @return this builder
*/
@NonNull InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
/**
* Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
*
* @param args {@link PathArgument}s to be added
* @return this builder
* @throws NullPointerException if any of the arguments is null
*/
@Beta
@NonNull InstanceIdentifierBuilder append(Collection extends PathArgument> args);
/**
* Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
*
* @param args {@link PathArgument}s to be added
* @return this builder
* @throws NullPointerException if any of the arguments is null
*/
@Beta
default @NonNull InstanceIdentifierBuilder append(final PathArgument... args) {
return append(Arrays.asList(args));
}
/**
* Builds an {@link YangInstanceIdentifier} with path arguments from this builder.
*
* @return {@link YangInstanceIdentifier}
*/
@Override
YangInstanceIdentifier build();
}
}