/*******************************************************************************
* Copyright (c) 2008-2010 Sonatype, Inc.
* 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:
* Sonatype, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.m2e.core.ui.internal.wizards;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.apache.lucene.search.BooleanQuery;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.IDecoration;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.core.IMavenConstants;
import org.eclipse.m2e.core.embedder.ArtifactKey;
import org.eclipse.m2e.core.index.IIndex;
import org.eclipse.m2e.core.index.IndexManager;
import org.eclipse.m2e.core.index.IndexedArtifact;
import org.eclipse.m2e.core.index.IndexedArtifactFile;
import org.eclipse.m2e.core.index.UserInputSearchExpression;
import org.eclipse.m2e.core.ui.internal.Messages;
import org.eclipse.m2e.core.ui.internal.MavenImages;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
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.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import com.ibm.icu.text.DateFormat;
/**
* MavenPomSelectionComposite
*
* @author Eugene Kuleshov
*/
public class MavenPomSelectionComponent extends Composite {
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Widget#dispose()
*/
public void dispose() {
if(searchJob != null) {
searchJob.cancel();
}
super.dispose();
}
Text searchText = null;
TreeViewer searchResultViewer = null;
/**
* One of {@link IIndex#SEARCH_ARTIFACT}, {@link IIndex#SEARCH_CLASS_NAME},
*/
String queryType;
SearchJob searchJob;
private IStatus status;
private ISelectionChangedListener selectionListener;
/**
* @deprecated
*/
public static final String P_SEARCH_INCLUDE_JAVADOC = "searchIncludesJavadoc"; //$NON-NLS-1$
/**
* @deprecated
*/
public static final String P_SEARCH_INCLUDE_SOURCES = "searchIncludesSources"; //$NON-NLS-1$
/**
* @deprecated
*/
public static final String P_SEARCH_INCLUDE_TESTS = "searchIncludesTests"; //$NON-NLS-1$
private static final long SHORT_DELAY = 150L;
private static final long LONG_DELAY = 500L;
final HashSet<String> artifactKeys = new HashSet<String>();
final HashSet<String> managedKeys = new HashSet<String>();
public MavenPomSelectionComponent(Composite parent, int style) {
super(parent, style);
createSearchComposite();
}
private void createSearchComposite() {
GridLayout gridLayout = new GridLayout(2, false);
gridLayout.marginWidth = 0;
gridLayout.marginHeight = 0;
setLayout(gridLayout);
Label searchTextlabel = new Label(this, SWT.NONE);
searchTextlabel.setText(Messages.MavenPomSelectionComponent_search_title);
searchTextlabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
searchText = new Text(this, SWT.BORDER | SWT.SEARCH);
searchText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1));
searchText.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if(e.keyCode == SWT.ARROW_DOWN) {
searchResultViewer.getTree().setFocus();
selectFirstElementInTheArtifactTreeIfNoSelectionHasBeenMade();
}
}
});
searchText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
scheduleSearch(searchText.getText(), true);
}
});
Label searchResultsLabel = new Label(this, SWT.NONE);
searchResultsLabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1));
searchResultsLabel.setText(Messages.MavenPomSelectionComponent_lblResults);
Tree tree = new Tree(this, SWT.BORDER | SWT.SINGLE);
tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
tree.setData("name", "searchResultTree"); //$NON-NLS-1$ //$NON-NLS-2$
tree.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
selectFirstElementInTheArtifactTreeIfNoSelectionHasBeenMade();
}
public void focusLost(FocusEvent e) {
}
});
searchResultViewer = new TreeViewer(tree);
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Composite#setFocus()
*/
public boolean setFocus() {
return searchText.setFocus();
}
void selectFirstElementInTheArtifactTreeIfNoSelectionHasBeenMade() {
//
// If we have started a new search when focus is passed to the tree viewer we will automatically select
// the first element if no element has been selected from a previous expedition into the tree viewer.
//
if(searchResultViewer.getTree().getItemCount() > 0 && searchResultViewer.getSelection().isEmpty()) {
Object artifact = searchResultViewer.getTree().getTopItem().getData();
searchResultViewer.setSelection(new StructuredSelection(artifact), true);
}
}
protected boolean showClassifiers() {
return (queryType != null && IIndex.SEARCH_ARTIFACT.equals(queryType));
}
public void init(String queryText, String queryType, Set<ArtifactKey> artifacts, Set<ArtifactKey> managed) {
this.queryType = queryType;
if(queryText != null) {
searchText.setText(queryText);
}
if(artifacts != null) {
for(ArtifactKey a : artifacts) {
artifactKeys.add(a.getGroupId() + ":" + a.getArtifactId()); //$NON-NLS-1$
artifactKeys.add(a.getGroupId() + ":" + a.getArtifactId() + ":" + a.getVersion()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
if (managed != null) {
for (ArtifactKey a : managed) {
managedKeys.add(a.getGroupId() + ":" + a.getArtifactId()); //$NON-NLS-1$
managedKeys.add(a.getGroupId() + ":" + a.getArtifactId() + ":" + a.getVersion()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
searchResultViewer.setContentProvider(new SearchResultContentProvider());
searchResultViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new SearchResultLabelProvider(artifactKeys, managedKeys, queryType)));
searchResultViewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
if(!selection.isEmpty()) {
if(selection.size() == 1) {
IndexedArtifactFile f = getSelectedIndexedArtifactFile(selection.getFirstElement());
// int severity = artifactKeys.contains(f.group + ":" + f.artifact) ? IStatus.ERROR : IStatus.OK;
int severity = IStatus.OK;
String date = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).format(f.date);
setStatus(
severity,
NLS.bind(Messages.MavenPomSelectionComponent_detail1, f.fname,
(f.size != -1 ? NLS.bind(Messages.MavenPomSelectionComponent_details2, date, f.size) : date)));
} else {
setStatus(IStatus.OK, NLS.bind(Messages.MavenPomSelectionComponent_selected, selection.size()));
}
} else {
setStatus(IStatus.ERROR, Messages.MavenPomSelectionComponent_nosel);
}
}
});
setupClassifiers();
setStatus(IStatus.ERROR, ""); //$NON-NLS-1$
scheduleSearch(queryText, false);
}
protected void setupClassifiers() {
}
public IStatus getStatus() {
return this.status;
}
public void addDoubleClickListener(IDoubleClickListener listener) {
searchResultViewer.addDoubleClickListener(listener);
}
public void addSelectionChangedListener(ISelectionChangedListener listener) {
this.selectionListener = listener;
}
void setStatus(int severity, String message) {
this.status = new Status(severity, IMavenConstants.PLUGIN_ID, 0, message, null);
if(selectionListener != null) {
selectionListener.selectionChanged(new SelectionChangedEvent(searchResultViewer, searchResultViewer
.getSelection()));
}
}
public IndexedArtifact getIndexedArtifact() {
IStructuredSelection selection = (IStructuredSelection) searchResultViewer.getSelection();
Object element = selection.getFirstElement();
if(element instanceof IndexedArtifact) {
return (IndexedArtifact) element;
}
TreeItem[] treeItems = searchResultViewer.getTree().getSelection();
if(treeItems.length == 0) {
return null;
}
return (IndexedArtifact) treeItems[0].getParentItem().getData();
}
public IndexedArtifactFile getIndexedArtifactFile() {
IStructuredSelection selection = (IStructuredSelection) searchResultViewer.getSelection();
return getSelectedIndexedArtifactFile(selection.getFirstElement());
}
IndexedArtifactFile getSelectedIndexedArtifactFile(Object element) {
if(element instanceof IndexedArtifact) {
//the idea here is that if we have a managed version for something, then the IndexedArtifact shall
//represent that value..
IndexedArtifact ia = (IndexedArtifact)element;
if (managedKeys.contains(getKey(ia))) {
for (IndexedArtifactFile file : ia.getFiles()) {
if (managedKeys.contains(getKey(file))) {
return file;
}
}
}
return ia.getFiles().iterator().next();
}
return (IndexedArtifactFile) element;
}
void scheduleSearch(String query, boolean delay) {
if(query != null && query.length() > 2) {
if(searchJob == null) {
IndexManager indexManager = MavenPlugin.getIndexManager();
searchJob = new SearchJob(queryType, indexManager);
} else {
if(!searchJob.cancel()) {
//for already running ones, just create new instance so that the previous one can piecefully die
//without preventing the new one from completing first
IndexManager indexManager = MavenPlugin.getIndexManager();
searchJob = new SearchJob(queryType, indexManager);
}
}
searchJob.setQuery(query.toLowerCase());
searchJob.schedule(delay ? LONG_DELAY : SHORT_DELAY);
} else {
if(searchJob != null) {
searchJob.cancel();
}
}
}
public static String getKey(IndexedArtifactFile file) {
return file.group + ":" + file.artifact + ":" + file.version; //$NON-NLS-1$ //$NON-NLS-2$
}
public static String getKey(IndexedArtifact art) {
return art.getGroupId() + ":" + art.getArtifactId(); //$NON-NLS-1$
}
/**
* Search Job
*/
private class SearchJob extends Job {
private IndexManager indexManager;
private String query;
private String field;
private volatile boolean stop = false;
public SearchJob(String field, IndexManager indexManager) {
super(Messages.MavenPomSelectionComponent_searchJob);
this.field = field;
this.indexManager = indexManager;
}
public void setQuery(String query) {
this.query = query;
}
public boolean shouldRun() {
stop = false;
return super.shouldRun();
}
public int getClassifier() {
// mkleint: no more allowing people to opt in/out displaying javadoc and sources..
// allow tests and every other classifier..
return IIndex.SEARCH_JARS + IIndex.SEARCH_TESTS;
}
protected IStatus run(IProgressMonitor monitor) {
int classifier = showClassifiers() ? getClassifier() : IIndex.SEARCH_ALL;
if(searchResultViewer == null || searchResultViewer.getControl() == null
|| searchResultViewer.getControl().isDisposed()) {
return Status.CANCEL_STATUS;
}
if(query != null) {
String activeQuery = query;
try {
setResult(IStatus.OK, NLS.bind(Messages.MavenPomSelectionComponent_searching, activeQuery.toLowerCase()),
null);
Map<String, IndexedArtifact> res = indexManager.getAllIndexes().search( new UserInputSearchExpression(activeQuery), field, classifier);
//335139 have the managed entries always come up as first results
LinkedHashMap<String, IndexedArtifact> managed = new LinkedHashMap<String, IndexedArtifact>();
LinkedHashMap<String, IndexedArtifact> nonManaged = new LinkedHashMap<String, IndexedArtifact>();
for (Map.Entry<String, IndexedArtifact> art : res.entrySet()) {
String key = art.getValue().getGroupId() + ":" + art.getValue().getArtifactId();
if (managedKeys.contains(key)) {
managed.put(art.getKey(), art.getValue());
} else {
nonManaged.put(art.getKey(), art.getValue());
}
}
managed.putAll(nonManaged);
setResult(IStatus.OK, NLS.bind(Messages.MavenPomSelectionComponent_results, activeQuery, res.size()), managed);
} catch(BooleanQuery.TooManyClauses ex) {
setResult(IStatus.ERROR, Messages.MavenPomSelectionComponent_toomany,
Collections.<String, IndexedArtifact> emptyMap());
} catch(final RuntimeException ex) {
setResult(IStatus.ERROR, NLS.bind(Messages.MavenPomSelectionComponent_error, ex.toString()),
Collections.<String, IndexedArtifact> emptyMap());
} catch(final Exception ex) {
setResult(IStatus.ERROR, NLS.bind(Messages.MavenPomSelectionComponent_error, ex.getMessage()),
Collections.<String, IndexedArtifact> emptyMap());
}
}
return Status.OK_STATUS;
}
protected void canceling() {
stop = true;
}
private void setResult(final int severity, final String message, final Map<String, IndexedArtifact> result) {
if(stop)
return;
Display.getDefault().syncExec(new Runnable() {
public void run() {
setStatus(severity, message);
if(result != null) {
if(!searchResultViewer.getControl().isDisposed()) {
searchResultViewer.setInput(result);
}
}
}
});
}
}
public static class SearchResultLabelProvider extends LabelProvider implements IColorProvider, DelegatingStyledCellLabelProvider.IStyledLabelProvider {
private final Set<String> artifactKeys;
private final String queryType;
private final Set<String> managedKeys;
/**
* both managedkeys and artifctkeys are supposed to hold both gr:art:ver combos and gr:art combos
* @param artifactKeys
* @param managedKeys
* @param queryType
*/
public SearchResultLabelProvider(Set<String> artifactKeys, Set<String> managedKeys, String queryType) {
this.artifactKeys = artifactKeys;
this.queryType = queryType;
this.managedKeys = managedKeys;
}
public String getText(Object element) {
return super.getText(element);
}
protected String getRepoDisplayName(String repo) {
return repo;
}
public Color getForeground(Object element) {
if(element instanceof IndexedArtifactFile) {
IndexedArtifactFile f = (IndexedArtifactFile) element;
if(artifactKeys.contains(getKey(f))) {
return Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY);
}
} else if(element instanceof IndexedArtifact) {
IndexedArtifact i = (IndexedArtifact) element;
if(artifactKeys.contains(getKey(i))) {
return Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY);
}
}
return null;
}
public Color getBackground(Object element) {
return null;
}
public Image getImage(Object element) {
if(element instanceof IndexedArtifactFile) {
IndexedArtifactFile f = (IndexedArtifactFile) element;
if (managedKeys.contains(getKey(f))) {
return MavenImages.getOverlayImage(f.sourcesExists==IIndex.PRESENT ? MavenImages.PATH_VERSION_SRC : MavenImages.PATH_VERSION,
MavenImages.PATH_LOCK, IDecoration.BOTTOM_LEFT);
}
if(f.sourcesExists==IIndex.PRESENT) {
return MavenImages.IMG_VERSION_SRC;
}
return MavenImages.IMG_VERSION;
} else if(element instanceof IndexedArtifact) {
IndexedArtifact i = (IndexedArtifact) element;
if (managedKeys.contains(getKey(i))) {
return MavenImages.getOverlayImage(MavenImages.PATH_JAR, MavenImages.PATH_LOCK, IDecoration.BOTTOM_LEFT);
}
return MavenImages.IMG_JAR;
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider#getStyledText(java.lang.Object)
*/
public StyledString getStyledText(Object element) {
if(element instanceof IndexedArtifact) {
IndexedArtifact a = (IndexedArtifact) element;
String name = (a.getClassname() == null ? "" : a.getClassname() + " " + a.getPackageName() + " ") + a.getGroupId() + " " + a.getArtifactId(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
StyledString ss = new StyledString();
ss.append(name);
if (managedKeys.contains(getKey(a))) {
ss.append(Messages.MavenPomSelectionComponent_managed_decoration, StyledString.DECORATIONS_STYLER);
}
return ss;
} else if(element instanceof IndexedArtifactFile) {
IndexedArtifactFile f = (IndexedArtifactFile) element;
StyledString ss = new StyledString();
String name = f.version + " [" + (f.type == null ? "jar" : f.type) + (f.classifier != null ? ", " + f.classifier : "") + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
ss.append(name);
if (managedKeys.contains(getKey(f))) {
ss.append(Messages.MavenPomSelectionComponent_managed_decoration, StyledString.DECORATIONS_STYLER);
}
return ss;
}
return new StyledString();
}
}
public static class SearchResultContentProvider implements ITreeContentProvider {
private static Object[] EMPTY = new Object[0];
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
public Object[] getElements(Object inputElement) {
if(inputElement instanceof Map) {
return ((Map<?, ?>) inputElement).values().toArray();
}
return EMPTY;
}
public Object[] getChildren(Object parentElement) {
if(parentElement instanceof IndexedArtifact) {
IndexedArtifact a = (IndexedArtifact) parentElement;
return a.getFiles().toArray();
}
return EMPTY;
}
public boolean hasChildren(Object element) {
return element instanceof IndexedArtifact;
}
public Object getParent(Object element) {
return null;
}
public void dispose() {
}
}
}