blob: c04ed166a2edc4572e02e5babd4c74152d5a856c [file] [log] [blame]
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.
* <p>
* This implementation is designed to perform the following steps:
* <ul>
* <li>Parse the JSON object representation produced by the <tt>parse_ec2.pl</tt> perl script</li>
* <li>Convert the JSON into Java object models (@see org.jclouds.aws.codegen.models)</li>
* <li>Iterate through the Command objects generated above and with each one:
* <ul>
* <li>Load the <a href="http://freemarker.org/">FreeMarker</a>template file for the given object</li>
* <li>Create a package directory to receive the Java file, if necessary</li>
* <li>Process the object through the template, producing a Java implementation of the object</li>
* <ul></li>
*
* @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 <tt>aws/src/main/bin/parse_ec2.pl</tt>) 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<EC2RequestOptions>") == -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<String, Object> objectMap = new HashMap<String, Object>();
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<String, Object> 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);
}
}