blob: f6b85b71884b06da7ea99393ae07c13f7394e4e0 [file] [log] [blame]
package org.eclipse.jdt.debug.core;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
import org.eclipse.jdt.internal.debug.core.JDIDebugTarget;
import org.eclipse.jdt.internal.debug.core.JavaExceptionBreakpoint;
import org.eclipse.jdt.internal.debug.core.JavaLineBreakpoint;
import org.eclipse.jdt.internal.debug.core.JavaMethodEntryBreakpoint;
import org.eclipse.jdt.internal.debug.core.JavaPatternBreakpoint;
import org.eclipse.jdt.internal.debug.core.JavaRunToLineBreakpoint;
import org.eclipse.jdt.internal.debug.core.JavaWatchpoint;
import com.sun.jdi.VirtualMachine;
/**
* The JDI debug model plug-in provides an implementation of a debug
* model based on the standard "Java Debug Interface" (JDI). This class provides utility
* methods for creating debug targets and breakpoints specific to the JDI debug
* model.
* <p>
* To provide access to behavior and information specific to the JDI
* debug model, a set of interfaces are defined which extend the base
* set of debug element interfaces. For example, <code>IJavaStackFrame</code>
* is declared to extend <code>IStackFrame</code>, and provides methods
* specific to this debug model. The specialized interfaces are also
* available as adapters from the debug elements generated from this
* model.
* </p>
* <p>
* Clients are not intended to instantiate or subclass this class;
* this class provides static utility methods only.
* </p>
* <b>Note:</b> This class/interface is part of an interim API that is still under development and expected to
* change significantly before reaching stability. It is being made available at this early stage to solicit feedback
* from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
* (repeatedly) as the API evolves.
* </p>
* @see IJavaDebugTarget
* @see IJavaThread
* @see IJavaStackFrame
* @see IJavaVariable
*/
public class JDIDebugModel {
/**
* The default list of active step filters
*/
private static ArrayList fgDefaultActiveStepFilters = new ArrayList(7);
static {
fgDefaultActiveStepFilters.add("com.ibm.*"); //$NON-NLS-1$
fgDefaultActiveStepFilters.add("com.sun.*"); //$NON-NLS-1$
fgDefaultActiveStepFilters.add("java.*"); //$NON-NLS-1$
fgDefaultActiveStepFilters.add("javax.*"); //$NON-NLS-1$
fgDefaultActiveStepFilters.add("org.omg.*"); //$NON-NLS-1$
fgDefaultActiveStepFilters.add("sun.*"); //$NON-NLS-1$
fgDefaultActiveStepFilters.add("sunw.*"); //$NON-NLS-1$
}
/**
* State variables for step filters
*/
private static boolean fStepFiltersModified = false;
private static Properties fStepFilterProperties;
private static boolean fUseStepFilters = true;
private static boolean fFilterSynthetic = true;
private static boolean fFilterStatic = true;
private static boolean fFilterConstructor = true;
private static List fActiveStepFilterList;
private static List fInactiveStepFilterList;
/**
* Constants used for persisting step filter state
*/
private static final String STEP_FILTERS_FILE_NAME = "stepFilters.ini"; //$NON-NLS-1$
private static final String STEP_FILTER_PROPERTIES_HEADER = " Step filter properties"; //$NON-NLS-1$
private static final String USE_FILTERS_KEY = "use_filters"; //$NON-NLS-1$
private static final String FILTER_SYNTHETIC_KEY = "filter_synthetic"; //$NON-NLS-1$
private static final String FILTER_STATIC_KEY = "filter_static"; //$NON-NLS-1$
private static final String FILTER_CONSTRUCTOR_KEY = "filter_constructor"; //$NON-NLS-1$
private static final String ACTIVE_FILTERS_KEY = "active_filters"; //$NON-NLS-1$
private static final String INACTIVE_FILTERS_KEY = "inactive_filters"; //$NON-NLS-1$
/**
* Not to be instantiated.
*/
private JDIDebugModel() {
super();
}
//XXX Fix all hit count comments after Bug#1740 is addressed
/**
* Creates and returns a debug target for the given VM, with
* the specified name, and associates the debug target with the
* given process for console I/O. The allow terminate flag specifies whether
* the debug target will support termination (<code>ITerminate</code>).
* The allow disconnect flag specifies whether the debug target will
* support disconnection (<code>IDisconnect</code>). Launching the actual
* VM is a client responsibility.
*
* @param vm the VM to create a debug target for
* @param name the name to associate with the VM, which will be
* returned from <code>IDebugTarget.getName</code>. If <code>null</code>
* the name will be retrieved from the underlying VM.
* @param process the process to associate with the debug target,
* which will be returned from <code>IDebugTarget.getProcess</code>
* @param allowTerminate whether the target will support termianation
* @param allowDisconnect whether the target will support disconnection
* @return a debug target
* @see org.eclipse.debug.core.model.ITerminate
* @see org.eclipse.debug.core.model.IDisconnect
*/
public static IDebugTarget newDebugTarget(final VirtualMachine vm, final String name, final IProcess process, final boolean allowTerminate, final boolean allowDisconnect) {
final IJavaDebugTarget[] target = new IJavaDebugTarget[1];
IWorkspaceRunnable r = new IWorkspaceRunnable() {
public void run(IProgressMonitor m) {
target[0]= new JDIDebugTarget(vm, name, allowTerminate, allowDisconnect, process);
}
};
try {
ResourcesPlugin.getWorkspace().run(r, null);
} catch (CoreException e) {
JDIDebugPlugin.logError(e);
}
return target[0];
}
/**
* Returns the identifier for the JDI debug model plugin
*
* @return plugin identifier
*/
public static String getPluginIdentifier() {
return JDIDebugPlugin.getDefault().getDescriptor().getUniqueIdentifier();
}
/**
* Creates and returns a line breakpoint in the
* given type, at the given line number. If a character range within the
* line is known, it may be specified by charStart/charEnd.
* If hitCount is > 0, the breakpoint will suspend execution when it is
* "hit" the specified number of times.
*
* @param type the type in which to create the breakpoint
* @param lineNumber the lineNumber on which the breakpoint is created - line
* numbers are 1 based, associated with the compilation unit in which
* the type is defined
* @param charStart the first character index associated with the breakpoint,
* or -1 if unspecified
* @param charEnd the last character index associated with the breakpoint,
* or -1 if unspecified
* @param hitCount the number of times the breakpoint will be hit before
* suspending execution - 0 if it should always suspend
* @return a line breakpoint
* @exception DebugException If this method fails. Reasons include:<ul>
*<li>Failure creating underlying marker. The DebugException's status contains
* the underlying exception responsible for the failure.</li></ul>
*/
public static IJavaLineBreakpoint createLineBreakpoint(IType type, int lineNumber, int charStart, int charEnd, int hitCount) throws DebugException {
return new JavaLineBreakpoint(type, lineNumber, charStart, charEnd, hitCount);
}
/**
* Creates and returns a pattern breakpoint for the given resource at the
* given line number, which is installed in all classes whose fully
* qualified name matches the given pattern.
* If hitCount > 0, the breakpoint will suspend execution when it is
* "hit" the specified number of times.
* @param resource the original source file
* @param pattern the class name pattern in which the pattern breakpoint should
* be installed. The pattern breakpoint will install itself in every class which
* matches the pattern.
* @param lineNumber the line number on which this breakpoint should be placed.
* Note that the line number refers to the debug attributes in the generated
* class file. Generally, this refers to a line number in the original
* source, but the attribute is client defined.
* @param hitCount the number of times the breakpoint will be hit before
* suspending execution - 0 if it should always suspend
* @return a pattern breakpoint
* @exception DebugException If this method fails. Reasons include:<ul>
*<li>Failure creating underlying marker. The DebugException's status contains
* the underlying exception responsible for the failure.</li></ul>
*/
public static IJavaPatternBreakpoint createPatternBreakpoint(IResource resource, String pattern, int lineNumber, int hitCount) throws DebugException {
return new JavaPatternBreakpoint(resource, pattern, lineNumber, hitCount);
}
/**
* Creates and returns a run-to-line breakpoint in the
* given type, at the given line number. If a character range within the
* line is known, it may be specified by charStart/charEnd. Run-to-line
* breakpoints have a hit count of 1.
*
* @param type the type in which to create the breakpoint
* @param lineNumber the lineNumber on which the breakpoint is created - line
* numbers are 1 based, associated with the compilation unit in which
* the type is defined
* @param charStart the first character index associated with the breakpoint,
* or -1 if unspecified
* @param charEnd the last character index associated with the breakpoint,
* or -1 if unspecified
* @return a run-to-line breakpoint
* @exception DebugException If this method fails. Reasons include:<ul>
*<li>Failure creating underlying marker. The DebugException's status contains
* the underlying exception responsible for the failure.</li></ul>
*/
public static IJavaRunToLineBreakpoint createRunToLineBreakpoint(IType type, int lineNumber, int charStart, int charEnd) throws DebugException {
return new JavaRunToLineBreakpoint(type, lineNumber, charStart, charEnd);
}
/**
* Creates and returns an exception breakpoint for the
* given (throwable) type. Caught and uncaught specify where the exception
* should cause thread suspensions - that is, in caught and/or uncaught locations.
* Checked indicates if the given exception is a checked exception.
*
* @param type the exception for which to create the breakpoint
* @param caught whether to suspend in caught locations
* @param uncaught whether to suspend in uncaught locations
* @param checked whether the exception is a checked exception
* @return an exception breakpoint
* @exception DebugException If this method fails. Reasons include:<ul>
*<li>Failure creating underlying marker. The DebugException's status contains
* the underlying exception responsible for the failure.</li></ul>
*/
public static IJavaExceptionBreakpoint createExceptionBreakpoint(IType exception, boolean caught, boolean uncaught, boolean checked) throws DebugException {
return new JavaExceptionBreakpoint(exception, caught, uncaught, checked);
}
/**
* Creates and returns a watchpoint on the
* given field.
* If hitCount > 0, the breakpoint will suspend execution when it is
* "hit" the specified number of times.
*
* @param field the field on which to suspend (on access or modification)
* @param hitCount the number of times the breakpoint will be hit before
* suspending execution - 0 if it should always suspend
* @return a watchpoint
* @exception DebugException If this method fails. Reasons include:<ul>
*<li>Failure creating underlying marker. The DebugException's status contains
* the underlying exception responsible for the failure.</li></ul>
*/
public static IJavaWatchpoint createWatchpoint(IField field, int hitCount) throws DebugException {
return new JavaWatchpoint(field, hitCount);
}
/**
* Creates and returns a method entry breakpoint in the
* given binary method.
* If hitCount is > 0, the breakpoint will suspend execution when it is
* "hit" the specified number of times.
*
* @param method the method in which to suspend on entry
* @param hitCount the number of times the breakpoint will be hit before
* suspending execution - 0 if it should always suspend
* @return a method entry breakpoint
* @exception DebugException If this method fails. Reasons include:<ul>
*<li>Failure creating underlying marker. The DebugException's status contains
* the underlying exception responsible for the failure.</li></ul>
*/
public static IJavaMethodEntryBreakpoint createMethodEntryBreakpoint(final IMethod method, final int hitCount) throws DebugException {
return new JavaMethodEntryBreakpoint(method, hitCount);
}
/**
* Returns whether a line breakpoint already exists on the given line number in the
* given type.
*
* @param containingType the type in which to check for a line breakpoint
* @param lineNumber the line number on which to check for a line breakpoint
* @return whether a line breakpoint already exists on the given line number in
* the given type.
* @exception CoreException if unable to retrieve the associated marker
* attributes (line number).
*/
public static boolean lineBreakpointExists(IType containingType, int lineNumber) throws CoreException {
String modelId= getPluginIdentifier();
String markerType= JavaLineBreakpoint.getMarkerType();
IBreakpointManager manager= DebugPlugin.getDefault().getBreakpointManager();
IBreakpoint[] breakpoints= manager.getBreakpoints(modelId);
for (int i = 0; i < breakpoints.length; i++) {
if (!(breakpoints[i] instanceof IJavaLineBreakpoint)) {
continue;
}
IJavaLineBreakpoint breakpoint = (IJavaLineBreakpoint) breakpoints[i];
if (breakpoint.getMarker().getType().equals(markerType)) {
if (breakpoint.getType().equals(containingType)) {
if (breakpoint.getLineNumber() == lineNumber) {
return true;
}
}
}
}
return false;
}
/**
* Returns whether to use step filters.
*
* @return whether to use step filters
*/
public static boolean useStepFilters() {
return fUseStepFilters;
}
/**
* Sets whether to use step filters.
*
* @param useFilters whether to use step filters
*/
public static void setUseStepFilters(boolean useFilters) {
fUseStepFilters = useFilters;
setStepFiltersModified(true);
}
/**
* Returns whether to filter synthetic methods.
*
* @return whether to filter synthetic methods
*/
public static boolean getFilterSynthetic() {
return fFilterSynthetic;
}
/**
* Sets whether to use filter synthetic methods.
*
* @param filter whether to filter synthetic methods
*/
public static void setFilterSynthetic(boolean filter) {
fFilterSynthetic = filter;
setStepFiltersModified(true);
}
/**
* Returns whether to filter static initializers.
*
* @return whether to filter static initializers
*/
public static boolean getFilterStatic() {
return fFilterStatic;
}
/**
* Sets whether to use filter static initializers.
*
* @param filter whether to filter static initializers
*/
public static void setFilterStatic(boolean filter) {
fFilterStatic = filter;
setStepFiltersModified(true);
}
/**
* Returns whether to filter constructors.
*
* @return whether to filter constructors
*/
public static boolean getFilterConstructor() {
return fFilterConstructor;
}
/**
* Sets whether to use filter constructors.
*
* @param filter whether to filter constructors
*/
public static void setFilterConstructor(boolean filter) {
fFilterConstructor = filter;
setStepFiltersModified(true);
}
/**
* Returns the list of active step filters.
*
* @return the list of active step filters
*/
public static List getActiveStepFilters() {
return fActiveStepFilterList;
}
/**
* Sets the list of active step filters and sets the
* flag that the step filters have been modified.
*
* @param list The list to be the active step filters
*/
public static void setActiveStepFilters(List list) {
fActiveStepFilterList = list;
setStepFiltersModified(true);
}
/**
* Returns the list of inactive step filters.
*
* @return The list of inactive step filters
*/
public static List getInactiveStepFilters() {
return fInactiveStepFilterList;
}
/**
* Sets the list of inactive step filters and sets the
* flag that the step filters have been modified.
*
* @param list The list to be the inactive step filters
*/
public static void setInactiveStepFilters(List list) {
fInactiveStepFilterList = list;
setStepFiltersModified(true);
}
/**
* Returns the list of all step filters...both inactive and
* active.
*
* @return The list of all step filters.
*/
public static List getAllStepFilters() {
ArrayList concat = new ArrayList(getActiveStepFilters());
concat.addAll(getInactiveStepFilters());
return concat;
}
/**
* Loads the step filter state file if it exists, otherwise initializes
* the state to the specified default values.
*/
public static void setupStepFilterState() {
setStepFilterProperties(new Properties());
File stepFilterFile = JDIDebugPlugin.getDefault().getStateLocation().append(STEP_FILTERS_FILE_NAME).toFile();
if (stepFilterFile.exists()) {
readStepFilterState(stepFilterFile);
} else {
initializeFilters();
}
}
private static void initializeFilters() {
setUseStepFilters(getDefaultUseStepFilters());
setActiveStepFilters(getDefaultActiveStepFilters());
setInactiveStepFilters(getDefaultInactiveStepFilters());
}
/**
* Returns the default list of active step filters.
*
* @return default list of active step filters
*/
public static List getDefaultActiveStepFilters() {
return fgDefaultActiveStepFilters;
}
/**
* Returns the default list of inactive step filters.
*
* @return default list of inactive step filters
*/
public static List getDefaultInactiveStepFilters() {
return new ArrayList(0);
}
/**
* Returns whether to use step filters by default.
* Always returns <code>true</code>
*
* @return whether to use step filters by default.
*/
public static boolean getDefaultUseStepFilters() {
return true;
}
/**
* Returns whether to use filter synthetic methods by default.
* Always returns <code>true</code>
*
* @return whether to use filter synethic methods by default.
*/
public static boolean getDefaultFilterSynthetic() {
return true;
}
/**
* Returns whether to use filter static initializers by default.
* Always returns <code>true</code>
*
* @return whether to use filter static initializers by default.
*/
public static boolean getDefaultFilterStatic() {
return false;
}
/**
* Returns whether to use filter constructors by default.
* Always returns <code>true</code>
*
* @return whether to use filter constructors by default.
*/
public static boolean getDefaultFilterConstructor() {
return false;
}
/**
* Read the step filter state stored in the given File (which is assumed
* to be a java.util.Properties style file), and parse the String values into
* the appropriate data structures.
*
* @param file The file to read from
*/
private static void readStepFilterState(File file) {
FileInputStream fis= null;
try {
fis = new FileInputStream(file);
getStepFilterProperties().load(fis);
} catch (IOException ioe) {
JDIDebugPlugin.logError(ioe);
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException ie) {
JDIDebugPlugin.logError(ie);
}
}
setUseStepFilters(parseBoolean(getStepFilterProperties().getProperty(USE_FILTERS_KEY, "true"))); //$NON-NLS-1$
setFilterSynthetic(parseBoolean(getStepFilterProperties().getProperty(FILTER_SYNTHETIC_KEY, "true"))); //$NON-NLS-1$
setFilterStatic(parseBoolean(getStepFilterProperties().getProperty(FILTER_STATIC_KEY, "false"))); //$NON-NLS-1$
setFilterConstructor(parseBoolean(getStepFilterProperties().getProperty(FILTER_CONSTRUCTOR_KEY, "false"))); //$NON-NLS-1$
setActiveStepFilters(parseList(getStepFilterProperties().getProperty(ACTIVE_FILTERS_KEY, ""))); //$NON-NLS-1$
setInactiveStepFilters(parseList(getStepFilterProperties().getProperty(INACTIVE_FILTERS_KEY, ""))); //$NON-NLS-1$
setStepFiltersModified(false);
}
private static boolean parseBoolean(String booleanString) {
if (booleanString.toLowerCase().startsWith("f")) { //$NON-NLS-1$
return false;
}
return true;
}
private static List parseList(String listString) {
List list = new ArrayList(listString.length() + 1);
StringTokenizer tokenizer = new StringTokenizer(listString, ","); //$NON-NLS-1$
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
list.add(token);
}
return list;
}
/**
* Saves the current step filter state only if the state
* has been modified from that state stored on disk.
*/
public static void saveStepFilterState() {
if (!stepFiltersModified()) {
return;
}
File file = JDIDebugPlugin.getDefault().getStateLocation().append(STEP_FILTERS_FILE_NAME).toFile();
FileOutputStream fos= null;
try {
getStepFilterProperties().setProperty(USE_FILTERS_KEY, serializeBoolean(fUseStepFilters));
getStepFilterProperties().setProperty(FILTER_SYNTHETIC_KEY, serializeBoolean(fFilterSynthetic));
getStepFilterProperties().setProperty(FILTER_STATIC_KEY, serializeBoolean(fFilterStatic));
getStepFilterProperties().setProperty(FILTER_CONSTRUCTOR_KEY, serializeBoolean(fFilterConstructor));
getStepFilterProperties().setProperty(ACTIVE_FILTERS_KEY, serializeList(fActiveStepFilterList));
getStepFilterProperties().setProperty(INACTIVE_FILTERS_KEY, serializeList(fInactiveStepFilterList));
fos = new FileOutputStream(file);
getStepFilterProperties().store(fos, STEP_FILTER_PROPERTIES_HEADER);
} catch (IOException ioe) {
JDIDebugPlugin.logError(ioe);
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException ie) {
JDIDebugPlugin.logError(ie);
}
}
}
private static String serializeBoolean(boolean bool) {
if (bool) {
return Boolean.TRUE.toString();
}
return Boolean.FALSE.toString();
}
private static String serializeList(List list) {
if (list == null) {
return ""; //$NON-NLS-1$
}
StringBuffer buffer = new StringBuffer();
Iterator iterator = list.iterator();
int count = 0;
while (iterator.hasNext()) {
if (count > 0) {
buffer.append(',');
}
buffer.append((String)iterator.next());
count++;
}
return buffer.toString();
}
private static Properties getStepFilterProperties() {
return fStepFilterProperties;
}
private static void setStepFilterProperties(Properties stepFilterProperties) {
fStepFilterProperties = stepFilterProperties;
}
private static boolean stepFiltersModified() {
return fStepFiltersModified;
}
private static void setStepFiltersModified(boolean stepFiltersModified) {
fStepFiltersModified = stepFiltersModified;
}
}