/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.history;

import java.io.*;
import org.eclipse.persistence.expressions.*;
import org.eclipse.persistence.internal.expressions.*;
import org.eclipse.persistence.internal.helper.*;

/**
 * <b>Purpose:</b>Wraps an immutable value for a past time.
 * A session, query, or expression can be as of a past time.
 * <p>
 * For Oracle 9R2 Flasback corresponds to the sub clause which appears between
 * the table and alias name in the FROM clause:
 * <code>SELECT ... FROM EMPLOYEE AS OF TIMESTAMP (value) t0, ...</code>
 * <p>For generic historical schema support, a special criteria can be added to
 * the where clause for each table in a select:
 * <code>((t0.ROW_START {@literal <=} value) AND ((t0.END IS NULL) OR (t1.END {@literal >} value)))</code>
 * <p><b>Responsibilities:</b>
 * <ul>
 * <li>By default AsOfClause is a timestamp.  To specify a system change number use AsOfSCNClause.
 * <li>For Oracle 9R2 Flashback prints the correct AS OF clause before the alias name in the FROM clause.
 * <li>Read-only: the wrapped value can not change.
 * </ul>
 * @since OracleAS TopLink 10<i>g</i> (10.0.3)
 * @author Stephen McRitchie
 * @see org.eclipse.persistence.expressions.Expression#asOf(AsOfClause)
 * @see org.eclipse.persistence.queries.ObjectLevelReadQuery#setAsOfClause(AsOfClause)
 * @see org.eclipse.persistence.sessions.Session#acquireHistoricalSession(AsOfClause)
 * @see HistoryPolicy
 */
public class AsOfClause implements Serializable {
    public static final AsOfClause NO_CLAUSE = new AsOfClause((Expression)null);
    private Object value;

    protected AsOfClause() {
    }

    public AsOfClause(java.util.Date date) {
        this.value = date;
    }

    public AsOfClause(java.sql.Timestamp timestamp) {
        this.value = timestamp;
    }

    public AsOfClause(java.util.Calendar calendar) {
        this.value = calendar;
    }

    public AsOfClause(long time) {
        this.value = Long.valueOf(time);
    }

    public AsOfClause(Long time) {
        this.value = time;
    }

    protected AsOfClause(Number number) {
        this.value = number;
    }

    public AsOfClause(Expression expression) {
        this.value = expression;
    }

    protected AsOfClause(AsOfClause wrappedValue) {
        this.value = wrappedValue;
    }

    /**
     * INTERNAL:
     * Return if the as of is equal to the other.
     * Equality of asOf clauses is complex (with subclasses),
     * so only use identity.
     */
    public boolean equals(Object object) {
        return this == object;
    }

    /**
     * INTERNAL:
     * Prints the as of clause for an expression inside of the FROM clause.
     */
    public void printSQL(ExpressionSQLPrinter printer) {
        printer.printString("AS OF TIMESTAMP (");
        Object value = getValue();
        if (value instanceof Expression) {
            // Sort of an implementation of native sql.
            // Print AS OF TIMESTAMP (SYSDATE - 1000*60*10) not AS OF ('SYSDATE - 1000*60*10').
            if ((value instanceof ConstantExpression) && (((ConstantExpression)value).getValue() instanceof String)) {
                printer.printString((String)((ConstantExpression)value).getValue());
            } else {
                printer.printExpression((Expression)value);
            }
        } else {
            ConversionManager converter = ConversionManager.getDefaultManager();
            value = converter.convertObject(value, ClassConstants.TIMESTAMP);
            printer.printPrimitive(value);
        }
        printer.printString(")");
    }

    /**
     * PUBLIC:
     * The past time represented by the receiver.  Either a timestamp, a system
     * change number, or an Expression.
     */
    public Object getValue() {
        return value;
    }

    /**
     * PUBLIC:
     * Indicates that <code>value</code> is a system change number or an expression
     * evaluating to one.
     * <p>In Oracle the value will have to be printed using the syntax <code>AS OF SCN(value)</code>
     * instead of <code>AS OF TIMESTAMP(value)</code>.
     * @see AsOfSCNClause
     */
    public boolean isAsOfSCNClause() {
        return false;
    }

    /**
     * PUBLIC:
     * Answers if this is a UniversalAsOfClause, one to be applied
     * to the entire selection criteria.
     * <p>Used when a query is made as of a past time.
     */
    public boolean isUniversal() {
        return false;
    }

    public String toString() {
        StringWriter writer = new StringWriter();
        writer.write(Helper.getShortClassName(getClass()));
        writer.write("(");
        writer.write(String.valueOf(getValue()));
        writer.write(")");
        return writer.toString();
    }
}
