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}