/* An actor that writes input data to the specified file. @Copyright (c) 1998-2014 The Regents of the University of California. All rights reserved. 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. PT_COPYRIGHT_VERSION 2 COPYRIGHTENDKEY */ package ptolemy.actor.lib; import java.io.IOException; import java.io.OutputStreamWriter; import ptolemy.actor.parameters.FilePortParameter; import ptolemy.data.BooleanToken; import ptolemy.data.StringToken; import ptolemy.data.Token; import ptolemy.data.expr.SingletonParameter; import ptolemy.data.type.BaseType; import ptolemy.kernel.CompositeEntity; import ptolemy.kernel.util.IllegalActionException; import ptolemy.kernel.util.NameDuplicationException; import ptolemy.kernel.util.Workspace; /** This actor reads tokens from any number of input channels and writes their string values to the specified output file. The input type can be anything. If a StringToken is received, then its stringValue() method will be used to get the string to write to the file. Otherwise, the toString() method of the received token will be used. If no file name is given, then the values are written to the standard output. If multiple input channels are provided on the input port, then the values received are written separated by a tab character. Each time a new name is received on the filename input, a new file will be opened for writing. If no new filename is received, then the data will be appended to previously used file. When appending, the values received on subsequent firings are separated by a newline character (a newline character will be inserted if one is not already provide by the input string). Unlike @see{ExpressionWriter}, this actor makes no changes to the input string. It writes to the file exactly what it receives on its input. @author Yuhong Xiong, Edward A. Lee @version $Id: FileWriter.java 70402 2014-10-23 00:52:20Z cxh $ @since Ptolemy II 0.4 @Pt.ProposedRating Yellow (yuhong) @Pt.AcceptedRating Yellow (mudit) */ public class FileWriter extends Sink { /** 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 FileWriter(CompositeEntity container, String name) throws IllegalActionException, NameDuplicationException { super(container, name); if (_stdOut == null) { _stdOut = new OutputStreamWriter(System.out); } _setWriter(_stdOut); filename = new FilePortParameter(this, "filename"); filename.setExpression(""); filename.setTypeEquals(BaseType.STRING); new SingletonParameter(filename.getPort(), "_showName") .setToken(BooleanToken.TRUE); new SingletonParameter(input, "_showName").setToken(BooleanToken.TRUE); _attachText("_iconDescription", "\n" + "\n" + "\n" + "\n"); } /////////////////////////////////////////////////////////////////// //// ports and parameters //// /** The name of the file to write to. * By default, this parameter contains an empty string, which * is interpreted to mean that output should be directed to the * standard output. * See {@link ptolemy.actor.parameters.FilePortParameter} for * details about relative path names. */ public FilePortParameter filename; /////////////////////////////////////////////////////////////////// //// public methods //// /** Clone the actor. * @exception CloneNotSupportedException If the superclass throws it. */ @Override public Object clone(Workspace workspace) throws CloneNotSupportedException { FileWriter newObject = (FileWriter) super.clone(workspace); newObject._previousFilename = null; newObject._writer = null; return newObject; } /** Read at most one token from each input channel and write its * string value. If the filename input has changed since the * last writing, then open the new file for writing. Otherwise, * append to the previous file. If there are multiple channels * connected to the input, then the output values from each * channel are separated by tab characters. * If an input channel has no data, then two consecutive tab * characters are written. * @exception IllegalActionException If an IO error occurs. */ @Override public boolean postfire() throws IllegalActionException { filename.update(); try { // NOTE: getExpression() will not get the current value // of this sort of PortParameter. Instead, it gets the // default value. Have to use getToken(). String filenameValue = ((StringToken) filename.getToken()) .stringValue(); if (filenameValue == null || filenameValue.equals("\"\"")) { // See $PTII/ptolemy/domains/sdf/kernel/test/auto/zeroRate_delay5.xml, which sets // the filename to a string that has two doublequotes. "" _setWriter(null); } else if (!filenameValue.equals(_previousFilename)) { // New filename. Close the previous. _previousFilename = filenameValue; _setWriter(null); if (!filenameValue.trim().equals("")) { java.io.Writer writer = filename.openForWriting(); // Findbugs warns about the writer being created but // not closed. But it is closed in postfire(). _setWriter(writer); } } String last = ""; int width = input.getWidth(); for (int i = 0; i < width; i++) { if (i > 0) { _writer.write("\t"); } if (input.hasToken(i)) { Token inputToken = input.get(i); if (inputToken instanceof StringToken) { last = ((StringToken) inputToken).stringValue(); } else { last = inputToken.toString(); } _writer.write(last); } else { last = ""; } } // Write a newline character only if the last // string does not already have one. if (!last.endsWith("\n")) { _writer.write("\n"); } _writer.flush(); return super.postfire(); } catch (IOException ex) { throw new IllegalActionException(this, ex, "postfire() failed"); } } /** Close the file, if there is one. * @exception IllegalActionException If an IO error occurs. */ @Override public void wrapup() throws IllegalActionException { super.wrapup(); try { if (_writer != null) { _writer.flush(); } } catch (IOException ex) { throw new IllegalActionException(this, ex, "wrapup(" + _writer + ") failed"); } // To get the file to close. _setWriter(null); // Since we have closed the writer, we also need to clear // _previousFilename, so that a new writer will be opened for this // filename if the model is executed again _previousFilename = null; } /////////////////////////////////////////////////////////////////// //// private methods //// /** Set the writer. If there was a previous writer, close it. * To set standard output, call this method with argument null. * @param writer The writer to write to. * @exception IllegalActionException If an IO error occurs. */ private void _setWriter(java.io.Writer writer) throws IllegalActionException { try { if (_writer != null && _writer != _stdOut) { _writer.close(); } } catch (IOException ex) { throw new IllegalActionException(this, ex, "setWriter(" + writer + ") failed"); } if (writer != null) { _writer = writer; } else { _writer = _stdOut; } } /////////////////////////////////////////////////////////////////// //// private fields //// /** The previously used filename, or null if none has been previously used. */ private String _previousFilename = null; /** Standard out as a writer. */ private static java.io.Writer _stdOut = null; /** The writer to write to. */ private java.io.Writer _writer = null; }