import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.Serializable;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.lang.reflect.Array;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Function;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.yangtools.concepts.Builder;
import org.opendaylight.yangtools.concepts.HierarchicalIdentifier;
import org.opendaylight.yangtools.concepts.Immutable;
-import org.opendaylight.yangtools.util.HashCodeBuilder;
+import org.opendaylight.yangtools.concepts.Mutable;
import org.opendaylight.yangtools.util.ImmutableOffsetMap;
import org.opendaylight.yangtools.util.SingletonSet;
import org.opendaylight.yangtools.yang.common.QName;
*
* @see <a href="http://tools.ietf.org/html/rfc6020#section-9.13">RFC6020</a>
*/
-// FIXME: 7.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: sealed once we have JDK17+
public abstract class YangInstanceIdentifier implements HierarchicalIdentifier<YangInstanceIdentifier> {
- private static final AtomicReferenceFieldUpdater<YangInstanceIdentifier, String> TOSTRINGCACHE_UPDATER =
- AtomicReferenceFieldUpdater.newUpdater(YangInstanceIdentifier.class, String.class, "toStringCache");
private static final long serialVersionUID = 4L;
+ private static final VarHandle TO_STRING_CACHE;
+ private static final VarHandle HASH;
- private final int hash;
- private transient volatile String toStringCache = null;
+ static {
+ final var lookup = MethodHandles.lookup();
+ try {
+ HASH = lookup.findVarHandle(YangInstanceIdentifier.class, "hash", int.class);
+ TO_STRING_CACHE = lookup.findVarHandle(YangInstanceIdentifier.class, "toStringCache", String.class);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private int hash;
+ @SuppressWarnings("unused")
+ private transient String toStringCache = null;
- // Package-private to prevent outside subclassing
- YangInstanceIdentifier(final int hash) {
- this.hash = hash;
+ YangInstanceIdentifier() {
+ // Package-private to prevent outside subclassing
}
/**
abstract @NonNull YangInstanceIdentifier createRelativeIdentifier(int skipFromRoot);
- abstract @Nullable Collection<PathArgument> tryPathArguments();
+ abstract @Nullable List<PathArgument> tryPathArguments();
- abstract @Nullable Collection<PathArgument> tryReversePathArguments();
+ abstract @Nullable List<PathArgument> tryReversePathArguments();
/**
* Check if this instance identifier has empty path arguments, e.g. it is
public abstract PathArgument getLastPathArgument();
public static @NonNull YangInstanceIdentifier create(final Iterable<? extends PathArgument> path) {
- if (Iterables.isEmpty(path)) {
- return empty();
- }
-
- final HashCodeBuilder<PathArgument> hash = new HashCodeBuilder<>();
- for (PathArgument a : path) {
- hash.addArgument(a);
- }
-
- return FixedYangInstanceIdentifier.create(path, hash.build());
+ return Iterables.isEmpty(path) ? empty() : new FixedYangInstanceIdentifier(ImmutableList.copyOf(path));
}
public static @NonNull YangInstanceIdentifier create(final PathArgument pathArgument) {
- return new FixedYangInstanceIdentifier(ImmutableList.of(pathArgument),
- HashCodeBuilder.nextHashCode(1, pathArgument));
+ return new FixedYangInstanceIdentifier(ImmutableList.of(pathArgument));
}
public static @NonNull YangInstanceIdentifier create(final PathArgument... path) {
}
boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
- return Iterables.elementsEqual(getPathArguments(), other.getPathArguments());
+ return getPathArguments().equals(other.getPathArguments());
}
@Override
- public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof YangInstanceIdentifier)) {
- return false;
- }
- YangInstanceIdentifier other = (YangInstanceIdentifier) obj;
- if (this.hashCode() != obj.hashCode()) {
- return false;
- }
-
- return pathArgumentsEqual(other);
+ public final boolean equals(final Object obj) {
+ return this == obj || obj instanceof YangInstanceIdentifier && pathArgumentsEqual((YangInstanceIdentifier) obj);
}
/**
* @return Instance Identifier with additional path argument added to the end.
*/
public final @NonNull YangInstanceIdentifier node(final PathArgument arg) {
- return new StackedYangInstanceIdentifier(this, arg, HashCodeBuilder.nextHashCode(hash, arg));
+ return new StackedYangInstanceIdentifier(this, arg);
}
/**
* The cache is thread-safe - if multiple computations occurs at the
* same time, cache will be overwritten with same result.
*/
- String ret = toStringCache;
- if (ret == null) {
- final StringBuilder builder = new StringBuilder("/");
- PathArgument prev = null;
- for (PathArgument argument : getPathArguments()) {
- if (prev != null) {
- builder.append('/');
- }
- builder.append(argument.toRelativeString(prev));
- prev = argument;
- }
+ final String ret = (String) TO_STRING_CACHE.getAcquire(this);
+ return ret != null ? ret : loadToString();
+ }
- ret = builder.toString();
- TOSTRINGCACHE_UPDATER.lazySet(this, ret);
+ private String loadToString() {
+ final StringBuilder builder = new StringBuilder("/");
+ PathArgument prev = null;
+ for (PathArgument argument : getPathArguments()) {
+ if (prev != null) {
+ builder.append('/');
+ }
+ builder.append(argument.toRelativeString(prev));
+ prev = argument;
}
- return ret;
+
+ final String ret = builder.toString();
+ final String witness = (String) TO_STRING_CACHE.compareAndExchangeRelease(this, null, ret);
+ return witness == null ? ret : witness;
}
@Override
* Used lists, maps are immutable. Path Arguments (elements) are also
* immutable, since the PathArgument contract requires immutability.
*/
- return hash;
+ final int local = (int) HASH.getAcquire(this);
+ return local != 0 ? local : loadHashCode();
}
- @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
- justification = "https://github.com/spotbugs/spotbugs/issues/811")
private static int hashCode(final Object value) {
if (value == null) {
return 0;
return Objects.hashCode(value);
}
+ private int loadHashCode() {
+ final int computed = computeHashCode();
+ HASH.setRelease(this, computed);
+ return computed;
+ }
+
+ abstract int computeHashCode();
+
final Object writeReplace() {
return new YIDv1(this);
}
* @return new builder for InstanceIdentifier with path arguments copied from original instance identifier.
*/
public static @NonNull InstanceIdentifierBuilder builder(final YangInstanceIdentifier origin) {
- return new YangInstanceIdentifierBuilder(origin.getPathArguments(), origin.hashCode());
+ return new YangInstanceIdentifierBuilder(origin.getPathArguments());
}
/**
/**
* Fluent Builder of Instance Identifier instances.
*/
- public interface InstanceIdentifierBuilder extends Builder<YangInstanceIdentifier> {
+ public interface InstanceIdentifierBuilder extends Mutable {
/**
* Adds a {@link PathArgument} to path arguments of resulting instance identifier.
*
*
* @return {@link YangInstanceIdentifier}
*/
- @Override
- YangInstanceIdentifier build();
+ @NonNull YangInstanceIdentifier build();
}
}