/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package com.sun.jdo.spi.persistence.support.sqlstore.query.jqlc;

import java.util.*;

import persistence.antlr.ANTLRException;
import persistence.antlr.collections.AST;

import com.sun.enterprise.deployment.interfaces.QueryParser;
import com.sun.jdo.api.persistence.support.JDOFatalInternalException;
import com.sun.jdo.spi.persistence.utility.I18NHelper;

/** Helper class to support parsing of JDOQL parameter declarations.
 * 
 * Created on October 16, 2002
 * @author  Michael Bouschen
 */
public class JDOQLParameterDeclarationParser
    implements QueryParser
{
    /**
     * I18N support
     */
    protected final static ResourceBundle messages = I18NHelper.loadBundle(
        JDOQLParameterDeclarationParser.class);

    /**
     * Returns an iterator over the parameter types of the specified JDOQL
     * parameter declartion. The types are represented by their name, thus
     * the Iterator's next method returns Strings. 
     * @param text the JDOQL parameter declaration
     * @return an iterator over parameter types
     * @exception JDOQueryException indicates a parse error
     */
    public Iterator parameterTypeIterator(String text)
    {
        return new ParameterTypeIterator(parse(text));
    }
    
    /**
     * Internal method parsing the JDOQL parameter declaration.
     * @param text the JDOQL parameter declaration
     * @return an AST representing the parameter declarations
     */
    private AST parse(String text)
    {
        if (text == null) {
            return null;
        }

        // return value
        AST paramsAST = null;

        // error message helper
        ErrorMsg errorMsg = new ErrorMsg();

        // create parser
        JQLParser parser = JQLC.createStringParser(text, errorMsg);

        try {
            // start parsing
            parser.parseParameters();
            // get the AST representation
            paramsAST = parser.getAST();
        }
        catch (ANTLRException ex) {
            // handle any exceptions thrown by lexer or parser
            JQLParser.handleANTLRException(ex, errorMsg);
        }

        return paramsAST;
    }
    
   /**
    * Iterator over the parameter types. The next method returns the type
    * of the next parameter represented as String.
    */
    private static class ParameterTypeIterator 
        implements Iterator
    {
        // current parameter declaration node
        private AST current;

        /**
         * The constructor takes the parameter declarations AST. A
         * parameter declaration node must have a PARAMETER_DEF node as
         * root. The first child is the type, the next child is the name of
         * the parameter. All subsequent parameter declarations as siblings
         * of the specified ast node.
         * @param ast the list of parameter declarations nodes
         */
        ParameterTypeIterator(AST ast)
        {
            current = ast;
        }

        /**
         * Returns <code>true</code> if the iteration has more elements.
         * @return <code>true</code> if the iterator has more elements.
         */
        public boolean hasNext()
        {
            return (current != null);
        }
        
        /**
         * Returns the next element in the iteration. For this Iterator it
         * returns the String representation of the type of the next
         * parameter declaration. 
         * @return the type of the next parameter declaration.
         * @exception NoSuchElementException iteration has no more elements.
         */
        public Object next()
        {
            // check whether iteration has no more elements
            if (current == null)
                throw new NoSuchElementException();

            // Check whether the current node has the token type
            // PARAMETER_DEF => throw exception if not.
            if (current.getType() != JQLParser.PARAMETER_DEF)
                throw new JDOFatalInternalException(I18NHelper.getMessage(
                    messages,
                    "jqlc.jdoqlparameterdeclarationparser.next.wrongtoken", //NOI18N
                    current.getType()));

            // get string repr of parameter type node
            String typeRepr = getTypeRepr(current.getFirstChild());

            // advance current ast node to next parameter declaration node
            current = current.getNextSibling();

            return typeRepr;
        }
        
        /**
         * Not supported.
         * @exception UnsupportedOperationException remove is not supported
         * by this Iterator
         */
        public void remove() { throw new UnsupportedOperationException(); }

        /**
         * Internal method to calculate the string representation of a type
         * node.
         * @param ast the type node
         * @return the string representation 
         */
        private String getTypeRepr(AST ast)
        {
            if (ast == null)
                return "";

            // Check for DOT nodes #(DOT left right)
            // They are represented as left.right
            if (ast.getType() == JQLParser.DOT) {
                AST left = ast.getFirstChild();
                AST right = left.getNextSibling();
                return getTypeRepr(left) + "." + getTypeRepr(right);
            }

            // For all other nodes return the token text
            return ast.getText();
        }
    }
    
    /**
     * Method main for testing purposes. Parameter args is expected to a
     * an array of parameter declaration String. One parameter declaration
     * may declare multiple parameters according to the JDOQL parameter
     * declaration syntax. Calling  
     * java com...jqlc.ParameterDeclarationHelper "int a, String b"
     * will print
     * <br><code>
     * Parameter types for >int a, String b<
     * <br>
     *   int
     * <br>
     *   String</code>
     * <br>
     */
    public static void main(String[] args)
    {
        QueryParser helper = new JDOQLParameterDeclarationParser();
        for (int i = 0; i < args.length; i++) {
            String text = args[i];
            System.out.println("Parameter types for >" + text + "<");
            for (Iterator types = helper.parameterTypeIterator(text); 
                 types.hasNext();) {
                System.out.println("  " + types.next());
            }
        }
    }

}
