diff --git a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/FacesScaffold.java b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/FacesScaffold.java index 445e09b771..a381e69289 100644 --- a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/FacesScaffold.java +++ b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/FacesScaffold.java @@ -1,825 +1,787 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2011, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package org.jboss.forge.scaffold.faces; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.enterprise.event.Event; -import javax.inject.Inject; -import javax.persistence.CascadeType; -import javax.persistence.OneToOne; - -import org.jboss.forge.env.Configuration; -import org.jboss.forge.parser.JavaParser; -import org.jboss.forge.parser.java.Annotation; -import org.jboss.forge.parser.java.Field; -import org.jboss.forge.parser.java.JavaClass; -import org.jboss.forge.parser.java.Method; -import org.jboss.forge.parser.xml.Node; -import org.jboss.forge.parser.xml.XMLParser; -import org.jboss.forge.project.Project; -import org.jboss.forge.project.dependencies.Dependency; -import org.jboss.forge.project.dependencies.DependencyBuilder; -import org.jboss.forge.project.facets.BaseFacet; -import org.jboss.forge.project.facets.DependencyFacet; -import org.jboss.forge.project.facets.JavaSourceFacet; -import org.jboss.forge.project.facets.WebResourceFacet; -import org.jboss.forge.project.facets.events.InstallFacets; -import org.jboss.forge.resources.FileResource; -import org.jboss.forge.resources.Resource; -import org.jboss.forge.scaffold.AccessStrategy; -import org.jboss.forge.scaffold.ScaffoldProvider; -import org.jboss.forge.scaffold.TemplateStrategy; -import org.jboss.forge.scaffold.faces.metawidget.config.ForgeConfigReader; -import org.jboss.forge.scaffold.util.ScaffoldUtil; -import org.jboss.forge.shell.ShellPrompt; -import org.jboss.forge.shell.plugins.Alias; -import org.jboss.forge.shell.plugins.RequiresFacet; -import org.jboss.forge.shell.util.Streams; -import org.jboss.forge.spec.javaee.CDIFacet; -import org.jboss.forge.spec.javaee.EJBFacet; -import org.jboss.forge.spec.javaee.FacesAPIFacet; -import org.jboss.forge.spec.javaee.FacesFacet; -import org.jboss.forge.spec.javaee.PersistenceFacet; -import org.jboss.forge.spec.javaee.ServletFacet; -import org.jboss.seam.render.TemplateCompiler; -import org.jboss.seam.render.spi.TemplateResolver; -import org.jboss.seam.render.template.CompiledTemplateResource; -import org.jboss.seam.render.template.resolver.ClassLoaderTemplateResolver; -import org.jboss.shrinkwrap.descriptor.api.spec.servlet.web.WebAppDescriptor; -import org.metawidget.statically.StaticMetawidget; -import org.metawidget.statically.StaticUtils.IndentedWriter; -import org.metawidget.statically.StaticWidget; -import org.metawidget.statically.faces.StaticFacesUtils; -import org.metawidget.statically.faces.component.html.StaticHtmlMetawidget; -import org.metawidget.statically.faces.component.html.widgetbuilder.HtmlOutcomeTargetLink; -import org.metawidget.statically.faces.component.html.widgetbuilder.ReadOnlyWidgetBuilder; -import org.metawidget.statically.faces.component.html.widgetbuilder.richfaces.RichFacesWidgetBuilder; -import org.metawidget.statically.html.widgetbuilder.HtmlTag; -import org.metawidget.statically.javacode.StaticJavaMetawidget; -import org.metawidget.util.ArrayUtils; -import org.metawidget.util.CollectionUtils; -import org.metawidget.util.XmlUtils; -import org.metawidget.util.simple.StringUtils; -import org.metawidget.widgetbuilder.composite.CompositeWidgetBuilder; -import org.metawidget.widgetbuilder.composite.CompositeWidgetBuilderConfig; -import org.metawidget.widgetbuilder.iface.WidgetBuilder; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; - -/** - * Facet to generate a Java Server Faces UI. - *

- * This facet utilizes Metawidget internally. This enables the use of the Metawidget - * SPI (pluggable WidgetBuilders, Layouts etc) for customizing the generated User Interface. For more information on - * writing Metawidget plugins, see the Metawidget documentation. - *

- * This Facet does not require Metawidget to be in the final project. - * - * @author Lincoln Baxter, III - * @author Richard Kennard - */ - -@Alias("faces") -@RequiresFacet({ WebResourceFacet.class, - DependencyFacet.class, - PersistenceFacet.class, - EJBFacet.class, - CDIFacet.class, - FacesAPIFacet.class }) -public class FacesScaffold extends BaseFacet implements ScaffoldProvider -{ - // - // Private statics - // - - private static final String XMLNS_PREFIX = "xmlns:"; - - private static final String BACKING_BEAN_TEMPLATE = "scaffold/faces/BackingBean.jv"; - private static final String VIEW_UTILS_TEMPLATE = "scaffold/faces/ViewUtils.jv"; - private static final String TAGLIB_TEMPLATE = "scaffold/faces/forge.taglib.xml"; - private static final String VIEW_TEMPLATE = "scaffold/faces/view.xhtml"; - private static final String CREATE_TEMPLATE = "scaffold/faces/create.xhtml"; - private static final String SEARCH_TEMPLATE = "scaffold/faces/search.xhtml"; - private static final String NAVIGATION_TEMPLATE = "scaffold/faces/page.xhtml"; - - private static final String ERROR_TEMPLATE = "scaffold/faces/error.xhtml"; - private static final String INDEX_TEMPLATE = "scaffold/faces/index.xhtml"; - - private final Dependency richfaces3UI = DependencyBuilder.create("org.richfaces.ui:richfaces-ui"); - private final Dependency richfaces3Impl = DependencyBuilder.create("org.richfaces.framework:richfaces-impl"); - private final Dependency richfaces4UI = DependencyBuilder.create("org.richfaces.ui:richfaces-components-ui"); - private final Dependency richfaces4Impl = DependencyBuilder.create("org.richfaces.core:richfaces-core-impl"); - - // - // Protected members (nothing is private, to help subclassing) - // - - protected CompiledTemplateResource backingBeanTemplate; - protected int backingBeanTemplateQbeMetawidgetIndent; - - protected CompiledTemplateResource viewUtilsTemplate; - protected CompiledTemplateResource taglibTemplate; - protected CompiledTemplateResource viewTemplate; - protected Map viewTemplateNamespaces; - protected int viewTemplateEntityMetawidgetIndent; - - protected CompiledTemplateResource createTemplate; - protected Map createTemplateNamespaces; - protected int createTemplateEntityMetawidgetIndent; - - protected CompiledTemplateResource searchTemplate; - protected Map searchTemplateNamespaces; - protected int searchTemplateSearchMetawidgetIndent; - protected int searchTemplateBeanMetawidgetIndent; - - protected CompiledTemplateResource navigationTemplate; - protected int navigationTemplateIndent; - - protected CompiledTemplateResource errorTemplate; - protected CompiledTemplateResource indexTemplate; - protected TemplateResolver resolver; - - protected final ShellPrompt prompt; - protected final TemplateCompiler compiler; - protected final Event install; - protected StaticHtmlMetawidget entityMetawidget; - protected StaticHtmlMetawidget searchMetawidget; - protected StaticHtmlMetawidget beanMetawidget; - protected StaticJavaMetawidget qbeMetawidget; - - private Configuration config; - - // - // Constructor - // - - @Inject - public FacesScaffold(final Configuration config, - final ShellPrompt prompt, - final TemplateCompiler compiler, - final Event install) - { - this.config = config; - this.prompt = prompt; - this.compiler = compiler; - this.install = install; - - this.resolver = new ClassLoaderTemplateResolver(FacesScaffold.class.getClassLoader()); - - if (this.compiler != null) - { - this.compiler.getTemplateResolverFactory().addResolver(this.resolver); - } - } - - // - // Public methods - // - - @Override - public List> setup(String targetDir, final Resource template, final boolean overwrite) - { - List> resources = generateIndex(targetDir, template, overwrite); - setupWebXML(); - - return resources; - } - - /** - * Overridden to setup the Metawidgets. - *

- * Metawidgets must be configured per project and per Forge invocation. It is not sufficient to simply - * configure them in setup because the user may restart Forge and not run scaffold setup a - * second time. - */ - - @Override - public void setProject(Project project) - { - super.setProject(project); - - ForgeConfigReader configReader = new ForgeConfigReader(this.config, this.project); - - this.entityMetawidget = new StaticHtmlMetawidget(); - this.entityMetawidget.setConfigReader(configReader); - this.entityMetawidget.setConfig("scaffold/faces/metawidget-entity.xml"); - - this.searchMetawidget = new StaticHtmlMetawidget(); - this.searchMetawidget.setConfigReader(configReader); - this.searchMetawidget.setConfig("scaffold/faces/metawidget-search.xml"); - - this.beanMetawidget = new StaticHtmlMetawidget(); - this.beanMetawidget.setConfigReader(configReader); - this.beanMetawidget.setConfig("scaffold/faces/metawidget-bean.xml"); - - this.qbeMetawidget = new StaticJavaMetawidget(); - this.qbeMetawidget.setConfigReader(configReader); - this.qbeMetawidget.setConfig("scaffold/faces/metawidget-qbe.xml"); - } - - @Override - public List> generateFromEntity(String targetDir, final Resource template, final JavaClass entity, - final boolean overwrite) - { - // FORGE-460: setupRichFaces during generateFromEntity, not during setup, as generally 'richfaces setup' is called - // *after* 'scaffold setup' - - setupRichFaces(); - - // Track the list of resources generated - - List> result = new ArrayList>(); - try - { - JavaSourceFacet java = this.project.getFacet(JavaSourceFacet.class); - WebResourceFacet web = this.project.getFacet(WebResourceFacet.class); - - loadTemplates(); - Map context = CollectionUtils.newHashMap(); - context.put("entity", entity); - String ccEntity = StringUtils.decapitalize(entity.getName()); - context.put("ccEntity", ccEntity); - - // Prepare qbeMetawidget - this.qbeMetawidget.setPath(entity.getQualifiedName()); - StringWriter stringWriter = new StringWriter(); - this.qbeMetawidget.write(stringWriter, this.backingBeanTemplateQbeMetawidgetIndent); - context.put("qbeMetawidget", stringWriter.toString().trim()); - context.put("qbeMetawidgetImports", - CollectionUtils.toString(this.qbeMetawidget.getImports(), ";\r\nimport ", true, false)); - - // Create the Backing Bean for this entity - JavaClass viewBean = JavaParser.parse(JavaClass.class, this.backingBeanTemplate.render(context)); - viewBean.setPackage(java.getBasePackage() + ".view"); - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, java.getJavaResource(viewBean), viewBean.toString(), - overwrite)); - - // Set new context for view generation - context = getTemplateContext(template); - String beanName = StringUtils.decapitalize(viewBean.getName()); - context.put("beanName", beanName); - context.put("ccEntity", ccEntity); - context.put("entityName", StringUtils.uncamelCase(entity.getName())); - - // Prepare entityMetawidget - this.entityMetawidget.setValue(StaticFacesUtils.wrapExpression(beanName + "." + ccEntity)); - this.entityMetawidget.setPath(entity.getQualifiedName()); - this.entityMetawidget.setReadOnly(false); - this.entityMetawidget.setStyle(null); - - // Generate create - writeEntityMetawidget(context, this.createTemplateEntityMetawidgetIndent, this.createTemplateNamespaces); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, - web.getWebResource(targetDir + "/" + ccEntity + "/create.xhtml"), - this.createTemplate.render(context), - overwrite)); - - // Generate view - this.entityMetawidget.setReadOnly(true); - writeEntityMetawidget(context, this.viewTemplateEntityMetawidgetIndent, this.viewTemplateNamespaces); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, - web.getWebResource(targetDir + "/" + ccEntity + "/view.xhtml"), - this.viewTemplate.render(context), overwrite)); - - // Generate search - this.searchMetawidget.setValue(StaticFacesUtils.wrapExpression(beanName + ".search")); - this.searchMetawidget.setPath(entity.getQualifiedName()); - this.beanMetawidget.setValue(StaticFacesUtils.wrapExpression(beanName + ".pageItems")); - this.beanMetawidget.setPath(viewBean.getQualifiedName() + "/pageItems"); - writeSearchAndBeanMetawidget(context, this.searchTemplateSearchMetawidgetIndent, - this.searchTemplateBeanMetawidgetIndent, this.searchTemplateNamespaces); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, - web.getWebResource(targetDir + "/" + ccEntity + "/search.xhtml"), - this.searchTemplate.render(context), overwrite)); - - // Generate navigation - result.add(generateNavigation(targetDir, overwrite)); - - // Need ViewUtils and forge.taglib.xml for forgeview:asList - JavaClass viewUtils = JavaParser.parse(JavaClass.class, this.viewUtilsTemplate.render(context)); - viewUtils.setPackage(viewBean.getPackage()); - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, java.getJavaResource(viewUtils), viewUtils.toString(), - true)); - - context.put("viewPackage", viewBean.getPackage()); - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, - web.getWebResource("WEB-INF/classes/META-INF/forge.taglib.xml"), - this.taglibTemplate.render(context), true)); - - createInitializers(entity); - this.project.getFacet(JavaSourceFacet.class).saveJavaSource(entity); - - } - catch (Exception e) - { - throw new RuntimeException("Error generating default scaffolding: " + e.getMessage(), e); - } - return result; - } - - @Override - @SuppressWarnings("unchecked") - public boolean install() - { - if (!(this.project.hasFacet(WebResourceFacet.class) && this.project.hasFacet(PersistenceFacet.class) - && this.project.hasFacet(CDIFacet.class) && this.project.hasFacet(FacesFacet.class))) - { - this.install.fire(new InstallFacets(WebResourceFacet.class, PersistenceFacet.class, CDIFacet.class, - FacesFacet.class)); - } - - return true; - } - - @Override - public boolean isInstalled() - { - return true; - } - - @Override - public List> generateIndex(String targetDir, final Resource template, final boolean overwrite) - { - List> result = new ArrayList>(); - WebResourceFacet web = this.project.getFacet(WebResourceFacet.class); - - this.project.getFacet(ServletFacet.class).getConfig().welcomeFile("/index.html"); - loadTemplates(); - - generateTemplates(targetDir, overwrite); - HashMap context = getTemplateContext(template); - - // Basic pages - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/index.html"), getClass() - .getResourceAsStream("/scaffold/faces/index.html"), overwrite)); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/index.xhtml"), - this.indexTemplate.render(context), overwrite)); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("error.xhtml"), - this.errorTemplate.render(context), overwrite)); - - // Static resources - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/add.png"), - getClass().getResourceAsStream("/scaffold/faces/add.png"), overwrite)); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/background.gif"), - getClass().getResourceAsStream("/scaffold/faces/background.gif"), overwrite)); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/false.png"), - getClass().getResourceAsStream("/scaffold/faces/false.png"), overwrite)); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/favicon.ico"), - getClass().getResourceAsStream("/scaffold/faces/favicon.ico"), overwrite)); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/forge-logo.png"), - getClass().getResourceAsStream("/scaffold/faces/forge-logo.png"), overwrite)); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/forge-style.css"), - getClass().getResourceAsStream("/scaffold/faces/forge-style.css"), overwrite)); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/jboss-community.png"), - getClass().getResourceAsStream("/scaffold/faces/jboss-community.png"), overwrite)); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/remove.png"), - getClass().getResourceAsStream("/scaffold/faces/remove.png"), overwrite)); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/search.png"), - getClass().getResourceAsStream("/scaffold/faces/search.png"), overwrite)); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/true.png"), - getClass().getResourceAsStream("/scaffold/faces/true.png"), overwrite)); - - return result; - } - - @Override - public List> getGeneratedResources(String targetDir) - { - throw new RuntimeException("Not yet implemented!"); - } - - @Override - public AccessStrategy getAccessStrategy() - { - return new FacesAccessStrategy(this.project); - } - - @Override - public TemplateStrategy getTemplateStrategy() - { - return new FacesTemplateStrategy(this.project); - } - - @Override - public List> generateTemplates(String targetDir, final boolean overwrite) - { - List> result = new ArrayList>(); - - try - { - WebResourceFacet web = this.project.getFacet(WebResourceFacet.class); - - result.add(ScaffoldUtil.createOrOverwrite(this.prompt, - web.getWebResource("/resources/scaffold/paginator.xhtml"), - getClass().getResourceAsStream("/scaffold/faces/paginator.xhtml"), - overwrite)); - - result.add(generateNavigation(targetDir, overwrite)); - } - catch (Exception e) - { - throw new RuntimeException("Error generating default templates", e); - } - - return result; - } - - // - // Protected methods (nothing is private, to help subclassing) - // - - protected void loadTemplates() - { - if (this.backingBeanTemplate == null) - { - this.backingBeanTemplate = this.compiler.compile(BACKING_BEAN_TEMPLATE); - String template = Streams.toString(this.backingBeanTemplate.getSourceTemplateResource().getInputStream()); - this.backingBeanTemplateQbeMetawidgetIndent = parseIndent(template, "@{qbeMetawidget}"); - } - if (this.viewUtilsTemplate == null) - { - this.viewUtilsTemplate = this.compiler.compile(VIEW_UTILS_TEMPLATE); - } - if (this.taglibTemplate == null) - { - this.taglibTemplate = this.compiler.compile(TAGLIB_TEMPLATE); - } - if (this.viewTemplate == null) - { - this.viewTemplate = this.compiler.compile(VIEW_TEMPLATE); - String template = Streams.toString(this.viewTemplate.getSourceTemplateResource().getInputStream()); - this.viewTemplateNamespaces = parseNamespaces(template); - this.viewTemplateEntityMetawidgetIndent = parseIndent(template, "@{metawidget}"); - } - if (this.createTemplate == null) - { - this.createTemplate = this.compiler.compile(CREATE_TEMPLATE); - String template = Streams.toString(this.createTemplate.getSourceTemplateResource().getInputStream()); - this.createTemplateNamespaces = parseNamespaces(template); - this.createTemplateEntityMetawidgetIndent = parseIndent(template, "@{metawidget}"); - } - if (this.searchTemplate == null) - { - this.searchTemplate = this.compiler.compile(SEARCH_TEMPLATE); - String template = Streams.toString(this.searchTemplate.getSourceTemplateResource().getInputStream()); - this.searchTemplateNamespaces = parseNamespaces(template); - this.searchTemplateSearchMetawidgetIndent = parseIndent(template, "@{searchMetawidget}"); - this.searchTemplateBeanMetawidgetIndent = parseIndent(template, "@{beanMetawidget}"); - } - if (this.navigationTemplate == null) - { - this.navigationTemplate = this.compiler.compile(NAVIGATION_TEMPLATE); - String template = Streams.toString(this.navigationTemplate.getSourceTemplateResource().getInputStream()); - this.navigationTemplateIndent = parseIndent(template, "@{navigation}"); - } - if (this.errorTemplate == null) - { - this.errorTemplate = this.compiler.compile(ERROR_TEMPLATE); - } - if (this.indexTemplate == null) - { - this.indexTemplate = this.compiler.compile(INDEX_TEMPLATE); - } - } - - protected void setupRichFaces() - { - if ((this.project.getFacet(DependencyFacet.class).hasEffectiveDependency(this.richfaces3UI) - && this.project.getFacet(DependencyFacet.class).hasEffectiveDependency(this.richfaces3Impl)) - || (this.project.getFacet(DependencyFacet.class).hasEffectiveDependency(this.richfaces4UI) - && this.project.getFacet(DependencyFacet.class).hasEffectiveDependency(this.richfaces4Impl))) - { - this.entityMetawidget - .setWidgetBuilder(insertRichFacesWidgetBuilder((CompositeWidgetBuilder) this.entityMetawidget - .getWidgetBuilder())); - - this.searchMetawidget - .setWidgetBuilder(insertRichFacesWidgetBuilder((CompositeWidgetBuilder) this.searchMetawidget - .getWidgetBuilder())); - - this.beanMetawidget - .setWidgetBuilder(insertRichFacesWidgetBuilder((CompositeWidgetBuilder) this.beanMetawidget - .getWidgetBuilder())); - } - } - - /** - * Locates a ReadOnlyWidgetBuilder in the list of WidgetBuilders, and inserts a - * RichFacesWidgetBuilder after it (unless there's a RichFacesWidgetBuilder in there - * already). - */ - - protected CompositeWidgetBuilder insertRichFacesWidgetBuilder( - final CompositeWidgetBuilder compositeWidgetBuilder) - { - // Get the current WidgetBuilders... - - WidgetBuilder[] existingWidgetBuilders = compositeWidgetBuilder.getWidgetBuilders(); - - // ...find the ReadOnlyWidgetBuilder (if any)... - - int addAt = 0; - - for (int loop = 0; loop < existingWidgetBuilders.length; loop++) - { - // ...(abort if there's already a RichFacesWidgetBuilder)... - - // Use an Object loop variable here to avoid a nasty Java/Generics compiler bug - Object widgetBuilder = existingWidgetBuilders[loop]; - if (widgetBuilder instanceof RichFacesWidgetBuilder) - { - return compositeWidgetBuilder; - } - - if (widgetBuilder instanceof ReadOnlyWidgetBuilder) - { - addAt = loop + 1; - } - } - - // ...and insert our RichFacesWidgetBuilder just after it - - @SuppressWarnings("unchecked") - WidgetBuilder[] newWidgetBuilders = (WidgetBuilder[]) ArrayUtils - .addAt(existingWidgetBuilders, addAt, - new RichFacesWidgetBuilder()); - - return new CompositeWidgetBuilder( - new CompositeWidgetBuilderConfig() - .setWidgetBuilders(newWidgetBuilders)); - } - - protected void createInitializers(final JavaClass entity) - { - for (Field field : entity.getFields()) - { - if (field.hasAnnotation(OneToOne.class)) - { - Annotation oneToOne = field.getAnnotation(OneToOne.class); - if (oneToOne.getStringValue("mappedBy") == null) - { - oneToOne.setEnumValue("cascade", CascadeType.ALL); - } - String methodName = "new" + field.getTypeInspector().getName(); - if (!entity.hasMethodSignature(methodName)) - { - entity.addMethod().setName(methodName).setReturnTypeVoid().setPublic() - .setBody("this." + field.getName() + " = new " + field.getType() + "();"); - } - } - } - for (Method method : entity.getMethods()) - { - if (method.hasAnnotation(OneToOne.class)) - { - Annotation oneToOne = method.getAnnotation(OneToOne.class); - if (oneToOne.getStringValue("mappedBy") == null) - { - oneToOne.setEnumValue("cascade", CascadeType.ALL); - } - String methodName = "new" + method.getReturnTypeInspector().getName(); - if (!entity.hasMethodSignature(methodName)) - { - entity.addMethod().setName(methodName).setReturnTypeVoid().setPublic() - .setBody("this." + method.getName() + " = new " + method.getReturnType() + "();"); - } - } - } - } - - protected HashMap getTemplateContext(final Resource template) - { - HashMap context; - context = new HashMap(); - context.put("template", template); - context.put("templateStrategy", getTemplateStrategy()); - return context; - } - - protected void setupWebXML() - { - ServletFacet servlet = this.project.getFacet(ServletFacet.class); - - Node webXML = removeConflictingErrorPages(servlet); - servlet.getConfigFile().setContents(XMLParser.toXMLInputStream(webXML)); - - WebAppDescriptor servletConfig = servlet.getConfig(); - WebResourceFacet web = this.project.getFacet(WebResourceFacet.class); - - // (prefer /faces/error.xhtml) - - String errorLocation = getAccessStrategy().getWebPaths(web.getWebResource("error.xhtml")).get(1); - servletConfig.errorPage(404, errorLocation); - servletConfig.errorPage(500, errorLocation); - - servlet.saveConfig(servletConfig); - } - - protected Node removeConflictingErrorPages(final ServletFacet servlet) - { - Node webXML = XMLParser.parse(servlet.getConfigFile().getResourceInputStream()); - Node root = webXML.getRoot(); - List errorPages = root.get("error-page"); - - for (String code : Arrays.asList("404", "500")) - { - for (Node errorPage : errorPages) - { - if (code.equals(errorPage.getSingle("error-code").getText()) - && this.prompt.promptBoolean("Your web.xml already contains an error page for " + code - + " status codes, replace it?")) - { - root.removeChild(errorPage); - } - } - } - return webXML; - } - - /** - * Generates the navigation menu based on scaffolded entities. - */ - - protected Resource generateNavigation(final String targetDir, final boolean overwrite) - throws IOException - { - WebResourceFacet web = this.project.getFacet(WebResourceFacet.class); - HtmlTag unorderedList = new HtmlTag("ul"); - - for (Resource resource : web.getWebResource(targetDir).listResources()) - { - HtmlOutcomeTargetLink outcomeTargetLink = new HtmlOutcomeTargetLink(); - outcomeTargetLink.putAttribute("outcome", "/" + targetDir + "/" + resource.getName() + "/search"); - outcomeTargetLink.setValue(StringUtils.uncamelCase(resource.getName())); - - HtmlTag listItem = new HtmlTag("li"); - listItem.getChildren().add(outcomeTargetLink); - unorderedList.getChildren().add(listItem); - } - - Writer writer = new IndentedWriter(new StringWriter(), this.navigationTemplateIndent); - unorderedList.write(writer); - Map context = CollectionUtils.newHashMap(); - context.put("navigation", writer.toString().trim()); - - if (this.navigationTemplate == null) - { - loadTemplates(); - } - - return ScaffoldUtil.createOrOverwrite(this.prompt, (FileResource) getTemplateStrategy() - .getDefaultTemplate(), - this.navigationTemplate.render(context), - overwrite); - } - - /** - * Parses the given XML and determines what namespaces it already declares. These are later removed from the list of - * namespaces that Metawidget introduces. - */ - - protected Map parseNamespaces(final String template) - { - Map namespaces = CollectionUtils.newHashMap(); - Document document = XmlUtils.documentFromString(template); - Element element = document.getDocumentElement(); - NamedNodeMap attributes = element.getAttributes(); - - for (int loop = 0, length = attributes.getLength(); loop < length; loop++) - { - org.w3c.dom.Node node = attributes.item(loop); - String nodeName = node.getNodeName(); - int indexOf = nodeName.indexOf(XMLNS_PREFIX); - - if (indexOf == -1) - { - continue; - } - - namespaces.put(nodeName.substring(indexOf + XMLNS_PREFIX.length()), node.getNodeValue()); - } - - return namespaces; - } - - /** - * Parses the given XML and determines the indent of the given String namespaces that Metawidget introduces. - */ - - protected int parseIndent(final String template, final String indentOf) - { - int indent = 0; - int indexOf = template.indexOf(indentOf); - - while ((indexOf >= 0) && (template.charAt(indexOf) != '\n')) - { - if (template.charAt(indexOf) == '\t') - { - indent++; - } - - indexOf--; - } - - return indent; - } - - /** - * Writes the entity Metawidget and its namespaces into the given context. - */ - - protected void writeEntityMetawidget(final Map context, final int entityMetawidgetIndent, - final Map existingNamespaces) - { - StringWriter stringWriter = new StringWriter(); - this.entityMetawidget.write(stringWriter, entityMetawidgetIndent); - context.put("metawidget", stringWriter.toString().trim()); - - Map namespaces = this.entityMetawidget.getNamespaces(); - namespaces.keySet().removeAll(existingNamespaces.keySet()); - context.put("metawidgetNamespaces", namespacesToString(namespaces)); - } - - /** - * Writes the search Metawidget, the bean Metawidget and their namespaces into the given context. - */ - - protected void writeSearchAndBeanMetawidget(final Map context, final int searchMetawidgetIndent, - final int beanMetawidgetIndent, - final Map existingNamespaces) - { - StringWriter stringWriter = new StringWriter(); - this.searchMetawidget.write(stringWriter, searchMetawidgetIndent); - context.put("searchMetawidget", stringWriter.toString().trim()); - - stringWriter = new StringWriter(); - this.beanMetawidget.write(stringWriter, beanMetawidgetIndent); - context.put("beanMetawidget", stringWriter.toString().trim()); - - Map namespaces = this.searchMetawidget.getNamespaces(); - namespaces.putAll(this.beanMetawidget.getNamespaces()); - namespaces.keySet().removeAll(existingNamespaces.keySet()); - context.put("metawidgetNamespaces", namespacesToString(namespaces)); - } - - protected String namespacesToString(final Map namespaces) - { - StringBuilder builder = new StringBuilder(); - - for (Map.Entry entry : namespaces.entrySet()) - { - // At the start, break out of the current quote. Field must be in quotes so that we're valid XML - - builder.append("\"\r\n\txmlns:"); - builder.append(entry.getKey()); - builder.append("=\""); - builder.append(entry.getValue()); - } - - return builder.toString(); - } -} +/* + * JBoss, Home of Professional Open Source + * Copyright 2011, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.forge.scaffold.faces; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.enterprise.event.Event; +import javax.inject.Inject; +import javax.persistence.CascadeType; +import javax.persistence.Id; +import javax.persistence.OneToOne; + +import org.jboss.forge.env.Configuration; +import org.jboss.forge.parser.JavaParser; +import org.jboss.forge.parser.java.*; +import org.jboss.forge.parser.xml.Node; +import org.jboss.forge.parser.xml.XMLParser; +import org.jboss.forge.project.Project; +import org.jboss.forge.project.dependencies.Dependency; +import org.jboss.forge.project.dependencies.DependencyBuilder; +import org.jboss.forge.project.facets.BaseFacet; +import org.jboss.forge.project.facets.DependencyFacet; +import org.jboss.forge.project.facets.JavaSourceFacet; +import org.jboss.forge.project.facets.WebResourceFacet; +import org.jboss.forge.project.facets.events.InstallFacets; +import org.jboss.forge.resources.FileResource; +import org.jboss.forge.resources.Resource; +import org.jboss.forge.scaffold.AccessStrategy; +import org.jboss.forge.scaffold.ScaffoldProvider; +import org.jboss.forge.scaffold.TemplateStrategy; +import org.jboss.forge.scaffold.faces.metawidget.config.ForgeConfigReader; +import org.jboss.forge.scaffold.util.ScaffoldUtil; +import org.jboss.forge.shell.ShellPrompt; +import org.jboss.forge.shell.plugins.Alias; +import org.jboss.forge.shell.plugins.RequiresFacet; +import org.jboss.forge.shell.util.Streams; +import org.jboss.forge.spec.javaee.CDIFacet; +import org.jboss.forge.spec.javaee.EJBFacet; +import org.jboss.forge.spec.javaee.FacesAPIFacet; +import org.jboss.forge.spec.javaee.FacesFacet; +import org.jboss.forge.spec.javaee.PersistenceFacet; +import org.jboss.forge.spec.javaee.ServletFacet; +import org.jboss.seam.render.TemplateCompiler; +import org.jboss.seam.render.spi.TemplateResolver; +import org.jboss.seam.render.template.CompiledTemplateResource; +import org.jboss.seam.render.template.resolver.ClassLoaderTemplateResolver; +import org.jboss.shrinkwrap.descriptor.api.spec.servlet.web.WebAppDescriptor; +import org.metawidget.statically.StaticMetawidget; +import org.metawidget.statically.StaticUtils.IndentedWriter; +import org.metawidget.statically.StaticWidget; +import org.metawidget.statically.faces.StaticFacesUtils; +import org.metawidget.statically.faces.component.html.StaticHtmlMetawidget; +import org.metawidget.statically.faces.component.html.widgetbuilder.HtmlOutcomeTargetLink; +import org.metawidget.statically.faces.component.html.widgetbuilder.ReadOnlyWidgetBuilder; +import org.metawidget.statically.faces.component.html.widgetbuilder.richfaces.RichFacesWidgetBuilder; +import org.metawidget.statically.html.widgetbuilder.HtmlTag; +import org.metawidget.statically.javacode.StaticJavaMetawidget; +import org.metawidget.util.ArrayUtils; +import org.metawidget.util.CollectionUtils; +import org.metawidget.util.XmlUtils; +import org.metawidget.util.simple.StringUtils; +import org.metawidget.widgetbuilder.composite.CompositeWidgetBuilder; +import org.metawidget.widgetbuilder.composite.CompositeWidgetBuilderConfig; +import org.metawidget.widgetbuilder.iface.WidgetBuilder; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; + +/** + * Facet to generate a Java Server Faces UI.

This facet utilizes Metawidget internally. This enables the use + * of the Metawidget SPI (pluggable WidgetBuilders, Layouts etc) for customizing + * the generated User Interface. For more information on writing Metawidget + * plugins, see the Metawidget + * documentation.

This Facet does not require Metawidget to be + * in the final project. + * + * @author Lincoln Baxter, III + * @author Richard Kennard + */ +@Alias("faces") +@RequiresFacet({WebResourceFacet.class, + DependencyFacet.class, + PersistenceFacet.class, + EJBFacet.class, + CDIFacet.class, + FacesFacet.class}) +public class FacesScaffold extends BaseFacet implements ScaffoldProvider { + // + // Private statics + // + + private static final String XMLNS_PREFIX = "xmlns:"; + private static final String BACKING_BEAN_TEMPLATE = "scaffold/faces/BackingBean.jv"; + private static final String VIEW_UTILS_TEMPLATE = "scaffold/faces/ViewUtils.jv"; + private static final String TAGLIB_TEMPLATE = "scaffold/faces/forge.taglib.xml"; + private static final String VIEW_TEMPLATE = "scaffold/faces/view.xhtml"; + private static final String CREATE_TEMPLATE = "scaffold/faces/create.xhtml"; + private static final String SEARCH_TEMPLATE = "scaffold/faces/search.xhtml"; + private static final String NAVIGATION_TEMPLATE = "scaffold/faces/page.xhtml"; + private static final String ERROR_TEMPLATE = "scaffold/faces/error.xhtml"; + private static final String INDEX_TEMPLATE = "scaffold/faces/index.xhtml"; + private final Dependency richfaces3UI = DependencyBuilder.create("org.richfaces.ui:richfaces-ui"); + private final Dependency richfaces3Impl = DependencyBuilder.create("org.richfaces.framework:richfaces-impl"); + private final Dependency richfaces4UI = DependencyBuilder.create("org.richfaces.ui:richfaces-components-ui"); + private final Dependency richfaces4Impl = DependencyBuilder.create("org.richfaces.core:richfaces-core-impl"); + // + // Protected members (nothing is private, to help subclassing) + // + protected CompiledTemplateResource backingBeanTemplate; + protected int backingBeanTemplateQbeMetawidgetIndent; + protected CompiledTemplateResource viewUtilsTemplate; + protected CompiledTemplateResource taglibTemplate; + protected CompiledTemplateResource viewTemplate; + protected Map viewTemplateNamespaces; + protected int viewTemplateEntityMetawidgetIndent; + protected CompiledTemplateResource createTemplate; + protected Map createTemplateNamespaces; + protected int createTemplateEntityMetawidgetIndent; + protected CompiledTemplateResource searchTemplate; + protected Map searchTemplateNamespaces; + protected int searchTemplateSearchMetawidgetIndent; + protected int searchTemplateBeanMetawidgetIndent; + protected CompiledTemplateResource navigationTemplate; + protected int navigationTemplateIndent; + protected CompiledTemplateResource errorTemplate; + protected CompiledTemplateResource indexTemplate; + protected TemplateResolver resolver; + protected final ShellPrompt prompt; + protected final TemplateCompiler compiler; + protected final Event install; + protected StaticHtmlMetawidget entityMetawidget; + protected StaticHtmlMetawidget searchMetawidget; + protected StaticHtmlMetawidget beanMetawidget; + protected StaticJavaMetawidget qbeMetawidget; +// @Inject AnnotationLookup annotationLookup; +// @Inject RelationResolverWidgetProcessor relationResolver; +// @Inject RelationResolverWidgetProcessorConfig relationResolverConfig; + + private Configuration config; + + // + // Constructor + // + @Inject + public FacesScaffold(final Configuration config, + final ShellPrompt prompt, + final TemplateCompiler compiler, + final Event install) + { + this.config = config; + this.prompt = prompt; + this.compiler = compiler; + this.install = install; + + this.resolver = new ClassLoaderTemplateResolver(FacesScaffold.class.getClassLoader()); + + if (this.compiler != null) { + this.compiler.getTemplateResolverFactory().addResolver(this.resolver); + } + } + + // + // Public methods + // + @Override + public List> setup(String targetDir, final Resource template, final boolean overwrite) + { + List> resources = generateIndex(targetDir, template, overwrite); + setupWebXML(); + + return resources; + } + + /** + * Overridden to setup the Metawidgets.

Metawidgets must be configured + * per project and per Forge invocation. It is not sufficient to + * simply configure them in + * setup because the user may restart Forge and not run + * scaffold setup a second time. + */ + @Override + public void setProject(Project project) { + super.setProject(project); + + ForgeConfigReader configReader = new ForgeConfigReader(this.config, this.project); + + this.entityMetawidget = new StaticHtmlMetawidget(); + this.entityMetawidget.setConfigReader(configReader); + this.entityMetawidget.setConfig("scaffold/faces/metawidget-entity.xml"); + + this.searchMetawidget = new StaticHtmlMetawidget(); + this.searchMetawidget.setConfigReader(configReader); + this.searchMetawidget.setConfig("scaffold/faces/metawidget-search.xml"); + + this.beanMetawidget = new StaticHtmlMetawidget(); + this.beanMetawidget.setConfigReader(configReader); + this.beanMetawidget.setConfig("scaffold/faces/metawidget-bean.xml"); + + this.qbeMetawidget = new StaticJavaMetawidget(); + this.qbeMetawidget.setConfigReader(configReader); + this.qbeMetawidget.setConfig("scaffold/faces/metawidget-qbe.xml"); + + } + + @Override + public List> generateFromEntity(String targetDir, final Resource template, final JavaClass entity, + final boolean overwrite) + { + // FORGE-460: setupRichFaces during generateFromEntity, not during setup, as generally 'richfaces setup' is called + // *after* 'scaffold setup' + + setupRichFaces(); + + // Track the list of resources generated + + List> result = new ArrayList>(); + try { + JavaSourceFacet java = this.project.getFacet(JavaSourceFacet.class); + WebResourceFacet web = this.project.getFacet(WebResourceFacet.class); + + loadTemplates(); + Map context = CollectionUtils.newHashMap(); + context.put("entity", entity); + String ccEntity = StringUtils.decapitalize(entity.getName()); + context.put("ccEntity", ccEntity); + setPrimaryKeyMetaData(context, entity); + + // Prepare qbeMetawidget + this.qbeMetawidget.setPath(entity.getQualifiedName()); + StringWriter stringWriter = new StringWriter(); + this.qbeMetawidget.write(stringWriter, this.backingBeanTemplateQbeMetawidgetIndent); + + context.put("qbeMetawidget", stringWriter.toString().trim()); + context.put("qbeMetawidgetImports", + CollectionUtils.toString(this.qbeMetawidget.getImports(), ";\r\nimport ", true, false)); + + // Create the Backing Bean for this entity + JavaClass viewBean = JavaParser.parse(JavaClass.class, this.backingBeanTemplate.render(context)); + viewBean.setPackage(java.getBasePackage() + ".view"); + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, java.getJavaResource(viewBean), viewBean.toString(), + overwrite)); + + // Set new context for view generation + context = getTemplateContext(template); + String beanName = StringUtils.decapitalize(viewBean.getName()); + context.put("beanName", beanName); + context.put("ccEntity", ccEntity); + context.put("entityName", StringUtils.uncamelCase(entity.getName())); + setPrimaryKeyMetaData(context, entity); + + // Prepare entityMetawidget + this.entityMetawidget.setValue(StaticFacesUtils.wrapExpression(beanName + "." + ccEntity)); + this.entityMetawidget.setPath(entity.getQualifiedName()); + this.entityMetawidget.setReadOnly(false); + this.entityMetawidget.setStyle(null); + + // Generate create + writeEntityMetawidget(context, this.createTemplateEntityMetawidgetIndent, this.createTemplateNamespaces); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, + web.getWebResource(targetDir + "/" + ccEntity + "/create.xhtml"), + this.createTemplate.render(context), + overwrite)); + + // Generate view + this.entityMetawidget.setReadOnly(true); + writeEntityMetawidget(context, this.viewTemplateEntityMetawidgetIndent, this.viewTemplateNamespaces); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, + web.getWebResource(targetDir + "/" + ccEntity + "/view.xhtml"), + this.viewTemplate.render(context), overwrite)); + + // Generate search + this.searchMetawidget.setValue(StaticFacesUtils.wrapExpression(beanName + ".search")); + this.searchMetawidget.setPath(entity.getQualifiedName()); + this.beanMetawidget.setValue(StaticFacesUtils.wrapExpression(beanName + ".pageItems")); + this.beanMetawidget.setPath(viewBean.getQualifiedName() + "/pageItems"); + writeSearchAndBeanMetawidget(context, this.searchTemplateSearchMetawidgetIndent, + this.searchTemplateBeanMetawidgetIndent, this.searchTemplateNamespaces); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, + web.getWebResource(targetDir + "/" + ccEntity + "/search.xhtml"), + this.searchTemplate.render(context), overwrite)); + + // Generate navigation + result.add(generateNavigation(targetDir, overwrite)); + + // Need ViewUtils and forge.taglib.xml for forgeview:asList + JavaClass viewUtils = JavaParser.parse(JavaClass.class, this.viewUtilsTemplate.render(context)); + viewUtils.setPackage(viewBean.getPackage()); + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, java.getJavaResource(viewUtils), viewUtils.toString(), + true)); + + context.put("viewPackage", viewBean.getPackage()); + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, + web.getWebResource("WEB-INF/classes/META-INF/forge.taglib.xml"), + this.taglibTemplate.render(context), true)); + + createInitializers(entity); + this.project.getFacet(JavaSourceFacet.class).saveJavaSource(entity); + + } catch (Exception e) { + throw new RuntimeException("Error generating default scaffolding: " + e.getMessage(), e); + } + return result; + } + + @Override + @SuppressWarnings("unchecked") + public boolean install() { + if (!(this.project.hasFacet(WebResourceFacet.class) && this.project.hasFacet(PersistenceFacet.class) + && this.project.hasFacet(CDIFacet.class) && this.project.hasFacet(FacesFacet.class))) { + this.install.fire(new InstallFacets(WebResourceFacet.class, PersistenceFacet.class, CDIFacet.class, + FacesFacet.class)); + } + + return true; + } + + @Override + public boolean isInstalled() { + return true; + } + + @Override + public List> generateIndex(String targetDir, final Resource template, final boolean overwrite) + { + List> result = new ArrayList>(); + WebResourceFacet web = this.project.getFacet(WebResourceFacet.class); + + this.project.getFacet(ServletFacet.class).getConfig().welcomeFile("/index.html"); + loadTemplates(); + + generateTemplates(targetDir, overwrite); + HashMap context = getTemplateContext(template); + + // Basic pages + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/index.html"), getClass() + .getResourceAsStream("/scaffold/faces/index.html"), overwrite)); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/index.xhtml"), + this.indexTemplate.render(context), overwrite)); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("error.xhtml"), + this.errorTemplate.render(context), overwrite)); + + // Static resources + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/add.png"), + getClass().getResourceAsStream("/scaffold/faces/add.png"), overwrite)); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/background.gif"), + getClass().getResourceAsStream("/scaffold/faces/background.gif"), overwrite)); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/false.png"), + getClass().getResourceAsStream("/scaffold/faces/false.png"), overwrite)); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/favicon.ico"), + getClass().getResourceAsStream("/scaffold/faces/favicon.ico"), overwrite)); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/forge-logo.png"), + getClass().getResourceAsStream("/scaffold/faces/forge-logo.png"), overwrite)); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/forge-style.css"), + getClass().getResourceAsStream("/scaffold/faces/forge-style.css"), overwrite)); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/jboss-community.png"), + getClass().getResourceAsStream("/scaffold/faces/jboss-community.png"), overwrite)); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/remove.png"), + getClass().getResourceAsStream("/scaffold/faces/remove.png"), overwrite)); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/search.png"), + getClass().getResourceAsStream("/scaffold/faces/search.png"), overwrite)); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, web.getWebResource("/resources/true.png"), + getClass().getResourceAsStream("/scaffold/faces/true.png"), overwrite)); + + return result; + } + + @Override + public List> getGeneratedResources(String targetDir) + { + throw new RuntimeException("Not yet implemented!"); + } + + @Override + public AccessStrategy getAccessStrategy() { + return new FacesAccessStrategy(this.project); + } + + @Override + public TemplateStrategy getTemplateStrategy() { + return new FacesTemplateStrategy(this.project); + } + + @Override + public List> generateTemplates(String targetDir, final boolean overwrite) + { + List> result = new ArrayList>(); + + try { + WebResourceFacet web = this.project.getFacet(WebResourceFacet.class); + + result.add(ScaffoldUtil.createOrOverwrite(this.prompt, + web.getWebResource("/resources/scaffold/paginator.xhtml"), + getClass().getResourceAsStream("/scaffold/faces/paginator.xhtml"), + overwrite)); + + result.add(generateNavigation(targetDir, overwrite)); + } + catch (Exception e) + { + throw new RuntimeException("Error generating default templates", e); + } + + return result; + } + + // + // Protected methods (nothing is private, to help subclassing) + // + protected void loadTemplates() { + if (this.backingBeanTemplate == null) { + this.backingBeanTemplate = this.compiler.compile(BACKING_BEAN_TEMPLATE); + String template = Streams.toString(this.backingBeanTemplate.getSourceTemplateResource().getInputStream()); + this.backingBeanTemplateQbeMetawidgetIndent = parseIndent(template, "@{qbeMetawidget}"); + } + if (this.viewUtilsTemplate == null) { + this.viewUtilsTemplate = this.compiler.compile(VIEW_UTILS_TEMPLATE); + } + if (this.taglibTemplate == null) { + this.taglibTemplate = this.compiler.compile(TAGLIB_TEMPLATE); + } + if (this.viewTemplate == null) { + this.viewTemplate = this.compiler.compile(VIEW_TEMPLATE); + String template = Streams.toString(this.viewTemplate.getSourceTemplateResource().getInputStream()); + this.viewTemplateNamespaces = parseNamespaces(template); + this.viewTemplateEntityMetawidgetIndent = parseIndent(template, "@{metawidget}"); + } + if (this.createTemplate == null) { + this.createTemplate = this.compiler.compile(CREATE_TEMPLATE); + String template = Streams.toString(this.createTemplate.getSourceTemplateResource().getInputStream()); + this.createTemplateNamespaces = parseNamespaces(template); + this.createTemplateEntityMetawidgetIndent = parseIndent(template, "@{metawidget}"); + } + if (this.searchTemplate == null) { + this.searchTemplate = this.compiler.compile(SEARCH_TEMPLATE); + String template = Streams.toString(this.searchTemplate.getSourceTemplateResource().getInputStream()); + this.searchTemplateNamespaces = parseNamespaces(template); + this.searchTemplateSearchMetawidgetIndent = parseIndent(template, "@{searchMetawidget}"); + this.searchTemplateBeanMetawidgetIndent = parseIndent(template, "@{beanMetawidget}"); + } + if (this.navigationTemplate == null) { + this.navigationTemplate = this.compiler.compile(NAVIGATION_TEMPLATE); + String template = Streams.toString(this.navigationTemplate.getSourceTemplateResource().getInputStream()); + this.navigationTemplateIndent = parseIndent(template, "@{navigation}"); + } + if (this.errorTemplate == null) { + this.errorTemplate = this.compiler.compile(ERROR_TEMPLATE); + } + if (this.indexTemplate == null) { + this.indexTemplate = this.compiler.compile(INDEX_TEMPLATE); + } + } + + protected void setupRichFaces() { + if ((this.project.getFacet(DependencyFacet.class).hasEffectiveDependency(this.richfaces3UI) + && this.project.getFacet(DependencyFacet.class).hasEffectiveDependency(this.richfaces3Impl)) + || (this.project.getFacet(DependencyFacet.class).hasEffectiveDependency(this.richfaces4UI) + && this.project.getFacet(DependencyFacet.class).hasEffectiveDependency(this.richfaces4Impl))) { + this.entityMetawidget.setWidgetBuilder(insertRichFacesWidgetBuilder((CompositeWidgetBuilder) this.entityMetawidget.getWidgetBuilder())); + + this.searchMetawidget.setWidgetBuilder(insertRichFacesWidgetBuilder((CompositeWidgetBuilder) this.searchMetawidget.getWidgetBuilder())); + + this.beanMetawidget.setWidgetBuilder(insertRichFacesWidgetBuilder((CompositeWidgetBuilder) this.beanMetawidget.getWidgetBuilder())); + } + } + + /** + * Locates a + * ReadOnlyWidgetBuilder in the list of WidgetBuilders, and + * inserts a + * RichFacesWidgetBuilder after it (unless there's a + * RichFacesWidgetBuilder in there already). + */ + protected CompositeWidgetBuilder insertRichFacesWidgetBuilder( + final CompositeWidgetBuilder compositeWidgetBuilder) { + // Get the current WidgetBuilders... + + WidgetBuilder[] existingWidgetBuilders = compositeWidgetBuilder.getWidgetBuilders(); + + // ...find the ReadOnlyWidgetBuilder (if any)... + + int addAt = 0; + + for (int loop = 0; loop < existingWidgetBuilders.length; loop++) { + // ...(abort if there's already a RichFacesWidgetBuilder)... + + // Use an Object loop variable here to avoid a nasty Java/Generics compiler bug + Object widgetBuilder = existingWidgetBuilders[loop]; + if (widgetBuilder instanceof RichFacesWidgetBuilder) { + return compositeWidgetBuilder; + } + + if (widgetBuilder instanceof ReadOnlyWidgetBuilder) { + addAt = loop + 1; + } + } + + // ...and insert our RichFacesWidgetBuilder just after it + + @SuppressWarnings("unchecked") + WidgetBuilder[] newWidgetBuilders = (WidgetBuilder[]) ArrayUtils.addAt(existingWidgetBuilders, addAt, + new RichFacesWidgetBuilder()); + + return new CompositeWidgetBuilder( + new CompositeWidgetBuilderConfig().setWidgetBuilders(newWidgetBuilders)); + } + + protected void createInitializers(final JavaClass entity) { + for (Field field : entity.getFields()) { + if (field.hasAnnotation(OneToOne.class)) { + Annotation oneToOne = field.getAnnotation(OneToOne.class); + if (oneToOne.getStringValue("mappedBy") == null) { + oneToOne.setEnumValue("cascade", CascadeType.ALL); + } + String methodName = "new" + field.getTypeInspector().getName(); + if (!entity.hasMethodSignature(methodName)) { + entity.addMethod().setName(methodName).setReturnTypeVoid().setPublic().setBody("this." + field.getName() + " = new " + field.getType() + "();"); + } + } + } + for (Method method : entity.getMethods()) { + if (method.hasAnnotation(OneToOne.class)) { + Annotation oneToOne = method.getAnnotation(OneToOne.class); + if (oneToOne.getStringValue("mappedBy") == null) { + oneToOne.setEnumValue("cascade", CascadeType.ALL); + } + String methodName = "new" + method.getReturnTypeInspector().getName(); + if (!entity.hasMethodSignature(methodName)) { + entity.addMethod().setName(methodName).setReturnTypeVoid().setPublic().setBody("this." + method.getName() + " = new " + method.getReturnType() + "();"); + } + } + } + } + + protected HashMap getTemplateContext(final Resource template) { + HashMap context; + context = new HashMap(); + context.put("template", template); + context.put("templateStrategy", getTemplateStrategy()); + return context; + } + + protected void setupWebXML() { + ServletFacet servlet = this.project.getFacet(ServletFacet.class); + + Node webXML = removeConflictingErrorPages(servlet); + servlet.getConfigFile().setContents(XMLParser.toXMLInputStream(webXML)); + + WebAppDescriptor servletConfig = servlet.getConfig(); + WebResourceFacet web = this.project.getFacet(WebResourceFacet.class); + + // (prefer /faces/error.xhtml) + + String errorLocation = getAccessStrategy().getWebPaths(web.getWebResource("error.xhtml")).get(1); + servletConfig.errorPage(404, errorLocation); + servletConfig.errorPage(500, errorLocation); + + servlet.saveConfig(servletConfig); + } + + protected Node removeConflictingErrorPages(final ServletFacet servlet) { + Node webXML = XMLParser.parse(servlet.getConfigFile().getResourceInputStream()); + Node root = webXML.getRoot(); + List errorPages = root.get("error-page"); + + for (String code : Arrays.asList("404", "500")) { + for (Node errorPage : errorPages) { + if (code.equals(errorPage.getSingle("error-code").getText()) + && this.prompt.promptBoolean("Your web.xml already contains an error page for " + code + + " status codes, replace it?")) { + root.removeChild(errorPage); + } + } + } + return webXML; + } + + /** + * Generates the navigation menu based on scaffolded entities. + */ + + protected Resource generateNavigation(final String targetDir, final boolean overwrite) + throws IOException + { + WebResourceFacet web = this.project.getFacet(WebResourceFacet.class); + HtmlTag unorderedList = new HtmlTag("ul"); + + for (Resource resource : web.getWebResource(targetDir).listResources()) + { + HtmlOutcomeTargetLink outcomeTargetLink = new HtmlOutcomeTargetLink(); + outcomeTargetLink.putAttribute("outcome", "/" + targetDir + "/" + resource.getName() + "/search"); + outcomeTargetLink.setValue(StringUtils.uncamelCase(resource.getName())); + + HtmlTag listItem = new HtmlTag("li"); + listItem.getChildren().add(outcomeTargetLink); + unorderedList.getChildren().add(listItem); + } + + Writer writer = new IndentedWriter(new StringWriter(), this.navigationTemplateIndent); + unorderedList.write(writer); + Map context = CollectionUtils.newHashMap(); + context.put("navigation", writer.toString().trim()); + + if (this.navigationTemplate == null) { + loadTemplates(); + } + + return ScaffoldUtil.createOrOverwrite(this.prompt, (FileResource) getTemplateStrategy().getDefaultTemplate(), + this.navigationTemplate.render(context), + overwrite); + } + + /** + * Parses the given XML and determines what namespaces it already declares. + * These are later removed from the list of namespaces that Metawidget + * introduces. + */ + protected Map parseNamespaces(final String template) { + Map namespaces = CollectionUtils.newHashMap(); + Document document = XmlUtils.documentFromString(template); + Element element = document.getDocumentElement(); + NamedNodeMap attributes = element.getAttributes(); + + for (int loop = 0, length = attributes.getLength(); loop < length; loop++) { + org.w3c.dom.Node node = attributes.item(loop); + String nodeName = node.getNodeName(); + int indexOf = nodeName.indexOf(XMLNS_PREFIX); + + if (indexOf == -1) { + continue; + } + + namespaces.put(nodeName.substring(indexOf + XMLNS_PREFIX.length()), node.getNodeValue()); + } + + return namespaces; + } + + /** + * Parses the given XML and determines the indent of the given String + * namespaces that Metawidget introduces. + */ + protected int parseIndent(final String template, final String indentOf) { + int indent = 0; + int indexOf = template.indexOf(indentOf); + + while ((indexOf >= 0) && (template.charAt(indexOf) != '\n')) { + if (template.charAt(indexOf) == '\t') { + indent++; + } + + indexOf--; + } + + return indent; + } + + /** + * Writes the entity Metawidget and its namespaces into the given context. + */ + protected void writeEntityMetawidget(final Map context, final int entityMetawidgetIndent, + final Map existingNamespaces) { + StringWriter stringWriter = new StringWriter(); + this.entityMetawidget.write(stringWriter, entityMetawidgetIndent); + context.put("metawidget", stringWriter.toString().trim()); + + Map namespaces = this.entityMetawidget.getNamespaces(); + namespaces.keySet().removeAll(existingNamespaces.keySet()); + context.put("metawidgetNamespaces", namespacesToString(namespaces)); + } + + /** + * Writes the search Metawidget, the bean Metawidget and their namespaces + * into the given context. + */ + protected void writeSearchAndBeanMetawidget(final Map context, final int searchMetawidgetIndent, + final int beanMetawidgetIndent, + final Map existingNamespaces) { + StringWriter stringWriter = new StringWriter(); + this.searchMetawidget.write(stringWriter, searchMetawidgetIndent); + context.put("searchMetawidget", stringWriter.toString().trim()); + + stringWriter = new StringWriter(); + this.beanMetawidget.write(stringWriter, beanMetawidgetIndent); + context.put("beanMetawidget", stringWriter.toString().trim()); + + Map namespaces = this.searchMetawidget.getNamespaces(); + namespaces.putAll(this.beanMetawidget.getNamespaces()); + namespaces.keySet().removeAll(existingNamespaces.keySet()); + context.put("metawidgetNamespaces", namespacesToString(namespaces)); + } + + protected String namespacesToString(final Map namespaces) { + StringBuilder builder = new StringBuilder(); + + for (Map.Entry entry : namespaces.entrySet()) { + // At the start, break out of the current quote. Field must be in quotes so that we're valid XML + + builder.append("\"\r\n\txmlns:"); + builder.append(entry.getKey()); + builder.append("=\""); + builder.append(entry.getValue()); + } + + return builder.toString(); + } + + private void setPrimaryKeyMetaData(Map context, final JavaClass entity) { + String pkName = "id"; + String pkType = "Long"; + String nullablePkType = "Long"; + for (Member m : entity.getMembers()) { + if (m.hasAnnotation(Id.class)) { + if (m instanceof Field) { + Field field = (Field) m; + pkName = field.getName(); + pkType = field.getType(); + break; + } else if (m instanceof Member) { + Method method = (Method) m; + pkName = method.getName().substring(3); + if (method.getName().startsWith("get")) { + pkType = method.getReturnType(); + } else { + pkType = ((Parameter) method.getParameters().get(0)).getType(); + } + break; + } + } + } + + if ("int".equals(pkType)) { + nullablePkType = Integer.class.getSimpleName(); + } else if ("short".equals(pkType)) { + nullablePkType = Short.class.getSimpleName(); + } else if ("byte".equals(pkType)) { + nullablePkType = Byte.class.getSimpleName(); + } + + context.put("primaryKey", pkName); + context.put("primaryKeyCC", StringUtils.capitalize(pkName)); + context.put("primaryKeyType", pkType); + context.put("nullablePrimaryKeyType", nullablePkType); + } +} diff --git a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/config/ForgeConfigReader.java b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/config/ForgeConfigReader.java index 021b410434..5dba48d9ab 100644 --- a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/config/ForgeConfigReader.java +++ b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/config/ForgeConfigReader.java @@ -23,6 +23,7 @@ import org.jboss.forge.env.Configuration; import org.jboss.forge.project.Project; +import org.jboss.forge.scaffold.faces.util.AnnotationLookup; import org.metawidget.config.impl.BaseConfigReader; /** @@ -42,12 +43,15 @@ public class ForgeConfigReader private static final String PROJECT_ELEMENT_NAME = "forgeProject"; + private static final String ANNOTATION_LOOKUP = "annotationLookup"; + // // Private members // private Configuration config; private Project project; + private AnnotationLookup annotationLookup; // // Constructor @@ -57,6 +61,7 @@ public ForgeConfigReader(Configuration config, Project project) { this.config = config; this.project = project; + this.annotationLookup = new AnnotationLookup(project); } // @@ -66,7 +71,7 @@ public ForgeConfigReader(Configuration config, Project project) @Override protected boolean isNative(String name) { - if (PROJECT_ELEMENT_NAME.equals(name)) + if (PROJECT_ELEMENT_NAME.equals(name) || ANNOTATION_LOOKUP.equals(name)) { return true; } @@ -86,6 +91,9 @@ protected Object createNative(String name, Class namespace, String recordedTe { return this.project; } + if (ANNOTATION_LOOKUP.equals(name)) { + return this.annotationLookup; + } if(CONFIG_ELEMENT_NAME.equals(name)) { @@ -94,4 +102,5 @@ protected Object createNative(String name, Class namespace, String recordedTe return super.createNative(name, namespace, recordedText); } + } diff --git a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspectionResultConstants.java b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspectionResultConstants.java index a85e7c99ec..6d7ed0c9f4 100644 --- a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspectionResultConstants.java +++ b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspectionResultConstants.java @@ -37,6 +37,16 @@ public final class ForgeInspectionResultConstants public static final String ONE_TO_ONE = "one-to-one"; + public static final String PRIMARY_KEY = "primary-key"; + + public static final String PRIMARY_KEY_NOT_GENERATED = "primary-key-not-generated"; + + public static final String ENTITY_PRIMARY_KEY = "entity-primary-key"; + + public static final String REVERSE_PRIMARY_KEY = "reverse-primary-key"; + + public static final String REVERSE_PRIMARY_KEY_TYPE = "reverse-primary-key-type"; + // // Private constructor // diff --git a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspector.java b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspector.java index 770581d495..7358571653 100644 --- a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspector.java +++ b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspector.java @@ -21,6 +21,7 @@ */ package org.jboss.forge.scaffold.faces.metawidget.inspector; +import org.jboss.forge.scaffold.faces.util.AnnotationLookup; import static org.jboss.forge.scaffold.faces.metawidget.inspector.ForgeInspectionResultConstants.*; import static org.metawidget.inspector.InspectionResultConstants.*; import static org.metawidget.inspector.faces.StaticFacesInspectionResultConstants.*; @@ -28,15 +29,12 @@ import java.util.List; import java.util.Map; -import javax.persistence.Embedded; -import javax.persistence.ManyToMany; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; +import javax.persistence.*; import org.jboss.forge.parser.java.EnumConstant; import org.jboss.forge.parser.java.JavaEnum; import org.jboss.forge.scaffold.faces.metawidget.inspector.propertystyle.ForgePropertyStyle.ForgeProperty; +import org.jboss.solder.logging.Logger; import org.metawidget.inspector.impl.BaseObjectInspector; import org.metawidget.inspector.impl.BaseObjectInspectorConfig; import org.metawidget.inspector.impl.propertystyle.Property; @@ -50,76 +48,65 @@ * * @author Richard Kennard */ - public class ForgeInspector - extends BaseObjectInspector -{ + extends BaseObjectInspector { + + ForgeInspectorConfig config; + Logger log = Logger.getLogger(getClass()); + // // Constructor // - - public ForgeInspector() - { - this(new BaseObjectInspectorConfig()); + public ForgeInspector() { + super(new BaseObjectInspectorConfig()); } - public ForgeInspector(BaseObjectInspectorConfig config) - { + public ForgeInspector(ForgeInspectorConfig config) { super(config); + this.config = config; } // // Protected methods // - @Override protected Map inspectProperty(Property property) - throws Exception - { + throws Exception { Map attributes = CollectionUtils.newHashMap(); // OneToOne - if (property.isAnnotationPresent(OneToOne.class) || property.isAnnotationPresent(Embedded.class)) - { + if (property.isAnnotationPresent(OneToOne.class) || property.isAnnotationPresent(Embedded.class)) { attributes.put(ONE_TO_ONE, TRUE); } // ManyToOne - if (property.isAnnotationPresent(ManyToOne.class)) - { - attributes - .put(FACES_LOOKUP, - StaticFacesUtils.wrapExpression(StringUtils.decapitalize(ClassUtils.getSimpleName(property - .getType())) + "Bean.all")); - - attributes - .put(FACES_CONVERTER_ID, - StaticFacesUtils.wrapExpression(StringUtils.decapitalize(ClassUtils.getSimpleName(property - .getType())) + "Bean.converter")); + if (property.isAnnotationPresent(ManyToOne.class)) { + attributes.put(FACES_LOOKUP, + StaticFacesUtils.wrapExpression(StringUtils.decapitalize(ClassUtils.getSimpleName(property.getType())) + "Bean.all")); + + attributes.put(FACES_CONVERTER_ID, + StaticFacesUtils.wrapExpression(StringUtils.decapitalize(ClassUtils.getSimpleName(property.getType())) + "Bean.converter")); } // OneToMany and ManyToMany - if (property.isAnnotationPresent(OneToMany.class) || property.isAnnotationPresent(ManyToMany.class)) - { + if (property.isAnnotationPresent(OneToMany.class) || property.isAnnotationPresent(ManyToMany.class)) { attributes.put(N_TO_MANY, TRUE); } // Enums - if ( property instanceof ForgeProperty ) { + if (property instanceof ForgeProperty) { List> enumConstants = ((ForgeProperty) property).getEnumConstants(); - if (enumConstants != null) - { + if (enumConstants != null) { List lookup = CollectionUtils.newArrayList(); - for (EnumConstant anEnum : enumConstants) - { + for (EnumConstant anEnum : enumConstants) { lookup.add(anEnum.getName()); } @@ -127,6 +114,62 @@ protected Map inspectProperty(Property property) } } + // do @Id specific handling + if (null != property.getAnnotation(Id.class)) { + attributes.put(PRIMARY_KEY, property.getName()); + + if (null != property.getAnnotation(GeneratedValue.class)) { + attributes.put(PRIMARY_KEY_NOT_GENERATED, FALSE); + } else { + attributes.put(PRIMARY_KEY_NOT_GENERATED, TRUE); + } + } + + if (null != property.getAnnotation(ManyToOne.class)) { + attributes.put(REVERSE_PRIMARY_KEY_TYPE, property.getType()); + } + + if (attributes.containsKey(PRIMARY_KEY) && !TRUE.equals(attributes.get(PRIMARY_KEY_NOT_GENERATED))) { + // if primary key is not generated it cannot be hidden in view + attributes.remove(HIDDEN); + attributes.put(REQUIRED, TRUE); + } + + if (config != null && config.getAnnotationLookup() != null) { + final AnnotationLookup annotationLookup = config.getAnnotationLookup(); + + if (attributes.containsKey(REVERSE_PRIMARY_KEY_TYPE) && null != annotationLookup) { + try { + final String reverseKey = annotationLookup.getFieldName(Id.class, attributes.get(REVERSE_PRIMARY_KEY_TYPE)); + attributes.put(REVERSE_PRIMARY_KEY, reverseKey); + } catch (Exception e) { + throw new RuntimeException("cannot resolve reverse primary key", e); + } + } + } + + return attributes; + } + + @Override + protected Map inspectEntity(String declaredClass, String actualClass) throws Exception { + Map attributes = CollectionUtils.newHashMap(); + Map superMap = super.inspectEntity(declaredClass, actualClass); + if (superMap != null) + attributes.putAll(superMap); + + if (config != null && config.getAnnotationLookup() != null) { + final AnnotationLookup annotationLookup = config.getAnnotationLookup(); + + try { + final String primaryKey = annotationLookup.getFieldName(Id.class, declaredClass); + attributes.put(PRIMARY_KEY, primaryKey); + } catch (Exception e) { + log.debug("cannot resolve primary key for class "+declaredClass, e); + } + } return attributes; } + + } diff --git a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspectorConfig.java b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspectorConfig.java new file mode 100644 index 0000000000..afe9c572c2 --- /dev/null +++ b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspectorConfig.java @@ -0,0 +1,65 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2011, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.forge.scaffold.faces.metawidget.inspector; + +import org.jboss.forge.project.Project; +import org.jboss.forge.scaffold.faces.util.AnnotationLookup; +import org.metawidget.config.iface.NeedsResourceResolver; +import org.metawidget.config.iface.ResourceResolver; +import org.metawidget.inspector.impl.BaseObjectInspectorConfig; + +/** + * + * @author Thomas Frühbeck + */ +public class ForgeInspectorConfig extends BaseObjectInspectorConfig implements NeedsResourceResolver { + + ResourceResolver resolver; + Project project; + AnnotationLookup annotationLookup; + + @Override + public void setResourceResolver(ResourceResolver resourceResolver) { + this.resolver = resourceResolver; + } + + public ResourceResolver getResolver() { + return resolver; + } + + public void setProject(Project project) { + this.project = project; + } + + public Project getProject() { + return project; + } + + public void setAnnotationLookup(AnnotationLookup annotationLookup) { + this.annotationLookup = annotationLookup; + } + + public AnnotationLookup getAnnotationLookup() { + return annotationLookup; + } + +} diff --git a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/processor/ForgeInspectionResultProcessor.java b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/processor/ForgeInspectionResultProcessor.java new file mode 100644 index 0000000000..786066f26f --- /dev/null +++ b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/processor/ForgeInspectionResultProcessor.java @@ -0,0 +1,87 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2011, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.forge.scaffold.faces.metawidget.processor; + +import java.util.Map; +import static org.metawidget.inspector.InspectionResultConstants.*; +import static org.jboss.forge.scaffold.faces.metawidget.inspector.ForgeInspectionResultConstants.*; +import org.jboss.forge.project.Project; +import org.metawidget.inspectionresultprocessor.iface.InspectionResultProcessor; +import org.metawidget.statically.StaticMetawidget; +import org.metawidget.statically.javacode.StaticJavaMetawidget; +import org.metawidget.util.XmlUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * + * @author Thomas Frühbeck + */ +public class ForgeInspectionResultProcessor implements InspectionResultProcessor { + + private Project project; + + public ForgeInspectionResultProcessor() { + } + + public void setProject(Project project) { + this.project = project; + } + + @Override + public String processInspectionResult(String inspectionResult, StaticMetawidget metawidget, Object toInspect, String type, String... names) { + + Document document = XmlUtils.documentFromString(inspectionResult); + NodeList entities = document.getElementsByTagName(ENTITY); + + if (entities.getLength() > 0) { + for (int i=0; i attributes = XmlUtils.getAttributesAsMap(entity); + + String primaryKey = attributes.get(PRIMARY_KEY); + if (null != primaryKey) { + + NodeList properties = document.getElementsByTagName(PROPERTY); + if (properties.getLength() > 0) { + for (int j=0; j propAttribs = XmlUtils.getAttributesAsMap(property); + propAttribs.put(ENTITY_PRIMARY_KEY, primaryKey); + + XmlUtils.setMapAsAttributes(property, propAttribs); + } + } + } + } + } + + inspectionResult = XmlUtils.documentToString(document, false); + + return inspectionResult; + } + +} diff --git a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/widgetbuilder/EntityWidgetBuilder.java b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/widgetbuilder/EntityWidgetBuilder.java index 2c764625f0..4eb9a4293f 100644 --- a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/widgetbuilder/EntityWidgetBuilder.java +++ b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/widgetbuilder/EntityWidgetBuilder.java @@ -151,10 +151,12 @@ public StaticXmlWidget buildWidget(String elementName, Map attri (StaticUIMetawidget) metawidget); } + String reverseKey = getReversePrimaryKey(attributes); + Param param = new Param(); param.putAttribute("name", "id"); param.putAttribute("value", - StaticFacesUtils.wrapExpression(StaticFacesUtils.unwrapExpression(link.getValue()) + ".id")); + StaticFacesUtils.wrapExpression(StaticFacesUtils.unwrapExpression(link.getValue()) + "." + reverseKey)); link.getChildren().add(param); return link; @@ -257,6 +259,27 @@ public StaticXmlWidget buildWidget(String elementName, Map attri // Protected methods // + protected String getReversePrimaryKey(Map attributes) { + String reverseKey = "id"; + if (attributes.containsKey(REVERSE_PRIMARY_KEY)) + reverseKey = attributes.get(REVERSE_PRIMARY_KEY); + return reverseKey; + } + + protected String getPrimaryKey(Map attributes) { + String reverseKey = "id"; + if (attributes.containsKey(PRIMARY_KEY)) + reverseKey = attributes.get(PRIMARY_KEY); + return reverseKey; + } + + protected String getEntityPrimaryKey(Map attributes) { + String reverseKey = "id"; + if (attributes.containsKey(ENTITY_PRIMARY_KEY)) + reverseKey = attributes.get(ENTITY_PRIMARY_KEY); + return reverseKey; + } + /** * Overridden to add row creation/deletion. */ @@ -541,7 +564,7 @@ protected void addColumnComponent(HtmlDataTable dataTable, Map t Param param = new Param(); param.putAttribute("name", "id"); - param.putAttribute("value", StaticFacesUtils.wrapExpression(dataTable.getAttribute("var") + ".id")); + param.putAttribute("value", StaticFacesUtils.wrapExpression(dataTable.getAttribute("var") + "." + getEntityPrimaryKey(columnAttributes))); link.getChildren().add(param); link.getChildren().add(column.getChildren().remove(1)); column.getChildren().add(link); diff --git a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/widgetbuilder/QueryByExampleWidgetBuilder.java b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/widgetbuilder/QueryByExampleWidgetBuilder.java index fc45440967..8517885314 100644 --- a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/widgetbuilder/QueryByExampleWidgetBuilder.java +++ b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/metawidget/widgetbuilder/QueryByExampleWidgetBuilder.java @@ -16,6 +16,7 @@ package org.jboss.forge.scaffold.faces.metawidget.widgetbuilder; +import static org.jboss.forge.scaffold.faces.metawidget.inspector.ForgeInspectionResultConstants.*; import static org.metawidget.inspector.InspectionResultConstants.*; import static org.metawidget.inspector.faces.StaticFacesInspectionResultConstants.*; @@ -50,7 +51,7 @@ public StaticJavaWidget buildWidget(String elementName, Map attr // Suppress - if (TRUE.equals(attributes.get(HIDDEN))) + if (TRUE.equals(attributes.get(HIDDEN)) && !Boolean.valueOf(attributes.get(PRIMARY_KEY_NOT_GENERATED))) { return new StaticJavaStub(); } @@ -74,13 +75,13 @@ public StaticJavaWidget buildWidget(String elementName, Map attr return toReturn; } - // int + // int or short - if (int.class.equals(clazz)) + if (int.class.equals(clazz) || short.class.equals(clazz) || byte.class.equals(clazz)) { StaticJavaStub toReturn = new StaticJavaStub(); toReturn.getChildren().add( - new JavaStatement("int " + name + " = this.search.get" + StringUtils.capitalize(name) + "()")); + new JavaStatement(clazz.getSimpleName() + " " + name + " = this.search.get" + StringUtils.capitalize(name) + "()")); JavaStatement ifNotEmpty = new JavaStatement("if (" + name + " != 0)"); ifNotEmpty.getChildren().add( new JavaStatement("predicatesList.add(builder.equal(root.get(\"" + name + "\"), " + name + "))")); @@ -108,12 +109,16 @@ public StaticJavaWidget buildWidget(String elementName, Map attr if (attributes.containsKey(FACES_LOOKUP)) { - StaticJavaStub toReturn = new StaticJavaStub(); + String reverseKey = "Id"; + if (attributes.containsKey(REVERSE_PRIMARY_KEY)) + reverseKey = StringUtils.capitalize(attributes.get(REVERSE_PRIMARY_KEY)); + + StaticJavaStub toReturn = new StaticJavaStub(); JavaStatement getValue = new JavaStatement(ClassUtils.getSimpleName(type) + " " + name + " = this.search.get" + StringUtils.capitalize(name) + "()"); getValue.putImport(type); toReturn.getChildren().add(getValue); - JavaStatement ifNotEmpty = new JavaStatement("if (" + name + " != null && " + name + ".getId() != null)"); + JavaStatement ifNotEmpty = new JavaStatement("if (" + name + " != null && " + name + ".get" + reverseKey + "() != null)"); ifNotEmpty.getChildren().add( new JavaStatement("predicatesList.add(builder.equal(root.get(\"" + name + "\"), " + name + "))")); toReturn.getChildren().add(ifNotEmpty); diff --git a/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/util/AnnotationLookup.java b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/util/AnnotationLookup.java new file mode 100644 index 0000000000..cfe2a96fe0 --- /dev/null +++ b/scaffold-faces/src/main/java/org/jboss/forge/scaffold/faces/util/AnnotationLookup.java @@ -0,0 +1,107 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2011, Red Hat, Inc., and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.forge.scaffold.faces.util; + +import java.io.FileNotFoundException; +import org.jboss.forge.parser.java.Field; +import org.jboss.forge.parser.java.JavaSource; +import org.jboss.forge.parser.java.Member; +import org.jboss.forge.parser.java.Method; +import org.jboss.forge.project.Project; +import org.jboss.forge.project.facets.JavaSourceFacet; +import org.metawidget.util.simple.StringUtils; + +/** + * utility for easy lookup of fields or properties in related entities of the domain model + * @author Thomas Frühbeck + */ +public class AnnotationLookup { + + public static final String JAVA_EXTENSION = ".java"; + + private Project project; + + public AnnotationLookup(Project project) { + this.project = project; + } + + /** + * lookup the annotated member of the class + * @param annotation + * @param qualifiedType + * @return + * @throws FileNotFoundException + */ + public Member lookup (Class annotation, String qualifiedType) throws FileNotFoundException { + JavaSourceFacet java = project.getFacet(JavaSourceFacet.class); + JavaSource javaSource = java.getJavaResource(qualifiedType).getJavaSource(); + + Member member = lookup(javaSource, annotation); + return member; + } + + /** + * convert the member to a field name, assumes JavaBeans notation + * @param member + * @return + */ + public String getFieldName(Member member) { + if (null == member) + return null; + if (member instanceof Method) { + String methodName = member.getName(); + return StringUtils.decapitalize(methodName.substring(3)); + } else if (member instanceof Field) { + return member.getName(); + } + return null; + } + + /** + * get the field name of the annotated member of the class + * @param annotation + * @param qualifiedType + * @return + * @throws FileNotFoundException + */ + public String getFieldName (Class annotation, String qualifiedType) throws FileNotFoundException { + Member member = lookup(annotation, qualifiedType); + return getFieldName(member); + } + + /** + * find a member annotated with this annotation. + * @param javaSource + * @param ann + * @return + */ + public Member lookup(JavaSource javaSource, Class ann) { + //@TODO Is not prepared for multiple PrimKeys + for (Member member : javaSource.getMembers()) { + if (member.hasAnnotation(ann)) { + return member; + } + } + return null; + } + +} diff --git a/scaffold-faces/src/main/resources/scaffold/faces/BackingBean.jv b/scaffold-faces/src/main/resources/scaffold/faces/BackingBean.jv index 30c24043da..b706c30032 100644 --- a/scaffold-faces/src/main/resources/scaffold/faces/BackingBean.jv +++ b/scaffold-faces/src/main/resources/scaffold/faces/BackingBean.jv @@ -45,13 +45,13 @@ public class @{entity.getName()}Bean implements Serializable { * Support creating and retrieving @{entity.getName()} entities */ - private Long id; + private @{nullablePrimaryKeyType} id; - public Long getId() { + public @{nullablePrimaryKeyType} getId() { return this.id; } - public void setId(Long id) { + public void setId(@{nullablePrimaryKeyType} id) { this.id = id; } @@ -103,7 +103,7 @@ public class @{entity.getName()}Bean implements Serializable { return "search?faces-redirect=true"; } else { this.entityManager.merge(this.@{ccEntity}); - return "view?faces-redirect=true&id=" + this.@{ccEntity}.getId(); + return "view?faces-redirect=true&id=" + this.@{ccEntity}.get@{primaryKeyCC}(); } } catch( Exception e ) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage( e.getMessage() )); @@ -234,7 +234,7 @@ public class @{entity.getName()}Bean implements Serializable { return ""; } - return String.valueOf(((@{entity.getName()}) value).getId()); + return String.valueOf(((@{entity.getName()}) value).get@{primaryKeyCC}()); } }; } diff --git a/scaffold-faces/src/main/resources/scaffold/faces/metawidget-bean.xml b/scaffold-faces/src/main/resources/scaffold/faces/metawidget-bean.xml index 9650a0cd79..32555ce1c4 100644 --- a/scaffold-faces/src/main/resources/scaffold/faces/metawidget-bean.xml +++ b/scaffold-faces/src/main/resources/scaffold/faces/metawidget-bean.xml @@ -25,10 +25,16 @@ - + + + + + + + @@ -45,7 +51,17 @@ - + + + + + + + + + + + diff --git a/scaffold-faces/src/main/resources/scaffold/faces/metawidget-entity.xml b/scaffold-faces/src/main/resources/scaffold/faces/metawidget-entity.xml index 8d3c545aea..f03a29a675 100644 --- a/scaffold-faces/src/main/resources/scaffold/faces/metawidget-entity.xml +++ b/scaffold-faces/src/main/resources/scaffold/faces/metawidget-entity.xml @@ -25,10 +25,16 @@ - + + + + + + + @@ -44,6 +50,16 @@ + + + + + + + + + + diff --git a/scaffold-faces/src/main/resources/scaffold/faces/metawidget-qbe.xml b/scaffold-faces/src/main/resources/scaffold/faces/metawidget-qbe.xml index 03e3582cc5..90fac2db26 100644 --- a/scaffold-faces/src/main/resources/scaffold/faces/metawidget-qbe.xml +++ b/scaffold-faces/src/main/resources/scaffold/faces/metawidget-qbe.xml @@ -24,10 +24,16 @@ - + + + + + + + @@ -44,7 +50,17 @@ - + + + + + + + + + + + diff --git a/scaffold-faces/src/main/resources/scaffold/faces/metawidget-search.xml b/scaffold-faces/src/main/resources/scaffold/faces/metawidget-search.xml index 69baabe4d8..1e9f8f7c45 100644 --- a/scaffold-faces/src/main/resources/scaffold/faces/metawidget-search.xml +++ b/scaffold-faces/src/main/resources/scaffold/faces/metawidget-search.xml @@ -24,10 +24,16 @@ - + + + + + + + @@ -44,7 +50,17 @@ - + + + + + + + + + + + diff --git a/scaffold-faces/src/test/java/org/jboss/forge/scaffold/faces/PrimaryKeyFacesScaffoldTest.java b/scaffold-faces/src/test/java/org/jboss/forge/scaffold/faces/PrimaryKeyFacesScaffoldTest.java new file mode 100644 index 0000000000..8d8e7714ad --- /dev/null +++ b/scaffold-faces/src/test/java/org/jboss/forge/scaffold/faces/PrimaryKeyFacesScaffoldTest.java @@ -0,0 +1,242 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.jboss.forge.scaffold.faces; + +import java.io.FileNotFoundException; +import java.io.Serializable; +import java.util.Map; +import javax.inject.Inject; +import javax.persistence.*; +import junit.framework.Assert; +import org.jboss.arquillian.protocol.servlet.arq514hack.descriptors.impl.web.Strings; +import org.jboss.forge.parser.JavaParser; +import org.jboss.forge.parser.java.Field; +import org.jboss.forge.parser.java.JavaClass; +import org.jboss.forge.parser.java.Method; +import org.jboss.forge.parser.java.util.Refactory; +import org.jboss.forge.project.Project; +import org.jboss.forge.project.facets.JavaSourceFacet; +import org.jboss.forge.project.facets.WebResourceFacet; +import org.jboss.forge.project.services.ResourceFactory; +import org.jboss.forge.resources.FileResource; +import org.jboss.forge.resources.java.JavaResource; +import org.jboss.forge.scaffold.faces.metawidget.inspector.ForgeInspector; +import org.jboss.forge.scaffold.faces.metawidget.inspector.ForgeInspectorConfig; +import org.jboss.forge.scaffold.faces.util.AnnotationLookup; +import org.jboss.forge.shell.util.Streams; +import org.junit.Test; +import org.metawidget.util.XmlUtils; +import org.metawidget.util.simple.StringUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import static org.junit.Assert.*; +import static org.jboss.forge.scaffold.faces.metawidget.inspector.ForgeInspectionResultConstants.*; +import org.jboss.forge.scaffold.faces.metawidget.inspector.propertystyle.ForgePropertyStyle; +import org.jboss.forge.scaffold.faces.metawidget.inspector.propertystyle.ForgePropertyStyleConfig; +import org.jboss.forge.scaffold.faces.metawidget.processor.ForgeInspectionResultProcessor; +import static org.metawidget.inspector.InspectionResultConstants.*; + +/** + * + * @author Thomas Frühbeck + */ +public class PrimaryKeyFacesScaffoldTest extends AbstractFacesScaffoldTest { + + @Inject + private ResourceFactory factory; + + @Test + public void testGenerateFromLegacyPrimaryKey() throws Exception { + final String parentPrimaryKey = "parentPrimaryKey"; + final String parentPrimaryKeyCC = StringUtils.capitalize(parentPrimaryKey); + + Project project = setupScaffoldProject(); + + queueInputLines(""); + generateAlternateEntity(project, "com.test.model", "Parent", parentPrimaryKey); + + getShell().execute("entity --named Child"); + getShell().execute("field string --named name"); + getShell().execute("field manyToOne --named parent --fieldType com.test.model.Parent.java --inverseFieldName children"); + + queueInputLines("", "", ""); + getShell().execute("scaffold from-entity com.test.model.* --scaffoldType faces"); + + WebResourceFacet web = project.getFacet(WebResourceFacet.class); + JavaSourceFacet java = project.getFacet(JavaSourceFacet.class); + // Code + JavaResource parentBean = java.getJavaResource("com.test.view.ParentBean"); + JavaResource childBean = java.getJavaResource("com.test.view.ChildBean"); + + String parentContent = Streams.toString(parentBean.getResourceInputStream()); + assertTrue(parentContent.contains("id=\" + this.parent.get" + parentPrimaryKeyCC + "()")); + assertTrue(parentContent.contains("valueOf(((Parent) value).get" + parentPrimaryKeyCC + "()")); + + String childContent = Streams.toString(childBean.getResourceInputStream()); + assertTrue(childContent.contains("this.child.getId()")); + + // View + FileResource view = web.getWebResource("scaffold/parent/view.xhtml"); + assertTrue(view.exists()); + String contents = Streams.toString(view.getResourceInputStream()); + assertTrue(contents.contains( + "template=\"/resources/scaffold/page.xhtml")); + + view = web.getWebResource("scaffold/parent/search.xhtml"); + contents = Streams.toString(view.getResourceInputStream()); + assertTrue(view.exists()); + assertTrue(contents.contains("")); + + view = web.getWebResource("scaffold/child/view.xhtml"); + contents = Streams.toString(view.getResourceInputStream()); + assertTrue(view.exists()); + assertTrue(contents.contains("childBean.child.parent." + parentPrimaryKey)); +} + + private JavaResource generateAlternateEntity(Project project, String pkg, String entityName, String primaryKey) throws FileNotFoundException { + JavaSourceFacet java = project.getFacet(JavaSourceFacet.class); + JavaClass javaClass = JavaParser.create(JavaClass.class).setPackage(pkg).setName(entityName).setPublic().addAnnotation(Entity.class).getOrigin().addInterface(Serializable.class); + + String idName = primaryKey; + if (idName == null) { + StringUtils.decapitalize(entityName + "Id"); + } + + Field id = javaClass.addField("private String " + idName + " = null;"); + id.addAnnotation(Id.class); + id.addAnnotation(GeneratedValue.class).setEnumValue("strategy", GenerationType.AUTO); + id.addAnnotation(Column.class).setStringValue("name", idName).setLiteralValue("updatable", "false").setLiteralValue("nullable", "false"); + + Refactory.createGetterAndSetter(javaClass, id); + + Field name = javaClass.addField("private String name = null;"); + Refactory.createGetterAndSetter(javaClass, name); + + Refactory.createToStringFromFields(javaClass, id); + Refactory.createHashCodeAndEquals(javaClass); + + return java.saveJavaSource(javaClass); + } + + @Test + public void testPrimaryKeys() throws Exception { + Project project = initializeJavaProject(); + queueInputLines("HIBERNATE", "JBOSS_AS7", ""); + getShell().execute("persistence setup"); + + for (PrimaryKeyTestBase testClass : new PrimaryKeyTestBase[]{ + new PrimaryKeyFieldTest(), new PrimaryKeyPropertyTest(), new PrimaryKeyPropertyAssignedTest()}) { + testPrimaryKey(project, testClass); + } + } + + public void testPrimaryKey(Project project, PrimaryKeyTestBase testClass) throws Exception { + + final String parentPrimaryKey = "primaryKey"; + final String parentPrimaryKeyCC = StringUtils.capitalize(parentPrimaryKey); + + ForgeInspectorConfig config = new ForgeInspectorConfig(); + config.setAnnotationLookup(new AnnotationLookup(project)); + config.setPropertyStyle(new ForgePropertyStyle(new ForgePropertyStyleConfig().setProject(project))); + ForgeInspectionResultProcessor processor = new ForgeInspectionResultProcessor(); + + generatePkEntity(project, "org.test", "Parent", parentPrimaryKey, testClass); + + String xml = new ForgeInspector(config).inspect(null, "org.test.Parent"); + xml = processor.processInspectionResult(xml, null, project, xml, new String[]{}); + + Document document = XmlUtils.documentFromString(xml); + assertEquals("inspection-result", document.getFirstChild().getNodeName()); + Element entity = (Element) document.getFirstChild().getFirstChild(); + assertEquals(ENTITY, entity.getNodeName()); + + Map attributes = XmlUtils.getAttributesAsMap(entity); + assertEquals(parentPrimaryKey, attributes.get(PRIMARY_KEY)); + + NodeList properties = entity.getElementsByTagName(PROPERTY); + for (int i = 0; i < properties.getLength(); i++) { + Element prop = (Element) properties.item(i); + attributes = XmlUtils.getAttributesAsMap(prop); + + if (!(testClass instanceof PrimaryKeyPropertyAssignedTest)) { + assertTrue(attributes.containsKey(PRIMARY_KEY_NOT_GENERATED)); + } + } + Element property = (Element) entity.getFirstChild(); + attributes = XmlUtils.getAttributesAsMap(property); + assertEquals(parentPrimaryKey, attributes.get(PRIMARY_KEY)); + assertEquals(parentPrimaryKey, attributes.get(ENTITY_PRIMARY_KEY)); + + } + + private JavaResource generatePkEntity(Project project, String pkg, String entityName, String primaryKey, PrimaryKeyTestBase pkTest) throws FileNotFoundException { + JavaSourceFacet java = project.getFacet(JavaSourceFacet.class); + JavaClass javaClass = JavaParser.create(JavaClass.class).setPackage(pkg).setName(entityName).setPublic().addAnnotation(Entity.class).getOrigin().addInterface(Serializable.class); + + String idName = primaryKey; + if (idName == null) { + StringUtils.decapitalize(entityName + "Id"); + } + + Field id = javaClass.addField("private String " + idName + " = null;"); + if (pkTest.field) { + id.addAnnotation(Id.class); + if (pkTest.generated) { + id.addAnnotation(GeneratedValue.class).setEnumValue("strategy", GenerationType.AUTO); + id.addAnnotation(Column.class).setStringValue("name", idName).setLiteralValue("updatable", "false").setLiteralValue("nullable", "false"); + } + } + + + + Refactory.createGetterAndSetter(javaClass, id); + if (!pkTest.field) { + Method getPk = javaClass.getMethod("get" + StringUtils.capitalize(idName)); + getPk.addAnnotation(Id.class); + if (pkTest.generated) { + getPk.addAnnotation(GeneratedValue.class).setEnumValue("strategy", GenerationType.AUTO); + getPk.addAnnotation(Column.class).setStringValue("name", idName).setLiteralValue("updatable", "false").setLiteralValue("nullable", "false"); + } + } + + Field name = javaClass.addField("private String name = null;"); + Refactory.createGetterAndSetter(javaClass, name); + Field type = javaClass.addField("private String type = null;"); + Refactory.createGetterAndSetter(javaClass, type); + + return java.saveJavaSource(javaClass); + } + + + class PrimaryKeyTestBase { + + public boolean generated, field; + } + + class PrimaryKeyFieldTest extends PrimaryKeyTestBase { + + { + generated = true; + field = true; + } + } + + class PrimaryKeyPropertyTest extends PrimaryKeyTestBase { + + { + generated = true; + field = false; + } + } + + class PrimaryKeyPropertyAssignedTest extends PrimaryKeyTestBase { + + { + generated = false; + field = true; + } + } +} diff --git a/scaffold-faces/src/test/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspectorTest.java b/scaffold-faces/src/test/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspectorTest.java index 1e5280b052..e1accb29af 100644 --- a/scaffold-faces/src/test/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspectorTest.java +++ b/scaffold-faces/src/test/java/org/jboss/forge/scaffold/faces/metawidget/inspector/ForgeInspectorTest.java @@ -72,7 +72,7 @@ public void testRelationships() assertEquals("manyToOne", property.getAttribute(NAME)); assertEquals("#{forgeInspectorTest$BarBean.all}", property.getAttribute(FACES_LOOKUP)); assertEquals("#{forgeInspectorTest$BarBean.converter}", property.getAttribute(FACES_CONVERTER_ID)); - assertEquals(3, property.getAttributes().getLength()); + assertEquals(4, property.getAttributes().getLength()); property = XmlUtils.getNextSiblingElement(property); assertEquals(PROPERTY, property.getNodeName()); diff --git a/scaffold-faces/src/test/java/org/jboss/forge/scaffold/faces/scenario/petclinic/FacesScaffoldPetClinicTest.java b/scaffold-faces/src/test/java/org/jboss/forge/scaffold/faces/scenario/petclinic/FacesScaffoldPetClinicTest.java index a80df48298..38142d22f4 100644 --- a/scaffold-faces/src/test/java/org/jboss/forge/scaffold/faces/scenario/petclinic/FacesScaffoldPetClinicTest.java +++ b/scaffold-faces/src/test/java/org/jboss/forge/scaffold/faces/scenario/petclinic/FacesScaffoldPetClinicTest.java @@ -54,7 +54,8 @@ public class FacesScaffoldPetClinicTest extends AbstractFacesScaffoldTest public void testGenerate() throws Exception { Project current = getShell().getCurrentProject(); - Project project = setupScaffoldProject("petClinic"); + final String targetDir = "petClinic"; + Project project = setupScaffoldProject(targetDir); queueInputLines(""); getShell().execute("entity --named Owner"); @@ -97,7 +98,7 @@ public void testGenerate() throws Exception // Check search screen has h:message - FileResource search = web.getWebResource("petClinic/pet/search.xhtml"); + FileResource search = web.getWebResource(targetDir+"/pet/search.xhtml"); Assert.assertTrue(search.exists()); String contents = Streams.toString(search.getResourceInputStream()); @@ -111,14 +112,14 @@ public void testGenerate() throws Exception // Check search screen has boolean graphic - metawidget = "\t\t\t\t\t\r\n"; + metawidget = "\t\t\t\t\t\r\n"; metawidget += "\t\t\t\t\t\t\r\n"; metawidget += "\t\t\t\t\t\t\r\n"; metawidget += "\t\t\t\t\t\r\n"; Assert.assertTrue(contents.contains(metawidget)); - metawidget = "\t\t\t\t\t\r\n"; + metawidget = "\t\t\t\t\t\r\n"; metawidget += "\t\t\t\t\t\t\r\n"; metawidget += "\t\t\t\t\t\t\r\n"; metawidget += "\t\t\t\t\t\r\n"; @@ -127,7 +128,7 @@ public void testGenerate() throws Exception // Check create screen has h:selectBooleanCheckbox - FileResource create = web.getWebResource("petClinic/pet/create.xhtml"); + FileResource create = web.getWebResource(targetDir+"/pet/create.xhtml"); Assert.assertTrue(create.exists()); contents = Streams.toString(create.getResourceInputStream()); @@ -141,7 +142,7 @@ public void testGenerate() throws Exception // Check view screen has boolean graphic - FileResource view = web.getWebResource("petClinic/pet/view.xhtml"); + FileResource view = web.getWebResource(targetDir+"/pet/view.xhtml"); Assert.assertTrue(view.exists()); contents = Streams.toString(view.getResourceInputStream());