/*
// $Id: //open/dev/farrago/src/org/eigenbase/sql/parser/SqlParseException.java#19 $
// Package org.eigenbase is a class library of data management components.
// Copyright (C) 2005-2009 The Eigenbase Project
// Copyright (C) 2004-2009 SQLstream, Inc.
// Copyright (C) 2005-2009 LucidEra, Inc.
// Portions Copyright (C) 2004-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.sql.parser;

import java.util.*;

import org.eigenbase.util14.*;


/**
 * SqlParseException defines a checked exception corresponding to {@link
 * SqlParser}.
 *
 * @author John V. Sichi
 * @version $Id: //open/dev/farrago/src/org/eigenbase/sql/parser/SqlParseException.java#19 $
 */
public class SqlParseException
    extends Exception
    implements EigenbaseParserException
{
    //~ Instance fields --------------------------------------------------------

    private final SqlParserPos pos;
    private final int [][] expectedTokenSequences;
    private final String [] tokenImages;

    /**
     * The original exception thrown by the generated parser. Unfortunately,
     * each generated parser throws exceptions of a different class. So, we keep
     * the exception for forensic purposes, but don't print it publicly.
     *
     * <p>Also, make it transient, because it is a ParseException generated by
     * JavaCC and contains a non-serializable Token.
     */
    private transient final Throwable parserException;

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

    /**
     * Creates a SqlParseException.
     *
     * @param message Message
     * @param pos Position
     * @param expectedTokenSequences Token sequences
     * @param tokenImages Token images
     * @param parserException Parser exception
     */
    public SqlParseException(
        String message,
        SqlParserPos pos,
        int [][] expectedTokenSequences,
        String [] tokenImages,
        Throwable parserException)
    {
        // Cause must be null because the exception generated by JavaCC
        // contains a Token and is therefore not serializable (even though it
        // implements the Serializable interface). This is serious: one
        // non-serializable object poisons the entire chain, so the stack
        // cannot be transmitted over Java RMI.
        super(message, null);
        this.pos = pos;
        this.expectedTokenSequences = expectedTokenSequences;
        this.tokenImages = tokenImages;
        this.parserException = parserException;
    }

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

    /**
     * Returns the position where this error occurred.
     *
     * @return parser position
     */
    public SqlParserPos getPos()
    {
        return pos;
    }

    /**
     * Returns a list of the token names which could have legally occurred at
     * this point.
     *
     * <p>If some of the alternatives contain multiple tokens, returns the last
     * token of only these longest sequences. (This occurs when the parser is
     * maintaining more than the usual lookup.) For instance, if the possible
     * tokens are
     *
     * <blockquote>
     * <pre>
     * {"IN"}
     * {"BETWEEN"}
     * {"LIKE"}
     * {"=", "&lt;IDENTIFIER>"}
     * {"=", "USER"}
     * </pre>
     * </blockquote>
     *
     * returns
     *
     * <blockquote>
     * <pre>
     * "&lt;IDENTIFIER>"
     * "USER"
     * </pre>
     * </blockquote>
     *
     * @return list of token names which could have occurred at this point
     */
    public Collection<String> getExpectedTokenNames()
    {
        if (expectedTokenSequences == null) {
            return Collections.emptyList();
        }
        int maxLength = 0;
        for (int [] expectedTokenSequence : expectedTokenSequences) {
            maxLength = Math.max(expectedTokenSequence.length, maxLength);
        }
        final Set<String> set = new TreeSet<String>();
        for (int [] expectedTokenSequence : expectedTokenSequences) {
            if (expectedTokenSequence.length == maxLength) {
                set.add(
                    tokenImages[
                        expectedTokenSequence[expectedTokenSequence.length - 1]]);
            }
        }
        return set;
    }

    /**
     * Returns the token images.
     *
     * @return token images
     */
    public String [] getTokenImages()
    {
        return tokenImages;
    }

    /**
     * Returns the expected token sequences.
     *
     * @return expected token sequences
     */
    public int [][] getExpectedTokenSequences()
    {
        return expectedTokenSequences;
    }

    // override Exception
    public Throwable getCause()
    {
        return parserException;
    }

    /**
     * Per {@link java.io.Serializable} API, provides a replacement object to be
     * written during serialization.
     *
     * <p>SqlParseException is serializable but is not available on the client.
     * This implementation converts this SqlParseException into a vanilla {@link
     * RuntimeException} with the same message.
     */
    private Object writeReplace()
    {
        return new RuntimeException(
            getClass().getName() + ": " + getMessage());
    }
}

// End SqlParseException.java
