001/* Update annotations 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.vergil.kernel.attributes; 029 030import ptolemy.kernel.util.Attribute; 031import ptolemy.kernel.util.Location; 032import ptolemy.kernel.util.NamedObj; 033import ptolemy.moml.MoMLParser; 034import ptolemy.moml.filter.MoMLFilterSimple; 035 036/////////////////////////////////////////////////////////////////// 037//// UpdateAnnotations 038 039/** 040 Update the annotations. 041 <p>When this class is registered with the MoMLParser.addMoMLFilter() 042 method, it will cause MoMLParser to add a update annotations from 043 the older style: 044 <pre> 045 <property name="annotation" class="ptolemy.kernel.util.Attribute"> 046 <property name="_hideName" class="ptolemy.kernel.util.SingletonAttribute"> 047 </property> 048 <property name="_iconDescription" class="ptolemy.kernel.util.SingletonConfigurableAttribute"> 049 <configure><svg><text x="20" y="20" style="font-size:14; font-family:SansSerif; fill:blue">Create a state machine here (and ports, if needed) and 050create refinements for the states. Each refinement needs a director. 051For hybrid system models, use the CTEmbeddedDirector.</text></svg></configure> 052 </property> 053 <property name="_location" class="ptolemy.kernel.util.Location" value="75.0, 65.0"> 054 </property> 055 <property name="_controllerFactory" class="ptolemy.vergil.basic.NodeControllerFactory"> 056 </property> 057 <property name="_editorFactory" class="ptolemy.vergil.toolbox.AnnotationEditorFactory"> 058 </property> 059 </property> 060 </pre> 061 to 062 <pre> 063 064 <property name="annotation" class="ptolemy.vergil.kernel.attributes.TextAttribute"> 065 <property name="text" class="ptolemy.kernel.util.StringAttribute" value="Create a state machine here (and ports, if needed) and&#10;create refinements for the states. Each refinement needs a director.&#10;For hybrid system models, use the CTEmbeddedDirector.</text></svg></configure> 066 </property> 067 <property name="_location" class="ptolemy.kernel.util.Location" value="[75.0, 65.0]"> 068 </property> 069 </property> 070 </pre> 071 072 @author Christopher Brooks 073 @version $Id$ 074 @since Ptolemy II 8.0 075 @Pt.ProposedRating Red (cxh) 076 @Pt.AcceptedRating Red (cxh) 077 */ 078public class UpdateAnnotations extends MoMLFilterSimple { 079 080 /** Update annotations by removing old annotations and replacing 081 * them with new annotation. If the attributeName is "name" and 082 * attributeValue begins with with "annotation", then replace the 083 * property with a TextAttribute 084 * 085 * @param container The container for this attribute. 086 * in this method. 087 * @param element The XML element name. 088 * @param attributeName The name of the attribute. 089 * @param attributeValue The value of the attribute. 090 * @param xmlFile The file currently being parsed. 091 * @return the value of the attributeValue argument. 092 */ 093 @Override 094 public String filterAttributeValue(NamedObj container, String element, 095 String attributeName, String attributeValue, String xmlFile) { 096 //System.out.println("filterAttributeValue: " + container + "\t" 097 // + attributeName + "\t" + attributeValue); 098 099 if (attributeValue == null) { 100 // attributeValue == null is fairly common, so we check for 101 // that first 102 return null; 103 } 104 105 if (attributeName.equals("name")) { 106 if (attributeValue.startsWith("annotation") 107 || attributeValue.contains("annotation") 108 && attributeValue.contains(":")) { 109 110 // We found a line like 111 // <property name="annotation1" 112 // class="ptolemy.kernel.util.Attribute"> 113 _currentlyProcessingAnnotation = true; 114 _currentAnnotationContainerFullName = container.getFullName(); 115 _currentAnnotationFullName = container.getFullName() + "." 116 + attributeValue; 117 _currentlyProcessingLocation = false; 118 } else { 119 if (_currentlyProcessingAnnotation) { 120 if (attributeValue.equals("_location")) { 121 // We are processing a location 122 _currentlyProcessingLocation = true; 123 } else { 124 // Saw another name, done processing location 125 _currentlyProcessingLocation = false; 126 } 127 } 128 } 129 } else if (_currentlyProcessingAnnotation) { 130 if (attributeName.equals("class") 131 && attributeValue.equals( 132 "ptolemy.vergil.kernel.attributes.TextAttribute") 133 && container.getFullName() 134 .equals(_currentAnnotationContainerFullName)) { 135 // We have an annotation, but it is a TextAttribute, so we are done. 136 _reset(); 137 return attributeValue; 138 } 139 if (_currentlyProcessingLocation && attributeName.equals("value")) { 140 // Found the location 141 _currentlyProcessingLocation = false; 142 } 143 } 144 if (_currentlyProcessingAnnotation) { 145 if (container != null) { 146 if (!container.getFullName() 147 .equals(_currentAnnotationFullName)) { 148 if (_currentAnnotationFullName == null 149 || (!_currentAnnotationFullName 150 .startsWith(container.getFullName()) 151 && !container.getFullName().startsWith( 152 _currentAnnotationFullName))) { 153 // We found another class in a different container 154 // while handling an annotation. 155 _reset(); 156 } 157 } 158 } 159 } 160 161 return attributeValue; 162 } 163 164 /** Make modifications to the specified container, which is 165 * defined in a MoML element with the specified name. 166 * @param container The object created by this element. 167 * @param elementName The element name. 168 * @param currentCharData The character data, which appears 169 * only in the doc and configure elements 170 * @param xmlFile The file currently being parsed. 171 * @exception Exception if there is a problem substituting 172 * in the new value. 173 */ 174 @Override 175 public void filterEndElement(NamedObj container, String elementName, 176 StringBuffer currentCharData, String xmlFile) throws Exception { 177 178 if (!_currentlyProcessingAnnotation) { 179 return; 180 } 181 182 // Useful for debugging 183 // System.out.println("filterEndElement: " + container + "\t" 184 // + elementName 185 // + " container.fn: " + container.getFullName() 186 // + " _cafn: " + _currentAnnotationFullName 187 // + " _cacfn: " + _currentAnnotationContainerFullName 188 // + "\n" + currentCharData); 189 190 // We have three cases: 191 // 1) We have a configure, so we create the TextAttribute and 192 // populate it. 193 // 2) We have a Location, so we possibly create the TextAttribute 194 // and we add a Location 195 // 3) We are at the end of the annotation, so we delete the 196 // old annotation 197 198 if (elementName.equals("configure")) { 199 // 1) We have a configure, so we create the TextAttribute and 200 // populate it. 201 Attribute currentAttribute = (Attribute) container; 202 NamedObj parentContainer = currentAttribute.getContainer(); 203 if (currentAttribute.getName().equals("_smallIconDescription")) { 204 // Skip the smallIconDescription it is probably -A- 205 return; 206 } 207 if (!(parentContainer instanceof Attribute)) { 208 // ptolemy.domains.modal.modal.ModalController cannot be cast to ptolemy.kernel.util.Attribute 209 return; 210 } 211 212 if (_textAttribute == null) { 213 //System.out.println("UpdateAnnotation: create TextAttribute 1"); 214 NamedObj grandparentContainer = currentAttribute.getContainer() 215 .getContainer(); 216 //((Attribute)parentContainer).setContainer(null); 217 218 // Use a new name instead of annotation and avoid the HideAnnotationNames filter 219 _textAttribute = new TextAttribute(grandparentContainer, 220 grandparentContainer.uniqueName("AnnotationUpdated")); 221 // grandparentContainer.uniqueName(_currentAnnotationName)); 222 } 223 224 // Clean up the character data: remove svg and text tags 225 String charData = currentCharData.toString().trim(); 226 if (charData.startsWith("<svg>")) { 227 charData = charData.substring(5).trim(); 228 } 229 if (charData.endsWith("</svg>")) { 230 charData = charData.substring(0, charData.length() - 6).trim(); 231 } 232 if (charData.endsWith("</text>")) { 233 charData = charData.substring(0, charData.length() - 7).trim(); 234 } 235 236 // Map colors 237 if (charData.contains(" fill:")) { 238 if (charData.contains(" fill:black")) { 239 _textAttribute.textColor 240 .setExpression("{0.0, 0.0, 0.0, 1.0}"); 241 } 242 if (charData.contains(" fill:darkgray") 243 || charData.contains(" fill:gray")) { 244 _textAttribute.textColor 245 .setExpression("{0.2, 0.2, 0.2, 1.0}"); 246 } 247 if (charData.contains(" fill:green")) { 248 _textAttribute.textColor 249 .setExpression("{0.0, 1.0, 0.0, 1.0}"); 250 } 251 if (charData.contains(" fill:red")) { 252 _textAttribute.textColor 253 .setExpression("{1.0, 0.0, 0.0, 1.0}"); 254 } 255 } 256 257 // Map font sizes 258 if (charData.contains("font-size:")) { 259 if (charData.contains("font-size:12")) { 260 _textAttribute.textSize.setExpression("12"); 261 } 262 if (charData.contains("font-size:16")) { 263 _textAttribute.textSize.setExpression("16"); 264 } 265 if (charData.contains("font-size:18")) { 266 _textAttribute.textSize.setExpression("18"); 267 } 268 } 269 270 charData = charData.replaceAll("<text.*[^>]>", ""); 271 //System.out.println("UpdateAnnotation: Setting TextAttribute"); 272 _textAttribute.text.setExpression(charData); 273 } 274 275 if (container instanceof Location) { 276 // 2) We have a Location, so we possibly create the TextAttribute 277 // and we add a Location. 278 Attribute currentAttribute = (Attribute) container; 279 if (_textAttribute == null) { 280 //System.out.println("UpdateAnnotation: create TextAttribute 2"); 281 //NamedObj parentContainer = currentAttribute.getContainer(); 282 NamedObj grandparentContainer = currentAttribute.getContainer() 283 .getContainer(); 284 //((Attribute)parentContainer).setContainer(null); 285 // Use a new name instead of annotation and avoid the HideAnnotationNames filter 286 _textAttribute = new TextAttribute(grandparentContainer, 287 grandparentContainer.uniqueName("AnnotationUpdated")); 288 } 289 Location location = new Location(_textAttribute, "_location"); 290 291 Location oldLocation = (Location) container; 292 oldLocation.validate(); 293 //System.out.println("UpdateAnnotation: setting Location " + oldLocation.getExpression()); 294 double[] xyLocation = oldLocation.getLocation(); 295 296 xyLocation[0] += 15.0; 297 location.setLocation(xyLocation); 298 299 location.validate(); 300 } 301 302 if (container != null 303 && container.getFullName().equals(_currentAnnotationFullName) 304 && _textAttribute != null) { 305 // 3) We are at the end of the annotation, so we delete the 306 // old annotation by setting its container to null. 307 //System.out.println("UpdateAnnotation: closing up " + _textAttribute.getContainer().getFullName() + "\n" + _textAttribute.exportMoML()); 308 //NamedObj top = _textAttribute.toplevel(); 309 Attribute currentAttribute = (Attribute) container; 310 //String name = currentAttribute.getName(); 311 currentAttribute.setContainer(null); 312 MoMLParser.setModified(true); 313 _reset(); 314 //System.out.println("UpdateAnnotation: after reset" + top.exportMoML()); 315 } 316 } 317 318 /** Return a string that describes what the filter does. 319 * @return the description of the filter that ends with a newline. 320 */ 321 @Override 322 public String toString() { 323 return getClass().getName() + ": Update annotation to new style\n"; 324 } 325 326 /////////////////////////////////////////////////////////////////// 327 //// private variables //// 328 329 /** Reset the internal state of the filter. */ 330 private void _reset() { 331 _currentlyProcessingAnnotation = false; 332 _currentAnnotationContainerFullName = null; 333 _currentAnnotationFullName = null; 334 _currentlyProcessingLocation = false; 335 _textAttribute = null; 336 } 337 338 /////////////////////////////////////////////////////////////////// 339 //// private variables //// 340 // True if we are currently processing an annotation. 341 private boolean _currentlyProcessingAnnotation = false; 342 343 // The the full name of the container of the annotation we are 344 // currently processing. 345 private String _currentAnnotationContainerFullName; 346 347 // The the full name of the annotation we are currently processing. 348 private String _currentAnnotationFullName; 349 350 // True if we are currently processing a location inside an annotation. 351 private boolean _currentlyProcessingLocation = false; 352 353 // The TextAttribute that is being created. 354 private TextAttribute _textAttribute; 355}