Repository: zest-qi4j Updated Branches: refs/heads/Gradle_archetype_toolchain [created] 28e0817dc
Getting a Qi4j project creation tool working. Project: http://git-wip-us.apache.org/repos/asf/zest-qi4j/repo Commit: http://git-wip-us.apache.org/repos/asf/zest-qi4j/commit/28e0817d Tree: http://git-wip-us.apache.org/repos/asf/zest-qi4j/tree/28e0817d Diff: http://git-wip-us.apache.org/repos/asf/zest-qi4j/diff/28e0817d Branch: refs/heads/Gradle_archetype_toolchain Commit: 28e0817dc1b0e3475002a877c39f6a528d7db691 Parents: d7159b1 Author: Niclas Hedhman <[email protected]> Authored: Wed Apr 8 19:05:29 2015 +0800 Committer: Niclas Hedhman <[email protected]> Committed: Wed Apr 8 19:05:29 2015 +0800 ---------------------------------------------------------------------- build.gradle | 7 + .../qi4j/runtime/composite/CompositeModel.java | 2 +- tools/shell/build.gradle | 9 +- tools/shell/dev-status.xml | 20 +++ tools/shell/src/bin/qi4j | 4 - .../templates/defaultproject/project.properties | 0 tools/shell/src/main/dist/bin/qi4j | 4 + .../project-templates/simple/project.properties | 31 ++++ .../simple/src/main/ApplicationTemplate.java | 17 ++ .../simple/src/main/LayerTemplate.java | 22 +++ .../project-templates/simple/src/main/Main.java | 0 .../simple/src/main/ModuleTemplate.java | 16 ++ .../main/java/org/qi4j/tools/shell/Command.java | 3 +- .../java/org/qi4j/tools/shell/FileUtils.java | 62 ++++++++ .../main/java/org/qi4j/tools/shell/Main.java | 93 +++++++++-- .../java/org/qi4j/tools/shell/StringUtils.java | 32 ++++ .../qi4j/tools/shell/create/CreateProject.java | 43 ----- .../shell/create/project/CreateProject.java | 78 +++++++++ .../org/qi4j/tools/shell/generate/Generate.java | 76 +++++++++ .../org/qi4j/tools/shell/help/HelpCommand.java | 64 +++++--- .../tools/shell/model/ApplicationTemplate.java | 46 ++++++ .../qi4j/tools/shell/model/AssemblerModel.java | 78 +++++++++ .../java/org/qi4j/tools/shell/model/Layer.java | 83 ++++++++++ .../java/org/qi4j/tools/shell/model/Model.java | 96 ++++++++++++ .../java/org/qi4j/tools/shell/model/Module.java | 74 +++++++++ .../org/qi4j/tools/shell/model/Nameable.java | 57 +++++++ .../org/qi4j/tools/shell/model/Project.java | 157 +++++++++++++++++++ .../model/ProjectAlreadyExistsException.java | 17 ++ .../model/ProjectDescriptorByProperties.java | 86 ++++++++++ .../model/TemplateAlreadyExistsException.java | 16 ++ .../model/generation/AssemblerGenerator.java | 92 +++++++++++ .../tools/shell/templating/TemplateEngine.java | 40 +++++ .../shell/templating/TemplatingEngineTest.java | 40 +++++ 33 files changed, 1377 insertions(+), 88 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/build.gradle ---------------------------------------------------------------------- diff --git a/build.gradle b/build.gradle index 20f215e..575d6c0 100644 --- a/build.gradle +++ b/build.gradle @@ -558,8 +558,15 @@ def tutorialsImage = copySpec { exclude '**/*.iml' } +def shellImage = copySpec { + from( "$projectDir/tools/shell/src/main/dist" ) + into( "." ) + include '**' +} + def binDistImage = copySpec { into "qi4j-sdk-$version" + with shellImage with docsImage with reportsDistImage with runtimeDependenciesListImage http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/core/runtime/src/main/java/org/qi4j/runtime/composite/CompositeModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/composite/CompositeModel.java b/core/runtime/src/main/java/org/qi4j/runtime/composite/CompositeModel.java index c689929..abb0bbd 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/composite/CompositeModel.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/composite/CompositeModel.java @@ -246,7 +246,7 @@ public abstract class CompositeModel // if (!matchesAny( isAssignableFrom( mixinType ), types )) if( !mixinsModel.isImplemented( mixinType ) ) { - throw new IllegalArgumentException( "Composite does not implement type " + mixinType.getName() ); + throw new IllegalArgumentException( "Composite " + types().iterator().next() + " does not implement type " + mixinType.getName() ); } // Instantiate proxy for given mixin interface http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/build.gradle ---------------------------------------------------------------------- diff --git a/tools/shell/build.gradle b/tools/shell/build.gradle index a3d89ac..fb89106 100644 --- a/tools/shell/build.gradle +++ b/tools/shell/build.gradle @@ -1,12 +1,17 @@ -apply plugin: 'application' description = "Command line tools for building Qi4j applications." -mainClassName = "org.qi4j.tools.shell.Main" jar { manifest { name = "Qi4j Command Line" } } dependencies { + compile( project( ":org.qi4j.core:org.qi4j.core.api" ) ) compile( project( ":org.qi4j.core:org.qi4j.core.bootstrap" ) ) + compile( project( ":org.qi4j.extensions:org.qi4j.extension.entitystore-file" ) ) + compile( project( ":org.qi4j.extensions:org.qi4j.extension.valueserialization-jackson" ) ) + runtime( project( ":org.qi4j.core:org.qi4j.core.runtime" ) ) + testRuntime( libraries.logback ) } + + http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/dev-status.xml ---------------------------------------------------------------------- diff --git a/tools/shell/dev-status.xml b/tools/shell/dev-status.xml new file mode 100644 index 0000000..edd1bc6 --- /dev/null +++ b/tools/shell/dev-status.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<module xmlns="http://www.qi4j.org/schemas/2008/dev-status/1" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.qi4j.org/schemas/2008/dev-status/1 + http://www.qi4j.org/schemas/2008/dev-status/1/dev-status.xsd"> + <status> + <!--none,early,beta,stable,mature--> + <codebase>stable</codebase> + + <!-- none, brief, good, complete --> + <documentation>brief</documentation> + + <!-- none, some, good, complete --> + <unittests>some</unittests> + + </status> + <licenses> + <license>ALv2</license> + </licenses> +</module> http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/bin/qi4j ---------------------------------------------------------------------- diff --git a/tools/shell/src/bin/qi4j b/tools/shell/src/bin/qi4j deleted file mode 100644 index d678cfd..0000000 --- a/tools/shell/src/bin/qi4j +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -java -jar org.qi4j.tools.shell-@@version@@.jar "$@" - http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/etc/templates/defaultproject/project.properties ---------------------------------------------------------------------- diff --git a/tools/shell/src/etc/templates/defaultproject/project.properties b/tools/shell/src/etc/templates/defaultproject/project.properties deleted file mode 100644 index e69de29..0000000 http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/dist/bin/qi4j ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/dist/bin/qi4j b/tools/shell/src/main/dist/bin/qi4j new file mode 100644 index 0000000..d678cfd --- /dev/null +++ b/tools/shell/src/main/dist/bin/qi4j @@ -0,0 +1,4 @@ +#!/bin/sh + +java -jar org.qi4j.tools.shell-@@version@@.jar "$@" + http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/dist/etc/project-templates/simple/project.properties ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/dist/etc/project-templates/simple/project.properties b/tools/shell/src/main/dist/etc/project-templates/simple/project.properties new file mode 100644 index 0000000..db3e344 --- /dev/null +++ b/tools/shell/src/main/dist/etc/project-templates/simple/project.properties @@ -0,0 +1,31 @@ + +layers=5 +layer.0.name=configuration layer +layer.1.name=infrastructure layer +layer.2.name=domain layer +layer.3.name=service layer +layer.4.name=interface layer + +layer.0.modules=1 +layer.0.module.0.name=configuration + +layer.1.modules=2 +layer.1.uses=configuration layer +layer.1.module.0.name=persistence +layer.1.module.1.name=indexing + +layer.2.modules=2 +layer.2.uses=configuration layer, infrastructure layer +layer.2.module.0.name=first +layer.2.module.1.name=second + +layer.3.modules=2 +layer.3.uses=domain layer +layer.3.module.0.name=first +layer.3.module.1.name=second + +layer.4.modules=2 +layer.4.uses=service layer +layer.4.module.0.name=rest +layer.4.module.1.name=web + http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/dist/etc/project-templates/simple/src/main/ApplicationTemplate.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/dist/etc/project-templates/simple/src/main/ApplicationTemplate.java b/tools/shell/src/main/dist/etc/project-templates/simple/src/main/ApplicationTemplate.java new file mode 100644 index 0000000..638127c --- /dev/null +++ b/tools/shell/src/main/dist/etc/project-templates/simple/src/main/ApplicationTemplate.java @@ -0,0 +1,17 @@ + +package @@ROOT_PACKAGE@@.boot; + +import org.qi4j.bootstrap.ApplicationAssembly; +import org.qi4j.bootstrap.AssemblyException; +import org.qi4j.bootstrap.LayerAssembly; + +@@IMPORTS@@ + +public class @@APPLICATION_NAME@@Assembler +{ + public LayerAssembly assemble( ApplicationAssembly assembly ) + throws AssemblyException + { +@@LAYER_CREATION@@ + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/dist/etc/project-templates/simple/src/main/LayerTemplate.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/dist/etc/project-templates/simple/src/main/LayerTemplate.java b/tools/shell/src/main/dist/etc/project-templates/simple/src/main/LayerTemplate.java new file mode 100644 index 0000000..6878195 --- /dev/null +++ b/tools/shell/src/main/dist/etc/project-templates/simple/src/main/LayerTemplate.java @@ -0,0 +1,22 @@ + +package @@ROOT_PACKAGE@@.boot; + +import org.qi4j.bootstrap.ApplicationAssembly; +import org.qi4j.bootstrap.AssemblyException; +import org.qi4j.bootstrap.LayerAssembly; + +@@IMPORTS@@ + +public class @@LAYER_NAME@@Assembler +{ + public @@LAYER_NAME@@Assembler() + {} + + public LayerAssembly assemble( ApplicationAssembly assembly ) + throws AssemblyException + { +@@MODULE_CREATION@@ + } + +@@MODULE_CREATE_METHODS@@ +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/dist/etc/project-templates/simple/src/main/Main.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/dist/etc/project-templates/simple/src/main/Main.java b/tools/shell/src/main/dist/etc/project-templates/simple/src/main/Main.java new file mode 100644 index 0000000..e69de29 http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/dist/etc/project-templates/simple/src/main/ModuleTemplate.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/dist/etc/project-templates/simple/src/main/ModuleTemplate.java b/tools/shell/src/main/dist/etc/project-templates/simple/src/main/ModuleTemplate.java new file mode 100644 index 0000000..166de66 --- /dev/null +++ b/tools/shell/src/main/dist/etc/project-templates/simple/src/main/ModuleTemplate.java @@ -0,0 +1,16 @@ + +package @@ROOT_PACKAGE@@.boot; + +import org.qi4j.bootstrap.ApplicationAssembler; + + +public class @@MODULE_NAME@@Assembler + implements Assembler +{ + public void assemble( ModuleAssembly module ) + throws AssemblyException + { + @@MODULE_ASSEMBLY@@ + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/Command.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/Command.java b/tools/shell/src/main/java/org/qi4j/tools/shell/Command.java index 046de85..0b82675 100644 --- a/tools/shell/src/main/java/org/qi4j/tools/shell/Command.java +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/Command.java @@ -1,12 +1,13 @@ package org.qi4j.tools.shell; import java.io.BufferedReader; +import java.io.IOException; import java.io.PrintWriter; public interface Command { void execute( String[] args, BufferedReader input, PrintWriter output ) - throws HelpNeededException; + throws HelpNeededException, IOException; String description(); http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/FileUtils.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/FileUtils.java b/tools/shell/src/main/java/org/qi4j/tools/shell/FileUtils.java index e0f96ba..5dac990 100644 --- a/tools/shell/src/main/java/org/qi4j/tools/shell/FileUtils.java +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/FileUtils.java @@ -1,8 +1,15 @@ package org.qi4j.tools.shell; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.util.HashMap; import java.util.Map; import java.util.Properties; @@ -19,6 +26,15 @@ public class FileUtils } } + public static void createDir( File dir ) + { + if( !dir.mkdirs() ) + { + System.err.println( "Unable to create directory " + dir ); + System.exit( 1 ); + } + } + public static Map<String, String> readPropertiesResource( String resourceName ) { ClassLoader cl = FileUtils.class.getClassLoader(); @@ -48,4 +64,50 @@ public class FileUtils p.load( in ); return p; } + + public static void writeFile( File file, String data ) + throws IOException + { + try (BufferedWriter writer = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( file ), "UTF-8" ) )) + { + writer.write( data ); + } + } + + public static Map<String, String> readPropertiesFile( File file ) + { + try (InputStream in = new BufferedInputStream( new FileInputStream( file ) )) + { + Properties properties = readProperties( in ); + Map<String, String> result = new HashMap<String, String>(); + for( Map.Entry prop : properties.entrySet() ) + { + result.put( prop.getKey().toString(), prop.getValue().toString() ); + } + return result; + } + catch( IOException e ) + { + System.err.println( "Unable to read file " + file.getAbsolutePath() ); + System.exit( 2 ); + return null; + } + } + + public static String readFile( File file ) + throws IOException + { + try (BufferedReader reader = new BufferedReader( new InputStreamReader( new FileInputStream( file ), "UTF-8" ) )) + { + StringBuilder builder = new StringBuilder(); + String line = reader.readLine(); + while( line != null ) + { + builder.append( line ); + builder.append( '\n' ); + line = reader.readLine(); + } + return builder.toString(); + } + } } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/Main.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/Main.java b/tools/shell/src/main/java/org/qi4j/tools/shell/Main.java index 9a5966d..08810af 100644 --- a/tools/shell/src/main/java/org/qi4j/tools/shell/Main.java +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/Main.java @@ -1,40 +1,107 @@ package org.qi4j.tools.shell; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; -import java.util.TreeSet; -import org.qi4j.tools.shell.create.CreateProject; +import org.qi4j.api.activation.ActivationException; +import org.qi4j.api.activation.PassivationException; +import org.qi4j.api.common.Visibility; +import org.qi4j.api.service.ServiceReference; +import org.qi4j.api.structure.Module; +import org.qi4j.bootstrap.AssemblyException; +import org.qi4j.bootstrap.ModuleAssembly; +import org.qi4j.bootstrap.SingletonAssembler; +import org.qi4j.entitystore.file.assembly.FileEntityStoreAssembler; +import org.qi4j.entitystore.memory.MemoryEntityStoreService; +import org.qi4j.functional.Specification; +import org.qi4j.tools.shell.create.project.CreateProject; +import org.qi4j.tools.shell.generate.Generate; +import org.qi4j.tools.shell.model.Model; import org.qi4j.tools.shell.help.HelpCommand; +import org.qi4j.tools.shell.model.Layer; +import org.qi4j.tools.shell.model.Project; +import org.qi4j.valueserialization.jackson.JacksonValueSerializationAssembler; + +import static org.qi4j.functional.Iterables.filter; +import static org.qi4j.functional.Iterables.first; public class Main { - private TreeSet<Command> commands = new TreeSet<Command>(); - public static void main( String[] args ) + throws Exception { new Main().run( args ); } - public Main() - { - this.commands.add( new HelpCommand() ); - this.commands.add( new CreateProject() ); - } - private void run( String[] args ) + throws ActivationException, AssemblyException, IOException { if( !contains( args, "-q" ) ) { System.out.println( "Qi4j - Classes are Dead. Long Live Interfaces!" ); System.out.println( "----------------------------------------------\n" ); } + String commandText; if( args.length == 0 ) { - HelpCommand helpCommand = new HelpCommand(); - helpCommand.setCommands( commands ); - helpCommand.execute( args, input(), output() ); + commandText = "help"; } + else + { + commandText = args[ 0 ]; + } + final SingletonAssembler assembler = new SingletonAssembler() + { + @Override + public void assemble( ModuleAssembly module ) + throws AssemblyException + { + module.services( HelpCommand.class ).identifiedBy( "help" ).instantiateOnStartup(); + module.services( CreateProject.class ).identifiedBy( "create-project" ).instantiateOnStartup(); + module.services( Model.class ).instantiateOnStartup(); + module.services( Generate.class ).instantiateOnStartup(); + + module.entities( Project.class, Layer.class, org.qi4j.tools.shell.model.Module.class ); + new JacksonValueSerializationAssembler().assemble( module ); + + ModuleAssembly configModule = module.layer().module( "config" ); + new FileEntityStoreAssembler().withConfig( configModule, Visibility.layer ).assemble( module ); + new JacksonValueSerializationAssembler().assemble( configModule ); + configModule.services( MemoryEntityStoreService.class ); + } + }; + Runtime.getRuntime().addShutdownHook( new Thread( new Runnable() + { + @Override + public void run() + { + try + { + assembler.application().passivate(); + } + catch( PassivationException e ) + { + e.printStackTrace(); + } + } + } ) ); + executeCommand( commandText, args, assembler.module() ); + output().flush(); + } + + private void executeCommand( final String command, String[] args, Module module ) + throws IOException + { + ServiceReference<Command> ref = first( filter( new Specification<ServiceReference<Command>>() + { + @Override + public boolean satisfiedBy( ServiceReference<Command> item ) + { + return item.get().name().equals( command ); + } + }, module.findServices( Command.class ) ) ); + ref.get().execute( args, input(), output() ); } private boolean contains( String[] args, String s ) http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/StringUtils.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/StringUtils.java b/tools/shell/src/main/java/org/qi4j/tools/shell/StringUtils.java new file mode 100644 index 0000000..2ad31b8 --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/StringUtils.java @@ -0,0 +1,32 @@ +package org.qi4j.tools.shell; + +public class StringUtils +{ + private StringUtils() + { + } + + public static String camelCase( String text, boolean firstUpper ) + { + StringBuilder builder = new StringBuilder( text.length() ); + boolean initial = firstUpper; + for( int i = 0; i < text.length(); i++ ) + { + char ch = text.charAt( i ); + if( initial ) + { + ch = Character.toUpperCase( ch ); + initial = false; + } + if( ch != ' ' ) + { + builder.append( ch ); + } + else + { + initial = true; + } + } + return builder.toString(); + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/create/CreateProject.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/create/CreateProject.java b/tools/shell/src/main/java/org/qi4j/tools/shell/create/CreateProject.java deleted file mode 100644 index 0c864ab..0000000 --- a/tools/shell/src/main/java/org/qi4j/tools/shell/create/CreateProject.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.qi4j.tools.shell.create; - -import java.io.BufferedReader; -import java.io.PrintWriter; -import java.util.Map; -import java.util.Properties; -import org.qi4j.tools.shell.AbstractCommand; -import org.qi4j.tools.shell.FileUtils; -import org.qi4j.tools.shell.HelpNeededException; - -public class CreateProject extends AbstractCommand -{ - - @Override - public void execute( String[] args, BufferedReader input, PrintWriter output ) - throws HelpNeededException - { - if( args.length < 1 ) - throw new HelpNeededException(); - String projectName = args[0]; - String template = "defaultproject"; - if( args.length < 2 ) - template = args[1]; - FileUtils.createDir( projectName ); - Map<String, String> props = FileUtils.readPropertiesResource( "templates/" + template + "/project.properties" ); - for( Map.Entry<String,String> p: props.entrySet() ) - { - - } - } - - @Override - public String description() - { - return "create-project"; - } - - @Override - public String name() - { - return "create-project"; - } -} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/create/project/CreateProject.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/create/project/CreateProject.java b/tools/shell/src/main/java/org/qi4j/tools/shell/create/project/CreateProject.java new file mode 100644 index 0000000..58d69ff --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/create/project/CreateProject.java @@ -0,0 +1,78 @@ +package org.qi4j.tools.shell.create.project; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import org.qi4j.api.concern.Concerns; +import org.qi4j.api.injection.scope.Service; +import org.qi4j.api.injection.scope.Structure; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.unitofwork.UnitOfWorkFactory; +import org.qi4j.api.unitofwork.concern.UnitOfWorkConcern; +import org.qi4j.api.unitofwork.concern.UnitOfWorkPropagation; +import org.qi4j.tools.shell.Command; +import org.qi4j.tools.shell.HelpNeededException; +import org.qi4j.tools.shell.model.Model; +import org.qi4j.tools.shell.model.Project; +import org.qi4j.tools.shell.model.ProjectDescriptorByProperties; + +@Mixins( CreateProject.Mixin.class ) +@Concerns( UnitOfWorkConcern.class ) +public interface CreateProject extends Command +{ + public class Mixin + implements Command + { + @Service + private Model model; + + @Service + private ProjectDescriptorByProperties loadFromProperties; + + @Structure + private UnitOfWorkFactory uowf; + + @Override + @UnitOfWorkPropagation + public void execute( String[] args, BufferedReader input, PrintWriter output ) + throws HelpNeededException, IOException + { + if( args.length < 2 ) + { + throw new HelpNeededException(); + } + String projectName = args[ 1 ]; + + String rootPackage = "com.example." + projectName.toLowerCase().replace( '-', '_' ); + if( args.length >= 3 ) + { + rootPackage = args[ 2 ]; + } + String template = "simple"; + if( args.length >= 4 ) + { + template = args[ 3 ]; + } + model.setHomeDirectory( new File( System.getProperty( "homeDir" ) ) ); + String propsLocation = System.getProperty( "homeDir" ) + "/project-templates/" + template + "/project.properties"; + + loadFromProperties.parse( projectName, new File( propsLocation ) ); + Project project = loadFromProperties.findProject( projectName ); + project.setApplicationVersion( System.getProperty( "version", "1" ) ); + project.setRootPackageName( rootPackage ); + } + + @Override + public String description() + { + return "Creates a managed project."; + } + + @Override + public String name() + { + return "create-project"; + } + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/generate/Generate.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/generate/Generate.java b/tools/shell/src/main/java/org/qi4j/tools/shell/generate/Generate.java new file mode 100644 index 0000000..0e8eb87 --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/generate/Generate.java @@ -0,0 +1,76 @@ +package org.qi4j.tools.shell.generate; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; +import org.qi4j.api.concern.Concerns; +import org.qi4j.api.injection.scope.Service; +import org.qi4j.api.injection.scope.Structure; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.unitofwork.UnitOfWorkFactory; +import org.qi4j.api.unitofwork.concern.UnitOfWorkConcern; +import org.qi4j.api.unitofwork.concern.UnitOfWorkPropagation; +import org.qi4j.tools.shell.Command; +import org.qi4j.tools.shell.FileUtils; +import org.qi4j.tools.shell.HelpNeededException; +import org.qi4j.tools.shell.StringUtils; +import org.qi4j.tools.shell.model.Layer; +import org.qi4j.tools.shell.model.Model; +import org.qi4j.tools.shell.model.Project; +import org.qi4j.tools.shell.templating.TemplateEngine; + +@Mixins( Generate.Mixin.class ) +@Concerns( UnitOfWorkConcern.class ) +public interface Generate extends Command +{ + + public class Mixin + implements Generate + { + @Service + private Model model; + + @Structure + private UnitOfWorkFactory uowf; + + @Override + @UnitOfWorkPropagation + public void execute( String[] args, BufferedReader input, PrintWriter output ) + throws HelpNeededException, IOException + { + if( args.length < 2 ) + { + throw new HelpNeededException(); + } + + Project project = Project.Support.get( uowf.currentUnitOfWork(), args[ 1 ] ); + if( project == null ) + { + System.err.print( "Project " + args[ 1 ] + " does not exist." ); + return; + } + model.setHomeDirectory( new File( System.getProperty( "homeDir" ) ) ); + File projectDir = new File( project.applicationName() ); + if( args.length >= 3 ) + { + projectDir = new File(args[2]).getAbsoluteFile(); + } + project.generate( projectDir ); + } + + @Override + public String description() + { + return "Generates the file structure from what has been built up in the database."; + } + + @Override + public String name() + { + return "generate"; + } + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/help/HelpCommand.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/help/HelpCommand.java b/tools/shell/src/main/java/org/qi4j/tools/shell/help/HelpCommand.java index 45131d7..1d9ead3 100644 --- a/tools/shell/src/main/java/org/qi4j/tools/shell/help/HelpCommand.java +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/help/HelpCommand.java @@ -2,40 +2,56 @@ package org.qi4j.tools.shell.help; import java.io.BufferedReader; import java.io.PrintWriter; -import org.qi4j.tools.shell.AbstractCommand; +import org.qi4j.api.injection.scope.Service; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.service.ServiceReference; import org.qi4j.tools.shell.Command; -public class HelpCommand extends AbstractCommand +@Mixins( HelpCommand.Mixin.class ) +public interface HelpCommand extends Command { - private Iterable<Command> commands; - public HelpCommand() + public class Mixin + implements Command { - } - public void setCommands( Iterable<Command> comands ) - { - this.commands = commands; - } + @Service + private Iterable<ServiceReference<Command>> commands; - @Override - public void execute( String[] args, BufferedReader input, PrintWriter output ) - { - for( Command command : commands ) + @Override + public void execute( String[] args, BufferedReader input, PrintWriter output ) { - output.println( command.name() + "\t" + command.description() ); + for( ServiceReference<Command> ref : commands ) + { + Command command = ref.get(); + String name = command.name(); + String spacing = createSpacing( name ); + output.println( name + spacing + command.description() ); + output.flush(); + } } - } - @Override - public String description() - { - return "help"; - } + private String createSpacing( String name ) + { + int length = 20 - name.length(); + StringBuilder builder = new StringBuilder( length + 1 ); + for( int i = 0; i < length; i++ ) + { + builder.append( ' ' ); + } + return builder.toString(); + } - @Override - public String name() - { - return "Prints this help text."; + @Override + public String description() + { + return "Prints this help text."; + } + + @Override + public String name() + { + return "help"; + } } } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/model/ApplicationTemplate.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/model/ApplicationTemplate.java b/tools/shell/src/main/java/org/qi4j/tools/shell/model/ApplicationTemplate.java new file mode 100644 index 0000000..a901b0a --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/model/ApplicationTemplate.java @@ -0,0 +1,46 @@ +package org.qi4j.tools.shell.model; + +import java.util.Map; +import org.qi4j.api.injection.scope.This; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.property.Property; +import org.qi4j.api.unitofwork.UnitOfWork; +import org.qi4j.tools.shell.templating.TemplateEngine; + +@Mixins(ApplicationTemplate.Mixin.class) +public interface ApplicationTemplate extends Nameable +{ + String evaluate( Map<String, String> variables ); + + interface State + { + Property<String> template(); + } + + public class Support + { + public static ApplicationTemplate get( UnitOfWork uow, String name ) + { + return uow.get( ApplicationTemplate.class, identity(name) ); + } + + public static String identity( String name ) + { + return "Template:" + name; + } + } + + abstract class Mixin + implements ApplicationTemplate + { + @This + private State state; + + @Override + public String evaluate( Map<String, String> variables ) + { + TemplateEngine engine = new TemplateEngine( state.template().get() ); + return engine.create( variables ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/model/AssemblerModel.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/model/AssemblerModel.java b/tools/shell/src/main/java/org/qi4j/tools/shell/model/AssemblerModel.java new file mode 100644 index 0000000..c830d34 --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/model/AssemblerModel.java @@ -0,0 +1,78 @@ +package org.qi4j.tools.shell.model; + +import java.io.File; +import java.io.IOException; +import org.qi4j.api.injection.scope.This; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.property.Property; +import org.qi4j.tools.shell.model.generation.AssemblerGenerator; + +@Mixins( { AssemblerModel.Mixin.class, AssemblerGenerator.class } ) +public interface AssemblerModel +{ + // Commands + void generate( Project project, File projectDir ) + throws IOException; + + // Queries + String packageName(); + + // Queries + File mainJavaRootPackageDirectory( File projectDir ); + + File mainResourcesRootPackageDirectory( File projectDir ); + + File testJavaRootPackageDirectory( File projectDir ); + + File testResourcesRootPackageDirectory( File projectDir ); + + interface State + { + Property<String> packageName(); + } + + abstract class Mixin + implements AssemblerModel + { + @This + private State state; + + @Override + public File mainJavaRootPackageDirectory( File projectDir ) + { + return normalize( projectDir, "/bootstrap/src/main/java" ); + } + + @Override + public File mainResourcesRootPackageDirectory( File projectDir ) + { + return normalize( projectDir, "/bootstrap/src/main/resources" ); + } + + @Override + public File testJavaRootPackageDirectory( File projectDir ) + { + return normalize( projectDir, "/bootstrap/src/test/java" ); + } + + @Override + public File testResourcesRootPackageDirectory( File projectDir ) + { + return normalize( projectDir, "/bootstrap/src/test/resources" ); + } + + private File normalize( File projectDir, String location ) + { + File dir = new File( projectDir, location ); + dir = new File( dir, state.packageName().get().replace( '.', '/' ) ); + dir.mkdirs(); + return dir; + } + + @Override + public String packageName() + { + return state.packageName().get(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/model/Layer.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/model/Layer.java b/tools/shell/src/main/java/org/qi4j/tools/shell/model/Layer.java new file mode 100644 index 0000000..adf610e --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/model/Layer.java @@ -0,0 +1,83 @@ +package org.qi4j.tools.shell.model; + +import java.util.List; +import org.qi4j.api.association.Association; +import org.qi4j.api.association.ManyAssociation; +import org.qi4j.api.common.UseDefaults; +import org.qi4j.api.entity.Aggregated; +import org.qi4j.api.injection.scope.Structure; +import org.qi4j.api.injection.scope.This; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.property.Property; +import org.qi4j.api.unitofwork.UnitOfWork; +import org.qi4j.api.unitofwork.UnitOfWorkFactory; +import org.qi4j.api.unitofwork.concern.UnitOfWorkPropagation; +import org.qi4j.tools.shell.StringUtils; + +@Mixins( Layer.Mixin.class ) +public interface Layer extends Nameable +{ + // Commands + void usesLayer( String layer ); + + void createModule( String moduleName ); + + // Queries + Iterable<String> uses(); + + String packageName(); + + interface State + { + @UseDefaults + Property<String> packageName(); + + @UseDefaults + @Aggregated + ManyAssociation<Module> modules(); + + @UseDefaults + Property<List<String>> uses(); + } + + abstract class Mixin + implements Layer + { + @This + private Layer self; + + @This + private State state; + + @Structure + private UnitOfWorkFactory uowf; + + @Override + public Iterable<String> uses() + { + return state.uses().get(); + } + + @Override + public String packageName() + { + return state.packageName().get(); + } + + @Override + @UnitOfWorkPropagation + public void createModule( String moduleName ) + { + UnitOfWork uow = uowf.currentUnitOfWork(); + Nameable.Support.create( uow, Module.class, self, moduleName ); + Module module = Nameable.Support.get( uow, Module.class, self, moduleName ); + state.modules().add( module ); + } + + @Override + public void usesLayer( String layer ) + { + state.uses().get().add( layer ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/model/Model.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/model/Model.java b/tools/shell/src/main/java/org/qi4j/tools/shell/model/Model.java new file mode 100644 index 0000000..5f16038 --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/model/Model.java @@ -0,0 +1,96 @@ +package org.qi4j.tools.shell.model; + +import java.io.File; +import org.qi4j.api.entity.EntityBuilder; +import org.qi4j.api.injection.scope.Structure; +import org.qi4j.api.injection.scope.This; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.property.Property; +import org.qi4j.api.unitofwork.UnitOfWork; +import org.qi4j.api.unitofwork.UnitOfWorkFactory; +import org.qi4j.api.unitofwork.concern.UnitOfWorkPropagation; + +@Mixins( Model.Mixin.class ) +public interface Model +{ + // Commands + void setHomeDirectory( File homeDir ); + + void createProject( String name ) + throws ProjectAlreadyExistsException; + + void addTemplate( String name, String template ) + throws TemplateAlreadyExistsException; + + // Queries + File homeDirectory(); + + Project findProject( String name ); + + interface State + { + Property<File> homeDirectory(); + } + + class Mixin + implements Model + { + @Structure + private UnitOfWorkFactory uowf; + + @This + private State state; + + @Override + public void setHomeDirectory( File homeDir ) + { + state.homeDirectory().set( homeDir ); + } + + @UnitOfWorkPropagation + public void createProject( String name ) + throws ProjectAlreadyExistsException + { + UnitOfWork uow = uowf.currentUnitOfWork(); + Project existing = Project.Support.get( uow, name ); + if( existing != null ) + { + throw new ProjectAlreadyExistsException( name ); + } + Nameable.Support.create( uow, Project.class, null, name ); + } + + @Override + public void addTemplate( String name, String template ) + throws TemplateAlreadyExistsException + { + UnitOfWork uow = uowf.currentUnitOfWork(); + ApplicationTemplate existing = ApplicationTemplate.Support.get( uow, name ); + if( existing != null ) + { + throw new TemplateAlreadyExistsException( name ); + } + EntityBuilder<ApplicationTemplate> builder = + uow.newEntityBuilder( ApplicationTemplate.class, "Template(" + name + ")" ); + ApplicationTemplate.State prototype = builder.instanceFor( ApplicationTemplate.State.class ); + prototype.template().set( template ); + Nameable.State naming = builder.instanceFor( Nameable.State.class ); + naming.name().set( name ); + builder.newInstance(); + } + + @Override + public File homeDirectory() + { + return state.homeDirectory().get(); + } + + @Override + @UnitOfWorkPropagation + public Project findProject( String name ) + { + UnitOfWork uow = uowf.currentUnitOfWork(); + return Nameable.Support.get(uow, Project.class, null, name ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/model/Module.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/model/Module.java b/tools/shell/src/main/java/org/qi4j/tools/shell/model/Module.java new file mode 100644 index 0000000..ca20992 --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/model/Module.java @@ -0,0 +1,74 @@ +package org.qi4j.tools.shell.model; + +import java.io.File; +import org.qi4j.api.injection.scope.This; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.property.Property; + +@Mixins( Module.Mixin.class ) +public interface Module extends Nameable +{ + // Commands + void setPackageName( String packageName ); + + // Queries + File mainJavaRootPackageDirectory( File projectDir ); + + File mainResourcesRootPackageDirectory( File projectDir ); + + File testJavaRootPackageDirectory( File projectDir ); + + File testResourcesRootPackageDirectory( File projectDir ); + + public interface State + { + Property<String> name(); + + Property<String> packageName(); + } + + abstract class Mixin + implements Module + { + @This + private State state; + + @Override + public void setPackageName( String name ) + { + state.packageName().set( name ); + } + + @Override + public File mainJavaRootPackageDirectory( File projectDir ) + { + return normalize( projectDir, "/src/main/java" ); + } + + @Override + public File mainResourcesRootPackageDirectory( File projectDir ) + { + return normalize( projectDir, "/src/main/resources" ); + } + + @Override + public File testJavaRootPackageDirectory(File projectDir) + { + return normalize( projectDir, "/src/test/java" ); + } + + @Override + public File testResourcesRootPackageDirectory(File projectDir) + { + return normalize( projectDir, "/src/test/resources" ); + } + + private File normalize( File projectDir, String location ) + { + File dir = new File( projectDir, name() + location ); + dir = new File( dir, state.packageName().get().replace( '.', '/' ) ); + dir.mkdirs(); + return dir; + } + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/model/Nameable.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/model/Nameable.java b/tools/shell/src/main/java/org/qi4j/tools/shell/model/Nameable.java new file mode 100644 index 0000000..2dac152 --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/model/Nameable.java @@ -0,0 +1,57 @@ +package org.qi4j.tools.shell.model; + +import org.qi4j.api.entity.EntityBuilder; +import org.qi4j.api.injection.scope.This; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.property.Property; +import org.qi4j.api.unitofwork.UnitOfWork; + +@Mixins( Nameable.Mixin.class ) +public interface Nameable +{ + String name(); + + interface State + { + Property<String> name(); + } + + public class Mixin + implements Nameable + { + @This + private State state; + + @Override + public String name() + { + return state.name().get(); + } + } + + public class Support + { + public static <T extends Nameable> String identity( Class<T> type, Nameable parent, String name ) + { + if( parent == null ) + { + return type.getSimpleName() + "(" + name + ")"; + } + return parent.name() + "." + type.getSimpleName() + "(" + name + ")"; + } + + public static <T extends Nameable> T get( UnitOfWork uow, Class<T> type, Nameable parent, String name ) + { + String identity = identity( type, parent, name ); + return uow.get( type, identity ); + } + + public static <T extends Nameable> void create( UnitOfWork uow, Class<T> type, Nameable parent, String name ) + { + EntityBuilder<T> builder = uow.newEntityBuilder( type, identity( type, parent, name ) ); + Nameable.State prototype = builder.instanceFor( Nameable.State.class ); + prototype.name().set( name ); + builder.newInstance(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/model/Project.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/model/Project.java b/tools/shell/src/main/java/org/qi4j/tools/shell/model/Project.java new file mode 100644 index 0000000..f26b7ec --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/model/Project.java @@ -0,0 +1,157 @@ +package org.qi4j.tools.shell.model; + +import java.io.File; +import java.io.IOException; +import org.qi4j.api.association.Association; +import org.qi4j.api.association.ManyAssociation; +import org.qi4j.api.common.UseDefaults; +import org.qi4j.api.entity.Aggregated; +import org.qi4j.api.injection.scope.Structure; +import org.qi4j.api.injection.scope.This; +import org.qi4j.api.mixin.Mixins; +import org.qi4j.api.property.Property; +import org.qi4j.api.unitofwork.UnitOfWork; +import org.qi4j.api.unitofwork.UnitOfWorkFactory; +import org.qi4j.api.unitofwork.concern.UnitOfWorkPropagation; +import org.qi4j.tools.shell.FileUtils; + +@Mixins( Project.Mixin.class ) +public interface Project extends Nameable +{ + // Commands + void createLayer( String layerName ); + + void applyTemplate( ApplicationTemplate template ); + + void setApplicationVersion( String version ); + + void setRootPackageName( String name ); + + void generate( File directory ); + + // Query Methods + String applicationName(); + + String applicationVersion(); + + String rootPackageName(); + + ApplicationTemplate template(); + + Iterable<Layer> layers(); + + Layer findLayer( String layerName ); + + public class Support + { + public static String identity( String name ) + { + return "Project:" + name; + } + + public static Project get( UnitOfWork uow, String name ) + { + return uow.get( Project.class, identity( name ) ); + } + } + + interface State + { + + Property<String> name(); + + Property<String> version(); + + Property<String> rootPackage(); + + @UseDefaults + @Aggregated + ManyAssociation<Layer> layers(); + + @Aggregated + Association<ApplicationTemplate> template(); + } + + abstract class Mixin + implements Project + { + @This + private Project self; + + @This + private State state; + + @Structure + private UnitOfWorkFactory uowf; + + @Override + public String applicationName() + { + return state.name().get(); + } + + @Override + public String applicationVersion() + { + return state.version().get(); + } + + @Override + @UnitOfWorkPropagation + public void createLayer( String layerName ) + { + UnitOfWork uow = uowf.currentUnitOfWork(); + Nameable.Support.create( uow, Layer.class, self, layerName ); + } + + @Override + public void applyTemplate( ApplicationTemplate template ) + { + state.template().set(template); + } + + @Override + public void setApplicationVersion( String version ) + { + state.version().set( version ); + } + + @Override + public void setRootPackageName( String name ) + { + state.rootPackage().set( name ); + } + + @Override + public void generate( File directory ) + { + + } + + @Override + public String rootPackageName() + { + return state.rootPackage().get(); + } + + @Override + public ApplicationTemplate template() + { + return state.template().get(); + } + + @Override + @UnitOfWorkPropagation + public Layer findLayer( String name ) + { + UnitOfWork uow = uowf.currentUnitOfWork(); + return Nameable.Support.get( uow, Layer.class, self, name ); + } + + @Override + public Iterable<Layer> layers() + { + return state.layers(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/model/ProjectAlreadyExistsException.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/model/ProjectAlreadyExistsException.java b/tools/shell/src/main/java/org/qi4j/tools/shell/model/ProjectAlreadyExistsException.java new file mode 100644 index 0000000..4c798c0 --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/model/ProjectAlreadyExistsException.java @@ -0,0 +1,17 @@ +package org.qi4j.tools.shell.model; + +public class ProjectAlreadyExistsException extends Exception +{ + private final String name; + + public ProjectAlreadyExistsException( String name ) + { + super( "Project " + Project.Support.identity( name ) + " already exists." ); + this.name = name; + } + + public String name() + { + return name; + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/model/ProjectDescriptorByProperties.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/model/ProjectDescriptorByProperties.java b/tools/shell/src/main/java/org/qi4j/tools/shell/model/ProjectDescriptorByProperties.java new file mode 100644 index 0000000..5a69452 --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/model/ProjectDescriptorByProperties.java @@ -0,0 +1,86 @@ +package org.qi4j.tools.shell.model; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.qi4j.api.injection.scope.Service; +import org.qi4j.api.injection.scope.Structure; +import org.qi4j.api.unitofwork.UnitOfWork; +import org.qi4j.api.unitofwork.UnitOfWorkFactory; +import org.qi4j.api.unitofwork.concern.UnitOfWorkPropagation; +import org.qi4j.tools.shell.FileUtils; + +public interface ProjectDescriptorByProperties +{ + void parse( String projectName, File propertiesFile ); + + Project findProject( String projectName ); + + public class Mixin + implements ProjectDescriptorByProperties + { + @Service + private Model model; + + @Structure + private UnitOfWorkFactory uowf; + + public void parse( String projectName, File file ) + { + Map<String, String> props = FileUtils.readPropertiesFile( file ); + Project project = findProject( projectName ); + + int layers = Integer.parseInt( props.get( "layers" ) ); + for( int i = 0; i < layers; i++ ) + { + String layerName = props.get( "layer." + i + ".name" ); + project.createLayer( layerName ); + Layer layer = project.findLayer( layerName ); + + for( String use : extractUses( props, i ) ) + { + layer.usesLayer( use ); + } + + int modules = Integer.parseInt( props.get( "layer." + i + ".modules" ) ); + for( int j = 0; j < modules; j++ ) + { + String moduleName = props.get( "layer." + i + ".module." + j + ".name" ); + layer.createModule( moduleName ); + } + } + } + + private List<String> extractUses( Map<String, String> props, int i ) + { + String uses = props.get( "layer." + i + ".uses" ); + if( uses == null ) + { + return Collections.emptyList(); + } + return Arrays.asList( uses.split( "," ) ); + } + + @UnitOfWorkPropagation + public Project findProject( String projectName ) + { + UnitOfWork uow = uowf.currentUnitOfWork(); + Project project = Project.Support.get( uow, projectName ); + if( project == null ) + { + try + { + model.createProject( projectName ); + } + catch( ProjectAlreadyExistsException e ) + { + // Can not happen + e.printStackTrace(); + } + } + return project; + } + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/model/TemplateAlreadyExistsException.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/model/TemplateAlreadyExistsException.java b/tools/shell/src/main/java/org/qi4j/tools/shell/model/TemplateAlreadyExistsException.java new file mode 100644 index 0000000..0d1edd7 --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/model/TemplateAlreadyExistsException.java @@ -0,0 +1,16 @@ +package org.qi4j.tools.shell.model; + +public class TemplateAlreadyExistsException extends Exception +{ + private final String name; + + public TemplateAlreadyExistsException( String name ) + { + this.name = name; + } + + public String name() + { + return name; + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/model/generation/AssemblerGenerator.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/model/generation/AssemblerGenerator.java b/tools/shell/src/main/java/org/qi4j/tools/shell/model/generation/AssemblerGenerator.java new file mode 100644 index 0000000..2d6c187 --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/model/generation/AssemblerGenerator.java @@ -0,0 +1,92 @@ +package org.qi4j.tools.shell.model.generation; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import org.qi4j.api.injection.scope.This; +import org.qi4j.tools.shell.FileUtils; +import org.qi4j.tools.shell.StringUtils; +import org.qi4j.tools.shell.model.AssemblerModel; +import org.qi4j.tools.shell.model.Layer; +import org.qi4j.tools.shell.model.Project; + +public abstract class AssemblerGenerator + implements AssemblerModel +{ + @This + private AssemblerModel self; + + @Override + public void generate( Project project, File projectDir ) + throws IOException + { + File bootDir = self.mainJavaRootPackageDirectory( projectDir ); + File topAssembler = new File( bootDir, project.applicationName() + "Assembler.java" ); + + Map<String, String> variables = new HashMap<>(); + variables.put( "APPLICATION_NAME", project.applicationName() ); + variables.put( "IMPORTS", createImports() ); + variables.put( "LAYER_CREATION", createLayerCreation( project ) ); + variables.put( "LAYER_CREATE_METHODS", createLayerMethods( project ) ); + variables.put( "ROOT_PACKAGE", project.rootPackageName() ); + variables.put( "MAIN_JAVA_ROOT_PACKAGE_PATH", bootDir.getAbsolutePath() ); + variables.put( "MAIN_RESOURCES_ROOT_PACKAGE_PATH", + self.mainResourcesRootPackageDirectory( projectDir ).getAbsolutePath() ); + variables.put( "TEST_JAVA_ROOT_PACKAGE_PATH", + self.testJavaRootPackageDirectory( projectDir ).getAbsolutePath() ); + variables.put( "TEST_RESOURCES_ROOT_PACKAGE_PATH", + self.testResourcesRootPackageDirectory( projectDir ).getAbsolutePath() ); + String application = project.template().evaluate( variables ); + FileUtils.writeFile( topAssembler, application ); + } + + private String createLayerCreation( Project project ) + { + StringBuilder builder = new StringBuilder(); + for( Layer layer : project.layers() ) + { + builder.append( " " ); + builder.append( "LayerAssembly " ); + builder.append( StringUtils.camelCase( layer.name(), false ) ); + builder.append( "Assembler = new " ); + builder.append( StringUtils.camelCase( layer.name(), true ) ); + builder.append( "().assemble( assembly );" ); + builder.append( "\n" ); + } + builder.append( "\n" ); + + for( Layer layer : project.layers() ) + { + for( String use : layer.uses() ) + { + builder.append( " " ); + builder.append( StringUtils.camelCase( layer.name(), false ) ); + builder.append( ".uses( " ); + builder.append( StringUtils.camelCase( use, false ) ); + builder.append( ");\n" ); + } + } + return builder.toString(); + } + + private String createLayerMethods( Project project ) + { + StringBuilder builder = new StringBuilder(); + for( Layer layer : project.layers() ) + { + builder.append( "\n private LayerAssembly create" ); + builder.append( StringUtils.camelCase( layer.name(), true ) ); + builder.append( "( ApplicationAssembly assembly )\n {\n" ); + builder.append( " LayerAssembly layer = assembly.layer( \"" + layer.name() + "\" );\n" ); + builder.append( " return layer;\n" ); + builder.append( " }\n" ); + } + return builder.toString(); + } + + private String createImports() + { + return ""; + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/main/java/org/qi4j/tools/shell/templating/TemplateEngine.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/main/java/org/qi4j/tools/shell/templating/TemplateEngine.java b/tools/shell/src/main/java/org/qi4j/tools/shell/templating/TemplateEngine.java new file mode 100644 index 0000000..c3fbf8d --- /dev/null +++ b/tools/shell/src/main/java/org/qi4j/tools/shell/templating/TemplateEngine.java @@ -0,0 +1,40 @@ +package org.qi4j.tools.shell.templating; + +import java.util.Map; + +public class TemplateEngine +{ + private final String template; + + public TemplateEngine( String template ) + { + this.template = template; + } + + public String create(Map<String, String> variables) + { + StringBuilder builder = new StringBuilder( template.length() * 2 ); + for( int i = 0; i < template.length() - 1; i++ ) + { + char ch1 = template.charAt( i ); + char ch2 = template.charAt( i + 1 ); + if( ch1 == '@' && ch2 == '@' ) + { + i = replace( i + 2, builder, variables ); + } + else + { + builder.append( ch1 ); + } + } + return builder.toString(); + } + + private int replace( int current, StringBuilder builder, Map<String, String> variables ) + { + int pos = template.indexOf( '@', current ); + String name = template.substring( current, pos ); + builder.append( variables.get( name ) ); + return pos + 1; + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/28e0817d/tools/shell/src/test/java/org/qi4j/tools/shell/templating/TemplatingEngineTest.java ---------------------------------------------------------------------- diff --git a/tools/shell/src/test/java/org/qi4j/tools/shell/templating/TemplatingEngineTest.java b/tools/shell/src/test/java/org/qi4j/tools/shell/templating/TemplatingEngineTest.java new file mode 100644 index 0000000..fcfb675 --- /dev/null +++ b/tools/shell/src/test/java/org/qi4j/tools/shell/templating/TemplatingEngineTest.java @@ -0,0 +1,40 @@ +package org.qi4j.tools.shell.templating; + +import java.util.HashMap; +import java.util.Map; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + +public class TemplatingEngineTest +{ + @Test + public void givenCorrectTemplateWhenGeneratingExpectGoodResult() + throws Exception + { + Map<String, String> variables = new HashMap<>(); + variables.put( "REPLACE", "def" ); + TemplateEngine engine = new TemplateEngine( TEMPLATE1 ); + String result = engine.create( variables ); + assertThat( result, equalTo( "abcdefghi" ) ); + } + + @Test + public void givenCorrectTemplateWhenGeneratingMultipleOutputsExpectGoodResult() + throws Exception + { + TemplateEngine engine = new TemplateEngine( TEMPLATE1 ); + + Map<String, String> variables = new HashMap<>(); + variables.put( "REPLACE", "def" ); + String result = engine.create( variables ); + assertThat( result, equalTo( "abcdefghi" ) ); + + variables.put( "REPLACE", "rst" ); + result = engine.create( variables ); + assertThat( result, equalTo( "abcrstghi" ) ); + } + + private static final String TEMPLATE1 = "abc@@REPLACE@@ghi"; +}
