001/* An actor that writes the value of string tokens to a file, one per line. 002 003 @Copyright (c) 2002-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.io; 029 030import java.io.File; 031import java.io.PrintWriter; 032 033import ptolemy.actor.lib.Sink; 034import ptolemy.actor.parameters.FilePortParameter; 035import ptolemy.data.BooleanToken; 036import ptolemy.data.StringToken; 037import ptolemy.data.Token; 038import ptolemy.data.expr.FileParameter; 039import ptolemy.data.expr.Parameter; 040import ptolemy.data.expr.SingletonParameter; 041import ptolemy.data.type.BaseType; 042import ptolemy.kernel.CompositeEntity; 043import ptolemy.kernel.util.Attribute; 044import ptolemy.kernel.util.IllegalActionException; 045import ptolemy.kernel.util.NameDuplicationException; 046import ptolemy.kernel.util.Workspace; 047import ptolemy.util.MessageHandler; 048 049/////////////////////////////////////////////////////////////////// 050//// LineWriter 051 052/** 053 <p>This actor reads string-valued input tokens and writes them, 054 one line at a time, to a specified file. It does not 055 include any enclosing quotation marks in the output. 056 If you need the enclosing quotation marks, precede this 057 actor with TokenToExpression.</p> 058 <p> 059 The file is specified by the <i>fileName</i> attribute 060 using any form acceptable to {@link FileParameter}.</p> 061 <p> 062 If the <i>append</i> attribute has value <i>true</i>, 063 then the file will be appended to. If it has value <i>false</i>, 064 then if the file exists, the user will be queried for permission 065 to overwrite, and if granted, the file will be overwritten.</p> 066 <p> 067 If the <i>confirmOverwrite</i> parameter has value <i>false</i>, 068 then this actor will overwrite the specified file if it exists 069 without asking. If <i>true</i> (the default), then if the file 070 exists, then this actor will ask for confirmation before overwriting.</p> 071 072 @see FileParameter 073 @see ExpressionWriter 074 @author Edward A. Lee 075 @version $Id$ 076 @since Ptolemy II 2.2 077 @Pt.ProposedRating Yellow (eal) 078 @Pt.AcceptedRating Red (liuj) 079 */ 080public class LineWriter extends Sink { 081 /** Construct an actor with the given container and name. 082 * @param container The container. 083 * @param name The name of this actor. 084 * @exception IllegalActionException If the actor cannot be contained 085 * by the proposed container. 086 * @exception NameDuplicationException If the container already has an 087 * actor with this name. 088 */ 089 public LineWriter(CompositeEntity container, String name) 090 throws IllegalActionException, NameDuplicationException { 091 super(container, name); 092 093 input.setTypeEquals(BaseType.STRING); 094 new SingletonParameter(input, "_showName").setToken(BooleanToken.TRUE); 095 096 fileName = new FilePortParameter(this, "fileName"); 097 fileName.setExpression("System.out"); 098 new SingletonParameter(fileName.getPort(), "_showName") 099 .setToken(BooleanToken.TRUE); 100 101 append = new Parameter(this, "append"); 102 append.setTypeEquals(BaseType.BOOLEAN); 103 append.setToken(BooleanToken.FALSE); 104 105 confirmOverwrite = new Parameter(this, "confirmOverwrite"); 106 confirmOverwrite.setTypeEquals(BaseType.BOOLEAN); 107 confirmOverwrite.setToken(BooleanToken.TRUE); 108 109 endOfLineCharacter = new Parameter(this, "endOfLineCharacter"); 110 endOfLineCharacter.setTypeEquals(BaseType.STRING); 111 // Default value is null. 112 113 alwaysFlush = new Parameter(this, "alwaysFlush"); 114 alwaysFlush.setTypeEquals(BaseType.BOOLEAN); 115 alwaysFlush.setToken(BooleanToken.FALSE); 116 117 _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-25\" y=\"-20\" " 118 + "width=\"50\" height=\"40\" " + "style=\"fill:white\"/>\n" 119 + "<polygon points=\"-15,-10 -12,-10 -8,-14 -1,-14 3,-10" 120 + " 15,-10 15,10, -15,10\" " + "style=\"fill:red\"/>\n" 121 + "</svg>\n"); 122 } 123 124 /////////////////////////////////////////////////////////////////// 125 //// ports and parameters //// 126 127 /** If <i>true</i>, then append to the specified file. If <i>false</i> 128 * (the default), then overwrite any preexisting file after asking 129 * the user for permission. 130 */ 131 public Parameter append; 132 133 /** The file name to which to write. This is a string with 134 * any form accepted by FilePortParameter. The default value is 135 * "System.out". 136 * @see FilePortParameter 137 */ 138 public FilePortParameter fileName; 139 140 /** If <i>false</i>, then overwrite the specified file if it exists 141 * without asking. If <i>true</i> (the default), then if the file 142 * exists, ask for confirmation before overwriting. 143 */ 144 public Parameter confirmOverwrite; 145 146 /** End of line character to use. This is a string 147 * that defaults to null, which results in the current 148 * platform's standard end-of-line character being used. 149 * If an empty string is specified, 150 * then no end of line character is used after each 151 * output written to the file. 152 */ 153 public Parameter endOfLineCharacter; 154 155 /** If <i>true</i>, flush output after each line is written. If 156 * <i>false</i> (the default), the output may not be written until the 157 * stream is closed during wrapup(). 158 */ 159 public Parameter alwaysFlush; 160 161 /////////////////////////////////////////////////////////////////// 162 //// public methods //// 163 164 /** If the specified attribute is <i>fileName</i> and there is an 165 * open file being written, then close that file. The new file will 166 * be opened or created when it is next written to. 167 * @param attribute The attribute that has changed. 168 * @exception IllegalActionException If the specified attribute 169 * is <i>fileName</i> and the previously 170 * opened file cannot be closed. 171 */ 172 @Override 173 public void attributeChanged(Attribute attribute) 174 throws IllegalActionException { 175 if (attribute == fileName) { 176 // Do not close the file if it is the same file. 177 String newFileName = ((StringToken) fileName.getToken()) 178 .stringValue(); 179 180 if (_previousFileName != null 181 && !newFileName.equals(_previousFileName)) { 182 _previousFileName = newFileName; 183 fileName.close(); 184 _writer = null; 185 } 186 } else { 187 super.attributeChanged(attribute); 188 } 189 } 190 191 /** Clone the actor into the specified workspace. 192 * @param workspace The workspace for the new object. 193 * @return A new actor. 194 * @exception CloneNotSupportedException If a derived class contains 195 * an attribute that cannot be cloned. 196 */ 197 @Override 198 public Object clone(Workspace workspace) throws CloneNotSupportedException { 199 LineWriter newObject = (LineWriter) super.clone(workspace); 200 newObject._writer = null; 201 return newObject; 202 } 203 204 /** Read an input string token from each input 205 * channel and write it to the file, one line per token. 206 * If there is no input, do nothing. 207 * If the file is not open for writing then open it. If the file 208 * does not exist, then create it. If the file already exists, 209 * then query the user for overwrite, unless the <i>append</i> 210 * parameter has value <i>true</i>. 211 * @exception IllegalActionException If the file cannot be opened 212 * or created, or if the user refuses to overwrite an existing file. 213 */ 214 @Override 215 public boolean postfire() throws IllegalActionException { 216 fileName.update(); 217 for (int i = 0; i < input.getWidth(); i++) { 218 if (input.hasToken(i)) { 219 Token token = input.get(i); 220 221 if (_writer == null) { 222 // File has not been opened. 223 boolean appendValue = ((BooleanToken) append.getToken()) 224 .booleanValue(); 225 226 String fileNameValue = fileName.stringValue(); 227 228 // If previousFileName is null, we have never opened a file. 229 if (_previousFileName == null) { 230 _previousFileName = fileNameValue; 231 } 232 if (!fileNameValue.equals("System.out")) { 233 // Only check for append and overwrite if the 234 // fileName is not "System.out" 235 // Open the file. 236 File file = fileName.asFile(); 237 boolean confirmOverwriteValue = ((BooleanToken) confirmOverwrite 238 .getToken()).booleanValue(); 239 240 // Don't ask for confirmation in append mode, since there 241 // will be no loss of data. 242 if (file != null && file.exists() && !appendValue 243 && confirmOverwriteValue) { 244 // Query for overwrite. 245 // FIXME: This should be called in the event thread! 246 // There is a chance of deadlock since it is not. 247 if (!MessageHandler.yesNoQuestion( 248 "OK to overwrite " + file + "?")) { 249 throw new IllegalActionException(this, 250 "Please select another file name."); 251 } 252 } 253 } 254 255 _writer = new PrintWriter( 256 fileName.openForWriting(appendValue), true); 257 } 258 _writeToken(token); 259 } 260 } 261 return super.postfire(); 262 } 263 264 /** Read the value of alwaysFlush parameter. 265 * @exception IllegalActionException If there is an error reading the 266 * alwaysFlush parameter. 267 */ 268 @Override 269 public void preinitialize() throws IllegalActionException { 270 super.preinitialize(); 271 _flushValue = ((BooleanToken) alwaysFlush.getToken()).booleanValue(); 272 } 273 274 /** Close the writer if there is one. 275 * @exception IllegalActionException If an IO error occurs. 276 */ 277 @Override 278 public void wrapup() throws IllegalActionException { 279 fileName.close(); 280 _writer = null; 281 } 282 283 /////////////////////////////////////////////////////////////////// 284 //// protected methods //// 285 286 /** Write the specified token to the current writer. 287 * This is protected so that derived classes can modify the 288 * format in which the token is written. 289 * @param token The token to write. 290 * @exception IllegalActionException Not thrown in this base class. 291 */ 292 protected void _writeToken(Token token) throws IllegalActionException { 293 String eol = "\n"; 294 Token eolToken = endOfLineCharacter.getToken(); 295 if (eolToken != null) { 296 eol = ((StringToken) eolToken).stringValue(); 297 } 298 // In this base class, the cast is safe. 299 _writer.print(((StringToken) token).stringValue() + eol); 300 301 if (_flushValue) { 302 _writer.flush(); 303 } 304 305 } 306 307 /////////////////////////////////////////////////////////////////// 308 //// protected members //// 309 310 /** If true, flush the writer after every write. */ 311 protected boolean _flushValue; 312 313 /** The current writer. */ 314 protected PrintWriter _writer; 315 316 /////////////////////////////////////////////////////////////////// 317 //// private members //// 318 319 /** Previous value of fileName parameter. */ 320 private String _previousFileName; 321}