001    /*
002     * Copyright 2001-2004 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package javax.xml.namespace;
017    
018    import java.io.IOException;
019    import java.io.ObjectInputStream;
020    import java.io.Serializable;
021    
022    /**
023     * <code>QName</code> class represents the value of a qualified name
024     * as specified in <a href="http://www.w3.org/TR/xmlschema-2/#QName">XML
025     * Schema Part2: Datatypes specification</a>.
026     * <p>
027     * The value of a QName contains a <b>namespaceURI</b>, a <b>localPart</b> and a
028     * <b>prefix</b>. The localPart provides the local part of the qualified name.
029     * The namespaceURI is a URI reference identifying the namespace.
030     *
031     * @version 1.1
032     */
033    public class QName implements Serializable {
034    
035        /** Comment/shared empty <code>String</code>. */
036        private static final String emptyString = "".intern();
037    
038        private String namespaceURI;
039    
040        private String localPart;
041    
042        private String prefix;
043    
044        /**
045         * Constructor for the QName.
046         *
047         * @param localPart local part of the QName
048         */
049        public QName(String localPart) {
050            this(emptyString, localPart, emptyString);
051        }
052    
053        /**
054         * Constructor for the QName.
055         *
056         * @param namespaceURI namespace URI for the QName
057         * @param localPart local part of the QName.
058         */
059        public QName(String namespaceURI, String localPart) {
060            this(namespaceURI, localPart, emptyString);
061        }
062    
063        /**
064         * Constructor for the QName.
065         *
066         * @param namespaceURI Namespace URI for the QName
067         * @param localPart Local part of the QName.
068         * @param prefix Prefix of the QName.
069         */
070        public QName(String namespaceURI, String localPart, String prefix) {
071            this.namespaceURI = (namespaceURI == null)
072                    ? emptyString
073                    : namespaceURI.intern();
074            if (localPart == null) {
075                throw new IllegalArgumentException("invalid QName local part");
076            } else {
077                this.localPart = localPart.intern();
078            }
079    
080            if (prefix == null) {
081                throw new IllegalArgumentException("invalid QName prefix");
082            } else {
083                this.prefix = prefix.intern();
084            }
085        }
086    
087        /**
088         * Gets the namespace URI for this QName.
089         *
090         * @return namespace URI
091         */
092        public String getNamespaceURI() {
093            return namespaceURI;
094        }
095    
096        /**
097         * Gets the local part for this QName.
098         *
099         * @return the local part
100         */
101        public String getLocalPart() {
102            return localPart;
103        }
104    
105        /**
106         * Gets the prefix for this QName.
107         *
108         * @return the prefix
109         */
110        public String getPrefix() {
111            return prefix;
112        }
113    
114        /**
115         * Returns a string representation of this QName.
116         *
117         * @return  a string representation of the QName
118         */
119        public String toString() {
120    
121            return ((namespaceURI == emptyString)
122                    ? localPart
123                    : '{' + namespaceURI + '}' + localPart);
124        }
125    
126        /**
127         * Tests this QName for equality with another object.
128         * <p>
129         * If the given object is not a QName or is null then this method
130         * returns <tt>false</tt>.
131         * <p>
132         * For two QNames to be considered equal requires that both
133         * localPart and namespaceURI must be equal. This method uses
134         * <code>String.equals</code> to check equality of localPart
135         * and namespaceURI. Any class that extends QName is required
136         * to satisfy this equality contract.
137         * <p>
138         * This method satisfies the general contract of the <code>Object.equals</code> method.
139         *
140         * @param obj the reference object with which to compare
141         *
142         * @return <code>true</code> if the given object is identical to this
143         *      QName: <code>false</code> otherwise.
144         */
145        public final boolean equals(Object obj) {
146    
147            if (obj == this) {
148                return true;
149            }
150    
151            if (!(obj instanceof QName)) {
152                return false;
153            }
154    
155            if ((namespaceURI == ((QName) obj).namespaceURI)
156                    && (localPart == ((QName) obj).localPart)) {
157                return true;
158            }
159    
160            return false;
161        }
162    
163        /**
164         * Returns a QName holding the value of the specified String.
165         * <p>
166         * The string must be in the form returned by the QName.toString()
167         * method, i.e. "{namespaceURI}localPart", with the "{namespaceURI}"
168         * part being optional.
169         * <p>
170         * This method doesn't do a full validation of the resulting QName.
171         * In particular, it doesn't check that the resulting namespace URI
172         * is a legal URI (per RFC 2396 and RFC 2732), nor that the resulting
173         * local part is a legal NCName per the XML Namespaces specification.
174         *
175         * @param s the string to be parsed
176         * @throws java.lang.IllegalArgumentException If the specified String cannot be parsed as a QName
177         * @return QName corresponding to the given String
178         */
179        public static QName valueOf(String s) {
180    
181            if ((s == null) || s.equals("")) {
182                throw new IllegalArgumentException("invalid QName literal");
183            }
184    
185            if (s.charAt(0) == '{') {
186                int i = s.indexOf('}');
187    
188                if (i == -1) {
189                    throw new IllegalArgumentException("invalid QName literal");
190                }
191    
192                if (i == s.length() - 1) {
193                    throw new IllegalArgumentException("invalid QName literal");
194                } else {
195                    return new QName(s.substring(1, i), s.substring(i + 1));
196                }
197            } else {
198                return new QName(s);
199            }
200        }
201    
202        /**
203         * Returns a hash code value for this QName object. The hash code
204         * is based on both the localPart and namespaceURI parts of the
205         * QName. This method satisfies the  general contract of the
206         * <code>Object.hashCode</code> method.
207         *
208         * @return a hash code value for this Qname object
209         */
210        public final int hashCode() {
211            return namespaceURI.hashCode() ^ localPart.hashCode();
212        }
213    
214        /**
215         * Ensure that deserialization properly interns the results.
216         * @param in the ObjectInputStream to be read
217         * @throws IOException  if there was a failure in the object input stream
218         * @throws ClassNotFoundException   if the class of any sub-objects could
219         *              not be found
220         */
221        private void readObject(ObjectInputStream in) throws
222                IOException, ClassNotFoundException {
223            in.defaultReadObject();
224    
225            namespaceURI = namespaceURI.intern();
226            localPart = localPart.intern();
227            prefix = prefix.intern();
228        }
229    }