Migrate yang-common to use JDT annotations
[yangtools.git] / yang / yang-common / src / main / java / org / opendaylight / yangtools / yang / common / QNameModule.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.common;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.MoreObjects;
13 import com.google.common.collect.Interner;
14 import com.google.common.collect.Interners;
15 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
16 import java.io.DataInput;
17 import java.io.DataOutput;
18 import java.io.IOException;
19 import java.io.Serializable;
20 import java.net.URI;
21 import java.net.URISyntaxException;
22 import java.util.Objects;
23 import java.util.Optional;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.opendaylight.yangtools.concepts.Identifier;
27 import org.opendaylight.yangtools.concepts.Immutable;
28 import org.opendaylight.yangtools.concepts.WritableObject;
29
30 @NonNullByDefault
31 public final class QNameModule implements Comparable<QNameModule>, Immutable, Serializable, Identifier, WritableObject {
32     private static final Interner<QNameModule> INTERNER = Interners.newWeakInterner();
33     private static final long serialVersionUID = 3L;
34
35     private final URI namespace;
36     private final @Nullable Revision revision;
37
38     private transient int hash = 0;
39
40     private QNameModule(final URI namespace, final @Nullable Revision revision) {
41         this.namespace = requireNonNull(namespace);
42         this.revision = revision;
43     }
44
45     /**
46      * Return an interned reference to a equivalent QNameModule.
47      *
48      * @return Interned reference, or this object if it was interned.
49      */
50     public QNameModule intern() {
51         return INTERNER.intern(this);
52     }
53
54     /**
55      * Create a new QName module instance with specified namespace/revision.
56      *
57      * @param namespace Module namespace
58      * @param revision Module revision
59      * @return A new, potentially shared, QNameModule instance
60      */
61     public static QNameModule create(final URI namespace, final Optional<Revision> revision) {
62         return new QNameModule(namespace, revision.isPresent() ? revision.get() : null);
63     }
64
65     /**
66      * Create a new QName module instance with specified namespace and norevision.
67      *
68      * @param namespace Module namespace
69      * @return A new, potentially shared, QNameModule instance
70      */
71     @SuppressFBWarnings("NP_NULL_PARAM_DEREF_NONVIRTUAL")
72     public static QNameModule create(final URI namespace) {
73         return new QNameModule(namespace, null);
74     }
75
76     /**
77      * Create a new QName module instance with specified namespace/revision.
78      *
79      * @param namespace Module namespace
80      * @param revision Module revision
81      * @return A new, potentially shared, QNameModule instance
82      */
83     public static QNameModule create(final URI namespace, @Nullable final Revision revision) {
84         return new QNameModule(namespace, revision);
85     }
86
87     /**
88      * Read a QNameModule from a DataInput. The format is expected to match the output format
89      * of {@link #writeTo(DataOutput)}.
90      *
91      * @param in DataInput to read
92      * @return A QNameModule instance
93      * @throws IOException if I/O error occurs
94      */
95     public static QNameModule readFrom(final DataInput in) throws IOException {
96         final String namespace = in.readUTF();
97         final String revision = in.readUTF();
98         return new QNameModule(URI.create(namespace), revision.isEmpty() ? null : Revision.of(revision));
99     }
100
101     /**
102      * Returns the namespace of the module which is specified as argument of
103      * YANG Module <b><font color="#00FF00">namespace</font></b> keyword.
104      *
105      * @return URI format of the namespace of the module
106      */
107     public URI getNamespace() {
108         return namespace;
109     }
110
111     /**
112      * Returns the revision date for the module.
113      *
114      * @return date of the module revision which is specified as argument of
115      *         YANG Module <b><font color="#339900">revison</font></b> keyword
116      */
117     public Optional<Revision> getRevision() {
118         final Revision rev = revision;
119         return rev == null ? Optional.empty() : Optional.of(rev);
120     }
121
122     @Override
123     @SuppressWarnings("checkstyle:parameterName")
124     public int compareTo(final QNameModule o) {
125         int cmp = namespace.compareTo(o.namespace);
126         if (cmp != 0) {
127             return cmp;
128         }
129         return Revision.compare(revision, o.revision);
130     }
131
132     /**
133      * Returns a QNameModule with the same namespace, but with no revision. If this QNameModule does not have
134      * a revision, this object is returned.
135      *
136      * @return a QNameModule with the same namespace, but with no revision.
137      */
138     @SuppressFBWarnings("NP_NULL_PARAM_DEREF_NONVIRTUAL")
139     public QNameModule withoutRevision() {
140         return revision == null ? this : new QNameModule(namespace, null);
141     }
142
143     @Override
144     public void writeTo(final DataOutput out) throws IOException {
145         out.writeUTF(namespace.toString());
146         out.writeUTF(revision == null ? "" : revision.toString());
147     }
148
149     @Override
150     public int hashCode() {
151         if (hash == 0) {
152             hash = Objects.hash(namespace, revision);
153         }
154         return hash;
155     }
156
157     @Override
158     public boolean equals(final @Nullable Object obj) {
159         if (this == obj) {
160             return true;
161         }
162         if (!(obj instanceof QNameModule)) {
163             return false;
164         }
165         final QNameModule other = (QNameModule) obj;
166         return Objects.equals(revision, other.revision) && namespace.equals(other.namespace);
167     }
168
169     /**
170      * Returns a namespace in form defined by section 5.6.4. of
171      * <a href=https://tools.ietf.org/html/rfc6020">RFC6020</a>, for example
172      * {@code http://example.acme.com/system?revision=2008-04-01}.
173      *
174      * @return Namespace in form defined by section 5.6.4. of RFC6020.
175      * @throws URISyntaxException on incorrect namespace definition
176      *
177      */
178     URI getRevisionNamespace() throws URISyntaxException {
179         final String query = revision == null ? "" : "revision=" + revision.toString();
180         return new URI(namespace.getScheme(), namespace.getUserInfo(), namespace.getHost(), namespace.getPort(),
181             namespace.getPath(), query, namespace.getFragment());
182     }
183
184     @Override
185     public String toString() {
186         return MoreObjects.toStringHelper(QNameModule.class).omitNullValues().add("ns", namespace)
187             .add("rev", revision).toString();
188     }
189 }