diff --git a/bundles/.gitignore b/bundles/.gitignore
new file mode 100644
index 00000000000..d14e19fe321
--- /dev/null
+++ b/bundles/.gitignore
@@ -0,0 +1 @@
+*/bin/
\ No newline at end of file
diff --git a/bundles/org.eclipse.tools.templates.freemarker/.classpath b/bundles/org.eclipse.tools.templates.freemarker/.classpath
new file mode 100644
index 00000000000..eca7bdba8f0
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/bundles/org.eclipse.tools.templates.freemarker/.gitignore b/bundles/org.eclipse.tools.templates.freemarker/.gitignore
new file mode 100644
index 00000000000..ae3c1726048
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/.gitignore
@@ -0,0 +1 @@
+/bin/
diff --git a/bundles/org.eclipse.tools.templates.freemarker/.project b/bundles/org.eclipse.tools.templates.freemarker/.project
new file mode 100644
index 00000000000..3d79db07786
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/.project
@@ -0,0 +1,28 @@
+
+
+ org.eclipse.tools.templates.freemarker
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/bundles/org.eclipse.tools.templates.freemarker/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.tools.templates.freemarker/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000000..0c68a61dca8
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/bundles/org.eclipse.tools.templates.freemarker/META-INF/MANIFEST.MF b/bundles/org.eclipse.tools.templates.freemarker/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..1cce829b610
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/META-INF/MANIFEST.MF
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Eclipse Tools
+Bundle-SymbolicName: org.eclipse.tools.templates.freemarker
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.eclipse.tools.templates.freemarker.internal.Activator
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.core.resources,
+ org.freemarker
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ActivationPolicy: lazy
+Export-Package: org.eclipse.tools.templates.freemarker
diff --git a/bundles/org.eclipse.tools.templates.freemarker/about.html b/bundles/org.eclipse.tools.templates.freemarker/about.html
new file mode 100644
index 00000000000..d7c511887d6
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/about.html
@@ -0,0 +1,24 @@
+
+
+About
+
+
+About This Content
+
+June 22, 2007
+License
+
+The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at http://www.eclipse.org/legal/epl-v10.html.
+For purposes of the EPL, "Program" will mean the Content.
+
+If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at http://www.eclipse.org.
+
+
\ No newline at end of file
diff --git a/bundles/org.eclipse.tools.templates.freemarker/build.properties b/bundles/org.eclipse.tools.templates.freemarker/build.properties
new file mode 100644
index 00000000000..17daa5b49ca
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ about.html
diff --git a/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FMGenerator.java b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FMGenerator.java
new file mode 100644
index 00000000000..7646c53481b
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FMGenerator.java
@@ -0,0 +1,210 @@
+package org.eclipse.tools.templates.freemarker;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+
+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.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tools.templates.freemarker.internal.Activator;
+import org.osgi.framework.Bundle;
+
+import freemarker.cache.TemplateLoader;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
+public abstract class FMGenerator implements TemplateLoader {
+
+ private final Configuration templateConfig;
+ private Bundle bundle;
+ private String manifestPath;
+ private TemplateManifest manifest;
+ private List filesToOpen = new ArrayList<>();
+
+ protected FMGenerator() {
+ templateConfig = new Configuration(Configuration.VERSION_2_3_22);
+ templateConfig.setTemplateLoader(this);
+ }
+
+ public void setBundle(Bundle bundle) {
+ this.bundle = bundle;
+ }
+
+ public void setTemplateManifestPath(String manifestPath) {
+ this.manifestPath = manifestPath;
+ }
+
+ public TemplateManifest getManifest() {
+ return manifest;
+ }
+
+ protected Class extends TemplateManifest> getManifestClass() {
+ return TemplateManifest.class;
+ }
+
+ public void generate(Map model, IProgressMonitor monitor) throws CoreException {
+ manifest = null;
+ try {
+ // load manifest file
+ StringWriter writer = new StringWriter();
+ loadFile(manifestPath, model, writer); // $NON-NLS-1$
+ JAXBContext xmlContext = JAXBContext.newInstance(getManifestClass());
+ Unmarshaller unmarshaller = xmlContext.createUnmarshaller();
+ manifest = (TemplateManifest) unmarshaller.unmarshal(new StringReader(writer.toString()));
+ } catch (JAXBException e) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.getId(), "Loading template manifest", e));
+ }
+
+ // generate files
+ if (manifest != null) {
+ IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+ IFile fileToShow = null;
+ for (FileTemplate fileTemplate : manifest.getFiles()) {
+ IPath destPath = new Path(fileTemplate.getDest());
+ IProject project = root.getProject(destPath.segment(0));
+ IFile file = project.getFile(destPath.removeFirstSegments(1));
+ if (!fileTemplate.isCopy()) {
+ generateFile(fileTemplate.getSrc(), model, file, monitor);
+ } else {
+ try {
+ URL url = FileLocator.find(bundle, new Path(fileTemplate.getSrc()), null);
+ try (InputStream in = url.openStream()) {
+ createParent(file, monitor);
+ if (file.exists()) {
+ file.setContents(in, true, true, monitor);
+ } else {
+ file.create(in, true, monitor);
+ }
+ }
+ } catch (IOException e) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.getId(),
+ "Reading file " + fileTemplate.getSrc(), e));
+ }
+ }
+
+ if (fileTemplate.isOpen()) {
+ if (fileTemplate.isShow()) {
+ if (fileToShow != null) {
+ filesToOpen.add(fileToShow);
+ }
+ fileToShow = file;
+ } else {
+ filesToOpen.add(file);
+ }
+ }
+ }
+
+ if (fileToShow != null) {
+ filesToOpen.add(fileToShow);
+ }
+ }
+ }
+
+ public void loadFile(String templateFile, Object model, Writer out) throws CoreException {
+ try {
+ Template template = templateConfig.getTemplate(templateFile);
+ template.process(model, out);
+ } catch (IOException | TemplateException e) {
+ throw new CoreException(
+ new Status(IStatus.ERROR, Activator.getId(), "Processing template " + templateFile, e));
+ }
+ }
+
+ public void generateFile(String templateFile, final Object model, final IFile outputFile, IProgressMonitor monitor)
+ throws CoreException {
+ try (StringWriter writer = new StringWriter()) {
+ loadFile(templateFile, model, writer);
+ try (ByteArrayInputStream in = new ByteArrayInputStream(
+ writer.getBuffer().toString().getBytes(StandardCharsets.UTF_8))) {
+ createParent(outputFile, monitor);
+ if (outputFile.exists()) {
+ outputFile.setContents(in, true, true, monitor);
+ } else {
+ outputFile.create(in, true, monitor);
+ }
+ }
+ } catch (IOException e) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.getId(), "Generating file " + templateFile, e));
+ }
+ }
+
+ public static void createParent(IResource child, IProgressMonitor monitor) throws CoreException {
+ if (child == null)
+ return;
+
+ IContainer container = child.getParent();
+ if (container.exists()) {
+ return;
+ }
+
+ IFolder parent = container.getAdapter(IFolder.class);
+ createParent(parent, monitor);
+ parent.create(true, true, monitor);
+ }
+
+ public IFile[] getFilesToOpen() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Object findTemplateSource(String name) throws IOException {
+ return FileLocator.find(bundle, new Path(name), null);
+ }
+
+ @Override
+ public long getLastModified(Object source) {
+ try {
+ URL url = (URL) source;
+ if (url.getProtocol().equals("file")) { //$NON-NLS-1$
+ File file = new File(url.toURI());
+ return file.lastModified();
+ } else {
+ return 0;
+ }
+ } catch (URISyntaxException e) {
+ return 0;
+ }
+ }
+
+ @Override
+ public Reader getReader(Object source, String encoding) throws IOException {
+ URL url = (URL) source;
+ return new InputStreamReader(url.openStream(), encoding);
+ }
+
+ @Override
+ public void closeTemplateSource(Object arg0) throws IOException {
+ // Nothing
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FMProjectGenerator.java b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FMProjectGenerator.java
new file mode 100644
index 00000000000..e266af42869
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FMProjectGenerator.java
@@ -0,0 +1,70 @@
+package org.eclipse.tools.templates.freemarker;
+
+import java.net.URI;
+import java.util.Map;
+
+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;
+
+public abstract class FMProjectGenerator extends FMGenerator {
+
+ private String projectName;
+ private URI locationURI;
+ private IProject[] referencedProjects;
+
+ private IProject project;
+
+ protected abstract String[] getProjectNatures();
+
+ public void setProjectName(String projectName) {
+ this.projectName = projectName;
+ }
+
+ public void setLocationURI(URI locationURI) {
+ this.locationURI = locationURI;
+ }
+
+ public void setReferencedProjects(IProject[] referencedProjects) {
+ this.referencedProjects = referencedProjects;
+ }
+
+ public IProject getProject() {
+ return project;
+ }
+
+ @Override
+ public void generate(Map model, IProgressMonitor monitor) throws CoreException {
+ // Make sure project name is in model
+ model.put("projectName", projectName); //$NON-NLS-1$
+
+ // Create the project
+ createProject(monitor);
+
+ // Generate the files
+ super.generate(model, monitor);
+ }
+
+ protected void createProject(IProgressMonitor monitor) throws CoreException {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+ project = workspace.getRoot().getProject(projectName);
+ if (!project.exists()) {
+ IProjectDescription description = workspace.newProjectDescription(projectName);
+ description.setLocationURI(locationURI);
+ if (referencedProjects != null) {
+ description.setReferencedProjects(referencedProjects);
+ }
+ description.setNatureIds(getProjectNatures());
+ project.create(description, monitor);
+ project.open(monitor);
+ } else {
+ // TODO make sure it's got all our settings or is this an error
+ // condition?
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FileTemplate.java b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FileTemplate.java
new file mode 100644
index 00000000000..aa1f7082ef1
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FileTemplate.java
@@ -0,0 +1,57 @@
+package org.eclipse.tools.templates.freemarker;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+public class FileTemplate {
+ private String src;
+ private String dest;
+ private boolean open;
+ private boolean show;
+ private boolean copy;
+
+ @XmlAttribute(name = "src")
+ public String getSrc() {
+ return src;
+ }
+
+ public void setSrc(String src) {
+ this.src = src;
+ }
+
+ @XmlAttribute(name = "dest")
+ public String getDest() {
+ return dest;
+ }
+
+ public void setDest(String dest) {
+ this.dest = dest;
+ }
+
+ @XmlAttribute(name = "open")
+ public boolean isOpen() {
+ return open;
+ }
+
+ public void setOpen(boolean open) {
+ this.open = open;
+ }
+
+ @XmlAttribute(name = "show")
+ public boolean isShow() {
+ return show;
+ }
+
+ public void setShow(boolean show) {
+ this.show = show;
+ }
+
+ @XmlAttribute(name = "copy")
+ public boolean isCopy() {
+ return copy;
+ }
+
+ public void setCopy(boolean copy) {
+ this.copy = copy;
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/SourceRoot.java b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/SourceRoot.java
new file mode 100644
index 00000000000..328a6fd098a
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/SourceRoot.java
@@ -0,0 +1,18 @@
+package org.eclipse.tools.templates.freemarker;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+public class SourceRoot {
+
+ private String dir;
+
+ @XmlAttribute(name = "dir")
+ public String getDir() {
+ return dir;
+ }
+
+ public void setDir(String dir) {
+ this.dir = dir;
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/TemplateManifest.java b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/TemplateManifest.java
new file mode 100644
index 00000000000..32b02a829b3
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/TemplateManifest.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2016 QNX Software Systems 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
+ *******************************************************************************/
+package org.eclipse.tools.templates.freemarker;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class TemplateManifest {
+
+ private List files;
+ private List srcRoots;
+
+ @XmlElement(name = "file")
+ public List getFiles() {
+ return files;
+ }
+
+ public void setFiles(List files) {
+ this.files = files;
+ }
+
+ @XmlElement(name = "srcRoot")
+ public List getSrcRoots() {
+ return srcRoots;
+ }
+
+ public void setSrcRoots(List srcRoots) {
+ this.srcRoots = srcRoots;
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/internal/Activator.java b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/internal/Activator.java
new file mode 100644
index 00000000000..b1f9c3eac31
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/internal/Activator.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2016 QNX Software Systems 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
+ *******************************************************************************/
+package org.eclipse.tools.templates.freemarker.internal;
+
+import org.eclipse.core.runtime.Plugin;
+import org.osgi.framework.BundleContext;
+
+public class Activator extends Plugin {
+
+ private static Activator plugin;
+
+ @Override
+ public void start(BundleContext bundleContext) throws Exception {
+ super.start(bundleContext);
+ plugin = this;
+ }
+
+ @Override
+ public void stop(BundleContext bundleContext) throws Exception {
+ super.stop(bundleContext);
+ plugin = null;
+ }
+
+ public static String getId() {
+ return plugin.getBundle().getSymbolicName();
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.ui/.classpath b/bundles/org.eclipse.tools.templates.ui/.classpath
new file mode 100644
index 00000000000..eca7bdba8f0
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/bundles/org.eclipse.tools.templates.ui/.project b/bundles/org.eclipse.tools.templates.ui/.project
new file mode 100644
index 00000000000..f4f52f49bf6
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/.project
@@ -0,0 +1,28 @@
+
+
+ org.eclipse.tools.templates.ui
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/bundles/org.eclipse.tools.templates.ui/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.tools.templates.ui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000000..0c68a61dca8
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/bundles/org.eclipse.tools.templates.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.tools.templates.ui/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..75b48580da4
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,13 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Ui
+Bundle-SymbolicName: org.eclipse.tools.templates.ui;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.eclipse.tools.templates.ui.internal.Activator
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.ui.ide,
+ org.eclipse.core.resources
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ActivationPolicy: lazy
+Export-Package: org.eclipse.tools.templates.ui
diff --git a/bundles/org.eclipse.tools.templates.ui/about.html b/bundles/org.eclipse.tools.templates.ui/about.html
new file mode 100644
index 00000000000..d7c511887d6
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/about.html
@@ -0,0 +1,24 @@
+
+
+About
+
+
+About This Content
+
+June 22, 2007
+License
+
+The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at http://www.eclipse.org/legal/epl-v10.html.
+For purposes of the EPL, "Program" will mean the Content.
+
+If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at http://www.eclipse.org.
+
+
\ No newline at end of file
diff --git a/bundles/org.eclipse.tools.templates.ui/build.properties b/bundles/org.eclipse.tools.templates.ui/build.properties
new file mode 100644
index 00000000000..bdcc25a2886
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ about.html,\
+ plugin.xml
diff --git a/bundles/org.eclipse.tools.templates.ui/plugin.xml b/bundles/org.eclipse.tools.templates.ui/plugin.xml
new file mode 100644
index 00000000000..009fdc7a36b
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/plugin.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/bundles/org.eclipse.tools.templates.ui/schema/templates.exsd b/bundles/org.eclipse.tools.templates.ui/schema/templates.exsd
new file mode 100644
index 00000000000..bc058d85e72
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/schema/templates.exsd
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+
+
+ [Enter description of this extension point.]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [Enter the first release in which this extension point appears.]
+
+
+
+
+
+
+
+
+ [Enter extension point usage example here.]
+
+
+
+
+
+
+
+
+ [Enter API information here.]
+
+
+
+
+
+
+
+
+ [Enter information about supplied implementation of this extension point.]
+
+
+
+
+
diff --git a/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/TemplateSelectionPage.java b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/TemplateSelectionPage.java
new file mode 100644
index 00000000000..df7bad087c7
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/TemplateSelectionPage.java
@@ -0,0 +1,103 @@
+package org.eclipse.tools.templates.ui;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tools.templates.ui.internal.Activator;
+import org.eclipse.tools.templates.ui.internal.Tag;
+import org.eclipse.tools.templates.ui.internal.TagListViewer;
+import org.eclipse.tools.templates.ui.internal.Template;
+import org.eclipse.tools.templates.ui.internal.TemplateExtension;
+import org.eclipse.tools.templates.ui.internal.TemplateTable;
+import org.eclipse.ui.IWorkbenchWizard;
+
+public class TemplateSelectionPage extends WizardPage {
+
+ private final String[] requestedTags;
+
+ private ListViewer tagList;
+ private TemplateTable templateTable;
+
+ public TemplateSelectionPage(String pageName, String... tags) {
+ super(pageName);
+ this.requestedTags = tags;
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ SashForm form = new SashForm(parent, SWT.HORIZONTAL);
+ setControl(form);
+
+ tagList = new TagListViewer(form, SWT.BORDER);
+ tagList.addSelectionChangedListener(new ISelectionChangedListener() {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ }
+ });
+
+ templateTable = new TemplateTable(form, SWT.V_SCROLL | SWT.SINGLE | SWT.BORDER);
+ templateTable.getTable().addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setPageComplete(templateTable.getSelectedTemplate() != null);
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ getContainer().showPage(getNextPage());
+ }
+ });
+
+ TemplateExtension templateExtension = Activator.getTemplateExtension();
+ List templates = new ArrayList<>();
+ for (Template template : templateExtension.getTemplates()) {
+ for (String requestedTag : requestedTags) {
+ if (template.hasTag(requestedTag)) {
+ templates.add(template);
+ break;
+ }
+ }
+ }
+
+ Set tags = new HashSet<>();
+ for (Template template : templates) {
+ tags.addAll(template.getTags());
+ }
+
+ templateTable.setTemplates(templates);
+ tagList.setInput(tags);
+ tagList.getList().select(0);
+
+ form.setWeights(new int[] { 20, 80 });
+ }
+
+ @Override
+ public IWizardPage getNextPage() {
+ Template template = templateTable.getSelectedTemplate();
+ if (template != null) {
+ try {
+ IWorkbenchWizard nextWizard = template.getWizard();
+ nextWizard.addPages();
+ return nextWizard.getPages()[0];
+ } catch (CoreException e) {
+ Activator.log(e);
+ }
+ }
+ return super.getNextPage();
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/Activator.java b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/Activator.java
new file mode 100644
index 00000000000..9d8b3bcd7d7
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/Activator.java
@@ -0,0 +1,48 @@
+package org.eclipse.tools.templates.ui.internal;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+ private static Activator plugin;
+
+ private TemplateExtension templateExtension;
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+
+ templateExtension = new TemplateExtension();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ public static String getId() {
+ return plugin.getBundle().getSymbolicName();
+ }
+
+ public static TemplateExtension getTemplateExtension() {
+ return plugin.templateExtension;
+ }
+
+ public static void log(Exception e) {
+ if (e instanceof CoreException) {
+ plugin.getLog().log(((CoreException) e).getStatus());
+ } else {
+ plugin.getLog().log(new Status(IStatus.ERROR, getId(), e.getLocalizedMessage(), e));
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/Tag.java b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/Tag.java
new file mode 100644
index 00000000000..ddd864dbf25
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/Tag.java
@@ -0,0 +1,44 @@
+package org.eclipse.tools.templates.ui.internal;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+
+public class Tag {
+
+ public static final String ALL_ID = "all"; //$NON-NLS-1$
+
+ public String id;
+ public String label;
+
+ public Tag(IConfigurationElement element) {
+ id = element.getAttribute("id"); //$NON-NLS-1$
+ label = element.getAttribute("label"); //$NON-NLS-1$
+ }
+
+ public Tag(String id, String label) {
+ this.id = id;
+ this.label = label;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Tag) {
+ return id.equals(((Tag) obj).id);
+ } else {
+ return false;
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/TagListViewer.java b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/TagListViewer.java
new file mode 100644
index 00000000000..f757fb4e967
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/TagListViewer.java
@@ -0,0 +1,65 @@
+package org.eclipse.tools.templates.ui.internal;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.widgets.Composite;
+
+public class TagListViewer extends ListViewer {
+
+ public TagListViewer(Composite parent, int style) {
+ super(parent, style);
+
+ setLabelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof Tag) {
+ return ((Tag) element).getLabel();
+ } else {
+ return super.getText(element);
+ }
+ }
+ });
+
+ setContentProvider(new IStructuredContentProvider() {
+ private Tag[] tags;
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ if (newInput != null) {
+ @SuppressWarnings("unchecked")
+ Collection tagsList = (Collection) newInput;
+ tags = tagsList.toArray(new Tag[tagsList.size()]);
+ Arrays.sort(tags, new Comparator() {
+ @Override
+ public int compare(Tag o1, Tag o2) {
+ // Keep all at the top
+ if (o1.getId().equals(Tag.ALL_ID)) {
+ return -1;
+ }
+ if (o2.getId().equals(Tag.ALL_ID)) {
+ return 1;
+ }
+ return o1.getLabel().compareTo(o2.getLabel());
+ }
+ });
+ }
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ return tags;
+ }
+ });
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/Template.java b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/Template.java
new file mode 100644
index 00000000000..e6f490d0f3d
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/Template.java
@@ -0,0 +1,73 @@
+package org.eclipse.tools.templates.ui.internal;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.IWorkbenchWizard;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+public class Template {
+
+ private final TemplateExtension parent;
+ private final IConfigurationElement element;
+ private Map tags;
+
+ public Template(TemplateExtension parent, IConfigurationElement element) {
+ this.parent = parent;
+ this.element = element;
+ }
+
+ public String getId() {
+ return element.getAttribute("id"); //$NON-NLS-1$
+ }
+
+ public String getLabel() {
+ return element.getAttribute("label"); //$NON-NLS-1$
+ }
+
+ public String getDescription() {
+ IConfigurationElement[] descs = element.getChildren("description"); //$NON-NLS-1$
+ return descs.length > 0 ? descs[0].getValue() : null;
+ }
+
+ public ImageDescriptor getIcon() {
+ String iconPath = element.getAttribute("icon"); //$NON-NLS-1$
+ return AbstractUIPlugin.imageDescriptorFromPlugin(element.getNamespaceIdentifier(), iconPath);
+ }
+
+ private void initTags() {
+ if (tags == null) {
+ tags = new HashMap<>();
+ for (IConfigurationElement ref : element.getChildren("tagReference")) { //$NON-NLS-1$
+ String id = ref.getAttribute("id"); //$NON-NLS-1$
+ Tag tag = parent.getTag(id);
+ if (tag != null) {
+ tags.put(tag.getId(), tag);
+ }
+ }
+ }
+ }
+
+ public void addTag(Tag tag) {
+ initTags();
+ tags.put(tag.getId(), tag);
+ }
+
+ public boolean hasTag(String tagId) {
+ initTags();
+ return tags.containsKey(tagId);
+ }
+
+ public Collection getTags() {
+ return tags.values();
+ }
+
+ public IWorkbenchWizard getWizard() throws CoreException {
+ return (IWorkbenchWizard) element.createExecutableExtension("wizard"); //$NON-NLS-1$
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/TemplateExtension.java b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/TemplateExtension.java
new file mode 100644
index 00000000000..82df102eb33
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/TemplateExtension.java
@@ -0,0 +1,75 @@
+package org.eclipse.tools.templates.ui.internal;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+
+public class TemplateExtension {
+
+ private Map templates;
+ private Map tags;
+
+ private void init() {
+ if (templates != null)
+ return;
+
+ templates = new HashMap<>();
+ tags = new HashMap<>();
+
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtensionPoint point = registry.getExtensionPoint(Activator.getId(), "templates"); //$NON-NLS-1$
+
+ // tags
+ Tag allTag = new Tag(Tag.ALL_ID, "All");
+ tags.put(allTag.getId(), allTag);
+
+ for (IConfigurationElement element : point.getConfigurationElements()) {
+ if (element.getName().equals("tag")) { //$NON-NLS-1$
+ Tag tag = new Tag(element);
+ tags.put(tag.getId(), tag);
+ }
+ }
+
+ // templates
+ for (IConfigurationElement element : point.getConfigurationElements()) {
+ if (element.getName().equals("template")) { //$NON-NLS-1$
+ Template template = new Template(this, element);
+ templates.put(template.getId(), template);
+ template.addTag(allTag);
+ }
+ }
+
+ // template extensions
+ for (IConfigurationElement element : point.getConfigurationElements()) {
+ if (element.getName().equals("templateExtension")) { //$NON-NLS-1$
+ String templateId = element.getAttribute("templateId"); //$NON-NLS-1$
+ Template template = templates.get(templateId);
+ if (template != null) {
+ for (IConfigurationElement tagRef : element.getChildren("tagReference")) { //$NON-NLS-1$
+ String tagId = tagRef.getAttribute("id"); //$NON-NLS-1$
+ Tag tag = tags.get(tagId);
+ if (tag != null) {
+ template.addTag(tag);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public Collection getTemplates() {
+ init();
+ return templates.values();
+ }
+
+ public Tag getTag(String id) {
+ init();
+ return tags.get(id);
+ }
+
+}
diff --git a/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/TemplateTable.java b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/TemplateTable.java
new file mode 100644
index 00000000000..91df3e6cf35
--- /dev/null
+++ b/bundles/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/internal/TemplateTable.java
@@ -0,0 +1,148 @@
+package org.eclipse.tools.templates.ui.internal;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Widget;
+
+public class TemplateTable implements Listener {
+
+ private final Table table;
+ private Font fontBold;
+ private Font fontDefault;
+ private Map images = new HashMap<>();
+
+ private static final Rectangle EMPTY_IMAGE_BOUNDS = new Rectangle(0, 0, 48, 48);
+
+ public TemplateTable(Composite parent, int style) {
+ table = new Table(parent, style);
+ table.addListener(SWT.MeasureItem, this);
+ table.addListener(SWT.EraseItem, this);
+ table.addListener(SWT.PaintItem, this);
+ table.addListener(SWT.Dispose, this);
+ }
+
+ public Table getTable() {
+ return table;
+ }
+
+ public void setTemplates(Collection templates) {
+ table.clearAll();
+ for (Template template : templates) {
+ TableItem item = new TableItem(table, SWT.NONE);
+ item.setData(template);
+ }
+ }
+
+ public Template getSelectedTemplate() {
+ TableItem[] items = table.getSelection();
+ if (items.length > 0) {
+ return (Template) items[0].getData();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ case SWT.MeasureItem:
+ computeItemArea(event);
+ break;
+ case SWT.PaintItem:
+ paintItem(event);
+ break;
+ case SWT.EraseItem:
+ computeItemArea(event);
+ break;
+ case SWT.Dispose:
+ for (Image image : images.values()) {
+ image.dispose();
+ }
+ break;
+ }
+ }
+
+ private void computeItemArea(Event event) {
+ event.width = table.getClientArea().width - event.x;
+ event.height = 56; // 48 for icon, 8 buffer
+ }
+
+ private void paintItem(Event event) {
+ Widget w = event.item;
+ GC gc = event.gc;
+ if (fontDefault == null) {
+ Font font = gc.getFont();
+ FontData[] data = font.getFontData();
+ if (data.length > 0) {
+ FontData d = data[0];
+ FontData normal = new FontData(d.getName(), d.getHeight(), // Math.round(d.getHeight()
+ // *
+ // .85F),
+ d.getStyle() | SWT.ITALIC);
+ fontDefault = new Font(event.display, normal);
+ FontData bold = new FontData(d.getName(), Math.round(d.getHeight() * 1.15F), d.getStyle() | SWT.BOLD);
+ fontBold = new Font(event.display, bold);
+ }
+ }
+ if (w instanceof TableItem) {
+ TableItem item = (TableItem) w;
+ Template template = (Template) item.getData();
+ ImageDescriptor imageDesc = template.getIcon();
+ Image image = images.get(imageDesc);
+ if (image == null) {
+ image = imageDesc.createImage();
+ images.put(imageDesc, image);
+ }
+ Rectangle rect = EMPTY_IMAGE_BOUNDS;
+ int y = 0;
+ if (image != null) {
+ rect = image.getBounds();
+ y = event.y + Math.max(0, (event.height - rect.height) / 2);
+ gc.drawImage(image, event.x + 4, y);
+ }
+ gc.setFont(fontBold);
+ String name = template.getLabel();
+ Point nameExtent = gc.textExtent(name, SWT.DRAW_TRANSPARENT);
+ int iconMargin = 10;
+ gc.drawText(name, rect.x + rect.width + iconMargin, y, SWT.DRAW_TRANSPARENT);
+ gc.setFont(fontDefault);
+ int flags = SWT.DRAW_TRANSPARENT | SWT.DRAW_DELIMITER;
+ String description = template.getDescription();
+ int width = table.getClientArea().width;
+ if (description != null) {
+ Point descExt = gc.textExtent(description, flags);
+ int descWidth = width - (rect.x + rect.width + iconMargin);
+ if (descExt.x > descWidth) {
+ FontMetrics fm = gc.getFontMetrics();
+ int averageCharWidth = fm.getAverageCharWidth();
+ int charsMaxNumberInRow = descWidth / averageCharWidth;
+ if (description.length() > charsMaxNumberInRow) {
+ String firstLine = description.substring(0, charsMaxNumberInRow);
+ int lastWS = firstLine.lastIndexOf(' ');
+ int endIndex = lastWS + 1 + charsMaxNumberInRow;
+ description = firstLine.substring(0, lastWS) + '\n'
+ + description.substring(lastWS + 1, Math.min(endIndex, description.length()));
+ }
+ }
+ gc.drawText(description, rect.x + rect.width + iconMargin, y + nameExtent.y + 2, flags);
+ }
+ }
+ }
+
+}