blob: 8ce501068d419dd8b36bcac78f3360311a166306 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2011 Andrea Bittau, University College London, and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Andrea Bittau - initial API and implementation from the PsychoPath XPath 2.0
* Mukul Gandhi - bug 273719 - String Length does not work with Element arg.
* Mukul Gandhi - bug 273795 - improvements to function, substring (implemented
* numeric type promotion).
* Jesper Steen Moeller - bug 285145 - implement full arity checking
* Jesper Steen Moeller - bug 281159 - implement xs:anyUri -> xs:string promotion
* Jesper Steen Moller - bug 281938 - undefined context should raise error
* Jesper Steen Moller - bug 340933 - Migrate to new XPath2 API
*******************************************************************************/
package org.eclipse.wst.xml.xpath2.processor.internal.function;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import org.eclipse.wst.xml.xpath2.api.EvaluationContext;
import org.eclipse.wst.xml.xpath2.api.Item;
import org.eclipse.wst.xml.xpath2.api.ResultBuffer;
import org.eclipse.wst.xml.xpath2.api.typesystem.TypeDefinition;
import org.eclipse.wst.xml.xpath2.processor.DynamicError;
import org.eclipse.wst.xml.xpath2.processor.ResultSequence;
import org.eclipse.wst.xml.xpath2.processor.ResultSequenceFactory;
import org.eclipse.wst.xml.xpath2.processor.internal.SeqType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.AnyAtomicType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.AnyType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.NumericType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.QName;
import org.eclipse.wst.xml.xpath2.processor.internal.types.XSAnyURI;
import org.eclipse.wst.xml.xpath2.processor.internal.types.XSDouble;
import org.eclipse.wst.xml.xpath2.processor.internal.types.XSString;
import org.eclipse.wst.xml.xpath2.processor.internal.types.XSUntypedAtomic;
import org.eclipse.wst.xml.xpath2.processor.internal.types.builtin.BuiltinTypeLibrary;
/**
* Support for functions.
*/
public abstract class Function implements org.eclipse.wst.xml.xpath2.api.Function {
protected static DatatypeFactory _datatypeFactory;
static {
try {
_datatypeFactory = DatatypeFactory.newInstance();
}
catch (DatatypeConfigurationException e) {
throw new RuntimeException("Cannot initialize XML datatypes", e);
}
}
protected QName _name;
/**
* if negative, need to have "at least"
*/
protected int _min_arity;
/**
* If "at least", this speci, unlimited if -1
*/
protected int _max_arity;
/**
* Constructor for Function.
*
* @param name
* QName.
* @param arity
* the arity of a specific function.
*/
public Function(QName name, int arity) {
_name = name;
if (arity < 0) {
throw new RuntimeException("We want to avoid this!");
}
_min_arity = arity;
_max_arity = arity;
}
/**
* Constructor for Function.
*
* @param name
* QName.
* @param arity
* the arity of a specific function.
*/
public Function(QName name, int min_arity, int max_arity) {
_name = name;
if (min_arity < 0 || max_arity < 0 || max_arity < min_arity) {
throw new RuntimeException("We want to avoid this!");
}
_min_arity = min_arity;
_max_arity = max_arity;
}
/**
* Support for QName interface.
*
* @return Result of QName operation.
*/
public QName name() {
return _name;
}
/**
* Minimal number of allowed arguments.
*
* @return The smallest number of erguments possible
*/
public int min_arity() {
return _min_arity;
}
/**
* Maximum number of allowed arguments.
*
* @return The highest number of erguments possible
*/
public int max_arity() {
return _max_arity;
}
/**
* Checks if this function has an to the
*
* @param actual_arity
* @return
*/
public boolean matches_arity(int actual_arity) {
if (actual_arity < min_arity()) return false;
if (actual_arity > max_arity()) return false;
return true;
}
/**
* Default constructor for signature.
*
* @return Signature.
*/
public String signature() {
return signature(this);
}
/**
* Obtain the function name and arity from signature.
*
* @param f
* current function.
* @return Signature.
*/
public static String signature(Function f) {
return signature(f.name(), f.is_vararg() ? -1 : f.min_arity());
}
/**
* Apply the name and arity to signature.
*
* @param name
* QName.
* @param arity
* arity of the function.
* @return Signature.
*/
public static String signature(QName name, int arity) {
String n = name.expanded_name();
if (n == null)
return null;
n += "_";
if (arity < 0)
n += "x";
else
n += arity;
return n;
}
/**
* Evaluate arguments.
*
* @param args
* argument expressions.
* @throws DynamicError
* Dynamic error.
* @return Result of evaluation.
*/
public org.eclipse.wst.xml.xpath2.processor.ResultSequence evaluate(Collection args)
throws DynamicError {
throw new UnsupportedOperationException();
}
// convert argument according to section 3.1.5 of xpath 2.0 spec
/**
* Convert the input argument according to section 3.1.5 of specification.
*
* @param arg
* input argument.
* @param expected
* Expected Sequence type.
* @throws DynamicError
* Dynamic error.
* @return Converted argument.
*/
public static org.eclipse.wst.xml.xpath2.api.ResultSequence convert_argument(org.eclipse.wst.xml.xpath2.api.ResultSequence arg,
SeqType expected) throws DynamicError {
ResultBuffer result = new ResultBuffer();
// XXX: Should use type_class instead and use item.getClass().isAssignableTo(expected.type_class())
AnyType expected_type = expected.type();
// expected is atomic
if (expected_type instanceof AnyAtomicType) {
AnyAtomicType expected_aat = (AnyAtomicType) expected_type;
// atomize
org.eclipse.wst.xml.xpath2.api.ResultSequence rs = FnData.atomize(arg);
// cast untyped to expected type
for (Iterator i = rs.iterator(); i.hasNext();) {
AnyType item = (AnyType) i.next();
if (item instanceof XSUntypedAtomic) {
// create a new item of the expected
// type initialized with from the string
// value of the item
ResultSequence converted = null;
if (expected_aat instanceof XSString) {
XSString strType = new XSString(item.getStringValue());
converted = ResultSequenceFactory.create_new(strType);
}
else {
converted = ResultSequenceFactory.create_new(item);
}
result.concat(converted);
}
// xs:anyURI promotion to xs:string
else if (item instanceof XSAnyURI && expected_aat instanceof XSString) {
result.add(new XSString(item.getStringValue()));
}
// numeric type promotion
else if (item instanceof NumericType) {
if (expected_aat instanceof XSDouble) {
XSDouble doubleType = new XSDouble(item.getStringValue());
result.add(doubleType);
}
else {
result.add(item);
}
} else {
result.add(item);
}
}
// do sequence type matching on converted arguments
return expected.match(result.getSequence());
} else {
// do sequence type matching on converted arguments
return expected.match(arg);
}
}
// convert arguments
// returns collection of arguments
/**
* Convert arguments.
*
* @param args
* input arguments.
* @param expected
* expected arguments.
* @throws DynamicError
* Dynamic error.
* @return Converted arguments.
*/
public static Collection convert_arguments(Collection args,
Collection expected) throws DynamicError {
Collection result = new ArrayList();
assert args.size() <= expected.size();
Iterator argi = args.iterator();
Iterator expi = expected.iterator();
// convert all arguments
while (argi.hasNext()) {
result.add(convert_argument((org.eclipse.wst.xml.xpath2.api.ResultSequence) argi.next(),
(SeqType) expi.next()));
}
return result;
}
protected static ResultSequence getResultSetForArityZero(EvaluationContext ec)
throws DynamicError {
ResultSequence rs = ResultSequenceFactory.create_new();
Item contextItem = ec.getContextItem();
if (contextItem != null) {
// if context item is defined, then that is the default argument
// to fn:string function
rs.add(new XSString(contextItem.getStringValue()));
} else {
throw DynamicError.contextUndefined();
}
return rs;
}
public boolean is_vararg() {
return _min_arity != _max_arity;
}
public String getName() {
return name().local();
}
public int getMinArity() {
return min_arity();
}
public int getMaxArity() {
return max_arity();
}
public boolean isVariableArgument() {
return this.is_vararg();
}
public boolean canMatchArity(int actualArity) {
return matches_arity(actualArity);
}
public TypeDefinition getResultType() {
return BuiltinTypeLibrary.XS_UNTYPED;
}
public TypeDefinition getArgumentType(int index) {
return BuiltinTypeLibrary.XS_UNTYPED;
}
public String getArgumentNameHint(int index) {
return "argument_" + index;
}
public TypeDefinition computeReturnType(Collection args,
org.eclipse.wst.xml.xpath2.api.StaticContext sc) {
return BuiltinTypeLibrary.XS_UNTYPED;
}
public org.eclipse.wst.xml.xpath2.api.ResultSequence evaluate(Collection/*<ResultSequence>*/ args,
EvaluationContext evaluationContext) {
org.eclipse.wst.xml.xpath2.processor.ResultSequence result = evaluate(args);
return result;
}
}