001/* A representative of a file that contains one or more tokens.
002
003 Copyright (c) 1998-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026 */
027package ptolemy.actor.gui;
028
029import java.io.File;
030import java.io.FileWriter;
031import java.io.IOException;
032import java.io.InputStreamReader;
033import java.io.LineNumberReader;
034import java.io.OutputStreamWriter;
035import java.io.PrintWriter;
036import java.io.Writer;
037import java.net.URL;
038import java.util.ArrayList;
039import java.util.Iterator;
040import java.util.List;
041
042import ptolemy.data.Token;
043import ptolemy.data.expr.Variable;
044import ptolemy.kernel.CompositeEntity;
045import ptolemy.kernel.util.Attribute;
046import ptolemy.kernel.util.IllegalActionException;
047import ptolemy.kernel.util.KernelException;
048import ptolemy.kernel.util.NameDuplicationException;
049import ptolemy.kernel.util.Workspace;
050
051///////////////////////////////////////////////////////////////////
052//// TokenEffigy
053
054/**
055 An effigy for a file that contains one or more tokens, one per line,
056 represented as text in the expression language.
057
058 @author Edward A. Lee
059 @version $Id$
060 @since Ptolemy II 2.1
061 @Pt.ProposedRating Red (neuendor)
062 @Pt.AcceptedRating Red (neuendor)
063 */
064public class TokenEffigy extends Effigy {
065    /** Create a new effigy in the specified workspace with an empty string
066     *  for its name.
067     *  @param workspace The workspace for this effigy.
068     */
069    public TokenEffigy(Workspace workspace) {
070        super(workspace);
071    }
072
073    /** Create a new effigy in the given directory with the given name.
074     *  @param container The directory that contains this effigy.
075     *  @param name The name of this effigy.
076     *  @exception IllegalActionException If the entity cannot be contained
077     *   by the proposed container.
078     *  @exception NameDuplicationException If the name coincides with
079     *   an entity already in the container.
080     */
081    public TokenEffigy(CompositeEntity container, String name)
082            throws IllegalActionException, NameDuplicationException {
083        super(container, name);
084    }
085
086    ///////////////////////////////////////////////////////////////////
087    ////                         public methods                    ////
088
089    /** Append the specified token to the token array associated with this
090     *  effigy.
091     *  @param token The token to append.
092     *  @exception IllegalActionException If the token is not acceptable.
093     */
094    public void append(Token token) throws IllegalActionException {
095        _tokens.add(token);
096
097        // Notify the contained tableaux.
098        Iterator tableaux = entityList(TokenTableau.class).iterator();
099
100        while (tableaux.hasNext()) {
101            TokenTableau tableau = (TokenTableau) tableaux.next();
102            tableau.append(token);
103        }
104    }
105
106    /** If the argument is the <i>uri</i> parameter, then read the
107     *  specified URL and parse the data contained in it.
108     *  @param attribute The attribute that changed.
109     *  @exception IllegalActionException If the URL cannot be read or
110     *   if the data is malformed.
111     */
112    @Override
113    public void attributeChanged(Attribute attribute)
114            throws IllegalActionException {
115        // The superclass does some handling of the url attribute.
116        super.attributeChanged(attribute);
117
118        if (attribute == uri) {
119            try {
120                URL urlToRead = uri.getURL();
121
122                if (urlToRead != null) {
123                    read(urlToRead);
124                }
125            } catch (IOException ex) {
126                throw new IllegalActionException(this, null, ex,
127                        "Failed to read data: " + ex.getMessage());
128            }
129        }
130    }
131
132    /** Clear the token array associated with this effigy.
133     */
134    public void clear() {
135        _tokens.clear();
136
137        // Notify the contained tableaux.
138        Iterator tableaux = entityList(TokenTableau.class).iterator();
139
140        while (tableaux.hasNext()) {
141            TokenTableau tableau = (TokenTableau) tableaux.next();
142            tableau.clear();
143        }
144    }
145
146    /** Return an array of the tokens in the file.
147     *  @return An array of tokens in the file.
148     *  @see #setTokens(List)
149     */
150    public ArrayList getTokens() {
151        return _tokens;
152    }
153
154    /** Read the specified URL and parse the data.
155     *  @param input The URL to read.
156     *  @exception IOException If an error occurs while reading the URL
157     *   or parsing the data.
158     */
159    public void read(URL input) throws IOException {
160        if (input == null) {
161            throw new IOException("Attempt to read from null input.");
162        }
163
164        LineNumberReader reader = null;
165
166        try {
167            reader = new LineNumberReader(
168                    new InputStreamReader(input.openStream()));
169
170            while (true) {
171                // NOTE: The following tolerates all major line terminators.
172                String line = reader.readLine();
173
174                if (line == null) {
175                    break;
176                }
177
178                try {
179                    // Parse the line.
180                    if (_variable == null) {
181                        _variable = new Variable(workspace());
182                        _variable.setName("Expression evaluator");
183                    }
184
185                    _variable.setExpression(line);
186
187                    Token token = _variable.getToken();
188
189                    if (token != null) {
190                        _tokens.add(token);
191
192                        // Notify the contained tableaux.
193                        Iterator tableaux = entityList(TokenTableau.class)
194                                .iterator();
195
196                        while (tableaux.hasNext()) {
197                            ((TokenTableau) tableaux.next()).append(token);
198                        }
199                    }
200                } catch (KernelException ex) {
201                    throw new IOException("Error evaluating data expression: "
202                            + ex.getMessage());
203                }
204            }
205        } finally {
206            if (reader != null) {
207                reader.close();
208            }
209        }
210    }
211
212    /** Set the token array associated with this effigy.
213     *  @param tokens An array of tokens.
214     *  @exception IllegalActionException If the tokens are not acceptable.
215     *  @see #getTokens()
216     */
217    public void setTokens(List tokens) throws IllegalActionException {
218        _tokens.clear();
219        _tokens.addAll(tokens);
220
221        // Notify the contained tableaux.
222        Iterator tableaux = entityList(TokenTableau.class).iterator();
223
224        while (tableaux.hasNext()) {
225            TokenTableau tableau = (TokenTableau) tableaux.next();
226            tableau.clear();
227            tableau.append(tokens);
228        }
229    }
230
231    /** Write the current data of this effigy to the specified file.
232     *  This is done by producing one line per token using its toString()
233     *  method.
234     *  @param file The file to write to, or null to write to standard out.
235     *  @exception IOException If the write fails.
236     */
237    @Override
238    public void writeFile(File file) throws IOException {
239        Writer writer = null;
240
241        if (file == null) {
242            writer = new OutputStreamWriter(System.out);
243        } else {
244            writer = new FileWriter(file);
245        }
246
247        // Use a PrintWriter so that the local platform's notion
248        // of line termination is used, making a more user-friendly output.
249        // Note that the corresponding reader is platform-tolerant.
250        PrintWriter print = new PrintWriter(writer);
251        Iterator tokens = _tokens.iterator();
252
253        while (tokens.hasNext()) {
254            Token token = (Token) tokens.next();
255            print.println(token.toString());
256        }
257
258        print.close();
259    }
260
261    ///////////////////////////////////////////////////////////////////
262    ////                         private members                   ////
263    // The list of tokens read from the file.
264    private ArrayList _tokens = new ArrayList();
265
266    // The variable used to evaluate the expression.
267    private Variable _variable;
268
269    ///////////////////////////////////////////////////////////////////
270    ////                         inner classes                     ////
271
272    /** A factory for creating new effigies.
273     */
274    public static class Factory extends EffigyFactory {
275        /** Create a factory with the given name and container.
276         *  @param container The container.
277         *  @param name The name.
278         *  @exception IllegalActionException If the container is incompatible
279         *   with this entity.
280         *  @exception NameDuplicationException If the name coincides with
281         *   an entity already in the container.
282         */
283        public Factory(CompositeEntity container, String name)
284                throws IllegalActionException, NameDuplicationException {
285            super(container, name);
286        }
287
288        ///////////////////////////////////////////////////////////////
289        ////                     public methods                    ////
290
291        /** Return false, indicating that this effigy factory is not
292         *  capable of creating an effigy without a URL being specified.
293         *  @return False.
294         */
295        @Override
296        public boolean canCreateBlankEffigy() {
297            return false;
298        }
299
300        /** Create a new effigy in the given container by reading the
301         *  specified URL. If the specified URL is null, or
302         *  if the URL does not end with extension ".ptd"
303         *  (for Ptolemy data), then return null.
304         *  @param container The container for the effigy.
305         *  @param base The base for relative file references, which are
306         *   ignored here, and therefore can be null.
307         *  @param input The input URL.
308         *  @return A new instance of TokenEffigy, or null if the URL
309         *   does not have a recognized extension.
310         *  @exception Exception If the URL cannot be read.
311         */
312        @Override
313        public Effigy createEffigy(CompositeEntity container, URL base,
314                URL input) throws Exception {
315            if (input != null) {
316                String extension = getExtension(input);
317
318                if (extension.equals("ptd")) {
319                    TokenEffigy effigy = new TokenEffigy(container,
320                            container.uniqueName("effigy"));
321                    effigy.uri.setURL(input);
322                    return effigy;
323                }
324            }
325
326            return null;
327        }
328    }
329}