001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.ws.util;
038:
039: import java.util.ArrayList;
040: import java.util.EmptyStackException;
041: import java.util.HashMap;
042: import java.util.Iterator;
043: import java.util.List;
044: import java.util.Map;
045:
046: import com.sun.xml.ws.encoding.soap.streaming.SOAPNamespaceConstants;
047:
048: /**
049: * Encapsulate Namespace logic for use by SAX drivers.
050: *
051: * <blockquote>
052: * <em>This module, both source code and documentation, is in the
053: * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
054: * </blockquote>
055: *
056: * <p>This class encapsulates the logic of Namespace processing:
057: * it tracks the declarations currently in force for each context
058: * and automatically processes qualified XML 1.0 names into their
059: * Namespace parts; it can also be used in reverse for generating
060: * XML 1.0 from Namespaces.</p>
061: *
062: * <p>Namespace support objects are reusable, but the reset method
063: * must be invoked between each session.</p>
064: *
065: * <p>Here is a simple session:</p>
066: *
067: * <pre>
068: * String parts[] = new String[3];
069: * NamespaceSupport support = new NamespaceSupport();
070: *
071: * support.pushContext();
072: * support.declarePrefix("", "http://www.w3.org/1999/xhtml");
073: * support.declarePrefix("dc", "http://www.purl.org/dc#");
074: *
075: * String parts[] = support.processName("p", parts, false);
076: * System.out.println("Namespace URI: " + parts[0]);
077: * System.out.println("Local name: " + parts[1]);
078: * System.out.println("Raw name: " + parts[2]);
079:
080: * String parts[] = support.processName("dc:title", parts, false);
081: * System.out.println("Namespace URI: " + parts[0]);
082: * System.out.println("Local name: " + parts[1]);
083: * System.out.println("Raw name: " + parts[2]);
084:
085: * support.popContext();
086: * </pre>
087: *
088: * <p>Note that this class is optimized for the use case where most
089: * elements do not contain Namespace declarations: if the same
090: * prefix/URI mapping is repeated for each context (for example), this
091: * class will be somewhat less efficient.</p>
092: *
093: * @author David Megginson
094: * @author WS Development Team
095: */
096: public class NamespaceSupport {
097:
098: /* added two new methods, slideContextUp() and slideContextDown()
099: * needed to implement the revised streaming parser class (Parser2)
100: */
101:
102: ////////////////////////////////////////////////////////////////////
103: // Constants.
104: ////////////////////////////////////////////////////////////////////
105: /**
106: * The XML Namespace as a constant.
107: *
108: * <p>This is the Namespace URI that is automatically mapped
109: * to the "xml" prefix.</p>
110: */
111: public final static String XMLNS = "http://www.w3.org/XML/1998/namespace";
112:
113: /**
114: * An empty enumeration.
115: */
116: private final static Iterable<String> EMPTY_ENUMERATION = new ArrayList<String>();
117:
118: ////////////////////////////////////////////////////////////////////
119: // Constructor.
120: ////////////////////////////////////////////////////////////////////
121:
122: /**
123: * Create a new Namespace support object.
124: */
125: public NamespaceSupport() {
126: reset();
127: }
128:
129: // PBG May 6 2002 added a copy constructor to support recording
130: public NamespaceSupport(NamespaceSupport that) {
131: contexts = new Context[that.contexts.length];
132: currentContext = null;
133: contextPos = that.contextPos;
134:
135: Context currentParent = null;
136:
137: for (int i = 0; i < that.contexts.length; i++) {
138: Context thatContext = that.contexts[i];
139:
140: if (thatContext == null) {
141: contexts[i] = null;
142: continue;
143: }
144:
145: Context this Context = new Context(thatContext,
146: currentParent);
147: contexts[i] = this Context;
148: if (that.currentContext == thatContext) {
149: currentContext = this Context;
150: }
151:
152: currentParent = this Context;
153: }
154: }
155:
156: ////////////////////////////////////////////////////////////////////
157: // Context management.
158: ////////////////////////////////////////////////////////////////////
159:
160: /**
161: * Reset this Namespace support object for reuse.
162: *
163: * <p>It is necessary to invoke this method before reusing the
164: * Namespace support object for a new session.</p>
165: */
166: public void reset() {
167: contexts = new Context[32];
168: contextPos = 0;
169: contexts[contextPos] = currentContext = new Context();
170: currentContext.declarePrefix("xml", XMLNS);
171: }
172:
173: /**
174: * Start a new Namespace context.
175: *
176: * <p>Normally, you should push a new context at the beginning
177: * of each XML element: the new context will automatically inherit
178: * the declarations of its parent context, but it will also keep
179: * track of which declarations were made within this context.</p>
180: *
181: * <p>The Namespace support object always starts with a base context
182: * already in force: in this context, only the "xml" prefix is
183: * declared.</p>
184: *
185: * @see #popContext
186: */
187: public void pushContext() {
188: int max = contexts.length;
189: contextPos++;
190:
191: // Extend the array if necessary
192: if (contextPos >= max) {
193: Context newContexts[] = new Context[max * 2];
194: System.arraycopy(contexts, 0, newContexts, 0, max);
195: contexts = newContexts;
196: }
197:
198: // Allocate the context if necessary.
199: currentContext = contexts[contextPos];
200: if (currentContext == null) {
201: contexts[contextPos] = currentContext = new Context();
202: }
203:
204: // Set the parent, if any.
205: if (contextPos > 0) {
206: currentContext.setParent(contexts[contextPos - 1]);
207: }
208: }
209:
210: /**
211: * Revert to the previous Namespace context.
212: *
213: * <p>Normally, you should pop the context at the end of each
214: * XML element. After popping the context, all Namespace prefix
215: * mappings that were previously in force are restored.</p>
216: *
217: * <p>You must not attempt to declare additional Namespace
218: * prefixes after popping a context, unless you push another
219: * context first.</p>
220: *
221: * @see #pushContext
222: */
223: public void popContext() {
224: contextPos--;
225: if (contextPos < 0) {
226: throw new EmptyStackException();
227: }
228: currentContext = contexts[contextPos];
229: }
230:
231: /*
232: * added for the revised streaming parser class (Parser2)
233: * Move the context artificially up one level (i.e. contracting it).
234: */
235: public void slideContextUp() {
236: contextPos--;
237: currentContext = contexts[contextPos];
238: }
239:
240: /*
241: * added for the revised streaming parser class (Parser2)
242: * Move the context artificially down one level (i.e. expanding it).
243: */
244: public void slideContextDown() {
245: contextPos++;
246:
247: if (contexts[contextPos] == null) {
248: // trying to slide to a context that was never created
249: contexts[contextPos] = contexts[contextPos - 1];
250: }
251:
252: currentContext = contexts[contextPos];
253: }
254:
255: ////////////////////////////////////////////////////////////////////
256: // Operations within a context.
257: ////////////////////////////////////////////////////////////////////
258:
259: /**
260: * Declare a Namespace prefix.
261: *
262: * <p>This method declares a prefix in the current Namespace
263: * context; the prefix will remain in force until this context
264: * is popped, unless it is shadowed in a descendant context.</p>
265: *
266: * <p>To declare a default Namespace, use the empty string. The
267: * prefix must not be "xml" or "xmlns".</p>
268: *
269: * <p>Note that you must <em>not</em> declare a prefix after
270: * you've pushed and popped another Namespace.</p>
271: *
272: * <p>Note that there is an asymmetry in this library: while {@link
273: * #getPrefix getPrefix} will not return the default "" prefix,
274: * even if you have declared one; to check for a default prefix,
275: * you have to look it up explicitly using {@link #getURI getURI}.
276: * This asymmetry exists to make it easier to look up prefixes
277: * for attribute names, where the default prefix is not allowed.</p>
278: *
279: * @param prefix The prefix to declare, or null for the empty
280: * string.
281: * @param uri The Namespace URI to associate with the prefix.
282: * @return true if the prefix was legal, false otherwise
283: * @see #processName
284: * @see #getURI
285: * @see #getPrefix
286: */
287: public boolean declarePrefix(String prefix, String uri) {
288: // bugfix#: 4989753
289: if ((prefix.equals("xml") && !uri
290: .equals(SOAPNamespaceConstants.XMLNS))
291: || prefix.equals("xmlns")) {
292: return false;
293: } else {
294: currentContext.declarePrefix(prefix, uri);
295: return true;
296: }
297: }
298:
299: /**
300: * Process a raw XML 1.0 name.
301: *
302: * <p>This method processes a raw XML 1.0 name in the current
303: * context by removing the prefix and looking it up among the
304: * prefixes currently declared. The return value will be the
305: * array supplied by the caller, filled in as follows:</p>
306: *
307: * <dl>
308: * <dt>parts[0]</dt>
309: * <dd>The Namespace URI, or an empty string if none is
310: * in use.</dd>
311: * <dt>parts[1]</dt>
312: * <dd>The local name (without prefix).</dd>
313: * <dt>parts[2]</dt>
314: * <dd>The original raw name.</dd>
315: * </dl>
316: *
317: * <p>All of the strings in the array will be internalized. If
318: * the raw name has a prefix that has not been declared, then
319: * the return value will be null.</p>
320: *
321: * <p>Note that attribute names are processed differently than
322: * element names: an unprefixed element name will received the
323: * default Namespace (if any), while an unprefixed element name
324: * will not.</p>
325: *
326: * @param qName The raw XML 1.0 name to be processed.
327: * @param parts An array supplied by the caller, capable of
328: * holding at least three members.
329: * @param isAttribute A flag indicating whether this is an
330: * attribute name (true) or an element name (false).
331: * @return The supplied array holding three internalized strings
332: * representing the Namespace URI (or empty string), the
333: * local name, and the raw XML 1.0 name; or null if there
334: * is an undeclared prefix.
335: * @see #declarePrefix
336: * @see java.lang.String#intern */
337: public String[] processName(String qName, String parts[],
338: boolean isAttribute) {
339: String myParts[] = currentContext.processName(qName,
340: isAttribute);
341: if (myParts == null) {
342: return null;
343: } else {
344: parts[0] = myParts[0];
345: parts[1] = myParts[1];
346: parts[2] = myParts[2];
347: return parts;
348: }
349: }
350:
351: /**
352: * Look up a prefix and get the currently-mapped Namespace URI.
353: *
354: * <p>This method looks up the prefix in the current context.
355: * Use the empty string ("") for the default Namespace.</p>
356: *
357: * @param prefix The prefix to look up.
358: * @return The associated Namespace URI, or null if the prefix
359: * is undeclared in this context.
360: * @see #getPrefix
361: * @see #getPrefixes
362: */
363: public String getURI(String prefix) {
364: return currentContext.getURI(prefix);
365: }
366:
367: /**
368: * Return an enumeration of all prefixes currently declared.
369: *
370: * <p><strong>Note:</strong> if there is a default prefix, it will not be
371: * returned in this enumeration; check for the default prefix
372: * using the {@link #getURI getURI} with an argument of "".</p>
373: *
374: * @return An enumeration of all prefixes declared in the
375: * current context except for the empty (default)
376: * prefix.
377: * @see #getDeclaredPrefixes
378: * @see #getURI
379: */
380: public Iterable<String> getPrefixes() {
381: return currentContext.getPrefixes();
382: }
383:
384: /**
385: * Return one of the prefixes mapped to a Namespace URI.
386: *
387: * <p>If more than one prefix is currently mapped to the same
388: * URI, this method will make an arbitrary selection; if you
389: * want all of the prefixes, use the {@link #getPrefixes}
390: * method instead.</p>
391: *
392: * <p><strong>Note:</strong> this will never return the empty (default) prefix;
393: * to check for a default prefix, use the {@link #getURI getURI}
394: * method with an argument of "".</p>
395: *
396: * @param uri The Namespace URI.
397: * @return One of the prefixes currently mapped to the URI supplied,
398: * or null if none is mapped or if the URI is assigned to
399: * the default Namespace.
400: * @see #getPrefixes(java.lang.String)
401: * @see #getURI
402: */
403: public String getPrefix(String uri) {
404: return currentContext.getPrefix(uri);
405: }
406:
407: /**
408: * Return an enumeration of all prefixes currently declared for a URI.
409: *
410: * <p>This method returns prefixes mapped to a specific Namespace
411: * URI. The xml: prefix will be included. If you want only one
412: * prefix that's mapped to the Namespace URI, and you don't care
413: * which one you get, use the {@link #getPrefix getPrefix}
414: * method instead.</p>
415: *
416: * <p><strong>Note:</strong> the empty (default) prefix is <em>never</em> included
417: * in this enumeration; to check for the presence of a default
418: * Namespace, use the {@link #getURI getURI} method with an
419: * argument of "".</p>
420: *
421: * @param uri The Namespace URI.
422: * @return An enumeration of all prefixes declared in the
423: * current context.
424: * @see #getPrefix
425: * @see #getDeclaredPrefixes
426: * @see #getURI
427: */
428: public Iterator getPrefixes(String uri) {
429: List prefixes = new ArrayList();
430: for (String prefix : getPrefixes()) {
431: if (uri.equals(getURI(prefix))) {
432: prefixes.add(prefix);
433: }
434: }
435: return prefixes.iterator();
436: }
437:
438: /**
439: * Return an enumeration of all prefixes declared in this context.
440: *
441: * <p>The empty (default) prefix will be included in this
442: * enumeration; note that this behaviour differs from that of
443: * {@link #getPrefix} and {@link #getPrefixes}.</p>
444: *
445: * @return An enumeration of all prefixes declared in this
446: * context.
447: * @see #getPrefixes
448: * @see #getURI
449: */
450: public Iterable<String> getDeclaredPrefixes() {
451: return currentContext.getDeclaredPrefixes();
452: }
453:
454: ////////////////////////////////////////////////////////////////////
455: // Internal state.
456: ////////////////////////////////////////////////////////////////////
457:
458: private Context contexts[];
459: private Context currentContext;
460: private int contextPos;
461:
462: ////////////////////////////////////////////////////////////////////
463: // Internal classes.
464: ////////////////////////////////////////////////////////////////////
465:
466: /**
467: * Internal class for a single Namespace context.
468: *
469: * <p>This module caches and reuses Namespace contexts, so the number
470: * allocated will be equal to the element depth of the document, not to the
471: * total number of elements (i.e. 5-10 rather than tens of thousands).</p>
472: */
473: final static class Context {
474:
475: /**
476: * Create the root-level Namespace context.
477: */
478: Context() {
479: copyTables();
480: }
481:
482: // PGB May 6 2002 added copy constructor
483: Context(Context that, Context newParent) {
484: if (that == null) {
485: copyTables();
486: return;
487: }
488:
489: if (newParent != null && !that.tablesDirty) {
490: prefixTable = that.prefixTable == that.parent.prefixTable ? newParent.prefixTable
491: : (HashMap) that.prefixTable.clone();
492:
493: uriTable = that.uriTable == that.parent.uriTable ? newParent.uriTable
494: : (HashMap) that.uriTable.clone();
495: elementNameTable = that.elementNameTable == that.parent.elementNameTable ? newParent.elementNameTable
496: : (HashMap) that.elementNameTable.clone();
497: attributeNameTable = that.attributeNameTable == that.parent.attributeNameTable ? newParent.attributeNameTable
498: : (HashMap) that.attributeNameTable.clone();
499: defaultNS = that.defaultNS == that.parent.defaultNS ? newParent.defaultNS
500: : that.defaultNS;
501: } else {
502: prefixTable = (HashMap) that.prefixTable.clone();
503: uriTable = (HashMap) that.uriTable.clone();
504: elementNameTable = (HashMap) that.elementNameTable
505: .clone();
506: attributeNameTable = (HashMap) that.attributeNameTable
507: .clone();
508: defaultNS = that.defaultNS;
509: }
510:
511: tablesDirty = that.tablesDirty;
512: parent = newParent;
513: declarations = that.declarations == null ? null
514: : (ArrayList) that.declarations.clone();
515: }
516:
517: /**
518: * (Re)set the parent of this Namespace context.
519: *
520: * @param parent The parent Namespace context object.
521: */
522: void setParent(Context parent) {
523: this .parent = parent;
524: declarations = null;
525: prefixTable = parent.prefixTable;
526: uriTable = parent.uriTable;
527: elementNameTable = parent.elementNameTable;
528: attributeNameTable = parent.attributeNameTable;
529: defaultNS = parent.defaultNS;
530: tablesDirty = false;
531: }
532:
533: /**
534: * Declare a Namespace prefix for this context.
535: *
536: * @param prefix The prefix to declare.
537: * @param uri The associated Namespace URI.
538: * @see org.xml.sax.helpers.NamespaceSupport#declarePrefix
539: */
540: void declarePrefix(String prefix, String uri) {
541: // Lazy processing...
542: if (!tablesDirty) {
543: copyTables();
544: }
545: if (declarations == null) {
546: declarations = new ArrayList();
547: }
548:
549: prefix = prefix.intern();
550: uri = uri.intern();
551: if ("".equals(prefix)) {
552: if ("".equals(uri)) {
553: defaultNS = null;
554: } else {
555: defaultNS = uri;
556: }
557: } else {
558: prefixTable.put(prefix, uri);
559: uriTable.put(uri, prefix); // may wipe out another prefix
560: }
561: declarations.add(prefix);
562: }
563:
564: /**
565: * Process a raw XML 1.0 name in this context.
566: *
567: * @param qName The raw XML 1.0 name.
568: * @param isAttribute true if this is an attribute name.
569: * @return An array of three strings containing the
570: * URI part (or empty string), the local part,
571: * and the raw name, all internalized, or null
572: * if there is an undeclared prefix.
573: * @see org.xml.sax.helpers.NamespaceSupport#processName
574: */
575: String[] processName(String qName, boolean isAttribute) {
576: String name[];
577: Map table;
578:
579: // Select the appropriate table.
580: if (isAttribute) {
581: table = elementNameTable;
582: } else {
583: table = attributeNameTable;
584: }
585:
586: // Start by looking in the cache, and
587: // return immediately if the name
588: // is already known in this content
589: name = (String[]) table.get(qName);
590: if (name != null) {
591: return name;
592: }
593:
594: // We haven't seen this name in this
595: // context before.
596: name = new String[3];
597: int index = qName.indexOf(':');
598:
599: // No prefix.
600: if (index == -1) {
601: if (isAttribute || defaultNS == null) {
602: name[0] = "";
603: } else {
604: name[0] = defaultNS;
605: }
606: name[1] = qName.intern();
607: name[2] = name[1];
608: }
609:
610: // Prefix
611: else {
612: String prefix = qName.substring(0, index);
613: String local = qName.substring(index + 1);
614: String uri;
615: if ("".equals(prefix)) {
616: uri = defaultNS;
617: } else {
618: uri = (String) prefixTable.get(prefix);
619: }
620: if (uri == null) {
621: return null;
622: }
623: name[0] = uri;
624: name[1] = local.intern();
625: name[2] = qName.intern();
626: }
627:
628: // Save in the cache for future use.
629: table.put(name[2], name);
630: tablesDirty = true;
631: return name;
632: }
633:
634: /**
635: * Look up the URI associated with a prefix in this context.
636: *
637: * @param prefix The prefix to look up.
638: * @return The associated Namespace URI, or null if none is
639: * declared.
640: * @see org.xml.sax.helpers.NamespaceSupport#getURI
641: */
642: String getURI(String prefix) {
643: if ("".equals(prefix)) {
644: return defaultNS;
645: } else if (prefixTable == null) {
646: return null;
647: } else {
648: return (String) prefixTable.get(prefix);
649: }
650: }
651:
652: /**
653: * Look up one of the prefixes associated with a URI in this context.
654: *
655: * <p>Since many prefixes may be mapped to the same URI,
656: * the return value may be unreliable.</p>
657: *
658: * @param uri The URI to look up.
659: * @return The associated prefix, or null if none is declared.
660: * @see org.xml.sax.helpers.NamespaceSupport#getPrefix
661: */
662: String getPrefix(String uri) {
663: if (uriTable == null) {
664: return null;
665: } else {
666: return (String) uriTable.get(uri);
667: }
668: }
669:
670: /**
671: * Return an enumeration of prefixes declared in this context.
672: *
673: * @return An enumeration of prefixes (possibly empty).
674: * @see org.xml.sax.helpers.NamespaceSupport#getDeclaredPrefixes
675: */
676: Iterable<String> getDeclaredPrefixes() {
677: if (declarations == null) {
678: return EMPTY_ENUMERATION;
679: } else {
680: return declarations;
681: }
682: }
683:
684: /**
685: * Return an enumeration of all prefixes currently in force.
686: *
687: * <p>The default prefix, if in force, is <em>not</em>
688: * returned, and will have to be checked for separately.</p>
689: *
690: * @return An enumeration of prefixes (never empty).
691: * @see org.xml.sax.helpers.NamespaceSupport#getPrefixes
692: */
693: Iterable<String> getPrefixes() {
694: if (prefixTable == null) {
695: return EMPTY_ENUMERATION;
696: } else {
697: return prefixTable.keySet();
698: }
699: }
700:
701: ////////////////////////////////////////////////////////////////
702: // Internal methods.
703: ////////////////////////////////////////////////////////////////
704:
705: /**
706: * Copy on write for the internal tables in this context.
707: *
708: * <p>This class is optimized for the normal case where most
709: * elements do not contain Namespace declarations.</p>
710: */
711: private void copyTables() {
712: if (prefixTable != null) {
713: prefixTable = (HashMap) prefixTable.clone();
714: } else {
715: prefixTable = new HashMap();
716: }
717: if (uriTable != null) {
718: uriTable = (HashMap) uriTable.clone();
719: } else {
720: uriTable = new HashMap();
721: }
722: elementNameTable = new HashMap();
723: attributeNameTable = new HashMap();
724: tablesDirty = true;
725: }
726:
727: ////////////////////////////////////////////////////////////////
728: // Protected state.
729: ////////////////////////////////////////////////////////////////
730:
731: HashMap prefixTable;
732: HashMap uriTable;
733: // PBG May 6 2002 changed these two from Map to HashMap
734: HashMap elementNameTable;
735: HashMap attributeNameTable;
736: String defaultNS = null;
737:
738: ////////////////////////////////////////////////////////////////
739: // Internal state.
740: ////////////////////////////////////////////////////////////////
741:
742: // PBG May 6 2002 changed this from List to ArrayList
743: private ArrayList declarations = null;
744: private boolean tablesDirty = false;
745: private Context parent = null;
746: }
747: }
|