blob: 441f9047207ef0f63281e8bcb2d7b6d092118038 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2010 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
* Jesper Steen Moeller - bug 285145 - implement full arity checking
* Jesper Steen Moller - Bug 286062 - Add type promotion for numeric operators
* Mukul Gandhi - bug 280798 - PsychoPath support for JDK 1.4
*******************************************************************************/
package org.eclipse.wst.xml.xpath2.processor.internal.function;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
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.ResultSequence;
import org.eclipse.wst.xml.xpath2.processor.DynamicError;
import org.eclipse.wst.xml.xpath2.processor.internal.TypeError;
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.XSDecimal;
import org.eclipse.wst.xml.xpath2.processor.internal.types.XSDouble;
import org.eclipse.wst.xml.xpath2.processor.internal.types.XSFloat;
import org.eclipse.wst.xml.xpath2.processor.internal.types.XSUntypedAtomic;
/**
* Class for Plus function.
*/
public class FsPlus extends Function {
/**
* Constructor for FsPlus.
*/
public FsPlus() {
super(new QName("plus"), 2);
}
/**
* Evaluate arguments.
*
* @param args
* argument expressions.
* @throws DynamicError
* Dynamic error.
* @return Result of evaluation.
*/
public ResultSequence evaluate(Collection args, EvaluationContext ec) {
assert args.size() >= min_arity() && args.size() <= max_arity();
return fs_plus(args);
}
/**
* Convert and promote arguments for operation.
*
* @param args
* input arguments.
* @param sc
* @throws DynamicError
* Dynamic error.
* @return Result of conversion.
*/
private static Collection convert_args(Collection args) throws DynamicError {
Collection result = new ArrayList();
// Keep track of numeric types for promotion
boolean has_float = false;
boolean has_double = false;
// atomize arguments
for (Iterator i = args.iterator(); i.hasNext();) {
org.eclipse.wst.xml.xpath2.api.ResultSequence rs = FnData.atomize((org.eclipse.wst.xml.xpath2.api.ResultSequence) i.next());
if (rs.empty())
return new ArrayList();
if (rs.size() > 1)
throw new DynamicError(TypeError.invalid_type(null));
AnyType arg = (AnyType) rs.item(0);
if (arg instanceof XSUntypedAtomic) {
arg = new XSDouble(arg.getStringValue());
}
if (arg instanceof XSDouble) has_double = true;
if (arg instanceof XSFloat) has_float = true;
result.add(ResultBuffer.wrap(arg));
}
if (has_double) has_float = false;
if (has_double || has_float) {
Collection result2 = new ArrayList();
// promote arguments
for (Iterator i = result.iterator(); i.hasNext();) {
org.eclipse.wst.xml.xpath2.api.ResultSequence rs = (org.eclipse.wst.xml.xpath2.api.ResultSequence) i.next();
Item arg = rs.item(0);
if (has_double && (arg instanceof XSFloat)) {
arg = new XSDouble(((XSFloat)arg).float_value());
} else if (has_double && (arg instanceof XSDecimal)) {
arg = new XSDouble(((XSDecimal)arg).getValue().doubleValue());
} else if (has_float && (arg instanceof XSDecimal)) {
arg = new XSFloat(((XSDecimal)arg).getValue().floatValue());
}
result2.add(arg);
}
return result2;
}
return result;
}
/**
* General operation on the arguments.
*
* @param args
* input arguments.
* @param sc
* @throws DynamicError
* Dynamic error.
* @return Result of the operation.
*/
public static ResultSequence fs_plus(Collection args) throws DynamicError {
return do_math_op(args, MathPlus.class, "plus");
}
/**
* Unary operation on the arguments.
*
* @param args
* input arguments.
* @throws DynamicError
* Dynamic error.
* @return Result of the operation.
*/
public static ResultSequence fs_plus_unary(Collection args)
throws DynamicError {
// make sure we got only one arg
if (args.size() != 1)
DynamicError.throw_type_error();
ResultSequence arg = (ResultSequence) args.iterator().next();
// make sure we got only one numeric atom
if (arg.size() != 1)
DynamicError.throw_type_error();
Item at = arg.first();
if (!(at instanceof NumericType))
DynamicError.throw_type_error();
// no-op
return arg;
}
// voodoo
/**
* Mathematical operation on the arguments.
*
* @param args
* input arguments.
* @param type
* type of arguments.
* @param mname
* Method name for template simulation.
* @param sc
* @throws DynamicError
* Dynamic error.
* @return Result of operation.
*/
public static ResultSequence do_math_op(Collection args, Class type,
String mname) throws DynamicError {
// sanity check args + convert em
if (args.size() != 2)
DynamicError.throw_type_error();
Collection cargs = convert_args(args);
if (cargs.size() == 0)
return ResultBuffer.EMPTY;
// make sure arugments are good [at least the first one]
Iterator argi = cargs.iterator();
Item arg = ((org.eclipse.wst.xml.xpath2.api.ResultSequence) argi.next()).item(0);
org.eclipse.wst.xml.xpath2.api.ResultSequence arg2 = (org.eclipse.wst.xml.xpath2.api.ResultSequence) argi.next();
if (!(type.isInstance(arg)))
DynamicError.throw_type_error();
// here is da ownage
try {
Class margsdef[] = { ResultSequence.class };
Method method = null;
method = type.getMethod(mname, margsdef);
Object margs[] = { arg2 };
return (ResultSequence) method.invoke(arg, margs);
} catch (NoSuchMethodException err) {
System.out.println("NoSuchMethodException: " + err.getMessage());
assert false;
} catch (IllegalAccessException err) {
System.out.println("IllegalAccessException: " + err.getMessage());
assert false;
} catch (InvocationTargetException err) {
Throwable ex = err.getTargetException();
if (ex instanceof DynamicError) {
throw (DynamicError) ex;
} else {
throw new RuntimeException(ex);
}
}
return null; // unreach!
}
}