001/* A schedule element that contains a reference to an actor.
002
003 Copyright (c) 1998-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.actor.sched;
029
030import java.util.Collections;
031import java.util.ConcurrentModificationException;
032import java.util.Iterator;
033import java.util.List;
034import java.util.NoSuchElementException;
035
036import ptolemy.actor.Actor;
037import ptolemy.kernel.util.InvalidStateException;
038
039///////////////////////////////////////////////////////////////////
040//// Firing
041
042/**
043 This class is a schedule element that contains a reference to an
044 actor and an iteration count.  This class is used together
045 with the Schedule class to construct a static schedule.
046 This class is used to represent a single actor repeated some
047 number of times, whereas Schedule is used for multi-actor schedules.
048 The setActor() method is used to
049 create the reference to an actor, and the setIterationCount() method
050 to set the iteration count. The getActor() method will return
051 a reference to this actor, and getIterationCount() will return
052 the iteration count. <p>
053
054 It is more efficient to use this class than to simply maintain a list of
055 actors since actors will often fire multiple times consecutively.  Using
056 this class (and the Schedule data structure in general) greatly reduces the
057 memory requirements of most large schedules.
058
059 @author Brian K. Vogel, Steve Neuendorffer
060 @version $Id$
061 @since Ptolemy II 1.0
062 @Pt.ProposedRating Green (vogel)
063 @Pt.AcceptedRating Yellow (chf)
064 @see ptolemy.actor.sched.Schedule
065 @see ptolemy.actor.sched.ScheduleElement
066 */
067public class Firing extends ScheduleElement {
068    /** Construct a firing with a default iteration count equal to one
069     *  and with no parent schedule.
070     */
071    public Firing() {
072        super();
073    }
074
075    /** Construct a firing with a actor, an iteration count equal to one
076     *  and no parent schedule.
077     *  @param actor The actor in the firing.
078     */
079    public Firing(Actor actor) {
080        super();
081        _actor = actor;
082    }
083
084    ///////////////////////////////////////////////////////////////////
085    ////                         public methods                    ////
086
087    /** Return the actor invocation sequence of the schedule in the
088     *  form of a sequence of actors. For a valid schedule, all of the
089     *  lowest-level nodes should be an instance of Actor. If the
090     *  schedule is not valid, then the returned iterator will contain
091     *  null elements.
092     *  <p>
093     *  A runtime exception is thrown if the
094     *  underlying schedule structure is modified while the iterator
095     *  is active.
096     *
097     *  @return An iterator over a sequence of actors.
098     *  @exception ConcurrentModificationException If the
099     *   underlying schedule structure is modified while the iterator
100     *   is active.
101     */
102    @Override
103    public Iterator actorIterator() {
104        return new ActorIterator(getIterationCount());
105    }
106
107    /** Return the actor invocation sequence in the form
108     *  of a sequence of firings.
109     *  Since this ScheduleElement is a Firing, which
110     *  represents an Actor and an iteration count. the
111     *  iterator returned will contain exactly one Firing (this Firing).
112     *  <p>
113     *  A runtime exception is thrown if the
114     *  underlying schedule structure is modified while the iterator
115     *  is active.
116     *
117     *  @return An iterator over a sequence of firings.
118     *  @exception ConcurrentModificationException If the
119     *   underlying schedule structure is modified while the iterator
120     *   is active.
121     */
122    @Override
123    public Iterator firingIterator() {
124        // FIXME: a ConcurrentModificationException will not necessarily
125        // be thrown, see the failing tests.
126        if (_firing == null) {
127            _firing = Collections.singletonList(this);
128        }
129
130        return _firing.iterator();
131    }
132
133    /** Get the actor associated with this Firing. The setActor()
134     *  method is used to set the actor that this method returns.
135     *  If setActor() was never called, then throw an exception.
136     *
137     *  @return The actor associated with this Firing.
138     *  @see #setActor(Actor)
139     */
140    public Actor getActor() {
141        return _actor;
142    }
143
144    /** Set the actor associated with this firing. This actor will
145     *  then be returned when the getActor() method is invoked. If this
146     *  firing already contains a reference to an actor, then the
147     *  reference will overwritten.
148     *
149     *  @param actor The actor to associate with this firing.
150     *  @see #getActor()
151     */
152    public void setActor(Actor actor) {
153        _incrementVersion();
154        _actor = actor;
155    }
156
157    /**
158     * Output a string representation of this Firing.
159     */
160    @Override
161    public String toString() {
162        String result = "Fire Actor " + _actor;
163        int iterationCount = getIterationCount();
164
165        if (iterationCount > 1) {
166            result += " " + iterationCount + " times";
167        }
168
169        return result;
170    }
171
172    ///////////////////////////////////////////////////////////////////
173    ////                         inner classes                     ////
174
175    /** An adapter class for iterating over the elements of this
176     *  schedule. An exception is thrown if the schedule structure
177     *  changes while this iterator is active.
178     */
179    private class ActorIterator implements Iterator {
180        // As of 8/02, it seems like this inner class is not really
181        // used except by the test suite.
182
183        /** Construct a ScheduleIterator.
184         */
185        public ActorIterator(int iterationCount) {
186            _startingVersion = _getVersion();
187            _currentElement = 0;
188            _iterationCount = iterationCount;
189        }
190
191        /** Return true if the iteration has more elements.
192         *
193         *  @exception ConcurrentModificationException If the schedule
194         *   data structure has changed since this iterator
195         *   was created.
196         *  @return true if the iterator has more elements.
197         */
198        @Override
199        public boolean hasNext() {
200            if (_startingVersion != _getVersion()) {
201                throw new ConcurrentModificationException(
202                        "Schedule structure changed while iterator is active.");
203            } else {
204                return _currentElement <= _iterationCount;
205            }
206        }
207
208        /** Return the next object in the iteration.
209         *
210         *  @exception InvalidStateException If the schedule
211         *   data structure has changed since this iterator
212         *   was created.
213         *  @return the next object in the iteration.
214         */
215        @Override
216        public Object next() throws NoSuchElementException {
217            if (!hasNext()) {
218                throw new NoSuchElementException("No element to return.");
219            } else {
220                _currentElement++;
221                return getActor();
222            }
223        }
224
225        /** Throw an exception, since removal is not allowed. It really
226         *  doesn't make sense to remove an actor from an actor invocation
227         *  sequence anyway.
228         */
229        @Override
230        public void remove() {
231            throw new UnsupportedOperationException();
232        }
233
234        private long _startingVersion;
235
236        private int _currentElement;
237
238        private int _iterationCount;
239    }
240
241    ///////////////////////////////////////////////////////////////////
242    ////                         private variables                 ////
243    // The actor associated with this firing.
244    private Actor _actor;
245
246    // The list containing this firing as the only element.
247    private List _firing = null;
248}