Java tutorial
/* * Copyright (c) 2003-2010 The Regents of the University of California. * All rights reserved. * * '$Author: crawl $' * '$Date: 2012-11-26 14:23:15 -0800 (Mon, 26 Nov 2012) $' * '$Revision: 31126 $' * * Permission is hereby granted, without written agreement and without * license or royalty fees, to use, copy, modify, and distribute this * software and its documentation for any purpose, provided that the above * copyright notice and the following two paragraphs appear in all copies * of this software. * * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, * ENHANCEMENTS, OR MODIFICATIONS. * */ package org.ecoinformatics.seek.R; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.StringReader; import java.net.URL; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.kepler.util.DotKeplerManager; import ptolemy.actor.TypedAtomicActor; import ptolemy.actor.TypedIOPort; import ptolemy.actor.gui.BrowserLauncher; import ptolemy.actor.gui.style.TextStyle; import ptolemy.data.ArrayToken; import ptolemy.data.BooleanMatrixToken; import ptolemy.data.BooleanToken; import ptolemy.data.DoubleMatrixToken; import ptolemy.data.DoubleToken; import ptolemy.data.FloatToken; import ptolemy.data.IntMatrixToken; import ptolemy.data.MatrixToken; import ptolemy.data.RecordToken; import ptolemy.data.StringToken; import ptolemy.data.Token; import ptolemy.data.expr.Parameter; import ptolemy.data.expr.StringParameter; import ptolemy.data.type.ArrayType; import ptolemy.data.type.BaseType; import ptolemy.data.type.Type; import ptolemy.kernel.CompositeEntity; import ptolemy.kernel.util.IllegalActionException; import ptolemy.kernel.util.InternalErrorException; import ptolemy.kernel.util.NameDuplicationException; import ptolemy.kernel.util.Nameable; import ptolemy.kernel.util.StringAttribute; import ptolemy.kernel.util.Workspace; import ptolemy.util.StringUtilities; import ptolemy.vergil.toolbox.TextEditorTableauFactory; import util.WorkflowExecutionListener; ////RExpression /** * The RExpression actor is an actor designed to run an R script or function * with inputs and outputs determined by the ports created by the user. Port * names will correspond to R object names. The RExpression actor is modeled * after the Ptolemy expression actor, except that that instead of using a * single mathematical expression in Ptolemy's expression language, it uses a * set of the more powerful, higher order expressions available under R. Both * input and output port will usually be added to the actor; The names of these * ports correspond to R variables used in the R script. * * @author Dan Higgins, NCEAS, UC Santa Barbara * @version 3/3/2006 * @UserLevelDocumentation This actor let the user insert R scripts in a Kepler * workflow. It requires the R system to be installed on * the computer executing the workflow */ public class RExpression extends TypedAtomicActor { public static Log log = LogFactory.getLog(RExpression.class); // ///////////////////////////////////////////////////////////////// // // ports and parameters //// /** * The output port. */ public TypedIOPort output; /** * The expression that is evaluated to produce the output. */ public StringAttribute expression; /** * This setting determines whether or not to save the R workspace when R is * closed; set to '--save' if you need to retreive the workspace later in a * workflow in another RExpression actor. */ public StringParameter save_nosave; /** * The 'R' working directory (home dir by default) */ public StringParameter Rcwd; private static String NO_SAVE = "--no-save"; private static String SAVE = "--save"; private static String NO_RESTORE = "--no-restore"; private static String RdotExe = "R"; private int cntr = 0; /** * If <i>true</i>, then display plot. If <i>false</i>, then don't. (the * default) */ public Parameter displayGraphicsOutput; /** * The graphics output format. Currently the format is either a *.pdf or a * *.png */ public StringParameter graphicsFormat; /** * If <i>true</i>, then create a graphics output port. (the default); If * <i>false</i>, then don't. */ public Parameter graphicsOutput; /** *The width of the output graphics bitmap in pixels */ public StringParameter numXPixels; /** * The height of the output graphics bitmap in pixels */ public StringParameter numYPixels; private String saveString; private String restoreString = NO_RESTORE; // if arrays sent to R are longer than this value, then use a file // rather than a string on the command line to pass the data // This is necessary because apparently R has a fixed buffer // for passing long commands private int maxCommandLineLength = 30000; /** * The name of the default graphics output file created by the actor */ public TypedIOPort graphicsFileName; /** * Construct an actor with the given container and name. * * @param container * The container. * @param name * The name of this actor. * @exception IllegalActionException * If the actor cannot be contained by the proposed * container. * @exception NameDuplicationException * If the container already has an actor with this name. */ public RExpression(CompositeEntity container, String name) throws NameDuplicationException, IllegalActionException { super(container, name); expression = new StringAttribute(this, "expression"); expression.setDisplayName("R function or script"); new TextStyle(expression, "R Expression"); //looks odd, but gives us the larger text area expression.setExpression("a <- c(1,2,3,5)\nplot(a)"); // use the text editor when we "open" the actor TextEditorTableauFactory _editorFactory = new TextEditorTableauFactory(this, "_editorFactory"); _editorFactory.attributeName.setExpression("expression"); Rcwd = new StringParameter(this, "Rcwd"); Rcwd.setDisplayName("R working directory"); Rcwd.setExpression(DotKeplerManager.getInstance().getTransientModuleDirectory("r").toString()); save_nosave = new StringParameter(this, "save_nosave"); save_nosave.setDisplayName("Save or not"); save_nosave.setExpression(NO_SAVE); save_nosave.addChoice(NO_SAVE); save_nosave.addChoice(SAVE); graphicsFormat = new StringParameter(this, "graphicsFormat"); graphicsFormat.setDisplayName("Graphics Format"); graphicsFormat.setExpression("png"); graphicsFormat.addChoice("pdf"); graphicsFormat.addChoice("png"); graphicsFormat.addChoice("jpg"); graphicsFormat.addChoice("bmp"); graphicsFormat.addChoice("tiff"); graphicsFormat.addChoice("eps"); graphicsFormat.addChoice("ps"); //graphicsFormat.addChoice("wmf"); graphicsFormat.addChoice("svg"); //graphicsFormat.addChoice("fig"); graphicsFormat.addChoice("ghostscript bitmap type pngalpha"); graphicsFormat.addChoice("ghostscript bitmap type png16m"); graphicsFormat.addChoice("ghostscript bitmap type png256"); // restore parameter is removed for now because it doesn't work // .RData is saved in the working directory by 'save' but R doesn't look // there // To restore a saved workspace, add the command 'load(".RData') to the // script // restore_norestore = new StringParameter(this, "restore or not"); // restore_norestore.setExpression(NO_RESTORE); // restore_norestore.addChoice(NO_RESTORE); // restore_norestore.addChoice(RESTORE); graphicsOutput = new Parameter(this, "graphicsOutput"); graphicsOutput.setDisplayName("Graphics Output"); graphicsOutput.setTypeEquals(BaseType.BOOLEAN); graphicsOutput.setToken(BooleanToken.TRUE); displayGraphicsOutput = new Parameter(this, "displayGraphicsOutput"); displayGraphicsOutput.setDisplayName("Automatically display graphics"); displayGraphicsOutput.setTypeEquals(BaseType.BOOLEAN); displayGraphicsOutput.setToken(BooleanToken.FALSE); numXPixels = new StringParameter(this, "numXPixels"); numXPixels.setDisplayName("Number of X pixels in image"); numXPixels.setExpression("480"); numYPixels = new StringParameter(this, "numYPixels"); numYPixels.setDisplayName("Number of Y pixels in image"); numYPixels.setExpression("480"); graphicsFileName = new TypedIOPort(this, "graphicsFileName", false, true); graphicsFileName.setTypeEquals(BaseType.STRING); output = new TypedIOPort(this, "output", false, true); output.setTypeEquals(BaseType.STRING); } /** * Override the base class to set type constraints. * * @param workspace * The workspace for the new object. * @return A new instance of RExpression. * @exception CloneNotSupportedException * If a derived class contains an attribute that cannot be * cloned. */ public Object clone(Workspace workspace) throws CloneNotSupportedException { RExpression newObject = (RExpression) super.clone(workspace); newObject.output.setTypeEquals(BaseType.STRING); newObject.graphicsFileName.setTypeEquals(BaseType.STRING); return newObject; } public void preinitialize() throws IllegalActionException { super.preinitialize(); // Check for "unknown"-to-"unknown" port connections that are "unacceptable" // See: http://bugzilla.ecoinformatics.org/show_bug.cgi?id=3985 List opList = outputPortList(); Iterator outputIter = opList.iterator(); while (outputIter.hasNext()) { TypedIOPort outputPort = (TypedIOPort) outputIter.next(); if (outputPort.getType().equals(BaseType.UNKNOWN)) { List connectedPorts = outputPort.connectedPortList(); Iterator connectedPortIter = connectedPorts.iterator(); while (connectedPortIter.hasNext()) { TypedIOPort connectedPort = (TypedIOPort) connectedPortIter.next(); if (connectedPort.getType().equals(BaseType.UNKNOWN)) { outputPort.setTypeEquals(BaseType.GENERAL); break; } } } } } public void initialize() throws IllegalActionException { super.initialize(); // reset the tempfile counter cntr = 0; // set the home home = Rcwd.stringValue(); File homeFile = new File(home); // if not a directory, use 'home' if (!homeFile.isDirectory()) home = DotKeplerManager.getInstance().getTransientModuleDirectory("r").toString(); home = home.replace('\\', '/'); if (!home.endsWith("/")) home = home + "/"; // reset the name when workflow execution completes this.getManager().addExecutionListener(WorkflowExecutionListener.getInstance()); String workflowName = this.toplevel().getName(); // workflowName = workflowName.replace(' ','_'); // workflowName = workflowName.replace('-','_'); String execDir = home + workflowName + "_" + WorkflowExecutionListener.getInstance().getId(toplevel()); File dir = new File(execDir); if (!dir.exists()) { dir.mkdir(); } home = execDir + "/"; } /* * The fire method should first call the superclass. Then all the input * ports should be scanned to see which ones have tokens. The names of those * ports should be used to create a named R object (array?). R script for * creating objects corresponding to these ports should be inserted before * the script in the expressions parameter. Then the R engine should be * started and run, with the output sent to the output port. */ public synchronized void fire() throws IllegalActionException { String newline = System.getProperty("line.separator"); super.fire(); boolean graphicsOutputValue = ((BooleanToken) graphicsOutput.getToken()).booleanValue(); boolean displayGraphicsOutputValue = ((BooleanToken) displayGraphicsOutput.getToken()).booleanValue(); saveString = save_nosave.stringValue(); // restoreString = restore_norestore.stringValue(); String graphicsFormatString = graphicsFormat.stringValue(); // following line insures that graphics is pdf if automatically // displayed // NOT going to automatically do this anymore. Do what the user asks. //if (displayGraphicsOutputValue) // graphicsFormatString = "pdf"; // force file format to 'pdf' is this is a Mac // NOT going to force PDF for Mac - not sure why this was in place (legacy?) // String lcOSName = System.getProperty("os.name").toLowerCase(); // boolean MAC_OS_X = lcOSName.startsWith("mac os x"); // if (MAC_OS_X) { // graphicsFormatString = "pdf"; // } String nxs = numXPixels.stringValue(); try { (new Integer(nxs)).intValue(); } catch (Exception w) { nxs = "480"; } String nys = numYPixels.stringValue(); try { (new Integer(nys)).intValue(); } catch (Exception w1) { nys = "480"; } String setCWD = "setwd('" + home + "')\n"; graphicsOutputFile = _getUniqueFileName(graphicsFormatString); String graphicsDevice = ""; if (graphicsOutputValue) { // Why not move this stuff up to the try statements for nxs and nys? // It looks like we're doing this twice. --Oliver int nxp = (new Integer(nxs)).intValue(); double nxd = nxp / 72.0; int nyp = (new Integer(nys)).intValue(); double nyd = nyp / 72.0; if (graphicsFormatString.equals("pdf")) { graphicsDevice = "pdf(file = '" + graphicsOutputFile + "'" + ", width = " + nxd + ", height = " + nyd + ")"; } else if (graphicsFormatString.equals("jpeg") || graphicsFormatString.equals("jpg")) { graphicsDevice = "jpeg(filename = '" + graphicsOutputFile + "'" + ", width = " + nxs + ", height = " + nys + ")"; } else if (graphicsFormatString.equals("png")) { graphicsDevice = "png(file = '" + graphicsOutputFile + "'" + ", width = " + nxs + ", height = " + nys + ")"; } else if (graphicsFormatString.equals("bmp")) { graphicsDevice = "bmp(filename = '" + graphicsOutputFile + "'" + ", width = " + nxs + ", height = " + nys + ")"; } else if (graphicsFormatString.equals("tiff") || graphicsFormatString.equals("tif")) { graphicsDevice = "tiff(filename = '" + graphicsOutputFile + "'" + ", width = " + nxs + ", height = " + nys + ")"; } else if (graphicsFormatString.equals("postscript") || graphicsFormatString.equals("ps")) { graphicsOutputFile = _getUniqueFileName("ps"); graphicsDevice = "postscript(file = '" + graphicsOutputFile + "'" + ", width = " + nxd + ", height = " + nyd + ")"; } else if (graphicsFormatString.equals("eps")) { graphicsDevice = "setEPS()\n"; graphicsDevice += "postscript(file = '" + graphicsOutputFile + "'" + ", width = " + nxd + ", height = " + nyd + ")"; } else if (graphicsFormatString.equals("win.metafile") || graphicsFormatString.equals("wmf")) { graphicsOutputFile = _getUniqueFileName("wmf"); graphicsDevice = "win.metafile(filename = '" + graphicsOutputFile + "'" + ", width = " + nxd + ", height = " + nyd + ")"; } else if (graphicsFormatString.equals("svg")) { graphicsDevice = "svg(filename = '" + graphicsOutputFile + "'" + ", width = " + nxd + ", height = " + nyd + ")"; } else if (graphicsFormatString.equals("xfig") || graphicsFormatString.equals("fig")) { graphicsOutputFile = _getUniqueFileName("fig"); graphicsDevice = "xfig(file = '" + graphicsOutputFile + "'" + ", width = " + nxd + ", height = " + nyd + ")"; } else if (graphicsFormatString.equals("ghostscript bitmap type pngalpha")) { graphicsDevice = "bitmap(file = '" + graphicsOutputFile + "', type = \"pngalpha\", width = " + nxd + ", height = " + nyd + ")"; } else if (graphicsFormatString.equals("ghostscript bitmap type png16m")) { graphicsDevice = "bitmap(file = '" + graphicsOutputFile + "', type = \"png16m\", width = " + nxd + ", height = " + nyd + ")"; } else if (graphicsFormatString.equals("ghostscript bitmap type png256")) { graphicsDevice = "bitmap(file = '" + graphicsOutputFile + "', type = \"png256\", width = " + nxd + ", height = " + nyd + ")"; } } List ipList = inputPortList(); Iterator iter_i = ipList.iterator(); opList = outputPortList(); iter_o = opList.iterator(); String RPortInfo = ""; RPortInfo = setCWD + graphicsDevice + "\n"; Token at; String temp1; while (iter_i.hasNext()) { TypedIOPort tiop = (TypedIOPort) iter_i.next(); int multiPortSize = tiop.numberOfSources(); List sourcePorts = tiop.sourcePortList(); for (int i = 0; i < multiPortSize; i++) { try { if (tiop.hasToken(i)) { String finalPortName = tiop.getName(); String sourcePortName = ((TypedIOPort) sourcePorts.get(i)).getName(); String tempPortName = tiop.getName(); String temp = tiop.getName(); Token token = tiop.get(i); String token_type_string = token.getType().toString(); String token_class_name = token.getType().getTokenClass().getName(); // if this is a multiport, use the upstream source for // the variable name if (tiop.isMultiport()) { temp = temp + i; tempPortName = temp; } // log.debug("token_type_string - " + // token_type_string); // log.debug("token_class_name - " + // token_class_name); // check token type and convert to R appropriately if (token_type_string.equals("string")) { // check for special strings that indicate dataframe // file reference at = (Token) token; temp1 = at.toString(); temp1 = temp1.substring(1, temp1.length() - 1); // remove // quotes if (temp1.startsWith("_dataframe_:")) { // assume that the string for a dataframe file // reference is of the form // '_dataframe_:"+<filename> temp1 = temp1.substring(12); // should be // filename // temp = "`" + temp + "` <- " + // "read.table(file='"+temp1+"')"; // RPortInfo = RPortInfo + temp + "\n"; // use binary version that was serialized RPortInfo = RPortInfo + "conn <- file('" + temp1 + "', 'rb');\n`" + temp + "` <- unserialize(conn);\n" + "close(conn);\n"; // remove the transfer file when we are done // consuming it // this is problematic when dataframes are // output to multiple sinks! // String removeCommand = "file.remove('" + // temp1 + "')"; // RPortInfo = RPortInfo + removeCommand + "\n"; continue; // stop for this token and go to the // next } else if (temp1.startsWith("_object_:")) { // assume that the string for an object file // reference is of the form // '_object_:"+<filename> temp1 = temp1.substring(9); // should be // filename // use binary version that was serialized RPortInfo = RPortInfo + "conn <- file('" + temp1 + "', 'rb');\n`" + temp + "` <- unserialize(conn);\n" + "close(conn);\n"; // remove the transfer file when we are done // consuming it // this is problematic when objects are output // to multiple sinks! // String removeCommand = "file.remove('" + // temp1 + "')"; // RPortInfo = RPortInfo + removeCommand + "\n"; continue; // stop for this token and go to the // next } } if (token instanceof RecordToken) { String Rcommands = _recordToDataFrame((RecordToken) token, temp); Rcommands = _breakIntoLines(Rcommands); RPortInfo = RPortInfo + Rcommands + "\n"; } // convert Kepler matrices to R matrices else if ((token_class_name.indexOf("IntMatrixToken") > -1) || (token_class_name.indexOf("DoubleMatrixToken") > -1) || (token_class_name.indexOf("BooleanMatrixToken") > -1)) { int rows = ((MatrixToken) token).getRowCount(); int cols = ((MatrixToken) token).getColumnCount(); temp1 = token.toString(); temp1 = temp1.replace('\\', '/'); temp1 = temp1.replace('[', '('); temp1 = temp1.replace(']', ')'); temp1 = temp1.replace(';', ','); temp1 = temp1.replace('"', '\''); // assume that the token's string value might be // 'nil' for a missing value temp1 = temp1.replaceAll("nil", "NA"); // TO DO:if string is long, should create a temp // file for passing array data temp = "`" + temp + "` <- matrix(c" + temp1 + ", nrow=" + rows + ",ncol=" + cols + ")"; temp = _breakIntoLines(temp); RPortInfo = RPortInfo + temp + "\n"; } else if ((token_type_string.equals("double")) || (token_type_string.equals("int")) || (token_type_string.equals("string"))) { at = (Token) token; temp1 = at.toString(); // we need to check here if we are passing a string // like '/t' (tab) // Note that quotes are returned around string // tokens // The string "/t" is particularly meaningful when // passed as a seperator // for R expressions -- DFH April 19 // Note that previous versions of PTII returned // slightly different format // strings, so this was not necessary. if (!temp1.equals("\"\\t\"")) { temp1 = temp1.replace('\\', '/'); // assume that the token's string value might be // 'nil' for a missing value temp1 = temp1.replaceAll("nil", "NA"); } temp = "`" + temp + "` <- " + temp1; RPortInfo = RPortInfo + temp + "\n"; } else if ((token_type_string.equals("boolean"))) { at = (Token) token; temp1 = at.toString(); // ensure uppercase for boolean temp1 = temp1.toUpperCase(); temp = "`" + temp + "` <- " + temp1; RPortInfo = RPortInfo + temp + "\n"; } else if ((token_type_string.equals("float"))) { FloatToken ft = (FloatToken) token; DoubleToken dt = new DoubleToken(ft.doubleValue()); at = (Token) token; temp1 = dt.toString(); // we need to check here if we are passing a string // like '/t' (tab) // Note that quotes are returned around string // tokens // The string "/t" is particularly meaningful when // passed as a seperator // for R expressions -- DFH April 19 // Note that previous versions of PTII returned // slightly different format // strings, so this was not necessary. if (!temp1.equals("\"\\t\"")) { temp1 = temp1.replace('\\', '/'); // assume that the token's string value might be // 'nil' for a missing value temp1 = temp1.replaceAll("nil", "NA"); } temp = "`" + temp + "` <- " + temp1; RPortInfo = RPortInfo + temp + "\n"; } else if ((token_type_string.equals("{double}")) || (token_type_string.equals("{int}")) || (token_type_string.startsWith("arrayType(double")) || (token_type_string.startsWith("arrayType(int")) || (token_type_string.startsWith("arrayType(niltype")) || (token_type_string.startsWith("arrayType(arrayType(double")) || (token_type_string.startsWith("arrayType(arrayType(int"))) { // token is an arrayToken !!! at = (Token) token; temp1 = at.toString(); temp1 = temp1.replace('\\', '/'); temp1 = temp1.replaceFirst("\\{", "("); temp1 = temp1.replaceAll("\\{", "c("); temp1 = temp1.replace('}', ')'); temp1 = temp1.replace('"', '\''); // assume that the token's string value might be // 'nil' for a missing value temp1 = temp1.replaceAll("nil", "NA"); // if string is long, create a temp file for passing // array data if (temp1.length() > maxCommandLineLength && (!token_type_string.startsWith("arrayType(arrayType(double")) && (!token_type_string.startsWith("arrayType(arrayType(int"))) { temp1 = temp1.replace('(', ' '); temp1 = temp1.replace(')', ' '); String filename = _writeDataFile(temp1); temp = "`" + temp + "` <- scan('" + filename + "', sep=',')"; temp = temp + "\n" + "file.remove('" + filename + "')"; RPortInfo = RPortInfo + temp + "\n"; } else { // otherwise use the modified string if (token_type_string.startsWith("arrayType(arrayType(int") || token_type_string.startsWith("arrayType(arrayType(double")) { temp = "`" + temp + "` <- list" + temp1; } else { temp = "`" + temp + "` <- c" + temp1; } temp = _breakIntoLines(temp); RPortInfo = RPortInfo + temp + "\n"; } } else if ((token_type_string.equals("{float}")) || (token_type_string.startsWith("arrayType(float")) || token_type_string.startsWith("arrayType(arrayType(float")) { // token is an arrayToken !!! ArrayToken arrtok = (ArrayToken) token; StringBuffer buffer = new StringBuffer("{"); for (int j = 0; j < arrtok.length(); j++) { FloatToken ft = (FloatToken) arrtok.getElement(j); buffer.append(new DoubleToken(ft.doubleValue()).toString()); if (j < (arrtok.length() - 1)) { buffer.append(", "); } } buffer.append("}"); temp1 = buffer.toString(); temp1 = temp1.replace('\\', '/'); temp1 = temp1.replaceFirst("\\{", "("); temp1 = temp1.replaceAll("\\{", "c("); temp1 = temp1.replace('}', ')'); temp1 = temp1.replace('"', '\''); // assume that the token's string value might be // 'nil' for a missing value temp1 = temp1.replaceAll("nil", "NA"); // if string is long, create a temp file for passing // array data if (temp1.length() > maxCommandLineLength && (!token_type_string.startsWith("arrayType(arrayType(float"))) { temp1 = temp1.replace('(', ' '); temp1 = temp1.replace(')', ' '); String filename = _writeDataFile(temp1); temp = "`" + temp + "` <- scan('" + filename + "', sep=',')"; temp = temp + "\n" + "file.remove('" + filename + "')"; RPortInfo = RPortInfo + temp + "\n"; } else { // otherwise use the modified string if (token_type_string.startsWith("arrayType(arrayType(float")) { temp = "`" + temp + "` <- list" + temp1; } else { temp = "`" + temp + "` <- c" + temp1; } temp = _breakIntoLines(temp); RPortInfo = RPortInfo + temp + "\n"; } } else if ((token_type_string.equals("{string}")) || (token_type_string.startsWith("arrayType(string") || (token_type_string.startsWith("arrayType(arrayType(string")))) { // token is an arrayToken !!! at = (Token) token; temp1 = at.toString(); temp1 = temp1.replace('\\', '/'); temp1 = temp1.replaceFirst("\\{", "("); temp1 = temp1.replaceAll("\\{", "c("); temp1 = temp1.replace('}', ')'); temp1 = temp1.replace('"', '\''); // assume that the token's string value might be // 'nil' for a missing value temp1 = temp1.replaceAll("nil", "NA"); // if string is long, create a temp file for passing // array data ONLY if ((temp1.length() > maxCommandLineLength) && (!token_type_string.startsWith("arrayType(arrayType(string"))) { temp1 = temp1.replace('(', ' '); temp1 = temp1.replace(')', ' '); String filename = _writeDataFile(temp1); temp = "`" + temp + "` <- scan('" + filename + "', what='character', sep=',', strip.white=TRUE)"; temp = temp + "\n" + "file.remove('" + filename + "')"; RPortInfo = RPortInfo + temp + "\n"; } else { // otherwise use the modified string //for arrays of arrays, use list() if (token_type_string.startsWith("arrayType(arrayType(string")) { temp = "`" + temp + "` <- list" + temp1; } else { temp = "`" + temp + "` <- c" + temp1; } temp = _breakIntoLines(temp); RPortInfo = RPortInfo + temp + "\n"; } } else if (token_type_string.equals("niltype")) { at = token; temp1 = at.toString(); temp1 = temp1.replaceAll("nil", "NA"); temp = "`" + temp + "` <- " + temp1; RPortInfo = RPortInfo + temp + "\n"; } // set metadata on the R objects // String metadataCommand = null; if (tiop.isMultiport()) { // set the metadata on each list item // ("tempPortName") before adding it to the list /* metadataCommand = */_createMetadataCommand(tempPortName, "name", sourcePortName); } else { // just set the metadata attribute for the final // variable name /* metadataCommand = */_createMetadataCommand(finalPortName, "name", sourcePortName); } // add the metadata attribute to the R object // leinfelder, 4/14/2008: // do not include the metadata as it introduces // incompatibility with // certain R methods that expect attributeless objects // (barplot(vector)) // RPortInfo = RPortInfo + metadataCommand + "\n"; // use lists for making multiport input available in R if (tiop.isMultiport()) { String commandList = null; if (i == 0) { // create list commandList = "`" + finalPortName + "` <- list(" + tempPortName + ")"; } else if (i > 0) { // append to list commandList = "`" + finalPortName + "` <- c(" + finalPortName + ", list(" + tempPortName + ") )"; } RPortInfo = RPortInfo + commandList + "\n"; } } } catch (IllegalActionException iae) { // just continue (port is probably not connected) } } // for multiport } // log.debug("RPortInfo: "+RPortInfo); // The following command casues R to output a series of 4 dashes which // are used as a marker // Any R output after this marker is used to construct information for // the actor output // ports. This information is removed from the R output text displayed // to the user. String r_out = "cat('----\\n')\n"; // Ensure that output is echoed from this point on // We don't need to echo before cat('----\\n') because the cat statement forces output. // This way, the options(echo = TRUE) isn't sent to the "output" port r_out += "options(echo = TRUE)\n"; // The following creates an R function called 'myput' to output port // info to output ports // r_out = r_out + // "if (class(x)=='data.frame') {write.table(x,file='"+df_fn+"');cat('_dataframe_:"+df_fn+"')}\n"; r_out = r_out + "myput <- function(x, filename) {\n" // I'm wrapping the serialization into the doserialize function // because it's gotten big. Unique filename generation is // done here because this is where file creation is actually done. // This code relies on the replaceAll code and the added - in the // auto-generated .sav filename. Remember that a \ in the regular // expression is quadrupled for passing through both Java and R. + " doserialize <- function(x, filename) {\n" + " if (file.exists(filename)) {" + " path <- dirname(filename); " + " filename <- basename(filename); " + " base <- sub('^(.*-)([0-9*])\\\\.(.*)$', '\\\\1', filename); " + " ext <- sub('^(.*-)([0-9*])\\\\.(.*)$', '\\\\3', filename); " + " dir_base_ext <- dir(pattern = paste(base, '[0-9]*\\\\.', ext, sep = '')); " + " cnt <- max(as.numeric(sub('^(.*-)([0-9*])\\\\.(.*)$', '\\\\2', dir_base_ext)), na.rm = TRUE) + 1; " + " filename <- file.path(path, paste(base, cnt, '.', ext, sep = ''))" + " }\n" + " conn <- file(filename, 'wb');" + " serialize(x, conn);" + " close(conn);" + " filename" + " }\n" // use a binary serialization for data frames + " if (class(x)=='data.frame') {cat('_dataframe_:', doserialize(x, filename), '\\n', sep = '')}\n" + " else if (class(x)=='matrix') {cat('_matrix_:',deparse(x, control=c('keepNA', 'showAttributes')), '\\n', sep = '') }\n" + " else if (mode(x)=='numeric' && substr(deparse(x)[1], 1, 9) != \"structure\") {dput(as.double(x), control = NULL)}\n" + " else if (mode(x)=='character' && substr(deparse(x)[1], 1, 9) != \"structure\") {dput(x)}\n" + " else if (mode(x)=='logical' && substr(deparse(x)[1], 1, 9) != \"structure\") {dput(x)}\n" // use R serialization for other unknown objects + " else {cat('_object_:', doserialize(x, filename), '\\n', sep = '')}" + "}\n"; // Controlled newline test r_out = r_out + "cat(\"before newline\\nafter newline\\n\")\n"; while (iter_o.hasNext()) { TypedIOPort tiop_o = (TypedIOPort) iter_o.next(); String temp_o = tiop_o.getName(); // now need to create an R script that returns info about an R // object with the // port name for use in creating Kepler output object if ((!temp_o.equals("output")) && (!temp_o.equals("graphicsFileName"))) { String df_fn = _getUniqueFileName(temp_o, "sav"); String temp_o_escaped = temp_o; // Doing some basic escaping for the exists statement, // although I'm not 100% sure all of these characters // might occur. --Oliver temp_o_escaped = temp_o_escaped.replace("\\", "\\\\"); temp_o_escaped = temp_o_escaped.replace("'", "\'"); r_out = r_out + "if(exists('" + temp_o_escaped + "', .GlobalEnv)) {" + "cat(\"portName: " + temp_o + "\\nvectorVal: \"); " + "myput(get(\"" + temp_o_escaped + "\", .GlobalEnv),'" + df_fn + "'); " + "cat(\"endVectorVal\\n\")" + "}\n"; } } String script = expression.getExpression(); script = RPortInfo + script + "\n" + r_out + "\nquit()\n"; try { _exec(); } catch (Exception w) { log.error("Error in _exec()"); } String outputString = ""; String errorString = ""; String noRErrorMessage = "There has been a problem running the R script!\n" + "It may be due to an error in your script, it may be that R is not\n" + "installed on your system, or it may not be on your path and cannot\n" + "be located by Kepler. Please make sure R is installed and the\n" + "R command line executable is in the path." + "For more information, see \n section 8.2.2 of the Kepler User Manual."; try { _inputBufferedWriter.write(script); _inputBufferedWriter.flush(); _inputBufferedWriter.close(); } catch (IOException ex) { log.error("IOException while executing R script."); // Commenting out this loop--this can cause an infinite loop on XP, // (when R is not on user's PATH), which keeps the noRErrorMessage // from ever showing. See bugs #4985 and #5025. //while(outputString.equals("")) { // outputString = _outputGobbler.getAndReset(); // errorString = _errorGobbler.getAndReset(); // log.debug("R standard output: " + newline + outputString); // log.debug("R standard error: " + newline + errorString); //} throw new IllegalActionException(this, ex, "Problem writing input. " + noRErrorMessage); } catch (NullPointerException npe) { throw new IllegalActionException(this, npe, noRErrorMessage); } try { int result = _process.waitFor(); log.debug("Process complete: " + result); if (result != 0) throw new IllegalActionException(this, "R returned with value " + result + ", likely caused " + newline + "by an error while executing the script."); } catch (IllegalActionException e) { log.error(e.getMessage()); while (outputString.equals("")) { outputString = _outputGobbler.getAndReset(); errorString = _errorGobbler.getAndReset(); log.debug("R standard output: " + newline + outputString); log.error("R standard error: " + newline + errorString); } throw e; } catch (Exception www) { log.error("Exception waiting for _process to end!"); } while (outputString.equals("")) { try { Thread.sleep(100); } catch (Exception e) { log.error("Error in TestApp while sleeping!"); } outputString = _outputGobbler.getAndReset(); errorString = _errorGobbler.getAndReset(); int loc = outputString.lastIndexOf("cat('----\\n')"); int loc1 = outputString.lastIndexOf("----"); String outputStringDisp = outputString; if (loc1 > -1) { if (loc < 0) { loc = loc1; } outputStringDisp = outputString.substring(0, loc); String rem = outputString.substring(loc1, outputString.length()); _getOutput(rem); } output.send(0, new StringToken(outputStringDisp + "\n" + errorString)); if (displayGraphicsOutputValue && (!graphicsDevice.equals(""))) { try { File fout = new File(home + graphicsOutputFile); URL furl = fout.toURL(); BrowserLauncher.openURL(furl.toString()); } catch (Exception e) { log.warn("problem launching browser:" + e); } } if (!graphicsDevice.equals("")) graphicsFileName.send(0, new StringToken(home + graphicsOutputFile)); } } public boolean postfire() throws IllegalActionException { if (_errorGobbler != null) { // If R was not in the path, then there is a chance that // errorGobbler is null. // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=3735 _errorGobbler.quit(); } if (_outputGobbler != null) { _outputGobbler.quit(); } return super.postfire(); } // remove for now since it causes problems with ENMs /* * public void preinitialize() throws IllegalActionException { * super.preinitialize(); opList = outputPortList(); iter_o = * opList.iterator(); while (iter_o.hasNext()) { TypedIOPort tiop_o = * (TypedIOPort)iter_o.next(); tiop_o.setTypeEquals(BaseType.GENERAL); } } */ // ///////////////////////////////////////////////////////////////// // // private methods //// private void _getOutput(String str) { // Newline behavior is inconsistent. Using an inline test. //String newline = System.getProperty("line.separator"); int nlp1 = -1; int nlp2 = -1; String beforeNL = "\nbefore newline"; String afterNL = "\nafter newline"; nlp1 = str.indexOf(beforeNL); nlp2 = str.indexOf(afterNL); String newline = str.substring(nlp1 + beforeNL.length(), nlp2 + 1); // nlp2 + 1 for the \n in afterNL // log.debug("output-"+str); // These are the strings we use to find the port and token information String findStr1 = newline + "portName: "; String findStr2 = newline + "vectorVal: "; String findStr3 = newline + "endVectorVal"; int pos1 = -1; int pos1n = -1; int pos2e = -1; int pos3 = -1; pos1 = str.indexOf(findStr1); while (pos1 > -1) { pos1 = pos1 + findStr1.length(); pos1n = str.indexOf(newline, pos1); String portName = str.substring(pos1, pos1n); pos2e = str.indexOf(findStr2, pos1) + findStr2.length(); pos3 = str.indexOf(findStr3, pos2e); String vectorVal = str.substring(pos2e, pos3); // log.debug("portName: "+portName+ " value: " +vectorVal); _setOutputToken(portName, vectorVal); pos1 = str.indexOf(findStr1, pos3); } } private void _setOutputToken(String portName, String tokenValue) { opList = outputPortList(); iter_o = opList.iterator(); while (iter_o.hasNext()) { TypedIOPort tiop_o = (TypedIOPort) iter_o.next(); String temp_o = tiop_o.getName(); Token token = null; if ((!temp_o.equals("output")) && (!temp_o.equals("graphicsFileName"))) { if (temp_o.equals(portName)) { try { if (tokenValue.equals("TRUE")) { BooleanToken btt = BooleanToken.getInstance(true); tiop_o.setTypeEquals(BaseType.BOOLEAN); token = btt; } if (tokenValue.equals("FALSE")) { BooleanToken btf = BooleanToken.getInstance(false); tiop_o.setTypeEquals(BaseType.BOOLEAN); token = btf; } if (tokenValue.equals("NA")) { tiop_o.setTypeEquals(BaseType.STRING); token = StringToken.NIL; // this solution just sends a string token with // value 'nil' // in R, 'NA' is considered a boolean state // i.e. 3 state logic, so this isn't really correct // but nil support for Ptolemy BooleanTokens not yet // available } if (tokenValue.startsWith("_dataframe_:")) { StringToken st = new StringToken(tokenValue); tiop_o.setTypeEquals(BaseType.STRING); token = st; } if (tokenValue.startsWith("_object_:")) { StringToken st = new StringToken(tokenValue); tiop_o.setTypeEquals(BaseType.STRING); token = st; } if (tokenValue.startsWith("_matrix_:")) { int pos1, pos2; pos1 = tokenValue.indexOf(".Dim"); pos1 = tokenValue.indexOf("c(", pos1); pos2 = tokenValue.indexOf(",", pos1); String nrowS = tokenValue.substring(pos1 + 2, pos2); String ncolS = tokenValue.substring(pos2 + 1, tokenValue.indexOf(")", pos2 + 1)); int nrows = Integer.parseInt(nrowS.trim()); int ncols = Integer.parseInt(ncolS.trim()); pos1 = "_matrix_:".length(); pos1 = tokenValue.indexOf("c(", pos1); pos2 = tokenValue.indexOf(")", pos1); String valS = tokenValue.substring(pos1 + 2, pos2); pos1 = 0; for (int j = 0; j < nrows - 1; j++) { for (int i = 0; i < ncols; i++) { pos2 = valS.indexOf(",", pos1); pos1 = pos2 + 1; } valS = valS.substring(0, pos1 - 1) + ";" + valS.substring(pos1, valS.length()); } valS = "[" + valS + "]"; MatrixToken mt = null; try { mt = new IntMatrixToken(valS); } catch (Exception ee) { try { mt = new DoubleMatrixToken(valS); } catch (Exception eee) { mt = new BooleanMatrixToken(); } } token = mt; } if (tokenValue.startsWith("\"")) { // these are strings // now remove the start and end quotes tokenValue = tokenValue.substring(1, tokenValue.length() - 1); //remove the escapes that dput() added tokenValue = StringEscapeUtils.unescapeJava(tokenValue); StringToken st = new StringToken(tokenValue); tiop_o.setTypeEquals(BaseType.STRING); token = st; } NumberFormat nf = NumberFormat.getInstance(); try { nf.parse(tokenValue); DoubleToken dt = new DoubleToken(tokenValue); tiop_o.setTypeEquals(BaseType.DOUBLE); token = dt; } catch (Exception eee) { // just continue if not a number } if (tokenValue.startsWith("c(")) { // handles R vectors // hack alert! this does not support R's c(1:10) // syntax for arrays String temp = "{" + tokenValue.substring(2, tokenValue.length()); temp = temp.replace(')', '}'); // convert NA values to 'nil' temp = temp.replaceAll("NA", "nil"); ArrayToken at = new ArrayToken(temp); tiop_o.setTypeEquals(new ArrayType(at.getElementType(), at.length())); token = at; } // check for empty arrays if (tokenValue.equals("character(0)")) { token = new ArrayToken(BaseType.STRING); } if (tokenValue.equals("numeric(0)")) { token = new ArrayToken(BaseType.DOUBLE); } if (tokenValue.equals("logical(0)")) { token = new ArrayToken(BaseType.BOOLEAN); } // verify that we have a token if (token == null) { log.warn("No token could be created on portName: " + portName + ", for tokenValue: " + tokenValue); return; } // send whatever token we happened to generate - all // channels // (note: sinkPortList size does not necessarily == port // width) int numSinkPorts = tiop_o.sinkPortList().size(); int portWidth = tiop_o.getWidth(); // check the types of the sink ports for compatibility for (int channelIndex = 0; channelIndex < numSinkPorts; channelIndex++) { Type sinkType = ((TypedIOPort) tiop_o.sinkPortList().get(channelIndex)).getType(); // if (!sinkType.isCompatible(token.getType())) { // change to equals for bug #3451: if (!sinkType.equals(token.getType())) { log.debug("[re]Setting sink type to: " + token.getType().toString()); // set the Type for the sinks // POSSIBLE BUG - not sure why the automatic // type resolution was failing for downstream // port ((TypedIOPort) tiop_o.sinkPortList().get(channelIndex)) .setTypeEquals(token.getType()); } } // send the token to the channel[s] of the port for (int channelIndex = 0; channelIndex < portWidth; channelIndex++) { tiop_o.send(channelIndex, token); } } catch (Exception w) { log.error("Problem sending to output port! " + w); w.printStackTrace(); } return; } } } } // given a recordToken and a portName, create the R script to make a // dataframe with the // portName as its R name. Should check that all the items in the record are // the same length private String _recordToDataFrame(RecordToken recordToken, String portName) { boolean isDataframe = true; String ret = ""; String temp = ""; String tempA = ""; String labellist = ""; int arrayLength = -1; Set labels = recordToken.labelSet(); Iterator iter_l = labels.iterator(); ret = "`" + portName + "` <- local({\n"; while (iter_l.hasNext()) { String label = (String) iter_l.next(); Token labelvaltoken = (recordToken).get(label); String token_type_string = labelvaltoken.getType().toString(); if ((token_type_string.equals("{double}")) || (token_type_string.equals("{int}")) || (token_type_string.equals("{string}")) || (token_type_string.startsWith("arrayType")) || (token_type_string.equals("double")) || (token_type_string.equals("int")) || (token_type_string.equals("string"))) { labellist = labellist + "`" + label + "`,"; if (token_type_string.equals("double") || token_type_string.equals("int") || token_type_string.equals("string")) { if (arrayLength == -1) { arrayLength = 1; } else { if (arrayLength != 1) { log.warn("record elements are not all the same length!"); isDataframe = false; //return ""; } } } else { if (arrayLength == -1) { arrayLength = ((ArrayToken) labelvaltoken).length(); } else { int a_len = ((ArrayToken) labelvaltoken).length(); if (a_len != arrayLength) { log.warn("record elements are not all the same length!"); isDataframe = false; //return ""; } } } temp = labelvaltoken.toString(); if (token_type_string.equals("double") || token_type_string.equals("int") || token_type_string.equals("string")) { temp = "(" + temp + ")"; } temp = temp.replace('{', '('); temp = temp.replace('}', ')'); // using double quotes for strings so that single quotes work // within them // temp = temp.replace('"', '\''); // assume that the token's string value might be 'nil' for a // missing value temp = temp.replaceAll("nil", "NA"); // if string is long, create a temp file for passing array data String temp1 = temp; // need to estimate the total number of characters that this // record might have int estimatedTotalLength = temp1.length() * labels.size(); log.debug("column length: " + temp1.length() + " * number of columns: " + labels.size() + " = estimated total record length: " + estimatedTotalLength + ", maxCommandLineLength: " + maxCommandLineLength); if (estimatedTotalLength > maxCommandLineLength) { temp1 = temp1.replace('(', ' '); temp1 = temp1.replace(')', ' '); String filename = _writeDataFile(temp1); if (token_type_string.indexOf("string") > -1) { tempA = "`" + label + "` <- scan('" + filename + "', sep=',', strip.white=TRUE, what='character' )"; } else { tempA = "`" + label + "` <- scan('" + filename + "', sep=',')"; } tempA = tempA + "\n" + "file.remove('" + filename + "')"; ret = ret + tempA + "\n"; } else { // otherwise use the modified string tempA = "`" + label + "` <- c" + temp; ret = ret + tempA + "\n"; } } } labellist = labellist.substring(0, labellist.length() - 1); // remove // last ',' if (isDataframe) { ret = ret + "data.frame(" + labellist + ", check.names = FALSE)\n"; } else { ret = ret + "list(" + labellist + ")\n"; } ret += "})\n"; // log.debug("ret: "+ret); return ret; } // there is ta problem when length of lines sent to R are too long // thus, break the line into pieces with newlines; // assume pieces of are approx. 512 chars but must be separate at ',' // (R will accept multiple lines but the seperation cannot be arbitrart; // i.e. not in // middle of floating point number) private String _breakIntoLines(String temp) { int size = 512; int pieces = (int) temp.length() / size; int start = size; int indx = 0; for (int k = 0; k < pieces - 1; k++) { indx = temp.indexOf(",", start); temp = temp.substring(0, indx) + "\n" + temp.substring(indx, temp.length()); start = start + size; } return temp; } // Execute a command, set _process to point to the subprocess // and set up _errorGobbler and _outputGobbler to read data. private void _exec() throws IllegalActionException { Runtime runtime = Runtime.getRuntime(); String[] commandArray; String osName = System.getProperty("os.name"); if (osName.equals("Windows NT") || osName.equals("Windows XP") || osName.equals("Windows 2000")) { // checkRLocation is commented out for now since it slows down the // first execution of a // workflow with an RExpression actor too much (>= 30 sec for a // 'cold' machine) _checkRLocation(); commandArray = new String[6]; commandArray[0] = "cmd.exe"; commandArray[1] = "/C"; commandArray[2] = RdotExe; commandArray[3] = "--silent"; commandArray[4] = restoreString; commandArray[5] = saveString; } else if (osName.equals("Windows 95")) { _checkRLocation(); commandArray = new String[6]; commandArray[0] = "command.com"; commandArray[1] = "/C"; commandArray[2] = RdotExe; commandArray[3] = "--silent"; commandArray[4] = restoreString; commandArray[5] = saveString; } else { commandArray = new String[4]; commandArray[0] = RdotExe; commandArray[1] = "--silent"; commandArray[2] = restoreString; commandArray[3] = saveString; } // log.debug("commandArray :"+commandArray); try { // log.debug("ready to create _process!"); _process = runtime.exec(commandArray); log.debug("Process :" + _process); } catch (Exception e) { log.error("Problem with creating process in RExpression!"); } // log.debug("Ready to create threads"); // Create two threads to read from the subprocess. _outputGobbler = new _StreamReaderThread(_process.getInputStream(), "Exec Stdout Gobbler-" + _streamReaderThreadCount++, this); _errorGobbler = new _StreamReaderThread(_process.getErrorStream(), "Exec Stderr Gobbler-" + _streamReaderThreadCount++, this); _errorGobbler.start(); _outputGobbler.start(); if (_streamReaderThreadCount > 1000) { // Avoid overflow in the thread count. _streamReaderThreadCount = 0; } OutputStreamWriter inputStreamWriter = new OutputStreamWriter(_process.getOutputStream()); _inputBufferedWriter = new BufferedWriter(inputStreamWriter); } private void _checkRLocation() { if (RdotExe.equals("R")) { List<File> l = new ArrayList<File>(); // check '$KEPLER/R' String keplerDir = StringUtilities.getProperty("KEPLER"); _findFile(new File(keplerDir + "/R"), "R.exe", l); if (!l.isEmpty()) { RdotExe = l.get(0) + ""; log.debug(RdotExe); } } } private String _getUniqueFileName(String extender) { int cnt = 1; // String usr_name = System.getProperty("user.name"); String actor_name = this.getName(); actor_name = actor_name.replaceAll("[^a-zA-Z0-9.]", "_"); String fn = actor_name + "-" + cnt + "." + extender; String path = home; while (new File(path, fn).exists()) { cnt++; fn = actor_name + "-" + cnt + "." + extender; } return fn; } // // overloaded version for use with new form of .sav files that have portname // prefix // private String _getUniqueFileName(String portname, String extender) { int cnt = 1; // String usr_name = System.getProperty("user.name"); String actor_name = this.getName(); // These replaceAll statements will make this operation OS-independent, // but will increase the likelihood of a .sav file collision since this // is evaluated before any files are created. In other words, // "input 1" and "input_1" will collide with the same .sav file as both // will find input_1-RExpression-1.sav uncreated. There is now R code to // handle this. actor_name = actor_name.replaceAll("[^a-zA-Z0-9.]", "_"); portname = portname.replaceAll("[^a-zA-Z0-9.]", "_"); String fn = portname + "-" + actor_name + "-" + cnt + "." + extender; String path = home; while (new File(path, fn).exists()) { cnt++; fn = portname + "-" + actor_name + "-" + cnt + "." + extender; } //make the filename play nice String retPath = new File(path, fn).getAbsolutePath(); retPath = retPath.replace('\\', '/'); return retPath; } private String _createMetadataCommand(String objectName, String attributeName, String attributeValue) { String retVal = "attr(`" + objectName + "`, " + "\"" + attributeName + "\"" + ") <- " + "\"" + attributeValue + "\""; return retVal; } private String _writeDataFile(String dat) { String fn = ""; try { String home = System.getProperty("user.home"); home = home.replace('\\', '/'); fn = home + "/" + _getUniqueFileName("dat") + cntr; File dataFile = new File(fn); StringReader is = new StringReader(dat); FileWriter os = new FileWriter(dataFile); int c; while ((c = is.read()) != -1) { os.write(c); } is.close(); os.close(); } catch (Exception exc) { log.error("error writing data file! - RExpression"); } cntr++; return fn; } private void _findFile(File f, String name, List<File> r) { if (f.isDirectory()) { File[] files = f.listFiles(); if (files == null) return; for (int i = 0; i < files.length; i++) { // log.debug(files[i]+""); _findFile(files[i], name, r); } } else { String fn = f + ""; // log.debug("fn: "+fn); if (fn.indexOf(name) > -1) { r.add(f); } } } // ///////////////////////////////////////////////////////////////// // // inner classes //// // Private class that reads a stream in a thread and updates the // stringBuffer. private class _StreamReaderThread extends Thread { /** * Create a _StreamReaderThread. * * @param inputStream * The stream to read from. * @param name * The name of this StreamReaderThread, which is useful for * debugging. * @param actor * The parent actor of this thread, which is used in error * messages. */ _StreamReaderThread(InputStream inputStream, String name, Nameable actor) { super(name); _inputStream = inputStream; _inputStreamReader = new InputStreamReader(_inputStream); _stringBuffer = new StringBuffer(); _keepRunning = true; chars = new char[100001]; } /** * Read any remaining data in the input stream and return the data read * thus far. Calling this method resets the cache of data read thus far. */ public String getAndReset() { if (_debugging) { try { _debug("getAndReset: Gobbler '" + getName()); // + "' Ready: " + _inputStreamReader.ready() // + " Available: " + _inputStream.available()); // the previous lines (now commented out) cause a thread // problem because (?) // the inputStreamReader is used by the threads monitoring // process io. } catch (Exception ex) { throw new InternalErrorException(ex); } } // do a final _read before clearing buffer in case some characters // are available; this was added to collect information that was // sometimes missing on a newer, faster computer ! -- DFH 11/2005 _read(); // DFH - last chance to read String results = _stringBuffer.toString(); _stringBuffer = new StringBuffer(); return results; } /** * Read lines from the inputStream and append them to the stringBuffer. */ public void run() { while (_keepRunning) { // log.debug("Starting read"); _read(); try { Thread.sleep(100); } catch (Exception e) { log.error("Error in StreamReaderThread while sleeping!"); } // log.debug("Finishing read"); } } public void quit() { _keepRunning = false; } // Read from the stream until we get to the end of the stream private void _read() { // We read the data as a char[] instead of using readline() // so that we can get strings that do not end in end of // line chars. // char [] chars = new char[20001]; int length; // Number of characters read. try { // Oddly, InputStreamReader.read() will return -1 // if there is no data present, but the string can still // read. length = _inputStreamReader.read(chars, 0, 20000); if (_debugging) { // Note that ready might be false here since // we already read the data. _debug("_read(): Gobbler '" + getName() + "' Ready: " + _inputStreamReader.ready() + " Value: '" + String.valueOf(chars, 0, length) + "'"); } if (length > 0) { String temp = String.valueOf(chars, 0, length); // _stringBuffer.append(chars, 0, length); _stringBuffer.append(temp); } } catch (Throwable throwable) { log.warn("In catch block of _read: " + throwable.getMessage()); _keepRunning = false; } } // character array private char[] chars; // StringBuffer to update. private StringBuffer _stringBuffer; // Stream from which to read. private InputStream _inputStream; // Stream from which to read. private InputStreamReader _inputStreamReader; // this thread private boolean _keepRunning; } // ///////////////////////////////////////////////////////////////// // // private variables //// // The subprocess gets its input from this BufferedWriter. private BufferedWriter _inputBufferedWriter; // StreamReader with which we read stderr. private _StreamReaderThread _errorGobbler; // StreamReader with which we read stdout. private _StreamReaderThread _outputGobbler; // The Process that we are running. private Process _process; protected String graphicsOutputFile = ""; // Instance count of output and error threads, used for debugging. // When the value is greater than 1000, we reset it to 0. private static int _streamReaderThreadCount = 0; private List opList; private Iterator iter_o; protected String home; }