/*
* Copyright 2010 Zhihua (Dennis) Jiang
*
* 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 com.gwtmobile.persistence.rebind;
import java.io.PrintWriter;
import java.util.List;
import java.util.Set;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
public class GenUtils {
private TreeLogger logger;
private GeneratorContext context;
private SourceWriter sw;
private ClassSourceFileComposerFactory factory;
public GenUtils(TreeLogger logger, GeneratorContext context) {
this.logger = logger;
this.context = context;
}
public String generateClass(String requestedTypeName, String gennedTypeNameSuffix,
ClassGenerator classGenerator) {
// The package the class is going to be in.
String packageName = getPackageName(requestedTypeName);
String gennedTypeName = getClassName(requestedTypeName) + gennedTypeNameSuffix;
PrintWriter pw = context.tryCreate(logger, packageName, gennedTypeName);
// No PrintWriter means we've generated the same code before.
if (pw != null) {
factory = new ClassSourceFileComposerFactory(packageName, gennedTypeName);
classGenerator.classSetup();
sw = factory.createSourceWriter(context, pw);
classGenerator.generateClass();
sw.commit(logger);
}
return packageName + "." + gennedTypeName;
}
public void generateInnerClass(String modifiers, String className, String superClass, String[] interfaces,
ClassGenerator classGenerator) {
sw.println();
print("%s class %s", modifiers, className);
if (superClass != null) {
print(" extends %s", superClass);
}
if (interfaces != null) {
print(" implements ");
for (int i = 0; i < interfaces.length; i++) {
if (i > 0) {
print(",");
}
print(interfaces[i]);
}
}
println(" {");
sw.indent();
classGenerator.classSetup();
classGenerator.generateClass();
sw.outdent();
println("}");
}
public void genrateStaticConstructor(MethodGenerator bodyGenerator) {
sw.println();
sw.println("static {");
sw.indent();
bodyGenerator.generateMethod();
sw.outdent();
sw.println("}");
}
public boolean generateMethod(String modifiers, String returnType,
String methodName, String[][] params, MethodGenerator methodGenerator) {
return generateMethod(modifiers, returnType, methodName, params, false, methodGenerator);
}
public boolean generateNativeMethod(String modifiers, String returnType,
String methodName, String[][] params, MethodGenerator methodGenerator) {
return generateMethod(modifiers, returnType, methodName, params, true, methodGenerator);
}
private boolean generateMethod(String modifiers, String returnType,
String methodName, String[][] params, boolean isNative, MethodGenerator methodGenerator) {
sw.println();
sw.print(modifiers + " ");
if (isNative) {
sw.print("native ");
}
if (returnType != null) {
sw.print(returnType + " ");
}
sw.print(methodName + "(");
if (params != null ) {
for (int i = 0; i < params.length; i++) {
print("%s %s", params[i][0], params[i][1]);
if (i < params.length - 1) {
print(", ");
}
}
}
sw.print(") ");
if (isNative) {
sw.print("/*-");
}
sw.println("{");
sw.indent();
if (methodGenerator != null) {
methodGenerator.generateMethod();
}
sw.outdent();
sw.print("}");
if (isNative) {
sw.print("-*/;");
}
sw.println();
return true;
}
public SourceWriter sw() {
return sw;
}
ClassSourceFileComposerFactory factory() {
return factory;
}
public void print(String format, Object... args) {
sw.print(String.format(format, args));
}
public void println(String format, Object... args) {
sw.println(String.format(format, args));
}
public void addVariable(String modifiers, String type, String name) {
sw.println(String.format("%s %s %s;", modifiers, type, name));
}
public void addVariable(String modifiers, String type, String name, String initValue) {
sw.println(String.format("%s %s %s = %s;", modifiers, type, name, initValue));
}
public String getPackageName(String typeName) {
final JClassType classType = getClassType(typeName);
if (classType != null) {
return classType.getPackage().getName();
}
return null;
}
public String getClassName(String typeName) {
final JClassType classType = getClassType(typeName);
if (classType != null) {
return classType.getName();
}
return null;
}
public Boolean isInterface(String typeName) {
JClassType classType = getClassType(typeName);
return classType.isInterface() != null;
}
public JClassType getClassType(String typeName) {
if (typeName.indexOf('.') < 0) {
typeName = factory.getCreatedPackage() + "." + typeName;
}
TypeOracle typeOracle = context.getTypeOracle();
return typeOracle.findType(typeName);
}
public String getSQLiteType(JType returnType) {
String sqliteType = null;
JPrimitiveType primitiveReturnType = returnType.isPrimitive();
if (primitiveReturnType != null) {
if (primitiveReturnType == JPrimitiveType.INT)
{
sqliteType = "INTEGER";
}
else if (primitiveReturnType == JPrimitiveType.BOOLEAN)
{
sqliteType = "BOOL";
}
else {
sqliteType = primitiveReturnType.getSimpleSourceName().toUpperCase();
}
}
else
{
String returnTypeName = returnType.getSimpleSourceName();
if (returnTypeName.equals("String")) {
sqliteType = "TEXT";
}
else if (isSubclassOf(returnType, "JSONValue")) {
sqliteType = "JSON";
}
else {
sqliteType = returnTypeName.toUpperCase();
}
}
return sqliteType;
}
public void inspectType(String typeName, List<JMethod> getters, List<JMethod> hasManyRels, List<JMethod> hasOneRels) throws UnableToCompleteException {
JClassType classType = getClassType(typeName);
for (JMethod method : classType.getOverridableMethods()) {
if (!method.isAbstract()) {
continue;
}
String methodName = method.getName();
if (methodName.startsWith("get")) {
String propertyName = methodName.substring(3); // getId() is reserved.
if (propertyName.equals("Id")) {
continue;
}
JType returnType = method.getReturnType();
String returnTypeName = returnType.getSimpleSourceName();
if (returnType.isPrimitive() != null && !returnTypeName.equals("long")
|| returnTypeName.equals("String")
|| returnTypeName.equals("Date")
|| isSubclassOf(returnType, "JSONValue")) {
getters.add(method);
continue;
}
if (returnTypeName.equals("long")) {
logger.log(TreeLogger.ERROR,
"GWT JSNI does not support 'long' as return type on getter '" + methodName + "'. Use 'double' instead.");
throw new UnableToCompleteException();
}
if (returnTypeName.startsWith("Collection")) {
hasManyRels.add(method);
continue;
}
if (isSubclassOf(returnType, "Persistable")) {
hasOneRels.add(method);
continue;
}
logger.log(TreeLogger.ERROR,
"Unsupported return type '" + returnTypeName + "' on getter '" + methodName + "'.");
throw new UnableToCompleteException();
}
else {
// TODO: check if method is a setter. ignore if so, error if not.
}
}
}
public boolean isSubclassOf(JType type, String superClass) {
JClassType classType = type.isInterface();
if (classType == null) {
classType = type.isClass();
}
if (classType == null) {
return false;
}
Set<? extends JClassType> superTypes = classType.getFlattenedSupertypeHierarchy();
for (JClassType superType : superTypes) {
if (superType.getSimpleSourceName().equals(superClass)) {
return true;
}
}
return false;
}
public String getGenericTypeShortName(String collectionTypeName) {
int beginIndex = collectionTypeName.indexOf('<') + 1;
int endIndex = collectionTypeName.indexOf('>');
String genericTypeName = collectionTypeName.substring(beginIndex, endIndex);
beginIndex = genericTypeName.lastIndexOf('.');
if (beginIndex >= 0) {
return genericTypeName.substring(beginIndex + 1);
}
else {
return genericTypeName;
}
}
}