| package org.eclipse.jdt.internal.debug.ui.actions; |
| |
| /* |
| * (c) Copyright IBM Corp. 2002. |
| * All Rights Reserved. |
| */ |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.search.SearchEngine; |
| import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint; |
| import org.eclipse.jdt.internal.debug.ui.ExceptionHandler; |
| import org.eclipse.jdt.internal.debug.ui.Filter; |
| import org.eclipse.jdt.internal.debug.ui.FilterLabelProvider; |
| import org.eclipse.jdt.internal.debug.ui.FilterViewerSorter; |
| import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; |
| import org.eclipse.jdt.ui.IJavaElementSearchConstants; |
| import org.eclipse.jdt.ui.JavaUI; |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.ProgressMonitorDialog; |
| import org.eclipse.jface.preference.FieldEditor; |
| import org.eclipse.jface.viewers.ColumnLayoutData; |
| import org.eclipse.jface.viewers.ColumnWeightData; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredContentProvider; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.TableLayout; |
| import org.eclipse.jface.viewers.TableViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.TableEditor; |
| import org.eclipse.swt.events.FocusAdapter; |
| import org.eclipse.swt.events.FocusEvent; |
| import org.eclipse.swt.events.KeyAdapter; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.graphics.FontMetrics; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.Table; |
| import org.eclipse.swt.widgets.TableColumn; |
| import org.eclipse.swt.widgets.TableItem; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.ui.dialogs.SelectionDialog; |
| |
| public class ExceptionBreakpointFilterEditor extends FieldEditor { |
| |
| private IJavaExceptionBreakpoint fBreakpoint; |
| private Button fAddFilterButton; |
| private Button fAddPackageButton; |
| private Button fAddTypeButton; |
| private Button fRemoveFilterButton; |
| private Text fEditorText; |
| private String fInvalidEditorText= null; |
| private TableEditor fTableEditor; |
| private TableItem fNewTableItem; |
| private Filter fNewFilter; |
| private Label fTableLabel; |
| private TableViewer fFilterViewer; |
| private Table fFilterTable; |
| private Composite fOuter; |
| |
| private FilterContentProvider fFilterContentProvider; |
| |
| /** |
| * Content provider for the table. Content consists of instances of Filter. |
| */ |
| protected class FilterContentProvider implements IStructuredContentProvider { |
| |
| private TableViewer fViewer; |
| private List fFilters; |
| |
| public FilterContentProvider(TableViewer viewer) { |
| fViewer = viewer; |
| populateFilters(); |
| } |
| |
| protected void populateFilters() { |
| String[] filters= null; |
| try { |
| filters = fBreakpoint.getFilters(); |
| } catch (CoreException ce) { |
| JDIDebugUIPlugin.log(ce); |
| filters= new String[]{}; |
| } |
| fFilters= new ArrayList(1); |
| for (int i = 0; i < filters.length; i++) { |
| String name = filters[i]; |
| addFilter(name); |
| } |
| } |
| |
| public Filter addFilter(String name) { |
| Filter filter = new Filter(name, false); |
| if (!fFilters.contains(filter)) { |
| fFilters.add(filter); |
| fViewer.add(filter); |
| } |
| return filter; |
| } |
| |
| |
| public void removeFilters(Object[] filters) { |
| for (int i = 0; i < filters.length; i++) { |
| Filter filter = (Filter)filters[i]; |
| fFilters.remove(filter); |
| } |
| fViewer.remove(filters); |
| } |
| |
| /** |
| * @see IStructuredContentProvider#getElements(Object) |
| */ |
| public Object[] getElements(Object inputElement) { |
| return fFilters.toArray(); |
| } |
| |
| /** |
| * @see IContentProvider#inputChanged(Viewer, Object, Object) |
| */ |
| public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { |
| } |
| |
| /** |
| * @see IContentProvider#dispose() |
| */ |
| public void dispose() { |
| } |
| } |
| |
| public ExceptionBreakpointFilterEditor(Composite parent, IJavaExceptionBreakpoint breakpoint) { |
| fBreakpoint= breakpoint; |
| init(JavaBreakpointPreferenceStore.EXCEPTION_FILTER, ActionMessages.getString("ExceptionBreakpointFilterEditor.Re&strict_to_Selected_Location(s)__1")); //$NON-NLS-1$ |
| createControl(parent); |
| } |
| |
| /** |
| * @see FieldEditor#adjustForNumColumns(int) |
| */ |
| protected void adjustForNumColumns(int numColumns) { |
| ((GridData) fOuter.getLayoutData()).horizontalSpan = numColumns; |
| } |
| |
| /** |
| * @see FieldEditor#doFillIntoGrid(Composite, int) |
| */ |
| protected void doFillIntoGrid(Composite parent, int numColumns) { |
| // top level container |
| fOuter = new Composite(parent, SWT.NONE); |
| GridLayout layout = new GridLayout(); |
| layout.numColumns = numColumns; |
| layout.marginHeight = 0; |
| layout.marginWidth = 0; |
| |
| fOuter.setLayout(layout); |
| GridData gd = new GridData(); |
| gd.verticalAlignment = GridData.FILL; |
| gd.horizontalAlignment = GridData.FILL; |
| fOuter.setLayoutData(gd); |
| |
| gd = new GridData(); |
| gd.horizontalSpan = numColumns; |
| getLabelControl(fOuter).setLayoutData(gd); |
| |
| // filter table |
| fFilterTable= new Table(fOuter, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION); |
| |
| TableLayout tableLayout= new TableLayout(); |
| ColumnLayoutData[] columnLayoutData= new ColumnLayoutData[1]; |
| columnLayoutData[0]= new ColumnWeightData(100); |
| tableLayout.addColumnData(columnLayoutData[0]); |
| fFilterTable.setLayout(tableLayout); |
| new TableColumn(fFilterTable, SWT.NONE); |
| |
| fFilterViewer = new TableViewer(fFilterTable); |
| fTableEditor = new TableEditor(fFilterTable); |
| fFilterViewer.setLabelProvider(new FilterLabelProvider()); |
| fFilterViewer.setSorter(new FilterViewerSorter()); |
| fFilterContentProvider = new FilterContentProvider(fFilterViewer); |
| fFilterViewer.setContentProvider(fFilterContentProvider); |
| // input just needs to be non-null |
| fFilterViewer.setInput(this); |
| gd = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL); |
| gd.widthHint = 100; |
| fFilterViewer.getTable().setLayoutData(gd); |
| fFilterViewer.addSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| ISelection selection = event.getSelection(); |
| if (selection.isEmpty()) { |
| fRemoveFilterButton.setEnabled(false); |
| } else { |
| fRemoveFilterButton.setEnabled(true); |
| } |
| } |
| }); |
| |
| createFilterButtons(fOuter); |
| } |
| |
| private void createFilterButtons(Composite container) { |
| // button container |
| Composite buttonContainer = new Composite(container, SWT.NONE); |
| GridData gd = new GridData(GridData.FILL_VERTICAL); |
| buttonContainer.setLayoutData(gd); |
| GridLayout buttonLayout = new GridLayout(); |
| buttonLayout.numColumns = 1; |
| buttonLayout.marginHeight = 0; |
| buttonLayout.marginWidth = 0; |
| buttonContainer.setLayout(buttonLayout); |
| |
| // Add filter button |
| fAddFilterButton = new Button(buttonContainer, SWT.PUSH); |
| fAddFilterButton.setText(ActionMessages.getString("ExceptionBreakpointFilterEditor.&Add_2")); //$NON-NLS-1$ |
| fAddFilterButton.setToolTipText(ActionMessages.getString("ExceptionBreakpointFilterEditor.Key_in_the_name_of_a_new_scope_expression_3")); //$NON-NLS-1$ |
| gd = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING); |
| fAddFilterButton.setLayoutData(gd); |
| fAddFilterButton.addSelectionListener(new SelectionListener() { |
| public void widgetSelected(SelectionEvent se) { |
| editFilter(); |
| } |
| public void widgetDefaultSelected(SelectionEvent se) { |
| } |
| }); |
| |
| // Add type button |
| fAddTypeButton = new Button(buttonContainer, SWT.PUSH); |
| fAddTypeButton.setText(ActionMessages.getString("ExceptionBreakpointFilterEditor.Add_&Class_4")); //$NON-NLS-1$ |
| fAddTypeButton.setToolTipText(ActionMessages.getString("ExceptionBreakpointFilterEditor.Choose_a_Java_Class_to_Add_It_to_the_Breakpoint_Scope_5")); //$NON-NLS-1$ |
| gd = getButtonGridData(fAddTypeButton); |
| fAddTypeButton.setLayoutData(gd); |
| fAddTypeButton.addSelectionListener(new SelectionListener() { |
| public void widgetSelected(SelectionEvent se) { |
| addType(); |
| } |
| public void widgetDefaultSelected(SelectionEvent se) { |
| } |
| }); |
| |
| // Add package button |
| fAddPackageButton = new Button(buttonContainer, SWT.PUSH); |
| fAddPackageButton.setText(ActionMessages.getString("ExceptionBreakpointFilterEditor.Add_&Package_6")); //$NON-NLS-1$ |
| fAddPackageButton.setToolTipText(ActionMessages.getString("ExceptionBreakpointFilterEditor.Choose_a_Package_to_Add_to_the_Breakpoint_Scope_7")); //$NON-NLS-1$ |
| gd = getButtonGridData(fAddPackageButton); |
| fAddPackageButton.setLayoutData(gd); |
| fAddPackageButton.addSelectionListener(new SelectionListener() { |
| public void widgetSelected(SelectionEvent se) { |
| addPackage(); |
| } |
| public void widgetDefaultSelected(SelectionEvent se) { |
| } |
| }); |
| |
| // Remove button |
| fRemoveFilterButton = new Button(buttonContainer, SWT.PUSH); |
| fRemoveFilterButton.setText(ActionMessages.getString("ExceptionBreakpointFilterEditor.&Remove_8")); //$NON-NLS-1$ |
| fRemoveFilterButton.setToolTipText(ActionMessages.getString("ExceptionBreakpointFilterEditor.Remove_all_selected_scoping_elements_9")); //$NON-NLS-1$ |
| gd = getButtonGridData(fRemoveFilterButton); |
| fRemoveFilterButton.setLayoutData(gd); |
| fRemoveFilterButton.addSelectionListener(new SelectionListener() { |
| public void widgetSelected(SelectionEvent se) { |
| removeFilters(); |
| } |
| public void widgetDefaultSelected(SelectionEvent se) { |
| } |
| }); |
| fRemoveFilterButton.setEnabled(false); |
| |
| } |
| |
| private GridData getButtonGridData(Button button) { |
| GridData gd= new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING); |
| GC gc = new GC(button); |
| gc.setFont(button.getFont()); |
| FontMetrics fontMetrics= gc.getFontMetrics(); |
| gc.dispose(); |
| int widthHint= Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.BUTTON_WIDTH); |
| gd.widthHint= Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x); |
| |
| gd.heightHint= Dialog.convertVerticalDLUsToPixels(fontMetrics, IDialogConstants.BUTTON_HEIGHT); |
| return gd; |
| } |
| |
| /** |
| * Create a new filter in the table (with the default 'new filter' value), |
| * then open up an in-place editor on it. |
| */ |
| private void editFilter() { |
| // if a previous edit is still in progress, finish it |
| if (fEditorText != null) { |
| validateChangeAndCleanup(); |
| } |
| |
| fNewFilter = fFilterContentProvider.addFilter(""); //$NON-NLS-1$ |
| fNewTableItem = fFilterTable.getItem(0); |
| |
| // create & configure Text widget for editor |
| // Fix for bug 1766. Border behavior on Windows & Linux for text |
| // fields is different. On Linux, you always get a border, on Windows, |
| // you don't. Specifying a border on Linux results in the characters |
| // getting pushed down so that only there very tops are visible. Thus, |
| // we have to specify different style constants for the different platforms. |
| int textStyles = SWT.SINGLE | SWT.LEFT; |
| if (SWT.getPlatform().equals("win32")) { //$NON-NLS-1$ |
| textStyles |= SWT.BORDER; |
| } |
| fEditorText = new Text(fFilterTable, textStyles); |
| GridData gd = new GridData(GridData.FILL_BOTH); |
| fEditorText.setLayoutData(gd); |
| |
| // set the editor |
| fTableEditor.horizontalAlignment = SWT.LEFT; |
| fTableEditor.grabHorizontal = true; |
| fTableEditor.setEditor(fEditorText, fNewTableItem, 0); |
| |
| // get the editor ready to use |
| fEditorText.setText(fNewFilter.getName()); |
| fEditorText.selectAll(); |
| setEditorListeners(fEditorText); |
| fEditorText.setFocus(); |
| } |
| |
| private void setEditorListeners(Text text) { |
| // CR means commit the changes, ESC means abort and don't commit |
| text.addKeyListener(new KeyAdapter() { |
| public void keyReleased(KeyEvent event) { |
| if (event.character == SWT.CR) { |
| if (fInvalidEditorText != null) { |
| fEditorText.setText(fInvalidEditorText); |
| fInvalidEditorText= null; |
| } else { |
| validateChangeAndCleanup(); |
| } |
| } else if (event.character == SWT.ESC) { |
| removeNewFilter(); |
| cleanupEditor(); |
| } |
| } |
| }); |
| // Consider loss of focus on the editor to mean the same as CR |
| text.addFocusListener(new FocusAdapter() { |
| public void focusLost(FocusEvent event) { |
| if (fInvalidEditorText != null) { |
| fEditorText.setText(fInvalidEditorText); |
| fInvalidEditorText= null; |
| } else { |
| validateChangeAndCleanup(); |
| } |
| } |
| }); |
| // Consume traversal events from the text widget so that CR doesn't |
| // traverse away to dialog's default button. Without this, hitting |
| // CR in the text field closes the entire dialog. |
| text.addListener(SWT.Traverse, new Listener() { |
| public void handleEvent(Event event) { |
| event.doit = false; |
| } |
| }); |
| } |
| |
| private void validateChangeAndCleanup() { |
| String trimmedValue = fEditorText.getText().trim(); |
| // if the new value is blank, remove the filter |
| if (trimmedValue.length() < 1) { |
| removeNewFilter(); |
| } |
| // if it's invalid, beep and leave sitting in the editor |
| else if (!validateEditorInput(trimmedValue)) { |
| fInvalidEditorText= trimmedValue; |
| fEditorText.setText(ActionMessages.getString("ExceptionBreakpointFilterEditor.Invalid_expression._Return_to_continue;_escape_to_exit_11")); //$NON-NLS-1$ |
| fEditorText.getDisplay().beep(); |
| return; |
| // otherwise, commit the new value if not a duplicate |
| } else { |
| Object[] filters= fFilterContentProvider.getElements(null); |
| for (int i = 0; i < filters.length; i++) { |
| Filter filter = (Filter)filters[i]; |
| if (filter.getName().equals(trimmedValue)) { |
| removeNewFilter(); |
| cleanupEditor(); |
| return; |
| } |
| } |
| fNewTableItem.setText(trimmedValue); |
| fNewFilter.setName(trimmedValue); |
| fFilterViewer.refresh(); |
| } |
| cleanupEditor(); |
| } |
| |
| /** |
| * A valid filter is simply one that is a valid Java identifier. |
| * and, as defined in the JDI spec, the regular expressions used for |
| * scoping must be limited to exact matches or patterns that |
| * begin with '*' or end with '*'. Beyond this, a string cannot be validated |
| * as corresponding to an existing type or package (and this is probably not |
| * even desirable). |
| */ |
| private boolean validateEditorInput(String trimmedValue) { |
| char firstChar= trimmedValue.charAt(0); |
| if (!Character.isJavaIdentifierStart(firstChar)) { |
| if (!(firstChar == '*')) { |
| return false; |
| } |
| } |
| int length= trimmedValue.length(); |
| for (int i= 1; i < length; i++) { |
| char c= trimmedValue.charAt(i); |
| if (!Character.isJavaIdentifierPart(c)) { |
| if (c == '.' && i != (length - 1)) { |
| continue; |
| } |
| if (c == '*' && i == (length - 1)) { |
| continue; |
| } |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Cleanup all widgetry & resources used by the in-place editing |
| */ |
| private void cleanupEditor() { |
| if (fEditorText != null) { |
| fNewFilter = null; |
| fNewTableItem = null; |
| fTableEditor.setEditor(null, null, 0); |
| fEditorText.getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| fEditorText.dispose(); |
| fEditorText = null; |
| } |
| }); |
| } |
| } |
| |
| private void removeFilters() { |
| IStructuredSelection selection = (IStructuredSelection)fFilterViewer.getSelection(); |
| fFilterContentProvider.removeFilters(selection.toArray()); |
| } |
| |
| private void removeNewFilter() { |
| fFilterContentProvider.removeFilters(new Object[] {fNewFilter}); |
| } |
| |
| private void addPackage() { |
| Shell shell= fAddPackageButton.getDisplay().getActiveShell(); |
| SelectionDialog dialog = null; |
| try { |
| dialog = JDIDebugUIPlugin.createAllPackagesDialog(shell, null, true); |
| } catch (JavaModelException jme) { |
| String title= ActionMessages.getString("ExceptionBreakpointFilterEditor.Add_Package_to_Scope_14"); //$NON-NLS-1$ |
| String message= ActionMessages.getString("ExceptionBreakpointFilterEditor.Could_not_open_package_selection_dialog_for_scope_definition_13"); //$NON-NLS-1$ |
| ExceptionHandler.handle(jme, title, message); |
| return; |
| } |
| |
| dialog.setTitle(ActionMessages.getString("ExceptionBreakpointFilterEditor.Add_Package_to_Scope_14")); //$NON-NLS-1$ |
| dialog.setMessage(ActionMessages.getString("ExceptionBreakpointFilterEditor.&Select_a_package_to_add_to_the_scope_of_the_breakpoint_15")); //$NON-NLS-1$ |
| if (dialog.open() == IDialogConstants.CANCEL_ID) { |
| return; |
| } |
| Object[] packages= dialog.getResult(); |
| if (packages != null && packages.length > 0) { |
| IJavaElement pkg = (IJavaElement)packages[0]; |
| String filter = pkg.getElementName(); |
| if (filter.length() < 1) { |
| filter = "(default package)"; //$NON-NLS-1$ |
| } else { |
| filter += ".*"; //$NON-NLS-1$ |
| } |
| fFilterContentProvider.addFilter(filter); |
| } |
| } |
| |
| private void addType() { |
| Shell shell= fAddTypeButton.getDisplay().getActiveShell(); |
| SelectionDialog dialog= null; |
| try { |
| dialog= JavaUI.createTypeDialog(shell, new ProgressMonitorDialog(shell), |
| SearchEngine.createWorkspaceScope(), IJavaElementSearchConstants.CONSIDER_CLASSES, false); |
| } catch (JavaModelException jme) { |
| String title= ActionMessages.getString("ExceptionBreakpointFilterEditor.Add_Class_to_Scope_17"); //$NON-NLS-1$ |
| String message= ActionMessages.getString("ExceptionBreakpointFilterEditor.Could_not_open_class_selection_dialog_for_scope_definition_18"); //$NON-NLS-1$ |
| ExceptionHandler.handle(jme, title, message); |
| return; |
| } |
| |
| dialog.setTitle(ActionMessages.getString("ExceptionBreakpointFilterEditor.Add_Class_to_Scope_17")); //$NON-NLS-1$ |
| dialog.setMessage(ActionMessages.getString("ExceptionBreakpointFilterEditor.&Select_a_class_to_add_to_the_scope_of_the_breakpoint_20")); //$NON-NLS-1$ |
| if (dialog.open() == IDialogConstants.CANCEL_ID) { |
| return; |
| } |
| |
| Object[] types= dialog.getResult(); |
| if (types != null && types.length > 0) { |
| IType type = (IType)types[0]; |
| fFilterContentProvider.addFilter(type.getFullyQualifiedName()); |
| } |
| } |
| |
| /** |
| * Creates composite control and sets the default layout data. |
| * |
| * @param parent the parent of the new composite |
| * @param numColumns the number of columns for the new composite |
| * @param labelText the text label of the new composite |
| * @return the newly-created composite |
| */ |
| private Composite createLabelledComposite(Composite parent, int numColumns, String labelText) { |
| Composite comp = new Composite(parent, SWT.NONE); |
| |
| //GridLayout |
| GridLayout layout = new GridLayout(); |
| layout.numColumns = numColumns; |
| comp.setLayout(layout); |
| |
| //GridData |
| GridData gd= new GridData(); |
| gd.verticalAlignment = GridData.FILL; |
| gd.horizontalAlignment = GridData.FILL; |
| comp.setLayoutData(gd); |
| |
| //Label |
| Label label = new Label(comp, SWT.NONE); |
| label.setText(labelText); |
| gd = new GridData(); |
| gd.horizontalSpan = numColumns; |
| label.setLayoutData(gd); |
| |
| return comp; |
| } |
| |
| /** |
| * @see FieldEditor#doLoad() |
| */ |
| protected void doLoad() { |
| } |
| |
| /** |
| * @see FieldEditor#doLoadDefault() |
| */ |
| protected void doLoadDefault() { |
| } |
| |
| /** |
| * @see FieldEditor#doStore() |
| */ |
| protected void doStore() { |
| Object[] filters= fFilterContentProvider.getElements(null); |
| String[] stringFilters= new String[filters.length]; |
| for (int i = 0; i < filters.length; i++) { |
| Filter filter = (Filter)filters[i]; |
| stringFilters[i]= filter.getName(); |
| } |
| try { |
| fBreakpoint.setFilters(stringFilters, true); |
| } catch (CoreException ce) { |
| JDIDebugUIPlugin.log(ce); |
| } |
| } |
| |
| /** |
| * @see FieldEditor#getNumberOfControls() |
| */ |
| public int getNumberOfControls() { |
| return 2; |
| } |
| } |