/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-06, Wolfgang M. Meier (meier@ifs.tu-darmstadt.de)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This library 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Id$
*/
package org.exist.xquery.value;
import org.exist.EXistException;
import org.exist.dom.DocumentSet;
import org.exist.dom.EmptyNodeSet;
import org.exist.dom.NodeSet;
import org.exist.dom.StoredNode;
import org.exist.memtree.DocumentBuilderReceiver;
import org.exist.numbering.NodeId;
import org.exist.storage.DBBroker;
import org.exist.storage.Indexable;
import org.exist.storage.ValueIndexFactory;
import org.exist.xquery.Cardinality;
import org.exist.xquery.Constants;
import org.exist.xquery.XPathException;
import org.exist.xquery.util.ExpressionDumper;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import java.text.Collator;
import java.util.Iterator;
import java.util.Properties;
/**
* Represents an atomic value. All simple values that are not nodes extend AtomicValue.
* As every single item is also a sequence, this class implements both: Item and Sequence.
*
* @author wolf
*/
public abstract class AtomicValue implements Item, Sequence, Indexable {
/** An empty atomic value */
public final static AtomicValue EMPTY_VALUE = new EmptyValue();
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#getType()
*/
public int getType() {
return Type.ATOMIC;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#getStringValue()
*/
public abstract String getStringValue() throws XPathException;
public abstract AtomicValue convertTo(int requiredType) throws XPathException;
public abstract boolean compareTo(Collator collator, int operator, AtomicValue other)
throws XPathException;
public abstract int compareTo(Collator collator, AtomicValue other) throws XPathException;
public abstract AtomicValue max(Collator collator, AtomicValue other) throws XPathException;
public abstract AtomicValue min(Collator collator, AtomicValue other) throws XPathException;
/**
* Compares this atomic value to another. Returns true if the current value is of type string
* and its value starts with the string value of the other value.
*
* @param collator Collator used for string comparison.
* @param other
* @throws XPathException if this is not a string.
*/
public boolean startsWith(Collator collator, AtomicValue other) throws XPathException {
throw new XPathException("Cannot call starts-with on value of type " +
Type.getTypeName(getType()));
}
/**
* Compares this atomic value to another. Returns true if the current value is of type string
* and its value ends with the string value of the other value.
*
* @param collator Collator used for string comparison.
* @param other
* @throws XPathException if this is not a string.
*/
public boolean endsWith(Collator collator, AtomicValue other) throws XPathException {
throw new XPathException("Cannot call ends-with on value of type " +
Type.getTypeName(getType()));
}
/**
* Compares this atomic value to another. Returns true if the current value is of type string
* and its value contains the string value of the other value.
*
* @param collator Collator used for string comparison.
* @param other
* @throws XPathException if this is not a string.
*/
public boolean contains(Collator collator, AtomicValue other) throws XPathException {
throw new XPathException("Cannot call contains on value of type " +
Type.getTypeName(getType()));
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#getLength()
*/
public int getItemCount() {
return 1;
}
public int getCardinality() {
return Cardinality.EXACTLY_ONE;
}
public void removeDuplicates() {
// this is a single value, so there are no duplicates to remove
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#iterate()
*/
public SequenceIterator iterate() throws XPathException {
return new SingleItemIterator(this);
}
public SequenceIterator unorderedIterator() throws XPathException {
return new SingleItemIterator(this);
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#getItemType()
*/
public int getItemType() {
return getType();
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#itemAt(int)
*/
public Item itemAt(int pos) {
return pos > 0 ? null : this;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#toSequence()
*/
public Sequence toSequence() {
return this;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#toSAX(org.exist.storage.DBBroker, org.xml.sax.ContentHandler)
*/
public void toSAX(DBBroker broker, ContentHandler handler, Properties properties) throws SAXException {
try {
final String s = getStringValue();
handler.characters(s.toCharArray(), 0, s.length());
} catch (XPathException e) {
throw new SAXException(e);
}
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#copyTo(org.exist.storage.DBBroker, org.exist.memtree.DocumentBuilderReceiver)
*/
public void copyTo(DBBroker broker, DocumentBuilderReceiver receiver) throws SAXException {
try {
final String s = getStringValue();
receiver.characters(s);
} catch (XPathException e) {
throw new SAXException(e);
}
}
public boolean isEmpty() {
return false;
}
public boolean hasOne() {
return true;
}
public boolean hasMany() {
return false;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#add(org.exist.xquery.value.Item)
*/
public void add(Item item) throws XPathException {
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#addAll(org.exist.xquery.value.Sequence)
*/
public void addAll(Sequence other) throws XPathException {
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#atomize()
*/
public AtomicValue atomize() throws XPathException {
return this;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#effectiveBooleanValue()
*/
public abstract boolean effectiveBooleanValue() throws XPathException;
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#toNodeSet()
*/
public NodeSet toNodeSet() throws XPathException {
//TODO : solution that may be worth to investigate
/*
if (!effectiveBooleanValue())
return NodeSet.EMPTY_SET;
*/
throw new XPathException(
"cannot convert " + Type.getTypeName(getType()) + "('" + getStringValue() + "')"
+ " to a node set");
}
public MemoryNodeSet toMemNodeSet() throws XPathException {
throw new XPathException(
"cannot convert " + Type.getTypeName(getType()) + "('" + getStringValue() + "')"
+ " to a node set");
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#getDocumentSet()
*/
public DocumentSet getDocumentSet() {
return DocumentSet.EMPTY_DOCUMENT_SET;
}
public Iterator getCollectionIterator() {
return EmptyNodeSet.EMPTY_ITERATOR;
}
public AtomicValue promote(AtomicValue otherValue) throws XPathException {
if (getType() != otherValue.getType()) {
if (Type.subTypeOf(getType(), Type.DECIMAL) &&
(Type.subTypeOf(otherValue.getType(), Type.DOUBLE)
|| Type.subTypeOf(otherValue.getType(), Type.FLOAT)))
return convertTo(otherValue.getType());
if (Type.subTypeOf(getType(), Type.FLOAT) &&
Type.subTypeOf(otherValue.getType(), Type.DOUBLE))
return convertTo(Type.DOUBLE);
if (Type.subTypeOf(getType(), Type.ANY_URI) &&
Type.subTypeOf(otherValue.getType(), Type.STRING))
return convertTo(Type.STRING);
}
return this;
}
/**
* Dump a string representation of this value to the given
* ExpressionDumper.
*
* @param dumper
*/
public void dump(ExpressionDumper dumper) {
try {
dumper.display(getStringValue());
} catch (XPathException e) {
}
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#conversionPreference(java.lang.Class)
*/
public int conversionPreference(Class javaClass) {
return Integer.MAX_VALUE;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#toJavaObject(java.lang.Class)
*/
public Object toJavaObject(Class target) throws XPathException {
throw new XPathException(
"cannot convert value of type "
+ Type.getTypeName(getType())
+ " to Java object of type "
+ target.getName());
}
public String toString() {
try {
return getStringValue();
} catch (XPathException e) {
return super.toString();
}
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#isCached()
*/
public boolean isCached() {
// always returns false by default
return false;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#setIsCached(boolean)
*/
public void setIsCached(boolean cached) {
// ignore
}
public void clearContext(int contextId) throws XPathException {
// ignore
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#setSelfAsContext()
*/
public void setSelfAsContext(int contextId) throws XPathException {
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#isPersistentSet()
*/
public boolean isPersistentSet() {
return false;
}
public void nodeMoved(NodeId oldNodeId, StoredNode newNode) {
}
/*
public byte[] serialize(short collectionId) throws EXistException {
//TODO : pass the factory as an argument
return ValueIndexFactory.serialize(this, collectionId);
}
*/
/* (non-Javadoc)
* @deprecated
* @see org.exist.storage.Indexable#serialize(short, boolean)
*/
/*
public byte[] serialize(short collectionId, boolean caseSensitive) throws EXistException {
//TODO : pass the factory as an argument
return ValueIndexFactory.serialize(this, collectionId, caseSensitive);
}
*/
public byte[] serializeValue(int offset) throws EXistException {
//TODO : pass the factory as an argument
return ValueIndexFactory.serialize(this, offset);
}
/* (non-Javadoc)
* @deprecated
* @see org.exist.storage.Indexable#serializeValue(int, boolean)
*/
/*
public byte[] serializeValue(int offset, boolean caseSensitive) throws EXistException {
//TODO : pass the factory as an argument
return ValueIndexFactory.serialize(this, offset, caseSensitive);
}
*/
public int compareTo(Object other) {
throw new IllegalArgumentException("Invalid call to compareTo by " + Type.getTypeName(this.getItemType()));
}
public int getState() {
return 0;
}
public boolean hasChanged(int previousState) {
return false; // never changes
}
public boolean isCacheable() {
return true;
}
private final static class EmptyValue extends AtomicValue {
public boolean isEmpty() {
return true;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.AtomicValue#getStringValue()
*/
public String getStringValue() {
return "";
}
/* (non-Javadoc)
* @see org.exist.xquery.value.AtomicValue#convertTo(int)
*/
public AtomicValue convertTo(int requiredType) throws XPathException {
switch (requiredType) {
case Type.ATOMIC :
case Type.ITEM :
case Type.STRING :
return StringValue.EMPTY_STRING;
case Type.NORMALIZED_STRING:
case Type.TOKEN:
case Type.LANGUAGE:
case Type.NMTOKEN:
case Type.NAME:
case Type.NCNAME:
case Type.ID:
case Type.IDREF:
case Type.ENTITY:
return new StringValue("", requiredType);
case Type.ANY_URI :
return AnyURIValue.EMPTY_URI;
case Type.BOOLEAN :
return BooleanValue.FALSE;
//case Type.FLOAT :
//return new FloatValue(value);
//case Type.DOUBLE :
//case Type.NUMBER :
//return new DoubleValue(this);
//case Type.DECIMAL :
//return new DecimalValue(value);
//case Type.INTEGER :
//case Type.NON_POSITIVE_INTEGER :
//case Type.NEGATIVE_INTEGER :
//case Type.POSITIVE_INTEGER :
//case Type.LONG :
//case Type.INT :
//case Type.SHORT :
//case Type.BYTE :
//case Type.NON_NEGATIVE_INTEGER :
//case Type.UNSIGNED_LONG :
//case Type.UNSIGNED_INT :
//case Type.UNSIGNED_SHORT :
//case Type.UNSIGNED_BYTE :
//return new IntegerValue(value, requiredType);
//case Type.BASE64_BINARY :
//return new Base64Binary(value);
//case Type.HEX_BINARY :
//return new HexBinary(value);
//case Type.DATE_TIME :
//return new DateTimeValue(value);
//case Type.TIME :
//return new TimeValue(value);
//case Type.DATE :
//return new DateValue(value);
//case Type.DURATION :
//return new DurationValue(value);
//case Type.YEAR_MONTH_DURATION :
//return new YearMonthDurationValue(value);
//case Type.DAY_TIME_DURATION :
//return new DayTimeDurationValue(value);
//case Type.GYEAR :
//return new GYearValue(value);
//case Type.GMONTH :
//return new GMonthValue(value);
//case Type.GDAY :
//return new GDayValue(value);
//case Type.GYEARMONTH :
//return new GYearMonthValue(value);
//case Type.GMONTHDAY :
//return new GMonthDayValue(value);
//case Type.UNTYPED_ATOMIC :
//return new UntypedAtomicValue(getStringValue());
default :
throw new XPathException("cannot convert empty value to " + requiredType);
}
}
public boolean effectiveBooleanValue() throws XPathException {
return false;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.AtomicValue#compareTo(java.lang.Object)
*/
public int compareTo(Collator collator, AtomicValue other) throws XPathException {
if (other instanceof EmptyValue)
return Constants.EQUAL;
else
return Constants.INFERIOR;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.AtomicValue#compareTo(int, org.exist.xquery.value.AtomicValue)
*/
public boolean compareTo(Collator collator, int operator, AtomicValue other) throws XPathException {
return false;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.AtomicValue#itemAt(int)
*/
public Item itemAt(int pos) {
return null;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#toSequence()
*/
public Sequence toSequence() {
return this;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.AtomicValue#max(org.exist.xquery.value.AtomicValue)
*/
public AtomicValue max(Collator collator, AtomicValue other) throws XPathException {
return this;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#add(org.exist.xquery.value.Item)
*/
public void add(Item item) throws XPathException {
}
/* (non-Javadoc)
* @see org.exist.xquery.value.AtomicValue#min(org.exist.xquery.value.AtomicValue)
*/
public AtomicValue min(Collator collator, AtomicValue other) throws XPathException {
return this;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#conversionPreference(java.lang.Class)
*/
public int conversionPreference(Class javaClass) {
return Integer.MAX_VALUE;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#toJavaObject(java.lang.Class)
*/
public Object toJavaObject(Class target) throws XPathException {
throw new XPathException(
"cannot convert value of type "
+ Type.getTypeName(getType())
+ " to Java object of type "
+ target.getName());
}
}
}