Delegate Html element type lookups to a pluggable factory.

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

Review by: rjrjr@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9713 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml b/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml
index 541306a..bdf0743 100644
--- a/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml
+++ b/user/src/com/google/gwt/uibinder/UiBinder.gwt.xml
@@ -18,7 +18,9 @@
   
   <source path="client"/>
   <source path="resources"/>
-  
+  <!-- Pluggable factory for creating field types for HTML elements -->
+  <define-configuration-property name="uibinder.html.elementfactory" is-multi-valued="false"/>  
+  <set-configuration-property name="uibinder.html.elementfactory" value="com.google.gwt.uibinder.rebind.GwtDomHtmlElementFactory"/>
   <generate-with class="com.google.gwt.uibinder.rebind.UiBinderGenerator">
     <when-type-assignable class="com.google.gwt.uibinder.client.UiBinder"/>
   </generate-with>
diff --git a/user/src/com/google/gwt/uibinder/rebind/GwtDomHtmlElementFactory.java b/user/src/com/google/gwt/uibinder/rebind/GwtDomHtmlElementFactory.java
new file mode 100644
index 0000000..a0a4395
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/rebind/GwtDomHtmlElementFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2011 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.rebind;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dom.client.TagName;
+
+/**
+ * Looks up implementation of DOM elements via @TagName interface in GWT DOM
+ * package.
+ */
+public class GwtDomHtmlElementFactory implements HtmlElementFactory {
+
+  public JClassType findElementTypeForTag(String htmlTag, TypeOracle oracle) {
+    JClassType elementClass = oracle.findType("com.google.gwt.dom.client.Element");
+    JClassType[] types = elementClass.getSubtypes();
+    for (JClassType type : types) {
+      TagName annotation = type.getAnnotation(TagName.class);
+      if (annotation != null) {
+        for (String annotationTag : annotation.value()) {
+          if (annotationTag.equals(htmlTag)) {
+            return type;
+          }
+        }
+      }
+    }
+    return elementClass;
+  }
+}
diff --git a/user/src/com/google/gwt/uibinder/rebind/HtmlElementFactory.java b/user/src/com/google/gwt/uibinder/rebind/HtmlElementFactory.java
new file mode 100644
index 0000000..84f05ae
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/rebind/HtmlElementFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 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.rebind;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+
+/**
+ * Looks up html tag names and returns corresponding type used to represent
+ * its bindings.
+ */
+public interface HtmlElementFactory {
+   JClassType findElementTypeForTag(String htmlTag, TypeOracle oracle);
+}
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
index 57710e2..0c6e7e7 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderGenerator.java
@@ -15,8 +15,10 @@
  */
 package com.google.gwt.uibinder.rebind;
 
+import com.google.gwt.core.ext.ConfigurationProperty;
 import com.google.gwt.core.ext.Generator;
 import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.PropertyOracle;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
@@ -115,9 +117,21 @@
         packageName);
     PrintWriter printWriter = writers.tryToMakePrintWriterFor(implName);
 
+    Class<?> elementFactoryClass;
+    HtmlElementFactory elementFactory = null;
+    try {
+      PropertyOracle propertyOracle = genCtx.getPropertyOracle();
+      ConfigurationProperty factoryProperty = propertyOracle
+          .getConfigurationProperty("uibinder.html.elementfactory");
+      elementFactoryClass = Class.forName(factoryProperty.getValues().get(0));
+      elementFactory = (HtmlElementFactory) elementFactoryClass.newInstance();
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+
     if (printWriter != null) {
       generateOnce(interfaceType, implName, printWriter, logger, oracle,
-          resourceOracle, writers, designTime);
+          resourceOracle, writers, designTime, elementFactory);
     }
     return packageName + "." + implName;
   }
@@ -125,7 +139,8 @@
   private void generateOnce(JClassType interfaceType, String implName,
       PrintWriter binderPrintWriter, TreeLogger treeLogger, TypeOracle oracle,
       ResourceOracle resourceOracle, PrintWriterManager writerManager,
-      DesignTimeUtils designTime) throws UnableToCompleteException {
+      DesignTimeUtils designTime,
+      HtmlElementFactory elementFactory) throws UnableToCompleteException {
 
     MortalLogger logger = new MortalLogger(treeLogger);
     String templatePath = deduceTemplateFile(logger, interfaceType);
@@ -134,7 +149,7 @@
 
     UiBinderWriter uiBinderWriter = new UiBinderWriter(interfaceType, implName,
         templatePath, oracle, logger, new FieldManager(oracle, logger),
-        messages, designTime, uiBinderCtx);
+        messages, designTime, uiBinderCtx, elementFactory);
 
     Document doc = getW3cDoc(logger, designTime, resourceOracle, templatePath);
     designTime.rememberPathForElements(doc);
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
index e6be10a..e8844b8 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -19,7 +19,6 @@
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JPackage;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.dom.client.TagName;
 import com.google.gwt.resources.client.ClientBundle;
 import com.google.gwt.uibinder.attributeparsers.AttributeParser;
 import com.google.gwt.uibinder.attributeparsers.AttributeParsers;
@@ -181,6 +180,9 @@
    * The type we have been asked to generated, e.g. MyUiBinder
    */
   private final JClassType baseClass;
+
+  private final HtmlElementFactory elementFactory;
+
   /**
    * The name of the class we're creating, e.g. MyUiBinderImpl
    */
@@ -227,7 +229,8 @@
   public UiBinderWriter(JClassType baseClass, String implClassName,
       String templatePath, TypeOracle oracle, MortalLogger logger,
       FieldManager fieldManager, MessagesWriter messagesWriter,
-      DesignTimeUtils designTime, UiBinderContext uiBinderCtx)
+      DesignTimeUtils designTime, UiBinderContext uiBinderCtx,
+      HtmlElementFactory elementFactory)
       throws UnableToCompleteException {
     this.baseClass = baseClass;
     this.implClassName = implClassName;
@@ -238,6 +241,7 @@
     this.messages = messagesWriter;
     this.designTime = designTime;
     this.uiBinderCtx = uiBinderCtx;
+    this.elementFactory = elementFactory;
 
     // Check for possible misuse 'GWT.create(UiBinder.class)'
     JClassType uibinderItself = oracle.findType(UiBinder.class.getCanonicalName());
@@ -494,7 +498,7 @@
     String tagName = elem.getLocalName();
 
     if (!isWidgetElement(elem)) {
-      return findGwtDomElementTypeForTag(tagName);
+      return findDomElementTypeForTag(tagName);
     }
 
     String ns = elem.getNamespaceUri();
@@ -760,24 +764,10 @@
   }
 
   /**
-   * Given a DOM tag name, return the corresponding
-   * {@link com.google.gwt.dom.client.Element} subclass.
+   * Given a DOM tag name, return the corresponding JSO subclass.
    */
-  private JClassType findGwtDomElementTypeForTag(String tag) {
-    JClassType elementClass = oracle.findType("com.google.gwt.dom.client.Element");
-    JClassType[] types = elementClass.getSubtypes();
-    for (JClassType type : types) {
-      TagName annotation = type.getAnnotation(TagName.class);
-      if (annotation != null) {
-        for (String annotationTag : annotation.value()) {
-          if (annotationTag.equals(tag)) {
-            return type;
-          }
-        }
-      }
-    }
-
-    return elementClass;
+  private JClassType findDomElementTypeForTag(String tag) {
+    return elementFactory.findElementTypeForTag(tag, oracle);
   }
 
   /**
diff --git a/user/test/com/google/gwt/uibinder/elementparsers/ElementParserTester.java b/user/test/com/google/gwt/uibinder/elementparsers/ElementParserTester.java
index 3d9f200..14dbbff 100644
--- a/user/test/com/google/gwt/uibinder/elementparsers/ElementParserTester.java
+++ b/user/test/com/google/gwt/uibinder/elementparsers/ElementParserTester.java
@@ -30,6 +30,8 @@
 import com.google.gwt.uibinder.rebind.FieldManager;
 import com.google.gwt.uibinder.rebind.FieldWriter;
 import com.google.gwt.uibinder.rebind.MockMortalLogger;
+import com.google.gwt.uibinder.rebind.HtmlElementFactory;
+import com.google.gwt.uibinder.rebind.GwtDomHtmlElementFactory;
 import com.google.gwt.uibinder.rebind.W3cDomHelper;
 import com.google.gwt.uibinder.rebind.XMLElement;
 import com.google.gwt.uibinder.rebind.XMLElementProvider;
@@ -113,8 +115,9 @@
     MessagesWriter messages = new MessagesWriter(BINDER_URI, logger,
         templatePath, baseType.getPackage().getName(), implName);
 
+    HtmlElementFactory elementFactory = new GwtDomHtmlElementFactory();
     writer = new MockUiBinderWriter(baseType, implName, templatePath, types,
-        logger, fieldManager, messages);
+        logger, fieldManager, messages, elementFactory);
     fieldManager.registerField(types.findType(parsedTypeName), FIELD_NAME);
     parsedType = types.findType(parsedTypeName);
   }
diff --git a/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java b/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java
index b08ab16..a2984bc 100644
--- a/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java
+++ b/user/test/com/google/gwt/uibinder/elementparsers/MockUiBinderWriter.java
@@ -21,6 +21,7 @@
 import com.google.gwt.uibinder.rebind.DesignTimeUtilsStub;
 import com.google.gwt.uibinder.rebind.FieldManager;
 import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.HtmlElementFactory;
 import com.google.gwt.uibinder.rebind.UiBinderContext;
 import com.google.gwt.uibinder.rebind.UiBinderWriter;
 import com.google.gwt.uibinder.rebind.XMLElement;
@@ -34,10 +35,11 @@
 
   public MockUiBinderWriter(JClassType baseClass, String implClassName,
       String templatePath, TypeOracle oracle, MortalLogger logger,
-      FieldManager fieldManager, MessagesWriter messagesWriter)
-      throws UnableToCompleteException {
+      FieldManager fieldManager, MessagesWriter messagesWriter, 
+      HtmlElementFactory factory) throws UnableToCompleteException {
     super(baseClass, implClassName, templatePath, oracle, logger, fieldManager,
-        messagesWriter, DesignTimeUtilsStub.EMPTY, new UiBinderContext());
+	  messagesWriter, DesignTimeUtilsStub.EMPTY, new UiBinderContext(),
+          factory);
   }
 
   @Override
@@ -50,4 +52,4 @@
       throws UnableToCompleteException {
     return elem.consumeOpeningTag();
   }
-}
\ No newline at end of file
+}