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