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}