0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.debug.core.breakpoints;
0011:
0012: import java.util.ArrayList;
0013: import java.util.Collection;
0014: import java.util.HashMap;
0015: import java.util.HashSet;
0016: import java.util.Iterator;
0017: import java.util.List;
0018: import java.util.Map;
0019: import java.util.Set;
0020:
0021: import org.eclipse.core.resources.IMarker;
0022: import org.eclipse.core.runtime.CoreException;
0023: import org.eclipse.core.runtime.MultiStatus;
0024: import org.eclipse.core.runtime.Preferences;
0025: import org.eclipse.debug.core.DebugEvent;
0026: import org.eclipse.debug.core.DebugPlugin;
0027: import org.eclipse.debug.core.IDebugEventSetListener;
0028: import org.eclipse.debug.core.model.Breakpoint;
0029: import org.eclipse.debug.core.model.IDebugTarget;
0030: import org.eclipse.jdt.debug.core.IJavaBreakpoint;
0031: import org.eclipse.jdt.debug.core.IJavaDebugTarget;
0032: import org.eclipse.jdt.debug.core.IJavaObject;
0033: import org.eclipse.jdt.debug.core.IJavaThread;
0034: import org.eclipse.jdt.debug.core.IJavaType;
0035: import org.eclipse.jdt.debug.core.JDIDebugModel;
0036: import org.eclipse.jdt.internal.debug.core.IJDIEventListener;
0037: import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
0038: import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
0039: import org.eclipse.jdt.internal.debug.core.model.JDIObjectValue;
0040: import org.eclipse.jdt.internal.debug.core.model.JDIThread;
0041: import org.eclipse.jdt.internal.debug.core.model.JDIType;
0042:
0043: import com.ibm.icu.text.MessageFormat;
0044: import com.sun.jdi.ObjectReference;
0045: import com.sun.jdi.ReferenceType;
0046: import com.sun.jdi.ThreadReference;
0047: import com.sun.jdi.VMDisconnectedException;
0048: import com.sun.jdi.event.ClassPrepareEvent;
0049: import com.sun.jdi.event.Event;
0050: import com.sun.jdi.event.LocatableEvent;
0051: import com.sun.jdi.request.ClassPrepareRequest;
0052: import com.sun.jdi.request.EventRequest;
0053: import com.sun.jdi.request.EventRequestManager;
0054:
0055: public abstract class JavaBreakpoint extends Breakpoint implements
0056: IJavaBreakpoint, IJDIEventListener, IDebugEventSetListener {
0057:
0058: /**
0059: * Breakpoint attribute storing the expired value (value <code>"org.eclipse.jdt.debug.core.expired"</code>).
0060: * This attribute is stored as a <code>boolean</code>. Once a hit count has
0061: * been reached, a breakpoint is considered to be "expired".
0062: */
0063: protected static final String EXPIRED = "org.eclipse.jdt.debug.core.expired"; //$NON-NLS-1$
0064: /**
0065: * Breakpoint attribute storing a breakpoint's hit count value
0066: * (value <code>"org.eclipse.jdt.debug.core.hitCount"</code>). This attribute is stored as an
0067: * <code>int</code>.
0068: */
0069: protected static final String HIT_COUNT = "org.eclipse.jdt.debug.core.hitCount"; //$NON-NLS-1$
0070: /**
0071: * Breakpoint attribute storing the number of debug targets a
0072: * breakpoint is installed in (value <code>"org.eclipse.jdt.debug.core.installCount"</code>).
0073: * This attribute is a <code>int</code>.
0074: */
0075: protected static final String INSTALL_COUNT = "org.eclipse.jdt.debug.core.installCount"; //$NON-NLS-1$
0076:
0077: /**
0078: * Breakpoint attribute storing the fully qualified name of the type
0079: * this breakpoint is located in.
0080: * (value <code>"org.eclipse.jdt.debug.core.typeName"</code>). This attribute is a <code>String</code>.
0081: */
0082: protected static final String TYPE_NAME = "org.eclipse.jdt.debug.core.typeName"; //$NON-NLS-1$
0083:
0084: /**
0085: * Breakpoint attribute storing suspend policy code for
0086: * this breakpoint.
0087: * (value <code>"org.eclipse.jdt.debug.core.suspendPolicy</code>).
0088: * This attribute is an <code>int</code> corresponding
0089: * to <code>IJavaBreakpoint.SUSPEND_VM</code> or
0090: * <code>IJavaBreakpoint.SUSPEND_THREAD</code>.
0091: */
0092: protected static final String SUSPEND_POLICY = "org.eclipse.jdt.debug.core.suspendPolicy"; //$NON-NLS-1$
0093:
0094: /**
0095: * Stores the collection of requests that this breakpoint has installed in
0096: * debug targets.
0097: * key: a debug target
0098: * value: the requests this breakpoint has installed in that target
0099: */
0100: protected HashMap fRequestsByTarget;
0101:
0102: /**
0103: * The list of threads (ThreadReference objects) in which this breakpoint will suspend,
0104: * associated with the target in which each thread exists (JDIDebugTarget).
0105: * key: targets the debug targets (IJavaDebugTarget)
0106: * value: thread the filtered thread (IJavaThread) in the given target
0107: */
0108: protected Map fFilteredThreadsByTarget;
0109:
0110: /**
0111: * Stores the type name that this breakpoint was last installed
0112: * in. When a breakpoint is created, the TYPE_NAME attribute assigned to it
0113: * is that of its top level enclosing type. When installed, the type
0114: * may actually be an inner type. We need to keep track of the type
0115: * type the breakpoint was installed in, in case we need to re-install
0116: * the breakpoint for HCR (i.e. in case an inner type is HCR'd).
0117: */
0118: protected String fInstalledTypeName = null;
0119:
0120: /**
0121: * List of targets in which this breakpoint is installed.
0122: * Used to prevent firing of more than one install notification
0123: * when a breakpoint's requests are re-created.
0124: */
0125: protected Set fInstalledTargets = null;
0126:
0127: /**
0128: * List of active instance filters for this breakpoint
0129: * (list of <code>IJavaObject</code>).
0130: */
0131: protected List fInstanceFilters = null;
0132:
0133: /**
0134: * Empty instance filters array.
0135: */
0136: protected static final IJavaObject[] fgEmptyInstanceFilters = new IJavaObject[0];
0137:
0138: /**
0139: * Property identifier for a breakpoint object on an event request
0140: */
0141: public static final String JAVA_BREAKPOINT_PROPERTY = "org.eclipse.jdt.debug.breakpoint"; //$NON-NLS-1$
0142:
0143: /**
0144: * JavaBreakpoint attributes
0145: */
0146: protected static final String[] fgExpiredEnabledAttributes = new String[] {
0147: EXPIRED, ENABLED };
0148:
0149: public JavaBreakpoint() {
0150: fRequestsByTarget = new HashMap(1);
0151: fFilteredThreadsByTarget = new HashMap(1);
0152: }
0153:
0154: /* (non-Javadoc)
0155: * @see org.eclipse.debug.core.model.IBreakpoint#getModelIdentifier()
0156: */
0157: public String getModelIdentifier() {
0158: return JDIDebugModel.getPluginIdentifier();
0159: }
0160:
0161: /* (non-Javadoc)
0162: * @see org.eclipse.debug.core.model.Breakpoint#setMarker(org.eclipse.core.resources.IMarker)
0163: */
0164: public void setMarker(IMarker marker) throws CoreException {
0165: super .setMarker(marker);
0166: configureAtStartup();
0167: }
0168:
0169: /**
0170: * Add this breakpoint to the breakpoint manager,
0171: * or sets it as unregistered.
0172: */
0173: protected void register(boolean register) throws CoreException {
0174: DebugPlugin plugin = DebugPlugin.getDefault();
0175: if (plugin != null && register) {
0176: plugin.getBreakpointManager().addBreakpoint(this );
0177: } else {
0178: setRegistered(false);
0179: }
0180: }
0181:
0182: /**
0183: * Add the given event request to the given debug target. If
0184: * the request is the breakpoint request associated with this
0185: * breakpoint, increment the install count.
0186: */
0187: protected void registerRequest(EventRequest request,
0188: JDIDebugTarget target) throws CoreException {
0189: if (request == null) {
0190: return;
0191: }
0192: List reqs = getRequests(target);
0193: if (reqs.isEmpty()) {
0194: fRequestsByTarget.put(target, reqs);
0195: }
0196: reqs.add(request);
0197: target.addJDIEventListener(this , request);
0198: // update the install attribute on the breakpoint
0199: if (!(request instanceof ClassPrepareRequest)) {
0200: incrementInstallCount();
0201: // notification
0202: fireInstalled(target);
0203: }
0204: }
0205:
0206: /**
0207: * Returns a String corresponding to the reference type
0208: * name to the top enclosing type in which this breakpoint
0209: * is located or <code>null</code> if no reference type could be
0210: * found.
0211: */
0212: protected String getEnclosingReferenceTypeName()
0213: throws CoreException {
0214: String name = getTypeName();
0215: int index = name.indexOf('$');
0216: if (index == -1) {
0217: return name;
0218: }
0219: return name.substring(0, index);
0220: }
0221:
0222: /**
0223: * Returns the requests that this breakpoint has installed
0224: * in the given target.
0225: */
0226: protected ArrayList getRequests(JDIDebugTarget target) {
0227: ArrayList list = (ArrayList) fRequestsByTarget.get(target);
0228: if (list == null) {
0229: list = new ArrayList(2);
0230: }
0231: return list;
0232: }
0233:
0234: /**
0235: * Remove the given request from the given target. If the request
0236: * is the breakpoint request associated with this breakpoint,
0237: * decrement the install count.
0238: */
0239: protected void deregisterRequest(EventRequest request,
0240: JDIDebugTarget target) throws CoreException {
0241: target.removeJDIEventListener(this , request);
0242: // A request may be getting de-registered because the breakpoint has
0243: // been deleted. It may be that this occurred because of a marker deletion.
0244: // Don't try updating the marker (decrementing the install count) if
0245: // it no longer exists.
0246: if (!(request instanceof ClassPrepareRequest)
0247: && getMarker().exists()) {
0248: decrementInstallCount();
0249: }
0250: }
0251:
0252: /* (non-Javadoc)
0253: * @see org.eclipse.jdt.internal.debug.core.IJDIEventListener#handleEvent(com.sun.jdi.event.Event, org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget)
0254: */
0255: public boolean handleEvent(Event event, JDIDebugTarget target) {
0256: if (event instanceof ClassPrepareEvent) {
0257: return handleClassPrepareEvent((ClassPrepareEvent) event,
0258: target);
0259: }
0260: ThreadReference threadRef = ((LocatableEvent) event).thread();
0261: JDIThread thread = target.findThread(threadRef);
0262: if (thread == null || thread.isIgnoringBreakpoints()) {
0263: return true;
0264: }
0265: return handleBreakpointEvent(event, target, thread);
0266: }
0267:
0268: /* (non-Javadoc)
0269: * @see org.eclipse.jdt.internal.debug.core.IJDIEventListener#wonSuspendVote(com.sun.jdi.event.Event, org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget)
0270: */
0271: public void wonSuspendVote(Event event, JDIDebugTarget target) {
0272: ThreadReference threadRef = null;
0273: if (event instanceof ClassPrepareEvent) {
0274: threadRef = ((ClassPrepareEvent) event).thread();
0275: } else if (event instanceof LocatableEvent) {
0276: threadRef = ((LocatableEvent) event).thread();
0277: }
0278: if (threadRef == null) {
0279: return;
0280: }
0281: JDIThread thread = target.findThread(threadRef);
0282: if (thread == null || thread.isIgnoringBreakpoints()) {
0283: return;
0284: }
0285: thread.wonSuspendVote(this );
0286: }
0287:
0288: /**
0289: * Handle the given class prepare event, which was generated by the
0290: * class prepare event installed in the given target by this breakpoint.
0291: *
0292: * If the class which has been loaded is a class in which this breakpoint
0293: * should install, create a breakpoint request for that class.
0294: */
0295: public boolean handleClassPrepareEvent(ClassPrepareEvent event,
0296: JDIDebugTarget target) {
0297: try {
0298: if (!installableReferenceType(event.referenceType(), target)) {
0299: // Don't install this breakpoint in an
0300: // inappropriate type
0301: return true;
0302: }
0303: createRequest(target, event.referenceType());
0304: } catch (CoreException e) {
0305: JDIDebugPlugin.log(e);
0306: }
0307: return true;
0308: }
0309:
0310: /**
0311: * @see IJDIEventListener#handleEvent(Event, JDIDebugTarget)
0312: *
0313: * Handle the given event, which was generated by the breakpoint request
0314: * installed in the given target by this breakpoint.
0315: */
0316: public boolean handleBreakpointEvent(Event event,
0317: JDIDebugTarget target, JDIThread thread) {
0318: expireHitCount(event);
0319: return !suspend(thread); // Resume if suspend fails
0320: }
0321:
0322: /**
0323: * Delegates to the given thread to suspend, and
0324: * returns whether the thread suspended
0325: * It is possible that the thread will not suspend
0326: * as directed by a Java breakpoint listener.
0327: *
0328: * @see IJavaBreakpointListener#breakpointHit(IJavaThread, IJavaBreakpoint)
0329: */
0330: protected boolean suspend(JDIThread thread) {
0331: return thread.handleSuspendForBreakpoint(this , true);
0332: }
0333:
0334: /**
0335: * Returns whether the given reference type is appropriate for this
0336: * breakpoint to be installed in the given target. Query registered
0337: * breakpoint listeners.
0338: */
0339: protected boolean installableReferenceType(ReferenceType type,
0340: JDIDebugTarget target) throws CoreException {
0341: String installableType = getTypeName();
0342: String queriedType = type.name();
0343: if (installableType == null || queriedType == null) {
0344: return false;
0345: }
0346: int index = queriedType.indexOf('<');
0347: if (index != -1) {
0348: queriedType = queriedType.substring(0, index);
0349: }
0350: if (installableType.equals(queriedType)) {
0351: return queryInstallListeners(target, type);
0352: }
0353: index = queriedType.indexOf('$', 0);
0354: if (index == -1) {
0355: return false;
0356: }
0357: if (installableType.regionMatches(0, queriedType, 0, index)) {
0358: return queryInstallListeners(target, type);
0359: }
0360: return false;
0361: }
0362:
0363: /**
0364: * Called when a breakpoint event is encountered. Expires the
0365: * hit count in the event's request and updates the marker.
0366: * @param event the event whose request should have its hit count
0367: * expired or <code>null</code> to only update the breakpoint marker.
0368: */
0369: protected void expireHitCount(Event event) {
0370: Integer requestCount = null;
0371: EventRequest request = null;
0372: if (event != null) {
0373: request = event.request();
0374: requestCount = (Integer) request.getProperty(HIT_COUNT);
0375: }
0376: if (requestCount != null) {
0377: if (request != null) {
0378: request.putProperty(EXPIRED, Boolean.TRUE);
0379: }
0380: try {
0381: setAttributes(fgExpiredEnabledAttributes, new Object[] {
0382: Boolean.TRUE, Boolean.FALSE });
0383: // make a note that we auto-disabled this breakpoint.
0384: } catch (CoreException ce) {
0385: JDIDebugPlugin.log(ce);
0386: }
0387: }
0388: }
0389:
0390: /**
0391: * Returns whether this breakpoint should be "skipped". Breakpoints
0392: * are skipped if the breakpoint manager is disabled and the breakpoint
0393: * is registered with the manager
0394: *
0395: * @return whether this breakpoint should be skipped
0396: */
0397: public boolean shouldSkipBreakpoint() throws CoreException {
0398: DebugPlugin plugin = DebugPlugin.getDefault();
0399: return plugin != null && isRegistered()
0400: && !plugin.getBreakpointManager().isEnabled();
0401: }
0402:
0403: /**
0404: * Attempts to create a breakpoint request for this breakpoint in the given
0405: * reference type in the given target.
0406: *
0407: * @return Whether a request was created
0408: */
0409: protected boolean createRequest(JDIDebugTarget target,
0410: ReferenceType type) throws CoreException {
0411: if (shouldSkipBreakpoint()) {
0412: return false;
0413: }
0414: EventRequest[] requests = newRequests(target, type);
0415: if (requests == null) {
0416: return false;
0417: }
0418: fInstalledTypeName = type.name();
0419: for (int i = 0; i < requests.length; i++) {
0420: EventRequest request = requests[i];
0421: registerRequest(request, target);
0422: }
0423: return true;
0424: }
0425:
0426: /**
0427: * Configure a breakpoint request with common properties:
0428: * <ul>
0429: * <li><code>JAVA_BREAKPOINT_PROPERTY</code></li>
0430: * <li><code>HIT_COUNT</code></li>
0431: * <li><code>EXPIRED</code></li>
0432: * </ul>
0433: * and sets the suspend policy of the request to suspend
0434: * the event thread.
0435: */
0436: protected void configureRequest(EventRequest request,
0437: JDIDebugTarget target) throws CoreException {
0438: request.setSuspendPolicy(getJDISuspendPolicy());
0439: request.putProperty(JAVA_BREAKPOINT_PROPERTY, this );
0440: configureRequestThreadFilter(request, target);
0441: configureRequestHitCount(request);
0442: configureInstanceFilters(request, target);
0443: // Important: only enable a request after it has been configured
0444: updateEnabledState(request, target);
0445: }
0446:
0447: /**
0448: * Adds an instance filter to the given request. Since the implementation is
0449: * request specific, subclasses must override.
0450: *
0451: * @param request
0452: * @param object instance filter
0453: */
0454: protected abstract void addInstanceFilter(EventRequest request,
0455: ObjectReference object);
0456:
0457: /**
0458: * Configure the thread filter property of the given request.
0459: */
0460: protected void configureRequestThreadFilter(EventRequest request,
0461: JDIDebugTarget target) {
0462: IJavaThread thread = (IJavaThread) fFilteredThreadsByTarget
0463: .get(target);
0464: if (thread == null || (!(thread instanceof JDIThread))) {
0465: return;
0466: }
0467: setRequestThreadFilter(request, ((JDIThread) thread)
0468: .getUnderlyingThread());
0469: }
0470:
0471: /**
0472: * Configure the given request's hit count
0473: */
0474: protected void configureRequestHitCount(EventRequest request)
0475: throws CoreException {
0476: int hitCount = getHitCount();
0477: if (hitCount > 0) {
0478: request.addCountFilter(hitCount);
0479: request.putProperty(HIT_COUNT, new Integer(hitCount));
0480: }
0481: }
0482:
0483: protected void configureInstanceFilters(EventRequest request,
0484: JDIDebugTarget target) {
0485: if (fInstanceFilters != null && !fInstanceFilters.isEmpty()) {
0486: Iterator iter = fInstanceFilters.iterator();
0487: while (iter.hasNext()) {
0488: IJavaObject object = (IJavaObject) iter.next();
0489: if (object.getDebugTarget().equals(target)) {
0490: addInstanceFilter(request,
0491: ((JDIObjectValue) object)
0492: .getUnderlyingObject());
0493: }
0494: }
0495: }
0496: }
0497:
0498: /**
0499: * Creates, installs, and returns all event requests for this breakpoint
0500: * in the given reference type and and target.
0501: *
0502: * @return the event requests created or <code>null</code> if creation failed
0503: */
0504: protected abstract EventRequest[] newRequests(
0505: JDIDebugTarget target, ReferenceType type)
0506: throws CoreException;
0507:
0508: /**
0509: * Add this breakpoint to the given target. After it has been
0510: * added to the given target, this breakpoint will suspend
0511: * execution of that target as appropriate.
0512: */
0513: public void addToTarget(JDIDebugTarget target) throws CoreException {
0514: fireAdding(target);
0515: createRequests(target);
0516: }
0517:
0518: /**
0519: * Creates event requests for the given target
0520: */
0521: protected void createRequests(JDIDebugTarget target)
0522: throws CoreException {
0523: if (target.isTerminated() || shouldSkipBreakpoint()) {
0524: return;
0525: }
0526: String referenceTypeName = getTypeName();
0527: String enclosingTypeName = getEnclosingReferenceTypeName();
0528: if (referenceTypeName == null || enclosingTypeName == null) {
0529: return;
0530: }
0531: // create request to listen to class loads
0532: if (referenceTypeName.indexOf('$') == -1) {
0533: registerRequest(target
0534: .createClassPrepareRequest(enclosingTypeName),
0535: target);
0536: //register to ensure we hear about local and anonymous inner classes
0537: registerRequest(
0538: target.createClassPrepareRequest(enclosingTypeName
0539: + "$*"), target); //$NON-NLS-1$
0540: } else {
0541: registerRequest(target
0542: .createClassPrepareRequest(referenceTypeName),
0543: target);
0544: //register to ensure we hear about local and anonymous inner classes
0545: registerRequest(
0546: target.createClassPrepareRequest(enclosingTypeName
0547: + "$*", referenceTypeName), target); //$NON-NLS-1$
0548: }
0549:
0550: // create breakpoint requests for each class currently loaded
0551: List classes = target.jdiClassesByName(referenceTypeName);
0552: if (classes.isEmpty()
0553: && enclosingTypeName.equals(referenceTypeName)) {
0554: return;
0555: }
0556:
0557: boolean success = false;
0558: Iterator iter = classes.iterator();
0559: while (iter.hasNext()) {
0560: ReferenceType type = (ReferenceType) iter.next();
0561: if (createRequest(target, type)) {
0562: success = true;
0563: }
0564: }
0565:
0566: if (!success) {
0567: addToTargetForLocalType(target, enclosingTypeName);
0568: }
0569: }
0570:
0571: /**
0572: * Local types (types defined in methods) are handled specially due to the
0573: * different types that the local type is associated with as well as the
0574: * performance problems of using ReferenceType#nestedTypes. From the Java
0575: * model perspective a local type is defined within a method of a type.
0576: * Therefore the type of a breakpoint placed in a local type is the type
0577: * that encloses the method where the local type was defined.
0578: * The local type is enclosed within the top level type according
0579: * to the VM.
0580: * So if "normal" attempts to create a request when a breakpoint is
0581: * being added to a target fail, we must be dealing with a local type and therefore resort
0582: * to looking up all of the nested types of the top level enclosing type.
0583: */
0584: protected void addToTargetForLocalType(JDIDebugTarget target,
0585: String enclosingTypeName) throws CoreException {
0586: List classes = target.jdiClassesByName(enclosingTypeName);
0587: if (!classes.isEmpty()) {
0588: Iterator iter = classes.iterator();
0589: while (iter.hasNext()) {
0590: ReferenceType type = (ReferenceType) iter.next();
0591: Iterator nestedTypes = type.nestedTypes().iterator();
0592: while (nestedTypes.hasNext()) {
0593: ReferenceType nestedType = (ReferenceType) nestedTypes
0594: .next();
0595: if (createRequest(target, nestedType)) {
0596: break;
0597: }
0598: }
0599: }
0600: }
0601: }
0602:
0603: /**
0604: * Returns the JDI suspend policy that corresponds to this
0605: * breakpoint's suspend policy
0606: *
0607: * @return the JDI suspend policy that corresponds to this
0608: * breakpoint's suspend policy
0609: * @exception CoreException if unable to access this breakpoint's
0610: * suspend policy setting
0611: */
0612: protected int getJDISuspendPolicy() throws CoreException {
0613: int breakpointPolicy = getSuspendPolicy();
0614: if (breakpointPolicy == IJavaBreakpoint.SUSPEND_THREAD) {
0615: return EventRequest.SUSPEND_EVENT_THREAD;
0616: }
0617: return EventRequest.SUSPEND_ALL;
0618: }
0619:
0620: /**
0621: * returns the default suspend policy based on the pref setting on the
0622: * Java-Debug pref page
0623: * @return the default suspend policy
0624: * @since 3.2
0625: */
0626: protected int getDefaultSuspendPolicy() {
0627: Preferences store = JDIDebugModel.getPreferences();
0628: return store
0629: .getInt(JDIDebugPlugin.PREF_DEFAULT_BREAKPOINT_SUSPEND_POLICY);
0630: }
0631:
0632: /**
0633: * Returns whether the hitCount of this breakpoint is equal to the hitCount of
0634: * the associated request.
0635: */
0636: protected boolean hasHitCountChanged(EventRequest request)
0637: throws CoreException {
0638: int hitCount = getHitCount();
0639: Integer requestCount = (Integer) request.getProperty(HIT_COUNT);
0640: int oldCount = -1;
0641: if (requestCount != null) {
0642: oldCount = requestCount.intValue();
0643: }
0644: return hitCount != oldCount;
0645: }
0646:
0647: /**
0648: * Removes this breakpoint from the given target.
0649: */
0650: public void removeFromTarget(final JDIDebugTarget target)
0651: throws CoreException {
0652: removeRequests(target);
0653: Object removed = fFilteredThreadsByTarget.remove(target);
0654: boolean changed = removed != null;
0655: boolean markerExists = markerExists();
0656: if (!markerExists || (markerExists && getInstallCount() == 0)) {
0657: fInstalledTypeName = null;
0658: }
0659:
0660: // remove instance filters
0661: if (fInstanceFilters != null && !fInstanceFilters.isEmpty()) {
0662: for (int i = 0; i < fInstanceFilters.size(); i++) {
0663: IJavaObject object = (IJavaObject) fInstanceFilters
0664: .get(i);
0665: if (object.getDebugTarget().equals(target)) {
0666: fInstanceFilters.remove(i);
0667: changed = true;
0668: }
0669: }
0670: }
0671:
0672: // fire change notification if required
0673: if (changed) {
0674: fireChanged();
0675: }
0676:
0677: // notification
0678: fireRemoved(target);
0679: }
0680:
0681: /**
0682: * Remove all requests that this breakpoint has installed in the given
0683: * debug target.
0684: */
0685: protected void removeRequests(final JDIDebugTarget target)
0686: throws CoreException {
0687: // removing was previously done is a workspace runnable, but that is
0688: // not possible since it can be a resource callback (marker deletion) that
0689: // causes a breakpoint to be removed
0690: ArrayList requests = (ArrayList) getRequests(target).clone();
0691: // Iterate over a copy of the requests since this list of requests
0692: // can be changed in other threads which would cause an ConcurrentModificationException
0693: Iterator iter = requests.iterator();
0694: EventRequest req;
0695: while (iter.hasNext()) {
0696: req = (EventRequest) iter.next();
0697: try {
0698: if (target.isAvailable() && !isExpired(req)) { // cannot delete an expired request
0699: EventRequestManager manager = target
0700: .getEventRequestManager();
0701: if (manager != null) {
0702: manager.deleteEventRequest(req); // disable & remove
0703: }
0704: }
0705: } catch (VMDisconnectedException e) {
0706: if (target.isAvailable()) {
0707: JDIDebugPlugin.log(e);
0708: }
0709: } catch (RuntimeException e) {
0710: target.internalError(e);
0711: } finally {
0712: deregisterRequest(req, target);
0713: }
0714: }
0715: fRequestsByTarget.remove(target);
0716: }
0717:
0718: /**
0719: * Update the enabled state of the given request in the given target, which is associated
0720: * with this breakpoint. Set the enabled state of the request
0721: * to the enabled state of this breakpoint.
0722: */
0723: protected void updateEnabledState(EventRequest request,
0724: JDIDebugTarget target) throws CoreException {
0725: internalUpdateEnabledState(request, isEnabled(), target);
0726: }
0727:
0728: /**
0729: * Set the enabled state of the given request to the given
0730: * value, also taking into account instance filters.
0731: */
0732: protected void internalUpdateEnabledState(EventRequest request,
0733: boolean enabled, JDIDebugTarget target) {
0734: if (request.isEnabled() != enabled) {
0735: // change the enabled state
0736: try {
0737: // if the request has expired, do not disable.
0738: // BreakpointRequests that have expired cannot be deleted.
0739: if (!isExpired(request)) {
0740: request.setEnabled(enabled);
0741: }
0742: } catch (VMDisconnectedException e) {
0743: } catch (RuntimeException e) {
0744: target.internalError(e);
0745: }
0746: }
0747: }
0748:
0749: /**
0750: * Returns whether this breakpoint has expired.
0751: */
0752: public boolean isExpired() throws CoreException {
0753: return ensureMarker().getAttribute(EXPIRED, false);
0754: }
0755:
0756: /**
0757: * Returns whether the given request is expired
0758: */
0759: protected boolean isExpired(EventRequest request) {
0760: Boolean requestExpired = (Boolean) request.getProperty(EXPIRED);
0761: if (requestExpired == null) {
0762: return false;
0763: }
0764: return requestExpired.booleanValue();
0765: }
0766:
0767: /* (non-Javadoc)
0768: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#isInstalled()
0769: */
0770: public boolean isInstalled() throws CoreException {
0771: return ensureMarker().getAttribute(INSTALL_COUNT, 0) > 0;
0772: }
0773:
0774: /**
0775: * Increments the install count of this breakpoint
0776: */
0777: protected void incrementInstallCount() throws CoreException {
0778: int count = getInstallCount();
0779: setAttribute(INSTALL_COUNT, count + 1);
0780: }
0781:
0782: /**
0783: * Returns the <code>INSTALL_COUNT</code> attribute of this breakpoint
0784: * or 0 if the attribute is not set.
0785: */
0786: public int getInstallCount() throws CoreException {
0787: return ensureMarker().getAttribute(INSTALL_COUNT, 0);
0788: }
0789:
0790: /**
0791: * Decrements the install count of this breakpoint.
0792: */
0793: protected void decrementInstallCount() throws CoreException {
0794: int count = getInstallCount();
0795: if (count > 0) {
0796: setAttribute(INSTALL_COUNT, count - 1);
0797: }
0798: if (count == 1) {
0799: if (isExpired()) {
0800: // if breakpoint was auto-disabled, re-enable it
0801: setAttributes(fgExpiredEnabledAttributes, new Object[] {
0802: Boolean.FALSE, Boolean.TRUE });
0803: }
0804: }
0805: }
0806:
0807: /**
0808: * Sets the type name in which to install this breakpoint.
0809: */
0810: protected void setTypeName(String typeName) throws CoreException {
0811: setAttribute(TYPE_NAME, typeName);
0812: }
0813:
0814: /* (non-Javadoc)
0815: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#getTypeName()
0816: */
0817: public String getTypeName() throws CoreException {
0818: if (fInstalledTypeName == null) {
0819: return ensureMarker().getAttribute(TYPE_NAME, null);
0820: }
0821: return fInstalledTypeName;
0822: }
0823:
0824: /**
0825: * Resets the install count attribute on this breakpoint's marker
0826: * to "0". Resets the expired attribute on all breakpoint markers to <code>false</code>.
0827: * Resets the enabled attribute on the breakpoint marker to <code>true</code>.
0828: * If a workbench crashes, the attributes could have been persisted
0829: * in an incorrect state.
0830: */
0831: private void configureAtStartup() throws CoreException {
0832: List attributes = null;
0833: List values = null;
0834: if (isInstalled()) {
0835: attributes = new ArrayList(3);
0836: values = new ArrayList(3);
0837: attributes.add(INSTALL_COUNT);
0838: values.add(new Integer(0));
0839: }
0840: if (isExpired()) {
0841: if (attributes == null) {
0842: attributes = new ArrayList(3);
0843: values = new ArrayList(3);
0844: }
0845: // if breakpoint was auto-disabled, re-enable it
0846: attributes.add(EXPIRED);
0847: values.add(Boolean.FALSE);
0848: attributes.add(ENABLED);
0849: values.add(Boolean.TRUE);
0850: }
0851: if (attributes != null) {
0852: String[] strAttributes = new String[attributes.size()];
0853: setAttributes((String[]) attributes.toArray(strAttributes),
0854: values.toArray());
0855: }
0856: }
0857:
0858: /* (non-Javadoc)
0859: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#getHitCount()
0860: */
0861: public int getHitCount() throws CoreException {
0862: return ensureMarker().getAttribute(HIT_COUNT, -1);
0863: }
0864:
0865: /* (non-Javadoc)
0866: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#setHitCount(int)
0867: */
0868: public void setHitCount(int count) throws CoreException {
0869: if (getHitCount() != count) {
0870: if (!isEnabled() && count > -1) {
0871: setAttributes(new String[] { ENABLED, HIT_COUNT,
0872: EXPIRED }, new Object[] { Boolean.TRUE,
0873: new Integer(count), Boolean.FALSE });
0874: } else {
0875: setAttributes(new String[] { HIT_COUNT, EXPIRED },
0876: new Object[] { new Integer(count),
0877: Boolean.FALSE });
0878: }
0879: recreate();
0880: }
0881: }
0882:
0883: protected String getMarkerMessage(int hitCount, int suspendPolicy) {
0884: StringBuffer buff = new StringBuffer();
0885: if (hitCount > 0) {
0886: buff
0887: .append(MessageFormat
0888: .format(
0889: JDIDebugBreakpointMessages.JavaBreakpoint___Hit_Count___0___1,
0890: new Object[] { Integer
0891: .toString(hitCount) }));
0892: buff.append(' ');
0893: }
0894: String suspendPolicyString;
0895: if (suspendPolicy == IJavaBreakpoint.SUSPEND_THREAD) {
0896: suspendPolicyString = JDIDebugBreakpointMessages.JavaBreakpoint__suspend_policy__thread__1;
0897: } else {
0898: suspendPolicyString = JDIDebugBreakpointMessages.JavaBreakpoint__suspend_policy__VM__2;
0899: }
0900:
0901: buff.append(suspendPolicyString);
0902: return buff.toString();
0903: }
0904:
0905: /**
0906: * Sets whether this breakpoint's hit count has expired.
0907: */
0908: public void setExpired(boolean expired) throws CoreException {
0909: setAttribute(EXPIRED, expired);
0910: }
0911:
0912: /* (non-Javadoc)
0913: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#getSuspendPolicy()
0914: */
0915: public int getSuspendPolicy() throws CoreException {
0916: return ensureMarker().getAttribute(SUSPEND_POLICY,
0917: IJavaBreakpoint.SUSPEND_THREAD);
0918: }
0919:
0920: /* (non-Javadoc)
0921: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#setSuspendPolicy(int)
0922: */
0923: public void setSuspendPolicy(int suspendPolicy)
0924: throws CoreException {
0925: if (getSuspendPolicy() != suspendPolicy) {
0926: setAttribute(SUSPEND_POLICY, suspendPolicy);
0927: recreate();
0928: }
0929: }
0930:
0931: /**
0932: * Notifies listeners this breakpoint is to be added to the
0933: * given target.
0934: *
0935: * @param target debug target
0936: */
0937: protected void fireAdding(IJavaDebugTarget target) {
0938: JDIDebugPlugin plugin = JDIDebugPlugin.getDefault();
0939: if (plugin != null)
0940: plugin.fireBreakpointAdding(target, this );
0941: }
0942:
0943: /**
0944: * Notifies listeners this breakpoint has been removed from the
0945: * given target.
0946: *
0947: * @param target debug target
0948: */
0949: protected void fireRemoved(IJavaDebugTarget target) {
0950: JDIDebugPlugin plugin = JDIDebugPlugin.getDefault();
0951: if (plugin != null) {
0952: plugin.fireBreakpointRemoved(target, this );
0953: setInstalledIn(target, false);
0954: }
0955: }
0956:
0957: /**
0958: * Notifies listeners this breakpoint has been installed in the
0959: * given target.
0960: *
0961: * @param target debug target
0962: */
0963: protected void fireInstalled(IJavaDebugTarget target) {
0964: JDIDebugPlugin plugin = JDIDebugPlugin.getDefault();
0965: if (plugin != null && !isInstalledIn(target)) {
0966: plugin.fireBreakpointInstalled(target, this );
0967: setInstalledIn(target, true);
0968: }
0969: }
0970:
0971: /**
0972: * Returns whether this breakpoint is installed in the given target.
0973: *
0974: * @param target
0975: * @return whether this breakpoint is installed in the given target
0976: */
0977: protected boolean isInstalledIn(IJavaDebugTarget target) {
0978: return fInstalledTargets != null
0979: && fInstalledTargets.contains(target);
0980: }
0981:
0982: /**
0983: * Sets this breakpoint as installed in the given target
0984: *
0985: * @param target
0986: * @param installed whether installed
0987: */
0988: protected void setInstalledIn(IJavaDebugTarget target,
0989: boolean installed) {
0990: if (installed) {
0991: if (fInstalledTargets == null) {
0992: fInstalledTargets = new HashSet();
0993: }
0994: fInstalledTargets.add(target);
0995: } else {
0996: if (fInstalledTargets != null) {
0997: fInstalledTargets.remove(target);
0998: }
0999: }
1000: }
1001:
1002: /* (non-Javadoc)
1003: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#setThreadFilter(org.eclipse.jdt.debug.core.IJavaThread)
1004: */
1005: public void setThreadFilter(IJavaThread thread)
1006: throws CoreException {
1007: if (!(thread.getDebugTarget() instanceof JDIDebugTarget)
1008: || !(thread instanceof JDIThread)) {
1009: return;
1010: }
1011: JDIDebugTarget target = (JDIDebugTarget) thread
1012: .getDebugTarget();
1013: if (thread != fFilteredThreadsByTarget.put(target, thread)) {
1014: // recreate the breakpoint only if it is not the same thread
1015:
1016: // Other breakpoints set attributes on the underlying
1017: // marker and the marker changes are eventually
1018: // propagated to the target. The target then asks the
1019: // breakpoint to update its request. Since thread filters
1020: // are transient properties, they are not set on
1021: // the marker. Thus we must update the request
1022: // here.
1023: recreate(target);
1024: fireChanged();
1025: }
1026: }
1027:
1028: /* (non-Javadoc)
1029: * @see org.eclipse.debug.core.IDebugEventSetListener#handleDebugEvents(org.eclipse.debug.core.DebugEvent[])
1030: */
1031: public void handleDebugEvents(DebugEvent[] events) {
1032: for (int i = 0; i < events.length; i++) {
1033: DebugEvent event = events[i];
1034: if (event.getKind() == DebugEvent.TERMINATE) {
1035: Object source = event.getSource();
1036: if (!(source instanceof JDIThread)) {
1037: return;
1038: }
1039: try {
1040: cleanupForThreadTermination((JDIThread) source);
1041: } catch (VMDisconnectedException exception) {
1042: // Thread death often occurs at shutdown.
1043: // A VMDisconnectedException trying to
1044: // update the breakpoint request is
1045: // acceptable.
1046: }
1047: }
1048: }
1049: }
1050:
1051: /**
1052: * Removes cached information relevant to this thread which has
1053: * terminated.
1054: *
1055: * Remove thread filters for terminated threads
1056: *
1057: * Subclasses may override but need to call super.
1058: */
1059: protected void cleanupForThreadTermination(JDIThread thread) {
1060: JDIDebugTarget target = (JDIDebugTarget) thread
1061: .getDebugTarget();
1062: try {
1063: if (thread == getThreadFilter(target)) {
1064: removeThreadFilter(target);
1065: }
1066: } catch (CoreException exception) {
1067: JDIDebugPlugin.log(exception);
1068: }
1069: }
1070:
1071: /**
1072: * EventRequest does not support thread filters, so they
1073: * can't be set generically here. However, each of the breakpoint
1074: * subclasses of EventRequest do support thread filters. So
1075: * subclasses can set thread filters on their specific
1076: * request type.
1077: */
1078: protected abstract void setRequestThreadFilter(
1079: EventRequest request, ThreadReference thread);
1080:
1081: /* (non-Javadoc)
1082: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#getThreadFilter(org.eclipse.jdt.debug.core.IJavaDebugTarget)
1083: */
1084: public IJavaThread getThreadFilter(IJavaDebugTarget target) {
1085: return (IJavaThread) fFilteredThreadsByTarget.get(target);
1086: }
1087:
1088: /* (non-Javadoc)
1089: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#getThreadFilters()
1090: */
1091: public IJavaThread[] getThreadFilters() {
1092: IJavaThread[] threads = null;
1093: Collection values = fFilteredThreadsByTarget.values();
1094: threads = new IJavaThread[values.size()];
1095: values.toArray(threads);
1096: return threads;
1097: }
1098:
1099: /* (non-Javadoc)
1100: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#removeThreadFilter(org.eclipse.jdt.debug.core.IJavaDebugTarget)
1101: */
1102: public void removeThreadFilter(IJavaDebugTarget javaTarget)
1103: throws CoreException {
1104: if (!(javaTarget instanceof JDIDebugTarget)) {
1105: return;
1106: }
1107: JDIDebugTarget target = (JDIDebugTarget) javaTarget;
1108: if (fFilteredThreadsByTarget.remove(target) != null) {
1109: recreate(target);
1110: fireChanged();
1111: }
1112: }
1113:
1114: /**
1115: * Returns whether this breakpoint should be installed in the given reference
1116: * type in the given target according to registered breakpoint listeners.
1117: *
1118: * @param target debug target
1119: * @param type reference type or <code>null</code> if this breakpoint is
1120: * not installed in a specific type
1121: */
1122: protected boolean queryInstallListeners(JDIDebugTarget target,
1123: ReferenceType type) {
1124: JDIDebugPlugin plugin = JDIDebugPlugin.getDefault();
1125: if (plugin != null) {
1126: IJavaType jt = null;
1127: if (type != null) {
1128: jt = JDIType.createType(target, type);
1129: }
1130: return plugin.fireInstalling(target, this , jt);
1131: }
1132: return false;
1133: }
1134:
1135: /* (non-Javadoc)
1136: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#addInstanceFilter(org.eclipse.jdt.debug.core.IJavaObject)
1137: */
1138: public void addInstanceFilter(IJavaObject object)
1139: throws CoreException {
1140: if (fInstanceFilters == null) {
1141: fInstanceFilters = new ArrayList();
1142: }
1143: if (!fInstanceFilters.contains(object)) {
1144: fInstanceFilters.add(object);
1145: recreate((JDIDebugTarget) object.getDebugTarget());
1146: fireChanged();
1147: }
1148: }
1149:
1150: /**
1151: * Change notification when there are no marker changes. If the marker
1152: * does not exist, do not fire a change notification (the marker may not
1153: * exist if the associated project was closed).
1154: */
1155: protected void fireChanged() {
1156: DebugPlugin plugin = DebugPlugin.getDefault();
1157: if (plugin != null && markerExists()) {
1158: plugin.getBreakpointManager().fireBreakpointChanged(this );
1159: }
1160: }
1161:
1162: /* (non-Javadoc)
1163: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#getInstanceFilters()
1164: */
1165: public IJavaObject[] getInstanceFilters() {
1166: if (fInstanceFilters == null || fInstanceFilters.isEmpty()) {
1167: return fgEmptyInstanceFilters;
1168: }
1169: return (IJavaObject[]) fInstanceFilters
1170: .toArray(new IJavaObject[fInstanceFilters.size()]);
1171: }
1172:
1173: /* (non-Javadoc)
1174: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#removeInstanceFilter(org.eclipse.jdt.debug.core.IJavaObject)
1175: */
1176: public void removeInstanceFilter(IJavaObject object)
1177: throws CoreException {
1178: if (fInstanceFilters == null) {
1179: return;
1180: }
1181: if (fInstanceFilters.remove(object)) {
1182: recreate((JDIDebugTarget) object.getDebugTarget());
1183: fireChanged();
1184: }
1185: }
1186:
1187: /**
1188: * An attribute of this breakpoint has changed - recreate event requests in
1189: * all targets.
1190: */
1191: protected void recreate() throws CoreException {
1192: DebugPlugin plugin = DebugPlugin.getDefault();
1193: if (plugin != null) {
1194: IDebugTarget[] targets = plugin.getLaunchManager()
1195: .getDebugTargets();
1196: for (int i = 0; i < targets.length; i++) {
1197: IDebugTarget target = targets[i];
1198: MultiStatus multiStatus = new MultiStatus(
1199: JDIDebugPlugin.getUniqueIdentifier(),
1200: JDIDebugPlugin.ERROR,
1201: JDIDebugBreakpointMessages.JavaBreakpoint_Exception,
1202: null);
1203: IJavaDebugTarget jdiTarget = (IJavaDebugTarget) target
1204: .getAdapter(IJavaDebugTarget.class);
1205: if (jdiTarget instanceof JDIDebugTarget) {
1206: try {
1207: recreate((JDIDebugTarget) jdiTarget);
1208: } catch (CoreException e) {
1209: multiStatus.add(e.getStatus());
1210: }
1211: }
1212: if (!multiStatus.isOK()) {
1213: throw new CoreException(multiStatus);
1214: }
1215: }
1216: }
1217: }
1218:
1219: /**
1220: * Recreate this breakpoint in the given target, as long as the
1221: * target already contains this breakpoint.
1222: *
1223: * @param target the target in which to re-create the breakpoint
1224: */
1225: protected void recreate(JDIDebugTarget target) throws CoreException {
1226: if (target.isAvailable()
1227: && target.getBreakpoints().contains(this )) {
1228: removeRequests(target);
1229: createRequests(target);
1230: }
1231: }
1232:
1233: /* (non-Javadoc)
1234: * @see org.eclipse.debug.core.model.Breakpoint#setEnabled(boolean)
1235: */
1236: public void setEnabled(boolean enabled) throws CoreException {
1237: super .setEnabled(enabled);
1238: recreate();
1239: }
1240:
1241: /* (non-Javadoc)
1242: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#supportsInstanceFilters()
1243: */
1244: public boolean supportsInstanceFilters() {
1245: return true;
1246: }
1247:
1248: /* (non-Javadoc)
1249: * @see org.eclipse.jdt.debug.core.IJavaBreakpoint#supportsThreadFilters()
1250: */
1251: public boolean supportsThreadFilters() {
1252: return true;
1253: }
1254: }
|