001/* A helper class to handle actions in GUI properties. 002 003 Copyright (c) 2008-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 027 */ 028package ptolemy.actor.gui.properties; 029 030import java.awt.geom.Point2D; 031import java.io.IOException; 032import java.io.InputStreamReader; 033import java.lang.reflect.Method; 034import java.net.URL; 035import java.util.Iterator; 036import java.util.List; 037 038import javax.swing.JFrame; 039 040import ptolemy.actor.gui.Configuration; 041import ptolemy.actor.gui.PtolemyFrame; 042import ptolemy.actor.gui.Tableau; 043import ptolemy.kernel.util.Attribute; 044import ptolemy.kernel.util.IllegalActionException; 045import ptolemy.kernel.util.InternalErrorException; 046import ptolemy.kernel.util.KernelException; 047import ptolemy.kernel.util.KernelRuntimeException; 048import ptolemy.kernel.util.Location; 049import ptolemy.kernel.util.NameDuplicationException; 050import ptolemy.kernel.util.NamedObj; 051import ptolemy.kernel.util.Singleton; 052import ptolemy.kernel.util.StringAttribute; 053import ptolemy.moml.MoMLChangeRequest; 054import ptolemy.moml.MoMLParser; 055import ptolemy.vergil.basic.BasicGraphFrame; 056 057////////////////////////////////////////////////////////////////////////// 058//// GUIAction 059 060/** 061 A helper class to handle actions in GUI properties. 062 063 @author Thomas Huining Feng 064 @version $Id$ 065 @since Ptolemy II 8.0 066 @Pt.ProposedRating Red (tfeng) 067 @Pt.AcceptedRating Red (tfeng) 068 */ 069public class GUIAction extends Attribute { 070 071 /** Construct an item with the given name contained by the specified 072 * entity. The container argument must not be null, or a 073 * NullPointerException will be thrown. This attribute will use the 074 * workspace of the container for synchronization and version counts. 075 * If the name argument is null, then the name is set to the empty 076 * string. Increment the version of the workspace. 077 * @param container The container. 078 * @param name The name of this attribute. 079 * @exception IllegalActionException If the attribute is not of an 080 * acceptable class for the container, or if the name contains a 081 * period. 082 * @exception NameDuplicationException If the name coincides with 083 * an attribute already in the container. 084 */ 085 public GUIAction(NamedObj container, String name) 086 throws IllegalActionException, NameDuplicationException { 087 super(container, name); 088 } 089 090 /** Configure the object with data from the specified input source 091 * (a URL) and/or textual data. The object should interpret the 092 * source first, if it is specified, followed by the literal text, 093 * if that is specified. The new configuration should usually 094 * override any old configuration wherever possible, in order to 095 * ensure that the current state can be successfully retrieved. 096 * <p> 097 * This method is defined to throw a very general exception to allow 098 * classes that implement the interface to use whatever exceptions 099 * are appropriate. 100 * @param base The base relative to which references within the input 101 * are found, or null if this is not known, or there is none. 102 * @param source The input source, which specifies a URL, or null 103 * if none. 104 * @param text Configuration information given as text, or null if 105 * none. 106 * @exception Exception If something goes wrong. 107 */ 108 public void configure(URL base, String source, String text) 109 throws Exception { 110 _momlSource = source; 111 _momlText = text; 112 _parsedObject = null; 113 } 114 115 /** Return the input source that was specified the last time the configure 116 * method was called. 117 * @return The string representation of the input URL, or null if the 118 * no source has been used to configure this object, or null if no 119 * external source need be used to configure this object. 120 */ 121 public String getConfigureSource() { 122 return _momlSource; 123 } 124 125 /** Return the text string that represents the current configuration of 126 * this object. Note that any configuration that was previously 127 * specified using the source attribute need not be represented here 128 * as well. 129 * @return A configuration string, or null if no configuration 130 * has been used to configure this object, or null if no 131 * configuration string need be used to configure this object. 132 */ 133 public String getConfigureText() { 134 return _momlText; 135 } 136 137 /** Get the frame in which this item is selected. 138 * 139 * @return The frame. 140 */ 141 public JFrame getFrame() { 142 NamedObj container = getContainer(); 143 while (container != null && !(container instanceof Tableau)) { 144 container = container.getContainer(); 145 } 146 if (container == null) { 147 throw new InternalErrorException("Unable to find tableau."); 148 } 149 150 return ((Tableau) container).getFrame(); 151 } 152 153 /** Get the model contained in the current frame. 154 * 155 * @return The model. 156 */ 157 public NamedObj getModel() { 158 JFrame frame = getFrame(); 159 if (!(frame instanceof PtolemyFrame)) { 160 throw new InternalErrorException( 161 "The current frame has " + "no model."); 162 } 163 return ((PtolemyFrame) frame).getModel(); 164 } 165 166 /** React to this item being selected. In this base class, if a source 167 * file is specified in the configuration of this item, e.g.: 168 * <pre> 169 * <configure source="some_file.xml"> 170 * </configure> 171 * </pre> 172 * then the source is read and its contents are used as the moml text. 173 * The moml text can also be given directly: 174 * <pre> 175 * <configure> 176 * <entity name="C" class="ptolemy.actor.lib.Const"> 177 * </entity> 178 * </configure> 179 * </pre> 180 * 181 * Depending on whether the parse parameter is true or false, 182 * the moml text may be parsed first or not. If it is parsed, the 183 * returned NamedObj is used to generate a new moml string to be 184 * applied to the model in the current tableau (the nearest tableau 185 * that contains this GUI property). If it is not parsed, then the moml 186 * text is directly applied to the model. 187 * 188 * @param parse Whether the configure text should be parsed before applying 189 * to the current model. 190 * @exception Exception If error occurs in performing the action. 191 */ 192 public void perform(boolean parse) throws Exception { 193 if (_momlText != null) { 194 NamedObj model = getModel(); 195 196 String moml; 197 if (parse) { 198 _parseSource(); 199 moml = getMoml(model, _parsedObject); 200 } else { 201 if (_momlSource != null) { 202 URL url = _parser.fileNameToURL(_momlSource, null); 203 InputStreamReader reader = null; 204 try { 205 reader = new InputStreamReader(url.openStream()); 206 207 int bufferSize = 1024; 208 char[] buffer = new char[bufferSize]; 209 int readSize = 0; 210 StringBuffer string = new StringBuffer(); 211 while (readSize >= 0) { 212 readSize = reader.read(buffer); 213 if (readSize >= 0) { 214 string.append(buffer, 0, readSize); 215 } 216 } 217 _momlText = string.toString(); 218 _momlSource = null; 219 } finally { 220 if (reader != null) { 221 try { 222 reader.close(); 223 } catch (IOException ex) { 224 throw new InternalErrorException( 225 "Failed to close \"" + url + "\"."); 226 } 227 } 228 } 229 } 230 moml = _momlText; 231 } 232 MoMLChangeRequest request = new MoMLChangeRequest(this, model, 233 moml) { 234 @Override 235 protected void _postParse(MoMLParser parser) { 236 Iterator topObjects = parser.topObjectsCreated().iterator(); 237 while (topObjects.hasNext()) { 238 NamedObj topObject = (NamedObj) topObjects.next(); 239 if (topObject.attributeList(Location.class).isEmpty()) { 240 try { 241 Location location = new Location(topObject, 242 topObject.uniqueName("_location")); 243 Point2D center = ((BasicGraphFrame) getFrame()) 244 .getCenter(); 245 location.setLocation(new double[] { 246 center.getX(), center.getY() }); 247 } catch (KernelException e) { 248 throw new InternalErrorException(e); 249 } 250 } 251 } 252 parser.clearTopObjectsList(); 253 } 254 255 @Override 256 protected void _preParse(MoMLParser parser) { 257 super._preParse(parser); 258 parser.clearTopObjectsList(); 259 } 260 }; 261 request.setUndoable(true); 262 model.requestChange(request); 263 } 264 } 265 266 /** Parse the configuration source if it has not been parsed, and store 267 * the result in protected field {@link #_parsedObject}. 268 * 269 * @exception Exception If it occurs in the parsing. 270 */ 271 protected void _parseSource() throws Exception { 272 if (_parsedObject == null) { 273 if (_momlSource != null) { 274 URL url = _parser.fileNameToURL(_momlSource, null); 275 _parsedObject = _parser.parse(url, url); 276 _momlSource = null; 277 } else { 278 _parsedObject = _parser.parse(_momlText); 279 } 280 _parser.reset(); 281 } 282 } 283 284 /** The input source that was specified the last time the configure 285 * method was called. 286 */ 287 protected String _momlSource; 288 289 /** The text string that represents the current configuration of this 290 * object. 291 */ 292 protected String _momlText; 293 294 /** The object obtained by parsing the moml text, or null. 295 */ 296 protected NamedObj _parsedObject; 297 298 /** The parser used to parse the moml text. 299 */ 300 protected MoMLParser _parser = new MoMLParser(); 301 302 /** Get the moml for the object to be added to the container. 303 * @param container The container. 304 * @param object The object whose moml is to be got. 305 * @return The moml string. 306 * @exception Exception If error occurs. 307 */ 308 private static String getMoml(NamedObj container, NamedObj object) 309 throws Exception { 310 String name; 311 312 if (object instanceof Singleton) { 313 name = object.getName(); 314 } else { 315 name = container.uniqueName(object.getName()); 316 } 317 318 boolean lsidFlag = true; 319 try { 320 String lsidString = ((StringAttribute) object 321 .getAttribute("entityId")).getExpression(); 322 if (lsidString == null || lsidString.equals("")) { 323 lsidFlag = false; 324 } 325 } catch (Exception eee) { 326 lsidFlag = false; 327 } 328 329 StringAttribute alternateGetMomlActionAttribute = null; 330 alternateGetMomlActionAttribute = (StringAttribute) object 331 .getAttribute("_alternateGetMomlAction"); 332 if (alternateGetMomlActionAttribute == null && lsidFlag) { 333 Configuration config = null; 334 List configsList = Configuration.configurations(); 335 for (Iterator it = configsList.iterator(); it.hasNext();) { 336 config = (Configuration) it.next(); 337 if (config != null) { 338 break; 339 } 340 } 341 if (config == null) { 342 throw new KernelRuntimeException(object, "Could not find " 343 + "configuration, list of configurations was " 344 + configsList.size() + " elements, all were null."); 345 } 346 alternateGetMomlActionAttribute = (StringAttribute) config 347 .getAttribute("_alternateGetMomlAction"); 348 } 349 350 if (alternateGetMomlActionAttribute != null) { 351 String alternateGetMomlClassName = alternateGetMomlActionAttribute 352 .getExpression(); 353 Class getMomlClass = Class.forName(alternateGetMomlClassName); 354 Object getMomlAction = getMomlClass.newInstance(); 355 try { 356 Method getMomlMethod = getMomlClass.getMethod("getMoml", 357 new Class[] { NamedObj.class, String.class }); 358 return (String) getMomlMethod.invoke(getMomlAction, 359 new Object[] { object, name }); 360 } catch (NoSuchMethodException e) { 361 } 362 } 363 364 return "<group name=\"auto\">\n" + object.exportMoML(name) 365 + "</group>\n"; 366 } 367}