package com.sap.furcas.ide.projectwizard.util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
/**
* Supplies the wizard with methods to build pluginprojects and necessary files for the Furcas Workbench, Works together with the
* classes CreateProject and SourceCodeFactory in the wizards package.
*
* @author Frederik Petersen (D054528)
*
*/
public class WizardProjectHelper {
/**
* Generates a fresh plugin project for eclipse.
*
* @param pi
* The user input from the wizard.
* @param srcFolders
* List of all src folders
* @param nonSrcFolders
* List of all non source folders
* @param exportedPackages
* List of all exported packages
* @param progressMonitor
* The ProgressMonitor
* @param theShell
* The Window containing the wizard.
* @param metamodel
* States wether this is the metamodel project or not. This is used for including different natures and builders
* and a different project name.
* @return The generated project.
* @throws CodeGenerationException
*/
public static IProject createPlugInProject(final ProjectInfo pi, final List<String> srcFolders,
final List<String> nonSrcFolders, final List<String> exportedPackages, final IProgressMonitor progressMonitor,
final boolean metamodel) throws CodeGenerationException {
IProject project = null;
final String projectName;
// Check if this is the creation of the metamodel project or not.
//
if (metamodel) {
projectName = pi.getProjectName() + ".metamodel";
} else {
projectName = pi.getProjectName();
}
progressMonitor.beginTask("", 10);
progressMonitor.subTask("Creating project " + projectName);
IWorkspace workspace = ResourcesPlugin.getWorkspace();
project = workspace.getRoot().getProject(projectName);
if (project.exists()) {
deleteOldProject(progressMonitor, project, projectName);
}
// Create the project
//
IJavaProject javaProject = JavaCore.create(project);
IProjectDescription projectDescription = ResourcesPlugin.getWorkspace().newProjectDescription(projectName);
projectDescription.setLocation(null);
try {
project.create(projectDescription, new SubProgressMonitor(progressMonitor, 1));
} catch (CoreException e) {
throw new CodeGenerationException("Error while creating project: " + project.getName(), e.getCause());
}
List<IClasspathEntry> classpathEntries = new ArrayList<IClasspathEntry>();
// Add the required natures depending on wether this is a dsl project or a metamodelproject.
//
if (!metamodel) {
projectDescription.setNatureIds(new String[] { "com.sap.furcas.ide.dslproject.syntaxGenerationNature",
JavaCore.NATURE_ID, "org.eclipse.emf.query2.index.ui.queryIndexNature" ,"org.eclipse.pde.PluginNature" });
} else {
projectDescription.setNatureIds(new String[] { JavaCore.NATURE_ID, "org.eclipse.pde.PluginNature" });
}
addBuilders(progressMonitor, metamodel, project, projectDescription);
setClasspath(srcFolders, nonSrcFolders, progressMonitor, project, javaProject, classpathEntries);
try {
javaProject.setOutputLocation(new Path("/" + projectName + "/bin"), new SubProgressMonitor(progressMonitor, 1));
} catch (JavaModelException e) {
throw new CodeGenerationException("Error while setting output location: " + "/" + projectName + "/bin", e.getCause());
}
if (!metamodel) {
String mmBundleName = "";
if (!pi.isLoadMetamodel()) {
mmBundleName = pi.getBasePath().replaceAll(Path.SEPARATOR+"", ".")+".metamodel";
} else {
mmBundleName = pi.getMmBundleName();
}
createManifestAndBuildProps(pi, progressMonitor, project, mmBundleName);
}
progressMonitor.done();
return project;
}
/**
* Create the manifest and build properties. If it's a metamodel project those files are created automatically.
*
* @param pi
* @param progressMonitor
* @param project
* @throws CoreException
*/
private static void createManifestAndBuildProps(final ProjectInfo pi, final IProgressMonitor progressMonitor, IProject project, String mmBundleName)
throws CodeGenerationException {
SourceCodeFactory scf = new SourceCodeFactory();
createFile("build.properties", project, scf.createBuildPropertiesFile(pi), progressMonitor);
IFolder metaInf = project.getFolder("META-INF");
try {
metaInf.create(false, true, new SubProgressMonitor(progressMonitor, 1));
} catch (CoreException e) {
throw new CodeGenerationException("Failed to create folder 'META-INF'", e.getCause());
}
createFile("MANIFEST.MF", metaInf, scf.createManifest(pi,mmBundleName), progressMonitor);
}
/**
*
* Set the classpath entries for the source folders
*
* @param srcFolders
* @param nonSrcFolders
* @param progressMonitor
* @param project
* @param javaProject
* @param classpathEntries
* @throws CoreException
* @throws JavaModelException
*/
private static void setClasspath(final List<String> srcFolders, final List<String> nonSrcFolders,
final IProgressMonitor progressMonitor, IProject project, IJavaProject javaProject,
List<IClasspathEntry> classpathEntries) throws CodeGenerationException {
Collections.reverse(srcFolders);
for (String src : srcFolders) {
IFolder srcContainer = project.getFolder(src);
if (!srcContainer.exists()) {
try {
srcContainer.create(false, true, new SubProgressMonitor(progressMonitor, 1));
} catch (CoreException e) {
throw new CodeGenerationException("Failed to create source folder 'src'", e.getCause());
}
}
IClasspathEntry srcClasspathEntry = JavaCore.newSourceEntry(srcContainer.getFullPath());
classpathEntries.add(0, srcClasspathEntry);
}
if (nonSrcFolders != null) {
for (String src : nonSrcFolders) {
IFolder srcContainer = project.getFolder(src);
if (!srcContainer.exists()) {
try {
srcContainer.create(false, true, new SubProgressMonitor(progressMonitor, 1));
} catch (CoreException e) {
throw new CodeGenerationException("Failed to create source folder 'src'", e.getCause());
}
}
}
}
classpathEntries.add(JavaCore.newContainerEntry(new Path("org.eclipse.jdt.launching.JRE_CONTAINER")));
classpathEntries.add(JavaCore.newContainerEntry(new Path("org.eclipse.pde.core.requiredPlugins")));
try {
javaProject.setRawClasspath(classpathEntries.toArray(new IClasspathEntry[classpathEntries.size()]),
new SubProgressMonitor(progressMonitor, 1));
} catch (JavaModelException e) {
throw new CodeGenerationException("Failed to set projects classpath", e.getCause());
}
}
/**
*
* Add the required builders depending on whether this is a dsl project or a metamodelproject.
*
* @param progressMonitor
* @param metamodel
* @param project
* @param projectDescription
* @throws CoreException
*/
private static void addBuilders(final IProgressMonitor progressMonitor, final boolean metamodel, IProject project,
IProjectDescription projectDescription) throws CodeGenerationException {
ICommand java = projectDescription.newCommand();
java.setBuilderName(JavaCore.BUILDER_ID);
ICommand manifest = projectDescription.newCommand();
manifest.setBuilderName("org.eclipse.pde.ManifestBuilder");
ICommand schema = projectDescription.newCommand();
schema.setBuilderName("org.eclipse.pde.SchemaBuilder");
ICommand query2 = projectDescription.newCommand();
schema.setBuilderName("org.eclipse.emf.query2.index.ui.queryIndexBuilder");
if (!metamodel) {
ICommand furcas = projectDescription.newCommand();
furcas.setBuilderName("com.sap.furcas.ide.dslproject.syntaxBuilder");
projectDescription.setBuildSpec(new ICommand[] { furcas, query2, java, manifest, schema });
} else {
projectDescription.setBuildSpec(new ICommand[] { java, manifest, schema });
}
try {
project.open(new SubProgressMonitor(progressMonitor, 1));
project.setDescription(projectDescription, new SubProgressMonitor(progressMonitor, 1));
} catch (CoreException e) {
throw new CodeGenerationException("Failed to set projects builders", e.getCause());
}
}
/**
*
* Clean up any old project information.
*
* @param progressMonitor
* @param theShell
* @param project
* @param projectName
* @throws CoreException
*/
private static void deleteOldProject(final IProgressMonitor progressMonitor, IProject project,
final String projectName) throws CodeGenerationException {
final boolean[] result = new boolean[1];
PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
result[0] = MessageDialog.openQuestion(null, "Do you want to overwrite the project " + projectName,
"Note that everything inside the project '" + projectName
+ "' will be deleted if you confirm this dialog.");
}
});
if (result[0]) {
try {
project.delete(true, true, new SubProgressMonitor(progressMonitor, 1));
} catch (CoreException e) {
throw new CodeGenerationException("Failed to delete existing project with same name", e.getCause());
}
} else {
return;
}
}
/**
* Creates a file.
*
* @param name
* The name of the file.
* @param container
* The container in which the file is to be created.
* @param content
* The content of the file.
* @param progressMonitor
* The Progress Monitor.
* @return The file.
* @throws CodeGenerationException
*/
public static IFile createFile(String name, IContainer container, String content, IProgressMonitor progressMonitor)
throws CodeGenerationException {
IFile file = container.getFile(new Path(name));
assertExist(file.getParent());
try {
InputStream stream = new ByteArrayInputStream(content.getBytes(file.getCharset()));
if (file.exists()) {
file.setContents(stream, true, true, progressMonitor);
} else {
file.create(stream, true, progressMonitor);
}
stream.close();
} catch (Exception e) {
throw new CodeGenerationException("Failed to create file '" + name + "'", e.getCause());
}
progressMonitor.worked(1);
return file;
}
/**
* Creates a .genmodel file for the ecore metamodel. See {@link}CreateMMProject.
*
* @param progressMonitor
* The progress monitor
* @param project
* the project where the genmodel is to be created
* @param pi
* The user input
* @return The .genmodel file
* @throws CoreException
*/
public static IFile createGenmodel(final IProgressMonitor progressMonitor, IProject project, ProjectInfo pi)
throws CodeGenerationException {
SourceCodeFactory scf = new SourceCodeFactory();
IFolder folder = project.getFolder("model");
return createFile(CreateProject.capitalizeFirstChar(pi.getLanguageName()) + ".genmodel", folder,
scf.createGenmodelCode(pi), progressMonitor);
}
/**
* @param name
* of the destination file
* @param container
* directory containing the the destination file
* @param contentUrl
* Url pointing to the src of the content
* @param progressMonitor
* used to interact with and show the user the current operation status
* @return
* @throws CodeGenerationException
*/
public static IFile createFile(String name, IContainer container, URL contentUrl, IProgressMonitor progressMonitor)
throws CodeGenerationException {
IFile file = container.getFile(new Path(name));
InputStream inputStream = null;
try {
inputStream = contentUrl.openStream();
if (file.exists()) {
file.setContents(inputStream, true, true, progressMonitor);
} else {
file.create(inputStream, true, progressMonitor);
}
inputStream.close();
} catch (Exception e) {
throw new CodeGenerationException("Failed to create File: '" + name + "'", e.getCause());
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
throw new CodeGenerationException("Failed to close inputStream while creating file: '" + name + "'",
e.getCause());
}
}
}
progressMonitor.worked(1);
return file;
}
/**
* Assert that c exists. If it doesn't exist. It will be created.
*
* @param container
* The container whose existence is checked.
* @throws CodeGenerationException
*/
private static void assertExist(IContainer container) throws CodeGenerationException {
if (!container.exists()) {
if (!container.getParent().exists()) {
assertExist(container.getParent());
}
if (container instanceof IFolder) {
try {
((IFolder) container).create(false, true, new NullProgressMonitor());
} catch (CoreException e) {
throw new CodeGenerationException("Failed to create container: " + container.getName(), e.getCause());
}
}
}
}
/**
* Opens a file for editing in the Shell.
*
* @param s
* The shell where to open it.
* @param file
* The file to open.
*/
public static void openFileToEdit(Shell s, final IFile file) {
s.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
try {
IDE.openEditor(page, file, true);
} catch (PartInitException e) {
Throwable realException = e.getCause();
MessageDialog.openError(page.getWorkbenchWindow().getShell(), "Error", realException.getMessage());
}
}
});
}
}