001: /*
002: * The Apache Software License, Version 1.1
003: *
004: *
005: * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
006: * reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * 1. Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright
016: * notice, this list of conditions and the following disclaimer in
017: * the documentation and/or other materials provided with the
018: * distribution.
019: *
020: * 3. The end-user documentation included with the redistribution,
021: * if any, must include the following acknowledgment:
022: * "This product includes software developed by the
023: * Apache Software Foundation ( http://www.apache.org/ )."
024: * Alternately, this acknowledgment may appear in the software itself,
025: * if and wherever such third-party acknowledgments normally appear.
026: *
027: * 4. The names "Axis" and "Apache Software Foundation" must
028: * not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact apache@apache.org .
031: *
032: * 5. Products derived from this software may not be called "Apache",
033: * nor may "Apache" appear in their name, without prior written
034: * permission of the Apache Software Foundation.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This software consists of voluntary contributions made by many
051: * individuals on behalf of the Apache Software Foundation. For more
052: * information on the Apache Software Foundation, please see
053: * < http://www.apache.org/ >.
054: */
055: package groovy.xml;
056:
057: import java.io.IOException;
058: import java.io.ObjectInputStream;
059: import java.io.Serializable;
060:
061: /**
062: * <code>QName</code> class represents the value of a qualified name
063: * as specified in <a href=" http://www.w3.org/TR/xmlschema-2/#QName ">XML
064: * Schema Part2: Datatypes specification</a>.
065: * <p>
066: * The value of a QName contains a <b>namespaceURI</b>, a <b>localPart</b> and a <b>prefix</b>.
067: * The localPart provides the local part of the qualified name. The
068: * namespaceURI is a URI reference identifying the namespace.
069: *
070: * @version 1.1
071: */
072: public class QName implements Serializable {
073:
074: /** comment/shared empty string */
075: private static final String emptyString = "".intern();
076:
077: /** Field namespaceURI */
078: private String namespaceURI;
079:
080: /** Field localPart */
081: private String localPart;
082:
083: /** Field prefix */
084: private String prefix;
085:
086: /**
087: * Constructor for the QName.
088: *
089: * @param localPart Local part of the QName
090: */
091: public QName(String localPart) {
092: this (emptyString, localPart, emptyString);
093: }
094:
095: /**
096: * Constructor for the QName.
097: *
098: * @param namespaceURI Namespace URI for the QName
099: * @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) ? emptyString
114: : namespaceURI.intern();
115: if (localPart == null) {
116: throw new IllegalArgumentException(
117: "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)) ? localPart : prefix + ':'
164: + localPart);
165: }
166:
167: /**
168: * Returns a string representation of this QName
169: *
170: * @return a string representation of the QName
171: */
172: public String toString() {
173:
174: return ((namespaceURI.equals(emptyString)) ? localPart : '{'
175: + namespaceURI + '}' + localPart);
176: }
177:
178: /**
179: * Tests this QName for equality with another object.
180: * <p>
181: * If the given object is not a QName or String equivalent or is null then this method
182: * returns <tt>false</tt>.
183: * <p>
184: * For two QNames to be considered equal requires that both
185: * localPart and namespaceURI must be equal. This method uses
186: * <code>String.equals</code> to check equality of localPart
187: * and namespaceURI. Any class that extends QName is required
188: * to satisfy this equality contract.
189: *
190: * If the supplied object is a String, then it is split in two on the last colon
191: * and the first half is compared against the prefix || namespaceURI
192: * and the second half is compared against the localPart
193: *
194: * i.e. assert new QName("namespace","localPart").equals("namespace:localPart")
195: *
196: * Intended Usage: for gpath accessors, e.g. root.'urn:mynamespace:node'
197: *
198: * Warning: this equivalence is not commutative,
199: * i.e. qname.equals(string) may be true/false but string.equals(qname) is always false
200: *
201: * <p>
202: * This method satisfies the general contract of the <code>Object.equals</code> method.
203: *
204: * @param o the reference object with which to compare
205: *
206: * @return <code>true</code> if the given object is identical to this
207: * QName: <code>false</code> otherwise.
208: */
209: public boolean equals(Object o) {
210: if (this == o)
211: return true;
212: if (o == null)
213: return false;
214: if (o instanceof QName) {
215: final QName qName = (QName) o;
216: if (!namespaceURI.equals(qName.namespaceURI))
217: return false;
218: return localPart.equals(qName.localPart);
219:
220: } else if (o instanceof String) {
221: final String string = (String) o;
222: if (string.length() == 0)
223: return false;
224: int lastColonIndex = string.lastIndexOf(":");
225: if (lastColonIndex < 0
226: || lastColonIndex == string.length() - 1)
227: return false;
228: final String stringPrefix = string.substring(0,
229: lastColonIndex);
230: final String stringLocalPart = string
231: .substring(lastColonIndex + 1);
232: if (stringPrefix.equals(prefix)
233: || stringPrefix.equals(namespaceURI)) {
234: return localPart.equals(stringLocalPart);
235: }
236: return false;
237: }
238: return false;
239: }
240:
241: /**
242: * Returns a QName holding the value of the specified String.
243: * <p>
244: * The string must be in the form returned by the QName.toString()
245: * method, i.e. "{namespaceURI}localPart", with the "{namespaceURI}"
246: * part being optional.
247: * <p>
248: * This method doesn't do a full validation of the resulting QName.
249: * In particular, it doesn't check that the resulting namespace URI
250: * is a legal URI (per RFC 2396 and RFC 2732), nor that the resulting
251: * local part is a legal NCName per the XML Namespaces specification.
252: *
253: * @param s the string to be parsed
254: * @throws java.lang.IllegalArgumentException If the specified String cannot be parsed as a QName
255: * @return QName corresponding to the given String
256: */
257: public static QName valueOf(String s) {
258:
259: if ((s == null) || s.equals("")) {
260: throw new IllegalArgumentException("invalid QName literal");
261: }
262:
263: if (s.charAt(0) == '{') {
264: int i = s.indexOf('}');
265:
266: if (i == -1) {
267: throw new IllegalArgumentException(
268: "invalid QName literal");
269: }
270:
271: if (i == s.length() - 1) {
272: throw new IllegalArgumentException(
273: "invalid QName literal");
274: } else {
275: return new QName(s.substring(1, i), s.substring(i + 1));
276: }
277: } else {
278: return new QName(s);
279: }
280: }
281:
282: /**
283: * Returns a hash code value for this QName object. The hash code
284: * is based on both the localPart and namespaceURI parts of the
285: * QName. This method satisfies the general contract of the
286: * <code>Object.hashCode</code> method.
287: *
288: * @return a hash code value for this Qname object
289: */
290: public int hashCode() {
291: int result;
292: result = namespaceURI.hashCode();
293: result = 29 * result + localPart.hashCode();
294: return result;
295: }
296:
297: /**
298: * Ensure that deserialization properly interns the results.
299: * @param in the ObjectInputStream to be read
300: */
301: private void readObject(ObjectInputStream in) throws IOException,
302: ClassNotFoundException {
303: in.defaultReadObject();
304:
305: namespaceURI = namespaceURI.intern();
306: localPart = localPart.intern();
307: prefix = prefix.intern();
308: }
309: }
|