package org.springframework.roo.addon.web.mvc.views.template.engines; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.StringBuilderWriter; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.springframework.roo.addon.web.mvc.views.AbstractViewGenerationService; import org.springframework.roo.addon.web.mvc.views.AbstractViewMetadata; import org.springframework.roo.addon.web.mvc.views.ViewContext; import org.springframework.roo.addon.web.mvc.views.components.FieldItem; import org.springframework.roo.process.manager.FileManager; import org.springframework.roo.support.osgi.OSGiUtils; import org.springframework.roo.support.util.FileUtils; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateExceptionHandler; import freemarker.template.Version; @Component(componentAbstract = true) public abstract class AbstractFreeMarkerViewGenerationService<DOC, T extends AbstractViewMetadata> extends AbstractViewGenerationService<DOC, T> { @Reference FileManager fileManager; protected abstract Class<?> getResourceLoaderClass(); protected boolean checkTemplates(String location, String templateName) { // Check if provided template exists and has .ftl extension return fileManager.exists(location.concat("/").concat(templateName).concat(".ftl")); } protected DOC process(String templateName, ViewContext<T> ctx) { String content = ""; try { Configuration cfg = new Configuration(new Version(2, 3, 23)); cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); // Check if exists some template. If not, use classpath to locate the template if (checkTemplates(getTemplatesLocation(), templateName)) { // Process using FreeMarker and setDirectoryForTemplateLoading cfg.setDirectoryForTemplateLoading(new File(getTemplatesLocation())); } else { // Process using FreeMarker and setClassForTemplateLoading cfg.setClassForTemplateLoading(getResourceLoaderClass(), "templates"); } // Prepare the template input: Map<String, Object> input = new HashMap<String, Object>(); // Getting project information from ViewContext input.put("projectName", ctx.getProjectName()); input.put("description", ctx.getDescription()); input.put("version", ctx.getVersion()); // Getting controller information from ViewContext input.put("controllerPath", ctx.getControllerPath()); // Getting entity information from ViewContext input.put("entityName", ctx.getEntityName()); if (ctx.getEntityName() != null) { input.put("entityLabel", FieldItem.buildLabel(ctx.getEntityName(), "")); input.put("entityLabelPlural", FieldItem.buildLabel(ctx.getEntityName(), "plural")); input.put("z", new FieldItem("", ctx.getEntityName()).getZ()); } input.put("identifierField", ctx.getIdentifierField()); input.put("modelAttribute", String.format("${%s}", ctx.getModelAttribute())); input.put("modelAttributeName", ctx.getModelAttributeName()); // Getting security information from ViewContext input.put("isSecurityEnabled", ctx.isSecurityEnabled()); // Add all extra elements from ViewContext. This is useful if some // implementation wants to include its own information for (Entry<String, Object> extraInformation : ctx.getExtraInformation().entrySet()) { input.put(extraInformation.getKey(), extraInformation.getValue()); } Template template = cfg.getTemplate(templateName.concat(".ftl")); StringBuilderWriter writer = new StringBuilderWriter(); template.process(input, writer); writer.close(); content = writer.toString(); if (StringUtils.isBlank(content)) { throw new RuntimeException(String.format( "ERROR: Error trying to generate final content from provided template '%s.ftl'", templateName)); } return parse(content); } catch (Exception e) { throw new RuntimeException( String.format( "ERROR: Error trying to generate final content from provided template '%s.ftl'. You should provide a valid .ftl file", templateName), e); } } /** * This method will copy the contents of a directory to another if the * resource does not already exist in the target directory * * @param sourceAntPath the source path * @param targetDirectory the target directory */ protected void copyDirectoryContents(final String sourceAntPath, String targetDirectory, final boolean replace) { Validate.notBlank(sourceAntPath, "Source path required"); Validate.notBlank(targetDirectory, "Target directory required"); if (!targetDirectory.endsWith("/")) { targetDirectory += "/"; } if (!fileManager.exists(targetDirectory)) { fileManager.createDirectory(targetDirectory); } final String path = FileUtils.getPath(getClass(), sourceAntPath); final Iterable<URL> urls = OSGiUtils.findEntriesByPattern(context, path); Validate.notNull(urls, "Could not search bundles for resources for Ant Path '%s'", path); for (final URL url : urls) { final String fileName = url.getPath().substring(url.getPath().lastIndexOf("/") + 1); if (replace) { try { String contents = IOUtils.toString(url); fileManager.createOrUpdateTextFileIfRequired(targetDirectory + fileName, contents, false); } catch (final Exception e) { throw new IllegalStateException(e); } } else { if (!fileManager.exists(targetDirectory + fileName)) { InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = url.openStream(); outputStream = fileManager.createFile(targetDirectory + fileName).getOutputStream(); IOUtils.copy(inputStream, outputStream); } catch (final Exception e) { throw new IllegalStateException( "Encountered an error during copying of resources for the add-on.", e); } finally { IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(outputStream); } } } } } }