001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.debug.core.breakpoints;
011:
012: import java.util.HashMap;
013: import java.util.Map;
014: import org.eclipse.core.resources.IResource;
015: import org.eclipse.core.resources.IWorkspaceRunnable;
016: import org.eclipse.core.runtime.CoreException;
017: import org.eclipse.core.runtime.IProgressMonitor;
018: import org.eclipse.core.runtime.IStatus;
019: import org.eclipse.core.runtime.Status;
020: import org.eclipse.debug.core.DebugException;
021: import org.eclipse.debug.core.DebugPlugin;
022: import org.eclipse.debug.core.model.IDebugTarget;
023: import org.eclipse.jdt.debug.core.IJavaWatchpoint;
024: import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
025: import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
026: import com.sun.jdi.Field;
027: import com.sun.jdi.ObjectReference;
028: import com.sun.jdi.ReferenceType;
029: import com.sun.jdi.ThreadReference;
030: import com.sun.jdi.VMDisconnectedException;
031: import com.sun.jdi.event.AccessWatchpointEvent;
032: import com.sun.jdi.event.Event;
033: import com.sun.jdi.event.ModificationWatchpointEvent;
034: import com.sun.jdi.request.AccessWatchpointRequest;
035: import com.sun.jdi.request.EventRequest;
036: import com.sun.jdi.request.EventRequestManager;
037: import com.sun.jdi.request.ModificationWatchpointRequest;
038: import com.sun.jdi.request.WatchpointRequest;
039:
040: public class JavaWatchpoint extends JavaLineBreakpoint implements
041: IJavaWatchpoint {
042:
043: private static final String JAVA_WATCHPOINT = "org.eclipse.jdt.debug.javaWatchpointMarker"; //$NON-NLS-1$
044: /**
045: * Watchpoint attribute storing the access value (value <code>"org.eclipse.jdt.debug.core.access"</code>).
046: * This attribute is stored as a <code>boolean</code>, indicating whether a
047: * watchpoint is an access watchpoint.
048: */
049: protected static final String ACCESS = "org.eclipse.jdt.debug.core.access"; //$NON-NLS-1$
050: /**
051: * Watchpoint attribute storing the modification value (value <code>"org.eclipse.jdt.debug.core.modification"</code>).
052: * This attribute is stored as a <code>boolean</code>, indicating whether a
053: * watchpoint is a modification watchpoint.
054: */
055: protected static final String MODIFICATION = "org.eclipse.jdt.debug.core.modification"; //$NON-NLS-1$
056: /**
057: * Watchpoint attribute storing the auto_disabled value (value <code>"org.eclipse.jdt.debug.core.auto_disabled"</code>).
058: * This attribute is stored as a <code>boolean</code>, indicating whether a
059: * watchpoint has been auto-disabled (as opposed to being disabled explicitly by the user)
060: */
061: protected static final String AUTO_DISABLED = "org.eclipse.jdt.debug.core.auto_disabled"; //$NON-NLS-1$
062:
063: /**
064: * Breakpoint attribute storing the name of the field
065: * on which a breakpoint is set.
066: * (value <code>"org.eclipse.jdt.debug.core.fieldName"</code>). This attribute is a <code>String</code>.
067: */
068: protected static final String FIELD_NAME = "org.eclipse.jdt.debug.core.fieldName"; //$NON-NLS-1$
069: /**
070: * Flag indicating that this breakpoint last suspended execution
071: * due to a field access
072: */
073: protected static final Integer ACCESS_EVENT = new Integer(0);
074: /**
075: * Flag indicating that this breakpoint last suspended execution
076: * due to a field modification
077: */
078: protected static final Integer MODIFICATION_EVENT = new Integer(1);
079: /**
080: * Maps each debug target that is suspended for this breakpiont to reason that
081: * this breakpoint suspended it. Reasons include:
082: * <ol>
083: * <li>Field access (value <code>ACCESS_EVENT</code>)</li>
084: * <li>Field modification (value <code>MODIFICATION_EVENT</code>)</li>
085: * </ol>
086: */
087: private HashMap fLastEventTypes = new HashMap(10);
088:
089: public JavaWatchpoint() {
090: }
091:
092: /**
093: * @see JDIDebugModel#createWatchpoint(IResource, String, String, int, int, int, int, boolean, Map)
094: */
095: public JavaWatchpoint(final IResource resource,
096: final String typeName, final String fieldName,
097: final int lineNumber, final int charStart,
098: final int charEnd, final int hitCount, final boolean add,
099: final Map attributes) throws DebugException {
100: IWorkspaceRunnable wr = new IWorkspaceRunnable() {
101: public void run(IProgressMonitor monitor)
102: throws CoreException {
103: setMarker(resource.createMarker(JAVA_WATCHPOINT));
104:
105: // add attributes
106: addLineBreakpointAttributes(attributes,
107: getModelIdentifier(), true, lineNumber,
108: charStart, charEnd);
109: addTypeNameAndHitCount(attributes, typeName, hitCount);
110: attributes.put(SUSPEND_POLICY, new Integer(
111: getDefaultSuspendPolicy()));
112: // configure the field handle
113: addFieldName(attributes, fieldName);
114: // configure the access and modification flags to defaults
115: addDefaultAccessAndModification(attributes);
116:
117: // set attributes
118: ensureMarker().setAttributes(attributes);
119:
120: register(add);
121: }
122: };
123: run(getMarkerRule(resource), wr);
124: }
125:
126: /**
127: * @see JavaBreakpoint#createRequest(JDIDebugTarget, ReferenceType)
128: *
129: * Creates and installs an access and modification watchpoint request
130: * in the given reference type, configuring the requests as appropriate
131: * for this watchpoint. The requests are then enabled based on whether
132: * this watchpoint is an access watchpoint, modification watchpoint, or
133: * both. Finally, the requests are registered with the given target.
134: */
135: protected boolean createRequest(JDIDebugTarget target,
136: ReferenceType type) throws CoreException {
137: if (shouldSkipBreakpoint()) {
138: return false;
139: }
140: Field field = null;
141:
142: field = type.fieldByName(getFieldName());
143: if (field == null) {
144: // error
145: return false;
146: }
147: AccessWatchpointRequest accessRequest = null;
148: ModificationWatchpointRequest modificationRequest = null;
149: if (target.supportsAccessWatchpoints()) {
150: accessRequest = createAccessWatchpoint(target, field);
151: registerRequest(accessRequest, target);
152: } else {
153: notSupported(JDIDebugBreakpointMessages.JavaWatchpoint_no_access_watchpoints);
154: }
155: if (target.supportsModificationWatchpoints()) {
156: modificationRequest = createModificationWatchpoint(target,
157: field);
158: if (modificationRequest == null) {
159: return false;
160: }
161: registerRequest(modificationRequest, target);
162: return true;
163: }
164: notSupported(JDIDebugBreakpointMessages.JavaWatchpoint_no_modification_watchpoints);
165: return false;
166: }
167:
168: /**
169: * @see JavaBreakpoint#setRequestThreadFilter(EventRequest)
170: */
171: protected void setRequestThreadFilter(EventRequest request,
172: ThreadReference thread) {
173: ((WatchpointRequest) request).addThreadFilter(thread);
174: }
175:
176: /**
177: * Either access or modification watchpoints are not supported. Throw an appropriate exception.
178: *
179: * @param message the message that states that access or modification watchpoints
180: * are not supported
181: */
182: protected void notSupported(String message) throws DebugException {
183: throw new DebugException(new Status(IStatus.ERROR, DebugPlugin
184: .getUniqueIdentifier(), DebugException.NOT_SUPPORTED,
185: message, null)); //
186: }
187:
188: /**
189: * Create an access watchpoint for the given breakpoint and associated field
190: */
191: protected AccessWatchpointRequest createAccessWatchpoint(
192: JDIDebugTarget target, Field field) throws CoreException {
193: return (AccessWatchpointRequest) createWatchpoint(target,
194: field, true);
195: }
196:
197: /**
198: * Create a modification watchpoint for the given breakpoint and associated field
199: */
200: protected ModificationWatchpointRequest createModificationWatchpoint(
201: JDIDebugTarget target, Field field) throws CoreException {
202: return (ModificationWatchpointRequest) createWatchpoint(target,
203: field, false);
204: }
205:
206: /**
207: * Create a watchpoint for the given breakpoint and associated field.
208: *
209: * @param target the target in which the request will be installed
210: * @param field the field on which the request will be set
211: * @param access <code>true</code> if an access watchpoint will be
212: * created. <code>false</code> if a modification watchpoint will
213: * be created.
214: *
215: * @return an WatchpointRequest (AccessWatchpointRequest if access is
216: * <code>true</code>; ModificationWatchpointRequest if access is <code>false</code>).
217: */
218: protected WatchpointRequest createWatchpoint(JDIDebugTarget target,
219: Field field, boolean access) throws CoreException {
220: WatchpointRequest request = null;
221: EventRequestManager manager = target.getEventRequestManager();
222: if (manager == null) {
223: target
224: .requestFailed(
225: JDIDebugBreakpointMessages.JavaWatchpoint_Unable_to_create_breakpoint_request___VM_disconnected__1,
226: null);
227: }
228: try {
229: if (access) {
230: request = manager.createAccessWatchpointRequest(field);
231: } else {
232: request = manager
233: .createModificationWatchpointRequest(field);
234: }
235: configureRequest(request, target);
236: } catch (VMDisconnectedException e) {
237: if (!target.isAvailable()) {
238: return null;
239: }
240: target.internalError(e);
241: return null;
242: } catch (RuntimeException e) {
243: target.internalError(e);
244: return null;
245: }
246: return request;
247: }
248:
249: /**
250: * @see JavaBreakpoint#recreateRequest(EventRequest, JDIDebugTarget)
251: */
252: protected EventRequest recreateRequest(EventRequest request,
253: JDIDebugTarget target) throws CoreException {
254: try {
255: Field field = ((WatchpointRequest) request).field();
256: if (request instanceof AccessWatchpointRequest) {
257: request = createAccessWatchpoint(target, field);
258: } else if (request instanceof ModificationWatchpointRequest) {
259: request = createModificationWatchpoint(target, field);
260: }
261: } catch (VMDisconnectedException e) {
262: if (!target.isAvailable()) {
263: return request;
264: }
265: target.internalError(e);
266: return request;
267: } catch (RuntimeException e) {
268: target.internalError(e);
269: }
270: return request;
271: }
272:
273: /**
274: * @see IBreakpoint#setEnabled(boolean)
275: *
276: * If the watchpoint is not watching access or modification,
277: * set the default values. If this isn't done, the resulting
278: * state (enabled with access and modification both disabled)
279: * is ambiguous.
280: */
281: public void setEnabled(boolean enabled) throws CoreException {
282: if (enabled) {
283: if (!(isAccess() || isModification())) {
284: setDefaultAccessAndModification();
285: }
286: }
287: super .setEnabled(enabled);
288: }
289:
290: /**
291: * @see org.eclipse.debug.core.model.IWatchpoint#isAccess()
292: */
293: public boolean isAccess() throws CoreException {
294: return ensureMarker().getAttribute(ACCESS, false);
295: }
296:
297: /**
298: * Sets whether this breakpoint will suspend execution when its associated
299: * field is accessed. If true and this watchpoint is disabled, this watchpoint
300: * is automatically enabled. If both access and modification are false,
301: * this watchpoint is automatically disabled.
302: *
303: * @param access whether to suspend on field access
304: * @exception CoreException if unable to set the property
305: * on this breakpoint's underlying marker
306: * @see org.eclipse.debug.core.model.IWatchpoint#setAccess(boolean)
307: */
308: public void setAccess(boolean access) throws CoreException {
309: if (access == isAccess()) {
310: return;
311: }
312: setAttribute(ACCESS, access);
313: if (access && !isEnabled()) {
314: setEnabled(true);
315: } else if (!(access || isModification())) {
316: setEnabled(false);
317: }
318: recreate();
319: }
320:
321: /**
322: * @see org.eclipse.debug.core.model.IWatchpoint#isModification()
323: */
324: public boolean isModification() throws CoreException {
325: return ensureMarker().getAttribute(MODIFICATION, false);
326: }
327:
328: /**
329: * Sets whether this breakpoint will suspend execution when its associated
330: * field is modified. If true and this watchpoint is disabled, this watchpoint
331: * is automatically enabled. If both access and modification are false,
332: * this watchpoint is automatically disabled.
333: *
334: * @param modification whether to suspend on field modification
335: * @exception CoreException if unable to set the property on
336: * this breakpoint's underlying marker
337: * @see org.eclipse.debug.core.model.IWatchpoint#setModification(boolean)
338: */
339: public void setModification(boolean modification)
340: throws CoreException {
341: if (modification == isModification()) {
342: return;
343: }
344: setAttribute(MODIFICATION, modification);
345: if (modification && !isEnabled()) {
346: setEnabled(true);
347: } else if (!(modification || isAccess())) {
348: setEnabled(false);
349: }
350: recreate();
351: }
352:
353: /**
354: * Sets the default access and modification attributes of the watchpoint.
355: * The default values are:
356: * <ul>
357: * <li>access = <code>false</code>
358: * <li>modification = <code>true</code>
359: * <ul>
360: */
361: protected void setDefaultAccessAndModification()
362: throws CoreException {
363: Object[] values = new Object[] { getDefaultAccessAndModificationValues() };
364: String[] attributes = new String[] { ACCESS, MODIFICATION };
365: setAttributes(attributes, values);
366: }
367:
368: /**
369: * Returns the default access and modification suspend option for a new watchpoint based on the user preference settings
370: * The return array will only ever contain two values, where the possibilities are:
371: * <ul>
372: * <li> <code>{true, true}</code> - both access and modification are enabled</li>
373: * <li> <code>{true, false}</code> - access is enabled and modification is disabled</li>
374: * <li> <code>{false, true}</code> -access is disabled and modification is enabled</li>
375: * </ul>
376: * The default returned array is <code>{true, true}</code>
377: * @return an array of two boolean values representing the default access and modification settings
378: *
379: * @since 3.3.1
380: */
381: protected boolean[] getDefaultAccessAndModificationValues() {
382: int value = JDIDebugPlugin
383: .getDefault()
384: .getPluginPreferences()
385: .getInt(
386: JDIDebugPlugin.PREF_DEFAULT_WATCHPOINT_SUSPEND_POLICY);
387: switch (value) {
388: case 0: {
389: return new boolean[] { true, true };
390: }
391: case 1: {
392: return new boolean[] { true, false };
393: }
394: case 2: {
395: return new boolean[] { false, true };
396: }
397: default: {
398: return new boolean[] { true, true };
399: }
400: }
401: }
402:
403: /**
404: * Adds the default access and modification attributes of
405: * the watchpoint to the given map
406: * <ul>
407: * <li>access = true
408: * <li>modification = true
409: * <li>auto disabled = false
410: * <ul>
411: */
412: protected void addDefaultAccessAndModification(Map attributes) {
413: boolean[] values = getDefaultAccessAndModificationValues();
414: attributes.put(ACCESS, (values[0] ? Boolean.TRUE
415: : Boolean.FALSE));
416: attributes.put(MODIFICATION, (values[1] ? Boolean.TRUE
417: : Boolean.FALSE));
418: attributes.put(AUTO_DISABLED, Boolean.FALSE);
419: }
420:
421: /**
422: * Adds the field name to the given attribute map
423: */
424: protected void addFieldName(Map attributes, String fieldName) {
425: attributes.put(FIELD_NAME, fieldName);
426: }
427:
428: /**
429: * @see IJavaWatchpoint#getFieldName()
430: */
431: public String getFieldName() throws CoreException {
432: return ensureMarker().getAttribute(FIELD_NAME, null);
433: }
434:
435: /**
436: * Store the type of the event, then handle it as specified in
437: * the superclass. This is useful for correctly generating the
438: * thread text when asked (assumes thread text is requested after
439: * the event is passed to this breakpoint.
440: *
441: * Also, @see JavaBreakpoint#handleEvent(Event, JDIDebugTarget)
442: */
443: public boolean handleEvent(Event event, JDIDebugTarget target) {
444: if (event instanceof AccessWatchpointEvent) {
445: fLastEventTypes.put(target, ACCESS_EVENT);
446: } else if (event instanceof ModificationWatchpointEvent) {
447: fLastEventTypes.put(target, MODIFICATION_EVENT);
448: }
449: return super .handleEvent(event, target);
450: }
451:
452: /**
453: * @see JavaBreakpoint#updateEnabledState(EventRequest, JDIDebugTarget)
454: */
455: protected void updateEnabledState(EventRequest request,
456: JDIDebugTarget target) throws CoreException {
457: boolean enabled = isEnabled();
458: if (request instanceof AccessWatchpointRequest) {
459: if (isAccess()) {
460: if (enabled != request.isEnabled()) {
461: internalUpdateEnabledState(request, enabled, target);
462: }
463: } else {
464: if (request.isEnabled()) {
465: internalUpdateEnabledState(request, false, target);
466: }
467: }
468: }
469: if (request instanceof ModificationWatchpointRequest) {
470: if (isModification()) {
471: if (enabled != request.isEnabled()) {
472: internalUpdateEnabledState(request, enabled, target);
473: }
474: } else {
475: if (request.isEnabled()) {
476: internalUpdateEnabledState(request, false, target);
477: }
478: }
479: }
480: }
481:
482: /**
483: * @see IJavaWatchpoint#isAccessSuspend(IDebugTarget)
484: */
485: public boolean isAccessSuspend(IDebugTarget target) {
486: Integer lastEventType = (Integer) fLastEventTypes.get(target);
487: if (lastEventType == null) {
488: return false;
489: }
490: return lastEventType.equals(ACCESS_EVENT);
491: }
492:
493: /**
494: * @see IJavaLineBreakpoint#supportsCondition()
495: */
496: public boolean supportsCondition() {
497: return false;
498: }
499:
500: /**
501: * @see org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint#removeFromTarget(JDIDebugTarget)
502: */
503: public void removeFromTarget(JDIDebugTarget target)
504: throws CoreException {
505: fLastEventTypes.remove(target);
506: super .removeFromTarget(target);
507: }
508:
509: /**
510: * @see org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint#addInstanceFilter(EventRequest, ObjectReference)
511: */
512: protected void addInstanceFilter(EventRequest request,
513: ObjectReference object) {
514: if (request instanceof WatchpointRequest) {
515: ((WatchpointRequest) request).addInstanceFilter(object);
516: }
517: }
518:
519: /* (non-Javadoc)
520: * @see org.eclipse.debug.core.model.IWatchpoint#supportsAccess()
521: */
522: public boolean supportsAccess() {
523: return true;
524: }
525:
526: /* (non-Javadoc)
527: * @see org.eclipse.debug.core.model.IWatchpoint#supportsModification()
528: */
529: public boolean supportsModification() {
530: return true;
531: }
532:
533: /* (non-Javadoc)
534: * @see org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint#installableReferenceType(com.sun.jdi.ReferenceType, org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget)
535: */
536: protected boolean installableReferenceType(ReferenceType type,
537: JDIDebugTarget target) throws CoreException {
538: String installableType = getTypeName();
539: String queriedType = type.name();
540: if (installableType == null || queriedType == null) {
541: return false;
542: }
543: if (installableType.equals(queriedType)) {
544: return queryInstallListeners(target, type);
545: }
546:
547: return false;
548: }
549: }
|