/* * Copyright 2011, The gwtquery team. * * 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.google.gwt.query.rebind; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.ext.Generator; import com.google.gwt.core.ext.GeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.TreeLogger.Type; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JArrayType; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JParameter; import com.google.gwt.core.ext.typeinfo.JParameterizedType; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.query.client.Function; import com.google.gwt.query.client.IsProperties; import com.google.gwt.query.client.Properties; import com.google.gwt.query.client.builders.JsonBuilder; import com.google.gwt.query.client.builders.JsonBuilderBase; import com.google.gwt.query.client.builders.JsonFactory; import com.google.gwt.query.client.builders.Name; import com.google.gwt.query.client.js.JsUtils; import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; import com.google.gwt.user.rebind.SourceWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** */ public class JsonBuilderGenerator extends Generator { static JClassType functionType; static JClassType jsonBuilderType; static JClassType settingsType; static JClassType jsType; static JClassType listType; static JClassType stringType; static JClassType jsonFactoryType; public static String capitalize(String s) { if (s.length() == 0) return s; return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase(); } public static String classNameToJsonName(String name) { return deCapitalize(name.replaceAll("^.*[\\.\\$_]", "")); } public static String deCapitalize(String s) { return s == null || s.isEmpty() ? s : (s.substring(0, 1).toLowerCase() + (s.length() > 1 ? s.substring(1) : "")); } TypeOracle oracle; public String generate(TreeLogger treeLogger, GeneratorContext generatorContext, String requestedClass) throws UnableToCompleteException { oracle = generatorContext.getTypeOracle(); JClassType clazz = oracle.findType(requestedClass); jsonBuilderType = oracle.findType(JsonBuilder.class.getName()); settingsType = oracle.findType(IsProperties.class.getName()); stringType = oracle.findType(String.class.getName()); jsType = oracle.findType(JavaScriptObject.class.getName()); listType = oracle.findType(List.class.getName()); functionType = oracle.findType(Function.class.getName()); jsonFactoryType = oracle.findType(JsonFactory.class.getName()); String t[] = generateClassName(clazz); boolean isFactory = clazz.isAssignableTo(jsonFactoryType); SourceWriter sw = getSourceWriter(treeLogger, generatorContext, t[0], t[1], isFactory, requestedClass); if (sw != null) { if (isFactory) { generateCreateMethod(sw, treeLogger); } else { Set attrs = new HashSet(); for (JMethod method : clazz.getInheritableMethods()) { String methName = method.getName(); // skip method from JsonBuilder if (jsonBuilderType.findMethod(method.getName(), method.getParameterTypes()) != null || settingsType.findMethod(method.getName(), method.getParameterTypes()) != null) { continue; } Name nameAnnotation = method.getAnnotation(Name.class); String name = nameAnnotation != null ? nameAnnotation.value() : methName.replaceFirst("^(get|set)", ""); if (nameAnnotation == null) { name = name.substring(0, 1).toLowerCase() + name.substring(1); } attrs.add(name); generateMethod(sw, method, name, treeLogger); } generateFieldNamesMethod(sw, attrs, treeLogger); generateToJsonMethod(sw, t[3], treeLogger); } sw.commit(treeLogger); } return t[2]; } public String[] generateClassName(JType t) { String[] ret = new String[4]; JClassType c = t.isClassOrInterface(); ret[0] = c.getPackage().getName(); ret[1] = c.getName().replace('.', '_') + "_JsonBuilder"; ret[2] = ret[0] + "." + ret[1]; ret[3] = classNameToJsonName(c.getName()); return ret; } public void generateFieldNamesMethod(SourceWriter sw, Collection attrs, TreeLogger logger) { String ret = ""; for (Iterator it = attrs.iterator(); it.hasNext();) { ret += (ret.isEmpty() ? "" : ",") + "\"" + it.next() + "\""; } sw.println("{ fieldNames = new String[]{" + ret + "}; }"); } public void generateMethod(SourceWriter sw, JMethod method, String name, TreeLogger logger) throws UnableToCompleteException { String ifaceName = method.getEnclosingType().getQualifiedSourceName(); String retType = method.getReturnType().getParameterizedQualifiedSourceName(); sw.print("public final " + retType + " " + method.getName()); JParameter[] params = method.getParameters(); if (params.length == 0) { JArrayType arr = method.getReturnType().isArray(); JParameterizedType list = method.getReturnType().isParameterized(); sw.println("() {"); sw.indent(); if (retType.matches("(java.lang.Boolean|boolean)")) { sw.println("return p.getBoolean(\"" + name + "\");"); } else if (retType.matches("java.util.Date")) { sw.println("return new Date(java.lang.Long.parseLong(p.getStr(\"" + name + "\")));"); } else if (method.getReturnType().isPrimitive() != null) { sw.println("return (" + retType + ")p.getFloat(\"" + name + "\");"); } else if (retType.equals("java.lang.Character")) { sw.println("return (char) p.getFloat(\"" + name + "\");"); } else if (retType.equals("java.lang.Byte")) { sw.println("return (byte) p.getFloat(\"" + name + "\");"); } else if (retType.equals("java.lang.Integer")) { sw.println("return (int) p.getFloat(\"" + name + "\");"); } else if (retType.equals("java.lang.Float")) { sw.println("return p.getFloat(\"" + name + "\");"); } else if (retType.equals("java.lang.Double")) { sw.println("return (double) p.getFloat(\"" + name + "\");"); } else if (retType.equals("java.lang.Long")) { sw.println("return (long) p.getFloat(\"" + name + "\");"); } else if (retType.equals("java.lang.Byte")) { sw.println("return (byte) p.getFloat(\"" + name + "\");"); } else if (isTypeAssignableTo(method.getReturnType(), stringType)) { sw.println("return p.getStr(\"" + name + "\");"); } else if (isTypeAssignableTo(method.getReturnType(), jsonBuilderType)) { String q = method.getReturnType().getQualifiedSourceName(); sw.println("return " + "getIsPropertiesBase(getPropertiesBase(\"" + name + "\")," + q + ".class);"); } else if (isTypeAssignableTo(method.getReturnType(), settingsType)) { String q = method.getReturnType().getQualifiedSourceName(); sw.println("return " + "((" + q + ")getPropertiesBase(\"" + name + "\"));"); } else if (retType.equals(Properties.class.getName())) { sw.println("return getPropertiesBase(\"" + name + "\");"); } else if (isTypeAssignableTo(method.getReturnType(), jsType)) { sw.println("return p.getJavaScriptObject(\"" + name + "\");"); } else if (isTypeAssignableTo(method.getReturnType(), functionType)) { sw.println("return p.getFunction(\"" + name + "\");"); } else if (arr != null || list != null) { JType type = arr != null ? arr.getComponentType() : list.getTypeArgs()[0]; boolean buildType = isTypeAssignableTo(type, jsonBuilderType); String t = type.getQualifiedSourceName(); sw.println("JsArrayMixed a = p.getArray(\"" + name + "\");"); sw.println("int l = a == null ? 0 : a.length();"); String ret; if (buildType) { ret = "getIsPropertiesArrayBase(a, new " + t + "[l], " + t + ".class)"; } else { ret = "getArrayBase(\"" + name + "\", new " + t + "[l], " + t + ".class)"; } if (arr != null) { sw.println("return " + ret + ";"); } else { sw.println("return (a == null ? null : Arrays.asList(" + ret + "));"); } } else if (method.getReturnType().isEnum() != null) { sw.println("return " + method.getReturnType().getQualifiedSourceName() + ".valueOf(p.getStr(\"" + name + "\"));"); } else { sw.println("System.err.println(\"JsonBuilderGenerator WARN: unknown return type " + retType + " " + ifaceName + "." + name + "()\"); "); // We return the object because probably the user knows how to handle it sw.println("return p.get(\"" + name + "\");"); } sw.outdent(); sw.println("}"); } else if (params.length == 1) { JType type = params[0].getType(); JArrayType arr = type.isArray(); JParameterizedType list = type.isParameterized(); sw.print("(" + type.getParameterizedQualifiedSourceName() + " a)"); sw.println("{"); sw.indent(); if (arr != null || list != null) { String a = "a"; if (list != null) { a = "a.toArray(new " + list.getTypeArgs()[0].getQualifiedSourceName() + "[0])"; } sw.println("setArrayBase(\"" + name + "\", " + a + ");"); } else if (type.getParameterizedQualifiedSourceName().matches("java.util.Date")) { sw.println("p.setNumber(\"" + name + "\", a.getTime());"); } else if (type .getParameterizedQualifiedSourceName() .matches( "(java.lang.(Character|Long|Double|Integer|Float|Byte)|(char|long|double|int|float|byte))")) { sw.println("p.setNumber(\"" + name + "\", a);"); } else if (type.getParameterizedQualifiedSourceName().matches("(java.lang.Boolean|boolean)")) { sw.println("p.setBoolean(\"" + name + "\", a);"); } else if (type.getParameterizedQualifiedSourceName().matches( "com.google.gwt.query.client.Function")) { sw.println("p.setFunction(\"" + name + "\", a);"); } else if (type.isEnum() != null) { sw.println("p.set(\"" + name + "\", a.name());"); } else { sw.println("set(\"" + name + "\", a);"); } if (!"void".equals(retType)) { if (isTypeAssignableTo(method.getReturnType(), method.getEnclosingType())) { sw.println("return this;"); } else { sw.println("return null;"); } } sw.outdent(); sw.println("}"); } } public void generateToJsonMethod(SourceWriter sw, String name, TreeLogger logger) { sw.println("public final String getJsonName() {return \"" + name + "\";}"); } protected SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext context, String packageName, String className, boolean isFactory, String... interfaceNames) { PrintWriter printWriter = context.tryCreate(logger, packageName, className); if (printWriter == null) { return null; } ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory( packageName, className); if (!isFactory) { composerFactory.setSuperclass(JsonBuilderBase.class.getName() + "<" + packageName + "." + className + ">"); } composerFactory.addImport("com.google.gwt.query.client.js.*"); composerFactory.addImport("com.google.gwt.query.client.*"); composerFactory.addImport("com.google.gwt.core.client.*"); composerFactory.addImport("com.google.gwt.dom.client.*"); composerFactory.addImport("java.util.*"); for (String interfaceName : interfaceNames) { composerFactory.addImplementedInterface(interfaceName); } return composerFactory.createSourceWriter(context, printWriter); } public boolean isTypeAssignableTo(JType t, JClassType o) { JClassType c = t.isClassOrInterface(); return (c != null && c.isAssignableTo(o)); } private void generateCreateMethod(SourceWriter sw, TreeLogger logger) { sw.println("public T create(Class clz) {"); sw.indent(); ArrayList types = new ArrayList(); for (JClassType t : oracle.getTypes()) { if (t.isInterface() != null && t.isAssignableTo(jsonBuilderType)) { if (t.isPublic()) { sw.println("if (clz == " + t.getQualifiedSourceName() + ".class) return GWT.create(" + t.getQualifiedSourceName() + ".class);"); } else { logger.log(Type.WARN, t.getQualifiedSourceName() + " is not public"); } types.add(t); } } sw.println("GQuery.console.error(\"GQ.create: not registered class :\" + clz);"); sw.println("return null;"); sw.outdent(); sw.println("}"); sw.println("public " + IsProperties.class.getName() + " create(String s) {"); sw.indent(); sw.println("return (" + IsProperties.class.getName() + ")" + JsUtils.class.getName() + ".parseJSON(s);"); sw.outdent(); sw.println("}"); sw.println("public " + IsProperties.class.getName() + " create() {"); sw.indent(); sw.println("return " + Properties.class.getName() + ".create();"); sw.outdent(); sw.println("}"); } }