001/* A GUI widget for configuring breakpoints.
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.vergil.debugger;
029
030import javax.swing.BoxLayout;
031
032import ptolemy.actor.Actor;
033import ptolemy.actor.Director;
034import ptolemy.actor.FiringEvent;
035import ptolemy.actor.FiringEvent.FiringEventType;
036import ptolemy.gui.Query;
037import ptolemy.kernel.Entity;
038import ptolemy.kernel.util.ChangeListener;
039import ptolemy.kernel.util.ChangeRequest;
040import ptolemy.kernel.util.InternalErrorException;
041import ptolemy.moml.MoMLChangeRequest;
042import ptolemy.vergil.basic.BasicGraphController;
043
044///////////////////////////////////////////////////////////////////
045//// BreakpointConfigurer
046
047/**
048 A GUI widget for configuring breakpoints.  This class is an editor to
049 configure the breakpoints of an actor in an SDF model.
050 The user can set breakpoints before or after iterate().
051
052 <p>There is further documentation in the
053 <a href="package-summary.html">package summary</a>.
054
055 @see ptolemy.actor.gui.PortConfigurerDialog
056
057 @author Elaine Cheong, Contributor: Christopher Brooks
058 @version $Id$
059 @since Ptolemy II 2.1
060 @Pt.ProposedRating Red (celaine)
061 @Pt.AcceptedRating Red (celaine)
062 */
063@SuppressWarnings("serial")
064public class BreakpointConfigurer extends Query implements ChangeListener {
065    /** Construct a breakpoint configurer for the specified entity.
066     *  @param object The entity to configure.
067     *  @param graphController The associated graph controller for the object.
068     */
069    public BreakpointConfigurer(Entity object,
070            BasicGraphController graphController) {
071        super();
072
073        // FIXME: Perhaps this dialog should have a help button?
074        // The text in $PTII/doc/coding/debugging.htm could be used.
075        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
076        setTextWidth(15);
077
078        // Make sure the object we are trying to set a breakpoint on
079        // is an Actor.
080        if (object instanceof Actor) {
081            _actor = (Actor) object;
082        } else {
083            // This should not be thrown because the context menu
084            // (BreakpointDialogFactory) would not have allowed the
085            // breakpoint option for non-Actor objects.
086            throw new InternalErrorException(
087                    "Object selected is not an actor.");
088        }
089
090        // Save the GraphController associated with _actor.
091        _graphController = graphController;
092
093        // Get the director associated with _actor.
094        Director director = _actor.getExecutiveDirector();
095
096        if (director == null) {
097            // This should not be thrown because the context menu
098            // (BreakpointDialogFactory) would not have allowed the
099            // breakpoint option for actors without a director.
100            throw new InternalErrorException(
101                    "No director associated with this actor.");
102        } else {
103            // FIXME: Currently, it seems that this facility
104            // only works in SDF.
105            // Perhaps this constructor should throw IllegalActionException.
106            if (_sdfDirectorClass == null) {
107                try {
108                    _sdfDirectorClass = Class
109                            .forName("ptolemy.domains.sdf.kernel.SDFDirector");
110                } catch (Throwable throwable) {
111                    throw new InternalErrorException(object, throwable,
112                            "The breakpoint facility only works with "
113                                    + "that are instances of SDFDirector.  The "
114                                    + "SDFDirector was not found.");
115                }
116            }
117
118            if (!_sdfDirectorClass.isInstance(director)) {
119                throw new InternalErrorException(director, null,
120                        "The breakpoint facility only works with directors "
121                                + "that are instances of SDFDirector.  The director "
122                                + "of this model is a '" + director + "'.");
123            }
124
125            // See if the director already has a DebugController.
126            DebugController debugController = (DebugController) director
127                    .getAttribute(_DEBUGCONTROLLER);
128
129            // See if the DebugController has a DebugProfile for this
130            // actor.  Make a new DebugProfile if one does not already
131            // exist.
132            _actorProfile = null;
133
134            if (debugController != null) {
135                _actorProfile = debugController.getDebugProfile(_actor);
136            }
137
138            if (_actorProfile == null) {
139                _actorProfile = new DebugProfile(_graphController);
140            }
141
142            // Generate checkbox entries in dialog box.
143            for (int i = 0; i < _firingEventTypes.length; i++) {
144                if (_actorProfile.isListening(_firingEventTypes[i])) {
145                    addCheckBox(_firingEventTypeLabels[i],
146                            _firingEventTypeLabels[i], true);
147                } else {
148                    addCheckBox(_firingEventTypeLabels[i],
149                            _firingEventTypeLabels[i], false);
150                }
151            }
152        }
153    }
154
155    ///////////////////////////////////////////////////////////////////
156    ////                         public methods                    ////
157
158    /** Set up and save the new breakpoint configuration for this actor.
159     */
160    public void apply() {
161        boolean breakpointsSelected = false;
162
163        // Make a new DebugProfile.
164        _actorProfile = new DebugProfile(_graphController);
165
166        for (int i = 0; i < _firingEventTypes.length; i++) {
167            // Configure the DebugProfile with the selected FiringEventTypes.
168            if (getBooleanValue(_firingEventTypeLabels[i])) {
169                _actorProfile.listenForEvent(_firingEventTypes[i]);
170                breakpointsSelected = true;
171            } else {
172                _actorProfile.unlistenForEvent(_firingEventTypes[i]);
173            }
174        }
175
176        // The director should not be null because we already checked
177        // in the constructor.
178        Director director = _actor.getExecutiveDirector();
179        DebugController debugController = (DebugController) director
180                .getAttribute(_DEBUGCONTROLLER);
181
182        // If some breakpoints were selected
183        if (breakpointsSelected) {
184            if (debugController != null) {
185                // Add this actor to the set of objects being debugged.
186                debugController.putDebugProfile(_actor, _actorProfile);
187            } else {
188                // If the director does not already have a
189                // DebugController, create one.
190                String moml = "<property name=\"" + _DEBUGCONTROLLER
191                        + "\" class=\"ptolemy.vergil.debugger.DebugController\"/>";
192                ChangeRequest request = new MoMLChangeRequest(this, // originator
193                        director, // context
194                        moml);
195                request.addChangeListener(this);
196                director.requestChange(request);
197            }
198        } else {
199            // If BreakpointConfigurerDialog()._handlClosing()
200            // calls this in appropriately, then debugController might be
201            // null.
202            if (debugController != null) {
203                // Remove profile if there are no longer any
204                // breakpoints selected for this _actor.
205                debugController.removeDebugProfile(_actor);
206            }
207
208            // FIXME: removeDebugListener if no more actors have breakpoints.
209        }
210    }
211
212    /** React to a change request has been successfully executed.
213     *  This method is called after a change request
214     *  has been executed successfully.
215     *  @param change The change that has been executed, or null if
216     *   the change was not done via a ChangeRequest.
217     */
218    @Override
219    public void changeExecuted(ChangeRequest change) {
220        Director director = _actor.getExecutiveDirector();
221        // The DebugController should not be null since the change
222        // request should have added the DebugController to the
223        // director.
224        DebugController debugController = (DebugController) director
225                .getAttribute(_DEBUGCONTROLLER);
226
227        // Register a new DebugController with the director.
228        director.addDebugListener(debugController);
229
230        // Add this actor to the set of objects being debugged.
231        debugController.putDebugProfile(_actor, _actorProfile);
232
233        director.removeChangeListener(this);
234    }
235
236    /** React to a change request has resulted in an exception.
237     *  This method is called after a change request was executed,
238     *  but during the execution an exception was thrown.
239     *  @param change The change that was attempted or null if
240     *   the change was not done via a ChangeRequest.
241     *  @param exception The exception that resulted.
242     */
243    @Override
244    public void changeFailed(ChangeRequest change, Exception exception) {
245        throw new InternalErrorException(
246                "Could not add DebugController to the director");
247    }
248
249    ///////////////////////////////////////////////////////////////////
250    ////                         protected variables               ////
251
252    /** Firing event type labels.
253     * To add firing events for debugging, you must make changes in 2
254     * places in this file: _firingEventTypeLabels, _firingEventTypes
255     * Labels of FiringEventTypes to show in the dialog box.
256     */
257    protected static String[] _firingEventTypeLabels = {
258
259            // FIXME: Only BEFORE_ITERATE and AFTER_ITERATE work with SDF
260            //"before prefire",
261            //"after prefire",
262            //"before fire",
263            //"after fire",
264            //"before postfire",
265            //"after postfire",
266            "before iterate", "after iterate" };
267
268    /** FiringEventTypes that the user can set breakpoints on. */
269    protected static FiringEventType[] _firingEventTypes = {
270
271            // FIXME: Only BEFORE_ITERATE and AFTER_ITERATE work with SDF
272            //FiringEvent.BEFORE_PREFIRE,
273            //FiringEvent.AFTER_PREFIRE,
274            //FiringEvent.BEFORE_FIRE,
275            //FiringEvent.AFTER_FIRE,
276            //FiringEvent.BEFORE_POSTFIRE,
277            //FiringEvent.AFTER_POSTFIRE,
278            FiringEvent.BEFORE_ITERATE, FiringEvent.AFTER_ITERATE };
279
280    ///////////////////////////////////////////////////////////////////
281    ////                         private variables                 ////
282    // Name of the DebugController to attach to the director.
283    private static String _DEBUGCONTROLLER = "_DebugController";
284
285    // The object that this configurer configures.
286    private Actor _actor;
287
288    // DebugProfile associated with _actor.
289    private DebugProfile _actorProfile;
290
291    // The GraphController associated with _actor.
292    private BasicGraphController _graphController;
293
294    // The class of ptolemy.domains.sdf.Director
295    private static Class _sdfDirectorClass = null;
296}