| /******************************************************************************* |
| * Copyright (c) 2000, 2015 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.debug.ui.propertypages; |
| |
| 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.FilterViewerComparator; |
| 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.viewers.CheckStateChangedEvent; |
| import org.eclipse.jface.viewers.CheckboxTableViewer; |
| import org.eclipse.jface.viewers.ColumnLayoutData; |
| import org.eclipse.jface.viewers.ColumnWeightData; |
| import org.eclipse.jface.viewers.ICheckStateListener; |
| import org.eclipse.jface.viewers.IContentProvider; |
| 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.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.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.graphics.Font; |
| 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.PlatformUI; |
| import org.eclipse.ui.dialogs.ElementListSelectionDialog; |
| import org.eclipse.ui.dialogs.SelectionDialog; |
| |
| |
| public class ExceptionFilterEditor { |
| |
| protected static final String DEFAULT_PACKAGE = "(default package)"; //$NON-NLS-1$ |
| 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 CheckboxTableViewer fFilterViewer; |
| private Table fFilterTable; |
| private FilterContentProvider fFilterContentProvider; |
| |
| private SelectionListener fSelectionListener= new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| Object source = e.getSource(); |
| if (source == fAddTypeButton) { |
| addType(); |
| } else if (source == fAddPackageButton) { |
| addPackage(); |
| } else if (source == fAddFilterButton) { |
| editFilter(); |
| } else if (source == fRemoveFilterButton) { |
| removeFilters(); |
| } |
| } |
| }; |
| |
| public ExceptionFilterEditor(Composite parent, JavaExceptionBreakpointAdvancedPage page) { |
| fBreakpoint = (IJavaExceptionBreakpoint) page.getBreakpoint(); |
| // top level container |
| Composite outer = new Composite(parent, SWT.NONE); |
| GridLayout layout = new GridLayout(); |
| layout.numColumns = 2; |
| layout.marginHeight = 0; |
| layout.marginWidth = 0; |
| |
| outer.setLayout(layout); |
| GridData gd = new GridData(GridData.FILL_BOTH); |
| outer.setLayoutData(gd); |
| outer.setFont(parent.getFont()); |
| // filter table |
| Label label= new Label(outer, SWT.NONE); |
| label.setText(PropertyPageMessages.ExceptionFilterEditor_5); |
| label.setFont(parent.getFont()); |
| gd= new GridData(); |
| gd.horizontalSpan= 2; |
| label.setLayoutData(gd); |
| |
| fFilterTable = new Table(outer, SWT.CHECK | 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); |
| fFilterTable.setFont(parent.getFont()); |
| new TableColumn(fFilterTable, SWT.NONE); |
| |
| fFilterViewer = new CheckboxTableViewer(fFilterTable); |
| fTableEditor = new TableEditor(fFilterTable); |
| fFilterViewer.setLabelProvider(new FilterLabelProvider()); |
| fFilterViewer.setComparator(new FilterViewerComparator()); |
| 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; |
| gd.heightHint = 100; |
| fFilterViewer.getTable().setLayoutData(gd); |
| fFilterViewer.addCheckStateListener(new ICheckStateListener() { |
| @Override |
| public void checkStateChanged(CheckStateChangedEvent event) { |
| Filter filter = (Filter) event.getElement(); |
| fFilterContentProvider.toggleFilter(filter); |
| } |
| }); |
| fFilterViewer.addSelectionChangedListener(new ISelectionChangedListener() { |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| ISelection selection = event.getSelection(); |
| if (selection.isEmpty()) { |
| fRemoveFilterButton.setEnabled(false); |
| } else { |
| fRemoveFilterButton.setEnabled(true); |
| } |
| } |
| }); |
| fFilterViewer.getTable().addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyPressed(KeyEvent event) { |
| if (event.character == SWT.DEL && event.stateMask == 0) { |
| removeFilters(); |
| } |
| } |
| }); |
| |
| createFilterButtons(outer); |
| } |
| |
| protected void doStore() { |
| Object[] filters = fFilterContentProvider.getElements(null); |
| List<String> inclusionFilters = new ArrayList<>(filters.length); |
| List<String> exclusionFilters = new ArrayList<>(filters.length); |
| for (int i = 0; i < filters.length; i++) { |
| Filter filter = (Filter) filters[i]; |
| String name = filter.getName(); |
| if (name.equals(DEFAULT_PACKAGE)) { |
| name = ""; //$NON-NLS-1$ |
| } |
| if (filter.isChecked()) { |
| inclusionFilters.add(name); |
| } else { |
| exclusionFilters.add(name); |
| } |
| } |
| try { |
| fBreakpoint.setInclusionFilters(inclusionFilters.toArray(new String[inclusionFilters.size()])); |
| fBreakpoint.setExclusionFilters(exclusionFilters.toArray(new String[exclusionFilters.size()])); |
| } catch (CoreException ce) { |
| JDIDebugUIPlugin.log(ce); |
| } |
| |
| } |
| |
| private void createFilterButtons(Composite container) { |
| // button container |
| Font font= container.getFont(); |
| Composite buttonContainer = new Composite(container, SWT.NONE); |
| buttonContainer.setFont(font); |
| 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); |
| |
| fAddFilterButton = createPushButton(buttonContainer, |
| PropertyPageMessages.ExceptionFilterEditor_6, |
| PropertyPageMessages.ExceptionFilterEditor_7); |
| fAddTypeButton = createPushButton(buttonContainer, |
| PropertyPageMessages.ExceptionFilterEditor_8, |
| PropertyPageMessages.ExceptionFilterEditor_9); |
| fAddPackageButton = createPushButton(buttonContainer, |
| PropertyPageMessages.ExceptionFilterEditor_10, |
| PropertyPageMessages.ExceptionFilterEditor_11); |
| fRemoveFilterButton = createPushButton(buttonContainer, |
| PropertyPageMessages.ExceptionFilterEditor_12, |
| PropertyPageMessages.ExceptionFilterEditor_13); |
| fRemoveFilterButton.setEnabled(false); |
| } |
| |
| /** |
| * Creates a fully configured push button with the given label and tooltip. |
| */ |
| private Button createPushButton(Composite parent, String text, String tooltipText) { |
| Button button = new Button(parent, SWT.PUSH); |
| button.setFont(parent.getFont()); |
| button.setText(text); |
| button.setToolTipText(tooltipText); |
| GridData gd = getButtonGridData(button); |
| button.setLayoutData(gd); |
| button.addSelectionListener(fSelectionListener); |
| return button; |
| } |
| |
| private static 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); |
| 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 for text fields varies per platform. |
| // On Motif, you always get a border, on other platforms, |
| // you don't. Specifying a border on Motif 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("motif")) { //$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() { |
| @Override |
| 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() { |
| @Override |
| 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() { |
| @Override |
| 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(PropertyPageMessages.ExceptionFilterEditor_14); |
| 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() { |
| @Override |
| 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(); |
| ElementListSelectionDialog dialog = null; |
| try { |
| dialog = JDIDebugUIPlugin.createAllPackagesDialog(shell, null, false); |
| } catch (JavaModelException jme) { |
| String title = PropertyPageMessages.ExceptionFilterEditor_15; |
| String message = PropertyPageMessages.ExceptionFilterEditor_16; |
| ExceptionHandler.handle(jme, title, message); |
| return; |
| } |
| if (dialog == null) { |
| return; |
| } |
| dialog.setTitle(PropertyPageMessages.ExceptionFilterEditor_15); |
| dialog.setMessage(PropertyPageMessages.ExceptionFilterEditor_18); |
| dialog.setMultipleSelection(true); |
| if (dialog.open() == IDialogConstants.CANCEL_ID) { |
| return; |
| } |
| Object[] packages = dialog.getResult(); |
| if (packages != null) { |
| for (int i = 0; i < packages.length; i++) { |
| IJavaElement pkg = (IJavaElement) packages[i]; |
| String filter = pkg.getElementName(); |
| if (filter.length() < 1) { |
| filter = DEFAULT_PACKAGE; |
| } else { |
| filter += ".*"; //$NON-NLS-1$ |
| } |
| Filter f = fFilterContentProvider.addFilter(filter); |
| fFilterContentProvider.checkFilter(f, true); |
| } |
| } |
| } |
| |
| private void addType() { |
| Shell shell = fAddTypeButton.getDisplay().getActiveShell(); |
| SelectionDialog dialog = null; |
| try { |
| dialog = JavaUI.createTypeDialog(shell, PlatformUI.getWorkbench().getProgressService(), SearchEngine.createWorkspaceScope(), IJavaElementSearchConstants.CONSIDER_CLASSES, false); |
| } catch (JavaModelException jme) { |
| String title = PropertyPageMessages.ExceptionFilterEditor_19; |
| String message = PropertyPageMessages.ExceptionFilterEditor_20; |
| ExceptionHandler.handle(jme, title, message); |
| return; |
| } |
| |
| dialog.setTitle(PropertyPageMessages.ExceptionFilterEditor_19); |
| dialog.setMessage(PropertyPageMessages.ExceptionFilterEditor_22); |
| if (dialog.open() == IDialogConstants.CANCEL_ID) { |
| return; |
| } |
| |
| Object[] types = dialog.getResult(); |
| IType type; |
| if (types != null) { |
| for (int i = 0; i < types.length; i++) { |
| type = (IType) types[i]; |
| Filter f = fFilterContentProvider.addFilter(type.getFullyQualifiedName()); |
| fFilterContentProvider.checkFilter(f, true); |
| } |
| } |
| } |
| |
| /** |
| * Content provider for the table. Content consists of instances of Filter. |
| */ |
| protected class FilterContentProvider implements IStructuredContentProvider { |
| |
| private CheckboxTableViewer fViewer; |
| private List<Filter> fFilters; |
| |
| public FilterContentProvider(CheckboxTableViewer viewer) { |
| fViewer = viewer; |
| populateFilters(); |
| } |
| |
| protected void populateFilters() { |
| String[] iFilters = null; |
| String[] eFilters = null; |
| try { |
| iFilters = fBreakpoint.getInclusionFilters(); |
| eFilters = fBreakpoint.getExclusionFilters(); |
| } catch (CoreException ce) { |
| JDIDebugUIPlugin.log(ce); |
| iFilters = new String[] { |
| }; |
| eFilters = new String[] { |
| }; |
| } |
| fFilters = new ArrayList<>(); |
| populateFilters(iFilters, true); |
| populateFilters(eFilters, false); |
| |
| } |
| |
| protected void populateFilters(String[] filters, boolean checked) { |
| for (int i = 0; i < filters.length; i++) { |
| String name = filters[i]; |
| if (name.length() == 0) { |
| name = DEFAULT_PACKAGE; |
| } |
| Filter filter = addFilter(name); |
| checkFilter(filter, checked); |
| } |
| } |
| |
| 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); |
| } |
| |
| public void toggleFilter(Filter filter) { |
| boolean newState = !filter.isChecked(); |
| filter.setChecked(newState); |
| fViewer.setChecked(filter, newState); |
| } |
| |
| public void checkFilter(Filter filter, boolean checked) { |
| filter.setChecked(checked); |
| fViewer.setChecked(filter, checked); |
| } |
| |
| /** |
| * @see IStructuredContentProvider#getElements(Object) |
| */ |
| @Override |
| public Object[] getElements(Object inputElement) { |
| return fFilters.toArray(); |
| } |
| |
| /** |
| * @see IContentProvider#inputChanged(Viewer, Object, Object) |
| */ |
| @Override |
| public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { |
| } |
| |
| /** |
| * @see IContentProvider#dispose() |
| */ |
| @Override |
| public void dispose() { |
| } |
| } |
| |
| } |