/*
// $Id: //open/dev/farrago/src/org/eigenbase/rex/RexBuilder.java#41 $
// Package org.eigenbase is a class library of data management components.
// Copyright (C) 2005-2009 The Eigenbase Project
// Copyright (C) 2002-2009 SQLstream, Inc.
// Copyright (C) 2005-2009 LucidEra, Inc.
// Portions Copyright (C) 2003-2009 John V. Sichi
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version approved by The Eigenbase Project.
//
// 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package org.eigenbase.rex;

import java.math.*;

import java.nio.*;

import java.util.*;

import org.eigenbase.rel.*;
import org.eigenbase.reltype.*;
import org.eigenbase.sql.*;
import org.eigenbase.sql.fun.*;
import org.eigenbase.sql.type.*;
import org.eigenbase.util.*;


/**
 * Factory for row expressions.
 *
 * <p>Some common literal values (NULL, TRUE, FALSE, 0, 1, '') are cached.</p>
 *
 * @author jhyde
 * @version $Id: //open/dev/farrago/src/org/eigenbase/rex/RexBuilder.java#41 $
 * @since Nov 23, 2003
 */
public class RexBuilder
{
    //~ Instance fields --------------------------------------------------------

    protected final RelDataTypeFactory typeFactory;
    private final RexLiteral booleanTrue;
    private final RexLiteral booleanFalse;
    private final RexLiteral charEmpty;
    private final RexLiteral constantNull;
    private final SqlStdOperatorTable opTab = SqlStdOperatorTable.instance();

    //~ Constructors -----------------------------------------------------------

    /**
     * Creates a RexBuilder.
     *
     * @param typeFactory Type factory
     */
    public RexBuilder(RelDataTypeFactory typeFactory)
    {
        this.typeFactory = typeFactory;
        this.booleanTrue =
            makeLiteral(
                Boolean.TRUE,
                typeFactory.createSqlType(SqlTypeName.BOOLEAN),
                SqlTypeName.BOOLEAN);
        this.booleanFalse =
            makeLiteral(
                Boolean.FALSE,
                typeFactory.createSqlType(SqlTypeName.BOOLEAN),
                SqlTypeName.BOOLEAN);
        this.charEmpty =
            makeLiteral(
                new NlsString("", null, null),
                typeFactory.createSqlType(SqlTypeName.CHAR, 0),
                SqlTypeName.CHAR);
        this.constantNull =
            makeLiteral(
                null,
                typeFactory.createSqlType(SqlTypeName.NULL),
                SqlTypeName.NULL);
    }

    //~ Methods ----------------------------------------------------------------

    /**
     * Returns this RexBuilder's type factory
     *
     * @return type factory
     */
    public RelDataTypeFactory getTypeFactory()
    {
        return typeFactory;
    }

    /**
     * Returns this RexBuilder's operator table
     *
     * @return operator table
     */
    public SqlStdOperatorTable getOpTab()
    {
        return opTab;
    }

    /**
     * Creates an expression accessing a given named field from a record.
     *
     * @param expr Expression yielding a record
     * @param fieldName Name of field in record
     *
     * @return Expression accessing a given named field
     */
    public RexNode makeFieldAccess(
        RexNode expr,
        String fieldName)
    {
        final RelDataType type = expr.getType();
        final RelDataTypeField field = type.getField(fieldName);
        if (field == null) {
            throw Util.newInternal(
                "Type '" + type + "' has no field '"
                + fieldName + "'");
        }
        return makeFieldAccessInternal(expr, field);
    }

    /**
     * Creates an expression accessing a field with a given ordinal from a
     * record.
     *
     * @param expr Expression yielding a record
     * @param i Ordinal of field
     *
     * @return Expression accessing given field
     */
    public RexNode makeFieldAccess(
        RexNode expr,
        int i)
    {
        final RelDataType type = expr.getType();
        final RelDataTypeField [] fields = type.getFields();
        if ((i < 0) || (i >= fields.length)) {
            throw Util.newInternal(
                "Field ordinal " + i + " is invalid for "
                + " type '" + type + "'");
        }
        return makeFieldAccessInternal(expr, fields[i]);
    }

    /**
     * Creates an expression accessing a given field from a record.
     *
     * @param expr Expression yielding a record
     * @param field Field
     *
     * @return Expression accessing given field
     */
    private RexNode makeFieldAccessInternal(
        RexNode expr,
        final RelDataTypeField field)
    {
        if (expr instanceof RexRangeRef) {
            RexRangeRef range = (RexRangeRef) expr;
            return new RexInputRef(
                range.getOffset() + field.getIndex(),
                field.getType());
        }
        return new RexFieldAccess(expr, field);
    }

    /**
     * Creates a call with an array of arguments and a predetermined type.
     */
    public RexNode makeCall(
        RelDataType returnType,
        SqlOperator op,
        RexNode ... exprs)
    {
        return new RexCall(
            returnType,
            op,
            exprs);
    }

    /**
     * Creates a call with an array of arguments.
     *
     * <p>This is the fundamental method called by all of the other <code>
     * makeCall</code> methods. If you derive a class from {@link RexBuilder},
     * this is the only method you need to override.</p>
     */
    public RexNode makeCall(
        SqlOperator op,
        RexNode ... exprs)
    {
        final RelDataType type = deriveReturnType(op, typeFactory, exprs);
        RexNode [] fixExprs = exprs;

        // REVIEW: angel 2006-08-27 Commented out the following section
        // piece of code, not sure what its purpose is, but it was causing
        // errors
        /*
        if (type instanceof IntervalSqlType) {
            //if (op instanceof SqlDatetimeSubtractionOperator) {
            //    op = SqlStdOperatorTable.minusOperator;
            //}
            int count = 0;
            for (int i = 0; i < exprs.length; i++) {
                if ((exprs[i] instanceof RexLiteral)
                    && (exprs[i].getType() instanceof IntervalSqlType)
                    && (exprs[i].getType().getIntervalQualifier() != null)) {
                    exprs[i] = null;
                    continue;
                }
                count++;
            }
            fixExprs = new RexNode[count];
            for (int i = 0; i < exprs.length; i++) {
                if (exprs[i] == null) {
                    continue;
                }
                fixExprs[i] = exprs[i];
            }
        }  */
        return new RexCall(type, op, fixExprs);
    }

    /**
     * Creates a call with a list of arguments.
     *
     * <p>Equivalent to <code>makeCall(op, exprList.toArray(new
     * RexNode[exprList.size()]))</code>.
     */
    public final RexNode makeCall(
        SqlOperator op,
        List<? extends RexNode> exprList)
    {
        return makeCall(op, exprList.toArray(new RexNode[exprList.size()]));
    }

    /**
     * Derives the return type of a call to an operator.
     *
     * @param op the operator being called
     * @param typeFactory factory for return type
     * @param exprs actual operands
     *
     * @return derived type
     */
    public RelDataType deriveReturnType(
        SqlOperator op,
        RelDataTypeFactory typeFactory,
        RexNode [] exprs)
    {
        return op.inferReturnType(new RexCallBinding(typeFactory, op, exprs));
    }

    /**
     * Creates a reference to an aggregate call, checking for repeated calls.
     */
    public RexNode addAggCall(
        AggregateCall aggCall,
        int groupCount,
        List<AggregateCall> aggCalls,
        Map<AggregateCall, RexNode> aggCallMapping)
    {
        RexNode rex = aggCallMapping.get(aggCall);
        if (rex == null) {
            int index = aggCalls.size() + groupCount;
            aggCalls.add(aggCall);
            rex = makeInputRef(aggCall.getType(), index);
            aggCallMapping.put(aggCall, rex);
        }
        return rex;
    }

    /**
     * Creates a call to a windowed agg.
     */
    public RexNode makeOver(
        RelDataType type,
        SqlAggFunction operator,
        RexNode [] exprs,
        RexNode [] partitionKeys,
        RexNode [] orderKeys,
        SqlNode lowerBound,
        SqlNode upperBound,
        boolean physical,
        boolean allowPartial,
        boolean nullWhenCountZero)
    {
        assert operator != null;
        assert exprs != null;
        assert partitionKeys != null;
        assert orderKeys != null;
        final RexWindow window =
            new RexWindow(
                partitionKeys,
                orderKeys,
                lowerBound,
                upperBound,
                physical);
        final RexOver over = new RexOver(type, operator, exprs, window);
        RexNode result = over;

        // This should be correct but need time to go over test results.
        // Also want to look at combing with section below. if
        // (nullWhenCountZero) { final RelDataType bigintType =
        // getTypeFactory().createSqlType( SqlTypeName.BIGINT); result =
        // makeCall( SqlStdOperatorTable.caseOperator, makeCall(
        // SqlStdOperatorTable.greaterThanOperator, new RexOver( bigintType,
        // SqlStdOperatorTable.countOperator, exprs, window), makeLiteral( //
        // todo: read bound new BigDecimal(0), bigintType,
        // SqlTypeName.DECIMAL)), over, constantNull); }
        if (!allowPartial) {
            Util.permAssert(physical, "DISALLOW PARTIAL over RANGE");
            final RelDataType bigintType =
                getTypeFactory().createSqlType(
                    SqlTypeName.BIGINT);
            // todo: read bound
            result =
                makeCall(
                    SqlStdOperatorTable.caseOperator,
                    makeCall(
                        SqlStdOperatorTable.greaterThanOrEqualOperator,
                        new RexOver(
                            bigintType,
                            SqlStdOperatorTable.countOperator,
                            RexNode.EMPTY_ARRAY,
                            window),
                        makeLiteral(
                            new BigDecimal(2),
                            bigintType,
                            SqlTypeName.DECIMAL)),
                    over,
                    constantNull);
        }
        return result;
    }

    /**
     * Creates a constant for the SQL <code>NULL</code> value.
     */
    public RexLiteral constantNull()
    {
        return constantNull;
    }

    /**
     * Creates an expression referencing a correlation variable.
     *
     * @param type Type of variable
     * @param name Name of variable
     *
     * @return Correlation variable
     */
    public RexNode makeCorrel(
        RelDataType type,
        String name)
    {
        return new RexCorrelVariable(name, type);
    }

    /**
     * Creates an invocation of the NEW operator.
     *
     * @param type Type to be instantiated
     * @param exprs Arguments to NEW operator
     *
     * @return Expression invoking NEW operator
     */
    public RexNode makeNewInvocation(
        RelDataType type,
        RexNode [] exprs)
    {
        return new RexCall(
            type,
            SqlStdOperatorTable.newOperator,
            exprs);
    }

    /**
     * Creates a call to the CAST operator, expanding if possible.
     *
     * @param type Type to cast to
     * @param exp Expression being cast
     *
     * @return Call to CAST operator
     */
    public RexNode makeCast(
        RelDataType type,
        RexNode exp)
    {
        return makeAbstractCast(type, exp);
    }

    /**
     * Creates a call to the CAST operator.
     *
     * @param type Type to cast to
     * @param exp Expression being cast
     *
     * @return Call to CAST operator
     */
    public RexNode makeAbstractCast(
        RelDataType type,
        RexNode exp)
    {
        return new RexCall(
            type,
            SqlStdOperatorTable.castFunc,
            new RexNode[] { exp });
    }

    /**
     * Makes a reinterpret cast.
     *
     * @param type type returned by the cast
     * @param exp expression to be casted
     * @param checkOverflow whether an overflow check is required
     *
     * @return a RexCall with two operands and a special return type
     */
    public RexNode makeReinterpretCast(
        RelDataType type,
        RexNode exp,
        RexNode checkOverflow)
    {
        RexNode [] args;
        if ((checkOverflow != null) && checkOverflow.isAlwaysTrue()) {
            args = new RexNode[] { exp, checkOverflow };
        } else {
            args = new RexNode[] { exp };
        }
        return new RexCall(
            type,
            SqlStdOperatorTable.reinterpretOperator,
            args);
    }

    /**
     * Makes an expression which converts a value of type T to a value of type T
     * NOT NULL, or throws if the value is NULL. If the expression is already
     * NOT NULL, does nothing.
     */
    public RexNode makeNotNullCast(RexNode expr)
    {
        RelDataType type = expr.getType();
        if (!type.isNullable()) {
            return expr;
        }
        RelDataType typeNotNull =
            getTypeFactory().createTypeWithNullability(type, false);
        return new RexCall(
            typeNotNull,
            SqlStdOperatorTable.castFunc,
            new RexNode[] {
                expr
            });
    }

    /**
     * Creates a reference to all the fields in the row. That is, the whole row
     * as a single record object.
     *
     * @param rowType Type of the input row.
     */
    public RexNode makeRangeReference(RelDataType rowType)
    {
        return new RexRangeRef(rowType, 0);
    }

    /**
     * Creates a reference to all the fields in the row.
     *
     * <p>For example, if the input row has type <code>T{f0,f1,f2,f3,f4}</code>
     * then <code>makeRangeReference(T{f0,f1,f2,f3,f4}, S{f3,f4}, 3)</code> is
     * an expression which yields the last 2 fields.
     *
     * @param type Type of the resulting range record.
     * @param offset Index of first field.
     * @param nullable Whether the record is nullable.
     */
    public RexNode makeRangeReference(
        RelDataType type,
        int offset,
        boolean nullable)
    {
        if (nullable && !type.isNullable()) {
            type =
                typeFactory.createTypeWithNullability(
                    type,
                    nullable);
        }
        return new RexRangeRef(type, offset);
    }

    /**
     * Creates a referenence to a given field of the input record.
     *
     * @param type Type of field
     * @param i Ordinal of field
     *
     * @return Reference to field
     */
    public RexNode makeInputRef(
        RelDataType type,
        int i)
    {
        type = SqlTypeUtil.addCharsetAndCollation(type, typeFactory);
        return new RexInputRef(i, type);
    }

    /**
     * Creates a literal representing a flag.
     *
     * @param flag Flag value; must be either a {@link
     * org.eigenbase.util14.Enum14.Value} or a {@link Enum}, and hence a {@link
     * Comparable}.
     */
    public RexLiteral makeFlag(
        Object flag)
    {
        assert flag != null;
        assert (flag instanceof EnumeratedValues.Value)
            || (flag instanceof Enum);
        assert flag instanceof Comparable;
        return makeLiteral(
            (Comparable) flag,
            typeFactory.createSqlType(SqlTypeName.SYMBOL),
            SqlTypeName.SYMBOL);
    }

    /**
     * Internal method to create a call to a literal. Code outside this package
     * should call one of the type-specific methods such as {@link
     * #makeDateLiteral(Calendar)}, {@link #makeLiteral(boolean)}, {@link
     * #makeLiteral(String)}.
     *
     * @param o Value of literal, must be appropriate for the type
     * @param type Type of literal
     * @param typeName SQL type of literal
     *
     * @return Literal
     */
    protected RexLiteral makeLiteral(
        Comparable o,
        RelDataType type,
        SqlTypeName typeName)
    {
        // All literals except NULL have NOT NULL types.
        type = typeFactory.createTypeWithNullability(type, o == null);
        if (typeName == SqlTypeName.CHAR) {
            // Character literals must have a charset and collation. Populate
            // from the type if necessary.
            NlsString nlsString = (NlsString) o;
            if ((nlsString.getCollation() == null)
                || (nlsString.getCharset() == null))
            {
                assert type.getSqlTypeName() == SqlTypeName.CHAR;
                assert type.getCharset().name() != null;
                assert type.getCollation() != null;
                o = new NlsString(
                    nlsString.getValue(),
                    type.getCharset().name(),
                    type.getCollation());
            }
        }
        return new RexLiteral(o, type, typeName);
    }

    /**
     * Creates a boolean literal.
     */
    public RexLiteral makeLiteral(boolean b)
    {
        return b ? booleanTrue : booleanFalse;
    }

    /**
     * Creates a numeric literal.
     */
    public RexLiteral makeExactLiteral(BigDecimal bd)
    {
        RelDataType relType;
        int scale = bd.scale();
        long l = bd.unscaledValue().longValue();
        assert ((scale >= 0) && (scale <= SqlTypeName.MAX_NUMERIC_SCALE));
        assert (BigDecimal.valueOf(l, scale).equals(bd));
        if (scale == 0) {
            if ((l >= Integer.MIN_VALUE) && (l <= Integer.MAX_VALUE)) {
                relType = typeFactory.createSqlType(SqlTypeName.INTEGER);
            } else {
                relType = typeFactory.createSqlType(SqlTypeName.BIGINT);
            }
        } else {
            int precision = bd.unscaledValue().toString().length();
            relType =
                typeFactory.createSqlType(
                    SqlTypeName.DECIMAL,
                    scale,
                    precision);
        }
        return makeExactLiteral(bd, relType);
    }

    /**
     * Creates a numeric literal.
     */
    public RexLiteral makeExactLiteral(BigDecimal bd, RelDataType type)
    {
        return makeLiteral(
            bd,
            type,
            SqlTypeName.DECIMAL);
    }

    /**
     * Creates a byte array literal.
     */
    public RexLiteral makeBinaryLiteral(byte [] byteArray)
    {
        return makeLiteral(
            ByteBuffer.wrap(byteArray),
            typeFactory.createSqlType(SqlTypeName.BINARY,
                byteArray.length),
            SqlTypeName.BINARY);
    }

    /**
     * Creates a double-precision literal.
     */
    public RexLiteral makeApproxLiteral(BigDecimal bd)
    {
        // Validator should catch if underflow is allowed
        // If underflow is allowed, let underflow become zero
        if (bd.doubleValue() == 0) {
            bd = BigDecimal.ZERO;
        }
        return makeApproxLiteral(
            bd,
            typeFactory.createSqlType(SqlTypeName.DOUBLE));
    }

    /**
     * Creates an approximate numeric literal (double or float).
     *
     * @param bd literal value
     * @param type approximate numeric type
     *
     * @return new literal
     */
    public RexLiteral makeApproxLiteral(BigDecimal bd, RelDataType type)
    {
        assert (SqlTypeFamily.APPROXIMATE_NUMERIC.getTypeNames().contains(
            type.getSqlTypeName()));
        return makeLiteral(
            bd,
            type,
            SqlTypeName.DOUBLE);
    }

    /**
     * Creates a character string literal.
     *
     * @pre s != null
     */
    public RexLiteral makeLiteral(String s)
    {
        return makePreciseStringLiteral(s);
    }

    /**
     * Creates a character string literal with type CHAR and default charset and
     * collation.
     *
     * @param s String value
     *
     * @return Character string literal
     */
    protected RexLiteral makePreciseStringLiteral(String s)
    {
        Util.pre(s != null, "s != null");
        if (s.equals("")) {
            return charEmpty;
        } else {
            return makeLiteral(
                new NlsString(s, null, null),
                typeFactory.createSqlType(
                    SqlTypeName.CHAR,
                    s.length()),
                SqlTypeName.CHAR);
        }
    }

    /**
     * Creates a character string literal from an {@link NlsString}.
     *
     * <p>If the string's charset and collation are not set, uses the system
     * defaults.
     *
     * @pre str != null
     */
    public RexLiteral makeCharLiteral(NlsString str)
    {
        Util.pre(str != null, "str != null");
        RelDataType type = SqlUtil.createNlsStringType(typeFactory, str);
        return makeLiteral(str, type, SqlTypeName.CHAR);
    }

    /**
     * Creates a Date literal.
     *
     * @pre date != null
     */
    public RexLiteral makeDateLiteral(Calendar date)
    {
        Util.pre(date != null, "date != null");
        return makeLiteral(
            date,
            typeFactory.createSqlType(SqlTypeName.DATE),
            SqlTypeName.DATE);
    }

    /**
     * Creates a Time literal.
     *
     * @pre time != null
     */
    public RexLiteral makeTimeLiteral(
        Calendar time,
        int precision)
    {
        Util.pre(time != null, "time != null");
        return makeLiteral(
            time,
            typeFactory.createSqlType(SqlTypeName.TIME, precision),
            SqlTypeName.TIME);
    }

    /**
     * Creates a Timestamp literal.
     *
     * @pre timestamp != null
     */
    public RexLiteral makeTimestampLiteral(
        Calendar timestamp,
        int precision)
    {
        Util.pre(timestamp != null, "timestamp != null");
        return makeLiteral(
            timestamp,
            typeFactory.createSqlType(SqlTypeName.TIMESTAMP, precision),
            SqlTypeName.TIMESTAMP);
    }

    /**
     * Creates an interval literal.
     */
    public RexLiteral makeIntervalLiteral(
        SqlIntervalQualifier intervalQualifier)
    {
        Util.pre(intervalQualifier != null, "intervalQualifier != null");
        return makeLiteral(
            null,
            typeFactory.createSqlIntervalType(intervalQualifier),
            intervalQualifier.isYearMonth() ? SqlTypeName.INTERVAL_YEAR_MONTH
            : SqlTypeName.INTERVAL_DAY_TIME);
    }

    /**
     * Creates an interval literal.
     */
    public RexLiteral makeIntervalLiteral(
        long l,
        SqlIntervalQualifier intervalQualifier)
    {
        return makeLiteral(
            new BigDecimal(l),
            typeFactory.createSqlIntervalType(intervalQualifier),
            intervalQualifier.isYearMonth() ? SqlTypeName.INTERVAL_YEAR_MONTH
            : SqlTypeName.INTERVAL_DAY_TIME);
    }

    /**
     * Creates a reference to a dynamic parameter
     *
     * @param type Type of dynamic parameter
     * @param index Index of dynamic parameter
     *
     * @return Expression referencing dynamic parameter
     */
    public RexDynamicParam makeDynamicParam(
        RelDataType type,
        int index)
    {
        return new RexDynamicParam(type, index);
    }

    /**
     * Creates an expression corresponding to a null literal, cast to a specific
     * type and precision
     *
     * @param typeName name of the type that the null will be cast to
     * @param precision precision of the type
     *
     * @return created expression
     */
    public RexNode makeNullLiteral(SqlTypeName typeName, int precision)
    {
        RelDataType type =
            typeFactory.createTypeWithNullability(
                typeFactory.createSqlType(typeName, precision),
                true);
        return makeCast(
            type,
            constantNull());
    }

    /**
     * Creates a literal whose value is NULL, with a particular type.
     *
     * <p>The typing is necessary because RexNodes are strictly typed. For
     * example, in the Rex world the <code>NULL</code> parameter to <code>
     * SUBSTRING(NULL FROM 2 FOR 4)</code> must have a valid VARCHAR type so
     * that the result type can be determined.
     *
     * @param typeName Type to cast NULL to
     *
     * @return NULL literal of given type
     */
    public RexNode makeNullLiteral(SqlTypeName typeName)
    {
        RelDataType type =
            typeFactory.createTypeWithNullability(
                typeFactory.createSqlType(typeName),
                true);
        return makeCast(
            type,
            constantNull());
    }

    /**
     * Creates a copy of an expression, which may have been created using a
     * different RexBuilder and/or {@link RelDataTypeFactory}, using this
     * RexBuilder.
     *
     * @param expr Expression
     *
     * @return Copy of expression
     *
     * @see RelDataTypeFactory#copyType(RelDataType)
     */
    public RexNode copy(RexNode expr)
    {
        return expr.accept(new RexCopier(this));
    }
}

// End RexBuilder.java
