X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-data-api%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fdata%2Fapi%2FYangInstanceIdentifier.java;h=40265f83ffc9a801b1504ced3dbc71ce45974d69;hb=4d9034700f0ac327948c3be5ea347e628d82fd81;hp=6901cdbdbdb7e900e38586f9a26a79e038637dcb;hpb=62671f88f06fcf731009fdbd8180ddf7e45d1ab2;p=yangtools.git diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java index 6901cdbdbd..40265f83ff 100644 --- a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java +++ b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java @@ -23,6 +23,7 @@ import com.google.common.collect.Sets; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.Serializable; import java.lang.reflect.Array; +import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -44,6 +45,7 @@ import org.opendaylight.yangtools.concepts.Path; import org.opendaylight.yangtools.util.HashCodeBuilder; import org.opendaylight.yangtools.util.ImmutableOffsetMap; import org.opendaylight.yangtools.util.SharedSingletonMap; +import org.opendaylight.yangtools.util.SingletonSet; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; @@ -81,7 +83,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; * * @see RFC6020 */ -// FIXME: 3.0.0: this concept needs to be moved to yang-common, as parser components need the ability to refer +// FIXME: 4.0.0: this concept needs to be moved to yang-common, as parser components need the ability to refer // to data nodes -- most notably XPath expressions and {@code default} statement arguments need to be able // to represent these. // FIXME: FixedYangInstanceIdentifier needs YangInstanceIdentifier initialized, but that includes initializing @@ -91,7 +93,7 @@ public abstract class YangInstanceIdentifier implements Path TOSTRINGCACHE_UPDATER = AtomicReferenceFieldUpdater.newUpdater(YangInstanceIdentifier.class, String.class, "toStringCache"); @@ -126,7 +128,7 @@ public abstract class YangInstanceIdentifier implements Path getPathArguments(); + public abstract @NonNull List getPathArguments(); /** * Returns an iterable of path arguments in reverse order. This is useful @@ -158,7 +160,7 @@ public abstract class YangInstanceIdentifier implements Path getReversePathArguments(); + public abstract @NonNull List getReversePathArguments(); /** * Returns the last PathArgument. This is equivalent of iterating @@ -168,7 +170,7 @@ public abstract class YangInstanceIdentifier implements Path path) { + public static @NonNull YangInstanceIdentifier create(final Iterable path) { if (Iterables.isEmpty(path)) { return EMPTY; } @@ -181,7 +183,7 @@ public abstract class YangInstanceIdentifier implements Path pathTowardsRoot) { + public static @NonNull YangInstanceIdentifier createReverse(final Deque pathTowardsRoot) { final ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize( pathTowardsRoot.size()); pathTowardsRoot.descendingIterator().forEachRemaining(builder::add); @@ -208,7 +210,7 @@ public abstract class YangInstanceIdentifier implements Path YangInstanceIdentifier createReverse(final Deque stackTowardsRoot, + public static @NonNull YangInstanceIdentifier createReverse(final Deque stackTowardsRoot, final Function function) { final ImmutableList.Builder builder = ImmutableList.builderWithExpectedSize( stackTowardsRoot.size()); @@ -245,7 +247,7 @@ public abstract class YangInstanceIdentifier implements Path, 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. + * Returns unique QName of data node as defined in YANG Schema, if available. * * @return Node type + * @throws UnsupportedOperationException if node type is not applicable, for example in case of an augmentation. */ - QName getNodeType(); + @NonNull QName getNodeType(); /** * Return the string representation of this object for use in context @@ -461,14 +465,13 @@ public abstract class YangInstanceIdentifier implements Path keyValues; + private final @NonNull QName key; + private final @NonNull Object value; - public NodeIdentifierWithPredicates(final QName node) { - super(node); - this.keyValues = ImmutableMap.of(); + Singleton(final QName node, final QName key, final Object value) { + super(node); + this.key = requireNonNull(key); + this.value = requireNonNull(value); + } + + @Override + public SingletonSet> entrySet() { + return SingletonSet.of(new SimpleImmutableEntry<>(key, value)); + } + + @Override + public SingletonSet keySet() { + return SingletonSet.of(key); + } + + @Override + public SingletonSet values() { + return SingletonSet.of(value); + } + + @Override + public int size() { + return 1; + } + + @Override + public ImmutableMap asMap() { + return ImmutableMap.of(key, value); + } + + @Override + boolean equalMapping(final NodeIdentifierWithPredicates other) { + final Singleton single = (Singleton) other; + return key.equals(single.key) && Objects.deepEquals(value, single.value); + } + + @Override + Object keyValue(final QName qname) { + return key.equals(qname) ? value : null; + } } - 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); + private static final class Regular extends NodeIdentifierWithPredicates { + private static final long serialVersionUID = 1L; + + private final @NonNull Map keyValues; + + Regular(final QName node, final Map keyValues) { + super(node); + this.keyValues = requireNonNull(keyValues); + } + + @Override + public Set> entrySet() { + return keyValues.entrySet(); + } + + @Override + public Set keySet() { + return keyValues.keySet(); + } + + @Override + public Collection values() { + return keyValues.values(); + } + + @Override + public int size() { + return keyValues.size(); + } + + @Override + public Map asMap() { + return keyValues; + } + + @Override + Object keyValue(final QName qname) { + return keyValues.get(qname); + } + + @Override + boolean equalMapping(final NodeIdentifierWithPredicates other) { + final Map otherKeyValues = ((Regular) other).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 : entrySet()) { + final Object otherValue = otherKeyValues.get(entry.getKey()); + if (otherValue == null || !Objects.deepEquals(entry.getValue(), otherValue)) { + return false; + } + } + + return true; + } } - public NodeIdentifierWithPredicates(final QName node, final ImmutableOffsetMap keyValues) { + private static final long serialVersionUID = -4787195606494761540L; + + NodeIdentifierWithPredicates(final QName node) { super(node); - this.keyValues = requireNonNull(keyValues); } - public NodeIdentifierWithPredicates(final QName node, final SharedSingletonMap keyValues) { - super(node); - this.keyValues = requireNonNull(keyValues); + public static @NonNull NodeIdentifierWithPredicates of(final QName node) { + return new Regular(node, ImmutableMap.of()); } - public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) { - this(node, SharedSingletonMap.unorderedOf(key, value)); + public static @NonNull NodeIdentifierWithPredicates of(final QName node, final QName key, final Object value) { + return new Singleton(node, key, value); } - public Map getKeyValues() { - return keyValues; + public static @NonNull NodeIdentifierWithPredicates of(final QName node, final Entry entry) { + return of(node, entry.getKey(), entry.getValue()); } - @Override - protected int hashCodeImpl() { - final int prime = 31; - int result = super.hashCodeImpl(); - result = prime * result; + public static @NonNull NodeIdentifierWithPredicates of(final QName node, final Map keyValues) { + return keyValues.size() == 1 ? of(keyValues, node) + // Retains ImmutableMap for empty maps. For larger sizes uses a shared key set. + : new Regular(node, ImmutableOffsetMap.unorderedCopyOf(keyValues)); + } + + public static @NonNull NodeIdentifierWithPredicates of(final QName node, + final ImmutableOffsetMap keyValues) { + return keyValues.size() == 1 ? of(keyValues, node) : new Regular(node, keyValues); + } + + @Deprecated + public static @NonNull NodeIdentifierWithPredicates of(final QName node, + final SharedSingletonMap keyValues) { + return of(node, keyValues.getEntry()); + } + + private static @NonNull NodeIdentifierWithPredicates of(final Map keyValues, final QName node) { + return of(node, keyValues.entrySet().iterator().next()); + } + + /** + * Return the set of predicates keys and values. Keys are guaranteeed to be unique. + * + * @return Predicate set. + */ + @Beta + public abstract @NonNull Set> entrySet(); + + /** + * Return the predicate key in the iteration order of {@link #entrySet()}. + * + * @return Predicate values. + */ + @Beta + public abstract @NonNull Set keySet(); + + /** + * Return the predicate values in the iteration order of {@link #entrySet()}. + * + * @return Predicate values. + */ + @Beta + public abstract @NonNull Collection values(); - for (Entry entry : keyValues.entrySet()) { - result += Objects.hashCode(entry.getKey()) + YangInstanceIdentifier.hashCode(entry.getValue()); + @Beta + public final @Nullable Object getValue(final QName key) { + return keyValue(requireNonNull(key)); + } + + @Beta + public final @Nullable T getValue(final QName key, final Class valueClass) { + return valueClass.cast(getValue(key)); + } + + /** + * Return the number of predicates present. + * + * @return The number of predicates present. + */ + @Beta + public abstract int size(); + + /** + * A Map-like view of this identifier's predicates. The view is expected to be stable and effectively-immutable. + * + * @return Map of predicates. + * @deprecated This method in a provisional one. It can be used in the code base, but users requiring it should + * contact yangtools-dev for migration + * guidelines. Callers are strongly encouraged to explore {@link #entrySet()}, {@link #size()}, + * {@link #values()} and {@link #keySet()} as an alternative. + */ + @Beta + @Deprecated + // FIXME: 4.0.0: evaluate the real usefulness of this. The problem here is Map.hashCode() and Map.equals(), + // which limits our options. + public abstract @NonNull Map asMap(); + + @Override + protected final int hashCodeImpl() { + int result = 31 * super.hashCodeImpl(); + for (Entry entry : entrySet()) { + result += entry.getKey().hashCode() + YangInstanceIdentifier.hashCode(entry.getValue()); } return result; } @Override @SuppressWarnings("checkstyle:equalsHashCode") - public boolean equals(final Object obj) { - if (!super.equals(obj)) { - return false; - } + public final boolean equals(final Object obj) { + return super.equals(obj) && equalMapping((NodeIdentifierWithPredicates) obj); + } - final Map otherKeyValues = ((NodeIdentifierWithPredicates) obj).keyValues; + abstract boolean equalMapping(NodeIdentifierWithPredicates other); - // 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; - } + abstract @Nullable Object keyValue(@NonNull QName qname); - for (Entry entry : keyValues.entrySet()) { - if (!otherKeyValues.containsKey(entry.getKey()) - || !Objects.deepEquals(entry.getValue(), otherKeyValues.get(entry.getKey()))) { - - return false; - } - } - - return true; + @Override + public final String toString() { + return super.toString() + '[' + asMap() + ']'; } @Override - public String toString() { - return super.toString() + '[' + keyValues + ']'; + public final String toRelativeString(final PathArgument previous) { + return super.toRelativeString(previous) + '[' + asMap() + ']'; } @Override - public String toRelativeString(final PathArgument previous) { - return super.toRelativeString(previous) + '[' + keyValues + ']'; + final Object writeReplace() { + return new NIPv2(this); } } @@ -693,6 +857,11 @@ public abstract class YangInstanceIdentifier implements Path childNames; + + 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() { @@ -719,6 +897,17 @@ public abstract class YangInstanceIdentifier implements Path childNames) { + this.childNames = requireNonNull(childNames); + } + /** * Construct new augmentation identifier using supplied set of possible * child nodes. @@ -730,12 +919,35 @@ public abstract class YangInstanceIdentifier implements Path 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 Set getPossibleChildNames() { + public @NonNull Set getPossibleChildNames() { return childNames; } @@ -797,6 +1009,10 @@ public abstract class YangInstanceIdentifier implements Path keyValues); + @NonNull InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map keyValues); /** * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key, value. @@ -837,7 +1053,7 @@ public abstract class YangInstanceIdentifier implements Path args); + @NonNull InstanceIdentifierBuilder append(Collection args); /** * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier. @@ -857,7 +1073,7 @@ public abstract class YangInstanceIdentifier implements Path