Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.sun.electric.tool.lang.EvalJavaBsh Maven / Gradle / Ivy
/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: EvalJavaBsh.java
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) 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 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) 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 Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.lang;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.variable.CodeExpression;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.user.Highlighter;
import com.sun.electric.tool.user.User;
import com.sun.electric.tool.user.ui.WindowFrame;
/**
* Used for evaluating Java expressions in Variables
* It is meant to be invoked from the Variable context;
* these methods should not be used from other contexts, and
* thus are declared protected.
*
* This class is thread-safe, but be warned: if multiple threads are hammering
* the bean shell for evaluations, it will slow down a lot due to
* contested locks.
*
* @author gainsley
*/
public class EvalJavaBsh {
// ------------------------ private data ------------------------------------
/** The bean shell interpreter evaluation method */
private static Method evalMethod;
/** The bean shell interpreter source method */
private static Method sourceMethod;
/** The bean shell interpreter set method */
private static Method setMethod;
/** The bean shell interpreter set method */
private static Method getMethod;
/** The bean shell TargetError getTarget method */
private static Method getTargetMethod;
/** The bean shell interpreter class */
private static Class> interpreterClass = null;
/** The bean shell TargetError class */
private static Class> targetErrorClass;
/** The bean shell EvalError class */
private static Class> evalErrorClass;
/** For replacing @variable */
private static final Pattern atPat = Pattern.compile("@(\\w+)");
/** For replacing @variable */
private static final Pattern pPat = Pattern.compile("(P|PAR)\\(\"(\\w+)\"\\)");
/** Results of replacing */
private static HashMap replaceHash = new HashMap();
/** The bean shell interpreter object */
private Object envObject;
/** Context stack for recursive evaluation calls */
private Stack contextStack = new Stack();
/** Info stack for recursive evaluation calls */
private Stack infoStack = new Stack();
/** the singleton object of this class. */
public static final EvalJavaBsh evalJavaBsh = new EvalJavaBsh();
/** turn on Bsh verbose DEBUG statements */
private static boolean DEBUG = false;
/** turn on stack trace statements for exceptions */
private static boolean DEBUGSTACKTRACE = false;
// ------------------------ private and protected methods -------------------
/** the constructor */
public EvalJavaBsh() {
envObject = null;
initBSH();
// if interpreter class is null, we cannot create a new bean shell object
if (interpreterClass == null) {
return;
}
// create the BSH object
try {
envObject = interpreterClass.newInstance();
} catch (Exception e) {
System.out.println("Can't create an instance of the Bean Shell: " + e.getMessage());
envObject = null;
return;
}
setVariable("evalJavaBsh", this);
setVariable("launchingThread", Thread.currentThread());
try {
doEval("Object P(String par) { return evalJavaBsh.P(par); }");
doEval("Object PAR(String par) { return evalJavaBsh.PAR(par); }");
// the following is for running scripts
doEval("import com.sun.electric.tool.user.menus.MenuCommands;");
doEval("import com.sun.electric.database.hierarchy.*;");
doEval("import com.sun.electric.database.prototype.*;");
doEval("import com.sun.electric.database.topology.*;");
doEval("import com.sun.electric.database.variable.ElectricObject;");
doEval("import com.sun.electric.database.variable.FlagSet;");
doEval("import com.sun.electric.database.variable.TextDescriptor;");
doEval("import com.sun.electric.database.variable.VarContext;");
doEval("import com.sun.electric.database.variable.Variable;");
// do not import variable.EvalJavaBsh, because calling EvalJavaBsh.runScript
// will spawn jobs in an unexpected order
doEval("import com.sun.electric.tool.io.*;");
} catch (VarContext.EvalException e) {
e.printStackTrace(System.out);
}
}
/** Get the interpreter so other tools may add methods to it. There is only
* one interpreter, so be careful that separate tools do not conflict in
* terms of namespace. I recommend when adding objects or methods to the
* Interpreter you prepend the object or method names with the Tool name.
*/
// public static Interpreter getInterpreter() { return env; }
/**
* See what the current context of evaluation is.
* @return a VarContext.
*/
public synchronized VarContext getCurrentContext() {
return contextStack.peek();
}
/**
* See what the current info of evaluation is.
* @return an Object.
*/
public synchronized Object getCurrentInfo() {
return infoStack.peek();
}
/**
* Replaces @var calls to P("var")
* Replaces P("var") calls to P("ATTR_var")
* Replaces PAR("var") calls to PAR("ATTR_var")
* @param expr the expression
* @return replaced expression
*/
public static String replace(String expr) {
String result = replaceHash.get(expr);
if (result != null) {
return result;
}
StringBuffer sb = new StringBuffer();
Matcher atMat = atPat.matcher(expr);
while (atMat.find()) {
atMat.appendReplacement(sb, "P(\"" + atMat.group(1) + "\")");
}
atMat.appendTail(sb);
result = sb.toString();
sb = new StringBuffer();
Matcher pMat = pPat.matcher(result);
while (pMat.find()) {
if (pMat.group(2).startsWith("ATTR_")) {
pMat.appendReplacement(sb, pMat.group(0));
} else {
pMat.appendReplacement(sb, pMat.group(1) + "(\"ATTR_" + pMat.group(2) + "\")");
}
}
pMat.appendTail(sb);
result = sb.toString();
if (result.equals(expr)) {
result = expr;
}
replaceHash.put(expr, result);
return result;
}
/** Evaluate Object as if it were a String containing java code.
* Note that this function may call itself recursively.
* @param ce the CodeExpression to be evaluates.
* @param context the context in which the object will be evaluated.
* @param info used to pass additional info from Electric to the interpreter, if needed.
* @return the evaluated object.
*/
public synchronized Object evalVarObject(CodeExpression ce, VarContext context, Object info) throws VarContext.EvalException {
assert ce.isJava();
String expr = replace(ce.getExpr()); // change @var calls to P(var)
if (context == null) {
context = VarContext.globalContext;
}
// check for infinite recursion
for (int i = 0; i < contextStack.size(); i++) {
VarContext vc = contextStack.get(i);
Object inf = infoStack.get(i);
if ((vc == context) && (inf == info)) {
throw new VarContext.EvalException("JavaBeanShell Eval recursion error");
}
}
contextStack.push(context); // push context
infoStack.push(info); // push info
Object ret;
try {
ret = doEval(expr); // ask bsh to evaluate
} catch (VarContext.EvalException e) {
// we need to catch, pop off stacks, and re-throw to maintain
// proper state of stacks.
contextStack.pop();
infoStack.pop();
throw e;
}
contextStack.pop(); // pop context
infoStack.pop(); // pop info
if (DEBUG) {
System.out.println("BSH: " + expr.toString() + " --> " + ret);
}
// if (ret instanceof Number) {
// // get rid of lots of decimal places on floats and doubles
// ret = Variable.format((Number)ret, 3);
// }
return ret;
}
//------------------Methods that may be called through Interpreter--------------
/**
* Method to lookup a variable for evaluation.
* Finds that variable 1 level up the hierarchy.
* @param name the name of the variable to find.
* @return the value of the variable (null if not found).
* @throws VarContext.EvalException
*/
public synchronized Object P(String name) throws VarContext.EvalException {
VarContext context = contextStack.peek();
Object val = context.lookupVarEval(name);
if (DEBUG) {
System.out.println(name + " ---> " + val + " (" + val.getClass() + ")");
}
return val;
}
/**
* Method to lookup a variable for evaluation.
* Finds that variable anywhere up the hierarchy.
* @param name the name of the variable to find.
* @return the value of the variable (null if not found).
* @throws VarContext.EvalException
*/
public synchronized Object PAR(String name) throws VarContext.EvalException {
throw new VarContext.EvalException("The PAR() function has been disabled because "
+ "it confounds the techniques used in Topology.java for detecting when Cell "
+ "instances have the same transistor sizes. RKao");
// VarContext context = (VarContext)contextStack.peek();
// Object val = context.lookupVarFarEval(name);
// if (DEBUG) System.out.println(name + " ---> " + val + " ("+val.getClass()+")");
// return val;
}
//---------------------------Running Scripts-------------------------------------
/** Run a Java Bean Shell script */
public static void runScript(String script) {
runScriptJob job = new runScriptJob(script);
job.startJob();
}
/**
* returns EditingPreferences with default sizes and text descriptors
* @return EditingPreferences with default sizes and text descriptors
*/
public static EditingPreferences getEditingPreferences() {
return Job.getRunningJob().getEditingPreferences();
}
/**
* Display specified Cell after termination of currently running script
* @param cell
*/
public static void displayCell(Cell cell) {
Job curJob = Job.getRunningJob();
if (curJob instanceof runScriptJob) {
((runScriptJob) curJob).displayCell(cell);
}
}
/**
* Method to return the highlighted NodeInsts and ArcInsts to the currently running BSH script.
*/
public static List getHighlighted()
{
Job curJob = Job.getRunningJob();
if (curJob instanceof runScriptJob)
return ((runScriptJob)curJob).highlightedEObjs;
return null;
}
/** Run a Java Bean Shell script */
public static Job runScriptJob(String script) {
return new runScriptJob(script);
}
@SuppressWarnings("serial")
private static class runScriptJob extends Job {
private String script;
private Cell cell;
private List highlightedEObjs;
protected runScriptJob(String script) {
super("JavaBsh script: " + script, User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.script = script;
// cache highlighted objects
highlightedEObjs = null;
WindowFrame wf = WindowFrame.getCurrentWindowFrame(false);
if (wf != null)
{
Highlighter highlighter = wf.getContent().getHighlighter();
if (highlighter != null)
highlightedEObjs = highlighter.getHighlightedEObjs(true, true);
}
}
public boolean doIt() throws JobException {
EvalJavaBsh evaluator = new EvalJavaBsh();
evaluator.doSource(script); // May throw exception
return true;
}
private void displayCell(Cell cell) {
if (this.cell != null) {
return;
}
this.cell = cell;
fieldVariableChanged("cell");
}
@Override
public void terminateOK() {
if (cell != null) {
Job.getUserInterface().displayCell(cell);
}
}
}
// ****************************** REFLECTION FOR ACCESSING THE BEAN SHELL ******************************
private void initBSH() {
// if already initialized, return
if (interpreterClass != null) {
return;
}
// find the BSH classes
try {
interpreterClass = Class.forName("bsh.Interpreter");
targetErrorClass = Class.forName("bsh.TargetError");
evalErrorClass = Class.forName("bsh.EvalError");
} catch (ClassNotFoundException e) {
TextUtils.recordMissingComponent("Bean Shell");
interpreterClass = null;
return;
}
// find the necessary methods on the BSH class
try {
evalMethod = interpreterClass.getMethod("eval", new Class[]{String.class});
sourceMethod = interpreterClass.getMethod("source", new Class[]{String.class});
setMethod = interpreterClass.getMethod("set", new Class[]{String.class, Object.class});
getMethod = interpreterClass.getMethod("get", new Class[]{String.class});
getTargetMethod = targetErrorClass.getMethod("getTarget", (Class[]) null);
} catch (NoSuchMethodException e) {
System.out.println("Can't find methods in the Bean Shell: " + e.getMessage());
interpreterClass = null;
return;
}
}
/**
* Set a variable in the Java Bean Shell
* @param name the name of the variable
* @param value the value to set the variable to
*/
public void setVariable(String name, Object value) {
try {
if (envObject != null) {
setMethod.invoke(envObject, new Object[]{name, value});
}
} catch (Exception e) {
handleInvokeException(e, "Bean shell error setting " + name + " to " + value + ": ");
}
}
public Object getVariable(String name) {
try {
if (envObject != null) {
return getMethod.invoke(envObject, new Object[]{name});
}
} catch (Exception e) {
handleInvokeException(e, "Bean shell error getting variable " + name);
}
return null;
}
// -------------------------- Private Methods -----------------------------
/**
* Evaluate a string containing Java Bean Shell code.
* @param line the string to evaluate
* @return an object representing the evaluated string, or null on error.
* @throws VarContext.EvalException exception
*/
private Object doEval(String line) throws VarContext.EvalException {
Object returnVal = null;
try {
if (envObject != null) {
returnVal = evalMethod.invoke(envObject, new Object[]{line});
}
} catch (Exception e) {
if (e instanceof InvocationTargetException) {
// re-throw original EvalException, if any
VarContext.EvalException ee = getEvalException((InvocationTargetException) e);
if (ee != null) {
throw ee;
}
}
if (!handleInvokeException(e, "Bean shell error evaluating " + line)) {
throw new VarContext.EvalException(e.getMessage(), e);
}
}
return returnVal;
}
public Object doEvalLine(String line) {
Object obj = null;
try {
obj = doEval(line);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
/**
* Method to determine whether a string is valid Java code.
* @param line the string to test.
* @return true if the string is valid, evaluatable Java code.
*/
public boolean isValidJava(String line) {
try {
if (envObject != null) {
evalMethod.invoke(envObject, new Object[]{line});
return true;
}
} catch (Exception e) {
}
return false;
}
/**
* Execute a Java Bean Shell script file.
* @param file the file to run.
*/
public void doSource(String file) throws JobException {
try {
if (envObject != null) {
sourceMethod.invoke(envObject, new Object[]{file});
}
} catch (Exception e) {
String description = "Java Bean shell error sourcing '" + file + "'";
if (e instanceof InvocationTargetException) {
// This wraps an exception thrown by the method invoked.
Throwable t = e.getCause();
if (t != null) {
handleBshError((Exception) t, description);
}
if (evalErrorClass.isInstance(t)) {
// The Bean Shell had an error
t = doGetTarget(t);
}
throw new JobException(t);
} else if (e instanceof IllegalArgumentException) {
System.out.println(description + ": " + e.getMessage());
if (DEBUG) {
e.printStackTrace(System.out);
}
throw new JobException(e);
} else if (e instanceof IllegalAccessException) {
System.out.println(description + ": " + e.getMessage());
if (DEBUG) {
e.printStackTrace(System.out);
}
throw new JobException(e);
} else {
System.out.println("Unhandled Exception: ");
System.out.println(description + ": " + e.getMessage());
e.printStackTrace(System.out);
throw new JobException(e);
}
}
}
private static Throwable doGetTarget(Object ex) {
Throwable returnVal = null;
if (interpreterClass != null && targetErrorClass.isInstance(ex)) {
try {
returnVal = (Throwable) getTargetMethod.invoke(ex, (Object[]) null);
} catch (Exception e) {
handleInvokeException(e, "Java Bean shell error getting exception target");
}
}
return returnVal;
}
/**
* If the InvocationTargetException was generated because of an EvalException,
* get the EvalException that is wrapped by the target exception.
* @param e the invocation target exception
* @return the initial evaluation exception, or null if none.
*/
private VarContext.EvalException getEvalException(InvocationTargetException e) {
Throwable t = e.getCause();
if (t == null) {
return null;
}
Throwable tt = doGetTarget(t);
if (tt == null) {
return null;
}
if (tt instanceof VarContext.EvalException) {
return (VarContext.EvalException) tt;
}
return null;
}
/**
* Handle exceptions thrown by attempting to invoke a reflected method or constructor.
* @param e The exception thrown by the invoked method or constructor.
* @param description a description of the event to be printed with the error message.
* @return true if exception handled (error message printed), false if not
*/
private static boolean handleInvokeException(Exception e, String description) {
boolean handled = false;
if (e instanceof InvocationTargetException) {
// This wraps an exception thrown by the method invoked.
Throwable t = e.getCause();
if (t != null) {
handled = handleBshError((Exception) t, description);
}
} else if (e instanceof IllegalArgumentException) {
System.out.println(description + ": " + e.getMessage());
if (DEBUG) {
e.printStackTrace(System.out);
}
} else if (e instanceof IllegalAccessException) {
System.out.println(description + ": " + e.getMessage());
if (DEBUG) {
e.printStackTrace(System.out);
}
} else {
System.out.println("Unhandled Exception: ");
System.out.println(description + ": " + e.getMessage());
e.printStackTrace(System.out);
handled = false;
}
return handled;
}
/**
* Handle Bean Shell evaluation errors. Sends it to system.out.
* @param e the TargetError exception thrown.
* @param description a description of the event that caused the error to be thrown.
*/
private static boolean handleBshError(Exception e, String description) {
if (targetErrorClass.isInstance(e)) {
// The Bean Shell had an error
Throwable t = doGetTarget(e);
if (t != null) {
if (t instanceof VarContext.EvalException) {
if (DEBUG) {
System.out.println("Java EvalException: " + description + ": " + t.getMessage());
if (DEBUGSTACKTRACE) {
e.printStackTrace(System.out);
}
}
} else {
if (t.getMessage() != null) {
System.out.println(description + ": " + t.getMessage());
} else if (t.getStackTrace() != null) {
System.out.println(description + ": ");
t.printStackTrace(System.out);
} else {
System.out.println(description + ": " + t);
}
if (DEBUGSTACKTRACE) {
e.printStackTrace(System.out);
}
}
}
} else {
System.out.println("Unhandled Java Bsh Exception: " + description + ":\n " + e.getMessage());
if (DEBUGSTACKTRACE) {
e.printStackTrace(System.out);
}
return false;
}
return true;
}
}