/*******************************************************************************
* 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.WindupMarker.SEVERITY;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.apache.commons.collections.BidiMap;
import org.apache.commons.collections.bidimap.DualHashBidiMap;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.jdt.internal.ui.navigator.IExtensionStateConstants;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.internal.navigator.NavigatorContentServiceContentProvider;
import org.eclipse.ui.navigator.ICommonContentExtensionSite;
import org.eclipse.ui.navigator.ICommonContentProvider;
import org.eclipse.ui.navigator.IExtensionStateModel;
import org.eclipse.ui.navigator.INavigatorContentService;
import org.jboss.tools.windup.model.domain.ModelService;
import org.jboss.tools.windup.model.domain.WindupConstants;
import org.jboss.tools.windup.model.domain.WindupMarker;
import org.jboss.tools.windup.ui.internal.Messages;
import org.jboss.tools.windup.ui.internal.explorer.IssueConstants.Severity;
import org.jboss.tools.windup.ui.internal.services.IssueGroupService;
import org.jboss.tools.windup.ui.internal.services.MarkerService;
import org.jboss.tools.windup.windup.ConfigurationElement;
import org.jboss.tools.windup.windup.Hint;
import org.jboss.tools.windup.windup.Input;
import org.jboss.tools.windup.windup.Issue;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* Content provider for the Windup explorer.
*/
@SuppressWarnings("restriction")
public class IssueExplorerContentProvider implements ICommonContentProvider {
@Inject private IssueExplorerContentService contentService;
@Override
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
@Override
public Object[] getChildren(Object parent) {
return contentService.getChildren(parent);
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
@Override
public Object getParent(Object element) {
return contentService.getParent(element);
}
@Override
public boolean hasChildren(Object element) {
return contentService.hasChildren(element);
}
@Override
public void restoreState(IMemento aMemento) {}
@Override
public void saveState(IMemento aMemento) {}
@Override
public void init(ICommonContentExtensionSite aConfig) {}
public static class TreeNodeBuilder {
private List<IMarker> markers;
private NavigatorContentServiceContentProvider contentProvider;
private IssueGroupService groupService;
private IEclipseContext context;
private ConfigurationElement configuration;
private MarkerService markerService;
private ModelService modelService;
private BidiMap nodeMap = new DualHashBidiMap();
// Used for explorer linking
private Map<IResource, TreeNode> resourceMap = Maps.newHashMap();
public TreeNodeBuilder(List<IMarker> markers, IssueExplorer explorer,
IssueGroupService groupService, IEclipseContext context,
MarkerService markerService, ConfigurationElement configuration,
ModelService modelService) {
this.markers = markers;
this.groupService = groupService;
this.contentProvider = (NavigatorContentServiceContentProvider)explorer.getCommonViewer().getContentProvider();
contentProvider.getParents(ResourcesPlugin.getWorkspace().getRoot());
this.configuration = configuration;
this.markerService = markerService;
this.modelService = modelService;
// TODO: Correct this temporary hack to get flat package layout.
INavigatorContentService contentService = explorer.getCommonViewer().getNavigatorContentService();
IExtensionStateModel m = contentService.findStateModel(IssueConstants.JDT_CONTENT);
m.setBooleanProperty(IExtensionStateConstants.Values.IS_LAYOUT_FLAT, true);
this.context = context;
}
public Map<IResource, TreeNode> getResourceMap() {
return resourceMap;
}
public TreeNode[] build() {
if (configuration == null) {
return new TreeNode[0];
}
RootTreeNode root = new RootTreeNode();
for (IMarker marker : markers) {
List<TreePath> paths = Lists.newArrayList(contentProvider.getParents(marker.getResource()));
if (!paths.isEmpty()) {
TreePath path = paths.get(0);
build(root, root, path, marker, 1);
}
}
List<TreeNode> children = root.getChildren();
if (children.isEmpty()) {
return new TreeNode[0];
}
return children.stream().toArray(TreeNode[]::new);
}
public BidiMap getNodeMap() {
return nodeMap;
}
public void build(TreeNode root, TreeNode node, TreePath path, IMarker marker, int index) {
if (groupService.isGroupByHierarchy()) {
// build the hierarchy.
if (index < path.getSegmentCount()) {
Object segment = path.getSegment(index);
TreeNode child = node.getChildPath(segment);
if (child == null) {
child = new TreeNode(segment);
node.addChild(child);
if (segment instanceof IProject) {
resourceMap.put((IProject)segment, child);
if (configuration.isGenerateReport() && !configuration.getInputs().isEmpty()) {
Input input = configuration.getInputs().get(0);
IPath reportPath = modelService.getGeneratedReport(configuration, input);
File report = new File(reportPath.toString());
if (report.exists()) {
TreeNode reportNode = child.getChildPath(Messages.generatedReport);
if (reportNode == null) {
reportNode = new RootReportNode(Messages.generatedReport,
reportPath.toString());
child.addChild(reportNode);
}
}
}
}
}
build(root, child, path, marker, ++index);
return;
}
}
TreeNode parent = groupService.isGroupByHierarchy() ? node : root;
if (groupService.isGroupByFile()) {
TreeNode resourceNode = parent.getChildPath(marker.getResource());
if (resourceNode == null) {
resourceNode = new TreeNode(marker.getResource());
parent.addChild(resourceNode);
resourceMap.put(marker.getResource(), resourceNode);
}
parent = resourceNode;
if (configuration.isGenerateReport()) {
Issue issue = markerService.find(marker);
if (issue != null) {
if (issue.getGeneratedReportLocation() != null) {
File report = new File(issue.getGeneratedReportLocation());
if (report.exists()) {
TreeNode reportNode = parent.getChildPath(Messages.generatedReport);
if (reportNode == null) {
reportNode = new ReportNode(Messages.generatedReport, marker);
parent.addChild(reportNode);
}
}
}
}
}
}
if (groupService.isGroupBySeverity()) {
Object segment = marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
String severity = marker.getAttribute(SEVERITY, Severity.POTENTIAL.toString());
TreeNode severityNode = parent.getChildPath(segment);
if (severityNode == null) {
severityNode = new SeverityNode(segment, severity);
parent.addChild(severityNode);
}
parent = severityNode;
}
if (groupService.isGroupByRule()) {
String ruleId = marker.getAttribute(WindupMarker.RULE_ID, WindupConstants.DEFAULT_RULE_ID);
String title = "";
Issue issue = markerService.find(marker);
if (issue instanceof Hint) {
title = ((Hint)issue).getTitle();
}
String segment = ruleId+title;
TreeNode ruleNode = parent.getChildPath(segment);
if (ruleNode == null) {
ruleNode = new RuleGroupNode(segment, ruleId, title);
parent.addChild(ruleNode);
}
parent = ruleNode;
}
Issue issue = markerService.find(marker);
if (issue != null) {
IEclipseContext child = context.createChild();
child.set(IMarker.class, marker);
child.set(Issue.class, issue);
MarkerNode markerNode = ContextInjectionFactory.make(MarkerNode.class, child);
parent.addChild(markerNode);
nodeMap.put(marker, markerNode);
}
}
}
public static class TreeNode {
private Object segment;
private TreeNode parent;
private Map<Object, TreeNode> children = Maps.newHashMap();
public TreeNode(Object segment) {
this.segment = segment;
}
public List<TreeNode> getChildren() {
return Lists.newArrayList(children.values());
}
public void removeChild(Object key) {
children.remove(key);
}
public TreeNode getParent() {
return parent;
}
public Object getSegment() {
return segment;
}
public TreeNode getChildPath(Object path) {
return children.get(path);
}
public void addChild(TreeNode node) {
node.parent = this;
children.put(node.segment, node);
}
public boolean isLeafParent() {
if (children.isEmpty()) {
return false;
}
for (Iterator<Map.Entry<Object, TreeNode>> iter = children.entrySet().iterator(); iter.hasNext();) {
TreeNode child = iter.next().getValue();
if (!(child instanceof RootReportNode) && !(child instanceof ReportNode)) {
if (child.getChildren().isEmpty()) {
return true;
}
}
}
return false;
}
}
public static class SeverityNode extends TreeNode {
private String severity;
public SeverityNode(Object segment, String severity) {
super(segment);
this.severity = severity;
}
public String getSeverity() {
return severity;
}
}
public static class RootTreeNode extends TreeNode {
public RootTreeNode() {
super(null);
}
}
public static class RuleGroupNode extends TreeNode {
private String ruleId;
private String title;
public RuleGroupNode(Object segment, String ruleId, String title) {
super(segment);
this.ruleId = ruleId;
this.title = title;
}
public String getRuleId() {
return ruleId;
}
public String getTitle() {
return title;
}
}
public static class TreeGroupNode extends TreeNode {
public TreeGroupNode(Object segment) {
super(segment);
}
}
public static class ReportNode extends TreeNode {
private IMarker marker;
public ReportNode (Object segment, IMarker marker) {
super(segment);
this.marker = marker;
}
public IMarker getMarker() {
return marker;
}
}
public static class RootReportNode extends TreeNode {
private String reportLocation;
public RootReportNode(Object segment, String reportLocation) {
super(segment);
this.reportLocation = reportLocation;
}
public String getReportLocation() {
return reportLocation;
}
}
}