/* QuiXProc: efficient evaluation of XProc Pipelines. Copyright (C) 2011-2015 Innovimax All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package innovimax.quixproc.datamodel; import java.io.Serializable; import javax.xml.XMLConstants; import javax.xml.namespace.QName; /** * <p> * {@code QuiXQName} represents a <strong>qualified name</strong> as defined in * the XML specifications: <a href="http://www.w3.org/TR/xmlschema-2/#QName">XML * Schema Part2: Datatypes specification</a>, * <a href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">Namespaces in * XML</a>, <a href="http://www.w3.org/XML/xml-names-19990114-errata">Namespaces * in XML Errata</a>. * </p> * * <p> * The value of a {@code QuiXQName} contains a <strong>Namespace URI</strong>, * <strong>local part</strong> and <strong>prefix</strong>. * </p> * * <p> * {@code QuiXQName} is QuiXCharStream friendly implementation of {@code QName} * </p> * * <p> * The prefix is included in {@code QName} to retain lexical information * <strong><em>when present</em></strong> in an * {@link javax.xml.transform.Source XML input source}. The prefix is <strong> * <em>NOT</em></strong> used in {@link #equals(Object) QName.equals(Object)} or * to compute the {@link #hashCode() QName.hashCode()}. Equality and the hash * code are defined using <strong><em>only</em></strong> the Namespace URI and * local part. * </p> * * <p> * If not specified, the Namespace URI is set to {@link XMLConstants#NULL_NS_URI * XMLConstants.NULL_NS_URI}. If not specified, the prefix is set to * {@link XMLConstants#DEFAULT_NS_PREFIX XMLConstants.DEFAULT_NS_PREFIX}. * </p> * * <p> * {@code QuiXQName} is immutable. * </p> * * @author innovimax * @version new * @see <a href="http://www.w3.org/TR/xmlschema-2/#QName"> XML Schema Part2: * Datatypes specification</a> * @see <a href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames"> Namespaces * in XML</a> * @see <a href="http://www.w3.org/XML/xml-names-19990114-errata"> Namespaces in * XML Errata</a> */ public class QuiXQName implements Serializable { /** * <p> * Namespace URI of this {@code QName}. * </p> */ private final QuiXCharStream namespaceURI; /** * <p> * local part of this {@code QName}. * </p> */ private final QuiXCharStream localPart; /** * <p> * prefix of this {@code QName}. * </p> */ private final QuiXCharStream prefix; /** * <p> * {@code QuiXQName} constructor specifying the Namespace URI and local * part. * </p> * * <p> * If the Namespace URI is {@code null}, it is set to * {@link XMLConstants#NULL_NS_URI XMLConstants.NULL_NS_URI}. This value * represents no explicitly defined Namespace as defined by the * <a href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">Namespaces in * XML</a> specification. This action preserves compatible behavior with * QName 1.0. Explicitly providing the {@link XMLConstants#NULL_NS_URI * XMLConstants.NULL_NS_URI} value is the preferred coding style. * </p> * * <p> * If the local part is {@code null} an {@code IllegalArgumentException} is * thrown. A local part of "" is allowed to preserve compatible behavior * with QName 1.0. * </p> * * <p> * When using this constructor, the prefix is set to * {@link XMLConstants#DEFAULT_NS_PREFIX XMLConstants.DEFAULT_NS_PREFIX}. * </p> * * <p> * The Namespace URI is not validated as a * <a href="http://www.ietf.org/rfc/rfc2396.txt">URI reference</a>. The * local part is not validated as a * <a href="http://www.w3.org/TR/REC-xml-names/#NT-NCName">NCName</a> as * specified in <a href="http://www.w3.org/TR/REC-xml-names/">Namespaces in * XML</a>. * </p> * * @param namespaceURI * Namespace URI of the {@code QName} * @param localPart * local part of the {@code QName} * * @throws IllegalArgumentException * When <code>localPart</code> is {@code null} * * @see #QName(String namespaceURI, String localPart, String prefix) * QName(String namespaceURI, String localPart, String prefix) */ public QuiXQName(final QuiXCharStream namespaceURI, final QuiXCharStream localPart) { this(namespaceURI, localPart, QuiXCharStream.DEFAULT_NS_PREFIX); } /** * <p> * {@code QuiXQName} constructor specifying the Namespace URI, local part * and prefix. * </p> * * <p> * If the Namespace URI is {@code null}, it is set to * {@link XMLConstants#NULL_NS_URI XMLConstants.NULL_NS_URI}. This value * represents no explicitly defined Namespace as defined by the * <a href="http://www.w3.org/TR/REC-xml-names/#ns-qualnames">Namespaces in * XML</a> specification. This action preserves compatible behavior with * QName 1.0. Explicitly providing the {@link XMLConstants#NULL_NS_URI * XMLConstants.NULL_NS_URI} value is the preferred coding style. * </p> * * <p> * If the local part is {@code null} an {@code IllegalArgumentException} is * thrown. A local part of "" is allowed to preserve compatible behavior * with QName 1.0. * </p> * * <p> * If the prefix is {@code null}, an {@code IllegalArgumentException} is * thrown. Use {@link XMLConstants#DEFAULT_NS_PREFIX * XMLConstants.DEFAULT_NS_PREFIX} to explicitly indicate that no prefix is * present or the prefix is not relevant. * </p> * * <p> * The Namespace URI is not validated as a * <a href="http://www.ietf.org/rfc/rfc2396.txt">URI reference</a>. The * local part and prefix are not validated as a * <a href="http://www.w3.org/TR/REC-xml-names/#NT-NCName">NCName</a> as * specified in <a href="http://www.w3.org/TR/REC-xml-names/">Namespaces in * XML</a>. * </p> * * @param namespaceURI * Namespace URI of the {@code QName} * @param localPart * local part of the {@code QName} * @param prefix * prefix of the {@code QName} * * @throws IllegalArgumentException * When <code>localPart</code> or {@code prefix} is {@code null} */ public QuiXQName(QuiXCharStream namespaceURI, QuiXCharStream localPart, QuiXCharStream prefix) { // map null Namespace URI to default // to preserve compatibility with QName 1.0 this.namespaceURI = namespaceURI == null ? QuiXCharStream.NULL_NS_URI : namespaceURI; // local part is required. // "" is allowed to preserve compatibility with QName 1.0 if (localPart == null) { throw new IllegalArgumentException("local part cannot be \"null\" when creating a QName"); } this.localPart = localPart; // prefix is required if (prefix == null) { throw new IllegalArgumentException("prefix cannot be \"null\" when creating a QName"); } this.prefix = prefix; } /** * <p> * {@code QuiXQName} constructor specifying the local part. * </p> * * <p> * If the local part is {@code null} an {@code IllegalArgumentException} is * thrown. A local part of "" is allowed to preserve compatible behavior * with QName 1.0. * </p> * * <p> * When using this constructor, the Namespace URI is set to * {@link XMLConstants#NULL_NS_URI XMLConstants.NULL_NS_URI} and the prefix * is set to {@link XMLConstants#DEFAULT_NS_PREFIX * XMLConstants.DEFAULT_NS_PREFIX}. * </p> * * <p> * <em>In an XML context, all Element and Attribute names exist * in the context of a Namespace. Making this explicit during the * construction of a {@code QName} helps prevent hard to diagnosis XML * validity errors. The constructors * {@link #QName(String namespaceURI, String localPart) QName(String * namespaceURI, String localPart)} and * {@link #QName(String namespaceURI, String localPart, String prefix)} are * preferred.</em> * </p> * * <p> * The local part is not validated as a * <a href="http://www.w3.org/TR/REC-xml-names/#NT-NCName">NCName</a> as * specified in <a href="http://www.w3.org/TR/REC-xml-names/">Namespaces in * XML</a>. * </p> * * @param localPart * local part of the {@code QName} * * @throws IllegalArgumentException * When <code>localPart</code> is {@code null} * * @see #QName(String namespaceURI, String localPart) QName(String * namespaceURI, String localPart) * @see #QName(String namespaceURI, String localPart, String prefix) * QName(String namespaceURI, String localPart, String prefix) */ public QuiXQName(QuiXCharStream localPart) { this(QuiXCharStream.NULL_NS_URI, localPart, QuiXCharStream.DEFAULT_NS_PREFIX); } /** * <p> * Get the Namespace URI of this {@code QName}. * </p> * * @return Namespace URI of this {@code QName} */ public QuiXCharStream getNamespaceURI() { return this.namespaceURI; } /** * <p> * Get the local part of this {@code QName}. * </p> * * @return local part of this {@code QName} */ public QuiXCharStream getLocalPart() { return this.localPart; } /** * <p> * Get the prefix of this {@code QName}. * </p> * * <p> * The prefix assigned to a {@code QName} might <strong> * <em>NOT</em></strong> be valid in a different context. For example, a * {@code QName} may be assigned a prefix in the context of parsing a * document but that prefix may be invalid in the context of a different * document. * </p> * * @return prefix of this {@code QName} */ public QuiXCharStream getPrefix() { return this.prefix; } /** * <p> * Test this {@code QName} for equality with another {@code Object}. * </p> * * <p> * If the <code>Object</code> to be tested is not a {@code QName} or is * <code>null</code>, then this method returns {@code false}. * </p> * * <p> * Two {@code QName}s are considered equal if and only if both the Namespace * URI and local part are equal. This method uses {@code String.equals()} to * check equality of the Namespace URI and local part. The prefix is * <strong><em>NOT</em></strong> used to determine equality. * </p> * * <p> * This method satisfies the general contract of * {@link Object#equals(Object) Object.equals(Object)} * </p> * * @param objectToTest * the {@code Object} to test for equality with this * {@code QName} * @return <code>true</code> if the given {@code Object} is equal to this * <code>QName</code> else {@code false} */ @Override public final boolean equals(Object objectToTest) { if (objectToTest == this) { return true; } if (!(objectToTest instanceof QuiXQName)) { return false; } QuiXQName qName = (QuiXQName) objectToTest; return this.localPart.equals(qName.localPart) && this.namespaceURI.equals(qName.namespaceURI); } /** * <p> * Generate the hash code for this {@code QName}. * </p> * * <p> * The hash code is calculated using both the Namespace URI and the local * part of the {@code QName}. The prefix is <strong> <em>NOT</em></strong> * used to calculate the hash code. * </p> * * <p> * This method satisfies the general contract of {@link Object#hashCode() * Object.hashCode()}. * </p> * * @return hash code for this <code>QName</code> {@code Object} */ @Override public final int hashCode() { return this.namespaceURI.hashCode() ^ this.localPart.hashCode(); } /** * <p> * <code>String</code> representation of this {@code QName}. * </p> * * <p> * The commonly accepted way of representing a {@code QName} as a * {@code String} was <a href="http://jclark.com/xml/xmlns.htm">defined</a> * by James Clark. Although this is not a <em>standard</em> specification, * it is in common use, e.g. * {@link javax.xml.transform.Transformer#setParameter(String name, Object value)} * . This implementation represents a {@code QName} as: "{" + Namespace URI * + "}" + local part. If the Namespace URI * {@code .equals(XMLConstants.NULL_NS_URI)}, only the local part is * returned. An appropriate use of this method is for debugging or logging * for human consumption. * </p> * * <p> * Note the prefix value is <strong><em>NOT</em></strong> returned as part * of the {@code String} representation. * </p> * * <p> * This method satisfies the general contract of {@link Object#toString() * Object.toString()}. * </p> * * @return <code>String</code> representation of this {@code QName} */ @Override public String toString() { // TODO convert XMLConstants to a local one if (this.namespaceURI.equals(QuiXCharStream.NULL_NS_URI)) { return this.localPart.toString(); } return "{" + this.namespaceURI + "}" + this.localPart; } /** * <p> * <code>QName</code> derived from parsing the formatted {@code String} . * </p> * * <p> * If the <code>String</code> is {@code null} or does not conform to * {@link #toString() QName.toString()} formatting, an * {@code IllegalArgumentException} is thrown. * </p> * * <p> * <em>The {@code String} <strong>MUST</strong> be in the form returned * by {@link #toString() QName.toString()}.</em> * </p> * * <p> * The commonly accepted way of representing a {@code QName} as a * {@code String} was <a href="http://jclark.com/xml/xmlns.htm">defined</a> * by James Clark. Although this is not a <em>standard</em> specification, * it is in common use, e.g. * {@link javax.xml.transform.Transformer#setParameter(String name, Object value)} * . This implementation parses a {@code String} formatted as: "{" + * Namespace URI + "}" + local part. If the Namespace URI * {@code .equals(XMLConstants.NULL_NS_URI)}, only the local part should be * provided. * </p> * * <p> * The prefix value <strong><em>CANNOT</em></strong> be represented in the * {@code String} and will be set to {@link XMLConstants#DEFAULT_NS_PREFIX * XMLConstants.DEFAULT_NS_PREFIX}. * </p> * * <p> * This method does not do full validation of the resulting {@code QName}. * <p> * The Namespace URI is not validated as a * <a href="http://www.ietf.org/rfc/rfc2396.txt">URI reference</a>. The * local part is not validated as a * <a href="http://www.w3.org/TR/REC-xml-names/#NT-NCName">NCName</a> as * specified in <a href="http://www.w3.org/TR/REC-xml-names/">Namespaces in * XML</a>. * </p> * * @param qNameAsString * <code>String</code> representation of the {@code QName} * * @throws IllegalArgumentException * When <code>qNameAsString</code> is {@code null} or malformed * * @return <code>QName</code> corresponding to the given {@code String} * @see #toString() QName.toString() */ public static QuiXQName valueOf(String qNameAsString) { // null is not valid if (qNameAsString == null) { throw new IllegalArgumentException("cannot create QName from \"null\" or \"\" String"); } // "" local part is valid to preserve compatible behavior with QName 1.0 if (qNameAsString.isEmpty()) { return new QuiXQName(QuiXCharStream.NULL_NS_URI, QuiXCharStream.fromSequence(qNameAsString), QuiXCharStream.DEFAULT_NS_PREFIX); } // local part only? if (qNameAsString.charAt(0) != '{') { return new QuiXQName(QuiXCharStream.NULL_NS_URI, QuiXCharStream.fromSequence(qNameAsString), QuiXCharStream.DEFAULT_NS_PREFIX); } // Namespace URI improperly specified? if (qNameAsString.startsWith("{" + XMLConstants.NULL_NS_URI + "}")) { throw new IllegalArgumentException("Namespace URI .equals(XMLConstants.NULL_NS_URI), " + ".equals(\"" + XMLConstants.NULL_NS_URI + "\"), " + "only the local part, " + "\"" + qNameAsString.substring(2 + XMLConstants.NULL_NS_URI.length()) + "\", " + "should be provided."); } // Namespace URI and local part specified int endOfNamespaceURI = qNameAsString.indexOf('}'); if (endOfNamespaceURI == -1) { throw new IllegalArgumentException( "cannot create QName from \"" + qNameAsString + "\", missing closing \"}\""); } return new QuiXQName(QuiXCharStream.fromSequence(qNameAsString.substring(1, endOfNamespaceURI)), QuiXCharStream.fromSequence(qNameAsString.substring(endOfNamespaceURI + 1)), QuiXCharStream.DEFAULT_NS_PREFIX); } public QName asQName() { return new QName(this.namespaceURI.toString(), this.localPart.toString(), this.prefix.toString()); } }