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 "License"); 007 * you may not use this file except in compliance with the License. 008 * 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, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: NamespaceSupport2.java 468655 2006-10-28 07:12:06Z minchau $ 020 */ 021 package org.apache.xml.utils; 022 023 import java.util.EmptyStackException; 024 import java.util.Enumeration; 025 import java.util.Hashtable; 026 import java.util.Vector; 027 028 /** 029 * Encapsulate Namespace tracking logic for use by SAX drivers. 030 * 031 * <p>This class is an attempt to rewrite the SAX NamespaceSupport 032 * "helper" class for improved efficiency. It can be used to track the 033 * namespace declarations currently in scope, providing lookup 034 * routines to map prefixes to URIs and vice versa.</p> 035 * 036 * <p>ISSUE: For testing purposes, I've extended NamespaceSupport even 037 * though I'm completely reasserting all behaviors and fields. 038 * Wasteful.... But SAX did not put an interface under that object and 039 * we seem to have written that SAX class into our APIs... and I don't 040 * want to argue with it right now. </p> 041 * 042 * @see org.xml.sax.helpers.NamespaceSupport 043 * */ 044 public class NamespaceSupport2 045 extends org.xml.sax.helpers.NamespaceSupport 046 { 047 //////////////////////////////////////////////////////////////////// 048 // Internal state. 049 //////////////////////////////////////////////////////////////////// 050 051 private Context2 currentContext; // Current point on the double-linked stack 052 053 054 //////////////////////////////////////////////////////////////////// 055 // Constants. 056 //////////////////////////////////////////////////////////////////// 057 058 059 /** 060 * The XML Namespace as a constant. 061 * 062 * <p>This is the Namespace URI that is automatically mapped 063 * to the "xml" prefix.</p> 064 */ 065 public final static String XMLNS = 066 "http://www.w3.org/XML/1998/namespace"; 067 068 069 //////////////////////////////////////////////////////////////////// 070 // Constructor. 071 //////////////////////////////////////////////////////////////////// 072 073 074 /** 075 * Create a new Namespace support object. 076 */ 077 public NamespaceSupport2 () 078 { 079 reset(); 080 } 081 082 083 //////////////////////////////////////////////////////////////////// 084 // Context management. 085 //////////////////////////////////////////////////////////////////// 086 087 088 /** 089 * Reset this Namespace support object for reuse. 090 * 091 * <p>It is necessary to invoke this method before reusing the 092 * Namespace support object for a new session.</p> 093 */ 094 public void reset () 095 { 096 // Discarding the whole stack doesn't save us a lot versus 097 // creating a new NamespaceSupport. Do we care, or should we 098 // change this to just reset the root context? 099 currentContext = new Context2(null); 100 currentContext.declarePrefix("xml", XMLNS); 101 } 102 103 104 /** 105 * Start a new Namespace context. 106 * 107 * <p>Normally, you should push a new context at the beginning 108 * of each XML element: the new context will automatically inherit 109 * the declarations of its parent context, but it will also keep 110 * track of which declarations were made within this context.</p> 111 * 112 * <p>The Namespace support object always starts with a base context 113 * already in force: in this context, only the "xml" prefix is 114 * declared.</p> 115 * 116 * @see #popContext 117 */ 118 public void pushContext () 119 { 120 // JJK: Context has a parent pointer. 121 // That means we don't need a stack to pop. 122 // We may want to retain for reuse, but that can be done via 123 // a child pointer. 124 125 Context2 parentContext=currentContext; 126 currentContext = parentContext.getChild(); 127 if (currentContext == null){ 128 currentContext = new Context2(parentContext); 129 } 130 else{ 131 // JJK: This will wipe out any leftover data 132 // if we're reusing a previously allocated Context. 133 currentContext.setParent(parentContext); 134 } 135 } 136 137 138 /** 139 * Revert to the previous Namespace context. 140 * 141 * <p>Normally, you should pop the context at the end of each 142 * XML element. After popping the context, all Namespace prefix 143 * mappings that were previously in force are restored.</p> 144 * 145 * <p>You must not attempt to declare additional Namespace 146 * prefixes after popping a context, unless you push another 147 * context first.</p> 148 * 149 * @see #pushContext 150 */ 151 public void popContext () 152 { 153 Context2 parentContext=currentContext.getParent(); 154 if(parentContext==null) 155 throw new EmptyStackException(); 156 else 157 currentContext = parentContext; 158 } 159 160 161 162 //////////////////////////////////////////////////////////////////// 163 // Operations within a context. 164 //////////////////////////////////////////////////////////////////// 165 166 167 /** 168 * Declare a Namespace prefix. 169 * 170 * <p>This method declares a prefix in the current Namespace 171 * context; the prefix will remain in force until this context 172 * is popped, unless it is shadowed in a descendant context.</p> 173 * 174 * <p>To declare a default Namespace, use the empty string. The 175 * prefix must not be "xml" or "xmlns".</p> 176 * 177 * <p>Note that you must <em>not</em> declare a prefix after 178 * you've pushed and popped another Namespace.</p> 179 * 180 * <p>Note that there is an asymmetry in this library: while {@link 181 * #getPrefix getPrefix} will not return the default "" prefix, 182 * even if you have declared one; to check for a default prefix, 183 * you have to look it up explicitly using {@link #getURI getURI}. 184 * This asymmetry exists to make it easier to look up prefixes 185 * for attribute names, where the default prefix is not allowed.</p> 186 * 187 * @param prefix The prefix to declare, or null for the empty 188 * string. 189 * @param uri The Namespace URI to associate with the prefix. 190 * @return true if the prefix was legal, false otherwise 191 * @see #processName 192 * @see #getURI 193 * @see #getPrefix 194 */ 195 public boolean declarePrefix (String prefix, String uri) 196 { 197 if (prefix.equals("xml") || prefix.equals("xmlns")) { 198 return false; 199 } else { 200 currentContext.declarePrefix(prefix, uri); 201 return true; 202 } 203 } 204 205 206 /** 207 * Process a raw XML 1.0 name. 208 * 209 * <p>This method processes a raw XML 1.0 name in the current 210 * context by removing the prefix and looking it up among the 211 * prefixes currently declared. The return value will be the 212 * array supplied by the caller, filled in as follows:</p> 213 * 214 * <dl> 215 * <dt>parts[0]</dt> 216 * <dd>The Namespace URI, or an empty string if none is 217 * in use.</dd> 218 * <dt>parts[1]</dt> 219 * <dd>The local name (without prefix).</dd> 220 * <dt>parts[2]</dt> 221 * <dd>The original raw name.</dd> 222 * </dl> 223 * 224 * <p>All of the strings in the array will be internalized. If 225 * the raw name has a prefix that has not been declared, then 226 * the return value will be null.</p> 227 * 228 * <p>Note that attribute names are processed differently than 229 * element names: an unprefixed element name will received the 230 * default Namespace (if any), while an unprefixed element name 231 * will not.</p> 232 * 233 * @param qName The raw XML 1.0 name to be processed. 234 * @param parts A string array supplied by the caller, capable of 235 * holding at least three members. 236 * @param isAttribute A flag indicating whether this is an 237 * attribute name (true) or an element name (false). 238 * @return The supplied array holding three internalized strings 239 * representing the Namespace URI (or empty string), the 240 * local name, and the raw XML 1.0 name; or null if there 241 * is an undeclared prefix. 242 * @see #declarePrefix 243 * @see java.lang.String#intern */ 244 public String [] processName (String qName, String[] parts, 245 boolean isAttribute) 246 { 247 String[] name=currentContext.processName(qName, isAttribute); 248 if(name==null) 249 return null; 250 251 // JJK: This recopying is required because processName may return 252 // a cached result. I Don't Like It. ***** 253 System.arraycopy(name,0,parts,0,3); 254 return parts; 255 } 256 257 258 /** 259 * Look up a prefix and get the currently-mapped Namespace URI. 260 * 261 * <p>This method looks up the prefix in the current context. 262 * Use the empty string ("") for the default Namespace.</p> 263 * 264 * @param prefix The prefix to look up. 265 * @return The associated Namespace URI, or null if the prefix 266 * is undeclared in this context. 267 * @see #getPrefix 268 * @see #getPrefixes 269 */ 270 public String getURI (String prefix) 271 { 272 return currentContext.getURI(prefix); 273 } 274 275 276 /** 277 * Return an enumeration of all prefixes currently declared. 278 * 279 * <p><strong>Note:</strong> if there is a default prefix, it will not be 280 * returned in this enumeration; check for the default prefix 281 * using the {@link #getURI getURI} with an argument of "".</p> 282 * 283 * @return An enumeration of all prefixes declared in the 284 * current context except for the empty (default) 285 * prefix. 286 * @see #getDeclaredPrefixes 287 * @see #getURI 288 */ 289 public Enumeration getPrefixes () 290 { 291 return currentContext.getPrefixes(); 292 } 293 294 295 /** 296 * Return one of the prefixes mapped to a Namespace URI. 297 * 298 * <p>If more than one prefix is currently mapped to the same 299 * URI, this method will make an arbitrary selection; if you 300 * want all of the prefixes, use the {@link #getPrefixes} 301 * method instead.</p> 302 * 303 * <p><strong>Note:</strong> this will never return the empty 304 * (default) prefix; to check for a default prefix, use the {@link 305 * #getURI getURI} method with an argument of "".</p> 306 * 307 * @param uri The Namespace URI. 308 * @return One of the prefixes currently mapped to the URI supplied, 309 * or null if none is mapped or if the URI is assigned to 310 * the default Namespace. 311 * @see #getPrefixes(java.lang.String) 312 * @see #getURI */ 313 public String getPrefix (String uri) 314 { 315 return currentContext.getPrefix(uri); 316 } 317 318 319 /** 320 * Return an enumeration of all prefixes currently declared for a URI. 321 * 322 * <p>This method returns prefixes mapped to a specific Namespace 323 * URI. The xml: prefix will be included. If you want only one 324 * prefix that's mapped to the Namespace URI, and you don't care 325 * which one you get, use the {@link #getPrefix getPrefix} 326 * method instead.</p> 327 * 328 * <p><strong>Note:</strong> the empty (default) prefix is 329 * <em>never</em> included in this enumeration; to check for the 330 * presence of a default Namespace, use the {@link #getURI getURI} 331 * method with an argument of "".</p> 332 * 333 * @param uri The Namespace URI. 334 * @return An enumeration of all prefixes declared in the 335 * current context. 336 * @see #getPrefix 337 * @see #getDeclaredPrefixes 338 * @see #getURI */ 339 public Enumeration getPrefixes (String uri) 340 { 341 // JJK: The old code involved creating a vector, filling it 342 // with all the matching prefixes, and then getting its 343 // elements enumerator. Wastes storage, wastes cycles if we 344 // don't actually need them all. Better to either implement 345 // a specific enumerator for these prefixes... or a filter 346 // around the all-prefixes enumerator, which comes out to 347 // roughly the same thing. 348 // 349 // **** Currently a filter. That may not be most efficient 350 // when I'm done restructuring storage! 351 return new PrefixForUriEnumerator(this,uri,getPrefixes()); 352 } 353 354 355 /** 356 * Return an enumeration of all prefixes declared in this context. 357 * 358 * <p>The empty (default) prefix will be included in this 359 * enumeration; note that this behaviour differs from that of 360 * {@link #getPrefix} and {@link #getPrefixes}.</p> 361 * 362 * @return An enumeration of all prefixes declared in this 363 * context. 364 * @see #getPrefixes 365 * @see #getURI 366 */ 367 public Enumeration getDeclaredPrefixes () 368 { 369 return currentContext.getDeclaredPrefixes(); 370 } 371 372 373 374 } 375 376 //////////////////////////////////////////////////////////////////// 377 // Local classes. 378 // These were _internal_ classes... but in fact they don't have to be, 379 // and may be more efficient if they aren't. 380 //////////////////////////////////////////////////////////////////// 381 382 /** 383 * Implementation of Enumeration filter, wrapped 384 * aroung the get-all-prefixes version of the operation. This is NOT 385 * necessarily the most efficient approach; finding the URI and then asking 386 * what prefixes apply to it might make much more sense. 387 */ 388 class PrefixForUriEnumerator implements Enumeration 389 { 390 private Enumeration allPrefixes; 391 private String uri; 392 private String lookahead=null; 393 private NamespaceSupport2 nsup; 394 395 // Kluge: Since one can't do a constructor on an 396 // anonymous class (as far as I know)... 397 PrefixForUriEnumerator(NamespaceSupport2 nsup,String uri, Enumeration allPrefixes) 398 { 399 this.nsup=nsup; 400 this.uri=uri; 401 this.allPrefixes=allPrefixes; 402 } 403 404 public boolean hasMoreElements() 405 { 406 if(lookahead!=null) 407 return true; 408 409 while(allPrefixes.hasMoreElements()) 410 { 411 String prefix=(String)allPrefixes.nextElement(); 412 if(uri.equals(nsup.getURI(prefix))) 413 { 414 lookahead=prefix; 415 return true; 416 } 417 } 418 return false; 419 } 420 421 public Object nextElement() 422 { 423 if(hasMoreElements()) 424 { 425 String tmp=lookahead; 426 lookahead=null; 427 return tmp; 428 } 429 else 430 throw new java.util.NoSuchElementException(); 431 } 432 } 433 434 /** 435 * Internal class for a single Namespace context. 436 * 437 * <p>This module caches and reuses Namespace contexts, so the number allocated 438 * will be equal to the element depth of the document, not to the total 439 * number of elements (i.e. 5-10 rather than tens of thousands).</p> 440 */ 441 final class Context2 { 442 443 //////////////////////////////////////////////////////////////// 444 // Manefest Constants 445 //////////////////////////////////////////////////////////////// 446 447 /** 448 * An empty enumeration. 449 */ 450 private final static Enumeration EMPTY_ENUMERATION = 451 new Vector().elements(); 452 453 //////////////////////////////////////////////////////////////// 454 // Protected state. 455 //////////////////////////////////////////////////////////////// 456 457 Hashtable prefixTable; 458 Hashtable uriTable; 459 Hashtable elementNameTable; 460 Hashtable attributeNameTable; 461 String defaultNS = null; 462 463 //////////////////////////////////////////////////////////////// 464 // Internal state. 465 //////////////////////////////////////////////////////////////// 466 467 private Vector declarations = null; 468 private boolean tablesDirty = false; 469 private Context2 parent = null; 470 private Context2 child = null; 471 472 /** 473 * Create a new Namespace context. 474 */ 475 Context2 (Context2 parent) 476 { 477 if(parent==null) 478 { 479 prefixTable = new Hashtable(); 480 uriTable = new Hashtable(); 481 elementNameTable=null; 482 attributeNameTable=null; 483 } 484 else 485 setParent(parent); 486 } 487 488 489 /** 490 * @returns The child Namespace context object, or null if this 491 * is the last currently on the chain. 492 */ 493 Context2 getChild() 494 { 495 return child; 496 } 497 498 /** 499 * @returns The parent Namespace context object, or null if this 500 * is the root. 501 */ 502 Context2 getParent() 503 { 504 return parent; 505 } 506 507 /** 508 * (Re)set the parent of this Namespace context. 509 * This is separate from the c'tor because it's re-applied 510 * when a Context2 is reused by push-after-pop. 511 * 512 * @param context The parent Namespace context object. 513 */ 514 void setParent (Context2 parent) 515 { 516 this.parent = parent; 517 parent.child = this; // JJK: Doubly-linked 518 declarations = null; 519 prefixTable = parent.prefixTable; 520 uriTable = parent.uriTable; 521 elementNameTable = parent.elementNameTable; 522 attributeNameTable = parent.attributeNameTable; 523 defaultNS = parent.defaultNS; 524 tablesDirty = false; 525 } 526 527 528 /** 529 * Declare a Namespace prefix for this context. 530 * 531 * @param prefix The prefix to declare. 532 * @param uri The associated Namespace URI. 533 * @see org.xml.sax.helpers.NamespaceSupport2#declarePrefix 534 */ 535 void declarePrefix (String prefix, String uri) 536 { 537 // Lazy processing... 538 if (!tablesDirty) { 539 copyTables(); 540 } 541 if (declarations == null) { 542 declarations = new Vector(); 543 } 544 545 prefix = prefix.intern(); 546 uri = uri.intern(); 547 if ("".equals(prefix)) { 548 if ("".equals(uri)) { 549 defaultNS = null; 550 } else { 551 defaultNS = uri; 552 } 553 } else { 554 prefixTable.put(prefix, uri); 555 uriTable.put(uri, prefix); // may wipe out another prefix 556 } 557 declarations.addElement(prefix); 558 } 559 560 561 /** 562 * Process a raw XML 1.0 name in this context. 563 * 564 * @param qName The raw XML 1.0 name. 565 * @param isAttribute true if this is an attribute name. 566 * @return An array of three strings containing the 567 * URI part (or empty string), the local part, 568 * and the raw name, all internalized, or null 569 * if there is an undeclared prefix. 570 * @see org.xml.sax.helpers.NamespaceSupport2#processName 571 */ 572 String [] processName (String qName, boolean isAttribute) 573 { 574 String name[]; 575 Hashtable table; 576 577 // Select the appropriate table. 578 if (isAttribute) { 579 if(elementNameTable==null) 580 elementNameTable=new Hashtable(); 581 table = elementNameTable; 582 } else { 583 if(attributeNameTable==null) 584 attributeNameTable=new Hashtable(); 585 table = attributeNameTable; 586 } 587 588 // Start by looking in the cache, and 589 // return immediately if the name 590 // is already known in this content 591 name = (String[])table.get(qName); 592 if (name != null) { 593 return name; 594 } 595 596 // We haven't seen this name in this 597 // context before. 598 name = new String[3]; 599 int index = qName.indexOf(':'); 600 601 602 // No prefix. 603 if (index == -1) { 604 if (isAttribute || defaultNS == null) { 605 name[0] = ""; 606 } else { 607 name[0] = defaultNS; 608 } 609 name[1] = qName.intern(); 610 name[2] = name[1]; 611 } 612 613 // Prefix 614 else { 615 String prefix = qName.substring(0, index); 616 String local = qName.substring(index+1); 617 String uri; 618 if ("".equals(prefix)) { 619 uri = defaultNS; 620 } else { 621 uri = (String)prefixTable.get(prefix); 622 } 623 if (uri == null) { 624 return null; 625 } 626 name[0] = uri; 627 name[1] = local.intern(); 628 name[2] = qName.intern(); 629 } 630 631 // Save in the cache for future use. 632 table.put(name[2], name); 633 tablesDirty = true; 634 return name; 635 } 636 637 638 /** 639 * Look up the URI associated with a prefix in this context. 640 * 641 * @param prefix The prefix to look up. 642 * @return The associated Namespace URI, or null if none is 643 * declared. 644 * @see org.xml.sax.helpers.NamespaceSupport2#getURI 645 */ 646 String getURI (String prefix) 647 { 648 if ("".equals(prefix)) { 649 return defaultNS; 650 } else if (prefixTable == null) { 651 return null; 652 } else { 653 return (String)prefixTable.get(prefix); 654 } 655 } 656 657 658 /** 659 * Look up one of the prefixes associated with a URI in this context. 660 * 661 * <p>Since many prefixes may be mapped to the same URI, 662 * the return value may be unreliable.</p> 663 * 664 * @param uri The URI to look up. 665 * @return The associated prefix, or null if none is declared. 666 * @see org.xml.sax.helpers.NamespaceSupport2#getPrefix 667 */ 668 String getPrefix (String uri) 669 { 670 if (uriTable == null) { 671 return null; 672 } else { 673 return (String)uriTable.get(uri); 674 } 675 } 676 677 678 /** 679 * Return an enumeration of prefixes declared in this context. 680 * 681 * @return An enumeration of prefixes (possibly empty). 682 * @see org.xml.sax.helpers.NamespaceSupport2#getDeclaredPrefixes 683 */ 684 Enumeration getDeclaredPrefixes () 685 { 686 if (declarations == null) { 687 return EMPTY_ENUMERATION; 688 } else { 689 return declarations.elements(); 690 } 691 } 692 693 694 /** 695 * Return an enumeration of all prefixes currently in force. 696 * 697 * <p>The default prefix, if in force, is <em>not</em> 698 * returned, and will have to be checked for separately.</p> 699 * 700 * @return An enumeration of prefixes (never empty). 701 * @see org.xml.sax.helpers.NamespaceSupport2#getPrefixes 702 */ 703 Enumeration getPrefixes () 704 { 705 if (prefixTable == null) { 706 return EMPTY_ENUMERATION; 707 } else { 708 return prefixTable.keys(); 709 } 710 } 711 712 //////////////////////////////////////////////////////////////// 713 // Internal methods. 714 //////////////////////////////////////////////////////////////// 715 716 /** 717 * Copy on write for the internal tables in this context. 718 * 719 * <p>This class is optimized for the normal case where most 720 * elements do not contain Namespace declarations. In that case, 721 * the Context2 will share data structures with its parent. 722 * New tables are obtained only when new declarations are issued, 723 * so they can be popped off the stack.</p> 724 * 725 * <p> JJK: **** Alternative: each Context2 might declare 726 * _only_ its local bindings, and delegate upward if not found.</p> 727 */ 728 private void copyTables () 729 { 730 // Start by copying our parent's bindings 731 prefixTable = (Hashtable)prefixTable.clone(); 732 uriTable = (Hashtable)uriTable.clone(); 733 734 // Replace the caches with empty ones, rather than 735 // trying to determine which bindings should be flushed. 736 // As far as I can tell, these caches are never actually 737 // used in Xalan... More efficient to remove the whole 738 // cache system? **** 739 if(elementNameTable!=null) 740 elementNameTable=new Hashtable(); 741 if(attributeNameTable!=null) 742 attributeNameTable=new Hashtable(); 743 tablesDirty = true; 744 } 745 746 } 747 748 749 // end of NamespaceSupport2.java