Bug 438591 - [tooling] Add option to generate lifecycle class when
creating application

Change-Id: Ieac47eacf4ac35c9ec4a557f435d7acd1fb911b0
Signed-off-by: Steven Spungin <steven@spungin.tv>
diff --git a/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/E4NewProjectWizard.java b/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/E4NewProjectWizard.java
index 99e0362..256a3da 100644
--- a/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/E4NewProjectWizard.java
+++ b/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/E4NewProjectWizard.java
@@ -11,6 +11,7 @@
  *     Sopot Cela - ongoing enhancements
  *     Lars Vogel - ongoing enhancements
  *     Wim Jongman - ongoing enhancements
+ *     Steven Spungin - ongoing enhancements, Bug 438591
  *******************************************************************************/
 package org.eclipse.e4.internal.tools.wizards.project;
 
@@ -292,6 +293,15 @@
 						cssValue);
 			}
 
+			String lifeCycleValue = map
+					.get(NewApplicationWizardPage.APPLICATION_LIFECYCLE_PROPERTY);
+			if (lifeCycleValue != null) {
+				lifeCycleValue = "bundleclass://" + fPluginData.getId() + "/"
+						+ fPluginData.getId().toLowerCase() + "." + lifeCycleValue;
+				map.put(NewApplicationWizardPage.APPLICATION_LIFECYCLE_PROPERTY,
+						lifeCycleValue);
+			}
+
 			extension.setPoint("org.eclipse.core.runtime.products");
 			extension.setId("product");
 			IPluginElement productElement = fmodel.getFactory().createElement(
@@ -363,9 +373,20 @@
 		// If the project has invalid characters, the plug-in name would replace
 		// them with underscores, product name does the same
 		String pluginName = fPluginData.getId();
-
-		// If there's no Activator created we create default package
-		if (!fPluginData.doGenerateClass()) {
+		
+		// BEGIN Generate E4Lifecycle class with annotations
+		String classname = fPluginData.getId() + "." + map.get(NewApplicationWizardPage.APPLICATION_LIFECYCLE_PROPERTY);
+		LifeCycleClassCodeGenerator fGenerator = new LifeCycleClassCodeGenerator(project, classname, fPluginData, false, getContainer());
+		try {
+			fGenerator.generate(new NullProgressMonitor());
+		} catch (CoreException e2) {
+			e2.printStackTrace();
+		}
+		boolean lifeCycleCreated = true;
+		// END Generate E4Lifecycle class with annotations
+		
+		// If there's no Activator or LifeCycle created we create default package
+		if (!fPluginData.doGenerateClass() && !lifeCycleCreated) {
 			String packageName = fPluginData.getId();
 			IPath path = new Path(packageName.replace('.', '/'));
 			if (fPluginData.getSourceFolderName().trim().length() > 0)
diff --git a/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/LifeCycleClassCodeGenerator.java b/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/LifeCycleClassCodeGenerator.java
new file mode 100644
index 0000000..aed4af2
--- /dev/null
+++ b/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/LifeCycleClassCodeGenerator.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2014 TwelveTone LLC and others.
+ * 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:
+ *     Steven Spungin <steven@spungin.tv> - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.e4.internal.tools.wizards.project;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.pde.internal.core.util.CoreUtility;
+import org.eclipse.pde.internal.ui.PDEPlugin;
+import org.eclipse.pde.internal.ui.wizards.plugin.PluginFieldData;
+import org.osgi.framework.FrameworkUtil;
+
+public class LifeCycleClassCodeGenerator {
+	private PluginFieldData fPluginData;
+	private IProject fProject;
+	private String fQualifiedClassName;
+	private boolean fGenerateTemplate;
+	private IWizardContainer fWizardContainer;
+
+	public LifeCycleClassCodeGenerator(IProject project, String qualifiedClassName, PluginFieldData data, boolean generateTemplate, IWizardContainer wizardContainer) {
+		fProject = project;
+		fQualifiedClassName = qualifiedClassName;
+		fPluginData = data;
+		fGenerateTemplate = generateTemplate;
+		fWizardContainer = wizardContainer;
+	}
+
+	public IFile generate(IProgressMonitor monitor) throws CoreException {
+		int nameloc = fQualifiedClassName.lastIndexOf('.');
+		String packageName = (nameloc == -1) ? "" : fQualifiedClassName.substring(0, nameloc); //$NON-NLS-1$
+		//CoreUtility.createFolder was throwing exception if Activator was already created with lower case package and this method called it with different case.
+		packageName = packageName.toLowerCase();
+		String className = fQualifiedClassName.substring(nameloc + 1);
+
+		IPath path = new Path(packageName.replace('.', '/'));
+		if (fPluginData.getSourceFolderName().trim().length() > 0) {
+			path = new Path(fPluginData.getSourceFolderName()).append(path);
+		}
+
+		CoreUtility.createFolder(fProject.getFolder(path));
+
+		IFile file = fProject.getFile(path.append(className + ".java")); //$NON-NLS-1$
+		StringWriter swriter = new StringWriter();
+		PrintWriter writer = new PrintWriter(swriter);
+		generateClass(packageName, className, swriter);
+		writer.flush();
+
+		try {
+			swriter.close();
+			ByteArrayInputStream stream = new ByteArrayInputStream(swriter.toString().getBytes(fProject.getDefaultCharset()));
+			if (file.exists())
+				file.setContents(stream, false, true, monitor);
+			else
+				file.create(stream, false, monitor);
+			stream.close();
+		} catch (Exception e) {
+
+		}
+		return file;
+	}
+
+	private void generateClass(String packageName, String className, StringWriter writer) {
+
+		Map<String, String> map = new HashMap<String, String>();
+		if (packageName.equals("")) {
+			map.put("PACKAGE_DECLARATION", "");
+		} else {
+			map.put("PACKAGE_DECLARATION", "package " + packageName + ";");
+		}
+		map.put("PACKAGE_NAME", packageName);
+		map.put("CLASS_NAME", className);
+
+		try {
+			String template = getResourceAsString(this.getClass(), "/templates/E4LifeCycle.java");
+			template = SimpleTemplate.process(template, map);
+			writer.write(template);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+
+	}
+
+	public static String getResourceAsString(Class<?> classInBundle, String templatePath) throws FileNotFoundException, IOException {
+		URL url = FrameworkUtil.getBundle(classInBundle).getResource(templatePath);
+		return readTextFile(url.openStream());
+	}
+
+	public static String readTextFile(InputStream stream) throws IOException, FileNotFoundException {
+		StringBuilder sb = new StringBuilder();
+		BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+
+		char[] chars = new char[1024];
+		int numRead = 0;
+		while ((numRead = reader.read(chars)) > -1) {
+			sb.append(String.copyValueOf(chars, 0, numRead));
+		}
+
+		reader.close();
+		return sb.toString();
+	}
+}
diff --git a/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/NewApplicationWizardPage.java b/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/NewApplicationWizardPage.java
index a248d91..1faac65 100644
--- a/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/NewApplicationWizardPage.java
+++ b/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/NewApplicationWizardPage.java
@@ -51,6 +51,7 @@
 public class NewApplicationWizardPage extends WizardPage {
 	public static final String E4_APPLICATION = "org.eclipse.e4.ui.workbench.swt.E4Application";
 	public static final String APPLICATION_CSS_PROPERTY = "applicationCSS";
+	public static final String APPLICATION_LIFECYCLE_PROPERTY = "lifeCycleURI";
 	public static final String PRODUCT_NAME = "productName";
 	public static final String APPLICATION = "application";
 	public static final String CLEAR_PERSISTED_STATE = "clearPersistedState";
@@ -424,6 +425,8 @@
 			PROPERTIES = new PropertyData[] {
 					new PropertyData(APPLICATION_CSS_PROPERTY, "CSS Style:",
 							"css/default.css", String.class, true),
+					new PropertyData(APPLICATION_LIFECYCLE_PROPERTY, "LifeCycle Class Name:",
+							"E4LifeCycle", String.class, true),
 					new PropertyData(
 							IProductConstants.PREFERENCE_CUSTOMIZATION,
 							"Preference Customization:", "", String.class, true),
diff --git a/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/SimpleTemplate.java b/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/SimpleTemplate.java
new file mode 100644
index 0000000..d13efba
--- /dev/null
+++ b/bundles/org.eclipse.e4.tools/src/org/eclipse/e4/internal/tools/wizards/project/SimpleTemplate.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2014 TwelveTone LLC and others.
+ * 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:
+ *     Steven Spungin <steven@spungin.tv> - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.internal.tools.wizards.project;
+
+import java.util.Map;
+
+/**
+ * A <em>very</em> basic template processor.<br />
+ * Any value between pairs of @@ will be substituted by the value in a map.<br />
+ * Any values not in the map will be replaced by an empty string.
+ * <br /> <br />
+ * Behavior is undefined if keys or values contain the @@ marker.
+ * @author Steven Spungin
+ *
+ */
+public class SimpleTemplate {
+
+	public static String process(String source, Map<String, String> substitutionMap) {
+		for (String key : substitutionMap.keySet()) {
+			source = source.replaceAll("@@" + key + "@@", substitutionMap.get(key));
+		}
+		source = source.replaceAll("@@.*?@@", "");
+		return source;
+	}
+}
diff --git a/bundles/org.eclipse.e4.tools/templates/E4LifeCycle.java b/bundles/org.eclipse.e4.tools/templates/E4LifeCycle.java
new file mode 100644
index 0000000..eb63e04
--- /dev/null
+++ b/bundles/org.eclipse.e4.tools/templates/E4LifeCycle.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2014 TwelveTone LLC and others.
+ * 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:
+ *     Steven Spungin <steven@spungin.tv> - initial API and implementation
+ *******************************************************************************/
+@@PACKAGE_DECLARATION@@
+
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate;
+import org.eclipse.e4.ui.workbench.lifecycle.PreSave;
+import org.eclipse.e4.ui.workbench.lifecycle.ProcessAdditions;
+import org.eclipse.e4.ui.workbench.lifecycle.ProcessRemovals;
+
+/**
+ * This is a stub implementation containing e4 LifeCycle annotated methods.<br />
+ * There is a corresponding entry in <em>plugin.xml</em> (under the
+ * <em>org.eclipse.core.runtime.products' extension point</em>) that references
+ * this class.
+ **/
+@SuppressWarnings("restriction")
+public class @@CLASS_NAME@@ {
+
+	@PostContextCreate
+	void postContextCreate(IEclipseContext workbenchContext) {
+	}
+
+	@PreSave
+	void preSave(IEclipseContext workbenchContext) {
+	}
+
+	@ProcessAdditions
+	void processAdditions(IEclipseContext workbenchContext) {
+	}
+
+	@ProcessRemovals
+	void processRemovals(IEclipseContext workbenchContext) {
+	}
+}