Use RevisionUnion in QNameModule
[yangtools.git] / common / 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 java.io.DataInput;
16 import java.io.DataOutput;
17 import java.io.IOException;
18 import java.io.ObjectInputStream;
19 import java.io.ObjectOutputStream;
20 import java.io.ObjectStreamException;
21 import java.io.Serializable;
22 import java.util.Objects;
23 import java.util.Optional;
24 import org.eclipse.jdt.annotation.NonNull;
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 /**
31  * A {@link XMLNamespace} bound to a particular model {@link Revision}. This is the primary way of identifying a YANG
32  * module namespace within an effective model world. The reason for this is that we support coexistence of multiple
33  * module revisions and hence cannot use plain module name or namespace to address them.
34  */
35 public final class QNameModule implements Comparable<QNameModule>, Immutable, Serializable, Identifier, WritableObject {
36     private static final Interner<QNameModule> INTERNER = Interners.newWeakInterner();
37     @java.io.Serial
38     private static final long serialVersionUID = 3L;
39
40     private final @NonNull XMLNamespace namespace;
41     private final @NonNull RevisionUnion revUnion;
42
43     private transient int hash = 0;
44
45     private QNameModule(final XMLNamespace namespace, final RevisionUnion revUnion) {
46         this.namespace = requireNonNull(namespace);
47         this.revUnion = requireNonNull(revUnion);
48     }
49
50     /**
51      * Create a new QName module instance with specified {@link XMLNamespace} and no revision.
52      *
53      * @param namespace Module namespace
54      * @return A new, potentially shared, QNameModule instance
55      * @throws NullPointerException if any argument is {@code null}
56      */
57     public static @NonNull QNameModule of(final XMLNamespace namespace) {
58         return of(namespace, RevisionUnion.none());
59     }
60
61     /**
62      * Create a new QName module instance with specified {@link XMLNamespace} and {@link RevisionUnion}.
63      *
64      * @param namespace Module namespace
65      * @param revUnion Module revision union
66      * @return A new, potentially shared, QNameModule instance
67      * @throws NullPointerException if any argument is {@code null}
68      */
69     public static @NonNull QNameModule of(final XMLNamespace namespace, final RevisionUnion revUnion) {
70         return new QNameModule(namespace, revUnion);
71     }
72
73     /**
74      * Create a new QName module instance with specified {@link XMLNamespace} and {@link Revision}.
75      *
76      * @param namespace Module namespace
77      * @param revision Module revision
78      * @return A new, potentially shared, QNameModule instance
79      * @throws NullPointerException if any argument is {@code null}
80      */
81     public static @NonNull QNameModule of(final XMLNamespace namespace, final Revision revision) {
82         return new QNameModule(namespace, revision);
83     }
84
85     /**
86      * Create a new QName module instance with specified namespace string and no revision.
87      *
88      * @param namespace Module namespace
89      * @return A new, potentially shared, QNameModule instance
90      * @throws NullPointerException if any argument is {@code null}
91      */
92     public static @NonNull QNameModule of(final String namespace) {
93         return of(XMLNamespace.of(namespace));
94     }
95
96     /**
97      * Create a new QName module instance with specified namespace string and {@link RevisionUnion} string.
98      *
99      * @param namespace Module namespace
100      * @param unionString Module revision string or an empty string
101      * @return A new, potentially shared, QNameModule instance
102      * @throws NullPointerException if any argument is {@code null}
103      */
104     public static @NonNull QNameModule of(final String namespace, final String unionString) {
105         return of(XMLNamespace.of(namespace), RevisionUnion.of(unionString));
106     }
107
108     /**
109      * Create a new QName module instance with specified {@link XMLNamespace} and an optional {@link Revision}.
110      *
111      * @param namespace Module namespace
112      * @param revision Module revision
113      * @return A new, potentially shared, QNameModule instance
114      * @throws NullPointerException if any argument is {@code null}
115      */
116     public static @NonNull QNameModule ofRevision(final XMLNamespace namespace, final @Nullable Revision revision) {
117         return of(namespace, revision != null ? revision : RevisionUnion.none());
118     }
119
120     /**
121      * Create a new QName module instance with specified {@link XMLNamespace} and an optional {@link Revision}.
122      *
123      * @param namespace Module namespace
124      * @param revision Module revision string
125      * @return A new, potentially shared, QNameModule instance
126      * @throws NullPointerException if {@code namespace} is {@code null}
127      */
128     public static @NonNull QNameModule ofRevision(final String namespace, final @Nullable String revision) {
129         return of(XMLNamespace.of(namespace), revision != null ? Revision.of(revision) : RevisionUnion.none());
130     }
131
132     /**
133      * Create a new QName module instance with specified namespace/revision.
134      *
135      * @param namespace Module namespace
136      * @param revision Module revision
137      * @return A new, potentially shared, QNameModule instance
138      * @throws NullPointerException if any argument is {@code null}
139      * @deprecated Use {@link #ofRevision(XMLNamespace, Revision)} instead
140      */
141     @Deprecated(since = "12.0.1", forRemoval = true)
142     public static @NonNull QNameModule create(final XMLNamespace namespace, final Optional<Revision> revision) {
143         return ofRevision(namespace, revision.orElse(null));
144     }
145
146     /**
147      * Create a new QName module instance with specified namespace and no revision.
148      *
149      * @param namespace Module namespace
150      * @return A new, potentially shared, QNameModule instance
151      * @throws NullPointerException if {@code namespace} is null
152      * @deprecated Use {@link #of(XMLNamespace)} instead
153      */
154     @Deprecated(since = "12.0.1", forRemoval = true)
155     public static @NonNull QNameModule create(final XMLNamespace namespace) {
156         return of(namespace);
157     }
158
159     /**
160      * Create a new QName module instance with specified namespace/revision.
161      *
162      * @param namespace Module namespace
163      * @param revision Module revision
164      * @return A new, potentially shared, QNameModule instance
165      * @throws NullPointerException if any argument is {@code null}
166      * @deprecated Use {@link #ofRevision(XMLNamespace, Revision)} instead
167      */
168     @Deprecated(since = "12.0.1", forRemoval = true)
169     public static @NonNull QNameModule create(final XMLNamespace namespace, final @Nullable Revision revision) {
170         return ofRevision(namespace, revision);
171     }
172
173     /**
174      * Read a QNameModule from a DataInput. The format is expected to match the output format of
175      * {@link #writeTo(DataOutput)}.
176      *
177      * @param in DataInput to read
178      * @return A QNameModule instance
179      * @throws IOException if I/O error occurs
180      */
181     public static @NonNull QNameModule readFrom(final DataInput in) throws IOException {
182         return new QNameModule(XMLNamespace.readFrom(in), RevisionUnion.readFrom(in));
183     }
184
185     /**
186      * Returns the namespace of the module which is specified as argument of YANG Module {@code namespace} keyword.
187      *
188      * @return XMLNamespace of the namespace of the module
189      */
190     public @NonNull XMLNamespace namespace() {
191         return namespace;
192     }
193
194     /**
195      * Returns the namespace of the module which is specified as argument of YANG Module {@code namespace} keyword.
196      *
197      * @return XMLNamespace of the namespace of the module
198      * @deprecated Use {@link #namespace()} instead.
199      */
200     @Deprecated(since = "12.0.1", forRemoval = true)
201     public @NonNull XMLNamespace getNamespace() {
202         return namespace();
203     }
204
205     /**
206      * Returns the revision date for the module.
207      *
208      * @return date of the module revision which is specified as argument of YANG Module {@code revision} keyword
209      */
210     public @NonNull RevisionUnion revisionUnion() {
211         return revUnion;
212     }
213
214     /**
215      * Returns the revision date for the module.
216      *
217      * @return date of the module revision which is specified as argument of YANG Module {@code revision} keyword
218      */
219     public @Nullable Revision revision() {
220         return revUnion.revision();
221     }
222
223     /**
224      * Returns the revision date for the module.
225      *
226      * @return date of the module revision which is specified as argument of YANG Module {@code revision} keyword
227      */
228     public @NonNull Optional<Revision> findRevision() {
229         return revUnion.findRevision();
230     }
231
232     /**
233      * Returns the revision date for the module.
234      *
235      * @return date of the module revision which is specified as argument of YANG Module {@code revision} keyword
236      * @deprecated Use {@link #findRevision()} or {@link #revision()} instead.
237      */
238     @Deprecated(since = "12.0.1", forRemoval = true)
239     public @NonNull Optional<Revision> getRevision() {
240         return findRevision();
241     }
242
243     /**
244      * Return an interned reference to a equivalent QNameModule.
245      *
246      * @return Interned reference, or this object if it was interned.
247      */
248     public @NonNull QNameModule intern() {
249         return INTERNER.intern(this);
250     }
251
252     @Override
253     @SuppressWarnings("checkstyle:parameterName")
254     public int compareTo(final QNameModule o) {
255         int cmp;
256         return (cmp = namespace.compareTo(o.namespace)) != 0 ? cmp : revUnion.compareTo(o.revUnion);
257     }
258
259     /**
260      * Returns a QNameModule with the same namespace, but with no revision. If this QNameModule does not have a
261      * revision, this object is returned.
262      *
263      * @return a QNameModule with the same namespace, but with no revision.
264      */
265     public @NonNull QNameModule withoutRevision() {
266         return revUnion instanceof NotRevision ? this : of(namespace);
267     }
268
269     @Override
270     public void writeTo(final DataOutput out) throws IOException {
271         out.writeUTF(namespace.toString());
272         out.writeUTF(revUnion.unionString());
273     }
274
275     @Override
276     public int hashCode() {
277         if (hash == 0) {
278             hash = Objects.hash(namespace, revUnion);
279         }
280         return hash;
281     }
282
283     @Override
284     public boolean equals(final Object obj) {
285         return this == obj || obj instanceof QNameModule other && revUnion.equals(other.revUnion)
286             && namespace.equals(other.namespace);
287     }
288
289     @Override
290     public @NonNull String toString() {
291         return MoreObjects.toStringHelper(QNameModule.class).omitNullValues()
292             .add("ns", namespace)
293             .add("rev", revUnion.revision())
294             .toString();
295     }
296
297     @java.io.Serial
298     Object writeReplace() {
299         return new NSv1(this);
300     }
301
302     @java.io.Serial
303     private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
304         Revision.throwNSE();
305     }
306
307     @java.io.Serial
308     private void readObjectNoData() throws ObjectStreamException {
309         Revision.throwNSE();
310     }
311
312     @java.io.Serial
313     private void writeObject(final ObjectOutputStream stream) throws IOException {
314         Revision.throwNSE();
315     }
316 }