/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-06 Wolfgang M. Meier
* wolfgang@exist-db.org
* http://exist.sourceforge.net
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package org.exist.xquery.value;
import org.exist.dom.QName;
import org.exist.xquery.Cardinality;
import org.exist.xquery.XPathException;
import org.w3c.dom.Node;
/**
* Represents an XQuery SequenceType and provides methods to check
* sequences and items against this type.
*
* @author wolf
*/
public class SequenceType {
private int primaryType = Type.ITEM;
private int cardinality = Cardinality.EXACTLY_ONE;
private QName nodeName = null;
public SequenceType() {
}
/**
* Construct a new SequenceType using the specified
* primary type and cardinality constants.
*
* @param primaryType
* @param cardinality
*/
public SequenceType(int primaryType, int cardinality) {
this.primaryType = primaryType;
this.cardinality = cardinality;
}
/**
* Returns the primary type as one of the
* constants defined in {@link Type}.
*/
public int getPrimaryType() {
return primaryType;
}
public void setPrimaryType(int type) {
this.primaryType = type;
}
/**
* Returns the expected cardinality. See the constants
* defined in {@link Cardinality}.
*
*/
public int getCardinality() {
return cardinality;
}
public void setCardinality(int cardinality) {
this.cardinality = cardinality;
}
public QName getNodeName() {
return nodeName;
}
public void setNodeName(QName qname) {
this.nodeName = qname;
}
/**
* Check the specified sequence against this SequenceType.
*
* @param seq
* @throws XPathException
* @throws XPathException
*/
public boolean checkType(Sequence seq) throws XPathException {
if (nodeName != null) {
Item next;
for (SequenceIterator i = seq.iterate(); i.hasNext(); ) {
next = i.nextItem();
if (!checkType(next))
return false;
}
return true;
} else {
return Type.subTypeOf(seq.getItemType(), primaryType);
}
}
/**
* Check a single item against this SequenceType.
*
* @param item
*/
public boolean checkType(Item item) {
Node realNode = null;
int type = item.getType();
if (type == Type.NODE) {
realNode = ((NodeValue)item).getNode();
type = realNode.getNodeType();
}
if(!Type.subTypeOf(type, primaryType))
return false;
if(nodeName != null) {
//TODO : how to improve performance ?
if (realNode == null)
realNode = ((NodeValue)item).getNode();
if (nodeName.getNamespaceURI() != null) {
if (!nodeName.getNamespaceURI().equals(realNode.getNamespaceURI()))
return false;
}
if (nodeName.getLocalName() != null) {
return nodeName.getLocalName().equals(realNode.getLocalName());
}
}
return true;
}
/**
* Check the given type against the primary type
* declared in this SequenceType.
*
* @param type
* @throws XPathException
*/
public void checkType(int type) throws XPathException {
if (type == Type.EMPTY || type == Type.ITEM)
return;
//Although xs:anyURI is not a subtype of xs:string, both types are compatible
if (type == Type.ANY_URI && primaryType == Type.STRING)
return;
if (!Type.subTypeOf(type, primaryType))
throw new XPathException(
"Type error: expected type: "
+ Type.getTypeName(primaryType)
+ "; got: "
+ Type.getTypeName(type));
}
/**
* Check if the given sequence has the cardinality required
* by this sequence type.
*
* @param seq
* @throws XPathException
*/
public void checkCardinality(Sequence seq) throws XPathException {
if (!seq.isEmpty() && cardinality == Cardinality.EMPTY)
throw new XPathException("Empty sequence expected; got " + seq.getItemCount());
if (seq.isEmpty() && (cardinality & Cardinality.ZERO) == 0)
throw new XPathException("Empty sequence is not allowed here");
else if (seq.hasMany() && (cardinality & Cardinality.MANY) == 0)
throw new XPathException("Sequence with more than one item is not allowed here");
}
public String toString() {
if (cardinality == Cardinality.EMPTY)
return Cardinality.toString(cardinality);
return Type.getTypeName(primaryType) + Cardinality.toString(cardinality);
}
}