1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 package groovy.xml;
56
57 import java.io.IOException;
58 import java.io.ObjectInputStream;
59 import java.io.Serializable;
60
61 /***
62 * <code>QName</code> class represents the value of a qualified name
63 * as specified in <a href=" http://www.w3.org/TR/xmlschema-2/#QName ">XML
64 * Schema Part2: Datatypes specification</a>.
65 * <p>
66 * The value of a QName contains a <b>namespaceURI</b>, a <b>localPart</b> and a <b>prefix</b>.
67 * The localPart provides the local part of the qualified name. The
68 * namespaceURI is a URI reference identifying the namespace.
69 *
70 * @version 1.1
71 */
72 public class QName implements Serializable {
73
74 /*** comment/shared empty string */
75 private static final String emptyString = "".intern();
76
77 /*** Field namespaceURI */
78 private String namespaceURI;
79
80 /*** Field localPart */
81 private String localPart;
82
83 /*** Field prefix */
84 private String prefix;
85
86 /***
87 * Constructor for the QName.
88 *
89 * @param localPart Local part of the QName
90 */
91 public QName(String localPart) {
92 this(emptyString, localPart, emptyString);
93 }
94
95 /***
96 * Constructor for the QName.
97 *
98 * @param namespaceURI Namespace URI for the QName
99 * @param localPart Local part of the QName.
100 */
101 public QName(String namespaceURI, String localPart) {
102 this(namespaceURI, localPart, emptyString);
103 }
104
105 /***
106 * Constructor for the QName.
107 *
108 * @param namespaceURI Namespace URI for the QName
109 * @param localPart Local part of the QName.
110 * @param prefix Prefix of the QName.
111 */
112 public QName(String namespaceURI, String localPart, String prefix) {
113 this.namespaceURI = (namespaceURI == null)
114 ? emptyString
115 : namespaceURI.intern();
116 if (localPart == null) {
117 throw new IllegalArgumentException("invalid QName local part");
118 } else {
119 this.localPart = localPart.intern();
120 }
121
122 if (prefix == null) {
123 throw new IllegalArgumentException("invalid QName prefix");
124 } else {
125 this.prefix = prefix.intern();
126 }
127 }
128
129 /***
130 * Gets the Namespace URI for this QName
131 *
132 * @return Namespace URI
133 */
134 public String getNamespaceURI() {
135 return namespaceURI;
136 }
137
138 /***
139 * Gets the Local part for this QName
140 *
141 * @return Local part
142 */
143 public String getLocalPart() {
144 return localPart;
145 }
146
147 /***
148 * Gets the Prefix for this QName
149 *
150 * @return Prefix
151 */
152 public String getPrefix() {
153 return prefix;
154 }
155
156 /***
157 * Returns the fully qualified name of this QName
158 *
159 * @return a string representation of the QName
160 */
161 public String getQualifiedName() {
162
163 return ((prefix.equals(emptyString))
164 ? localPart
165 : prefix + ':' + localPart);
166 }
167
168 /***
169 * Returns a string representation of this QName
170 *
171 * @return a string representation of the QName
172 */
173 public String toString() {
174
175 return ((namespaceURI.equals(emptyString))
176 ? localPart
177 : '{' + namespaceURI + '}' + localPart);
178 }
179
180 /***
181 * Tests this QName for equality with another object.
182 * <p>
183 * If the given object is not a QName or String equivalent or is null then this method
184 * returns <tt>false</tt>.
185 * <p>
186 * For two QNames to be considered equal requires that both
187 * localPart and namespaceURI must be equal. This method uses
188 * <code>String.equals</code> to check equality of localPart
189 * and namespaceURI. Any class that extends QName is required
190 * to satisfy this equality contract.
191 *
192 * If the supplied object is a String, then it is split in two on the last colon
193 * and the first half is compared against the prefix || namespaceURI
194 * and the second half is compared against the localPart
195 *
196 * i.e. assert new QName("namespace","localPart").equals("namespace:localPart")
197 *
198 * Intended Usage: for gpath accessors, e.g. root.'urn:mynamespace:node'
199 *
200 * Warning: this equivalence is not commutative,
201 * i.e. qname.equals(string) may be true/false but string.equals(qname) is always false
202 *
203 * <p>
204 * This method satisfies the general contract of the <code>Object.equals</code> method.
205 *
206 * @param o the reference object with which to compare
207 *
208 * @return <code>true</code> if the given object is identical to this
209 * QName: <code>false</code> otherwise.
210 */
211 public boolean equals(Object o) {
212 if (this == o) return true;
213 if (o == null) return false;
214 if (o instanceof QName) {
215 final QName qName = (QName) o;
216 if (!namespaceURI.equals(qName.namespaceURI)) return false;
217 return localPart.equals(qName.localPart);
218
219 } else if (o instanceof String) {
220 final String string = (String)o;
221 if (string.length() == 0) return false;
222 int lastColonIndex = string.lastIndexOf(":");
223 if (lastColonIndex < 0 || lastColonIndex == string.length() - 1) return false;
224 final String stringPrefix = string.substring(0,lastColonIndex);
225 final String stringLocalPart = string.substring(lastColonIndex + 1);
226 if (stringPrefix.equals(prefix) || stringPrefix.equals(namespaceURI)) {
227 return localPart.equals(stringLocalPart);
228 }
229 return false;
230 }
231 return false;
232 }
233
234 /***
235 * Returns a QName holding the value of the specified String.
236 * <p>
237 * The string must be in the form returned by the QName.toString()
238 * method, i.e. "{namespaceURI}localPart", with the "{namespaceURI}"
239 * part being optional.
240 * <p>
241 * This method doesn't do a full validation of the resulting QName.
242 * In particular, it doesn't check that the resulting namespace URI
243 * is a legal URI (per RFC 2396 and RFC 2732), nor that the resulting
244 * local part is a legal NCName per the XML Namespaces specification.
245 *
246 * @param s the string to be parsed
247 * @throws java.lang.IllegalArgumentException If the specified String cannot be parsed as a QName
248 * @return QName corresponding to the given String
249 */
250 public static QName valueOf(String s) {
251
252 if ((s == null) || s.equals("")) {
253 throw new IllegalArgumentException("invalid QName literal");
254 }
255
256 if (s.charAt(0) == '{') {
257 int i = s.indexOf('}');
258
259 if (i == -1) {
260 throw new IllegalArgumentException("invalid QName literal");
261 }
262
263 if (i == s.length() - 1) {
264 throw new IllegalArgumentException("invalid QName literal");
265 } else {
266 return new QName(s.substring(1, i), s.substring(i + 1));
267 }
268 } else {
269 return new QName(s);
270 }
271 }
272
273 /***
274 * Returns a hash code value for this QName object. The hash code
275 * is based on both the localPart and namespaceURI parts of the
276 * QName. This method satisfies the general contract of the
277 * <code>Object.hashCode</code> method.
278 *
279 * @return a hash code value for this Qname object
280 */
281 public int hashCode() {
282 int result;
283 result = namespaceURI.hashCode();
284 result = 29 * result + localPart.hashCode();
285 return result;
286 }
287
288 /***
289 * Ensure that deserialization properly interns the results.
290 * @param in the ObjectInputStream to be read
291 */
292 private void readObject(ObjectInputStream in) throws
293 IOException, ClassNotFoundException {
294 in.defaultReadObject();
295
296 namespaceURI = namespaceURI.intern();
297 localPart = localPart.intern();
298 prefix = prefix.intern();
299 }
300 }