blob: 3a695918895763c591fee8602bc4e3088b4ba847 [file] [log] [blame]
/*******************************************************************************
* 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() {
}
}
}