001/* A schedule element that contains a reference to a firing element.
002
003 Copyright (c) 2003-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.graph.sched;
029
030import java.util.ConcurrentModificationException;
031import java.util.Iterator;
032import java.util.LinkedList;
033import java.util.List;
034import java.util.Map;
035import java.util.NoSuchElementException;
036
037import ptolemy.kernel.util.InvalidStateException;
038
039///////////////////////////////////////////////////////////////////
040//// Firing
041
042/**
043 This class is a ScheduleElement that contains a reference to a
044 firing element.  The firingElement could be any Object.
045 This class is used together with the Schedule class to
046 construct a Schedule. The setFiringElement() method is used to create the
047 reference to a firing element. The getFiringElement() method will return a
048 reference to this firing element.
049 <p>
050
051 It is more efficient to use this class than to simply maintain a list of
052 firing elements since firing elements will often firing multiple times
053 consecutively.  Using
054 this class (and the Schedule data structure in general) greatly reduces the
055 memory requirements of most large schedules.
056
057 @author Shahrooz Shahparnia, Mingyung Ko,
058 University of Maryland at College Park based on a file by
059 Brian K. Vogel, Steve Neuendorffer
060 @version $Id$
061 @since Ptolemy II 4.0
062 @Pt.ProposedRating red (shahrooz)
063 @Pt.AcceptedRating red (ssb)
064 @see ptolemy.graph.sched.Firing
065 @see ptolemy.graph.sched.Schedule
066 @see ptolemy.graph.sched.ScheduleElement
067 */
068public class Firing extends ScheduleElement {
069    /** Construct a firing with a default iteration count equal to one
070     *  and with no parent schedule.
071     */
072    public Firing() {
073        super();
074    }
075
076    /** Construct a firing with a firingElement, an iteration count equal to one
077     *  and no parent schedule. A Firing constructed using this constructor,
078     *  using the setFiringElement() method, will only accept firing elements
079     *  with the same class type as the given firingElement, using the
080     *  setFiringElement() method.
081     *  @param firingElement The firing element in the firing.
082     */
083    public Firing(Object firingElement) {
084        super(firingElement.getClass());
085        _firingElement = firingElement;
086    }
087
088    /** Construct a firing with a given firing element type, an iteration
089     *  count equal to one and no parent schedule.
090     *  In a Firing constructed using this constructor, the
091     *  setFiringElement() method, will only accept firing elements
092     *  with the same given class type, using the setFiringElement() method.
093     *  @param firingElementClass The class of the firing element in the firing.
094     */
095    public Firing(Class firingElementClass) {
096        super(firingElementClass);
097    }
098
099    ///////////////////////////////////////////////////////////////////
100    ////                         public methods                    ////
101
102    /** Return the firing element invocation sequence of the schedule in the
103     *  form of a sequence of firing elements. For a valid schedule, all of the
104     *  lowest-level nodes should be an instance of Firing. If the
105     *  schedule is not valid, then the returned iterator will contain
106     *  null elements.
107     *  <p>
108     *  A runtime exception is thrown if the underlying schedule structure
109     *  is modified while the iterator is active.
110     *
111     *  @return An iterator over a sequence of firing elements.
112     *  @exception ConcurrentModificationException If the
113     *   underlying schedule structure is modified while the iterator
114     *   is active.
115     */
116    @Override
117    public Iterator firingElementIterator() {
118        return new FiringElementIterator();
119    }
120
121    /** Return the firing element invocation sequence in the form
122     *  of a sequence of firings.
123     *  Since this ScheduleElement is a Firing, the
124     *  iterator returned will contain exactly one Firing (this Firing).
125     *  <p>
126     *  A runtime exception is thrown if the
127     *  underlying schedule structure is modified while the iterator
128     *  is active.
129     *
130     *  @return An iterator over a sequence of firings.
131     *  @exception ConcurrentModificationException If the
132     *  underlying schedule structure is modified while the iterator
133     *  is active.
134     */
135    @Override
136    public Iterator firingIterator() {
137        // FIXME: a ConcurrentModificationException will not necessarily
138        // be thrown, see the failing tests.
139        if (_firing == null) {
140            _firing = new LinkedList();
141            _firing.add(this);
142        }
143
144        return _firing.iterator();
145    }
146
147    /** Get the firing element associated with this Firing. The
148     *  setFiringElement() method is used to set the firing element that this
149     *  method returns.
150     *  If setFiringElement() was never called, then throw an exception.
151     *
152     *  @return The actor associated with this Firing.
153     *  @see #setFiringElement(Object)
154     */
155    public Object getFiringElement() {
156        // FIXME: the exception is never thrown in the original version
157        return _firingElement;
158    }
159
160    /** Set the firing element associated with this firing. This firing element
161     *  will then be returned when the getFiringElement() method is invoked.
162     *  If this firing already contains a reference to a firing element,
163     *  then the reference will overwritten.
164     *
165     *  @param firingElement The firing element to associate with this firing.
166     *  @see #getFiringElement()
167     */
168    public void setFiringElement(Object firingElement) {
169        if (this.firingElementClass() != null) {
170            if (this.firingElementClass()
171                    .isAssignableFrom(firingElement.getClass())) {
172                _incrementVersion();
173                _firingElement = firingElement;
174
175                if (_firing != null) {
176                    _firing.clear();
177                    _firing.add(this);
178                }
179            } else {
180                throw new RuntimeException(
181                        "Attempt to add a non " + "authorized firing element");
182            }
183        } else {
184            _incrementVersion();
185            _firingElement = firingElement;
186
187            if (_firing != null) {
188                _firing.clear();
189                _firing.add(this);
190            }
191        }
192    }
193
194    /** Print the firing in a parenthesis style.
195     *
196     *  @param nameMap A mapping from firing element to its short name.
197     *  @param delimiter The delimiter between iteration count and iterand.
198     *  @return The parenthesis expression for this firing.
199     */
200    @Override
201    public String toParenthesisString(Map nameMap, String delimiter) {
202        String name = (String) nameMap.get(getFiringElement());
203        int iterations = getIterationCount();
204
205        if (iterations > 1) {
206            return "(" + iterations + delimiter + name + ")";
207        } else {
208            return name;
209        }
210    }
211
212    /** Return a string representation of this Firing.
213     *
214     *  @return Return a string representation of this Firing.
215     */
216    @Override
217    public String toString() {
218        String result = "Fire firing element " + _firingElement;
219
220        if (getIterationCount() > 1) {
221            result += " " + getIterationCount() + " times";
222        }
223
224        return result;
225    }
226
227    ///////////////////////////////////////////////////////////////////
228    ////                         inner classes                     ////
229
230    /** An adapter class for iterating over the elements of this
231     *  schedule. An exception is thrown if the schedule structure
232     *  changes while this iterator is active.
233     */
234    private class FiringElementIterator implements Iterator {
235        // As of 8/02, it seems like this inner class is not really
236        // used except by the test suite.
237
238        /** Construct a ScheduleIterator.
239         */
240        public FiringElementIterator() {
241            _startingVersion = _getVersion();
242            _currentElement = 0;
243        }
244
245        /** Return true if the iteration has more elements.
246         *  @exception ConcurrentModificationException If the schedule
247         *  data structure has changed since this iterator
248         *  was created.
249         *  @return True if the iterator has more elements.
250         */
251        @Override
252        public boolean hasNext() {
253            if (_startingVersion != _getVersion()) {
254                throw new ConcurrentModificationException(
255                        "Schedule structure changed while iterator is active.");
256            } else {
257                return _currentElement <= getIterationCount();
258            }
259        }
260
261        /** Return the next object in the iteration.
262         *
263         *  @exception InvalidStateException If the schedule
264         *  data structure has changed since this iterator
265         *  was created.
266         *  @return The next object in the iteration.
267         */
268        @Override
269        public Object next() throws NoSuchElementException {
270            if (!hasNext()) {
271                throw new NoSuchElementException("No element to return.");
272            } else if (_startingVersion != _getVersion()) {
273                throw new ConcurrentModificationException(
274                        "Schedule structure changed while iterator is active.");
275            } else {
276                _currentElement++;
277                return getFiringElement();
278            }
279        }
280
281        /** Throw an exception, since removal is not allowed. It really
282         *  doesn't make sense to remove an actor from an actor invocation
283         *  sequence anyway.
284         */
285        @Override
286        public void remove() {
287            throw new UnsupportedOperationException();
288        }
289
290        private long _startingVersion;
291
292        private int _currentElement;
293    }
294
295    ///////////////////////////////////////////////////////////////////
296    ////                         private variables                 ////
297    // The firing element associated with this firing.
298    private Object _firingElement;
299
300    // The list containing this firing as the only element.
301    private List _firing = null;
302}