/*******************************************************************************
* Copyright (c) 2010 Neil Bartlett.
* 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:
* Neil Bartlett - initial API and implementation
*******************************************************************************/
package bndtools.views;
import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.ResourceUtil;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import bndtools.Plugin;
import bndtools.model.importanalysis.ExportPackage;
import bndtools.model.importanalysis.ImportPackage;
import bndtools.model.importanalysis.ImportsAndExportsViewerSorter;
import bndtools.model.importanalysis.ImportsExportsAnalysisResult;
import bndtools.model.importanalysis.ImportsExportsTreeContentProvider;
import bndtools.model.importanalysis.ImportsExportsTreeContentProvider.ImportUsedByClass;
import bndtools.model.importanalysis.ImportsExportsTreeLabelProvider;
import bndtools.model.importanalysis.RequiredBundle;
import bndtools.tasks.AnalyseBundleResolutionJob;
import bndtools.utils.PartAdapter;
import bndtools.utils.SelectionUtils;
public class ImportsExportsView extends ViewPart implements ISelectionListener, IResourceChangeListener {
private Display display = null;
private Tree tree = null;
private TreeViewer viewer;
private ViewerFilter hideSelfImportsFilter;
private boolean outOfDate = false;
private File[] selectedFiles;
private Job analysisJob;
private final IPartListener partAdapter = new PartAdapter() {
@Override
public void partActivated(IWorkbenchPart part) {
if (part == ImportsExportsView.this) {
if (outOfDate) {
executeAnalysis();
}
} else if (part instanceof IEditorPart) {
IEditorInput editorInput = ((IEditorPart) part).getEditorInput();
IFile file = ResourceUtil.getFile(editorInput);
if (file != null) {
if (file.getName().toLowerCase().endsWith(".bnd") || file.getName().toLowerCase().endsWith(".jar")) {
selectedFiles = new File[] {
file.getLocation().toFile()
};
if (getSite().getPage().isPartVisible(ImportsExportsView.this)) {
executeAnalysis();
} else {
outOfDate = true;
}
}
}
}
}
};
@Override
public void createPartControl(Composite parent) {
this.display = parent.getDisplay();
tree = new Tree(parent, SWT.FULL_SELECTION | SWT.MULTI);
tree.setHeaderVisible(true);
tree.setLinesVisible(true);
TreeColumn col;
col = new TreeColumn(tree, SWT.NONE);
col.setText("Package");
col.setWidth(400);
col = new TreeColumn(tree, SWT.NONE);
col.setText("Attribs");
col.setWidth(100);
viewer = new TreeViewer(tree);
viewer.setContentProvider(new ImportsExportsTreeContentProvider());
viewer.setSorter(new ImportsAndExportsViewerSorter());
viewer.setLabelProvider(new ImportsExportsTreeLabelProvider());
viewer.setAutoExpandLevel(2);
hideSelfImportsFilter = new ViewerFilter() {
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
if (element instanceof ImportPackage) {
return !((ImportPackage) element).isSelfImport();
} else if (element instanceof RequiredBundle) {
return !((RequiredBundle) element).isSatisfied();
}
return true;
}
};
viewer.setFilters(new ViewerFilter[] {
hideSelfImportsFilter
});
viewer.addDragSupport(DND.DROP_MOVE | DND.DROP_COPY, new Transfer[] {
LocalSelectionTransfer.getTransfer()
}, new DragSourceListener() {
public void dragStart(DragSourceEvent event) {}
public void dragSetData(DragSourceEvent event) {
LocalSelectionTransfer transfer = LocalSelectionTransfer.getTransfer();
if (transfer.isSupportedType(event.dataType))
transfer.setSelection(viewer.getSelection());
}
public void dragFinished(DragSourceEvent event) {}
});
viewer.addOpenListener(new IOpenListener() {
public void open(OpenEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
for (Iterator< ? > iter = selection.iterator(); iter.hasNext();) {
Object item = iter.next();
if (item instanceof ImportUsedByClass) {
ImportUsedByClass importUsedBy = (ImportUsedByClass) item;
String className = importUsedBy.getClazz().getFQN();
IType type = null;
if (selectedFiles != null) {
IWorkspaceRoot wsroot = ResourcesPlugin.getWorkspace().getRoot();
for (File selectedFile : selectedFiles) {
IFile[] wsfiles = wsroot.findFilesForLocationURI(selectedFile.toURI());
for (IFile wsfile : wsfiles) {
IJavaProject javaProject = JavaCore.create(wsfile.getProject());
try {
type = javaProject.findType(className);
if (type != null)
break;
} catch (JavaModelException e) {
ErrorDialog.openError(getSite().getShell(), "Error", "", new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, MessageFormat.format("Error opening Java class '{0}'.", className), e));
}
}
}
}
try {
if (type != null)
JavaUI.openInEditor(type, true, true);
} catch (PartInitException e) {
ErrorDialog.openError(getSite().getShell(), "Error", "", new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, MessageFormat.format("Error opening Java editor for class '{0}'.", className), e));
} catch (JavaModelException e) {
ErrorDialog.openError(getSite().getShell(), "Error", "", new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, MessageFormat.format("Error opening Java class '{0}'.", className), e));
}
}
}
}
});
fillActionBars();
getSite().getPage().addPostSelectionListener(this);
getSite().getPage().addPartListener(partAdapter);
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
// Current selection & part
IWorkbenchPart activePart = getSite().getPage().getActivePart();
ISelection activeSelection = getSite().getWorkbenchWindow().getSelectionService().getSelection();
selectionChanged(activePart, activeSelection);
}
void fillActionBars() {
IAction toggleShowSelfImports = new Action("showSelfImports", IAction.AS_CHECK_BOX) {
@Override
public void runWithEvent(Event event) {
if (isChecked()) {
viewer.removeFilter(hideSelfImportsFilter);
} else {
viewer.addFilter(hideSelfImportsFilter);
}
}
};
toggleShowSelfImports.setChecked(false);
toggleShowSelfImports.setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "/icons/package_folder_impexp.gif"));
toggleShowSelfImports.setToolTipText("Show self-imported packages");
IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
toolBarManager.add(toggleShowSelfImports);
}
@Override
public void setFocus() {}
@Override
public void dispose() {
getSite().getPage().removeSelectionListener(this);
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
getSite().getPage().removePartListener(partAdapter);
super.dispose();
}
public void setInput(File[] sourceFiles, Collection< ? extends ImportPackage> imports, Collection< ? extends ExportPackage> exports, Collection< ? extends RequiredBundle> requiredBundles) {
selectedFiles = sourceFiles;
if (tree != null && !tree.isDisposed()) {
viewer.setInput(new ImportsExportsAnalysisResult(imports, exports, requiredBundles));
String label;
if (sourceFiles != null) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < sourceFiles.length; i++) {
if (i > 0)
builder.append(", ");
builder.append(sourceFiles[i].getAbsolutePath());
}
label = builder.toString();
} else {
label = "<no input>";
}
setContentDescription(label);
}
}
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
if (selection == null || selection.isEmpty())
return;
Collection<File> fileList = getFilesFromSelection(selection);
// Filter out non-bundle files/dirs
for (Iterator<File> iter = fileList.iterator(); iter.hasNext();) {
File file = iter.next();
if (file.isDirectory()) {
File manifestFile = new File(file, "META-INF/MANIFEST.MF");
if (!manifestFile.isFile())
iter.remove();
} else {
String fileName = file.getName().toLowerCase();
if (!fileName.endsWith(".bnd") && !fileName.endsWith(".jar"))
iter.remove();
}
}
if (fileList.isEmpty())
return;
File[] files = fileList.toArray(new File[fileList.size()]);
if (!Arrays.equals(files, selectedFiles)) {
this.selectedFiles = files;
if (getSite().getPage().isPartVisible(this)) {
executeAnalysis();
} else {
outOfDate = true;
}
}
}
private static Collection<File> getFilesFromSelection(ISelection selection) {
if (selection.isEmpty() || !(selection instanceof IStructuredSelection)) {
return Collections.emptyList();
}
IStructuredSelection structSel = (IStructuredSelection) selection;
Collection<File> result = new ArrayList<File>();
Iterator< ? > iter = structSel.iterator();
while (iter.hasNext()) {
Object element = iter.next();
File file = SelectionUtils.adaptObject(element, File.class);
if (file != null) {
result.add(file);
} else {
IResource resource = SelectionUtils.adaptObject(element, IResource.class);
if (resource != null) {
IPath location = resource.getLocation();
if (location != null)
result.add(location.toFile());
}
}
}
return result;
}
void executeAnalysis() {
outOfDate = false;
synchronized (this) {
Job oldJob = analysisJob;
if (oldJob != null && oldJob.getState() != Job.NONE)
oldJob.cancel();
if (selectedFiles != null) {
final AnalyseBundleResolutionJob tmp = new AnalyseBundleResolutionJob("importExportAnalysis", selectedFiles);
tmp.setSystem(true);
tmp.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
IStatus result = tmp.getResult();
if (result != null && result.isOK()) {
if (display != null && !display.isDisposed())
display.asyncExec(new Runnable() {
public void run() {
if (!tree.isDisposed())
setInput(tmp.getResultFileArray(), tmp.getImportResults(), tmp.getExportResults(), tmp.getRequiredBundles());
}
});
}
}
});
analysisJob = tmp;
analysisJob.schedule(500);
} else {
analysisJob = null;
}
}
}
public void resourceChanged(IResourceChangeEvent event) {
if (selectedFiles != null) {
IWorkspaceRoot wsroot = ResourcesPlugin.getWorkspace().getRoot();
for (File file : selectedFiles) {
IFile[] wsfiles = wsroot.findFilesForLocationURI(file.toURI());
for (IFile wsfile : wsfiles) {
if (event.getDelta().findMember(wsfile.getFullPath()) != null) {
executeAnalysis();
break;
}
}
}
}
}
}