Use RevisionUnion in QNameModule 42/109642/16
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 5 Jan 2024 14:35:28 +0000 (15:35 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 27 Jan 2024 02:17:23 +0000 (03:17 +0100)
QNameModule holds an optional Revision, which is inconvenient. This
patch retrofits RevisionUnion into it.

Since we are in this area, improve instantiation api, introducing of()
and ofRevision() convenience methods and deprecate create() methods.

Since QNameModule is a WritableObject composed of XMLNamespace and
RevisionUnion, make the two also WritableObjects and delegate to them.

Change-Id: Ib5349ef324d73340b285e9634e579825e55ab95d
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/NSv1.java
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/NotRevision.java
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QName.java
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/QNameModule.java
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Revision.java
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/RevisionUnion.java
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/XMLNamespace.java
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/XNv1.java
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangConstants.java
common/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/BiMapYangNamespaceContextTest.java
common/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/QNameTest.java

index 10d762e4f5934d42020cee9424fca8de3bb8b18b..a265c169afd8201d6efe22360b5bcd8b817fbafa 100644 (file)
@@ -14,13 +14,12 @@ import java.io.Externalizable;
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
-import java.io.Serial;
 
 /**
  * Externalizable proxy for {@link QNameModule}.
  */
 final class NSv1 implements Externalizable {
-    @Serial
+    @java.io.Serial
     private static final long serialVersionUID = 1L;
 
     private QNameModule namespace;
@@ -44,7 +43,7 @@ final class NSv1 implements Externalizable {
         namespace = QNameModule.readFrom(in);
     }
 
-    @Serial
+    @java.io.Serial
     Object readResolve() {
         return verifyNotNull(namespace);
     }
index 75b5472b6d5ce109395e843b9f47d1f9201dad7e..f35f83ffe35d4b1f54dd4e9665ceeebfc882ef9f 100644 (file)
@@ -59,6 +59,16 @@ public final class NotRevision implements RevisionUnion {
         return "";
     }
 
+    @Override
+    public int hashCode() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(final @Nullable Object obj) {
+        return this == obj;
+    }
+
     @Override
     public String toString() {
         return unionString();
index 4ab4af6dd6aa8f1506eaf1293b854798089e89d2..5a291d9f88339a19bd97544d5a395c59bbda1b04 100644 (file)
@@ -76,7 +76,7 @@ public final class QName extends AbstractQName implements Comparable<QName> {
      *            YANG schema identifier
      */
     private QName(final XMLNamespace namespace, final String localName) {
-        this(QNameModule.create(namespace), checkLocalName(localName));
+        this(QNameModule.of(namespace), checkLocalName(localName));
     }
 
     public static @NonNull QName create(final String input) {
@@ -123,7 +123,7 @@ public final class QName extends AbstractQName implements Comparable<QName> {
      */
     public static @NonNull QName create(final XMLNamespace namespace, final @Nullable Revision revision,
             final String localName) {
-        return create(QNameModule.create(namespace, revision), localName);
+        return create(QNameModule.ofRevision(namespace, revision), localName);
     }
 
     /**
@@ -136,7 +136,7 @@ public final class QName extends AbstractQName implements Comparable<QName> {
      */
     public static @NonNull QName create(final XMLNamespace namespace, final Optional<Revision> revision,
             final String localName) {
-        return create(QNameModule.create(namespace, revision), localName);
+        return create(QNameModule.ofRevision(namespace, revision.orElse(null)), localName);
     }
 
     /**
@@ -148,7 +148,7 @@ public final class QName extends AbstractQName implements Comparable<QName> {
      * @return Instance of QName
      */
     public static @NonNull QName create(final String namespace, final String localName, final Revision revision) {
-        return create(QNameModule.create(XMLNamespace.of(namespace), revision), localName);
+        return create(QNameModule.of(XMLNamespace.of(namespace), revision), localName);
     }
 
     /**
@@ -163,7 +163,7 @@ public final class QName extends AbstractQName implements Comparable<QName> {
      *         to {@code YYYY-mm-dd}.
      */
     public static @NonNull QName create(final String namespace, final String revision, final String localName) {
-        return create(XMLNamespace.of(namespace), Revision.of(revision), localName);
+        return create(QNameModule.ofRevision(namespace, revision), localName);
     }
 
     /**
@@ -204,7 +204,7 @@ public final class QName extends AbstractQName implements Comparable<QName> {
             return aware.readQName();
         }
 
-        final QNameModule module = QNameModule.readFrom(in);
+        final var module = QNameModule.readFrom(in);
         return new QName(module, checkLocalName(in.readUTF()));
     }
 
@@ -240,7 +240,7 @@ public final class QName extends AbstractQName implements Comparable<QName> {
      * @return XMLNamespace assigned to the YANG module.
      */
     public @NonNull XMLNamespace getNamespace() {
-        return module.getNamespace();
+        return module.namespace();
     }
 
     /**
@@ -249,7 +249,7 @@ public final class QName extends AbstractQName implements Comparable<QName> {
      * @return revision of the YANG module if the module has defined revision.
      */
     public @NonNull Optional<Revision> getRevision() {
-        return module.getRevision();
+        return module.findRevision();
     }
 
     @Override
index 0441105afc9390ff0e8b295816632217f500bc5d..8d12af8deef3484c1ce6f0fff8cf76b24072fc29 100644 (file)
@@ -15,7 +15,9 @@ import com.google.common.collect.Interners;
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
-import java.io.Serial;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
 import java.io.Serializable;
 import java.util.Objects;
 import java.util.Optional;
@@ -32,26 +34,99 @@ import org.opendaylight.yangtools.concepts.WritableObject;
  */
 public final class QNameModule implements Comparable<QNameModule>, Immutable, Serializable, Identifier, WritableObject {
     private static final Interner<QNameModule> INTERNER = Interners.newWeakInterner();
-    @Serial
+    @java.io.Serial
     private static final long serialVersionUID = 3L;
 
     private final @NonNull XMLNamespace namespace;
-    private final @Nullable Revision revision;
+    private final @NonNull RevisionUnion revUnion;
 
     private transient int hash = 0;
 
-    private QNameModule(final XMLNamespace namespace, final @Nullable Revision revision) {
+    private QNameModule(final XMLNamespace namespace, final RevisionUnion revUnion) {
         this.namespace = requireNonNull(namespace);
-        this.revision = revision;
+        this.revUnion = requireNonNull(revUnion);
     }
 
     /**
-     * Return an interned reference to a equivalent QNameModule.
+     * Create a new QName module instance with specified {@link XMLNamespace} and no revision.
      *
-     * @return Interned reference, or this object if it was interned.
+     * @param namespace Module namespace
+     * @return A new, potentially shared, QNameModule instance
+     * @throws NullPointerException if any argument is {@code null}
      */
-    public @NonNull QNameModule intern() {
-        return INTERNER.intern(this);
+    public static @NonNull QNameModule of(final XMLNamespace namespace) {
+        return of(namespace, RevisionUnion.none());
+    }
+
+    /**
+     * Create a new QName module instance with specified {@link XMLNamespace} and {@link RevisionUnion}.
+     *
+     * @param namespace Module namespace
+     * @param revUnion Module revision union
+     * @return A new, potentially shared, QNameModule instance
+     * @throws NullPointerException if any argument is {@code null}
+     */
+    public static @NonNull QNameModule of(final XMLNamespace namespace, final RevisionUnion revUnion) {
+        return new QNameModule(namespace, revUnion);
+    }
+
+    /**
+     * Create a new QName module instance with specified {@link XMLNamespace} and {@link Revision}.
+     *
+     * @param namespace Module namespace
+     * @param revision Module revision
+     * @return A new, potentially shared, QNameModule instance
+     * @throws NullPointerException if any argument is {@code null}
+     */
+    public static @NonNull QNameModule of(final XMLNamespace namespace, final Revision revision) {
+        return new QNameModule(namespace, revision);
+    }
+
+    /**
+     * Create a new QName module instance with specified namespace string and no revision.
+     *
+     * @param namespace Module namespace
+     * @return A new, potentially shared, QNameModule instance
+     * @throws NullPointerException if any argument is {@code null}
+     */
+    public static @NonNull QNameModule of(final String namespace) {
+        return of(XMLNamespace.of(namespace));
+    }
+
+    /**
+     * Create a new QName module instance with specified namespace string and {@link RevisionUnion} string.
+     *
+     * @param namespace Module namespace
+     * @param unionString Module revision string or an empty string
+     * @return A new, potentially shared, QNameModule instance
+     * @throws NullPointerException if any argument is {@code null}
+     */
+    public static @NonNull QNameModule of(final String namespace, final String unionString) {
+        return of(XMLNamespace.of(namespace), RevisionUnion.of(unionString));
+    }
+
+    /**
+     * Create a new QName module instance with specified {@link XMLNamespace} and an optional {@link Revision}.
+     *
+     * @param namespace Module namespace
+     * @param revision Module revision
+     * @return A new, potentially shared, QNameModule instance
+     * @throws NullPointerException if any argument is {@code null}
+     */
+    public static @NonNull QNameModule ofRevision(final XMLNamespace namespace, final @Nullable Revision revision) {
+        return of(namespace, revision != null ? revision : RevisionUnion.none());
+    }
+
+    /**
+     * Create a new QName module instance with specified {@link XMLNamespace} and an optional {@link Revision}.
+     *
+     * @param namespace Module namespace
+     * @param revision Module revision string
+     * @return A new, potentially shared, QNameModule instance
+     * @throws NullPointerException if {@code namespace} is {@code null}
+     */
+    public static @NonNull QNameModule ofRevision(final String namespace, final @Nullable String revision) {
+        return of(XMLNamespace.of(namespace), revision != null ? Revision.of(revision) : RevisionUnion.none());
     }
 
     /**
@@ -60,10 +135,12 @@ public final class QNameModule implements Comparable<QNameModule>, Immutable, Se
      * @param namespace Module namespace
      * @param revision Module revision
      * @return A new, potentially shared, QNameModule instance
-     * @throws NullPointerException if any argument is null
+     * @throws NullPointerException if any argument is {@code null}
+     * @deprecated Use {@link #ofRevision(XMLNamespace, Revision)} instead
      */
+    @Deprecated(since = "12.0.1", forRemoval = true)
     public static @NonNull QNameModule create(final XMLNamespace namespace, final Optional<Revision> revision) {
-        return new QNameModule(namespace, revision.orElse(null));
+        return ofRevision(namespace, revision.orElse(null));
     }
 
     /**
@@ -72,9 +149,11 @@ public final class QNameModule implements Comparable<QNameModule>, Immutable, Se
      * @param namespace Module namespace
      * @return A new, potentially shared, QNameModule instance
      * @throws NullPointerException if {@code namespace} is null
+     * @deprecated Use {@link #of(XMLNamespace)} instead
      */
+    @Deprecated(since = "12.0.1", forRemoval = true)
     public static @NonNull QNameModule create(final XMLNamespace namespace) {
-        return new QNameModule(namespace, null);
+        return of(namespace);
     }
 
     /**
@@ -83,24 +162,24 @@ public final class QNameModule implements Comparable<QNameModule>, Immutable, Se
      * @param namespace Module namespace
      * @param revision Module revision
      * @return A new, potentially shared, QNameModule instance
-     * @throws NullPointerException if any argument is null
+     * @throws NullPointerException if any argument is {@code null}
+     * @deprecated Use {@link #ofRevision(XMLNamespace, Revision)} instead
      */
+    @Deprecated(since = "12.0.1", forRemoval = true)
     public static @NonNull QNameModule create(final XMLNamespace namespace, final @Nullable Revision revision) {
-        return new QNameModule(namespace, revision);
+        return ofRevision(namespace, revision);
     }
 
     /**
-     * Read a QNameModule from a DataInput. The format is expected to match the output format
-     * of {@link #writeTo(DataOutput)}.
+     * Read a QNameModule from a DataInput. The format is expected to match the output format of
+     * {@link #writeTo(DataOutput)}.
      *
      * @param in DataInput to read
      * @return A QNameModule instance
      * @throws IOException if I/O error occurs
      */
     public static @NonNull QNameModule readFrom(final DataInput in) throws IOException {
-        final var namespace = XMLNamespace.of(in.readUTF());
-        final var revStr = in.readUTF();
-        return new QNameModule(namespace, revStr.isEmpty() ? null : Revision.of(revStr));
+        return new QNameModule(XMLNamespace.readFrom(in), RevisionUnion.readFrom(in));
     }
 
     /**
@@ -108,66 +187,130 @@ public final class QNameModule implements Comparable<QNameModule>, Immutable, Se
      *
      * @return XMLNamespace of the namespace of the module
      */
-    public @NonNull XMLNamespace getNamespace() {
+    public @NonNull XMLNamespace namespace() {
         return namespace;
     }
 
+    /**
+     * Returns the namespace of the module which is specified as argument of YANG Module {@code namespace} keyword.
+     *
+     * @return XMLNamespace of the namespace of the module
+     * @deprecated Use {@link #namespace()} instead.
+     */
+    @Deprecated(since = "12.0.1", forRemoval = true)
+    public @NonNull XMLNamespace getNamespace() {
+        return namespace();
+    }
+
+    /**
+     * Returns the revision date for the module.
+     *
+     * @return date of the module revision which is specified as argument of YANG Module {@code revision} keyword
+     */
+    public @NonNull RevisionUnion revisionUnion() {
+        return revUnion;
+    }
+
+    /**
+     * Returns the revision date for the module.
+     *
+     * @return date of the module revision which is specified as argument of YANG Module {@code revision} keyword
+     */
+    public @Nullable Revision revision() {
+        return revUnion.revision();
+    }
+
+    /**
+     * Returns the revision date for the module.
+     *
+     * @return date of the module revision which is specified as argument of YANG Module {@code revision} keyword
+     */
+    public @NonNull Optional<Revision> findRevision() {
+        return revUnion.findRevision();
+    }
+
     /**
      * Returns the revision date for the module.
      *
      * @return date of the module revision which is specified as argument of YANG Module {@code revision} keyword
+     * @deprecated Use {@link #findRevision()} or {@link #revision()} instead.
      */
+    @Deprecated(since = "12.0.1", forRemoval = true)
     public @NonNull Optional<Revision> getRevision() {
-        return Optional.ofNullable(revision);
+        return findRevision();
+    }
+
+    /**
+     * Return an interned reference to a equivalent QNameModule.
+     *
+     * @return Interned reference, or this object if it was interned.
+     */
+    public @NonNull QNameModule intern() {
+        return INTERNER.intern(this);
     }
 
     @Override
     @SuppressWarnings("checkstyle:parameterName")
     public int compareTo(final QNameModule o) {
         int cmp;
-        return (cmp = namespace.compareTo(o.namespace)) != 0 ? cmp : Revision.compare(revision, o.revision);
+        return (cmp = namespace.compareTo(o.namespace)) != 0 ? cmp : revUnion.compareTo(o.revUnion);
     }
 
     /**
-     * Returns a QNameModule with the same namespace, but with no revision. If this QNameModule does not have
-     * revision, this object is returned.
+     * Returns a QNameModule with the same namespace, but with no revision. If this QNameModule does not have a
+     * revision, this object is returned.
      *
      * @return a QNameModule with the same namespace, but with no revision.
      */
     public @NonNull QNameModule withoutRevision() {
-        return revision == null ? this : new QNameModule(namespace, null);
+        return revUnion instanceof NotRevision ? this : of(namespace);
     }
 
     @Override
     public void writeTo(final DataOutput out) throws IOException {
         out.writeUTF(namespace.toString());
-        out.writeUTF(revision == null ? "" : revision.toString());
+        out.writeUTF(revUnion.unionString());
     }
 
     @Override
     public int hashCode() {
         if (hash == 0) {
-            hash = Objects.hash(namespace, revision);
+            hash = Objects.hash(namespace, revUnion);
         }
         return hash;
     }
 
     @Override
     public boolean equals(final Object obj) {
-        return this == obj || obj instanceof QNameModule other
-            && Objects.equals(revision, other.revision) && namespace.equals(other.namespace);
+        return this == obj || obj instanceof QNameModule other && revUnion.equals(other.revUnion)
+            && namespace.equals(other.namespace);
     }
 
     @Override
     public @NonNull String toString() {
         return MoreObjects.toStringHelper(QNameModule.class).omitNullValues()
             .add("ns", namespace)
-            .add("rev", revision)
+            .add("rev", revUnion.revision())
             .toString();
     }
 
-    @Serial
+    @java.io.Serial
     Object writeReplace() {
         return new NSv1(this);
     }
+
+    @java.io.Serial
+    private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
+        Revision.throwNSE();
+    }
+
+    @java.io.Serial
+    private void readObjectNoData() throws ObjectStreamException {
+        Revision.throwNSE();
+    }
+
+    @java.io.Serial
+    private void writeObject(final ObjectOutputStream stream) throws IOException {
+        Revision.throwNSE();
+    }
 }
index 53722d3ca877e70d8871bdfe268b566506ced98f..cb4ca183f4ec82bc8ae5a3431f32f8a911fec1ef 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.yangtools.yang.common;
 
 import static java.util.Objects.requireNonNull;
 
+import java.io.DataInput;
 import java.io.Externalizable;
 import java.io.IOException;
 import java.io.NotSerializableException;
@@ -109,6 +110,18 @@ public final class Revision implements RevisionUnion {
         return str;
     }
 
+    public static @NonNull Revision readFrom(final DataInput in) throws IOException {
+        return ofRead(in.readUTF());
+    }
+
+    static @NonNull Revision ofRead(final @NonNull String str) throws IOException {
+        try {
+            return of(str);
+        } catch (DateTimeParseException e) {
+            throw new IOException("Invalid revision-date string", e);
+        }
+    }
+
     /**
      * Compare two {@link Optional}s wrapping Revisions. Arguments and return value are consistent with
      * {@link java.util.Comparator#compare(Object, Object)} interface contract. Missing revisions compare as lower
index ea6cfc09d9d83949dbbc58db323c3a8c0adc8965..6c62189d91967a9a92b0951a94c3fe506ec177ce 100644 (file)
@@ -7,12 +7,16 @@
  */
 package org.opendaylight.yangtools.yang.common;
 
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
 import java.io.Serializable;
 import java.util.NoSuchElementException;
 import java.util.Optional;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.WritableObject;
 
 /**
  * A capture of an optional {@code revision-date}. This is a replacement for {@code Optional<Revision>}, with the added
@@ -31,7 +35,7 @@ import org.opendaylight.yangtools.concepts.Immutable;
  * }</pre>
  */
 @NonNullByDefault
-public sealed interface RevisionUnion extends Comparable<RevisionUnion>, Immutable, Serializable
+public sealed interface RevisionUnion extends Comparable<RevisionUnion>, Immutable, Serializable, WritableObject
         permits Revision, NotRevision {
     /**
      * Return empty {@link RevisionUnion}.
@@ -46,6 +50,10 @@ public sealed interface RevisionUnion extends Comparable<RevisionUnion>, Immutab
         return unionString.isEmpty() ? none() : Revision.of(unionString);
     }
 
+    static RevisionUnion of(final @Nullable Revision revision) {
+        return revision != null ? revision : none();
+    }
+
     /**
      * A {@code revision-date}-compliant date, or an empty string ({@code ""}).
      *
@@ -53,14 +61,6 @@ public sealed interface RevisionUnion extends Comparable<RevisionUnion>, Immutab
      */
     String unionString();
 
-    @Override
-    @SuppressWarnings("checkstyle:parameterName")
-    default int compareTo(final RevisionUnion o) {
-        // Since all strings conform to the format, we can use their comparable property to do the correct thing
-        // with respect to temporal ordering.
-        return unionString().compareTo(o.unionString());
-    }
-
     /**
      * Return the {@link Revision}, if present.
      *
@@ -80,6 +80,24 @@ public sealed interface RevisionUnion extends Comparable<RevisionUnion>, Immutab
         return revision;
     }
 
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    default int compareTo(final RevisionUnion o) {
+        // Since all strings conform to the format, we can use their comparable property to do the correct thing
+        // with respect to temporal ordering.
+        return unionString().compareTo(o.unionString());
+    }
+
+    @Override
+    default void writeTo(final DataOutput out) throws IOException {
+        out.writeUTF(unionString());
+    }
+
+    static RevisionUnion readFrom(final DataInput in) throws IOException {
+        final var unionString = in.readUTF();
+        return unionString.isEmpty() ? none() : Revision.ofRead(unionString);
+    }
+
     @Override
     int hashCode();
 
index 3e98f06e51285aceae9071d7714b0e40367f49be..1803854c749740f1426043d0d23e70082fe17257 100644 (file)
@@ -7,25 +7,35 @@
  */
 package org.opendaylight.yangtools.yang.common;
 
+import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.Interner;
 import com.google.common.collect.Interners;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
 import java.io.Serial;
 import java.io.Serializable;
 import java.net.URI;
 import java.net.URISyntaxException;
-import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.WritableObject;
 
 /**
  * A simple type capture of {@code namespace} statement's argument according to
  * <a href="https://www.rfc-editor.org/rfc/rfc6020#section-7.1.3">RFC6020</a>.
  */
-public final class XMLNamespace implements Comparable<XMLNamespace>, Immutable, Serializable {
-    private static final Interner<XMLNamespace> INTERNER = Interners.newWeakInterner();
-    @Serial
+@NonNullByDefault
+public final class XMLNamespace implements Comparable<XMLNamespace>, Immutable, Serializable, WritableObject {
+    @java.io.Serial
     private static final long serialVersionUID = 1L;
+    private static final Interner<XMLNamespace> INTERNER = Interners.newWeakInterner();
 
     private final String namespace;
 
@@ -34,10 +44,10 @@ public final class XMLNamespace implements Comparable<XMLNamespace>, Immutable,
     }
 
     // FIXME: add documentation
-    public static @NonNull XMLNamespace of(final String namespace) {
+    public static XMLNamespace of(final String namespace) {
         try {
-            // Validation only
-            new URI(namespace);
+            // FIXME: we want this validation, can we get it without the object allocation?
+            verifyNotNull(new URI(namespace));
         } catch (final URISyntaxException e) {
             throw new IllegalArgumentException("Namespace '" + namespace + "' is not a valid URI", e);
         }
@@ -50,7 +60,7 @@ public final class XMLNamespace implements Comparable<XMLNamespace>, Immutable,
      *
      * @return Interned reference, or this object if it was interned.
      */
-    public @NonNull XMLNamespace intern() {
+    public XMLNamespace intern() {
         return INTERNER.intern(this);
     }
 
@@ -60,13 +70,26 @@ public final class XMLNamespace implements Comparable<XMLNamespace>, Immutable,
         return namespace.compareTo(o.namespace);
     }
 
+    @Override
+    public void writeTo(final DataOutput out) throws IOException {
+        out.writeUTF(namespace);
+    }
+
+    public static XMLNamespace readFrom(final DataInput in) throws IOException {
+        try {
+            return of(in.readUTF());
+        } catch (IllegalArgumentException e) {
+            throw new IOException("Invalid namespace", e);
+        }
+    }
+
     @Override
     public int hashCode() {
         return namespace.hashCode();
     }
 
     @Override
-    public boolean equals(final Object obj) {
+    public boolean equals(final @Nullable Object obj) {
         return this == obj || obj instanceof XMLNamespace other && namespace.equals(other.namespace);
     }
 
@@ -79,4 +102,19 @@ public final class XMLNamespace implements Comparable<XMLNamespace>, Immutable,
     Object writeReplace() {
         return new XNv1(this);
     }
+
+    @java.io.Serial
+    private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
+        Revision.throwNSE();
+    }
+
+    @java.io.Serial
+    private void readObjectNoData() throws ObjectStreamException {
+        Revision.throwNSE();
+    }
+
+    @java.io.Serial
+    private void writeObject(final ObjectOutputStream stream) throws IOException {
+        Revision.throwNSE();
+    }
 }
index 3a84e236283d6c3bbf59a132495b01e81d26f0e1..4604f3ad52b864346d0a80e7b6abd442ec5f9fec 100644 (file)
@@ -14,13 +14,12 @@ import java.io.Externalizable;
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
-import java.io.Serial;
 
 /**
  * Externalizable proxy for {@link XMLNamespace}.
  */
 final class XNv1 implements Externalizable {
-    @Serial
+    @java.io.Serial
     private static final long serialVersionUID = 1L;
 
     private XMLNamespace namespace;
@@ -41,14 +40,10 @@ final class XNv1 implements Externalizable {
 
     @Override
     public void readExternal(final ObjectInput in) throws IOException {
-        try {
-            namespace = XMLNamespace.of(in.readUTF());
-        } catch (IllegalArgumentException e) {
-            throw new IOException("Invalid namespace", e);
-        }
+        namespace = XMLNamespace.readFrom(in);
     }
 
-    @Serial
+    @java.io.Serial
     Object readResolve() {
         return verifyNotNull(namespace);
     }
index f5019bfc6922de8f4929b1b8952b13d6553cafaf..f4eecc09035fe1f6bfd35695dfb85f3dc7b88f18 100644 (file)
@@ -38,7 +38,7 @@ public final class YangConstants {
     /**
      * Base QNameModule for all YANG statements.
      */
-    public static final QNameModule RFC6020_YANG_MODULE = QNameModule.create(RFC6020_YANG_NAMESPACE).intern();
+    public static final QNameModule RFC6020_YANG_MODULE = QNameModule.of(RFC6020_YANG_NAMESPACE).intern();
     /**
      * YIN File Extension, as defined in <a href="https://www.rfc-editor.org/rfc/rfc6020#section-14.2">RFC6020</a>.
      */
@@ -62,7 +62,7 @@ public final class YangConstants {
     /**
      * Base QNameModule for all YIN statements.
      */
-    public static final QNameModule RFC6020_YIN_MODULE = QNameModule.create(RFC6020_YIN_NAMESPACE).intern();
+    public static final QNameModule RFC6020_YIN_MODULE = QNameModule.of(RFC6020_YIN_NAMESPACE).intern();
     /**
      * Prefix for YANG-specific XPath functions.
      */
@@ -82,7 +82,7 @@ public final class YangConstants {
      * <a href="https://www.rfc-editor.org/rfc/rfc6241#section-10.3">ietf-netconf@2011-06-01.yang</a>.
      */
     public static final QNameModule RFC6241_YANG_MODULE =
-        QNameModule.create(NETCONF_NAMESPACE, Revision.of("2011-06-01")).intern();
+        QNameModule.of(NETCONF_NAMESPACE, Revision.of("2011-06-01")).intern();
     /**
      * {@code bad-attribute}, value is the name of the attribute.
      */
@@ -135,13 +135,13 @@ public final class YangConstants {
      * <a href="https://www.rfc-editor.org/rfc/rfc7895#section-2.2">ietf-yang-library@2016-06-21.yang</a>.
      */
     public static final QNameModule RFC7895_YANG_MODULE =
-        QNameModule.create(YANG_LIBRARY_NAMESPACE, Revision.of("2016-06-21")).intern();
+        QNameModule.of(YANG_LIBRARY_NAMESPACE, Revision.of("2016-06-21")).intern();
     /**
      * {@code ietf-yang-library} namespace bound to YANG through
      * <a href="https://www.rfc-editor.org/rfc/rfc8525#section-4">ietf-yang-library@2019-01-04.yang</a>.
      */
     public static final QNameModule RFC8525_YANG_MODULE =
-        QNameModule.create(YANG_LIBRARY_NAMESPACE, Revision.of("2019-01-04")).intern();
+        QNameModule.of(YANG_LIBRARY_NAMESPACE, Revision.of("2019-01-04")).intern();
 
     // Dummy template UnqualifiedQName. These are never leaked, but are used for efficient instantiation via
     // UnqualifiedQName#bindTo()
index 9113c8a183192be6da0638381a15593b9ed19a7a..9cdf9855550684e1b1786ee12157d64155046f17 100644 (file)
@@ -21,9 +21,9 @@ import java.util.Optional;
 import org.junit.jupiter.api.Test;
 
 public class BiMapYangNamespaceContextTest {
-    private static final QNameModule FOO = QNameModule.create(XMLNamespace.of("foo"));
-    private static final QNameModule BAR = QNameModule.create(XMLNamespace.of("bar"));
-    private static final QNameModule BAZ = QNameModule.create(XMLNamespace.of("baz"));
+    private static final QNameModule FOO = QNameModule.of("foo");
+    private static final QNameModule BAR = QNameModule.of("bar");
+    private static final QNameModule BAZ = QNameModule.of("baz");
 
     private final BiMapYangNamespaceContext context = new BiMapYangNamespaceContext(
         ImmutableBiMap.of("foo", FOO, "bar", BAR));
index 7ec6496cd4ec0a15bf14e53c5bc000da5dcad8bf..ef6238317b900d30fb782c44aec92b56a6bbc9a4 100644 (file)
@@ -11,7 +11,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import java.net.URISyntaxException;
 import org.junit.jupiter.api.Test;
 
 public class QNameTest {
@@ -22,7 +21,7 @@ public class QNameTest {
 
     @Test
     public void testStringSerialization() throws Exception {
-        QName qname = QName.create(NAMESPACE, REVISION, LOCALNAME);
+        var qname = QName.create(NAMESPACE, REVISION, LOCALNAME);
         assertEquals("(urn:foo?revision=2013-12-24)bar", qname.toString());
         assertEquals(qname, QName.create(qname.toString()));
     }
@@ -30,7 +29,7 @@ public class QNameTest {
     @Test
     public void testStringSerializationNoRevision() throws Exception {
         // no revision
-        QName qname = QName.create(NS, LOCALNAME);
+        var qname = QName.create(NS, LOCALNAME);
         assertEquals("(urn:foo)bar", qname.toString());
         assertEquals(qname, QName.create(qname.toString()));
     }
@@ -51,8 +50,8 @@ public class QNameTest {
         final String B = "b";
 
         // compare with namespace
-        QName qa = QName.create(A, REVISION, A);
-        QName qb = QName.create(B, REVISION, A);
+        var qa = QName.create(A, REVISION, A);
+        var qb = QName.create(B, REVISION, A);
         assertTrue(qa.compareTo(qb) < 0);
         assertTrue(qb.compareTo(qa) > 0);
 
@@ -76,9 +75,9 @@ public class QNameTest {
 
     @Test
     public void testQName() {
-        final QName qname = QName.create(NAMESPACE, REVISION, LOCALNAME);
-        final QName qname1 = QName.create(NAMESPACE, LOCALNAME);
-        final QName qname2 = QName.create(qname1, LOCALNAME);
+        final var qname = QName.create(NAMESPACE, REVISION, LOCALNAME);
+        final var qname1 = QName.create(NAMESPACE, LOCALNAME);
+        final var qname2 = QName.create(qname1, LOCALNAME);
         assertEquals(qname1, qname.withoutRevision());
         assertEquals(qname1, qname2);
         assertTrue(qname.isEqualWithoutRevision(qname1));
@@ -87,8 +86,8 @@ public class QNameTest {
     }
 
     @Test
-    public void testQNameModule() throws URISyntaxException {
-        final QNameModule qnameModule = QNameModule.create(NS, Revision.of("2000-01-01"));
+    public void testQNameModule() throws Exception {
+        final var qnameModule = QNameModule.of(NS, Revision.of("2000-01-01"));
         assertEquals("QNameModule{ns=urn:foo, rev=2000-01-01}", qnameModule.toString());
     }
 }