Initial Commit
[packages] / xemacs-packages / jde / java / src / jde / debugger / spec / EventRequestSpec.java
1 package jde.debugger.spec;
2
3 import com.sun.jdi.*;
4 import com.sun.jdi.event.*;
5 import com.sun.jdi.request.*;
6
7 import java.util.*;
8 import jde.debugger.Protocol;
9 import jde.debugger.JDE;
10 import jde.debugger.JDEException;
11 import jde.debugger.SessionManager;
12
13 /**
14  * EventRequestSpec.java
15  * <p>
16  * A request specification. This is used for watchpoints, exception-catches,
17  * and breakpoints, and provides a mechanism for implementing deferral.
18  * <p>
19  * The intuition is that the user should be allowed to specify things like
20  * breakpoints, even though the corresponding classes haven't been loaded
21  * yet.
22  * <p>
23  * When the user does a, for example, "break on_line test.Test 42", jdebug
24  * tries to find if test.Test has been loaded. If it has, it tries to set
25  * the breakpoint, and sends an error on failure.
26  * <p>
27  * If, however, no class matching test.Test exists, jdebug places this
28  * "spec" in a list, and each time a class is prepared, matches the class
29  * with the spec. If the spec matches, it tries to set the breakpoint /
30  * watchpoint / exception-catch. If it works, fine, else it sends the
31  * error over to jde.
32  * <p>
33  * This also allows for neat things like setting breakpoints on source file
34  * + line number combinations, since each reference type (given it was
35  * compiled with debug info) also contains the source file name in it.
36  * <p>
37  * Information that would normally be stuck right into the actual requests,
38  * for example a thread filter, is stored in the spec until the time it can
39  * resolve the request. At that time, it is set in {@link #setRequest}.
40  * <p>
41  * XXX
42  * <p>
43  * Note that as of now, when the doc is being written, there is no way of
44  * ascertaining if the user mistyped the referencetype name/pattern, since
45  * jdebug will just wait <i>ad infinitum</i> for that class to be prepared.
46  * <p>
47  * Created: Thu Jul 15 12:17:34 1999
48  *
49  * @author Amit Kumar
50  * @since 0.1
51  * @version 
52  */
53
54 abstract public class EventRequestSpec implements Protocol {
55
56   private final Long m_ID;
57
58
59   /**
60    * While setting some specs, the user is allowed to specify a boolean
61    * expression that must evaluate to true if the event is to be passed
62    * on to the user. This expression is stored in the EventRequest object
63    * as a property. On an event, the EventRequest object is also passed,
64    * and the property can then be extracted, evaluated, and
65    * handled correspondingly
66    */
67   public static final Object expressionKey = "expr";
68   private String expr = null;
69
70   public EventRequestSpec(ReferenceTypeSpec refSpec) {
71     this.refSpec = refSpec;
72     
73     m_ID = SessionManager.generateObjectID();
74   }
75
76
77   public void setExpression(String expr) {
78     this.expr = expr;
79     if (request != null)
80       request.putProperty(expressionKey, expr);
81   }
82
83   /**
84    * For specs that allow for it,
85    * the {@link #thread thread} object is either null, a Long, or a
86    * String. Depending on the type, it is matched at the time the
87    * breakpoint is hit. If it matches the thread, the breakpoint is
88    * deemed non-hit.
89    */
90   public static final Object threadKey = "thread";
91   private Object thread = null;
92   public void setThread(Object thread) {
93     this.thread = thread;
94     if (request != null)
95       request.putProperty(threadKey, thread);
96   }
97
98   /**
99    * Determines the suspend policy for the corresponding event. See
100    * {@link jde.debugger.EventHandler EventHandler}  for more details
101    * <p>
102    * Note that the request needs to be disabled for us to be able to
103    * do this
104    */
105   private int suspendPolicy = EventRequest.SUSPEND_ALL; // the default
106   public void setSuspendPolicy(int policy) {
107     this.suspendPolicy = policy;
108     if (request != null) {
109       request.setSuspendPolicy(policy);
110       //            System.out.println("XXX:"+((request.suspendPolicy() == EventRequest.SUSPEND_NONE)?"none":"not none"));
111     }
112   }
113
114   /**
115    * Stores a list of class filters that are to be applied to the event
116    * request when it gets resolved. This will restrict any events from
117    * being reported only if they match the class filters.
118    * <p>
119    * Not all event-requests support class filters. This filters will be
120    * silently ignored for event-requests that do not support them.
121    */
122   private List classFilters = null;
123   public void setClassFilters(List filters) {
124     this.classFilters = filters;
125     if (request != null)
126       installClassFilters(request);
127   }
128
129   /**
130    * Install class filters.
131    * Note that the request needs to be disabled for us to be able to
132    * do this
133    */
134   private void installClassFilters(EventRequest request) {
135     if (classFilters == null) return;
136     Iterator iter = classFilters.iterator();
137     while (iter.hasNext()) {
138       String f = iter.next().toString();
139       if (request instanceof ClassPrepareRequest) {
140         ((ClassPrepareRequest)request).addClassFilter(f);
141       } else if (request instanceof ClassUnloadRequest) {
142         ((ClassUnloadRequest)request).addClassFilter(f);
143       } else if (request instanceof ExceptionRequest) {
144         ((ExceptionRequest)request).addClassFilter(f);
145       } else if (request instanceof WatchpointRequest) {
146         ((WatchpointRequest)request).addClassFilter(f);
147       }
148     }
149   }
150
151
152   /**
153    * Stores a list of class exclusion filters that are to be applied to
154    * the event
155    * request when it gets resolved. This will restrict any events from
156    * being reported only if they do <b>not</b> match the class ex-filters.
157    * <p>
158    * Not all event-requests support class ex-filters. This filters will be
159    * silently ignored for event-requests that do not support them.
160    */
161   private List classExFilters = null;
162   public void setClassExFilters(List filters) {
163     this.classExFilters = filters;
164     if (request != null)
165       installClassExFilters(request);
166   }
167
168   /**
169    * Install class exclusion filters.
170    * Note that the request needs to be disabled for us to be able to
171    * do this
172    */
173   private void installClassExFilters(EventRequest request) {
174     if (classExFilters == null) return;
175     Iterator iter = classExFilters.iterator();
176     while (iter.hasNext()) {
177       String f = iter.next().toString();
178       if (request instanceof ClassPrepareRequest) {
179         ((ClassPrepareRequest)request).addClassExclusionFilter(f);
180       } else if (request instanceof ClassUnloadRequest) {
181         ((ClassUnloadRequest)request).addClassExclusionFilter(f);
182       } else if (request instanceof ExceptionRequest) {
183         ((ExceptionRequest)request).addClassExclusionFilter(f);
184       } else if (request instanceof WatchpointRequest) {
185         ((WatchpointRequest)request).addClassExclusionFilter(f);
186       }
187     }
188   }
189
190
191
192   /**
193    * Unlike the original javadt (from which most of the spec code comes,
194    * we do not maintain three spec states, ie, resolved, unresolved, and
195    * error. In our case, on an error, we simply remove the spec from the
196    * list of specs being maintained by the application, and inform the
197    * jde of this fact (that there was an error resolving the spec)
198    * (using app.removeSpecAndInformJDE(this))
199    * <p>
200    * XXX see if the above needs to be changed
201    * <p>
202    * Consequently, we only need keep track of if we're resolved yet or
203    * not.
204    */
205   boolean isResolved = false;
206
207   /**
208    * Used to cross-reference the EventRequest to its
209    * spec.
210    */
211   static public final Object specPropertyKey = "spec";
212
213   /**
214    * The reference type spec for this event request spec: this should 
215    * match the ReferenceType for the spec to be
216    * "resolved"
217    */
218   ReferenceTypeSpec refSpec;
219
220   /**
221    * The EventRequest corresponding to this spec. This
222    * is set when the spec resolves successfully.
223    */
224   EventRequest request = null;
225     
226   /** get the id corresponding to this spec */
227   public Long getID() { return m_ID; }
228
229   /**
230    * sets the request up. This is called when a resolve succedes. 
231    */
232   void setRequest(EventRequest request) {
233     this.request = request;
234     // put a link to this spec in the request itself. a sort of
235     // cross referencing
236     request.putProperty(specPropertyKey, this);
237     request.putProperty(threadKey, thread);
238     request.putProperty(expressionKey, expr);
239     request.setSuspendPolicy(suspendPolicy);
240     installClassFilters(request);
241     installClassExFilters(request);
242     //  System.out.println("YYY:"+((request.suspendPolicy() == EventRequest.SUSPEND_NONE)?"none":"not none"));
243
244     request.enable();
245   }
246
247   public EventRequest getEventRequest() { return request; }
248
249   /**
250    * This function is called to resolve an {@link EventRequestSpec} when 
251    * the ReferenceType is known to match
252    * <p>
253    * if any errors occur at any time during resolution of the event-
254    * requestspec, its entry in the {@link EventRequestSpecList} is
255    * removed, and jde informed about it 
256    * <p>
257    * @return true if the resolution was successful
258    */
259   abstract boolean resolve(ReferenceType refType) throws JDEException;
260
261   /**
262    * This function is called after each new class is loaded. If this
263    * spec hasn't been resolved yet, it's attempted to be resolved. the
264    * handling is almost exactly the same as that in
265    * {@link #attemptImmediateResolve}
266    * <p>
267    */
268   public void attemptResolve(ReferenceType refType, Integer procID) throws JDEException {
269     if (!isResolved() && refSpec.matches(refType)) {
270       if (resolve(refType)) {
271         JDE.debug(EVENTS, "resolve succeeded: " + refType.name());
272         setIsResolved(procID);
273       }
274     }
275   }
276
277   /**
278    * Attempts to resolve the eventRequestSpec immediately. There are
279    * three possibilities:
280    * <ul>
281    * <li> The corresponding class hasn't been loaded. the method returns
282    * normally.
283    * <li> The class has been loaded, and the resolution is successful.
284    * The method returns normally, having set the isResolved flag in this
285    * class
286    * <li> The class has been loaded, but there was an error trying to
287    * resolve this spec. An exception is raised, and is caught in this
288    * method. This spec is then removed from the spec list kept in the
289    * Application object, and jde informed that this spec could not be
290    * resolved, so that the UI can take appropriate actions (for example
291    * removing the highlighting of a breakpoint)
292    * </ul>
293    */
294   void attemptImmediateResolve(VirtualMachine vm, Integer procID) throws JDEException {
295     Iterator iter = vm.allClasses().iterator();
296         
297     // XXX - I added the !isResolved condition, to save some loop iterations.
298     //       Since I don't fully understand it, it could be a bug, but I think not. / Petter
299     while (iter.hasNext() && !isResolved) {
300       ReferenceType refType = (ReferenceType)iter.next();
301       if (refSpec.matches(refType)) {
302         if (resolve(refType)) {
303           setIsResolved(procID);
304         } 
305       }
306     }
307   }
308     
309   /**
310    * @return true if this spec has been resolved.
311    */
312   public boolean isResolved() {
313     return isResolved;
314   }
315
316   /**
317    * set resolved status and notify Emacs.
318    */
319   public void setIsResolved(Integer procID) {
320     isResolved = true;
321     JDE.signal(procID, SPEC_RESOLVED, m_ID.toString(), NOQUOTE);
322   }
323
324   boolean isJavaIdentifier(String s) {
325     if (s.length() == 0) {                              
326       return false;
327     }
328
329     if (! Character.isJavaIdentifierStart(s.charAt(0))) {
330       return false;
331     }
332
333     for (int i = 1; i < s.length(); i++) {
334       if (! Character.isJavaIdentifierPart(s.charAt(i))) {
335         return false;
336       }
337     }
338
339     return true;
340   }
341
342 } // EventRequestSpec
343
344 /*
345  * $Log: EventRequestSpec.java,v $
346  * Revision 1.4  2003/01/15 06:06:15  paulk
347  * Petter Mahlen's changes.
348  *
349  */
350
351 // End of EventRequestSpec.java