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