1. // $Id: QName.java,v 1.10 2004/02/09 23:41:21 jsuttor Exp $
  2. /*
  3. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  4. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  5. */
  6. package javax.xml.namespace;
  7. import java.io.Serializable;
  8. import javax.xml.XMLConstants;
  9. /**
  10. * <p><code>QName</code> represents a <strong>qualified name</strong>
  11. * as defined in the XML specifications: <a
  12. * href="http://www.w3.org/TR/xmlschema-2/#QName">XML Schema Part2:
  13. * Datatypes specification</a>, <a
  14. * href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">Namespaces
  15. * in XML</a>, <a
  16. * href="http://www.w3.org/XML/xml-names-19990114-errata">Namespaces
  17. * in XML Errata</a>.</p>
  18. *
  19. * <p>The value of a <code>QName</code> contains a <strong>Namespace
  20. * URI</strong>, <strong>local part</strong> and
  21. * <strong>prefix</strong>.</p>
  22. *
  23. * <p>The prefix is included in <code>QName</code> to retain lexical
  24. * information <strong><em>when present</em></strong> in an {@link
  25. * javax.xml.transform.Source XML input source}. The prefix is
  26. * <strong><em>NOT</em></strong> used in {@link #equals(Object)
  27. * QName.equals(Object)} or to compute the {@link #hashCode()
  28. * QName.hashCode()}. Equality and the hash code are defined using
  29. * <strong><em>only</em></strong> the Namespace URI and local part.</p>
  30. *
  31. * <p>If not specified, the Namespace URI is set to {@link
  32. * javax.xml.XMLConstants#NULL_NS_URI XMLConstants.NULL_NS_URI}.
  33. * If not specified, the prefix is set to {@link
  34. * javax.xml.XMLConstants#DEFAULT_NS_PREFIX
  35. * XMLConstants.DEFAULT_NS_PREFIX}.</p>
  36. *
  37. * <p><code>QName</code> is immutable.</p>
  38. *
  39. * @author <a href="mailto:Jeff.Suttor@Sun.com">Jeff Suttor</a>
  40. * @version $Revision: 1.10 $, $Date: 2004/02/09 23:41:21 $
  41. * @see <a href="http://www.w3.org/TR/xmlschema-2/#QName">XML Schema Part2: Datatypes specification</a>
  42. * @see <a href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">Namespaces in XML</a>
  43. * @see <a href="http://www.w3.org/XML/xml-names-19990114-errata">Namespaces in XML Errata</a>
  44. * @since 1.5
  45. */
  46. public class QName implements Serializable {
  47. /**
  48. * <p>Stream Unique Identifier.</p>
  49. */
  50. private static final long serialVersionUID = 4418622981026545151L;
  51. /**
  52. * <p>Namespace URI of this <code>QName</code>.</p>
  53. */
  54. private final String namespaceURI;
  55. /**
  56. * <p>local part of this <code>QName</code>.</p>
  57. */
  58. private final String localPart;
  59. /**
  60. * <p>prefix of this <code>QName</code>.</p>
  61. */
  62. private final String prefix;
  63. /**
  64. * <p><code>QName</code> constructor specifying the Namespace URI
  65. * and local part.</p>
  66. *
  67. * <p>If the Namespace URI is <code>null</code>, it is set to
  68. * {@link javax.xml.XMLConstants#NULL_NS_URI
  69. * XMLConstants.NULL_NS_URI}. This value represents no
  70. * explicitly defined Namespace as defined by the <a
  71. * href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">Namespaces
  72. * in XML</a> specification. This action preserves compatible
  73. * behavior with QName 1.0. Explicitly providing the {@link
  74. * javax.xml.XMLConstants#NULL_NS_URI
  75. * XMLConstants.NULL_NS_URI} value is the preferred coding
  76. * style.</p>
  77. *
  78. * <p>If the local part is <code>null</code> an
  79. * <code>IllegalArgumentException</code> is thrown.
  80. * A local part of "" is allowed to preserve
  81. * compatible behavior with QName 1.0. </p>
  82. *
  83. * <p>When using this constructor, the prefix is set to {@link
  84. * javax.xml.XMLConstants#DEFAULT_NS_PREFIX
  85. * XMLConstants.DEFAULT_NS_PREFIX}.</p>
  86. *
  87. * <p>The Namespace URI is not validated as a
  88. * <a href="http://www.ietf.org/rfc/rfc2396.txt">URI reference</a>.
  89. * The local part is not validated as a
  90. * <a href="http://www.w3.org/TR/REC-xml-names/#NT-NCName">NCName</a>
  91. * as specified in <a href="http://www.w3.org/TR/REC-xml-names/">Namespaces
  92. * in XML</a>.</p>
  93. *
  94. * @param namespaceURI Namespace URI of the <code>QName</code>
  95. * @param localPart local part of the <code>QName</code>
  96. *
  97. * @see #QName(String namespaceURI, String localPart, String
  98. * prefix) QName(String namespaceURI, String localPart, String
  99. * prefix)
  100. */
  101. public QName(final String namespaceURI, final String localPart) {
  102. this(namespaceURI, localPart, XMLConstants.DEFAULT_NS_PREFIX);
  103. }
  104. /**
  105. * <p><code>QName</code> constructor specifying the Namespace URI,
  106. * local part and prefix.</p>
  107. *
  108. * <p>If the Namespace URI is <code>null</code>, it is set to
  109. * {@link javax.xml.XMLConstants#NULL_NS_URI
  110. * XMLConstants.NULL_NS_URI}. This value represents no
  111. * explicitly defined Namespace as defined by the <a
  112. * href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">Namespaces
  113. * in XML</a> specification. This action preserves compatible
  114. * behavior with QName 1.0. Explicitly providing the {@link
  115. * javax.xml.XMLConstants#NULL_NS_URI
  116. * XMLConstants.NULL_NS_URI} value is the preferred coding
  117. * style.</p>
  118. *
  119. * <p>If the local part is <code>null</code> an
  120. * <code>IllegalArgumentException</code> is thrown.
  121. * A local part of "" is allowed to preserve
  122. * compatible behavior with QName 1.0. </p>
  123. *
  124. * <p>If the prefix is <code>null</code>, an
  125. * <code>IllegalArgumentException</code> is thrown. Use {@link
  126. * javax.xml.XMLConstants#DEFAULT_NS_PREFIX
  127. * XMLConstants.DEFAULT_NS_PREFIX} to explicitly indicate that no
  128. * prefix is present or the prefix is not relevant.</p>
  129. *
  130. * <p>The Namespace URI is not validated as a
  131. * <a href="http://www.ietf.org/rfc/rfc2396.txt">URI reference</a>.
  132. * The local part and prefix are not validated as a
  133. * <a href="http://www.w3.org/TR/REC-xml-names/#NT-NCName">NCName</a>
  134. * as specified in <a href="http://www.w3.org/TR/REC-xml-names/">Namespaces
  135. * in XML</a>.</p>
  136. *
  137. * @param namespaceURI Namespace URI of the <code>QName<code>
  138. * @param localPart local part of the <code>QName<code>
  139. * @param prefix prefix of the <code>QName<code>
  140. */
  141. public QName(String namespaceURI, String localPart, String prefix) {
  142. // map null Namespace URI to default to preserve compatibility with QName 1.0
  143. if (namespaceURI == null) {
  144. this.namespaceURI = XMLConstants.NULL_NS_URI;
  145. } else {
  146. this.namespaceURI = namespaceURI;
  147. }
  148. // local part is required. "" is allowed to preserve compatibility with QName 1.0
  149. if (localPart == null) {
  150. throw new IllegalArgumentException("local part cannot be \"null\" when creating a QName");
  151. }
  152. this.localPart = localPart;
  153. // prefix is required
  154. if (prefix == null) {
  155. throw new IllegalArgumentException("prefix cannot be \"null\" when creating a QName");
  156. }
  157. this.prefix = prefix;
  158. }
  159. /**
  160. * <p><code>QName</code> constructor specifying the local part.</p>
  161. *
  162. * <p>If the local part is <code>null</code> an
  163. * <code>IllegalArgumentException</code> is thrown.
  164. * A local part of "" is allowed to preserve
  165. * compatible behavior with QName 1.0. </p>
  166. *
  167. * <p>When using this constructor, the Namespace URI is set to
  168. * {@link javax.xml.XMLConstants#NULL_NS_URI
  169. * XMLConstants.NULL_NS_URI} and the prefix is set to {@link
  170. * javax.xml.XMLConstants#DEFAULT_NS_PREFIX
  171. * XMLConstants.DEFAULT_NS_PREFIX}.</p>
  172. *
  173. * <p><em>In an XML context, all Element and Attribute names exist
  174. * in the context of a Namespace. Making this explicit during the
  175. * construction of a <code>QName</code> helps prevent hard to
  176. * diagnosis XML validity errors. The constructors {@link
  177. * #QName(String namespaceURI, String localPart) QName(String
  178. * namespaceURI, String localPart)} and
  179. * {@link #QName(String namespaceURI, String localPart, String prefix)}
  180. * are preferred.</em></p>
  181. *
  182. * <p>The local part is not validated as a
  183. * <a href="http://www.w3.org/TR/REC-xml-names/#NT-NCName">NCName</a>
  184. * as specified in <a href="http://www.w3.org/TR/REC-xml-names/">Namespaces
  185. * in XML</a>.</p>
  186. *
  187. * @param localPart local part of the <code>QName</code>
  188. * @see #QName(String namespaceURI, String localPart) QName(String
  189. * namespaceURI, String localPart)
  190. * @see #QName(String namespaceURI, String localPart, String
  191. * prefix) QName(String namespaceURI, String localPart, String
  192. * prefix)
  193. */
  194. public QName(String localPart) {
  195. this(
  196. XMLConstants.NULL_NS_URI,
  197. localPart,
  198. XMLConstants.DEFAULT_NS_PREFIX);
  199. }
  200. /**
  201. * <p>Get the Namespace URI of this <code>QName</code>.</p>
  202. *
  203. * @return Namespace URI of this <code>QName</code>
  204. */
  205. public String getNamespaceURI() {
  206. return namespaceURI;
  207. }
  208. /**
  209. * <p>Get the local part of this <code>QName</code>.</p>
  210. *
  211. * @return local part of this <code>QName</code>
  212. */
  213. public String getLocalPart() {
  214. return localPart;
  215. }
  216. /**
  217. * <p>Get the prefix of this <code>QName</code>.</p>
  218. *
  219. * <p>The prefix assigned to a <code>QName</code> might
  220. * <strong><em>NOT</em></strong> be valid in a different
  221. * context. For example, a <code>QName</code> may be assigned a
  222. * prefix in the context of parsing a document but that prefix may
  223. * be invalid in the context of a different document.</p>
  224. *
  225. * @return prefix of this <code>QName</code>
  226. */
  227. public String getPrefix() {
  228. return prefix;
  229. }
  230. /**
  231. * <p>Test this <code>QName</code> for equality with another
  232. * <code>Object</code>.</p>
  233. *
  234. * <p>If the <code>Object</code> to be tested is not a
  235. * <code>QName</code> or is <code>null</code>, then this method
  236. * returns <code>false</code>.</p>
  237. *
  238. * <p>Two <code>QName</code>s are considered equal if and only if
  239. * both the Namespace URI and local part are equal. This method
  240. * uses <code>String.equals()</code> to check equality of the
  241. * Namespace URI and local part. The prefix is
  242. * <strong><em>NOT</em></strong> used to determine equality.</p>
  243. *
  244. * <p>This method satisfies the general contract of {@link
  245. * java.lang.Object#equals(Object) Object.equals(Object)}</p>
  246. *
  247. * @param objectToTest the <code>Object</code> to test for
  248. * equality with this <code>QName</code>
  249. * @return <code>true</code> if the given <code>Object</code> is
  250. * equal to this <code>QName</code> else <code>false</code>
  251. */
  252. public final boolean equals(Object objectToTest) {
  253. if (objectToTest == null || !(objectToTest instanceof QName)) {
  254. return false;
  255. }
  256. QName qName = (QName) objectToTest;
  257. return namespaceURI.equals(qName.namespaceURI)
  258. && localPart.equals(qName.localPart);
  259. }
  260. /**
  261. * <p>Generate the hash code for this <code>QName</code>.</p>
  262. *
  263. * <p>The hash code is calculated using both the Namespace URI and
  264. * the local part of the <code>QName</code>. The prefix is
  265. * <strong><em>NOT</em></strong> used to calculate the hash
  266. * code.</p>
  267. *
  268. * <p>This method satisfies the general contract of {@link
  269. * java.lang.Object#hashCode() Object.hashCode()}.</p>
  270. *
  271. * @return hash code for this <code>QName</code> <code>Object</code>
  272. */
  273. public final int hashCode() {
  274. return namespaceURI.hashCode() ^ localPart.hashCode();
  275. }
  276. /**
  277. * <p><code>String</code> representation of this
  278. * <code>QName</code>.</p>
  279. *
  280. * <p>The commonly accepted way of representing a <code>QName</code>
  281. * as a <code>String</code> was <a href="http://jclark.com/xml/xmlns.htm">defined</a>
  282. * by James Clark. Although this is not a <em>standard</em>
  283. * specification, it is in common use, e.g. {@link javax.xml.transform.Transformer#setParameter(String name, Object value)}.
  284. * This implementation represents a <code>QName</code> as:
  285. * "{" + Namespace URI + "}" + local part. If the Namespace URI
  286. * <code>.equals(XMLConstants.NULL_NS_URI)</code>, only the
  287. * local part is returned. An appropriate use of this method is
  288. * for debugging or logging for human consumption.</p>
  289. *
  290. * <p>Note the prefix value is <strong><em>NOT</em></strong>
  291. * returned as part of the <code>String</code> representation.</p>
  292. *
  293. * <p>This method satisfies the general contract of {@link
  294. * java.lang.Object#toString() Object.toString()}.</p>
  295. *
  296. * @return <code>String</code> representation of this <code>QName</code>
  297. */
  298. public String toString() {
  299. if (namespaceURI.equals(XMLConstants.NULL_NS_URI)) {
  300. return localPart;
  301. } else {
  302. return "{" + namespaceURI + "}" + localPart;
  303. }
  304. }
  305. /**
  306. * <p><code>QName</code> derived from parsing the formatted
  307. * <code>String</code>.</p>
  308. *
  309. * <p>If the <code>String</code> is <code>null</code> or does not conform to
  310. * {@link #toString() QName.toString()} formatting, an
  311. * <code>IllegalArgumentException</code> is thrown.</p>
  312. *
  313. * <p><em>The <code>String</code> <strong>MUST</strong> be in the
  314. * form returned by {@link #toString() QName.toString()}.</em></p>
  315. * <p>The commonly accepted way of representing a <code>QName</code>
  316. * as a <code>String</code> was <a href="http://jclark.com/xml/xmlns.htm">defined</a>
  317. * by James Clark. Although this is not a <em>standard</em>
  318. * specification, it is in common use, e.g. {@link javax.xml.transform.Transformer#setParameter(String name, Object value)}.
  319. * This implementation parses a <code>String</code> formatted
  320. * as: "{" + Namespace URI + "}" + local part. If the Namespace
  321. * URI <code>.equals(XMLConstants.NULL_NS_URI)</code>, only the
  322. * local part should be provided.</p>
  323. *
  324. * <p>The prefix value <strong><em>CANNOT</em></strong> be
  325. * represented in the <code>String</code> and will be set to
  326. * {@link javax.xml.XMLConstants#DEFAULT_NS_PREFIX
  327. * XMLConstants.DEFAULT_NS_PREFIX}.</p>
  328. *
  329. * <p>This method does not do full validation of the resulting
  330. * <code>QName</code>.
  331. * <p>The Namespace URI is not validated as a
  332. * <a href="http://www.ietf.org/rfc/rfc2396.txt">URI reference</a>.
  333. * The local part is not validated as a
  334. * <a href="http://www.w3.org/TR/REC-xml-names/#NT-NCName">NCName</a>
  335. * as specified in
  336. * <a href="http://www.w3.org/TR/REC-xml-names/">Namespaces in XML</a>.</p>
  337. *
  338. * @param qNameAsString <code>String</code> representation
  339. * of the <code>QName</code>
  340. * @return <code>QName</code> corresponding to the given <code>String</code>
  341. * @see #toString() QName.toString()
  342. */
  343. public static QName valueOf(String qNameAsString) {
  344. // null is not valid
  345. if (qNameAsString == null) {
  346. throw new IllegalArgumentException("cannot create QName from \"null\" or \"\" String");
  347. }
  348. // "" local part is valid to preserve compatible behavior with QName 1.0
  349. if (qNameAsString.length() == 0) {
  350. return new QName(
  351. XMLConstants.NULL_NS_URI,
  352. qNameAsString,
  353. XMLConstants.DEFAULT_NS_PREFIX);
  354. }
  355. // local part only?
  356. if (qNameAsString.charAt(0) != '{') {
  357. return new QName(
  358. XMLConstants.NULL_NS_URI,
  359. qNameAsString,
  360. XMLConstants.DEFAULT_NS_PREFIX);
  361. }
  362. // Namespace URI improperly specified?
  363. if (qNameAsString.startsWith("{" + XMLConstants.NULL_NS_URI + "}")) {
  364. throw new IllegalArgumentException(
  365. "Namespace URI .equals(XMLConstants.NULL_NS_URI), "
  366. + ".equals(\"" + XMLConstants.NULL_NS_URI + "\"), "
  367. + "only the local part, "
  368. + "\"" + qNameAsString.substring(2 + XMLConstants.NULL_NS_URI.length()) + "\", "
  369. + "should be provided.");
  370. }
  371. // Namespace URI and local part specified
  372. int endOfNamespaceURI = qNameAsString.indexOf('}');
  373. if (endOfNamespaceURI == -1) {
  374. throw new IllegalArgumentException(
  375. "cannot create QName from \""
  376. + qNameAsString
  377. + "\", missing closing \"}\"");
  378. }
  379. return new QName(
  380. qNameAsString.substring(1, endOfNamespaceURI),
  381. qNameAsString.substring(endOfNamespaceURI + 1),
  382. XMLConstants.DEFAULT_NS_PREFIX);
  383. }
  384. }