8b3db1cf824eaac4d3249768b47cd6d18f05f1c4
[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 javax.annotation.Nullable;
24 import org.eclipse.jdt.annotation.NonNull;
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
35     //Nullable
36     private final Revision revision;
37
38     private transient int hash = 0;
39
40     private QNameModule(final @NonNull URI namespace, final 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.orElse(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     public static 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      */
82     public static QNameModule create(final URI namespace, @Nullable final Revision revision) {
83         return new QNameModule(namespace, revision);
84     }
85
86     /**
87      * Read a QNameModule from a DataInput. The format is expected to match the output format
88      * of {@link #writeTo(DataOutput)}.
89      *
90      * @param in DataInput to read
91      * @return A QNameModule instance
92      * @throws IOException if I/O error occurs
93      */
94     public static QNameModule readFrom(final DataInput in) throws IOException {
95         final String namespace = in.readUTF();
96         final String revision = in.readUTF();
97         return new QNameModule(URI.create(namespace), revision.isEmpty() ? null : Revision.of(revision));
98     }
99
100     /**
101      * Returns the namespace of the module which is specified as argument of
102      * YANG Module <b><font color="#00FF00">namespace</font></b> keyword.
103      *
104      * @return URI format of the namespace of the module
105      */
106     public 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
114      *         YANG Module <b><font color="#339900">revison</font></b> keyword
115      */
116     public Optional<Revision> getRevision() {
117         return Optional.ofNullable(revision);
118     }
119
120     @Override
121     @SuppressWarnings("checkstyle:parameterName")
122     public int compareTo(final QNameModule o) {
123         int cmp = namespace.compareTo(o.namespace);
124         if (cmp != 0) {
125             return cmp;
126         }
127         return Revision.compare(revision, o.revision);
128     }
129
130     /**
131      * Returns a QNameModule with the same namespace, but with no revision. If this QNameModule does not have
132      * a revision, this object is returned.
133      *
134      * @return a QNameModule with the same namespace, but with no revision.
135      */
136     public QNameModule withoutRevision() {
137         return revision == null ? this : new QNameModule(namespace, null);
138     }
139
140     @Override
141     public void writeTo(final DataOutput out) throws IOException {
142         out.writeUTF(namespace.toString());
143         out.writeUTF(revision == null ? "" : revision.toString());
144     }
145
146     @Override
147     public int hashCode() {
148         if (hash == 0) {
149             hash = Objects.hash(namespace, revision);
150         }
151         return hash;
152     }
153
154     @Override
155     public boolean equals(final Object obj) {
156         if (this == obj) {
157             return true;
158         }
159         if (!(obj instanceof QNameModule)) {
160             return false;
161         }
162         final QNameModule other = (QNameModule) obj;
163         return Objects.equals(revision, other.revision) && namespace.equals(other.namespace);
164     }
165
166     /**
167      * Returns a namespace in form defined by section 5.6.4. of
168      * <a href=https://tools.ietf.org/html/rfc6020">RFC6020</a>, for example
169      * {@code http://example.acme.com/system?revision=2008-04-01}.
170      *
171      * @return Namespace in form defined by section 5.6.4. of RFC6020.
172      * @throws URISyntaxException on incorrect namespace definition
173      *
174      */
175     URI getRevisionNamespace() throws URISyntaxException {
176         final String query = revision == null ? "" : "revision=" + revision.toString();
177         return new URI(namespace.getScheme(), namespace.getUserInfo(), namespace.getHost(), namespace.getPort(),
178             namespace.getPath(), query, namespace.getFragment());
179     }
180
181     @Override
182     public String toString() {
183         return MoreObjects.toStringHelper(QNameModule.class).omitNullValues().add("ns", namespace)
184             .add("rev", revision).toString();
185     }
186 }