1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 1999-2004 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 /* 21 * $Id: QName.java,v 1.2.4.1 2005/09/15 08:15:52 suresh_emailid Exp $ 22 */ 23 package com.sun.org.apache.xml.internal.utils; 24 25 import java.util.Stack; 26 import java.util.StringTokenizer; 27 28 import com.sun.org.apache.xml.internal.res.XMLErrorResources; 29 import com.sun.org.apache.xml.internal.res.XMLMessages; 30 31 import org.w3c.dom.Element; 32 33 /** 34 * Class to represent a qualified name: "The name of an internal XSLT object, 35 * specifically a named template (see [7 Named Templates]), a mode (see [6.7 Modes]), 36 * an attribute set (see [8.1.4 Named Attribute Sets]), a key (see [14.2 Keys]), 37 * a locale (see [14.3 Number Formatting]), a variable or a parameter (see 38 * [12 Variables and Parameters]) is specified as a QName. If it has a prefix, 39 * then the prefix is expanded into a URI reference using the namespace declarations 40 * in effect on the attribute in which the name occurs. The expanded name 41 * consisting of the local part of the name and the possibly null URI reference 42 * is used as the name of the object. The default namespace is not used for 43 * unprefixed names." 44 * @xsl.usage general 45 */ 46 public class QName implements java.io.Serializable 47 { 48 static final long serialVersionUID = 467434581652829920L; 49 50 /** 51 * The local name. 52 * @serial 53 */ 54 protected String _localName; 55 56 /** 57 * The namespace URI. 58 * @serial 59 */ 60 protected String _namespaceURI; 61 62 /** 63 * The namespace prefix. 64 * @serial 65 */ 66 protected String _prefix; 67 68 /** 69 * The XML namespace. 70 */ 71 public static final String S_XMLNAMESPACEURI = 72 "http://www.w3.org/XML/1998/namespace"; 73 74 /** 75 * The cached hashcode, which is calculated at construction time. 76 * @serial 77 */ 78 private int m_hashCode; 79 80 /** 81 * Constructs an empty QName. 82 * 20001019: Try making this public, to support Serializable? -- JKESS 83 */ QName()84 public QName(){} 85 86 /** 87 * Constructs a new QName with the specified namespace URI and 88 * local name. 89 * 90 * @param namespaceURI The namespace URI if known, or null 91 * @param localName The local name 92 */ QName(String namespaceURI, String localName)93 public QName(String namespaceURI, String localName) 94 { 95 this(namespaceURI, localName, false); 96 } 97 98 /** 99 * Constructs a new QName with the specified namespace URI and 100 * local name. 101 * 102 * @param namespaceURI The namespace URI if known, or null 103 * @param localName The local name 104 * @param validate If true the new QName will be validated and an IllegalArgumentException will 105 * be thrown if it is invalid. 106 */ QName(String namespaceURI, String localName, boolean validate)107 public QName(String namespaceURI, String localName, boolean validate) 108 { 109 110 // This check was already here. So, for now, I will not add it to the validation 111 // that is done when the validate parameter is true. 112 if (localName == null) 113 throw new IllegalArgumentException(XMLMessages.createXMLMessage( 114 XMLErrorResources.ER_ARG_LOCALNAME_NULL, null)); //"Argument 'localName' is null"); 115 116 if (validate) 117 { 118 if (!XML11Char.isXML11ValidNCName(localName)) 119 { 120 throw new IllegalArgumentException(XMLMessages.createXMLMessage( 121 XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); 122 } 123 } 124 125 _namespaceURI = namespaceURI; 126 _localName = localName; 127 m_hashCode = toString().hashCode(); 128 } 129 130 /** 131 * Constructs a new QName with the specified namespace URI, prefix 132 * and local name. 133 * 134 * @param namespaceURI The namespace URI if known, or null 135 * @param prefix The namespace prefix is known, or null 136 * @param localName The local name 137 * 138 */ QName(String namespaceURI, String prefix, String localName)139 public QName(String namespaceURI, String prefix, String localName) 140 { 141 this(namespaceURI, prefix, localName, false); 142 } 143 144 /** 145 * Constructs a new QName with the specified namespace URI, prefix 146 * and local name. 147 * 148 * @param namespaceURI The namespace URI if known, or null 149 * @param prefix The namespace prefix is known, or null 150 * @param localName The local name 151 * @param validate If true the new QName will be validated and an IllegalArgumentException will 152 * be thrown if it is invalid. 153 */ QName(String namespaceURI, String prefix, String localName, boolean validate)154 public QName(String namespaceURI, String prefix, String localName, boolean validate) 155 { 156 157 // This check was already here. So, for now, I will not add it to the validation 158 // that is done when the validate parameter is true. 159 if (localName == null) 160 throw new IllegalArgumentException(XMLMessages.createXMLMessage( 161 XMLErrorResources.ER_ARG_LOCALNAME_NULL, null)); //"Argument 'localName' is null"); 162 163 if (validate) 164 { 165 if (!XML11Char.isXML11ValidNCName(localName)) 166 { 167 throw new IllegalArgumentException(XMLMessages.createXMLMessage( 168 XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); 169 } 170 171 if ((null != prefix) && (!XML11Char.isXML11ValidNCName(prefix))) 172 { 173 throw new IllegalArgumentException(XMLMessages.createXMLMessage( 174 XMLErrorResources.ER_ARG_PREFIX_INVALID,null )); //"Argument 'prefix' not a valid NCName"); 175 } 176 177 } 178 _namespaceURI = namespaceURI; 179 _prefix = prefix; 180 _localName = localName; 181 m_hashCode = toString().hashCode(); 182 } 183 184 /** 185 * Construct a QName from a string, without namespace resolution. Good 186 * for a few odd cases. 187 * 188 * @param localName Local part of qualified name 189 * 190 */ QName(String localName)191 public QName(String localName) 192 { 193 this(localName, false); 194 } 195 196 /** 197 * Construct a QName from a string, without namespace resolution. Good 198 * for a few odd cases. 199 * 200 * @param localName Local part of qualified name 201 * @param validate If true the new QName will be validated and an IllegalArgumentException will 202 * be thrown if it is invalid. 203 */ QName(String localName, boolean validate)204 public QName(String localName, boolean validate) 205 { 206 207 // This check was already here. So, for now, I will not add it to the validation 208 // that is done when the validate parameter is true. 209 if (localName == null) 210 throw new IllegalArgumentException(XMLMessages.createXMLMessage( 211 XMLErrorResources.ER_ARG_LOCALNAME_NULL, null)); //"Argument 'localName' is null"); 212 213 if (validate) 214 { 215 if (!XML11Char.isXML11ValidNCName(localName)) 216 { 217 throw new IllegalArgumentException(XMLMessages.createXMLMessage( 218 XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); 219 } 220 } 221 _namespaceURI = null; 222 _localName = localName; 223 m_hashCode = toString().hashCode(); 224 } 225 226 /** 227 * Construct a QName from a string, resolving the prefix 228 * using the given namespace stack. The default namespace is 229 * not resolved. 230 * 231 * @param qname Qualified name to resolve 232 * @param namespaces Namespace stack to use to resolve namespace 233 */ QName(String qname, Stack namespaces)234 public QName(String qname, Stack namespaces) 235 { 236 this(qname, namespaces, false); 237 } 238 239 /** 240 * Construct a QName from a string, resolving the prefix 241 * using the given namespace stack. The default namespace is 242 * not resolved. 243 * 244 * @param qname Qualified name to resolve 245 * @param namespaces Namespace stack to use to resolve namespace 246 * @param validate If true the new QName will be validated and an IllegalArgumentException will 247 * be thrown if it is invalid. 248 */ QName(String qname, Stack namespaces, boolean validate)249 public QName(String qname, Stack namespaces, boolean validate) 250 { 251 252 String namespace = null; 253 String prefix = null; 254 int indexOfNSSep = qname.indexOf(':'); 255 256 if (indexOfNSSep > 0) 257 { 258 prefix = qname.substring(0, indexOfNSSep); 259 260 if (prefix.equals("xml")) 261 { 262 namespace = S_XMLNAMESPACEURI; 263 } 264 // Do we want this? 265 else if (prefix.equals("xmlns")) 266 { 267 return; 268 } 269 else 270 { 271 int depth = namespaces.size(); 272 273 for (int i = depth - 1; i >= 0; i--) 274 { 275 NameSpace ns = (NameSpace) namespaces.elementAt(i); 276 277 while (null != ns) 278 { 279 if ((null != ns.m_prefix) && prefix.equals(ns.m_prefix)) 280 { 281 namespace = ns.m_uri; 282 i = -1; 283 284 break; 285 } 286 287 ns = ns.m_next; 288 } 289 } 290 } 291 292 if (null == namespace) 293 { 294 throw new RuntimeException( 295 XMLMessages.createXMLMessage( 296 XMLErrorResources.ER_PREFIX_MUST_RESOLVE, 297 new Object[]{ prefix })); //"Prefix must resolve to a namespace: "+prefix); 298 } 299 } 300 301 _localName = (indexOfNSSep < 0) 302 ? qname : qname.substring(indexOfNSSep + 1); 303 304 if (validate) 305 { 306 if ((_localName == null) || (!XML11Char.isXML11ValidNCName(_localName))) 307 { 308 throw new IllegalArgumentException(XMLMessages.createXMLMessage( 309 XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); 310 } 311 } 312 _namespaceURI = namespace; 313 _prefix = prefix; 314 m_hashCode = toString().hashCode(); 315 } 316 317 /** 318 * Construct a QName from a string, resolving the prefix 319 * using the given namespace context and prefix resolver. 320 * The default namespace is not resolved. 321 * 322 * @param qname Qualified name to resolve 323 * @param namespaceContext Namespace Context to use 324 * @param resolver Prefix resolver for this context 325 */ QName(String qname, Element namespaceContext, PrefixResolver resolver)326 public QName(String qname, Element namespaceContext, 327 PrefixResolver resolver) 328 { 329 this(qname, namespaceContext, resolver, false); 330 } 331 332 /** 333 * Construct a QName from a string, resolving the prefix 334 * using the given namespace context and prefix resolver. 335 * The default namespace is not resolved. 336 * 337 * @param qname Qualified name to resolve 338 * @param namespaceContext Namespace Context to use 339 * @param resolver Prefix resolver for this context 340 * @param validate If true the new QName will be validated and an IllegalArgumentException will 341 * be thrown if it is invalid. 342 */ QName(String qname, Element namespaceContext, PrefixResolver resolver, boolean validate)343 public QName(String qname, Element namespaceContext, 344 PrefixResolver resolver, boolean validate) 345 { 346 347 _namespaceURI = null; 348 349 int indexOfNSSep = qname.indexOf(':'); 350 351 if (indexOfNSSep > 0) 352 { 353 if (null != namespaceContext) 354 { 355 String prefix = qname.substring(0, indexOfNSSep); 356 357 _prefix = prefix; 358 359 if (prefix.equals("xml")) 360 { 361 _namespaceURI = S_XMLNAMESPACEURI; 362 } 363 364 // Do we want this? 365 else if (prefix.equals("xmlns")) 366 { 367 return; 368 } 369 else 370 { 371 _namespaceURI = resolver.getNamespaceForPrefix(prefix, 372 namespaceContext); 373 } 374 375 if (null == _namespaceURI) 376 { 377 throw new RuntimeException( 378 XMLMessages.createXMLMessage( 379 XMLErrorResources.ER_PREFIX_MUST_RESOLVE, 380 new Object[]{ prefix })); //"Prefix must resolve to a namespace: "+prefix); 381 } 382 } 383 else 384 { 385 386 // TODO: error or warning... 387 } 388 } 389 390 _localName = (indexOfNSSep < 0) 391 ? qname : qname.substring(indexOfNSSep + 1); 392 393 if (validate) 394 { 395 if ((_localName == null) || (!XML11Char.isXML11ValidNCName(_localName))) 396 { 397 throw new IllegalArgumentException(XMLMessages.createXMLMessage( 398 XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); 399 } 400 } 401 402 m_hashCode = toString().hashCode(); 403 } 404 405 406 /** 407 * Construct a QName from a string, resolving the prefix 408 * using the given namespace stack. The default namespace is 409 * not resolved. 410 * 411 * @param qname Qualified name to resolve 412 * @param resolver Prefix resolver for this context 413 */ QName(String qname, PrefixResolver resolver)414 public QName(String qname, PrefixResolver resolver) 415 { 416 this(qname, resolver, false); 417 } 418 419 /** 420 * Construct a QName from a string, resolving the prefix 421 * using the given namespace stack. The default namespace is 422 * not resolved. 423 * 424 * @param qname Qualified name to resolve 425 * @param resolver Prefix resolver for this context 426 * @param validate If true the new QName will be validated and an IllegalArgumentException will 427 * be thrown if it is invalid. 428 */ QName(String qname, PrefixResolver resolver, boolean validate)429 public QName(String qname, PrefixResolver resolver, boolean validate) 430 { 431 432 String prefix = null; 433 _namespaceURI = null; 434 435 int indexOfNSSep = qname.indexOf(':'); 436 437 if (indexOfNSSep > 0) 438 { 439 prefix = qname.substring(0, indexOfNSSep); 440 441 if (prefix.equals("xml")) 442 { 443 _namespaceURI = S_XMLNAMESPACEURI; 444 } 445 else 446 { 447 _namespaceURI = resolver.getNamespaceForPrefix(prefix); 448 } 449 450 if (null == _namespaceURI) 451 { 452 throw new RuntimeException( 453 XMLMessages.createXMLMessage( 454 XMLErrorResources.ER_PREFIX_MUST_RESOLVE, 455 new Object[]{ prefix })); //"Prefix must resolve to a namespace: "+prefix); 456 } 457 _localName = qname.substring(indexOfNSSep + 1); 458 } 459 else if (indexOfNSSep == 0) 460 { 461 throw new RuntimeException( 462 XMLMessages.createXMLMessage( 463 XMLErrorResources.ER_NAME_CANT_START_WITH_COLON, 464 null)); 465 } 466 else 467 { 468 _localName = qname; 469 } 470 471 if (validate) 472 { 473 if ((_localName == null) || (!XML11Char.isXML11ValidNCName(_localName))) 474 { 475 throw new IllegalArgumentException(XMLMessages.createXMLMessage( 476 XMLErrorResources.ER_ARG_LOCALNAME_INVALID,null )); //"Argument 'localName' not a valid NCName"); 477 } 478 } 479 480 481 m_hashCode = toString().hashCode(); 482 _prefix = prefix; 483 } 484 485 /** 486 * Returns the namespace URI. Returns null if the namespace URI 487 * is not known. 488 * 489 * @return The namespace URI, or null 490 */ getNamespaceURI()491 public String getNamespaceURI() 492 { 493 return _namespaceURI; 494 } 495 496 /** 497 * Returns the namespace prefix. Returns null if the namespace 498 * prefix is not known. 499 * 500 * @return The namespace prefix, or null 501 */ getPrefix()502 public String getPrefix() 503 { 504 return _prefix; 505 } 506 507 /** 508 * Returns the local part of the qualified name. 509 * 510 * @return The local part of the qualified name 511 */ getLocalName()512 public String getLocalName() 513 { 514 return _localName; 515 } 516 517 /** 518 * Return the string representation of the qualified name, using the 519 * prefix if available, or the '{ns}foo' notation if not. Performs 520 * string concatenation, so beware of performance issues. 521 * 522 * @return the string representation of the namespace 523 */ toString()524 public String toString() 525 { 526 527 return _prefix != null 528 ? (_prefix + ":" + _localName) 529 : (_namespaceURI != null 530 ? ("{"+_namespaceURI + "}" + _localName) : _localName); 531 } 532 533 /** 534 * Return the string representation of the qualified name using the 535 * the '{ns}foo' notation. Performs 536 * string concatenation, so beware of performance issues. 537 * 538 * @return the string representation of the namespace 539 */ toNamespacedString()540 public String toNamespacedString() 541 { 542 543 return (_namespaceURI != null 544 ? ("{"+_namespaceURI + "}" + _localName) : _localName); 545 } 546 547 548 /** 549 * Get the namespace of the qualified name. 550 * 551 * @return the namespace URI of the qualified name 552 */ getNamespace()553 public String getNamespace() 554 { 555 return getNamespaceURI(); 556 } 557 558 /** 559 * Get the local part of the qualified name. 560 * 561 * @return the local part of the qualified name 562 */ getLocalPart()563 public String getLocalPart() 564 { 565 return getLocalName(); 566 } 567 568 /** 569 * Return the cached hashcode of the qualified name. 570 * 571 * @return the cached hashcode of the qualified name 572 */ hashCode()573 public int hashCode() 574 { 575 return m_hashCode; 576 } 577 578 /** 579 * Override equals and agree that we're equal if 580 * the passed object is a string and it matches 581 * the name of the arg. 582 * 583 * @param ns Namespace URI to compare to 584 * @param localPart Local part of qualified name to compare to 585 * 586 * @return True if the local name and uri match 587 */ equals(String ns, String localPart)588 public boolean equals(String ns, String localPart) 589 { 590 591 String thisnamespace = getNamespaceURI(); 592 593 return getLocalName().equals(localPart) 594 && (((null != thisnamespace) && (null != ns)) 595 ? thisnamespace.equals(ns) 596 : ((null == thisnamespace) && (null == ns))); 597 } 598 599 /** 600 * Override equals and agree that we're equal if 601 * the passed object is a QName and it matches 602 * the name of the arg. 603 * 604 * @return True if the qualified names are equal 605 */ equals(Object object)606 public boolean equals(Object object) 607 { 608 609 if (object == this) 610 return true; 611 612 if (object instanceof QName) { 613 QName qname = (QName) object; 614 String thisnamespace = getNamespaceURI(); 615 String thatnamespace = qname.getNamespaceURI(); 616 617 return getLocalName().equals(qname.getLocalName()) 618 && (((null != thisnamespace) && (null != thatnamespace)) 619 ? thisnamespace.equals(thatnamespace) 620 : ((null == thisnamespace) && (null == thatnamespace))); 621 } 622 else 623 return false; 624 } 625 626 /** 627 * Given a string, create and return a QName object 628 * 629 * 630 * @param name String to use to create QName 631 * 632 * @return a QName object 633 */ getQNameFromString(String name)634 public static QName getQNameFromString(String name) 635 { 636 637 StringTokenizer tokenizer = new StringTokenizer(name, "{}", false); 638 QName qname; 639 String s1 = tokenizer.nextToken(); 640 String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; 641 642 if (null == s2) 643 qname = new QName(null, s1); 644 else 645 qname = new QName(s1, s2); 646 647 return qname; 648 } 649 650 /** 651 * This function tells if a raw attribute name is a 652 * xmlns attribute. 653 * 654 * @param attRawName Raw name of attribute 655 * 656 * @return True if the attribute starts with or is equal to xmlns 657 */ isXMLNSDecl(String attRawName)658 public static boolean isXMLNSDecl(String attRawName) 659 { 660 661 return (attRawName.startsWith("xmlns") 662 && (attRawName.equals("xmlns") 663 || attRawName.startsWith("xmlns:"))); 664 } 665 666 /** 667 * This function tells if a raw attribute name is a 668 * xmlns attribute. 669 * 670 * @param attRawName Raw name of attribute 671 * 672 * @return Prefix of attribute 673 */ getPrefixFromXMLNSDecl(String attRawName)674 public static String getPrefixFromXMLNSDecl(String attRawName) 675 { 676 677 int index = attRawName.indexOf(':'); 678 679 return (index >= 0) ? attRawName.substring(index + 1) : ""; 680 } 681 682 /** 683 * Returns the local name of the given node. 684 * 685 * @param qname Input name 686 * 687 * @return Local part of the name if prefixed, or the given name if not 688 */ getLocalPart(String qname)689 public static String getLocalPart(String qname) 690 { 691 692 int index = qname.indexOf(':'); 693 694 return (index < 0) ? qname : qname.substring(index + 1); 695 } 696 697 /** 698 * Returns the local name of the given node. 699 * 700 * @param qname Input name 701 * 702 * @return Prefix of name or empty string if none there 703 */ getPrefixPart(String qname)704 public static String getPrefixPart(String qname) 705 { 706 707 int index = qname.indexOf(':'); 708 709 return (index >= 0) ? qname.substring(0, index) : ""; 710 } 711 } 712