package bndtools.wizards.workspace;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import org.bndtools.core.ui.wizards.shared.TemplateSelectionWizardPage;
import org.bndtools.templating.Resource;
import org.bndtools.templating.ResourceMap;
import org.bndtools.templating.Template;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import aQute.bnd.build.Project;
import aQute.lib.io.IO;
import bndtools.Plugin;
import bndtools.central.Central;
public class WorkspaceSetupWizard extends Wizard implements INewWizard {
private IWorkbench workbench;
private WorkspaceSetupWizardPage setupPage;
private TemplateSelectionWizardPage templatePage;
private WorkspacePreviewPage previewPage;
public WorkspaceSetupWizard() {
setNeedsProgressMonitor(true);
}
@Override
public void init(IWorkbench workbench, IStructuredSelection selection) {
this.workbench = workbench;
setupPage = new WorkspaceSetupWizardPage();
updateSetupPageForExistingProjects();
templatePage = new TemplateSelectionWizardPage("workspaceTemplateSelection", "workspace", null);
templatePage.setTitle("Select Workspace Template");
previewPage = new WorkspacePreviewPage();
previewPage.setTargetDir(setupPage.getLocation().toFile());
setupPage.addPropertyChangeListener(WorkspaceLocationPart.PROP_LOCATION, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
previewPage.setTargetDir(setupPage.getLocation().toFile());
}
});
templatePage.addPropertyChangeListener(TemplateSelectionWizardPage.PROP_TEMPLATE, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
Template template = templatePage.getTemplate();
previewPage.setTemplate(template);
}
});
}
@Override
public void addPages() {
addPage(setupPage);
addPage(templatePage);
addPage(previewPage);
}
@Override
public boolean performFinish() {
final IWorkspace workspace = ResourcesPlugin.getWorkspace();
final File targetDir = previewPage.getTargetDir();
final Set<String> checkedPaths = previewPage.getCheckedPaths();
final boolean cleanBuild = setupPage.isCleanBuild();
try {
// Expand the template
ResourceMap outputs = previewPage.getTemplateOutputs();
final Set<File> topLevelFolders = new HashSet<>();
for (Entry<String,Resource> entry : outputs.entries()) {
String path = entry.getKey();
if (checkedPaths.contains(path)) {
Resource resource = entry.getValue();
// Create the folder or file resource
File file = new File(targetDir, path);
switch (resource.getType()) {
case Folder :
Files.createDirectories(file.toPath());
break;
case File :
File parentDir = file.getParentFile();
Files.createDirectories(parentDir.toPath());
try (InputStream in = resource.getContent()) {
IO.copy(in, file);
}
break;
default :
throw new IllegalArgumentException("Unknown resource type " + resource.getType());
}
// Remember the top-level folders we create, for importing below
if (file.getParentFile().equals(targetDir))
topLevelFolders.add(file);
}
}
// Import anything that looks like an Eclipse project & do a full rebuild
final IWorkspaceRunnable importProjectsRunnable = new IWorkspaceRunnable() {
@Override
public void run(IProgressMonitor monitor) throws CoreException {
File[] children = targetDir.listFiles();
if (children != null) {
int work = children.length;
if (cleanBuild)
work += 2;
SubMonitor progress = SubMonitor.convert(monitor, work);
for (File folder : children) {
if (folder.isDirectory() && topLevelFolders.contains(folder)) {
String projectName = folder.getName();
File projectFile = new File(folder, IProjectDescription.DESCRIPTION_FILE_NAME);
if (projectFile.exists()) {
IProject project = workspace.getRoot().getProject(projectName);
if (!project.exists()) {
// No existing project in the workspace, so import the generated project.
SubMonitor subProgress = progress.newChild(1);
project.create(subProgress.newChild(1));
project.open(subProgress.newChild(1));
// Now make sure it is associated with the right location
IProjectDescription description = project.getDescription();
IPath path = Path.fromOSString(projectFile.getParentFile().getAbsolutePath());
description.setLocation(path);
project.move(description, IResource.REPLACE, progress);
} else {
// If a project with the same name exists, does it live in the same location? If not, we can't import the generated project.
File existingLocation = project.getLocation().toFile();
if (!existingLocation.equals(folder)) {
String message = String.format("Cannot import generated project from %s. A project named %s already exists in the workspace and is mapped to location %s", folder.getAbsolutePath(), projectName,
existingLocation);
throw new CoreException(new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, message, null));
}
SubMonitor subProgress = progress.newChild(1);
// Open it if closed
project.open(subProgress.newChild(1));
// Refresh, as the template may have generated new content
project.refreshLocal(IResource.DEPTH_INFINITE, subProgress.newChild(1));
}
}
}
}
if (cleanBuild)
workspace.build(IncrementalProjectBuilder.CLEAN_BUILD, progress.newChild(2));
}
}
};
getContainer().run(true, true, new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
workspace.run(importProjectsRunnable, monitor);
} catch (CoreException e) {
throw new InvocationTargetException(e);
}
new WorkspaceJob("Load Repositories") {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
try {
Central.refreshPlugins();
} catch (Exception e) {
// There may be no workspace yet
}
return Status.OK_STATUS;
}
}.schedule();
}
});
// Prompt to switch to the bndtools perspective
IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
IPerspectiveDescriptor currentPerspective = window.getActivePage().getPerspective();
if (!"bndtools.perspective".equals(currentPerspective.getId())) {
if (MessageDialog.openQuestion(getShell(), "Bndtools Perspective", "Switch to the Bndtools perspective?")) {
workbench.showPerspective("bndtools.perspective", window);
}
}
return true;
} catch (InvocationTargetException e) {
ErrorDialog.openError(getShell(), "Error", null, new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, "Error generating template output", e.getTargetException()));
return false;
} catch (Exception e) {
ErrorDialog.openError(getShell(), "Error", null, new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, "Error generating template output", e));
return false;
}
}
private void updateSetupPageForExistingProjects() {
// find existing bnd projects
File existingBndLocation = null;
for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
if (!project.isOpen())
continue;
try {
IProjectNature bndNature = project.getNature(Plugin.BNDTOOLS_NATURE);
if (bndNature != null || project.getName().equals(Project.BNDCNF)) {
File projectLocation = project.getLocation().toFile();
existingBndLocation = projectLocation.getParentFile();
break;
}
} catch (CoreException ex) {
Plugin.getDefault().getLog().log(new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, "Failed to load project nature", ex));
}
}
if (existingBndLocation != null) {
setupPage.setLocation(new LocationSelection(false, existingBndLocation.getAbsolutePath()));
}
}
}