Added support for parsing submodules & added dependency utility parser
[yangtools.git] / yang / yang-common / src / main / java / org / opendaylight / yangtools / yang / common / QName.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 java.io.Serializable;
11 import java.net.URI;
12 import java.net.URISyntaxException;
13 import java.text.ParseException;
14 import java.text.SimpleDateFormat;
15 import java.util.Date;
16 import java.util.Objects;
17
18 import org.opendaylight.yangtools.concepts.Immutable;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21 import org.opendaylight.yangtools.yang.common.QName;
22
23 /**
24  * The QName from XML consists of local name of element and XML namespace, but
25  * for our use, we added module revision to it.
26  *
27  * In YANG context QName is full name of defined node, type, procedure or
28  * notification. QName consists of XML namespace, YANG model revision and local
29  * name of defined type. It is used to prevent name clashes between nodes with
30  * same local name, but from different schemas.
31  *
32  * <ul>
33  * <li><b>XMLNamespace</b> - the namespace assigned to the YANG module which
34  * defined element, type, procedure or notification.</li>
35  * <li><b>Revision</b> - the revision of the YANG module which describes the
36  * element</li>
37  * <li><b>LocalName</b> - the YANG schema identifier which were defined for this
38  * node in the YANG module</li>
39  * </ul>
40  *
41  *
42  */
43 public final class QName implements Immutable,Serializable {
44
45     private static final long serialVersionUID = 5398411242927766414L;
46
47     protected static final Logger LOGGER = LoggerFactory.getLogger(QName.class);
48
49     private static final ThreadLocal<SimpleDateFormat> REVISION_FORMAT = new ThreadLocal<SimpleDateFormat>() {
50
51         protected SimpleDateFormat initialValue() {
52             return new SimpleDateFormat("yyyy-MM-dd");
53         };
54
55         public void set(SimpleDateFormat value) {
56             throw new UnsupportedOperationException();
57         };
58
59     };
60
61     private final URI namespace;
62     private final String localName;
63     private final String prefix;
64     private final String formattedRevision;
65     private final Date revision;
66
67     /**
68      * QName Constructor.
69      *
70      * @param namespace
71      *            the namespace assigned to the YANG module
72      * @param revision
73      *            the revision of the YANG module
74      * @param prefix
75      *            locally defined prefix assigned to local name
76      * @param localName
77      *            YANG schema identifier
78      */
79     public QName(URI namespace, Date revision, String prefix, String localName) {
80         this.namespace = namespace;
81         this.localName = localName;
82         this.revision = revision;
83         this.prefix = prefix;
84         if(revision != null) {
85             this.formattedRevision = REVISION_FORMAT.get().format(revision);
86         } else {
87             this.formattedRevision = null;
88         }
89     }
90
91     /**
92      * QName Constructor.
93      *
94      * @param namespace
95      *            the namespace assigned to the YANG module
96      * @param localName
97      *            YANG schema identifier
98      */
99     public QName(URI namespace, String localName) {
100         this(namespace, null, "", localName);
101     }
102
103     /**
104      * QName Constructor.
105      *
106      * @param namespace
107      *            the namespace assigned to the YANG module
108      * @param revision
109      *            the revision of the YANG module
110      * @param localName
111      *            YANG schema identifier
112      */
113     public QName(URI namespace, Date revision, String localName) {
114         this(namespace, revision, null, localName);
115     }
116
117     public QName(QName base, String localName) {
118         this(base.getNamespace(), base.getRevision(), base.getPrefix(), localName);
119     }
120
121     // TODO: rework with pattern
122     public QName(String base) throws ParseException {
123         Date revision = null;
124         String nsAndRev = base.substring(base.indexOf("(") + 1, base.indexOf(")"));
125         if (nsAndRev.contains("?")) {
126             String[] splitted = nsAndRev.split("\\?");
127             this.namespace = URI.create(splitted[0]);
128             revision = REVISION_FORMAT.get().parse(splitted[1]);
129         } else {
130             this.namespace = URI.create(nsAndRev);
131         }
132
133         this.localName = base.substring(base.indexOf(")") + 1);
134         this.revision = revision;
135         this.prefix = null;
136         if (revision != null) {
137             this.formattedRevision = REVISION_FORMAT.get().format(revision);
138         } else {
139             this.formattedRevision = null;
140         }
141     }
142
143     /**
144      * Returns XMLNamespace assigned to the YANG module.
145      *
146      * @return XMLNamespace assigned to the YANG module.
147      */
148     public URI getNamespace() {
149         return namespace;
150     }
151
152     /**
153      * Returns YANG schema identifier which were defined for this node in the
154      * YANG module
155      *
156      * @return YANG schema identifier which were defined for this node in the
157      *         YANG module
158      */
159     public String getLocalName() {
160         return localName;
161     }
162
163     /**
164      * Returns revision of the YANG module if the module has defined revision,
165      * otherwise returns <code>null</code>
166      *
167      * @return revision of the YANG module if the module has defined revision,
168      *         otherwise returns <code>null</code>
169      */
170     public Date getRevision() {
171         return revision;
172     }
173
174     /**
175      * Returns locally defined prefix assigned to local name
176      *
177      * @return locally defined prefix assigned to local name
178      */
179     public String getPrefix() {
180         return prefix;
181     }
182
183     @Override
184     public int hashCode() {
185         final int prime = 31;
186         int result = 1;
187         result = prime * result + ((localName == null) ? 0 : localName.hashCode());
188         result = prime * result + ((namespace == null) ? 0 : namespace.hashCode());
189         result = prime * result + ((formattedRevision == null) ? 0 : formattedRevision.hashCode());
190         return result;
191     }
192
193     @Override
194     public boolean equals(Object obj) {
195         if (this == obj) {
196             return true;
197         }
198         if (obj == null) {
199             return false;
200         }
201         if (getClass() != obj.getClass()) {
202             return false;
203         }
204         QName other = (QName) obj;
205         if (localName == null) {
206             if (other.localName != null) {
207                 return false;
208             }
209         } else if (!localName.equals(other.localName)) {
210             return false;
211         }
212         if (namespace == null) {
213             if (other.namespace != null) {
214                 return false;
215             }
216         } else if (!namespace.equals(other.namespace)) {
217             return false;
218         }
219         if (formattedRevision == null) {
220             if (other.formattedRevision != null) {
221                 return false;
222             }
223         } else if (!revision.equals(other.revision)) {
224             return false;
225         }
226         return true;
227     }
228
229
230     public static QName create(QName base, String localName){
231         return new QName(base, localName);
232     }
233
234     public static QName create(URI namespace, Date revision, String localName){
235         return new QName(namespace, revision, localName);
236     }
237
238
239     public static QName create(String namespace, String revision, String localName) throws IllegalArgumentException{
240         try {
241             URI namespaceUri = new URI(namespace);
242             Date revisionDate = parseRevision(revision);
243             return create(namespaceUri, revisionDate, localName);
244         }  catch (URISyntaxException ue) {
245             throw new IllegalArgumentException("Namespace is is not valid URI", ue);
246         }
247     }
248
249     @Override
250     public String toString() {
251         StringBuilder sb = new StringBuilder();
252         if (namespace != null) {
253             sb.append("(" + namespace);
254
255             if (revision != null) {
256                 sb.append("?revision=" + REVISION_FORMAT.get().format(revision));
257             }
258             sb.append(")");
259         }
260         sb.append(localName);
261         return sb.toString();
262     }
263
264     /**
265      * Returns a namespace in form defined by section 5.6.4. of {@link https
266      * ://tools.ietf.org/html/rfc6020}, if namespace is not correctly defined,
267      * the method will return <code>null</code> <br>
268      * example "http://example.acme.com/system?revision=2008-04-01"
269      *
270      * @return namespace in form defined by section 5.6.4. of {@link https
271      *         ://tools.ietf.org/html/rfc6020}, if namespace is not correctly
272      *         defined, the method will return <code>null</code>
273      *
274      */
275     URI getRevisionNamespace() {
276
277         if (namespace == null) {
278             return null;
279         }
280
281         String query = "";
282         if (revision != null) {
283             query = "revision=" + formattedRevision;
284         }
285
286         URI compositeURI = null;
287         try {
288             compositeURI = new URI(namespace.getScheme(), namespace.getUserInfo(), namespace.getHost(),
289                     namespace.getPort(), namespace.getPath(), query, namespace.getFragment());
290         } catch (URISyntaxException e) {
291             LOGGER.error("", e);
292         }
293         return compositeURI;
294     }
295
296     public String getFormattedRevision() {
297         return formattedRevision;
298     }
299
300     public QName withoutRevision() {
301         return QName.create(namespace, null, localName);
302     }
303
304     public static Date parseRevision(String formatedDate) {
305         try {
306             return REVISION_FORMAT.get().parse(formatedDate);
307         } catch (ParseException e) {
308             throw new IllegalArgumentException("Revision is not in supported format",e);
309         }
310     }
311
312     public static String formattedRevision(Date revision) {
313         if(revision == null) {
314             return null;
315         }
316         return REVISION_FORMAT.get().format(revision);
317     }
318
319     public boolean isEqualWithoutRevision(QName other) {
320         return localName.equals(other.getLocalName()) && Objects.equals(namespace, other.getNamespace());
321     }
322 }