/* * eXist Open Source Native XML Database * Copyright (C) 2001-2007 The eXist Project * http://exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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. * * $Id$ */ package org.exist.dom; import org.exist.storage.ElementValue; import org.exist.util.XMLChar; import org.exist.xquery.Constants; import org.exist.xquery.XPathException; import org.exist.xquery.XQueryContext; /** * Represents a QName, consisting of a local name, a namespace URI and a prefix. * * @author Wolfgang <wolfgang@exist-db.org> */ public class QName implements Comparable { /* public final static QName DOCUMENT_QNAME = new QName("#document", "", null); public final static QName TEXT_QNAME = new QName("#text", "", null); public final static QName COMMENT_QNAME = new QName("#comment", "", null); public final static QName DOCTYPE_QNAME = new QName("#doctype", "", null); */ public final static QName EMPTY_QNAME = new QName("", "", null); public final static QName DOCUMENT_QNAME = EMPTY_QNAME; public final static QName TEXT_QNAME = EMPTY_QNAME; public final static QName COMMENT_QNAME = EMPTY_QNAME; public final static QName DOCTYPE_QNAME = EMPTY_QNAME; private String localName_ = null; private String namespaceURI_ = null; private String prefix_ = null; //TODO ; use ElementValue.INKNOWN and type explicitely ? private byte nameType_ = ElementValue.ELEMENT; /** * Construct a QName. The prefix might be null for the default namespace or if no prefix * has been defined for the QName. The namespace URI should be set to the empty * string, if no namespace URI is defined. * * @param localName * @param namespaceURI * @param prefix */ public QName(String localName, String namespaceURI, String prefix) { localName_ = localName; if(namespaceURI == null) namespaceURI_ = ""; else namespaceURI_ = namespaceURI; prefix_ = prefix; } public QName(String localName, String namespaceURI) { this(localName, namespaceURI, null); } public QName(QName other) { this(other.localName_, other.namespaceURI_, other.prefix_); nameType_ = other.nameType_; } public QName(String name) { this(extractLocalName(name), null, extractPrefix(name)); } public String getLocalName() { return localName_; } public void setLocalName(String name) { localName_ = name; } public String getNamespaceURI() { return namespaceURI_; } public void setNamespaceURI(String namespaceURI) { namespaceURI_ = namespaceURI; } /** * Returns true if the QName defines a namespace URI. * */ public boolean needsNamespaceDecl() { return namespaceURI_ != null && namespaceURI_.length() > 0; } public String getPrefix() { return prefix_; } public void setPrefix(String prefix) { prefix_ = prefix; } public void setNameType(byte type) { nameType_ = type; } public byte getNameType() { return nameType_; } public String getStringValue() { if (prefix_ != null && prefix_.length() > 0) return prefix_ + ':' + localName_; else return localName_; } /** * (deprecated) : use for debugging purpose only, * use getStringValue() for production */ public String toString() { //TODO : remove this copy of getStringValue() return getStringValue(); //TODO : replace by something like this /* if (prefix_ != null && prefix_.length() > 0) return prefix_ + ':' + localName_; if (needsNamespaceDecl()) { if (prefix_ != null && prefix_.length() > 0) return "{" + namespaceURI_ + "}" + prefix_ + ':' + localName_; return "{" + namespaceURI_ + "}" + localName_; } else return localName_; */ } /** * Compares two QNames by comparing namespace URI * and local names. The prefixes are not relevant. * * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(Object o) { QName other = (QName) o; if(nameType_ != other.nameType_) { return nameType_ < other.nameType_ ? Constants.INFERIOR : Constants.SUPERIOR; } int c; if (namespaceURI_ == null) c = other.namespaceURI_ == null ? Constants.EQUAL : Constants.INFERIOR; else if (other.namespaceURI_ == null) c = Constants.SUPERIOR; else c = namespaceURI_.compareTo(other.namespaceURI_); return c == Constants.EQUAL ? localName_.compareTo(other.localName_) : c; } /** * Checks two QNames for equality. Two QNames are equal * if their namespace URIs, local names and prefixes are equal. * * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { int cmp = compareTo(obj); if(cmp != 0) return false; QName other = (QName) obj; if(prefix_ == null) return other.prefix_ == null ? true : false; else if(other.prefix_ == null) return false; else return prefix_.equals(other.prefix_); } public boolean equalsSimple(QName other) { int c; if (namespaceURI_ == null) c = other.namespaceURI_ == null ? Constants.EQUAL : Constants.INFERIOR; else if (other.namespaceURI_ == null) c = Constants.SUPERIOR; else c = namespaceURI_.compareTo(other.namespaceURI_); if (c == Constants.EQUAL) return localName_.equals(other.localName_); return false; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ public int hashCode() { int h = nameType_ + 31 + localName_.hashCode(); h += 31*h + (namespaceURI_ == null ? 1 : namespaceURI_.hashCode()); h += 31*h + (prefix_ == null ? 1 : prefix_.hashCode()); return h; } public javax.xml.namespace.QName toJavaQName() { return new javax.xml.namespace.QName(namespaceURI_ == null ? "" : namespaceURI_, localName_, prefix_ == null ? "" : prefix_); } /** * Extract the prefix from a QName string. * * @param qname * @return the prefix, if found * @exception IllegalArgumentException if the qname starts with a leading : */ public static String extractPrefix(String qname) throws IllegalArgumentException { int p = qname.indexOf(':'); if (p == Constants.STRING_NOT_FOUND) return null; if (p == 0) throw new IllegalArgumentException("Illegal QName: starts with a :"); // fixme! Should we not use isQName() here? /ljo if (Character.isDigit(qname.substring(0,1).charAt(0))) { throw new IllegalArgumentException("Illegal QName: starts with a digit"); } return qname.substring(0, p); } /** * Extract the local name from a QName string. * * @param qname * @exception IllegalArgumentException if the qname starts with a leading : or ends with a : */ public static String extractLocalName(String qname) throws IllegalArgumentException { int p = qname.indexOf(':'); if (p == Constants.STRING_NOT_FOUND) return qname; if (p == 0) throw new IllegalArgumentException("Illegal QName: starts with a :"); if (p == qname.length()) throw new IllegalArgumentException("Illegal QName: ends with a :"); if (!isQName(qname)) { throw new IllegalArgumentException("Illegal QName: not a valid local name."); } return qname.substring(p + 1); } /** * Parses the given string into a QName. The method uses context to look up * a namespace URI for an existing prefix. * * @param context * @param qname * @param defaultNS the default namespace to use if no namespace prefix is present. * @return QName * @exception IllegalArgumentException if no namespace URI is mapped to the prefix */ public static QName parse(XQueryContext context, String qname, String defaultNS) throws XPathException { String prefix = extractPrefix(qname); String namespaceURI; if (prefix != null) { namespaceURI = context.getURIForPrefix(prefix); if (namespaceURI == null) throw new XPathException("XPST0081: No namespace defined for prefix " + prefix); } else namespaceURI = defaultNS; if (namespaceURI == null) namespaceURI = ""; return new QName(extractLocalName(qname), namespaceURI, prefix); } /** * Parses the given string into a QName. The method uses context to look up * a namespace URI for an existing prefix. * * This method uses the default element namespace for qnames without prefix. * * @param context * @param qname * @exception IllegalArgumentException if no namespace URI is mapped to the prefix */ public static QName parse(XQueryContext context, String qname) throws XPathException { return parse(context, qname, context.getURIForPrefix("")); } public final static boolean isQName(String name) { int colon = name.indexOf(':'); if (colon == Constants.STRING_NOT_FOUND) return XMLChar.isValidNCName(name); if (colon == 0 || colon == name.length() - 1) return false; if (!XMLChar.isValidNCName(name.substring(0, colon))) return false; if (!XMLChar.isValidNCName(name.substring(colon + 1))) return false; return true; } public static QName fromJavaQName(javax.xml.namespace.QName jQn) { return new QName(jQn.getLocalPart(), jQn.getNamespaceURI(), jQn.getPrefix()); } }