/* Copyright (c) 2001-2007, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 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.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS 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 HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR 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.
 */


package org.hsqldb.util.preprocessor;

import java.util.Hashtable;

/* $Id: Defines.java 5793 2018-01-06 13:12:38Z fredt $ */

/**
 * Simple preprocessor symbol table.
 *
 * @author Campbell Burnet (campbell-burnet@users dot sourceforge.net)
 * @version 1.8.1
 * @since 1.8.1
 */
class Defines {
    private Hashtable symbols = new Hashtable();

    public Defines() {}

    public Defines(String csvExpressions) throws PreprocessorException {
        defineCSV(csvExpressions);
    }

    public void clear() {
        this.symbols.clear();
    }

    public void defineCSV(String csvExpressions)
    throws PreprocessorException {
        if (csvExpressions != null) {
            csvExpressions = csvExpressions + ',';

            int start = 0;
            int len   = csvExpressions.length();

            while (start < len) {
                int    end  = csvExpressions.indexOf(',', start);
                String expr = csvExpressions.substring(start, end).trim();

                if (expr.length() > 0) {
                    defineSingle(expr);
                }

                start = end + 1;
            }
        }
    }

    public void defineSingle(String expression) throws PreprocessorException {
        Tokenizer tokenizer = new Tokenizer(expression);

        tokenizer.next();

        if (!tokenizer.isToken(Token.IDENT)) {
            throw new PreprocessorException("IDENT token required at position: "
                    + tokenizer.getStartIndex()
                    + " in ["
                    + expression +
                    "]"); // NOI18N
        }

        String ident = tokenizer.getIdent();

        int tokenType = tokenizer.next();

        switch(tokenType) {
            case Token.EOI : {
                this.symbols.put(ident, ident);
                return;
            }
            case Token.ASSIGN : {
                tokenType = tokenizer.next();
                break;
            }
            default : {
                break;
            }
        }

        switch(tokenType) {
            case Token.NUMBER : {
                Number number = tokenizer.getNumber();

                this.symbols.put(ident, number);

                break;
            }
            case Token.STRING : {
                String string = tokenizer.getString();

                this.symbols.put(ident, string);

                break;
            }
            case Token.IDENT : {
                String rhsIdent = tokenizer.getIdent();

                if (!isDefined(rhsIdent)) {
                    throw new PreprocessorException("Right hand side" +
                            "IDENT token [" + rhsIdent + "] at position: "
                            + tokenizer.getStartIndex()
                            + " is undefined in ["
                            + expression
                            + "]"); // NOI18N
                }

                Object value = this.symbols.get(rhsIdent);

                symbols.put(ident, value);
                break;
            }
            default : {
                throw new PreprocessorException("Right hand side NUMBER,"
                        + "STRING or IDENT token required at position: " +
                        + tokenizer.getStartIndex()
                        + " in ["
                        + expression
                        + "]"); // NOI18N
            }
        }

        tokenizer.next();

        if (!tokenizer.isToken(Token.EOI)) {
            throw new PreprocessorException("Illegal trailing "
                    + "characters at position: "
                    + tokenizer.getStartIndex()
                    + " in ["
                    + expression
                    + "]"); // NOI18N
        }
    }

    public void undefine(String symbol) {
        this.symbols.remove(symbol);
    }

    public boolean isDefined(String symbol) {
        return this.symbols.containsKey(symbol);
    }

    public Object getDefintion(String symbol) {
        return this.symbols.get(symbol);
    }

    public boolean evaluate(String expression) throws PreprocessorException {
        Tokenizer tokenizer = new Tokenizer(expression);

        tokenizer.next();

        Parser parser = new Parser(this, tokenizer);

        boolean result = parser.parseExpression();

        if (!tokenizer.isToken(Token.EOI)) {
            throw new PreprocessorException("Illegal trailing "
                    + "characters at position: "
                    + tokenizer.getStartIndex()
                    + " in ["
                    + expression
                    + "]"); // NOI18N
        }

        return result;
    }

    public String toString() {
        return super.toString() + this.symbols.toString();
    }
}
