Java tutorial
/** * Copyright 2013-2015 Asakusa Framework Team. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.asakusafw.shafu.internal.ui.wizards; import java.io.Closeable; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.ResourcesPlugin; 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.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.window.Window; import org.eclipse.jface.wizard.Wizard; import org.eclipse.swt.SWT; import org.eclipse.ui.INewWizard; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkingSet; import org.eclipse.ui.dialogs.ListDialog; import org.eclipse.ui.wizards.newresource.BasicNewResourceWizard; import com.asakusafw.shafu.core.gradle.GradleBuildTask; import com.asakusafw.shafu.core.gradle.GradleContext; import com.asakusafw.shafu.core.net.IContentProcessor; import com.asakusafw.shafu.core.net.ShafuNetwork; import com.asakusafw.shafu.core.util.IRunnable; import com.asakusafw.shafu.core.util.IoUtils; import com.asakusafw.shafu.core.util.StatusUtils; import com.asakusafw.shafu.internal.ui.Activator; import com.asakusafw.shafu.internal.ui.LogUtil; import com.asakusafw.shafu.ui.ShafuUi; import com.asakusafw.shafu.ui.consoles.ShafuConsole; import com.asakusafw.shafu.ui.util.ProgressUtils; /** * Wizard of creating project from application templates. */ public class NewProjectWizard extends Wizard implements INewWizard { private IWorkbench currentWorkbench; private IStructuredSelection currentSelection; private SelectProjectTemplatePage templatePage; private ProjectInformationPage informationPage; @Override public void init(IWorkbench workbench, IStructuredSelection selection) { this.currentWorkbench = workbench; this.currentSelection = selection; setNeedsProgressMonitor(true); setWindowTitle(Messages.NewProjectWizard_title); } @Override public void addPages() { this.templatePage = new SelectProjectTemplatePage(); this.informationPage = new ProjectInformationPage(); addPage(informationPage); addPage(templatePage); informationPage.setSelection(currentSelection); } @Override public boolean canFinish() { if (getContainer().getCurrentPage().getNextPage() != null) { return false; } return super.canFinish(); } @Override public boolean performFinish() { final File projectDirectory = computeProjectDirectory(); if (projectDirectory.exists()) { boolean delete = MessageDialog.openConfirm(getShell(), Messages.NewProjectWizard_dialogOverwriteTitle, MessageFormat.format(Messages.NewProjectWizard_dialogOverwriteMessage, projectDirectory)); if (delete == false) { return false; } } final IWorkingSet[] workingSets = informationPage.getSelectedWorkingSets(); final List<String> taskNames = templatePage.getTaskNames(); final ShafuConsole console = templatePage.getConsole(); final AtomicReference<Stage> stageRef = new AtomicReference<Stage>(Stage.INIT); final Archive templateArchive = new Archive(templatePage.getTargetFile(), templatePage.getTargetUrl()); try { ProgressUtils.run(getContainer(), new IRunnable() { @Override public void run(IProgressMonitor monitor) throws CoreException { SubMonitor sub = SubMonitor.convert(monitor); try { perform(sub, projectDirectory, workingSets, templateArchive, taskNames, console, stageRef); } finally { monitor.done(); } } }); } catch (RuntimeException e) { IStatus status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.NewProjectWizard_errorUnknown, e); LogUtil.log(status); ErrorDialog.openError(getShell(), null, null, status); templatePage.setPageComplete(false); return false; } catch (CoreException e) { if (StatusUtils.hasCancel(e.getStatus())) { return false; } switch (stageRef.get()) { case BUILD: templatePage.setErrorMessage(Messages.NewProjectWizard_errorTemplateBuild); templatePage.openConsole(); break; default: templatePage.setErrorMessage(e.getStatus().getMessage()); LogUtil.log(e.getStatus()); break; } templatePage.setPageComplete(false); return false; } finally { templateArchive.close(); } return true; } private File computeProjectDirectory() { if (informationPage.useDefaults()) { return informationPage.getLocationPath().append(informationPage.getProjectName()).toFile(); } else { return informationPage.getLocationPath().toFile(); } } void perform(SubMonitor monitor, File projectDirectory, IWorkingSet[] workingSets, Archive archive, List<String> taskNames, ShafuConsole console, AtomicReference<Stage> stageRef) throws CoreException { monitor.beginTask(Messages.NewProjectWizard_monitorMain, 100); extractProjectContents(monitor.newChild(20, SubMonitor.SUPPRESS_NONE), projectDirectory, archive); boolean succeed = false; try { stageRef.set(Stage.BUILD); buildProject(monitor.newChild(60, SubMonitor.SUPPRESS_NONE), projectDirectory, taskNames, console); stageRef.set(Stage.DEPLOY); deployProject(monitor.newChild(20, SubMonitor.SUPPRESS_NONE), projectDirectory, workingSets); succeed = true; } finally { if (succeed == false && projectDirectory.exists()) { IoUtils.deleteQuietly(projectDirectory); } } } private void extractProjectContents(SubMonitor monitor, File projectDirectory, Archive archive) throws CoreException { monitor.beginTask(Messages.NewProjectWizard_monitorExtractContents, 100); StatusUtils.checkCanceled(monitor); File temporaryFolder = archive.extract(monitor.newChild(80)); try { File temporaryProjectFolder = detectProjectEntry(monitor.newChild(10), temporaryFolder); if (projectDirectory.exists()) { IoUtils.deleteQuietly(projectDirectory); } copyContents(monitor.newChild(10), temporaryProjectFolder, projectDirectory); } finally { IoUtils.deleteQuietly(temporaryFolder); } } private File detectProjectEntry(SubMonitor monitor, File temporaryFolder) throws CoreException { monitor.beginTask(Messages.NewProjectWizard_monitorInspectProject, 100); List<File> results = new ArrayList<File>(); detectProjectEntry0(monitor, temporaryFolder, results); if (results.isEmpty()) { throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format( Messages.NewProjectWizard_errorTemplateInvalid, GradleContext.DEFAULT_BUILD_SCRIPT_NAME))); } else { File result = selectProjectEntry(temporaryFolder, results); if (result == null) { throw new CoreException(Status.CANCEL_STATUS); } return result; } } private File selectProjectEntry(File temporaryFolder, List<File> results) { assert results.isEmpty() == false; if (results.size() == 1) { return results.get(0).getParentFile(); } final List<IPath> paths = new ArrayList<IPath>(); IPath base = Path.fromOSString(temporaryFolder.getAbsolutePath()); for (File file : results) { IPath path = Path.fromOSString(file.getAbsolutePath()); paths.add(path.removeFirstSegments(base.segmentCount())); } Collections.sort(paths, new Comparator<IPath>() { @Override public int compare(IPath o1, IPath o2) { return o1.toPortableString().compareToIgnoreCase(o2.toPortableString()); } }); IPath selection = selectProjectEntryByDialog(paths); if (selection == null) { return null; } return base.append(selection).toFile().getParentFile(); } private IPath selectProjectEntryByDialog(final List<IPath> paths) { final AtomicReference<IPath> selectionResult = new AtomicReference<IPath>(); Activator.getDisplay().syncExec(new Runnable() { @Override public void run() { ListDialog dialog = new ListDialog(getShell()) { @Override protected int getTableStyle() { return super.getTableStyle() | SWT.SINGLE; } }; dialog.setTitle(Messages.NewProjectWizard_selectEntryTitle); dialog.setMessage(Messages.NewProjectWizard_selectEntryMessage); dialog.setInitialSelections(new Object[] { paths.get(0) }); dialog.setInput(paths); dialog.setContentProvider(ArrayContentProvider.getInstance()); dialog.setLabelProvider(new ShortenLabelProvider(paths)); if (dialog.open() != Window.OK) { return; } Object[] selected = dialog.getResult(); if (selected.length >= 1) { selectionResult.set((IPath) selected[0]); } } }); return selectionResult.get(); } private void detectProjectEntry0(SubMonitor monitor, File folder, List<File> results) { File file = new File(folder, GradleContext.DEFAULT_BUILD_SCRIPT_NAME); if (file.isFile()) { results.add(file); } else { for (File child : folder.listFiles()) { if (child.isDirectory()) { detectProjectEntry0(monitor, child, results); } } } monitor.worked(1); monitor.setWorkRemaining(100); } private void copyContents(SubMonitor monitor, File from, File to) throws CoreException { monitor.beginTask(Messages.NewProjectWizard_monitorCopyContents, 100); try { IoUtils.move(monitor.newChild(100), from, to); } catch (IOException e) { throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format(Messages.NewProjectWizard_errorProjectFailedToCopyContent, to))); } } private void buildProject(SubMonitor monitor, File projectDirectory, List<String> taskNames, ShafuConsole console) throws CoreException { GradleContext configuration = ShafuUi.createContext(projectDirectory); console.clearConsole(); console.attachTo(configuration); new GradleBuildTask(configuration, taskNames).run(monitor); } private void deployProject(SubMonitor monitor, File projectDirectory, IWorkingSet[] workingSets) throws CoreException { monitor.beginTask(Messages.NewProjectWizard_monitorLoadProject, 100); IProjectDescription description = loadDescription(monitor.newChild(20), projectDirectory); IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(description.getName()); if (project.exists()) { throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format(Messages.NewProjectWizard_errorProjectConflict, project.getName()))); } project.create(description, new SubProgressMonitor(monitor, 20)); project.open(new SubProgressMonitor(monitor, 10)); if (workingSets.length > 0) { currentWorkbench.getWorkingSetManager().addToWorkingSets(project, workingSets); } BasicNewResourceWizard.selectAndReveal(project, currentWorkbench.getActiveWorkbenchWindow()); } private IProjectDescription loadDescription(SubMonitor monitor, File projectDirectory) throws CoreException { monitor.beginTask(Messages.NewProjectWizard_monitorLoadDescription, 1); try { File file = new File(projectDirectory, IProjectDescription.DESCRIPTION_FILE_NAME); IPath path = Path.fromOSString(file.getAbsolutePath()); return ResourcesPlugin.getWorkspace().loadProjectDescription(path); } finally { monitor.done(); } } private enum Stage { INIT, BUILD, DEPLOY, } private static class ShortenLabelProvider extends LabelProvider { private final Map<IPath, IPath> resolved = new HashMap<IPath, IPath>(); public ShortenLabelProvider(List<? extends IPath> paths) { if (paths.size() >= 2) { int min = getMinimumSize(paths); int prefix = getCommonPrefixSize(paths); int suffix = getCommonSuffixSize(paths); if (prefix + suffix < min) { for (IPath path : paths) { resolved.put(path, path.removeFirstSegments(prefix).removeLastSegments(suffix)); } } } } @Override public String getText(Object element) { IPath path = (IPath) element; if (path == null) { return null; } IPath result = resolved.get(path); if (result == null) { result = path; } return result.toPortableString(); } private int getMinimumSize(List<? extends IPath> paths) { int min = paths.get(0).segmentCount(); for (IPath path : paths) { min = Math.min(min, path.segmentCount()); } return min; } private int getCommonPrefixSize(List<? extends IPath> paths) { IPath first = paths.get(0); int commonSize = first.segmentCount(); for (IPath path : paths) { if (commonSize == 0) { break; } commonSize = getCommonPrefixSize(commonSize, first, path); } return commonSize; } private int getCommonSuffixSize(List<? extends IPath> paths) { assert paths.size() >= 2; IPath first = paths.get(0); int commonSize = first.segmentCount(); for (IPath path : paths) { if (commonSize == 0) { break; } commonSize = getCommonSuffixSize(commonSize, first, path); } return commonSize; } private int getCommonPrefixSize(int max, IPath a, IPath b) { int count = Math.min(max, Math.min(a.segmentCount(), b.segmentCount())); if (count == 0) { return 0; } for (int i = 0; i < count; i++) { if (a.segment(i).equals(b.segment(i)) == false) { return i; } } return count; } private int getCommonSuffixSize(int max, IPath a, IPath b) { int count = Math.min(max, Math.min(a.segmentCount(), b.segmentCount())); if (count == 0) { return 0; } for (int i = count - 1; i >= 0; i--) { if (a.segment(i).equals(b.segment(i)) == false) { return i; } } return count; } } private static class Archive implements Closeable { private final File file; private final URL url; private File temporaryFile; public Archive(File file, URL url) { if (file == null && url == null) { throw new IllegalArgumentException(); } this.file = file; this.url = url; } public File extract(SubMonitor monitor) throws CoreException { monitor.beginTask(Messages.NewProjectWizard_monitorExtractArchive, 100); File archive = getFile(monitor.newChild(50)); String name = getFileName(); try { if (name.endsWith(".zip") || name.endsWith(".jar")) { //$NON-NLS-1$ //$NON-NLS-2$ return IoUtils.extractZip(monitor, archive); } else if (name.endsWith(".tar.gz")) { //$NON-NLS-1$ return IoUtils.extractTarGz(monitor, archive); } else { throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.NewProjectWizard_errorArchiveNotSupport)); } } catch (IOException e) { throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.NewProjectWizard_errorArchiveExtract)); } } private String getFileName() { if (file != null) { return file.getName(); } else { String path = url.getPath(); int index = path.lastIndexOf('/'); return path.substring(index + 1); } } private File getFile(final SubMonitor monitor) throws CoreException { if (file != null) { return file; } else if (temporaryFile != null) { return temporaryFile; } try { temporaryFile = File.createTempFile("tmp", ".bin"); //$NON-NLS-1$ //$NON-NLS-2$ final OutputStream output = new FileOutputStream(temporaryFile); try { ShafuNetwork.processContent(url, new IContentProcessor<Void>() { @Override public Void process(InputStream input) throws IOException { byte[] buf = new byte[1024]; while (true) { if (monitor.isCanceled()) { throw new OperationCanceledException(); } int read = input.read(buf); if (read < 0) { break; } monitor.setWorkRemaining(10000); monitor.worked(read); output.write(buf, 0, read); } return null; } }); } finally { output.close(); } return temporaryFile; } catch (OperationCanceledException e) { throw new CoreException(Status.CANCEL_STATUS); } catch (IOException e) { throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format(Messages.NewProjectWizard_errorArchiveDownload, url), e)); } } @Override public void close() { if (temporaryFile != null) { temporaryFile.delete(); } } } }