/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* 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
*******************************************************************************/
package org.ebayopensource.turmeric.tools.codegen.builders;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.ebayopensource.turmeric.common.v1.types.ErrorCategory;
import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceException;
import org.ebayopensource.turmeric.runtime.common.impl.utils.LogManager;
import org.ebayopensource.turmeric.runtime.common.service.CommonServiceOperations;
import org.ebayopensource.turmeric.tools.codegen.CodeGenContext;
import org.ebayopensource.turmeric.tools.codegen.exception.CodeGenFailedException;
import org.ebayopensource.turmeric.tools.codegen.util.CodeGenConstants;
import org.ebayopensource.turmeric.tools.codegen.util.CodeGenUtil;
import org.ebayopensource.turmeric.tools.codegen.util.ContextClassLoaderUtil;
import org.ebayopensource.turmeric.tools.codegen.util.IntrospectUtil;
import org.ebayopensource.turmeric.tools.codegen.util.JavacHelper;
import com.sun.codemodel.ClassType;
import com.sun.codemodel.CodeWriter;
import com.sun.codemodel.JArray;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JClassAlreadyExistsException;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JDocComment;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JFieldRef;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JPackage;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;
import com.sun.codemodel.writer.FileCodeWriter;
public abstract class BaseCodeGenerator {
private static Logger s_logger = LogManager.getInstance(BaseCodeGenerator.class);
static final String PARAM_NAME_PREFIX = "param";
static final String DOT = ".";
static final String GEN_PKG_NAME = "gen";
static final Map<String, String[]> COMMON_SVC_OP_METHOD_PARAM_NAME_MAP =
new HashMap<String, String[]>();
static {
COMMON_SVC_OP_METHOD_PARAM_NAME_MAP.put("isServiceVersionSupported", new String[] { "version"});
}
protected JMethod[] addMethods(
JDefinedClass jDefinedClass,
List<Method> methodList) {
Method[] methods = methodList.toArray(new Method[0]);
return addMethods(jDefinedClass, methods);
}
protected JMethod[] addMethods(
JDefinedClass jDefinedClass,
Method[] methods) {
JMethod[] jMethods = new JMethod[methods.length];
int index = 0;
for (Method method : methods) {
jMethods[index] = addMethod(jDefinedClass, method);
index++;
}
return jMethods;
}
protected JMethod addMethod(
JDefinedClass jDefinedClass,
Method method) {
return addMethod(jDefinedClass, method, method.getName());
}
protected JMethod addMethod(
JDefinedClass jDefinedClass,
Method method,
String[] paramNames) {
Type returnType = method.getGenericReturnType();
JMethod jCodeGenMethod =
addMethod(jDefinedClass, method.getName(), returnType);
int index = 0;
Type[] paramTypes = method.getGenericParameterTypes();
for (Type paramType : paramTypes) {
addParameter(
jDefinedClass,
jCodeGenMethod,
paramType,
paramNames[index]);
index++;
}
Type[] exceptionTypes = method.getGenericExceptionTypes();
addThrowsClause(jCodeGenMethod, exceptionTypes);
return jCodeGenMethod;
}
protected JMethod addMethod(
JDefinedClass jDefinedClass,
Method method,
String methodName) {
JCodeModel jCodeModel = jDefinedClass.owner();
JMethod jCodeGenMethod = null;
Type returnType = method.getGenericReturnType();
jCodeGenMethod =
jDefinedClass.method(
JMod.PUBLIC, getJType(returnType, jCodeModel), methodName);
Type[] paramTypes = method.getGenericParameterTypes();
Type[] exceptionTypes = method.getGenericExceptionTypes();
jCodeGenMethod =
addMethod(jCodeGenMethod, paramTypes, exceptionTypes, jCodeModel);
return jCodeGenMethod;
}
protected JMethod addMethod(
JDefinedClass jDefinedClass,
String methodName,
Type[] paramTypes,
Type[] exceptionTypes,
Type returnType) {
JMethod jCodeGenMethod = null;
JCodeModel jCodeModel = jDefinedClass.owner();
JType returnJType = getJType(returnType, jCodeModel);
jCodeGenMethod =
jDefinedClass.method(JMod.PUBLIC, returnJType, methodName);
addMethod(jCodeGenMethod, paramTypes, exceptionTypes, jCodeModel);
return jCodeGenMethod;
}
protected JMethod addMethod(
JMethod jMethod,
Type[] paramTypes,
Type[] exceptionTypes,
JCodeModel jCodeModel) {
int paramIndex = 0;
for (Type paramType : paramTypes) {
String paramName = PARAM_NAME_PREFIX+String.valueOf(paramIndex);
jMethod.param(getJType(paramType, jCodeModel), paramName);
paramIndex++;
}
for (Type exceptionType : exceptionTypes) {
jMethod._throws(getTypeClass(exceptionType));
}
return jMethod;
}
protected JMethod addMethod(
JDefinedClass jDefinedClass,
String methodName,
Type returnType) {
JCodeModel jCodeModel = jDefinedClass.owner();
JType returnJType = getJType(returnType, jCodeModel);
return addMethod(jDefinedClass, methodName,returnJType);
}
protected JMethod addMethod(
JDefinedClass jDefinedClass,
String methodName,
JType returnJType) {
JMethod jCodeGenMethod =
addMethod(jDefinedClass,
methodName,
JMod.PUBLIC,
returnJType);
return jCodeGenMethod;
}
protected JMethod addMethod(
JDefinedClass jDefinedClass,
String methodName,
int methodModifiers,
JType returnJType) {
JMethod jCodeGenMethod =
jDefinedClass.method(methodModifiers, returnJType, methodName);
return jCodeGenMethod;
}
protected JVar addParameter(
JDefinedClass jDefinedClass,
JMethod jMethod,
Type paramType,
String paramName) {
JCodeModel jCodeModel = jDefinedClass.owner();
JType paramJType = getJType(paramType, jCodeModel);
JVar paramVar = jMethod.param(paramJType, paramName);
return paramVar;
}
protected JVar addParameter(
JDefinedClass jDefinedClass,
JMethod jMethod,
JType paramJType,
String paramName) {
JVar paramVar = jMethod.param(paramJType, paramName);
return paramVar;
}
protected void addThrowsClause(
JMethod jMethod,
Type[] exceptionTypes) {
for (Type exceptionType : exceptionTypes) {
jMethod._throws(getTypeClass(exceptionType));
}
}
protected void addThrowsClause(
JMethod jMethod,
JClass exceptionJType) {
jMethod._throws(exceptionJType);
}
protected JType getJType(Type type, JCodeModel jCodeModel) {
if (CodeGenUtil.isParameterizedType(type)) {
return createJType((ParameterizedType) type, jCodeModel);
}else if (CodeGenUtil.isGenericArrayType(type)) {
return getJType(((GenericArrayType) type).getGenericComponentType(), jCodeModel).array();
}
else {
Class<?> clazz = (Class<?>) type;
if (clazz.isPrimitive()) {
return JType.parse(jCodeModel, clazz.getName());
} else {
return jCodeModel.ref(clazz);
}
}
}
protected Class<?> getTypeClass(Type type) {
if (CodeGenUtil.isParameterizedType(type)) {
return (Class<?>) ((ParameterizedType) type).getRawType();
}
else {
return (Class<?>) type;
}
}
/**
* public Map<String, List<MyOwnType>> getResultMap(List<MyOwnType> myTypeList);
* cm.method(JMod.PUBLIC, cm.ref(Map.class).narrow(String.class).
* narrow( cm.ref(List.class).narrow(myOwnType)), "getResultMap").param(...);
*
* @param parameterizedType
* @param codeModel
* @return
*/
private JClass createJType(ParameterizedType parameterizedType, JCodeModel codeModel) {
Class<?> rawClass = getTypeClass(parameterizedType);
JClass narrowedJClass = codeModel.ref(rawClass);
Type[] actualArgTypes = parameterizedType.getActualTypeArguments();
for (Type actualType : actualArgTypes){
if (CodeGenUtil.isParameterizedType(actualType)) {
ParameterizedType paramType = (ParameterizedType) actualType;
narrowedJClass =
narrowedJClass.narrow(
createJType(paramType, codeModel));
} else if (CodeGenUtil.isWildCardType(actualType)) {
narrowedJClass = narrowedJClass.narrow(codeModel.wildcard());
} else if(CodeGenUtil.isGenericArrayType(actualType)){
Class<?> clazz = (Class<?>) ((GenericArrayType) actualType).getGenericComponentType();
narrowedJClass = narrowedJClass.narrow(getJClass(clazz, codeModel).array());
}
else {
narrowedJClass = narrowedJClass.narrow(getTypeClass(actualType));
}
}
return narrowedJClass;
}
protected JDefinedClass createNewClass(
JCodeModel jCodeModel,
String fullyQualifiedClassName) throws CodeGenFailedException {
return createNewJavaType(jCodeModel, fullyQualifiedClassName, ClassType.CLASS);
}
protected JDefinedClass createNewAbstractClass(JCodeModel jCodeModel,
String fullyQualifiedClassName) throws CodeGenFailedException {
try {
int lastDotPos = fullyQualifiedClassName.lastIndexOf(".");
if (lastDotPos < 0) {
return jCodeModel.rootPackage()._class(
JMod.PUBLIC | JMod.ABSTRACT, fullyQualifiedClassName);
} else {
String packageName = fullyQualifiedClassName.substring(0,
lastDotPos);
String className = fullyQualifiedClassName
.substring(lastDotPos + 1);
JPackage jPackage = jCodeModel._package(packageName);
return jPackage._class(JMod.PUBLIC | JMod.ABSTRACT, className);
}
} catch (JClassAlreadyExistsException ex) {
throw new CodeGenFailedException("Class already exists : "
+ fullyQualifiedClassName, ex);
}
}
protected JClass createDirectClass(
JCodeModel jCodeModel,
String fullyQualifiedClassName) throws CodeGenFailedException {
return jCodeModel.directClass(fullyQualifiedClassName);
}
protected JDefinedClass createNewInterface(
JCodeModel jCodeModel,
String fullyQualifiedClassName) throws CodeGenFailedException {
return createNewJavaType(
jCodeModel, fullyQualifiedClassName, ClassType.INTERFACE);
}
private JDefinedClass createNewJavaType(
JCodeModel jCodeModel,
String fullyQualifiedTypeName,
ClassType javaType) throws CodeGenFailedException {
JDefinedClass newJavaType = null;
try {
newJavaType = jCodeModel._class(fullyQualifiedTypeName, javaType);
} catch (JClassAlreadyExistsException ex) {
throw new CodeGenFailedException(
"Class already exists : " + fullyQualifiedTypeName, ex);
}
return newJavaType;
}
protected final void generateJavaFile(JCodeModel jCodeModel, String destLoc) throws CodeGenFailedException {
try {
File destDir = new File(destLoc);
CodeWriter codeWriter = new FileCodeWriter(destDir);
jCodeModel.build(codeWriter);
} catch (IOException ex) {
throw new CodeGenFailedException("Failed to create file at : " + destLoc, ex);
}
}
protected final void generateJavaFile(CodeGenContext codeGenCtx,
JDefinedClass targetClass, String locationSuffix)
throws CodeGenFailedException
{
// Attempt to use as set Java Src Dest Location
// If user specifies a Java Src Dest Location, use it.
// Do not tack on arbitrary extra paths, as this will
// break the classloader lookup at loadClass and introspection
// in later codegen tasks, especially when running in Eclipse
// and Maven Plugin.
String javaSrcDestLoc = codeGenCtx.getJavaSrcDestLocation(false);
// Use [LEGACY] behavior if javaSrcDestLoc is unset.
if(javaSrcDestLoc == null) {
// [LEGACY] use the locationSuffix to compile in a unique directory.
javaSrcDestLoc = codeGenCtx.getJavaSrcDestLocation(true);
javaSrcDestLoc = CodeGenUtil.toOSFilePath(javaSrcDestLoc) + locationSuffix;
}
try {
CodeGenUtil.createDir(javaSrcDestLoc);
} catch (IOException ioEx) {
throw new CodeGenFailedException(ioEx.getMessage(), ioEx);
}
JCodeModel jCodeModel = targetClass.owner();
generateJavaFile(jCodeModel, javaSrcDestLoc);
String javaFilePath = CodeGenUtil.toJavaSrcFilePath(javaSrcDestLoc, targetClass.fullName());
codeGenCtx.addGeneratedJavaSrcFile(javaFilePath);
}
protected final void generateJavaFile(CodeGenContext codeGenCtx,
JDefinedClass targetClass, String baseDestLocation , String locationSuffix)
throws CodeGenFailedException
{
String destLocation = generateDestLocation(
baseDestLocation,
locationSuffix);
JCodeModel jCodeModel = targetClass.owner();
generateJavaFile(jCodeModel, destLocation);
String javaFilePath = CodeGenUtil.toJavaSrcFilePath(destLocation, targetClass.fullName());
codeGenCtx.addGeneratedJavaSrcFile(javaFilePath);
}
protected String generateQualifiedClassName(
String classNameForPkg,
String pkgPrefix,
String genClassName) {
String genPkgName = null;
int lastDotPos = classNameForPkg.lastIndexOf(DOT);
if (lastDotPos > -1) {
genPkgName = classNameForPkg.substring(0, lastDotPos) + DOT + pkgPrefix;
} else {
genPkgName = pkgPrefix;
}
return genPkgName + DOT + genClassName;
}
protected String generateQualifiedClassName(String packageName, String className) {
if (CodeGenUtil.isEmptyString(packageName)) {
return className;
}
else {
return (packageName + DOT + className);
}
}
protected void addJavaDocs(JDefinedClass jDefinedClass) {
JDocComment javaDocs = jDefinedClass.javadoc();
javaDocs.add("Note : Generated file, any changes will be lost upon regeneration.");
}
protected void implement(JDefinedClass jDefinedClass, Class<?> clazzToImplement) {
jDefinedClass._implements(clazzToImplement);
}
protected void extend(JDefinedClass jDefinedClass, Class<?> clazzToExtend) {
jDefinedClass._extends(clazzToExtend);
}
protected void extend(JDefinedClass jDefinedClass, JClass clazzToExtend) {
jDefinedClass._extends(clazzToExtend);
}
/*
protected Class<?> loadClass(String qualifiedClassName) throws CodeGenFailedException {
return ContextClassLoaderUtil.loadOptionalClass(qualifiedClassName);
}
*/
protected JClass getJClass(String className, JCodeModel jCodeModel) throws CodeGenFailedException {
Class<?> clazz = ContextClassLoaderUtil.loadRequiredClass(className);
return getJClass(clazz, jCodeModel);
}
protected JClass getJClass(Class<?> clazz, JCodeModel jCodeModel) {
if (clazz.isPrimitive()) {
return jCodeModel.ref(JCodeModel.primitiveToBox.get(clazz));
} else {
return jCodeModel.ref(clazz);
}
}
protected JInvocation getServiceException(JCodeModel jCodeModel) {
JInvocation serviceException =
JExpr._new(getJClass(ServiceException.class, jCodeModel));
return serviceException;
}
protected JClass getErrorCategoryClass(JCodeModel jCodeModel) {
return getJClass(ErrorCategory.class, jCodeModel);
}
protected JFieldRef getErrorCategoryType(
JCodeModel jCodeModel,
String errorCategoryName) {
JClass errCategoryClazz = getErrorCategoryClass(jCodeModel);
return errCategoryClazz.staticRef(errorCategoryName);
}
protected JInvocation createServiceException(
JCodeModel jCodeModel,
JFieldRef errorDesc,
JArray errorParams) {
JInvocation serviceException = getServiceException(jCodeModel);
serviceException.arg(errorDesc);
serviceException.arg(errorParams);
return serviceException;
}
protected JInvocation createServiceException(
JCodeModel jCodeModel,
JFieldRef errorDesc,
JVar errorVar) {
JInvocation serviceException = getServiceException(jCodeModel);
serviceException.arg(errorDesc);
serviceException.arg(errorVar);
return serviceException;
}
protected String generateDestLocation(
String srcDestLocation,
String suffix) throws CodeGenFailedException {
String destLocation =
CodeGenUtil.genDestFolderPath(srcDestLocation, suffix);
try {
CodeGenUtil.createDir(destLocation);
} catch (IOException ioEx) {
throw new CodeGenFailedException(ioEx.getMessage(), ioEx);
}
return destLocation;
}
protected JExpression getValueForType(Class<?> paramType) {
if (!paramType.isPrimitive()) {
return JExpr._null();
}
else if (paramType == Boolean.TYPE) {
return JExpr.FALSE;
}
else if ((paramType == Byte.TYPE) ||
(paramType == Short.TYPE) ||
(paramType == Integer.TYPE)) {
return JExpr.lit(0);
}
else if (paramType == Long.TYPE) {
return JExpr.lit(0L);
}
else if (paramType == Float.TYPE) {
return JExpr.lit(0.0F);
}
else if (paramType == Double.TYPE) {
return JExpr.lit(0.0D);
}
else { // Char
return JExpr.lit(' ');
}
}
protected JExpression getValueForType(JType paramType) {
if (!paramType.isPrimitive()) {
return JExpr._null();
}
else if (paramType == paramType.owner().BOOLEAN) {
return JExpr.FALSE;
}
else if ((paramType == paramType.owner().BYTE) ||
(paramType == paramType.owner().SHORT) ||
(paramType == paramType.owner().INT)) {
return JExpr.lit(0);
}
else if (paramType == paramType.owner().LONG) {
return JExpr.lit(0L);
}
else if (paramType == paramType.owner().FLOAT) {
return JExpr.lit(0.0F);
}
else if (paramType == paramType.owner().DOUBLE) {
return JExpr.lit(0.0D);
}
else { // Char
return JExpr.lit(' ');
}
}
protected Class<?> getCommonServiceInterface() {
return CommonServiceOperations.class;
}
public static void compileJavaFiles(
List<String> javaSrcFiles,
String outputDir) throws Exception {
JavacHelper javacHelper = new JavacHelper(System.out); //KEEPME
javacHelper.compileJavaSource(javaSrcFiles, outputDir);
}
public static void compileJavaFilesNoException(
List<String> javaSrcFiles,
String outputDir) {
try {
compileJavaFiles(javaSrcFiles, outputDir);
} catch (Exception ex) {
s_logger.log(Level.SEVERE,
"Unable to compile " + javaSrcFiles.toString() + ": " + ex.toString(), ex);
}
}
public static void compileJavaFile(
String javaSrcFilePath,
String outputDir) throws Exception {
s_logger.info("Compiling Java: " + javaSrcFilePath);
s_logger.info(" outputDir: " + outputDir);
List<String> javaSrcFiles = new ArrayList<String>(1);
javaSrcFiles.add(javaSrcFilePath);
JavacHelper javacHelper = new JavacHelper(System.out); //KEEPME
javacHelper.compileJavaSource(javaSrcFiles, outputDir);
}
public static void compileJavaFile(
String qualifiedJavaName,
String srcDir,
String outputDir)throws Exception {
s_logger.finer("Compiling Java: " + qualifiedJavaName);
s_logger.finer(" srcDir: " + srcDir);
s_logger.finer(" outputDir: " + outputDir);
JavacHelper javacHelper = new JavacHelper(System.out); //KEEPME
String javaSrcFilePath = CodeGenUtil.toJavaSrcFilePath(srcDir, qualifiedJavaName);
javacHelper.compileJavaSource(javaSrcFilePath, srcDir, outputDir);
}
protected String getAsyncInterfaceClassName(String interfaceClass) {
String asyncPackageName = CodeGenUtil.getPackageName(interfaceClass);
String asyncInterfaceClassName = CodeGenConstants.ASYNC_NAME + CodeGenUtil.getJavaClassName(interfaceClass);
return generateQualifiedClassName(asyncPackageName, asyncInterfaceClassName);
}
}