/*******************************************************************************
* 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.launch;
import static org.jboss.tools.windup.model.domain.ConfigurationResourceUtil.computePackages;
import static org.jboss.tools.windup.model.domain.ConfigurationResourceUtil.getCurrentPackages;
import static org.jboss.tools.windup.model.domain.ConfigurationResourceUtil.getCurrentProjects;
import static org.jboss.tools.windup.ui.internal.Messages.inputProjectsDescription;
import static org.jboss.tools.windup.ui.internal.Messages.inputTabName;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.internal.ui.SWTFactory;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.jboss.tools.windup.model.domain.ModelService;
import org.jboss.tools.windup.model.util.MavenUtil;
import org.jboss.tools.windup.model.util.MavenUtil.ProjectInfo;
import org.jboss.tools.windup.ui.FilteredListDialog;
import org.jboss.tools.windup.ui.internal.Messages;
import org.jboss.tools.windup.windup.ConfigurationElement;
import org.jboss.tools.windup.windup.MigrationPath;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* The tab where the user specifies the input to analyze by Windup.
*/
@SuppressWarnings("restriction")
public class WindupInputTab extends AbstractLaunchConfigurationTab {
private static final String ID = "org.jboss.tools.windup.ui.launch.WindupInputTab"; //$NON-NLS-1$
private ModelService modelService;
private ConfigurationElement configuration;
private TreeViewer projectsTree;
private TableViewer packagesTable;
private Combo migrationPathCombo;
public WindupInputTab(ModelService modelService) {
this.modelService = modelService;
}
@Override
public void createControl(Composite parent) {
Composite container = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().margins(5, 5).applyTo(container);
GridDataFactory.fillDefaults().grab(true, true).minSize(150, 250).applyTo(container);
createMigrationPathGroup(container);
createProjectsGroup(container);
createPackagesGroup(container);
createVerticalSpacer(container, 1);
super.setControl(container);
PlatformUI.getWorkbench().getHelpSystem().setHelp(container, ID);
}
private void createProjectsGroup(Composite parent) {
Group group = SWTFactory.createGroup(parent, Messages.windupProjects+":", 2, 1, GridData.FILL_BOTH);
GridDataFactory.fillDefaults().grab(true, true).applyTo(group);
projectsTree = new TreeViewer(group, SWT.BORDER|SWT.MULTI);
GridDataFactory.fillDefaults().grab(true, true).applyTo(projectsTree.getTree());
projectsTree.setLabelProvider(new DelegatingStyledCellLabelProvider(new InputTreeLabelProvider()));
projectsTree.setContentProvider(new InputTreeContentProvider());
createProjectsButtonBar(group);
}
private void reload() {
reloadMigrationPath();
reloadProjectsTable();
reloadPackagesTable();
}
private void reloadProjectsTable() {
if (projectsTree != null) {
projectsTree.setInput(new InputTreeContentProvider.
ProjectInfoRoot(computeProjectsInfos()));
projectsTree.expandAll();
}
}
/**
* Computes all the ProjectInfos for the project's tree.
*/
private List<ProjectInfo> computeProjectsInfos() {
List<ProjectInfo> input = Lists.newArrayList();
// Add all implicitly analyzed projects
for (IProject project : getCurrentProjects(configuration)) {
input.add(MavenUtil.computeProjectInfo(project));
}
return input;
}
/**
* Filters out ProjectInfo's from 'projectInfosToRemove' list that share the project with another 'child' of the
* 'topProjectInfos' list.
*/
private Set<IProject> filterDuplicateProjects(List<ProjectInfo> projectInfosToRemove, List<ProjectInfo> topProjectInfos) {
// Remove 'projectInfosToRemove' from 'topProjectInfos'
for (Iterator<ProjectInfo> iter = topProjectInfos.iterator(); iter.hasNext();) {
ProjectInfo info = iter.next();
boolean found = projectInfosToRemove.stream().anyMatch(pInfo -> pInfo.getProject().equals(info.getProject()));
if (found) {
iter.remove();
}
}
// All top-level children
List<ProjectInfo> topChildren = Lists.newArrayList();
for (ProjectInfo topInfo : topProjectInfos) {
for (ProjectInfo child : flattenProjectInfos(topInfo)) {
if (child.projectExists()) {
topChildren.add(child);
}
}
}
// Remove ProjectInfo from 'projectInfosToRemove' if it shares a project with child of 'topProjectInfos'.
for (Iterator<ProjectInfo> iter = projectInfosToRemove.iterator(); iter.hasNext();) {
ProjectInfo info = iter.next();
boolean found = topChildren.stream().anyMatch(pInfo -> pInfo.getProject().equals(info.getProject()));
if (found) {
iter.remove();
}
}
// Now the other way around, remove ProjectInfos of children of 'projectInfosToRemove' if it shares an IProject with
// ProjectInfo from 'topProjectInfos'.
// 'projectInfosToRemove' children
List<ProjectInfo> toRemoveChildren = Lists.newArrayList();
for (ProjectInfo toRemoveInfo : projectInfosToRemove) {
for (ProjectInfo child : flattenProjectInfos(toRemoveInfo)) {
if (child.projectExists()) {
toRemoveChildren.add(child);
}
}
}
List<ProjectInfo> duplicates = Lists.newArrayList();
// Track child ProjectInfos that share a project with a top-level ProjectInfo.
for (ProjectInfo topProject : topProjectInfos) {
toRemoveChildren.stream().anyMatch(pInfo -> {
if (pInfo.getProject().equals(topProject.getProject())) {
duplicates.add(pInfo);
}
return false;
});
}
Set<IProject> result = Sets.newHashSet();
for (ProjectInfo info : projectInfosToRemove) {
boolean found = duplicates.stream().anyMatch(pInfo -> pInfo.getProject().equals(info.getProject()));
if (!found) {
result.add(info.getProject());
}
// Do the same for the children.
for (ProjectInfo grand : flattenProjectInfos(info)) {
found = duplicates.stream().anyMatch(pInfo -> pInfo.getProject().equals(grand.getProject()));
if (!found) {
result.add(grand.getProject());
}
}
}
return result;
}
private List<ProjectInfo> flattenProjectInfos(ProjectInfo info) {
List<ProjectInfo> infos = Lists.newArrayList();
for (ProjectInfo child : info.getProjects()) {
flattenProjectInfosHelper(child, infos);
}
return infos;
}
private void flattenProjectInfosHelper(ProjectInfo info, List<ProjectInfo> infos) {
infos.add(info);
for (ProjectInfo child : info.getProjects()) {
flattenProjectInfosHelper(child, infos);
}
}
private Set<IProject> collectProjects(List<ProjectInfo> projectInfos) {
Set<IProject> projects = Sets.newHashSet();
projectInfos.stream().forEach(info -> {
projects.add(info.getProject());
});
return projects;
}
private void filterNonParentProjectInfos(List<ProjectInfo> projects) {
for (Iterator<ProjectInfo> iter = projects.iterator(); iter.hasNext();) {
ProjectInfo info = iter.next();
if (!info.isParentProject()) {
iter.remove();
}
}
}
private void reloadPackagesTable() {
if (packagesTable != null) {
packagesTable.setInput(getCurrentPackages(configuration));
}
}
private void reloadMigrationPath() {
if (migrationPathCombo != null) {
int current = Lists.newArrayList(migrationPathCombo.getItems()).
indexOf(configuration.getMigrationPath().getName());
migrationPathCombo.select(current);
}
}
private void createProjectsButtonBar(Composite parent) {
Composite container = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().margins(0, 0).spacing(0, 0).applyTo(container);
GridDataFactory.fillDefaults().grab(false, true).applyTo(container);
Button addButton = new Button(container, SWT.PUSH);
addButton.setText(Messages.windupAdd);
GridDataFactory.fillDefaults().grab(true, false).applyTo(addButton);
addButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
List<IProject> projects = Lists.newArrayList(ResourcesPlugin.getWorkspace().getRoot().getProjects());
projects.removeAll(Lists.newArrayList(getCurrentProjects(configuration)));
FilteredListDialog dialog = new FilteredListDialog(parent.getShell(), new WorkbenchLabelProvider());
dialog.setMultipleSelection(true);
dialog.setMessage(Messages.windupProjectsSelect);
dialog.setElements(projects.toArray(new IProject[projects.size()]));
dialog.setTitle(Messages.windupProjects);
dialog.setHelpAvailable(false);
dialog.create();
if (dialog.open() == Window.OK) {
Object[] selected = (Object[])dialog.getResult();
if (selected.length > 0) {
List<IProject> newProjects = Lists.newArrayList();
Arrays.stream(selected).forEach(p -> newProjects.add((IProject)p));
modelService.createInput(configuration, newProjects);
reloadProjectsTable();
}
}
updateLaunchConfigurationDialog();
}
});
Button removeButton = new Button(container, SWT.PUSH);
removeButton.setText(Messages.windupRemove);
GridDataFactory.fillDefaults().grab(true, false).applyTo(removeButton);
removeButton.addSelectionListener(new SelectionAdapter() {
@SuppressWarnings("unchecked")
@Override
public void widgetSelected(SelectionEvent e) {
ISelection selection = projectsTree.getSelection();
if (!selection.isEmpty()) {
StructuredSelection ss = (StructuredSelection)selection;
List<ProjectInfo> projectInfosToRemove = Lists.newArrayList((List<ProjectInfo>)ss.toList());
List<ProjectInfo> currentProjectInfos = computeProjectsInfos();
filterNonParentProjectInfos(projectInfosToRemove);
Set<IProject> projectsToRemove = collectProjects(projectInfosToRemove);
modelService.deleteProjects(configuration, projectsToRemove);
Set<IProject> projects = filterDuplicateProjects(projectInfosToRemove, currentProjectInfos);
modelService.deletePackages(configuration, projects);
modelService.synch(configuration);
reloadProjectsTable();
reloadPackagesTable();
}
}
});
}
private void createPackagesGroup(Composite parent) {
Group group = SWTFactory.createGroup(parent, Messages.windupPackages+":", 2, 1, GridData.FILL_BOTH);
GridDataFactory.fillDefaults().grab(true, true).applyTo(group);
packagesTable = new TableViewer(group, SWT.BORDER|SWT.MULTI);
GridDataFactory.fillDefaults().grab(true, true).applyTo(packagesTable.getTable());
packagesTable.setLabelProvider(new WorkbenchLabelProvider());
packagesTable.setContentProvider(ArrayContentProvider.getInstance());
createPackagesButtonBar(group, packagesTable);
}
private void createPackagesButtonBar(Composite parent, TableViewer table) {
Composite container = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().margins(0, 0).spacing(0, 0).applyTo(container);
GridDataFactory.fillDefaults().grab(false, true).applyTo(container);
Button addButton = new Button(container, SWT.PUSH);
addButton.setText(Messages.windupAdd);
GridDataFactory.fillDefaults().grab(true, false).applyTo(addButton);
addButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
FilteredListDialog dialog = new FilteredListDialog(parent.getShell(), new WorkbenchLabelProvider());
dialog.setMultipleSelection(true);
dialog.setMessage(Messages.windupPackagesSelect);
dialog.setElements(computePackages(configuration));
dialog.setTitle(Messages.windupPackages);
dialog.setHelpAvailable(false);
dialog.create();
if (dialog.open() == Window.OK) {
Object[] selected = (Object[])dialog.getResult();
if (selected.length > 0) {
List<IPackageFragment> packages = Lists.newArrayList();
Arrays.stream(selected).forEach(p -> packages.add((IPackageFragment)p));
modelService.addPackages(configuration, packages);
reloadPackagesTable();
}
}
}
});
Button removeButton = new Button(container, SWT.PUSH);
removeButton.setText(Messages.windupRemove);
GridDataFactory.fillDefaults().grab(true, false).applyTo(removeButton);
removeButton.addSelectionListener(new SelectionAdapter() {
@SuppressWarnings("unchecked")
@Override
public void widgetSelected(SelectionEvent e) {
ISelection selection = table.getSelection();
if (!selection.isEmpty()) {
StructuredSelection ss = (StructuredSelection)selection;
modelService.removePackages(configuration, (List<IPackageFragment>)ss.toList());
reloadPackagesTable();
}
}
});
}
private void createMigrationPathGroup(Composite parent) {
Group group = SWTFactory.createGroup(parent, Messages.windupMigrationPath+":", 1, 1, GridData.FILL_HORIZONTAL);
GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
String[] items = buildMigrationPaths();
migrationPathCombo = SWTFactory.createCombo(group, SWT.READ_ONLY|SWT.BORDER, GridData.FILL_HORIZONTAL, items);
migrationPathCombo.select(items.length-1);
migrationPathCombo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
configuration.setMigrationPath(findMigrationPath(migrationPathCombo.getText()));
}
});
}
private String[] buildMigrationPaths() {
List<String> paths = Lists.newArrayList();
modelService.getModel().getMigrationPaths().stream().forEach(p -> paths.add(p.getName()));
return paths.toArray(new String[paths.size()]);
}
private MigrationPath findMigrationPath(String name) {
return modelService.getModel().getMigrationPaths().stream().filter(p -> {
return p.getName().equals(name);
}).findFirst().get();
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy launchConfig) {
initializeConfiguration(launchConfig);
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy launchConfig) {
configuration.setName(launchConfig.getName());
modelService.save();
}
@Override
public String getName() {
return inputTabName;
}
@Override
public Image getImage() {
return PlatformUI.getWorkbench().getSharedImages().getImage(IDE.SharedImages.IMG_OBJ_PROJECT);
}
@Override
public boolean isValid(ILaunchConfiguration launchConfig) {
setErrorMessage(null);
setMessage(null);
if (configuration.getInputs().isEmpty()) {
setErrorMessage(inputProjectsDescription);
return false;
}
return true;
}
@Override
public void initializeFrom(ILaunchConfiguration launchConfig) {
initializeConfiguration(launchConfig);
}
private void initializeConfiguration(ILaunchConfiguration launchConfig) {
this.configuration = modelService.findConfiguration(launchConfig.getName());
if (configuration == null) {
this.configuration = modelService.createConfiguration(launchConfig.getName());
}
reload();
}
}