/******************************************************************************* * Copyright (c) 2012, 2013 VMware, Inc. * All rights reserved. This program and the accompanying materials * are 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: * VMware, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.wizard.template.infrastructure.processor; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; 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.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.ui.dialogs.IOverwriteQuery; import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider; import org.eclipse.ui.wizards.datatransfer.ImportOperation; import org.springframework.ide.eclipse.wizard.WizardPlugin; import org.springframework.ide.eclipse.wizard.template.ErrorUtils; import org.springframework.util.AntPathMatcher; import org.springframework.util.FileSystemUtils; import org.springsource.ide.eclipse.commons.content.core.util.IContentConstants; import org.springsource.ide.eclipse.commons.core.Policy; import org.springsource.ide.eclipse.commons.core.ZipFileUtil; import org.springsource.ide.eclipse.commons.ui.UiStatusHandler; /** * * Requires a shell. Should be run as a UI Job. * @author Terry Denney */ public class TemplateProjectCreator { private final Shell shell; private volatile IProject project; private final URL archiveFile; private final ProcessingInfo processingInfo; private final TemplateProcessor templateProcessor; private final TemplateProcessor fileNameProcessor; private final URI projectPath; public TemplateProjectCreator(IProject project, URI projectPath, URL archiveFile, Shell shell, TemplateProcessor templateProcessor, TemplateProcessor fileNameProcessor, ProcessingInfo processingInfo) { this.project = project; this.projectPath = projectPath; this.archiveFile = archiveFile; this.shell = shell; this.templateProcessor = templateProcessor; this.fileNameProcessor = fileNameProcessor; this.processingInfo = processingInfo; } public IProject createProject(IProgressMonitor monitor) throws CoreException { final String projectName = project.getName(); final File unzipFolder = unzipProject(monitor); final WorkspaceModifyOperation op = new WorkspaceModifyOperation() { @Override protected void execute(IProgressMonitor monitor) throws CoreException, InvocationTargetException, InterruptedException { monitor.subTask("Creating project " + projectName); Policy.checkCancelled(monitor); IWorkspace workspace = ResourcesPlugin.getWorkspace(); IProjectDescription projectDescription = workspace.newProjectDescription(projectName); URI projectLocation = (projectPath != null && !ResourcesPlugin.getWorkspace().getRoot() .getLocationURI().equals(projectPath)) ? projectPath : null; projectDescription.setLocationURI(projectLocation); project.create(projectDescription, new SubProgressMonitor(monitor, 10)); project.open(new SubProgressMonitor(monitor, 20)); monitor.subTask("Importing project " + projectName); Policy.checkCancelled(monitor); try { importFiles(project, unzipFolder.listFiles()[0], new SubProgressMonitor(monitor, 70)); } catch (IOException e) { final Status status = new Status(Status.ERROR, WizardPlugin.PLUGIN_ID, "Unable to create template project", e); throw new CoreException(status); } } }; try { monitor.beginTask("Importing project", 100); IWorkspace workspace = ResourcesPlugin.getWorkspace(); project = workspace.getRoot().getProject(projectName); if (project.exists()) { final boolean[] result = new boolean[1]; Display.getDefault().syncExec(new Runnable() { public void run() { result[0] = MessageDialog.openQuestion(shell, "Create Template Project", "A project named " + project.getName() + " already exists in the workspace. Overwrite the existing project?"); } }); if (!result[0]) { return null; } project.delete(true, true, monitor); } op.run(monitor); monitor.done(); } catch (InterruptedException e) { throw new OperationCanceledException(); } catch (InvocationTargetException e) { String errorMessage = ErrorUtils.getErrorMessage(e); Status status = new Status(IStatus.ERROR, WizardPlugin.PLUGIN_ID, "Error during template project creation" + (errorMessage != null ? " due to: " + errorMessage : ""), e); UiStatusHandler.logAndDisplay(status); throw new CoreException(status); } finally { if (unzipFolder != null) { deleteFile(unzipFolder); } monitor.done(); } return project; } private void deleteFile(File file) { if (file.isDirectory()) { File[] children = file.listFiles(); for (File child : children) { deleteFile(child); } } file.delete(); } private File getMappedFile(File source, File destination, String sourceBasePath, String destinationBasePath) { if (source.isDirectory()) { String path = source.getPath(); String mappedPath = fileNameProcessor.replacePathForDirectory(path, File.separatorChar); if (mappedPath != null) { mappedPath = destinationBasePath + mappedPath.substring(sourceBasePath.length()); return new File(mappedPath); } } String contentName = source.getName(); String mappedContentName = fileNameProcessor.replaceTokens(contentName); return new File(destination, mappedContentName); } private void importFiles(IProject project, File unzipFolder, IProgressMonitor monitor) throws IOException, InvocationTargetException, InterruptedException { FileSystemStructureProvider provider = FileSystemStructureProvider.INSTANCE; ImportOperation operation = new ImportOperation(project.getFullPath(), unzipFolder, provider, new IOverwriteQuery() { // always overwrite public String queryOverwrite(String pathString) { return IOverwriteQuery.YES; } }); operation.setContext(shell); // need to overwrite .project file operation.setOverwriteResources(true); operation.setCreateContainerStructure(false); operation.run(monitor); } private void processDirectory(File source, File destination, String sourceBasePath, String destinationBasePath) throws IOException { for (File content : source.listFiles()) { File subFileOrDir = getMappedFile(content, destination, sourceBasePath, destinationBasePath); if (shouldExclude(subFileOrDir)) { continue; } if (content.isDirectory()) { subFileOrDir.mkdirs(); processDirectory(content, subFileOrDir, sourceBasePath, destinationBasePath); } else { templateProcessor.process(content, subFileOrDir); } } } private boolean shouldExclude(File file) { AntPathMatcher pathMatcher = new AntPathMatcher(); for (String exclusionPattern : processingInfo.getExclusionPatterns()) { if (pathMatcher.match(exclusionPattern, file.getAbsolutePath().replace('\\', '/'))) { return true; } } return false; } private File unzipProject(IProgressMonitor monitor) throws CoreException { monitor.beginTask("Unzip project", 10); File unzipFolder = null; try { File tempFile = File.createTempFile("tempFile", null); String parentPath = tempFile.getParent(); tempFile.delete(); unzipFolder = new File(parentPath + "/unzipFolder"); if (unzipFolder.exists()) { deleteFile(unzipFolder); } unzipFolder.mkdir(); if (archiveFile.getProtocol().equals("file") && !new File(archiveFile.toURI()).isDirectory()) { ZipFileUtil.unzip(archiveFile, unzipFolder, monitor); } else { File templateDir = new File(unzipFolder, "template"); FileSystemUtils.copyRecursively(new File(archiveFile.toURI()), templateDir); // template.xml and wizard.json are not needed at this point File templateXml = new File(templateDir, IContentConstants.TEMPLATE_DATA_FILE_NAME); File wizardJson = new File(templateDir, IContentConstants.WIZARD_DATA_FILE_NAME); templateXml.delete(); wizardJson.delete(); } File tempFolder = new File(parentPath + "/tempFolder"); if (tempFolder.exists()) { deleteFile(tempFolder); } tempFolder.mkdir(); processDirectory(unzipFolder, tempFolder, unzipFolder.getPath(), tempFolder.getPath()); deleteFile(unzipFolder); monitor.done(); return tempFolder; } catch (IOException e) { String message = NLS.bind("Could not create template project {0} because {1}", project, e.getLocalizedMessage()); final Status status = new Status(IStatus.ERROR, WizardPlugin.PLUGIN_ID, message, e); Display.getDefault().asyncExec(new Runnable() { public void run() { UiStatusHandler.logAndDisplay(status); } }); throw new CoreException(status); } catch (URISyntaxException e) { String message = NLS.bind("The project path {0} in template.xml does not exist or can't be read: {1}.", project, e.getLocalizedMessage()); final Status status = new Status(IStatus.ERROR, WizardPlugin.PLUGIN_ID, message, e); Display.getDefault().asyncExec(new Runnable() { public void run() { UiStatusHandler.logAndDisplay(status); } }); throw new CoreException(status); } } }