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