/******************************************************************************* * Copyright (c) 2016 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is 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: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.windup.ui.internal.explorer; import static org.jboss.tools.windup.model.domain.WindupConstants.GROUPS_CHANGED; import java.io.File; import java.util.List; import java.util.stream.Collectors; import javax.inject.Inject; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; 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.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.e4.core.services.events.IEventBroker; import org.eclipse.e4.ui.di.UIEventTopic; import org.eclipse.e4.ui.model.application.MApplication; import org.eclipse.e4.ui.model.application.ui.basic.MPart; import org.eclipse.e4.ui.workbench.modeling.EPartService; import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState; import org.eclipse.jdt.core.IOpenable; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.preference.JFacePreferences; import org.eclipse.jface.preference.PreferenceDialog; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.PreferencesUtil; import org.eclipse.ui.forms.widgets.Form; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.internal.misc.StringMatcher; import org.eclipse.ui.navigator.CommonNavigator; import org.eclipse.ui.navigator.CommonViewer; import org.eclipse.ui.progress.UIJob; import org.jboss.tools.windup.model.domain.ModelService; import org.jboss.tools.windup.model.domain.WindupConstants; import org.jboss.tools.windup.runtime.WindupRmiClient; import org.jboss.tools.windup.ui.WindupUIPlugin; import org.jboss.tools.windup.ui.internal.Messages; import org.jboss.tools.windup.ui.internal.explorer.IssueExplorerContentProvider.ReportNode; import org.jboss.tools.windup.ui.internal.explorer.IssueExplorerContentProvider.RootReportNode; import org.jboss.tools.windup.ui.internal.explorer.IssueExplorerContentProvider.RuleGroupNode; import org.jboss.tools.windup.ui.internal.explorer.IssueExplorerContentProvider.SeverityNode; import org.jboss.tools.windup.ui.internal.explorer.IssueExplorerContentProvider.TreeNode; import org.jboss.tools.windup.ui.internal.intro.ShowGettingStartedAction; import org.jboss.tools.windup.ui.internal.services.IssueGroupService; import org.jboss.tools.windup.ui.internal.services.MarkerService; import org.jboss.tools.windup.ui.internal.views.WindupReportView; import org.jboss.tools.windup.ui.util.WindupLauncher; import org.jboss.tools.windup.ui.util.WindupServerCallbackAdapter; import org.jboss.tools.windup.windup.Issue; import org.jboss.windup.tooling.ExecutionBuilder; import org.osgi.service.event.Event; import org.osgi.service.event.EventHandler; import com.google.common.base.Objects; import com.google.common.collect.Lists; /** * Explorer view for displaying and navigating Windup issues, classifications, etc. */ @SuppressWarnings("restriction") public class IssueExplorer extends CommonNavigator { public static final String VIEW_ID = "org.jboss.tools.windup.ui.explorer"; //$NON-NLS-1$ @Inject private IEclipseContext context; @Inject private IEventBroker broker; @Inject private ModelService modelService; @Inject private IssueGroupService groupService; @Inject private EPartService partService; private IssueExplorerContentService contentService; @Inject private WindupLauncher windupLauncher; @Inject private WindupRmiClient windupClient; @Inject private MarkerService markerService; private Text searchText; @Inject public IssueExplorer(IssueExplorerContentService contentService) { this.contentService = contentService; contentService.setIssuExplorer(this); } @Override protected CommonViewer createCommonViewerObject(Composite aParent) { // See: https://issues.jboss.org/browse/WINDUP-1290 // Newly imported projects automatically get added to the tree via PackageExplorer#postAdd // The primary issue with calling super.refresh is it will cause IssueExplorer's tree nodes // to collapse. CommonViewer viewer = new CommonViewer(getViewSite().getId(), aParent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL) { @Override public void add(Object parentElement, Object[] childElements) { super.add(parentElement, childElements); super.refresh(parentElement); } }; viewer.setUseHashlookup(false); return viewer; } @SuppressWarnings("unchecked") public TreeNode computeNode(IResource resource) { TreeNode node = contentService.findResourceNode(resource); ISelection selection = getCommonViewer().getSelection(); if (!selection.isEmpty() && selection instanceof IStructuredSelection) { List<Object> elements = ((IStructuredSelection)selection).toList(); for (Object element : elements) { if (element instanceof TreeNode) { TreeNode currentNode = (TreeNode)element; while (currentNode != null) { if (currentNode.getSegment() != null && Objects.equal(currentNode.getSegment(), resource)) { return null; } currentNode = currentNode.getParent(); } } } } return node; } private IssueExplorerService explorerSerivce = new IssueExplorerService() { @Override public void expandAll() { getCommonViewer().expandAll(); } public void refresh() { getCommonViewer().refresh(); } }; public void showIssue(IMarker marker) { MarkerNode node = contentService.findMarkerNode(marker); if (node != null) { List<Object> segments = Lists.newArrayList(); TreeNode parent = node.getParent(); while (parent != null) { if (parent.getSegment() != null) { segments.add(0, parent); } parent = parent.getParent(); } segments.add(node); TreePath path = new TreePath(segments.toArray(new TreeNode[segments.size()])); getCommonViewer().setSelection(new StructuredSelection(path), true); } } @Override public void createPartControl(Composite aParent) { createServerArea(aParent); FormToolkit toolkit = new FormToolkit(aParent.getDisplay()); createSearchArea(toolkit, aParent); super.createPartControl(aParent); GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).indent(0, 5).applyTo(getCommonViewer().getControl()); getCommonViewer().addFilter(getRuleFilter()); getCommonViewer().addDoubleClickListener(new OpenIssueListener()); getCommonViewer().addDoubleClickListener(new OpenReportListener()); getCommonViewer().addSelectionChangedListener((e) -> { StructuredSelection ss = (StructuredSelection)e.getSelection(); if (ss.size() == 1) { Object selection = ss.getFirstElement(); IMarker type = null; if (selection instanceof MarkerNode) { type = ((MarkerNode)selection).getMarker(); } context.set(IMarker.class, type); if (selection instanceof ReportNode) { ReportNode reportNode = (ReportNode)selection; IMarker marker = reportNode.getMarker(); Issue issue = markerService.find(marker); if (issue != null) { String reportLocation = issue.getGeneratedReportLocation(); if (reportLocation != null) { updateReportView(reportLocation, false, partService); } } } } }); getCommonViewer().setComparator(new IssueExplorerComparator()); getServiceContext().set(IssueExplorerService.class, explorerSerivce); broker.subscribe(GROUPS_CHANGED, groupsChangedHandler); initGettingStarted(); buildTree(); } private Label serverImage; private Label textLabel; private Label statusLabel; private CButton startStopButton; @Inject @Optional private void updateServer(@UIEventTopic(WindupRmiClient.WINDUP_SERVER_STATUS) ExecutionBuilder executionBuilder) { if (textLabel != null && !textLabel.isDisposed()) { updateServerGroup(); } } private void createServerArea(final Composite parent) { GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo(parent); GridDataFactory.fillDefaults().grab(true, true).applyTo(parent); Composite top = new Composite(parent, SWT.NONE); GridLayoutFactory.fillDefaults().spacing(0, 0).numColumns(2).applyTo(top); GridDataFactory.fillDefaults().grab(true, false).applyTo(top); Composite container = new Composite(top, SWT.NONE); GridLayoutFactory.fillDefaults().numColumns(4).margins(0, 5).spacing(0, 0).applyTo(container); GridDataFactory.fillDefaults().indent(0, 0).grab(true, false).applyTo(container); serverImage = new Label(container, SWT.NONE); serverImage.setImage(WindupUIPlugin.getDefault().getImageRegistry().get(WindupUIPlugin.IMG_SERVER)); textLabel = new Label(container, SWT.NONE); textLabel.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); textLabel.setText(Messages.WindupServerLabel); //$NON-NLS-1$ statusLabel = new Label(container, SWT.NONE); statusLabel.setForeground(JFaceResources.getColorRegistry().get(JFacePreferences.DECORATIONS_COLOR)); Composite buttonBar = new Composite(top, SWT.NONE); GridLayoutFactory.fillDefaults().numColumns(3).margins(0, 5).spacing(12, 0).applyTo(buttonBar); GridDataFactory.fillDefaults().indent(0, 0).grab(false, false).applyTo(buttonBar); startStopButton = new CButton(buttonBar, SWT.NONE); startStopButton.setHotImage(WindupUIPlugin.getDefault().getImageRegistry().get(WindupUIPlugin.IMG_START)); updateServerGroup(); final Shell shell = parent.getShell(); startStopButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (!windupClient.isWindupServerRunning()) { windupLauncher.shutdown(new WindupServerCallbackAdapter(shell) { @Override public void serverShutdown(IStatus status) { Boolean shutdown; if (status.isOK()) { shutdown = Boolean.TRUE; } else { shutdown = Boolean.FALSE; MessageDialog.openError(parent.getShell(), Messages.WindupShuttingDownError, status.getMessage()); } if (shutdown) { windupLauncher.start(new WindupServerCallbackAdapter(shell) { @Override public void windupNotExecutable() { MessageDialog.openError(parent.getShell(), Messages.WindupNotExecutableTitle, Messages.WindupNotExecutableInfo); } @Override public void serverStart(IStatus status) { if (status.getSeverity() == IStatus.ERROR) { MessageDialog.openError(parent.getShell(), Messages.WindupStartingError, status.getMessage()); } updateServerGroup(); } }); } } }); } else { windupLauncher.shutdown(new WindupServerCallbackAdapter(shell) { @Override public void serverShutdown(IStatus status) { Display.getDefault().asyncExec(() -> { if (status.getSeverity() == Status.ERROR || !status.isOK()) { MessageDialog.openError(parent.getShell(), Messages.WindupShuttingDownError, status.getMessage()); } updateServerGroup(); }); } }); } } }); CButton preferenceButton = new CButton(buttonBar, SWT.NONE); preferenceButton.setHotImage(WindupUIPlugin.getDefault().getImageRegistry().get(WindupUIPlugin.IMG_CONFIG_HOT)); preferenceButton.setColdImage(WindupUIPlugin.getDefault().getImageRegistry().get(WindupUIPlugin.IMG_CONFIG_COLD)); preferenceButton.setToolTipText("Configure Windup"); //$NON-NLS-1$ preferenceButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { String id = "org.jboss.tools.windup.ui.preferences.WindupPreferencePage"; //$NON-NLS-1$ PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(parent.getShell(), id, new String[]{id}, null); dialog.open(); } }); Composite separatorContainer = new Composite(parent, SWT.NONE); GridLayoutFactory.fillDefaults().applyTo(separatorContainer); GridDataFactory.fillDefaults().indent(0, 3).grab(true, false).applyTo(separatorContainer); } private void updateServerGroup() { if (windupClient.isWindupServerRunning()) { //serverImage //statusImage.setImage(WindupUIPlugin.getDefault().getImageRegistry().get(WindupUIPlugin.IMG_SERVER_RUNNING_STATUS)); statusLabel.setText("[Running - " + windupClient.getWindupVersion() + "]"); //$NON-NLS-1$ startStopButton.setHotImage(WindupUIPlugin.getDefault().getImageRegistry().get(WindupUIPlugin.IMG_STOP)); startStopButton.setToolTipText("Stop Windup Server"); //$NON-NLS-1$ } else { //serverImage //statusImage.setImage(WindupUIPlugin.getDefault().getImageRegistry().get(WindupUIPlugin.IMG_SERVER_NOT_RUNNING_STATUS)); statusLabel.setText("[Not Running]"); //$NON-NLS-1$ startStopButton.setHotImage(WindupUIPlugin.getDefault().getImageRegistry().get(WindupUIPlugin.IMG_START)); startStopButton.setToolTipText("Start Windup Server"); //$NON-NLS-1$ } startStopButton.redraw(); startStopButton.update(); statusLabel.getParent().layout(true); } private void initGettingStarted() { IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (WindupUIPlugin.getDefault().getPreferenceStore() .getBoolean(WindupConstants.SHOW_GETTING_STARTED)) { if (window.getWorkbench().getIntroManager().getIntro() != null) { return; } Job gettingStartedJob = new UIJob(WindupConstants.INIT_GETTING_STARTED) { public IStatus runInUIThread(IProgressMonitor monitor) { Display.getDefault().asyncExec(new Runnable() { public void run() { ShowGettingStartedAction action = new ShowGettingStartedAction(); action.init(window); action.run(null); return; } }); return Status.OK_STATUS; } }; gettingStartedJob.setSystem(true); gettingStartedJob.schedule(); } } private void createSearchArea(FormToolkit toolkit, Composite parent) { Form form = toolkit.createForm(parent); GridDataFactory.fillDefaults().grab(true, false).applyTo(form); Composite container = form.getBody(); GridLayoutFactory.fillDefaults().numColumns(1).margins(2, 0).applyTo(container); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); searchText = new Text(container, SWT.SEARCH | SWT.ICON_SEARCH); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, false).applyTo(searchText); searchText.addModifyListener(onSearch()); } public String getFilterText() { return searchText.getText().trim(); } private ModifyListener onSearch() { return e -> { getCommonViewer().refresh(); getCommonViewer().expandAll(); }; } // Creating local copy here b/c we cannot get a reference to the one. private IssueExplorerLabelProvider filterLabelProvider = new IssueExplorerLabelProvider(); private ViewerFilter getRuleFilter() { return new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { if (element instanceof TreeNode) { return containsFilterNode((TreeNode)element, parentElement); } return false; } }; } private boolean doesParentMatchFilter(TreeNode treeNode) { if (isFilterMatch(treeNode)) { return true; } if (treeNode.getParent() != null) { TreeNode parentNode = treeNode.getParent(); if ((parentNode instanceof RuleGroupNode || parentNode.getSegment() instanceof IResource || parentNode instanceof SeverityNode || parentNode.getSegment() instanceof IOpenable || parentNode.getSegment() instanceof IContainer) && !(parentNode.getSegment() instanceof IProject)) { return doesParentMatchFilter(treeNode.getParent()); } } return false; } private boolean containsFilterNode(TreeNode treeNode, Object parentElement) { if (isFilterMatch(treeNode)) { return true; } if (treeNode instanceof MarkerNode) { if (doesParentMatchFilter(treeNode)) { return true; } } if (treeNode instanceof ReportNode || treeNode instanceof RootReportNode) { TreeNode parentNode = treeNode.getParent(); if (parentNode.getSegment() instanceof IResource) { IResource resource = (IResource)parentNode.getSegment(); TreeNode resourceNode = contentService.findResourceNode(resource); List<TreeNode> children = resourceNode.getChildren().stream().filter(node -> { return !Objects.equal(node, treeNode); }).collect(Collectors.toList()); for (TreeNode childNode : children) { if (containsFilterNode(childNode, parentElement)) { return true; } } } return false; } for (TreeNode child : treeNode.getChildren()) { if (containsFilterNode(child, parentElement)) { return true; } } return false; } public static StringMatcher getFilterMatcher(String text) { String pattern = text + "*"; // Include leading wild cards. if (!(text.charAt(0) == '*')) { pattern = "*" + pattern; } return new StringMatcher(pattern, true, false); } private boolean isFilterMatch(TreeNode node) { String text = searchText.getText(); if (text.trim().isEmpty()) { return true; } String nodeText = ((StyledString)filterLabelProvider.getStyledText(node)).getString(); return getFilterMatcher(text).match(nodeText); } public void clear() { getCommonViewer().getTree().removeAll(); getCommonViewer().setSelection(StructuredSelection.EMPTY); boolean visible = getCommonViewer().getTree().getItemCount() > 0; searchText.setVisible(visible); } public void buildTree() { refresh(); boolean visible = getCommonViewer().getTree().getItemCount() > 0; searchText.setVisible(visible); } public void delete(Issue issue) { IMarker marker = (IMarker)issue.getMarker(); MarkerNode markerNode = contentService.findMarkerNode(marker); if (markerNode != null) { getCommonViewer().remove(markerNode); TreeNode parent = markerNode.getParent(); Object segment = markerNode.getSegment(); while (parent != null) { TreeNode childNode = parent.getChildPath(segment); getCommonViewer().remove(childNode); parent.removeChild(segment); getCommonViewer().refresh(parent, true); if (isEmptyParent(parent)) { segment = parent.getSegment(); parent = parent.getParent(); } else if (parent.getChildren().isEmpty()) { segment = parent.getSegment(); parent = parent.getParent(); } else { break; } } } boolean visible = getCommonViewer().getTree().getItemCount() > 0; searchText.setVisible(visible); } private boolean isEmptyParent(TreeNode node) { return (!node.getChildren().isEmpty() && node.getChildren().size() == 1 && (node.getChildren().get(0) instanceof ReportNode || node.getChildren().get(0) instanceof RootReportNode)); } public void update(Issue issue, IMarker oldMarker) { IMarker newMarker = (IMarker)issue.getMarker(); MarkerNode markerNode = contentService.findMarkerNode(oldMarker); markerNode.setMarker(newMarker); contentService.updateNodeMapping(oldMarker, newMarker); getCommonViewer().refresh(markerNode, true); } private EventHandler groupsChangedHandler = new EventHandler() { @Override public void handleEvent(Event event) { refresh(); // TODO: restore previously expanded nodes, selection, scrolls, etc. getCommonViewer().collapseAll(); } }; private void refresh() { if (getCommonViewer() != null && !getCommonViewer().getTree().isDisposed()) { getCommonViewer().refresh(true); } // TODO: after memento is integrated, re-set to current selection. context.set(IMarker.class, null); } private class OpenIssueListener implements IDoubleClickListener { @Override public void doubleClick(DoubleClickEvent event) { StructuredSelection ss = (StructuredSelection)event.getSelection(); if (ss.size() == 1) { Object node = ss.getFirstElement(); if (node instanceof MarkerNode) { MarkerNode issue = (MarkerNode)node; try { IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), issue.getMarker(), true); } catch (PartInitException e) { WindupUIPlugin.log(e); } } } } } private class OpenReportListener implements IDoubleClickListener { @Override public void doubleClick(DoubleClickEvent event) { StructuredSelection ss = (StructuredSelection)event.getSelection(); if (ss.size() == 1) { Object element = ss.getFirstElement(); if (element instanceof ReportNode) { ReportNode node = (ReportNode)element; IMarker marker = node.getMarker(); Issue issue = markerService.find(marker); String reportLocation = issue.getGeneratedReportLocation(); if (reportLocation != null) { updateReportView(reportLocation, true, partService); } } else if (element instanceof RootReportNode) { String reportLocation = ((RootReportNode)element).getReportLocation(); updateReportView(reportLocation, true, partService); } } } } public static void updateReportView(String reportLocation, boolean open, EPartService partService) { File file = new File(reportLocation); MPart part = partService.findPart(WindupReportView.ID); WindupReportView view = (WindupReportView)part.getObject(); if (file.exists()) { if (open) { view = (WindupReportView)partService.showPart(part, PartState.ACTIVATE).getObject(); } if (view != null) { view.showReport(reportLocation, true); } } } private IEclipseContext getServiceContext() { return context.get(MApplication.class).getContext(); } public static interface IssueExplorerService { void expandAll(); void refresh(); } @Override public void dispose() { super.dispose(); broker.unsubscribe(groupsChangedHandler); modelService.save(); groupService.save(); } }