/******************************************************************************* * 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 v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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.resources.IResourceDeltaVisitor; 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 = new Comparator<Node>() { public int compare(Node p1, Node 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(new Runnable() { public void run() { viewer.refresh(); // for(TreeColumn column : viewer.getTree().getColumns()) { // column.pack(); // } } }); 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(new IResourceDeltaVisitor() { public boolean visit(IResourceDelta delta) { if(delta.getAffectedChildren().length == 0) { IResource resource = delta.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); } } }