Index: plugin.xml =================================================================== --- plugin.xml (revision 822319) +++ plugin.xml (working copy) @@ -233,6 +251,14 @@ name="Reverse Dependency Explorer" restorable="true"> + + Index: META-INF/MANIFEST.MF =================================================================== --- META-INF/MANIFEST.MF (revision 822319) +++ META-INF/MANIFEST.MF (working copy) @@ -31,10 +31,12 @@ org.eclipse.jdt.core, org.eclipse.jdt.ui, org.eclipse.ui.console, + org.eclipse.wst.xml.core, org.apache.ivy, org.eclipse.help, - org.eclipse.wst.xml.core, org.eclipse.debug.core, - org.eclipse.jdt.launching + org.eclipse.jdt.launching, + org.eclipse.zest.core;bundle-version="1.0.0", + org.eclipse.zest.layouts;bundle-version="1.0.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.4 Index: icons/evicted.gif =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: icons\evicted.gif ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: icons/focus.gif =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: icons\focus.gif ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: icons/screenshot.gif =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: icons\screenshot.gif ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: src/java/org/apache/ivyde/eclipse/cpcontainer/IvyClasspathContainerState.java =================================================================== --- src/java/org/apache/ivyde/eclipse/cpcontainer/IvyClasspathContainerState.java (revision 822319) +++ src/java/org/apache/ivyde/eclipse/cpcontainer/IvyClasspathContainerState.java (working copy) @@ -32,6 +32,7 @@ import org.apache.ivy.Ivy; import org.apache.ivy.core.cache.DefaultRepositoryCacheManager; import org.apache.ivy.core.module.descriptor.ModuleDescriptor; +import org.apache.ivy.core.report.ResolveReport; import org.apache.ivy.core.settings.IvySettings; import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry; import org.apache.ivy.util.Message; @@ -66,6 +67,8 @@ private long ivySettingsLastModified = -1; private ModuleDescriptor md; + + private ResolveReport resolveReport; private IvyClasspathContainerConfiguration conf; @@ -414,9 +417,16 @@ throw ex; } } - + public String toString() { return conf.toString(); } + public ResolveReport getResolveReport() { + return resolveReport; + } + + public void setResolveReport(ResolveReport resolveReport) { + this.resolveReport = resolveReport; + } } Index: src/java/org/apache/ivyde/eclipse/cpcontainer/IvyResolveJob.java =================================================================== --- src/java/org/apache/ivyde/eclipse/cpcontainer/IvyResolveJob.java (revision 822319) +++ src/java/org/apache/ivyde/eclipse/cpcontainer/IvyResolveJob.java (working copy) @@ -102,6 +102,7 @@ } if (resolver.getStatus() == Status.OK_STATUS) { container.updateClasspathEntries(resolver.getClasspathEntries()); + container.getState().setResolveReport(resolver.getResolveReport()); } setResolveStatus(resolver.getStatus()); return resolver.getStatus(); Index: src/java/org/apache/ivyde/eclipse/cpcontainer/IvyResolveJobThread.java =================================================================== --- src/java/org/apache/ivyde/eclipse/cpcontainer/IvyResolveJobThread.java (revision 822319) +++ src/java/org/apache/ivyde/eclipse/cpcontainer/IvyResolveJobThread.java (working copy) @@ -47,7 +47,6 @@ import org.apache.ivy.util.filter.ArtifactTypeFilter; import org.apache.ivyde.eclipse.FakeProjectManager; import org.apache.ivyde.eclipse.IvyPlugin; -import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; @@ -82,6 +81,8 @@ private String[] confs; private Map artifactsByDependency = new HashMap(); + + private ResolveReport report; public IvyResolveJobThread(IvyClasspathContainerConfiguration conf, Ivy ivy, ModuleDescriptor md, boolean usePreviousResolveIfExist, IProgressMonitor monitor) { @@ -99,6 +100,10 @@ public IClasspathEntry[] getClasspathEntries() { return classpathEntries; } + + public ResolveReport getResolveReport() { + return report; + } public void run() { try { @@ -221,7 +226,7 @@ private boolean resolve() throws ParseException, IOException { ResolveOptions resolveOption = new ResolveOptions().setConfs(confs); resolveOption.setValidate(ivy.getSettings().doValidate()); - ResolveReport report = ivy.resolve(md, resolveOption); + report = ivy.resolve(md, resolveOption); problemMessages = report.getAllProblemMessages(); all = new LinkedHashSet(Arrays.asList(report.getArtifactsReports(null, false))); confs = report.getConfigurations(); Index: src/java/org/apache/ivyde/eclipse/ui/views/ResolveVisualizerView.java =================================================================== --- src/java/org/apache/ivyde/eclipse/ui/views/ResolveVisualizerView.java (revision 0) +++ src/java/org/apache/ivyde/eclipse/ui/views/ResolveVisualizerView.java (revision 0) @@ -0,0 +1,486 @@ +package org.apache.ivyde.eclipse.ui.views; + +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Stack; + +import org.apache.ivy.core.report.ResolveReport; +import org.apache.ivy.core.resolve.IvyNode; +import org.apache.ivyde.eclipse.IvyPlugin; +import org.apache.ivyde.eclipse.cpcontainer.IvyClasspathContainer; +import org.apache.ivyde.resolvevisualizer.ClasspathContainerSelectionDialog; +import org.apache.ivyde.resolvevisualizer.IvyNodeLabelProvider; +import org.apache.ivyde.resolvevisualizer.MessageContentProvider; +import org.apache.ivyde.resolvevisualizer.ResolveVisualizerContentProvider; +import org.apache.ivyde.resolvevisualizer.ResolveVisualizerForm; +import org.apache.ivyde.resolvevisualizer.label.ILabelDecoratorAlgorithm; +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElementAdapter; +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElementFilterAdapter; +import org.eclipse.core.runtime.IProgressMonitor; +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.jface.action.Action; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.part.ViewPart; +import org.eclipse.zest.core.viewers.AbstractZoomableViewer; +import org.eclipse.zest.core.viewers.EntityConnectionData; +import org.eclipse.zest.core.viewers.GraphViewer; +import org.eclipse.zest.core.viewers.IZoomableWorkbenchPart; +import org.eclipse.zest.core.viewers.ZoomContributionViewItem; +import org.eclipse.zest.core.widgets.Graph; +import org.eclipse.zest.core.widgets.GraphItem; +import org.eclipse.zest.core.widgets.GraphNode; +import org.eclipse.zest.core.widgets.ZestStyles; +import org.eclipse.zest.layouts.LayoutAlgorithm; +import org.eclipse.zest.layouts.LayoutStyles; +import org.eclipse.zest.layouts.algorithms.CompositeLayoutAlgorithm; +import org.eclipse.zest.layouts.algorithms.DirectedGraphLayoutAlgorithm; +import org.eclipse.zest.layouts.algorithms.HorizontalShift; + +public class ResolveVisualizerView extends ViewPart implements IZoomableWorkbenchPart { + private GraphViewer viewer; + private FormToolkit toolKit; + + private Action focusDialogAction; + private Action focusDialogActionToolbar; + private Action focusOnSelectionAction; + private Action hideSelectionAction; + private Action showHiddenAction; + private Action applyDefaultLayoutAction; + private Action historyAction; + private Action forwardAction; + + private ZoomContributionViewItem contextZoomContributionViewItem; + private ZoomContributionViewItem toolbarZoomContributionViewItem; + + private Stack/**/ historyStack; + private Stack/**/ forwardStack; + + private IvyNodeElement currentRoot; + private IvyNodeElement currentSelection; + + private ResolveVisualizerContentProvider contentProvider = new ResolveVisualizerContentProvider(); + private MessageContentProvider messageContentProvider = new MessageContentProvider(); + private IvyNodeLabelProvider labelProvider; + private ResolveVisualizerForm visualizationForm; + + private ForceHiddenFilter forceHiddenFilter; + + public ResolveVisualizerView() { + historyStack = new Stack/**/(); + forwardStack = new Stack/**/(); + + forceHiddenFilter = new ForceHiddenFilter(); + forceHiddenFilter.setEnabled(true); + contentProvider.addFilter(forceHiddenFilter); + } + + /** + * This is a callback that will allow us + * to create the viewer and initialize it. + */ + public void createPartControl(Composite parent) { + toolKit = new FormToolkit(parent.getDisplay()); + + visualizationForm = new ResolveVisualizerForm(parent, toolKit, this); + viewer = visualizationForm.getGraphViewer(); + + this.labelProvider = new IvyNodeLabelProvider(this.viewer); + viewer.setLabelProvider(labelProvider); + viewer.setContentProvider(contentProvider); + viewer.setInput(null); + viewer.setConnectionStyle(ZestStyles.CONNECTIONS_DIRECTED); + viewer.setLayoutAlgorithm( + new CompositeLayoutAlgorithm( + LayoutStyles.NO_LAYOUT_NODE_RESIZING, + new LayoutAlgorithm[] { new DirectedGraphLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING), + new HorizontalShift(LayoutStyles.NO_LAYOUT_NODE_RESIZING) }) + ); + + viewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + Object selectedElement = ((IStructuredSelection) event.getSelection()).getFirstElement(); + if (selectedElement instanceof EntityConnectionData) { + return; + } + ResolveVisualizerView.this.selectionChanged((IvyNodeElement) selectedElement); + } + }); + + viewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + focusOnSelectionAction.run(); + } + }); + + visualizationForm.getSearchBox().addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + String textString = visualizationForm.getSearchBox().getText(); + + HashMap figureListing = new HashMap(); + ArrayList list = new ArrayList(); + Iterator iterator = viewer.getGraphControl().getNodes().iterator(); + while (iterator.hasNext()) { + GraphItem item = (GraphItem) iterator.next(); + figureListing.put(item.getText(), item); + } + iterator = figureListing.keySet().iterator(); + if (textString.length() > 0) { + while (iterator.hasNext()) { + String string = (String) iterator.next(); + if (string.toLowerCase().indexOf(textString.toLowerCase()) >= 0) { + list.add(figureListing.get(string)); + } + } + } + viewer.getGraphControl().setSelection((GraphItem[]) list.toArray(new GraphItem[list.size()])); + } + }); + + messageContentProvider.setMessageManager(visualizationForm.getManagedForm().getMessageManager()); + contextZoomContributionViewItem = new ZoomContributionViewItem(this); + toolbarZoomContributionViewItem = new ZoomContributionViewItem(this); + + // Create the help context id for the viewer's control + makeActions(); + hookContextMenu(); + contributeToActionBars(); + } + + private void hookContextMenu() { + MenuManager menuMgr = new MenuManager("#PopupMenu"); + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + ResolveVisualizerView.this.fillContextMenu(manager); + } + }); + Menu menu = menuMgr.createContextMenu(viewer.getControl()); + viewer.getControl().setMenu(menu); + getSite().registerContextMenu(menuMgr, viewer); + } + + private void contributeToActionBars() { + IActionBars bars = getViewSite().getActionBars(); + bars.getMenuManager().add(toolbarZoomContributionViewItem); + fillLocalPullDown(bars.getMenuManager()); + fillLocalToolBar(bars.getToolBarManager()); + } + + private void fillLocalPullDown(IMenuManager manager) { + } + + private void fillContextMenu(IMenuManager manager) { + manager.add(new Separator()); + manager.add(focusDialogAction); + manager.add(focusOnSelectionAction); + manager.add(new Separator()); + manager.add(historyAction); + manager.add(forwardAction); + manager.add(new Separator()); + manager.add(hideSelectionAction); + manager.add(showHiddenAction); + manager.add(new Separator()); + manager.add(applyDefaultLayoutAction); + manager.add(new Separator()); + manager.add(contextZoomContributionViewItem); + } + + private void fillLocalToolBar(IToolBarManager toolBarManager) { + toolBarManager.add(focusDialogActionToolbar); + toolBarManager.add(new Separator()); + toolBarManager.add(historyAction); + toolBarManager.add(forwardAction); + } + + private void makeActions() { + focusDialogAction = new Action() { + public void run() { + ClasspathContainerSelectionDialog dialog = new ClasspathContainerSelectionDialog(viewer.getControl().getShell(), true); + dialog.create(); + int dialogStatus = dialog.open(); + if (dialogStatus == Window.OK) { + + final IvyClasspathContainer container = (IvyClasspathContainer) dialog.getFirstResult(); + + ResolveReport report = container.getState().getResolveReport(); + if(report == null) { + // TODO Employing this resolve job is a temporary technique for taking our best shot at + // guaranteeing a ResolveReport is available to the view. There is still a decision + // to be made on whether we should use the ResolveReport or some other Ivy api construct + // to build the view. + Job resolveJob = new Job("Preparing report for view") { + + protected IStatus run(IProgressMonitor monitor) { + container.launchResolve(false, false, monitor); + return Status.OK_STATUS; + } + }; + + resolveJob.addJobChangeListener(new JobChangeAdapter() { + public void done(IJobChangeEvent event) { + super.done(event); + getViewSite().getShell().getDisplay().syncExec(new Runnable() { + public void run() { + focusOnContainer(container); + } + }); + } + }); + resolveJob.setUser(true); + resolveJob.schedule(); + } + else { + // a resolve report is already saved on the container's state, we will use it + focusOnContainer(container); + } + + // When a new plug-in is selected, disable the forward action + // The forward action only stores history when the back button was used (much like a browser) + forwardStack.clear(); + forwardAction.setEnabled(false); + } + } + }; + focusDialogAction.setText("Focus on ivy file..."); + + focusDialogActionToolbar = new Action() { + public void run() { + focusDialogAction.run(); + } + }; + focusDialogActionToolbar.setToolTipText("Focus on ivy file..."); + focusDialogActionToolbar.setImageDescriptor(IvyPlugin.getImageDescriptor("icons/focus.gif")); + + focusOnSelectionAction = new Action() { + public void run() { + if(currentSelection != null) { + if (currentRoot != currentSelection) { + if(currentRoot != null) { + historyStack.push(currentRoot); + historyAction.setEnabled(true); + } + focusOn(currentSelection); + } + } + } + }; + focusOnSelectionAction.setText("Focus on selection"); + focusOnSelectionAction.setEnabled(false); + + historyAction = new Action() { + public void run() { + if (historyStack.size() > 0) { + IvyNodeElement element = (IvyNodeElement) historyStack.pop(); + forwardStack.push(currentRoot); + forwardAction.setEnabled(true); + focusOn(element); + if (historyStack.size() <= 0) { + historyAction.setEnabled(false); + } + } + } + }; + historyAction.setText("Back"); + historyAction.setToolTipText("Back"); + historyAction.setEnabled(false); + historyAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_BACK)); + + forwardAction = new Action() { + public void run() { + if (forwardStack.size() > 0) { + IvyNodeElement element = (IvyNodeElement) forwardStack.pop(); + + historyStack.push(currentRoot); + historyAction.setEnabled(true); + + focusOn(element); + if (forwardStack.size() <= 0) { + forwardAction.setEnabled(false); + } + } + } + }; + + forwardAction.setText("Forward"); + forwardAction.setToolTipText("Forward"); + forwardAction.setEnabled(false); + forwardAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD)); + + hideSelectionAction = new Action() { + public void run() { + forceHiddenFilter.addHidden(currentSelection); + refresh(); + } + }; + hideSelectionAction.setText("Hide"); + + showHiddenAction = new Action() { + public void run() { + forceHiddenFilter.clearHidden(); + refresh(); + } + }; + showHiddenAction.setText("Show hidden"); + + applyDefaultLayoutAction = new Action() { + public void run() { + viewer.applyLayout(); + } + }; + applyDefaultLayoutAction.setText("Apply default layout"); + applyDefaultLayoutAction.setImageDescriptor(IvyPlugin.getImageDescriptor("icons/refresh.gif")); + } + + /** + * Passing the focus request to the viewer's control. + */ + public void setFocus() { + viewer.getControl().setFocus(); + } + + private final void focusOnContainer(IvyClasspathContainer container) { + ResolveReport report = container.getState().getResolveReport(); + + if(report != null) { + visualizationForm.getForm().setText(ResolveVisualizerForm.HeaderText + " - " + container.getConf().getIvyXmlPath() + " in \"" + container.getConf() + .getJavaProject().getProject().getName() + "\""); + + // TODO is there a better way to get the root? + IvyNode rootNode = ((IvyNode) report.getDependencies().get(0)).getRoot(); +// IvyNodeElement.clearCache(); + forceHiddenFilter.clearHidden(); + +// IvyNodeElement nextRoot = IvyNodeElement.adapt(rootNode); + IvyNodeElement nextRoot = IvyNodeElementAdapter.adapt(report); + + if (currentRoot != nextRoot) { + if(currentRoot != null) { + historyStack.push(currentRoot); + historyAction.setEnabled(true); + } + focusOn(nextRoot); + } + } + } + + /** + * Update the view to focus on a particular bundle. If record history is set + * to true, and bundle does not equal the current bundle, then the current + * bundle will be saved on the history stack + * + * @param focus + * @param recordHistory + */ + public void focusOn(IvyNodeElement focus) { + viewer.setSelection(new StructuredSelection(focus)); + viewer.setFilters(new ViewerFilter[] {}); + viewer.setInput(focus); + + Iterator nodes = viewer.getGraphControl().getNodes().iterator(); + Graph graph = viewer.getGraphControl(); + Dimension centre = new Dimension(graph.getBounds().width / 2, graph.getBounds().height / 2); + while (nodes.hasNext()) { + GraphNode graphNode = (GraphNode) nodes.next(); + if (graphNode.getLocation().x <= 1 && graphNode.getLocation().y <= 1) { + graphNode.setLocation(centre.width, centre.height); + } + } + + currentRoot = focus; + + if (viewer.getGraphControl().getNodes().size() > 0) { + visualizationForm.enableSearchBox(true); + } else { + visualizationForm.enableSearchBox(false); + } + visualizationForm.enableSearchBox(true); + focusOnSelectionAction.setEnabled(true); + + selectionChanged(focus); + } + + /** + * Handle the select changed. This will update the view whenever a selection + * occurs. + * + * @param selectedItem + */ + private void selectionChanged(IvyNodeElement selectedItem) { + currentSelection = selectedItem; + labelProvider.setCurrentSelection(currentRoot, selectedItem); + messageContentProvider.selectionChanged(currentRoot); + viewer.update(contentProvider.getElements(currentRoot), null); + } + + public AbstractZoomableViewer getZoomableViewer() { + return viewer; + } + + public void setAutoSelectDecorator(ILabelDecoratorAlgorithm algorithm) { + labelProvider.setAutoSelectDecorator(algorithm); + + if (viewer.getSelection() != null) { + Object selected = ((IStructuredSelection) viewer.getSelection()).getFirstElement(); + this.selectionChanged((IvyNodeElement) selected); + } + } + + public ResolveVisualizerContentProvider getContentProvider() { + return contentProvider; + } + + public void refresh() { + viewer.refresh(); + viewer.applyLayout(); + } + + public IvyNodeElement getCurrentRoot() { + return currentRoot; + } + + private class ForceHiddenFilter extends IvyNodeElementFilterAdapter { + private Collection/**/ forceHidden = new HashSet/**/(); + + public boolean accept(IvyNodeElement unfiltered) { + return !forceHidden.contains(unfiltered); + } + + public void addHidden(IvyNodeElement hide) { + forceHidden.addAll(Arrays.asList(hide.getDeepDependencies())); + } + + public void clearHidden() { + forceHidden.clear(); + } + } +} \ No newline at end of file Index: src/java/org/apache/ivyde/resolvevisualizer/ClasspathContainerSelectionDialog.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/ClasspathContainerSelectionDialog.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/ClasspathContainerSelectionDialog.java (revision 0) @@ -0,0 +1,37 @@ +package org.apache.ivyde.resolvevisualizer; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.ivyde.eclipse.cpcontainer.IvyClasspathContainer; +import org.apache.ivyde.eclipse.cpcontainer.IvyClasspathUtil; +import org.apache.ivyde.eclipse.revdepexplorer.IvyUtil; +import org.eclipse.core.resources.IProject; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.ElementListSelectionDialog; + +public class ClasspathContainerSelectionDialog extends ElementListSelectionDialog { + public ClasspathContainerSelectionDialog(Shell parentShell, boolean multipleSelection) { + super(parentShell, new LabelProvider() { + public String getText(Object element) { + IvyClasspathContainer container = (IvyClasspathContainer) element; + return container.getConf().getJavaProject().getProject().getName() + " -> " + + container.getDescription(); + } + }); + setTitle("Ivy Classpath Containers"); + setMessage("Select a container to view in the resolve visualizer."); + + // TODO refactor IvyUtil out of the revdepexplorer package as it is + // a more general purpose utility + List/**/ classpathContainers = new ArrayList/**/(); + IProject[] ivyProjects = IvyUtil.getIvyProjectsInWorkspace(); + for(int i = 0; i < ivyProjects.length; i++) { + classpathContainers.addAll(IvyClasspathUtil.getIvyClasspathContainers(ivyProjects[i])); + } + + setElements(classpathContainers.toArray()); + setMultipleSelection(multipleSelection); + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/IvyNodeLabelProvider.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/IvyNodeLabelProvider.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/IvyNodeLabelProvider.java (revision 0) @@ -0,0 +1,294 @@ +package org.apache.ivyde.resolvevisualizer; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.ivyde.eclipse.IvyPlugin; +import org.apache.ivyde.resolvevisualizer.label.ConfigurationConflictAlgorithm; +import org.apache.ivyde.resolvevisualizer.label.ConnectionStyle; +import org.apache.ivyde.resolvevisualizer.label.DirectDependenciesAlgorithm; +import org.apache.ivyde.resolvevisualizer.label.ILabelDecoratorAlgorithm; +import org.apache.ivyde.resolvevisualizer.label.ShortestRootPathAlgorithm; +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.Label; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.eclipse.zest.core.viewers.EntityConnectionData; +import org.eclipse.zest.core.viewers.GraphViewer; +import org.eclipse.zest.core.viewers.IConnectionStyleProvider; +import org.eclipse.zest.core.viewers.IEntityStyleProvider; +import org.eclipse.zest.core.widgets.ZestStyles; + +/** + * Alters the color of the labels and connections based on the selected analysis algorithm. + */ +public class IvyNodeLabelProvider implements ILabelProvider, IConnectionStyleProvider, IEntityStyleProvider { + public Color GRAY = new Color(Display.getDefault(), 128, 128, 128); + public Color LIGHT_GRAY = new Color(Display.getDefault(), 220, 220, 220); + public Color BLACK = new Color(Display.getDefault(), 0, 0, 0); + public Color RED = new Color(Display.getDefault(), 255, 0, 0); + public Color LIGHT_GREEN = new Color(Display.getDefault(), 96, 255, 96); + + private IvyNodeElement selected = null; + private IvyNodeElement rootNode = null; + private Map/**/ highlightedRelationships = new HashMap/**/(); + private Map/**/ highlightedDependencies = new HashMap/**/(); + private Color disabledColor = null; + private IvyNodeElement pinnedNode = null; + private GraphViewer viewer; + + private ILabelDecoratorAlgorithm autoSelectDecorator = new ShortestRootPathAlgorithm(); + private DirectDependenciesAlgorithm rootDirectDependenciesDecorator = new DirectDependenciesAlgorithm();; + private ConfigurationConflictAlgorithm conflictDecorator = new ConfigurationConflictAlgorithm(); + + private Color rootColor; + private Color rootSelectedColor; + + public IvyNodeLabelProvider(GraphViewer viewer) { + this.viewer = viewer; + this.rootDirectDependenciesDecorator.setStyles( + new Color(Display.getDefault(), 197, 237, 197), + new ConnectionStyle(ZestStyles.CONNECTIONS_SOLID, + new Color(Display.getDefault(), 175, 175, 175), 1, false)); + } + + public Image getImage(Object element) { + if (element instanceof IvyNodeElement) { + IvyNodeElement node = (IvyNodeElement) element; + if(node.isEvicted()) { + return IvyPlugin.getImageDescriptor("icons/evicted.gif").createImage(); + } + } + + return null; + } + + public String getText(Object element) { + if (element instanceof IvyNodeElement) { + IvyNodeElement node = (IvyNodeElement) element; + String text = node.getOrganization() + "#" + node.getName() + ";"; + if (node.getRevision().indexOf("working@") != -1) + text += "WORKSPACE"; + else + text += node.getRevision(); + return text; + } + + return ""; + } + + public void addListener(ILabelProviderListener listener) { + } + + public void removeListener(ILabelProviderListener listener) { + } + + public boolean isLabelProperty(Object element, String property) { + return false; + } + + /** + * Colors all connections regardless of their selection status. + */ + public Color getColor(Object rel) { + if (highlightedRelationships.keySet().contains(rel)) { + ConnectionStyle style = (ConnectionStyle) highlightedRelationships.get(rel); + return style.getHighlightColor(); + } + return LIGHT_GRAY; + } + + public int getConnectionStyle(Object rel) { + return ZestStyles.CONNECTIONS_DIRECTED; + } + + /** + * Colors "highlighted" relationships. We want to differentiate + * between those highlighted programatically by the auto-select + * mechanism, and those hand-selected by the user. + */ + public Color getHighlightColor(Object rel) { + if (highlightedRelationships.keySet().contains(rel)) { + ConnectionStyle style = (ConnectionStyle) highlightedRelationships.get(rel); + return style.getHighlightColor(); + } + return ColorConstants.blue; + } + + public Color getNodeHighlightColor(Object entity) { + return null; + } + + public int getLineWidth(Object rel) { + if(highlightedRelationships.keySet().contains(rel)) { + ConnectionStyle style = (ConnectionStyle) highlightedRelationships.get(rel); + if(style.isRevealOnHighlight()) { + return style.getLineWidth(); + } + } + return 1; + } + + public Color getAdjacentEntityHighlightColor(Object entity) { + return null; + } + + public Color getBorderColor(Object entity) { + if (this.selected != null || this.pinnedNode != null) { + if (entity == this.selected || entity == this.pinnedNode) { + return BLACK; + } else if (highlightedDependencies.keySet().contains(entity)) { + // If this entity is directly connected to the selected entity + return BLACK; + } else { + return LIGHT_GRAY; + } + + } + + return BLACK; + } + + public Color getBorderHighlightColor(Object entity) { + return null; + } + + public int getBorderWidth(Object entity) { + return 0; + } + + public Color getBackgroundColour(Object entity) { + + if (entity == this.rootNode) { + if (rootColor == null) { + rootColor = LIGHT_GREEN; + } + return rootColor; + } + if (highlightedDependencies.keySet().contains(entity)) { + return (Color) highlightedDependencies.get(entity); //viewer.getGraphControl().HIGHLIGHT_ADJACENT_COLOR; + } else { + return viewer.getGraphControl().DEFAULT_NODE_COLOR; + } + } + + public Color getForegroundColour(Object entity) { + if (this.selected != null || this.pinnedNode != null) { + if (entity == this.selected || this.pinnedNode == entity) { + return BLACK; + } else if (highlightedDependencies.keySet().contains(entity)) { + // If this entity is directly connected to the selected entity + return BLACK; + } else { + return GRAY; + } + + } + return BLACK; + } + + public void setPinnedNode(IvyNodeElement pinnedNode) { + this.pinnedNode = pinnedNode; + } + + protected IvyNodeElement getSelected() { + if (pinnedNode != null) { + return pinnedNode; + } + return selected; + } + + /** + * Sets the current selection + * + * @param root + * @param currentSelection + */ + public void setCurrentSelection(IvyNodeElement root, IvyNodeElement currentSelection) { + for (Iterator iter = highlightedRelationships.keySet().iterator(); iter.hasNext();) { + EntityConnectionData entityConnectionData = (EntityConnectionData) iter.next(); + + ConnectionStyle style = (ConnectionStyle) highlightedRelationships.get(entityConnectionData); + if(style.isRevealOnHighlight()) { + viewer.unReveal(entityConnectionData); + } + } + + this.rootNode = root; + this.selected = null; + this.selected = currentSelection; + + highlightedRelationships = new HashMap/**/(); + highlightedDependencies = new HashMap/**/(); + + rootDirectDependenciesDecorator.calculateHighlighted(root, root, highlightedRelationships, + highlightedDependencies); + conflictDecorator.calculateHighlighted(root, root, highlightedRelationships, + highlightedDependencies); + + if (this.selected != null || this.pinnedNode != null) { + autoSelectDecorator.calculateHighlighted( + root, selected, highlightedRelationships, + highlightedDependencies); + } + + Object[] connections = viewer.getConnectionElements(); + for (Iterator iter = highlightedRelationships.keySet().iterator(); iter.hasNext();) { + Object entityConnectionData = iter.next(); + + ConnectionStyle style = (ConnectionStyle) highlightedRelationships.get(entityConnectionData); + if (style.isRevealOnHighlight()) { + viewer.reveal(entityConnectionData); + } + } + + for (int i = 0; i < connections.length; i++) { + viewer.update(connections[i], null); + } + } + + public void dispose() { + if (this.disabledColor != null) { + this.disabledColor.dispose(); + this.disabledColor = null; + } + if ( this.rootColor != null) { + this.rootColor.dispose(); + this.rootColor = null; + } + if ( this.rootSelectedColor != null) { + this.rootSelectedColor.dispose(); + this.rootSelectedColor = null; + } + } + + public IFigure getTooltip(Object entity) { + if(entity instanceof EntityConnectionData) { + EntityConnectionData connection = (EntityConnectionData) entity; + IvyNodeElement source = (IvyNodeElement) connection.source; + IvyNodeElement dest = (IvyNodeElement) connection.dest; + String[] confs = dest.getCallerConfigurations(source); + String tooltipText = ""; + for(int i = 0; i < confs.length; i++) { + tooltipText += confs[i] + ", "; + } + return new Label(tooltipText.substring(0, tooltipText.length() - 2)); + } + + return null; + } + + public boolean fisheyeNode(Object entity) { + return false; + } + + public void setAutoSelectDecorator(ILabelDecoratorAlgorithm decoratorAlgorithm) { + this.autoSelectDecorator = decoratorAlgorithm; + } +} \ No newline at end of file Index: src/java/org/apache/ivyde/resolvevisualizer/MessageContentProvider.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/MessageContentProvider.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/MessageContentProvider.java (revision 0) @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +import org.apache.ivy.core.module.id.ModuleId; +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.ui.forms.IMessageManager; + +public class MessageContentProvider { + private IMessageManager manager; + + /** + * Called when the view selection changes and the message list needs to be rebuilt. + */ + public void selectionChanged(IvyNodeElement root) { + manager.removeAllMessages(); + + Map/*>*/ conflicts = new HashMap/*>*/(); + + IvyNodeElement[] deepDependencies = root.getDeepDependencies(); + for(int i = 0; i < deepDependencies.length; i++) { + if(deepDependencies[i].getConflicts().length > 0) { + Collection/**/ conflictParticipants = (Collection) conflicts.get( + deepDependencies[i].getModuleRevisionId().getModuleId()); + if(conflictParticipants == null) + conflictParticipants = new HashSet/**/(); + conflictParticipants.add(deepDependencies[i]); + conflicts.put(deepDependencies[i].getModuleRevisionId().getModuleId(), conflictParticipants); + } + } + + for(Iterator conflictIter = conflicts.keySet().iterator(); conflictIter.hasNext();) { + ModuleId conflictKey = (ModuleId) conflictIter.next(); + manager.addMessage(conflictKey, "Conflict on module " + conflictKey.getOrganisation() + "#" + conflictKey.getName(), + conflicts.get(conflictKey), IMessageProvider.ERROR); + } + } + + public void setMessageManager(IMessageManager manager) { + this.manager = manager; + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/ResolveVisualizerContentProvider.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/ResolveVisualizerContentProvider.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/ResolveVisualizerContentProvider.java (revision 0) @@ -0,0 +1,71 @@ +package org.apache.ivyde.resolvevisualizer; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import org.apache.ivyde.resolvevisualizer.model.IIvyNodeElementFilter; +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.zest.core.viewers.IGraphEntityContentProvider; + +public class ResolveVisualizerContentProvider implements IGraphEntityContentProvider { + Collection/**/ filters = new HashSet/**/(); + + // Returns all entities that should be linked with the given entity + public Object[] getConnectedTo(Object entity) { + return filter(((IvyNodeElement) entity).getDependencies()); + } + + public Object[] getElements(Object inputElement) { + if(inputElement == null) { + return new Object[] {}; + } + else + { + IvyNodeElement inputNode = (IvyNodeElement) inputElement; + List elements = Arrays.asList(filter(inputNode.getDeepDependencies())); + Collections.sort(elements, new IvyNodeElementComparator()); + return elements.toArray(); + } + } + + public IvyNodeElement[] filter(IvyNodeElement[] deepDependencies) { + IvyNodeElement[] filtered = deepDependencies; + for(Iterator iter = filters.iterator(); iter.hasNext();) { + IIvyNodeElementFilter filter = (IIvyNodeElementFilter) iter.next(); + filtered = filter.filter(filtered); // I love this line + } + + return filtered; + } + + public void addFilter(IIvyNodeElementFilter filter) { + filters.add(filter); + } + + public void dispose() { + // nothing to dispose + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + private class IvyNodeElementComparator implements Comparator { + public int compare(Object arg1, Object arg2) { + IvyNodeElement element1 = (IvyNodeElement) arg1; + IvyNodeElement element2 = (IvyNodeElement) arg2; + + if(element1.getDepth() > element2.getDepth()) + return -1; + else if(element1.getDepth() < element2.getDepth()) + return 1; + + return element1.getModuleRevisionId().toString().compareTo(element2.getModuleRevisionId().toString()); + } + } +} \ No newline at end of file Index: src/java/org/apache/ivyde/resolvevisualizer/ResolveVisualizerForm.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/ResolveVisualizerForm.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/ResolveVisualizerForm.java (revision 0) @@ -0,0 +1,514 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Set; + +import org.apache.ivyde.eclipse.ui.views.ResolveVisualizerView; +import org.apache.ivyde.resolvevisualizer.label.AllCallersAlgorithm; +import org.apache.ivyde.resolvevisualizer.label.AllDependencyAlgorithm; +import org.apache.ivyde.resolvevisualizer.label.AllRootPathsAlgorithm; +import org.apache.ivyde.resolvevisualizer.label.SameModuleIdAlgorithm; +import org.apache.ivyde.resolvevisualizer.label.ShortestRootPathAlgorithm; +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElementFilterAdapter; +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.FigureCanvas; +import org.eclipse.draw2d.LineBorder; +import org.eclipse.draw2d.parts.ScrollableThumbnail; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.layout.FormLayout; +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.Display; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Spinner; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.forms.IMessage; +import org.eclipse.ui.forms.ManagedForm; +import org.eclipse.ui.forms.events.HyperlinkAdapter; +import org.eclipse.ui.forms.events.HyperlinkEvent; +import org.eclipse.ui.forms.widgets.Form; +import org.eclipse.ui.forms.widgets.FormText; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; +import org.eclipse.ui.forms.widgets.Section; +import org.eclipse.ui.forms.widgets.TableWrapData; +import org.eclipse.ui.forms.widgets.TableWrapLayout; +import org.eclipse.zest.core.viewers.GraphViewer; +import org.eclipse.zest.core.widgets.Graph; + +public class ResolveVisualizerForm { + public static final String HeaderText = "Ivy Resolve Visualization"; + + private FormToolkit toolkit; + private GraphViewer viewer; + private ScrolledForm form; + private ManagedForm managedForm; + private ResolveVisualizerView view; + + private Label searchLabel; + private Text searchBox; + + private SashForm sash; + + // various auto-select options + private Button showAllDependencies; + private Button showAllCallers; + private Button showShortestRootPath; + private Button showAllRootPaths; + private Button showSameModuleId; + + private Button evictionFilterEnablement; + private Button depthLimitFilterEnablement; + private Spinner depthLimit; + + private ThumbnailNavigator thumbnailNavigator; + + private DepthFilter depthFilter = new DepthFilter(); + private EvictionFilter evictionFilter = new EvictionFilter(); + + public ResolveVisualizerForm(Composite parent, FormToolkit toolkit, ResolveVisualizerView view) { + this.toolkit = toolkit; + this.view = view; + form = this.toolkit.createScrolledForm(parent); + managedForm = new ManagedForm(this.toolkit, this.form); + createHeaderRegion(form); + FillLayout layout = new FillLayout(); + layout.marginHeight = 10; + layout.marginWidth = 4; + form.getBody().setLayout(layout); + + this.toolkit.decorateFormHeading(this.form.getForm()); + createSash(form.getBody()); + + view.getContentProvider().addFilter(depthFilter); + view.getContentProvider().addFilter(evictionFilter); + } + + /** + * Creates the section of the form where the graph is drawn + * + * @param parent + */ + private void createGraphSection(Composite parent) { + Section section = this.toolkit.createSection(parent, Section.TITLE_BAR); + thumbnailNavigator = new ThumbnailNavigator(section, SWT.NONE); + viewer = new InternalGraphViewer(thumbnailNavigator, SWT.NONE); + viewer.getGraphControl().setVerticalScrollBarVisibility(FigureCanvas.NEVER); + viewer.getGraphControl().setHorizontalScrollBarVisibility(FigureCanvas.NEVER); + thumbnailNavigator.setGraph((Graph)viewer.getControl()); + thumbnailNavigator.setSize(100, 25); + section.setClient(thumbnailNavigator); + } + + private void createHeaderRegion(ScrolledForm form) { + Composite headClient = new Composite(form.getForm().getHead(), SWT.NULL); + GridLayout glayout = new GridLayout(); + glayout.marginWidth = glayout.marginHeight = 0; + glayout.numColumns = 3; + headClient.setLayout(glayout); + headClient.setBackgroundMode(SWT.INHERIT_DEFAULT); + searchLabel = new Label(headClient, SWT.NONE); + searchLabel.setText("Search:"); + searchBox = toolkit.createText(headClient, ""); + GridData data = new GridData(); + data.widthHint = 300; + searchBox.setLayoutData(data); + + toolkit.paintBordersFor(headClient); + form.setHeadClient(headClient); + form.setText(HeaderText); + enableSearchBox(false); + + form.getForm().addMessageHyperlinkListener(new HyperlinkAdapter() { + public void linkActivated(org.eclipse.ui.forms.events.HyperlinkEvent e) { + String title = e.getLabel(); + Object href = e.getHref(); + if (href instanceof IMessage[] && ((IMessage[]) href).length > 1) { + Point hl = ((Control) e.widget).toDisplay(0, 0); + hl.x += 10; + hl.y += 10; + final Shell shell = new Shell(ResolveVisualizerForm.this.form.getShell(), SWT.ON_TOP | SWT.TOOL); + shell.setImage(getImage(ResolveVisualizerForm.this.form.getMessageType())); + shell.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + shell.setBackgroundMode(SWT.INHERIT_DEFAULT); + GridLayout layout = new GridLayout(); + layout.numColumns = 1; + layout.verticalSpacing = 0; + shell.setText(title); + shell.setLayout(layout); + Link link = new Link(shell, SWT.NONE); + link.setText("close"); + GridData data = new GridData(SWT.RIGHT, SWT.CENTER, false, false); + link.setLayoutData(data); + link.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + shell.close(); + } + }); + Group group = new Group(shell, SWT.NONE); + data = new GridData(SWT.LEFT, SWT.TOP, true, true); + group.setLayoutData(data); + group.setLayout(layout); + group.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); + FormText text = toolkit.createFormText(group, true); + configureFormText(ResolveVisualizerForm.this.form.getForm(), text); + if (href instanceof IMessage[]) { + text.setText(createFormTextContent((IMessage[]) href), true, false); + } + + shell.setLocation(hl); + shell.pack(); + shell.open(); + } else if (href instanceof IMessage[]) { + IMessage oneMessage = ((IMessage[]) href)[0]; + Set/**/ conflicts = (Set/**/) oneMessage.getData(); + if (conflicts != null) { + viewer.setSelection(new StructuredSelection(new ArrayList(conflicts))); + } + } + } + }); + } + + public void enableSearchBox(boolean enable) { + this.searchLabel.setEnabled(enable); + this.searchBox.setEnabled(enable); + } + + /** + * Creates the sash form to separate the graph from the controls. + * + * @param parent + */ + private void createSash(Composite parent) { + sash = new SashForm(parent, SWT.NONE); + this.toolkit.paintBordersFor(parent); + + createGraphSection(sash); + createOptionsSection(sash); + sash.setWeights(new int[] { 10, 2 }); + } + + private void createOptionsSection(Composite parent) { + Section controls = this.toolkit.createSection(parent, Section.TITLE_BAR | Section.EXPANDED); + + controls.setText("Options"); + Composite controlComposite = new Composite(controls, SWT.NONE) { + public Point computeSize(int hint, int hint2, boolean changed) { + return new Point(0, 0); + } + }; + this.toolkit.adapt(controlComposite); + controlComposite.setLayout(new GridLayout()); + + Section autoSelectOptions = this.toolkit.createSection(controlComposite, Section.EXPANDED); + autoSelectOptions.setText("Auto Selection"); + autoSelectOptions.setLayout(new FillLayout()); + Composite autoSelectOptionsComposite = this.toolkit.createComposite(autoSelectOptions); + autoSelectOptionsComposite.setLayout(new TableWrapLayout()); + + showShortestRootPath = this.toolkit.createButton(autoSelectOptionsComposite, "Shortest path to root", SWT.RADIO); + showShortestRootPath.setLayoutData(new TableWrapData(TableWrapData.FILL)); + showShortestRootPath.setSelection(true); + showShortestRootPath.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + view.setAutoSelectDecorator(new ShortestRootPathAlgorithm()); + } + }); + + showAllRootPaths = this.toolkit.createButton(autoSelectOptionsComposite, "All paths to root", SWT.RADIO); + showAllRootPaths.setLayoutData(new TableWrapData(TableWrapData.FILL)); + showAllRootPaths.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + view.setAutoSelectDecorator(new AllRootPathsAlgorithm()); + } + }); + + showAllCallers = this.toolkit.createButton(autoSelectOptionsComposite, "All callers", SWT.RADIO); + showAllCallers.setLayoutData(new TableWrapData(TableWrapData.FILL)); + showAllCallers.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + view.setAutoSelectDecorator(new AllCallersAlgorithm()); + } + }); + + showAllDependencies = this.toolkit.createButton(autoSelectOptionsComposite, "All dependencies", SWT.RADIO); + showAllDependencies.setLayoutData(new TableWrapData(TableWrapData.FILL)); + showAllDependencies.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + view.setAutoSelectDecorator(new AllDependencyAlgorithm()); + } + }); + + showSameModuleId = this.toolkit.createButton(autoSelectOptionsComposite, "Other revisions", SWT.RADIO); + showSameModuleId.setLayoutData(new TableWrapData(TableWrapData.FILL)); + showSameModuleId.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + view.setAutoSelectDecorator(new SameModuleIdAlgorithm()); + } + }); + + autoSelectOptions.setClient(autoSelectOptionsComposite); + + Section filterOptions = this.toolkit.createSection(controlComposite, Section.EXPANDED); + filterOptions.setText("Filter Options"); + filterOptions.setLayout(new FillLayout()); + Composite filterOptionsComposite = this.toolkit.createComposite(filterOptions); + filterOptionsComposite.setLayout(new TableWrapLayout()); + + evictionFilterEnablement = this.toolkit.createButton(filterOptionsComposite, "Hide evicted nodes", SWT.CHECK); + evictionFilterEnablement.setLayoutData(new TableWrapData(TableWrapData.FILL)); + evictionFilterEnablement.setSelection(true); + evictionFilter.setEnabled(true); + evictionFilterEnablement.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + if(evictionFilterEnablement.getSelection()) { + evictionFilter.setEnabled(true); + view.refresh(); + } + else { + evictionFilter.setEnabled(false); + view.refresh(); + } + } + }); + + depthLimitFilterEnablement = this.toolkit.createButton(filterOptionsComposite, "Limit depth", SWT.CHECK); + depthLimitFilterEnablement.setLayoutData(new TableWrapData(TableWrapData.FILL)); + depthLimitFilterEnablement.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + if(depthLimitFilterEnablement.getSelection()) { + depthFilter.setDepth(depthLimit.getSelection()); + depthFilter.setEnabled(true); + view.refresh(); + depthLimit.setEnabled(true); + } + else { + depthFilter.setEnabled(false); + view.refresh(); + depthLimit.setEnabled(false); + } + } + }); + + depthLimit = new Spinner(filterOptionsComposite, 0); + toolkit.adapt(depthLimit); + depthLimit.setMinimum(1); + depthLimit.setSelection(2); + depthLimit.setIncrement(1); + depthLimit.setSize(150,40); + depthLimit.setBackground(new Color(Display.getDefault(), 216, 228, 248)); + depthLimit.setEnabled(false); + depthLimit.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + depthFilter.setDepth(depthLimit.getSelection()); + depthFilter.setEnabled(true); + view.refresh(); + } + }); + + filterOptions.setClient(filterOptionsComposite); + + controls.setClient(controlComposite); + } + + public GraphViewer getGraphViewer() { + return viewer; + } + + public ScrolledForm getForm() { + return form; + } + + public Text getSearchBox() { + return this.searchBox; + } + + private class InternalGraphViewer extends GraphViewer { + public InternalGraphViewer(Composite parent, int style) { + super(parent, style); + Graph graph = new Graph(parent, style) { + public Point computeSize(int hint, int hint2, boolean changed) { + return new Point(0, 0); + } + }; + setControl(graph); + } + } + + private static class ThumbnailNavigator extends Composite { + FigureCanvas thumbnail; + ScrollableThumbnail tb; + + public ThumbnailNavigator(Composite parent, int style) { + super(parent, style); + this.setLayout(new FormLayout()); + createZoomableCanvas(this); + } + + public void setGraph(Graph graph) { + if (graph.getParent() != this) { + throw new AssertionError("Graph must be a child of this zoomable composite."); + } + createContents(graph); + tb.setViewport(graph.getViewport()); + tb.setSource(graph.getContents()); + } + + private void createZoomableCanvas(Composite parent) { + FormData data = new FormData(); + data.top = new FormAttachment(100,-100); + data.left = new FormAttachment(100,-100); + data.right = new FormAttachment(100,0); + data.bottom = new FormAttachment(100,0); + + thumbnail = new FigureCanvas(parent, SWT.NONE); + thumbnail.setBackground(ColorConstants.white); + thumbnail.setLayoutData(data); + + tb = new ScrollableThumbnail(); + tb.setBorder(new LineBorder(1)); + thumbnail.setContents(tb); + } + + private void createContents(Control control) { + FormData data = new FormData(); + data.top = new FormAttachment(0,0); + data.left = new FormAttachment(0,0); + data.right = new FormAttachment(100,0); + data.bottom = new FormAttachment(100,0); + control.setParent(this); + control.setLayoutData(data); + } + + } + + private class DepthFilter extends IvyNodeElementFilterAdapter { + private int depth = 2; + + public boolean accept(IvyNodeElement unfiltered) { + return unfiltered.getDepth() - view.getCurrentRoot().getDepth() <= depth; + } + + public void setDepth(int depth) { + this.depth = depth; + } + } + + private class EvictionFilter extends IvyNodeElementFilterAdapter { + public boolean accept(IvyNodeElement unfiltered) { + return !unfiltered.isEvicted(); + } + } + + public ManagedForm getManagedForm() { + return managedForm; + } + + private Image getImage(int type) { + switch (type) { + case IMessageProvider.ERROR: + return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_ERROR_TSK); + case IMessageProvider.WARNING: + return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_WARN_TSK); + case IMessageProvider.INFORMATION: + return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_INFO_TSK); + } + return null; + } + + private void configureFormText(final Form form, FormText text) { + text.addHyperlinkListener(new HyperlinkAdapter() { + public void linkActivated(HyperlinkEvent e) { + String is = (String) e.getHref(); + try { + ((FormText) e.widget).getShell().dispose(); + int index = Integer.parseInt(is); + IMessage[] messages = form.getChildrenMessages(); + IMessage message = messages[index]; + Set/**/ conflicts = (Set/**/) message.getData(); + if (conflicts != null) { + viewer.setSelection(new StructuredSelection(new ArrayList(conflicts))); + } + } catch (NumberFormatException ex) { + } + } + }); + text.setImage("error", getImage(IMessageProvider.ERROR)); + text.setImage("warning", getImage(IMessageProvider.WARNING)); + text.setImage("info", getImage(IMessageProvider.INFORMATION)); + } + + String createFormTextContent(IMessage[] messages) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + pw.println("
"); + for (int i = 0; i < messages.length; i++) { + IMessage message = messages[i]; + pw.print("
  • "); + if (message.getPrefix() != null) { + pw.print(message.getPrefix()); + } + pw.print(message.getMessage()); + pw.println("
  • "); + } + pw.println("
    "); + pw.flush(); + return sw.toString(); + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/label/AllCallersAlgorithm.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/label/AllCallersAlgorithm.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/label/AllCallersAlgorithm.java (revision 0) @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.label; + +import java.util.Map; + +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; +import org.eclipse.zest.core.viewers.EntityConnectionData; + +public class AllCallersAlgorithm extends LabelDecoratorAlgorithmAdapter { + public void calculateHighlighted(IvyNodeElement root, IvyNodeElement selected, + Map/**/ highlightRelationships, + Map/**/ highlightEntities) { + if (selected != null) { + highlightEntities.put(selected, entityColor); + IvyNodeElement[] directCallers = selected.getCallers(); + for (int i = 0; i < directCallers.length; i++) { + highlightRelationships.put(new EntityConnectionData(directCallers[i], selected), relationshipColor); + highlightEntities.put(directCallers[i], entityColor); + highlightEntities.put(directCallers[i], entityColor); + } + } + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/label/AllDependencyAlgorithm.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/label/AllDependencyAlgorithm.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/label/AllDependencyAlgorithm.java (revision 0) @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.label; + +import java.util.Map; + +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; +import org.eclipse.zest.core.viewers.EntityConnectionData; + +public class AllDependencyAlgorithm extends LabelDecoratorAlgorithmAdapter { + public void calculateHighlighted(IvyNodeElement root, IvyNodeElement selected, + Map/**/ highlightRelationships, + Map/**/ highlightEntities) { + if(selected != null) { + highlightDependenciesRecursive(selected, highlightRelationships, highlightEntities); + } + } + + private void highlightDependenciesRecursive(IvyNodeElement node, + Map/**/ highlightRelationships, + Map/**/ highlightEntities) { + highlightEntities.put(node, entityColor); + + IvyNodeElement[] directDependencies = node.getDependencies(); + for(int i = 0; i < directDependencies.length; i++) { + highlightRelationships.put(new EntityConnectionData(node, directDependencies[i]), relationshipColor); + highlightDependenciesRecursive(directDependencies[i], highlightRelationships, highlightEntities); + } + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/label/AllRootPathsAlgorithm.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/label/AllRootPathsAlgorithm.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/label/AllRootPathsAlgorithm.java (revision 0) @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.label; + +import java.util.Map; + +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; +import org.eclipse.zest.core.viewers.EntityConnectionData; + +public class AllRootPathsAlgorithm extends LabelDecoratorAlgorithmAdapter { + public void calculateHighlighted(IvyNodeElement root, IvyNodeElement selected, + Map/**/ highlightRelationships, + Map/**/ highlightEntities) { + if (selected != null) { + highlightCallersRecursive(selected, highlightRelationships, highlightEntities); + } + } + + private void highlightCallersRecursive(IvyNodeElement node, + Map/**/ highlightRelationships, + Map/**/ highlightEntities) { + highlightEntities.put(node, entityColor); + IvyNodeElement[] directCallers = node.getCallers(); + for(int i = 0; i < directCallers.length; i++) { + highlightRelationships.put(new EntityConnectionData(directCallers[i], node), relationshipColor); + highlightCallersRecursive(directCallers[i], highlightRelationships, highlightEntities); + } + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/label/ConfigurationConflictAlgorithm.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/label/ConfigurationConflictAlgorithm.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/label/ConfigurationConflictAlgorithm.java (revision 0) @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.label; + +import java.util.Map; + +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.swt.graphics.Color; +import org.eclipse.zest.core.widgets.ZestStyles; + +public class ConfigurationConflictAlgorithm extends LabelDecoratorAlgorithmAdapter { + public ConfigurationConflictAlgorithm() { + // set default colors for this algorithm + entityColor = new Color(null, 215, 27, 27); + relationshipColor = new ConnectionStyle(ZestStyles.CONNECTIONS_SOLID, + ColorConstants.red, 1, false); + } + + public void calculateHighlighted(IvyNodeElement root, IvyNodeElement selected, + Map highlightRelationships, Map highlightEntities) { + IvyNodeElement[] deepDependencies = root.getDeepDependencies(); + for(int i = 0; i < deepDependencies.length; i++) { + if(deepDependencies[i].getConflicts().length > 0) { + highlightEntities.put(deepDependencies[i], entityColor); + } + } + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/label/ConnectionStyle.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/label/ConnectionStyle.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/label/ConnectionStyle.java (revision 0) @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.label; + +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Display; +import org.eclipse.zest.core.widgets.ZestStyles; + +public class ConnectionStyle { + private static final Color DARK_RED = new Color(Display.getDefault(), 127, 0, 0); + + private int lineWidth = 1; + private Color highlightColor = DARK_RED; + private int connectionStyle = ZestStyles.CONNECTIONS_SOLID; + private boolean revealOnHighlight = true; + + /** + * Accept the defaults + */ + public ConnectionStyle() { + } + + public ConnectionStyle(int connectionStyle, Color highlightColor, int lineWidth, + boolean revealOnHighlight) { + super(); + this.connectionStyle = connectionStyle; + this.highlightColor = highlightColor; + this.lineWidth = lineWidth; + this.revealOnHighlight = revealOnHighlight; + } + + public static Color getDARK_RED() { + return DARK_RED; + } + + public int getLineWidth() { + return lineWidth; + } + + public Color getHighlightColor() { + return highlightColor; + } + + public int getConnectionStyle() { + return connectionStyle; + } + + public boolean isRevealOnHighlight() { + return revealOnHighlight; + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/label/DirectDependenciesAlgorithm.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/label/DirectDependenciesAlgorithm.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/label/DirectDependenciesAlgorithm.java (revision 0) @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.label; + +import java.util.Map; + +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; +import org.eclipse.zest.core.viewers.EntityConnectionData; + +public class DirectDependenciesAlgorithm extends LabelDecoratorAlgorithmAdapter { + public void calculateHighlighted(IvyNodeElement root, IvyNodeElement selected, + Map/**/ highlightRelationships, + Map/**/ highlightEntities) { + if(selected != null) { + highlightEntities.put(root, entityColor); + IvyNodeElement[] dependencies = root.getDependencies(); + for(int i = 0; i < dependencies.length; i++) { + highlightEntities.put(dependencies[i], entityColor); + highlightRelationships.put( + new EntityConnectionData(root, dependencies[i]), + relationshipColor); + } + } + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/label/ILabelDecoratorAlgorithm.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/label/ILabelDecoratorAlgorithm.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/label/ILabelDecoratorAlgorithm.java (revision 0) @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.label; + +import java.util.Map; + +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; + +public interface ILabelDecoratorAlgorithm { + public void calculateHighlighted(IvyNodeElement root, IvyNodeElement selected, + Map/**/ highlightRelationships, + Map/**/ highlightEntities); +} \ No newline at end of file Index: src/java/org/apache/ivyde/resolvevisualizer/label/LabelDecoratorAlgorithmAdapter.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/label/LabelDecoratorAlgorithmAdapter.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/label/LabelDecoratorAlgorithmAdapter.java (revision 0) @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.label; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.swt.graphics.Color; + + +public abstract class LabelDecoratorAlgorithmAdapter implements ILabelDecoratorAlgorithm { + protected Color entityColor = ColorConstants.orange; + protected ConnectionStyle relationshipColor = new ConnectionStyle(); + + /** + * Specify custom colors for this algorithm instance. + * @param entityColor + * @param relationshipColor + */ + public void setStyles(Color entityColor, ConnectionStyle relationshipColor) { + this.entityColor = entityColor; + this.relationshipColor = relationshipColor; + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/label/SameModuleIdAlgorithm.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/label/SameModuleIdAlgorithm.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/label/SameModuleIdAlgorithm.java (revision 0) @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.label; + +import java.util.Map; + +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; + +public class SameModuleIdAlgorithm extends LabelDecoratorAlgorithmAdapter { + public void calculateHighlighted(IvyNodeElement root, IvyNodeElement selected, + Map/**/ highlightRelationships, + Map/**/ highlightEntities) { + if (selected != null) { + IvyNodeElement[] deepDependencies = root.getDeepDependencies(); + for(int i = 0; i < deepDependencies.length; i++) { + if(deepDependencies[i].getOrganization().equals(selected.getOrganization()) && + deepDependencies[i].getName().equals(selected.getName())) { + highlightEntities.put(deepDependencies[i], entityColor); + } + } + } + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/label/ShortestRootPathAlgorithm.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/label/ShortestRootPathAlgorithm.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/label/ShortestRootPathAlgorithm.java (revision 0) @@ -0,0 +1,78 @@ +package org.apache.ivyde.resolvevisualizer.label; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; + +import org.apache.ivyde.resolvevisualizer.model.IvyNodeElement; +import org.eclipse.zest.core.viewers.EntityConnectionData; + +public class ShortestRootPathAlgorithm extends LabelDecoratorAlgorithmAdapter { + public void calculateHighlighted(IvyNodeElement root, IvyNodeElement selected, + Map/**/ highlightRelationships, + Map/**/ highlightEntities) { + // Calculates the smart path. + if (selected != null) { + IvyNodeElement[] path = getShortestPathToDescendent(root, selected); + if (path.length > 1) { + for (int i = 0; i < path.length-1; i++) { + EntityConnectionData entityConnectionData = new EntityConnectionData(path[i+1], path[i]); + highlightRelationships.put(entityConnectionData, relationshipColor); + highlightEntities.put(path[i], entityColor); + } + highlightEntities.put(path[path.length-1], entityColor); + } +// highlightEntities.put(root, DEFAULT_ENTITY_HIGHLIGHT); + } + } + + public IvyNodeElement[] getShortestPathToDescendent(IvyNodeElement root, IvyNodeElement target) { + LinkedList/**/ q = new LinkedList/**/(); + Set/**/ orderedSet = new HashSet/**/(); + LinkedList/**/ orderedList = new LinkedList/**/(); + q.add(root); + while (!q.isEmpty()) { + IvyNodeElement head = (IvyNodeElement) q.remove(0); + if (!orderedSet.contains(head)) { + orderedSet.add(head); + orderedList.add(head); + q.addAll(Arrays.asList(head.getDependencies())); + } + } + IvyNodeElement[] path = fixedWeightDijkstraAlgorithm(orderedList, root, target); + return path; + } + + private IvyNodeElement[] fixedWeightDijkstraAlgorithm(LinkedList q, IvyNodeElement s, IvyNodeElement t) { + HashMap/**/ previous = new HashMap/**/(); + HashMap/**/ dValues = new HashMap/**/(); + for (Iterator/**/ iter = q.iterator(); iter.hasNext();) { + dValues.put(iter.next(), new Integer(Integer.MAX_VALUE / 10)); + } + dValues.put(s, new Integer(0)); + + while (!q.isEmpty()) { + IvyNodeElement head = (IvyNodeElement) q.remove(0); + IvyNodeElement[] outgoing = head.getDependencies(); + for (int i = 0; i < outgoing.length; i++) { + IvyNodeElement v = outgoing[i]; + if (((Integer) dValues.get(head)).intValue() + 1 < ((Integer) dValues.get(v)).intValue()) { + previous.put(v, head); + dValues.put(v, new Integer(((Integer) dValues.get(head)).intValue() + 1)); + } + } + } + LinkedList/**/ path = new LinkedList/**/(); + IvyNodeElement currentNode = t; + while (previous.containsKey(currentNode)) { + path.add(currentNode); + currentNode = (IvyNodeElement) previous.get(currentNode); + } + path.add(currentNode); + return (IvyNodeElement[]) path.toArray(new IvyNodeElement[path.size()]); + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/model/IIvyNodeElementFilter.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/model/IIvyNodeElementFilter.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/model/IIvyNodeElementFilter.java (revision 0) @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.model; + +public interface IIvyNodeElementFilter { + public IvyNodeElement[] filter(IvyNodeElement[] unfiltered); + public boolean isEnabled(); + public void setEnabled(boolean enabled); +} Index: src/java/org/apache/ivyde/resolvevisualizer/model/IvyNodeElement.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/model/IvyNodeElement.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/model/IvyNodeElement.java (revision 0) @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.model; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +import org.apache.ivy.core.module.id.ModuleRevisionId; + +/** + * Assists in the further separation of concerns between the + * view and the Ivy resolve report. The view looks at the IvyNode + * in a unique way that can lead to expensive operations if we do + * not achieve this separation. + */ +public class IvyNodeElement { + private ModuleRevisionId moduleRevisionId; + private boolean evicted = false; + private int depth = Integer.MAX_VALUE / 10; + private Collection/**/ dependencies = new HashSet/**/(); + private Collection/**/ callers = new HashSet/**/(); + private Collection/**/ conflicts = new HashSet/**/(); + + /** + * The caller configurations that caused this node to be reached in the resolution, grouped by caller. + */ + private Map/**/ callerConfigurationMap = new HashMap/**/(); + + /** + * We try to avoid building the list of this nodes deep dependencies by storing them in this cache by depth level. + */ + private IvyNodeElement[] deepDependencyCache; + + public boolean equals(Object obj) { + if(obj instanceof IvyNodeElement) { + IvyNodeElement elem = (IvyNodeElement) obj; + if(elem.getOrganization().equals(getOrganization()) && elem.getName().equals(getName()) && elem.getRevision().equals(getRevision())) + return true; + } + return false; + } + + public IvyNodeElement[] getDependencies() { + return (IvyNodeElement[]) dependencies.toArray(new IvyNodeElement[dependencies.size()]); + } + + /** + * Recursive dependency retrieval + * @return The array of nodes that represents a node's immediate and transitive dependencies down to + * an arbitrary depth. + */ + public IvyNodeElement[] getDeepDependencies() { + if(deepDependencyCache == null) { + deepDependencyCache = (IvyNodeElement[]) getDeepDependencies(this).toArray(new IvyNodeElement[] {}); + } + return deepDependencyCache; + } + + /** + * Recursive dependency retrieval + * @param node + * @return + */ + private Collection/**/ getDeepDependencies(IvyNodeElement node) { + Collection/**/ deepDependencies = new HashSet/**/(); + deepDependencies.add(node); + + IvyNodeElement[] directDependencies = node.getDependencies(); + for(int i = 0; i < directDependencies.length; i++) { + deepDependencies.addAll(getDeepDependencies(directDependencies[i])); + } + + return deepDependencies; + } + + /** + * @return An array of configurations by which this module was resolved + */ + public String[] getCallerConfigurations(IvyNodeElement caller) { + return (String[]) callerConfigurationMap.get(caller); + } + + public void setCallerConfigurations(IvyNodeElement caller, String[] configurations) { + callerConfigurationMap.put(caller, configurations); + } + + public String getOrganization() { + return moduleRevisionId.getOrganisation(); + } + + public String getName() { + return moduleRevisionId.getName(); + } + + public String getRevision() { + return moduleRevisionId.getRevision(); + } + + public boolean isEvicted() { + return evicted; + } + + public void setEvicted(boolean evicted) { + this.evicted = evicted; + } + + public int getDepth() { + return depth; + } + + /** + * Set this node's depth and recursively update the node's children to relative to the new + * value. + * + * @param depth + */ + public void setDepth(int depth) { + this.depth = depth; + for(Iterator iter = dependencies.iterator(); iter.hasNext();) { + IvyNodeElement dependency = (IvyNodeElement) iter.next(); + dependency.setDepth(depth + 1); + } + } + + public IvyNodeElement[] getConflicts() { + return (IvyNodeElement[]) conflicts.toArray(new IvyNodeElement[conflicts.size()]); + } + + public void setConflicts(Collection conflicts) { + this.conflicts = conflicts; + } + + public ModuleRevisionId getModuleRevisionId() { + return moduleRevisionId; + } + + public void setModuleRevisionId(ModuleRevisionId moduleRevisionId) { + this.moduleRevisionId = moduleRevisionId; + } + + public void addCaller(IvyNodeElement caller) { + callers.add(caller); + caller.dependencies.add(this); + } + + public IvyNodeElement[] getCallers() { + return (IvyNodeElement[]) callers.toArray(new IvyNodeElement[callers.size()]); + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/model/IvyNodeElementAdapter.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/model/IvyNodeElementAdapter.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/model/IvyNodeElementAdapter.java (revision 0) @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.ivy.core.module.id.ModuleId; +import org.apache.ivy.core.report.ResolveReport; +import org.apache.ivy.core.resolve.IvyNode; +import org.apache.ivy.core.resolve.IvyNodeCallers.Caller; + +public class IvyNodeElementAdapter { + /** + * Adapt all dependencies and evictions from the ResolveReport. + * @param report + * @return the root node adapted from the ResolveReport + */ + public static IvyNodeElement adapt(ResolveReport report) { + Map/**/ resolvedNodes = new HashMap/**/(); + + IvyNodeElement root = new IvyNodeElement(); + root.setModuleRevisionId(report.getModuleDescriptor().getModuleRevisionId()); + resolvedNodes.put(report.getModuleDescriptor().getModuleRevisionId(), root); + + List/**/ dependencies = report.getDependencies(); + + // first pass - build the map of resolved nodes by revision id + for(Iterator iter = dependencies.iterator(); iter.hasNext();) { + IvyNode node = (IvyNode) iter.next(); + IvyNodeElement nodeElement = new IvyNodeElement(); + nodeElement.setModuleRevisionId(node.getResolvedId()); + resolvedNodes.put(node.getResolvedId(), nodeElement); + } + + // second pass - establish relationships between the resolved nodes + for(Iterator iter = dependencies.iterator(); iter.hasNext();) { + IvyNode node = (IvyNode) iter.next(); + IvyNodeElement nodeElement = (IvyNodeElement) resolvedNodes.get(node.getResolvedId()); + Caller[] callers = node.getAllRealCallers(); + for(int i = 0; i < callers.length; i++) { + IvyNodeElement caller = (IvyNodeElement) resolvedNodes.get(callers[i].getModuleRevisionId()); + nodeElement.addCaller(caller); + nodeElement.setCallerConfigurations(caller, callers[i].getCallerConfigurations()); + } + } + + IvyNode[] evictions = report.getEvictedNodes(); + for(int i = 0; i < evictions.length; i++) { + IvyNode eviction = evictions[i]; + IvyNodeElement evictionElement = new IvyNodeElement(); + evictionElement.setModuleRevisionId(eviction.getResolvedId()); + evictionElement.setEvicted(true); + + Caller[] callers = eviction.getAllRealCallers(); + for(int j = 0; j < callers.length; j++) { + evictionElement.addCaller((IvyNodeElement) resolvedNodes.get(callers[j].getModuleRevisionId())); + } + } + + // recursively set depth starting at root + root.setDepth(0); + findConflictsBeneathNode(root); + + return root; + } + + /** + * Derives configuration conflicts that exist between node and all of its descendant dependencies. + * @param node + */ + private static void findConflictsBeneathNode(IvyNodeElement node) { + // derive conflicts + Map/*>*/ moduleRevisionMap = new HashMap/*>*/(); + IvyNodeElement[] deepDependencies = node.getDeepDependencies(); + for(int i = 0; i < deepDependencies.length; i++) { + if(deepDependencies[i].isEvicted()) + continue; + + ModuleId moduleId = deepDependencies[i].getModuleRevisionId().getModuleId(); + if(moduleRevisionMap.containsKey(moduleId)) { + Collection/**/ conflicts = (Collection/**/) moduleRevisionMap.get(moduleId); + conflicts.add(deepDependencies[i]); + for(Iterator iter = conflicts.iterator(); iter.hasNext();) { + IvyNodeElement conflict = (IvyNodeElement) iter.next(); + conflict.setConflicts(conflicts); + } + } + else { + List/**/ immutableMatchingSet = Arrays.asList(new IvyNodeElement[] {deepDependencies[i]}); + moduleRevisionMap.put(moduleId, new HashSet(immutableMatchingSet)); + } + } + } +} Index: src/java/org/apache/ivyde/resolvevisualizer/model/IvyNodeElementFilterAdapter.java =================================================================== --- src/java/org/apache/ivyde/resolvevisualizer/model/IvyNodeElementFilterAdapter.java (revision 0) +++ src/java/org/apache/ivyde/resolvevisualizer/model/IvyNodeElementFilterAdapter.java (revision 0) @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ivyde.resolvevisualizer.model; + +import java.util.Collection; +import java.util.HashSet; + +public abstract class IvyNodeElementFilterAdapter implements IIvyNodeElementFilter { + protected boolean enabled = false; + + public IvyNodeElement[] filter(IvyNodeElement[] unfiltered) { + if(!enabled) + return unfiltered; + + Collection/**/ filtered = new HashSet/**/(); + for(int i = 0; i < unfiltered.length; i++) { + if(accept(unfiltered[i])) + filtered.add(unfiltered[i]); + } + + return (IvyNodeElement[]) filtered.toArray(new IvyNodeElement[filtered.size()]); + } + + public abstract boolean accept(IvyNodeElement unfiltered); + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } +}