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 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 }