001: // NamespaceSupport.java - generic Namespace support for SAX.
002: // http://www.saxproject.org
003: // Written by David Megginson
004: // This class is in the Public Domain. NO WARRANTY!
005: // $Id: NamespaceSupport.java,v 1.9 2002/02/01 20:06:20 db Exp $
006:
007: package org.xml.sax.helpers;
008:
009: import java.util.EmptyStackException;
010: import java.util.Enumeration;
011: import java.util.Hashtable;
012: import java.util.Vector;
013:
014: /**
015: * Encapsulate Namespace logic for use by applications using SAX,
016: * or internally by SAX drivers.
017: *
018: * <blockquote>
019: * <em>This module, both source code and documentation, is in the
020: * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
021: * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
022: * for further information.
023: * </blockquote>
024: *
025: * <p>This class encapsulates the logic of Namespace processing:
026: * it tracks the declarations currently in force for each context
027: * and automatically processes qualified XML 1.0 names into their
028: * Namespace parts; it can also be used in reverse for generating
029: * XML 1.0 from Namespaces.</p>
030: *
031: * <p>Namespace support objects are reusable, but the reset method
032: * must be invoked between each session.</p>
033: *
034: * <p>Here is a simple session:</p>
035: *
036: * <pre>
037: * String parts[] = new String[3];
038: * NamespaceSupport support = new NamespaceSupport();
039: *
040: * support.pushContext();
041: * support.declarePrefix("", "http://www.w3.org/1999/xhtml");
042: * support.declarePrefix("dc", "http://www.purl.org/dc#");
043: *
044: * parts = support.processName("p", parts, false);
045: * System.out.println("Namespace URI: " + parts[0]);
046: * System.out.println("Local name: " + parts[1]);
047: * System.out.println("Raw name: " + parts[2]);
048: *
049: * parts = support.processName("dc:title", parts, false);
050: * System.out.println("Namespace URI: " + parts[0]);
051: * System.out.println("Local name: " + parts[1]);
052: * System.out.println("Raw name: " + parts[2]);
053: *
054: * support.popContext();
055: * </pre>
056: *
057: * <p>Note that this class is optimized for the use case where most
058: * elements do not contain Namespace declarations: if the same
059: * prefix/URI mapping is repeated for each context (for example), this
060: * class will be somewhat less efficient.</p>
061: *
062: * <p>Although SAX drivers (parsers) may choose to use this class to
063: * implement namespace handling, they are not required to do so.
064: * Applications must track namespace information themselves if they
065: * want to use namespace information.
066: *
067: * @since SAX 2.0
068: * @author David Megginson
069: * @version 2.0.1 (sax2r2)
070: */
071: public class NamespaceSupport {
072:
073: ////////////////////////////////////////////////////////////////////
074: // Constants.
075: ////////////////////////////////////////////////////////////////////
076:
077: /**
078: * The XML Namespace URI as a constant.
079: * The value is <code>http://www.w3.org/XML/1998/namespace</code>
080: * as defined in the XML Namespaces specification.
081: *
082: * <p>This is the Namespace URI that is automatically mapped
083: * to the "xml" prefix.</p>
084: */
085: public final static String XMLNS = "http://www.w3.org/XML/1998/namespace";
086:
087: /**
088: * An empty enumeration.
089: */
090: private final static Enumeration EMPTY_ENUMERATION = new Vector()
091: .elements();
092:
093: ////////////////////////////////////////////////////////////////////
094: // Constructor.
095: ////////////////////////////////////////////////////////////////////
096:
097: /**
098: * Create a new Namespace support object.
099: */
100: public NamespaceSupport() {
101: reset();
102: }
103:
104: ////////////////////////////////////////////////////////////////////
105: // Context management.
106: ////////////////////////////////////////////////////////////////////
107:
108: /**
109: * Reset this Namespace support object for reuse.
110: *
111: * <p>It is necessary to invoke this method before reusing the
112: * Namespace support object for a new session.</p>
113: */
114: public void reset() {
115: contexts = new Context[32];
116: contextPos = 0;
117: contexts[contextPos] = currentContext = new Context();
118: currentContext.declarePrefix("xml", XMLNS);
119: }
120:
121: /**
122: * Start a new Namespace context.
123: * The new context will automatically inherit
124: * the declarations of its parent context, but it will also keep
125: * track of which declarations were made within this context.
126: *
127: * <p>Event callback code should start a new context once per element.
128: * This means being ready to call this in either of two places.
129: * For elements that don't include namespace declarations, the
130: * <em>ContentHandler.startElement()</em> callback is the right place.
131: * For elements with such a declaration, it'd done in the first
132: * <em>ContentHandler.startPrefixMapping()</em> callback.
133: * A boolean flag can be used to
134: * track whether a context has been started yet. When either of
135: * those methods is called, it checks the flag to see if a new context
136: * needs to be started. If so, it starts the context and sets the
137: * flag. After <em>ContentHandler.startElement()</em>
138: * does that, it always clears the flag.
139: *
140: * <p>Normally, SAX drivers would push a new context at the beginning
141: * of each XML element. Then they perform a first pass over the
142: * attributes to process all namespace declarations, making
143: * <em>ContentHandler.startPrefixMapping()</em> callbacks.
144: * Then a second pass is made, to determine the namespace-qualified
145: * names for all attributes and for the element name.
146: * Finally all the information for the
147: * <em>ContentHandler.startElement()</em> callback is available,
148: * so it can then be made.
149: *
150: * <p>The Namespace support object always starts with a base context
151: * already in force: in this context, only the "xml" prefix is
152: * declared.</p>
153: *
154: * @see org.xml.sax.ContentHandler
155: * @see #popContext
156: */
157: public void pushContext() {
158: int max = contexts.length;
159:
160: contexts[contextPos].declsOK = false;
161: contextPos++;
162:
163: // Extend the array if necessary
164: if (contextPos >= max) {
165: Context newContexts[] = new Context[max * 2];
166: System.arraycopy(contexts, 0, newContexts, 0, max);
167: max *= 2;
168: contexts = newContexts;
169: }
170:
171: // Allocate the context if necessary.
172: currentContext = contexts[contextPos];
173: if (currentContext == null) {
174: contexts[contextPos] = currentContext = new Context();
175: }
176:
177: // Set the parent, if any.
178: if (contextPos > 0) {
179: currentContext.setParent(contexts[contextPos - 1]);
180: }
181: }
182:
183: /**
184: * Revert to the previous Namespace context.
185: *
186: * <p>Normally, you should pop the context at the end of each
187: * XML element. After popping the context, all Namespace prefix
188: * mappings that were previously in force are restored.</p>
189: *
190: * <p>You must not attempt to declare additional Namespace
191: * prefixes after popping a context, unless you push another
192: * context first.</p>
193: *
194: * @see #pushContext
195: */
196: public void popContext() {
197: contexts[contextPos].clear();
198: contextPos--;
199: if (contextPos < 0) {
200: throw new EmptyStackException();
201: }
202: currentContext = contexts[contextPos];
203: }
204:
205: ////////////////////////////////////////////////////////////////////
206: // Operations within a context.
207: ////////////////////////////////////////////////////////////////////
208:
209: /**
210: * Declare a Namespace prefix. All prefixes must be declared
211: * before they are referenced. For example, a SAX driver (parser)
212: * would scan an element's attributes
213: * in two passes: first for namespace declarations,
214: * then a second pass using {@link #processName processName()} to
215: * interpret prefixes against (potentially redefined) prefixes.
216: *
217: * <p>This method declares a prefix in the current Namespace
218: * context; the prefix will remain in force until this context
219: * is popped, unless it is shadowed in a descendant context.</p>
220: *
221: * <p>To declare the default element Namespace, use the empty string as
222: * the prefix.</p>
223: *
224: * <p>Note that you must <em>not</em> declare a prefix after
225: * you've pushed and popped another Namespace context, or
226: * treated the declarations phase as complete by processing
227: * a prefixed name.</p>
228: *
229: * <p>Note that there is an asymmetry in this library: {@link
230: * #getPrefix getPrefix} will not return the "" prefix,
231: * even if you have declared a default element namespace.
232: * To check for a default namespace,
233: * you have to look it up explicitly using {@link #getURI getURI}.
234: * This asymmetry exists to make it easier to look up prefixes
235: * for attribute names, where the default prefix is not allowed.</p>
236: *
237: * @param prefix The prefix to declare, or the empty string to
238: * indicate the default element namespace. This may never have
239: * the value "xml" or "xmlns".
240: * @param uri The Namespace URI to associate with the prefix.
241: * @return true if the prefix was legal, false otherwise
242: * @exception IllegalStateException when a prefix is declared
243: * after looking up a name in the context, or after pushing
244: * another context on top of it.
245: *
246: * @see #processName
247: * @see #getURI
248: * @see #getPrefix
249: */
250: public boolean declarePrefix(String prefix, String uri) {
251: if (prefix.equals("xml") || prefix.equals("xmlns")) {
252: return false;
253: } else {
254: currentContext.declarePrefix(prefix, uri);
255: return true;
256: }
257: }
258:
259: /**
260: * Process a raw XML 1.0 name, after all declarations in the current
261: * context have been handled by {@link #declarePrefix declarePrefix()}.
262: *
263: * <p>This method processes a raw XML 1.0 name in the current
264: * context by removing the prefix and looking it up among the
265: * prefixes currently declared. The return value will be the
266: * array supplied by the caller, filled in as follows:</p>
267: *
268: * <dl>
269: * <dt>parts[0]</dt>
270: * <dd>The Namespace URI, or an empty string if none is
271: * in use.</dd>
272: * <dt>parts[1]</dt>
273: * <dd>The local name (without prefix).</dd>
274: * <dt>parts[2]</dt>
275: * <dd>The original raw name.</dd>
276: * </dl>
277: *
278: * <p>All of the strings in the array will be internalized. If
279: * the raw name has a prefix that has not been declared, then
280: * the return value will be null.</p>
281: *
282: * <p>Note that attribute names are processed differently than
283: * element names: an unprefixed element name will received the
284: * default Namespace (if any), while an unprefixed attribute name
285: * will not.</p>
286: *
287: * @param qName The raw XML 1.0 name to be processed.
288: * @param parts An array supplied by the caller, capable of
289: * holding at least three members.
290: * @param isAttribute A flag indicating whether this is an
291: * attribute name (true) or an element name (false).
292: * @return The supplied array holding three internalized strings
293: * representing the Namespace URI (or empty string), the
294: * local name, and the raw XML 1.0 name; or null if there
295: * is an undeclared prefix.
296: * @see #declarePrefix
297: * @see java.lang.String#intern */
298: public String[] processName(String qName, String parts[],
299: boolean isAttribute) {
300: String myParts[] = currentContext.processName(qName,
301: isAttribute);
302: if (myParts == null) {
303: return null;
304: } else {
305: parts[0] = myParts[0];
306: parts[1] = myParts[1];
307: parts[2] = myParts[2];
308: return parts;
309: }
310: }
311:
312: /**
313: * Look up a prefix and get the currently-mapped Namespace URI.
314: *
315: * <p>This method looks up the prefix in the current context.
316: * Use the empty string ("") for the default Namespace.</p>
317: *
318: * @param prefix The prefix to look up.
319: * @return The associated Namespace URI, or null if the prefix
320: * is undeclared in this context.
321: * @see #getPrefix
322: * @see #getPrefixes
323: */
324: public String getURI(String prefix) {
325: return currentContext.getURI(prefix);
326: }
327:
328: /**
329: * Return an enumeration of all prefixes currently declared.
330: *
331: * <p><strong>Note:</strong> if there is a default prefix, it will not be
332: * returned in this enumeration; check for the default prefix
333: * using the {@link #getURI getURI} with an argument of "".</p>
334: *
335: * @return An enumeration of all prefixes declared in the
336: * current context except for the empty (default)
337: * prefix.
338: * @see #getDeclaredPrefixes
339: * @see #getURI
340: */
341: public Enumeration getPrefixes() {
342: return currentContext.getPrefixes();
343: }
344:
345: /**
346: * Return one of the prefixes mapped to a Namespace URI.
347: *
348: * <p>If more than one prefix is currently mapped to the same
349: * URI, this method will make an arbitrary selection; if you
350: * want all of the prefixes, use the {@link #getPrefixes}
351: * method instead.</p>
352: *
353: * <p><strong>Note:</strong> this will never return the empty (default) prefix;
354: * to check for a default prefix, use the {@link #getURI getURI}
355: * method with an argument of "".</p>
356: *
357: * @param uri The Namespace URI.
358: * @param isAttribute true if this prefix is for an attribute
359: * (and the default Namespace is not allowed).
360: * @return One of the prefixes currently mapped to the URI supplied,
361: * or null if none is mapped or if the URI is assigned to
362: * the default Namespace.
363: * @see #getPrefixes(java.lang.String)
364: * @see #getURI
365: */
366: public String getPrefix(String uri) {
367: return currentContext.getPrefix(uri);
368: }
369:
370: /**
371: * Return an enumeration of all prefixes currently declared for a URI.
372: *
373: * <p>This method returns prefixes mapped to a specific Namespace
374: * URI. The xml: prefix will be included. If you want only one
375: * prefix that's mapped to the Namespace URI, and you don't care
376: * which one you get, use the {@link #getPrefix getPrefix}
377: * method instead.</p>
378: *
379: * <p><strong>Note:</strong> the empty (default) prefix is <em>never</em> included
380: * in this enumeration; to check for the presence of a default
381: * Namespace, use the {@link #getURI getURI} method with an
382: * argument of "".</p>
383: *
384: * @param uri The Namespace URI.
385: * @return An enumeration of all prefixes declared in the
386: * current context.
387: * @see #getPrefix
388: * @see #getDeclaredPrefixes
389: * @see #getURI
390: */
391: public Enumeration getPrefixes(String uri) {
392: Vector prefixes = new Vector();
393: Enumeration allPrefixes = getPrefixes();
394: while (allPrefixes.hasMoreElements()) {
395: String prefix = (String) allPrefixes.nextElement();
396: if (uri.equals(getURI(prefix))) {
397: prefixes.addElement(prefix);
398: }
399: }
400: return prefixes.elements();
401: }
402:
403: /**
404: * Return an enumeration of all prefixes declared in this context.
405: *
406: * <p>The empty (default) prefix will be included in this
407: * enumeration; note that this behaviour differs from that of
408: * {@link #getPrefix} and {@link #getPrefixes}.</p>
409: *
410: * @return An enumeration of all prefixes declared in this
411: * context.
412: * @see #getPrefixes
413: * @see #getURI
414: */
415: public Enumeration getDeclaredPrefixes() {
416: return currentContext.getDeclaredPrefixes();
417: }
418:
419: ////////////////////////////////////////////////////////////////////
420: // Internal state.
421: ////////////////////////////////////////////////////////////////////
422:
423: private Context contexts[];
424: private Context currentContext;
425: private int contextPos;
426:
427: ////////////////////////////////////////////////////////////////////
428: // Internal classes.
429: ////////////////////////////////////////////////////////////////////
430:
431: /**
432: * Internal class for a single Namespace context.
433: *
434: * <p>This module caches and reuses Namespace contexts,
435: * so the number allocated
436: * will be equal to the element depth of the document, not to the total
437: * number of elements (i.e. 5-10 rather than tens of thousands).
438: * Also, data structures used to represent contexts are shared when
439: * possible (child contexts without declarations) to further reduce
440: * the amount of memory that's consumed.
441: * </p>
442: */
443: final class Context {
444:
445: /**
446: * Create the root-level Namespace context.
447: */
448: Context() {
449: copyTables();
450: }
451:
452: /**
453: * (Re)set the parent of this Namespace context.
454: * The context must either have been freshly constructed,
455: * or must have been cleared.
456: *
457: * @param context The parent Namespace context object.
458: */
459: void setParent(Context parent) {
460: this .parent = parent;
461: declarations = null;
462: prefixTable = parent.prefixTable;
463: uriTable = parent.uriTable;
464: elementNameTable = parent.elementNameTable;
465: attributeNameTable = parent.attributeNameTable;
466: defaultNS = parent.defaultNS;
467: declSeen = false;
468: declsOK = true;
469: }
470:
471: /**
472: * Makes associated state become collectible,
473: * invalidating this context.
474: * {@link #setParent} must be called before
475: * this context may be used again.
476: */
477: void clear() {
478: parent = null;
479: prefixTable = null;
480: uriTable = null;
481: elementNameTable = null;
482: attributeNameTable = null;
483: defaultNS = null;
484: }
485:
486: /**
487: * Declare a Namespace prefix for this context.
488: *
489: * @param prefix The prefix to declare.
490: * @param uri The associated Namespace URI.
491: * @see org.xml.sax.helpers.NamespaceSupport#declarePrefix
492: */
493: void declarePrefix(String prefix, String uri) {
494: // Lazy processing...
495: if (!declsOK)
496: throw new IllegalStateException(
497: "can't declare any more prefixes in this context");
498: if (!declSeen) {
499: copyTables();
500: }
501: if (declarations == null) {
502: declarations = new Vector();
503: }
504:
505: prefix = prefix.intern();
506: uri = uri.intern();
507: if ("".equals(prefix)) {
508: if ("".equals(uri)) {
509: defaultNS = null;
510: } else {
511: defaultNS = uri;
512: }
513: } else {
514: prefixTable.put(prefix, uri);
515: uriTable.put(uri, prefix); // may wipe out another prefix
516: }
517: declarations.addElement(prefix);
518: }
519:
520: /**
521: * Process a raw XML 1.0 name in this context.
522: *
523: * @param qName The raw XML 1.0 name.
524: * @param isAttribute true if this is an attribute name.
525: * @return An array of three strings containing the
526: * URI part (or empty string), the local part,
527: * and the raw name, all internalized, or null
528: * if there is an undeclared prefix.
529: * @see org.xml.sax.helpers.NamespaceSupport#processName
530: */
531: String[] processName(String qName, boolean isAttribute) {
532: String name[];
533: Hashtable table;
534:
535: // detect errors in call sequence
536: declsOK = false;
537:
538: // Select the appropriate table.
539: if (isAttribute) {
540: table = attributeNameTable;
541: } else {
542: table = elementNameTable;
543: }
544:
545: // Start by looking in the cache, and
546: // return immediately if the name
547: // is already known in this content
548: name = (String[]) table.get(qName);
549: if (name != null) {
550: return name;
551: }
552:
553: // We haven't seen this name in this
554: // context before. Maybe in the parent
555: // context, but we can't assume prefix
556: // bindings are the same.
557: name = new String[3];
558: name[2] = qName.intern();
559: int index = qName.indexOf(':');
560:
561: // No prefix.
562: if (index == -1) {
563: if (isAttribute || defaultNS == null) {
564: name[0] = "";
565: } else {
566: name[0] = defaultNS;
567: }
568: name[1] = name[2];
569: }
570:
571: // Prefix
572: else {
573: String prefix = qName.substring(0, index);
574: String local = qName.substring(index + 1);
575: String uri;
576: if ("".equals(prefix)) {
577: uri = defaultNS;
578: } else {
579: uri = (String) prefixTable.get(prefix);
580: }
581: if (uri == null) {
582: return null;
583: }
584: name[0] = uri;
585: name[1] = local.intern();
586: }
587:
588: // Save in the cache for future use.
589: // (Could be shared with parent context...)
590: table.put(name[2], name);
591: return name;
592: }
593:
594: /**
595: * Look up the URI associated with a prefix in this context.
596: *
597: * @param prefix The prefix to look up.
598: * @return The associated Namespace URI, or null if none is
599: * declared.
600: * @see org.xml.sax.helpers.NamespaceSupport#getURI
601: */
602: String getURI(String prefix) {
603: if ("".equals(prefix)) {
604: return defaultNS;
605: } else if (prefixTable == null) {
606: return null;
607: } else {
608: return (String) prefixTable.get(prefix);
609: }
610: }
611:
612: /**
613: * Look up one of the prefixes associated with a URI in this context.
614: *
615: * <p>Since many prefixes may be mapped to the same URI,
616: * the return value may be unreliable.</p>
617: *
618: * @param uri The URI to look up.
619: * @return The associated prefix, or null if none is declared.
620: * @see org.xml.sax.helpers.NamespaceSupport#getPrefix
621: */
622: String getPrefix(String uri) {
623: if (uriTable == null) {
624: return null;
625: } else {
626: return (String) uriTable.get(uri);
627: }
628: }
629:
630: /**
631: * Return an enumeration of prefixes declared in this context.
632: *
633: * @return An enumeration of prefixes (possibly empty).
634: * @see org.xml.sax.helpers.NamespaceSupport#getDeclaredPrefixes
635: */
636: Enumeration getDeclaredPrefixes() {
637: if (declarations == null) {
638: return EMPTY_ENUMERATION;
639: } else {
640: return declarations.elements();
641: }
642: }
643:
644: /**
645: * Return an enumeration of all prefixes currently in force.
646: *
647: * <p>The default prefix, if in force, is <em>not</em>
648: * returned, and will have to be checked for separately.</p>
649: *
650: * @return An enumeration of prefixes (never empty).
651: * @see org.xml.sax.helpers.NamespaceSupport#getPrefixes
652: */
653: Enumeration getPrefixes() {
654: if (prefixTable == null) {
655: return EMPTY_ENUMERATION;
656: } else {
657: return prefixTable.keys();
658: }
659: }
660:
661: ////////////////////////////////////////////////////////////////
662: // Internal methods.
663: ////////////////////////////////////////////////////////////////
664:
665: /**
666: * Copy on write for the internal tables in this context.
667: *
668: * <p>This class is optimized for the normal case where most
669: * elements do not contain Namespace declarations.</p>
670: */
671: private void copyTables() {
672: if (prefixTable != null) {
673: prefixTable = (Hashtable) prefixTable.clone();
674: } else {
675: prefixTable = new Hashtable();
676: }
677: if (uriTable != null) {
678: uriTable = (Hashtable) uriTable.clone();
679: } else {
680: uriTable = new Hashtable();
681: }
682: elementNameTable = new Hashtable();
683: attributeNameTable = new Hashtable();
684: declSeen = true;
685: }
686:
687: ////////////////////////////////////////////////////////////////
688: // Protected state.
689: ////////////////////////////////////////////////////////////////
690:
691: Hashtable prefixTable;
692: Hashtable uriTable;
693: Hashtable elementNameTable;
694: Hashtable attributeNameTable;
695: String defaultNS = null;
696: boolean declsOK = true;
697:
698: ////////////////////////////////////////////////////////////////
699: // Internal state.
700: ////////////////////////////////////////////////////////////////
701:
702: private Vector declarations = null;
703: private boolean declSeen = false;
704: private Context parent = null;
705: }
706: }
707:
708: // end of NamespaceSupport.java
|