Add AbstractHierarchicalIdentifier
[yangtools.git] / common / concepts / src / main / java / org / opendaylight / yangtools / concepts / AbstractHierarchicalIdentifier.java
diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractHierarchicalIdentifier.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/AbstractHierarchicalIdentifier.java
new file mode 100644 (file)
index 0000000..25bfd2f
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.concepts;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
+import java.util.Iterator;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * An opinionated superclass for implementing {@link HierarchicalIdentifier}s.
+ *
+ * <p>
+ * It assumes that the identifier is composed of multiple non-null steps available via {@link #itemIterator()} and that
+ * {@link #contains(AbstractHierarchicalIdentifier)} semantics can be implemented using simple in-order comparison of
+ * these steps.
+ *
+ * <p>
+ * Furthermore it mandates that serialization occurs via {@link #writeReplace()}, following the Serialization Proxy
+ * pattern.
+ */
+public abstract class AbstractHierarchicalIdentifier<T extends AbstractHierarchicalIdentifier<T, I>, I>
+        implements HierarchicalIdentifier<T> {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public final boolean contains(final T other) {
+        if (this != other) {
+            final var oit = other.itemIterator();
+            final var it = itemIterator();
+            while (it.hasNext()) {
+                if (!oit.hasNext() || !it.next().equals(oit.next())) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    protected abstract @NonNull Iterator<@NonNull I> itemIterator();
+
+    @java.io.Serial
+    protected abstract @NonNull Object writeReplace() throws ObjectStreamException;
+
+    /**
+     * Utility method throwing a {@link NotSerializableException}. It is useful when implementing
+     * {@link #readObject(ObjectInputStream)}, {@link #readObjectNoData()} and {@link #writeObject(ObjectOutputStream)}
+     * methods, which all subclasses should define as serialization is driven via {@link #writeReplace()}.
+     *
+     * @throws NotSerializableException always
+     */
+    protected final void throwNSE() throws NotSerializableException {
+        throw new NotSerializableException(getClass().getName());
+    }
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(Object obj);
+
+    @Override
+    public abstract String toString();
+
+    @java.io.Serial
+    private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
+        throwNSE();
+    }
+
+    @java.io.Serial
+    private void readObjectNoData() throws ObjectStreamException {
+        throwNSE();
+    }
+
+    @java.io.Serial
+    private void writeObject(final ObjectOutputStream stream) throws IOException {
+        throwNSE();
+    }
+}