001: /**
002: * Redistribution and use of this software and associated documentation
003: * ("Software"), with or without modification, are permitted provided
004: * that the following conditions are met:
005: *
006: * 1. Redistributions of source code must retain copyright
007: * statements and notices. Redistributions must also contain a
008: * copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the
011: * above copyright notice, this list of conditions and the
012: * following disclaimer in the documentation and/or other
013: * materials provided with the distribution.
014: *
015: * 3. The name "Exolab" must not be used to endorse or promote
016: * products derived from this Software without prior written
017: * permission of Intalio, Inc. For written permission,
018: * please contact info@exolab.org.
019: *
020: * 4. Products derived from this Software may not be called "Exolab"
021: * nor may "Exolab" appear in their names without prior written
022: * permission of Intalio, Inc. Exolab is a registered
023: * trademark of Intalio, Inc.
024: *
025: * 5. Due credit should be given to the Exolab Project
026: * (http://www.exolab.org/).
027: *
028: * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
029: * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
030: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
031: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
032: * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
033: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
035: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
036: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
037: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
038: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
039: * OF THE POSSIBILITY OF SUCH DAMAGE.
040: *
041: * Copyright 2001-2003 (C) Intalio, Inc. All Rights Reserved.
042: *
043: * $Id: Namespaces.java 5951 2006-05-30 22:18:48Z bsnyder $
044: */package org.exolab.castor.xml;
045:
046: import org.xml.sax.ContentHandler;
047: import org.xml.sax.helpers.AttributeListImpl;
048: import org.xml.sax.SAXException;
049:
050: import java.util.Enumeration;
051: import java.util.Vector;
052:
053: /**
054: * A class for handling Namespace declaration and scoping
055: *
056: * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
057: * @version $Revision: 5951 $ $Date: 2004-09-09 23:04:08 -0600 (Thu, 09 Sep 2004) $
058: **/
059: public final class Namespaces {
060:
061: /**
062: * The reserved XML Namespace Prefix
063: */
064: public static final String XML_NAMESPACE_PREFIX = "xml";
065:
066: /**
067: * The reserved XML 1.0 Namespace URI
068: */
069: public static final String XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";
070:
071: /**
072: * The first namespace in this set of Namespaces
073: **/
074: private Namespace _first = null;
075:
076: /**
077: * The last namespace in this set of Namespaces
078: **/
079: private Namespace _last = null;
080:
081: private Namespaces _parent = null;
082:
083: /**
084: * The CDATA type..uses for SAX attributes
085: */
086: private static final String CDATA = "CDATA";
087:
088: /**
089: * The namespace declaration String
090: **/
091: private static final String XMLNS = "xmlns";
092:
093: /**
094: * Creates a new Namespaces instance
095: **/
096: public Namespaces() {
097: super ();
098: } //-- Namespaces
099:
100: /**
101: * Creates a new Namespaces instance
102: **/
103: public Namespaces(Namespaces parent) {
104: super ();
105: _parent = parent;
106: } //-- Namespaces
107:
108: /**
109: * Adds the given namespace declaration to this Namespaces
110: *
111: * @param prefix the namespace prefix
112: * @param uri the namespace URI to be associated with the given prefix
113: **/
114: public synchronized void addNamespace(String prefix, String uri) {
115:
116: if (uri == null) {
117: throw new IllegalArgumentException(
118: "Namespace URI must not be null");
119: }
120:
121: //-- adjust prefix to prevent null value
122: if (prefix == null)
123: prefix = "";
124:
125: //-- Make sure prefix is not equal to "xml"
126: if (XML_NAMESPACE_PREFIX.equalsIgnoreCase(prefix)) {
127: if (!XML_NAMESPACE.equals(uri)) {
128: String err = "The prefix 'xml' is reserved (XML 1.0 Specification) "
129: + "and cannot be declared.";
130: throw new IllegalArgumentException(err);
131: }
132: //-- if we make it here, just ignore it (it's already supported internally)
133: return;
134: }
135: //-- make sure URI is not equal to the XML 1.0 namespace
136: else if (XML_NAMESPACE.equals(uri)) {
137: String err = "The namespace '" + XML_NAMESPACE;
138: err += "' is reserved (XML 1.0 Specification) and cannot be declared.";
139: throw new IllegalArgumentException(err);
140: }
141:
142: if (_first == null) {
143: _first = new Namespace(prefix, uri);
144: _last = _first;
145: }
146:
147: //-- check for existing namespace declaration for
148: //-- prefix
149: else {
150: boolean found = false;
151: Namespace ns = _first;
152: while (ns != null) {
153: if (ns.prefix.equals(prefix)) {
154: found = true;
155: ns.uri = uri;
156: break;
157: }
158: ns = ns.next;
159: }
160: if (!found) {
161: _last.next = new Namespace(prefix, uri);
162: _last = _last.next;
163: }
164: }
165: } //-- method: addNamespace
166:
167: /**
168: * Creates a new Namespaces instance with this Namespaces as the parent
169: **/
170: public Namespaces createNamespaces() {
171: return new Namespaces(this );
172: } //-- method: createNamespaces
173:
174: /**
175: * Returns an Enumeration of local namespace URIs for this Namespaces.
176: *
177: * @return an Enumeration of local namespace URIs.
178: **/
179: public Enumeration getLocalNamespaces() {
180: return new NamespaceEnumerator(_first);
181: } //-- getLocalNamespace
182:
183: /**
184: * Returns the Namespace URI associated with the given prefix
185: *
186: * @param prefix the namespace prefix to lookup
187: * @return the namespace URI associated with the given prefix
188: **/
189: public String getNamespaceURI(String prefix) {
190: //-- adjust prefix to prevent null value
191: if (prefix == null)
192: prefix = "";
193:
194: Namespace ns = _first;
195:
196: while (ns != null) {
197: if (ns.prefix.equals(prefix)) {
198: return ns.uri;
199: }
200: ns = ns.next;
201: }
202:
203: if (_parent != null) {
204: return _parent.getNamespaceURI(prefix);
205: }
206:
207: //-- handle built-in namespace URIs
208: if (XML_NAMESPACE_PREFIX.equals(prefix)) {
209: return XML_NAMESPACE;
210: }
211:
212: return null;
213:
214: } //-- method: getNamespaceURI
215:
216: /**
217: * Returns the Namespace prefix associated with the given URI.
218: * If multiple namespace prefixes have been declared, then
219: * the first one found is returned. To obtain all prefixes see
220: * <code>#getNamespacePrefixes</code>.
221: *
222: * @param nsURI the namespace URI to lookup
223: * @return the namespace prefix associated with the given URI
224: **/
225: public String getNamespacePrefix(String nsURI) {
226: //-- prevent null value
227: if (nsURI == null)
228: throw new IllegalArgumentException(
229: "Namespace URI must not be null.");
230:
231: Namespace ns = _first;
232: while (ns != null) {
233: if (ns.uri.equals(nsURI)) {
234: return ns.prefix;
235: }
236: ns = ns.next;
237: }
238:
239: if (_parent != null) {
240: return _parent.getNamespacePrefix(nsURI);
241: }
242:
243: //-- handle built-in namespace prefixes
244: if (XML_NAMESPACE.equals(nsURI)) {
245: return XML_NAMESPACE_PREFIX;
246: }
247:
248: return null;
249:
250: } //-- method: getNamespacePrefix
251:
252: /**
253: * Returns all namespace prefixes declared locally
254: *
255: * @return an Enumeration of locally declared namespace prefixes
256: */
257: public Enumeration getLocalNamespacePrefixes() {
258: return new NamespaceEnumerator(_first,
259: NamespaceEnumerator.PREFIX);
260: } //-- method: getLocalNamespacePrefixes
261:
262: /**
263: * Returns all namespace prefixes associated with the given URI,
264: * including those from parent scopes.
265: *
266: * @param nsURI the namespace URI to lookup
267: * @return the namespace prefixes associated with the given URI
268: **/
269: public String[] getNamespacePrefixes(String nsURI) {
270: return getNamespacePrefixes(nsURI, false);
271: } //-- method: getNamespacePrefixes
272:
273: /**
274: * Returns the Namespace prefixes associated with the given URI.
275: *
276: * @param nsURI the namespace URI to lookup
277: * @param local a boolean that when true indicates only the local
278: * scope is searched.
279: * @return the namespace prefixes associated with the given URI
280: **/
281: public String[] getNamespacePrefixes(String nsURI, boolean local) {
282: //-- prevent null value
283: if (nsURI == null)
284: throw new IllegalArgumentException(
285: "Namespace URI must not be null.");
286:
287: Vector prefixes = new Vector(3);
288: getNamespacePrefixes(nsURI, local, prefixes);
289:
290: String[] pArray = new String[prefixes.size()];
291: prefixes.copyInto(pArray);
292: return pArray;
293:
294: } //-- method: getNamespacePrefixes
295:
296: /**
297: * Returns the Namespace prefix associated with the given URI.
298: * Or null if no prefix has been declared. This method will
299: * ignore the default namespace. This is useful when dealing
300: * with attributes that do not use the default namespace.
301: *
302: * @param nsURI the namespace URI to lookup
303: * @return the namespace prefix associated with the given URI
304: **/
305: public String getNonDefaultNamespacePrefix(String nsURI) {
306: //-- adjust prefix to prevent null value
307: if (nsURI == null)
308: throw new IllegalArgumentException(
309: "Namespace URI must not be null.");
310:
311: Namespace ns = _first;
312: while (ns != null) {
313: if (ns.uri.equals(nsURI)) {
314: if (ns.prefix.length() > 0) {
315: return ns.prefix;
316: }
317: }
318: ns = ns.next;
319: }
320:
321: if (_parent != null) {
322: return _parent.getNonDefaultNamespacePrefix(nsURI);
323: }
324:
325: //-- handle built-in namespace prefixes
326: if (XML_NAMESPACE.equals(nsURI)) {
327: return XML_NAMESPACE_PREFIX;
328: }
329:
330: return null;
331:
332: } //-- method: getNonDefaultNamespacePrefix
333:
334: /**
335: * Returns the parent Namespaces for this Namespaces instance.
336: *
337: * @return the parent Namespaces for this Namespaces instance.
338: **/
339: public Namespaces getParent() {
340: return _parent;
341: } //-- method: getParent
342:
343: /**
344: * Removes the namespace declaration for the given prefix.
345: * This is a local action only, the namespace declaration
346: * will not be removed from any parent Namespaces object.
347: *
348: * @param prefix the namespace prefix to remove the binding of
349: * @return true if the namespace declaration was removed,
350: * otherwise false.
351: */
352: public synchronized boolean removeNamespace(String prefix) {
353: if (prefix == null)
354: return false;
355:
356: Namespace ns = _first;
357: Namespace previous = null;
358:
359: while (ns != null) {
360: if (ns.prefix.equals(prefix)) {
361: if (ns == _first) {
362: _first = _first.next;
363: if (_last == ns) {
364: _last = null;
365: }
366: } else {
367: previous.next = ns.next;
368: if (_last == ns) {
369: _last = previous;
370: }
371: }
372: return true;
373: }
374: previous = ns;
375: ns = ns.next;
376: }
377:
378: return false;
379:
380: } //-- method: removeNamespace
381:
382: /**
383: * Sets the parent Namespaces for this Namespaces instance.
384: *
385: * @param namespaces the parent Namespaces
386: **/
387: public void setParent(Namespaces namespaces) {
388: _parent = namespaces;
389: } //-- method: setParent
390:
391: /**
392: * Calls the given ContentHandler's endPrefixMapping method
393: * for each locally declared namespace
394: *
395: * @param handler the ContentHandler
396: */
397: public void sendEndEvents(ContentHandler handler)
398: throws SAXException {
399: Namespace ns = _first;
400: while (ns != null) {
401: handler.endPrefixMapping(ns.prefix);
402: ns = ns.next;
403: }
404: } //-- sendEndEvents
405:
406: /**
407: * Calls the given ContentHandler's startPrefixMapping method
408: * for each locally declared namespace
409: *
410: * @param handler the ContentHandler
411: */
412: public void sendStartEvents(ContentHandler handler)
413: throws SAXException {
414: Namespace ns = _first;
415: while (ns != null) {
416: handler.startPrefixMapping(ns.prefix, ns.uri);
417: ns = ns.next;
418: }
419: } //-- sendStartEvents
420:
421: /**
422: * Declare the namespaces of this stack in as attributes.
423: * @param atts the Attribute List to fill in.
424: */
425: public void declareAsAttributes(AttributeListImpl atts,
426: boolean localOnly) {
427:
428: Namespace ns = _first;
429: String attName = null;
430: while (ns != null) {
431: if (ns.prefix != null) {
432: int len = ns.prefix.length();
433: if (len > 0) {
434: StringBuffer buf = new StringBuffer(6 + len);
435: buf.append(XMLNS);
436: buf.append(':');
437: buf.append(ns.prefix);
438: attName = buf.toString();
439: atts.addAttribute(attName, CDATA, ns.uri);
440: }
441: //case with no prefix but a nsURI
442: else {
443: atts.addAttribute(XMLNS, CDATA, ns.uri);
444: }
445: } //ns.prefix!=null
446: else {
447: atts.addAttribute(XMLNS, CDATA, ns.uri);
448: }
449:
450: ns = ns.next;
451: }
452:
453: if ((!localOnly) && (_parent != null)) {
454: _parent.declareAsAttributes(atts, false);
455: }
456: } //method:declareAsAttributes
457:
458: /**
459: * Adds the namespace prefixes associated with the given URI to the
460: * given Vector.
461: *
462: * @param nsURI the namespace URI to lookup
463: * @param local a boolean that when true indicates only the local
464: * scope is searched.
465: * @param prefixes the Vector to add the prefixes to
466: **/
467: private void getNamespacePrefixes(String nsURI, boolean local,
468: Vector prefixes) {
469:
470: Namespace ns = _first;
471: while (ns != null) {
472: if (ns.uri.equals(nsURI)) {
473: prefixes.addElement(ns.prefix);
474: }
475: ns = ns.next;
476: }
477:
478: if ((_parent != null) && (!local)) {
479: _parent.getNamespacePrefixes(nsURI, local, prefixes);
480: }
481:
482: } //-- method: getNamespacePrefixes
483:
484: /**
485: * An internal class used to represent a namespace
486: **/
487: class Namespace {
488:
489: String prefix = null;
490: String uri = null;
491:
492: Namespace next = null;
493:
494: Namespace() {
495: super ();
496: }
497:
498: Namespace(String prefix, String uri) {
499: this .prefix = prefix;
500: this .uri = uri;
501: }
502: } //-- class: Namespace
503:
504: /**
505: * A simple Enumeration for Namespace objects
506: */
507: static class NamespaceEnumerator implements java.util.Enumeration {
508: public static final int URI = 0;
509: public static final int PREFIX = 1;
510:
511: private Namespace _namespace = null;
512: private int _returnType = URI;
513:
514: NamespaceEnumerator(Namespace namespace) {
515: _namespace = namespace;
516: }
517:
518: NamespaceEnumerator(Namespace namespace, int returnType) {
519: _namespace = namespace;
520: _returnType = returnType;
521: }
522:
523: public boolean hasMoreElements() {
524: return (_namespace != null);
525: }
526:
527: public Object nextElement() {
528: String obj = null;
529: if (_namespace != null) {
530: if (_returnType == URI)
531: obj = _namespace.uri;
532: else
533: obj = _namespace.prefix;
534: _namespace = _namespace.next;
535: }
536: return obj;
537: }
538:
539: } //-- class: NamespaceEnumerator
540:
541: } //-- class: Namespaces
|