001/* An execution listener that suspends execution based on breakpoints.
002
003 Copyright (c) 1999-2016 The Regents of the University of California.
004 All rights reserved.  Permission is hereby granted, without written
005 agreement and without license or royalty fees, to use, copy, modify,
006 and distribute this software and its documentation for any purpose,
007 provided that the above copyright notice and the following two
008 paragraphs appear in all copies of this software.
009
010 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
011 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
012 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
013 IF THE UNIVERSITY OF CALIFORNIA HAVE BEEN ADVISED OF THE POSSIBILITY
014 OF SUCH DAMAGE.
015
016 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIM ANY WARRANTIES,
017 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
018 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
019 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
020 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
021 UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
022
023 PT_COPYRIGHT_VERSION_2
024 COPYRIGHTENDKEY
025
026 */
027package ptolemy.vergil.debugger;
028
029import java.util.Hashtable;
030
031import javax.swing.SwingUtilities;
032
033import diva.canvas.Figure;
034import ptolemy.actor.Actor;
035import ptolemy.actor.Executable;
036import ptolemy.actor.FiringEvent;
037import ptolemy.actor.Manager;
038import ptolemy.kernel.util.DebugEvent;
039import ptolemy.kernel.util.IllegalActionException;
040import ptolemy.kernel.util.NameDuplicationException;
041import ptolemy.kernel.util.NamedObj;
042import ptolemy.kernel.util.SingletonConfigurableAttribute;
043import ptolemy.vergil.basic.AbstractBasicGraphModel;
044import ptolemy.vergil.basic.BasicGraphController;
045import ptolemy.vergil.kernel.DebugRenderer;
046
047///////////////////////////////////////////////////////////////////
048//// DebugController
049
050/**
051 An execution listener that suspends execution based on breakpoints.
052 Instances of this class should be contained by a director.  This class
053 keeps a DebugProfile for each actor that belongs to that director and
054 is being debugged. This attribute is not persistent by default.
055
056 @see DebugProfile
057
058 @author Elaine Cheong
059 @version $Id$
060 @since Ptolemy II 2.0
061 @Pt.ProposedRating Red (celaine)
062 @Pt.AcceptedRating Red (celaine)
063 */
064public class DebugController extends SingletonConfigurableAttribute {
065    /** Construct a debug listener with the given container and name.
066     *  @param container The container.
067     *  @param name The name.
068     *  @exception IllegalActionException If the container does not accept
069     *   this entity (this should not occur).
070     *  @exception NameDuplicationException If the name coincides with an
071     *   attribute already in the container.
072     */
073    public DebugController(NamedObj container, String name)
074            throws IllegalActionException, NameDuplicationException {
075        super(container, name);
076        _toDebug = new Hashtable();
077        setPersistent(false);
078    }
079
080    ///////////////////////////////////////////////////////////////////
081    ////                         public methods                    ////
082
083    /** Clear the set of actors that are being debugged.
084     */
085    public void clear() {
086        _toDebug.clear();
087    }
088
089    /** Respond to all FiringEvents.  If the DebugController has a
090     *  DebugProfile containing a matching FiringEvent, then this
091     *  method highlights the actor and invokes pauseOnBreakpoint() on the
092     *  manager.
093     *  @see ptolemy.actor.Manager#pauseOnBreakpoint
094     *
095     *  This is similar to doing animation.
096     *  @see ptolemy.vergil.actor.ActorViewerGraphController#event
097     *
098     *  @param debugEvent The debug event.
099     */
100    @Override
101    public void event(DebugEvent debugEvent) {
102        // FIXME: this method is called every time the director gets a
103        // firing event for any actor...is this ok?
104        // Ignore debug events that aren't firing events.
105        if (debugEvent instanceof FiringEvent) {
106            FiringEvent event = (FiringEvent) debugEvent;
107
108            NamedObj objToHighlight = (NamedObj) event.getActor();
109
110            if (_toDebug.containsKey(objToHighlight)) {
111                // The actor associated with this firing event is in
112                // the set of actors to be debugged.
113                // If the object is not contained by the associated
114                // composite, then find an object above it in the hierarchy
115                // that is.
116                DebugProfile debugProfile = getDebugProfile(
117                        (Executable) objToHighlight);
118                BasicGraphController graphController = debugProfile
119                        .getGraphController();
120                AbstractBasicGraphModel graphModel = (AbstractBasicGraphModel) graphController
121                        .getGraphModel();
122                NamedObj toplevel = graphModel.getPtolemyModel();
123
124                while (objToHighlight != null
125                        && objToHighlight.getContainer() != toplevel) {
126                    objToHighlight = objToHighlight.getContainer();
127                }
128
129                if (objToHighlight == null) {
130                    return;
131                }
132
133                Object location = objToHighlight.getAttribute("_location");
134
135                if (location != null) {
136                    Figure figure = graphController.getFigure(location);
137
138                    if (figure != null) {
139                        // If the user has chosen to break on one of
140                        // the firing events, highlight the actor and
141                        // wait for the user to press the Resume
142                        // button.
143                        if (debugProfile.isListening(event.getType())) {
144                            String message = objToHighlight.getName() + " "
145                                    + event.getType().getName();
146                            Manager manager = ((Actor) objToHighlight)
147                                    .getManager();
148                            render(figure, manager, message);
149                        }
150                    }
151                }
152            }
153        }
154    }
155
156    /** Get the profile for an actor that is being debugged.
157     *  @param actor The actor for which to retrieve the profile.
158     *  @return The profile for the actor.
159     */
160    public DebugProfile getDebugProfile(Executable actor) {
161        return (DebugProfile) _toDebug.get(actor);
162    }
163
164    /** Determine whether debugging is enabled on the set of actors.
165     *  @return True if debugging is enabled.
166     */
167    public boolean isEnabled() {
168        // FIXME: not implemented yet
169        return false;
170    }
171
172    /** React to a debug message from the director that we are
173     *  listening to by ignoring the message.
174     *  @param string Debug message.
175     */
176    @Override
177    public void message(String string) {
178    }
179
180    /** Add an actor to the set of actors that are being debugged.
181     *  @param actor The actor to debug.
182     *  @param profile The breakpoint configuration for this actor.
183     */
184    public void putDebugProfile(Executable actor, DebugProfile profile) {
185        _toDebug.put(actor, profile);
186    }
187
188    /** Remove an actor from the set of actors that are being debugged.
189     *  @param actor The actor to remove.
190     */
191    public void removeDebugProfile(Executable actor) {
192        // delete the debug profile from the hashtable
193        _toDebug.remove(actor);
194    }
195
196    /** Enable/disable debugging on the set of actors.
197     *  @param enabled True if debugging should be enabled.
198     */
199    public void setEnabled(boolean enabled) {
200        // FIXME: not implemented yet
201    }
202
203    ///////////////////////////////////////////////////////////////////
204    ////                         private methods                   ////
205
206    /** Highlight the actor and wait for the user to select the Resume
207     *  button before unhighlighting the actor.  This calls
208     *  pauseOnBreakpoint() on the manager.
209     *
210     *  @param figure The figure that we are highlighting.
211     *  @param manager The manager for the figure.
212     *  @param message The message to display in the Run window while
213     *  pausing on the breakpoint.
214     *  @see ptolemy.vergil.kernel.DebugRenderer
215     */
216    private void render(final Figure figure, Manager manager, String message) {
217        if (_debugRenderer == null) {
218            _debugRenderer = new DebugRenderer();
219        }
220
221        // We don't want to call swing stuff in the execution thread,
222        // so we make an anonymous inner class to handle it in a
223        // different thread.
224        SwingUtilities.invokeLater(new Runnable() {
225            @Override
226            public void run() {
227                _debugRenderer.renderSelected(figure);
228            }
229        });
230
231        final Figure debugRendered = figure;
232
233        // Wait for user to select Resume.
234        manager.pauseOnBreakpoint(message);
235
236        if (debugRendered != null) {
237            // Unhighlight the actor after resuming execution.
238            SwingUtilities.invokeLater(new Runnable() {
239                @Override
240                public void run() {
241                    _debugRenderer.renderDeselected(debugRendered);
242                }
243            });
244        }
245    }
246
247    ///////////////////////////////////////////////////////////////////
248    ////                         private variables                 ////
249    // The _debugRenderer for _object.
250    private DebugRenderer _debugRenderer = null;
251
252    // The set of actors that are being debugged.
253    private Hashtable _toDebug = null;
254}