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.ArrayList;
013: import java.util.HashSet;
014: import java.util.List;
015: import java.util.Map;
016: import java.util.Set;
017: import java.util.StringTokenizer;
018: import java.util.regex.Pattern;
019:
020: import org.eclipse.core.resources.IResource;
021: import org.eclipse.core.resources.IWorkspaceRunnable;
022: import org.eclipse.core.runtime.CoreException;
023: import org.eclipse.core.runtime.IProgressMonitor;
024: import org.eclipse.debug.core.DebugException;
025: import org.eclipse.debug.core.model.IBreakpoint;
026: import org.eclipse.jdt.debug.core.IJavaDebugTarget;
027: import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint;
028: import org.eclipse.jdt.debug.core.IJavaObject;
029: import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
030: import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
031: import org.eclipse.jdt.internal.debug.core.model.JDIThread;
032: import org.eclipse.jdt.internal.debug.core.model.JDIValue;
033:
034: import com.sun.jdi.ClassType;
035: import com.sun.jdi.Location;
036: import com.sun.jdi.ObjectReference;
037: import com.sun.jdi.ReferenceType;
038: import com.sun.jdi.ThreadReference;
039: import com.sun.jdi.VMDisconnectedException;
040: import com.sun.jdi.event.Event;
041: import com.sun.jdi.event.ExceptionEvent;
042: import com.sun.jdi.request.EventRequest;
043: import com.sun.jdi.request.EventRequestManager;
044: import com.sun.jdi.request.ExceptionRequest;
045:
046: public class JavaExceptionBreakpoint extends JavaBreakpoint implements
047: IJavaExceptionBreakpoint {
048:
049: private static final String JAVA_EXCEPTION_BREAKPOINT = "org.eclipse.jdt.debug.javaExceptionBreakpointMarker"; //$NON-NLS-1$
050:
051: /**
052: * Exception breakpoint attribute storing the suspend on caught value
053: * (value <code>"org.eclipse.jdt.debug.core.caught"</code>). This attribute is stored as a <code>boolean</code>.
054: * When this attribute is <code>true</code>, a caught exception of the associated
055: * type will cause execcution to suspend .
056: */
057: protected static final String CAUGHT = "org.eclipse.jdt.debug.core.caught"; //$NON-NLS-1$
058: /**
059: * Exception breakpoint attribute storing the suspend on uncaught value
060: * (value <code>"org.eclipse.jdt.debug.core.uncaught"</code>). This attribute is stored as a
061: * <code>boolean</code>. When this attribute is <code>true</code>, an uncaught
062: * exception of the associated type will cause excecution to suspend.
063: */
064: protected static final String UNCAUGHT = "org.eclipse.jdt.debug.core.uncaught"; //$NON-NLS-1$
065: /**
066: * Exception breakpoint attribute storing the checked value (value <code>"org.eclipse.jdt.debug.core.checked"</code>).
067: * This attribute is stored as a <code>boolean</code>, indicating whether an
068: * exception is a checked exception.
069: */
070: protected static final String CHECKED = "org.eclipse.jdt.debug.core.checked"; //$NON-NLS-1$
071:
072: /**
073: * Exception breakpoint attribute storing the String value (value <code>"org.eclipse.jdt.debug.core.filters"</code>).
074: * This attribute is stored as a <code>String</code>, a comma delimited list
075: * of class filters. The filters are applied as inclusion or exclusion depending on
076: * INCLUSIVE_FILTERS.
077: */
078: protected static final String INCLUSION_FILTERS = "org.eclipse.jdt.debug.core.inclusion_filters"; //$NON-NLS-1$
079:
080: /**
081: * Exception breakpoint attribute storing the String value (value <code>"org.eclipse.jdt.debug.core.filters"</code>).
082: * This attribute is stored as a <code>String</code>, a comma delimited list
083: * of class filters. The filters are applied as inclusion or exclusion depending on
084: * INCLUSIVE_FILTERS.
085: */
086: protected static final String EXCLUSION_FILTERS = "org.eclipse.jdt.debug.core.exclusion_filters"; //$NON-NLS-1$
087: /**
088: * Allows the user to specify whether we should suspend if subclasses of the specified exception are thrown/caught
089: * @since 3.2
090: */
091: protected static final String SUSPEND_ON_SUBCLASSES = "org.eclipse.jdt.debug.core.suspend_on_subclasses"; //$NON-NLS-1$
092:
093: /**
094: * Name of the exception that was actually hit (could be a
095: * subtype of the type that is being caught).
096: */
097: protected String fExceptionName = null;
098:
099: /**
100: * The current set of inclusion class filters.
101: */
102: protected String[] fInclusionClassFilters = null;
103:
104: /**
105: * The current set of inclusion class filters.
106: */
107: protected String[] fExclusionClassFilters = null;
108:
109: private ObjectReference fLastException;
110: private JDIDebugTarget fLastTarget;
111:
112: public JavaExceptionBreakpoint() {
113: }
114:
115: /**
116: * Creates and returns an exception breakpoint for the
117: * given (throwable) type. Caught and uncaught specify where the exception
118: * should cause thread suspensions - that is, in caught and/or uncaught locations.
119: * Checked indicates if the given exception is a checked exception.
120: * @param resource the resource on which to create the associated
121: * breakpoint marker
122: * @param exceptionName the fully qualified name of the exception for
123: * which to create the breakpoint
124: * @param caught whether to suspend in caught locations
125: * @param uncaught whether to suspend in uncaught locations
126: * @param checked whether the exception is a checked exception
127: * @param add whether to add this breakpoint to the breakpoint manager
128: * @return a Java exception breakpoint
129: * @exception DebugException if unable to create the associated marker due
130: * to a lower level exception.
131: */
132: public JavaExceptionBreakpoint(final IResource resource,
133: final String exceptionName, final boolean caught,
134: final boolean uncaught, final boolean checked,
135: final boolean add, final Map attributes)
136: throws DebugException {
137: IWorkspaceRunnable wr = new IWorkspaceRunnable() {
138:
139: public void run(IProgressMonitor monitor)
140: throws CoreException {
141: // create the marker
142: setMarker(resource
143: .createMarker(JAVA_EXCEPTION_BREAKPOINT));
144:
145: // add attributes
146: attributes.put(IBreakpoint.ID, getModelIdentifier());
147: attributes.put(TYPE_NAME, exceptionName);
148: attributes.put(ENABLED, Boolean.TRUE);
149: attributes.put(CAUGHT, Boolean.valueOf(caught));
150: attributes.put(UNCAUGHT, Boolean.valueOf(uncaught));
151: attributes.put(CHECKED, Boolean.valueOf(checked));
152: attributes.put(SUSPEND_POLICY, new Integer(
153: getDefaultSuspendPolicy()));
154:
155: ensureMarker().setAttributes(attributes);
156:
157: register(add);
158: }
159:
160: };
161: run(getMarkerRule(resource), wr);
162: }
163:
164: /**
165: * Creates a request in the given target to suspend when the given exception
166: * type is thrown. The request is returned installed, configured, and enabled
167: * as appropriate for this breakpoint.
168: */
169: protected EventRequest[] newRequests(JDIDebugTarget target,
170: ReferenceType type) throws CoreException {
171: if (!isCaught() && !isUncaught()) {
172: return null;
173: }
174: ExceptionRequest request = null;
175: EventRequestManager manager = target.getEventRequestManager();
176: if (manager == null) {
177: target
178: .requestFailed(
179: JDIDebugBreakpointMessages.JavaExceptionBreakpoint_Unable_to_create_breakpoint_request___VM_disconnected__1,
180: null);
181: return null;
182: }
183:
184: try {
185: request = manager.createExceptionRequest(type, isCaught(),
186: isUncaught());
187: configureRequest(request, target);
188: } catch (VMDisconnectedException e) {
189: if (target.isAvailable()) {
190: JDIDebugPlugin.log(e);
191: }
192: return null;
193: } catch (RuntimeException e) {
194: target.internalError(e);
195: return null;
196: }
197: return new EventRequest[] { request };
198: }
199:
200: /**
201: * Enable this exception breakpoint.
202: *
203: * If the exception breakpoint is not catching caught or uncaught,
204: * turn both modes on. If this isn't done, the resulting
205: * state (enabled with caught and uncaught both disabled)
206: * is ambiguous.
207: */
208: public void setEnabled(boolean enabled) throws CoreException {
209: if (enabled) {
210: if (!(isCaught() || isUncaught())) {
211: setAttributes(new String[] { CAUGHT, UNCAUGHT },
212: new Object[] { Boolean.TRUE, Boolean.TRUE });
213: }
214: }
215: super .setEnabled(enabled);
216: }
217:
218: /**
219: * Sets the values for whether this breakpoint will
220: * suspend execution when the associated exception is thrown
221: * and caught or not caught.
222: */
223: protected void setCaughtAndUncaught(boolean caught, boolean uncaught)
224: throws CoreException {
225: Object[] values = new Object[] { Boolean.valueOf(caught),
226: Boolean.valueOf(uncaught) };
227: String[] attributes = new String[] { CAUGHT, UNCAUGHT };
228: setAttributes(attributes, values);
229: }
230:
231: /**
232: * @see IJavaExceptionBreakpoint#isCaught()
233: */
234: public boolean isCaught() throws CoreException {
235: return ensureMarker().getAttribute(CAUGHT, false);
236: }
237:
238: /**
239: * @see IJavaExceptionBreakpoint#setCaught(boolean)
240: */
241: public void setCaught(boolean caught) throws CoreException {
242: if (caught == isCaught()) {
243: return;
244: }
245: setAttribute(CAUGHT, caught);
246: if (caught && !isEnabled()) {
247: setEnabled(true);
248: } else if (!(caught || isUncaught())) {
249: setEnabled(false);
250: }
251: recreate();
252: }
253:
254: /* (non-Javadoc)
255: * @see org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint#setSuspendOnSubclasses(boolean)
256: */
257: public void setSuspendOnSubclasses(boolean suspend)
258: throws CoreException {
259: if (suspend != isSuspendOnSubclasses()) {
260: setAttribute(SUSPEND_ON_SUBCLASSES, suspend);
261: recreate();
262: }
263: }
264:
265: /* (non-Javadoc)
266: * @see org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint#isSuspendOnSubclasses()
267: */
268: public boolean isSuspendOnSubclasses() throws CoreException {
269: return ensureMarker()
270: .getAttribute(SUSPEND_ON_SUBCLASSES, false);
271: }
272:
273: /**
274: * @see IJavaExceptionBreakpoint#isUncaught()
275: */
276: public boolean isUncaught() throws CoreException {
277: return ensureMarker().getAttribute(UNCAUGHT, false);
278: }
279:
280: /**
281: * @see IJavaExceptionBreakpoint#setUncaught(boolean)
282: */
283: public void setUncaught(boolean uncaught) throws CoreException {
284: if (uncaught == isUncaught()) {
285: return;
286: }
287: setAttribute(UNCAUGHT, uncaught);
288: if (uncaught && !isEnabled()) {
289: setEnabled(true);
290: } else if (!(uncaught || isCaught())) {
291: setEnabled(false);
292: }
293: recreate();
294: }
295:
296: /**
297: * @see IJavaExceptionBreakpoint#isChecked()
298: */
299: public boolean isChecked() throws CoreException {
300: return ensureMarker().getAttribute(CHECKED, false);
301: }
302:
303: /**
304: * @see JavaBreakpoint#setRequestThreadFilter(EventRequest)
305: */
306: protected void setRequestThreadFilter(EventRequest request,
307: ThreadReference thread) {
308: ((ExceptionRequest) request).addThreadFilter(thread);
309: }
310:
311: /**
312: * @see JavaBreakpoint#handleBreakpointEvent(Event, JDIDebugTarget, JDIThread)
313: * Decides how to handle an exception being thrown
314: *
315: * @return true if we do not want to suspend false otherwise
316: */
317: public boolean handleBreakpointEvent(Event event,
318: JDIDebugTarget target, JDIThread thread) {
319: if (event instanceof ExceptionEvent) {
320: ObjectReference ex = ((ExceptionEvent) event).exception();
321: fLastTarget = target;
322: fLastException = ex;
323: String name = null;
324: try {
325: name = ex.type().name();
326: if (!name.equals(getTypeName())) {
327: if (!isSuspendOnSubclasses()
328: & isSubclass((ClassType) ex.type(),
329: getTypeName())) {
330: return true;
331: }
332: }
333: } catch (VMDisconnectedException e) {
334: return true;
335: } catch (CoreException e) {
336: JDIDebugPlugin.log(e);
337: } catch (RuntimeException e) {
338: try {
339: target.targetRequestFailed(e.getMessage(), e);
340: } catch (DebugException de) {
341: JDIDebugPlugin.log(e);
342: return false;
343: }
344: }
345: setExceptionName(name);
346: if (getExclusionClassFilters().length >= 1
347: || getInclusionClassFilters().length >= 1
348: || filtersIncludeDefaultPackage(fInclusionClassFilters)
349: || filtersIncludeDefaultPackage(fExclusionClassFilters)) {
350: Location location = ((ExceptionEvent) event).location();
351: String typeName = location.declaringType().name();
352: boolean defaultPackage = typeName.indexOf('.') == -1;
353: boolean included = true;
354: String[] filters = getInclusionClassFilters();
355: if (filters.length > 0) {
356: included = matchesFilters(filters, typeName,
357: defaultPackage);
358: }
359: boolean excluded = false;
360: filters = getExclusionClassFilters();
361: if (filters.length > 0) {
362: excluded = matchesFilters(filters, typeName,
363: defaultPackage);
364: }
365: if (included && !excluded) {
366: return !suspend(thread);
367: }
368: return true;
369: }
370: return !suspend(thread);
371: }
372: return true;
373: }
374:
375: /**
376: * Returns whether the given class type is a subclass of the classs
377: * with the given name.
378: *
379: * @param type the class type reference
380: * @return true if the specified the class type is a subclass of the class
381: * with the given name
382: * @since 3.2
383: */
384: private boolean isSubclass(ClassType type, String typeName) {
385: type = type.super class();
386: while (type != null) {
387: if (type.name().equals(typeName)) {
388: return true;
389: }
390: type = type.super class();
391: }
392: return false;
393: }
394:
395: /* (non-Javadoc)
396: * @see org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint#setInstalledIn(org.eclipse.jdt.debug.core.IJavaDebugTarget, boolean)
397: */
398: protected void setInstalledIn(IJavaDebugTarget target,
399: boolean installed) {
400: fLastException = null;
401: fLastTarget = null;
402: super .setInstalledIn(target, installed);
403: }
404:
405: /**
406: * Determines of the filters for this exception include the default package or not
407: * @param filters the list of filters to inspect
408: * @return true if any one of the spcified filters include the default package
409: */
410: protected boolean filtersIncludeDefaultPackage(String[] filters) {
411: for (int i = 0; i < filters.length; i++) {
412: if (filters[i].length() == 0
413: || (filters[i].indexOf('.') == -1)) {
414: return true;
415: }
416: }
417: return false;
418: }
419:
420: /**
421: * Returns whether the given type is in the given filter set.
422: *
423: * @param filters the filter set
424: * @param typeName fully qualified type name
425: * @param defaultPackage whether the type name is in the default package
426: * @return boolean
427: */
428: protected boolean matchesFilters(String[] filters, String typeName,
429: boolean defaultPackage) {
430: for (int i = 0; i < filters.length; i++) {
431: String filter = filters[i];
432: if (defaultPackage && filter.length() == 0) {
433: return true;
434: }
435:
436: filter = filter.replaceAll("\\.", "\\\\."); //$NON-NLS-1$//$NON-NLS-2$
437: filter = filter.replaceAll("\\*", "\\.\\*"); //$NON-NLS-1$//$NON-NLS-2$
438: Pattern pattern = Pattern.compile(filter);
439: if (pattern.matcher(typeName).find()) {
440: return true;
441: }
442: }
443: return false;
444: }
445:
446: /**
447: * Sets the name of the exception that was last hit
448: *
449: * @param name fully qualified exception name
450: */
451: protected void setExceptionName(String name) {
452: fExceptionName = name;
453: }
454:
455: /* (non-Javadoc)
456: * @see org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint#getExceptionTypeName()
457: */
458: public String getExceptionTypeName() {
459: return fExceptionName;
460: }
461:
462: /**
463: * @see IJavaExceptionBreakpoint#getFilters()
464: * @deprecated
465: */
466: public String[] getFilters() {
467: String[] iFilters = getInclusionFilters();
468: String[] eFilters = getExclusionFilters();
469: String[] filters = new String[iFilters.length + eFilters.length];
470: System.arraycopy(iFilters, 0, filters, 0, iFilters.length);
471: System.arraycopy(eFilters, 0, filters, iFilters.length,
472: eFilters.length);
473: return filters;
474: }
475:
476: /**
477: * @see IJavaExceptionBreakpoint#setFilters(String[], boolean)
478: * @deprecated
479: */
480: public void setFilters(String[] filters, boolean inclusive)
481: throws CoreException {
482: if (inclusive) {
483: setInclusionFilters(filters);
484: } else {
485: setExclusionFilters(filters);
486: }
487: recreate();
488: }
489:
490: /* (non-Javadoc)
491: * @see org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint#configureRequest(com.sun.jdi.request.EventRequest, org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget)
492: */
493: protected void configureRequest(EventRequest eRequest,
494: JDIDebugTarget target) throws CoreException {
495: String[] iFilters = getInclusionClassFilters();
496: String[] eFilters = getExclusionClassFilters();
497:
498: ExceptionRequest request = (ExceptionRequest) eRequest;
499:
500: if (iFilters.length == 1) {
501: if (eFilters.length == 0) {
502: request.addClassFilter(iFilters[0]);
503: }
504: } else if (eFilters.length == 1) {
505: if (iFilters.length == 0) {
506: request.addClassExclusionFilter(eFilters[0]);
507: }
508: }
509:
510: super .configureRequest(eRequest, target);
511: }
512:
513: /**
514: * Serializes the array of Strings into one comma
515: * separated String.
516: * Removes duplicates.
517: */
518: protected String serializeList(String[] list) {
519: if (list == null) {
520: return ""; //$NON-NLS-1$
521: }
522: Set set = new HashSet(list.length);
523:
524: StringBuffer buffer = new StringBuffer();
525: for (int i = 0; i < list.length; i++) {
526: if (i > 0) {
527: buffer.append(',');
528: }
529: String pattern = list[i];
530: if (!set.contains(pattern)) {
531: if (pattern.length() == 0) {
532: //serialize the default package
533: pattern = "."; //$NON-NLS-1$
534: }
535: buffer.append(pattern);
536: }
537: }
538: return buffer.toString();
539: }
540:
541: /**
542: * Parses the comma separated String into an array of Strings
543: */
544: protected String[] parseList(String listString) {
545: List list = new ArrayList(10);
546: StringTokenizer tokenizer = new StringTokenizer(listString, ","); //$NON-NLS-1$
547: while (tokenizer.hasMoreTokens()) {
548: String token = tokenizer.nextToken();
549: if (token.equals(".")) { //$NON-NLS-1$
550: //serialized form for the default package
551: //@see serializeList(String[])
552: token = ""; //$NON-NLS-1$
553: }
554: list.add(token);
555: }
556: return (String[]) list.toArray(new String[list.size()]);
557: }
558:
559: /**
560: * @see IJavaExceptionBreakpoint#isInclusiveFiltered()
561: * @deprecated
562: */
563: public boolean isInclusiveFiltered() throws CoreException {
564: return ensureMarker()
565: .getAttribute(INCLUSION_FILTERS, "").length() > 0; //$NON-NLS-1$
566: }
567:
568: protected String[] getInclusionClassFilters() {
569: if (fInclusionClassFilters == null) {
570: try {
571: fInclusionClassFilters = parseList(ensureMarker()
572: .getAttribute(INCLUSION_FILTERS, "")); //$NON-NLS-1$
573: } catch (CoreException ce) {
574: fInclusionClassFilters = new String[] {};
575: }
576: }
577: return fInclusionClassFilters;
578: }
579:
580: protected void setInclusionClassFilters(String[] filters) {
581: fInclusionClassFilters = filters;
582: }
583:
584: protected String[] getExclusionClassFilters() {
585: if (fExclusionClassFilters == null) {
586: try {
587: fExclusionClassFilters = parseList(ensureMarker()
588: .getAttribute(EXCLUSION_FILTERS, "")); //$NON-NLS-1$
589: } catch (CoreException ce) {
590: fExclusionClassFilters = new String[] {};
591: }
592: }
593: return fExclusionClassFilters;
594: }
595:
596: protected void setExclusionClassFilters(String[] filters) {
597: fExclusionClassFilters = filters;
598: }
599:
600: /**
601: * @see JavaBreakpoint#installableReferenceType(ReferenceType, JDIDebugTarget)
602: */
603: protected boolean installableReferenceType(ReferenceType type,
604: JDIDebugTarget target) throws CoreException {
605: String installableType = getTypeName();
606: String queriedType = type.name();
607: if (installableType == null || queriedType == null) {
608: return false;
609: }
610: if (installableType.equals(queriedType)) {
611: return queryInstallListeners(target, type);
612: }
613:
614: return false;
615: }
616:
617: /**
618: * @see org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint#getExclusionFilters()
619: */
620: public String[] getExclusionFilters() {
621: return getExclusionClassFilters();
622: }
623:
624: /**
625: * @see org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint#getInclusionFilters()
626: */
627: public String[] getInclusionFilters() {
628: return getInclusionClassFilters();
629: }
630:
631: /**
632: * @see org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint#setExclusionFilters(String[])
633: */
634: public void setExclusionFilters(String[] filters)
635: throws CoreException {
636: String serializedFilters = serializeList(filters);
637:
638: if (serializedFilters.equals(ensureMarker().getAttribute(
639: EXCLUSION_FILTERS, ""))) { //$NON-NLS-1$
640: //no change
641: return;
642: }
643:
644: setExclusionClassFilters(filters);
645:
646: setAttribute(EXCLUSION_FILTERS, serializedFilters);
647: recreate();
648: }
649:
650: /**
651: * @see org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint#setInclusionFilters(String[])
652: */
653: public void setInclusionFilters(String[] filters)
654: throws CoreException {
655: String serializedFilters = serializeList(filters);
656:
657: if (serializedFilters.equals(ensureMarker().getAttribute(
658: INCLUSION_FILTERS, ""))) { //$NON-NLS-1$
659: //no change
660: return;
661: }
662:
663: setInclusionClassFilters(filters);
664:
665: setAttribute(INCLUSION_FILTERS, serializedFilters);
666: recreate();
667: }
668:
669: /**
670: * @see org.eclipse.jdt.internal.debug.core.breakpoints.JavaBreakpoint#addInstanceFilter(EventRequest, ObjectReference)
671: */
672: protected void addInstanceFilter(EventRequest request,
673: ObjectReference object) {
674: if (request instanceof ExceptionRequest) {
675: ((ExceptionRequest) request).addInstanceFilter(object);
676: }
677: }
678:
679: /**
680: * Returns the last exception object that was encountered by this exception
681: *
682: * TODO: make API in future release.
683: *
684: * @return
685: */
686: public IJavaObject getLastException() {
687: if (fLastException != null) {
688: return (IJavaObject) JDIValue.createValue(fLastTarget,
689: fLastException);
690: }
691: return null;
692: }
693: }
|