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)
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 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         * <p>
192         * This method satisfies the general contract of the <code>Object.equals</code> method.
193         *
194         * @param obj the reference object with which to compare
195         *
196         * @return <code>true</code> if the given object is identical to this
197         *      QName: <code>false</code> otherwise.
198         */
199        public final boolean equals(Object obj) {
200    
201            if (obj == this) {
202                return true;
203            }
204    
205            if (!(obj instanceof QName)) {
206                return false;
207            }
208    
209            if ((namespaceURI.equals(((QName) obj).namespaceURI))
210                    && (localPart == ((QName) obj).localPart)) {
211                return true;
212            }
213    
214            return false;
215        }
216    
217        /**
218         * Returns a QName holding the value of the specified String.
219         * <p>
220         * The string must be in the form returned by the QName.toString()
221         * method, i.e. "{namespaceURI}localPart", with the "{namespaceURI}"
222         * part being optional.
223         * <p>
224         * This method doesn't do a full validation of the resulting QName.
225         * In particular, it doesn't check that the resulting namespace URI
226         * is a legal URI (per RFC 2396 and RFC 2732), nor that the resulting
227         * local part is a legal NCName per the XML Namespaces specification.
228         *
229         * @param s the string to be parsed
230         * @throws java.lang.IllegalArgumentException If the specified String cannot be parsed as a QName
231         * @return QName corresponding to the given String
232         */
233        public static QName valueOf(String s) {
234    
235            if ((s == null) || s.equals("")) {
236                throw new IllegalArgumentException("invalid QName literal");
237            }
238    
239            if (s.charAt(0) == '{') {
240                int i = s.indexOf('}');
241    
242                if (i == -1) {
243                    throw new IllegalArgumentException("invalid QName literal");
244                }
245    
246                if (i == s.length() - 1) {
247                    throw new IllegalArgumentException("invalid QName literal");
248                } else {
249                    return new QName(s.substring(1, i), s.substring(i + 1));
250                }
251            } else {
252                return new QName(s);
253            }
254        }
255    
256        /**
257         * Returns a hash code value for this QName object. The hash code
258         * is based on both the localPart and namespaceURI parts of the
259         * QName. This method satisfies the  general contract of the
260         * <code>Object.hashCode</code> method.
261         *
262         * @return a hash code value for this Qname object
263         */
264        public final int hashCode() {
265            return namespaceURI.hashCode() ^ localPart.hashCode();
266        }
267    
268        /**
269         * Ensure that deserialization properly interns the results.
270         * @param in the ObjectInputStream to be read
271         */
272        private void readObject(ObjectInputStream in) throws
273                IOException, ClassNotFoundException {
274            in.defaultReadObject();
275    
276            namespaceURI = namespaceURI.intern();
277            localPart = localPart.intern();
278            prefix = prefix.intern();
279        }
280    }