package client.net.sf.saxon.ce.om;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.value.Whitespace;
import client.net.sf.saxon.ce.tree.util.FastStringBuffer;
/**
* This class provides an economical representation of a QName triple (prefix, URI, and localname).
* The value is stored internally as a character array containing the concatenation of URI, localname,
* and prefix (in that order) with two integers giving the start positions of the localname and prefix.
*
* <p><i>Instances of this class are immutable.</i></p>
*/
public class StructuredQName {
private final static String EMPTY_STRING = "";
private char[] content;
private int localNameStart;
private int prefixStart;
/**
* Construct a StructuredQName from a prefix, URI, and local name. This method performs no validation.
* @param prefix The prefix. Use an empty string to represent the null prefix.
* @param uri The namespace URI. Use an empty string or null to represent the no-namespace
* @param localName The local part of the name
*/
public StructuredQName(String prefix, String uri, String localName) {
if (uri == null) {
uri = "";
}
int plen = prefix.length();
int ulen = uri.length();
int llen = localName.length();
localNameStart = ulen;
prefixStart = ulen + llen;
content = new char[ulen + llen + plen];
uri.getChars(0, ulen, content, 0);
localName.getChars(0, llen, content, ulen);
prefix.getChars(0, plen, content, ulen+llen);
}
/**
* Make a structuredQName from a Namepool nameCode
* @param pool the NamePool
* @param nameCode a name code that has been registered in the NamePool
*/
public StructuredQName(NamePool pool, int nameCode) {
this(pool.getPrefix(nameCode), pool.getURI(nameCode), pool.getLocalName(nameCode));
}
/**
* Make a structuredQName from a Clark name
* @param expandedName the name in Clark notation "{uri}local" if in a namespace, or "local" otherwise.
* The format "{}local" is also accepted for a name in no namespace.
* @return the constructed StructuredQName
* @throws IllegalArgumentException if the Clark name is malformed
*/
public static StructuredQName fromClarkName(String expandedName) {
String namespace;
String localName;
if (expandedName.charAt(0) == '{') {
int closeBrace = expandedName.indexOf('}');
if (closeBrace < 0) {
throw new IllegalArgumentException("No closing '}' in Clark name");
}
namespace = expandedName.substring(1, closeBrace);
if (closeBrace == expandedName.length()) {
throw new IllegalArgumentException("Missing local part in Clark name");
}
localName = expandedName.substring(closeBrace + 1);
} else {
namespace = "";
localName = expandedName;
}
return new StructuredQName("", namespace, localName);
}
/**
* Make a structured QName from a lexical QName, using a supplied NamespaceResolver to
* resolve the prefix
* @param lexicalName the QName as a lexical name (prefix:local)
* @param useDefault set to true if an absent prefix implies use of the default namespace;
* set to false if an absent prefix implies no namespace
* @param resolver NamespaceResolver used to look up a URI for the prefix
* @return the StructuredQName object corresponding to this lexical QName
* @throws XPathException if the namespace prefix is not in scope or if the value is lexically
* invalid. Error code FONS0004 is set if the namespace prefix has not been declared; error
* code FOCA0002 is set if the name is lexically invalid.
*/
public static StructuredQName fromLexicalQName(CharSequence lexicalName, boolean useDefault,
NamespaceResolver resolver)
throws XPathException {
try {
String[] parts = NameChecker.getQNameParts(Whitespace.trimWhitespace(lexicalName));
String uri = resolver.getURIForPrefix(parts[0], useDefault);
if (uri == null) {
XPathException de = new XPathException("Namespace prefix '" + parts[0] + "' has not been declared");
de.setErrorCode("FONS0004");
throw de;
}
return new StructuredQName(parts[0], uri, parts[1]);
} catch (QNameException e) {
XPathException de = new XPathException(e.getMessage());
de.setErrorCode("FOCA0002");
throw de;
}
}
/**
* Get the prefix of the QName.
* @return the prefix. Returns the empty string if the name is unprefixed.
*/
public String getPrefix() {
return new String(content, prefixStart, content.length - prefixStart);
}
/**
* Get the namespace URI of the QName.
* @return the URI. Returns the empty string to represent the no-namespace
*/
public String getNamespaceURI() {
if (localNameStart == 0) {
return EMPTY_STRING;
}
return new String(content, 0, localNameStart);
}
/**
* Get the local part of the QName
* @return the local part of the QName
*/
public String getLocalName() {
return new String(content, localNameStart, prefixStart - localNameStart);
}
/**
* Get the display name, that is the lexical QName in the form [prefix:]local-part
* @return the lexical QName
*/
public String getDisplayName() {
if (prefixStart == content.length) {
return getLocalName();
} else {
FastStringBuffer buff = new FastStringBuffer(content.length - localNameStart + 1);
buff.append(content, prefixStart, content.length - prefixStart);
buff.append(':');
buff.append(content, localNameStart, prefixStart - localNameStart);
return buff.toString();
}
}
/**
* Get the expanded QName in Clark format, that is "{uri}local" if it is in a namespace, or just "local"
* otherwise.
* @return the QName in Clark notation
*/
public String getClarkName() {
FastStringBuffer buff = new FastStringBuffer(content.length - prefixStart + 2);
if (localNameStart > 0) {
buff.append('{');
buff.append(content, 0, localNameStart);
buff.append('}');
}
buff.append(content, localNameStart, prefixStart - localNameStart);
return buff.toString();
}
/**
* The toString() method displays the QName as a lexical QName, that is prefix:local
* @return the lexical QName
*/
public String toString() {
return getDisplayName();
}
/**
* Compare two StructuredQName values for equality. This compares the URI and local name parts,
* excluding any prefix
*/
public boolean equals(Object other) {
if (other instanceof StructuredQName) {
StructuredQName sq2 = (StructuredQName)other;
if (localNameStart != sq2.localNameStart || prefixStart != sq2.prefixStart) {
return false;
}
for (int i=prefixStart-1; i>=0; i--) {
// compare from the end of the local name to maximize chance of finding a difference quickly
if (content[i] != sq2.content[i]) {
return false;
}
}
return true;
} else {
return false;
}
}
/**
* Get a hashcode to reflect the equals() method
* @return a hashcode based on the URI and local part only, ignoring the prefix.
*/
public int hashCode() {
int h = 0x8004a00b;
h ^= prefixStart;
h ^= localNameStart;
for (int i=prefixStart-1; i>=0; i--) {
h ^= (content[i] << (i&0x1f));
}
return h;
}
}
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.