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.BufferedReader;
033import java.io.IOException;
034import java.util.Vector;
035
036import ptolemy.actor.lib.Source;
037import ptolemy.data.ArrayToken;
038import ptolemy.data.IntToken;
039import ptolemy.data.Token;
040import ptolemy.data.expr.FileParameter;
041import ptolemy.data.expr.Parameter;
042import ptolemy.data.expr.Variable;
043import ptolemy.data.type.ArrayType;
044import ptolemy.data.type.BaseType;
045import ptolemy.kernel.CompositeEntity;
046import ptolemy.kernel.util.Attribute;
047import ptolemy.kernel.util.IllegalActionException;
048import ptolemy.kernel.util.NameDuplicationException;
049import ptolemy.kernel.util.Workspace;
050
051//////////////////////////////////////////////////////////////////////////
052//// FileToArray
053/**
054 * This actor reads a file or URL and outputs an array of the evaluations of all
055 * lines read. The file or URL is specified using any form acceptable to
056 * FileParameter.
057 * 
058 * This actor can skip some lines at the beginning of the file or URL, with the
059 * number specified by the numberOfLinesToSkip parameter. The default value of
060 * this parameter is 0.
061 * 
062 * @author Efrat Jaeger
063 * @version $Id: FileToArray.java 24234 2010-05-06 05:21:26Z welker $
064 * @since Ptolemy II 3.0.2
065 */
066public class FileToArray extends Source {
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 FileToArray(CompositeEntity container, String name)
082                        throws IllegalActionException, NameDuplicationException {
083                super(container, name);
084
085                _expressionEvaluator = new Variable(this, "_expressionEvaluator");
086
087                fileOrURL = new FileParameter(this, "fileOrURL");
088
089                numberOfLinesToSkip = new Parameter(this, "numberOfLinesToSkip",
090                                new IntToken(0));
091                numberOfLinesToSkip.setTypeEquals(BaseType.INT);
092
093                _attachText("_iconDescription", "<svg>\n"
094                                + "<rect x=\"-25\" y=\"-20\" " + "width=\"50\" height=\"40\" "
095                                + "style=\"fill:white\"/>\n"
096                                + "<polygon points=\"-15,-10 -12,-10 -8,-14 -1,-14 3,-10"
097                                + " 15,-10 15,10, -15,10\" " + "style=\"fill:red\"/>\n"
098                                + "</svg>\n");
099        }
100
101        // /////////////////////////////////////////////////////////////////
102        // // ports and parameters ////
103
104        /**
105         * The file name or URL from which to read. This is a string with any form
106         * accepted by FileParameter.
107         * 
108         * @see FileParameter
109         */
110        public FileParameter fileOrURL;
111
112        /**
113         * The number of lines to skip at the beginning of the file or URL. This
114         * parameter contains an IntToken, initially with value 0. The value of this
115         * parameter must be non-negative.
116         */
117        public Parameter numberOfLinesToSkip;
118
119        // /////////////////////////////////////////////////////////////////
120        // // public methods ////
121
122        /**
123         * If the specified attribute is <i>fileOrURL</i> and there is an open file
124         * being read, then close that file and open the new one; if the attribute
125         * is <i>numberOfLinesToSkip</i> and its value is negative, then throw an
126         * exception. In the case of <i>fileOrURL</i>, do nothing if the file name
127         * is the same as the previous value of this attribute.
128         * 
129         * @param attribute
130         *            The attribute that has changed.
131         * @exception IllegalActionException
132         *                If the specified attribute is <i>fileOrURL</i> and the
133         *                file cannot be opened, or the previously opened file
134         *                cannot be closed; or if the attribute is
135         *                <i>numberOfLinesToSkip</i> and its value is negative.
136         */
137        public void attributeChanged(Attribute attribute)
138                        throws IllegalActionException {
139                if (attribute == fileOrURL) {
140                        // NOTE: We do not want to close the file if the file
141                        // has not in fact changed. We check this by just comparing
142                        // name, which is not perfect...
143                        if (_previousFileOrURL != null
144                                        && !fileOrURL.getExpression().equals(_previousFileOrURL)) {
145                                _previousFileOrURL = fileOrURL.getExpression();
146                                fileOrURL.close();
147                                // Ignore if the fileOrUL is blank.
148                                if (fileOrURL.getExpression().trim().equals("")) {
149                                        _reader = null;
150                                } else {
151                                        _reader = fileOrURL.openForReading();
152                                }
153                        }
154                } else if (attribute == numberOfLinesToSkip) {
155                        int linesToSkip = ((IntToken) numberOfLinesToSkip.getToken())
156                                        .intValue();
157                        if (linesToSkip < 0) {
158                                throw new IllegalActionException(this, "The number of lines "
159                                                + "to skip cannot be negative.");
160                        }
161                } else {
162                        super.attributeChanged(attribute);
163                }
164        }
165
166        /**
167         * Clone the actor into the specified workspace.
168         * 
169         * @return A new actor.
170         * @exception CloneNotSupportedException
171         *                If a derived class contains an attribute that cannot be
172         *                cloned.
173         */
174        public Object clone(Workspace workspace) throws CloneNotSupportedException {
175                FileToArray newObject = (FileToArray) super.clone(workspace);
176                newObject._currentLine = null;
177                newObject._reader = null;
178                return newObject;
179        }
180
181        /**
182         * Output the data lines into an array.
183         * 
184         * @exception IllegalActionException
185         *                If there's no director.
186         */
187
188        public void fire() throws IllegalActionException {
189                super.fire();
190                Vector lines = new Vector();
191                while (_currentLine != null) {
192                        _expressionEvaluator.setExpression(_currentLine);
193                        lines.addElement(_expressionEvaluator.getToken());
194                        try {
195                                _currentLine = _reader.readLine();
196                        } catch (IOException ex) {
197                                throw new IllegalActionException(this, ex,
198                                                "fire failed reading line");
199                        }
200                }
201                Token expressions[] = new Token[lines.size()];
202                lines.toArray(expressions);
203                output.send(0, new ArrayToken(expressions));
204        }
205
206        /**
207         * Post fire the actor. Return false to indicate that the process has
208         * finished. If it returns true, the process will continue indefinitely.
209         */
210        public boolean postfire() {
211                return false;
212        }
213
214        /**
215         * Open the file or URL and read the first line, and use the first line to
216         * set the type of the output.
217         * 
218         * @exception IllegalActionException
219         *                If the file or URL cannot be opened, or if the first line
220         *                cannot be read.
221         */
222        public void preinitialize() throws IllegalActionException {
223                super.preinitialize();
224                _openAndReadFirstLine();
225
226                // Set the output type.
227                _expressionEvaluator.setExpression(_currentLine);
228                output.setTypeEquals(new ArrayType(_expressionEvaluator.getType()));
229        }
230
231        /**
232         * Close the reader if there is one.
233         * 
234         * @exception IllegalActionException
235         *                If an IO error occurs.
236         */
237        public void wrapup() throws IllegalActionException {
238                fileOrURL.close();
239                _reader = null;
240        }
241
242        // /////////////////////////////////////////////////////////////////
243        // // protected members ////
244
245        /** Cache of most recently read data. */
246        protected String _currentLine;
247
248        /** The current reader for the input file. */
249        protected BufferedReader _reader;
250
251        // /////////////////////////////////////////////////////////////////
252        // // private methods ////
253
254        /**
255         * Open the file and read the first line.
256         */
257        private void _openAndReadFirstLine() throws IllegalActionException {
258                _reader = fileOrURL.openForReading();
259                try {
260                        // Read (numberOfLinesToSkip + 1) lines
261                        int numberOfLines = ((IntToken) numberOfLinesToSkip.getToken())
262                                        .intValue();
263                        for (int i = 0; i <= numberOfLines; i++) {
264                                _currentLine = _reader.readLine();
265                                if (_currentLine == null) {
266                                        throw new IllegalActionException(this, "The file does not "
267                                                        + "have enough lines.");
268                                }
269                        }
270                } catch (IOException ex) {
271                        throw new IllegalActionException(this, ex, "Preinitialize failed.");
272                }
273        }
274
275        // /////////////////////////////////////////////////////////////////
276        // // private members ////
277
278        /** Previous value of fileOrURL parameter. */
279        private String _previousFileOrURL;
280
281        /** Variable used to evaluate expressions. */
282        private Variable _expressionEvaluator;
283
284}