Migrate yang-common to use JDT annotations
[yangtools.git] / yang / yang-common / src / main / java / org / opendaylight / yangtools / yang / common / QNameModule.java
index cd6c0e9ef855f74ae7dac5ea26314312ad2a7d22..92526280b8d5103d69235fdac5b3e212536a39d8 100644 (file)
@@ -7,45 +7,48 @@
  */
 package org.opendaylight.yangtools.yang.common;
 
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.base.MoreObjects;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
 import java.io.Serializable;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.Date;
+import java.util.Objects;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Identifier;
 import org.opendaylight.yangtools.concepts.Immutable;
-import org.opendaylight.yangtools.objcache.ObjectCache;
-import org.opendaylight.yangtools.objcache.ObjectCacheFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public final class QNameModule implements Immutable, Serializable {
-    private static final ObjectCache CACHE = ObjectCacheFactory.getObjectCache(QNameModule.class);
-    private static final Logger LOG = LoggerFactory.getLogger(QNameModule.class);
-    private static final QNameModule NULL_INSTANCE = new QNameModule(null, null);
-    private static final long serialVersionUID = 1L;
-
-    //Nullable
-    private final URI namespace;
+import org.opendaylight.yangtools.concepts.WritableObject;
 
-    //Nullable
-    private final Date revision;
+@NonNullByDefault
+public final class QNameModule implements Comparable<QNameModule>, Immutable, Serializable, Identifier, WritableObject {
+    private static final Interner<QNameModule> INTERNER = Interners.newWeakInterner();
+    private static final long serialVersionUID = 3L;
+
+    private final URI namespace;
+    private final @Nullable Revision revision;
 
-    //Nullable
-    private volatile String formattedRevision;
+    private transient int hash = 0;
 
-    private QNameModule(final URI namespace, final Date revision) {
-        this.namespace = namespace;
+    private QNameModule(final URI namespace, final @Nullable Revision revision) {
+        this.namespace = requireNonNull(namespace);
         this.revision = revision;
     }
 
     /**
-     * Look up specified module in the global cache and return a shared reference.
+     * Return an interned reference to a equivalent QNameModule.
      *
-     * @param module Module instance
-     * @return Cached instance, according to {@link ObjectCache} policy.
+     * @return Interned reference, or this object if it was interned.
      */
-    public static QNameModule cachedReference(final QNameModule module) {
-        return CACHE.getReference(module);
+    public QNameModule intern() {
+        return INTERNER.intern(this);
     }
 
     /**
@@ -55,26 +58,44 @@ public final class QNameModule implements Immutable, Serializable {
      * @param revision Module revision
      * @return A new, potentially shared, QNameModule instance
      */
-    public static QNameModule create(final URI namespace, final Date revision) {
-        if (namespace == null && revision == null) {
-            return NULL_INSTANCE;
-        }
-
-        return new QNameModule(namespace, revision);
+    public static QNameModule create(final URI namespace, final Optional<Revision> revision) {
+        return new QNameModule(namespace, revision.isPresent() ? revision.get() : null);
     }
 
-    public String getFormattedRevision() {
-        if (revision == null) {
-            return null;
-        }
+    /**
+     * Create a new QName module instance with specified namespace and norevision.
+     *
+     * @param namespace Module namespace
+     * @return A new, potentially shared, QNameModule instance
+     */
+    @SuppressFBWarnings("NP_NULL_PARAM_DEREF_NONVIRTUAL")
+    public static QNameModule create(final URI namespace) {
+        return new QNameModule(namespace, null);
+    }
 
-        String ret = formattedRevision;
-        if (ret == null) {
-            ret = SimpleDateFormatUtil.getRevisionFormat().format(revision);
-            formattedRevision = ret;
-        }
+    /**
+     * Create a new QName module instance with specified namespace/revision.
+     *
+     * @param namespace Module namespace
+     * @param revision Module revision
+     * @return A new, potentially shared, QNameModule instance
+     */
+    public static QNameModule create(final URI namespace, @Nullable final Revision revision) {
+        return new QNameModule(namespace, revision);
+    }
 
-        return ret;
+    /**
+     * 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 QNameModule readFrom(final DataInput in) throws IOException {
+        final String namespace = in.readUTF();
+        final String revision = in.readUTF();
+        return new QNameModule(URI.create(namespace), revision.isEmpty() ? null : Revision.of(revision));
     }
 
     /**
@@ -93,20 +114,48 @@ public final class QNameModule implements Immutable, Serializable {
      * @return date of the module revision which is specified as argument of
      *         YANG Module <b><font color="#339900">revison</font></b> keyword
      */
-    public Date getRevision() {
-        return revision;
+    public Optional<Revision> getRevision() {
+        final Revision rev = revision;
+        return rev == null ? Optional.empty() : Optional.of(rev);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:parameterName")
+    public int compareTo(final QNameModule o) {
+        int cmp = namespace.compareTo(o.namespace);
+        if (cmp != 0) {
+            return cmp;
+        }
+        return Revision.compare(revision, o.revision);
+    }
+
+    /**
+     * 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.
+     */
+    @SuppressFBWarnings("NP_NULL_PARAM_DEREF_NONVIRTUAL")
+    public QNameModule withoutRevision() {
+        return revision == null ? this : new QNameModule(namespace, null);
+    }
+
+    @Override
+    public void writeTo(final DataOutput out) throws IOException {
+        out.writeUTF(namespace.toString());
+        out.writeUTF(revision == null ? "" : revision.toString());
     }
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = (namespace == null) ? 0 : namespace.hashCode();
-        result = prime * result + ((revision == null) ? 0 : revision.hashCode());
-        return result;
+        if (hash == 0) {
+            hash = Objects.hash(namespace, revision);
+        }
+        return hash;
     }
 
     @Override
-    public boolean equals(final Object obj) {
+    public boolean equals(final @Nullable Object obj) {
         if (this == obj) {
             return true;
         }
@@ -114,57 +163,27 @@ public final class QNameModule implements Immutable, Serializable {
             return false;
         }
         final QNameModule other = (QNameModule) obj;
-        if (revision == null) {
-            if (other.revision != null) {
-                return false;
-            }
-        } else if (!revision.equals(other.revision)) {
-            return false;
-        }
-        if (namespace == null) {
-            if (other.namespace != null) {
-                return false;
-            }
-        } else if (!namespace.equals(other.namespace)) {
-            return false;
-        }
-        return true;
+        return Objects.equals(revision, other.revision) && namespace.equals(other.namespace);
     }
 
     /**
-     * Returns a namespace in form defined by section 5.6.4. of {@link https
-     * ://tools.ietf.org/html/rfc6020}, if namespace is not correctly defined,
-     * the method will return <code>null</code> <br>
-     * example "http://example.acme.com/system?revision=2008-04-01"
+     * Returns a namespace in form defined by section 5.6.4. of
+     * <a href=https://tools.ietf.org/html/rfc6020">RFC6020</a>, for example
+     * {@code http://example.acme.com/system?revision=2008-04-01}.
      *
-     * @return namespace in form defined by section 5.6.4. of {@link https
-     *         ://tools.ietf.org/html/rfc6020}, if namespace is not correctly
-     *         defined, the method will return <code>null</code>
+     * @return Namespace in form defined by section 5.6.4. of RFC6020.
+     * @throws URISyntaxException on incorrect namespace definition
      *
      */
-    URI getRevisionNamespace() {
-
-        if (namespace == null) {
-            return null;
-        }
-
-        String query = "";
-        if (revision != null) {
-            query = "revision=" + getFormattedRevision();
-        }
-
-        URI compositeURI = null;
-        try {
-            compositeURI = new URI(namespace.getScheme(), namespace.getUserInfo(), namespace.getHost(),
-                    namespace.getPort(), namespace.getPath(), query, namespace.getFragment());
-        } catch (URISyntaxException e) {
-            LOG.error("", e);
-        }
-        return compositeURI;
+    URI getRevisionNamespace() throws URISyntaxException {
+        final String query = revision == null ? "" : "revision=" + revision.toString();
+        return new URI(namespace.getScheme(), namespace.getUserInfo(), namespace.getHost(), namespace.getPort(),
+            namespace.getPath(), query, namespace.getFragment());
     }
 
     @Override
     public String toString() {
-        return MoreObjects.toStringHelper(this).omitNullValues().add("ns", getNamespace()).add("rev", getFormattedRevision()).toString();
+        return MoreObjects.toStringHelper(QNameModule.class).omitNullValues().add("ns", namespace)
+            .add("rev", revision).toString();
     }
 }