/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 2002, International
 * Business Machines Corporation., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
package org.apache.xpath.impl;

import org.apache.xpath.XPath20Exception;
import org.apache.xpath.expression.Expr;
import org.apache.xpath.expression.NodeTest;
import org.apache.xpath.expression.StepExpr;
import org.apache.xpath.expression.Visitor;
import org.apache.xpath.impl.parser.Axis;
import org.apache.xpath.impl.parser.Node;
import org.apache.xpath.impl.parser.SimpleNode;
import org.apache.xpath.impl.parser.XPath;
import org.apache.xpath.impl.parser.XPathTreeConstants;


/**
 * Default implementation of step. 
 */
public class StepExprImpl extends ExprImpl implements StepExpr
{
    final static boolean[] AXIS_FORWARD = 
                                          {
                                              false, true, true, false, true,
                                              true, true, false, true, false,
                                              true, false, true, false
                                          };

    /** Used when no axis apparent */
    private final short NO_AXIS_TYPE = -2;
    
    /** Used when this is a primary expr.  */
    private final short STEP_IS_PRIMARYEXPR = -1;

    /**
     * Axis type. STEP_IS_PRIMARYEXPR whenever this step expr is a primary expr
     */
    short m_axisType = NO_AXIS_TYPE;

    /**
     * Constructor for StepExprImpl. Internal uses only
     * @param i
     */
    public StepExprImpl(int i)
    {
        super(i);           
    }

    /**
     * Constructor for StepExprImpl. Internal uses only
     * @param p
     * @param i
     */
    public StepExprImpl(XPath p, int i)
    {
        super(p, i);
    }

    /**
     * Constructor for factory. Internal uses only
     * @param axisType
     * @param NodeTest
     */
    public StepExprImpl(short axisType, NodeTest nodeTest)
    {
        super(XPathTreeConstants.JJTSTEPEXPR);

        m_axisType = axisType;
        super.jjtAddChild((Node) nodeTest, 0);

        //  super.jjtAddChild(new OperatorImpl(XPathTreeConstants.JJTPREDICATES), 1);
    }
    
    /**
     * Constructor for cloning     
     */
    private StepExprImpl(StepExprImpl step)
    {
    	super(XPathTreeConstants.JJTSTEPEXPR);
    	
    	m_axisType = step.m_axisType;    	    	
    	m_children = step.cloneChildren();
    }

    /**
     * @see org.apache.xpath.expression.StepExpr#getPredicateAt(int)
     */
    public Expr getPredicateAt(int i)
    {
        return (Expr) m_children[i + 1];
    }

    /**
     * @see org.apache.xpath.expression.StepExpr#getPredicateCount()
     */
    public int getPredicateCount()
    {
        return m_children.length - 1;
    }

    /**
     * @see org.apache.xpath.expression.StepExpr#appendPredicate(Expr)
     */
    public void appendPredicate(Expr predicate)
    {
        super.jjtAddChild((Node) predicate, m_children.length);
    }

    /**
     * @see org.apache.xpath.expression.StepExpr#removePredicate(Expr)
     */
    public void removePredicate(Expr predicate) 
    {
		super.jjtRemoveChild((Node) predicate);
    }

    /**
     * @see org.apache.xpath.expression.Visitable#visit(Visitor)
     */
    public boolean visit(Visitor visitor)
    {
        return visitor.visitStep(this);
    }

    /**
     * @see org.apache.xpath.expression.StepExpr#isForwardStep()
     */
    public boolean isForwardStep()
    {
        return (m_axisType != STEP_IS_PRIMARYEXPR) && AXIS_FORWARD[m_axisType];
    }

    /**
     * @see org.apache.xpath.expression.StepExpr#isReversedStep()
     */
    public boolean isReversedStep()
    {
        return (m_axisType != STEP_IS_PRIMARYEXPR) && !AXIS_FORWARD[m_axisType];
    }

    /**
     * @see org.apache.xpath.expression.StepExpr#isPrimaryExpr()
     */
    public boolean isFilterStep()
    {
        return m_axisType == STEP_IS_PRIMARYEXPR;
    }

    /**
     * @see org.apache.xpath.expression.StepExpr#getAxisType()
     */
    public short getAxisType() throws XPath20Exception
    {
        if (m_axisType == STEP_IS_PRIMARYEXPR)
        {
            throw new XPath20Exception("Invalid call of this method on primary expression");
        }

        return m_axisType;
    }

    /**
     * @see org.apache.xpath.expression.StepExpr#setAxisType(short)
     */
    public void setAxisType(short newType) throws XPath20Exception
    {
        if (m_axisType == STEP_IS_PRIMARYEXPR)
        {
            throw new XPath20Exception("Invalid call of this method on primary expression");
        }

        m_axisType = newType;
    }

    /**
     * @see org.apache.xpath.expression.StepExpr#getAxisName()
     */
    public String getAxisName() throws XPath20Exception
    {
        if (m_axisType == STEP_IS_PRIMARYEXPR)
        {
            throw new XPath20Exception("Invalid call of this method on primary expression");
        }

        return StepExprImpl.FULL_AXIS_NAME[m_axisType];
    }

    /**
     * @see org.apache.xpath.expression.StepExpr#getStepNodeTest()
     */
    public NodeTest getNodeTest() throws XPath20Exception
    {
        if (m_axisType == STEP_IS_PRIMARYEXPR)
        {
            throw new XPath20Exception("Invalid call of this method on step compose of primary expression");
        }

        return (NodeTest) m_children[0];
    }
    
	/* (non-Javadoc)
	 * @see org.apache.xpath.expression.StepExpr#setNodeTest(org.apache.xpath.expression.NodeTest)
	 */
	public void setNodeTest(NodeTest test) throws XPath20Exception {
		if (m_axisType == STEP_IS_PRIMARYEXPR)
		{
				throw new XPath20Exception("Invalid call of this method on step compose of primary expression");
		}
		super.jjtAddChild((Node) test, 0);
	}


    /**
     * @see org.apache.xpath.expression.StepExpr#getPrimaryExpr()
     */
    public Expr getPrimaryExpr() throws XPath20Exception
    {
        if (m_axisType != STEP_IS_PRIMARYEXPR)
        {
            throw new XPath20Exception("Invalid call of this method on step compose of node test");
        }

        return (Expr) m_children[0];
    }

    /**
     * @see org.apache.xpath.expression.Expr#getExprType()
     */
    public short getExprType()
    {
        return StepExprImpl.STEP;
    }

    /**
     * @see org.apache.xpath.expression.Expr#cloneExpression()
     */
    public Expr cloneExpression()
    {
		return new StepExprImpl(this);
    }

    /**
     * @see org.apache.xpath.impl.parser.Node#jjtAddChild(Node, int)
     */
    final public void jjtAddChild(Node n, int i)
    {
        switch (n.getId())
        {
            case XPathTreeConstants.JJTAT:
                m_axisType = AXIS_ATTRIBUTE;

                break;
			case XPathTreeConstants.JJTDOT:
				m_axisType = AXIS_SELF;
				super.jjtAddChild(n, 0);
			break;
            case XPathTreeConstants.JJTAXISCHILD:
            case XPathTreeConstants.JJTAXISDESCENDANT:
            case XPathTreeConstants.JJTAXISANCESTOR:
            case XPathTreeConstants.JJTAXISSELF:
            case XPathTreeConstants.JJTAXISDESCENDANTORSELF:
            case XPathTreeConstants.JJTAXISFOLLOWINGSIBLING:
            case XPathTreeConstants.JJTAXISFOLLOWING:
            case XPathTreeConstants.JJTAXISNAMESPACE:
            case XPathTreeConstants.JJTAXISPARENT:
            case XPathTreeConstants.JJTAXISPRECEDINGSIBLING:
            case XPathTreeConstants.JJTAXISPRECEDING:
            case XPathTreeConstants.JJTAXISANCESTORORSELF:
			case XPathTreeConstants.JJTAXISATTRIBUTE:

                m_axisType = ((Axis) n).getAxis();

                break;

            case XPathTreeConstants.JJTNODETEST:

                if (m_axisType == NO_AXIS_TYPE)
                {
                    // NodeTest production
                    m_axisType = AXIS_CHILD;
                    super.jjtAddChild(n.jjtGetChild(0), 0);
                }
                else
                {
                    // reduce
                    super.jjtAddChild(n.jjtGetChild(0), 0);
                }

                break;

            case XPathTreeConstants.JJTDOTDOT:
                m_axisType = AXIS_PARENT;

                KindTestImpl node = new KindTestImpl();
                node.m_kindTest = NodeTest.ANY_KIND_TEST;
                super.jjtAddChild(node, 0);

                break;

            case XPathTreeConstants.JJTINTEGERLITERAL:
            case XPathTreeConstants.JJTSTRINGLITERAL:
            case XPathTreeConstants.JJTDECIMALLITERAL:
            case XPathTreeConstants.JJTDOUBLELITERAL:
            case XPathTreeConstants.JJTFUNCTIONCALL:
            case XPathTreeConstants.JJTVARNAME:
                m_axisType = STEP_IS_PRIMARYEXPR;
                super.jjtAddChild(n, 0);

                break;

            case XPathTreeConstants.JJTEXPRSEQUENCE:
                m_axisType = STEP_IS_PRIMARYEXPR;

			
                if (((SimpleNode) n).canBeReduced())
                {
                    super.jjtAddChild(n.jjtGetChild(0), 0);
                }
                else
                {
                    super.jjtAddChild(n, 0);
                }

                break;

            case XPathTreeConstants.JJTPREDICATES:				
                int size = n.jjtGetNumChildren();

                for (int j = 0; j < size; j++)
                {
                    super.jjtAddChild(n.jjtGetChild(j), size - j);
                }

                break;
                
			case XPathTreeConstants.JJTNAMETEST:				
            default:
				super.jjtAddChild(n, 0);
                //System.out.println("not implemented yet " + n.getId());
        }
    }

    /**
     * @see org.apache.xpath.impl.ExprImpl#getString(StringBuffer, boolean)
     */
    public void getString(StringBuffer expr, boolean abbreviate)
    {
        try
        {
            if (m_axisType == STEP_IS_PRIMARYEXPR)
            {
                ExprImpl p = (ExprImpl) getPrimaryExpr();

                if ((p.getExprType() == SEQUENCE_EXPR))
                {
                    expr.append('(');
                    p.getString(expr, abbreviate);
                    expr.append(')');
                }
                else
                {
                    p.getString(expr, abbreviate);
                }
            }
            else
            {
                if (abbreviate && (m_axisType == AXIS_CHILD))
                {
                    ((SimpleNode) getNodeTest()).getString(expr, abbreviate);
                }
                else if (abbreviate && (m_axisType == AXIS_ATTRIBUTE))
                {
                    expr.append("@");
                    ((SimpleNode) getNodeTest()).getString(expr, abbreviate);
                }
                else if (abbreviate && (m_axisType == AXIS_PARENT)
                             && getNodeTest().isKindTest()
                             && (getNodeTest().getKindTest() == NodeTest.ANY_KIND_TEST))
                {
                    expr.append("..");
                }
                else if (abbreviate && (m_axisType == AXIS_DESCENDANT_OR_SELF)
                             && getNodeTest().isKindTest()
                             && (getNodeTest().getKindTest() == NodeTest.ANY_KIND_TEST))
                {
                    // empty step
                }
                else
                {
                    expr.append(getAxisName()).append("::");
                    ((SimpleNode) getNodeTest()).getString(expr, abbreviate);
                }
            }

            // Predicates
            int size = getPredicateCount();

            for (int i = 0; i < size; i++)
            {
                expr.append('[');
                ((ExprImpl) getPredicateAt(i)).getString(expr, abbreviate);
                expr.append(']');
            }
        }
        catch (XPath20Exception e)
        {
            // never
        }
    }

    /**
     * Override to print out useful instance data.  
     * @see org.apache.xpath.impl.parser.SimpleNode#toString()
     */
    public String toString()
    {
        return XPathTreeConstants.jjtNodeName[id] + " " 
                + getClass() + " " 
                + ((m_axisType == STEP_IS_PRIMARYEXPR) ? "InvalidAxis" : StepExprImpl.FULL_AXIS_NAME[m_axisType]);
    }
	
}
