001/* Add a missing parameter. 002 003 Copyright (c) 2012-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.moml.filter; 029 030import java.util.HashMap; 031import java.util.Map; 032 033import ptolemy.kernel.util.IllegalActionException; 034import ptolemy.kernel.util.NamedObj; 035import ptolemy.moml.MoMLParser; 036 037/////////////////////////////////////////////////////////////////// 038//// AddMissingParameter 039 040/** Add a missing parameter. 041 042 <p>If a SDFDirector does not have an iterations parameter, then 043 add one with the value "0", which is the default for Ptolemy II 044 8.0 and earlier.</p> 045 046 <p>FIXME: This class is similar to AddEditorFactory. 047 The two classes should be merged by parameterizing. 048 049 @author Christopher Brooks 050 @version $Id$ 051 @since Ptolemy II 10.0 052 @Pt.ProposedRating Red (cxh) 053 @Pt.AcceptedRating Red (cxh) 054 */ 055public class AddMissingParameter extends MoMLFilterSimple { 056 /** Identify classes that need to have parameter added. 057 * 058 * @param container The container for this attribute. 059 * in this method. 060 * @param element The XML element name. 061 * @param attributeName The name of the attribute. 062 * @param attributeValue The value of the attribute. 063 * @param xmlFile The file currently being parsed. 064 * @return the value of the attributeValue argument. 065 */ 066 @Override 067 public String filterAttributeValue(NamedObj container, String element, 068 String attributeName, String attributeValue, String xmlFile) { 069 // This method gets called many times by the MoMLParser, 070 // so we try to be smart about the number of comparisons 071 // and we try to group comparisons together so that we 072 // are not making the same comparison more than once. 073 if (attributeValue == null) { 074 // attributeValue == null is fairly common, so we check for 075 // that first 076 return null; 077 } 078 079 if (attributeName.equals("name")) { 080 // Save the name of the for later use if we see a "class" 081 _lastNameSeen = attributeValue; 082 083 if (_currentlyProcessingActorThatMayNeedUpdating) { 084 if (attributeValue.equals(_addProperty.propertyName)) { 085 _currentlyProcessingActorThatMayNeedUpdating = false; 086 _currentAttributeHasValue = false; 087 } else if (attributeValue 088 .equals(_addProperty.onlyAddIfPresent)) { 089 // We only add _editorFactory to parameters that 090 // have _locations 091 _currentAttributeHasValue = true; 092 } 093 } 094 } 095 096 // If you change this class, you should run before and after 097 // timing tests on large moml files, a good command to run 098 // is: 099 // $PTII/bin/ptolemy -test $PTII/ptolemy/domains/ct/demo/CarTracking/CarTracking.xml 100 // which will open up a large xml file and then close after 2 seconds. 101 if (attributeName.equals("class")) { 102 if (_namedObjsWithMissingProperties.containsKey(attributeValue)) { 103 _currentlyProcessingActorThatMayNeedUpdating = true; 104 105 //System.out.println("AddMissingParameter: " + _lastNameSeen + " " + attributeValue); 106 if (container != null) { 107 _currentActorFullName = container.getFullName() + "." 108 + _lastNameSeen; 109 } else { 110 _currentActorFullName = "." + _lastNameSeen; 111 } 112 _addProperty = _namedObjsWithMissingProperties 113 .get(attributeValue); 114 } else if (_currentlyProcessingActorThatMayNeedUpdating 115 && container != null 116 && !container.getFullName().equals(_currentActorFullName) 117 && !container.getFullName() 118 .startsWith(_currentActorFullName)) { 119 // We found another class in a different container 120 // while handling a class with port name changes, so 121 _currentlyProcessingActorThatMayNeedUpdating = false; 122 //System.out.println("AddMissingParameter: " + _lastNameSeen + " " + attributeValue + " setting to _currentlyProcessingActorThatMayNeedUpdating to false"); 123 _currentAttributeHasValue = false; 124 _addProperty = null; 125 } 126 } 127 128 return attributeValue; 129 } 130 131 /** Make modifications to the specified container, which is 132 * defined in a MoML element with the specified name. 133 * @param container The object created by this element. 134 * @param elementName The element name. 135 * @param currentCharData The character data, which appears 136 * only in the doc and configure elements 137 * @param xmlFile The file currently being parsed. 138 * @exception Exception if there is a problem substituting 139 * in the new value. 140 * @deprecated Use {@link #filterEndElement(NamedObj, String, StringBuffer, String, MoMLParser)} 141 * instead and pass a MoMLParser. 142 */ 143 @Deprecated 144 @Override 145 public void filterEndElement(NamedObj container, String elementName, 146 StringBuffer currentCharData, String xmlFile) throws Exception { 147 filterEndElement(container, elementName, currentCharData, xmlFile, 148 new MoMLParser()); 149 } 150 151 /** Make modifications to the specified container, which is 152 * defined in a MoML element with the specified name. 153 * @param container The object created by this element. 154 * @param elementName The element name. 155 * @param currentCharData The character data, which appears 156 * only in the doc and configure elements 157 * @param xmlFile The file currently being parsed. 158 * @param parser The parser in which MoML is optionally evaluated. 159 * @exception Exception if there is a problem substituting 160 * in the new value. 161 */ 162 @Override 163 public void filterEndElement(NamedObj container, String elementName, 164 StringBuffer currentCharData, String xmlFile, MoMLParser parser) 165 throws Exception { 166 // System.out.println("AddMissingParameter: filterEndElement: " 167 // + _currentlyProcessingActorThatMayNeedUpdating 168 // + " elementName: " + (elementName == null? "null" : elementName) 169 // + "\n container: " + (container == null ? "null" : container.getFullName()) 170 // + "\n currentActorFullName: " + _currentActorFullName); 171 172 if (!_currentlyProcessingActorThatMayNeedUpdating) { 173 return; 174 } else if (_addProperty != null 175 && (_addProperty.onlyAddIfPresent != null 176 && _currentAttributeHasValue 177 || _addProperty.onlyAddIfPresent == null) 178 && elementName != null && elementName.equals("property") 179 && container != null 180 && container.getFullName().equals(_currentActorFullName)) { 181 182 //System.out.println("AddMissingParameter: filterEndElement: Processing!!!"); 183 _currentlyProcessingActorThatMayNeedUpdating = false; 184 _currentAttributeHasValue = false; 185 186 // We use parse moml here so that we can avoid adding 187 // dependencies. For SDFIterations, we could just 188 // add an attribute, but this class should be 189 // extended to be more general. 190 191 // setContext calls parser.reset() 192 parser.setContext(container); 193 194 String moml = _addProperty.moml; 195 _addProperty = null; 196 197 //System.out.println("AddMissingParameter: filterEndElement: moml: " + moml); 198 try { 199 // Do not call parse(moml) here, since that method 200 // will fail if we are in an applet because it tries 201 // to read user.dir 202 parser.parse(null, moml); 203 MoMLParser.setModified(true); 204 } catch (Exception ex) { 205 throw new IllegalActionException(null, ex, 206 "Failed to parse\n" + moml); 207 } 208 } 209 } 210 211 /** Return a string that describes what the filter does. 212 * @return the description of the filter that ends with a newline. 213 */ 214 @Override 215 public String toString() { 216 StringBuffer results = new StringBuffer(getClass().getName() 217 + ": If a NamedObj is missing a property, then add it.\n" 218 + "Optionally, only add the property if another property, " 219 + "such as _location is present.\n" 220 + "Below are the property names, the optional property and the moml:\n"); 221 for (Map.Entry<String, AddProperty> entry : _namedObjsWithMissingProperties 222 .entrySet()) { 223 String namedObjName = entry.getKey(); 224 AddProperty addProperty = entry.getValue(); 225 results.append( 226 namedObjName + "\t -> " + addProperty.propertyName + "\t" 227 + (addProperty.onlyAddIfPresent == null ? "null" 228 : addProperty.onlyAddIfPresent) 229 + "\n\t" + addProperty.moml + "\n"); 230 } 231 return results.toString(); 232 } 233 234 /** A Structure that contains the property name, the moml and 235 * the optional property name that, if non-null, must be present. 236 */ 237 private static class AddProperty { 238 AddProperty(String propertyName, String moml, String onlyAddIfPresent) { 239 this.propertyName = propertyName; 240 this.moml = moml; 241 this.onlyAddIfPresent = onlyAddIfPresent; 242 } 243 244 /** The property name to be added if it is missing. */ 245 public String propertyName; 246 /** The moml to be added */ 247 public String moml; 248 /** Only add the parameter if the attribute has a parameter 249 * that has the value equal to the value of onlyAddIfPresent. 250 * If this field is null, then the parameter is always added 251 * if propertyName is not present. 252 */ 253 public String onlyAddIfPresent; 254 } 255 256 /////////////////////////////////////////////////////////////////// 257 //// private variables //// 258 259 // Map of NamedObject names to an object of 260 private static HashMap<String, AddProperty> _namedObjsWithMissingProperties; 261 262 // Cache of parameters name to be added. 263 private static AddProperty _addProperty; 264 265 // The the full name of the actor we are currently processing 266 private String _currentActorFullName; 267 268 // Set to true if the current attribute has a value that matches. 269 // This variable is used to determine whether we need to add an 270 // iteration 271 private boolean _currentAttributeHasValue = false; 272 273 // Set to true if we are currently processing an actor that may 274 // need an update, set to false when we are done. 275 private boolean _currentlyProcessingActorThatMayNeedUpdating = false; 276 277 // Last "name" value seen, for use if we see a "class". 278 private String _lastNameSeen; 279 280 static { 281 _namedObjsWithMissingProperties = new HashMap<String, AddProperty>(); 282 283 // SDFDirector 284 AddProperty sdfDirectorChanges = new AddProperty("iterations", 285 "<property " + "name=\"iterations\" " 286 + "class=\"ptolemy.data.expr.Parameter\" " 287 + "value=\"0\"/>", 288 null); 289 _namedObjsWithMissingProperties.put( 290 "ptolemy.domains.sdf.kernel.SDFDirector", sdfDirectorChanges); 291 292 // EditorFactory 293 AddProperty editorFactoryChanges = new AddProperty("_editorFactory", 294 "<property name=\"_editorFactory\"" 295 + " class=\"ptolemy.vergil.toolbox." 296 + "VisibleParameterEditorFactory\"/>", 297 "_location"); 298 299 _namedObjsWithMissingProperties.put("ptolemy.data.expr.Parameter", 300 editorFactoryChanges); 301 } 302}