/*******************************************************************************
* Copyright (c) 2009 the CHISEL group and contributors.
* 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:
* Del Myers - initial API and implementation
*******************************************************************************/
package ca.uvic.chisel.javasketch.persistence.ui.internal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.TreeSet;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.ISharedImages;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.bindings.keys.ParseException;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
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.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
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.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;
import ca.uvic.chisel.javasketch.FilterSettings;
import ca.uvic.chisel.javasketch.ui.IFilterContext;
import ca.uvic.chisel.javasketch.utils.LaunchConfigurationUtilities;
/**
* A composite that can be used for setting up filters for java traces
* @author Del Myers
*
*/
public class FiltersComposite extends Composite {
public static final int SHORTCUT_PROJECT_CLASSES = 0;
public static final int SHORTCUT_PREVIOUS_SKETCH = 1;
public static final int SHORTCUT_NONE = 2;
private final class SimpleJavaContentProposalProvider implements IContentProposalProvider {
private IJavaElement[] packagesAndClasses;
/* (non-Javadoc)
* @see org.eclipse.jface.fieldassist.IContentProposalProvider#getProposals(java.lang.String, int)
*/
@Override
public IContentProposal[] getProposals(String contents, int position) {
String subString = contents.substring(0, position);
IJavaElement[] elements = getPackagesAndClasses();
LinkedList<IContentProposal> proposals = new LinkedList<IContentProposal>();
for (IJavaElement element : elements) {
String name = element.getElementName();
if (element instanceof IType) {
name = ((IType)element).getFullyQualifiedName();
}
if (select(name, subString)) {
proposals.add(makeContentProposal(element, position));
}
}
return proposals.toArray(new IContentProposal[proposals.size()]);
}
/**
* @param elementName
* @return
*/
private boolean select(String elementName, String filter) {
if (filter.startsWith("*")) {
filter = filter.substring(1);
if (filter.endsWith("*")) {
filter = filter.substring(0, filter.length()-1);
}
return elementName.contains(filter);
}
return elementName.startsWith(filter);
}
/**
* @param element
* @return
*/
private IContentProposal makeContentProposal(final IJavaElement element, final int cursor) {
return new IContentProposal() {
@Override
public String getContent() {
String s = ((element instanceof IType) ? ((IType)element).getFullyQualifiedName() : element.getElementName());
s += ".*";
return s.substring(cursor);
}
@Override
public int getCursorPosition() {
String s = ((element instanceof IType) ? ((IType)element).getFullyQualifiedName() : element.getElementName());
s += ".*";
return s.length();
}
@Override
public String getDescription() {
return (
(element instanceof IType) ?
((IType)element).getFullyQualifiedName() + ": Java Type" :
element.getElementName() + ": Java Package");
}
@Override
public String getLabel() {
String s = element.getElementName();
if (element instanceof IType) {
s = " " + s;
}
return s;
}
};
}
/**
* @return
*/
private IJavaElement[] getPackagesAndClasses() {
if (javaContext != null) {
if (packagesAndClasses == null) {
LinkedList<IJavaElement> list = new LinkedList<IJavaElement>();
try {
Collection<IPackageFragmentRoot> roots = getFragmentRoots();
for (IPackageFragmentRoot root : roots) {
for (IJavaElement element : root.getChildren()) {
if (element instanceof IPackageFragment) {
list.add(element);
IPackageFragment fragment = (IPackageFragment) element;
for (IJavaElement child : fragment.getChildren()) {
if (child instanceof IClassFile) {
list.add(((IClassFile)child).getType());
} else if (child instanceof ICompilationUnit) {
ICompilationUnit cu = (ICompilationUnit) child;
list.addAll(Arrays.asList(cu.getAllTypes()));
}
}
}
}
}
} catch (JavaModelException e) {
}
packagesAndClasses = list.toArray(new IJavaElement[list.size()]);
}
return packagesAndClasses;
}
return new IJavaElement[0];
}
/**
* @return
*/
private Collection<IPackageFragmentRoot> getFragmentRoots() {
Comparator<IPackageFragmentRoot> fragmentComparator = new Comparator<IPackageFragmentRoot>() {
@Override
public int compare(IPackageFragmentRoot o1,
IPackageFragmentRoot o2) {
return o1.getElementName().compareTo(o2.getElementName());
}
};
TreeSet<IPackageFragmentRoot> roots = new TreeSet<IPackageFragmentRoot>(fragmentComparator);
for (IJavaProject jp : javaContext) {
try {
roots.addAll(Arrays.asList(jp.getPackageFragmentRoots()));
} catch (JavaModelException e) {}
}
return roots;
}
}
/**
* @author Del Myers
*
*/
private final class RemoveItemListener implements
SelectionListener {
TableViewer viewer;
/**
* @param inclusionTable
*/
public RemoveItemListener(TableViewer viewer) {
this.viewer = viewer;
}
@Override
public void widgetSelected(SelectionEvent e) {
ISelection s = viewer.getSelection();
TreeSet<String> input = new TreeSet<String>(Arrays.asList((String[])viewer.getInput()));
if (s instanceof IStructuredSelection) {
Iterator<?> i = ((IStructuredSelection)s).iterator();
while (i.hasNext()) {
input.remove(i.next());
}
}
viewer.setInput(input.toArray(new String[input.size()]));
if (parentContext != null) {
parentContext.filterChanged();
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
}
/**
* @author Del Myers
*
*/
private final class AddFilterListener extends KeyAdapter implements
SelectionListener {
private Text text;
private TableViewer viewer;
/**
* @param addIncludeText
* @param inclusionTable
*/
public AddFilterListener(Text text, TableViewer viewer) {
this.text = text;
this.viewer = viewer;
}
@Override
public void widgetSelected(SelectionEvent e) {
if (FilterSettings.isValidFilterString(text.getText()) < 0) {
String[] input = (String[]) viewer.getInput();
TreeSet<String> newInput = new TreeSet<String>(Arrays.asList(input));
newInput.add(text.getText());
viewer.setInput(newInput.toArray(new String[newInput.size()]));
if (parentContext != null) {
parentContext.filterChanged();
}
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.KeyAdapter#keyReleased(org.eclipse.swt.events.KeyEvent)
*/
@Override
public void keyReleased(KeyEvent e) {
if (e.character=='\n' && (FilterSettings.isValidFilterString(text.getText()) < 0)) {
String[] input = (String[]) viewer.getInput();
TreeSet<String> newInput = new TreeSet<String>(Arrays.asList(input));
newInput.add(text.getText());
viewer.setInput(newInput.toArray(new String[newInput.size()]));
}
}
}
/**
* @author Del Myers
*
*/
private final class AddFilterAssistListener implements ModifyListener {
ControlDecoration decoration;
private Button addButton;
/**
* @param addButton
* @param controlDecoration
*/
public AddFilterAssistListener(Button addButton, ControlDecoration controlDecoration) {
this.decoration = controlDecoration;
this.addButton = addButton;
}
@Override
public void modifyText(ModifyEvent e) {
Text text = (Text) e.widget;
String s = text.getText();
int problem = FilterSettings.isValidFilterString(s);
if (decoration != null) {
String decorationImage = null;
String decorationText = null;
if (javaContext != null) {
decorationImage = FieldDecorationRegistry.DEC_CONTENT_PROPOSAL;
decorationText = "Content assist available";
}
if (problem >= 0) {
decorationImage = FieldDecorationRegistry.DEC_ERROR;
decorationText = "Error: unexpected character '" + s.charAt(problem) + "'";
if (addButton != null) {
addButton.setEnabled(false);
}
} else {
if (addButton != null) {
addButton.setEnabled(true);
}
}
if (decorationImage != null) {
FieldDecoration dec =
FieldDecorationRegistry.getDefault().getFieldDecoration(decorationImage);
decoration.setImage(dec.getImage());
decoration.setDescriptionText(decorationText);
decoration.show();
} else {
decoration.hide();
}
}
}
}
/**
* @author Del Myers
*
*/
private final class SimpleCellLabelProvider extends CellLabelProvider {
@Override
public void update(ViewerCell cell) {
if (cell.getColumnIndex() == 0) {
if (cell.getElement() == null) {
cell.setText("");
}
else {
String s = cell.getElement().toString();
//a simple method of telling whether or not
//the item is a class or a package
boolean capital = false;
for (int i = 0; i < s.length(); i++) {
if (Character.isUpperCase(s.charAt(i))) {
capital = true;
break;
}
}
if (capital) {
cell.setImage(JavaUI.getSharedImages().getImage(ISharedImages.IMG_OBJS_CLASS));
} else {
cell.setImage(JavaUI.getSharedImages().getImage(ISharedImages.IMG_OBJS_PACKAGE));
}
cell.setText(s);
}
}
}
}
private Button quickFilterButton;
private TableViewer inclusionTable;
private TableViewer exclusionTable;
private IJavaProject[] javaContext;
private Group inclusionGroup;
private Group exclusionGroup;
private IFilterContext parentContext;
//removing the following buttons because they can be confusing.
//private Button selectSketchButton;
//private Combo sketchCombo;
private Button noShortcutButton;
/**
* @param parent
* @param style
*/
public FiltersComposite(Composite parent) {
super(parent, SWT.NONE);
setLayout(new GridLayout(2, true));
//create a button for quickly selecting only classes in the project
Composite buttonArea = new Composite(this, SWT.NONE);
buttonArea.setLayout(new GridLayout(2, false));
GridData gd = new GridData();
gd.grabExcessHorizontalSpace=true;
gd.grabExcessVerticalSpace=false;
gd.horizontalAlignment = SWT.FILL;
gd.horizontalSpan=2;
buttonArea.setLayoutData(gd);
quickFilterButton = new Button(buttonArea, SWT.RADIO);
quickFilterButton.setText("Only index classes defined in this project");
gd = new GridData();
gd.grabExcessHorizontalSpace=true;
gd.grabExcessVerticalSpace=false;
gd.horizontalSpan=2;
gd.horizontalAlignment=GridData.BEGINNING;
quickFilterButton.setLayoutData(gd);
quickFilterButton.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
updateEnabledState();
if (parentContext != null) {
parentContext.filterChanged();
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
});
// selectSketchButton = new Button(buttonArea, SWT.RADIO);
// selectSketchButton.setText("Filter Based on Previous Trace");
// selectSketchButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
// selectSketchButton.addSelectionListener(new SelectionListener() {
// @Override
// public void widgetSelected(SelectionEvent e) {
// updateEnabledState();
// parentContext.filterChanged(FiltersComposite.this);
//
// }
//
// @Override
// public void widgetDefaultSelected(SelectionEvent e) {
// widgetSelected(e);
// }
// });
//
// sketchCombo = new Combo(buttonArea, SWT.DROP_DOWN | SWT.READ_ONLY);
// sketchCombo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
// sketchCombo.addSelectionListener(new SelectionListener() {
// @Override
// public void widgetSelected(SelectionEvent e) {
// parentContext.filterChanged(FiltersComposite.this);
//
// }
//
// @Override
// public void widgetDefaultSelected(SelectionEvent e) {
// widgetSelected(e);
// }
// });
// if (javaContext != null) {
// populateCombo();
// }
noShortcutButton = new Button(buttonArea, SWT.RADIO);
noShortcutButton.setText("Set Filter Manually");
gd = new GridData();
gd.grabExcessHorizontalSpace=true;
gd.grabExcessVerticalSpace=false;
gd.horizontalSpan=2;
gd.horizontalAlignment=GridData.BEGINNING;
noShortcutButton.setLayoutData(gd);
inclusionGroup = new Group(this, SWT.FULL_SELECTION);
inclusionGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
inclusionTable = createTableArea(inclusionGroup);
inclusionGroup.setText("Inclusion Filters");
exclusionGroup = new Group(this, SWT.FULL_SELECTION);
exclusionGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
exclusionGroup.setText("Exclusion Filters");
exclusionTable = createTableArea(exclusionGroup);
}
/**
*
*/
// private void populateCombo() {
// IProgramSketch[] sketches = SketchPlugin.getDefault().getStoredSketches();
// if (sketches != null) {
// Arrays.sort(sketches, new Comparator<IProgramSketch>() {
//
// @Override
// public int compare(IProgramSketch o1, IProgramSketch o2) {
// int diff = o1.getProcessTime().compareTo(o2.getProcessTime());
// if (diff == 0) {
// diff = o1.getID().compareTo(o2.getID());
// }
// return diff;
// }
// });
// sketchCombo.setData(sketches);
// DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
// for (IProgramSketch sketch : sketches) {
// sketchCombo.add(sketch.getLabel() + " at " + dateFormat.format(sketch.getProcessTime()));
// }
// }
//
// }
/**
* @param group
* @return
*/
private TableViewer createTableArea(Group group) {
group.setLayout(new GridLayout(3, false));
final Text addText = new Text(group, SWT.SINGLE | SWT.BORDER);
addText.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
final Button addButton = new Button(group, SWT.PUSH);
addButton.setText("Add");
addButton.setLayoutData(new GridData(GridData.FILL, GridData.FILL, false, false));
Button removeButton = new Button(group, SWT.PUSH);
removeButton.setText("Remove");
addButton.setLayoutData(new GridData(GridData.FILL, GridData.FILL, false, false));
TableViewer tableViewer = new TableViewer(group);
tableViewer.getTable().setLinesVisible(true);
GridData tableData = new GridData(GridData.FILL, GridData.FILL, true, true);
tableData.horizontalSpan = 3;
tableViewer.getTable().setLayoutData(tableData);
// TableLayout tl = new TableLayout();
// tableViewer.getTable().setLayout(tl);
final TableViewerColumn labelColumn = new TableViewerColumn(tableViewer, SWT.NONE);
labelColumn.setLabelProvider(new SimpleCellLabelProvider());
tableViewer.setLabelProvider(new SimpleCellLabelProvider());
tableViewer.setContentProvider(new ArrayContentProvider());
tableViewer.setInput(new String[0]);
addText.addModifyListener(new AddFilterAssistListener(addButton, new ControlDecoration(addText, SWT.TOP | SWT.LEFT)));
AddFilterListener addInclusionListener = new AddFilterListener(addText, tableViewer);
addButton.addSelectionListener(addInclusionListener);
addButton.addKeyListener(addInclusionListener);
addText.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
getShell().setDefaultButton(addButton);
}
});
removeButton.addSelectionListener(new RemoveItemListener(tableViewer));
try {
new ContentProposalAdapter(
addText,
new TextContentAdapter(),
new SimpleJavaContentProposalProvider(),
KeyStroke.getInstance("Ctrl+Space"),
new char[] { '.' }
);
} catch (ParseException e1) {
}
tableViewer.getTable().addControlListener(new ControlAdapter() {
/* (non-Javadoc)
* @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent)
*/
@Override
public void controlResized(ControlEvent e) {
labelColumn.getColumn().setWidth(((Table)e.widget).getClientArea().width);
}
});
return tableViewer;
}
public void setInclusionFilters(String[] include) {
TreeSet<String> filtered = new TreeSet<String>();
for (String s : include) {
filtered.add(s);
}
inclusionTable.setInput(filtered.toArray(new String[filtered.size()]));
}
public void setExclusionFilters(String[] exclude) {
TreeSet<String> filtered = new TreeSet<String>();
for (String s : exclude) {
filtered.add(s);
}
exclusionTable.setInput(filtered.toArray(new String[filtered.size()]));
}
public String[] getInclusionFilters() {
String[] input = (String[]) inclusionTable.getInput();
TreeSet<String> filtered = new TreeSet<String>();
for (String s : input) {
filtered.add(s);
}
return filtered.toArray(new String[filtered.size()]);
}
public String[] getExclusionFilters() {
String[] input = (String[]) exclusionTable.getInput();
TreeSet<String> filtered = new TreeSet<String>();
for (String s : input) {
filtered.add(s);
}
filtered.remove("");
return filtered.toArray(new String[filtered.size()]);
}
public boolean isUsingProjectClassesOnly() {
return quickFilterButton.getSelection();
}
public void setFilterShortcut(int shortcut) {
switch (shortcut) {
case SHORTCUT_NONE:
quickFilterButton.setSelection(false);
noShortcutButton.setSelection(true);
break;
// case SHORTCUT_PREVIOUS_SKETCH:
// selectSketchButton.setSelection(true);
// break;
case SHORTCUT_PROJECT_CLASSES:
noShortcutButton.setSelection(false);
quickFilterButton.setSelection(true);
break;
}
updateEnabledState();
}
// public IProgramSketch getReferencedFilterSketch() {
// int i = sketchCombo.getSelectionIndex();
// if (i < 0) return null;
// return ((IProgramSketch[])sketchCombo.getData())[i];
// }
protected void updateEnabledState() {
boolean enabled = !quickFilterButton.getSelection();
// sketchCombo.setEnabled(enabled);
LinkedList<Control> controls = new LinkedList<Control>();
if (inclusionGroup != null) {
controls.add(inclusionGroup);
}
if (exclusionGroup != null) {
controls.add(exclusionGroup);
}
while(controls.size() > 0) {
Control c = controls.removeFirst();
c.setEnabled(enabled);
if (c instanceof Composite) {
controls.addAll(Arrays.asList(((Composite)c).getChildren()));
}
}
}
public void setJavaContext(IJavaProject[] projects) {
this.javaContext = projects;
}
public void setLaunchType(String launchType) {
if (LaunchConfigurationUtilities.ECLIPSE_LAUNCH_TYPE.equals(launchType)) {
quickFilterButton.setText("Only index classes defined in workspace plugins");
layout();
}
}
/**
* Set the context in which this composite is placed. The context is used to set dirty states of
* wizards or configurations.
* @param context
*/
public void setParentContext(IFilterContext context) {
this.parentContext = context;
}
/**
* @param referencedSketch
*/
// public void setReferencedFilterSketch(IProgramSketch referencedSketch) {
// if (referencedSketch == null) return;
// IProgramSketch[] sketches = (IProgramSketch[]) sketchCombo.getData();
// if (sketches != null) {
// for (int i = 0; i < sketches.length; i++) {
// if (sketches[i].equals(sketches[i])) {
// sketchCombo.select(i);
// break;
// }
// }
// }
// }
}