001/*
002 * Copyright (c) 2002-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2016-07-13 00:37:38 +0000 (Wed, 13 Jul 2016) $' 
007 * '$Revision: 34505 $'
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.IOException;
033import java.io.InputStream;
034import java.net.URL;
035
036import ptolemy.actor.TypedIOPort;
037import ptolemy.actor.lib.Source;
038import ptolemy.data.ArrayToken;
039import ptolemy.data.BooleanToken;
040import ptolemy.data.StringToken;
041import ptolemy.data.Token;
042import ptolemy.data.UnsignedByteToken;
043import ptolemy.data.expr.FileParameter;
044import ptolemy.data.expr.Parameter;
045import ptolemy.data.type.ArrayType;
046import ptolemy.data.type.BaseType;
047import ptolemy.kernel.CompositeEntity;
048import ptolemy.kernel.util.IllegalActionException;
049import ptolemy.kernel.util.NameDuplicationException;
050import ptolemy.kernel.util.Workspace;
051
052//////////////////////////////////////////////////////////////////////////
053//// BinaryFileReader
054/**
055 * This actor reads a file or URL and outputs its content as a bytes array. At
056 * each iteration a chunk of bytes is read from the file and outputed as a bytes
057 * array.
058 * 
059 * @UserLevelDocumentation This actor reads binary files and streams its content
060 *                         as a bytes array.
061 * @author Efrat Jaeger
062 * @version $Id: BinaryFileReader.java 34505 2016-07-13 00:37:38Z crawl $
063 * @since Ptolemy II 3.0.2
064 */
065public class BinaryFileReader extends Source {
066
067        /**
068         * Construct an actor with the given container and name.
069         * 
070         * @param container
071         *            The container.
072         * @param name
073         *            The name of this actor.
074         * @exception IllegalActionException
075         *                If the actor cannot be contained by the proposed
076         *                container.
077         * @exception NameDuplicationException
078         *                If the container already has an actor with this name.
079         */
080        public BinaryFileReader(CompositeEntity container, String name)
081                        throws IllegalActionException, NameDuplicationException {
082                super(container, name);
083
084                output.setTypeEquals(new ArrayType(BaseType.UNSIGNED_BYTE));
085
086                endOfFile = new TypedIOPort(this, "endOfFile", false, true);
087                endOfFile.setTypeEquals(BaseType.BOOLEAN);
088
089                fileOrURL = new FileParameter(this, "fileOrURL");
090
091                fileOrURLPort = new TypedIOPort(this, "fileOrURLPort", true, false);
092                fileOrURLPort.setTypeEquals(BaseType.STRING);
093
094                readAll = new Parameter(this, "readAll");
095                readAll.setTypeEquals(BaseType.BOOLEAN);
096                readAll.setToken(BooleanToken.FALSE);
097                
098                _attachText("_iconDescription", "<svg>\n"
099                                + "<rect x=\"-25\" y=\"-20\" " + "width=\"50\" height=\"40\" "
100                                + "style=\"fill:white\"/>\n"
101                                + "<polygon points=\"-15,-10 -12,-10 -8,-14 -1,-14 3,-10"
102                                + " 15,-10 15,10, -15,10\" " + "style=\"fill:red\"/>\n"
103                                + "</svg>\n");
104        }
105
106        // /////////////////////////////////////////////////////////////////
107        // // ports and parameters ////
108
109        /**
110         * An output port that produces false until the end of file is reached, at
111         * which point it produces true. The type is boolean.
112         * 
113         * @UserLevelDocumentation An output port that produces false until the end
114         *                         of file is reached, at which point it produces
115         *                         true.
116         */
117        public TypedIOPort endOfFile;
118
119        /**
120         * The file name or URL from which to read. This is a string with any form
121         * accepted by FileParameter.
122         * 
123         * @UserLevelDocumentation The path to the file to be read.
124         * @see FileParameter
125         */
126        public FileParameter fileOrURL;
127
128        /**
129         * An input for optionally providing a file name.
130         * 
131         * @UserLevelDocumentation An input port for passing a file path sent from a
132         *                         previous step in the workflow.
133         * @see FileParameter
134         */
135        public TypedIOPort fileOrURLPort;
136
137        /** If true, read the entire contents of the file at once. */
138        public Parameter readAll;
139        
140        // /////////////////////////////////////////////////////////////////
141        // // public methods ////
142
143        /**
144         * Clone the actor into the specified workspace.
145         * 
146         * @return A new actor.
147         * @exception CloneNotSupportedException
148         *                If a derived class contains an attribute that cannot be
149         *                cloned.
150         */
151        @Override
152    public Object clone(Workspace workspace) throws CloneNotSupportedException {
153                BinaryFileReader newObject = (BinaryFileReader) super.clone(workspace);
154                newObject._nBytesRead = 0;
155                newObject._bytesRead = null;
156                newObject._newFile = true;
157                newObject._previousFileOrURL = null;
158                newObject._reader = null;
159                return newObject;
160        }
161
162        /**
163         * Output the data read initially in the prefire() than in each invocation
164         * of postfire(), if there is any.
165         * 
166         * @exception IllegalActionException
167         *                If there's no director.
168         */
169        @Override
170    public void fire() throws IllegalActionException {
171                super.fire();
172                if (_nBytesRead > 0) {
173                        Token _bytes[] = new Token[_nBytesRead];
174                        for (int i = 0; i < _nBytesRead; i++) {
175                                _bytes[i] = new UnsignedByteToken(_bytesRead[i]);
176                        }
177                        output.send(0, new ArrayToken(_bytes));
178                }
179        }
180
181        /**
182         * If this is called after prefire() has been called but before wrapup() has
183         * been called, then close any open file.
184         * 
185         * @exception IllegalActionException
186         *                If the file or URL cannot be opened or read.
187         */
188        @Override
189    public void initialize() throws IllegalActionException {
190                super.initialize();
191                _reader = null;
192                fileOrURL.close();
193        }
194
195        /**
196         * Read the next bytes from the file. If there reached EOF, return false.
197         * Otherwise, return whatever the superclass returns.
198         * 
199         * @exception IllegalActionException
200         *                If there is a problem reading the file.
201         */
202        @Override
203    public boolean postfire() throws IllegalActionException {
204                if (_reader == null) {
205                        return false;
206                }
207                try {
208                        _nBytesRead = _reader.read(_bytesRead);
209                        if (_nBytesRead <= 0) {
210                                // In case the return value gets ignored by the domain:
211                                _newFile = true;
212                                endOfFile.broadcast(BooleanToken.TRUE);
213                                String fileNameStr = ((StringToken) fileOrURL.getToken())
214                                                .stringValue();
215                                // If the fileName is read from a parameter and hasn't changed -
216                                // meaning there is no next file to read.
217                                if (fileOrURLPort.getWidth() == 0
218                                                && fileNameStr.equals(_previousFileOrURL)) {
219                                        return false;
220                                }
221                        } else {
222                                endOfFile.broadcast(BooleanToken.FALSE);
223                        }
224                        return super.postfire();
225                } catch (IOException ex) {
226                        throw new IllegalActionException(this, ex, "Postfire failed");
227                }
228        }
229
230        /**
231         * If this method is called after wrapup() has been called, then open the
232         * file, and read the first chunk of bytes Return false if there is no more
233         * data available in the file. Otherwise, return whatever the superclass
234         * returns.
235         * 
236         * @exception IllegalActionException
237         *                If the superclass throws it.
238         */
239        @Override
240    public boolean prefire() throws IllegalActionException {
241                if (_newFile) {
242                        if (fileOrURLPort.getWidth() > 0) {
243                                if (fileOrURLPort.hasToken(0)) {
244                                        String name = ((StringToken) fileOrURLPort.get(0))
245                                                        .stringValue();
246                                        int lineEndInd = name.indexOf("\n");
247                                        if (lineEndInd != -1) // if the string contains a CR.
248                                                name = name.substring(0, lineEndInd);
249                                        fileOrURL.setExpression(name);
250                                }
251                        }
252                        String fileNameStr = ((StringToken) fileOrURL.getToken())
253                                        .stringValue();
254                        /*
255                         * if (fileNameStr.equals(_previousFileOrURL)) { _reader = null;
256                         * return false; } else
257                         */
258                        _previousFileOrURL = fileNameStr;
259
260                        _openAndReadFirstBytes();
261                        _newFile = false;
262                }
263
264                // if (_reachedEOF) return false;
265                // else
266                return super.prefire();
267        }
268
269        /**
270         * Close the reader if there is one.
271         * 
272         * @exception IllegalActionException
273         *                If an IO error occurs.
274         */
275        @Override
276    public void wrapup() throws IllegalActionException {
277            super.wrapup();
278                fileOrURL.close();
279                _reader = null;
280                _newFile = true;
281                _previousFileOrURL = null;
282                _bytesRead = null;
283        }
284
285        // /////////////////////////////////////////////////////////////////
286        // // protected members ////
287
288        /** number of bytes read. */
289        protected int _nBytesRead;
290        
291        /** The current bytes read. */
292        protected byte[] _bytesRead;
293
294        /** The current reader for the input file. */
295        protected InputStream _reader;
296
297        // /////////////////////////////////////////////////////////////////
298        // // private methods ////
299
300        /**
301         * Open the file and read the first bytes.
302         */
303        private void _openAndReadFirstBytes() throws IllegalActionException {
304                URL url = fileOrURL.asURL();
305                if (url == null) {
306                        throw new IllegalActionException(this,
307                                        "No file name has been specified.");
308                }
309                try {
310                        _reader = url.openStream();
311                } catch (IOException ex) {
312                        throw new IllegalActionException(this, ex,
313                                        "Cannot open file or URL");
314                }
315
316                
317                Token token = readAll.getToken();
318                if(token == null || !((BooleanToken)token).booleanValue()) {
319                    _bytesRead = new byte[DEFAULT_BUFFER_SIZE];
320                } else {
321                    _bytesRead = new byte[Long.valueOf(fileOrURL.asFile().length()).intValue()];
322                }
323                
324                try {
325                        _nBytesRead = _reader.read(_bytesRead);
326                } catch (IOException ex) {
327                        throw new IllegalActionException(this, ex, "Preinitialize failed.");
328                }
329        }
330
331        // /////////////////////////////////////////////////////////////////
332        // // private members ////
333
334        /** Previous value of fileOrURL parameter. */
335        private String _previousFileOrURL;
336
337        /** Indicator to open a new file in the prefire */
338        private boolean _newFile = true;
339        
340        /** Number of bytes in read buffer if readAll parameter is false. */
341        private final static int DEFAULT_BUFFER_SIZE = 20000;
342}