blob: 888b9c6b31c78e03a628f79037107e622b6c1696 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 Igor Fedorenko
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Igor Fedorenko - initial API and implementation
*******************************************************************************/
package org.eclipse.m2e.core.ui.internal.views.build;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
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.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.m2e.core.internal.builder.BuildDebugHook;
import org.eclipse.m2e.core.internal.builder.MavenBuilder;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.project.configurator.AbstractBuildParticipant;
import org.eclipse.m2e.core.project.configurator.MojoExecutionKey;
import org.eclipse.m2e.core.ui.internal.MavenImages;
import org.eclipse.m2e.core.ui.internal.Messages;
@SuppressWarnings("restriction")
public class BuildDebugView extends ViewPart implements BuildDebugHook {
/*package*/static final Comparator<Node> NODE_COMPARATOR = (p1, p2) -> {
int d = p2.getBuildCount() - p1.getBuildCount();
if(d != 0) {
return d;
}
return p1.getName().compareTo(p2.getName());
};
/*package*/TreeViewer viewer;
/*package*/final Object projectsLock = new Object() {
};
/*package*/final Map<String, ProjectNode> projects = new ConcurrentHashMap<String, ProjectNode>();
/*package*/final Job refreshJob = new Job("") {
protected IStatus run(IProgressMonitor monitor) {
getSite().getShell().getDisplay().asyncExec(() -> viewer.refresh());
return Status.OK_STATUS;
}
};
/*package*/volatile boolean suspended = true;
public void createPartControl(Composite parent) {
viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
Tree tree = viewer.getTree();
tree.setHeaderVisible(true);
tree.setLinesVisible(true);
TreeViewerColumn treeViewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
TreeColumn trclmnName = treeViewerColumn.getColumn();
trclmnName.setWidth(400);
trclmnName.setText(Messages.BuildDebugView_columnName);
TreeViewerColumn treeViewerColumn_1 = new TreeViewerColumn(viewer, SWT.NONE);
TreeColumn trclmnBuildCount = treeViewerColumn_1.getColumn();
trclmnBuildCount.setWidth(100);
trclmnBuildCount.setText(Messages.BuildDebugView_columnBuildNumber);
viewer.setLabelProvider(new ITableLabelProvider() {
public void removeListener(ILabelProviderListener listener) {
}
public boolean isLabelProperty(Object element, String property) {
return false;
}
public void dispose() {
}
public void addListener(ILabelProviderListener listener) {
}
public String getColumnText(Object element, int columnIndex) {
if(element instanceof Node) {
return getColumnText((Node) element, columnIndex);
}
if(columnIndex == 0) {
return element.toString();
}
return null;
}
private String getColumnText(Node element, int columnIndex) {
switch(columnIndex) {
case 0:
return element.getName();
case 1:
return Integer.toString(element.getBuildCount());
default:
// fall through
}
return null;
}
public Image getColumnImage(Object element, int columnIndex) {
return null;
}
});
viewer.setContentProvider(new ITreeContentProvider() {
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
public void dispose() {
}
public boolean hasChildren(Object element) {
if(element instanceof ContainerNode) {
return !((ContainerNode) element).getResources().isEmpty();
}
if(element instanceof CollectionNode<?>) {
return !((CollectionNode<?>) element).getMembers().isEmpty();
}
return false;
}
public Object getParent(Object element) {
return null;
}
public Object[] getElements(Object inputElement) {
if(inputElement == projects) {
List<ProjectNode> sorted;
synchronized(projectsLock) {
sorted = new ArrayList<ProjectNode>(projects.values());
}
Collections.sort(sorted, NODE_COMPARATOR);
return sorted.toArray();
}
return new Object[0];
}
public Object[] getChildren(Object parentElement) {
if(parentElement instanceof ProjectNode) {
ArrayList<Object> result = new ArrayList<Object>();
final ProjectNode projectNode = (ProjectNode) parentElement;
final List<ResourceNode> resources = new ArrayList<ResourceNode>(projectNode.getResources());
if(!resources.isEmpty()) {
Collections.sort(resources, NODE_COMPARATOR);
result.add(new CollectionNode<ResourceNode>(Messages.BuildDebugView_nodeDelta, resources));
}
final List<MojoExecutionNode> executions = new ArrayList<MojoExecutionNode>(projectNode.getMojoExecutions());
if(!executions.isEmpty()) {
Collections.sort(executions, NODE_COMPARATOR);
result.add(new CollectionNode<MojoExecutionNode>(Messages.BuildDebugView_nodeExecutions, executions));
}
return result.toArray();
} else if(parentElement instanceof CollectionNode<?>) {
return ((CollectionNode<?>) parentElement).getMembers().toArray();
} else if(parentElement instanceof ContainerNode) {
return ((ContainerNode) parentElement).getResources().toArray();
}
return null;
}
});
viewer.setInput(projects);
IActionBars actionBars = getViewSite().getActionBars();
IToolBarManager toolBar = actionBars.getToolBarManager();
Action suspendAction = new Action(Messages.BuildDebugView_actionSuspend, IAction.AS_CHECK_BOX) {
public void run() {
suspended = isChecked();
}
};
suspendAction.setImageDescriptor(MavenImages.SUSPEND);
suspendAction.setChecked(suspended);
Action clearAction = new Action(Messages.BuildDebugView_actionClear, MavenImages.CLEAR) {
public void run() {
synchronized(projectsLock) {
projects.clear();
}
refreshJob.schedule();
}
};
Action collapseAll = new Action(Messages.BuildDebugView_actionCollapseAll, MavenImages.COLLAPSEALL) {
public void run() {
viewer.collapseAll();
}
};
toolBar.add(collapseAll);
toolBar.add(clearAction);
toolBar.add(suspendAction);
actionBars.updateActionBars();
}
public void setFocus() {
}
public void init(IViewSite site) throws PartInitException {
super.init(site);
MavenBuilder.addDebugHook(this);
}
public void dispose() {
MavenBuilder.removeDebugHook(this);
super.dispose();
}
public void buildStart(IMavenProjectFacade projectFacade, int kind, Map<String, String> args,
Map<MojoExecutionKey, List<AbstractBuildParticipant>> participants, IResourceDelta delta, IProgressMonitor monitor) {
if(suspended) {
return;
}
final ProjectNode projectNode = getProjectNode(projectFacade);
final int buildCount = projectNode.incrementBuildCount();
try {
if(delta != null) {
delta.accept(delta1 -> {
if(delta1.getAffectedChildren().length == 0) {
IResource resource = delta1.getResource();
if(resource instanceof IFile || resource instanceof IFolder) {
projectNode.addResource(resource.getProjectRelativePath()).setBuildCount(buildCount);
}
}
return true; // keep visiting
});
}
refreshJob.schedule(1000L);
} catch(CoreException ex) {
ErrorDialog.openError(getSite().getShell(), Messages.BuildDebugView_errorTitle,
Messages.BuildDebugView_errorDescription, ex.getStatus());
}
}
private ProjectNode getProjectNode(IMavenProjectFacade projectFacade) {
synchronized(projectsLock) {
IProject project = projectFacade.getProject();
ProjectNode projectNode = projects.get(project.getName());
if(projectNode == null) {
projectNode = new ProjectNode(project.getName());
projects.put(project.getName(), projectNode);
}
return projectNode;
}
}
public void buildParticipant(IMavenProjectFacade projectFacade, MojoExecutionKey mojoExecutionKey,
AbstractBuildParticipant participant, Set<File> files, IProgressMonitor monitor) {
if(suspended || files == null || files.isEmpty()) {
return;
}
final ProjectNode projectNode = getProjectNode(projectFacade);
final int buildCount = projectNode.getBuildCount();
// TODO secondary participants
// ... although they are unlikely to use BuildContext so we don't know what resources they modify
final MojoExecutionNode executionNode = projectNode.getMojoExecutionNode(mojoExecutionKey);
executionNode.setBuildCount(buildCount);
for(File file : files) {
executionNode.addResource(projectFacade.getProjectRelativePath(file.getAbsolutePath())).setBuildCount(buildCount);
}
}
}