001/* An actor whose execution is specified by BeanShell Java code.
002
003Copyright (c) 2014 The Regents of the University of California.
004All rights reserved.
005Permission is hereby granted, without written agreement and without
006license or royalty fees, to use, copy, modify, and distribute this
007software and its documentation for any purpose, provided that the above
008copyright notice and the following two paragraphs appear in all copies
009of this software.
010
011IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015SUCH DAMAGE.
016
017THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022ENHANCEMENTS, OR MODIFICATIONS.
023
024*/
025package org.kepler.scriptengine;
026
027import java.lang.reflect.Field;
028
029import javax.script.ScriptException;
030
031import ptolemy.kernel.CompositeEntity;
032import ptolemy.kernel.util.IllegalActionException;
033import ptolemy.kernel.util.NameDuplicationException;
034import ptolemy.kernel.util.Workspace;
035
036/** An actor whose execution is defined by BeanShell Java code.
037 *  The code can be edited by double-clicking on the actor.   
038 *  <p>There are several differences between this actor and
039 *  the Java actor. The Java actor requires:
040 *  <ul>
041 *  <li>A Java compiler.</li>
042 *  <li>All the referenced classes must be imported.</li>
043 *  <li>All ports and parameters must be explicitly declared
044 *  as fields in the script.</li>
045 *  </ul>
046 *  There are several Java features not supported by this actor but
047 *  allowed in the Java actor:
048 *  <ul>
049 *  <li>Annotations</li>
050 *  <li>Generics</li>
051 *  </ul>
052 *  </p>
053  
054 *  <p>The following example defines an actor in BeanShell Java that
055 *  computes factorials:</p>
056 *  
057<pre>
0581. import ptolemy.data.IntToken;
0592.
0603. public class Actor {
0614.   public void fire() {
0625.     int val = ((IntToken)super.in.get(0)).intValue();
0636.
0647.     if(val < 0) {
0658.       actor.error("Input must be non-negative");
0669.     }
06710.    int total = 1;
06811.    while(val > 1) {
06912.      total *= val;
07013.      val--;
07114.    } 
07215.    out.broadcast(new IntToken(total));
07316.  }
07417.}
075</pre>
076 *
077 *  <p>Line 1 imports the Java class used by the actor. Line 3 defines the
078 *  Actor object. Lines 4-16 define the fire() method of the actor; this
079 *  method is called each time this actor executes in the workflow. Line 5
080 *  reads an integer from the input port called "input". A port or parameter
081 *  called "foo" can be accessed in the Java code by simply using "foo".
082 *  Lines 10-14 compute the factorial of the integer read from the port.
083 *  Line 15 writes the result to the output port called "output".
084 *  
085 *  @author Daniel Crawl
086 *  @version $Id: BeanShell.java 33884 2015-09-11 18:07:23Z crawl $
087 */
088public class BeanShell extends ScriptEngineActor {
089
090    public BeanShell(Workspace workspace) {
091        super(workspace);
092    }
093
094    public BeanShell(CompositeEntity container, String name)
095            throws IllegalActionException, NameDuplicationException {
096        super(container, name);
097
098        _invokeMethodWithReflection = true;
099        
100        _editorFactory.syntaxStyle.setExpression("text/java");
101
102        language.setToken("beanshell");
103        
104        script.setExpression("public class Actor {\n" +
105                "  public void fire() {\n" +
106                "     System.out.println(\"in fire\");\n" +
107                "  }\n" +
108                "}\n");
109    }
110
111    ///////////////////////////////////////////////////////////////////
112    ////                         protected methods                 ////
113    
114    /** Put the given object to the actor instance in the script.
115     *  @param name the name of the object to put.
116     *  @param object the object to put.
117     */
118    @Override
119    protected void _putObjectToActorInstance(String name, Object object) throws ScriptException {
120        
121        // see if the actor class has this field
122        Field field = null;
123        try {
124            field = _actorObject.getClass().getField(name);
125        } catch(Exception e) {
126            // do nothing
127        }
128        
129        if(field == null) {
130            // actor class does not have the field so put as global            
131            String globalName = "_yyy_" + name;
132            _engine.put(globalName, object);
133            _engine.eval(object.getClass().getName() + " " + name + " = " + globalName);
134        } else {
135            try {
136                // set the field in the actor instance.
137                field.set(_actorObject, object);
138            } catch (Exception e) {
139                throw new ScriptException("Error setting port/parameter " + name + ": " + e.getMessage());
140            }
141        }
142    }
143}