Save This Page
Home » zk-src-3.0.6 » org.zkoss.zk.scripting.bsh » [javadoc | source]
    1   /* BSHInterpreter.java
    2   
    3   {{IS_NOTE
    4   	Purpose:
    5   		
    6   	Description:
    7   		
    8   	History:
    9   		Thu Jun  1 14:28:43     2006, Created by tomyeh
   10   }}IS_NOTE
   11   
   12   Copyright (C) 2006 Potix Corporation. All Rights Reserved.
   13   
   14   {{IS_RIGHT
   15   	This program is distributed under GPL Version 2.0 in the hope that
   16   	it will be useful, but WITHOUT ANY WARRANTY.
   17   }}IS_RIGHT
   18   */
   19   package org.zkoss.zk.scripting.bsh;
   20   
   21   import java.lang.reflect.Field;
   22   import java.util.Iterator;
   23   import java.util.Map;
   24   import java.util.Collection;
   25   
   26   import bsh.BshClassManager;
   27   import bsh.NameSpace;
   28   import bsh.BshMethod;
   29   import bsh.Variable;
   30   import bsh.Primitive;
   31   import bsh.EvalError;
   32   import bsh.UtilEvalError;
   33   
   34   import org.zkoss.lang.Classes;
   35   import org.zkoss.lang.reflect.Fields;
   36   import org.zkoss.xel.Function;
   37   
   38   import org.zkoss.zk.ui.Page;
   39   import org.zkoss.zk.ui.UiException;
   40   import org.zkoss.zk.scripting.Namespace;
   41   import org.zkoss.zk.scripting.NamespaceChangeListener;
   42   import org.zkoss.zk.scripting.util.GenericInterpreter;
   43   import org.zkoss.zk.scripting.SerializableAware;
   44   import org.zkoss.zk.scripting.HierachicalAware;
   45   
   46   /**
   47    * The interpreter that uses BeanShell to interpret zscript codes.
   48    *
   49    * <p>Unlike many other implementations, it supports the hierachical
   50    * scopes ({@link HierachicalAware}).
   51    * That is, it uses an independent BeanShell NameSpace
   52    * (aka. interpreter's scope) to store the variables/classes/methods
   53    * defined in BeanShell script for each ZK namespace ({@link Namespace}).
   54    * Since one-to-one relationship between BeanShell's scope and ZK namespace,
   55    * the invocation of BeanShell methods can execute correctly without knowing
   56    * what namespace it is.
   57    * However, if you want your codes portable across different interpreters,
   58    * you had better to call
   59    * {@link org.zkoss.zk.scripting.Namespaces#beforeInterpret}
   60    * to prepare the proper namespace, before calling any method defined in
   61    * zscript.
   62    *
   63    * @author tomyeh
   64    */
   65   public class BSHInterpreter extends GenericInterpreter
   66   implements SerializableAware, HierachicalAware {
   67   	/** A variable of {@link Namespace}. The value is an instance of
   68   	 * BeanShell's NameSpace.
   69   	 */
   70   	private static final String VAR_NS = "z_bshnS";
   71   	private bsh.Interpreter _ip;
   72   	private GlobalNS _bshns;
   73   
   74   	public BSHInterpreter() {
   75   	}
   76   
   77   	//Deriving to override//
   78   	/** Called when the top-level BeanShell namespace is created.
   79   	 * By default, it does nothing.
   80   	 *
   81   	 * <p>Note: to speed up the performance, this implementation
   82   	 * disabled {@link bsh.NameSpace#loadDefaultImports}.
   83   	 * It only imports the java.lang and java.util packages.
   84   	 * If you want the built command and import packages, you can override
   85   	 * this method. For example,
   86   	 * <pre><code>
   87   	 *protected void loadDefaultImports(NameSpace bshns) {
   88   	 *  bshns.importCommands("/bsh/commands");
   89   	 *}</code></pre>
   90   	 *
   91   	 * @since 3.0.2
   92   	 */
   93       protected void loadDefaultImports(NameSpace bshns) {
   94       }
   95   
   96   	//GenericInterpreter//
   97   	protected void exec(String script) {
   98   		try {
   99   			final Namespace ns = getCurrent();
  100   			if (ns != null) _ip.eval(script, prepareNS(ns));
  101   			else _ip.eval(script); //unlikely (but just in case)
  102   		} catch (EvalError ex) {
  103   			throw UiException.Aide.wrap(ex);
  104   		}
  105   	}
  106   
  107   	protected boolean contains(String name) {
  108   		try {
  109   			return _ip.getNameSpace().getVariable(name) != Primitive.VOID;
  110   				//Primitive.VOID means not defined
  111   		} catch (UtilEvalError ex) {
  112   			throw UiException.Aide.wrap(ex);
  113   		}
  114   	}
  115   	protected Object get(String name) {
  116   		try {
  117   			return Primitive.unwrap(_ip.get(name));
  118   		} catch (EvalError ex) {
  119   			throw UiException.Aide.wrap(ex);
  120   		}
  121   	}
  122   	protected void set(String name, Object val) {
  123   		try {
  124   			_ip.set(name, val);
  125   				//unlike NameSpace.setVariable, _ip.set() handles null
  126   		} catch (EvalError ex) {
  127   			throw UiException.Aide.wrap(ex);
  128   		}
  129   	}
  130   	protected void unset(String name) {
  131   		try {
  132   			_ip.unset(name);
  133   		} catch (EvalError ex) {
  134   			throw UiException.Aide.wrap(ex);
  135   		}
  136   	}
  137   
  138   	protected boolean contains(Namespace ns, String name) {
  139   		if (ns != null) {
  140   			final NameSpace bshns = prepareNS(ns);
  141   				//note: we have to create NameSpace (with prepareNS)
  142   				//to have the correct chain
  143   			if (bshns != _bshns) {
  144   		 		try {
  145   			 		return bshns.getVariable(name) != Primitive.VOID;
  146   				} catch (UtilEvalError ex) {
  147   					throw UiException.Aide.wrap(ex);
  148   				}
  149   			}
  150   		}
  151   		return contains(name);
  152   	}
  153   	protected Object get(Namespace ns, String name) {
  154   		if (ns != null) {
  155   			final NameSpace bshns = prepareNS(ns);
  156   				//note: we have to create NameSpace (with prepareNS)
  157   				//to have the correct chain
  158   			if (bshns != _bshns) {
  159   		 		try {
  160   			 		return Primitive.unwrap(bshns.getVariable(name));
  161   				} catch (UtilEvalError ex) {
  162   					throw UiException.Aide.wrap(ex);
  163   				}
  164   			}
  165   		}
  166   		return get(name);
  167   	}
  168   	protected void set(Namespace ns, String name, Object val) {
  169   		if (ns != null) {
  170   			final NameSpace bshns = prepareNS(ns);
  171   				//note: we have to create NameSpace (with prepareNS)
  172   				//to have the correct chain
  173   			if (bshns != _bshns) {
  174   		 		try {
  175   			 		bshns.setVariable(
  176   			 			name, val != null ? val: Primitive.NULL, false);
  177   		 			return;
  178   				} catch (UtilEvalError ex) {
  179   					throw UiException.Aide.wrap(ex);
  180   				}
  181   			}
  182   		}
  183   		set(name, val);
  184   	}
  185   	protected void unset(Namespace ns, String name) {
  186   		if (ns != null) {
  187   			final NameSpace bshns = prepareNS(ns);
  188   				//note: we have to create NameSpace (with prepareNS)
  189   				//to have the correct chain
  190   			if (bshns != _bshns) {
  191   		 		bshns.unsetVariable(name);
  192   	 			return;
  193   			}
  194   		}
  195   		unset(name);
  196   	}
  197   
  198   	//-- Interpreter --//
  199   	public void init(Page owner, String zslang) {
  200   		super.init(owner, zslang);
  201   
  202   		_ip = new bsh.Interpreter();
  203   		_ip.setClassLoader(Thread.currentThread().getContextClassLoader());
  204   
  205   		_bshns = new GlobalNS(_ip.getClassManager(), "global");
  206   		_ip.setNameSpace(_bshns);
  207   	}
  208   	public void destroy() {
  209   		getOwner().getNamespace().unsetVariable(VAR_NS, false);
  210   		
  211   		//bug 1814819 ,clear variable, dennis
  212   		try{
  213   			_bshns.clear();
  214   			_ip.setNameSpace(null);
  215   		} catch (Throwable t) { //silently ignore (in case of upgrading to new bsh)
  216   		}
  217   		
  218   		_ip = null;
  219   		_bshns = null;
  220   		super.destroy();
  221   	}
  222   
  223   	/** Returns the native interpreter, or null if it is not initialized
  224   	 * or destroyed.
  225   	 * From application's standpoint, it never returns null, and the returned
  226   	 * object must be an instance of {@link bsh.Interpreter}
  227   	 * @since 3.0.2
  228   	 */
  229   	public Object getNativeInterpreter() {
  230   		return _ip;
  231   	}
  232   
  233   	public Class getClass(String clsnm) {
  234   		try {
  235   			return _bshns.getClass(clsnm);
  236   		} catch (UtilEvalError ex) {
  237   			throw new UiException("Failed to load class "+clsnm, ex);
  238   		}
  239   	}
  240   	public Function getFunction(String name, Class[] argTypes) {
  241   		return getFunction0(_bshns, name, argTypes);
  242   	}
  243   	public Function getFunction(Namespace ns, String name, Class[] argTypes) {
  244   		return getFunction0(prepareNS(ns), name, argTypes);
  245   	}
  246   	private Function getFunction0(NameSpace bshns, String name, Class[] argTypes) {
  247   		try {
  248   		 	final BshMethod m = bshns.getMethod(
  249   		 		name, argTypes != null ? argTypes: new Class[0], false);
  250   		 	return m != null ? new BSHFunction(m): null;
  251   		} catch (UtilEvalError ex) {
  252   			throw UiException.Aide.wrap(ex);
  253   		}
  254   	}
  255   
  256   	/** Prepares the namespace for non-top-level namespace.
  257   	 */
  258   	private NameSpace prepareNS(Namespace ns) {
  259   		if (ns == getOwner().getNamespace())
  260   			return _bshns;
  261   
  262   		NSX nsx = (NSX)ns.getVariable(VAR_NS, true);
  263   		if (nsx != null)
  264   			return nsx.ns;
  265   
  266   		//bind bshns and ns
  267   		Namespace p = ns.getParent();
  268   		NameSpace bshns = //Bug 1831534: we have to pass class manager
  269   			new NS(p != null ? prepareNS(p): _bshns, _ip.getClassManager(), ns);
  270   				//Bug 1899353: we have to use _bshns instead of null
  271   				//Reason: unknown
  272   		ns.setVariable(VAR_NS, new NSX(bshns), true);
  273   		return bshns;
  274   	}
  275   	/** Prepares the namespace for detached components. */
  276   	private static NameSpace prepareDetachedNS(Namespace ns) {
  277   		NSX nsx = (NSX)ns.getVariable(VAR_NS, true);
  278   		if (nsx != null)
  279   			return nsx.ns;
  280   
  281   		//bind bshns and ns
  282   		Namespace p = ns.getParent();
  283   		NameSpace bshns = new NS(p != null ? prepareDetachedNS(p): null, null, ns);
  284   		ns.setVariable(VAR_NS, new NSX(bshns), true);
  285   		return bshns;
  286   	}
  287   
  288   	//supporting classes//
  289   	/** The global namespace. */
  290   	private static abstract class AbstractNS extends NameSpace {
  291   		private boolean _inGet;
  292   
  293   	    protected AbstractNS(NameSpace parent, BshClassManager classManager,
  294   	    String name) {
  295   	    	super(parent, classManager, name);
  296   	    }
  297   
  298   		/** Deriver has to override this method. */
  299   		abstract protected Object getFromNamespace(String name);
  300   
  301   		//super//
  302   		protected Variable getVariableImpl(String name, boolean recurse)
  303   		throws UtilEvalError {
  304   			//Note: getVariableImpl returns null if not defined,
  305   			//while getVariable return Primitive.VOID if not defined
  306   
  307   			//Tom M Yeh: 20060606:
  308   			//We cannot override getVariable because BeanShell use
  309   			//getVariableImpl to resolve a variable recusrivly
  310   			//
  311   			//setVariable will callback this method,
  312   			//so use _inGet to prevent dead loop
  313   			Variable var = super.getVariableImpl(name, recurse);
  314   			if (!_inGet && var == null) {
  315   				Object v = getFromNamespace(name);
  316   				if (v != UNDEFINED) {
  317   			//Variable has no public/protected contructor, so we have to
  318   			//store the value back (with setVariable) and retrieve again
  319   					_inGet = true;
  320   					try {
  321   						this.setVariable(name,
  322   							v != null ? v: Primitive.NULL, false);
  323   						var = super.getVariableImpl(name, false);
  324   						this.unsetVariable(name); //restore
  325   					} finally {
  326   						_inGet = false;
  327   					}
  328   				}
  329   			}
  330   			return var;
  331   		}
  332   	    public void loadDefaultImports() {
  333   	    	 //to speed up the formance
  334   	    }
  335   	}
  336   	/** The global NameSpace. */
  337   	private class GlobalNS extends AbstractNS {
  338   	    private GlobalNS(BshClassManager classManager,
  339   	    String name) {
  340   	    	super(null, classManager, name);
  341   	    }
  342   		protected Object getFromNamespace(String name) {
  343   			return BSHInterpreter.this.getFromNamespace(name);
  344   		}
  345   	    public void loadDefaultImports() {
  346   	    	BSHInterpreter.this.loadDefaultImports(this);
  347   	    }
  348   	}
  349   	/** The per-Namespace NameSpace. */
  350   	private static class NS extends AbstractNS {
  351   		private final Namespace _ns;
  352   
  353   		private NS(NameSpace parent, BshClassManager classManager, Namespace ns) {
  354   			super(parent, classManager, "ns" + System.identityHashCode(ns));
  355   			_ns = ns;
  356   			_ns.addChangeListener(new NSCListener(this));
  357   		}
  358   
  359   		//super//
  360   		/** Search _ns instead. */
  361   		protected Object getFromNamespace(String name) {
  362   			final BSHInterpreter ip = getInterpreter();
  363   			return ip != null ? ip.getFromNamespace(_ns, name):
  364   				_ns.getVariable(name, false);
  365   		}
  366   		private BSHInterpreter getInterpreter() {
  367   			Page owner = _ns.getOwnerPage();
  368   			if (owner != null) {
  369   				for (Iterator it = owner.getLoadedInterpreters().iterator();
  370   				it.hasNext();) {
  371   					final Object ip = it.next();
  372   					if (ip instanceof BSHInterpreter)
  373   						return (BSHInterpreter)ip;
  374   				}
  375   			}
  376   			return null;
  377   		}
  378   	}
  379   	private static class NSCListener implements NamespaceChangeListener {
  380   		private final NS _bshns;
  381   		private NSCListener(NS bshns) {
  382   			_bshns = bshns;
  383   		}
  384   		public void onAdd(String name, Object value) {
  385   		}
  386   		public void onRemove(String name) {
  387   		}
  388   		public void onParentChanged(Namespace newparent) {
  389   			if (newparent != null) {
  390   				final BSHInterpreter ip = _bshns.getInterpreter();
  391   				_bshns.setParent(
  392   					ip != null ? ip.prepareNS(newparent):
  393   						prepareDetachedNS(newparent));
  394   				return;
  395   			}
  396   
  397   			_bshns.setParent(null);
  398   		}
  399   	}
  400   	/** Non-serializable namespace. It is used to prevent itself from
  401   	 * being serialized
  402   	 */
  403   	private static class NSX {
  404   		NameSpace ns;
  405   		private NSX(NameSpace ns) {
  406   			this.ns = ns;
  407   		}
  408   	}
  409   
  410   	//SerializableAware//
  411   	public void write(java.io.ObjectOutputStream s, Filter filter)
  412   	throws java.io.IOException {
  413   		//1. variables
  414   		final String[] vars = _bshns.getVariableNames();
  415   		for (int j = vars != null ? vars.length: 0; --j >= 0;) {
  416   			final String nm = vars[j];
  417   			if (nm != null && !"bsh".equals(nm)) {
  418   				final Object val = get(nm);
  419   				if ((val == null || (val instanceof java.io.Serializable)
  420   					|| (val instanceof java.io.Externalizable))
  421   				&& (filter == null || filter.accept(nm, val))) {
  422   					s.writeObject(nm);
  423   					s.writeObject(val);
  424   				}
  425   			}
  426   		}
  427   		s.writeObject(null); //denote end-of-vars
  428   
  429   		//2. methods
  430   		final BshMethod[] mtds = _bshns.getMethods();
  431   		for (int j = mtds != null ? mtds.length: 0; --j >= 0;) {
  432   			final String nm = mtds[j].getName();
  433   			if (filter == null || filter.accept(nm, mtds[j])) {
  434   				//hack BeanShell 2.0b4 which cannot be serialized correctly
  435   				Field f = null;
  436   				boolean acs = false;
  437   				try {
  438   					f = Classes.getAnyField(BshMethod.class, "declaringNameSpace");
  439   					acs = f.isAccessible();
  440   					Fields.setAccessible(f, true);
  441   					final Object old = f.get(mtds[j]);
  442   					try {
  443   						f.set(mtds[j], null);				
  444   						s.writeObject(mtds[j]);
  445   					} finally {
  446   						f.set(mtds[j], old);
  447   					}
  448   				} catch (java.io.IOException ex) {
  449   					throw ex;
  450   				} catch (Throwable ex) {
  451   					throw UiException.Aide.wrap(ex);
  452   				} finally {
  453   					if (f != null) Fields.setAccessible(f, acs);
  454   				}
  455   			}
  456   		}
  457   		s.writeObject(null); //denote end-of-mtds
  458   
  459   		//3. imported class
  460   		Field f = null;
  461   		boolean acs = false;
  462   		try {
  463   			f = Classes.getAnyField(NameSpace.class, "importedClasses");
  464   			acs = f.isAccessible();
  465   			Fields.setAccessible(f, true);
  466   			final Map clses = (Map)f.get(_bshns);
  467   			if (clses != null)
  468   				for (Iterator it = clses.values().iterator(); it.hasNext();) {
  469   					final String clsnm = (String)it.next();
  470   					if (!clsnm.startsWith("bsh."))
  471   						s.writeObject(clsnm);
  472   				}
  473   		} catch (java.io.IOException ex) {
  474   			throw ex;
  475   		} catch (Throwable ex) {
  476   			throw UiException.Aide.wrap(ex);
  477   		} finally {
  478   			if (f != null) Fields.setAccessible(f, acs);
  479   		}
  480   		s.writeObject(null); //denote end-of-cls
  481   
  482   		//4. imported package
  483   		f = null;
  484   		acs = false;
  485   		try {
  486   			f = Classes.getAnyField(NameSpace.class, "importedPackages");
  487   			acs = f.isAccessible();
  488   			Fields.setAccessible(f, true);
  489   			final Collection pkgs = (Collection)f.get(_bshns);
  490   			if (pkgs != null)
  491   				for (Iterator it = pkgs.iterator(); it.hasNext();) {
  492   					final String pkgnm = (String)it.next();
  493   					if (!pkgnm.startsWith("java.awt")
  494   					&& !pkgnm.startsWith("javax.swing"))
  495   						s.writeObject(pkgnm);
  496   				}
  497   		} catch (java.io.IOException ex) {
  498   			throw ex;
  499   		} catch (Throwable ex) {
  500   			throw UiException.Aide.wrap(ex);
  501   		} finally {
  502   			if (f != null) Fields.setAccessible(f, acs);
  503   		}
  504   		s.writeObject(null); //denote end-of-cls
  505   	}
  506   	public void read(java.io.ObjectInputStream s)
  507   	throws java.io.IOException, ClassNotFoundException {
  508   		for (;;) {
  509   			final String nm = (String)s.readObject();
  510   			if (nm == null) break; //no more
  511   
  512   			set(nm, s.readObject());
  513   		}
  514   
  515   		try {
  516   			for (;;) {
  517   				final BshMethod mtd = (BshMethod)s.readObject();
  518   				if (mtd == null) break; //no more
  519   
  520   				//fix declaringNameSpace
  521   				Field f = null;
  522   				boolean acs = false;
  523   				try {
  524   					f = Classes.getAnyField(BshMethod.class, "declaringNameSpace");
  525   					acs = f.isAccessible();
  526   					Fields.setAccessible(f, true);
  527   					f.set(mtd, _bshns);				
  528   				} catch (Throwable ex) {
  529   					throw UiException.Aide.wrap(ex);
  530   				} finally {
  531   					if (f != null) Fields.setAccessible(f, acs);
  532   				}
  533   
  534   				_bshns.setMethod(mtd.getName(), mtd);
  535   			}
  536   		} catch (UtilEvalError ex) {
  537   			throw UiException.Aide.wrap(ex);
  538   		}
  539   
  540   		for (;;) {
  541   			final String nm = (String)s.readObject();
  542   			if (nm == null) break; //no more
  543   
  544   			_bshns.importClass(nm);
  545   		}
  546   
  547   		for (;;) {
  548   			final String nm = (String)s.readObject();
  549   			if (nm == null) break; //no more
  550   
  551   			_bshns.importPackage(nm);
  552   		}
  553   	}
  554   
  555   	private class BSHFunction implements Function {
  556   		private final bsh.BshMethod _method;
  557   		private BSHFunction(bsh.BshMethod method) {
  558   			if (method == null)
  559   				throw new IllegalArgumentException("null");
  560   			_method = method;
  561   		}
  562   
  563   		//-- Function --//
  564   		public Class[] getParameterTypes() {
  565   			return _method.getParameterTypes();
  566   		}
  567   		public Class getReturnType() {
  568   			return _method.getReturnType();
  569   		}
  570   		public Object invoke(Object obj, Object[] args) throws Exception {
  571   			return _method.invoke(args != null ? args: new Object[0], _ip);
  572   		}
  573   		public java.lang.reflect.Method toMethod() {
  574   			return null;
  575   		}
  576   	}
  577   }

Save This Page
Home » zk-src-3.0.6 » org.zkoss.zk.scripting.bsh » [javadoc | source]