Skip to content

Instantly share code, notes, and snippets.

@lincolnthree
Created December 6, 2010 19:57
Show Gist options
  • Save lincolnthree/730830 to your computer and use it in GitHub Desktop.
Save lincolnthree/730830 to your computer and use it in GitHub Desktop.
/*
* JBoss, by Red Hat.
* Copyright 2010, 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.seam.forge.shell.command;
import org.jboss.seam.forge.project.Resource;
import org.jboss.seam.forge.project.util.Annotations;
import org.jboss.seam.forge.shell.plugins.*;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessBean;
import javax.inject.Named;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
/**
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
public class CommandLibraryExtension implements Extension
{
private final Map<String, List<PluginMetadata>> plugins = new HashMap<String, List<PluginMetadata>>();
public Map<String, List<PluginMetadata>> getPlugins()
{
return plugins;
}
@SuppressWarnings("unchecked")
public void scan(@Observes final ProcessBean<?> event)
{
Bean<?> bean = event.getBean();
Class<?> clazz = bean.getBeanClass();
if (Plugin.class.isAssignableFrom(clazz))
{
PluginMetadata pluginMeta = getMetadataFor((Class<? extends Plugin>) clazz);
if (!plugins.containsKey(pluginMeta.getName()))
{
plugins.put(pluginMeta.getName(), new ArrayList<PluginMetadata>());
}
plugins.get(pluginMeta.getName()).add(pluginMeta);
}
}
public PluginMetadata getMetadataFor(final Class<? extends Plugin> plugin)
{
String name = getPluginName(plugin);
PluginMetadata pluginMeta = new PluginMetadata();
pluginMeta.setName(name);
pluginMeta.setType(plugin);
if (Annotations.isAnnotationPresent(plugin, Help.class))
{
pluginMeta.setHelp(Annotations.getAnnotation(plugin, Help.class).value());
}
else
{
pluginMeta.setHelp("");
}
if (Annotations.isAnnotationPresent(plugin, ResourceScope.class))
{
List<Class<? extends Resource<?>>> resourceTypes = Arrays.asList(Annotations.getAnnotation(plugin,
ResourceScope.class).value());
pluginMeta.setResourceScopes(resourceTypes);
}
if (Annotations.isAnnotationPresent(plugin, Topic.class))
{
pluginMeta.setTopic(Annotations.getAnnotation(plugin, Topic.class).value());
}
processPluginCommands(pluginMeta, plugin);
return pluginMeta;
}
private List<CommandMetadata> processPluginCommands(final PluginMetadata pluginMeta, final Class<?> plugin)
{
List<CommandMetadata> results = new ArrayList<CommandMetadata>();
for (Method method : plugin.getMethods())
{
if (Annotations.isAnnotationPresent(method, Command.class))
{
Command command = Annotations.getAnnotation(method, Command.class);
CommandMetadata commandMeta = new CommandMetadata();
commandMeta.setMethod(method);
commandMeta.setHelp(command.help());
commandMeta.setParent(pluginMeta);
// Default commands are invoked via the name of the plug-in, not by
// plug-in + command
if ("".equals(command.value()))
{
commandMeta.setName(method.getName().trim().toLowerCase());
}
else
{
commandMeta.setName(command.value());
}
// This works because @DefaultCommand is annotated by @Command
if (Annotations.isAnnotationPresent(method, DefaultCommand.class))
{
if (pluginMeta.hasDefaultCommand())
{
throw new IllegalStateException("Plugins may only have one @"
+ DefaultCommand.class.getSimpleName()
+ ", but [" + pluginMeta.getType() + "] has more than one.");
}
commandMeta.setDefault(true);
commandMeta.setName(pluginMeta.getName());
// favor help text from this annotation over others
DefaultCommand def = Annotations.getAnnotation(method, DefaultCommand.class);
if ((def.help() != null) && !def.help().trim().isEmpty())
{
commandMeta.setHelp(def.help());
}
}
if (Annotations.isAnnotationPresent(method, ResourceScope.class))
{
List<Class<? extends Resource>> resourceTypes = new ArrayList<Class<? extends Resource>>(
pluginMeta.getResourceScopes());
resourceTypes.addAll(Arrays.asList(Annotations.getAnnotation(method, ResourceScope.class).value()));
commandMeta.setResourceScopes(resourceTypes);
}
// fall back to the pluginMetadata for help text
if ((commandMeta.getHelp() == null) || commandMeta.getHelp().trim().isEmpty())
{
commandMeta.setHelp(pluginMeta.getHelp());
}
Class<?>[] parameterTypes = method.getParameterTypes();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
int i = 0;
int effectiveIndex = 0;
for (Class<?> clazz : parameterTypes)
{
OptionMetadata optionMeta = new OptionMetadata();
optionMeta.setType(clazz);
optionMeta.setIndex(i);
optionMeta.setEffectiveIndex(effectiveIndex);
if (PipeOut.class.isAssignableFrom(clazz))
{
optionMeta.setPipeOut(true);
}
for (Annotation annotation : parameterAnnotations[i])
{
if (annotation instanceof Option)
{
Option option = (Option) annotation;
optionMeta.setParent(commandMeta);
optionMeta.setName(option.name());
optionMeta.setShortName(option.shortName());
optionMeta.setFlagOnly(option.flagOnly());
optionMeta.setDescription(option.description());
optionMeta.setDefaultValue(option.defaultValue());
optionMeta.setHelp(option.help());
optionMeta.setRequired(option.required());
optionMeta.setPromptType(option.type());
effectiveIndex++;
}
else if (annotation instanceof PipeIn)
{
PipeIn pipeIn = (PipeIn) annotation;
optionMeta.setPipeIn(true);
}
}
commandMeta.addOption(optionMeta);
i++;
}
results.add(commandMeta);
}
}
pluginMeta.addCommands(results);
for (Method method : plugin.getMethods())
{
}
return results;
}
private String getPluginName(final Class<?> plugin)
{
String name = null;
if (Annotations.isAnnotationPresent(plugin, OverloadedName.class))
{
OverloadedName named = Annotations.getAnnotation(plugin, OverloadedName.class);
if (named != null)
{
name = named.value();
}
if ((name == null) || "".equals(name.trim()))
{
name = plugin.getSimpleName();
}
}
else
{
Named named = Annotations.getAnnotation(plugin, Named.class);
if (named != null)
{
name = named.value();
}
if ((name == null) || "".equals(name.trim()))
{
name = plugin.getSimpleName();
}
}
return name.toLowerCase();
}
}
/*
* JBoss, by Red Hat.
* Copyright 2010, 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.seam.forge.project;
import org.jboss.seam.forge.project.constraints.RequiresFacet;
import org.jboss.seam.forge.project.constraints.RequiresFacets;
import org.jboss.seam.forge.project.constraints.RequiresPackagingType;
import org.jboss.seam.forge.project.constraints.RequiresPackagingTypes;
/**
* Represents a standardized piece of a project, on which certain plugins may rely for a set of domain-specific
* operations. Facets are an access point to common functionality, file manipulations, descriptors that extend a basic
* {@link Project}.
* <p>
* Facets may be annotated with any of the following constraints in order to ensure proper dependencies are satisfied at
* runtime: {@link RequiresFacet}, {@link RequiresFacets}, {@link RequiresPackagingTypes}, {@link RequiresPackagingType}
*
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*
*/
public interface Facet
{
/**
* Return the {@link Project} instance on which this {@link Facet} operates.
*/
Project getProject();
/**
* Initialize this {@link Facet} for operation on the given {@link Project}. This method is responsible for ensuring
* that the {@link Facet} instance is ready for use, and must be called before any other methods.
*/
void setProject(Project project);
/**
* Perform necessary setup for this {@link Facet} to be considered installed in the given {@link Project}. If
* installation is successful, the {@link Facet} should be registered in the {@link Project} by calling
* {@link Project#registerFacet(Facet)}
*
* @return a builder pattern reference to this {@link Facet}
*/
Facet install();
/**
* Return true if the {@link Facet} is available for the given {@link Project}, false if otherwise.
*/
boolean isInstalled();
}
/*
* JBoss, Home of Professional Open Source
* Copyright 2010, 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.seam.forge.persistence;
import java.io.FileNotFoundException;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Version;
import org.jboss.seam.forge.parser.JavaParser;
import org.jboss.seam.forge.parser.java.Field;
import org.jboss.seam.forge.parser.java.JavaClass;
import org.jboss.seam.forge.parser.java.util.Refactory;
import org.jboss.seam.forge.project.Project;
import org.jboss.seam.forge.project.constraints.RequiresFacet;
import org.jboss.seam.forge.project.constraints.RequiresProject;
import org.jboss.seam.forge.project.facets.JavaSourceFacet;
import org.jboss.seam.forge.project.resources.builtin.JavaResource;
import org.jboss.seam.forge.shell.PromptType;
import org.jboss.seam.forge.shell.Shell;
import org.jboss.seam.forge.shell.plugins.DefaultCommand;
import org.jboss.seam.forge.shell.plugins.Help;
import org.jboss.seam.forge.shell.plugins.Option;
import org.jboss.seam.forge.shell.plugins.Plugin;
import org.jboss.seam.forge.shell.plugins.Topic;
/**
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
@Named("new-entity")
@Topic("Project")
@RequiresProject
@RequiresFacet(PersistenceFacet.class)
@Help("A plugin to manage simple @Entity and View creation; a basic MVC framework plugin.")
public class NewEntityPlugin implements Plugin
{
private final Instance<Project> projectInstance;
private final Shell shell;
@Inject
public NewEntityPlugin(final Instance<Project> projectInstance, final Shell shell)
{
this.projectInstance = projectInstance;
this.shell = shell;
}
@DefaultCommand(help = "Create a JPA @Entity")
public void newEntity(
@Option(required = true,
name = "named",
description = "The @Entity name") final String entityName) throws FileNotFoundException
{
// TODO this should accept a qualified name as a parameter instead of
// prompting for the package later
Project project = projectInstance.get();
PersistenceFacet scaffold = project.getFacet(PersistenceFacet.class);
JavaSourceFacet java = project.getFacet(JavaSourceFacet.class);
String entityPackage = shell.promptCommon(
"In which package you'd like to create this @Entity, or enter for default:",
PromptType.JAVA_PACKAGE, scaffold.getEntityPackage());
JavaClass javaClass = JavaParser.createClass()
.setPackage(entityPackage)
.setName(entityName)
.setPublic()
.addAnnotation(Entity.class)
.getOrigin();
Field id = javaClass.addField("private long id = 0;");
id.addAnnotation(Id.class);
id.addAnnotation(GeneratedValue.class)
.setEnumValue("strategy", GenerationType.AUTO);
id.addAnnotation(Column.class)
.setStringValue("name", "id")
.setLiteralValue("updatable", "false")
.setLiteralValue("nullable", "false");
Field version = javaClass.addField("private int version = 0;");
version.addAnnotation(Version.class);
version.addAnnotation(Column.class).setStringValue("name", "version");
Refactory.createGetterAndSetter(javaClass, id);
Refactory.createGetterAndSetter(javaClass, version);
JavaResource javaFileLocation = java.saveJavaClass(javaClass);
shell.println("Created @Entity [" + javaClass.getQualifiedName() + "]");
/**
* Pick up the generated resource.
*/
shell.execute("pick-up " + javaFileLocation.getFullyQualifiedName());
}
}
/*
* JBoss, Home of Professional Open Source
* Copyright 2010, 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.seam.forge.persistence;
import java.io.FileNotFoundException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.Column;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import org.jboss.seam.forge.parser.java.Field;
import org.jboss.seam.forge.parser.java.JavaClass;
import org.jboss.seam.forge.parser.java.util.Refactory;
import org.jboss.seam.forge.parser.java.util.Types;
import org.jboss.seam.forge.project.Project;
import org.jboss.seam.forge.project.constraints.RequiresFacet;
import org.jboss.seam.forge.project.constraints.RequiresProject;
import org.jboss.seam.forge.project.facets.JavaSourceFacet;
import org.jboss.seam.forge.project.resources.builtin.JavaResource;
import org.jboss.seam.forge.shell.PromptType;
import org.jboss.seam.forge.shell.Shell;
import org.jboss.seam.forge.shell.plugins.Command;
import org.jboss.seam.forge.shell.plugins.DefaultCommand;
import org.jboss.seam.forge.shell.plugins.Help;
import org.jboss.seam.forge.shell.plugins.Option;
import org.jboss.seam.forge.shell.plugins.Plugin;
import org.jboss.seam.forge.shell.plugins.ResourceScope;
import org.jboss.seam.forge.shell.plugins.Topic;
/**
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
@Named("new-field")
@Topic("File & Resources")
@RequiresProject
@RequiresFacet(PersistenceFacet.class)
@ResourceScope(JavaResource.class)
@Help("A plugin to manage simple @Entity and View creation; a basic MVC framework plugin.")
public class NewFieldPlugin implements Plugin
{
private final Instance<Project> projectInstance;
private final Shell shell;
@Inject
public NewFieldPlugin(final Instance<Project> project, final Shell shell)
{
this.projectInstance = project;
this.shell = shell;
}
@DefaultCommand(help = "Add many custom field to an existing @Entity class")
public void newExpressionField(
@Option(required = true, description = "The field descriptor") final String... fields)
{
System.out.println(Arrays.asList(fields));
}
@Command(value = "custom", help = "Add a custom field to an existing @Entity class")
public void newCustomField(
@Option(name = "fieldName",
required = true,
description = "The field name",
type = PromptType.JAVA_VARIABLE_NAME) final String fieldName,
@Option(name = "type",
required = true,
type = PromptType.JAVA_CLASS,
description = "The qualified Class to be used as this field's type") final String type
)
{
try
{
JavaClass entity = getJavaClass();
addFieldTo(entity, type, fieldName, Column.class);
}
catch (FileNotFoundException e)
{
shell.println("Could not locate the @Entity requested. No update was made.");
}
}
@Command(value = "boolean", help = "Add a boolean field to an existing @Entity class")
public void newBooleanField(
@Option(name = "fieldName",
required = true,
description = "The field name",
type = PromptType.JAVA_VARIABLE_NAME) final String fieldName,
@Option(name = "primitive",
required = false,
defaultValue = "true",
description = "Marks this field to be created as a primitive.",
type = PromptType.JAVA_VARIABLE_NAME) final boolean primitive)
{
try
{
JavaClass entity = getJavaClass();
if (primitive)
{
addFieldTo(entity, boolean.class, fieldName, Column.class);
}
else
{
addFieldTo(entity, Boolean.class, fieldName, Column.class);
}
}
catch (FileNotFoundException e)
{
shell.println("Could not locate the @Entity requested. No update was made.");
}
}
@Command(value = "int", help = "Add an int field to an existing @Entity class")
public void newIntField(
@Option(name = "fieldName",
required = true,
description = "The field name",
type = PromptType.JAVA_VARIABLE_NAME) final String fieldName,
@Option(name = "primitive",
required = false,
defaultValue = "true",
description = "Marks this field to be created as a primitive.",
type = PromptType.JAVA_VARIABLE_NAME) final boolean primitive)
{
try
{
JavaClass entity = getJavaClass();
if (primitive)
{
addFieldTo(entity, int.class, fieldName, Column.class);
}
else
{
addFieldTo(entity, Integer.class, fieldName, Column.class);
}
}
catch (FileNotFoundException e)
{
shell.println("Could not locate the @Entity requested. No update was made.");
}
}
@Command(value = "long", help = "Add a long field to an existing @Entity class")
public void newLongField(
@Option(name = "fieldName",
required = true,
description = "The field name",
type = PromptType.JAVA_VARIABLE_NAME) final String fieldName,
@Option(name = "primitive",
required = false,
defaultValue = "true",
description = "Marks this field to be created as a primitive.",
type = PromptType.JAVA_VARIABLE_NAME) final boolean primitive)
{
try
{
JavaClass entity = getJavaClass();
if (primitive)
{
addFieldTo(entity, long.class, fieldName, Column.class);
}
else
{
addFieldTo(entity, Long.class, fieldName, Column.class);
}
}
catch (FileNotFoundException e)
{
shell.println("Could not locate the @Entity requested. No update was made.");
}
}
@Command(value = "number", help = "Add a number field to an existing @Entity class")
public void newNumberField(
@Option(name = "fieldName",
required = true,
description = "The field name",
type = PromptType.JAVA_VARIABLE_NAME) final String fieldName,
@Option(name = "type",
required = true,
type = PromptType.JAVA_CLASS,
description = "The qualified Class to be used as this field's type") final String type)
{
try
{
JavaClass entity = getJavaClass();
addFieldTo(entity, Class.forName(type), fieldName, Column.class);
}
catch (FileNotFoundException e)
{
shell.println("Could not locate the @Entity requested. No update was made.");
}
catch (ClassNotFoundException e)
{
shell.println("Sorry, I don't think [" + type
+ "] is a valid Java number type. Try something in the 'java.lang.* or java.math*' packages.");
}
}
@Command(value = "string", help = "Add a String field to an existing @Entity class")
public void newLongField(
@Option(name = "fieldName",
required = true,
description = "The field name",
type = PromptType.JAVA_VARIABLE_NAME) final String fieldName,
@Option(name = "addToClass",
required = false,
type = PromptType.JAVA_CLASS,
description = "The @Entity to which this field will be added") final String entityName)
{
try
{
JavaClass entity = getJavaClass();
addFieldTo(entity, String.class, fieldName, Column.class);
}
catch (FileNotFoundException e)
{
shell.println("Could not locate the @Entity requested. No update was made.");
}
}
@Command(value = "oneToOne", help = "Add a One-to-one relationship field to an existing @Entity class")
public void newOneToOneRelationship(
@Option(name = "fieldName",
required = true,
description = "The field name",
type = PromptType.JAVA_VARIABLE_NAME) final String fieldName,
@Option(name = "fieldType",
required = true,
description = "The @Entity type to which this field is a relationship",
type = PromptType.JAVA_CLASS) final String fieldType,
@Option(name = "inverseFieldName",
required = false,
description = "Create a bi-directional relationship, using this value as the name of the inverse field.",
type = PromptType.JAVA_VARIABLE_NAME) final String inverseFieldName)
{
try
{
JavaClass field = findEntity(fieldType);
JavaClass entity = getJavaClass();
addFieldTo(entity, field, fieldName, OneToOne.class);
if ((inverseFieldName != null) && !inverseFieldName.isEmpty())
{
addFieldTo(field, entity, inverseFieldName, OneToOne.class);
}
}
catch (FileNotFoundException e)
{
shell.println("Could not locate the @Entity requested. No update was made.");
}
}
@Command(value = "manyToMany", help = "Add a many-to-many relationship field (java.lang.Set<?>) to an existing @Entity class")
public void newManyToManyRelationship(
@Option(name = "fieldName",
required = true,
description = "The field name",
type = PromptType.JAVA_VARIABLE_NAME) final String fieldName,
@Option(name = "fieldType",
required = true,
description = "The @Entity type to which this field is a relationship",
type = PromptType.JAVA_CLASS) final String fieldType,
@Option(name = "inverseFieldName",
required = false,
description = "Create an bi-directional relationship, using this value as the name of the inverse field.",
type = PromptType.JAVA_VARIABLE_NAME) final String inverseFieldName)
{
Project project = getCurrentProject();
JavaSourceFacet java = project.getFacet(JavaSourceFacet.class);
try
{
JavaClass entity = getJavaClass();
JavaClass otherEntity = findEntity(fieldType);
entity.addImport(Set.class);
entity.addImport(HashSet.class);
entity.addImport(otherEntity.getQualifiedName());
Field field = entity.addField("private Set<" + otherEntity.getName() + "> " + fieldName + "= new HashSet<"
+ otherEntity.getName() + ">();");
org.jboss.seam.forge.parser.java.Annotation annotation = field.addAnnotation(ManyToMany.class);
Refactory.createGetterAndSetter(entity, field);
if ((inverseFieldName != null) && !inverseFieldName.isEmpty())
{
annotation.setStringValue("mappedBy", inverseFieldName);
otherEntity.addImport(Set.class);
otherEntity.addImport(HashSet.class);
otherEntity.addImport(entity.getQualifiedName());
Field otherField = otherEntity.addField("private Set<" + entity.getName() + "> " + inverseFieldName
+ "= new HashSet<" + entity.getName() + ">();");
otherField.addAnnotation(ManyToMany.class);
Refactory.createGetterAndSetter(otherEntity, otherField);
java.saveJavaClass(otherEntity);
}
java.saveJavaClass(entity);
}
catch (FileNotFoundException e)
{
shell.println("Could not locate the @Entity requested. No update was made.");
}
}
@Command(value = "oneToMany", help = "Add a one-to-many relationship field (java.lang.Set<?>) to an existing @Entity class")
public void newOneToManyRelationship(
@Option(name = "fieldName",
required = true,
description = "The field name",
type = PromptType.JAVA_VARIABLE_NAME) final String fieldName,
@Option(name = "fieldType",
required = true,
description = "The @Entity representing the 'many' side of the relationship.",
type = PromptType.JAVA_CLASS) final String fieldType,
@Option(name = "inverseFieldName",
required = false,
description = "Create an bi-directional relationship, using this value as the name of the inverse field.",
type = PromptType.JAVA_VARIABLE_NAME) final String inverseFieldName)
{
Project project = getCurrentProject();
JavaSourceFacet java = project.getFacet(JavaSourceFacet.class);
try
{
JavaClass entity = getJavaClass();
JavaClass otherEntity = findEntity(fieldType);
entity.addImport(Set.class);
entity.addImport(HashSet.class);
entity.addImport(otherEntity.getQualifiedName());
Field field = entity.addField("private Set<" + otherEntity.getName() + "> " + fieldName + "= new HashSet<"
+ otherEntity.getName() + ">();");
org.jboss.seam.forge.parser.java.Annotation annotation = field.addAnnotation(OneToMany.class);
Refactory.createGetterAndSetter(entity, field);
if ((inverseFieldName != null) && !inverseFieldName.isEmpty())
{
annotation.setStringValue("mappedBy", inverseFieldName);
otherEntity.addImport(Set.class);
otherEntity.addImport(HashSet.class);
otherEntity.addImport(entity.getQualifiedName());
otherEntity.addField("private Set<" + entity.getName() + "> " + inverseFieldName
+ "= new HashSet<" + entity.getName() + ">();")
.addAnnotation(ManyToOne.class);
java.saveJavaClass(otherEntity);
}
java.saveJavaClass(entity);
}
catch (FileNotFoundException e)
{
shell.println("Could not locate the @Entity requested. No update was made.");
}
}
/*
* Helpers
*/
private void addFieldTo(final JavaClass targetEntity, final JavaClass fieldEntity, final String fieldName,
final Class<? extends Annotation> annotation) throws FileNotFoundException
{
Project project = getCurrentProject();
JavaSourceFacet java = project.getFacet(JavaSourceFacet.class);
Field field = targetEntity.addField();
field.setName(fieldName).setPrivate().setType(fieldEntity.getName()).addAnnotation(annotation);
targetEntity.addImport(fieldEntity.getQualifiedName());
Refactory.createGetterAndSetter(targetEntity, field);
java.saveJavaClass(targetEntity);
shell.println("Added field to " + targetEntity.getQualifiedName() + ": " + field);
}
private void addFieldTo(final JavaClass targetEntity, final String fieldType, final String fieldName,
final Class<Column> annotation) throws FileNotFoundException
{
Project project = getCurrentProject();
JavaSourceFacet java = project.getFacet(JavaSourceFacet.class);
Field field = targetEntity.addField();
field.setName(fieldName).setPrivate().setType(Types.toSimpleName(fieldType)).addAnnotation(annotation);
targetEntity.addImport(fieldType);
Refactory.createGetterAndSetter(targetEntity, field);
java.saveJavaClass(targetEntity);
shell.println("Added field to " + targetEntity.getQualifiedName() + ": " + field);
}
private void addFieldTo(final JavaClass targetEntity, final Class<?> fieldType, final String fieldName,
final Class<? extends Annotation> annotation) throws FileNotFoundException
{
Project project = getCurrentProject();
JavaSourceFacet java = project.getFacet(JavaSourceFacet.class);
Field field = targetEntity.addField();
field.setName(fieldName).setPrivate().setType(fieldType).addAnnotation(annotation);
if (!fieldType.getName().startsWith("java.lang.") && !fieldType.isPrimitive())
{
targetEntity.addImport(fieldType);
}
Refactory.createGetterAndSetter(targetEntity, field);
java.saveJavaClass(targetEntity);
shell.println("Added field to " + targetEntity.getQualifiedName() + ": " + field);
}
public Project getCurrentProject()
{
return projectInstance.get();
}
private JavaClass getJavaClass() throws FileNotFoundException
{
if (shell.getCurrentResource() instanceof JavaResource)
{
return ((JavaResource) shell.getCurrentResource()).getJavaClass();
}
else
{
throw new RuntimeException("current resource is not a JavaResource!");
}
}
private JavaClass findEntity(final String entity) throws FileNotFoundException
{
JavaClass result = null;
Project project = getCurrentProject();
PersistenceFacet scaffold = project.getFacet(PersistenceFacet.class);
JavaSourceFacet java = project.getFacet(JavaSourceFacet.class);
if (entity != null)
{
result = java.getJavaResource(entity).getJavaClass();
if (result == null)
{
result = java.getJavaResource(scaffold.getEntityPackage() + "." + entity).getJavaClass();
}
}
if (result == null)
{
result = promptForEntity();
}
if (result == null)
{
throw new FileNotFoundException("Could not locate JavaClass on which to operate.");
}
return result;
}
private JavaClass promptForEntity()
{
Project project = getCurrentProject();
PersistenceFacet scaffold = project.getFacet(PersistenceFacet.class);
List<JavaClass> entities = scaffold.getAllEntities();
List<String> entityNames = new ArrayList<String>();
for (JavaClass javaClass : entities)
{
String fullName = javaClass.getPackage();
if (!fullName.isEmpty())
{
fullName += ".";
}
fullName += javaClass.getName();
entityNames.add(fullName);
}
if (!entityNames.isEmpty())
{
int index = shell.promptChoice("Which entity would you like to modify?", entityNames);
return entities.get(index);
}
return null;
}
}
/*
* JBoss, Home of Professional Open Source
* Copyright 2010, 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.seam.forge.persistence;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Named;
import javax.persistence.Entity;
import org.jboss.seam.forge.parser.JavaParser;
import org.jboss.seam.forge.parser.java.JavaClass;
import org.jboss.seam.forge.project.Facet;
import org.jboss.seam.forge.project.PackagingType;
import org.jboss.seam.forge.project.Project;
import org.jboss.seam.forge.project.Resource;
import org.jboss.seam.forge.project.constraints.RequiresFacets;
import org.jboss.seam.forge.project.constraints.RequiresPackagingTypes;
import org.jboss.seam.forge.project.dependencies.Dependency;
import org.jboss.seam.forge.project.dependencies.DependencyBuilder;
import org.jboss.seam.forge.project.facets.DependencyFacet;
import org.jboss.seam.forge.project.facets.JavaSourceFacet;
import org.jboss.seam.forge.project.facets.ResourceFacet;
import org.jboss.seam.forge.project.resources.FileResource;
import org.jboss.seam.forge.project.resources.builtin.DirectoryResource;
import org.jboss.seam.forge.project.resources.builtin.JavaResource;
import org.jboss.shrinkwrap.descriptor.api.DescriptorImporter;
import org.jboss.shrinkwrap.descriptor.api.Descriptors;
import org.jboss.shrinkwrap.descriptor.api.spec.jpa.persistence.PersistenceDescriptor;
import org.jboss.shrinkwrap.descriptor.api.spec.jpa.persistence.PersistenceUnitDef;
import org.jboss.shrinkwrap.descriptor.api.spec.jpa.persistence.ProviderType;
import org.jboss.shrinkwrap.descriptor.api.spec.jpa.persistence.SchemaGenerationModeType;
import org.jboss.shrinkwrap.descriptor.api.spec.jpa.persistence.TransactionType;
import org.jboss.shrinkwrap.descriptor.impl.spec.jpa.persistence.PersistenceDescriptorImpl;
import org.jboss.shrinkwrap.descriptor.impl.spec.jpa.persistence.PersistenceModel;
import org.jboss.shrinkwrap.descriptor.spi.SchemaDescriptorProvider;
/**
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
@Named("persistence")
@RequiresFacets({ JavaSourceFacet.class, ResourceFacet.class, DependencyFacet.class })
@RequiresPackagingTypes({ PackagingType.JAR, PackagingType.WAR })
public class PersistenceFacet implements Facet
{
private static final Dependency dep =
DependencyBuilder.create("org.jboss.spec:jboss-javaee-6.0:1.0.0.CR1:provided:basic");
private Project project;
@Override
public Project getProject()
{
return project;
}
@Override
public void setProject(final Project project)
{
this.project = project;
}
@Override
public Facet install()
{
if (!isInstalled())
{
DependencyFacet deps = project.getFacet(DependencyFacet.class);
if (!deps.hasDependency(dep))
{
deps.addDependency(dep);
}
DirectoryResource entityRoot = getEntityPackageFile();
if (!entityRoot.exists())
{
entityRoot.mkdirs();
}
installUtils();
FileResource<?> descriptor = getConfigFile();
if (!descriptor.exists())
{
PersistenceUnitDef unit = Descriptors.create(PersistenceDescriptor.class)
.persistenceUnit("default")
.description("The Seam Forge default Persistence Unit")
.transactionType(TransactionType.JTA)
.provider(ProviderType.HIBERNATE)
.jtaDataSource("java:/DefaultDS")
.includeUnlistedClasses()
.schemaGenerationMode(SchemaGenerationModeType.CREATE_DROP)
.showSql()
.formatSql()
.property("hibernate.transaction.flush_before_completion", true);
descriptor.setContents(unit.exportAsString());
}
}
project.registerFacet(this);
return this;
}
private void installUtils()
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
JavaClass util = JavaParser.parse(loader.getResourceAsStream("templates/PersistenceUtil.java"));
JavaClass producer = JavaParser.parse(loader.getResourceAsStream("templates/DatasourceProducer.java"));
JavaSourceFacet java = project.getFacet(JavaSourceFacet.class);
util.setPackage(java.getBasePackage() + ".persist");
producer.setPackage(java.getBasePackage() + ".persist");
try
{
java.saveJavaClass(producer);
java.saveJavaClass(util);
}
catch (FileNotFoundException e)
{
throw new RuntimeException(e);
}
}
@Override
public boolean isInstalled()
{
DependencyFacet deps = project.getFacet(DependencyFacet.class);
boolean hasDependency = deps.hasDependency(dep);
return hasDependency && getEntityPackageFile().exists() && getConfigFile().exists();
}
public String getEntityPackage()
{
JavaSourceFacet sourceFacet = project.getFacet(JavaSourceFacet.class);
return sourceFacet.getBasePackage() + ".domain";
}
public DirectoryResource getEntityPackageFile()
{
JavaSourceFacet sourceFacet = project.getFacet(JavaSourceFacet.class);
return (DirectoryResource) sourceFacet.getBasePackageResource().getChildDirectory("domain");
}
@SuppressWarnings("unchecked")
public PersistenceModel getConfig()
{
DescriptorImporter<PersistenceDescriptor> importer = Descriptors.importAs(PersistenceDescriptor.class);
PersistenceDescriptor descriptor = importer.from(getConfigFile().getResourceInputStream());
PersistenceModel model = ((SchemaDescriptorProvider<PersistenceModel>) descriptor).getSchemaModel();
return model;
}
public void saveConfig(final PersistenceModel model)
{
PersistenceDescriptor descriptor = new PersistenceDescriptorImpl(model);
String output = descriptor.exportAsString();
getConfigFile().setContents(output);
}
private FileResource<?> getConfigFile()
{
ResourceFacet resources = project.getFacet(ResourceFacet.class);
return (FileResource<?>) resources.getResourceFolder().getChild("META-INF" + File.separator + "persistence.xml");
}
public List<JavaClass> getAllEntities()
{
DirectoryResource packageFile = getEntityPackageFile();
return findEntitiesInFolder(packageFile);
}
private List<JavaClass> findEntitiesInFolder(final DirectoryResource packageFile)
{
List<JavaClass> result = new ArrayList<JavaClass>();
if (packageFile.exists())
{
for (Resource<?> source : packageFile.listResources())
{
if (source instanceof JavaResource)
{
try
{
JavaClass javaClass = ((JavaResource) source).getJavaClass();
if (javaClass.hasAnnotation(Entity.class))
{
result.add(javaClass);
}
}
catch (FileNotFoundException e)
{
throw new IllegalStateException(e);
}
}
}
for (Resource<?> source : packageFile.listResources())
{
if (source instanceof DirectoryResource)
{
List<JavaClass> subResults = findEntitiesInFolder((DirectoryResource) source);
result.addAll(subResults);
}
}
}
return result;
}
}
/*
* JBoss, by Red Hat.
* Copyright 2010, 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.seam.forge.shell.plugins;
/**
* A custom {@link Plugin} must implement this interface in order to be detected and installed at framework boot-time.
* In order to create plugin shell-commands, one must create a method annotated with @{@link Command}. Any command
* method parameters to be provided as input through the shell must be individually annotated with the @{@link Option}
* annotation; other (non-annotated) command parameters are ignored.
*
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*
*/
public interface Plugin
{
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment