001/*
002 * Copyright (c) 2002-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: welker $'
006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 
007 * '$Revision: 24234 $'
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.geon;
031
032import java.io.File;
033import java.io.FileOutputStream;
034
035import ptolemy.actor.TypedIOPort;
036import ptolemy.actor.lib.Sink;
037import ptolemy.data.ArrayToken;
038import ptolemy.data.BooleanToken;
039import ptolemy.data.StringToken;
040import ptolemy.data.Token;
041import ptolemy.data.UnsignedByteToken;
042import ptolemy.data.expr.FileParameter;
043import ptolemy.data.expr.Parameter;
044import ptolemy.data.type.ArrayType;
045import ptolemy.data.type.BaseType;
046import ptolemy.kernel.CompositeEntity;
047import ptolemy.kernel.util.Attribute;
048import ptolemy.kernel.util.IllegalActionException;
049import ptolemy.kernel.util.NameDuplicationException;
050import ptolemy.kernel.util.Workspace;
051import ptolemy.util.MessageHandler;
052
053//////////////////////////////////////////////////////////////////////////
054//// BinaryFileWriter
055/**
056 * This actor receives an array of bytes as an input and writes it to a file. At
057 * each iteration a bytes array is read from the input port and written to a
058 * specified file.
059 * 
060 * @UserLevelDocumentation This actor writes streams of bytes array to a file
061 *                         specified by filename and outputs the filePath.
062 * @author Efrat Jaeger
063 * @version $Id: BinaryFileWriter.java 24234 2010-05-06 05:21:26Z welker $
064 * @since Ptolemy II 3.0.2
065 */
066public class BinaryFileWriter extends Sink {
067
068        /**
069         * Construct an actor with the given container and name.
070         * 
071         * @param container
072         *            The container.
073         * @param name
074         *            The name of this actor.
075         * @exception IllegalActionException
076         *                If the actor cannot be contained by the proposed
077         *                container.
078         * @exception NameDuplicationException
079         *                If the container already has an actor with this name.
080         */
081        public BinaryFileWriter(CompositeEntity container, String name)
082                        throws IllegalActionException, NameDuplicationException {
083                super(container, name);
084
085                input.setTypeEquals(new ArrayType(BaseType.UNSIGNED_BYTE));
086
087                filePath = new TypedIOPort(this, "filePath", false, true);
088                filePath.setTypeEquals(BaseType.STRING);
089
090                fileName = new FileParameter(this, "fileName");
091                fileName.setExpression("System.out");
092
093                append = new Parameter(this, "append");
094                append.setTypeEquals(BaseType.BOOLEAN);
095                append.setToken(BooleanToken.FALSE);
096
097                confirmOverwrite = new Parameter(this, "confirmOverwrite");
098                confirmOverwrite.setTypeEquals(BaseType.BOOLEAN);
099                confirmOverwrite.setToken(BooleanToken.TRUE);
100
101                _attachText("_iconDescription", "<svg>\n"
102                                + "<rect x=\"-25\" y=\"-20\" " + "width=\"50\" height=\"40\" "
103                                + "style=\"fill:white\"/>\n"
104                                + "<polygon points=\"-15,-10 -12,-10 -8,-14 -1,-14 3,-10"
105                                + " 15,-10 15,10, -15,10\" " + "style=\"fill:red\"/>\n"
106                                + "</svg>\n");
107        }
108
109        // /////////////////////////////////////////////////////////////////
110        // // ports and parameters ////
111
112        /**
113         * output the file URL for display purposes.
114         * 
115         * @UserLevelDocumentation Outputs the path to the written file.
116         */
117        public TypedIOPort filePath;
118
119        /**
120         * If true, then append to the specified file. If false (the default), then
121         * overwrite any preexisting file after asking the user for permission.
122         * 
123         * @UserLevelDocumentation If true, then append to the specified file. If
124         *                         false (the default), then overwrite any
125         *                         preexisting file after asking the user for
126         *                         permission.
127         */
128        public Parameter append;
129
130        /**
131         * The file name to which to write. This is a string with any form accepted
132         * by FileParameter. The default value is "System.out".
133         * 
134         * @see FileParameter
135         * @UserLevelDocumentation Path to the file to be written.
136         */
137        public FileParameter fileName;
138
139        /**
140         * If false, then overwrite the specified file if it exists without asking.
141         * If true (the default), then if the file exists, ask for confirmation
142         * before overwriting.
143         * 
144         * @UserLevelDocumentation If false, then overwrite the specified file if it
145         *                         exists without asking. If true (the default),
146         *                         then if the file exists, ask for confirmation
147         *                         before overwriting.
148         */
149        public Parameter confirmOverwrite;
150
151        // /////////////////////////////////////////////////////////////////
152        // // public methods ////
153
154        /**
155         * If the specified attribute is <i>fileName</i> and there is an open file
156         * being written, then close that file. The new file will be opened or
157         * created when it is next written to.
158         * 
159         * @param attribute
160         *            The attribute that has changed.
161         * @exception IllegalActionException
162         *                If the specified attribute is <i>fileName</i> and the
163         *                previously opened file cannot be closed.
164         */
165        public void attributeChanged(Attribute attribute)
166                        throws IllegalActionException {
167                if (attribute == fileName) {
168                        // Do not close the file if it is the same file.
169                        String newFileName = ((StringToken) fileName.getToken())
170                                        .stringValue();
171                        // calling getToken forces an evaluation of the expression
172                        // System.out.println("newFileName:"+newFileName);
173                        if (_previousFileName != null
174                                        && !newFileName.equals(_previousFileName)) {
175                                _previousFileName = newFileName;
176                                fileName.close();
177                                _writer = null;
178                        }
179                } else {
180                        super.attributeChanged(attribute);
181                }
182        }
183
184        /**
185         * Clone the actor into the specified workspace.
186         * 
187         * @return A new actor.
188         * @exception CloneNotSupportedException
189         *                If a derived class contains an attribute that cannot be
190         *                cloned.
191         */
192        public Object clone(Workspace workspace) throws CloneNotSupportedException {
193                BinaryFileWriter newObject = (BinaryFileWriter) super.clone(workspace);
194                newObject._writer = null;
195                return newObject;
196        }
197
198        /**
199         * Writes the input content to the file. At the first iteration opens the
200         * file for writing.
201         * 
202         */
203        public boolean postfire() throws IllegalActionException {
204                if (input.hasToken(0)) {
205                        Token token = input.get(0);
206                        if (_writer == null) {
207                                // Open the file.
208                                File file = fileName.asFile();
209                                boolean appendValue = ((BooleanToken) append.getToken())
210                                                .booleanValue();
211                                boolean confirmOverwriteValue = ((BooleanToken) confirmOverwrite
212                                                .getToken()).booleanValue();
213                                // Don't ask for confirmation in append mode, since there
214                                // will be no loss of data.
215                                if (file.exists() && !appendValue && confirmOverwriteValue) {
216                                        // Query for overwrite.
217                                        if (!MessageHandler.yesNoQuestion("OK to overwrite " + file
218                                                        + "?")) {
219                                                throw new IllegalActionException(this,
220                                                                "Please select another file name.");
221                                        }
222                                }
223                                try {
224                                        _writer = new FileOutputStream(file);
225                                } catch (Exception ex) {
226                                        System.out.println("Error opening stream");
227                                }
228                        }
229                        _writeToken(token);
230                        // String _fileName =
231                        // java.net.URLDecoder.decode(fileName.getExpression());
232                        // above line replaced so that expressions used in fileName will be
233                        // evaluated
234                        filePath.broadcast(new StringToken(((StringToken) fileName
235                                        .getToken()).stringValue()));
236                }
237                return super.postfire();
238        }
239
240        /**
241         * Close the writer if there is one.
242         * 
243         * @exception IllegalActionException
244         *                If an IO error occurs.
245         */
246        public void wrapup() throws IllegalActionException {
247                try {
248                        _writer.close();
249                } catch (Exception ex) {
250                        System.out.println("Error closing stream");
251                }
252
253                _writer = null;
254        }
255
256        // /////////////////////////////////////////////////////////////////
257        // // protected methods ////
258
259        private void _writeToken(Token token) {
260                ArrayToken dataArrayToken = (ArrayToken) token;
261                byte[] dataBytes = new byte[dataArrayToken.length()];
262                for (int j = 0; j < dataArrayToken.length(); j++) {
263                        UnsignedByteToken dataToken = (UnsignedByteToken) dataArrayToken
264                                        .getElement(j);
265                        dataBytes[j] = (byte) dataToken.byteValue();
266                }
267                try {
268                        _writer.write(dataBytes);
269                } catch (Exception ex) {
270                        System.out.println("Error writing to stream");
271                }
272
273        }
274
275        // /////////////////////////////////////////////////////////////////
276        // // protected members ////
277
278        /** The current writer. */
279        private FileOutputStream _writer;
280
281        // /////////////////////////////////////////////////////////////////
282        // // private members ////
283
284        /** Previous value of fileName parameter. */
285        private String _previousFileName;
286
287}