/*
 * 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.netbeans.modules.debugger.jpda.expr;

import java.util.Random;

/**
 * Represents an pre-parsed Java expression to be later evaluated in a specific JVM context.
 * The expression can have a 1.4 or 1.5 syntax.
 *
 * 
 * Uses Compiler API
 * 1) Generate a method (like evaluate_<long random number>), which takes all local variables as arguments
 * 2) Add that method into the current source file
 * 3) Compile the current source file, like http://www.javabeat.net/javabeat/java6/articles/java_6_0_compiler_api_4.php
 * 4) Traverse the parsed tree and perform the evaluation - http://java.sun.com/javase/6/docs/jdk/api/javac/tree/com/sun/source/util/JavacTask.html
 * 5) Instead of (4) it would be cool to be able to do a hot-swap. But we can not add new methods. :-(
 * 
 * 
 * @author Martin Entlicher
 */
public class JavaExpression {

    public static final String LANGUAGE_JAVA_1_5 = "1.5.0"; // NOI18N

    private static final String REPLACE_return = "return01234";
    private static final String REPLACE_class = "class01234";

    static final String RETURN_MACRO = "{return}";
    static final String CLASS_MACRO = "{class}";
    
    private String       strExpression;
    private String       language;
    private String       replace_return;
    private String       replace_class;

    /**
     * Creates a new expression by pre-parsing the given String representation of the expression.
     *
     * @param expr textual representation of an expression
     * @param language one of the LANGUAGE_XXX constants
     * @return pre-parsed Java expression
     * @throws ParseException if the expression has wrong syntax
     */
    public static JavaExpression parse (String expr, String language) {
    //throws ParseException {
        String replace_return = REPLACE_return;
        while (expr.indexOf(replace_return) >= 0) {
            replace_return = "return" + new Random().nextLong(); // NOI18N
        }
        String replace_class = REPLACE_class;
        while (expr.indexOf(replace_class) >= 0) {
            replace_class = "class" + new Random().nextLong(); // NOI18N
        }
        String replacedExpr = replaceSpecialVar(expr, RETURN_MACRO, replace_return); // NOI18N
        replacedExpr = replaceSpecialVar(replacedExpr, CLASS_MACRO, replace_class); // NOI18N
        return new JavaExpression(replacedExpr, language, replace_return, replace_class);
    }
    
    private static String replaceSpecialVar(String expr, String var, String replace_var) {
        int i = expr.indexOf(var);
        while (i >= 0) {
            boolean replace;
            if (i > 0) {
                char ch = expr.charAt(i - 1);
                if (Character.isJavaIdentifierStart(ch) ||
                    Character.isJavaIdentifierPart(ch) ||
                    ch == '.') {
                    replace = false;
                } else {
                    replace = true;
                }
            } else {
                replace = true;
            }
            if (replace && i < (expr.length() - var.length())) {
                char ch = expr.charAt(i + var.length());
                if (Character.isJavaIdentifierPart(ch)) {
                    replace = false;
                }
            }
            if (replace) {
                expr = expr.substring(0, i) + replace_var + expr.substring(i + var.length());
                i += replace_var.length();
            } else {
                i += var.length();
            }
            i = expr.indexOf(var, i);
        }
        return expr;
    }
    
    private JavaExpression(String expression, String language,
                       String replace_return, String replace_class) {
        strExpression = expression;
        this.language = language;
        this.replace_return = replace_return;
        this.replace_class = replace_class;
    }

    /**
     * Creates an evaluator engine that can be used to evaluate this expression in a given
     * runtime JVM context.
     *
     * @param context a runtime JVM context
     * @return the evaluator engine
     */
    public TreeEvaluator evaluator(EvaluationContext context,
                                   CompilationInfoHolder ciHolder) {
        return new TreeEvaluator(this, context, ciHolder);
    }

    public String getLanguage() {
        return language;
    }

    public String getExpression() {
        return strExpression;
    }
    
    String returnReplaced() {
        return replace_return;
    }
    
    String classReplaced() {
        return replace_class;
    }
}
