import com.google.common.collect.ImmutableList;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
/**
- * Represents unique path to the every schema node inside the schema node identifier namespace. This concept is defined
+ * Represents unique path to every schema node inside the schema node identifier namespace. This concept is defined
* in <a href="https://tools.ietf.org/html/rfc7950#section-6.5">RFC7950</a>.
*/
public abstract class SchemaNodeIdentifier implements Immutable {
/**
* An absolute schema node identifier.
*/
- public static final class Absolute extends SchemaNodeIdentifier {
+ public abstract static class Absolute extends SchemaNodeIdentifier {
private static final Interner<Absolute> INTERNER = Interners.newWeakInterner();
- Absolute(final QName qname) {
- super(qname);
- }
-
- Absolute(final Collection<QName> qnames) {
- super(qnames);
+ Absolute() {
+ // Hidden on purpose
}
+ /**
+ * Create an absolute schema node identifier composed of a single node identifier.
+ *
+ * @param nodeIdentifier Single node identifier
+ * @return An absolute schema node identifier
+ * @throws NullPointerException if {@code nodeIdentifier} is null
+ */
public static @NonNull Absolute of(final QName nodeIdentifier) {
- return new Absolute(nodeIdentifier);
+ return new AbsoluteSingle(nodeIdentifier);
}
+ /**
+ * Create an absolute schema node identifier composed of multiple node identifiers.
+ *
+ * @param nodeIdentifiers Node identifiers
+ * @return An absolute schema node identifier
+ * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
+ * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
+ */
public static @NonNull Absolute of(final QName... nodeIdentifiers) {
- return new Absolute(Arrays.asList(nodeIdentifiers));
+ return of(Arrays.asList(nodeIdentifiers));
}
+ /**
+ * Create an absolute schema node identifier composed of multiple node identifiers.
+ *
+ * @param nodeIdentifiers Node identifiers
+ * @return An absolute schema node identifier
+ * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
+ * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
+ */
public static @NonNull Absolute of(final Collection<QName> nodeIdentifiers) {
- return new Absolute(nodeIdentifiers);
+ final ImmutableList<QName> qnames = checkQNames(nodeIdentifiers);
+ return qnames.size() == 1 ? of(qnames.get(0)) : new AbsoluteMultiple(qnames);
}
- public @NonNull Absolute intern() {
+ /**
+ * Return an interned reference to an equivalent object.
+ *
+ * @return An interned reference, or this object if it was previously interned.
+ */
+ public final @NonNull Absolute intern() {
return INTERNER.intern(this);
}
@Override
- SchemaPath implicitSchemaPathParent() {
+ final SchemaPath implicitSchemaPathParent() {
return SchemaPath.ROOT;
}
+
+ @Override
+ final String className() {
+ return "Absolute";
+ }
}
/**
* A descendant schema node identifier.
*/
- public static final class Descendant extends SchemaNodeIdentifier {
- Descendant(final QName qname) {
- super(qname);
- }
-
- Descendant(final Collection<QName> qnames) {
- super(qnames);
+ public abstract static class Descendant extends SchemaNodeIdentifier {
+ Descendant() {
+ // Hidden on purpose
}
+ /**
+ * Create a descendant schema node identifier composed of a single node identifier.
+ *
+ * @param nodeIdentifier Single node identifier
+ * @return A descendant schema node identifier
+ * @throws NullPointerException if {@code nodeIdentifier} is null
+ */
public static @NonNull Descendant of(final QName nodeIdentifier) {
- return new Descendant(nodeIdentifier);
+ return new DescendantSingle(nodeIdentifier);
}
+ /**
+ * Create a descendant schema node identifier composed of multiple node identifiers.
+ *
+ * @param nodeIdentifiers Node identifiers
+ * @return A descendant schema node identifier
+ * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
+ * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
+ */
public static @NonNull Descendant of(final QName... nodeIdentifiers) {
- return new Descendant(Arrays.asList(nodeIdentifiers));
+ return of(Arrays.asList(nodeIdentifiers));
}
+ /**
+ * Create a descendant schema node identifier composed of multiple node identifiers.
+ *
+ * @param nodeIdentifiers Node identifiers
+ * @return A descendant schema node identifier
+ * @throws NullPointerException if {@code nodeIdentifiers} or any of its members is null
+ * @throws IllegalArgumentException if {@code nodeIdentifiers} is empty
+ */
public static @NonNull Descendant of(final Collection<QName> nodeIdentifiers) {
- return new Descendant(nodeIdentifiers);
+ final ImmutableList<QName> qnames = checkQNames(nodeIdentifiers);
+ return qnames.size() == 1 ? of(qnames.get(0)) : new DescandantMultiple(qnames);
}
@Override
- SchemaPath implicitSchemaPathParent() {
+ final SchemaPath implicitSchemaPathParent() {
return SchemaPath.SAME;
}
+
+ @Override
+ final String className() {
+ return "Descendant";
+ }
+ }
+
+ private static final class AbsoluteSingle extends Absolute {
+ private final @NonNull QName qname;
+
+ AbsoluteSingle(final QName qname) {
+ this.qname = requireNonNull(qname);
+ }
+
+ @Override
+ public ImmutableList<QName> getNodeIdentifiers() {
+ return ImmutableList.of(qname);
+ }
+
+ @Override
+ public QName firstNodeIdentifier() {
+ return qname;
+ }
+
+ @Override
+ public QName lastNodeIdentifier() {
+ return qname;
+ }
+
+ @Override
+ Object pathObject() {
+ return qname;
+ }
+ }
+
+ private static final class AbsoluteMultiple extends Absolute {
+ private final @NonNull ImmutableList<QName> qnames;
+
+ AbsoluteMultiple(final ImmutableList<QName> qnames) {
+ this.qnames = requireNonNull(qnames);
+ }
+
+ @Override
+ public ImmutableList<QName> getNodeIdentifiers() {
+ return qnames;
+ }
+
+ @Override
+ Object pathObject() {
+ return qnames;
+ }
+ }
+
+ private static final class DescendantSingle extends Descendant {
+ private final @NonNull QName qname;
+
+ DescendantSingle(final QName qname) {
+ this.qname = requireNonNull(qname);
+ }
+
+ @Override
+ public ImmutableList<QName> getNodeIdentifiers() {
+ return ImmutableList.of(qname);
+ }
+
+ @Override
+ public QName firstNodeIdentifier() {
+ return qname;
+ }
+
+ @Override
+ public QName lastNodeIdentifier() {
+ return qname;
+ }
+
+ @Override
+ Object pathObject() {
+ return qname;
+ }
+ }
+
+ private static final class DescandantMultiple extends Descendant {
+ private final @NonNull ImmutableList<QName> qnames;
+
+ DescandantMultiple(final ImmutableList<QName> qnames) {
+ this.qnames = requireNonNull(qnames);
+ }
+
+ @Override
+ public ImmutableList<QName> getNodeIdentifiers() {
+ return qnames;
+ }
+
+ @Override
+ Object pathObject() {
+ return qnames;
+ }
}
private static final AtomicReferenceFieldUpdater<SchemaNodeIdentifier, SchemaPath> SCHEMAPATH_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(SchemaNodeIdentifier.class, SchemaPath.class, "schemaPath");
- private final @NonNull Object qnames;
-
// Cached SchemaPath.
private volatile SchemaPath schemaPath;
// Cached hashCode
private volatile int hash;
- SchemaNodeIdentifier(final QName qname) {
- this.qnames = requireNonNull(qname);
+ SchemaNodeIdentifier() {
+ // Hidden on purpose
}
- SchemaNodeIdentifier(final Collection<QName> qnames) {
- final ImmutableList<QName> tmp = ImmutableList.copyOf(qnames);
- checkArgument(!tmp.isEmpty(), "SchemaNodeIdentifier has to have at least one node identifier");
- this.qnames = tmp.size() == 1 ? tmp.get(0) : tmp;
+ /**
+ * Return the non-empty sequence of node identifiers which constitute this schema node identifier.
+ *
+ * @return Non-empty sequence of node identifiers
+ */
+ public abstract @NonNull List<QName> getNodeIdentifiers();
+
+ /**
+ * Return the first node identifier. This method is equivalent to {@code getNodeIdentifiers().get(0)}, but is
+ * potentially more efficient.
+ *
+ * @return The first node identifier
+ */
+ public @NonNull QName firstNodeIdentifier() {
+ return getNodeIdentifiers().get(0);
}
- public @NonNull List<QName> getNodeIdentifiers() {
- return qnames instanceof QName ? ImmutableList.of((QName) qnames) : (ImmutableList<QName>) qnames;
+ /**
+ * Return the last node identifier. This method is equivalent to {@code getNodeIdentifiers().get(size - 1)}, but
+ * is potentially more efficient.
+ *
+ * @return The last node identifier
+ */
+ public @NonNull QName lastNodeIdentifier() {
+ final List<QName> local = getNodeIdentifiers();
+ return local.get(local.size() - 1);
}
/**
return ret != null ? ret : loadSchemaPath();
}
- private SchemaPath loadSchemaPath() {
- final SchemaPath newPath = implicitSchemaPathParent().createChild(getNodeIdentifiers());
- return SCHEMAPATH_UPDATER.compareAndSet(this, null, newPath) ? newPath : schemaPath;
- }
-
- abstract SchemaPath implicitSchemaPathParent();
-
@Override
public final int hashCode() {
final int local;
- return (local = hash) != 0 ? local : (hash = qnames.hashCode());
+ return (local = hash) != 0 ? local : (hash = pathObject().hashCode());
}
@Override
public final boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- return qnames.equals(((SchemaNodeIdentifier) obj).qnames);
+ return this == obj || obj != null && getClass() == obj.getClass()
+ && pathObject().equals(((SchemaNodeIdentifier) obj).pathObject());
}
@Override
public final String toString() {
- return MoreObjects.toStringHelper(this).add("qnames", toStringQNames()).toString();
+ return MoreObjects.toStringHelper(className()).add("qnames", toStringQNames()).toString();
+ }
+
+ abstract @NonNull SchemaPath implicitSchemaPathParent();
+
+ abstract @NonNull Object pathObject();
+
+ abstract @NonNull String className();
+
+ private @NonNull SchemaPath loadSchemaPath() {
+ final SchemaPath newPath = implicitSchemaPathParent().createChild(getNodeIdentifiers());
+ return SCHEMAPATH_UPDATER.compareAndSet(this, null, newPath) ? newPath : schemaPath;
}
private List<?> toStringQNames() {
return ids.size() < 2 ? ids : simplifyQNames(ids);
}
+ @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+ justification = "https://github.com/spotbugs/spotbugs/issues/811")
+ private static ImmutableList<QName> checkQNames(final Collection<QName> qnames) {
+ final ImmutableList<QName> ret = ImmutableList.copyOf(qnames);
+ checkArgument(!ret.isEmpty(), "SchemaNodeIdentifier has to have at least one node identifier");
+ return ret;
+ }
+
private static List<?> simplifyQNames(final List<QName> qnames) {
final List<Object> ret = new ArrayList<>(qnames.size());