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