/*******************************************************************************
* Copyright 2001, 2007 JamesLuo(JamesLuo.au@gmail.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.
*
* Contributors:
*******************************************************************************/
package com.gwtent.gen.reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.typeinfo.JAnnotationMethod;
import com.google.gwt.core.ext.typeinfo.JAnnotationType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.SourceWriter;
import com.gwtent.common.client.CheckedExceptionWrapper;
import com.gwtent.gen.reflection.accessadapter.JFeildAdapter;
import com.gwtent.gen.reflection.accessadapter.JMethodAdapter;
import com.gwtent.reflection.client.AccessDef;
import com.gwtent.reflection.client.ClassType;
import com.gwtent.reflection.client.ReflectionTarget;
import com.gwtent.reflection.client.ReflectionUtils;
import com.gwtent.reflection.client.impl.TypeOracleImpl;
public class GeneratorHelper {
public static boolean isSystemClass(JClassType type){
return type.getPackage().getName().startsWith("java.") || type.getPackage().getName().startsWith("javax.");
}
/**
* private interface ClassTypeOfA extends ClassType<ClassA>{
* <p>
* <p> }
*
* <p> find a Parameterized class/interface in super classs/interfaces,
* this class should a sub class of ClassType class and will point out what's the class need generate reflection information
*
* <p> if can NOT found, give a error, user should correct this before final compile
* @param classType
* @return
*/
public static JClassType getReflectionClassType(TypeOracle oracle, JClassType classType){
JClassType classClassType;
try {
classClassType = oracle.getType(ClassType.class.getCanonicalName());
} catch (NotFoundException e) {
throw new RuntimeException("Can not found reflection class, forgot include module xml file?" + e.getMessage());
}
ReflectionTarget target = classType.getAnnotation(ReflectionTarget.class);
if (target != null){
if (target.value() != null && target.value().length() > 0){
try {
return oracle.getType(target.value());
} catch (NotFoundException e) {
}
}
}
for (JClassType supClass : classType.getFlattenedSupertypeHierarchy()){
if (supClass.isParameterized() != null && supClass.isAssignableTo(classClassType)){
if (supClass.isParameterized().getTypeArgs().length == 1){
return supClass.isParameterized().getTypeArgs()[0];
}else{
throw new RuntimeException("ClassType should have only one Parameterized type, please see document of ClassType interface. Current processing type: " + classType.getQualifiedSourceName() + ", Current parameterized type count:" + classType.isParameterized().getTypeArgs().length);
}
}
}
throw new RuntimeException("ClassType should have at least one Parameterized type or annotated by @ReflectionTarget, please see document of ClassType interface. Current processing type: " + classType.getQualifiedSourceName());
}
public static int AccessDefToInt(AccessDef accessDef){
int result = 0;
if (accessDef.isFinal()) result += TypeOracleImpl.MOD_FINAL;
if (accessDef.isPrivate()) result += TypeOracleImpl.MOD_PRIVATE;
if (accessDef.isProtected()) result += TypeOracleImpl.MOD_PROTECTED;
if (accessDef.isPublic()) result += TypeOracleImpl.MOD_PUBLIC;
if (accessDef.isStatic()) result += TypeOracleImpl.MOD_STATIC;
return result;
}
public static int AccessDefToInt(JField field){
JFeildAdapter adapter = new JFeildAdapter(field);
return AccessDefToInt(adapter);
}
public static int AccessDefToInt(JMethod method){
JMethodAdapter adapter = new JMethodAdapter(method);
return AccessDefToInt(adapter);
}
/**
* Give a array of JClassTypes, return the array of qualified source name of it.
* @param types
* @return
*/
public static String[] convertJClassTypeToStringArray(JClassType[] types){
String[] result = new String[types.length];
for (int i = 0; i < types.length; i++)
result[i] = types[i].getQualifiedSourceName();
return result;
}
public static String stringArrayToCode(String[] strs){
StringBuilder result = new StringBuilder("new String[]{");
for (int i = 0; i < strs.length; i++){
result.append("\"" + processInvertedComma(strs[i]) + "\"");
if (i < (strs.length - 1)) result.append(", ");
}
result.append("}");
return result.toString();
}
/**
* generator metaData
* @param dest field or method or class
* @param source source to print code
* @param metaData
*/
public static void addMetaDatas(String dest, SourceWriter source, com.google.gwt.core.ext.typeinfo.HasMetaData metaData) {
String[] tags = metaData.getMetaDataTags();
for (int j = 0; j < tags.length; j++){
String[][] metas = metaData.getMetaData(tags[j]);
for (int k = 0; k < metas.length; k++){
source.println(dest + ".addMetaData(\"" + tags[j] + "\", " + GeneratorHelper.stringArrayToCode(metas[k]) + ");");
}
}
}
public static String processInvertedComma(String str){
// return str.replaceAll("\"", "\\\"");
StringBuffer sb = new StringBuffer();
int length = str.length();
for (int i = 0; i < length; i++){
//not first char and last char (i != 0) && (i != (length - 1)) &&
if ((str.charAt(i) == '"')){
sb.append("\\\"");
}else{
sb.append(str.charAt(i));
}
}
return sb.toString();
}
private static String toString(Object object){
if (object instanceof Class)
return ((Class)object).getName();
else if (object.getClass().isArray()){
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < Array.getLength(object); i++){
if (i > 0)
sb.append(", ");
sb.append(Array.get(object, i).toString());
}
sb.append("]");
return sb.toString();
}
else
return object.toString();
}
public static void addAnnotations_AnnotationImpl(com.google.gwt.core.ext.typeinfo.TypeOracle typeOracle,
String dest, SourceWriter source, Annotation[] annotations, TreeLogger logger){
if (annotations.length <= 0)
return;
for (Annotation annotation : annotations) {
JClassType classType = typeOracle.findType(ReflectionUtils.getQualifiedSourceName(annotation.annotationType()));
if (classType != null){
source.print(dest + ".addAnnotation(" + createAnnotationValues(typeOracle, annotation, logger) + ");");
}else{
logger.log(Type.ERROR, "Annotation (" + ReflectionUtils.getQualifiedSourceName(annotation.annotationType()) + ") not exists in compiled client source code, please ensure this class is exists and included in your module(.gwt.xml) file. GWTENT reflection process will ignore it and continue. ");
}
}
}
public static String createAnnotationValues(com.google.gwt.core.ext.typeinfo.TypeOracle typeOracle,
Annotation annotation, TreeLogger logger){
StringBuilder sb = new StringBuilder();
JClassType classType = typeOracle.findType(ReflectionUtils.getQualifiedSourceName(annotation.annotationType()));
if (classType != null){
sb.append("com.gwtent.reflection.client.impl.AnnotationValues.toAnnotation(new com.gwtent.reflection.client.impl.AnnotationValues(");
sb.append("\"" + classType.getQualifiedSourceName() + "\", new Object[]{");
JAnnotationType annoType = classType.isAnnotation();
// JAnnotationMethod[] methods = annoType.getMethods();
JAnnotationMethod[] methods = (JAnnotationMethod[]) annoType.getMethods();
int index = 0;
for (JAnnotationMethod method : methods) {
Object value = null;
try {
value = annotation.annotationType().getMethod(method.getName(), new Class[]{}).invoke(annotation);
if (index > 0)
sb.append(", ");
sb.append(annoValueToCode(typeOracle, value, logger));
index ++;
} catch (Exception e){
throw new CheckedExceptionWrapper(e);
}
}
sb.append("}))");
}else{
logger.log(Type.ERROR, "Annotation (" + ReflectionUtils.getQualifiedSourceName(annotation.annotationType()) + ") not exists in compiled client source code, please ensure this class is exists and included in your module(.gwt.xml) file. GWTENT reflection process will ignore it and continue. ");
}
return sb.toString();
}
/**
* value type: primitive, String, Class, enumerated, annotation, array of
*
* primitive need take care.
* For example float 2.0, it need changed to 2.0F, otherwise it's becomes a Double
*
* @param object
* @return
*/
public static String annoValueToCode(com.google.gwt.core.ext.typeinfo.TypeOracle typeOracle, Object object, TreeLogger logger){
if (object == null)
return "null";
if (object instanceof String){
return "\"" + object.toString().replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
}else if (object instanceof Class){
return ((Class)object).getCanonicalName() + ".class"; //inner class will got problem
}else if (object.getClass().isArray()){
StringBuilder sb = new StringBuilder();
//new ElementType[]{ElementType.ANNOTATION_TYPE};
Class<?> compType = object.getClass().getComponentType();
sb.append("new ").append(compType.getCanonicalName()).append("[] {");
for (int i = 0; i < Array.getLength(object); i++){
if (i > 0)
sb.append(", ");
if (compType.isAnnotation())
sb.append("(").append(compType.getCanonicalName()).append(")");
sb.append(annoValueToCode(typeOracle, Array.get(object, i), logger));
}
sb.append("}");
return sb.toString();
} else if (object.getClass().isEnum()){
return object.getClass().getCanonicalName() + "." + ((Enum)object).name();
}else if (object instanceof Annotation){
return createAnnotationValues(typeOracle, (Annotation)object, logger);
}else if (object instanceof Float){
return object.toString() + "F";
}else if (object instanceof Double){
return object.toString() + "D";
} else if (object instanceof Long){
return object.toString() + "L";
}
return object.toString();
}
}