package com.google.gwt.gwtpages.generator.page;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.JField;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.gwtpages.client.ui.HandlerRegistrationCache;
import com.google.gwt.user.rebind.SourceWriter;
public class FieldBindingUtil {
public static ViewBindings printHandlerBindings(
JClassType classType, JClassType widgetOrViewType,
SourceWriter sourceWriter, TypeOracle typeOracle, TreeLogger logger, boolean isViewInterface)
throws UnableToCompleteException {
JClassType domEventType = null;
try {
domEventType = typeOracle.getType(DomEvent.class.getName());
} catch (NotFoundException e) {
logger.log(logger.ERROR, "Could not locate source for '"
+ DomEvent.class.getName() + "'");
throw new UnableToCompleteException();
}
Map<JClassType, List<BindingData>> knownHandledTypes = new HashMap<JClassType, List<BindingData>>();
for (JMethod m : classType.getMethods()) {
if (m.getParameters().length == 1
&& m.getParameters()[0].getType().isClassOrInterface()
.isAssignableTo(domEventType)) {
JClassType eventType = m.getParameters()[0].getType()
.isClassOrInterface();
JClassType fieldType = null;
JMethod viewMethod = null;
String[] parts = m.getName().split("[$]");
if (parts.length >= 2) {
// sanity check
for (int i = 0; i < parts.length - 1; i++) {
if (isViewInterface) {
JClassType superclass = widgetOrViewType;
while (superclass != null && viewMethod == null) {
for (JMethod method : superclass.getMethods()) {
String name = method.getName();
if (name.startsWith("get") && name.length() > 3) {
name = name.substring(3, 4).toLowerCase() + name.substring(4);
}
if (parts[i].equals(name)) {
viewMethod = method;
break;
}
}
superclass = superclass.getSuperclass();
}
}
else {
JField field = classType.getField(parts[i]);
if (null != field)
fieldType = classType.getField(parts[i]).getType()
.isClassOrInterface();
}
if (isViewInterface) {
if (null == viewMethod) {
logger.log(
logger.ERROR,
"Could not find view method '" + parts[i]
+ "' referenced by method '"
+ m.getName() + "' in '"
+ classType.getName() + "'");
throw new UnableToCompleteException();
}
}
else {
if (null == fieldType) {
logger.log(
logger.ERROR,
"Could not find field '" + parts[i]
+ "' referenced by method '"
+ m.getName() + "' in '"
+ classType.getName() + "'");
throw new UnableToCompleteException();
}
}
}
// get the handler method name
String handlerName = null;
JClassType handlerType = null;
JClassType superClassType = m.getParameters()[0].getType()
.isClassOrInterface().getSuperclass();
while (null != superClassType && null == handlerName) {
JParameterizedType type = superClassType
.isParameterized();
if (null != type && type.getTypeArgs().length == 1) {
handlerType = type.getTypeArgs()[0]
.isClassOrInterface();
// we need to figure out the handler name
if (null == handlerName) {
JClassType _check = null;
if (isViewInterface)
_check = viewMethod.getReturnType().isClassOrInterface();
else
_check = fieldType;
while (_check != null && handlerName == null) {
for (JMethod method : _check.getMethods()) {
if (method.getParameters().length == 1
&& method.getParameters()[0]
.getType()
.equals(handlerType)) {
handlerName = method.getName();
break;
}
}
if (null != handlerName)
break;
_check = _check.getSuperclass();
}
}
if (null != handlerName)
break;
else {
logger.log(
logger.ERROR,
"Could not find event name for '"
+ handlerType.getQualifiedBinaryName()
+ "' on '" + fieldType.getQualifiedBinaryName() + "'");
throw new UnableToCompleteException();
}
} else {
superClassType = superClassType.getSuperclass();
}
}
if (null == handlerType) {
logger.log(
logger.ERROR,
"Could not find handler type for '"
+ m.getName()
+ "'");
throw new UnableToCompleteException();
}
if (null == handlerName) {
logger.log(
logger.ERROR,
"Could not find event name for '"
+ handlerType.getQualifiedBinaryName()
+ "'");
throw new UnableToCompleteException();
}
for (int i = 0; i < parts.length - 1; i++) {
List<BindingData> fields = knownHandledTypes
.get(handlerType);
if (null == fields) {
fields = new ArrayList<BindingData>();
knownHandledTypes.put(handlerType, fields);
}
BindingData bindingData = new BindingData();
bindingData.eventType = eventType;
bindingData.handlerType = handlerType;
bindingData.fieldName = parts[i];
bindingData.method = m;
bindingData.viewMethod = viewMethod;
bindingData.addHandlerMethodName = handlerName;
fields.add(bindingData);
}
}
}
}
ViewBindings bindings = new ViewBindings();
bindings.handledEvents = knownHandledTypes;
bindings.viewClassType = widgetOrViewType;
return bindings;
}
public static void printHandlerBindingsInnerClass(
ViewBindings bindings,
JClassType classType, SourceWriter sourceWriter,
TypeOracle typeOracle, TreeLogger logger, boolean isViewInterface)
throws UnableToCompleteException {
Map<JClassType, List<BindingData>> handledEvents = bindings.handledEvents;
sourceWriter.println();
sourceWriter.println("protected void bindHandlers() {");
sourceWriter.indent();
sourceWriter
.println("_Handler _handler = (_Handler) getHandlerCache();");
for (Map.Entry<JClassType, List<BindingData>> entry : handledEvents.entrySet()) {
for (BindingData bd : entry.getValue()) {
if (isViewInterface)
sourceWriter.println("_handler.add(((" + bindings.viewClassType.getQualifiedSourceName() + ") getView())." + bd.viewMethod.getName() + "()." + bd.addHandlerMethodName + "(_handler));");
else
sourceWriter.println("_handler.add(" + bd.fieldName + "." + bd.addHandlerMethodName + "(_handler));");
}
}
sourceWriter.outdent();
sourceWriter.println("}");
sourceWriter.println();
sourceWriter.println("protected " + HandlerRegistrationCache.class.getName() + " createHandlerCache() {");
sourceWriter.indent();
sourceWriter.println("return new _Handler();");
sourceWriter.outdent();
sourceWriter.println("}");
sourceWriter.println();
sourceWriter.print("private class _Handler extends "
+ HandlerRegistrationCache.class.getName() + " ");
boolean started = false;
for (JClassType handlerType : handledEvents.keySet()) {
if (started)
sourceWriter.print(", ");
else {
sourceWriter.print("implements ");
started = true;
}
sourceWriter.print(handlerType.getQualifiedBinaryName());
}
sourceWriter.println(" {");
sourceWriter.indent();
sourceWriter
.println("private List<HandlerRegistration> regs = new ArrayList<HandlerRegistration>();");
for (Map.Entry<JClassType, List<BindingData>> entry : handledEvents
.entrySet()) {
sourceWriter.print("public void ");
JClassType handler = entry.getKey();
if (handler.getMethods().length == 1) {
sourceWriter.print(handler.getMethods()[0].getName());
sourceWriter.print("(");
sourceWriter.print(handler.getMethods()[0].getParameters()[0]
.getType().isClassOrInterface()
.getQualifiedBinaryName());
sourceWriter.println(" event) {");
sourceWriter.indent();
if (isViewInterface) {
sourceWriter.println(bindings.viewClassType.getQualifiedSourceName() + " view = (" + bindings.viewClassType.getQualifiedSourceName() + ") getView();");
}
for (BindingData bindingData : entry.getValue()) {
if (isViewInterface) {
sourceWriter.println("if (event.getSource().equals(view."
+ bindingData.viewMethod.getName() + "())) { "
+ bindingData.method.getName() + "(event); }");
}
else {
sourceWriter.println("if (event.getSource().equals("
+ bindingData.fieldName + ")) { "
+ bindingData.method.getName() + "(event); }");
}
}
sourceWriter.outdent();
sourceWriter.println("}");
} else {
logger.log(logger.ERROR,
"Invalid event handler - has more than 1 method '"
+ handler.getName() + "'");
throw new UnableToCompleteException();
}
}
sourceWriter.outdent();
sourceWriter.println("}");
}
public static class BindingData {
public String fieldName;
public JMethod method;
public JMethod viewMethod;
public String addHandlerMethodName;
public JClassType eventType;
public JClassType handlerType;
}
public static class ViewBindings {
public Map<JClassType, List<BindingData>> handledEvents;
public JClassType viewClassType;
}
}