001/** 002 A filter for backward compatibility with 7.2.devel or earlier models for width inference. 003 004 Copyright (c) 2008-2016 The Regents of the University of California. 005 All rights reserved. 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the above 009 copyright notice and the following two paragraphs appear in all copies 010 of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 ENHANCEMENTS, OR MODIFICATIONS. 024 025 PT_COPYRIGHT_VERSION_2 026 COPYRIGHTENDKEY 027 028 */ 029package ptolemy.moml.filter; 030 031import java.util.HashMap; 032import java.util.Map; 033 034import ptolemy.actor.IORelation; 035import ptolemy.kernel.attributes.VersionAttribute; 036import ptolemy.kernel.util.Attribute; 037import ptolemy.kernel.util.IllegalActionException; 038import ptolemy.kernel.util.NamedObj; 039import ptolemy.moml.MoMLParser; 040 041/////////////////////////////////////////////////////////////////// 042//// RelationWidthChanges 043 044/** When this class is registered with the MoMLParser.setMoMLFilter() 045 method, it will cause MoMLParser to filter so that models from 046 earlier releases will run in the current release. 047 This class will filter for relations that have a width equal to zero 048 for Ptolemy versions 7.2.devel or older. The width value will be changed 049 from 0 to -1, which is the new default for width inference. 050 If the width has not been specified for a models with Ptolemy version 051 less than 7.2.devel, the width will be changed to 1. This because 1 052 used to be the default value, while now -1 has become the default. 053 054 @author Bert Rodiers 055 @version $Id$ 056 @since Ptolemy II 8.0 057 @Pt.ProposedRating Red (rodiers) 058 @Pt.AcceptedRating Red (rodiers) 059 */ 060public class RelationWidthChanges extends MoMLFilterSimple { 061 062 /////////////////////////////////////////////////////////////////// 063 //// public methods //// 064 065 /** Filter relations widths and change 0 to -1. 066 * @param container The container for XML element. 067 * @param element The XML element name. 068 * @param attributeName The name of the attribute. 069 * @param attributeValue The value of the attribute. 070 * @param xmlFile The file currently being parsed. 071 * @return A new value for the attribute, or the same value 072 * to leave it unchanged, or null to cause the current element 073 * to be ignored (unless the attributeValue argument is null). 074 */ 075 @Override 076 public String filterAttributeValue(NamedObj container, String element, 077 String attributeName, String attributeValue, String xmlFile) { 078 079 // This method gets called many times by the MoMLParser, 080 // so we try to be smart about the number of comparisons 081 // and we try to group comparisons together so that we 082 // are not making the same comparison more than once. 083 if (attributeValue == null) { 084 // attributeValue == null is fairly common, so we check for 085 // that first 086 return null; 087 } 088 089 if (xmlFile != null) { 090 Boolean changedNeeded = _changesNeededForXmlFile.get(xmlFile); 091 if (changedNeeded != null && !changedNeeded) { 092 return attributeValue; 093 } 094 } 095 096 if (_currentlyProcessingRelation) { 097 if (_currentlyProcessingWidth) { 098 if (attributeName.equals("value")) { 099 _currentlyProcessingRelation = false; 100 _currentlyProcessingWidth = false; 101 102 if (_changedNeeded(container, xmlFile) 103 && attributeValue.equals("0")) { 104 MoMLParser.setModified(true); 105 return Integer.toString(IORelation.WIDTH_TO_INFER); 106 } 107 } 108 } else { 109 if (attributeValue.equals("width") 110 && element.equals("property")) { 111 _currentlyProcessingWidth = true; 112 } 113 } 114 } else if (element != null && element.equals("relation") 115 && attributeName != null && attributeName.equals("class")) { 116 _currentlyProcessingRelation = true; 117 } 118 return attributeValue; 119 } 120 121 /** Make modifications to the specified container, which is 122 * defined in a MoML element with the specified name. 123 * This method is called when an end element in MoML is 124 * encountered. A typical use of this method is to make 125 * some modification to the object (the container) that 126 * was constructed. 127 * <p> 128 * If an implementor makes changes to the specified container, 129 * then it should call MoMLParser.setModified(true) which indicates 130 * that the model was modified so that the user can optionally 131 * save the modified model. 132 * 133 * @param container The object defined by the element that this 134 * is the end of. 135 * @param elementName The element name. 136 * @param currentCharData The character data, which appears 137 * only in the doc and configure elements 138 * @param xmlFile The file currently being parsed. 139 * @exception Exception If there is a problem modifying the 140 * specified container. 141 */ 142 @Override 143 public void filterEndElement(NamedObj container, String elementName, 144 StringBuffer currentCharData, String xmlFile) throws Exception { 145 if (container instanceof VersionAttribute) { 146 VersionAttribute version = (VersionAttribute) container; 147 try { 148 if (xmlFile != null) { 149 _changesNeededForXmlFile.put(xmlFile, version 150 .isLessThan(new VersionAttribute("7.2.devel"))); 151 } 152 } catch (IllegalActionException e) { 153 // We don't expect that this fails. 154 throw new IllegalStateException(e); 155 } 156 } else if (container instanceof IORelation) { 157 if (_currentlyProcessingRelation && !_currentlyProcessingWidth) { 158 // We have processed a relation, but we did not encounter 159 // the width. This means that the default one is being used. 160 // The default one was 1, but this has been changed to 161 // IORelation.WIDTH_TO_INFER. If we encounter this case with 162 // a Ptolemy model before 7.2 we need to update the width of 163 // the relation to 1 to not change the user's model. 164 165 if (_changedNeeded(container, xmlFile)) { 166 IORelation relation = (IORelation) container; 167 relation.setWidth(1); 168 relation.width.propagateValue(); 169 MoMLParser.setModified(true); 170 } 171 } 172 _currentlyProcessingRelation = false; 173 _currentlyProcessingWidth = false; 174 } 175 } 176 177 /** Return a string that describes what the filter does. 178 * @return A description of the filter (ending with a newline). 179 */ 180 @Override 181 public String toString() { 182 return Integer.toHexString(hashCode()); 183 } 184 185 /////////////////////////////////////////////////////////////////// 186 //// private methods //// 187 188 /** Return whether changes are necessary. 189 * This is only the case for models older than version 7.2.devel 190 * @param container The container. 191 * @param xmlFile The xmlFile 192 * @return True when changes are necessary. 193 */ 194 private boolean _changedNeeded(NamedObj container, String xmlFile) { 195 // First Check whether we already have the version 196 Boolean changesNeeded = xmlFile != null 197 ? _changesNeededForXmlFile.get(xmlFile) 198 : null; 199 if (changesNeeded != null && changesNeeded) { 200 return _changesNeededForXmlFile.get(xmlFile); 201 } else { 202 // Retrieve the version number. This is only available on the toplevel. 203 NamedObj toplevel = container; 204 NamedObj parent = toplevel.getContainer(); 205 206 while (parent != null) { 207 toplevel = parent; 208 parent = toplevel.getContainer(); 209 } 210 Attribute version = toplevel.getAttribute("_createdBy"); 211 if (version != null) { 212 try { 213 return ((VersionAttribute) version) 214 .isLessThan(new VersionAttribute("7.2.devel")); 215 } catch (IllegalActionException e) { 216 } 217 } else { 218 // If there is no _createdBy attribute, then this might be a 219 // copy and paste, in which case we assume we are copying 220 // from the current version. Note that this might not 221 // be always be true, but it is more likely that we are copying 222 // from a version recent version than a pre 7.2.devel version. 223 // See http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4804 224 return false; 225 } 226 } 227 return true; 228 } 229 230 /////////////////////////////////////////////////////////////////// 231 //// private variables //// 232 233 /**A flag that specifies whether we are currently processing a relation*/ 234 private boolean _currentlyProcessingRelation = false; 235 236 /**A flag that specifies whether we are currently processing the width of a relation*/ 237 private boolean _currentlyProcessingWidth = false; 238 239 /**A flag that specifies whether changes might be needed for the model with a certain xmlPath. 240 * This is only the case for models older than version 7.2.devel 241 */ 242 private Map<String, Boolean> _changesNeededForXmlFile = new HashMap<String, Boolean>(); 243}