Allowing support to field references inside html elements and GWT
widgets: <span><ui:text from="{m.message}" /></span>.

http://gwt-code-reviews.appspot.com/153813


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7640 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java
index f7c1317..c127a47 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 2008 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
@@ -40,7 +40,7 @@
    * {@link com.google.gwt.user.client.ui.UIObject} (or really, any object that
    * responds to <code>getElement()</code>). Uses an instance of
    * {@link HtmlMessageInterpreter} to process message elements.
-   * 
+   *
    * @param uiExpression An expression that can be evaluated at runtime to find
    *          an object whose getElement() method can be called to get an
    *          ancestor of all Elements generated from the interpreted HTML.
@@ -50,7 +50,7 @@
     String ancestorExpression = uiExpression + ".getElement()";
     return new HtmlInterpreter(writer, ancestorExpression,
         new HtmlMessageInterpreter(writer, ancestorExpression));
-  } 
+  }
 
   private final UiBinderWriter writer;
   private final InterpreterPipe<String> pipe;
@@ -58,7 +58,7 @@
   /**
    * Rather than using this constructor, you probably want to use the
    * {@link #newInterpreterForUiObject} factory method.
-   * 
+   *
    * @param ancestorExpression An expression that can be evaluated at runtime to
    *          find an Element that will be an ancestor of all Elements generated
    *          from the interpreted HTML.
@@ -74,6 +74,7 @@
     pipe.add(new FieldInterpreter(writer, ancestorExpression));
     pipe.add(new ComputedAttributeInterpreter(writer));
     pipe.add(new AttributeMessageInterpreter(writer));
+    pipe.add(new UiTextInterpreter(writer.getLogger()));
     pipe.add(messageInterpreter);
   }
 
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/TextInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/TextInterpreter.java
index d007bd8..187ea61 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/TextInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/TextInterpreter.java
@@ -46,7 +46,7 @@
       return writer.tokenForExpression(messageInvocation);
     }
 
-    return null;
+    return new UiTextInterpreter(writer.getLogger()).interpretElement(elem);
   }
 
   private String consumeAsTextMessage(XMLElement elem, MessagesWriter messages)
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/UiTextInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/UiTextInterpreter.java
new file mode 100644
index 0000000..6043326
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/elementparsers/UiTextInterpreter.java
@@ -0,0 +1,51 @@
+/*
+ * 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.elementparsers;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
+
+/**
+ * Interprets generic message tags like:
+ * <b>&lt;ui:text from="{myMsg.message}" /&gt;</b>. It's called in both hasText
+ * and hasHTML context.
+ */
+public class UiTextInterpreter implements XMLElement.Interpreter<String> {
+
+  private static final String BINDER_URI = "urn:ui:com.google.gwt.uibinder";
+  private static final String LOCAL_NAME = "text";
+
+  private final MortalLogger logger;
+
+  public UiTextInterpreter(MortalLogger logger) {
+    this.logger = logger;
+  }
+
+  public String interpretElement(XMLElement elem)
+      throws UnableToCompleteException {
+   // Must be in the format: <ui:string from="{myMsg.message}" />
+   if (BINDER_URI.equals(elem.getNamespaceUri())
+        && LOCAL_NAME.equals(elem.getLocalName())) {
+      String fieldRef = elem.consumeStringAttribute("from");
+      if (fieldRef == null) {
+        logger.die("Attribute 'from' not found in '%s'.", elem);
+      }
+      return "\" + " + fieldRef + " + \"";
+    }
+    return null;
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/I18nMessageTest.java b/user/test/com/google/gwt/uibinder/test/client/I18nMessageTest.java
new file mode 100644
index 0000000..0ba1b85
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/I18nMessageTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.core.client.GWT;
+import com.google.gwt.i18n.client.Messages;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * A simple widget for i18n tests.
+ */
+public class I18nMessageTest extends Composite {
+
+  /**
+   * The UiBinder interface.
+   */
+  interface Binder extends UiBinder<Widget, I18nMessageTest> { }
+  private static final Binder BINDER = GWT.create(Binder.class);
+
+  /**
+   * The message holder to use in the template.
+   */
+  public interface TestMessages extends Messages {
+    @DefaultMessage("Message from Brazil")
+    String messageBrazil();
+
+    @DefaultMessage("Message from USA")
+    String messageUsa();
+  }
+
+  public I18nMessageTest() {
+    initWidget(BINDER.createAndBindUi(this));
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/I18nMessageTest.ui.xml b/user/test/com/google/gwt/uibinder/test/client/I18nMessageTest.ui.xml
new file mode 100644
index 0000000..f38554e
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/I18nMessageTest.ui.xml
@@ -0,0 +1,51 @@
+<!--
+  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
+  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. License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<ui:UiBinder
+    xmlns:ui="urn:ui:com.google.gwt.uibinder"
+    xmlns:gwt="urn:import:com.google.gwt.user.client.ui">
+
+  <ui:with field="msg"
+      type="com.google.gwt.uibinder.test.client.I18nMessageTest.TestMessages" />
+
+  <gwt:HTMLPanel>
+
+    <hr />
+    <h1>Test</h1>
+    <h2><ui:text from="{msg.messageBrazil}" /></h2>
+    <h2 /><ui:text from="{msg.messageUsa}" />
+
+    <hr />
+
+    <gwt:Label>Brazil</gwt:Label>
+    <gwt:Label><ui:text from="{msg.messageBrazil}" /></gwt:Label>
+    <gwt:Label>
+      Label =
+      <ui:text from="{msg.messageBrazil}" />
+      <ui:text from="{msg.messageUsa}" />
+    </gwt:Label>
+
+      <gwt:HTML text="{msg.messageUsa}"/>
+      <gwt:HTML>USA</gwt:HTML>
+      <gwt:HTML>
+        HTML =
+        <ui:text from="{msg.messageBrazil}" />
+        <ui:text from="{msg.messageUsa}" />
+      </gwt:HTML>
+      <gwt:HTML><ui:text from="{msg.messageBrazil}" /></gwt:HTML>
+
+    </gwt:HTMLPanel>
+
+</ui:UiBinder>
diff --git a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml
index 476437b..40e8dba 100644
--- a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml
+++ b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml
@@ -13,25 +13,25 @@
   limitations under the License.
 -->
 
-<!DOCTYPE ui:UiBinder 
+<!DOCTYPE ui:UiBinder
   SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"
   [
     <!ENTITY % MyEntities SYSTEM "MyEntities.ent">
     %MyEntities;
   ]
 >
-<!-- 
-  This scary DOCTYPE section is not required. It's here to demonstrate two 
+<!--
+  This scary DOCTYPE section is not required. It's here to demonstrate two
   things.
-  
+
   First, this bit:
-  
+
     SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"
- 
-  allows you to use familiar HTML entities like &nbsp; and &bull;, 
-  which are not part of XML. (The file is also available as 
+
+  allows you to use familiar HTML entities like &nbsp; and &bull;,
+  which are not part of XML. (The file is also available as
   https://dl-ssl.google.com/gwt/DTD/xhtml.ent.)
-  
+
   Next, the bit in square brackets is even more optional. It shows how
   to add your own entities, in this case pulling in additional
   definitions for &point-left; and &point-right; from local file
@@ -39,16 +39,16 @@
 
   You don't have to be so verbose to include a local file! For
   example, you might instead grab your own copy of xhtml.ent
-  and add your custom entity definitions to it, and use this 
+  and add your custom entity definitions to it, and use this
   for your DOCTYPE:
-  
+
   <!DOCTYPE ui:UiBinder SYSTEM "my-xhtml.ent">
 -->
 
 <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
   xmlns:gwt='urn:import:com.google.gwt.user.client.ui'
   xmlns:demo='urn:import:com.google.gwt.uibinder.test.client'
-  
+
   xmlns:legacyValuesForBeans='urn:with:com.google.gwt.uibinder.test.client.WidgetBasedUi.FakeBundle2'
   xmlns:legacyValuesForHtml='urn:with:com.google.gwt.uibinder.test.client.WidgetBasedUi.FakeBundle3'
 
@@ -61,7 +61,7 @@
 
 <ui:with field='external' type='com.google.gwt.uibinder.test.client.WidgetBasedUiExternalResources'>
   (This text is ignored, but it's a handy place to document a resource.)
-  
+
   external is used to test receiving a resource from the owner via
   @UiField(provided = true), particularly useful for dependency
   injection via Gin and the like
@@ -72,20 +72,20 @@
   for a resource to provide arbitrary objects to arbitrary attributes (look for FooLabel)
 </ui:with>
 
-<!-- 
+<!--
   Tests creating a CssResource from an external file.
  -->
-<ui:style field='myStyle' src='WidgetBasedUi.css Menu.css' 
+<ui:style field='myStyle' src='WidgetBasedUi.css Menu.css'
     type='com.google.gwt.uibinder.test.client.WidgetBasedUi.Style'>
-    .menuBar { 
+    .menuBar {
       font-family: sans-serif;
     }
 </ui:style>
- 
+
 <ui:style field='myOtherStyle' type='com.google.gwt.uibinder.test.client.WidgetBasedUi.Style'>
-  /* because we extend WidgetBasedUi.Style and it's tagged @Shared, we can refine the menubar 
+  /* because we extend WidgetBasedUi.Style and it's tagged @Shared, we can refine the menubar
      style defined in other files */
-  
+
   .menuBar.psychedelic {
     background-color: Yellow;
   }
@@ -98,7 +98,7 @@
 
 <ui:style field='myTotallyPrivateStyle'>
   /* An inline style tied to no public type */
-  
+
   .super-private-color {
     background-color: Gold;
   }
@@ -113,14 +113,14 @@
 <ui:data field='heartCursorResource' src='heart.cur'/>
 
 <ui:style field='mySpritelyStyle'>
-  @sprite .simpleSprite { 
-    gwt-image: "prettyImage"; 
+  @sprite .simpleSprite {
+    gwt-image: "prettyImage";
   }
 
-  @sprite .tilingSprite { 
-    gwt-image: "prettyTilingImage"; 
+  @sprite .tilingSprite {
+    gwt-image: "prettyTilingImage";
   }
-  
+
   .garish {
     color: Purple;
     font-weight: bold;
@@ -129,7 +129,7 @@
    }
 
    .garish.tilingSprite {
-      font-size: 1.5em; 
+      font-size: 1.5em;
     }
 </ui:style>
 <ui:image field='prettyImage' />
@@ -174,9 +174,9 @@
       adopters should recognize themselves as such, and be prepared to have the
       api rug yanked out from under them.</ui:msg></p>
       <p ui:field="main">
-        &point-right;&nbsp;<span ui:field="messageInMain"><ui:msg>This is the main area. 
-        It's an <b>HTMLPanel</b>, which allows you to mix 
-          &point-right; &bull; XHTML &bull; &point-left; and regular GWT widgets, more or less with 
+        &point-right;&nbsp;<span ui:field="messageInMain"><ui:msg>This is the main area.
+        It's an <b>HTMLPanel</b>, which allows you to mix
+          &point-right; &bull; XHTML &bull; &point-left; and regular GWT widgets, more or less with
           impunity. Take the following button for example.</ui:msg></span>&nbsp;&point-left;
       </p>
       <gwt:Button ui:field='myButton'>
@@ -200,7 +200,7 @@
       />
       </div>
 
-      <p ui:field='simpleSpriteParagraph' 
+      <p ui:field='simpleSpriteParagraph'
           class='{mySpritelyStyle.simpleSprite} {mySpritelyStyle.garish}
             {cursorifficStyle.cursor}' >
         And sprites too
@@ -210,7 +210,7 @@
           {cursorifficStyle.cursor}'>
         Well how do you like  <br/>
         tiled sprited images...of babies!! <br/>
-        Well of course you do. Who wouldn't? 
+        Well of course you do. Who wouldn't?
       </p>
 
       <p>
@@ -248,7 +248,7 @@
             <div id="pop-can-has-submenu">
               <ui:msg description="Last 7 Day Period">Pop</ui:msg>
             </div>
-            <gwt:MenuBar vertical="true" ui:field="dropdownMenuBar" 
+            <gwt:MenuBar vertical="true" ui:field="dropdownMenuBar"
               styleName="{myStyle.menuBar}">
               <gwt:MenuItem ui:field='menuItemCustomDateRange'>
                 <ui:msg description="Custom date range">
@@ -271,7 +271,7 @@
         <gwt:Tree ui:field='myTree' width="100px" />
 
       <p>...TextBoxes...</p>
-      
+
       <gwt:TextBox maxLength="21">21 chars only, please</gwt:TextBox>
 
       <p>...or perhaps a handful of RadioButtons:</p>
@@ -285,11 +285,11 @@
         text="Charlie (this one is a subclass of RadioButton)">
         <ui:attribute name="text" description="radio button name"/>
       </demo:PointlessRadioButtonSubclass>
-      
-	<gwt:HorizontalPanel horizontalAlignment="ALIGN_LEFT">
-	  <gwt:Cell><gwt:HTMLPanel>
+
+        <gwt:HorizontalPanel horizontalAlignment="ALIGN_LEFT">
+          <gwt:Cell><gwt:HTMLPanel>
       <p> ... a StackPanel ... </p>
-      
+
       <gwt:StackPanel stylePrimaryName="myStyle" width="280px" ui:field='myStackPanel'>
         <gwt:Label text="Stack One Text"  gwt:StackPanel-text="Stack One"
             ui:field='myStackPanelItem'>
@@ -303,10 +303,10 @@
         <gwt:Label text="Stack Three Text"  gwt:StackPanel-text="Stack Three" />
       </gwt:StackPanel>
       </gwt:HTMLPanel></gwt:Cell>
-      
+
       <gwt:cell><gwt:HTMLPanel>
         <p> ... a TabPanel </p>
-      
+
         <gwt:TabPanel>
           <gwt:Tab text='Able'><gwt:Label>Able Widget</gwt:Label></gwt:Tab>
           <gwt:Tab><gwt:TabHTML><b>B</b>aker</gwt:TabHTML>
@@ -314,12 +314,12 @@
           </gwt:Tab>
         </gwt:TabPanel>
       </gwt:HTMLPanel></gwt:cell>
-      
+
       </gwt:HorizontalPanel>
-      
-      
+
+
       <p> ... a DisclosurePanel with a text header ... </p>
-      
+
       <gwt:DisclosurePanel ui:field='myDisclosurePanel' animationEnabled='true'>
         <gwt:header>
           <ui:msg description="Label for Disclosure One">I just have a text header</ui:msg>
@@ -331,9 +331,9 @@
           </gwt:Label>
 
       </gwt:DisclosurePanel>
-      
+
       <p> ... a DisclosurePanel with a widget header ... </p>
-      
+
       <gwt:DisclosurePanel animationEnabled='true'>
         <gwt:customHeader>
           <gwt:Label>
@@ -391,9 +391,9 @@
       <gwt:DisclosurePanel>
         <gwt:header><ui:msg>"I just have a text header"</ui:msg></gwt:header>
       </gwt:DisclosurePanel>
-      
+
       <p> ... a DisclosurePanel with custom images ... </p>
-      
+
       <gwt:DisclosurePanel open='false' animationEnabled='true'>
         <gwt:header openImage='{down}' closedImage='{right}'>
           <ui:msg description="Content for Disclosure Two Text">Custom images header</ui:msg>
@@ -404,7 +404,7 @@
           </gwt:Label>
 
       </gwt:DisclosurePanel>
-      
+
       <h2>Stylish</h2>
       <p>
        Templates work with ClientBundle. For example,
@@ -422,7 +422,7 @@
         This one is has a private style, defined out in WidgetBaseUi.css and used only by this ui.xml file.
       </p>
       <p ui:field='reallyPrivateStyleParagraph' class='{myOtherStyle.privateColor}'>
-        And this style is defined right here inside the ui.xml file. 
+        And this style is defined right here inside the ui.xml file.
         <span ui:field='totallyPrivateStyleSpan' class='{myTotallyPrivateStyle.super-private-color}'>
           (This one too, but even more so.)
         </span>
@@ -487,8 +487,8 @@
       <p><ui:msg>Of course you'll want to be able to do this kind of thing
       with widgets <demo:MyDatePicker/> as well, whether they <gwt:Label>HasText<ui:ph name="tm">*TM*</ui:ph></gwt:Label>
       or <gwt:HTML>HasHTML<ui:ph name="TM2"><sup ui:field="tmElementJr"
-        class="{external.style.tmText}">TM</sup></ui:ph></gwt:HTML></ui:msg></p> 
-        
+        class="{external.style.tmText}">TM</sup></ui:ph></gwt:HTML></ui:msg></p>
+
       <p><ui:msg>
         You can mix both widgets and named dom fields within a
         <gwt:HTML ui:field='mixedMessageWidget'>single translatable message</gwt:HTML>, another source
@@ -509,12 +509,12 @@
         text="Sometimes people do things they don't need to do. Don't punish them."/></p>
 
       <demo:FooLabel ui:field='theFoo' pojo="{values.pojo}"/>
-      
+
       <demo:FooLabel ui:field='primitiveIntoObject' objectInteger='{values.anInt}'/>
       <demo:FooLabel ui:field='objectIntoPrimitive' rawInt='{values.anIntegerObject}' />
       <demo:FooLabel ui:field='allObject'  objectInteger='{values.anIntegerObject}' />
       <demo:FooLabel ui:field='allPrimitive' rawInt='{values.anInt}' />
-      
+
       <demo:FooLabel ui:field='mismatchPrimitiveIntoObject' objectInteger='{values.aDouble}'/>
       <demo:FooLabel ui:field='allMismatchPrimitive' rawInt='{values.aDouble}' />
 
@@ -522,7 +522,7 @@
       <demo:FooLabel ui:field='objectBooleanIntoPrimitive' rawBoolean='{values.aBooleanObject}' />
       <demo:FooLabel ui:field='allObjectBoolean'  objectBoolean='{values.aBooleanObject}' />
       <demo:FooLabel ui:field='allPrimitiveBoolean' rawBoolean='{values.aBoolean}' />
-      
+
       <demo:EnumeratedLabel ui:field='enumLabel' suffix='tail'>This label uses an enum for its</demo:EnumeratedLabel>
 
       <gwt:PushButton ui:field='pushButton' enabled='true'>
@@ -534,7 +534,7 @@
         <gwt:downDisabledFace image='{prettyImage}'/>
       </gwt:PushButton>
 
-	<gwt:ToggleButton ui:field='toggle'>Hi mom</gwt:ToggleButton>
+      <gwt:ToggleButton ui:field='toggle'>Hi mom</gwt:ToggleButton>
 
     <demo:FooDialog ui:field='fooDialog'>
       <gwt:caption>Custom dialog am I</gwt:caption>
@@ -542,9 +542,9 @@
     </demo:FooDialog>
 
     <gwt:FlowPanel>
-      <gwt:Label ui:field="lblDebugId" debugId="joe" 
+      <gwt:Label ui:field="lblDebugId" debugId="joe"
           styleName="styleName"
-          addStyleNames="newStyle, anotherStyle {external.style.prettyText}" 
+          addStyleNames="newStyle, anotherStyle {external.style.prettyText}"
           addStyleDependentNames="dependentStyle anotherDependentStyle, {external.style.prettyText}">
         A label with a debug id
       </gwt:Label>
@@ -552,6 +552,7 @@
     </gwt:FlowPanel>
 
     <demo:HandlerDemo />
+    <demo:I18nMessageTest />
 
   <h2>People write the darndest markup</h2>
   <table border="1" bcolor="yellow" cellpadding="3" ui:field="widgetCrazyTable">
@@ -587,7 +588,7 @@
     <tr><td>Even HTMLPanel gets in on the game</td></tr>
     <tr><td>Lately, anyway.</td></tr>
   </gwt:HTMLPanel>
-  
+
    <gwt:DialogBox autoHide="true" modal="true">
      <gwt:caption>Hello, I <b>caption</b> you.</gwt:caption>
      <gwt:HTMLPanel>
@@ -596,7 +597,7 @@
        <gwt:Button ui:field='ok'>Okay</gwt:Button>
      </gwt:HTMLPanel>
    </gwt:DialogBox>
-   
+
    <gwt:ListBox ui:field='fooListBox'>
      <gwt:item>bar</gwt:item>
      <gwt:item value='bar2'>bar 2</gwt:item>