Merge branch 'master' of ../controller
[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 java.io.DataInput;
16 import java.io.DataOutput;
17 import java.io.IOException;
18 import java.io.Serializable;
19 import java.net.URI;
20 import java.net.URISyntaxException;
21 import java.util.Objects;
22 import java.util.Optional;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.opendaylight.yangtools.concepts.Identifier;
26 import org.opendaylight.yangtools.concepts.Immutable;
27 import org.opendaylight.yangtools.concepts.WritableObject;
28
29 public final class QNameModule implements Comparable<QNameModule>, Immutable, Serializable, Identifier, WritableObject {
30     private static final Interner<QNameModule> INTERNER = Interners.newWeakInterner();
31     private static final long serialVersionUID = 3L;
32
33     private final @NonNull URI namespace;
34     private final @Nullable Revision revision;
35
36     private transient int hash = 0;
37
38     private QNameModule(final URI namespace, final @Nullable Revision revision) {
39         this.namespace = requireNonNull(namespace);
40         this.revision = revision;
41     }
42
43     /**
44      * Return an interned reference to a equivalent QNameModule.
45      *
46      * @return Interned reference, or this object if it was interned.
47      */
48     public @NonNull QNameModule intern() {
49         return INTERNER.intern(this);
50     }
51
52     /**
53      * Create a new QName module instance with specified namespace/revision.
54      *
55      * @param namespace Module namespace
56      * @param revision Module revision
57      * @return A new, potentially shared, QNameModule instance
58      * @throws NullPointerException if any argument is null
59      */
60     public static @NonNull QNameModule create(final URI namespace, final Optional<Revision> revision) {
61         return new QNameModule(namespace, revision.orElse(null));
62     }
63
64     /**
65      * Create a new QName module instance with specified namespace and no revision.
66      *
67      * @param namespace Module namespace
68      * @return A new, potentially shared, QNameModule instance
69      * @throws NullPointerException if {@code namespace} is null
70      */
71     public static @NonNull QNameModule create(final URI namespace) {
72         return new QNameModule(namespace, null);
73     }
74
75     /**
76      * Create a new QName module instance with specified namespace/revision.
77      *
78      * @param namespace Module namespace
79      * @param revision Module revision
80      * @return A new, potentially shared, QNameModule instance
81      * @throws NullPointerException if any argument is null
82      */
83     public static @NonNull QNameModule create(final URI namespace, final @Nullable 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 @NonNull 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 YANG Module {@code namespace} keyword.
103      *
104      * @return URI format of the namespace of the module
105      */
106     public @NonNull URI getNamespace() {
107         return namespace;
108     }
109
110     /**
111      * Returns the revision date for the module.
112      *
113      * @return date of the module revision which is specified as argument of YANG Module {@code revision} keyword
114      */
115     public @NonNull Optional<Revision> getRevision() {
116         return Optional.ofNullable(revision);
117     }
118
119     @Override
120     @SuppressWarnings("checkstyle:parameterName")
121     public int compareTo(final QNameModule o) {
122         int cmp = namespace.compareTo(o.namespace);
123         if (cmp != 0) {
124             return cmp;
125         }
126         return Revision.compare(revision, o.revision);
127     }
128
129     /**
130      * Returns a QNameModule with the same namespace, but with no revision. If this QNameModule does not have
131      * a revision, this object is returned.
132      *
133      * @return a QNameModule with the same namespace, but with no revision.
134      */
135     public @NonNull QNameModule withoutRevision() {
136         return revision == null ? this : new QNameModule(namespace, null);
137     }
138
139     @Override
140     public void writeTo(final DataOutput out) throws IOException {
141         out.writeUTF(namespace.toString());
142         out.writeUTF(revision == null ? "" : revision.toString());
143     }
144
145     @Override
146     public int hashCode() {
147         if (hash == 0) {
148             hash = Objects.hash(namespace, revision);
149         }
150         return hash;
151     }
152
153     @Override
154     public boolean equals(final Object obj) {
155         if (this == obj) {
156             return true;
157         }
158         if (!(obj instanceof QNameModule)) {
159             return false;
160         }
161         final QNameModule other = (QNameModule) obj;
162         return Objects.equals(revision, other.revision) && namespace.equals(other.namespace);
163     }
164
165     /**
166      * Returns a namespace in form defined by section 5.6.4. of
167      * <a href=https://tools.ietf.org/html/rfc6020">RFC6020</a>, for example
168      * {@code http://example.acme.com/system?revision=2008-04-01}.
169      *
170      * @return Namespace in form defined by section 5.6.4. of RFC6020.
171      * @throws URISyntaxException on incorrect namespace definition
172      *
173      */
174     @NonNull URI getRevisionNamespace() throws URISyntaxException {
175         final String query = revision == null ? "" : "revision=" + revision.toString();
176         return new URI(namespace.getScheme(), namespace.getUserInfo(), namespace.getHost(), namespace.getPort(),
177             namespace.getPath(), query, namespace.getFragment());
178     }
179
180     @Override
181     public @NonNull String toString() {
182         return MoreObjects.toStringHelper(QNameModule.class).omitNullValues().add("ns", namespace)
183             .add("rev", revision).toString();
184     }
185
186     Object writeReplace() {
187         return new NSv1(this);
188     }
189 }