/* * 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.xquery.value; import org.exist.dom.QName; import org.exist.xquery.Constants.Comparison; import org.exist.xquery.ErrorCodes; import org.exist.xquery.XPathException; import org.exist.xquery.XQueryContext; import java.text.Collator; /** * Wrapper class around a {@link org.exist.dom.QName} value which extends * {@link org.exist.xquery.value.AtomicValue}. * * @author wolf */ public class QNameValue extends AtomicValue { private final QName qname; private final String stringValue; /** * Constructs a new QNameValue by parsing the given name using * the namespace declarations in context. * * @param context * @param name * @throws XPathException */ public QNameValue(XQueryContext context, String name) throws XPathException { if (name.isEmpty()) { throw new XPathException(ErrorCodes.FORG0001, "An empty string is not a valid lexical representation of xs:QName."); } qname = QName.parse(context, name, context.getURIForPrefix("")); stringValue = computeStringValue(); } public QNameValue(XQueryContext context, QName name) { this.qname = name; stringValue = computeStringValue(); } /** * @see org.exist.xquery.value.AtomicValue#getType() */ public int getType() { return Type.QNAME; } /** * Returns the wrapped QName object. */ public QName getQName() { return qname; } /** * @see org.exist.xquery.value.Sequence#getStringValue() */ public String getStringValue() throws XPathException { //TODO : previous approach was to resolve the qname when needed. We now try to keep the original qname return stringValue; } private String computeStringValue() { //TODO : previous approach was to resolve the qname when needed. We now try to keep the original qname final String prefix = qname.getPrefix(); //Not clear what to work with here... // WM: Changing the prefix is problematic (e.g. if a module // defines different prefixes than the main module). We should // keep the current in-scope prefix. // if((prefix == null || "".equals(prefix)) && qname.hasNamespace()) { // prefix = context.getPrefixForURI(qname.getNamespaceURI()); // if (prefix != null) // qname.setPrefix(prefix); // //throw new XPathException( // // "namespace " + qname.getNamespaceURI() + " is not defined"); // // } //TODO : check that the prefix matches the URI in the current context ? if (prefix != null && !prefix.isEmpty()) { return prefix + ':' + qname.getLocalPart(); } else { return qname.getLocalPart(); } } /** * @see org.exist.xquery.value.Sequence#convertTo(int) */ public AtomicValue convertTo(int requiredType) throws XPathException { switch (requiredType) { case Type.ATOMIC: case Type.ITEM: case Type.QNAME: return this; case Type.STRING: return new StringValue(getStringValue()); case Type.UNTYPED_ATOMIC: return new UntypedAtomicValue(getStringValue()); default: throw new XPathException( "A QName cannot be converted to " + Type.getTypeName(requiredType)); } } @Override public boolean compareTo(Collator collator, Comparison operator, AtomicValue other) throws XPathException { if (other.getType() == Type.QNAME) { final int cmp = qname.compareTo(((QNameValue) other).qname); switch (operator) { case EQ: return cmp == 0; case NEQ: return cmp != 0; /* * QNames are unordered case GT : return cmp > 0; case GTEQ : return cmp >= 0; case LT : return cmp < 0; case LTEQ : return cmp >= 0; */ default: throw new XPathException(ErrorCodes.XPTY0004, "cannot apply operator to QName"); } } else { throw new XPathException( "Type error: cannot compare QName to " + Type.getTypeName(other.getType())); } } /** * @see org.exist.xquery.value.AtomicValue#compareTo(Collator, AtomicValue) */ public int compareTo(Collator collator, AtomicValue other) throws XPathException { if (other.getType() == Type.QNAME) { return qname.compareTo(((QNameValue) other).qname); } else { throw new XPathException( "Type error: cannot compare QName to " + Type.getTypeName(other.getType())); } } /** * @see org.exist.xquery.value.AtomicValue#max(Collator, AtomicValue) */ public AtomicValue max(Collator collator, AtomicValue other) throws XPathException { throw new XPathException("Invalid argument to aggregate function: QName"); } public AtomicValue min(Collator collator, AtomicValue other) throws XPathException { throw new XPathException("Invalid argument to aggregate function: QName"); } /** * @see org.exist.xquery.value.Item#conversionPreference(java.lang.Class) */ public int conversionPreference(Class<?> javaClass) { if (javaClass.isAssignableFrom(QNameValue.class)) { return 0; } if (javaClass == String.class) { return 1; } if (javaClass == Object.class) { return 20; } return Integer.MAX_VALUE; } /** * @see org.exist.xquery.value.Item#toJavaObject(java.lang.Class) */ @Override public <T> T toJavaObject(final Class<T> target) throws XPathException { if (target.isAssignableFrom(QNameValue.class)) { return (T) this; } else if (target == String.class) { return (T) getStringValue(); } else if (target == Object.class) { return (T) qname; } throw new XPathException( "cannot convert value of type " + Type.getTypeName(getType()) + " to Java object of type " + target.getName()); } public String toString() { try { return this.getStringValue(); } catch (final XPathException e) { return super.toString(); } } public boolean effectiveBooleanValue() throws XPathException { throw new XPathException(ErrorCodes.FORG0006, "value of type " + Type.getTypeName(getType()) + " has no boolean value."); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof QNameValue) { return ((QNameValue) obj).qname.equals(qname); } return false; } @Override public int hashCode() { return qname.hashCode(); } }