001/* A representative of a file that contains one or more tokens.
002
003 Copyright (c) 1998-2018 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.awt.image.BufferedImage;
030import java.io.File;
031import java.io.IOException;
032import java.net.URL;
033import java.util.Iterator;
034
035import javax.imageio.ImageIO;
036
037import ptolemy.actor.lib.image.ImageTableau;
038import ptolemy.data.AWTImageToken;
039import ptolemy.data.ImageToken;
040import ptolemy.kernel.CompositeEntity;
041import ptolemy.kernel.util.Attribute;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.NameDuplicationException;
044import ptolemy.kernel.util.Workspace;
045
046///////////////////////////////////////////////////////////////////
047//// ImageTokenEffigy
048
049/**
050 An effigy for a file that contains an image.
051
052 @author Edward A. Lee
053 @version $Id$
054 @since Ptolemy II 11.0
055 @Pt.ProposedRating Red (neuendor)
056 @Pt.AcceptedRating Red (neuendor)
057 */
058public class ImageTokenEffigy extends Effigy {
059    /** Create a new effigy in the specified workspace with an empty string
060     *  for its name.
061     *  @param workspace The workspace for this effigy.
062     */
063    public ImageTokenEffigy(Workspace workspace) {
064        super(workspace);
065    }
066
067    /** Create a new effigy in the given directory with the given name.
068     *  @param container The directory that contains this effigy.
069     *  @param name The name of this effigy.
070     *  @exception IllegalActionException If the entity cannot be contained
071     *   by the proposed container.
072     *  @exception NameDuplicationException If the name coincides with
073     *   an entity already in the container.
074     */
075    public ImageTokenEffigy(CompositeEntity container, String name)
076            throws IllegalActionException, NameDuplicationException {
077        super(container, name);
078    }
079
080    ///////////////////////////////////////////////////////////////////
081    ////                         public methods                    ////
082
083    /** If the argument is the <i>uri</i> parameter, then read the
084     *  specified URL and parse the data contained in it.
085     *  @param attribute The attribute that changed.
086     *  @exception IllegalActionException If the URL cannot be read or
087     *   if the data is malformed.
088     */
089    @Override
090    public void attributeChanged(Attribute attribute)
091            throws IllegalActionException {
092        // The superclass does some handling of the url attribute.
093        super.attributeChanged(attribute);
094
095        if (attribute == uri) {
096            try {
097                URL urlToRead = uri.getURL();
098
099                if (urlToRead != null) {
100                    read(urlToRead);
101                }
102            } catch (IOException ex) {
103                throw new IllegalActionException(this, null, ex,
104                        "Failed to read data: " + ex.getMessage());
105            }
106        }
107    }
108
109    /** Clear the token array associated with this effigy.
110     */
111    public void clear() {
112        _token = null;
113
114        // Notify the contained tableaux.
115        Iterator tableaux = entityList(TokenTableau.class).iterator();
116
117        while (tableaux.hasNext()) {
118            TokenTableau tableau = (TokenTableau) tableaux.next();
119            tableau.clear();
120        }
121    }
122
123    /** Return the image represented by this effigy.
124     *  @return An image token.
125     *  @see #setImage(ImageToken)
126     */
127    public ImageToken getImage() {
128        return _token;
129    }
130
131    /** Read the specified URL and parse the data.
132     *  @param input The URL to read.
133     *  @exception IOException If an error occurs while reading the URL
134     *   or parsing the data.
135     */
136    public void read(URL input) throws IOException {
137        if (input == null) {
138            throw new IOException("Attempt to read from null input.");
139        }
140        BufferedImage image = ImageIO.read(input);
141        if (image != null) {
142            // Notify the contained tableaux.
143            Iterator tableaux = entityList(ImageTableau.class).iterator();
144            while (tableaux.hasNext()) {
145                try {
146                    ((ImageTableau) tableaux.next())
147                            .append(new AWTImageToken(image));
148                } catch (IllegalActionException e) {
149                    throw new IOException(e.getMessage());
150                }
151            }
152        }
153    }
154
155    /** Specify the image represented by this effigy.
156     *  @param token The image represented by this effigy.
157     *  @exception IllegalActionException If the token is not acceptable.
158     *  @see #getImage()
159     */
160    public void setImage(ImageToken token) throws IllegalActionException {
161        _token = token;
162
163        // Notify the contained tableaux.
164        Iterator tableaux = entityList(TokenTableau.class).iterator();
165
166        while (tableaux.hasNext()) {
167            TokenTableau tableau = (TokenTableau) tableaux.next();
168            tableau.append(token);
169        }
170    }
171
172    /** Write the current data of this effigy to the specified file.
173     *  The filename extension is used to determine the format.
174     *  Understood extensions include "jpg", "jpeg", "png", and "gif" (not
175     *  case sensitive).
176     *  @param file The file to write to, or null to write to standard out.
177     *  @exception IOException If the write fails.
178     */
179    @Override
180    public void writeFile(File file) throws IOException {
181        // Get the filename extension.
182        String name = file.getName();
183        int index = name.lastIndexOf(".");
184        if (index > 0) {
185            String extension = name.substring(index + 1).toLowerCase();
186            if (extension.equals("jpeg")) {
187                extension = "jpg";
188            } else if (!extension.equals("jpg") && !extension.equals("png")
189                    && !extension.equals("gif")) {
190                throw new IOException("Unrecognized file extension: "
191                        + extension + ". Should be one of jpg, png, or gif.");
192            }
193            if (!ImageIO.write((BufferedImage) _token.asAWTImage(), extension,
194                    file)) {
195                throw new IOException(
196                        "No writer found for file type: " + extension);
197            }
198        }
199    }
200
201    ///////////////////////////////////////////////////////////////////
202    ////                         private members                   ////
203
204    // The image token.
205    private ImageToken _token;
206
207    ///////////////////////////////////////////////////////////////////
208    ////                         inner classes                     ////
209
210    /** A factory for creating new effigies.
211     */
212    public static class Factory extends EffigyFactory {
213        /** Create a factory with the given name and container.
214         *  @param container The container.
215         *  @param name The name.
216         *  @exception IllegalActionException If the container is incompatible
217         *   with this entity.
218         *  @exception NameDuplicationException If the name coincides with
219         *   an entity already in the container.
220         */
221        public Factory(CompositeEntity container, String name)
222                throws IllegalActionException, NameDuplicationException {
223            super(container, name);
224        }
225
226        ///////////////////////////////////////////////////////////////
227        ////                     public methods                    ////
228
229        /** Return false, indicating that this effigy factory is not
230         *  capable of creating an effigy without a URL being specified.
231         *  @return False.
232         */
233        @Override
234        public boolean canCreateBlankEffigy() {
235            return false;
236        }
237
238        /** Create a new effigy in the given container by reading the
239         *  specified URL. If the specified URL is null, or
240         *  if the URL does not end with extension ".jpeg", ".jpg", ".png",
241         *  or ".gif", then return null.
242         *  @param container The container for the effigy.
243         *  @param base The base for relative file references, which are
244         *   ignored here, and therefore can be null.
245         *  @param input The input URL.
246         *  @return A new instance of ImageTokenEffigy, or null if the URL
247         *   does not have a recognized extension.
248         *  @exception Exception If the URL cannot be read.
249         */
250        @Override
251        public Effigy createEffigy(CompositeEntity container, URL base,
252                URL input) throws Exception {
253            if (input != null) {
254                String extension = getExtension(input).toLowerCase();
255
256                if (extension.equals("jpg") || extension.equals("jpeg")
257                        || extension.equals("png") || extension.equals("gif")) {
258                    ImageTokenEffigy effigy = new ImageTokenEffigy(container,
259                            container.uniqueName("effigy"));
260                    effigy.uri.setURL(input);
261                    BufferedImage image = ImageIO.read(input);
262                    ImageToken token = new AWTImageToken(image);
263                    effigy.setImage(token);
264                    return effigy;
265                }
266            }
267
268            return null;
269        }
270    }
271}