/**
* Copyright 2015 Santhosh Kumar Tekuri
*
* The JLibs authors license this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package jlibs.xml.sax.dog.expr.func;
import jlibs.xml.ClarkName;
import jlibs.xml.sax.dog.DataType;
import jlibs.xml.sax.dog.Scope;
import jlibs.xml.sax.dog.expr.Evaluation;
import jlibs.xml.sax.dog.expr.Expression;
import jlibs.xml.sax.dog.expr.Literal;
import jlibs.xml.sax.dog.expr.nodset.ExactPosition;
import jlibs.xml.sax.dog.expr.nodset.Position;
import jlibs.xml.sax.dog.expr.nodset.Strings;
import jlibs.xml.sax.dog.sniff.Event;
/**
* @author Santhosh Kumar T
*/
public final class FunctionCall extends Expression{
public final Function function;
public FunctionCall(Function function){
super(Scope.GLOBAL, function.resultType);
this.function = function;
members = new Expression[function.mandatory];
}
public FunctionCall(Function function, int noOfMembers){
super(Scope.GLOBAL, function.resultType);
this.function = function;
if(!function.canAccept(noOfMembers))
throw new IllegalArgumentException(String.format("%s function cannot accept %d arguments", function.name, noOfMembers));
members = new Expression[noOfMembers];
}
private FunctionCall(Function function, Expression members[], int scope){
super(scope, function.resultType);
this.function = function;
this.members = members;
}
public final Expression members[];
public Expression addMember(Object member, int i){
assert members[i]==null : "overwriting exising member";
Expression expr = Functions.typeCast(member, function.memberType(i));
int memberScope = expr.scope();
if(scope<memberScope)
scope = memberScope;
return members[i]=expr;
}
public Expression addValidMember(Expression member, int i){
assert members[i]==null : "overwriting exising member";
assert member == Functions.typeCast(member, function.memberType(i));
int memberScope = member.scope();
if(scope<memberScope)
scope = memberScope;
return members[i]=member;
}
@Override
public Object getResult(){
Object memberResults[] = new Object[members.length];
int i=0;
for(Expression member: members){
memberResults[i] = member.getResult();
i++;
}
return function.evaluate(memberResults);
}
@Override
public Object getResult(Event event){
Function function = this.function;
Expression members[] = this.members;
PeekingFunction peekingFunction = function instanceof PeekingFunction ? (PeekingFunction)function: null;
int pending = members.length;
Object memberResults[] = new Object[pending];
for(int i=0, len=pending; i<len; i++){
Object memberResult = event.evaluate(members[i]);
if(memberResult!=null){
if(--pending>0 && peekingFunction!=null){
Object result = peekingFunction.onMemberResult(i, memberResult);
if(result!=null)
return result;
}
memberResults[i] = memberResult;
}else
memberResults[i] = event.evaluation;
}
if(pending==0)
return function.evaluate(memberResults);
else{
if(peekingFunction!=null)
return new PeekingFunctionEvaluation(this, event, memberResults, pending);
else
return new FunctionEvaluation(this, event, memberResults, pending);
}
}
@Override
public Expression simplify(){
if(scope==Scope.GLOBAL)
return super.simplify();
if(function==Functions.EQUALS){
if(scope==Scope.LOCAL){
Double d = null;
if(members[0] instanceof Position && members[1].scope()==Scope.GLOBAL) // position()=number can be simplified to exact-position(number)
d = (Double)members[1].getResult();
else if(members[1] instanceof Position && members[0].scope()==Scope.GLOBAL) // number=position() can be simplified to exact-position(number)
d = (Double)members[0].getResult();
if(d!=null){
int pos = d.intValue();
if(d!=pos)
return new Literal(Boolean.FALSE, DataType.BOOLEAN);
else
return new ExactPosition(pos);
}
}
DataType member0Type = members[0].resultType;
DataType member1Type = members[1].resultType;
if(member0Type==DataType.NUMBER){
if(member1Type==DataType.NUMBER)
return new FunctionCall(Functions.NUMBER_EQUALS_NUMBER, members, scope);
else if(member1Type==DataType.STRINGS){
FunctionCall functionCall = new FunctionCall(Functions.NUMBERS_EQUALS_NUMBER);
Strings member1 = (Strings)members[1];
functionCall.members[0] = new Strings(member1.locationPath, DataType.NUMBERS, true, false);
functionCall.members[1] = members[0];
functionCall.scope = scope;
return functionCall;
}else
System.out.println(members[0].resultType+"=="+members[1].resultType);
}else if(member0Type==DataType.STRING){
if(member1Type==DataType.STRING)
return new FunctionCall(Functions.STRING_EQUALS_STRING, members, scope);
else if(member1Type==DataType.STRINGS){
FunctionCall functionCall = new FunctionCall(Functions.STRINGS_EQUALS_STRING);
functionCall.members[0] = members[1];
functionCall.members[1] = members[0];
functionCall.scope = scope;
return functionCall;
}else
System.out.println(members[0].resultType+"=="+members[1].resultType);
}else if(member0Type==DataType.STRINGS){
if(member1Type==DataType.STRINGS)
return new FunctionCall(Functions.STRINGS_EQUALS_STRINGS, members, scope);
else if(member1Type==DataType.STRING)
return new FunctionCall(Functions.STRINGS_EQUALS_STRING, members, scope);
else if(member1Type==DataType.NUMBER){
Strings member0 = (Strings)members[0];
members[0] = new Strings(member0.locationPath, DataType.NUMBERS, true, false);
return new FunctionCall(Functions.NUMBERS_EQUALS_NUMBER, members, scope);
}else
System.out.println(members[0].resultType+"=="+members[1].resultType);
}else
System.out.println(members[0].resultType+"=="+members[1].resultType);
}
return this;
}
@Override
public String toString(){
String separator = ", ";
boolean operator = function.isOperator();
if(operator)
separator = " "+function.name+" ";
StringBuilder buff = new StringBuilder();
for(Expression member: members){
if(buff.length()>0)
buff.append(separator);
boolean enclose = operator && member instanceof FunctionCall && ((FunctionCall)member).function.isOperator();
if(enclose)
buff.append('(');
buff.append(member);
if(enclose)
buff.append(')');
}
if(separator.equals(", "))
return String.format("%s(%s)", ClarkName.valueOf(function.namespace, function.name), buff);
else
return buff.toString();
}
}
class FunctionEvaluation extends Evaluation<FunctionCall>{
protected int pending;
protected final Object[] memberResults;
protected final Event event;
public FunctionEvaluation(FunctionCall expression, Event event, Object memberResults[], int pending){
super(expression, event.order());
this.event = event;
this.memberResults = memberResults;
this.pending = pending;
}
@Override
public final void start(){
Object[] memberResults = this.memberResults;
for(int i=0, len=memberResults.length; i<len; i++){
Object memberResult = memberResults[i];
if(memberResult instanceof Evaluation){
Evaluation eval = (Evaluation)memberResult;
eval.addListener(this);
eval.start();
}else if(memberResult==null){
assert expression.members[i].scope()==Scope.DOCUMENT;
event.addListener(expression.members[i], this);
}
if(result!=null)
return;
}
if(result==null && pending==0)
fireFinished();
}
protected void setMemberResult(int i, Object memberResult){
memberResults[i] = memberResult;
pending--;
}
protected Object result;
@Override
public final Object getResult(){
return result;
}
@Override
public final void finished(Evaluation evaluation){
assert result==null : "can't consume any child evaluation";
Expression item = evaluation.expression;
Expression members[] = expression.members;
for(int i=members.length-1; i>=0; --i){
if(members[i]==item){
setMemberResult(i, evaluation.getResult());
if(pending==0)
fireFinished();
return;
}
}
assert false: "Impossible";
}
@Override
protected final void fireFinished(){
if(result==null)
result = expression.function.evaluate(memberResults);
super.fireFinished();
}
@Override
protected final void dispose(){
assert pending!=0;
for(int i=0, len=memberResults.length; i<len; i++){
Object memberResult = memberResults[i];
if(memberResult instanceof Evaluation)
((Evaluation)memberResult).removeListener(this);
else if(memberResult==null){
assert expression.members[i].scope()==Scope.DOCUMENT;
event.removeListener(expression.members[i], this);
}
}
}
}
class PeekingFunctionEvaluation extends FunctionEvaluation{
private final PeekingFunction function;
public PeekingFunctionEvaluation(FunctionCall expression, Event event, Object memberResults[], int pending){
super(expression, event, memberResults, pending);
function = (PeekingFunction)expression.function;
}
@Override
protected void setMemberResult(int memberIndex, Object memberResult){
super.setMemberResult(memberIndex, memberResult);
if(pending>0){
if((result=function.onMemberResult(memberIndex, memberResult))!=null){
fireFinished();
dispose();
}
}
}
}