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}