package org.objectstyle.wolips.templateengine; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IProgressMonitor; import org.objectstyle.wolips.baseforplugins.util.FileUtilities; import org.objectstyle.wolips.core.resources.internal.types.project.ProjectPatternsets; import org.objectstyle.wolips.core.resources.types.project.ProjectAdapter; import org.objectstyle.wolips.variables.BuildProperties; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * ProjectTemplate models a single external project template folder along with * its template metadata. * * @author mschrag */ public class ProjectTemplate implements Comparable<ProjectTemplate> { public static final String PROJECT_TEMPLATES = "Project Templates"; private File _folder; private Document _metadata; private String _name; private List<ProjectInput> _inputs; /** * Constructs a new ProjectTemplate * * @param folder * the location of the template foldere * @param metadata * the metadata XML document for the template */ public ProjectTemplate(File folder, Document metadata) { _folder = folder; _metadata = metadata; } /** * Returns the template folder for this template. * * @return the template folder for this template */ public File getFolder() { return _folder; } /** * Returns the metadata for this template. * * @return the metadata for this template */ public Document getMetadata() { return _metadata; } /** * Returns the name of this template. * * @return the name of this template */ public synchronized String getName() { if (_name == null) { if (_metadata != null) { _name = _metadata.getDocumentElement().getAttribute("name"); } if (_name == null || _name.length() == 0) { _name = _folder.getName(); } } return _name; } /** * Returns the value of the input with the given name. * * @param name the name of the input * @return the value of the input */ public Object valueForInputNamed(String name) { return getInputNamed(name).getValue(); } /** * Sets the value of the input with the given name. * * @param inputValue the value * @param name the input name */ public void setValueForInputNamed(Object inputValue, String name) { getInputNamed(name).setValue(inputValue); } /** * Returns the input with the given name. * * @param name the name of the input * @return the value of the input */ public ProjectInput getInputNamed(String name) { for (ProjectInput input : getInputs()) { if (input.getName().equals(name)) { return input; } } return null; } /** * Adds the given input to this template. * * @param input the input to add */ public synchronized void addInput(ProjectInput input) { getInputs().add(input); } /** * Returns the inputs for this template. * * @return the inputs for this template */ public synchronized List<ProjectInput> getInputs() { if (_inputs == null) { _inputs = new LinkedList<ProjectInput>(); if (_metadata != null) { NodeList inputsNodeList = _metadata.getDocumentElement().getElementsByTagName("input"); for (int nodeNum = 0; nodeNum < inputsNodeList.getLength(); nodeNum++) { Element inputNode = (Element) inputsNodeList.item(nodeNum); String name = inputNode.getAttribute("name"); String type = inputNode.getAttribute("type"); ProjectInput input = new ProjectInput(name, ProjectInput.Type.valueOf(type)); NodeList optionsNodes = inputNode.getElementsByTagName("options"); if (optionsNodes.getLength() > 0) { Element optionsElement = (Element) optionsNodes.item(0); NodeList optionNodes = optionsElement.getElementsByTagName("option"); for (int optionNum = 0; optionNum < optionNodes.getLength(); optionNum++) { Element optionElement = (Element) optionNodes.item(optionNum); String optionName = optionElement.getAttribute("name"); String optionValue = optionElement.getAttribute("value"); input.addOption(optionName, optionValue); } } NodeList defaultValueNodes = inputNode.getElementsByTagName("default"); if (defaultValueNodes.getLength() > 0) { Node defaultValueNode = defaultValueNodes.item(0); String defaultValue = defaultValueNode.getTextContent(); input.setDefaultText(defaultValue); } NodeList questionNodes = inputNode.getElementsByTagName("question"); if (questionNodes.getLength() > 0) { Node questionNode = questionNodes.item(0); String question = questionNode.getTextContent(); input.setQuestion(question); } _inputs.add(input); } } } return _inputs; } @Override public int hashCode() { String name = getName(); return name == null ? super.hashCode() : name.toLowerCase().hashCode(); } @Override public boolean equals(Object obj) { return (obj instanceof ProjectTemplate && compareTo((ProjectTemplate) obj) == 0); } public int compareTo(ProjectTemplate o) { int comparison; if (o == null) { comparison = -1; } else { String name = getName(); String oName = o.getName(); if (name == null) { if (oName == null) { comparison = 0; } else { comparison = 1; } } else { comparison = name.compareToIgnoreCase(oName); } } return comparison; } public String toString() { return "[ProjectTemplate: name = " + getName() + "]"; } /** * Creates the contents of the given project from the definition and inputs * of this template. * * @param project * the project to fill in * @param targetContainer * @param progressMonitor the container to install into * the progress tracker * @throws Exception * if the project cannot be created */ public void createProjectContents(IProject project, IContainer targetContainer, IProgressMonitor progressMonitor) throws Exception { TemplateEngine templateEngine = new TemplateEngine(); templateEngine.setTemplatePath(getFolder().getParentFile().getAbsolutePath()); templateEngine.init(); templateEngine.getWolipsContext().setProjectName(project.getName()); templateEngine.getWolipsContext().setAntFolderName(ProjectPatternsets.ANT_FOLDER_NAME); templateEngine.setPropertyForKey(project.getName(), "projectName"); templateEngine.setPropertyForKey(project.getName().toLowerCase(), "projectName_lowercase"); for (ProjectInput input : getInputs()) { Object value = input.getValue(); templateEngine.setPropertyForKey(value, input.getName()); if (input.getValue() instanceof String) { templateEngine.setPropertyForKey(((String) value).toLowerCase(), input.getName() + "_lowercase"); } // Package types get a yourname_folder variable made if (input.getType() == ProjectInput.Type.Package) { templateEngine.setPropertyForKey(((String) value).replace('.', '/'), input.getName() + "_folder"); } } Object[] keys = templateEngine.getKeys(); List<String> templateKeys = new LinkedList<String>(); for (Object key : keys) { if (key instanceof String) { templateKeys.add((String) key); } } // MS: Sort inverse by name so "basePackage_folder" evaluates before // "basePackage" (so the longer one wins). Collections.sort(templateKeys, new ReverseStringLengthComparator()); _createProjectFolder(templateEngine, getFolder().getParentFile(), targetContainer.getLocation().toFile(), getFolder(), templateKeys, progressMonitor); templateEngine.run(progressMonitor); } protected void _createProjectFolder(TemplateEngine templateEngine, File baseFolder, File projectFolder, File templateFolder, List<String> templateKeys, IProgressMonitor progressMonitor) throws CoreException, FileNotFoundException, IOException { for (File templateChild : templateFolder.listFiles()) { String templateChildName = templateChild.getName(); // Skip over files named "__placeholder__". These exist only so // empty folders make it into the plugin. if ("__placeholder__".equals(templateChildName) || "__placeholder".equals(templateChildName) || ".svn".equals(templateChildName) || "CVS".equals(templateChildName)) { continue; } boolean binary = false; if (templateChildName.endsWith("__binary")) { templateChildName = templateChildName.substring(0, templateChildName.indexOf("__binary")); binary = true; } for (String key : templateKeys) { Object value = templateEngine.getPropertyForKey(key); if (value instanceof String) { templateChildName = templateChildName.replaceAll("__" + key + "__", (String) value); } } File destinationFile = new File(projectFolder, templateChildName); if (templateChild.isDirectory()) { destinationFile.mkdirs(); _createProjectFolder(templateEngine, baseFolder, destinationFile, templateChild, templateKeys, progressMonitor); } else { if (!"template.xml".equals(templateChildName)) { if (binary) { FileUtilities.copyFileToFile(templateChild, destinationFile, false, false); } else { String templatePath = templateChild.getAbsolutePath(); templatePath = templatePath.substring(baseFolder.getAbsolutePath().length()); templateEngine.addTemplate(new TemplateDefinition(templatePath, destinationFile.getParentFile().getAbsolutePath(), destinationFile.getName(), destinationFile.getName(), "UTF-8")); } } } } } /** * Returns the list of template folder locations. * * @param project optional project reference (can be null), to load project-relative templates * @param baseFolderName the name of the base folder to load templates from ("Project Templates") * @return the list of template folder locations */ public static List<File> templateBaseFolders(IProject project, String baseFolderName) { LinkedList<File> templateBaseFolders = new LinkedList<File>(); try { if (baseFolderName != null) { Bundle bundle = FrameworkUtil.getBundle(TemplateEngine.class); URL urlInBundle = bundle.getEntry(baseFolderName); if (urlInBundle == null) { urlInBundle = bundle.getEntry(baseFolderName.replaceAll(" ", "")); } if (urlInBundle != null) { URL fileUrl = FileLocator.toFileURL(urlInBundle); File baseFolder = new File(fileUrl.toURI()); templateBaseFolders.add(baseFolder); } } } catch (URISyntaxException e) { TemplateEnginePlugin.getDefault().log(e); } catch (IOException e) { TemplateEnginePlugin.getDefault().log(e); } if (project != null) { String templatesFolderName = "Templates"; ProjectAdapter projectAdapter = (ProjectAdapter) project.getProject().getAdapter(ProjectAdapter.class); if (projectAdapter != null) { BuildProperties buildProperties = projectAdapter.getBuildProperties(); if (buildProperties != null) { templatesFolderName = buildProperties.get("projectTemplatesFolder", templatesFolderName); } } IFolder projectTemplatesFolder = project.getFolder(templatesFolderName).getFolder(baseFolderName); if (projectTemplatesFolder.exists()) { templateBaseFolders.add(projectTemplatesFolder.getLocation().toFile()); } } templateBaseFolders.add(new File("/Library/Application Support/TBLips/" + baseFolderName)); templateBaseFolders.add(new File(System.getProperty("user.home"), "Documents and Settings/Application Data/TBLips/" + baseFolderName)); templateBaseFolders.add(new File(System.getProperty("user.home"), "Documents and Settings/AppData/Local/TBLips/" + baseFolderName)); templateBaseFolders.add(new File(System.getProperty("user.home"), "Library/Application Support/TBLips/" + baseFolderName)); return templateBaseFolders; } /** * Loads the project templates from the predefined project template folder * locations. * * @param baseFolderName the name of the base folder to load templates from ("Project Templates") * @return the available project templates; */ public static List<ProjectTemplate> loadProjectTemplates(String baseFolderName) { return ProjectTemplate.loadProjectTemplates(null, baseFolderName); } /** * Loads the project templates from the predefined project template folder * locations. * * @param project optional project reference (can be null), to load project-relative templates * @param baseFolderName the name of the base folder to load templates from ("Project Templates") * @return the available project templates; */ public static List<ProjectTemplate> loadProjectTemplates(IProject project, String baseFolderName) { return ProjectTemplate.loadProjectTemplatesFromFolders(ProjectTemplate.templateBaseFolders(project, baseFolderName)); } /** * Loads the project templates and metadata from the given template folders. * * @param templateBaseFolders * the template base folders * @return the project templates */ public static List<ProjectTemplate> loadProjectTemplatesFromFolders(List<File> templateBaseFolders) { List<ProjectTemplate> templates = new LinkedList<ProjectTemplate>(); for (File templateBaseFolder : templateBaseFolders) { List<ProjectTemplate> folderTemplates = ProjectTemplate.loadProjectTemplatesFromFolder(templateBaseFolder); for (ProjectTemplate projectTemplate : folderTemplates) { // last template for that name wins templates.remove(projectTemplate); templates.add(projectTemplate); } } Collections.sort(templates); return templates; } /** * Loads the project templates and metadata from the given template folder. * * @param templateBaseFolder * the template base folder * @return the project templates */ public static List<ProjectTemplate> loadProjectTemplatesFromFolder(File templateBaseFolder) { List<ProjectTemplate> templates = new LinkedList<ProjectTemplate>(); if (templateBaseFolder.exists()) { for (File templateFolder : templateBaseFolder.listFiles()) { if (templateFolder.isDirectory() && !templateFolder.isHidden()) { File templateXML = new File(templateFolder, "template.xml"); if (templateXML.exists()) { try { Document templateDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(templateXML); templateDocument.normalize(); ProjectTemplate template = new ProjectTemplate(templateFolder, templateDocument); templates.add(template); } catch (Exception e) { TemplateEnginePlugin.getDefault().log(e); } } else { ProjectTemplate template = new ProjectTemplate(templateFolder, null); templates.add(template); } } } } return templates; } /** * Returns the ProjectTemplate with the given name. * * @param baseFolderName the name of the base folder to load templates from ("Project Templates") * @param name * the name of the template to load * @return the requested Project Template or null if there is no template * with the given name */ public static ProjectTemplate loadProjectTemplateNamed(String baseFolderName, String name) { return ProjectTemplate.loadProjectTemplateNamed(null, baseFolderName, name); } /** * Returns the ProjectTemplate with the given name. * * @param project the project to load project-relative templates out of * @param baseFolderName the name of the base folder to load templates from ("Project Templates") * @param name * the name of the template to load * @return the requested Project Template or null if there is no template * with the given name */ public static ProjectTemplate loadProjectTemplateNamed(IProject project, String baseFolderName, String name) { List<ProjectTemplate> projectTemplates = ProjectTemplate.loadProjectTemplates(project, baseFolderName); for (ProjectTemplate projectTemplate : projectTemplates) { if (name.equals(projectTemplate.getName())) { return projectTemplate; } } return null; } /** * Returns the ProjectTemplate with the given name. * * @param project the project to load project-relative templates out of * @param baseFolderName the name of the base folder to load templates from ("Project Templates") * @param name * the name of the template to load * @return the requested Project Template or null if there is no template * with the given name */ public static ProjectTemplate loadProjectTemplateNamedFromFolder(IProject project, String baseFolderName, String name) { List<ProjectTemplate> projectTemplates = ProjectTemplate.loadProjectTemplates(project, baseFolderName); for (ProjectTemplate projectTemplate : projectTemplates) { if (name.equals(projectTemplate.getName())) { return projectTemplate; } } return null; } /** * Sorts Strings in reverse order by length. * * @author mschrag */ protected static class ReverseStringLengthComparator implements Comparator<String> { public int compare(String s1, String s2) { return (s1.length() > s2.length()) ? -1 : (s1.length() < s2.length()) ? 1 : 0; } } }