+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+package org.jclouds.codegen.util;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jclouds.codegen.model.API;
+import org.jclouds.codegen.model.Command;
+import org.jclouds.codegen.model.DomainType;
+import org.jclouds.codegen.model.Package;
+import org.jclouds.codegen.model.Type;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonParseException;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
+/**
+ * Converts object models representing AWS API beans into Java classes.
+ *
+ * This implementation is designed to perform the following steps:
+ *
+ * - Parse the JSON object representation produced by the parse_ec2.pl perl script
+ * - Convert the JSON into Java object models (@see org.jclouds.aws.codegen.models)
+ * - Iterate through the Command objects generated above and with each one:
+ *
+ * - Load the FreeMarkertemplate file for the given object
+ * - Create a package directory to receive the Java file, if necessary
+ * - Process the object through the template, producing a Java implementation of the object
+ *
+ *
+ * @author James Murty
+ */
+public class CodeGenerator {
+ public final static String COMMAND_TEMPLATE_FILENAME = "Command.ftl";
+ public final static String BEAN_TEMPLATE_FILENAME = "Bean.ftl";
+ public final static String OPTIONS_TEMPLATE_FILENAME = "Options.ftl";
+ public final static String VALUE_TEMPLATE_FILENAME = "Value.ftl";
+ public final static String RESPONSE_TEMPLATE_FILENAME = "Response.ftl";
+
+ private final File targetDirectory;
+ private final String rootPackageName;
+ private final Configuration config;
+
+ public CodeGenerator(String rootPackageName, File targetDirectory) throws IOException {
+ this.rootPackageName = rootPackageName;
+ this.targetDirectory = targetDirectory;
+
+ config = new Configuration();
+ config.setClassForTemplateLoading(this.getClass(), "/templates");
+ }
+
+ /**
+ * Parse a JSON object model file (as generated by aws/src/main/bin/parse_ec2.pl) and
+ * convert it into a Java object graph.
+ *
+ * @param objectModelFile
+ * @return
+ * @throws JsonParseException
+ * @throws FileNotFoundException
+ */
+ public API parseModelFromJSON(File objectModelFile) throws JsonParseException,
+ FileNotFoundException {
+ Gson gson = new Gson();
+ return gson.fromJson(new FileReader(objectModelFile), API.class);
+ }
+
+ /**
+ * Parse a JSON object model file and iterate over the resulting objects generating Java code
+ * files.
+ *
+ * @param objectModelFile
+ * @throws JsonParseException
+ * @throws IOException
+ * @throws TemplateException
+ */
+ public void generateCode(File objectModelFile) throws JsonParseException, IOException,
+ TemplateException {
+ API model = parseModelFromJSON(objectModelFile);
+
+ for (Package pkg : model.getPackages()) {
+ for (Command command : pkg.getCommands()) {
+ generateClassFile(command, COMMAND_TEMPLATE_FILENAME);
+
+ if (command.getHandler() != null) {
+ generateClassFile(command.getHandler(), BEAN_TEMPLATE_FILENAME);
+ }
+ if (command.getOptions() != null
+ && command.getOptions().getJavaName().indexOf(
+ "BaseEC2RequestOptions") == -1) {
+ generateClassFile(command.getOptions(), OPTIONS_TEMPLATE_FILENAME);
+ }
+ if (command.getResponse() != null
+ && !command.getResponse().getJavaName().equals("Boolean")) {
+ generateClassFile(command.getResponse(), RESPONSE_TEMPLATE_FILENAME);
+ }
+ }
+ }
+ for (DomainType value : model.getDomain().values()) {
+ generateClassFile(value, VALUE_TEMPLATE_FILENAME);
+ }
+
+ }
+
+ /**
+ * Generate a Java code file for the given object model bean, using the given template name as
+ * the basis for the Java class.
+ *
+ * @param bean
+ * @param templateFileName
+ * @throws IOException
+ * @throws TemplateException
+ */
+ public void generateClassFile(Type bean, String templateFileName) throws IOException,
+ TemplateException {
+ String shortClassName = bean.getJavaName().substring(
+ bean.getJavaName().lastIndexOf('.') + 1);
+
+ Map objectMap = new HashMap();
+ objectMap.put("bean", bean);
+ objectMap.put("rootPackageName", this.rootPackageName);
+ objectMap.put("shortClassName", shortClassName);
+
+ File packageDir = new File(this.targetDirectory, bean.getPackageName().replace('.', '/'));
+
+ if (packageDir.exists() || packageDir.mkdirs()) {
+ File javaFile = new File(packageDir, shortClassName + ".java");
+ FileWriter fw = new FileWriter(javaFile);
+ System.out.println("Generated " + javaFile);
+ applyTemplate(templateFileName, objectMap, fw);
+ } else {
+ throw new IllegalStateException("Unable to create target package directory: " + packageDir);
+ }
+ }
+
+ /**
+ * Apply the given template to the given object, and output the results into the given writer.
+ *
+ * @param templateFileName
+ * @param objectMap
+ * @param writer
+ * @throws IOException
+ * @throws TemplateException
+ */
+ public void applyTemplate(String templateFileName, Map objectMap, Writer writer)
+ throws IOException, TemplateException {
+ Template template = this.config.getTemplate(templateFileName, "UTF-8");
+ template.process(objectMap, writer);
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 3) {
+ System.out.println("Usage: CodeGenerator objectModelFile rootPackage targetDir");
+ System.out.println(" E.g: CodeGenerator ec2.json org.jclouds.aws.ec2 aws/ec2/api/src");
+ System.exit(1);
+ }
+ File objectModelFile = new File(args[0]);
+
+ String rootPackage = args[1];
+
+ File targetDir = new File(args[2]);
+
+ new CodeGenerator(rootPackage, targetDir).generateCode(objectModelFile);
+ }
+
+}
diff --git a/codegen/core/src/main/resources/templates/Bean.ftl b/codegen/core/src/main/resources/templates/Bean.ftl
new file mode 100644
index 0000000000..8c37e08689
--- /dev/null
+++ b/codegen/core/src/main/resources/templates/Bean.ftl
@@ -0,0 +1,81 @@
+[#ftl]
+[#--
+
+
+ Copyright (C) 2009 Global Cloud Specialists, Inc.
+
+ ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ ====================================================================
+
+--]
+package ${bean.packageName};
+
+[#if bean.packageName != rootPackageName]
+import ${rootPackageName}.*;
+[/#if]
+import java.util.Set;
+import java.util.List;
+
+
+/**
+ *
+[#list bean.see as see]
+[#if see?contains(".html")]
+ * @see
+[#else]
+ * @see ${see}
+[/#if]
+[/#list]
+ * @author Generated
+ */
+public class ${shortClassName} {
+
+[#-- Print fields --]
+[#list bean.parameters![] as field]
+ /**
+ *
+ * ${field.desc}
+ */
+ private ${field.javaType} ${field.javaName?uncap_first};
+
+[/#list]
+
+[#-- Print get/set --]
+[#list bean.parameters![] as field]
+[#assign lowerName = field.javaName?uncap_first]
+[#assign upperName = field.javaName?cap_first]
+ /**
+ *
+ * @return ${field.desc}
+ */
+ public ${field.javaType} get${upperName}(){
+ return this.${lowerName};
+ }
+
+ /**
+ *
+ * @param ${lowerName}
+ * ${field.desc}
+ */
+ public void set${upperName}(${field.javaType} ${lowerName}) {
+ this.${lowerName} = ${lowerName};
+ }
+
+[/#list]
+}
diff --git a/codegen/core/src/main/resources/templates/Command.ftl b/codegen/core/src/main/resources/templates/Command.ftl
new file mode 100644
index 0000000000..a7f6736865
--- /dev/null
+++ b/codegen/core/src/main/resources/templates/Command.ftl
@@ -0,0 +1,80 @@
+[#ftl]
+[#--
+
+
+ Copyright (C) 2009 Global Cloud Specialists, Inc.
+
+ ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ ====================================================================
+
+--]
+package ${bean.packageName};
+
+import ${rootPackageName}.domain.*;
+[#if bean.response??]
+import ${bean.response.packageName}.*;
+[/#if]
+[#if bean.options??]
+import ${bean.options.packageName}.*;
+[/#if]
+import org.jclouds.aws.reference.AWSConstants;
+import java.util.Set;
+import java.util.List;
+
+import org.jclouds.http.commands.callables.xml.ParseSax;
+
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.name.Named;
+
+import org.jclouds.http.HttpFutureCommand;
+
+/**
+ *
+[#list bean.see as see]
+[#if see?contains(".html")]
+ * @see
+[#else]
+ * @see ${see}
+[/#if]
+[/#list]
+ * @author Generated
+ */
+[#assign RT = bean.response.javaType]
+public class ${shortClassName} extends HttpFutureCommand<${RT}> {
+ @Inject
+ public ${shortClassName}(@Named(AWSConstants.PROPERTY_AWS_ACCESSKEYID) String awsAccessKeyId,
+ @Named(AWSConstants.PROPERTY_AWS_SECRETACCESSKEY) String awsSecretAccessKey,
+ ParseSax<${RT}> callable,
+[#if bean.options.javaType?? ]
+ @Assisted ${bean.options.javaType} options
+[#else]
+ @Assisted BaseEC2RequestOptions options
+[/#if]
+[#list bean.parameters![] as param]
+ ,@Assisted ${param.javaType} ${param.javaName?uncap_first}[#rt]
+[/#list]) {
+ super("GET",
+ "/" + options
+[#list bean.parameters![] as param]
+ .with${param.javaName?cap_first}(${param.javaName?uncap_first})
+[/#list]
+ .signWith(awsAccessKeyId,awsSecretAccessKey).buildQueryString(), callable);
+ }
+}
\ No newline at end of file
diff --git a/codegen/core/src/main/resources/templates/Handler.ftl b/codegen/core/src/main/resources/templates/Handler.ftl
new file mode 100644
index 0000000000..a82c994f21
--- /dev/null
+++ b/codegen/core/src/main/resources/templates/Handler.ftl
@@ -0,0 +1,80 @@
+[#ftl]
+[#--
+
+
+ Copyright (C) 2009 Global Cloud Specialists, Inc.
+
+ ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ ====================================================================
+
+--]
+package ${bean.packageName};
+
+[#if bean.packageName != rootPackageName]
+import ${rootPackageName}.*;
+[/#if]
+import java.util.Set;
+import java.util.List;
+
+/**
+ *
+[#list bean.see as see]
+[#if see?contains(".html")]
+ * @see
+[#else]
+ * @see ${see}
+[/#if]
+[/#list]
+ * @author Generated
+ */
+public class ${shortClassName} {
+
+[#-- Print fields --]
+[#list bean.parameters![] as field]
+ /**
+ *
+ * ${field.desc}
+ */
+ private ${field.javaType} ${field.javaName?uncap_first};
+
+[/#list]
+
+[#-- Print get/set --]
+[#list bean.parameters![] as field]
+[#assign lowerName = field.javaName?uncap_first]
+[#assign upperName = field.javaName?cap_first]
+ /**
+ *
+ * @return ${field.desc}
+ */
+ public ${field.javaType} get${upperName}(){
+ return this.${lowerName};
+ }
+
+ /**
+ *
+ * @param ${lowerName}
+ * ${field.desc}
+ */
+ public void set${upperName}(${field.javaType} ${lowerName}) {
+ this.${lowerName} = ${lowerName};
+ }
+
+[/#list]
+}
diff --git a/codegen/core/src/main/resources/templates/Options.ftl b/codegen/core/src/main/resources/templates/Options.ftl
new file mode 100644
index 0000000000..0fb0bcce2c
--- /dev/null
+++ b/codegen/core/src/main/resources/templates/Options.ftl
@@ -0,0 +1,103 @@
+[#ftl]
+[#--
+
+
+ Copyright (C) 2009 Global Cloud Specialists, Inc.
+
+ ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ ====================================================================
+
+--]
+package ${bean.packageName};
+
+[#if bean.packageName != rootPackageName]
+import ${rootPackageName}.*;
+[/#if]
+import java.util.Set;
+import java.util.List;
+
+import org.jclouds.aws.ec2.commands.options.BaseEC2RequestOptions;
+
+/**
+ *
+ *
+ * ${bean.example}
+ *
+[#list bean.see as see]
+[#if see?contains(".html")]
+ * @see ${shortClassName}
+[#else]
+ * @see ${see}
+[/#if]
+[/#list]
+ * @author Generated
+ */
+public class ${shortClassName} extends
+ BaseEC2RequestOptions<${shortClassName}> {
+
+
+ public static final ${shortClassName} NONE = new ${shortClassName}();
+
+ static {
+ realClass = ${shortClassName}.class;
+ }
+
+ @Override
+ public String getAction() {
+ return "${shortClassName}".replaceAll("Options","");
+ }
+
+[#list bean.parameters![] as field]
+[#assign lowerName = field.javaName?uncap_first]
+[#assign upperName = field.javaName?cap_first]
+
+ /**
+ * @see ${shortClassName}#with${upperName}(String)
+ */
+ public String get${upperName}() {
+ return parameters.get("${lowerName}");
+ }
+
+ /**
+ * TODO: document this bad boy
+ *
+ */
+ public ${shortClassName} with${upperName}(String ${lowerName}) {
+ encodeAndReplaceParameter("${lowerName}", ${lowerName});
+ return this;
+ }
+[/#list]
+
+
+ public static class Builder {
+
+[#list bean.parameters![] as field]
+[#assign lowerName = field.javaName?uncap_first]
+[#assign upperName = field.javaName?cap_first]
+ /**
+ * @see ${shortClassName}#withId(String)
+ */
+ public static ${shortClassName} with${upperName}(String ${lowerName}) {
+ ${shortClassName} options = new ${shortClassName}();
+ return options.with${upperName}(${lowerName});
+ }
+
+[/#list]
+ }
+}
\ No newline at end of file
diff --git a/codegen/core/src/main/resources/templates/Response.ftl b/codegen/core/src/main/resources/templates/Response.ftl
new file mode 100644
index 0000000000..9ebbae7890
--- /dev/null
+++ b/codegen/core/src/main/resources/templates/Response.ftl
@@ -0,0 +1,70 @@
+[#ftl]
+[#--
+
+
+ Copyright (C) 2009 Global Cloud Specialists, Inc.
+
+ ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ ====================================================================
+
+--]
+package ${bean.packageName};
+
+[#if bean.packageName != rootPackageName]
+import ${rootPackageName}.domain.*;
+[/#if]
+import org.joda.time.DateTime;
+import java.util.Set;
+import java.util.List;
+
+/**
+ *
+[#list bean.see as see]
+[#if see?contains(".html")]
+ * @see
+[#else]
+ * @see ${see}
+[/#if]
+[/#list]
+ * @author Generated
+ */
+public class ${shortClassName} {
+
+[#-- Print fields --]
+[#list bean.fields![] as field]
+
+ private ${field.javaType} ${field.javaName?uncap_first};
+
+[/#list]
+
+[#-- Print get/set --]
+[#list bean.fields![] as field]
+[#assign lowerName = field.javaName?uncap_first]
+[#assign upperName = field.javaName?cap_first]
+
+ public ${field.javaType} get${upperName}(){
+ return this.${lowerName};
+ }
+
+ public void set${upperName}(${field.javaType} ${lowerName}) {
+ this.${lowerName} = ${lowerName};
+ }
+
+[/#list]
+}
diff --git a/codegen/core/src/main/resources/templates/Value.ftl b/codegen/core/src/main/resources/templates/Value.ftl
new file mode 100644
index 0000000000..7ee1fa3682
--- /dev/null
+++ b/codegen/core/src/main/resources/templates/Value.ftl
@@ -0,0 +1,70 @@
+[#ftl]
+[#--
+
+
+ Copyright (C) 2009 Global Cloud Specialists, Inc.
+
+ ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ ====================================================================
+
+--]
+package ${bean.packageName};
+
+[#if bean.packageName != rootPackageName]
+import ${rootPackageName}.*;
+[/#if]
+import org.joda.time.DateTime;
+import java.util.Set;
+import java.util.List;
+
+/**
+ *
+[#list bean.see as see]
+[#if see?contains(".html")]
+ * @see
+[#else]
+ * @see ${see}
+[/#if]
+[/#list]
+ * @author Generated
+ */
+public class ${shortClassName} {
+
+[#-- Print fields --]
+[#list bean.fields![] as field]
+
+ private ${field.javaType} ${field.javaName?uncap_first};
+
+[/#list]
+
+[#-- Print get/set --]
+[#list bean.fields![] as field]
+[#assign lowerName = field.javaName?uncap_first]
+[#assign upperName = field.javaName?cap_first]
+
+ public ${field.javaType} get${upperName}(){
+ return this.${lowerName};
+ }
+
+ public void set${upperName}(${field.javaType} ${lowerName}) {
+ this.${lowerName} = ${lowerName};
+ }
+
+[/#list]
+}