001: /*--
002:
003: $Id: Namespace.java,v 1.2 2005/05/03 07:02:04 wittek Exp $
004:
005: Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
006: All rights reserved.
007:
008: Redistribution and use in source and binary forms, with or without
009: modification, are permitted provided that the following conditions
010: are met:
011:
012: 1. Redistributions of source code must retain the above copyright
013: notice, this list of conditions, and the following disclaimer.
014:
015: 2. Redistributions in binary form must reproduce the above copyright
016: notice, this list of conditions, and the disclaimer that follows
017: these conditions in the documentation and/or other materials
018: provided with the distribution.
019:
020: 3. The name "JDOM" must not be used to endorse or promote products
021: derived from this software without prior written permission. For
022: written permission, please contact <request_AT_jdom_DOT_org>.
023:
024: 4. Products derived from this software may not be called "JDOM", nor
025: may "JDOM" appear in their name, without prior written permission
026: from the JDOM Project Management <request_AT_jdom_DOT_org>.
027:
028: In addition, we request (but do not require) that you include in the
029: end-user documentation provided with the redistribution and/or in the
030: software itself an acknowledgement equivalent to the following:
031: "This product includes software developed by the
032: JDOM Project (http://www.jdom.org/)."
033: Alternatively, the acknowledgment may be graphical using the logos
034: available at http://www.jdom.org/images/logos.
035:
036: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
040: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: SUCH DAMAGE.
048:
049: This software consists of voluntary contributions made by many
050: individuals on behalf of the JDOM Project and was originally
051: created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
052: Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
053: on the JDOM Project, please see <http://www.jdom.org/>.
054:
055: */
056:
057: package org.jdom;
058:
059: import java.util.*;
060:
061: /**
062: * An XML namespace representation, as well as a factory for creating XML
063: * namespace objects. Namespaces are not Serializable, however objects that use
064: * namespaces have special logic to handle serialization manually. These classes
065: * call the getNamespace() method on deserialization to ensure there is one
066: * unique Namespace object for any unique prefix/uri pair.
067: *
068: * @version $Revision: 1.2 $, $Date: 2005/05/03 07:02:04 $
069: * @author Brett McLaughlin
070: * @author Elliotte Rusty Harold
071: * @author Jason Hunter
072: * @author Wesley Biggs
073: */
074: public final class Namespace {
075:
076: // XXX May want to use weak references to keep the maps from growing
077: // large with extended use
078:
079: // XXX We may need to make the namespaces HashMap synchronized with
080: // reader/writer locks or perhaps make Namespace no longer a flyweight.
081: // As written, multiple put() calls may happen from different threads
082: // concurrently and cause a ConcurrentModificationException. See
083: // http://lists.denveronline.net/lists/jdom-interest/2000-September/003009.html.
084: // No one has ever reported this over the many years, so don't worry yet.
085:
086: private static final String CVS_ID = "@(#) $RCSfile: Namespace.java,v $ $Revision: 1.2 $ $Date: 2005/05/03 07:02:04 $ $Name: $";
087:
088: /**
089: * Factory list of namespaces.
090: * Keys are <i>prefix</i>&<i>URI</i>.
091: * Values are Namespace objects
092: */
093: private static HashMap namespaces;
094:
095: /** Define a <code>Namespace</code> for when <i>not</i> in a namespace */
096: public static final Namespace NO_NAMESPACE = new Namespace("", "");
097:
098: /** Define a <code>Namespace</code> for the standard xml prefix. */
099: public static final Namespace XML_NAMESPACE = new Namespace("xml",
100: "http://www.w3.org/XML/1998/namespace");
101:
102: /** The prefix mapped to this namespace */
103: private String prefix;
104:
105: /** The URI for this namespace */
106: private String uri;
107:
108: /**
109: * This static initializer acts as a factory contructor.
110: * It sets up storage and required initial values.
111: */
112: static {
113: namespaces = new HashMap();
114:
115: // Add the "empty" namespace
116: namespaces.put("&", NO_NAMESPACE);
117: namespaces.put("xml&http://www.w3.org/XML/1998/namespace",
118: XML_NAMESPACE);
119: }
120:
121: /**
122: * This will retrieve (if in existence) or create (if not) a
123: * <code>Namespace</code> for the supplied prefix and URI.
124: *
125: * @param prefix <code>String</code> prefix to map to
126: * <code>Namespace</code>.
127: * @param uri <code>String</code> URI of new <code>Namespace</code>.
128: * @return <code>Namespace</code> - ready to use namespace.
129: * @throws IllegalNameException if the given prefix and uri make up
130: * an illegal namespace name.
131: */
132: public static Namespace getNamespace(String prefix, String uri) {
133: // Sanity checking
134: if ((prefix == null) || (prefix.trim().equals(""))) {
135: prefix = "";
136: }
137: if ((uri == null) || (uri.trim().equals(""))) {
138: uri = "";
139: }
140:
141: // Return existing namespace if found. The preexisting namespaces
142: // should all be legal. In other words, an illegal namespace won't
143: // have been placed in this. Thus we can do this test before
144: // verifying the URI and prefix.
145: String lookup = new StringBuffer(64).append(prefix).append('&')
146: .append(uri).toString();
147: Namespace preexisting = (Namespace) namespaces.get(lookup);
148: if (preexisting != null) {
149: return preexisting;
150: }
151:
152: // Ensure proper naming
153: String reason;
154: if ((reason = Verifier.checkNamespacePrefix(prefix)) != null) {
155: throw new IllegalNameException(prefix, "Namespace prefix",
156: reason);
157: }
158: if ((reason = Verifier.checkNamespaceURI(uri)) != null) {
159: throw new IllegalNameException(uri, "Namespace URI", reason);
160: }
161:
162: // Unless the "empty" Namespace (no prefix and no URI), require a URI
163: if ((!prefix.equals("")) && (uri.equals(""))) {
164: throw new IllegalNameException("", "namespace",
165: "Namespace URIs must be non-null and non-empty Strings");
166: }
167:
168: // Handle XML namespace mislabels. If the user requested the correct
169: // namespace and prefix -- xml, http://www.w3.org/XML/1998/namespace
170: // -- then it was already returned from the preexisting namespaces.
171: // Thus any use of the xml prefix or the
172: // http://www.w3.org/XML/1998/namespace URI at this point must be
173: // incorrect.
174: if (prefix.equals("xml")) {
175: throw new IllegalNameException(prefix, "Namespace prefix",
176: "The xml prefix can only be bound to "
177: + "http://www.w3.org/XML/1998/namespace");
178: }
179: // The erratum to Namespaces in XML 1.0 that suggests this
180: // next check is controversial. NotElement everyone accepts it.
181: if (uri.equals("http://www.w3.org/XML/1998/namespace")) {
182: throw new IllegalNameException(uri, "Namespace URI",
183: "The http://www.w3.org/XML/1998/namespace must be bound to "
184: + "the xml prefix.");
185: }
186:
187: // Finally, store and return
188: Namespace ns = new Namespace(prefix, uri);
189: namespaces.put(lookup, ns);
190: return ns;
191: }
192:
193: /**
194: * This will retrieve (if in existence) or create (if not) a
195: * <code>Namespace</code> for the supplied URI, and make it usable
196: * as a default namespace, as no prefix is supplied.
197: *
198: * @param uri <code>String</code> URI of new <code>Namespace</code>.
199: * @return <code>Namespace</code> - ready to use namespace.
200: */
201: public static Namespace getNamespace(String uri) {
202: return getNamespace("", uri);
203: }
204:
205: /**
206: * This constructor handles creation of a <code>Namespace</code> object
207: * with a prefix and URI; it is intentionally left <code>private</code>
208: * so that it cannot be invoked by external programs/code.
209: *
210: * @param prefix <code>String</code> prefix to map to this namespace.
211: * @param uri <code>String</code> URI for namespace.
212: */
213: private Namespace(String prefix, String uri) {
214: this .prefix = prefix;
215: this .uri = uri;
216: }
217:
218: /**
219: * This returns the prefix mapped to this <code>Namespace</code>.
220: *
221: * @return <code>String</code> - prefix for this <code>Namespace</code>.
222: */
223: public String getPrefix() {
224: return prefix;
225: }
226:
227: /**
228: * This returns the namespace URI for this <code>Namespace</code>.
229: *
230: * @return <code>String</code> - URI for this <code>Namespace</code>.
231: */
232: public String getURI() {
233: return uri;
234: }
235:
236: /**
237: * This tests for equality - Two <code>Namespaces</code>
238: * are equal if and only if their URIs are byte-for-byte equals.
239: *
240: * @param ob <code>Object</code> to compare to this <code>Namespace</code>.
241: * @return <code>boolean</code> - whether the supplied object is equal to
242: * this <code>Namespace</code>.
243: */
244: public boolean equals(Object ob) {
245: if (this == ob) {
246: return true;
247: }
248: if (ob instanceof Namespace) { // instanceof returns false if null
249: return uri.equals(((Namespace) ob).uri);
250: }
251: return false;
252: }
253:
254: /**
255: * This returns a <code>String</code> representation of this
256: * <code>Namespace</code>, suitable for use in debugging.
257: *
258: * @return <code>String</code> - information about this instance.
259: */
260: public String toString() {
261: return "[Namespace: prefix \"" + prefix
262: + "\" is mapped to URI \"" + uri + "\"]";
263: }
264:
265: /**
266: * This returns a probably unique hash code for the <code>Namespace</code>.
267: * If two namespaces have the same URI, they are equal and have the same
268: * hash code, even if they have different prefixes.
269: *
270: * @return <code>int</code> - hash code for this <code>Namespace</code>.
271: */
272: public int hashCode() {
273: return uri.hashCode();
274: }
275: }
|