Allowing final methods as Tree.addOpenHandler to be bind with
@UiHandler and also customized generic events.

Review at http://gwt-code-reviews.appspot.com/881802

Review by: rjrjr@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8891 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java b/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
index f9db520..311f18b 100644
--- a/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
+++ b/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
@@ -249,15 +249,21 @@
   private JMethod getAddHandlerMethodForObject(JClassType objectType,
       JClassType handlerType) throws UnableToCompleteException {
     JMethod handlerMethod = null;
-    for (JMethod method : objectType.getOverridableMethods()) {
+    JMethod alternativeHandlerMethod = null;
+    for (JMethod method : objectType.getInheritableMethods()) {
 
       // Condition 1: returns HandlerRegistration?
       if (method.getReturnType() == handlerRegistrationJClass) {
 
         // Condition 2: single parameter of the same type of handlerType?
         JParameter[] parameters = method.getParameters();
-        if ((parameters.length == 1)
-            && handlerType.equals(parameters[0].getType())) {
+        if (parameters.length != 1) {
+          continue;
+        }
+
+        JType subjectHandler = parameters[0].getType();
+
+        if (handlerType.equals(subjectHandler)) {
 
           // Condition 3: does more than one method match the condition?
           if (handlerMethod != null) {
@@ -268,9 +274,29 @@
 
           handlerMethod = method;
         }
+
+        /**
+         * Normalize the parameter and check for an alternative handler method.
+         * Might be the case where the given objectType is generic. In this
+         * situation we need to normalize the method parameter to test for
+         * equality. For instance:
+         *
+         *   handlerType => TableHandler<String>
+         *   subjectHandler => TableHandler
+         *
+         * This is done as an alternative handler method to preserve the
+         * original logic.
+         */
+        JParameterizedType ptype = handlerType.isParameterized();
+        if (ptype != null) {
+          if (subjectHandler.equals(ptype.getRawType())) {
+            alternativeHandlerMethod = method;
+          }
+        }
       }
     }
-    return handlerMethod;
+
+    return (handlerMethod != null) ? handlerMethod : alternativeHandlerMethod;
   }
 
   /**
diff --git a/user/test/com/google/gwt/uibinder/test/client/CustomEvent.java b/user/test/com/google/gwt/uibinder/test/client/CustomEvent.java
new file mode 100644
index 0000000..901af5c
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/CustomEvent.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * 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.uibinder.test.client;
+
+import com.google.gwt.event.shared.EventHandler;
+import com.google.gwt.event.shared.GwtEvent;
+
+/**
+ * A custom event test class.
+ *
+ * @param <T> the type associated with the event
+ */
+public class CustomEvent<T> extends GwtEvent<CustomEvent.Handler<T>> {
+
+  /**
+   * The handler for the custom event.
+   */
+  public interface Handler<T> extends EventHandler {
+    void onEvent(CustomEvent<T> event);
+  }
+
+  public static Type<Handler<?>> type;
+
+  static {
+    type = new Type<Handler<?>>();
+  }
+
+  private T value;
+
+  public CustomEvent(T value) {
+    this.value = value;
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public Type<Handler<T>> getAssociatedType() {
+    return (Type) type;
+  }
+
+  public T getValue() {
+    return value;
+  }
+
+  @Override
+  protected void dispatch(Handler<T> handler) {
+    handler.onEvent(this);
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/CustomEventWidget.java b/user/test/com/google/gwt/uibinder/test/client/CustomEventWidget.java
new file mode 100644
index 0000000..42b4e33
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/CustomEventWidget.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * 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.uibinder.test.client;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.ui.HTML;
+
+/**
+ * Tests a customized event by allowing registering of
+ * {@link CustomEvent.Handler}.
+ *
+ * @param <T> the type associated with the widget
+ */
+public class CustomEventWidget<T> extends HTML {
+
+  private final T value;
+
+  public CustomEventWidget(final T value) {
+    this.value = value;
+    addClickHandler(new ClickHandler() {
+      @Override
+      public void onClick(ClickEvent event) {
+        fireEvent(new CustomEvent<T>(value));
+      }
+    });
+  }
+
+  public HandlerRegistration addCustomHandler(CustomEvent.Handler<T> handler) {
+    return addHandler(handler, CustomEvent.type);
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/HandlerDemo.java b/user/test/com/google/gwt/uibinder/test/client/HandlerDemo.java
index 30ccdcd..ad812af 100644
--- a/user/test/com/google/gwt/uibinder/test/client/HandlerDemo.java
+++ b/user/test/com/google/gwt/uibinder/test/client/HandlerDemo.java
@@ -19,6 +19,8 @@
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.MouseOutEvent;
 import com.google.gwt.event.dom.client.MouseOverEvent;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.OpenEvent;
 import com.google.gwt.event.logical.shared.ValueChangeEvent;
 import com.google.gwt.event.shared.GwtEvent;
 import com.google.gwt.uibinder.client.UiBinder;
@@ -28,9 +30,10 @@
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.FormPanel;
-import com.google.gwt.user.client.ui.Panel;
 import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.TreeItem;
 import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
+import com.google.gwt.user.client.ui.Widget;
 
 /**
  * A simple demo showing how UiHandler works.
@@ -38,14 +41,16 @@
 public class HandlerDemo extends Composite {
 
   @UiTemplate("HandlerDemo.ui.xml")
-  interface MyUiBinder extends UiBinder<Panel, HandlerDemo> {
+  interface MyUiBinder extends UiBinder<Widget, HandlerDemo> {
   }
   private static final MyUiBinder binder = GWT.create(MyUiBinder.class);
 
   @UiField FormPanel panelForm;
   @UiField TextBox textBoxValueChange;
+  @UiField(provided = true) final CustomEventWidget<String> customEventWidget;
 
   public HandlerDemo() {
+    this.customEventWidget = new CustomEventWidget<String>("a simple test");
     initWidget(binder.createAndBindUi(this));
   }
 
@@ -66,7 +71,7 @@
   }
 
   @UiHandler("buttonSubmit")
-  @SuppressWarnings("unused") 
+  @SuppressWarnings("unused")
   void doClickSubmit(ClickEvent ignored) {
     panelForm.submit();
   }
@@ -81,7 +86,27 @@
     eventMessage(event);
   }
 
+  @UiHandler("tree")
+  void onTreeClose(CloseEvent<TreeItem> event) {
+    eventMessage(event);
+  }
+
+  @UiHandler("tree")
+  void onTreeOpen(OpenEvent<TreeItem> event) {
+    eventMessage(event);
+  }
+
+  @UiHandler("customEventWidget")
+  void onCustomEvent(CustomEvent<String> event) {
+    eventMessage("CustomEventWidget clicked. Event throwing '"
+        + event.getValue() + "'");
+  }
+
   private void eventMessage(GwtEvent<?> event) {
-    Window.alert(event.toDebugString());
+    eventMessage(event.toDebugString());
+  }
+
+  private void eventMessage(String message) {
+    Window.alert(message);
   }
 }