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