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}