/*

   Derby - Class org.apache.derby.impl.sql.compile.DefaultNode

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses 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	org.apache.derby.impl.sql.compile;

import java.util.List;
import org.apache.derby.catalog.types.DefaultInfoImpl;
import org.apache.derby.shared.common.error.StandardException;
import org.apache.derby.iapi.services.compiler.MethodBuilder;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.sql.compile.CompilerContext;
import org.apache.derby.iapi.sql.compile.Parser;
import org.apache.derby.iapi.sql.compile.Visitable;
import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
import org.apache.derby.iapi.sql.dictionary.DefaultDescriptor;
import org.apache.derby.iapi.sql.dictionary.TableDescriptor;

/**
 * DefaultNode represents a column/parameter default.
 */
public  class DefaultNode extends ValueNode
{
	private String		columnName;
	private String		defaultText;
	private ValueNode	defaultTree;

	/**
     * Constructor for a generated by default as identity column.
     *
     * @param cm            The context manager
     */
    DefaultNode(ContextManager cm) {
        super(cm);
    }

    /**
     * Constructor for a column/parameter default.
	 *
     * @param defaultTree   Query tree for default
	 * @param defaultText	The text of the default.
     * @param cm            The context manager
	 */
    DefaultNode(ValueNode defaultTree,
                String defaultText,
                ContextManager cm) {
        super(cm);
        this.defaultTree = defaultTree;
        this.defaultText = defaultText;
	}

	/**
     * Constructor for insert/update
     * @param columnName The column name for which a default is to be applied
	 */
    DefaultNode(String columnName, ContextManager cm) {
        super(cm);
        this.columnName = columnName;
	}

	/**
	  * Get the text of the default.
	  */
	public	String	getDefaultText()
	{
		return	defaultText;
	}

	/**
	 * Get the query tree for the default.
	 *
	 * @return The query tree for the default.
	 */
	ValueNode getDefaultTree()
	{
		return defaultTree;
	}

	/**
	 * Convert this object to a String.  See comments in QueryTreeNode.java
	 * for how this should be done for tree printing.
	 *
	 * @return	This object as a String
	 */
    @Override
	public String toString()
	{
		if (SanityManager.DEBUG)
		{
			return "columnName: " + columnName + "\n" +
				"defaultText: " + defaultText + "\n" +
				super.toString();
		}
		else
		{
			return "";
		}
	}

    /**
	 * Prints the sub-nodes of this object.  See QueryTreeNode.java for
	 * how tree printing is supposed to work.
	 *
	 * @param depth		The depth of this node in the tree
	 */
    @Override
    void printSubNodes(int depth)
	{
		if (SanityManager.DEBUG)
		{
			super.printSubNodes(depth);

			if (defaultTree != null) {
				printLabel(depth, "defaultTree:");
				defaultTree.treePrint(depth + 1);
			}
		}
	}


	/**
	 * Bind this expression.  This means binding the sub-expressions,
	 * as well as figuring out what the return type is for this expression.
	 * In this case, there are no sub-expressions, and the return type
	 * is already known, so this is just a stub.
	 *
	 * @param fromList		The FROM list for the query this
	 *				expression is in, for binding columns.
	 * @param subqueryList		The subquery list being built as we find SubqueryNodes
     * @param aggregates        The aggregate list being built as we find AggregateNodes
	 *
	 * @return	The new top of the expression tree.
	 *
	 * @exception StandardException		Thrown on failure
	 */
    @Override
    ValueNode bindExpression(FromList fromList, SubqueryList subqueryList, List<AggregateNode> aggregates)
		throws StandardException
	{
		ColumnDescriptor	cd;
		TableDescriptor		td;

		if (SanityManager.DEBUG)
		{
			SanityManager.ASSERT(fromList.size() != 0,
				"fromList expected to be non-empty");
			if (! (fromList.elementAt(0) instanceof FromBaseTable))
			{
				SanityManager.THROWASSERT(
					"fromList.elementAt(0) expected to be instanceof FromBaseTable, not " +
					fromList.elementAt(0).getClass().getName());
			}

		}
		// Get the TableDescriptor for the target table
		td = ((FromBaseTable) fromList.elementAt(0)).getTableDescriptor();

		// Get the ColumnDescriptor for the column
		cd = td.getColumnDescriptor(columnName);
		if (SanityManager.DEBUG)
		{
			SanityManager.ASSERT(cd != null,
				"cd expected to be non-null");
		}

		/* If we have the default text, then parse and bind it and
		 * return the tree.
		 */
		DefaultInfoImpl defaultInfo = (DefaultInfoImpl) cd.getDefaultInfo();
		if (defaultInfo != null)
		{
            String      defaultTxt = defaultInfo.getDefaultText();
            ValueNode   defaultTre = parseDefault(
                defaultTxt,
                getLanguageConnectionContext(),
                getCompilerContext());

			/* Query is dependent on the DefaultDescriptor */
			DefaultDescriptor defaultDescriptor = cd.getDefaultDescriptor(
													getDataDictionary());
			getCompilerContext().createDependency(defaultDescriptor);

            return defaultTre.bindExpression(
									fromList, 
									subqueryList,
                                    aggregates);
		}
		else
		{
			// Default is null
            return new UntypedNullConstantNode(getContextManager());
		}
	}

	/**
	  *	Parse a default and turn it into a query tree.
	  *
	  *	@param	defaultText			Text of Default.
	  * @param	lcc					LanguageConnectionContext
	  * @param	cc					CompilerContext
	  *
	  * @return	The parsed default as a query tree.
	  *
	  * @exception StandardException		Thrown on failure
	  */
	public	static ValueNode	parseDefault
	(
		String						defaultText,
		LanguageConnectionContext	lcc,
		CompilerContext				cc
    )
		throws StandardException
	{
		Parser						p;
		ValueNode					defaultTree;

		/* Get a Statement to pass to the parser */

		/* We're all set up to parse. We have to build a compilable SQL statement
		 * before we can parse -  So, we goober up a VALUES defaultText.
		 */
		String values = "VALUES " + defaultText;
		
		/*
		** Get a new compiler context, so the parsing of the select statement
		** doesn't mess up anything in the current context (it could clobber
		** the ParameterValueSet, for example).
		*/
		CompilerContext newCC = lcc.pushCompilerContext();

		p = newCC.getParser();

		
		/* Finally, we can call the parser */
		// Since this is always nested inside another SQL statement, so topLevel flag
		// should be false
		Visitable qt = p.parseStatement(values);
		if (SanityManager.DEBUG)
		{
			if (! (qt instanceof CursorNode))
			{
				SanityManager.THROWASSERT(
					"qt expected to be instanceof CursorNode, not " +
					qt.getClass().getName());
			}
			CursorNode cn = (CursorNode) qt;
			if (! (cn.getResultSetNode() instanceof RowResultSetNode))
			{
				SanityManager.THROWASSERT(
					"cn.getResultSetNode() expected to be instanceof RowResultSetNode, not " +
					cn.getResultSetNode().getClass().getName());
			}
		}

        defaultTree = ((CursorNode) qt).getResultSetNode().getResultColumns().
                elementAt(0).getExpression();

		lcc.popCompilerContext(newCC);

		return	defaultTree;
	}

	/**
	 * @exception StandardException		Thrown on failure
	 */
    @Override
    void generateExpression(ExpressionClassBuilder acb, MethodBuilder mb)
		throws StandardException
	{
		if (SanityManager.DEBUG)
		{
			SanityManager.THROWASSERT(
				"generateExpression not expected to be called");
		}
	}

    /**
     * @inheritDoc
     */
    boolean isEquivalent(ValueNode other)
    {
		return false;
    }
}
