/******************************************************************************* * Copyright (c) 2012 OpenLegacy Inc. * 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: * OpenLegacy Inc. - initial API and implementation *******************************************************************************/ package org.openlegacy.designtime.terminal.generators; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openlegacy.EntityDefinition; import org.openlegacy.definitions.page.support.SimplePageDefinition; import org.openlegacy.designtime.UserInteraction; import org.openlegacy.designtime.mains.GenerateControllerRequest; import org.openlegacy.designtime.mains.GenerateViewRequest; import org.openlegacy.exceptions.GenerationException; import org.openlegacy.layout.PageDefinition; import org.openlegacy.terminal.definitions.ScreenEntityDefinition; import org.openlegacy.terminal.layout.support.DefaultScreenPageBuilder; import org.springframework.stereotype.Component; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.MessageFormat; import java.util.List; import javax.inject.Inject; /** * Generates all Spring MVC web related content * * @author RoiM * */ @Component public class ScreenEntityMvcGenerator implements ScreenEntityWebGenerator { private static final String WEB_VIEWS_DIR = "src/main/webapp/WEB-INF/web/views/"; private static final String MOBILE_VIEWS_DIR = "src/main/webapp/WEB-INF/mobile/views/"; private static final String HELP_DIR = "src/main/webapp/help/"; private static final String VIEWS_FILE = "views.xml"; private static final String TILES_VIEW_PLACEHOLDER_START = "<!-- Marker for code generation start:"; private static final String TILES_VIEW_PLACEHOLDER_END = "Marker for code generation end -->"; private static final String VIEW_TOKEN = "VIEW-NAME"; private static final String TEMPLATE_TOKEN = "TEMPLATE-NAME"; private static final String DEFAULT_TEMPLATE = "template"; private static final String VIEW_ONLY_TEMPLATE = "view"; private static final String INNER_VIEW_MOBILE_TEMPLATE = "innerView"; private static final String COMPOSITE_SUFFIX = "Composite"; private static final String COMPOSITE_TEMPLATE = "compositeTemplate"; private static final String COMPOSITE_VIEW = "compositeView"; private static final CharSequence TILES_VIEW_PLACEHOLDER = "<!-- Place holder for code generation -->"; // must ends with slash public static final String TEMPLATE_WEB_DIR_PREFIX = "web/"; public static final String TEMPLATE_MOBILE_DIR_PREFIX = "mobile/"; @Inject private GenerateUtil generateUtil; @Inject private HelpGenerator helpGenerator; private final static Log logger = LogFactory.getLog(ScreenEntityMvcGenerator.class); public void generatePage(PageDefinition pageDefinition, OutputStream output, String templateDirectoryPrefix) { String typeName = MessageFormat.format("{0}{1}", templateDirectoryPrefix, pageDefinition.getEntityDefinition().getTypeName()); generateUtil.generate(pageDefinition, output, "ScreenEntityMvcPage.jspx.template", typeName); } public void generateController(PageDefinition pageDefinition, OutputStream output) { String typeName = pageDefinition.getEntityDefinition().getTypeName(); generateUtil.generate(pageDefinition, output, "ScreenEntityMvcController.java.template", typeName); } public void generateControllerAspect(PageDefinition pageDefinition, OutputStream output) { String typeName = pageDefinition.getEntityDefinition().getTypeName(); generateUtil.generate(pageDefinition, output, "ScreenEntityMvcController.aj.template", typeName); } /** * Generate all web page related content: jspx, controller, controller aspect file, and views.xml file */ public void generateView(GenerateViewRequest generatePageRequest, ScreenEntityDefinition screenEntityDefinition) throws GenerationException { if (screenEntityDefinition.isChild()) { logger.warn("Skipping generation of child entity" + screenEntityDefinition.getEntityClassName()); return; } generateView(generatePageRequest, screenEntityDefinition, false); } /** * Generate all web page related content: jspx, controller, controller aspect file, and views.xml file */ private void generateView(GenerateViewRequest generatePageRequest, EntityDefinition<?> entityDefinition, boolean isChild) throws GenerationException { generateUtil.setTemplateDirectory(generatePageRequest.getTemplatesDirectory()); // Whether to generate a simple or composite page boolean isComposite = !isChild && entityDefinition.getChildEntitiesDefinitions().size() > 0; UserInteraction userInteraction = generatePageRequest.getUserInteraction(); FileOutputStream fos = null; try { String entityClassName = entityDefinition.getEntityClassName(); SimplePageDefinition pageDefinition = (SimplePageDefinition)new DefaultScreenPageBuilder().build((ScreenEntityDefinition)entityDefinition); if (generatePageRequest.isGenerateHelp()) { boolean generateHelp = true; File helpFile = new File(generatePageRequest.getProjectPath(), MessageFormat.format("{0}{1}.html", HELP_DIR, entityClassName)); if (helpFile.exists()) { boolean override = userInteraction.isOverride(helpFile); if (!override) { generateHelp = false; } } if (generateHelp) { helpFile.getParentFile().mkdirs(); OutputStream out = new FileOutputStream(helpFile); try { helpGenerator.generate(pageDefinition, out); } finally { IOUtils.closeQuietly(out); org.openlegacy.utils.FileUtils.deleteEmptyFile(helpFile); } } } // generate web view String mvcTemplateType = getMvcTemplateType(entityDefinition, isComposite, isChild, false); generateView(generatePageRequest, entityDefinition, pageDefinition, WEB_VIEWS_DIR, TEMPLATE_WEB_DIR_PREFIX, userInteraction, isComposite, mvcTemplateType, COMPOSITE_TEMPLATE); // generate mobile view if (generatePageRequest.isGenerateMobilePage()) { mvcTemplateType = getMvcTemplateType(entityDefinition, isComposite, isChild, true); generateView(generatePageRequest, entityDefinition, pageDefinition, MOBILE_VIEWS_DIR, TEMPLATE_MOBILE_DIR_PREFIX, userInteraction, isComposite, mvcTemplateType, COMPOSITE_VIEW); } } catch (Exception e) { throw (new GenerationException(e)); } finally { IOUtils.closeQuietly(fos); } } /** * Generate all controller related content: controller, controller aspect file */ public void generateController(GenerateControllerRequest generateControllerRequest, ScreenEntityDefinition screenEntityDefinition) throws GenerationException { if (screenEntityDefinition.isChild()) { logger.warn("Skipping generation of child entity" + screenEntityDefinition.getEntityClassName()); return; } generateController(generateControllerRequest, screenEntityDefinition, false); } /** * Generate all controller related content: controller, controller aspect file */ private void generateController(GenerateControllerRequest generateControllerRequest, EntityDefinition<?> entityDefinition, boolean isChild) throws GenerationException { generateUtil.setTemplateDirectory(generateControllerRequest.getTemplatesDirectory()); // Whether to generate a simple or composite page boolean isComposite = !isChild && entityDefinition.getChildEntitiesDefinitions().size() > 0; UserInteraction userInteraction = generateControllerRequest.getUserInteraction(); FileOutputStream fos = null; try { File packageDir = new File(generateControllerRequest.getSourceDirectory(), generateControllerRequest.getPackageDirectory()); String entityClassName = entityDefinition.getEntityClassName(); if (isComposite) { File compositeContollerFile = new File(packageDir, entityClassName + "CompositeController.java"); boolean generateCompositeController = true; if (compositeContollerFile.exists()) { boolean override = userInteraction.isOverride(compositeContollerFile); if (!override) { generateCompositeController = false; } } if (generateCompositeController) { compositeContollerFile.getParentFile().mkdirs(); fos = new FileOutputStream(compositeContollerFile); try { generateCompositeContoller(entityDefinition, fos); } finally { IOUtils.closeQuietly(fos); org.openlegacy.utils.FileUtils.deleteEmptyFile(compositeContollerFile); } } } File contollerFile = new File(packageDir, entityClassName + "Controller.java"); boolean generateController = true; if (contollerFile.exists()) { boolean override = userInteraction.isOverride(contollerFile); if (!override) { generateController = false; } } SimplePageDefinition pageDefinition = (SimplePageDefinition)new DefaultScreenPageBuilder().build((ScreenEntityDefinition)entityDefinition); pageDefinition.setPackageName(generateControllerRequest.getPackageDirectory().replaceAll("/", ".")); if (generateController) { contollerFile.getParentFile().mkdirs(); fos = new FileOutputStream(contollerFile); try { generateController(pageDefinition, fos); logger.info(MessageFormat.format("Generated controller : {0}", contollerFile.getAbsoluteFile())); } finally { IOUtils.closeQuietly(fos); org.openlegacy.utils.FileUtils.deleteEmptyFile(contollerFile); } } if (generateController) { File contollerAspectFile = new File(packageDir, entityClassName + "Controller_Aspect.aj"); fos = new FileOutputStream(contollerAspectFile); try { generateControllerAspect(pageDefinition, fos); logger.info(MessageFormat.format("Generated controller aspect: {0}", contollerAspectFile.getAbsoluteFile())); } finally { IOUtils.closeQuietly(fos); org.openlegacy.utils.FileUtils.deleteEmptyFile(contollerAspectFile); } } } catch (Exception e) { throw (new GenerationException(e)); } finally { IOUtils.closeQuietly(fos); } } /** * Updates sprint views.xml file which contains all web page views definitions * * @param projectDir * @param entityDefinition * @throws IOException */ private static void updateViewsFile(File projectDir, EntityDefinition<?> entityDefinition, String viewName, String mcvTemplateType, String tilesViewsFile) throws IOException { File viewsFile = new File(projectDir, tilesViewsFile); if (!viewsFile.exists()) { logger.warn(MessageFormat.format("Views file {0} not found in project directory:{1}", tilesViewsFile, projectDir)); } FileOutputStream fos = null; try { // Find a marker block within Spring MVC tiles views.xml file String viewsFileContent = FileUtils.readFileToString(viewsFile); String definitionTemplate = getViewTemplate(entityDefinition, viewsFile, viewsFileContent); if (definitionTemplate == null) { logger.warn(MessageFormat.format("Could not find template markers within views file: {0}", viewsFile.getAbsolutePath())); return; } String newViewDefinition = definitionTemplate.replaceAll(VIEW_TOKEN, viewName); newViewDefinition = newViewDefinition.replaceAll(TEMPLATE_TOKEN, mcvTemplateType); viewsFileContent = viewsFileContent.replace(TILES_VIEW_PLACEHOLDER, TILES_VIEW_PLACEHOLDER + newViewDefinition); fos = new FileOutputStream(viewsFile); IOUtils.write(viewsFileContent, fos); logger.info(MessageFormat.format("Added view {0} to {1}", viewName, viewsFile.getAbsoluteFile())); } finally { IOUtils.closeQuietly(fos); } } private static String getViewTemplate(EntityDefinition<?> entityDefinition, File viewsFile, String viewsFileContent) { // check for marker with typeName int tokenLength = TILES_VIEW_PLACEHOLDER_START.length() + entityDefinition.getTypeName().length(); int templateMarkerStart = viewsFileContent.indexOf(TILES_VIEW_PLACEHOLDER_START + entityDefinition.getTypeName()) + tokenLength; // use default marker if (templateMarkerStart < tokenLength) { templateMarkerStart = viewsFileContent.indexOf(TILES_VIEW_PLACEHOLDER_START) + TILES_VIEW_PLACEHOLDER_START.length(); } // use default marker int templateMarkerEnd = viewsFileContent.indexOf(entityDefinition.getTypeName() + ":" + TILES_VIEW_PLACEHOLDER_END) - 1; if (templateMarkerEnd < 0) { templateMarkerEnd = viewsFileContent.indexOf(TILES_VIEW_PLACEHOLDER_END) - 1; } if (templateMarkerStart < 0 || templateMarkerEnd < 0) { return null; } // replace tokens within the place holder tag String definitionTemplate = viewsFileContent.substring(templateMarkerStart, templateMarkerEnd); return definitionTemplate; } public void generateCompositePage(EntityDefinition<?> entityDefinition, OutputStream output, String templateDirectoryPrefix) { generateUtil.generate(entityDefinition, output, templateDirectoryPrefix + "ScreenEntityMvcCompositePage.jspx.template"); } private void generateCompositeContoller(EntityDefinition<?> entityDefinition, OutputStream output) { generateUtil.generate(entityDefinition, output, "ScreenEntityMvcCompositeController.java.template"); } private void generateView(GenerateViewRequest generatePageRequest, EntityDefinition<?> entityDefinition, SimplePageDefinition pageDefinition, String viewsDir, String templateDirectoryPrefix, UserInteraction overrideConfirmer, boolean isComposite, String mvcTemplateType, String mvcCompositeTemplateType) throws IOException { String entityClassName = entityDefinition.getEntityClassName(); FileOutputStream fos = null; File pageFile = new File(generatePageRequest.getProjectPath(), MessageFormat.format("{0}{1}.jspx", viewsDir, entityClassName)); boolean pageFileExists = pageFile.exists(); boolean generatePage = true; if (pageFileExists) { boolean override = overrideConfirmer.isOverride(pageFile); if (!override) { generatePage = false; } } if (generatePage) { pageFile.getParentFile().mkdirs(); fos = new FileOutputStream(pageFile); try { generatePage(pageDefinition, fos, templateDirectoryPrefix); logger.info(MessageFormat.format("Generated jspx file: {0}", pageFile.getAbsoluteFile())); } finally { IOUtils.closeQuietly(fos); org.openlegacy.utils.FileUtils.deleteEmptyFile(pageFile); } // generate a composite page (with tabs) if (isComposite) { File pageCompositeFile = new File(generatePageRequest.getProjectPath(), MessageFormat.format( "{0}{1}Composite.jspx", viewsDir, entityClassName)); fos = new FileOutputStream(pageCompositeFile); try { generateCompositePage(entityDefinition, fos, templateDirectoryPrefix); } finally { IOUtils.closeQuietly(fos); org.openlegacy.utils.FileUtils.deleteEmptyFile(pageCompositeFile); } List<EntityDefinition<?>> childScreens = entityDefinition.getChildEntitiesDefinitions(); // generate page content for each of the child screens for (EntityDefinition<?> childDefinition : childScreens) { pageDefinition = (SimplePageDefinition)new DefaultScreenPageBuilder().build((ScreenEntityDefinition)childDefinition); generateView(generatePageRequest, childDefinition, pageDefinition, viewsDir, templateDirectoryPrefix, overrideConfirmer, false, mvcTemplateType, mvcCompositeTemplateType); } } // update views file only if web page wasn't exists (if exists, it's probably registered in views.xml) if (!pageFileExists) { // mvc template type is the name of a template file defined in layouts.xml String viewName = entityDefinition.getEntityClassName(); String tilesViewsFile = viewsDir + VIEWS_FILE; updateViewsFile(generatePageRequest.getProjectPath(), entityDefinition, viewName, mvcTemplateType, tilesViewsFile); if (isComposite) { // add view for composite screen updateViewsFile(generatePageRequest.getProjectPath(), entityDefinition, viewName + COMPOSITE_SUFFIX, mvcCompositeTemplateType, tilesViewsFile); } } } } private static String getMvcTemplateType(EntityDefinition<?> entityDefinition, boolean isComposite, boolean isChild, boolean isMobile) { String mvcTemplateType = null; if (isMobile) { // in mobile - generate pages as views by default. composite (main screen) and it's child entities - generate as inner // views (child of view) mvcTemplateType = (isComposite || isChild) ? INNER_VIEW_MOBILE_TEMPLATE : VIEW_ONLY_TEMPLATE; } else { // in web - generate pages as template by default. composite (main screen) and it's child entities - generate as views mvcTemplateType = (isComposite || isChild) ? VIEW_ONLY_TEMPLATE : DEFAULT_TEMPLATE; } return mvcTemplateType; } }