/* * Smart GWT (GWT for SmartClient) * Copyright 2008 and beyond, Isomorphic Software, Inc. * * Smart GWT is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 * is published by the Free Software Foundation. Smart GWT is also * available under typical commercial license terms - see * http://smartclient.com/license * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package com.smartgwt.rebind; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.JEnumType; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JArrayType; import com.google.gwt.core.ext.typeinfo.JPrimitiveType; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.user.rebind.SourceWriter; import com.google.gwt.core.client.JavaScriptObject; import com.smartgwt.rebind.BeanProperty; import com.smartgwt.client.types.ValueEnum; import com.smartgwt.client.widgets.BaseWidget; import com.smartgwt.client.bean.types.*; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.HashSet; import java.util.Set; import java.util.Arrays; import java.util.Date; // We import the run-time value types to fail fast if there are typos import com.smartgwt.client.bean.types.*; public class BeanMethod { // The method private JMethod method; // The type we return, or take private JType valueType; // The class literal for the valueType private String classLiteral; // The generic type equivalent to the valueType, if the valueType is // primitive private JType genericType; // Are we a setter or a getter? private boolean setter = false; private boolean getter = false; // Our name, with get, set or is cut off, and recapitalized // But, we keep any As... suffix for now private String name; // Our prefix ... i.e. "get", "set" or "is" private String prefix; // A fragment which deboxes for setting primitive values, since // we always box the signature. private String deboxer = ""; public JType getValueType () { return valueType; } public JType getGenericType () { return genericType; } public boolean isSetter () { return setter; } public boolean isGetter () { return getter; } public String getName () { return name; } public String getPrefix () { return prefix; } public JMethod getMethod () { return method; } public boolean isPrimitive () { return valueType.isPrimitive() != null; } // Determines whether the method might be a custom getter, considering // everything but name private boolean couldBeCustomGetter () { // We may be able to optimize later in various ways to avoid calling // setters or getters that ultimately just set or get a value on the // underlying JSO (which is what we will do by default anyway). return method.getReturnType() != JPrimitiveType.VOID && method.isPublic() && !method.isAbstract() && !method.isStatic() && method.getParameters().length == 0; } // Determines whether the method might be a custom setter, considering // everything but name private boolean couldBeCustomSetter () { // We may be able to optimize later in various ways to avoid calling // setters or getters that ultimately just set or get a value on the // underlying JSO (which is what we will do by default anyway). return method.getReturnType() == JPrimitiveType.VOID && method.isPublic() && !method.isAbstract() && !method.isStatic() && method.getParameters().length == 1; } public boolean isStaticInitMethod () { return method.isPublic() && method.isStatic() && method.getParameters().length == 0 && method.getName().equals("beanFactoryInit"); } // We lowercase the first character, unless the second character is // uppercase. This catches cases like ID, or HAlign and VAlign private String recapitalize(String propertyName) { if (propertyName.length() < 2) { return propertyName.toLowerCase(); } else if (Character.isUpperCase(propertyName.charAt(1))) { return propertyName; } else { return propertyName.substring(0,1).toLowerCase() + propertyName.substring(1); } } // Some regexes to use in recognizing getters and setters final static Pattern getterPattern = Pattern.compile("^(get|is)([A-Z].*)"); final static Pattern setterPattern = Pattern.compile("^set([A-Z].*)"); public BeanMethod (JMethod method, TypeOracle typeOracle) { this.method = method; if (couldBeCustomSetter()) { Matcher matcher = setterPattern.matcher(method.getName()); if (matcher.matches()) { valueType = method.getParameters()[0].getType(); setter = true; name = recapitalize(matcher.group(1)); prefix = "set"; } } else if (couldBeCustomGetter()) { Matcher matcher = getterPattern.matcher(method.getName()); if (matcher.matches()) { valueType = method.getReturnType(); getter = true; prefix = matcher.group(1); name = recapitalize(matcher.group(2)); } } if (valueType != null) { JPrimitiveType primitiveType = valueType.isPrimitive(); if (primitiveType == null) { genericType = valueType; classLiteral = valueType.getQualifiedSourceName() + ".class"; } else { if (primitiveType == JPrimitiveType.BOOLEAN) { genericType = typeOracle.findType(Boolean.class.getCanonicalName()); classLiteral = boolean.class.getCanonicalName() + ".class"; deboxer = ".@" + genericType.getQualifiedSourceName() + "::booleanValue()()"; } else if (primitiveType == JPrimitiveType.FLOAT) { genericType = typeOracle.findType(Float.class.getCanonicalName()); classLiteral = float.class.getCanonicalName() + ".class"; deboxer = ".@" + genericType.getQualifiedSourceName() + "::floatValue()()"; } else if (primitiveType == JPrimitiveType.DOUBLE) { genericType = typeOracle.findType(Double.class.getCanonicalName()); classLiteral = double.class.getCanonicalName() + ".class"; deboxer = ".@" + genericType.getQualifiedSourceName() + "::doubleValue()()"; } else if (primitiveType == JPrimitiveType.INT) { genericType = typeOracle.findType(Integer.class.getCanonicalName()); classLiteral = int.class.getCanonicalName() + ".class"; deboxer = ".@" + genericType.getQualifiedSourceName() + "::intValue()()"; } else if (primitiveType == JPrimitiveType.LONG) { genericType = typeOracle.findType(Long.class.getCanonicalName()); classLiteral = long.class.getCanonicalName() + ".class"; deboxer = ".@" + genericType.getQualifiedSourceName() + "::longValue()()"; } } } } public String jsniGetter () { return "b.@" + method.getEnclosingType().getQualifiedSourceName() + "::" + method.getName() + "()()"; } public String box (String basicGetter) { if (isPrimitive()) { return "@" + genericType.getQualifiedSourceName() + "::new(" + valueType.getJNISignature() + ")(" + basicGetter + ")"; } else { return basicGetter; } } public void writeJSNIFunction (SourceWriter source, boolean addComma) { if (isGetter()) { source.println("function (b) {return " + box(jsniGetter()) + "}" + (addComma ? "," : "")); } else { source.println( "function (b,v) {b.@" + method.getEnclosingType().getQualifiedSourceName() + "::" + method.getName() + "(" + valueType.getJNISignature() + ")" + "(v" + deboxer + ")}" + (addComma ? "," : "") ); } } public void writeNew (SourceWriter source, String beanClassName, Integer getterMethodNumber, Integer setterMethodNumber, boolean addComma) { // This should look like this: // new BeanMethod<Canvas, Integer>(Integer.class, 27, 35), StringBuilder s = new StringBuilder(); s.append("new BeanMethod<"); s.append(beanClassName); s.append(", "); s.append(genericType.getQualifiedSourceName()); s.append(">("); s.append(classLiteral); s.append(", "); s.append(getterMethodNumber == null ? "null" : ("methods.get(" + getterMethodNumber.toString() + ")")); s.append(", "); s.append(setterMethodNumber == null ? "null" : ("methods.get(" + setterMethodNumber.toString() + ")")); s.append(")"); if (addComma) s.append(","); source.println(s.toString()); } }