Skip to content

Commit

Permalink
Fixes handling of double mustache in UiBinder.
Browse files Browse the repository at this point in the history
In UiBinder, when an attiribute is set to an expression with double mustache
the first curly bracket disappears.
 e.g. {{p in posts}} becomes {p in posts}}
This is related to some undocumented behavior to disable field reference
resoulution but it is pretty bad and  makes it diffucult to integrate UiBinder
with runtime templating systems like Polymer.

This patch introduces a  new escaping option: \{ABC} becomes {ABC} and not
treated as a field reference. In addition, double mustache is also not treated
as field reference nor replaced due escaping so that it will be correctly
preserved in the final output.

Change-Id: I24a202f3165a8ecdca8fb419e9c3197818df87b5
  • Loading branch information
gkdn authored and Gerrit Code Review committed Jan 6, 2015
1 parent 23394f4 commit 7114bf5
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 13 deletions.
Expand Up @@ -42,8 +42,11 @@
* dashes are converted to camel case. That is, {able.baker-charlie} is the same * dashes are converted to camel case. That is, {able.baker-charlie} is the same
* as {able.bakerCharlie} * as {able.bakerCharlie}
* <p> * <p>
* Opening braces may be escape by doubling them. That is, "{{foo}" will * Double mustaches (i.e. "{{..}}") are not matched as references to play well
* converted to "{foo}", with no field reference detected. * with modern templating systems.
* <p>
* Opening braces may be escape by slash. That is, "\{foo}" will converted to
* "{foo}", with no field reference detected.
*/ */
public class FieldReferenceConverter { public class FieldReferenceConverter {
/** /**
Expand Down Expand Up @@ -176,12 +179,17 @@ public String convert(XMLElement source, String in, Delegate delegate) {
Matcher m = BRACES.matcher(in); Matcher m = BRACES.matcher(in);
while (m.find(nextFindStart)) { while (m.find(nextFindStart)) {
String fieldReference = m.group(1); String fieldReference = m.group(1);
if (!legalFirstCharacter(fieldReference)) { int start = m.start();
nextFindStart = m.start() + 2; if (!isLegalPreviousCharacter(in, start)) {
nextFindStart = start + 1;
continue;
}
if (!isLegalFirstCharacter(fieldReference)) {
nextFindStart = start + 2;
continue; continue;
} }


String precedingFragment = in.substring(lastMatchEnd, m.start()); String precedingFragment = in.substring(lastMatchEnd, start);
precedingFragment = handleFragment(precedingFragment, delegate); precedingFragment = handleFragment(precedingFragment, delegate);
b.append(precedingFragment); b.append(precedingFragment);


Expand Down Expand Up @@ -220,11 +228,19 @@ private String expandDots(String value) {
} }


private String handleFragment(String fragment, Delegate delegate) { private String handleFragment(String fragment, Delegate delegate) {
fragment = fragment.replace("{{", "{"); fragment = fragment.replace("\\{", "{");
return delegate.handleFragment(fragment); return delegate.handleFragment(fragment);
} }


private boolean legalFirstCharacter(String fieldReference) { private boolean isLegalFirstCharacter(String fieldReference) {
return LEGAL_FIRST_CHAR.matcher(fieldReference).matches(); return LEGAL_FIRST_CHAR.matcher(fieldReference).matches();
} }

private boolean isLegalPreviousCharacter(String in, int start) {
if (start < 1) {
return true;
}
char previousChar = in.charAt(start - 1);
return previousChar != '{' && previousChar != '\\';
}
} }
Expand Up @@ -71,7 +71,7 @@ public String interpretElement(XMLElement elem)
* TODO(rjrjr) Move this to XMLElement RSN * TODO(rjrjr) Move this to XMLElement RSN
*/ */
String n = att.getName(); String n = att.getName();
String v = att.consumeRawValue().replace("{{", "{"); String v = att.consumeRawValue().replace("\\{", "{");
elem.setAttribute(n, v); elem.setAttribute(n, v);
} }
} }
Expand Down
Expand Up @@ -60,7 +60,7 @@ public void testOne() {


assertEquals(expected, converter.convert(before, frDelegate)); assertEquals(expected, converter.convert(before, frDelegate));
} }

public void testReplaceSimple() { public void testReplaceSimple() {
String before = "able {baker} charlie"; String before = "able {baker} charlie";
String expected = "*able * & baker & * charlie*"; String expected = "*able * & baker & * charlie*";
Expand All @@ -83,7 +83,7 @@ public void testReplaceSeveral() {
} }


public void testEscaping() { public void testEscaping() {
String before = "Well {{Hi mom}!"; String before = "Well \\{Hi mom}!";
String expected = "*Well {Hi mom}!*"; String expected = "*Well {Hi mom}!*";


assertEquals(expected, converter.convert(before, frDelegate)); assertEquals(expected, converter.convert(before, frDelegate));
Expand All @@ -96,6 +96,13 @@ public void testIgnoreEmpty() {
assertEquals(expected, converter.convert(before, frDelegate)); assertEquals(expected, converter.convert(before, frDelegate));
} }


public void testIgnoreMustache() {
String before = "{{abc.dfc in klm}}";
String expected = "*{{abc.dfc in klm}}*";

assertEquals(expected, converter.convert(before, frDelegate));
}

public void testIgnoreNonIdentifierFirstChar() { public void testIgnoreNonIdentifierFirstChar() {
String before = "Hi { } mom, how { are } {1you}?"; String before = "Hi { } mom, how { are } {1you}?";
String expected = "*Hi { } mom, how { are } {1you}?*"; String expected = "*Hi { } mom, how { are } {1you}?*";
Expand All @@ -105,6 +112,8 @@ public void testIgnoreNonIdentifierFirstChar() {


public void testHasFieldReferences() { public void testHasFieldReferences() {
assertTrue(FieldReferenceConverter.hasFieldReferences("{able} {baker}")); assertTrue(FieldReferenceConverter.hasFieldReferences("{able} {baker}"));
assertFalse(FieldReferenceConverter.hasFieldReferences("{{able}}"));
assertFalse(FieldReferenceConverter.hasFieldReferences("{{able} baker { Charlie }")); assertFalse(FieldReferenceConverter.hasFieldReferences("{{able} baker { Charlie }"));
assertFalse(FieldReferenceConverter.hasFieldReferences("\\{able} baker { Charlie }"));
} }
} }
Expand Up @@ -165,6 +165,7 @@ public void testAllowIdOnDomElements() {
public void testBraceEscaping() { public void testBraceEscaping() {
assertEquals("blah di blah {foo: \"bar\"} di blah", assertEquals("blah di blah {foo: \"bar\"} di blah",
widgetUi.bracedParagraph.getAttribute("fnord")); widgetUi.bracedParagraph.getAttribute("fnord"));
assertEquals("{{blah in blah}}", widgetUi.bracedParagraph.getAttribute("doubleMustache"));
} }


public void testBundle() { public void testBundle() {
Expand Down
Expand Up @@ -201,9 +201,9 @@
</gwt:Dock> </gwt:Dock>
<gwt:Dock direction='CENTER'> <gwt:Dock direction='CENTER'>
<gwt:HTMLPanel> <gwt:HTMLPanel>
<p ui:field='bracedParagraph' fnord='blah di blah {{foo: "bar"} di blah' <p ui:field='bracedParagraph' fnord='blah di blah \{foo: "bar"} di blah'
foo='{CONST_FOO}' bar='{CONST_BAR} {CONST_BAZ}' foo='{CONST_FOO}' bar='{CONST_BAR} {CONST_BAZ}' enum='{ENUM_1.name} {ENUM_2.name}'
enum='{ENUM_1.name} {ENUM_2.name}' ><ui:msg>This is a doubleMustache="{{blah in blah}}" > <ui:msg>This is a
demonstration and test bed of GWT's shiny UiBinder demonstration and test bed of GWT's shiny UiBinder
package. At the moment it works mainly as described in package. At the moment it works mainly as described in
<a href="http://code.google.com/p/google-web-toolkit-incubator/wiki/DeclarativeUi" <a href="http://code.google.com/p/google-web-toolkit-incubator/wiki/DeclarativeUi"
Expand Down

0 comments on commit 7114bf5

Please sign in to comment.