org.openanzo.glitter.syntax.abstrakt.FunctionCall.java Source code

Java tutorial

Introduction

Here is the source code for org.openanzo.glitter.syntax.abstrakt.FunctionCall.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2007 IBM Corporation and Cambridge Semantics Incorporated.
 * 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
 *
 * File: $Source: /cvsroot/slrp/glitter/com.ibm.adtech.glitter/src/com/ibm/adtech/glitter/syntax/abstrakt/FunctionCall.java,v $
 * Created by:  Lee Feigenbaum (<a href="mailto:feigenbl@us.ibm.com">feigenbl@us.ibm.com</a>)
 * Created on: 10/23/06
 * Revision: $Id: FunctionCall.java 229 2007-08-07 15:22:00Z mroy $
 *
 * Contributors: IBM Corporation - initial API and implementation
 *     Cambridge Semantics Incorporated - Fork to Anzo
 *******************************************************************************/
package org.openanzo.glitter.syntax.abstrakt;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.builder.HashCodeBuilder;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.glitter.exception.ExpressionEvaluationException;
import org.openanzo.glitter.exception.GlitterRuntimeException;
import org.openanzo.glitter.exception.IncompatibleTypeException;
import org.openanzo.glitter.exception.InvalidAggregateArgumentException;
import org.openanzo.glitter.exception.InvalidAggregateFunctionException;
import org.openanzo.glitter.exception.InvalidArgumentCountException;
import org.openanzo.glitter.exception.UnknownFunctionException;
import org.openanzo.glitter.expression.AggregateFunction;
import org.openanzo.glitter.expression.BinaryFunction;
import org.openanzo.glitter.expression.Function;
import org.openanzo.glitter.expression.FunctionRegistry;
import org.openanzo.glitter.expression.FunctionWithAttributes;
import org.openanzo.glitter.expression.InfixOperator;
import org.openanzo.glitter.expression.PrefixOperator;
import org.openanzo.glitter.expression.ScalarFunction;
import org.openanzo.glitter.expression.ScalarFunctionOnTerms;
import org.openanzo.glitter.expression.ScalarFunctionOnValues;
import org.openanzo.glitter.expression.ScalarFunctionOnValuesAndErrors;
import org.openanzo.glitter.query.PatternSolution;
import org.openanzo.glitter.query.PatternSolutionImpl;
import org.openanzo.glitter.query.Projection;
import org.openanzo.glitter.query.SolutionList;
import org.openanzo.glitter.query.SolutionSet;
import org.openanzo.glitter.query.QueryController.QueryStringPrintOptions;
import org.openanzo.glitter.syntax.concrete.ParseException;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.Value;
import org.openanzo.rdf.Variable;

/**
 * A {@link FunctionCall} is an {@link Expression} that represents the invocation of a particular {@link Function} against a list of arguments.
 *
 * @author lee <lee@cambridgesemantics.com>
 *
 */
public class FunctionCall implements Expression {

    private List<Expression> arguments = null;

    private Function function = null;

    private boolean distinct = false; // for aggregates

    private boolean star = false; // for aggregates

    private List<Variable> argumentsAsVariables = null; // for aggregates

    private Map<String, Object> attributes = null;

    private int hashCode = -1;

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof FunctionCall))
            return false;
        FunctionCall other = (FunctionCall) obj;
        return this.function.equals(other.function) && this.arguments.equals(other.arguments)
                && this.distinct == other.distinct && this.star == other.star
                && ((this.argumentsAsVariables == null && other.argumentsAsVariables == null)
                        || (this.argumentsAsVariables.equals(other.argumentsAsVariables)))
                && ((this.attributes == null && other.attributes == null)
                        || (this.attributes.equals(other.attributes)));
    }

    @Override
    public int hashCode() {
        if (hashCode == -1) {
            HashCodeBuilder hcb = new HashCodeBuilder();
            hcb.append(this.function);
            hcb.append(this.arguments);
            hcb.append(this.distinct);
            hcb.append(this.star);
            hcb.append(this.argumentsAsVariables);
            hcb.append(this.attributes);
            hashCode = hcb.toHashCode();
        }
        return hashCode;
    }

    /**
     *
     * @param f
     * @param arguments
     * @throws ParseException
     */
    public FunctionCall(Function f, List<Expression> arguments) throws ParseException {
        this(f, arguments, false, false, null);
    }

    /**
     * Construct a {@link FunctionCall} from a {@link Function} and a list of arguemnts.
     *
     * @param f
     * @param arguments
     * @param star
     * @param distinct
     * @throws ParseException
     */
    public FunctionCall(Function f, List<Expression> arguments, boolean star, boolean distinct)
            throws ParseException {
        this(f, arguments, star, distinct, null);
    }

    /**
     * Construct a {@link FunctionCall} from a {@link Function} and a list of arguemnts.
     *
     * @param u
     * @param arguments
     * @param star
     * @param distinct
     * @throws ParseException
     */
    public FunctionCall(URI u, List<Expression> arguments, boolean star, boolean distinct) throws ParseException {
        this(u, arguments, star, distinct, null);
    }

    /**
     * Construct a {@link FunctionCall} from a {@link Function} and a list of arguemnts.
     *
     * @param f
     * @param arguments
     * @param star
     * @param distinct
     * @param attributes
     * @throws ParseException
     */
    public FunctionCall(Function f, List<Expression> arguments, boolean star, boolean distinct,
            Map<String, Object> attributes) throws ParseException {
        initialize(f, arguments, star, distinct, attributes);
    }

    /**
     * Construct a {@link FunctionCall} from the name of a function (as a URI) and a list of arguments. Finds the {@link Function} in the static
     * {@link FunctionRegistry}.
     *
     * @param u
     * @param arguments
     * @param star
     * @param distinct
     * @param attributes
     * @throws ParseException
     */
    public FunctionCall(URI u, List<Expression> arguments, boolean star, boolean distinct,
            Map<String, Object> attributes) throws ParseException {
        Function f = FunctionRegistry.getRegistry().getFunction(u);
        if (f == null)
            throw new UnknownFunctionException(u);
        initialize(f, arguments, star, distinct, attributes);
    }

    private void initialize(Function f, List<Expression> arguments, boolean star, boolean distinct,
            Map<String, Object> attributes) throws ParseException {
        this.function = f;
        this.arguments = arguments == null ? new ArrayList<Expression>() : arguments;
        this.distinct = distinct;
        this.star = star;
        this.attributes = attributes;
        if (this.function instanceof AggregateFunction) {
            this.argumentsAsVariables = new ArrayList<Variable>();
            for (Expression e : this.arguments) {
                if (e instanceof SimpleExpression) {
                    SimpleExpression se = (SimpleExpression) e;
                    if (se.getTerm() instanceof Variable) {
                        this.argumentsAsVariables.add((Variable) se.getTerm());
                        continue;
                    }
                }
                throw new InvalidAggregateArgumentException(e);
            }
            // aggregates can't have both a * and a variable
            if (star && !this.arguments.isEmpty())
                throw new InvalidAggregateArgumentException(this.arguments.get(0));
            // aggregates can't be empty
            if (!star && this.arguments.isEmpty())
                throw new InvalidAggregateArgumentException();
        } else {
            // distinct and star are not valid for non-aggregate functions - this is a parse error
            if (star)
                throw new ParseException("'*' found as argument to non-aggregate function:" + f);
            if (distinct)
                throw new ParseException("'DISTINCT' found as modifier to non-aggregate function:" + f);
        }
        if (this.attributes != null && this.attributes.size() > 0) {
            if (this.function instanceof FunctionWithAttributes) {
                ((FunctionWithAttributes) this.function).setAttributes(this.attributes);
            } else {
                throw new ParseException("Attributes included for function that does not expect attributes.");
            }
        }

    }

    public Value evaluate(PatternSolution solution, SolutionSet group) throws ExpressionEvaluationException {

        if (this.function instanceof ScalarFunction) {
            ScalarFunction f = (ScalarFunction) this.function;
            // TODO - we can catch errors in, for example, numbers of arguments to a function
            // at parse time, but we don't currently do that
            if (f.operatesOnValues()) {
                if (!f.operatesOnTypeErrors() && f instanceof BinaryFunction) {
                    if (arguments.size() != 2) {
                        throw new InvalidArgumentCountException(arguments.size(), 2);
                    }
                    Value argValue1 = arguments.get(0).evaluate(solution, group);
                    Value argValue2 = arguments.get(1).evaluate(solution, group);

                    return ((BinaryFunction) f).call(argValue1, argValue2);
                } else {
                    // evaluate all the arguments, storing them and any errors their
                    // evaluation produces - some functions actually care about the errors,
                    // while others blindly propagate them
                    ArrayList<Value> evaluatedArguments = new ArrayList<Value>();
                    ArrayList<ExpressionEvaluationException> argumentExceptions = new ArrayList<ExpressionEvaluationException>();
                    for (Expression argument : this.arguments) {
                        try {
                            Value argValue = argument.evaluate(solution, group);
                            if (argValue == null) // argument is unbound --> type error
                                throw new IncompatibleTypeException(argument, "bound-value");
                            evaluatedArguments.add(argValue);
                            argumentExceptions.add(null);
                        } catch (ExpressionEvaluationException e) {
                            if (e instanceof IncompatibleTypeException && f.operatesOnTypeErrors()) {
                                evaluatedArguments.add(null);
                                argumentExceptions.add(e);
                            } else {
                                throw e;
                            }
                        }
                    }
                    if (f.operatesOnTypeErrors())
                        return ((ScalarFunctionOnValuesAndErrors) f).call(evaluatedArguments, argumentExceptions);
                    else
                        return ((ScalarFunctionOnValues) f).call(evaluatedArguments);
                }
            } else {
                return ((ScalarFunctionOnTerms) f).call(this.arguments, solution);
            }
        } else if (this.function instanceof AggregateFunction) {
            AggregateFunction f = (AggregateFunction) this.function;
            if (group == null)
                throw new InvalidAggregateFunctionException(f);
            // screen the solution set to contain only the variables given by the arguments
            // if we need to distinct the arguments
            SolutionSet screened = group;
            if (this.distinct && this.arguments != null && this.arguments.size() > 0) {
                screened = new SolutionList();
                for (PatternSolution sol : group) {
                    PatternSolutionImpl psi = new PatternSolutionImpl();
                    Collection<Variable> bound = sol.getBoundVariables();
                    for (Variable v : this.argumentsAsVariables)
                        if (bound.contains(v))
                            psi.setBinding(v, sol.getBinding(v));
                    screened.add(psi);
                }
            }

            if (this.distinct)
                screened = Projection.projectDistinctSolutions(screened, null, null);

            return f.call(this.argumentsAsVariables, screened);
        } else {
            throw new GlitterRuntimeException(ExceptionConstants.GLITTER.SCALER_OR_AGGREGATE);
        }
    }

    public Set<Variable> getReferencedVariables() {
        HashSet<Variable> vars = new HashSet<Variable>();
        for (Expression e : this.arguments)
            vars.addAll(e.getReferencedVariables());
        return vars;
    }

    /**
     * @return the function
     */
    public Function getFunction() {
        return this.function;
    }

    /**
     *
     * @return The list of arguments to this function call.
     */
    public List<Expression> getArguments() {
        return this.arguments;
    }

    /**
     * Is this a star argument
     *
     * @return true if this ia star argument
     */
    public boolean starArgument() {
        return this.star;
    }

    public void prettyPrintQueryPart(EnumSet<QueryStringPrintOptions> printFlags, int indentLevel,
            Map<String, String> uri2prefix, StringBuilder s) {
        Function fn = this.getFunction();
        List<Expression> args = this.getArguments();

        if (fn instanceof PrefixOperator) {
            s.append("(");
            PrefixOperator pre = (PrefixOperator) fn;
            s.append(pre.getOperator());
            args.get(0).prettyPrintQueryPart(printFlags, indentLevel, uri2prefix, s);
            s.append(")");
        } else if (fn instanceof InfixOperator) {
            s.append("(");
            InfixOperator infix = (InfixOperator) fn;
            args.get(0).prettyPrintQueryPart(printFlags, indentLevel, uri2prefix, s);
            s.append(" ");
            s.append(infix.getOperator());
            s.append(" ");
            args.get(1).prettyPrintQueryPart(printFlags, indentLevel, uri2prefix, s);
            s.append(")");
        } else {
            s.append(fn);
            {
                s.append("(");
                if (this.distinct)
                    s.append("DISTINCT ");
                if (this.star) {
                    s.append("*");
                } else {
                    int j = 0;
                    for (Expression arg : this.getArguments()) {
                        arg.prettyPrintQueryPart(printFlags, indentLevel, uri2prefix, s);
                        if (++j < this.getArguments().size())
                            s.append(", ");
                    }
                }
                s.append(")");
            }
        }
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        Function fn = this.getFunction();
        List<Expression> args = this.getArguments();

        if (fn instanceof PrefixOperator) {
            builder.append("(");
            PrefixOperator pre = (PrefixOperator) fn;
            builder.append(pre.getOperator());
            builder.append(args.get(0));
            builder.append(")");
        } else if (fn instanceof InfixOperator) {
            builder.append("(");
            InfixOperator infix = (InfixOperator) fn;
            builder.append(args.get(0));
            builder.append(" ");
            builder.append(infix.getOperator());
            builder.append(" ");
            builder.append(args.get(1));
            builder.append(")");
        } else {
            builder.append(fn);
            {
                builder.append("(");
                if (this.distinct)
                    builder.append("DISTINCT ");
                if (this.star) {
                    builder.append("*");
                } else {
                    int j = 0;
                    for (Expression arg : this.getArguments()) {
                        builder.append(arg);
                        if (++j < this.getArguments().size())
                            builder.append(", ");
                    }
                }
                builder.append(")");
            }
        }

        return builder.toString();
    }

    public void prettyPrint(StringBuilder output) {
        output.append(this.getFunction().getClass().getSimpleName());

        output.append("(");
        {
            int j = 0;
            if (this.distinct)
                output.append("DISTINCT ");
            if (this.star) {
                output.append("*");
            } else {
                for (Expression arg : this.getArguments()) {
                    arg.prettyPrint(output);
                    if (++j < this.getArguments().size())
                        output.append(", ");
                }
            }
        }
        output.append(")");
    }

    public Collection<Variable> getBindableVariables() {
        return Collections.emptyList();
    }

    public Collection<URI> getReferencedURIs() {
        HashSet<URI> uris = new HashSet<URI>();
        URI f = this.function.getIdentifier();
        if (f != null)
            uris.add(f);
        for (Expression e : this.arguments)
            uris.addAll(e.getReferencedURIs());
        return uris;
    }
}