001/*
002 * Copyright (c) 2003-2017 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author$'
006 * '$Date$'
007 * '$Revision$'
008 *
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package ptolemy.actor.lib.r;
031
032import java.io.File;
033import java.io.FileOutputStream;
034import java.io.IOException;
035import java.io.OutputStreamWriter;
036import java.io.PrintWriter;
037import java.net.URL;
038import java.util.ArrayList;
039import java.util.Iterator;
040import java.util.List;
041import java.util.Set;
042
043import org.apache.commons.logging.Log;
044import org.apache.commons.logging.LogFactory;
045//import org.kepler.util.DotKeplerManager;
046import org.rosuda.JRI.RBool;
047import org.rosuda.JRI.REXP;
048import org.rosuda.JRI.RFactor;
049import org.rosuda.JRI.RList;
050import org.rosuda.JRI.RVector;
051import org.rosuda.JRI.Rengine;
052
053import ptolemy.actor.NoTokenException;
054import ptolemy.actor.TypedAtomicActor;
055import ptolemy.actor.TypedIOPort;
056import ptolemy.actor.gui.BrowserLauncher;
057import ptolemy.actor.gui.style.TextStyle;
058import ptolemy.data.ArrayToken;
059import ptolemy.data.BooleanMatrixToken;
060import ptolemy.data.BooleanToken;
061import ptolemy.data.DoubleMatrixToken;
062import ptolemy.data.DoubleToken;
063import ptolemy.data.IntMatrixToken;
064import ptolemy.data.IntToken;
065import ptolemy.data.MatrixToken;
066import ptolemy.data.OrderedRecordToken;
067import ptolemy.data.RecordToken;
068import ptolemy.data.StringToken;
069import ptolemy.data.Token;
070import ptolemy.data.expr.Parameter;
071import ptolemy.data.expr.StringParameter;
072import ptolemy.data.type.BaseType;
073import ptolemy.data.type.Type;
074import ptolemy.kernel.CompositeEntity;
075import ptolemy.kernel.util.IllegalActionException;
076import ptolemy.kernel.util.NameDuplicationException;
077import ptolemy.kernel.util.StringAttribute;
078import ptolemy.kernel.util.Workspace;
079//import util.WorkflowExecutionListener;
080
081///////////////////////////////////////////////////////////////////
082//// RExpression2
083/**
084 * The RExpression actor is an actor designed to run an R script or function
085 * with inputs and outputs determined by the ports created by the user. Port
086 * names will correspond to R object names. The RExpression actor is modeled
087 * after the Ptolemy expression actor, except that that instead of using a
088 * single mathematical expression in Ptolemy's expression language, it uses a
089 * set of the more powerful, higher order expressions available under R. Both
090 * input and output port will usually be added to the actor; The names of these
091 * ports correspond to R variables used in the R script.
092 *
093 * <p>Note that RExpression2 uses the Java/R interface from
094 * <a href="https://rforge.net/JRI/#in_browser">https://rforge.net/JRI/</a>
095 * which requires that native libraries be installed in your java.library.path.
096 * </p>
097 *
098 * @author Dan Higgins and Matt Jones, NCEAS, UC Santa Barbara
099 * @version $Id$
100 * @since Ptolemy II 11.0
101 */
102
103public class RExpression2 extends TypedAtomicActor {
104
105    /**
106     * Construct an actor with the given container and name.
107     *
108     * @param container
109     *            The container.
110     * @param name
111     *            The name of this actor.
112     * @exception IllegalActionException
113     *                If the actor cannot be contained by the proposed
114     *                container.
115     * @exception NameDuplicationException
116     *                If the container already has an actor with this name.
117     */
118    public RExpression2(CompositeEntity container, String name)
119            throws NameDuplicationException, IllegalActionException {
120        super(container, name);
121
122        expression = new StringAttribute(this, "expression");
123        expression.setDisplayName("R function or script");
124        new TextStyle(expression, "R Expression"); //keep this for larger text area
125        expression.setExpression("a <- c(1,2,3,5)\nplot(a)");
126
127        Rcwd = new StringParameter(this, "Rcwd");
128        Rcwd.setDisplayName("R working directory");
129        //Rcwd.setExpression( DotKeplerManager.getInstance()
130        //.getTransientModuleDirectory("r").toString() );
131
132        graphicsFormat = new StringParameter(this, "graphicsFormat");
133        graphicsFormat.setDisplayName("Graphics Format");
134        graphicsFormat.setExpression("png");
135        graphicsFormat.addChoice("pdf");
136        graphicsFormat.addChoice("png");
137
138        showDebug = new Parameter(this, "showDebug");
139        showDebug.setDisplayName("Debug");
140        showDebug.setTypeEquals(BaseType.BOOLEAN);
141        showDebug.setToken(BooleanToken.TRUE);
142
143        serializeData = new Parameter(this, "serializeData");
144        serializeData.setDisplayName("Serialize Data Frame");
145        serializeData.setTypeEquals(BaseType.BOOLEAN);
146        serializeData.setToken(BooleanToken.TRUE);
147
148        graphicsOutput = new Parameter(this, "graphicsOutput");
149        graphicsOutput.setDisplayName("Graphics Output");
150        graphicsOutput.setTypeEquals(BaseType.BOOLEAN);
151        graphicsOutput.setToken(BooleanToken.TRUE);
152
153        displayGraphicsOutput = new Parameter(this, "displayGraphicsOutput");
154        displayGraphicsOutput.setDisplayName("Automatically display graphics");
155        displayGraphicsOutput.setTypeEquals(BaseType.BOOLEAN);
156        displayGraphicsOutput.setToken(BooleanToken.FALSE);
157
158        numXPixels = new StringParameter(this, "numXPixels");
159        numXPixels.setDisplayName("Number of X pixels in image");
160        numXPixels.setExpression("480");
161        numYPixels = new StringParameter(this, "numYPixels");
162        numYPixels.setDisplayName("Number of Y pixels in image");
163        numYPixels.setExpression("480");
164
165        graphicsFileName = new TypedIOPort(this, "graphicsFileName", false,
166                true);
167        graphicsFileName.setTypeEquals(BaseType.STRING);
168
169        output = new TypedIOPort(this, "output", false, true);
170        output.setTypeEquals(BaseType.STRING);
171
172    }
173
174    /** The log. */
175    public static Log log = LogFactory.getLog(RExpression2.class);
176
177    ///////////////////////////////////////////////////////////////////
178    ////          ports and parameters                             ////
179
180    /**
181     * The output port.
182     */
183    public TypedIOPort output;
184
185    /**
186     * The expression that is evaluated to produce the output.
187     */
188    public StringAttribute expression;
189
190    /**
191     * The 'R' working directory (home dir by default).
192     */
193    public StringParameter Rcwd;
194
195    /**
196     * If <i>true</i>, then shoe debugging information about script.
197     * If <i>false</i>, then don't. (the default)
198     */
199    public Parameter showDebug;
200
201    /**
202     * If <i>true</i>, then daata frames (and other complexe data objects
203     * will be transferred by serialization to disk.
204     * If <i>false</i>, then they will be converted as losslessly as possible
205     * to a Ptolemy data structure
206     */
207    public Parameter serializeData;
208
209    /**
210     * If <i>true</i>, then display plot. If <i>false</i>, then don't. (the
211     * default)
212     */
213    public Parameter displayGraphicsOutput;
214
215    /**
216     * The graphics output format. Currently the format is either a *.pdf or a
217     * *.png
218     */
219    public StringParameter graphicsFormat;
220
221    /**
222     * If <i>true</i>, then create a graphics output port. (the default); If
223     * <i>false</i>, then don't.
224     */
225    public Parameter graphicsOutput;
226
227    /**
228     * The width of the output graphics bitmap in pixels.
229     */
230    public StringParameter numXPixels;
231
232    /**
233     * The height of the output graphics bitmap in pixels.
234     */
235    public StringParameter numYPixels;
236
237    /**
238     * The name of the default graphics output file created by the actor.
239     */
240    public TypedIOPort graphicsFileName;
241
242    /**
243     * Override the base class to set type constraints.
244     *
245     * @param workspace
246     *            The workspace for the new object.
247     * @return A new instance of RExpression.
248     * @exception CloneNotSupportedException
249     *                If a derived class contains an attribute that cannot be
250     *                cloned.
251     */
252    @Override
253    public Object clone(Workspace workspace) throws CloneNotSupportedException {
254        RExpression2 newObject = (RExpression2) super.clone(workspace);
255        String lcOSName = System.getProperty("os.name").toLowerCase();
256        boolean MAC_OS_X = lcOSName.startsWith("mac os x");
257        if (MAC_OS_X) {
258            try {
259                newObject.graphicsFormat.setExpression("pdf");
260                newObject.displayGraphicsOutput.setToken(BooleanToken.TRUE);
261            } catch (Exception w) {
262                System.out.println("Error in special Mac response in clone");
263            }
264        }
265        newObject.output.setTypeEquals(BaseType.STRING);
266        newObject.graphicsFileName.setTypeEquals(BaseType.STRING);
267        return newObject;
268    }
269
270    /*
271     * The fire method should first call the superclass. Then all the input
272     * ports should be scanned to see which ones have tokens. The names of those
273     * ports should be used to create a named R object (array?). R script for
274     * creating objects corresponding to these ports should be inserted before
275     * the script in the expressions parameter. Then the R engine should be
276     * started and run, with the output sent to the output port.
277     */
278    @Override
279    public synchronized void fire() throws IllegalActionException {
280        super.fire();
281
282        // _fireUsingCommandLine();
283        _fireUsingJRI();
284    }
285
286    @Override
287    public void initialize() throws IllegalActionException {
288        super.initialize();
289
290        // set the _home
291        _home = Rcwd.stringValue();
292        File homeFile = new File(_home);
293
294        // if not a directory, use 'home'
295        if (!homeFile.isDirectory()) {
296            throw new IllegalActionException(this,
297                    "Rcwd = \"" + _home + "\", which is not a directory?");
298            // home = DotKeplerManager.getInstance()
299            // .getTransientModuleDirectory("r").toString();
300        }
301
302        _home = _home.replace('\\', '/');
303        if (!_home.endsWith("/")) {
304            _home = _home + "/";
305        }
306
307        // reset the name when workflow execution completes
308        // this.getManager().addExecutionListener(
309        //  WorkflowExecutionListener.getInstance());
310
311        String workflowName = this.toplevel().getName();
312        // workflowName = workflowName.replace(' ','_');
313        // workflowName = workflowName.replace('-','_');
314        String execDir = _home + workflowName + "_"
315        //+ WorkflowExecutionListener.getInstance().getId(toplevel())
316                + "/";
317
318        File dir = new File(execDir);
319        if (!dir.exists()) {
320            if (!dir.mkdir()) {
321                throw new IllegalActionException(null, this,
322                        "Failed to make directory " + dir);
323            }
324            ;
325        }
326        _home = execDir;
327
328    }
329
330    // public boolean postfire() throws IllegalActionException {
331    //         // _errorGobbler.quit();
332    //         // _outputGobbler.quit();
333
334    //         return super.postfire();
335    // }
336
337    /** Iterate through the ports.
338     *  @exception IllegalActionException If thrown by the
339     *  base class or while accessing the ports.
340     */
341    @Override
342    public void preinitialize() throws IllegalActionException {
343        // NOTE: there is a note about commenting out this method:
344        // "remove for now since it causes problems with ENMs"
345        // TODO: see if this causes problems (there are problems without it, too)
346
347        super.preinitialize();
348        // set all the ports to unknown for type resolution?
349        _opList = outputPortList();
350        _iterO = _opList.iterator();
351        while (_iterO.hasNext()) {
352            TypedIOPort tiop = (TypedIOPort) _iterO.next();
353            if (tiop.getName().equals("output")
354                    || tiop.getName().equals("graphicsFileName")) {
355                continue;
356            }
357            tiop.setTypeEquals(BaseType.GENERAL);
358        }
359    }
360
361    ///////////////////////////////////////////////////////////////////
362    ////                         private methods                   ////
363
364    //assign the array of arrays directly in RNI
365    private void _convertArrayTokenToObject(ArrayToken token, String varName) {
366        int arrayLength = -1;
367        List columnRefs = new ArrayList();
368        for (int i = 0; i < token.length(); i++) {
369            Token genericToken = token.getElement(i);
370            String token_type_string = genericToken.getType().toString();
371
372            ArrayToken arrayToken = null;
373            if (genericToken instanceof ArrayToken) {
374                arrayToken = (ArrayToken) genericToken;
375            }
376            long columnRef = 0;
377            if ((token_type_string.equals("{double}"))
378                    || (token_type_string.equals("{int}"))
379                    || (token_type_string.equals("{string}"))
380                    || (token_type_string.equals("{boolean}"))
381                    || (token_type_string.startsWith("arrayType"))) {
382
383                // make primative arrays for R
384                if ((token_type_string.equals("{double}"))
385                        || (token_type_string.startsWith("arrayType(double"))) {
386                    double[] values = _convertArrayToken(arrayToken);
387                    columnRef = _rEngine.rniPutDoubleArray(values);
388                } else if ((token_type_string.equals("{int}"))
389                        || (token_type_string.startsWith("arrayType(int"))) {
390                    int[] values = _convertArrayTokenToInt(arrayToken);
391                    columnRef = _rEngine.rniPutIntArray(values);
392                } else if ((token_type_string.equals("{string}"))
393                        || (token_type_string.startsWith("arrayType(string"))) {
394                    String[] values = _convertArrayTokenToString(arrayToken);
395                    columnRef = _rEngine.rniPutStringArray(values);
396                } else if ((token_type_string.equals("{boolean}"))
397                        || (token_type_string
398                                .startsWith("arrayType(boolean"))) {
399                    boolean[] values = _convertArrayTokenToBoolean(arrayToken);
400                    columnRef = _rEngine.rniPutBoolArray(values);
401                }
402            } else if (token_type_string.equals("string")) {
403                columnRef = _rEngine.rniPutStringArray(new String[] {
404                        ((StringToken) genericToken).stringValue() });
405            } else if (token_type_string.equals("double")) {
406                columnRef = _rEngine.rniPutDoubleArray(new double[] {
407                        ((DoubleToken) genericToken).doubleValue() });
408            } else if (token_type_string.equals("int")) {
409                columnRef = _rEngine.rniPutIntArray(
410                        new int[] { ((IntToken) genericToken).intValue() });
411            } else if (token_type_string.equals("boolean")) {
412                columnRef = _rEngine.rniPutBoolArray(new boolean[] {
413                        ((BooleanToken) genericToken).booleanValue() });
414            }
415            columnRefs.add(columnRef);
416        } // while
417          // capture the column references in a "vector"
418        long[] columns = new long[columnRefs.size()];
419        for (int i = 0; i < columnRefs.size(); i++) {
420            columns[i] = (Long) columnRefs.get(i);
421        }
422        // assemble the dataframe reference
423        long tableRef = _rEngine.rniPutVector(columns);
424
425        // add the column names to the dataframe
426        //                String[] columnNames = (String[]) labels.toArray(new String[0]);
427        //                long columnNamesRef = _rEngine.rniPutStringArray(columnNames);
428        //                _rEngine.rniSetAttr(tableRef, "names", columnNamesRef);
429
430        // set the class as data.frame
431        long classNameRef = _rEngine.rniPutString("data.frame");
432        _rEngine.rniSetAttr(tableRef, "class", classNameRef);
433
434        // set assign the data.frame to a variable
435        _rEngine.rniAssign(varName, tableRef, 0);
436
437    }
438
439    /**
440     *
441     * Given a recordToken and a portName, create the R script to make a
442     * dataframe with the portName as its R name.
443     *
444     * @param recordToken
445     *            the record to convert to R dataframe
446     * @param portName
447     *            will become the object name used for the R dataframe
448     */
449    private void _recordToDataFrame(RecordToken recordToken, String portName) {
450        int arrayLength = -1;
451        Set labels = recordToken.labelSet();
452        Iterator iter_l = labels.iterator();
453        List columnRefs = new ArrayList();
454        while (iter_l.hasNext()) {
455            String label = (String) iter_l.next();
456            // System.out.println("Label: "+label);
457            Token genericToken = recordToken.get(label);
458            String token_type_string = genericToken.getType().toString();
459
460            ArrayToken arrayToken = null;
461            if (genericToken instanceof ArrayToken) {
462                arrayToken = (ArrayToken) genericToken;
463            }
464            long columnRef = 0;
465            if ((token_type_string.equals("{double}"))
466                    || (token_type_string.equals("{int}"))
467                    || (token_type_string.equals("{string}"))
468                    || (token_type_string.equals("{boolean}"))
469                    || (token_type_string.startsWith("arrayType"))) {
470                // for now, assume that token is an arrayToken !!!
471                // other token types are just ignored
472                if (arrayLength == -1) {
473                    arrayLength = arrayToken.length();
474                } else {
475                    if (arrayToken == null) {
476                        log.error("Could not parse " + recordToken
477                                + ", arrayToken == null?");
478                    } else {
479                        int a_len = arrayToken.length();
480                        if (a_len != arrayLength) {
481                            log.error(
482                                    "record elements are not all the same length!");
483                            return;
484                        }
485                    }
486                }
487
488                // make primative arrays for R
489                if ((token_type_string.equals("{double}"))
490                        || (token_type_string.startsWith("arrayType(double"))) {
491                    double[] values = _convertArrayToken(arrayToken);
492                    columnRef = _rEngine.rniPutDoubleArray(values);
493                } else if ((token_type_string.equals("{int}"))
494                        || (token_type_string.startsWith("arrayType(int"))) {
495                    int[] values = _convertArrayTokenToInt(arrayToken);
496                    columnRef = _rEngine.rniPutIntArray(values);
497                } else if ((token_type_string.equals("{string}"))
498                        || (token_type_string.startsWith("arrayType(string"))) {
499                    String[] values = _convertArrayTokenToString(arrayToken);
500                    columnRef = _rEngine.rniPutStringArray(values);
501                } else if ((token_type_string.equals("{boolean}"))
502                        || (token_type_string
503                                .startsWith("arrayType(boolean"))) {
504                    boolean[] values = _convertArrayTokenToBoolean(arrayToken);
505                    columnRef = _rEngine.rniPutBoolArray(values);
506                }
507            } else if (token_type_string.equals("string")) {
508                columnRef = _rEngine.rniPutStringArray(new String[] {
509                        ((StringToken) genericToken).stringValue() });
510                arrayLength = 1;
511            } else if (token_type_string.equals("double")) {
512                columnRef = _rEngine.rniPutDoubleArray(new double[] {
513                        ((DoubleToken) genericToken).doubleValue() });
514                arrayLength = 1;
515            } else if (token_type_string.equals("int")) {
516                columnRef = _rEngine.rniPutIntArray(
517                        new int[] { ((IntToken) genericToken).intValue() });
518                arrayLength = 1;
519            } else if (token_type_string.equals("boolean")) {
520                columnRef = _rEngine.rniPutBoolArray(new boolean[] {
521                        ((BooleanToken) genericToken).booleanValue() });
522                arrayLength = 1;
523            }
524            columnRefs.add(columnRef);
525        } // while
526          // capture the column references in a "vector"
527        long[] columns = new long[columnRefs.size()];
528        for (int i = 0; i < columnRefs.size(); i++) {
529            columns[i] = (Long) columnRefs.get(i);
530        }
531        // assemble the dataframe reference
532        long tableRef = _rEngine.rniPutVector(columns);
533
534        // add the column names to the dataframe
535        String[] columnNames = (String[]) labels.toArray(new String[0]);
536        long columnNamesRef = _rEngine.rniPutStringArray(columnNames);
537        _rEngine.rniSetAttr(tableRef, "names", columnNamesRef);
538
539        // set the row names (just 1,2,3...n)
540        // this works now - and is very important!
541        String[] rowNames = new String[arrayLength];
542        for (int i = 0; i < rowNames.length; i++) {
543            rowNames[i] = "" + (i + 1);
544        }
545        long rowNamesRef = _rEngine.rniPutStringArray(rowNames);
546        _rEngine.rniSetAttr(tableRef, "row.names", rowNamesRef);
547
548        // set the class as data.frame
549        long classNameRef = _rEngine.rniPutString("data.frame");
550        _rEngine.rniSetAttr(tableRef, "class", classNameRef);
551
552        // set assign the data.frame to a variable
553        _rEngine.rniAssign(portName, tableRef, 0);
554
555        //make the character columns into factors (default DF behavior)
556        _rEngine.eval(portName + "[sapply(" + portName
557                + ", is.character)] <- lapply(" + portName + "[sapply("
558                + portName + ", is.character)], as.factor)");
559
560    }
561
562    /**
563     * The main execution of the actor as follows: Reads the input data, sets up
564     * the graphic device (as needed), executes the script, sends output to
565     * ports, optionally shows generated graphics, cleans up.
566     *
567     * @exception IllegalActionException
568     */
569    private void _fireUsingJRI() throws IllegalActionException {
570
571        _initializeRengine();
572        if (_rEngine != null) {
573            _readInputData();
574            _setupJRIGraphicsDevice();
575            _executeRModel();
576            _writeOutputData();
577            _showGraphics();
578            _teardownJRI();
579        } else {
580            throw new IllegalActionException(_noRErrorMessage);
581        }
582    }
583
584    /**
585     * Start up the R system by initalizing an instance of the JRI Rengine. This
586     * REngine can be used to execute R scripts and to retrieve the results of
587     * these execution events.
588     *
589     * @return the REngine to be sued for executing R scripts
590     * @exception IllegalActionException
591     */
592    private void _initializeRengine() throws IllegalActionException {
593        log.warn("RNI version: " + Rengine.rniGetVersion());
594        log.warn("API version: " + Rengine.getVersion());
595        if (Rengine.getMainEngine() != null) {
596            _rEngine = Rengine.getMainEngine();
597            _console = new RConsole();
598            _rEngine.addMainLoopCallbacks(_console);
599            // clear all objects for safety's sake
600            _rEngine.eval("rm(list=ls())");
601            return;
602        }
603
604        if (!Rengine.versionCheck()) {
605            String msg = "** Version mismatch - Java files don't match R library version.";
606            log.error(msg);
607            throw new IllegalActionException(msg);
608        }
609        log.debug("Creating Rengine (with arguments)");
610        // 1) we pass the arguments from the command line
611        // 2) we won't use the main loop at first, we'll start it later
612        // (that's the "false" as second argument)
613        // 3) the callbacks are implemented by the Text_Console class above
614        String args[] = new String[2];
615        args[0] = _NO_SAVE;
616        args[1] = _NO_RESTORE;
617
618        _console = new RConsole();
619        _rEngine = new Rengine(args, false, _console);
620        log.debug("Rengine created, waiting for R");
621        // the engine creates R in a new thread, so we should wait until it's
622        // ready
623        if (!_rEngine.waitForR()) {
624            String msg = "Cannot load R." + "\n " + _noRErrorMessage;
625            log.error(msg);
626            throw new IllegalActionException(msg);
627        }
628    }
629
630    /**
631     * Read the input data from the actors input ports, and for each port
632     * convert the data from a Kepler Token into an appropriate R object that
633     * can be loaded into the REngine before R scripts that depend on this input
634     * data can be executed.
635     *
636     * @param re
637     *            the REngine used for loading input data
638     */
639    private void _readInputData() {
640        log.debug("reading input form ports");
641        List ipList = inputPortList();
642        Iterator iter_i = ipList.iterator();
643        String RPortInfo = "";
644        Token at;
645        String tokenValue;
646        while (iter_i.hasNext()) {
647            TypedIOPort tiop = (TypedIOPort) iter_i.next();
648            int multiportSize = tiop.numberOfSources();
649
650            for (int i = 0; i < multiportSize; i++) {
651                try {
652                    if (tiop.hasToken(i)) {
653                        String portName = tiop.getName();
654                        String finalPortName = portName; // for use with
655                        // multiports
656                        if (tiop.isMultiport()) {
657                            portName = portName + i; // temporary variable for
658                            // list item
659                        }
660                        Token token = tiop.get(i);
661                        String token_type_string = token.getType().toString();
662                        String token_class_name = token.getType()
663                                .getTokenClass().getName();
664                        log.debug("setting: " + portName + "=" + token);
665
666                        // check token type and convert to R appropriately
667                        // RecordTokens
668                        if (token instanceof RecordToken) {
669                            // write it to the R environment
670                            _recordToDataFrame((RecordToken) token, portName);
671                        }
672
673                        // STRINGS
674                        else if (token_type_string.equals("string")) {
675
676                            // check for special strings that indicate dataframe
677                            // file reference
678                            at = token;
679                            tokenValue = at.toString();
680                            tokenValue = tokenValue.substring(1,
681                                    tokenValue.length() - 1); // remove quotes
682
683                            // DATAFRAME (old)
684                            if (tokenValue.startsWith("_dataframe_:")) {
685                                // assume that the string for a dataframe file
686                                // reference is of the form
687                                // '_dataframe_:"+<filename>
688                                tokenValue = tokenValue.substring(12); // should be filename
689                                REXP x = null;
690                                Token myToken = null;
691
692                                RPortInfo = "conn <- file('" + tokenValue
693                                        + "', 'rb');";
694                                x = _rEngine.eval(RPortInfo, true);
695                                myToken = _convertToToken(x, null);
696                                log.debug("myToken=" + myToken);
697
698                                RPortInfo = portName + " <- unserialize(conn);";
699                                x = _rEngine.eval(RPortInfo, true);
700                                myToken = _convertToToken(x, null);
701                                log.debug("myToken=" + myToken);
702
703                                RPortInfo = "close(conn);";
704
705                                x = _rEngine.eval(RPortInfo, true);
706                                myToken = _convertToToken(x, null);
707                                log.debug("myToken=" + myToken);
708
709                                continue; // stop for this token and go to the
710                                // next
711                            }
712
713                            // assume that the token's string value might be
714                            // 'nil' for a missing value
715                            tokenValue = tokenValue.replaceAll("nil", "NA");
716
717                            if (tokenValue.startsWith("\\")) {
718                                //use this evaluation as a workaround for getting the escaped chars (like tabs)
719                                _rEngine.eval(
720                                        portName + " <- '" + tokenValue + "'");
721                            } else {
722                                // set the string in the r engine directly
723                                _rEngine.assign(portName, tokenValue);
724                            }
725                        }
726
727                        // BOOLEAN
728                        else if (token_type_string.equals("boolean")) {
729                            at = token;
730                            tokenValue = at.toString();
731                            //use a boolean - JRI uses arrays...
732                            boolean booleanValue = Boolean
733                                    .parseBoolean(tokenValue);
734                            _rEngine.assign(portName,
735                                    new boolean[] { booleanValue });
736                            //_rEngine.assign(portName, tokenValue);
737                        }
738
739                        // NUMBERS
740                        else if (token_type_string.equals("double")
741                                || token_type_string.equals("float")
742                                || token_type_string.equals("int")) {
743                            at = token;
744                            tokenValue = at.toString();
745                            // TODO need support for assigning numeric scalars
746                            _rEngine.eval(portName + " <- " + tokenValue);
747                            // _rEngine.assign(portName, tokenValue);
748                        }
749
750                        // ARRAYS
751                        else if ((token_type_string.equals("{double}"))
752                                || (token_type_string
753                                        .startsWith("arrayType(double"))) {
754                            double[] values = _convertArrayToken(
755                                    (ArrayToken) token);
756                            _rEngine.assign(portName, values);
757                        } else if ((token_type_string.equals("{int}"))
758                                || (token_type_string
759                                        .startsWith("arrayType(int"))) {
760                            int[] values = _convertArrayTokenToInt(
761                                    (ArrayToken) token);
762                            _rEngine.assign(portName, values);
763                        } else if ((token_type_string.equals("{string}"))
764                                || (token_type_string
765                                        .startsWith("arrayType(string"))) {
766                            String[] values = _convertArrayTokenToString(
767                                    (ArrayToken) token);
768                            _rEngine.assign(portName, values);
769                        } else if ((token_type_string.equals("{boolean}"))
770                                || (token_type_string
771                                        .startsWith("arrayType(boolean"))) {
772                            boolean[] values = _convertArrayTokenToBoolean(
773                                    (ArrayToken) token);
774                            _rEngine.assign(portName, values);
775                        } else if ((token_type_string.equals("{scalar}"))
776                                || (token_type_string
777                                        .startsWith("arrayType(scalar"))) {
778                            //TODO: keep specific types for each item
779                            Object[] values = _convertScalarArrayTokenToString(
780                                    (ArrayToken) token);
781                            REXP objVal = new REXP(REXP.XT_ARRAY_STR, values);
782                            _rEngine.assign(portName, objVal);
783                        } else if (token_type_string
784                                .startsWith("arrayType(arrayType")) {
785                            //handle arrays of arrays
786                            _convertArrayTokenToObject((ArrayToken) token,
787                                    portName);
788                        }
789
790                        // MATRIX
791                        else if ((token_class_name
792                                .indexOf("IntMatrixToken") > -1)
793                                || (token_class_name
794                                        .indexOf("DoubleMatrixToken") > -1)
795                                || (token_class_name
796                                        .indexOf("BooleanMatrixToken") > -1)) {
797                            int rows = ((MatrixToken) token).getRowCount();
798                            int cols = ((MatrixToken) token).getColumnCount();
799                            ArrayToken matrixAsArray = ((MatrixToken) token)
800                                    .toArrayColumnMajor();
801                            String matrixType = matrixAsArray.getType()
802                                    .toString();
803                            if (matrixType.startsWith("arrayType(int")) {
804                                int[] values = _convertArrayTokenToInt(
805                                        matrixAsArray);
806                                _rEngine.assign(portName, values);
807                            } else if (matrixType
808                                    .startsWith("arrayType(double")) {
809                                double[] values = _convertArrayToken(
810                                        matrixAsArray);
811                                _rEngine.assign(portName, values);
812                            } else if (matrixType
813                                    .startsWith("arrayType(boolean")) {
814                                boolean[] values = _convertArrayTokenToBoolean(
815                                        matrixAsArray);
816                                _rEngine.assign(portName, values);
817                            }
818
819                            REXP x = _rEngine.eval(portName, true);
820                            log.debug(portName + "=" + x);
821
822                            // make a matrix from the array
823                            String cmd = portName + " <- " + "matrix("
824                                    + portName + ", nrow=" + rows + ", ncol="
825                                    + cols + ")";
826                            _rEngine.eval(cmd, false);
827                            // REXP x = _rEngine.eval(cmd, true);
828                            // _rEngine.assign(portName, x);
829                        }
830
831                        // CONSTRUCT LIST for objects on multiport
832                        if (tiop.isMultiport()) {
833                            String commandList = null;
834                            if (i == 0) {
835                                // create list
836                                commandList = finalPortName + " <- list("
837                                        + portName + ")";
838                            } else if (i > 0) {
839                                // append to list
840                                commandList = finalPortName + " <- c("
841                                        + finalPortName + ", list(" + portName
842                                        + ") )";
843                            }
844                            // set in the R environment
845                            _rEngine.eval(commandList);
846
847                            // remove temporary objects that are now in the list
848                            _rEngine.eval("rm(" + portName + ")");
849                        }
850
851                    }
852                } catch (NoTokenException e) {
853                    // TODO Auto-generated catch block
854                    e.printStackTrace();
855                } catch (IllegalActionException e) {
856                    // TODO Auto-generated catch block
857                    e.printStackTrace();
858                } catch (Exception e) {
859                    // TODO Auto-generated catch block
860                    e.printStackTrace();
861                }
862            } // multiport loop
863        }
864        log.debug("Done reading data from input ports");
865    }
866
867    private double[] _convertArrayToken(ArrayToken token) {
868        double[] returnArray = new double[token.length()];
869        Token[] tokens = token.arrayValue();
870        for (int i = 0; i < tokens.length; i++) {
871            double value = ((DoubleToken) tokens[i]).doubleValue();
872            returnArray[i] = value;
873        }
874        return returnArray;
875    }
876
877    private int[] _convertArrayTokenToInt(ArrayToken token) {
878        int[] returnArray = new int[token.length()];
879        Token[] tokens = token.arrayValue();
880        for (int i = 0; i < tokens.length; i++) {
881            int value = ((IntToken) tokens[i]).intValue();
882            returnArray[i] = value;
883        }
884        return returnArray;
885    }
886
887    private String[] _convertArrayTokenToString(ArrayToken token) {
888        String[] returnArray = new String[token.length()];
889        Token[] tokens = token.arrayValue();
890        for (int i = 0; i < tokens.length; i++) {
891            String value = ((StringToken) tokens[i]).stringValue();
892            returnArray[i] = value;
893        }
894        return returnArray;
895    }
896
897    private Object[] _convertScalarArrayTokenToString(ArrayToken token) {
898        Object[] returnArray = new String[token.length()];
899        Token[] tokens = token.arrayValue();
900        for (int i = 0; i < tokens.length; i++) {
901            Token t = tokens[i];
902            Object value = t.toString();
903            //TODO: REXP doesn't seem to support mixed arrays - needs to stay as String until fixed
904            if (t instanceof StringToken) {
905                value = ((StringToken) t).stringValue();
906            }
907            //                        else if (t instanceof IntToken) {
908            //                                value = ((IntToken) t).intValue();
909            //                        } else if (t instanceof DoubleToken) {
910            //                                value = ((DoubleToken) t).doubleValue();
911            //                        } else if (t instanceof BooleanToken) {
912            //                                value = ((BooleanToken) t).booleanValue();
913            //                        }
914
915            returnArray[i] = value;
916        }
917        return returnArray;
918    }
919
920    private boolean[] _convertArrayTokenToBoolean(ArrayToken token) {
921        boolean[] returnArray = new boolean[token.length()];
922        Token[] tokens = token.arrayValue();
923        for (int i = 0; i < tokens.length; i++) {
924            boolean value = ((BooleanToken) tokens[i]).booleanValue();
925            returnArray[i] = value;
926        }
927        return returnArray;
928    }
929
930    /** Return the array as an int matrix.
931     *  @param doubles The 2-d array of doubles.
932     *  @return The 2d array of integers.
933     */
934    public static int[][] asIntMatrix(double[][] doubles) {
935        int[][] returnArray = new int[doubles.length][doubles[0].length];
936        for (int i = 0; i < doubles.length; i++) {
937            for (int j = 0; j < doubles[i].length; j++) {
938                int value = Double.valueOf(doubles[i][j]).intValue();
939                returnArray[i][j] = value;
940            }
941        }
942        return returnArray;
943    }
944
945    /** Return as a matrix of booleans.
946     *  @param x The R expression.
947     *  @return the result as an array of booleans.
948     */
949    public static boolean[][] asBooleanMatrix(REXP x) {
950        int[] ct = x.asIntArray();
951        if (ct == null) {
952            return null;
953        }
954        REXP dim = x.getAttribute("dim");
955        if (dim == null || dim.getType() != REXP.XT_ARRAY_INT) {
956            return null; // we need dimension attr
957        }
958        int[] ds = dim.asIntArray();
959        if (ds == null || ds.length != 2) {
960            return null; // matrix must be 2-dimensional
961        }
962        int m = ds[0], n = ds[1];
963        boolean[][] r = new boolean[m][n];
964        if (ct == null) {
965            return null;
966        }
967        // R stores matrices as matrix(c(1,2,3,4),2,2) = col1:(1,2), col2:(3,4)
968        // we need to copy everything, since we create 2d array from 1d array
969        int i = 0, k = 0;
970        while (i < n) {
971            int j = 0;
972            while (j < m) {
973                boolean val = ct[k++] == 0 ? false : true;
974                r[j++][i] = val;
975            }
976            i++;
977        }
978        return r;
979    }
980
981    /**
982     * Creates a graphics device in the R environment where plots will be sent.
983     * Actor will emit [pointer to] the graphics file that is generated. TODO:
984     * emit actual file (object), not just a path (string)
985     *
986     * @exception IllegalActionException
987     */
988    private void _setupJRIGraphicsDevice() throws IllegalActionException {
989        log.debug("setting up graphics device: ");
990
991        boolean graphicsOutputValue = ((BooleanToken) graphicsOutput.getToken())
992                .booleanValue();
993
994        boolean displayGraphicsOutputValue = ((BooleanToken) displayGraphicsOutput
995                .getToken()).booleanValue();
996
997        String graphicsFormatString = graphicsFormat.stringValue();
998
999        // following line insures that graphics is pdf if automatically
1000        // displayed
1001        if (displayGraphicsOutputValue) {
1002            graphicsFormatString = "pdf";
1003        }
1004
1005        // force file format to 'pdf' is this is a Mac
1006        String lcOSName = System.getProperty("os.name").toLowerCase();
1007        boolean MAC_OS_X = lcOSName.startsWith("mac os x");
1008        if (MAC_OS_X) {
1009            graphicsFormatString = "pdf";
1010        }
1011
1012        String setCWD = "setwd('" + _home + "')\n";
1013        String graphicsDeviceCode = _generateGraphicsDeviceCode(
1014                graphicsFormatString, graphicsOutputValue);
1015
1016        if (graphicsDeviceCode != null && graphicsDeviceCode.length() > 0) {
1017            log.debug(setCWD + graphicsDeviceCode);
1018
1019            _rEngine.eval(setCWD);
1020            _rEngine.eval(graphicsDeviceCode);
1021        }
1022
1023        log.debug("done setting up graphics device");
1024
1025    }
1026
1027    /**
1028     * Turns off the graphics device that is created
1029     */
1030    private void _teardownJRI() {
1031        String cmd = "dev.off()";
1032        _rEngine.eval(cmd);
1033        // _rEngine.end();
1034        // re = null;
1035    }
1036
1037    /**
1038     * Write tokens onto each output port of the actor if it is available from
1039     * the execution of the R Script. The type of each R Object is inspected to
1040     * determine which subclass of Token should be used for the conversion. Port
1041     * names are used to locate objects in the R environment with the same name.
1042     *
1043     * @exception IllegalActionException
1044     */
1045    private void _writeOutputData() throws IllegalActionException {
1046        log.debug("Writing R output...");
1047
1048        // Loop through all output ports, looking for R Objects of the same
1049        // name, and if they exist, convert the R object to a Kepler Token
1050        // and write it on the output port
1051        _opList = outputPortList();
1052        _iterO = _opList.iterator();
1053        while (_iterO.hasNext()) {
1054            TypedIOPort tiop = (TypedIOPort) _iterO.next();
1055            if (tiop.getName().equals("output")) {
1056                tiop.send(0, new StringToken(_console.getConsoleOutput()));
1057            } else if (tiop.getName().equals("graphicsFileName")) {
1058                tiop.send(0, new StringToken(_graphicsOutputFile));
1059            } else {
1060                // handle multiport
1061                int numSinks = tiop.numberOfSinks();
1062                int width = tiop.getWidth();
1063
1064                REXP rDataObject = _rEngine.eval(tiop.getName());
1065                if (rDataObject != null) {
1066                    log.debug("Symbol found for port " + tiop.getName() + ": "
1067                            + rDataObject.toString());
1068
1069                    // get the token from R
1070                    Token t = _convertToToken(rDataObject, tiop.getName());
1071
1072                    // make sure that the sinks can handle the token
1073                    for (int channelIndex = 0; channelIndex < numSinks; channelIndex++) {
1074                        Type sinkType = ((TypedIOPort) tiop.sinkPortList()
1075                                .get(channelIndex)).getType();
1076
1077                        if (!sinkType.isCompatible(t.getType())) {
1078                            log.debug("[re]Setting sink type to: "
1079                                    + t.getType().toString());
1080                            // set the Type for the sinks
1081                            // POSSIBLE BUG - not sure why the automatic type
1082                            // resolution was failing for downstream port
1083                            ((TypedIOPort) tiop.sinkPortList()
1084                                    .get(channelIndex))
1085                                            .setTypeEquals(t.getType());
1086                        }
1087                    }
1088
1089                    // send token to each channel
1090                    for (int channelIndex = 0; channelIndex < width; channelIndex++) {
1091                        tiop.setTypeEquals(t.getType());
1092                        tiop.send(channelIndex, t);
1093                    }
1094
1095                } else {
1096                    log.debug("No symbol found for port: " + tiop.getName());
1097                }
1098            }
1099        }
1100        log.debug("Done writing R output.");
1101    }
1102
1103    private void _showGraphics() throws IllegalActionException {
1104
1105        boolean displayGraphicsOutputValue = ((BooleanToken) displayGraphicsOutput
1106                .getToken()).booleanValue();
1107
1108        if (displayGraphicsOutputValue) {
1109            try {
1110                File fout = new File(_graphicsOutputFile);
1111                URL furl = fout.toURL();
1112                BrowserLauncher.openURL(furl.toString());
1113            } catch (Exception e) {
1114                log.error("Oops!:" + e);
1115            }
1116        }
1117    }
1118
1119    private Token _serializeRDataObject(REXP rDataObject, String name) {
1120        if (name != null) {
1121            String fileName = _getUniqueFileName("sav");
1122            _rEngine.eval("conn <- file('" + fileName + "', 'wb')");
1123            _rEngine.eval("serialize(" + name + ", conn)");
1124            _rEngine.eval("close(conn)");
1125            return new StringToken("_dataframe_:" + fileName);
1126        }
1127        return null;
1128    }
1129
1130    /**
1131     * Convert the data from the R expression type by determining the type of
1132     * the R object returned and then convert this to an appropriate Kepler
1133     * Token object that can be sent on the output port.
1134     *
1135     * @param rDataObject
1136     *            the R Object that should be converted to a subclass of
1137     *            ptolemy.data.Token
1138     * @exception IllegalActionException
1139     */
1140    private Token _convertToToken(REXP rDataObject, String name)
1141            throws IllegalActionException {
1142        Token t = null;
1143        Token[] tokenArray = null;
1144        if (rDataObject != null) {
1145            int xt = rDataObject.getType();
1146            log.debug("Type found is: " + xt);
1147            //Object rawContent = rDataObject.getContent();
1148            //log.debug("Raw content is: " + rawContent);
1149
1150            switch (xt) {
1151            case REXP.XT_BOOL:
1152                RBool b = rDataObject.asBool();
1153                if (b == null) {
1154                    t = new BooleanToken("nil");
1155                } else if (b.isFALSE()) {
1156                    t = new BooleanToken(false);
1157                } else if (b.isTRUE()) {
1158                    t = new BooleanToken(true);
1159                } else { // b.isNA()
1160                    t = new BooleanToken("nil");
1161                }
1162                break;
1163            case REXP.XT_DOUBLE:
1164                t = new DoubleToken(rDataObject.asDouble());
1165                break;
1166            case REXP.XT_FACTOR:
1167                log.debug("R object is XT_FACTOR");
1168                RFactor factor = rDataObject.asFactor();
1169                if (factor == null) {
1170                    t = ArrayToken.NIL;
1171                } else {
1172                    List factorValues = new ArrayList();
1173                    for (int i = 0; i < factor.size(); i++) {
1174                        StringToken stringToken = new StringToken(factor.at(i));
1175                        factorValues.add(stringToken);
1176                    }
1177                    t = new ArrayToken(
1178                            (Token[]) factorValues.toArray(new Token[0]));
1179                }
1180                break;
1181            case REXP.XT_INT:
1182                t = new IntToken(rDataObject.asInt());
1183                break;
1184            case REXP.XT_LIST:
1185                log.debug("R object is XT_LIST");
1186                RList list = rDataObject.asList();
1187                if (list == null) {
1188                    t = new OrderedRecordToken();
1189                } else {
1190                    String[] keys = list.keys();
1191                    if (keys != null) {
1192                        List values = new ArrayList();
1193                        for (int i = 0; i < keys.length; i++) {
1194                            REXP col = list.at(i);
1195                            // recursion!
1196                            Token token = _convertToToken(col, null);
1197                            values.add(token);
1198                        }
1199                        // put it all together in a record
1200                        t = new OrderedRecordToken(keys,
1201                                (Token[]) values.toArray(new Token[0]));
1202                    }
1203                }
1204                break;
1205            case REXP.XT_NULL:
1206                t = Token.NIL;
1207                break;
1208            case REXP.XT_STR:
1209                t = new StringToken(rDataObject.asString());
1210                break;
1211            case REXP.XT_SYM:
1212                log.debug("R object is XT_SYM");
1213                break;
1214            case REXP.XT_UNKNOWN:
1215                log.debug("R object is XT_UNKNOWN");
1216                break;
1217            case REXP.XT_VECTOR:
1218                log.debug("I am a XT_VECTOR!");
1219                RVector vector = rDataObject.asVector();
1220                if (vector == null) {
1221                    t = new ArrayToken(Token.NIL.getType());
1222                } else {
1223                    // handle data.frame/Record structure
1224                    List names = vector.getNames();
1225                    if (names != null) {
1226                        // preserve _every_ aspect of the data object
1227                        if (((BooleanToken) serializeData.getToken())
1228                                .booleanValue()) {
1229                            t = _serializeRDataObject(rDataObject, name);
1230                        } else {
1231                            List values = new ArrayList();
1232                            for (int i = 0; i < names.size(); i++) {
1233                                String columnName = (String) names.get(i);
1234                                REXP col = vector.at(columnName);
1235                                // recursion!
1236                                Token token = _convertToToken(col, null);
1237                                values.add(token);
1238                            }
1239                            // put it all together in a record
1240                            String[] namesArray = (String[]) names
1241                                    .toArray(new String[0]);
1242                            Token[] valuesArray = (Token[]) values
1243                                    .toArray(new Token[0]);
1244                            t = new OrderedRecordToken(namesArray, valuesArray);
1245                        }
1246                    } else {
1247                        // handle a List
1248                        List values = new ArrayList();
1249                        for (int i = 0; i < vector.size(); i++) {
1250                            REXP value = vector.at(i);
1251                            // recursion!
1252                            Token token = _convertToToken(value, null);
1253                            values.add(token);
1254                        }
1255                        // put it all together in an array
1256                        if (values.isEmpty()) {
1257                            t = new ArrayToken(Token.NIL.getType());
1258                        } else {
1259                            t = new ArrayToken(
1260                                    (Token[]) values.toArray(new Token[0]));
1261                        }
1262                    }
1263                }
1264                break;
1265            case REXP.XT_ARRAY_BOOL:
1266                int[] xb = rDataObject.asIntArray();
1267                if (xb == null) {
1268                    t = ArrayToken.NIL;
1269                } else {
1270                    tokenArray = new Token[xb.length];
1271                    for (int i = 0; i < xb.length; i++) {
1272                        String val = xb[i] == 0 ? "false"
1273                                : (xb[i] == 1 ? "true" : "nil");
1274                        BooleanToken bt = new BooleanToken(val);
1275                        tokenArray[i] = bt;
1276                    }
1277                    t = new ArrayToken(tokenArray);
1278                }
1279                break;
1280            case REXP.XT_ARRAY_BOOL_INT:
1281                // try matrix first
1282                boolean[][] bMatrix = asBooleanMatrix(rDataObject);
1283                if (bMatrix != null) {
1284                    t = new BooleanMatrixToken(bMatrix);
1285                    break;
1286                }
1287                int[] xbi = rDataObject.asIntArray();
1288                if (xbi == null) {
1289                    t = ArrayToken.NIL;
1290                } else {
1291                    tokenArray = new Token[xbi.length];
1292                    for (int i = 0; i < xbi.length; i++) {
1293                        String val = xbi[i] == 0 ? "false"
1294                                : (xbi[i] == 1 ? "true" : "nil");
1295                        BooleanToken bt = new BooleanToken(val);
1296                        tokenArray[i] = bt;
1297                    }
1298                    t = new ArrayToken(tokenArray);
1299                }
1300                break;
1301            case REXP.XT_ARRAY_DOUBLE:
1302                // try as matrix first
1303                double[][] matrix = rDataObject.asDoubleMatrix();
1304                if (matrix != null) {
1305                    t = new DoubleMatrixToken(matrix);
1306                    break;
1307                }
1308                // otherwise it is a simple list
1309                double[] xd = rDataObject.asDoubleArray();
1310                if (xd == null) {
1311                    t = ArrayToken.NIL;
1312                } else {
1313                    tokenArray = new Token[xd.length];
1314                    for (int i = 0; i < xd.length; i++) {
1315                        DoubleToken dt = new DoubleToken(xd[i]);
1316                        tokenArray[i] = dt;
1317                    }
1318                    t = new ArrayToken(tokenArray);
1319                }
1320                break;
1321            case REXP.XT_ARRAY_INT:
1322                // try as matrix first
1323                double[][] matrixD = rDataObject.asDoubleMatrix();
1324                if (matrixD != null) {
1325                    t = new IntMatrixToken(asIntMatrix(matrixD));
1326                    break;
1327                }
1328                int[] xi = rDataObject.asIntArray();
1329                if (xi != null) {
1330                    t = ArrayToken.NIL;
1331                } else {
1332                    tokenArray = new Token[xi.length];
1333                    for (int i = 0; i < xi.length; i++) {
1334                        IntToken dt = new IntToken(xi[i]);
1335                        tokenArray[i] = dt;
1336                    }
1337                    t = new ArrayToken(tokenArray);
1338                }
1339                break;
1340            case REXP.XT_ARRAY_STR:
1341                String[] xs = rDataObject.asStringArray();
1342                if (xs == null) {
1343                    t = ArrayToken.NIL;
1344                } else {
1345                    tokenArray = new Token[xs.length];
1346                    for (int i = 0; i < xs.length; i++) {
1347                        StringToken st = new StringToken(xs[i]);
1348                        tokenArray[i] = st;
1349                    }
1350                    t = new ArrayToken(tokenArray);
1351                }
1352                break;
1353            }
1354        }
1355        //return a single token in cases when it's believed to be a list (JRI added this)
1356        //TODO: parameterize the switch
1357        boolean asArrayToken = true;
1358        if (!asArrayToken) {
1359            if (t instanceof ArrayToken) {
1360                if (((ArrayToken) t).length() == 1) {
1361                    t = ((ArrayToken) t).getElement(0);
1362                }
1363            }
1364        }
1365        return t;
1366    }
1367
1368    private String _generateGraphicsDeviceCode(String graphicsFormatString,
1369            boolean graphicsOutputValue) {
1370        String nxs = "";
1371        String nys = "";
1372        try {
1373            nxs = numXPixels.stringValue();
1374            try {
1375                int nxp = (new Integer(nxs)).intValue();
1376            } catch (Exception w) {
1377                nxs = "480";
1378            }
1379        } catch (IllegalActionException iae) {
1380
1381        }
1382
1383        try {
1384            nys = numYPixels.stringValue();
1385            try {
1386                int nyp = (new Integer(nys)).intValue();
1387            } catch (Exception w1) {
1388                nys = "480";
1389            }
1390        } catch (IllegalActionException iae) {
1391
1392        }
1393        _graphicsOutputFile = _getUniqueFileName(graphicsFormatString);
1394        String graphicsDevice = "";
1395        if (graphicsOutputValue) {
1396            int nxp = (new Integer(nxs)).intValue();
1397            double nxd = nxp / 72.0;
1398            int nyp = (new Integer(nys)).intValue();
1399            double nyd = nyp / 72.0;
1400            if (graphicsFormatString.equals("pdf")) {
1401                graphicsDevice = "pdf(file = '" + _graphicsOutputFile + "'"
1402                        + ",width = " + nxd + ", height = " + nyd + ")";
1403            } else {
1404                graphicsDevice = "png(filename = '" + _graphicsOutputFile + "'"
1405                        + ",width = " + nxs + ", height = " + nys
1406                        + ", pointsize = 12, bg = 'white')";
1407            }
1408        }
1409        return graphicsDevice;
1410    }
1411
1412    private String _getUniqueFileName(String extender) {
1413        int cnt = 1;
1414        // String usr_name = System.getProperty("user.name");
1415        String actor_name = this.getName();
1416        actor_name = actor_name.replace(' ', '_');
1417        String fn = actor_name + cnt + "." + extender;
1418        String path = _home;
1419        while (new File(path, fn).exists()) {
1420            cnt++;
1421            fn = actor_name + cnt + "." + extender;
1422        }
1423        return new File(path, fn).getAbsolutePath();
1424    }
1425
1426    ///////////////////////////////////////////////////////////////////
1427    ////                         private variables                 ////
1428
1429    /**
1430     * Execute the R system and run the model found in the expression provided
1431     * in the RExpression "expression" attribute. When complete, the REngine can
1432     * be queried to obtain an REXP object with the results of the execution.
1433     * @exception IllegalActionException
1434     *
1435     */
1436    private void _executeRModel() throws IllegalActionException {
1437        log.debug("Begin R script execution.");
1438
1439        // get the lines of the script
1440        String script = expression.getExpression();
1441
1442        // file for the source
1443        String tempFile = null;
1444        PrintWriter printWriter = null;
1445        try {
1446            tempFile = _getUniqueFileName("r");
1447            // write to file
1448            OutputStreamWriter w = new OutputStreamWriter(
1449                    new FileOutputStream(tempFile), "UTF-8");
1450            printWriter = new PrintWriter(w);
1451            printWriter.write(script);
1452        } catch (IOException e1) {
1453            log.error("could not create temp R source file");
1454            throw new IllegalActionException(this, e1,
1455                    "Could not create temp R source file: " + tempFile);
1456        } finally {
1457            if (printWriter != null) {
1458                printWriter.close();
1459            }
1460        }
1461
1462        // simple!
1463        String line = "source('" + tempFile + "')";
1464
1465        // write the expression to the logging _console
1466        _console.rWriteConsole(_rEngine, "> " + line + "\n", REXP.XT_STR);
1467        boolean showEval = ((BooleanToken) showDebug.getToken()).booleanValue();
1468
1469        // evaluate the expression
1470        REXP x = _rEngine.eval(line, showEval);
1471
1472        // show result if configured to do so
1473        if (showEval) {
1474            try {
1475                Token token = _convertToToken(x, null);
1476                _console.rWriteConsole(_rEngine, "Result: " + token + "\n",
1477                        x.rtype);
1478            } catch (Exception e) {
1479                log.debug("error showing debug results from R engine: "
1480                        + e.getMessage());
1481            }
1482        }
1483        log.debug(x);
1484
1485        log.debug("Finished R execution.");
1486    }
1487
1488    private static String _noRErrorMessage = "There has been a problem launching R!\n"
1489            + "It may be that R is not installed on your system, or it\n"
1490            + "may not be on your path and cannot be located by Kepler.\n Please"
1491            + "make sure R is installed and the R command line \n executable is in the path."
1492            + "For more information, see \n section 8.2.2 of the Kepler User Manual.";
1493
1494    private Rengine _rEngine = null;
1495
1496    private RConsole _console = null;
1497
1498    private static String _NO_SAVE = "--no-save";
1499
1500    private static String _NO_RESTORE = "--no-restore";
1501
1502    private String _graphicsOutputFile = "";
1503
1504    private List _opList;
1505
1506    private Iterator _iterO;
1507
1508    private String _home;
1509
1510}