001/* An actor that writes input data to the specified file. 002 003 @Copyright (c) 1998-2014 The Regents of the University of California. 004 All rights reserved. 005 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the 009 above copyright notice and the following two paragraphs appear in all 010 copies of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 ENHANCEMENTS, OR MODIFICATIONS. 024 025 PT_COPYRIGHT_VERSION 2 026 COPYRIGHTENDKEY 027 */ 028package ptolemy.actor.lib; 029 030import java.io.IOException; 031import java.io.OutputStreamWriter; 032 033import ptolemy.actor.parameters.FilePortParameter; 034import ptolemy.data.BooleanToken; 035import ptolemy.data.StringToken; 036import ptolemy.data.Token; 037import ptolemy.data.expr.SingletonParameter; 038import ptolemy.data.type.BaseType; 039import ptolemy.kernel.CompositeEntity; 040import ptolemy.kernel.util.IllegalActionException; 041import ptolemy.kernel.util.NameDuplicationException; 042import ptolemy.kernel.util.Workspace; 043 044/** 045 This actor reads tokens from any number of input channels and writes 046 their string values to the specified output file. The input type 047 can be anything. If a StringToken is received, then its stringValue() 048 method will be used to get the string to write to the file. Otherwise, 049 the toString() method of the received token will be used. If no file name 050 is given, then the values are written to the standard output. 051 If multiple input channels are provided on the input port, then 052 the values received are written separated by a tab character. 053 Each time a new name is received on the <i>filename</i> input, a 054 new file will be opened for writing. If no new filename is received, 055 then the data will be appended to previously used file. When appending, 056 the values received on subsequent firings are separated by a newline 057 character (a newline character will be inserted if one is not already 058 provide by the input string). 059 Unlike @see{ExpressionWriter}, this actor makes no changes to the 060 input string. It writes to the file exactly what it receives on its 061 input. 062 063 @author Yuhong Xiong, Edward A. Lee 064 @version $Id$ 065 @since Ptolemy II 0.4 066 @Pt.ProposedRating Yellow (yuhong) 067 @Pt.AcceptedRating Yellow (mudit) 068 */ 069public class FileWriter extends Sink { 070 /** Construct an actor with the given container and name. 071 * @param container The container. 072 * @param name The name of this actor. 073 * @exception IllegalActionException If the actor cannot be contained 074 * by the proposed container. 075 * @exception NameDuplicationException If the container already has an 076 * actor with this name. 077 */ 078 public FileWriter(CompositeEntity container, String name) 079 throws IllegalActionException, NameDuplicationException { 080 super(container, name); 081 082 if (_stdOut == null) { 083 _stdOut = new OutputStreamWriter(System.out); 084 } 085 086 _setWriter(_stdOut); 087 088 filename = new FilePortParameter(this, "filename"); 089 filename.setExpression(""); 090 filename.setTypeEquals(BaseType.STRING); 091 new SingletonParameter(filename.getPort(), "_showName") 092 .setToken(BooleanToken.TRUE); 093 094 new SingletonParameter(input, "_showName").setToken(BooleanToken.TRUE); 095 096 _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-25\" y=\"-20\" " 097 + "width=\"50\" height=\"40\" " + "style=\"fill:white\"/>\n" 098 + "<polygon points=\"-15,-10 -12,-10 -8,-14 -1,-14 3,-10" 099 + " 15,-10 15,10, -15,10\" " + "style=\"fill:red\"/>\n" 100 + "</svg>\n"); 101 } 102 103 /////////////////////////////////////////////////////////////////// 104 //// ports and parameters //// 105 106 /** The name of the file to write to. 107 * By default, this parameter contains an empty string, which 108 * is interpreted to mean that output should be directed to the 109 * standard output. 110 * See {@link ptolemy.actor.parameters.FilePortParameter} for 111 * details about relative path names. 112 */ 113 public FilePortParameter filename; 114 115 /////////////////////////////////////////////////////////////////// 116 //// public methods //// 117 118 /** Clone the actor. 119 * @exception CloneNotSupportedException If the superclass throws it. 120 */ 121 @Override 122 public Object clone(Workspace workspace) throws CloneNotSupportedException { 123 FileWriter newObject = (FileWriter) super.clone(workspace); 124 newObject._previousFilename = null; 125 newObject._writer = null; 126 return newObject; 127 } 128 129 /** Read at most one token from each input channel and write its 130 * string value. If the filename input has changed since the 131 * last writing, then open the new file for writing. Otherwise, 132 * append to the previous file. If there are multiple channels 133 * connected to the input, then the output values from each 134 * channel are separated by tab characters. 135 * If an input channel has no data, then two consecutive tab 136 * characters are written. 137 * @exception IllegalActionException If an IO error occurs. 138 */ 139 @Override 140 public boolean postfire() throws IllegalActionException { 141 filename.update(); 142 try { 143 // NOTE: getExpression() will not get the current value 144 // of this sort of PortParameter. Instead, it gets the 145 // default value. Have to use getToken(). 146 String filenameValue = ((StringToken) filename.getToken()) 147 .stringValue(); 148 149 if (filenameValue == null || filenameValue.equals("\"\"")) { 150 // See $PTII/ptolemy/domains/sdf/kernel/test/auto/zeroRate_delay5.xml, which sets 151 // the filename to a string that has two doublequotes. "" 152 _setWriter(null); 153 } else if (!filenameValue.equals(_previousFilename)) { 154 // New filename. Close the previous. 155 _previousFilename = filenameValue; 156 _setWriter(null); 157 if (!filenameValue.trim().equals("")) { 158 java.io.Writer writer = filename.openForWriting(); 159 // Findbugs warns about the writer being created but 160 // not closed. But it is closed in postfire(). 161 _setWriter(writer); 162 } 163 } 164 String last = ""; 165 int width = input.getWidth(); 166 167 for (int i = 0; i < width; i++) { 168 if (i > 0) { 169 _writer.write("\t"); 170 } 171 172 if (input.hasToken(i)) { 173 Token inputToken = input.get(i); 174 if (inputToken instanceof StringToken) { 175 last = ((StringToken) inputToken).stringValue(); 176 } else { 177 last = inputToken.toString(); 178 } 179 _writer.write(last); 180 } else { 181 last = ""; 182 } 183 } 184 // Write a newline character only if the last 185 // string does not already have one. 186 if (!last.endsWith("\n")) { 187 _writer.write("\n"); 188 } 189 _writer.flush(); 190 return super.postfire(); 191 } catch (IOException ex) { 192 throw new IllegalActionException(this, ex, "postfire() failed"); 193 } 194 } 195 196 /** Close the file, if there is one. 197 * @exception IllegalActionException If an IO error occurs. 198 */ 199 @Override 200 public void wrapup() throws IllegalActionException { 201 super.wrapup(); 202 203 try { 204 if (_writer != null) { 205 _writer.flush(); 206 } 207 } catch (IOException ex) { 208 throw new IllegalActionException(this, ex, 209 "wrapup(" + _writer + ") failed"); 210 } 211 212 // To get the file to close. 213 _setWriter(null); 214 215 // Since we have closed the writer, we also need to clear 216 // _previousFilename, so that a new writer will be opened for this 217 // filename if the model is executed again 218 _previousFilename = null; 219 } 220 221 /////////////////////////////////////////////////////////////////// 222 //// private methods //// 223 224 /** Set the writer. If there was a previous writer, close it. 225 * To set standard output, call this method with argument null. 226 * @param writer The writer to write to. 227 * @exception IllegalActionException If an IO error occurs. 228 */ 229 private void _setWriter(java.io.Writer writer) 230 throws IllegalActionException { 231 try { 232 if (_writer != null && _writer != _stdOut) { 233 _writer.close(); 234 } 235 } catch (IOException ex) { 236 throw new IllegalActionException(this, ex, 237 "setWriter(" + writer + ") failed"); 238 } 239 240 if (writer != null) { 241 _writer = writer; 242 } else { 243 _writer = _stdOut; 244 } 245 } 246 247 /////////////////////////////////////////////////////////////////// 248 //// private fields //// 249 250 /** The previously used filename, or null if none has been previously used. */ 251 private String _previousFilename = null; 252 253 /** Standard out as a writer. */ 254 private static java.io.Writer _stdOut = null; 255 256 /** The writer to write to. */ 257 private java.io.Writer _writer = null; 258}