import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
-
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Array;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
-
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.opendaylight.yangtools.concepts.Builder;
import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.concepts.Path;
import org.opendaylight.yangtools.util.HashCodeBuilder;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
/**
* </ul>
*
*
- * @see http://tools.ietf.org/html/rfc6020#section-9.13
+ * @see <a href="http://tools.ietf.org/html/rfc6020#section-9.13">RFC6020</a>
*/
public final class YangInstanceIdentifier implements Path<YangInstanceIdentifier>, Immutable, Serializable {
+ @SuppressWarnings("rawtypes")
+ private static final AtomicReferenceFieldUpdater<YangInstanceIdentifier, ImmutableList> LEGACYPATH_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(YangInstanceIdentifier.class, ImmutableList.class, "legacyPath");
+ private static final AtomicReferenceFieldUpdater<YangInstanceIdentifier, String> TOSTRINGCACHE_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(YangInstanceIdentifier.class, String.class, "toStringCache");
private static final YangInstanceIdentifier EMPTY = trustedCreate(Collections.<PathArgument>emptyList());
+ private static final Field PATHARGUMENTS_FIELD;
- private static final long serialVersionUID = 2L;
- private final Iterable<PathArgument> pathArguments;
+ private static final long serialVersionUID = 3L;
+ private final transient Iterable<PathArgument> pathArguments;
private final int hash;
- private transient volatile ImmutableList<PathArgument> legacyPath = null;
+ private volatile ImmutableList<PathArgument> legacyPath = null;
private transient volatile String toStringCache = null;
- private final ImmutableList<PathArgument> getLegacyPath() {
+ static {
+ final Field f;
+ try {
+ f = YangInstanceIdentifier.class.getDeclaredField("pathArguments");
+ } catch (NoSuchFieldException | SecurityException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ f.setAccessible(true);
+
+ PATHARGUMENTS_FIELD = f;
+ }
+
+ private ImmutableList<PathArgument> getLegacyPath() {
// Temporary variable saves a volatile read
ImmutableList<PathArgument> ret = legacyPath;
if (ret == null) {
- synchronized (this) {
- // We could have used a synchronized block, but let's just not bother
- ret = ImmutableList.copyOf(pathArguments);
- legacyPath = ret;
- }
+ // We could have used a synchronized block, but the window is quite
+ // small and worst that can happen is duplicate object construction.
+ ret = ImmutableList.copyOf(pathArguments);
+ LEGACYPATH_UPDATER.lazySet(this, ret);
}
return ret;
this.hash = hash;
}
- private static final YangInstanceIdentifier trustedCreate(final Iterable<PathArgument> path) {
+ private static YangInstanceIdentifier trustedCreate(final Iterable<PathArgument> path) {
final HashCodeBuilder<PathArgument> hash = new HashCodeBuilder<>();
for (PathArgument a : path) {
hash.addArgument(a);
}
- return new YangInstanceIdentifier(path, hash.toInstance());
+ return new YangInstanceIdentifier(path, hash.build());
}
- public static final YangInstanceIdentifier create(final Iterable<? extends PathArgument> path) {
+ public static YangInstanceIdentifier create(final Iterable<? extends PathArgument> path) {
if (Iterables.isEmpty(path)) {
return EMPTY;
}
return trustedCreate(ImmutableList.copyOf(path));
}
- public static final YangInstanceIdentifier create(final PathArgument... path) {
+ public static YangInstanceIdentifier create(final PathArgument... path) {
// We are forcing a copy, since we cannot trust the user
return create(Arrays.asList(path));
}
*
* @return new builder for InstanceIdentifier with empty path arguments.
*/
- static public InstanceIdentifierBuilder builder() {
+ public static InstanceIdentifierBuilder builder() {
return new BuilderImpl();
}
* @param origin Instace Identifier from which path arguments are copied.
* @return new builder for InstanceIdentifier with path arguments copied from original instance identifier.
*/
- static public InstanceIdentifierBuilder builder(final YangInstanceIdentifier origin) {
+ public static InstanceIdentifierBuilder builder(final YangInstanceIdentifier origin) {
return new BuilderImpl(origin.getPathArguments(), origin.hashCode());
}
- /**
- * Returns new builder for InstanceIdentifier with first path argument set to {@link NodeIdentifier}.
- *
- * @param node QName of first {@link NodeIdentifier} path argument.
- * @return new builder for InstanceIdentifier with first path argument set to {@link NodeIdentifier}.
- *
- * @deprecated Either use {@link #node(QName)} or instantiate an intermediate builder.
- */
- @Deprecated
- public static InstanceIdentifierBuilder builder(final QName node) {
- return builder().node(node);
- }
-
/**
* Path argument / component of InstanceIdentifier
*
- * Path argument uniquelly identifies node in data tree on particular
+ * Path argument uniquely identifies node in data tree on particular
* level.
* <p>
* This interface itself is used as common parent for actual
* @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
+ */
+ String toRelativeString(PathArgument previous);
}
private static abstract class AbstractPathArgument implements PathArgument {
+ private static final AtomicReferenceFieldUpdater<AbstractPathArgument, Integer> HASH_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(AbstractPathArgument.class, Integer.class, "hash");
private static final long serialVersionUID = -4546547994250849340L;
private final QName nodeType;
+ private volatile transient Integer hash = null;
protected AbstractPathArgument(final QName nodeType) {
this.nodeType = Preconditions.checkNotNull(nodeType);
return nodeType.compareTo(o.getNodeType());
}
- @Override
- public int hashCode() {
+ protected int hashCodeImpl() {
return 31 + getNodeType().hashCode();
}
+ @Override
+ public final int hashCode() {
+ Integer ret = hash;
+ if (ret == null) {
+ ret = hashCodeImpl();
+ HASH_UPDATER.lazySet(this, ret);
+ }
+
+ return ret;
+ }
+
@Override
public boolean equals(final Object obj) {
if (this == obj) {
public String toString() {
return getNodeType().toString();
}
+
+ @Override
+ public String toRelativeString(final PathArgument previous) {
+ if (previous instanceof AbstractPathArgument) {
+ final QNameModule mod = ((AbstractPathArgument)previous).getNodeType().getModule();
+ if (getNodeType().getModule().equals(mod)) {
+ return getNodeType().getLocalName();
+ }
+ }
+
+ return getNodeType().toString();
+ }
}
/**
- *
* Fluent Builder of Instance Identifier instances
- *
- * @
- *
*/
public interface InstanceIdentifierBuilder extends Builder<YangInstanceIdentifier> {
/**
*
* @return {@link YangInstanceIdentifier}
*/
+ @Override
YangInstanceIdentifier build();
+
+ /*
+ * @deprecated use #build()
+ */
+ @Deprecated
+ YangInstanceIdentifier toInstance();
}
/**
}
@Override
- public int hashCode() {
+ protected int hashCodeImpl() {
final int prime = 31;
- int result = super.hashCode();
+ int result = super.hashCodeImpl();
result = prime * result;
for (Entry<QName, Object> entry : keyValues.entrySet()) {
public String toString() {
return super.toString() + '[' + keyValues + ']';
}
+
+ @Override
+ public String toRelativeString(final PathArgument previous) {
+ return super.toRelativeString(previous) + '[' + keyValues + ']';
+ }
}
/**
}
@Override
- public int hashCode() {
+ protected int hashCodeImpl() {
final int prime = 31;
- int result = super.hashCode();
+ int result = super.hashCodeImpl();
result = prime * result + ((value == null) ? 0 : YangInstanceIdentifier.hashCode(value));
return result;
}
public String toString() {
return super.toString() + '[' + value + ']';
}
+
+ @Override
+ public String toRelativeString(final PathArgument previous) {
+ return super.toRelativeString(previous) + '[' + value + ']';
+ }
}
/**
* / module to the target node.
*
*
- * @see http://tools.ietf.org/html/rfc6020#section-7.15
+ * @see <a href="http://tools.ietf.org/html/rfc6020#section-7.15">RFC6020</a>
*/
public static final class AugmentationIdentifier implements PathArgument {
private static final long serialVersionUID = -8122335594681936939L;
}
/**
- * Augmentation node has no QName
- *
- * @deprecated Use
- * {@link AugmentationIdentifier#AugmentationIdentifier(Set)}
- * instead.
- */
- @Deprecated
- public AugmentationIdentifier(final QName nodeType, final Set<QName> childNames) {
- this(childNames);
- }
-
- /**
- *
* Returns set of all possible child nodes
*
* @return set of all possible child nodes.
@Override
public String toString() {
- final StringBuffer sb = new StringBuffer("AugmentationIdentifier{");
+ final StringBuilder sb = new StringBuilder("AugmentationIdentifier{");
sb.append("childNames=").append(childNames).append('}');
return sb.toString();
}
+ @Override
+ public String toRelativeString(final PathArgument previous) {
+ return toString();
+ }
+
@Override
public boolean equals(final Object o) {
if (this == o) {
@Override
public YangInstanceIdentifier build() {
- return new YangInstanceIdentifier(ImmutableList.copyOf(path), hash.toInstance());
+ return new YangInstanceIdentifier(ImmutableList.copyOf(path), hash.build());
}
}
*/
String ret = toStringCache;
if (ret == null) {
- synchronized (this) {
- ret = toStringCache;
- if (ret == null) {
- final StringBuilder builder = new StringBuilder('/');
- boolean first = true;
- for (PathArgument argument : getPathArguments()) {
- if (first) {
- first = false;
- } else {
- builder.append('/');
- }
- builder.append(argument.toString());
- }
-
- ret = builder.toString();
- toStringCache = ret;
+ final StringBuilder builder = new StringBuilder("/");
+ PathArgument prev = null;
+ for (PathArgument argument : getPathArguments()) {
+ if (prev != null) {
+ builder.append('/');
}
+ builder.append(argument.toRelativeString(prev));
+ prev = argument;
}
+
+ ret = builder.toString();
+ TOSTRINGCACHE_UPDATER.lazySet(this, ret);
}
return ret;
}
+
+ private void readObject(final ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
+ inputStream.defaultReadObject();
+
+ try {
+ PATHARGUMENTS_FIELD.set(this, legacyPath);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ return legacyPath.isEmpty() ? EMPTY : this;
+ }
+
+ private void writeObject(final ObjectOutputStream outputStream) throws IOException {
+ /*
+ * This may look strange, but what we are doing here is side-stepping the fact
+ * that pathArguments is not generally serializable. We are forcing instantiation
+ * of the legacy path, which is an ImmutableList (thus Serializable) and write
+ * it out. The read path does the opposite -- it reads the legacyPath and then
+ * uses invocation API to set the field.
+ */
+ getLegacyPath();
+ outputStream.defaultWriteObject();
+ }
}