package com.sap.furcas.ide.projectwizard.wizards;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.presentation.EcoreEditorPlugin;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.INewWizard;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.ISetSelectionTarget;
import org.eclipse.ui.progress.UIJob;
import com.sap.furcas.ide.dslproject.Activator;
import com.sap.furcas.ide.dslproject.conf.EcoreMetaProjectConf;
import com.sap.furcas.ide.dslproject.conf.ProjectMetaRefConfFactory;
import com.sap.furcas.ide.dslproject.conf.RegisteredEcoreMetamodelProjectConf;
import com.sap.furcas.ide.projectwizard.util.CodeGenerationException;
import com.sap.furcas.ide.projectwizard.util.CreateMMProject;
import com.sap.furcas.ide.projectwizard.util.CreateProject;
import com.sap.furcas.ide.projectwizard.util.CreateProjectOperation;
import com.sap.furcas.ide.projectwizard.util.ProjectInfo;
/**
* This Wizard creates a pair of projects for the Furcas DSL project. It generates the necessary folder, packages, files and first
* lines of code that help to quickly be able to create the DSL.
*
* @author Frederik Petersen (D054528)
*
*/
public class FurcasWizard extends Wizard implements INewWizard {
/**
* The first page of the wizard.
*/
protected LanguagePage page;
/**
* @return The first page of the wizard.
*/
public LanguagePage getPage() {
return page;
}
/**
* The second page of the wizard.
*/
protected SelectionPage page2;
/**
* @return The second page of the wizard.
*/
public SelectionPage getPage2() {
return page2;
}
/**
* @return The FurcasWizard instance.
*/
public FurcasWizard getFurcasWizard() {
return this;
}
boolean hadError = false;
/**
* @return the hadError
*/
public boolean isHadError() {
return hadError;
}
/**
* @param hadError
* the hadError to set
*/
public void setHadError(boolean hadError) {
this.hadError = hadError;
}
/**
* Sets the Window title, calls the super constructor.
*/
public FurcasWizard() {
super();
setNeedsProgressMonitor(true);
setWindowTitle(Messages.FurcasWizard_1);
}
/**
* User's workbench.
*/
protected IWorkbench workbench;
/**
* Wizard's classLoader
*/
private ClassLoader cL;
/**
* This method loads the image stored in the icons folder. It is displayed in the upper right corner of the wizard.
*
* @return The image descriptor of the image. (The furcas logo)
*/
protected ImageDescriptor getDefaultImageDescriptor() {
cL = this.getClass().getClassLoader();
ImageDescriptor iD = null;
URL url = cL.getResource("icons/furclipse_transparent_128.png"); //$NON-NLS-1$
iD = ImageDescriptor.createFromURL(url);
return iD;
}
/**
* This method is called, when pressing the finish button in the wizard. It calls the doFinish method.
* {@link #doFinish(ProjectInfo, IProgressMonitor) doFinish} TODO
*/
@Override
public boolean performFinish() {
final ProjectInfo pi = page.getProjectInfo();
IRunnableWithProgress op = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException {
doFinish(pi, monitor);
monitor.done();
}
};
try {
getContainer().run(true, false, op);
} catch (InvocationTargetException e) {
MessageDialog.openError(getShell(), "Error", //$NON-NLS-1$
Messages.FurcasWizard_3 + e.getMessage());
} catch (InterruptedException e) {
MessageDialog.openError(getShell(), "Error", //$NON-NLS-1$
Messages.FurcasWizard_5 + e.getMessage());
}
return true;
}
/**
* This method actually does all the work, after the Finish button is pressed. First it creates a new instance of the {@link}
* CreateProject class and calls its <code>run()</code> method. This leads to the creation of the Language Project with all
* its generated files, packages, folders and code. Afterwards {@link}createMMProject() creates another project for the
* MetaModel and doAdditional() creates the new .ecore file. loadmm() leads to loading an existing metamodel into the new one.
* But those steps only happen if the user wants to create a fresh metamodel project. Afterwards the method
* <code>generateSpecific()</code> generates all the MetaModel related files and coding into the Language Project.
*
* @param pi
* The user input.
* @param monitor
* The progress monitor.
*/
public void doFinish(final ProjectInfo pi, IProgressMonitor monitor) {
new UIJob(Messages.FurcasWizard_0) {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
structuredProcess(pi, monitor);
return Status.OK_STATUS;
}
}.schedule();
}
/**
* This method gets called when an error occured making sure that unneccessary projects in workspace get cleaned up.
*/
public void deleteJunk(ProjectInfo pi, IProgressMonitor monitor) {
if (!pi.isLoadMetamodel()) {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IProject metamodelProject = workspace.getRoot().getProject(pi.getProjectName() + ".metamodel"); //$NON-NLS-1$
try {
metamodelProject.delete(true, new SubProgressMonitor(monitor, 1));
} catch (CoreException e) {
// Nothing happens as there is no project to delete.
}
}
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IProject languageProject = workspace.getRoot().getProject(pi.getProjectName());
try {
languageProject.delete(true, new SubProgressMonitor(monitor, 1));
} catch (CoreException e) {
// Nothing happens as there is no project to delete.
}
}
/**
* This method is only called if the user chooses to create a new Metamodel Project. It uses the <code>create()</code> method
* of {@link CreateMMProject} to generate the project. And it then opens the file in the editor.
*
* @param pi
* The user input.
* @throws CodeGenerationException
* @throws InterruptedException
* @throws InvocationTargetException
*/
public void doAdditional(final ProjectInfo pi) throws CodeGenerationException, InvocationTargetException,
InterruptedException {
IWorkbenchPage wpage = null;
// Do the work within an operation.
//
WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
@Override
protected void execute(IProgressMonitor progressMonitor) {
try {
CreateMMProject.create(getFurcasWizard(), progressMonitor, pi);
} catch (CodeGenerationException e) {
getFurcasWizard().setHadError(true);
MessageDialog.openError(getShell(), "Error", e.getMessage()); //$NON-NLS-1$
}
}
};
if (getContainer() != null) {
getContainer().run(false, false, operation);
// Select the new .ecore file.
//
final IFile modelFile = getModelFile();
IWorkbenchWindow workbenchWindow = workbench.getActiveWorkbenchWindow();
wpage = workbenchWindow.getActivePage();
final IWorkbenchPart activePart = wpage.getActivePart();
if (activePart instanceof ISetSelectionTarget) {
final ISelection targetSelection = new StructuredSelection(modelFile);
getShell().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
((ISetSelectionTarget) activePart).selectReveal(targetSelection);
}
});
}
// Open an editor on the file.
//
try {
IEditorDescriptor defaultEditor = workbench.getEditorRegistry().getDefaultEditor(
modelFile.getFullPath().toString());
wpage.openEditor(new FileEditorInput(modelFile),
defaultEditor == null ? "org.eclipse.emf.ecore.presentation.EcoreEditorID" : defaultEditor.getId()); //$NON-NLS-1$
} catch (PartInitException exception) {
MessageDialog.openError(workbenchWindow.getShell(),
EcoreEditorPlugin.INSTANCE.getString("_UI_OpenEditorError_label"), exception.getMessage()); //$NON-NLS-1$
}
} else {
// That should only run if called by a test
//
NullProgressMonitor monitor = new NullProgressMonitor();
CreateMMProject.create(getFurcasWizard(), monitor, pi);
}
}
/**
* Adds the first two pages to the wizard (as they're always part of the wizard irrespective of the user input.
*/
@Override
public void addPages() {
page = new LanguagePage();
addPage(page);
page2 = new SelectionPage(this, page.getProjectInfo());
addPage(page2);
}
/**
* Get the model file.
*/
public IFile getModelFile() {
Path path = new Path(page.getProjectInfo().getProjectName() + ".metamodel/model/" //$NON-NLS-1$
+ CreateProject.capitalizeFirstChar(page.getProjectInfo().getLanguageName()) + ".ecore"); //$NON-NLS-1$
return ResourcesPlugin.getWorkspace().getRoot().getFile(path);
}
/**
* This method provides the generation of the MetaModelspecific files and coding in the dsl project. Since these are the last
* lines of code called by the wizard it also builds, refreshs and cleans the project for the purpose of generating all files
* at once, as some generators don't see previously generated files if the project is not rebuild/refreshed/cleaned.
*
* @param project
* The language project
* @param pi
* The user input
* @throws CodeGenerationException
*/
protected void generateSpecific(IProject project, ProjectInfo pi, IProgressMonitor monitor) throws CodeGenerationException {
if (project != null) {
EcoreMetaProjectConf conf;
if (!pi.isLoadMetamodel()) {
registerLocalMetamodel(pi);
}
// instantiates the configuration take a look at EcoreMetaProjectConf for more details
// uses the new URI list in the ReferenceScope to load the referenced metamodel from registered packages
conf = new RegisteredEcoreMetamodelProjectConf(project,
pi.getNsURI() + ",http://www.eclipse.org/emf/2002/Ecore", pi.isAutoResolve()); //$NON-NLS-1$
try {
ProjectMetaRefConfFactory.configure(project, conf);
} catch (CoreException e) {
throw new CodeGenerationException(Messages.FurcasWizard_14, e.getCause());
}
// Builds, refreshs, cleans the project to make sure, that all files will be found and generated
monitor.subTask(Messages.FurcasWizard_15);
try {
if (!pi.isLoadMetamodel()) {
// rebuild metamodel project so that there are no build problems on furcas project
if (project.getReferencedProjects().length == 1) {
project.getReferencedProjects()[0].build(IncrementalProjectBuilder.FULL_BUILD, monitor);
}
}
project.build(IncrementalProjectBuilder.FULL_BUILD, monitor);
IFolder folder = project.getFolder("generated").getFolder("generated"); //$NON-NLS-1$ //$NON-NLS-2$
folder.refreshLocal(1, new NullProgressMonitor());
} catch (CoreException e) {
throw new CodeGenerationException(Messages.FurcasWizard_18, e.getCause());
}
}
}
private void registerLocalMetamodel(ProjectInfo pi) {
ResourceSet resourceSet = new ResourceSetImpl();
Resource resource = resourceSet.createResource(URI.createPlatformResourceURI(pi.getModelPath(), true));
Map<Object, Object> options = new HashMap<Object, Object>();
options.put(XMLResource.OPTION_ENCODING, "UTF-8");
try {
resource.load(options);
} catch (IOException e) {
Activator.logger.logError("Error loading local metamodel: " + resource.getURI(), e);
}
EList<EObject> list = resource.getContents();
for (EObject object : list) {
if (object instanceof EPackage) {
EPackage newPackage = (EPackage) object;
EPackage.Registry.INSTANCE.put(newPackage.getNsURI(), newPackage);
}
}
}
/**
* Initializes this creation wizard using the passed workbench and object selection.
* <p>
* This method is called after the no argument constructor and before other methods are called.
* </p>
* <p>
* Selection will always be empty in FurcasWizard and has no effect here.
* </p>
*
* @param workbench
* the current workbench
* @param selection
* the current object selection -- no effect
*/
@Override
public void init(IWorkbench workbench, IStructuredSelection selection) {
this.workbench = workbench;
setWindowTitle(EcoreEditorPlugin.INSTANCE.getString("_UI_Wizard_label")); //$NON-NLS-1$
setDefaultPageImageDescriptor(getDefaultImageDescriptor());
}
public void structuredProcess(final ProjectInfo pi, IProgressMonitor monitor) {
CreateProjectOperation cP = new CreateProjectOperation(pi, getShell(), getFurcasWizard());
try {
cP.run(monitor);
} catch (InvocationTargetException e) {
hadError = true;
MessageDialog.openError(getShell(), "Error", //$NON-NLS-1$
Messages.FurcasWizard_21 + e.getMessage());
} catch (InterruptedException e) {
hadError = true;
MessageDialog.openError(getShell(), "Error", e.getMessage()); //$NON-NLS-1$
}
IProject project = cP.project;
if (!pi.isLoadMetamodel() && !hadError) {
try {
doAdditional(pi);
} catch (CodeGenerationException e) {
hadError = true;
MessageDialog.openError(getShell(), "Error", e.getMessage()); //$NON-NLS-1$
} catch (InvocationTargetException e) {
hadError = true;
MessageDialog.openError(getShell(), "Error", //$NON-NLS-1$
Messages.FurcasWizard_25 + e.getMessage());
} catch (InterruptedException e) {
hadError = true;
MessageDialog.openError(getShell(), "Error", e.getMessage()); //$NON-NLS-1$
}
}
if (!hadError) {
try {
generateSpecific(project, pi, monitor);
} catch (CodeGenerationException e) {
hadError = true;
MessageDialog.openError(getShell(), "Error", e.getMessage()); //$NON-NLS-1$
}
}
if (hadError) {
deleteJunk(pi, monitor);
}
}
}