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}