Pretty massive refactoring of FieldManager and HtmlTemplates to make
SafeUri work as expected. Similar will be needed for SafeStyles.
Introduces a hook in HtmlInterpreter to specialize parsing of specific
HTML attribute names, which previously were all String, period. And
each assignment of a FieldRef (that is, each call to
FieldReference#addLeftHandType) can now have multiple possible
values. This allows href to accept references to SafeUri or String,
and to prefer the former.
Took this opportunity to improve error reporting related to bad field
references: actual line numbers on type mismatch. Tidied error message
language in general while there. To this end, FieldReferences and
their left hand types now keep track of the XMLElement they came from.
Also simplifies the parsing code in XMLElement, possible now that
bundle attribute parsers are no more.
Review at http://gwt-code-reviews.appspot.com/1522803
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10548 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/AttributeParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/AttributeParser.java
index fc552a4..9dc5970 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/AttributeParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/AttributeParser.java
@@ -16,20 +16,22 @@
package com.google.gwt.uibinder.attributeparsers;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.uibinder.rebind.XMLElement;
/**
* Attribute parsers are classes that parse xml attribute values, turning them
* into valid Java expressions.
*/
public interface AttributeParser {
-
+
/**
* Parse the given attribute value.
- *
+ * @param source the source code the value came from, for error reporting purposes
* @param value the attribute value to be parsed
+ *
* @return a valid Java expression
* @throws UnableToCompleteException on parse error
*/
- String parse(String value)
+ String parse(XMLElement source, String value)
throws UnableToCompleteException;
}
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/AttributeParsers.java b/user/src/com/google/gwt/uibinder/attributeparsers/AttributeParsers.java
index fceb458..c4efefd 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/AttributeParsers.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/AttributeParsers.java
@@ -20,11 +20,11 @@
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.core.ext.typeinfo.TypeOracleException;
import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.uibinder.rebind.FieldManager;
import com.google.gwt.uibinder.rebind.MortalLogger;
import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
import com.google.gwt.user.client.ui.HasVerticalAlignment.VerticalAlignmentConstant;
-import com.google.gwt.user.client.ui.TextBoxBase.TextAlignConstant;
import java.util.HashMap;
import java.util.Map;
@@ -35,21 +35,24 @@
public class AttributeParsers {
private static final String HORIZ_CONSTANT = HorizontalAlignmentConstant.class.getCanonicalName();
private static final String VERT_CONSTANT = VerticalAlignmentConstant.class.getCanonicalName();
- private static final String TEXT_ALIGN_CONSTANT = TextAlignConstant.class.getCanonicalName();
+ @SuppressWarnings("deprecation")
+ private static final String TEXT_ALIGN_CONSTANT =
+ com.google.gwt.user.client.ui.TextBoxBase.TextAlignConstant.class.getCanonicalName();
private static final String INT = "int";
private static final String STRING = String.class.getCanonicalName();
private static final String DOUBLE = "double";
private static final String BOOLEAN = "boolean";
private static final String UNIT = Unit.class.getCanonicalName();
+ private static final String SAFE_URI = SafeUri.class.getCanonicalName();
private final MortalLogger logger;
private final FieldReferenceConverter converter;
-
+
/**
- * Class names of parsers for values of attributes with no namespace prefix,
- * keyed by method parameter signatures.
+ * Class names of parsers keyed by method parameter signatures.
*/
private final Map<String, AttributeParser> parsers = new HashMap<String, AttributeParser>();
+ private final SafeUriAttributeParser safeUriInHtmlParser;
public AttributeParsers(TypeOracle types, FieldManager fieldManager,
MortalLogger logger) {
@@ -82,19 +85,30 @@
addAttributeParser(TEXT_ALIGN_CONSTANT, new TextAlignConstantParser(
converter, types.parse(TEXT_ALIGN_CONSTANT), logger));
- addAttributeParser(STRING,
- new StringAttributeParser(converter, types.parse(STRING)));
+ StringAttributeParser stringParser = new StringAttributeParser(converter, types.parse(STRING));
+ addAttributeParser(STRING, stringParser);
EnumAttributeParser unitParser = new EnumAttributeParser(converter,
(JEnumType) types.parse(UNIT), logger);
addAttributeParser(DOUBLE + "," + UNIT, new LengthAttributeParser(
doubleParser, unitParser, logger));
+
+ SafeUriAttributeParser uriParser = new SafeUriAttributeParser(stringParser,
+ converter, types.parse(SAFE_URI), logger);
+ addAttributeParser(SAFE_URI, uriParser);
+
+ safeUriInHtmlParser = new SafeUriAttributeParser(stringParser,
+ converter, types.parse(SAFE_URI), types.parse(STRING), logger);
} catch (TypeOracleException e) {
throw new RuntimeException(e);
}
}
- public AttributeParser get(JType... types) {
+ /**
+ * Returns a parser for the given type(s). Accepts multiple types args to
+ * allow requesting parsers for things like for pairs of ints.
+ */
+ public AttributeParser getParser(JType... types) {
if (types.length == 0) {
throw new RuntimeException("Asked for attribute parser of no type");
}
@@ -114,9 +128,17 @@
* Dunno what it is, so let a StrictAttributeParser look for a
* {field.reference}
*/
- return new StrictAttributeParser(converter, types[0], logger);
+ return new StrictAttributeParser(converter, logger, types[0]);
}
-
+
+ /**
+ * Returns a parser specialized for handling URI references
+ * in html contexts, like <a href="{foo.bar}">.
+ */
+ public AttributeParser getSafeUriInHtmlParser() {
+ return safeUriInHtmlParser;
+ }
+
private void addAttributeParser(String signature,
AttributeParser attributeParser) {
parsers.put(signature, attributeParser);
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/BooleanAttributeParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/BooleanAttributeParser.java
index 769b26e..faf2141 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/BooleanAttributeParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/BooleanAttributeParser.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
/**
* Parses a single boolean attribute.
@@ -26,15 +27,15 @@
BooleanAttributeParser(FieldReferenceConverter converter,
JType booleanType, MortalLogger logger) {
- super(converter, booleanType, logger);
+ super(converter, logger, booleanType);
}
@Override
- public String parse(String value) throws UnableToCompleteException {
+ public String parse(XMLElement source, String value) throws UnableToCompleteException {
if (value.equals("true") || value.equals("false")) {
return value;
}
- return super.parse(value);
+ return super.parse(source, value);
}
}
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/DoubleAttributeParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/DoubleAttributeParser.java
index 50128e8..c2bcb15 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/DoubleAttributeParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/DoubleAttributeParser.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
/**
* Parses a single double attribute.
@@ -26,11 +27,11 @@
DoubleAttributeParser(FieldReferenceConverter converter,
JType doubleType, MortalLogger logger) {
- super(converter, doubleType, logger);
+ super(converter, logger, doubleType);
}
@Override
- public String parse(String value) throws UnableToCompleteException {
+ public String parse(XMLElement source, String value) throws UnableToCompleteException {
try {
Double.parseDouble(value);
// Happy double
@@ -38,7 +39,7 @@
} catch (NumberFormatException e) {
// Not a double, maybe super sees a field ref
}
- String fieldMaybe = super.parse(value);
+ String fieldMaybe = super.parse(source, value);
if ("".equals(fieldMaybe)) {
return "";
}
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/EnumAttributeParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/EnumAttributeParser.java
index e08edfd..753e1af 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/EnumAttributeParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/EnumAttributeParser.java
@@ -19,6 +19,7 @@
import com.google.gwt.core.ext.typeinfo.JEnumConstant;
import com.google.gwt.core.ext.typeinfo.JEnumType;
import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
import java.util.HashMap;
import java.util.Map;
@@ -31,7 +32,7 @@
EnumAttributeParser(FieldReferenceConverter converter, JEnumType enumType,
MortalLogger logger) {
- super(converter, enumType, logger);
+ super(converter, logger, enumType);
JEnumConstant[] constants = enumType.getEnumConstants();
for (JEnumConstant c : constants) {
values.put(c.getName(), c);
@@ -39,12 +40,12 @@
}
@Override
- public String parse(String value) throws UnableToCompleteException {
+ public String parse(XMLElement source, String value) throws UnableToCompleteException {
JEnumConstant c = values.get(value);
if (c != null) {
return String.format("%s.%s",
c.getEnclosingType().getQualifiedSourceName(), value);
}
- return super.parse(value);
+ return super.parse(source, value);
}
}
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverter.java b/user/src/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverter.java
index 7da4e8d..abb0168 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverter.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverter.java
@@ -1,12 +1,12 @@
/*
* Copyright 2009 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
@@ -17,6 +17,7 @@
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.uibinder.rebind.FieldManager;
+import com.google.gwt.uibinder.rebind.XMLElement;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -25,7 +26,8 @@
* Deals with field references, e.g. the bits in braces here: <code><div
* class="{style.enabled} fancy {style.impressive}" /></code>, by converting
* them to java expressions (with the help of a
- * {@link com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter.Delegate Delegate}).
+ * {@link com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter.Delegate
+ * Delegate}).
* <p>
* A field reference is one or more segments separated by dots. The first
* segment is considered to be a reference to a ui field, and succeeding
@@ -46,8 +48,8 @@
public class FieldReferenceConverter {
/**
* May be thrown by the
- * {@link com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter.Delegate Delegate}
- * for badly formatted input.
+ * {@link com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter.Delegate
+ * Delegate} for badly formatted input.
*/
@SuppressWarnings("serial")
public static class IllegalFieldReferenceException extends RuntimeException {
@@ -59,9 +61,11 @@
*/
interface Delegate {
/**
- * Returns the type any parsed field references are expected to return.
+ * Returns the types any parsed field references are expected to return.
+ * Multiple values indicates an overload. E.g., in <a href={...}> either a
+ * String or a SafeUri is allowed.
*/
- JType getType();
+ JType[] getTypes();
/**
* Called for fragment around and between field references.
@@ -73,27 +77,29 @@
* A string with no field references is treated as a single fragment, and
* causes a single call to this method.
*/
- String handleFragment(String fragment)
- throws IllegalFieldReferenceException;
+ String handleFragment(String fragment) throws IllegalFieldReferenceException;
/**
* Called for each expanded field reference, to allow it to be stitched
* together with surrounding fragments.
*/
- String handleReference(String reference)
- throws IllegalFieldReferenceException;
+ String handleReference(String reference) throws IllegalFieldReferenceException;
}
/**
- * Used by {@link #hasFieldReferences}. Passthrough implementation that notes
- * when handleReference has been called.
+ * Used by {@link FieldReferenceConverter#countFieldReferences}. Passthrough
+ * implementation that counts the number of calls handleReference has been called,
+ * so that we know how many field references a given string contains.
*/
- private static final class Telltale implements
- FieldReferenceConverter.Delegate {
- boolean hasComputed = false;
+ private static final class Telltale implements FieldReferenceConverter.Delegate {
+ private int computedCount;
- public JType getType() {
- return null;
+ public int getComputedCount() {
+ return computedCount;
+ }
+
+ public JType[] getTypes() {
+ return new JType[0];
}
public String handleFragment(String fragment) {
@@ -101,25 +107,45 @@
}
public String handleReference(String reference) {
- hasComputed = true;
+ computedCount++;
return reference;
}
-
- public boolean hasComputed() {
- return hasComputed;
- }
}
private static final Pattern BRACES = Pattern.compile("[{]([^}]*)[}]");
private static final Pattern LEGAL_FIRST_CHAR = Pattern.compile("^[$_a-zA-Z].*");
+ private static final String DOTS_AND_PARENS = "[().]";
+
+ /**
+ * Returns the number of field references in the given string.
+ */
+ public static int countFieldReferences(String string) {
+ Telltale telltale = new Telltale();
+ new FieldReferenceConverter(null).convert(null, string, telltale);
+ return telltale.getComputedCount();
+ }
+
+ /**
+ * Reverses most of the work of {@link #convert}, turning a java expresion
+ * back into a dotted path.
+ */
+ public static String expressionToPath(String expression) {
+ String[] chunks = expression.split(DOTS_AND_PARENS);
+ StringBuilder b = new StringBuilder();
+ for (String chunk : chunks) {
+ if (b.length() > 0 && chunk.length() > 0) {
+ b.append(".");
+ }
+ b.append(chunk);
+ }
+ return b.toString();
+ }
/**
* Returns true if the given string holds one or more field references.
*/
public static boolean hasFieldReferences(String string) {
- Telltale telltale = new Telltale();
- new FieldReferenceConverter(null).convert(string, telltale);
- return telltale.hasComputed();
+ return countFieldReferences(string) > 0;
}
private final CssNameConverter cssConverter = new CssNameConverter();
@@ -136,6 +162,13 @@
* @throws IllegalFieldReferenceException if the delegate does
*/
public String convert(String in, Delegate delegate) {
+ return convert(null, in, delegate);
+ }
+
+ /**
+ * @throws IllegalFieldReferenceException if the delegate does
+ */
+ public String convert(XMLElement source, String in, Delegate delegate) {
StringBuilder b = new StringBuilder();
int nextFindStart = 0;
int lastMatchEnd = 0;
@@ -153,7 +186,7 @@
b.append(precedingFragment);
if (fieldManager != null) {
- fieldManager.registerFieldReference(fieldReference, delegate.getType());
+ fieldManager.registerFieldReference(source, fieldReference, delegate.getTypes());
}
fieldReference = expandDots(fieldReference);
b.append(delegate.handleReference(fieldReference));
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParser.java
index 71abfd2..44f4feb 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParser.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import java.util.HashMap;
@@ -46,15 +47,15 @@
HorizontalAlignmentConstantParser(FieldReferenceConverter converter,
JType type, MortalLogger logger) {
- super(converter, type, logger);
+ super(converter, logger, type);
}
@Override
- public String parse(String value) throws UnableToCompleteException {
+ public String parse(XMLElement source, String value) throws UnableToCompleteException {
String translated = values.get(value.toUpperCase());
if (translated != null) {
return translated;
}
- return super.parse(value);
+ return super.parse(source, value);
}
}
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/IntAttributeParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/IntAttributeParser.java
index 2c2d321..4970d66 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/IntAttributeParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/IntAttributeParser.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
/**
* Parses an integer value.
@@ -26,11 +27,11 @@
IntAttributeParser(FieldReferenceConverter converter, JType intType,
MortalLogger logger) {
- super(converter, intType, logger);
+ super(converter, logger, intType);
}
@Override
- public String parse(String value) throws UnableToCompleteException {
+ public String parse(XMLElement source, String value) throws UnableToCompleteException {
try {
Integer.parseInt(value);
// Yup, it's an int, use it as such.
@@ -38,7 +39,7 @@
} catch (NumberFormatException e) {
// Not an int, let super see if it's a field ref
}
- String fieldMaybe = super.parse(value);
+ String fieldMaybe = super.parse(source, value);
if ("".equals(fieldMaybe)) {
return "";
}
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/IntPairAttributeParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/IntPairAttributeParser.java
index 1bb1238..6381b76 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/IntPairAttributeParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/IntPairAttributeParser.java
@@ -17,6 +17,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
/**
* Parses a pair of integer values.
@@ -31,18 +32,14 @@
this.logger = logger;
}
- public String parse(String value) throws UnableToCompleteException {
+ public String parse(XMLElement source, String value) throws UnableToCompleteException {
String[] values = value.split(",");
if (values.length != 2) {
- die(value);
+ logger.die(source, "Unable to parse \"%s\" as a pair of integers", value);
}
- String left = intParser.parse(values[0].trim());
- String right = intParser.parse(values[1].trim());
+ String left = intParser.parse(source, values[0].trim());
+ String right = intParser.parse(source, values[1].trim());
return String.format("%s, %s", left, right);
}
-
- private void die(String value) throws UnableToCompleteException {
- logger.die("Unable to parse \"%s\" as a pair of integers", value);
- }
}
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/LengthAttributeParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/LengthAttributeParser.java
index 3d4bae7..af83389 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/LengthAttributeParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/LengthAttributeParser.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -46,14 +47,14 @@
this.logger = logger;
}
- public String parse(String lengthStr) throws UnableToCompleteException {
+ public String parse(XMLElement source, String lengthStr) throws UnableToCompleteException {
Matcher matcher = pattern.matcher(lengthStr);
if (!matcher.matches()) {
- logger.die("Unable to parse %s as length", lengthStr);
+ logger.die(source, "Unable to parse %s as length", lengthStr);
}
String valueStr = matcher.group(1);
- String value = doubleParser.parse(valueStr);
+ String value = doubleParser.parse(source, valueStr);
String unit = null;
String unitStr = matcher.group(2);
@@ -67,7 +68,7 @@
}
// Now let the default enum parser handle it.
- unit = enumParser.parse(unitStr);
+ unit = enumParser.parse(source, unitStr);
} else {
// Use PX by default.
unit = UNIT + ".PX";
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/SafeUriAttributeParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/SafeUriAttributeParser.java
new file mode 100644
index 0000000..af95c6a
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/SafeUriAttributeParser.java
@@ -0,0 +1,97 @@
+/*
+ * 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.attributeparsers;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
+
+/**
+ * Parses {@link com.google.gwt.safehtml.shared.SafeUri SafeUri} literals or
+ * references.
+ * <p>
+ * Simple String literals are passed through
+ * {@link com.google.gwt.safehtml.shared.UriUtils#fromConstantString(String)
+ * UriUtils.fromConstantString(String)}
+ * <p>
+ * Accepts concatenated string expressions, mainly for compatibility with legacy
+ * <code><a href="{foo.bar}{baz.bang}"></code> abuses. Passes such nonsense
+ * through {@link com.google.gwt.safehtml.shared.UriUtils#fromString(String)
+ * UriUtils.fromString(String)}
+ */
+public class SafeUriAttributeParser extends StrictAttributeParser {
+ public static String wrapUnsafeStringAndWarn(MortalLogger logger, XMLElement source,
+ String expression) {
+ logger.warn(source, "Escaping unsafe runtime String expression "
+ + "used for URI with UriUtils.fromString(). Use SafeUri instead");
+ return "UriUtils.fromString(" + expression + ")";
+ }
+
+ private final boolean runtimeStringsAllowed;
+ private final StringAttributeParser stringParser;
+
+ /**
+ * Constructs an instance for particular use in html contexts, where
+ * {string.references} are acceptible.
+ */
+ SafeUriAttributeParser(StringAttributeParser stringParser, FieldReferenceConverter converter,
+ JType safeUriType, JType stringType, MortalLogger logger) {
+ super(converter, logger, safeUriType, stringType);
+ this.stringParser = stringParser;
+ runtimeStringsAllowed = true;
+ }
+
+ /**
+ * Constructs an instance for normal use, where String literals are okay but
+ * {string.references} are not.
+ */
+ SafeUriAttributeParser(StringAttributeParser stringParser, FieldReferenceConverter converter,
+ JType safeUriType, MortalLogger logger) {
+ super(converter, logger, safeUriType);
+ this.stringParser = stringParser;
+ runtimeStringsAllowed = false;
+ }
+
+ @Override
+ public String parse(XMLElement source, String value) throws UnableToCompleteException {
+ int howManyFieldRefs = FieldReferenceConverter.countFieldReferences(value);
+
+ /*
+ * No field refs, just a string literal.
+ *
+ * Isn't this a lot of pointless codegen for <a href="#whatever"/>? No. In
+ * such an html context this parser is called only for computed attributes,
+ * where howManyFieldRefs is > 0. See
+ * HtmlInterpreter#ComputedAttributeTypist
+ */
+ if (howManyFieldRefs == 0) {
+ // String literal, convert it for the nice people
+ return "UriUtils.fromSafeConstant(" + stringParser.parse(source, value) + ")";
+ }
+
+ /*
+ * No runtime string expressions are allowed, or they are but it
+ * "{looks.like.this}". Just do the usual parsing.
+ */
+ if (!runtimeStringsAllowed || howManyFieldRefs == 1 && value.substring(0, 1).equals("{")
+ && value.substring(value.length() - 1, value.length()).equals("}")) {
+ return super.parse(source, value);
+ }
+
+ return wrapUnsafeStringAndWarn(logger, source, stringParser.parse(source, value));
+ }
+}
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/StrictAttributeParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/StrictAttributeParser.java
index 2d2cd97..731befc 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/StrictAttributeParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/StrictAttributeParser.java
@@ -19,7 +19,9 @@
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter.Delegate;
import com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter.IllegalFieldReferenceException;
+import com.google.gwt.uibinder.rebind.FieldReference;
import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
/**
* Fall through attribute parser. Accepts a field reference or nothing.
@@ -31,14 +33,14 @@
*/
static class FieldReferenceDelegate implements Delegate {
private boolean sawReference = false;
- private final JType type;
+ private final JType[] types;
- FieldReferenceDelegate(JType type) {
- this.type = type;
+ FieldReferenceDelegate(JType... types) {
+ this.types = types;
}
- public JType getType() {
- return type;
+ public JType[] getTypes() {
+ return types;
}
public String handleFragment(String fragment)
@@ -65,13 +67,13 @@
private final FieldReferenceConverter converter;
protected final MortalLogger logger;
- private final JType type;
+ private final JType[] types;
- StrictAttributeParser(FieldReferenceConverter converter, JType type,
- MortalLogger logger) {
+ StrictAttributeParser(FieldReferenceConverter converter, MortalLogger logger,
+ JType... types) {
this.converter = converter;
- this.type = type;
this.logger = logger;
+ this.types = types;
}
/**
@@ -81,14 +83,14 @@
* In any other case (e.g. more than one field reference), an
* UnableToCompleteException is thrown.
*/
- public String parse(String value) throws UnableToCompleteException {
+ public String parse(XMLElement source, String value) throws UnableToCompleteException {
if ("".equals(value.trim())) {
- logger.die("Cannot use empty value as type %s", type.getSimpleSourceName());
+ logger.die(source, "Cannot use empty value as type %s", FieldReference.renderTypesList(types));
}
try {
- return converter.convert(value, new FieldReferenceDelegate(type));
+ return converter.convert(source, value, new FieldReferenceDelegate(types));
} catch (IllegalFieldReferenceException e) {
- logger.die("Cannot parse value: \"%s\" as type %s", value, type.getSimpleSourceName());
+ logger.die(source, "Cannot parse value: \"%s\" as type %s", value, FieldReference.renderTypesList(types));
return null; // Unreachable
}
}
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/StringAttributeParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/StringAttributeParser.java
index 3732176..92f9ca2 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/StringAttributeParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/StringAttributeParser.java
@@ -17,6 +17,7 @@
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.uibinder.rebind.UiBinderWriter;
+import com.google.gwt.uibinder.rebind.XMLElement;
/**
* Parses a string attribute.
@@ -25,14 +26,14 @@
/* package private for testing */
static class FieldReferenceDelegate implements
FieldReferenceConverter.Delegate {
- private final JType type;
+ private final JType[] types;
FieldReferenceDelegate(JType type) {
- this.type = type;
+ this.types = new JType[] { type };
}
- public JType getType() {
- return type;
+ public JType[] getTypes() {
+ return types;
}
public String handleFragment(String literal) {
@@ -46,15 +47,15 @@
}
private final FieldReferenceConverter converter;
- private final JType type;
+ private final JType stringType;
StringAttributeParser(FieldReferenceConverter converter,
JType stringType) {
this.converter = converter;
- this.type = stringType;
+ this.stringType = stringType;
}
- public String parse(String value) {
- return converter.convert(value, new FieldReferenceDelegate(type));
+ public String parse(XMLElement source, String value) {
+ return converter.convert(source, value, new FieldReferenceDelegate(stringType));
}
}
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/TextAlignConstantParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/TextAlignConstantParser.java
index 735ef74..bafd0fc 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/TextAlignConstantParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/TextAlignConstantParser.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
import com.google.gwt.user.client.ui.TextBoxBase;
import java.util.HashMap;
@@ -44,15 +45,15 @@
TextAlignConstantParser(FieldReferenceConverter converter, JType type,
MortalLogger logger) {
- super(converter, type, logger);
+ super(converter, logger, type);
}
@Override
- public String parse(String value) throws UnableToCompleteException {
+ public String parse(XMLElement source, String value) throws UnableToCompleteException {
String translated = values.get(value.toUpperCase());
if (translated != null) {
return translated;
}
- return super.parse(value);
+ return super.parse(source, value);
}
}
diff --git a/user/src/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParser.java b/user/src/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParser.java
index 66c3a21..b7c6552 100644
--- a/user/src/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParser.java
+++ b/user/src/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParser.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.XMLElement;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import java.util.HashMap;
@@ -44,15 +45,15 @@
VerticalAlignmentConstantParser(FieldReferenceConverter converter,
JType type, MortalLogger logger) {
- super(converter, type, logger);
+ super(converter, logger, type);
}
@Override
- public String parse(String value) throws UnableToCompleteException {
+ public String parse(XMLElement source, String value) throws UnableToCompleteException {
String translated = values.get(value.toUpperCase());
if (translated != null) {
return translated;
}
- return super.parse(value);
+ return super.parse(source, value);
}
}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/AttributeMessageInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/AttributeMessageInterpreter.java
index 9925615..a2e205c 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/AttributeMessageInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/AttributeMessageInterpreter.java
@@ -55,7 +55,7 @@
message += ".replaceAll(\"&\", \"&\").replaceAll(\"'\", \"'\")";
}
elem.setAttribute(am.getAttribute(),
- writer.tokenForStringExpression(message));
+ writer.tokenForStringExpression(elem, message));
}
/*
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/ComputedAttributeInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/ComputedAttributeInterpreter.java
index c8592e2..64b9f1d 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/ComputedAttributeInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/ComputedAttributeInterpreter.java
@@ -37,8 +37,8 @@
class DefaultDelegate implements Delegate {
public String getAttributeToken(XMLAttribute attribute)
- throws UnableToCompleteException {
- return writer.tokenForStringExpression(attribute.consumeStringValue());
+ throws UnableToCompleteException {
+ return writer.tokenForStringExpression(attribute.getElement(), attribute.consumeStringValue());
}
}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java
index 24d55c2..93d8d80 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/FieldInterpreter.java
@@ -37,7 +37,7 @@
throws UnableToCompleteException {
String fieldName = writer.declareFieldIfNeeded(elem);
if (fieldName != null) {
- String token = writer.declareDomField(fieldName, element);
+ String token = writer.declareDomField(elem, fieldName, element);
if (elem.hasAttribute("id")) {
writer.die(elem, String.format(
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/HtmlInterpreter.java
index 88ca481..be8874d 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
@@ -18,9 +18,15 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.uibinder.rebind.UiBinderWriter;
+import com.google.gwt.uibinder.rebind.XMLAttribute;
import com.google.gwt.uibinder.rebind.XMLElement;
import com.google.gwt.uibinder.rebind.XMLElement.Interpreter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* This is the most generally useful interpreter, and the most likely to be used
* by a custom parser when calling {@link XMLElement#consumeInnerHtml}.
@@ -35,23 +41,53 @@
* </ul>
*/
public class HtmlInterpreter implements XMLElement.Interpreter<String> {
+ private class AttributeTypist implements ComputedAttributeInterpreter.Delegate {
+ @Override
+ public String getAttributeToken(XMLAttribute attribute) throws UnableToCompleteException {
+ if (URI_ATTRIBUTES.contains(attribute.getLocalName())) {
+ return writer.tokenForSafeUriExpression(attribute.getElement(),
+ attribute.consumeSafeUriOrStringAttribute());
+ }
+
+ return writer.tokenForStringExpression(attribute.getElement(), attribute.consumeStringValue());
+ }
+ }
+
+ private static final Set<String> URI_ATTRIBUTES;
+ static {
+
+ /*
+ * Perhaps we "should" check the element names as well, but the spec implies
+ * that an attribute name always has the same meaning wherever it appears.
+ * It's less work this way, and we're more likely to catch new elements.
+ *
+ * http://dev.w3.org/html5/spec/index.html#attributes-1
+ * http://www.w3.org/TR/REC-html40/index/attributes.html
+ */
+ String[] urlAttributes =
+ {
+ "action", "background", "classid", "cite", "codebase", "data", "formaction", "href",
+ "icon", "longdesc", "manifest", "profile", "poster", "src", "usemap"};
+
+ URI_ATTRIBUTES = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(urlAttributes)));
+ }
+
/**
* A convenience factory method for the most common use of this class, to work
* with HTML that will eventually be rendered under a
* {@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.
*/
- public static HtmlInterpreter newInterpreterForUiObject(
- UiBinderWriter writer, String uiExpression) {
- String ancestorExpression = writer.useLazyWidgetBuilders()
- ? uiExpression : uiExpression + ".getElement()";
- return new HtmlInterpreter(writer, ancestorExpression,
- new HtmlMessageInterpreter(writer, ancestorExpression));
+ public static HtmlInterpreter newInterpreterForUiObject(UiBinderWriter writer, String uiExpression) {
+ String ancestorExpression =
+ writer.useLazyWidgetBuilders() ? uiExpression : uiExpression + ".getElement()";
+ return new HtmlInterpreter(writer, ancestorExpression, new HtmlMessageInterpreter(writer,
+ ancestorExpression));
}
private final UiBinderWriter writer;
@@ -60,7 +96,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.
@@ -71,29 +107,30 @@
public HtmlInterpreter(UiBinderWriter writer, String ancestorExpression,
Interpreter<String> messageInterpreter) {
this.writer = writer;
+
this.pipe = new InterpreterPipe<String>();
pipe.add(new FieldInterpreter(writer, ancestorExpression));
- /* UiTextInterpreter and UiSafeHtmlInterpreter must be invoked before
+ /*
+ * UiTextInterpreter and UiSafeHtmlInterpreter must be invoked before
* ComputedAttributeInterpreter to function properly
*/
pipe.add(new UiTextInterpreter(writer));
pipe.add(new UiSafeHtmlInterpreter(writer));
- pipe.add(new ComputedAttributeInterpreter(writer));
+ pipe.add(new ComputedAttributeInterpreter(writer, new AttributeTypist()));
pipe.add(new AttributeMessageInterpreter(writer));
pipe.add(messageInterpreter);
}
- public String interpretElement(XMLElement elem)
- throws UnableToCompleteException {
- if (writer.useLazyWidgetBuilders() &&
- writer.isElementAssignableTo(elem, SafeHtml.class)) {
+ public String interpretElement(XMLElement elem) throws UnableToCompleteException {
+ if (writer.useLazyWidgetBuilders() && writer.isElementAssignableTo(elem, SafeHtml.class)) {
String childFieldName = writer.parseElementToField(elem);
- return writer.tokenForSafeHtmlExpression(childFieldName);
+ return writer.tokenForSafeHtmlExpression(elem, childFieldName);
}
if (writer.isImportedElement(elem)) {
writer.die(elem, "Not allowed in an HTML context");
}
+
return pipe.interpretElement(elem);
}
}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/HtmlMessageInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/HtmlMessageInterpreter.java
index 8aa9371..9a085c7 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/HtmlMessageInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/HtmlMessageInterpreter.java
@@ -74,7 +74,7 @@
MessageWriter message = messages.newMessage(elem);
message.setDefaultMessage(elem.consumeInnerHtml(phiProvider.get(message)));
- return uiWriter.tokenForSafeConstant(messages.declareMessage(message));
+ return uiWriter.tokenForSafeConstant(elem, messages.declareMessage(message));
}
return null;
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/IsRenderableInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/IsRenderableInterpreter.java
index c9855ae..05ff6fc 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/IsRenderableInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/IsRenderableInterpreter.java
@@ -68,6 +68,6 @@
// TODO(rdcastro): use the render() call that receives the SafeHtmlBuilder
String elementHtml = fieldManager.convertFieldToGetter(childFieldWriter.getName()) + ".render("
+ fieldManager.convertFieldToGetter(stamper) + ")";
- return uiWriter.tokenForSafeHtmlExpression(elementHtml);
+ return uiWriter.tokenForSafeHtmlExpression(elem, elementHtml);
}
}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/RenderablePanelParser.java b/user/src/com/google/gwt/uibinder/elementparsers/RenderablePanelParser.java
index 4b1b258..7292162 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/RenderablePanelParser.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/RenderablePanelParser.java
@@ -81,7 +81,7 @@
// TODO(rdcastro): Add support for custom tags in RenderablePanel.
if (customTag != null) {
- writer.getLogger().die(
+ writer.getLogger().die(elem,
"RenderablePanel does not support custom root elements yet.");
}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/TextInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/TextInterpreter.java
index 0cc7d23..d480f81 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/TextInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/TextInterpreter.java
@@ -43,7 +43,7 @@
MessagesWriter messages = writer.getMessages();
if (messages.isMessage(elem)) {
String messageInvocation = consumeAsTextMessage(elem, messages);
- return writer.tokenForStringExpression(messageInvocation);
+ return writer.tokenForStringExpression(elem, messageInvocation);
}
return new UiTextInterpreter(writer).interpretElement(elem);
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/UiSafeHtmlInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/UiSafeHtmlInterpreter.java
index 4bb1fbd..1c41fd4 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/UiSafeHtmlInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/UiSafeHtmlInterpreter.java
@@ -28,9 +28,9 @@
* Used in {@link #interpretElement} to invoke the {@link ComputedAttributeInterpreter}.
*/
private class Delegate extends UiTextInterpreter.Delegate {
- public String getAttributeToken(XMLAttribute attribute)
- throws UnableToCompleteException {
- return writer.tokenForSafeHtmlExpression(attribute.consumeSafeHtmlValue());
+ public String getAttributeToken(XMLAttribute attribute) throws UnableToCompleteException {
+ return writer.tokenForSafeHtmlExpression(attribute.getElement(),
+ attribute.consumeSafeHtmlValue());
}
}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/UiTextInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/UiTextInterpreter.java
index d2abb21..91abcf6 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/UiTextInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/UiTextInterpreter.java
@@ -31,9 +31,8 @@
* Used in {@link #interpretElement} to invoke the {@link ComputedAttributeInterpreter}.
*/
protected class Delegate implements ComputedAttributeInterpreter.Delegate {
- public String getAttributeToken(XMLAttribute attribute)
- throws UnableToCompleteException {
- return writer.tokenForStringExpression(attribute.consumeStringValue());
+ public String getAttributeToken(XMLAttribute attribute) throws UnableToCompleteException {
+ return writer.tokenForStringExpression(attribute.getElement(), attribute.consumeStringValue());
}
}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java
index 9cb1032..4332c62 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java
@@ -124,7 +124,7 @@
String tag = getLegalPlaceholderTag(elem);
idHolder = fieldManager.convertFieldToGetter(idHolder);
if (uiWriter.useSafeHtmlTemplates()) {
- idHolder = uiWriter.tokenForStringExpression(idHolder);
+ idHolder = uiWriter.tokenForStringExpression(elem, idHolder);
} else {
idHolder = "\" + " + idHolder + " + \"";
}
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java
index f335218..cd4c81c 100644
--- a/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java
+++ b/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java
@@ -108,7 +108,7 @@
return handleHasTextPlaceholder(elem, name, idHolder);
}
- return handleOpaqueWidgetPlaceholder(name, idHolder);
+ return handleOpaqueWidgetPlaceholder(elem, name, idHolder);
}
/**
@@ -162,10 +162,10 @@
return closePlaceholder;
}
- private String genOpenTag(String name, String idHolder) {
+ private String genOpenTag(XMLElement source, String name, String idHolder) {
idHolder = fieldManager.convertFieldToGetter(idHolder);
if (uiWriter.useSafeHtmlTemplates()) {
- idHolder = uiWriter.tokenForStringExpression(idHolder);
+ idHolder = uiWriter.tokenForStringExpression(source, idHolder);
} else {
idHolder = "\" + " + idHolder + " + \"";
}
@@ -207,7 +207,7 @@
private String handleHasHTMLPlaceholder(XMLElement elem, String name,
String idHolder) throws UnableToCompleteException {
idIsHasHTML.add(idHolder);
- String openPlaceholder = genOpenTag(name, idHolder);
+ String openPlaceholder = genOpenTag(elem, name, idHolder);
String body =
elem.consumeInnerHtml(new HtmlPlaceholderInterpreter(uiWriter,
@@ -221,7 +221,7 @@
private String handleHasTextPlaceholder(XMLElement elem, String name,
String idHolder) throws UnableToCompleteException {
idIsHasText.add(idHolder);
- String openPlaceholder = genOpenTag(name, idHolder);
+ String openPlaceholder = genOpenTag(elem, name, idHolder);
String body =
elem.consumeInnerText(new TextPlaceholderInterpreter(uiWriter,
@@ -232,10 +232,10 @@
return openPlaceholder + bodyToken + closePlaceholder;
}
- private String handleOpaqueWidgetPlaceholder(String name, String idHolder) {
+ private String handleOpaqueWidgetPlaceholder(XMLElement source, String name, String idHolder) {
idHolder = fieldManager.convertFieldToGetter(idHolder);
if (uiWriter.useSafeHtmlTemplates()) {
- idHolder = uiWriter.tokenForStringExpression(idHolder);
+ idHolder = uiWriter.tokenForStringExpression(source, idHolder);
} else {
idHolder = "\" + " + idHolder + " + \"";
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
index 8381922..ea58f4cc 100644
--- a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
@@ -153,10 +153,6 @@
}
for (FieldWriter f : needs) {
- // TODO(rdamazio, rjrjr) This is simplistic, and will fail when
- // we support more interesting contexts (e.g. the same need being used
- // inside two different
- // LazyPanels)
f.write(w);
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldManager.java b/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
index 5b4b4c1..a6af812 100644
--- a/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
+++ b/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
@@ -15,11 +15,11 @@
*/
package com.google.gwt.uibinder.rebind;
-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.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.uibinder.attributeparsers.FieldReferenceConverter;
import com.google.gwt.uibinder.rebind.model.ImplicitCssResource;
import com.google.gwt.uibinder.rebind.model.OwnerClass;
import com.google.gwt.uibinder.rebind.model.OwnerField;
@@ -39,6 +39,20 @@
*/
public class FieldManager {
+ static class FieldAndSource {
+ final FieldWriter field;
+ final XMLElement element;
+
+ public FieldAndSource(FieldWriter field, XMLElement element) {
+ this.field = field;
+ this.element = element;
+ }
+ }
+
+ private static final String GETTER_PREFIX = "get_";
+
+ private static final String BUILDER_PREFIX = "build_";
+
private static final String DUPLICATE_FIELD_ERROR = "Duplicate declaration of field %1$s.";
private static final Comparator<FieldWriter> BUILD_DEFINITION_SORT =
@@ -58,13 +72,21 @@
Pattern.compile("[\\p{L}_$][\\p{L}\\p{N}_$]*");
public static String getFieldBuilder(String fieldName) {
- return String.format("build_%s()", fieldName);
+ return String.format(BUILDER_PREFIX + "%s()", fieldName);
}
public static String getFieldGetter(String fieldName) {
- return String.format("get_%s()", fieldName);
+ return String.format(GETTER_PREFIX + "%s()", fieldName);
}
- private final TypeOracle types;
+
+ public static String stripFieldGetter(String fieldName) {
+ if (fieldName.startsWith(GETTER_PREFIX)) {
+ return fieldName.substring(GETTER_PREFIX.length());
+ }
+ return fieldName;
+ }
+
+ private final TypeOracle typeOracle;
private final MortalLogger logger;
@@ -78,7 +100,7 @@
/**
* A stack of the fields.
*/
- private final LinkedList<FieldWriter> parsedFieldStack = new LinkedList<FieldWriter>();
+ private final LinkedList<FieldAndSource> parsedFieldStack = new LinkedList<FieldAndSource>();
private LinkedHashMap<String, FieldReference> fieldReferences =
new LinkedHashMap<String, FieldReference>();
@@ -95,8 +117,8 @@
*/
private final boolean useLazyWidgetBuilders;
- public FieldManager(TypeOracle types, MortalLogger logger, boolean useLazyWidgetBuilders) {
- this.types = types;
+ public FieldManager(TypeOracle typeOracle, MortalLogger logger, boolean useLazyWidgetBuilders) {
+ this.typeOracle = typeOracle;
this.logger = logger;
this.useLazyWidgetBuilders = useLazyWidgetBuilders;
}
@@ -107,8 +129,7 @@
* <li> f_html1 = get_f_html1()
*/
public String convertFieldToGetter(String fieldName) {
- // TODO(hermes, rjrjr, rdcastro): revisit this and evaluate if this
- // conversion can be made directly in FieldWriter.
+ // could this conversion can be moved to FieldWriter?
if (!useLazyWidgetBuilders) {
return fieldName;
}
@@ -118,6 +139,15 @@
return getFieldGetter(fieldName);
}
+ public FieldReference findFieldReference(String expressionIn) {
+ String expression = expressionIn;
+ if (useLazyWidgetBuilders) {
+ expression = stripFieldGetter(expression);
+ }
+ String converted = FieldReferenceConverter.expressionToPath(expression);
+ return fieldReferences.get(converted);
+ }
+
/**
* Initialize with field builders the generated <b>Widgets</b> inner class.
* {@see com.google.gwt.uibinder.rebind.FieldWriter#writeFieldBuilder}.
@@ -152,11 +182,12 @@
}
/**
+ * @param source the element this field was parsed from
* @param fieldWriter the field to push on the top of the
* {@link #parsedFieldStack}
*/
- public void push(FieldWriter fieldWriter) {
- parsedFieldStack.addFirst(fieldWriter);
+ public void push(XMLElement source, FieldWriter fieldWriter) {
+ parsedFieldStack.addFirst(new FieldAndSource(fieldWriter, source));
}
/**
@@ -189,7 +220,7 @@
public FieldWriter registerField(String type, String fieldName)
throws UnableToCompleteException {
- return registerField(types.findType(type), fieldName);
+ return registerField(typeOracle.findType(type), fieldName);
}
/**
@@ -211,7 +242,7 @@
public FieldWriter registerFieldForGeneratedCssResource(
ImplicitCssResource cssResource) throws UnableToCompleteException {
FieldWriter field = new FieldWriterOfGeneratedCssResource(
- types.findType(String.class.getCanonicalName()), cssResource, logger);
+ typeOracle.findType(String.class.getCanonicalName()), cssResource, logger);
return registerField(cssResource.getName(), field);
}
@@ -268,15 +299,16 @@
* Called to register a <code>{field.reference}</code> encountered during
* parsing, to be validated against the type oracle once parsing is complete.
*/
- public void registerFieldReference(String fieldReferenceString, JType type) {
- FieldReference fieldReference = fieldReferences.get(fieldReferenceString);
+ public void registerFieldReference(XMLElement source, String fieldReferenceString, JType... types) {
+ source = source != null ? source : parsedFieldStack.peek().element;
+ FieldReference fieldReference = fieldReferences.get(fieldReferenceString);
if (fieldReference == null) {
- fieldReference = new FieldReference(fieldReferenceString, this, types);
+ fieldReference = new FieldReference(fieldReferenceString, source, this, typeOracle);
fieldReferences.put(fieldReferenceString, fieldReference);
}
- fieldReference.addLeftHandType(type);
+ fieldReference.addLeftHandType(source, types);
}
/**
@@ -305,8 +337,7 @@
for (Map.Entry<String, FieldReference> entry : fieldReferences.entrySet()) {
FieldReference ref = entry.getValue();
- MonitoredLogger monitoredLogger = new MonitoredLogger(
- logger.getTreeLogger().branch(TreeLogger.TRACE, "validating " + ref));
+ MonitoredLogger monitoredLogger = new MonitoredLogger(logger);
ref.validate(monitoredLogger);
failed |= monitoredLogger.hasErrors();
}
@@ -314,7 +345,7 @@
throw new UnableToCompleteException();
}
}
-
+
/**
* Outputs the getter and builder definitions for all fields.
* {@see com.google.gwt.uibinder.rebind.AbstractFieldWriter#writeFieldDefinition}.
@@ -361,10 +392,6 @@
return (count == null) ? 0 : count;
}
- private FieldWriter peek() {
- return parsedFieldStack.getFirst();
- }
-
private FieldWriter registerField(String fieldName, FieldWriter field)
throws UnableToCompleteException {
ensureValidity(fieldName);
@@ -372,7 +399,7 @@
fieldsMap.put(fieldName, field);
if (parsedFieldStack.size() > 0) {
- FieldWriter parent = peek();
+ FieldWriter parent = parsedFieldStack.peek().field;
field.setBuildPrecedence(parent.getBuildPrecedence() + 1);
parent.needs(field);
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldReference.java b/user/src/com/google/gwt/uibinder/rebind/FieldReference.java
index 74fa092..3092525 100644
--- a/user/src/com/google/gwt/uibinder/rebind/FieldReference.java
+++ b/user/src/com/google/gwt/uibinder/rebind/FieldReference.java
@@ -1,12 +1,12 @@
/*
* Copyright 2009 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
@@ -20,6 +20,7 @@
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import java.util.Arrays;
import java.util.LinkedHashSet;
/**
@@ -28,92 +29,161 @@
* actually does so.
*/
public class FieldReference {
- private final FieldManager fieldManager;
+ private static class LeftHand {
+ /**
+ * The type of values acceptible to this LHS, in order of preference
+ */
+ private final JType[] types;
+ /**
+ * The element on the LHS, for error reporting
+ */
+ private final XMLElement source;
+ LeftHand(XMLElement source, JType... types) {
+ this.types = Arrays.copyOf(types, types.length);
+ this.source = source;
+ }
+ }
+
+ public static String renderTypesList(JType[] types) {
+ StringBuilder b = new StringBuilder();
+ for (int i = 0; i < types.length; i++) {
+ if (i > 0 && i == types.length - 1) {
+ b.append(" or ");
+ } else if (i > 0) {
+ b.append(", ");
+ }
+ b.append(types[i].getQualifiedSourceName());
+ }
+
+ return b.toString();
+ }
+
+ private final FieldManager fieldManager;
+ private final XMLElement source;
private final String debugString;
private final String[] elements;
- private final LinkedHashSet<JType> leftHandTypes = new LinkedHashSet<JType>();
- private final TypeOracle types;
+ private final LinkedHashSet<LeftHand> leftHandTypes = new LinkedHashSet<LeftHand>();
- FieldReference(String reference, FieldManager fieldManager, TypeOracle types) {
+ private final TypeOracle typeOracle;
+
+ FieldReference(String reference, XMLElement source, FieldManager fieldManager,
+ TypeOracle typeOracle) {
+ this.source = source;
this.debugString = "{" + reference + "}";
this.fieldManager = fieldManager;
- this.types = types;
+ this.typeOracle = typeOracle;
elements = reference.split("\\.");
}
- public void addLeftHandType(JType type) {
- leftHandTypes.add(type);
+ public void addLeftHandType(XMLElement source, JType... types) {
+ leftHandTypes.add(new LeftHand(source, types));
}
public String getFieldName() {
return elements[0];
}
+ public JType getReturnType() {
+ return getReturnType(null);
+ }
+
+ /**
+ * Returns the type returned by this field ref.
+ *
+ * @param logger optional logger to report errors on, may be null
+ * @return the field ref, or null
+ */
+ public JType getReturnType(MonitoredLogger logger) {
+ FieldWriter field = fieldManager.lookup(elements[0]);
+ if (field == null) {
+ if (logger != null) {
+ /*
+ * It's null when called from HtmlTemplateMethodWriter, which fires
+ * after validation has already succeeded.
+ */
+ logger.error(source, "in %s, no field named %s", this, elements[0]);
+ }
+ return null;
+ }
+
+ return field.getReturnType(elements, logger);
+ }
+
+ public XMLElement getSource() {
+ return source;
+ }
+
@Override
public String toString() {
return debugString;
}
public void validate(MonitoredLogger logger) {
- JType myReturnType = findReturnType(logger);
+ JType myReturnType = getReturnType(logger);
if (myReturnType == null) {
return;
}
- for (JType t : leftHandTypes) {
- ensureAssignable(t, myReturnType, logger);
+ for (LeftHand left : leftHandTypes) {
+ ensureAssignable(left, myReturnType, logger);
}
}
/**
* Returns a failure message if the types don't mesh, or null on success.
*/
- private void ensureAssignable(JType leftHandType, JType rightHandType,
- MonitoredLogger logger) {
+ private void ensureAssignable(LeftHand left, JType rightHandType, MonitoredLogger logger) {
+ assert left.types.length > 0;
- if (leftHandType == rightHandType) {
- return;
- }
+ for (JType leftType : left.types) {
- if (handleMismatchedNumbers(leftHandType, rightHandType)) {
- return;
- }
-
- if (handleMismatchedNonNumericPrimitives(leftHandType, rightHandType,
- logger)) {
- return;
- }
-
- JClassType leftClass = leftHandType.isClassOrInterface();
- if (leftClass != null) {
- JClassType rightClass = rightHandType.isClassOrInterface();
- if ((rightClass == null) || !leftClass.isAssignableFrom(rightClass)) {
- logTypeMismatch(leftHandType, rightHandType, logger);
+ if (leftType == rightHandType) {
+ return;
}
+
+ if (matchingNumberTypes(leftType, rightHandType)) {
+ return;
+ }
+
+ boolean[] explicitFailure = {false};
+ if (handleMismatchedNonNumericPrimitives(leftType, rightHandType, explicitFailure)) {
+ if (explicitFailure[0]) {
+ continue;
+ }
+ }
+
+ JClassType leftClass = leftType.isClassOrInterface();
+ if (leftClass != null) {
+ JClassType rightClass = rightHandType.isClassOrInterface();
+ if ((rightClass == null) || !leftClass.isAssignableFrom(rightClass)) {
+ continue;
+ }
+ }
+
+ /*
+ * If we have reached the bottom of the loop, we don't see a problem with
+ * assigning to this left hand type. Return without logging any error.
+ * This is pretty conservative -- we have a white list of bad conditions,
+ * not an exhaustive check of valid assignments. We're not confident that
+ * we know every error case, and are more worried about being artificially
+ * restrictive.
+ */
+ return;
}
/*
- * We're conservative and fall through here, allowing success. Not yet
- * confident that we know every error case, and are more worried about being
- * artificially restrictive.
+ * Every possible left hand type had some kind of failure. Log this sad
+ * fact, which will halt processing.
*/
+ logger.error(left.source, "%s required, but %s returns %s", renderTypesList(left.types),
+ FieldReference.this, rightHandType.getQualifiedSourceName());
}
- private JType findReturnType(MonitoredLogger logger) {
- FieldWriter field = fieldManager.lookup(elements[0]);
- if (field == null) {
- logger.error("No field named %s", elements[0]);
- return null;
- }
-
- return field.getReturnType(elements, logger);
- }
-
- private boolean handleMismatchedNonNumericPrimitives(JType leftHandType,
- JType rightHandType, MonitoredLogger logger) {
- JPrimitiveType leftPrimitive = leftHandType.isPrimitive();
+ private boolean handleMismatchedNonNumericPrimitives(JType leftType, JType rightHandType,
+ boolean[] explicitFailure) {
+ JPrimitiveType leftPrimitive = leftType.isPrimitive();
JPrimitiveType rightPrimitive = rightHandType.isPrimitive();
if (leftPrimitive == null && rightPrimitive == null) {
@@ -121,56 +191,47 @@
}
if (leftPrimitive != null) {
- JClassType autobox = types.findType(leftPrimitive.getQualifiedBoxedSourceName());
+ JClassType autobox = typeOracle.findType(leftPrimitive.getQualifiedBoxedSourceName());
if (rightHandType != autobox) {
- logger.error("Returns %s, can't be used as %s", rightHandType,
- leftHandType);
+ explicitFailure[0] = true;
}
} else { // rightPrimitive != null
- JClassType autobox = types.findType(rightPrimitive.getQualifiedBoxedSourceName());
- if (leftHandType != autobox) {
- logger.error("Returns %s, can't be used as %s", rightHandType,
- leftHandType);
+ JClassType autobox = typeOracle.findType(rightPrimitive.getQualifiedBoxedSourceName());
+ if (leftType != autobox) {
+ explicitFailure[0] = true;
}
}
return true;
}
- private boolean handleMismatchedNumbers(JType leftHandType,
- JType rightHandType) {
- /*
- * int i = (int) 1.0 is okay
- * Integer i = (int) 1.0 is okay
- * int i = (int) Double.valueOf(1.0) is not
- */
- if (isNumber(leftHandType) && isNumber(rightHandType)
- && (rightHandType.isPrimitive() != null)) {
- return true; // They will be cast into submission
- }
+ private boolean isNumber(JType type) {
+ JClassType numberType = typeOracle.findType(Number.class.getCanonicalName());
- return false;
- }
-
- private boolean isNumber(JType t) {
- JClassType numberType = types.findType(Number.class.getCanonicalName());
-
- JClassType asClass = t.isClass();
+ JClassType asClass = type.isClass();
if (asClass != null) {
return numberType.isAssignableFrom(asClass);
}
- JPrimitiveType asPrimitive = t.isPrimitive();
+ JPrimitiveType asPrimitive = type.isPrimitive();
if (asPrimitive != null) {
- JClassType autoboxed = types.findType(asPrimitive.getQualifiedBoxedSourceName());
+ JClassType autoboxed = typeOracle.findType(asPrimitive.getQualifiedBoxedSourceName());
return numberType.isAssignableFrom(autoboxed);
}
return false;
}
- private void logTypeMismatch(JType leftHandType, JType rightHandType,
- MonitoredLogger logger) {
- logger.error("Returns %s, can't be used as %s", rightHandType, leftHandType);
+ private boolean matchingNumberTypes(JType leftHandType, JType rightHandType) {
+ /*
+ * int i = (int) 1.0 is okay Integer i = (int) 1.0 is okay int i = (int)
+ * Double.valueOf(1.0) is not
+ */
+ if (isNumber(leftHandType) && isNumber(rightHandType) //
+ && (rightHandType.isPrimitive() != null)) {
+ return true; // They will be cast into submission
+ }
+
+ return false;
}
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/MonitoredLogger.java b/user/src/com/google/gwt/uibinder/rebind/MonitoredLogger.java
index 9f03693..1016430 100644
--- a/user/src/com/google/gwt/uibinder/rebind/MonitoredLogger.java
+++ b/user/src/com/google/gwt/uibinder/rebind/MonitoredLogger.java
@@ -23,10 +23,10 @@
*/
public class MonitoredLogger {
private boolean hasErrors = false;
- private final TreeLogger logger;
+ private final MortalLogger logger;
- public MonitoredLogger(TreeLogger logger) {
- this.logger = logger;
+ public MonitoredLogger(MortalLogger mortalLogger) {
+ this.logger = mortalLogger;
}
/**
@@ -34,7 +34,12 @@
*/
public void error(String message, Object... params) {
hasErrors = true;
- logger.log(TreeLogger.ERROR, String.format(message, params));
+ logger.getTreeLogger().log(TreeLogger.ERROR, String.format(message, params));
+ }
+
+ public void error(XMLElement context, String message, Object... params) {
+ hasErrors = true;
+ logger.logLocation(TreeLogger.ERROR, context, String.format(message, params));
}
/**
diff --git a/user/src/com/google/gwt/uibinder/rebind/MortalLogger.java b/user/src/com/google/gwt/uibinder/rebind/MortalLogger.java
index 7267da4..fac53f1 100644
--- a/user/src/com/google/gwt/uibinder/rebind/MortalLogger.java
+++ b/user/src/com/google/gwt/uibinder/rebind/MortalLogger.java
@@ -45,7 +45,7 @@
// We see this in the test cases that don't use actual source files
displayFileName = "Unknown";
} else {
- // Parse the system id as a URI, which is almost always is
+ // Parse the system id as a URI, which it almost always is
try {
URI uri = new URI(location.getSystemId());
String path = uri.getPath();
@@ -57,7 +57,7 @@
}
}
// Log in a way that usually triggers IDE hyperlinks
- return " Element " + context.toString() + " (" + displayFileName + ":"
+ return ": " + context.toString() + " (" + displayFileName + ":"
+ location.getLineNumber() + ")";
} else {
/*
@@ -73,7 +73,7 @@
public MortalLogger(TreeLogger logger) {
this.logger = logger;
}
-
+
/**
* Post an error message and halt processing. This method always throws an
* {@link UnableToCompleteException}.
@@ -97,6 +97,12 @@
return logger;
}
+ public void logLocation(TreeLogger.Type type, XMLElement context,
+ String message) {
+ message += locationOf(context);
+ logger.log(type, message);
+ }
+
/**
* Post a warning message.
*/
@@ -110,10 +116,4 @@
public void warn(XMLElement context, String message, Object... params) {
logLocation(TreeLogger.WARN, context, String.format(message, params));
}
-
- private void logLocation(TreeLogger.Type type, XMLElement context,
- String message) {
- message += locationOf(context);
- logger.log(type, message);
- }
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/Tokenator.java b/user/src/com/google/gwt/uibinder/rebind/Tokenator.java
index 709e7e9..728f488 100644
--- a/user/src/com/google/gwt/uibinder/rebind/Tokenator.java
+++ b/user/src/com/google/gwt/uibinder/rebind/Tokenator.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
@@ -21,10 +21,12 @@
import java.util.Map;
/**
- * Methods to dispense unique text tokens to be stitched into text, and
- * to help replace the tokens with arbitrary content. Multiple tokenators
- * can be used across the same body of text without fear of the tokens they
- * vend colliding with each other.
+ * Methods to dispense unique text tokens to be stitched into text, and to help
+ * replace the tokens with arbitrary content. Multiple tokenators can be used
+ * across the same body of text without fear of the tokens they vend colliding
+ * with each other.
+ * <p>
+ * A arbitrary metadata object ("info") can be associated with each token.
*/
public class Tokenator {
/**
@@ -34,6 +36,19 @@
String resolveToken(String token);
}
+ /**
+ * Return values for {@link Tokenator#getOrderedValues(String)}.
+ */
+ public static class ValueAndInfo {
+ public final String value;
+ public final Object info;
+
+ private ValueAndInfo(String value, Object info) {
+ this.value = value;
+ this.info = info;
+ }
+ }
+
private static final String TOKEN = "--token--";
private static final String TOKEN_REGEXP = "\\-\\-token\\-\\-";
private static int curId = 0;
@@ -55,34 +70,24 @@
detokenated.append(betokened.substring(index));
return detokenated.toString();
}
-
+
public static boolean hasToken(String s) {
return s.matches(".*" + TOKEN_REGEXP + "\\d+" + TOKEN_REGEXP + ".*");
}
- private static List<String> getOrderedValues(String betokened,
- Resolver resolver) {
- List<String> values = new ArrayList<String>();
-
- int index = 0, nextToken = 0;
- while ((nextToken = betokened.indexOf(TOKEN, index)) > -1) {
- int endToken = betokened.indexOf(TOKEN, nextToken + TOKEN.length());
- String token = betokened.substring(nextToken, endToken + TOKEN.length());
- values.add(resolver.resolveToken(token));
-
- index = endToken + TOKEN.length();
- }
-
- return values;
- }
-
private static String nextToken() {
return TOKEN + (curId++) + TOKEN;
}
- private Map<String, String> tokenToResolved =
- new HashMap<String, String>();
+ private final Map<String, Object> infoMap = new HashMap<String, Object>();
+ private Map<String, String> tokenToResolved = new HashMap<String, String>();
+
+ /**
+ * Given a string filled with tokens created by
+ * {@link #nextToken(Object, String)}, returns it with the tokens replaced by
+ * the original strings.
+ */
public String detokenate(String betokened) {
return detokenate(betokened, new Resolver() {
public String resolveToken(String token) {
@@ -90,8 +95,12 @@
}
});
}
-
- public List<String> getOrderedValues(String betokened) {
+
+ /**
+ * Returns a list of the values represented by tokens in the given string, and
+ * the info objects corresponding to them.
+ */
+ public List<ValueAndInfo> getOrderedValues(String betokened) {
return getOrderedValues(betokened, new Resolver() {
public String resolveToken(String token) {
return tokenToResolved.get(token);
@@ -99,9 +108,42 @@
});
}
- public String nextToken(final String resolved) {
+ /**
+ * Returns a token that can be used to replace the given String, to be
+ * restored by a later call to {@link #detokenate(String)}. Associates
+ * the token with the given info object.
+ *
+ * @param info An arbitrary object to associate with this token. Mmm, metadata
+ * @param resolved The value to replace this token with in later calls to
+ * {@link #detokenate(String)}
+ * @return the token
+ */
+ public String nextToken(Object info, final String resolved) {
String token = nextToken();
tokenToResolved.put(token, resolved);
+ infoMap.put(token, info);
return token;
}
+
+ /**
+ * Like {@link #nextToken(String)} with no info.
+ */
+ public String nextToken(String resolved) {
+ return nextToken(null, resolved);
+ }
+
+ private List<ValueAndInfo> getOrderedValues(String betokened, Resolver resolver) {
+ List<ValueAndInfo> values = new ArrayList<ValueAndInfo>();
+
+ int index = 0, nextToken = 0;
+ while ((nextToken = betokened.indexOf(TOKEN, index)) > -1) {
+ int endToken = betokened.indexOf(TOKEN, nextToken + TOKEN.length());
+ String token = betokened.substring(nextToken, endToken + TOKEN.length());
+ values.add(new ValueAndInfo(resolver.resolveToken(token), infoMap.get(token)));
+
+ index = endToken + TOKEN.length();
+ }
+
+ return values;
+ }
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
index 92adea4..293c848 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -38,7 +38,8 @@
import com.google.gwt.uibinder.elementparsers.IsEmptyParser;
import com.google.gwt.uibinder.elementparsers.UiChildParser;
import com.google.gwt.uibinder.rebind.messages.MessagesWriter;
-import com.google.gwt.uibinder.rebind.model.HtmlTemplates;
+import com.google.gwt.uibinder.rebind.model.HtmlTemplateMethodWriter;
+import com.google.gwt.uibinder.rebind.model.HtmlTemplatesWriter;
import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle;
import com.google.gwt.uibinder.rebind.model.ImplicitCssResource;
import com.google.gwt.uibinder.rebind.model.OwnerClass;
@@ -241,7 +242,6 @@
private final List<String> initStatements = new ArrayList<String>();
private final List<String> statements = new ArrayList<String>();
- private final HtmlTemplates htmlTemplates = new HtmlTemplates();
private final HandlerEvaluator handlerEvaluator;
private final MessagesWriter messages;
private final DesignTimeUtils designTime;
@@ -271,6 +271,8 @@
private final FieldManager fieldManager;
+ private final HtmlTemplatesWriter htmlTemplates;
+
private final ImplicitClientBundle bundleClass;
private final boolean useLazyWidgetBuilders;
@@ -326,6 +328,8 @@
this.useLazyWidgetBuilders = useLazyWidgetBuilders;
this.binderUri = binderUri;
+ this.htmlTemplates = new HtmlTemplatesWriter(fieldManager, logger);
+
// Check for possible misuse 'GWT.create(UiBinder.class)'
JClassType uibinderItself = oracle.findType(UiBinder.class.getCanonicalName());
if (uibinderItself.equals(baseClass)) {
@@ -461,7 +465,7 @@
* @param fieldName The name of the field being declared
* @param ancestorField The name of fieldName parent
*/
- public String declareDomField(String fieldName, String ancestorField)
+ public String declareDomField(XMLElement source, String fieldName, String ancestorField)
throws UnableToCompleteException {
ensureAttached();
String name = declareDomIdHolder(fieldName);
@@ -494,7 +498,7 @@
addInitStatement("%s.removeAttribute(\"id\");", fieldName);
}
- return tokenForStringExpression(fieldManager.convertFieldToGetter(name));
+ return tokenForStringExpression(source, fieldManager.convertFieldToGetter(name));
}
/**
@@ -522,30 +526,6 @@
}
/**
- * Declares a field of the given type name, returning the name of the declared
- * field. If the element has a field or id attribute, use its value.
- * Otherwise, create and return a new, private field name for it.
- */
- public String declareField(String typeName, XMLElement elem) throws UnableToCompleteException {
- JClassType type = oracle.findType(typeName);
- if (type == null) {
- die(elem, "Unknown type %s", typeName);
- }
-
- String fieldName = getFieldName(elem);
- if (fieldName == null) {
- // TODO(rjrjr) could collide with user declared name, as is
- // also a worry in HandlerEvaluator. Need a general scheme for
- // anonymous fields. See the note in HandlerEvaluator and do
- // something like that, but in FieldManager.
- fieldName = "f_" + elem.getLocalName() + ++fieldIndex;
- }
- fieldName = normalizeFieldName(fieldName);
- fieldManager.registerField(type, fieldName);
- return fieldName;
- }
-
- /**
* If this element has a gwt:field attribute, create a field for it of the
* appropriate type, and return the field name. If no gwt:field attribute is
* found, do nothing and return null
@@ -600,7 +580,12 @@
return '"' + html + '"';
}
FieldWriter w = fieldManager.lookup(fieldName);
- w.setHtml(htmlTemplates.addSafeHtmlTemplate(html, tokenator));
+ HtmlTemplateMethodWriter templateMethod = htmlTemplates.addSafeHtmlTemplate(html, tokenator);
+ if (useLazyWidgetBuilders) {
+ w.setHtml(templateMethod.getIndirectTemplateCall());
+ } else {
+ w.setHtml(templateMethod.getDirectTemplateCall());
+ }
return w.getHtml();
}
@@ -907,18 +892,20 @@
JClassType type = findFieldType(elem);
// Declare its field.
- String fieldName = declareField(type.getQualifiedSourceName(), elem);
+ FieldWriter field = declareField(elem, type.getQualifiedSourceName());
- FieldWriter field = fieldManager.lookup(fieldName);
-
- // Push the field that will hold this widget on top of the parsedFieldStack
- // to ensure that fields registered by its parsers will be noted as
- // dependencies of the new widget. See registerField.
- fieldManager.push(field);
+ /*
+ * Push the field that will hold this widget on top of the parsedFieldStack
+ * to ensure that fields registered by its parsers will be noted as
+ * dependencies of the new widget. (See registerField.) Also push the
+ * element being parsed, so that the fieldManager can hold that info for
+ * later error reporting when field reference left hand sides are validated.
+ */
+ fieldManager.push(elem, field);
// Give all the parsers a chance to generate their code.
for (ElementParser parser : getParsersForClass(type)) {
- parser.parse(elem, fieldName, type, this);
+ parser.parse(elem, field.getName(), type, this);
}
fieldManager.pop();
@@ -957,14 +944,14 @@
*
* @param expression must resolve to trusted HTML string
*/
- public String tokenForSafeConstant(String expression) {
+ public String tokenForSafeConstant(XMLElement source, String expression) {
if (!useSafeHtmlTemplates) {
- return tokenForStringExpression(expression);
+ return tokenForStringExpression(source, expression);
}
expression = "SafeHtmlUtils.fromSafeConstant(" + expression + ")";
htmlTemplates.noteSafeConstant(expression);
- return tokenator.nextToken(expression);
+ return nextToken(source, expression);
}
/**
@@ -973,13 +960,28 @@
*
* @param expression must resolve to SafeHtml object
*/
- public String tokenForSafeHtmlExpression(String expression) {
+ public String tokenForSafeHtmlExpression(XMLElement source, String expression) {
if (!useSafeHtmlTemplates) {
- return tokenForStringExpression(expression + ".asString()");
+ return tokenForStringExpression(source, expression + ".asString()");
}
htmlTemplates.noteSafeConstant(expression);
- return tokenator.nextToken(expression);
+ return nextToken(source, expression);
+ }
+
+ /**
+ * Like {@link #tokenForStringExpression}, but used for runtime
+ * {@link com.google.gwt.safehtml.shared.SafeUri SafeUri} instances.
+ *
+ * @param expression must resolve to SafeUri object
+ */
+ public String tokenForSafeUriExpression(XMLElement source, String expression) {
+ if (!useSafeHtmlTemplates) {
+ return tokenForStringExpression(source, expression + ".asString()");
+ }
+
+ htmlTemplates.noteUri(expression);
+ return nextToken(source, expression);
}
/**
@@ -992,8 +994,8 @@
*
* @param expression must resolve to String
*/
- public String tokenForStringExpression(String expression) {
- return tokenator.nextToken(("\" + " + expression + " + \""));
+ public String tokenForStringExpression(XMLElement source, String expression) {
+ return nextToken(source, "\" + " + expression + " + \"");
}
public boolean useLazyWidgetBuilders() {
@@ -1056,6 +1058,30 @@
}
/**
+ * Declares a field of the given type name, returning the name of the declared
+ * field. If the element has a field or id attribute, use its value.
+ * Otherwise, create and return a new, private field name for it.
+ */
+ private FieldWriter declareField(XMLElement source, String typeName)
+ throws UnableToCompleteException {
+ JClassType type = oracle.findType(typeName);
+ if (type == null) {
+ die(source, "Unknown type %s", typeName);
+ }
+
+ String fieldName = getFieldName(source);
+ if (fieldName == null) {
+ // TODO(rjrjr) could collide with user declared name, as is
+ // also a worry in HandlerEvaluator. Need a general scheme for
+ // anonymous fields. See the note in HandlerEvaluator and do
+ // something like that, but in FieldManager.
+ fieldName = "f_" + source.getLocalName() + ++fieldIndex;
+ }
+ fieldName = normalizeFieldName(fieldName);
+ return fieldManager.registerField(type, fieldName);
+ }
+
+ /**
* Ensures that all of the internal data structures are cleaned up correctly
* at the end of parsing the document.
*
@@ -1235,6 +1261,11 @@
}
}
+ private String nextToken(XMLElement source, String expression) {
+ String nextToken = tokenator.nextToken(source, expression);
+ return nextToken;
+ }
+
private String normalizeFieldName(String fieldName) {
// If a field name has a '.' in it, replace it with '$' to make it a legal
// identifier. This can happen with the field names associated with nested
@@ -1426,7 +1457,8 @@
w.newline();
// Create SafeHtml Template
- writeSafeHtmlTemplates(w);
+ writeTemplatesInterface(w);
+ w.newline();
// createAndBindUi method
w.write("public %s createAndBindUi(final %s owner) {",
@@ -1475,8 +1507,7 @@
writeStatics(w);
w.newline();
- // Create SafeHtml Template
- writeSafeHtmlTemplates(w);
+ writeTemplatesInterface(w);
w.newline();
@@ -1515,6 +1546,9 @@
fieldManager.initializeWidgetsInnerClass(w, getOwnerClass());
w.outdent();
w.write("}");
+ w.newline();
+
+ htmlTemplates.writeTemplateCallers(w);
evaluateUiFields();
@@ -1597,6 +1631,8 @@
w.write("import com.google.gwt.safehtml.shared.SafeHtml;");
w.write("import com.google.gwt.safehtml.shared.SafeHtmlUtils;");
w.write("import com.google.gwt.safehtml.shared.SafeHtmlBuilder;");
+ w.write("import com.google.gwt.safehtml.shared.SafeUri;");
+ w.write("import com.google.gwt.safehtml.shared.UriUtils;");
w.write("import com.google.gwt.uibinder.client.UiBinderUtil;");
}
@@ -1678,7 +1714,9 @@
w.newline();
// Create SafeHtml Template
- writeSafeHtmlTemplates(w);
+ writeTemplatesInterface(w);
+ w.newline();
+ htmlTemplates.writeTemplateCallers(w);
w.newline();
@@ -1766,27 +1804,6 @@
}
}
- /**
- * Write statements created by {@link HtmlTemplates#addSafeHtmlTemplate}. This
- * code must be placed after all instantiation code.
- */
- private void writeSafeHtmlTemplates(IndentedWriter w) {
- if (!(htmlTemplates.isEmpty())) {
- assert useSafeHtmlTemplates : "SafeHtml is off, but templates were made.";
-
- w.write("interface Template extends SafeHtmlTemplates {");
- w.indent();
-
- htmlTemplates.writeTemplates(w);
-
- w.outdent();
- w.write("}");
- w.newline();
- w.write("Template template = GWT.create(Template.class);");
- w.newline();
- }
- }
-
private void writeStaticMessagesInstance(IndentedWriter niceWriter) {
if (messages.hasMessages()) {
niceWriter.write(messages.getDeclaration());
@@ -1797,4 +1814,16 @@
writeStaticMessagesInstance(w);
designTime.addDeclarations(w);
}
+
+ /**
+ * Write statements created by {@link HtmlTemplatesWriter#addSafeHtmlTemplate}
+ * . This code must be placed after all instantiation code.
+ */
+ private void writeTemplatesInterface(IndentedWriter w) {
+ if (!(htmlTemplates.isEmpty())) {
+ assert useSafeHtmlTemplates : "SafeHtml is off, but templates were made.";
+ htmlTemplates.writeInterface(w);
+ w.newline();
+ }
+ }
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/XMLAttribute.java b/user/src/com/google/gwt/uibinder/rebind/XMLAttribute.java
index 39862ce..dcfbd89 100644
--- a/user/src/com/google/gwt/uibinder/rebind/XMLAttribute.java
+++ b/user/src/com/google/gwt/uibinder/rebind/XMLAttribute.java
@@ -41,6 +41,14 @@
return xmlElem.consumeSafeHtmlAttribute(w3cAttr.getName());
}
+ /**
+ * Consumes this attribute as either a SafeUri or a String. Used in HTML
+ * contexts.
+ */
+ public String consumeSafeUriOrStringAttribute() throws UnableToCompleteException {
+ return xmlElem.consumeSafeUriOrStringAttribute(w3cAttr.getName());
+ }
+
public String consumeStringValue() throws UnableToCompleteException {
return xmlElem.consumeStringAttribute(w3cAttr.getName());
}
diff --git a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
index 64a9629..27b4c1c 100644
--- a/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
+++ b/user/src/com/google/gwt/uibinder/rebind/XMLElement.java
@@ -142,8 +142,8 @@
private JType imageResourceType;
private JType doubleType;
private JType intType;
- private JType stringType;
private JType safeHtmlType;
+ private JType stringType;
{
// from com/google/gxp/compiler/schema/html.xml
@@ -258,29 +258,18 @@
* Like {@link #consumeAttributeWithDefault(String, String, JType)}, but
* accommodates more complex type signatures.
*/
- public String consumeAttributeWithDefault(String name, String defaultValue, JType[] types)
+ public String consumeAttributeWithDefault(String name, String defaultValue, JType... types)
throws UnableToCompleteException {
- XMLAttribute attribute = getAttribute(name);
- if (attribute == null) {
+
+ if (!hasAttribute(name)) {
if (defaultValue != null) {
designTime.putAttribute(this, name + ".default", defaultValue);
}
return defaultValue;
}
- String rawValue = attribute.consumeRawValue();
- AttributeParser parser = getParser(types);
- if (parser == null) {
- logger.die(this, "No such attribute %s", name);
- }
- try {
- String value = parser.parse(rawValue);
- designTime.putAttribute(this, name, value);
- return value;
- } catch (UnableToCompleteException e) {
- logger.die(this, "Cannot parse attribute %s", name);
- throw e;
- }
+ AttributeParser parser = attributeParsers.getParser(types);
+ return consumeAttributeWithParser(name, parser);
}
/**
@@ -558,21 +547,15 @@
*/
public String consumeRequiredAttribute(String name, JType... types)
throws UnableToCompleteException {
- XMLAttribute attribute = getAttribute(name);
- if (attribute == null) {
+ if (!hasAttribute(name)) {
failRequired(name);
}
- AttributeParser parser = getParser(types);
- String rawValue = consumeRequiredRawAttribute(name);
- try {
- String value = parser.parse(rawValue);
- designTime.putAttribute(this, name, value);
- return value;
- } catch (UnableToCompleteException e) {
- logger.die(this, "Cannot parse attribute \"%s\"", name);
- throw e;
- }
+ AttributeParser parser = attributeParsers.getParser(types);
+
+ String value = parser.parse(this, consumeRequiredRawAttribute(name));
+ designTime.putAttribute(this, name, value);
+ return value;
}
/**
@@ -629,6 +612,18 @@
}
/**
+ * Consumes an attribute as either a SafeUri or a String. Used in HTML
+ * contexts.
+ *
+ * @return an expression that will evaluate to a SafeUri value in the
+ * generated code, or null if there is no such attribute
+ * @throws UnableToCompleteException on unparseable value
+ */
+ public String consumeSafeUriOrStringAttribute(String name) throws UnableToCompleteException {
+ return consumeAttributeWithParser(name, attributeParsers.getSafeUriInHtmlParser());
+ }
+
+ /**
* Consumes a single child element, ignoring any text nodes and throwing an
* exception if no child is found, or more than one child element is found.
*
@@ -661,16 +656,11 @@
* @throws UnableToCompleteException on unparseable value
*/
public String[] consumeStringArrayAttribute(String name) throws UnableToCompleteException {
- AttributeParser parser = attributeParsers.get(getStringType());
+ AttributeParser parser = attributeParsers.getParser(getStringType());
String[] strings = consumeRawArrayAttribute(name);
for (int i = 0; i < strings.length; i++) {
- try {
- strings[i] = parser.parse(strings[i]);
- } catch (UnableToCompleteException e) {
- logger.die(this, "Cannot parse attribute " + name);
- throw e;
- }
+ strings[i] = parser.parse(this, strings[i]);
}
designTime.putAttribute(this, name, strings);
return strings;
@@ -829,6 +819,13 @@
return debugString;
}
+ private String consumeAttributeWithParser(String name, AttributeParser parser)
+ throws UnableToCompleteException {
+ String value = parser.parse(this, consumeRawAttribute(name));
+ designTime.putAttribute(this, name, value);
+ return value;
+ }
+
private Iterable<XMLElement> consumeChildElementsNoEmptyCheck() {
try {
Iterable<XMLElement> rtn = consumeChildElements(new NoBrainInterpeter<Boolean>(true));
@@ -895,10 +892,6 @@
return b.toString();
}
- private AttributeParser getParser(JType... types) {
- return attributeParsers.get(types);
- }
-
private JType getSafeHtmlType() {
if (safeHtmlType == null) {
safeHtmlType = oracle.findType(SafeHtml.class.getName());
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplate.java b/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplate.java
deleted file mode 100644
index f8c4994..0000000
--- a/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplate.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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.model;
-
-import com.google.gwt.uibinder.rebind.IndentedWriter;
-import com.google.gwt.uibinder.rebind.Tokenator;
-import com.google.gwt.uibinder.rebind.Tokenator.Resolver;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An individual SafeHtml template to be written by UiBinder.
- */
-public class HtmlTemplate {
- private final List<String> template = new ArrayList<String>();
- private final String methodName;
- private final ArrayList<HtmlTemplateArgument> methodArgs =
- new ArrayList<HtmlTemplateArgument>();
- private final HtmlTemplates templates;
-
- public HtmlTemplate(String html, Tokenator tokenator, HtmlTemplates templates)
- throws IllegalArgumentException {
- if (html == null) {
- throw new IllegalArgumentException("Template html cannot be null");
- }
- if (tokenator == null) {
- throw new IllegalArgumentException("Template tokenator cannot be null");
- }
- if (templates == null) {
- throw new IllegalArgumentException("HtmlTemplates container cannot be null");
- }
-
- this.templates = templates;
- methodName = "html" + this.templates.nextTemplateId();
-
- populateArgMap(html,tokenator);
-
- template.add("@Template(\"" + addTemplatePlaceholders(html) + "\")");
- template.add("SafeHtml " + methodName + "(" + getTemplateArgs() + ");");
- template.add(" ");
- }
-
- public List<String> getTemplate() {
- return template;
- }
-
- /**
- * Writes all templates to the provided {@link IndentedWriter}.
- *
- * @param w the writer to write the template to
- */
- public void writeTemplate(IndentedWriter w) {
- for (String s : template) {
- w.write(s);
- }
- }
-
- /**
- * Creates the template method invocation.
- *
- * @return String the template method call with parameters
- */
- public String writeTemplateCall() {
- return "template." + methodName + "(" + getSafeHtmlArgs()
- + ")";
- }
-
- /**
- * Replaces string tokens with {} placeholders for SafeHtml templating.
- *
- * @return the rendering string, with tokens replaced by {} placeholders
- */
- private String addTemplatePlaceholders(String html) {
- String rtn = Tokenator.detokenate(html, new Resolver() {
- int tokenId = 0;
- public String resolveToken(String token) {
- return "{" + tokenId++ + "}";
- }
- });
- return rtn;
- }
-
- /**
- * Retrieves the arguments for SafeHtml template function call from
- * the {@link Tokenator}.
- */
- private String getSafeHtmlArgs() {
- StringBuilder b = new StringBuilder();
-
- for (HtmlTemplateArgument arg : methodArgs) {
- if (b.length() > 0) {
- b.append(", ");
- }
- b.append(arg.getArg());
- }
-
- return b.toString();
- }
-
- /**
- * Creates the argument string for the generated SafeHtmlTemplate function.
- */
- private String getTemplateArgs() {
- StringBuilder b = new StringBuilder();
- int i = 0;
-
- for (HtmlTemplateArgument arg : methodArgs) {
- if (b.length() > 0) {
- b.append(", ");
- }
- b.append(arg.getType() + " arg" + i);
- i++;
- }
-
- return b.toString();
- }
-
- private void populateArgMap(String s, Tokenator t) {
- if (t != null) {
- List<String> args = t.getOrderedValues(s);
-
- for (String arg : args) {
- if (templates.isSafeConstant(arg)) {
- methodArgs.add(HtmlTemplateArgument.forHtml(arg));
- } else {
- methodArgs.add(HtmlTemplateArgument.forString(arg.substring(4,
- arg.length() - 4)));
- }
- }
- }
- }
-}
-
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplateArgument.java b/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplateArgument.java
deleted file mode 100644
index 49741ca..0000000
--- a/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplateArgument.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.model;
-
-/**
- * Container class for a UiBinder SafeHtml template argument.
- */
-public class HtmlTemplateArgument {
- static HtmlTemplateArgument forHtml(String arg) {
- return new HtmlTemplateArgument("SafeHtml", arg);
- }
- static HtmlTemplateArgument forString(String arg) {
- return new HtmlTemplateArgument("String", arg);
- }
-
- private final String arg;
-
- private final String type;
-
- private HtmlTemplateArgument(String type, String arg) {
- this.type = type;
- this.arg = arg;
- }
-
- public String getArg() {
- return arg;
- }
-
- public String getType() {
- return type;
- }
-}
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplateMethodWriter.java b/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplateMethodWriter.java
new file mode 100644
index 0000000..0a6dea0
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplateMethodWriter.java
@@ -0,0 +1,253 @@
+/*
+ * 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.model;
+
+import com.google.gwt.uibinder.attributeparsers.SafeUriAttributeParser;
+import com.google.gwt.uibinder.rebind.FieldReference;
+import com.google.gwt.uibinder.rebind.IndentedWriter;
+import com.google.gwt.uibinder.rebind.Tokenator;
+import com.google.gwt.uibinder.rebind.Tokenator.Resolver;
+import com.google.gwt.uibinder.rebind.Tokenator.ValueAndInfo;
+import com.google.gwt.uibinder.rebind.XMLElement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Models an individual SafeHtmlTemplates method in an
+ * {@link HtmlTemplatesWriter}.
+ */
+public class HtmlTemplateMethodWriter {
+ private class Argument {
+ final XMLElement source;
+ /**
+ * Type of the parameter.
+ */
+ final ArgumentType type;
+ /**
+ * The expression to fill this parameter when the template method is called.
+ */
+ final String expression;
+
+ Argument(XMLElement source, ArgumentType type, String expression) {
+ this.source = source;
+ this.type = type;
+ this.expression = expression;
+ }
+
+ public XMLElement getSource() {
+ return source;
+ }
+
+ @Override
+ public String toString() {
+ return "HtmlTemplateMethod.Argument: " + expression;
+ }
+
+ FieldReference getFieldReference() {
+ FieldReference fieldReference = templates.getFieldManager().findFieldReference(expression);
+ return fieldReference;
+ }
+ }
+
+ private enum ArgumentType {
+ STRING("String"), HTML("SafeHtml"), URI("SafeUri");
+
+ final String typeString;
+
+ ArgumentType(String typeString) {
+ this.typeString = typeString;
+ }
+
+ @Override
+ public String toString() {
+ return typeString;
+ }
+ }
+
+ private final List<String> strings = new ArrayList<String>();
+ private final String methodName;
+ private final ArrayList<Argument> methodArgs = new ArrayList<Argument>();
+ private final HtmlTemplatesWriter templates;
+ private final String html;
+ private final Tokenator tokenator;
+ private boolean argumentsResolved = false;
+
+ public HtmlTemplateMethodWriter(String html, Tokenator tokenator, HtmlTemplatesWriter templates)
+ throws IllegalArgumentException {
+ assertNotNull("html", html);
+ assertNotNull("tokenator", tokenator);
+ assertNotNull("templates", templates);
+
+ this.templates = templates;
+ methodName = "html" + this.templates.nextTemplateId();
+
+ this.html = html;
+ this.tokenator = tokenator;
+ }
+
+ public String getDirectTemplateCall() {
+ ensureArgumentsResolved();
+ return String.format("template.%s(%s)", methodName, getTemplateCallArguments());
+ }
+
+ /**
+ * Returns an expression that will return the results of a call to this
+ * method.
+ *
+ * @return
+ */
+ public String getIndirectTemplateCall() {
+ return "template_" + methodName + "()";
+ }
+
+ public boolean isStringReference(Argument arg) {
+ FieldReference fieldReference = arg.getFieldReference();
+ return fieldReference != null
+ && fieldReference.getReturnType().getSimpleSourceName().equals("String");
+ }
+
+ /**
+ * Creates the template method invocation.
+ *
+ * @param w
+ *
+ * @return String the template method call with parameters
+ */
+ public void writeTemplateCaller(IndentedWriter w) {
+ ensureArgumentsResolved();
+
+ w.write("SafeHtml template_%s() {", methodName);
+ w.indent();
+ w.write("return %s;", getDirectTemplateCall());
+ w.outdent();
+ w.write("}");
+ }
+
+ /**
+ * Writes all templates to the provided {@link IndentedWriter}.
+ *
+ * @param w the writer to write the template to
+ */
+ public void writeTemplateMethod(IndentedWriter w) {
+ ensureArgumentsResolved();
+ for (String s : strings) {
+ w.write(s);
+ }
+ }
+
+ /**
+ * Creates the argument string for the generated SafeHtmlTemplate function.
+ */
+ private String addTemplateParameters() {
+ StringBuilder b = new StringBuilder();
+ int i = 0;
+
+ for (Argument arg : methodArgs) {
+ if (b.length() > 0) {
+ b.append(", ");
+ }
+ b.append(arg.type + " arg" + i);
+ i++;
+ }
+
+ return b.toString();
+ }
+
+ /**
+ * Replaces string tokens with {} placeholders for SafeHtml templating.
+ *
+ * @return the rendering string, with tokens replaced by {} placeholders
+ */
+ private String addTemplatePlaceholders(String html) {
+ String rtn = Tokenator.detokenate(html, new Resolver() {
+ int tokenId = 0;
+
+ public String resolveToken(String token) {
+ return "{" + tokenId++ + "}";
+ }
+ });
+ return rtn;
+ }
+
+ private void assertNotNull(String name, Object value) {
+ if (value == null) {
+ throw new IllegalArgumentException(name + " cannot be null");
+ }
+ }
+
+ private void ensureArgumentsResolved() {
+ if (argumentsResolved) {
+ return;
+ }
+
+ if (tokenator != null) {
+ List<ValueAndInfo> valuesAndSources = tokenator.getOrderedValues(html);
+
+ for (ValueAndInfo valueAndSource : valuesAndSources) {
+ XMLElement source = (XMLElement) valueAndSource.info;
+ String expression = valueAndSource.value;
+
+ if (templates.isSafeConstant(expression)) {
+ methodArgs.add(new Argument(source, ArgumentType.HTML, expression));
+ } else if (templates.isUri(expression)) {
+ methodArgs.add(new Argument(source, ArgumentType.URI, expression));
+ } else {
+ // Nasty. Chop off the "" + stuff surrounding spring expressions
+ String guts = expression.substring(4, expression.length() - 4);
+ methodArgs.add(new Argument(source, ArgumentType.STRING, guts));
+ }
+ }
+ }
+
+ strings.add("@Template(\"" + addTemplatePlaceholders(html) + "\")");
+ strings.add("SafeHtml " + methodName + "(" + addTemplateParameters() + ");");
+ strings.add(" ");
+
+ argumentsResolved = true;
+ }
+
+ /**
+ * Retrieves the arguments for SafeHtml template function call from the
+ * {@link Tokenator}.
+ */
+ private String getTemplateCallArguments() {
+ StringBuilder b = new StringBuilder();
+
+ for (Argument arg : methodArgs) {
+ if (b.length() > 0) {
+ b.append(", ");
+ }
+ String argExpression = processArgExpression(arg);
+
+ b.append(argExpression);
+ }
+
+ return b.toString();
+ }
+
+ private String processArgExpression(Argument arg) {
+ String raw = arg.expression;
+ if (arg.type == ArgumentType.URI) {
+ if (isStringReference(arg)) {
+ return SafeUriAttributeParser.wrapUnsafeStringAndWarn(templates.getLogger(),
+ arg.getSource(), raw);
+ }
+ }
+ return raw;
+ }
+}
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplates.java b/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplates.java
deleted file mode 100644
index 908fa60..0000000
--- a/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplates.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.model;
-
-import com.google.gwt.uibinder.rebind.IndentedWriter;
-import com.google.gwt.uibinder.rebind.Tokenator;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Model class for SafeHtml templates used in generated
- * UiBinder rendering implementation.
- */
-
-public class HtmlTemplates {
- private final List<HtmlTemplate> htmlTemplates = new ArrayList<HtmlTemplate>();
- private final Set<String> safeConstantTokens = new HashSet<String>();
-
- public HtmlTemplates() {
- }
-
- /**
- * Add a SafeHtml template and an instance method for invoking the template
- * to the generated BinderImpl class. These templates are declared at the
- * beginning of the class and instantiated with a GWT.create(Template.class
- * call.
- * <p>
- * Note that the UiBinder#tokenator is used to determine the arguments to
- * the generated SafeHtml template.
- *
- * @return String the function to call this template
- */
- public String addSafeHtmlTemplate(String html, Tokenator t)
- throws IllegalArgumentException {
- if (html == null) {
- throw new IllegalArgumentException("Template html cannot be null");
- }
- if (t == null) {
- throw new IllegalArgumentException("Template tokenator cannot be null");
- }
-
- HtmlTemplate ht = new HtmlTemplate(html, t, this);
- htmlTemplates.add(ht);
-
- return ht.writeTemplateCall();
- }
-
- public int getNumTemplates() {
- return htmlTemplates.size();
- }
-
- public List<HtmlTemplate> getTemplates() {
- return htmlTemplates;
- }
-
- public boolean isEmpty() {
- return htmlTemplates.isEmpty();
- }
-
- public boolean isSafeConstant(String token) {
- return safeConstantTokens.contains(token);
- }
-
- public void noteSafeConstant(String token) {
- safeConstantTokens.add(token);
- }
-
- public void writeTemplates(IndentedWriter w) {
- for (HtmlTemplate t : htmlTemplates) {
- t.writeTemplate(w);
- }
- }
-
- /**
- * Increment the total number of templates.
- */
- protected int nextTemplateId() {
- return htmlTemplates.size() + 1;
- }
-}
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplatesWriter.java b/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplatesWriter.java
new file mode 100644
index 0000000..12db0f0
--- /dev/null
+++ b/user/src/com/google/gwt/uibinder/rebind/model/HtmlTemplatesWriter.java
@@ -0,0 +1,138 @@
+/*
+ * 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.model;
+
+import com.google.gwt.uibinder.rebind.FieldManager;
+import com.google.gwt.uibinder.rebind.IndentedWriter;
+import com.google.gwt.uibinder.rebind.MortalLogger;
+import com.google.gwt.uibinder.rebind.Tokenator;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Model class for SafeHtml templates used in generated UiBinder rendering
+ * implementation.
+ */
+
+public class HtmlTemplatesWriter {
+ private final List<HtmlTemplateMethodWriter> htmlTemplates = new ArrayList<HtmlTemplateMethodWriter>();
+ private final Set<String> safeConstantExpressions = new HashSet<String>();
+ private final Set<String> uriExpressions = new HashSet<String>();
+ private final FieldManager fieldManager;
+ private final MortalLogger logger;
+
+ public HtmlTemplatesWriter(FieldManager fieldManager, MortalLogger logger) {
+ this.fieldManager = fieldManager;
+ this.logger = logger;
+ }
+
+ /**
+ * Add a SafeHtml template and an instance method for invoking the template to
+ * the generated BinderImpl class. These templates are declared at the
+ * beginning of the class and instantiated with a GWT.create(Template.class
+ * call.
+ * <p>
+ * Note that the UiBinder#tokenator is used to determine the arguments to the
+ * generated SafeHtml template.
+ *
+ * @return the object that models this template method
+ */
+ public HtmlTemplateMethodWriter addSafeHtmlTemplate(String html, Tokenator t) throws IllegalArgumentException {
+ if (html == null) {
+ throw new IllegalArgumentException("Template html cannot be null");
+ }
+ if (t == null) {
+ throw new IllegalArgumentException("Template tokenator cannot be null");
+ }
+
+ HtmlTemplateMethodWriter method = new HtmlTemplateMethodWriter(html, t, this);
+ htmlTemplates.add(method);
+
+ return method;
+ }
+
+ public int getNumTemplates() {
+ return htmlTemplates.size();
+ }
+
+ public List<HtmlTemplateMethodWriter> getTemplates() {
+ return htmlTemplates;
+ }
+
+ public boolean isEmpty() {
+ return htmlTemplates.isEmpty();
+ }
+
+ public boolean isSafeConstant(String expression) {
+ return safeConstantExpressions.contains(expression);
+ }
+
+ public boolean isUri(String expression) {
+ return uriExpressions.contains(expression);
+ }
+
+ public void noteSafeConstant(String expression) {
+ safeConstantExpressions.add(expression);
+ }
+
+ public void noteUri(String expression) {
+ uriExpressions.add(expression);
+ }
+
+ /**
+ * Write the SafeHtmlTemplates interface and its GWT.create() call.
+ */
+ public void writeInterface(IndentedWriter w) {
+ w.write("interface Template extends SafeHtmlTemplates {");
+ w.indent();
+ for (HtmlTemplateMethodWriter t : htmlTemplates) {
+ t.writeTemplateMethod(w);
+ }
+ w.outdent();
+ w.write("}");
+ w.newline();
+ w.write("Template template = GWT.create(Template.class);");
+ }
+
+ /**
+ * Write the no-arg methods that that call each template method. These are
+ * called from HTMLPanelParser constructors and such.
+ */
+ public void writeTemplateCallers(IndentedWriter w) {
+ for (HtmlTemplateMethodWriter t1 : htmlTemplates) {
+ t1.writeTemplateCaller(w);
+ }
+ }
+
+ FieldManager getFieldManager() {
+ return fieldManager;
+ }
+
+ MortalLogger getLogger() {
+ return logger;
+ }
+
+ /**
+ * Increment the total number of templates.
+ */
+ int nextTemplateId() {
+ return htmlTemplates.size() + 1;
+ }
+}
diff --git a/user/test/com/google/gwt/uibinder/LazyWidgetBuilderSuite.java b/user/test/com/google/gwt/uibinder/LazyWidgetBuilderSuite.java
index 23342b5..fd64780 100644
--- a/user/test/com/google/gwt/uibinder/LazyWidgetBuilderSuite.java
+++ b/user/test/com/google/gwt/uibinder/LazyWidgetBuilderSuite.java
@@ -17,6 +17,7 @@
import com.google.gwt.junit.tools.GWTTestSuite;
import com.google.gwt.uibinder.test.client.IsRenderableIntegrationTest;
+import com.google.gwt.uibinder.test.client.LazyWidgetBuilderSafeUriIntegrationTest;
import com.google.gwt.uibinder.test.client.SafeHtmlAsComponentsTest;
import com.google.gwt.uibinder.test.client.UiRendererTest;
@@ -31,6 +32,7 @@
"Tests that rely on the useLazyWidgetBuilders switch");
suite.addTestSuite(IsRenderableIntegrationTest.class);
+ suite.addTestSuite(LazyWidgetBuilderSafeUriIntegrationTest.class);
suite.addTestSuite(SafeHtmlAsComponentsTest.class);
suite.addTestSuite(UiRendererTest.class);
diff --git a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
index 5bf6ee3..a45e261 100644
--- a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
+++ b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
@@ -21,6 +21,7 @@
import com.google.gwt.uibinder.attributeparsers.IntAttributeParserTest;
import com.google.gwt.uibinder.attributeparsers.IntPairAttributeParserTest;
import com.google.gwt.uibinder.attributeparsers.LengthAttributeParserTest;
+import com.google.gwt.uibinder.attributeparsers.SafeUriAttributeParserTest;
import com.google.gwt.uibinder.attributeparsers.StrictAttributeParserTest;
import com.google.gwt.uibinder.attributeparsers.StringAttributeParserTest;
import com.google.gwt.uibinder.attributeparsers.TextAlignConstantParserTest;
@@ -56,6 +57,7 @@
import com.google.gwt.uibinder.rebind.UiBinderParserUiWithTest;
import com.google.gwt.uibinder.rebind.UiRendererValidationTest;
import com.google.gwt.uibinder.rebind.XMLElementTest;
+import com.google.gwt.uibinder.rebind.model.HtmlTemplatesTest;
import com.google.gwt.uibinder.rebind.model.OwnerClassTest;
import com.google.gwt.uibinder.rebind.model.OwnerFieldClassTest;
import com.google.gwt.uibinder.rebind.model.OwnerFieldTest;
@@ -82,6 +84,7 @@
suite.addTestSuite(TypeOracleUtilsTest.class);
suite.addTestSuite(UiBinderParserUiWithTest.class);
suite.addTestSuite(UiRendererValidationTest.class);
+ suite.addTestSuite(HtmlTemplatesTest.class);
// model
suite.addTestSuite(OwnerClassTest.class);
@@ -90,15 +93,16 @@
// attributeparsers
suite.addTestSuite(CssNameConverterTest.class);
+ suite.addTestSuite(FieldReferenceConverterTest.class);
suite.addTestSuite(IntAttributeParserTest.class);
suite.addTestSuite(IntPairAttributeParserTest.class);
- suite.addTestSuite(FieldReferenceConverterTest.class);
+ suite.addTestSuite(HorizontalAlignmentConstantParserTest.class);
+ suite.addTestSuite(LengthAttributeParserTest.class);
+ suite.addTestSuite(SafeUriAttributeParserTest.class);
suite.addTestSuite(StrictAttributeParserTest.class);
suite.addTestSuite(StringAttributeParserTest.class);
- suite.addTestSuite(LengthAttributeParserTest.class);
- suite.addTestSuite(HorizontalAlignmentConstantParserTest.class);
- suite.addTestSuite(VerticalAlignmentConstantParserTest.class);
suite.addTestSuite(TextAlignConstantParserTest.class);
+ suite.addTestSuite(VerticalAlignmentConstantParserTest.class);
// elementparsers
suite.addTestSuite(AbsolutePanelParserTest.class);
diff --git a/user/test/com/google/gwt/uibinder/UiBinderSuite.java b/user/test/com/google/gwt/uibinder/UiBinderSuite.java
index a5d4678..98e77d3 100644
--- a/user/test/com/google/gwt/uibinder/UiBinderSuite.java
+++ b/user/test/com/google/gwt/uibinder/UiBinderSuite.java
@@ -19,6 +19,7 @@
import com.google.gwt.uibinder.client.UiBinderUtilTest;
import com.google.gwt.uibinder.test.client.InnerWidgetTest;
import com.google.gwt.uibinder.test.client.ParameterizedWidgetsTest;
+import com.google.gwt.uibinder.test.client.SafeUriIntegrationTest;
import com.google.gwt.uibinder.test.client.UiBinderTest;
import com.google.gwt.uibinder.test.client.UiChildTest;
@@ -33,11 +34,12 @@
"Integration tests for UiBinder");
suite.addTestSuite(InnerWidgetTest.class);
+ suite.addTestSuite(ParameterizedWidgetsTest.class);
+ suite.addTestSuite(SafeUriIntegrationTest.class);
suite.addTestSuite(UiBinderTest.class);
suite.addTestSuite(UiBinderUtilTest.class);
suite.addTestSuite(UiChildTest.class);
- suite.addTestSuite(ParameterizedWidgetsTest.class);
-
+
return suite;
}
diff --git a/user/test/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverterTest.java b/user/test/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverterTest.java
index c26bf0d..b7cf434 100644
--- a/user/test/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverterTest.java
+++ b/user/test/com/google/gwt/uibinder/attributeparsers/FieldReferenceConverterTest.java
@@ -33,7 +33,7 @@
return String.format(" & %s & ", reference);
}
- public JType getType() {
+ public JType[] getTypes() {
return null;
}
};
diff --git a/user/test/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParserTest.java b/user/test/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParserTest.java
index f224535..6b80a55 100644
--- a/user/test/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParserTest.java
+++ b/user/test/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParserTest.java
@@ -46,27 +46,27 @@
}
public void testFriendlyNames() throws UnableToCompleteException {
- assertEquals(HHA + ".ALIGN_LEFT", parser.parse("left"));
- assertEquals(HHA + ".ALIGN_CENTER", parser.parse("center"));
- assertEquals(HHA + ".ALIGN_RIGHT", parser.parse("right"));
- assertEquals(HHA + ".ALIGN_JUSTIFY", parser.parse("justify"));
+ assertEquals(HHA + ".ALIGN_LEFT", parser.parse(null, "left"));
+ assertEquals(HHA + ".ALIGN_CENTER", parser.parse(null, "center"));
+ assertEquals(HHA + ".ALIGN_RIGHT", parser.parse(null, "right"));
+ assertEquals(HHA + ".ALIGN_JUSTIFY", parser.parse(null, "justify"));
// capitalized
- assertEquals(HHA + ".ALIGN_LEFT", parser.parse("Left"));
- assertEquals(HHA + ".ALIGN_CENTER", parser.parse("Center"));
- assertEquals(HHA + ".ALIGN_RIGHT", parser.parse("Right"));
- assertEquals(HHA + ".ALIGN_JUSTIFY", parser.parse("Justify"));
+ assertEquals(HHA + ".ALIGN_LEFT", parser.parse(null, "Left"));
+ assertEquals(HHA + ".ALIGN_CENTER", parser.parse(null, "Center"));
+ assertEquals(HHA + ".ALIGN_RIGHT", parser.parse(null, "Right"));
+ assertEquals(HHA + ".ALIGN_JUSTIFY", parser.parse(null, "Justify"));
}
public void testUglyNames() throws UnableToCompleteException {
- assertEquals(HHA + ".ALIGN_LEFT", parser.parse("ALIGN_LEFT"));
- assertEquals(HHA + ".ALIGN_CENTER", parser.parse("ALIGN_CENTER"));
- assertEquals(HHA + ".ALIGN_RIGHT", parser.parse("ALIGN_RIGHT"));
- assertEquals(HHA + ".ALIGN_JUSTIFY", parser.parse("ALIGN_JUSTIFY"));
+ assertEquals(HHA + ".ALIGN_LEFT", parser.parse(null, "ALIGN_LEFT"));
+ assertEquals(HHA + ".ALIGN_CENTER", parser.parse(null, "ALIGN_CENTER"));
+ assertEquals(HHA + ".ALIGN_RIGHT", parser.parse(null, "ALIGN_RIGHT"));
+ assertEquals(HHA + ".ALIGN_JUSTIFY", parser.parse(null, "ALIGN_JUSTIFY"));
}
public void testBad() {
try {
- parser.parse("fnord");
+ parser.parse(null, "fnord");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
@@ -74,6 +74,6 @@
}
public void testFieldRef() throws UnableToCompleteException {
- assertEquals("foo.bar().baz()", parser.parse("{foo.bar.baz}"));
+ assertEquals("foo.bar().baz()", parser.parse(null, "{foo.bar.baz}"));
}
}
diff --git a/user/test/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParser_Test.java b/user/test/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParser_Test.java
index 69bcdfb..25a74ab 100644
--- a/user/test/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParser_Test.java
+++ b/user/test/com/google/gwt/uibinder/attributeparsers/HorizontalAlignmentConstantParser_Test.java
@@ -44,14 +44,14 @@
}
public void testGood() throws UnableToCompleteException {
- assertEquals(HHA + ".ALIGN_LEFT", parser.parse("ALIGN_LEFT"));
- assertEquals(HHA + ".ALIGN_CENTER", parser.parse("ALIGN_CENTER"));
- assertEquals(HHA + ".ALIGN_RIGHT", parser.parse("ALIGN_RIGHT"));
+ assertEquals(HHA + ".ALIGN_LEFT", parser.parse(null, "ALIGN_LEFT"));
+ assertEquals(HHA + ".ALIGN_CENTER", parser.parse(null, "ALIGN_CENTER"));
+ assertEquals(HHA + ".ALIGN_RIGHT", parser.parse(null, "ALIGN_RIGHT"));
}
public void testBad() {
try {
- parser.parse("fnord");
+ parser.parse(null, "fnord");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
@@ -59,6 +59,6 @@
}
public void testFieldRef() throws UnableToCompleteException {
- assertEquals("foo.bar().baz()", parser.parse("{foo.bar.baz}"));
+ assertEquals("foo.bar().baz()", parser.parse(null, "{foo.bar.baz}"));
}
}
diff --git a/user/test/com/google/gwt/uibinder/attributeparsers/IntAttributeParserTest.java b/user/test/com/google/gwt/uibinder/attributeparsers/IntAttributeParserTest.java
index 8dac439..b5687c9 100644
--- a/user/test/com/google/gwt/uibinder/attributeparsers/IntAttributeParserTest.java
+++ b/user/test/com/google/gwt/uibinder/attributeparsers/IntAttributeParserTest.java
@@ -42,13 +42,13 @@
}
public void testGood() throws UnableToCompleteException {
- assertEquals("1234", parser.parse("1234"));
- assertEquals("-4321", parser.parse("-4321"));
+ assertEquals("1234", parser.parse(null, "1234"));
+ assertEquals("-4321", parser.parse(null, "-4321"));
}
public void testBad() {
try {
- parser.parse("fnord");
+ parser.parse(null, "fnord");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
@@ -56,6 +56,6 @@
}
public void testFieldRef() throws UnableToCompleteException {
- assertEquals("(int)foo.bar().baz()", parser.parse("{foo.bar.baz}"));
+ assertEquals("(int)foo.bar().baz()", parser.parse(null, "{foo.bar.baz}"));
}
}
diff --git a/user/test/com/google/gwt/uibinder/attributeparsers/IntPairAttributeParserTest.java b/user/test/com/google/gwt/uibinder/attributeparsers/IntPairAttributeParserTest.java
index 25ff237..5349145 100644
--- a/user/test/com/google/gwt/uibinder/attributeparsers/IntPairAttributeParserTest.java
+++ b/user/test/com/google/gwt/uibinder/attributeparsers/IntPairAttributeParserTest.java
@@ -48,37 +48,37 @@
}
public void testGood() throws UnableToCompleteException {
- assertEquals("1, 1", parser.parse("1, 1"));
- assertEquals("123, 456", parser.parse("123, 456"));
+ assertEquals("1, 1", parser.parse(null, "1, 1"));
+ assertEquals("123, 456", parser.parse(null, "123, 456"));
assertEquals("(int)able.baker(), (int)charlie.delta()",
- parser.parse("{able.baker}, {charlie.delta}"));
- assertEquals("0001, 0002", parser.parse("0001, 0002"));
+ parser.parse(null, "{able.baker}, {charlie.delta}"));
+ assertEquals("0001, 0002", parser.parse(null, "0001, 0002"));
}
public void testBad() {
try {
- parser.parse("fnord");
+ parser.parse(null, "fnord");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
}
try {
- parser.parse("1, 2, 3");
+ parser.parse(null, "1, 2, 3");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
}
try {
- parser.parse("1");
+ parser.parse(null, "1");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
}
try {
- parser.parse("1.2, 3.4");
+ parser.parse(null, "1.2, 3.4");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
diff --git a/user/test/com/google/gwt/uibinder/attributeparsers/LengthAttributeParserTest.java b/user/test/com/google/gwt/uibinder/attributeparsers/LengthAttributeParserTest.java
index ed2d73b..901b084 100644
--- a/user/test/com/google/gwt/uibinder/attributeparsers/LengthAttributeParserTest.java
+++ b/user/test/com/google/gwt/uibinder/attributeparsers/LengthAttributeParserTest.java
@@ -57,49 +57,49 @@
}
public void testGood() throws UnableToCompleteException {
- assertEquals(lengthString("0", "PX"), parser.parse("0"));
- assertEquals(lengthString("0", "PT"), parser.parse("0pt"));
+ assertEquals(lengthString("0", "PX"), parser.parse(null, "0"));
+ assertEquals(lengthString("0", "PT"), parser.parse(null, "0pt"));
- assertEquals(lengthString("1", "PX"), parser.parse("1"));
+ assertEquals(lengthString("1", "PX"), parser.parse(null, "1"));
- assertEquals(lengthString("1", "PX"), parser.parse("1px"));
- assertEquals(lengthString("1", "PCT"), parser.parse("1%"));
- assertEquals(lengthString("1", "CM"), parser.parse("1cm"));
- assertEquals(lengthString("1", "MM"), parser.parse("1mm"));
- assertEquals(lengthString("1", "IN"), parser.parse("1in"));
- assertEquals(lengthString("1", "PC"), parser.parse("1pc"));
- assertEquals(lengthString("1", "PT"), parser.parse("1pt"));
- assertEquals(lengthString("1", "EM"), parser.parse("1em"));
- assertEquals(lengthString("1", "EX"), parser.parse("1ex"));
+ assertEquals(lengthString("1", "PX"), parser.parse(null, "1px"));
+ assertEquals(lengthString("1", "PCT"), parser.parse(null, "1%"));
+ assertEquals(lengthString("1", "CM"), parser.parse(null, "1cm"));
+ assertEquals(lengthString("1", "MM"), parser.parse(null, "1mm"));
+ assertEquals(lengthString("1", "IN"), parser.parse(null, "1in"));
+ assertEquals(lengthString("1", "PC"), parser.parse(null, "1pc"));
+ assertEquals(lengthString("1", "PT"), parser.parse(null, "1pt"));
+ assertEquals(lengthString("1", "EM"), parser.parse(null, "1em"));
+ assertEquals(lengthString("1", "EX"), parser.parse(null, "1ex"));
- assertEquals(lengthString("1", "PX"), parser.parse("1PX"));
- assertEquals(lengthString("1", "PCT"), parser.parse("1PCT"));
- assertEquals(lengthString("1", "CM"), parser.parse("1CM"));
- assertEquals(lengthString("1", "MM"), parser.parse("1MM"));
- assertEquals(lengthString("1", "IN"), parser.parse("1IN"));
- assertEquals(lengthString("1", "PC"), parser.parse("1PC"));
- assertEquals(lengthString("1", "PT"), parser.parse("1PT"));
- assertEquals(lengthString("1", "EM"), parser.parse("1EM"));
- assertEquals(lengthString("1", "EX"), parser.parse("1EX"));
+ assertEquals(lengthString("1", "PX"), parser.parse(null, "1PX"));
+ assertEquals(lengthString("1", "PCT"), parser.parse(null, "1PCT"));
+ assertEquals(lengthString("1", "CM"), parser.parse(null, "1CM"));
+ assertEquals(lengthString("1", "MM"), parser.parse(null, "1MM"));
+ assertEquals(lengthString("1", "IN"), parser.parse(null, "1IN"));
+ assertEquals(lengthString("1", "PC"), parser.parse(null, "1PC"));
+ assertEquals(lengthString("1", "PT"), parser.parse(null, "1PT"));
+ assertEquals(lengthString("1", "EM"), parser.parse(null, "1EM"));
+ assertEquals(lengthString("1", "EX"), parser.parse(null, "1EX"));
- assertEquals(lengthString("2.5", "EM"), parser.parse("2.5em"));
- assertEquals(lengthString("+1", "EM"), parser.parse("+1em"));
- assertEquals(lengthString("-1", "EM"), parser.parse("-1em"));
+ assertEquals(lengthString("2.5", "EM"), parser.parse(null, "2.5em"));
+ assertEquals(lengthString("+1", "EM"), parser.parse(null, "+1em"));
+ assertEquals(lengthString("-1", "EM"), parser.parse(null, "-1em"));
- assertEquals(lengthString("1", "EM"), parser.parse("1 em"));
+ assertEquals(lengthString("1", "EM"), parser.parse(null, "1 em"));
assertEquals("(double)foo.value(), " + UNIT + ".PX",
- parser.parse("{foo.value}px"));
+ parser.parse(null, "{foo.value}px"));
assertEquals("1, foo.unit()",
- parser.parse("1{foo.unit}"));
+ parser.parse(null, "1{foo.unit}"));
assertEquals("(double)foo.value(), foo.unit()",
- parser.parse("{foo.value}{foo.unit}"));
+ parser.parse(null, "{foo.value}{foo.unit}"));
}
public void testBad() {
// Garbage.
try {
- parser.parse("fnord");
+ parser.parse(null, "fnord");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
@@ -107,7 +107,7 @@
// Non-decimal value.
try {
- parser.parse("xpx");
+ parser.parse(null, "xpx");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
@@ -115,7 +115,7 @@
// Raw unit, no value.
try {
- parser.parse("px");
+ parser.parse(null, "px");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
@@ -123,7 +123,7 @@
// 0, but with invalid unit.
try {
- parser.parse("0foo");
+ parser.parse(null, "0foo");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
@@ -131,14 +131,14 @@
// Too many braces cases.
try {
- parser.parse("{{foo.value}px");
+ parser.parse(null, "{{foo.value}px");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
}
try {
- parser.parse("1{{foo.unit}");
+ parser.parse(null, "1{{foo.unit}");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
diff --git a/user/test/com/google/gwt/uibinder/attributeparsers/SafeUriAttributeParserTest.java b/user/test/com/google/gwt/uibinder/attributeparsers/SafeUriAttributeParserTest.java
new file mode 100644
index 0000000..54eb8ca
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/attributeparsers/SafeUriAttributeParserTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2009 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.attributeparsers;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.javac.CompilationStateBuilder;
+import com.google.gwt.safehtml.shared.SafeUri;
+import com.google.gwt.uibinder.rebind.MockMortalLogger;
+import com.google.gwt.uibinder.test.UiJavaResources;
+
+import junit.framework.TestCase;
+
+/**
+ * Test parsing SafeUri attributes.
+ */
+public class SafeUriAttributeParserTest extends TestCase {
+ private SafeUriAttributeParser parserForHtml;
+ private SafeUriAttributeParser parserForWidgets;
+ private MockMortalLogger logger = new MockMortalLogger();
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ CompilationState state =
+ CompilationStateBuilder.buildFrom(TreeLogger.NULL, UiJavaResources.getUiResources());
+ TypeOracle types = state.getTypeOracle();
+ JType stringType = types.parse(String.class.getName());
+ JType safeUriType = types.parse(SafeUri.class.getName());
+ StringAttributeParser stringParser =
+ new StringAttributeParser(new FieldReferenceConverter(null), stringType);
+ parserForHtml =
+ new SafeUriAttributeParser(stringParser, new FieldReferenceConverter(null), safeUriType,
+ stringType, logger);
+ parserForWidgets =
+ new SafeUriAttributeParser(stringParser, new FieldReferenceConverter(null), safeUriType,
+ logger);
+ }
+
+ public void testLiteral() throws UnableToCompleteException {
+ assertEquals("UriUtils.fromSafeConstant(\"hi mom\")", parserForHtml.parse(null, "hi mom"));
+ assertEquals("UriUtils.fromSafeConstant(\"hi mom\")", parserForWidgets.parse(null, "hi mom"));
+ // Don't get caught out by escaped braces
+ assertEquals("UriUtils.fromSafeConstant(\"hi {foo.bar.baz} friend\")", parserForHtml.parse(
+ null, "hi {{foo.bar.baz} friend"));
+ assertEquals("UriUtils.fromSafeConstant(\"hi {foo.bar.baz} friend\")", parserForWidgets.parse(
+ null, "hi {{foo.bar.baz} friend"));
+ }
+
+ public void testFieldRef() throws UnableToCompleteException {
+ assertEquals("foo.bar().baz()", parserForHtml.parse(null, "{foo.bar.baz}"));
+ assertEquals("foo.bar().baz()", parserForWidgets.parse(null, "{foo.bar.baz}"));
+ // Don't get caught out by escaped braces
+ assertEquals("UriUtils.fromSafeConstant(\"{foo.bar.baz}\")", parserForHtml.parse(null, "{{foo.bar.baz}"));
+ assertEquals("UriUtils.fromSafeConstant(\"{foo.bar.baz}\")", parserForWidgets.parse(null, "{{foo.bar.baz}"));
+ }
+
+ public void testConcatenatedFieldRefAllowed() throws UnableToCompleteException {
+ assertEquals("UriUtils.fromString(\"hi \" + foo.bar().baz() + \" friend\")",
+ parserForHtml.parse(null, "hi {foo.bar.baz} friend"));
+ assertNotNull(logger.warned);
+ logger.warned = null;
+ assertEquals(
+ "UriUtils.fromString(\"hi \" + foo.bar().baz() + \" friend \" + boo.bahh() + \" baz\")",
+ parserForHtml.parse(null, "hi {foo.bar.baz} friend {boo.bahh} baz"));
+ assertNotNull(logger.warned);
+ }
+
+ public void testConcatenatedFieldRefNotOkay() {
+ try {
+ parserForWidgets.parse(null, "hi {foo.bar.baz} friend");
+ fail("Expected UnableToCompleteException");
+ } catch (UnableToCompleteException e) {
+ // pass
+ }
+ try {
+ parserForWidgets.parse(null, "hi {foo.bar.baz} friend {boo.bahh} baz");
+ fail("Expected UnableToCompleteException");
+ } catch (UnableToCompleteException e) {
+ // pass
+ }
+ }
+}
diff --git a/user/test/com/google/gwt/uibinder/attributeparsers/TextAlignConstantParserTest.java b/user/test/com/google/gwt/uibinder/attributeparsers/TextAlignConstantParserTest.java
index a2b0d4a..22cab74 100644
--- a/user/test/com/google/gwt/uibinder/attributeparsers/TextAlignConstantParserTest.java
+++ b/user/test/com/google/gwt/uibinder/attributeparsers/TextAlignConstantParserTest.java
@@ -23,7 +23,6 @@
import com.google.gwt.uibinder.rebind.MortalLogger;
import com.google.gwt.uibinder.test.UiJavaResources;
import com.google.gwt.user.client.ui.TextBoxBase;
-import com.google.gwt.user.client.ui.TextBoxBase.TextAlignConstant;
import junit.framework.TestCase;
@@ -32,7 +31,9 @@
*/
public class TextAlignConstantParserTest extends TestCase {
private static final String TBB = TextBoxBase.class.getCanonicalName();
- private static final String TAC = TextAlignConstant.class.getCanonicalName();
+ @SuppressWarnings("deprecation")
+ private static final String TAC =
+ com.google.gwt.user.client.ui.TextBoxBase.TextAlignConstant.class.getCanonicalName();
private TextAlignConstantParser parser;
@Override
@@ -46,27 +47,27 @@
}
public void testFriendlyNames() throws UnableToCompleteException {
- assertEquals(TBB + ".ALIGN_LEFT", parser.parse("left"));
- assertEquals(TBB + ".ALIGN_CENTER", parser.parse("center"));
- assertEquals(TBB + ".ALIGN_RIGHT", parser.parse("right"));
- assertEquals(TBB + ".ALIGN_JUSTIFY", parser.parse("justify"));
+ assertEquals(TBB + ".ALIGN_LEFT", parser.parse(null, "left"));
+ assertEquals(TBB + ".ALIGN_CENTER", parser.parse(null, "center"));
+ assertEquals(TBB + ".ALIGN_RIGHT", parser.parse(null, "right"));
+ assertEquals(TBB + ".ALIGN_JUSTIFY", parser.parse(null, "justify"));
// capitalized
- assertEquals(TBB + ".ALIGN_LEFT", parser.parse("Left"));
- assertEquals(TBB + ".ALIGN_CENTER", parser.parse("Center"));
- assertEquals(TBB + ".ALIGN_RIGHT", parser.parse("Right"));
- assertEquals(TBB + ".ALIGN_JUSTIFY", parser.parse("Justify"));
+ assertEquals(TBB + ".ALIGN_LEFT", parser.parse(null, "Left"));
+ assertEquals(TBB + ".ALIGN_CENTER", parser.parse(null, "Center"));
+ assertEquals(TBB + ".ALIGN_RIGHT", parser.parse(null, "Right"));
+ assertEquals(TBB + ".ALIGN_JUSTIFY", parser.parse(null, "Justify"));
}
public void testUglyNames() throws UnableToCompleteException {
- assertEquals(TBB + ".ALIGN_LEFT", parser.parse("ALIGN_LEFT"));
- assertEquals(TBB + ".ALIGN_CENTER", parser.parse("ALIGN_CENTER"));
- assertEquals(TBB + ".ALIGN_RIGHT", parser.parse("ALIGN_RIGHT"));
- assertEquals(TBB + ".ALIGN_JUSTIFY", parser.parse("ALIGN_JUSTIFY"));
+ assertEquals(TBB + ".ALIGN_LEFT", parser.parse(null, "ALIGN_LEFT"));
+ assertEquals(TBB + ".ALIGN_CENTER", parser.parse(null, "ALIGN_CENTER"));
+ assertEquals(TBB + ".ALIGN_RIGHT", parser.parse(null, "ALIGN_RIGHT"));
+ assertEquals(TBB + ".ALIGN_JUSTIFY", parser.parse(null, "ALIGN_JUSTIFY"));
}
public void testBad() {
try {
- parser.parse("fnord");
+ parser.parse(null, "fnord");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
@@ -74,6 +75,6 @@
}
public void testFieldRef() throws UnableToCompleteException {
- assertEquals("foo.bar().baz()", parser.parse("{foo.bar.baz}"));
+ assertEquals("foo.bar().baz()", parser.parse(null, "{foo.bar.baz}"));
}
}
diff --git a/user/test/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParserTest.java b/user/test/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParserTest.java
index f4e9402..bcfa64c 100644
--- a/user/test/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParserTest.java
+++ b/user/test/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParserTest.java
@@ -46,24 +46,24 @@
}
public void testFriendlyNames() throws UnableToCompleteException {
- assertEquals(HVA + ".ALIGN_TOP", parser.parse("top"));
- assertEquals(HVA + ".ALIGN_MIDDLE", parser.parse("middle"));
- assertEquals(HVA + ".ALIGN_BOTTOM", parser.parse("bottom"));
+ assertEquals(HVA + ".ALIGN_TOP", parser.parse(null, "top"));
+ assertEquals(HVA + ".ALIGN_MIDDLE", parser.parse(null, "middle"));
+ assertEquals(HVA + ".ALIGN_BOTTOM", parser.parse(null, "bottom"));
// capitalized
- assertEquals(HVA + ".ALIGN_TOP", parser.parse("Top"));
- assertEquals(HVA + ".ALIGN_MIDDLE", parser.parse("Middle"));
- assertEquals(HVA + ".ALIGN_BOTTOM", parser.parse("Bottom"));
+ assertEquals(HVA + ".ALIGN_TOP", parser.parse(null, "Top"));
+ assertEquals(HVA + ".ALIGN_MIDDLE", parser.parse(null, "Middle"));
+ assertEquals(HVA + ".ALIGN_BOTTOM", parser.parse(null, "Bottom"));
}
public void testUglyNames() throws UnableToCompleteException {
- assertEquals(HVA + ".ALIGN_TOP", parser.parse("ALIGN_TOP"));
- assertEquals(HVA + ".ALIGN_MIDDLE", parser.parse("ALIGN_MIDDLE"));
- assertEquals(HVA + ".ALIGN_BOTTOM", parser.parse("ALIGN_BOTTOM"));
+ assertEquals(HVA + ".ALIGN_TOP", parser.parse(null, "ALIGN_TOP"));
+ assertEquals(HVA + ".ALIGN_MIDDLE", parser.parse(null, "ALIGN_MIDDLE"));
+ assertEquals(HVA + ".ALIGN_BOTTOM", parser.parse(null, "ALIGN_BOTTOM"));
}
public void testBad() {
try {
- parser.parse("fnord");
+ parser.parse(null, "fnord");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
@@ -71,6 +71,6 @@
}
public void testFieldRef() throws UnableToCompleteException {
- assertEquals("foo.bar().baz()", parser.parse("{foo.bar.baz}"));
+ assertEquals("foo.bar().baz()", parser.parse(null, "{foo.bar.baz}"));
}
}
diff --git a/user/test/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParser_Test.java b/user/test/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParser_Test.java
index 8a66727..cec1a02 100644
--- a/user/test/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParser_Test.java
+++ b/user/test/com/google/gwt/uibinder/attributeparsers/VerticalAlignmentConstantParser_Test.java
@@ -44,14 +44,14 @@
}
public void testGood() throws UnableToCompleteException {
- assertEquals(HVA + ".ALIGN_TOP", parser.parse("ALIGN_TOP"));
- assertEquals(HVA + ".ALIGN_MIDDLE", parser.parse("ALIGN_MIDDLE"));
- assertEquals(HVA + ".ALIGN_BOTTOM", parser.parse("ALIGN_BOTTOM"));
+ assertEquals(HVA + ".ALIGN_TOP", parser.parse(null, "ALIGN_TOP"));
+ assertEquals(HVA + ".ALIGN_MIDDLE", parser.parse(null, "ALIGN_MIDDLE"));
+ assertEquals(HVA + ".ALIGN_BOTTOM", parser.parse(null, "ALIGN_BOTTOM"));
}
public void testBad() {
try {
- parser.parse("fnord");
+ parser.parse(null, "fnord");
fail("Expected UnableToCompleteException");
} catch (UnableToCompleteException e) {
/* pass */
@@ -59,6 +59,6 @@
}
public void testFieldRef() throws UnableToCompleteException {
- assertEquals("foo.bar().baz()", parser.parse("{foo.bar.baz}"));
+ assertEquals("foo.bar().baz()", parser.parse(null, "{foo.bar.baz}"));
}
}
diff --git a/user/test/com/google/gwt/uibinder/elementparsers/AbsolutePanelParserTest.java b/user/test/com/google/gwt/uibinder/elementparsers/AbsolutePanelParserTest.java
index 8cdbabd..49bf28d 100644
--- a/user/test/com/google/gwt/uibinder/elementparsers/AbsolutePanelParserTest.java
+++ b/user/test/com/google/gwt/uibinder/elementparsers/AbsolutePanelParserTest.java
@@ -45,7 +45,7 @@
}
public void testBadPosition_left() throws Exception {
- checkBadPosition("left='bad' top='0'", "Cannot parse attribute \"left\"");
+ checkBadPosition("left='bad' top='0'", "Cannot parse");
}
public void testBadPosition_topNo() throws Exception {
@@ -53,7 +53,7 @@
}
public void testBadPosition_top() throws Exception {
- checkBadPosition("left='0' top='bad'", "Cannot parse attribute \"top\"");
+ checkBadPosition("left='0' top='bad'", "Cannot parse");
}
public void testBad_noWidget() throws Exception {
diff --git a/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedCssResourceTest.java b/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedCssResourceTest.java
index 9fc1691..00b1526 100644
--- a/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedCssResourceTest.java
+++ b/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedCssResourceTest.java
@@ -62,7 +62,7 @@
stringType, css, MortalLogger.NULL);
assertEquals(stringType, f.getReturnType(new String[] {
- "fieldName", "ableBaker"}, new MonitoredLogger(TreeLogger.NULL)));
+ "fieldName", "ableBaker"}, new MonitoredLogger(MortalLogger.NULL)));
assertEquals(FieldWriterType.GENERATED_CSS, f.getFieldType());
}
@@ -78,7 +78,7 @@
stringType, css, MortalLogger.NULL);
assertEquals(stringType, f.getReturnType(new String[] {
- "fieldName", "able-baker"}, new MonitoredLogger(TreeLogger.NULL)));
+ "fieldName", "able-baker"}, new MonitoredLogger(MortalLogger.NULL)));
assertEquals(FieldWriterType.GENERATED_CSS, f.getFieldType());
}
diff --git a/user/test/com/google/gwt/uibinder/rebind/MockMortalLogger.java b/user/test/com/google/gwt/uibinder/rebind/MockMortalLogger.java
index 798d430..69378c6 100644
--- a/user/test/com/google/gwt/uibinder/rebind/MockMortalLogger.java
+++ b/user/test/com/google/gwt/uibinder/rebind/MockMortalLogger.java
@@ -24,6 +24,7 @@
*/
public final class MockMortalLogger extends MortalLogger {
public String died;
+ public String warned;
public MockMortalLogger() {
super(TreeLogger.NULL);
@@ -36,4 +37,11 @@
died = died == null ? formatted : (died + "\n" + formatted);
super.die(context, message, params);
}
+
+ @Override
+ public void warn(XMLElement context, String message, Object... params) {
+ String formatted = String.format(message, params) + locationOf(context);
+ warned = warned == null ? formatted : (warned + "\n" + formatted);
+ super.warn(context, message, params);
+ }
}
\ No newline at end of file
diff --git a/user/test/com/google/gwt/uibinder/rebind/TokenatorTest.java b/user/test/com/google/gwt/uibinder/rebind/TokenatorTest.java
index 05442b9..5bd8b6f 100644
--- a/user/test/com/google/gwt/uibinder/rebind/TokenatorTest.java
+++ b/user/test/com/google/gwt/uibinder/rebind/TokenatorTest.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.uibinder.rebind;
+import com.google.gwt.uibinder.rebind.Tokenator.ValueAndInfo;
+
import junit.framework.TestCase;
/**
@@ -34,7 +36,7 @@
private String betokened;
@Override
- public void setUp() throws Exception {
+ public void setUp() {
tokenator = new Tokenator();
betokened =
BEGIN.replace("This", betoken("This")).replace("bunch",
@@ -51,19 +53,27 @@
public void testSimple() {
assertEquals(EXPECTED, tokenator.detokenate(betokened));
}
+
+ public void testInfo() {
+ int i = 0;
+ for (ValueAndInfo valueAndInfo : tokenator.getOrderedValues(betokened)) {
+ assertEquals(++i, valueAndInfo.info);
+ }
+ }
public void testStatic() {
assertEquals("0 is a 1 of 2", Tokenator.detokenate(betokened,
new Tokenator.Resolver() {
- int serial = 0;
+ int i = 0;
public String resolveToken(String token) {
- return String.format("%d", serial++);
+ return String.format("%d", i++);
}
}));
}
private String betoken(String in) {
- return tokenator.nextToken(String.format(FORMAT, ++serial, in));
+ int id = ++serial;
+ return tokenator.nextToken(id, String.format(FORMAT, id, in));
}
}
diff --git a/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java b/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java
index 97a13cc..3627cc1 100644
--- a/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java
+++ b/user/test/com/google/gwt/uibinder/rebind/XMLElementTest.java
@@ -392,8 +392,6 @@
fail("Should throw UnableToCompleteException on misparse");
} catch (UnableToCompleteException c) {
assertNotNull(logger.died);
- assertTrue("Expect attribute",
- logger.died.contains("Cannot parse attribute \"fnord\""));
assertTrue("Expect line number", logger.died.contains("Unknown:3"));
}
@@ -403,8 +401,6 @@
fail("Should throw UnableToCompleteException on no such attribute");
} catch (UnableToCompleteException c) {
assertNotNull(logger.died);
- assertTrue("Expect attribute",
- logger.died.contains("Missing required attribute \"empty\""));
assertTrue("Expect line number", logger.died.contains("Unknown:3"));
}
}
diff --git a/user/test/com/google/gwt/uibinder/rebind/model/HtmlTemplateTest.java b/user/test/com/google/gwt/uibinder/rebind/model/HtmlTemplateTest.java
deleted file mode 100644
index 9ffba6b..0000000
--- a/user/test/com/google/gwt/uibinder/rebind/model/HtmlTemplateTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.model;
-
-import com.google.gwt.uibinder.rebind.Tokenator;
-
-import junit.framework.TestCase;
-
-import java.util.Iterator;
-
-/**
- * HtmlTemplate unit tests.
- *
- */
-public class HtmlTemplateTest extends TestCase {
- public void testHappy() {
- Tokenator t = new Tokenator();
- HtmlTemplates templates = new HtmlTemplates();
-
- HtmlTemplate h = new HtmlTemplate("<DialogBox id=\'"
- + t.nextToken("\" + domId0 + \"")
- + "\'>this is a dialog box</DialogBox>", t, templates);
-
- String[] expectedTemplates = {
- "@Template(\"<DialogBox id=\'{0}\'>this is a dialog box</DialogBox>\")",
- "SafeHtml html1(String arg0);",
- " ",};
-
- Iterator<String> j = h.getTemplate().iterator();
- for (String et : expectedTemplates) {
- assertEquals(et, j.next());
- }
- assertFalse(j.hasNext());
-
- String expectedCall = "template.html1(domId0).asString()";
-
- assertEquals(h.writeTemplateCall(), expectedCall);
- }
-
- public void testNullHtml() {
- HtmlTemplates templates = new HtmlTemplates();
- Tokenator t = new Tokenator();
-
- try {
- new HtmlTemplate(null, t, templates);
- fail("Expected empty html to generate error");
- } catch (IllegalArgumentException e) {
- assertTrue(e.getMessage().equals("Template html cannot be null"));
- }
- }
-
- public void testNullTokenator() {
- HtmlTemplates templates = new HtmlTemplates();
-
- try {
- new HtmlTemplate("<p>this is a static string</p>", null,
- templates);
- fail("Expected empty tokenator to generate error");
- } catch (IllegalArgumentException e) {
- assertTrue(e.getMessage().equals("Template tokenator cannot be null"));
- }
- }
-
- public void testNullHtmlTemplates() {
- Tokenator t = new Tokenator();
-
- try {
- new HtmlTemplate("<p>this is a static string</p>", t, null);
- fail("Expected empty html to generate error");
- } catch (IllegalArgumentException e) {
- assertTrue(e.getMessage().equals("HtmlTemplates container cannot be null"));
- }
- }
-
-}
diff --git a/user/test/com/google/gwt/uibinder/rebind/model/HtmlTemplatesTest.java b/user/test/com/google/gwt/uibinder/rebind/model/HtmlTemplatesTest.java
index 920e368..db1b127 100644
--- a/user/test/com/google/gwt/uibinder/rebind/model/HtmlTemplatesTest.java
+++ b/user/test/com/google/gwt/uibinder/rebind/model/HtmlTemplatesTest.java
@@ -1,12 +1,12 @@
/*
* 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
@@ -17,86 +17,99 @@
package com.google.gwt.uibinder.rebind.model;
import com.google.gwt.uibinder.rebind.IndentedWriter;
+import com.google.gwt.uibinder.rebind.MortalLogger;
import com.google.gwt.uibinder.rebind.Tokenator;
import junit.framework.TestCase;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* HtmlTemplates unit tests.
- *
+ *
*/
public class HtmlTemplatesTest extends TestCase {
+ private Tokenator tokenator;
+ private HtmlTemplatesWriter h;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ tokenator = new Tokenator();
+ h = new HtmlTemplatesWriter(null, MortalLogger.NULL);
+ }
+
public void testHappy() throws IllegalArgumentException {
- Tokenator t = new Tokenator();
- HtmlTemplates h = new HtmlTemplates();
-
- String call = h.addSafeHtmlTemplate("<DialogBox id=\'"
- + t.nextToken("\" + domId0 + \"")
- + "\'>this is a dialog box</DialogBox>", t);
-
- // Check the generated template
- String[] expectedTemplates = {
- "@Template(\"<DialogBox id=\'{0}\'>this is a dialog box</DialogBox>\")",
- "SafeHtml html1(String arg0);",
- " ",};
-
- List<String> templates = new ArrayList<String>();
- for (HtmlTemplate ht : h.getTemplates()) {
- templates.addAll(ht.getTemplate());
- }
-
- Iterator<String> i = templates.iterator();
- for (String et : expectedTemplates) {
- assertEquals(et, i.next());
- }
- assertFalse(i.hasNext());
-
- // Check the returned template function call
- String expectedCall = "template.html1(domId0).asString()";
- assertEquals(call, expectedCall);
-
+ String call =
+ h.addSafeHtmlTemplate("<DialogBox id=\'" + tokenator.nextToken("\" + domId0 + \"")
+ + "\'>this is a dialog box</DialogBox>", tokenator).getIndirectTemplateCall();
+
+ assertEquals("template_html1()", call);
+
+ // Confirm that we write the correct SafeHtmlTemplates interface
+
StringWriter s = new StringWriter();
IndentedWriter n = new IndentedWriter(new PrintWriter(s));
-
- // Confirm that IndentedWriter writes the correct template.
- h.writeTemplates(n);
-
- assertEquals("@Template(\"<DialogBox id=\'{0}\'>this is a dialog "
- + "box</DialogBox>\")\nSafeHtml html1(String arg0);\n \n", s.toString());
+ h.writeInterface(n);
+
+ String[] expectedInterface = {"interface Template extends SafeHtmlTemplates {", //
+ " @Template(\"<DialogBox id='{0}'>this is a dialog box</DialogBox>\")", //
+ " SafeHtml html1(String arg0);", //
+ " ", //
+ "}", //
+ "", //
+ "Template template = GWT.create(Template.class);", //
+ };
+
+ assertExpectedStrings(expectedInterface, s.toString());
+
+ // Confirm that we write template caller methods
+
+ s = new StringWriter();
+ n = new IndentedWriter(new PrintWriter(s));
+ h.writeTemplateCallers(n);
+
+ String[] expectedCaller = {"SafeHtml template_html1() {", //
+ " return template.html1(domId0);", //
+ "}"};
+ assertExpectedStrings(expectedCaller, s.toString());
}
-
+
public void testNullHtml() {
- HtmlTemplates h = new HtmlTemplates();
- Tokenator t = new Tokenator();
-
try {
- h.addSafeHtmlTemplate(null, t);
- fail();
+ h.addSafeHtmlTemplate(null, tokenator);
+ fail();
} catch (IllegalArgumentException e) {
- assertTrue("Expected empty html to generate error",
- e.getMessage().equals("Template html cannot be null"));
+ assertTrue("Expected empty html to generate error", e.getMessage().equals(
+ "Template html cannot be null"));
}
assertTrue(h.isEmpty());
- }
-
+ }
+
public void testNullTokenator() throws IllegalArgumentException {
- HtmlTemplates h = new HtmlTemplates();
-
try {
- h.addSafeHtmlTemplate("<p>this is a static string</p>", null);
- fail();
+ h.addSafeHtmlTemplate("<p>this is a static string</p>", null);
+ fail();
} catch (IllegalArgumentException e) {
- assertTrue("Expected empty tokenator to generate error",
- e.getMessage().equals("Template tokenator cannot be null"));
+ assertTrue("Expected empty tokenator to generate error", e.getMessage().equals(
+ "Template tokenator cannot be null"));
}
assertTrue(h.isEmpty());
- }
+ }
+
+ private void assertExpectedStrings(String[] expectedInterface, String string) {
+ List<String> actual = Arrays.asList(string.split("\n"));
+
+ Iterator<String> i = actual.iterator();
+ for (String e : expectedInterface) {
+ assertEquals(e, i.next());
+ }
+ assertFalse(i.hasNext());
+ }
}
diff --git a/user/test/com/google/gwt/uibinder/test/UiJavaResources.java b/user/test/com/google/gwt/uibinder/test/UiJavaResources.java
index e975dff..948cb62 100644
--- a/user/test/com/google/gwt/uibinder/test/UiJavaResources.java
+++ b/user/test/com/google/gwt/uibinder/test/UiJavaResources.java
@@ -481,7 +481,7 @@
return code;
}
};
- public static final MockJavaResource SAFEHTML = new MockJavaResource(
+ public static final MockJavaResource SAFE_HTML = new MockJavaResource(
"com.google.gwt.safehtml.shared.SafeHtml") {
@Override
public CharSequence getContent() {
@@ -492,7 +492,7 @@
return code;
}
};
- public static final MockJavaResource SAFEHTML_BUILDER = new MockJavaResource(
+ public static final MockJavaResource SAFE_HTML_BUILDER = new MockJavaResource(
"com.google.gwt.safehtml.shared.SafeHtmlBuilder") {
@Override
public CharSequence getContent() {
@@ -503,6 +503,17 @@
return code;
}
};
+ public static final MockJavaResource SAFE_URI = new MockJavaResource(
+ "com.google.gwt.safehtml.shared.SafeUri") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.safehtml.shared;\n");
+ code.append("public interface SafeUri{\n");
+ code.append("}\n");
+ return code;
+ }
+ };
public static final MockJavaResource SPLIT_LAYOUT_PANEL = new MockJavaResource(
"com.google.gwt.user.client.ui.SplitLayoutPanel") {
@Override
@@ -761,8 +772,9 @@
rtn.add(NUMBER_LABEL);
rtn.add(NUMBER_FORMAT);
rtn.add(RENDERER);
- rtn.add(SAFEHTML);
- rtn.add(SAFEHTML_BUILDER);
+ rtn.add(SAFE_HTML);
+ rtn.add(SAFE_HTML_BUILDER);
+ rtn.add(SAFE_URI);
rtn.add(SPLIT_LAYOUT_PANEL);
rtn.add(STACK_LAYOUT_PANEL);
rtn.add(STACK_PANEL);
diff --git a/user/test/com/google/gwt/uibinder/test/client/DomBasedUi.java b/user/test/com/google/gwt/uibinder/test/client/DomBasedUi.java
index ffa6ad8..01e441e 100644
--- a/user/test/com/google/gwt/uibinder/test/client/DomBasedUi.java
+++ b/user/test/com/google/gwt/uibinder/test/client/DomBasedUi.java
@@ -63,7 +63,7 @@
@UiField TableCellElement th2;
@UiField TableSectionElement tbody;
@UiField TableCellElement th4;
-
+
public DomBasedUi(String yourNameHere) {
res.style().ensureInjected();
binder.createAndBindUi(this);
diff --git a/user/test/com/google/gwt/uibinder/test/client/DomBasedUi.ui.xml b/user/test/com/google/gwt/uibinder/test/client/DomBasedUi.ui.xml
index 794df55..1508b04 100644
--- a/user/test/com/google/gwt/uibinder/test/client/DomBasedUi.ui.xml
+++ b/user/test/com/google/gwt/uibinder/test/client/DomBasedUi.ui.xml
@@ -15,6 +15,7 @@
xmlns:ui='urn:ui:com.google.gwt.uibinder'
>
<ui:with field='res' type='com.google.gwt.uibinder.test.client.DomBasedUi.Resources' />
+
<div ui:field='root' class="{res.style.bodyColor} {res.style.bodyFont}"
title="The title of this div is localizable">
<ui:attribute name='title'/>
diff --git a/user/test/com/google/gwt/uibinder/test/client/FakeBundle.java b/user/test/com/google/gwt/uibinder/test/client/FakeBundle.java
index 87a68d8..52f3c2f 100644
--- a/user/test/com/google/gwt/uibinder/test/client/FakeBundle.java
+++ b/user/test/com/google/gwt/uibinder/test/client/FakeBundle.java
@@ -15,10 +15,21 @@
*/
package com.google.gwt.uibinder.test.client;
+import com.google.gwt.safehtml.shared.SafeUri;
+import com.google.gwt.safehtml.shared.UriUtils;
+
/**
* Faux bundle used by test.
*/
public class FakeBundle {
+ public boolean aBoolean() {
+ return true;
+ }
+
+ public Boolean aBooleanObject() {
+ return false;
+ }
+
public double aDouble() {
return 42;
}
@@ -26,7 +37,11 @@
public Double aDoubleObject() {
return 21.0;
}
-
+
+ public String aGifPath() {
+ return "www.google.com/images/logo_sm.gif";
+ }
+
public int anInt() {
return 42;
}
@@ -34,24 +49,28 @@
public Integer anIntegerObject() {
return 21;
}
-
- public boolean aBoolean() {
- return true;
- }
-
- public Boolean aBooleanObject() {
- return false;
+
+ public String anUnsafeUri() {
+ return "javascript:void(0)";
}
+ public SafeUri aSafeUri() {
+ return UriUtils.fromSafeConstant(anUnsafeUri());
+ }
+
+ public String aSelector() {
+ return "http://";
+ }
+
+ public String aUrl() {
+ return aSelector() + aGifPath();
+ }
+
public String helloText() {
return "hello";
}
-
+
public ArbitraryPojo pojo() {
return new ArbitraryPojo();
}
-
- public String aUrl() {
- return "http://www.google.com/images/logo_sm.gif";
- }
}
diff --git a/user/test/com/google/gwt/uibinder/test/client/HasUri.java b/user/test/com/google/gwt/uibinder/test/client/HasUri.java
new file mode 100644
index 0000000..7ab2cc5
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/HasUri.java
@@ -0,0 +1,30 @@
+/*
+ * 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.test.client;
+
+import com.google.gwt.safehtml.shared.SafeUri;
+import com.google.gwt.user.client.ui.SimplePanel;
+
+/**
+ * Used by {@link SafeUriIntegrationTest}
+ */
+public class HasUri extends SimplePanel {
+ SafeUri uri;
+
+ public void setUri(SafeUri uri) {
+ this.uri = uri;
+ }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuilderSafeUriIntegrationTest.Renderable.ui.xml b/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuilderSafeUriIntegrationTest.Renderable.ui.xml
new file mode 100644
index 0000000..d55aacb
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuilderSafeUriIntegrationTest.Renderable.ui.xml
@@ -0,0 +1,37 @@
+<!-- -->
+<!-- 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 -->
+<!-- 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'
+ xmlns:demo='urn:import:com.google.gwt.uibinder.test.client'
+ >
+ <ui:with field='values' type='com.google.gwt.uibinder.test.client.FakeBundle'/>
+ <gwt:RenderablePanel>
+
+ <a ui:field='jsAnchorFromSafeUri' href='{values.aSafeUri}'>JavaScript anchor from SafeUri</a> |
+ <a ui:field='jsAnchorFromString' href='{values.anUnsafeUri}'>JavaScript anchor from String</a> |
+ <a ui:field='httpAnchorFromString' href='{values.aUrl}'>Http anchor from String</a> |
+
+ <a ui:field='inlineHttpAnchor' href='http://www.google.com/images/logo_sm.gif'>inline http anchor</a> |
+ <a ui:field='inlineJavascriptAnchor' href='javascript:void(0)'>inline javascript anchor</a> |
+
+ <a ui:field='httpAnchorFromConstructedString' href='{values.aSelector}{values.aGifPath}'>http anchor from constructed string</a>
+
+ <img src="{values.aUrl}" ui:field='myImage'/>
+
+ <demo:HasUri ui:field='jsAnchorFromSafeUriObj' uri='{values.aSafeUri}'/>
+ <demo:HasUri ui:field='inlineHttpAnchorObj' uri='http://www.google.com/images/logo_sm.gif'/>
+ <demo:HasUri ui:field='inlineJavascriptAnchorObj' uri='javascript:void(0)'/>
+ </gwt:RenderablePanel>
+</ui:UiBinder>
diff --git a/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuilderSafeUriIntegrationTest.java b/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuilderSafeUriIntegrationTest.java
new file mode 100644
index 0000000..3aa0459
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuilderSafeUriIntegrationTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.test.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.AnchorElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiBinderUtil;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiRenderer;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Tests SafeUri parsing with the lazy widget builder.
+ */
+public class LazyWidgetBuilderSafeUriIntegrationTest extends SafeUriIntegrationTest {
+ interface Renderer extends UiRenderer<LazyWidgetBuilderSafeUriIntegrationTest> {
+ AnchorElement getJsAnchorFromSafeUri(Element ancestor);
+
+ AnchorElement getJsAnchorFromString(Element ancestor);
+
+ AnchorElement getHttpAnchorFromString(Element ancestor);
+
+ AnchorElement getInlineHttpAnchor(Element ancestor);
+
+ AnchorElement getInlineJavascriptAnchor(Element ancestor);
+
+ AnchorElement getHttpAnchorFromConstructedString(Element ancestor);
+
+ void render(SafeHtmlBuilder b, FakeBundle values);
+ }
+
+ static class Renderable extends Composite {
+ interface Binder extends UiBinder<Widget, Renderable> {
+ }
+
+ private static final Binder BINDER = GWT.create(Binder.class);
+
+ @UiField
+ AnchorElement jsAnchorFromSafeUri;
+ @UiField
+ AnchorElement jsAnchorFromString;
+ @UiField
+ AnchorElement httpAnchorFromString;
+ @UiField
+ AnchorElement inlineHttpAnchor;
+ @UiField
+ AnchorElement inlineJavascriptAnchor;
+ @UiField
+ AnchorElement httpAnchorFromConstructedString;
+
+ @UiField
+ HasUri jsAnchorFromSafeUriObj;
+ @UiField
+ HasUri inlineHttpAnchorObj;
+ @UiField
+ HasUri inlineJavascriptAnchorObj;
+
+ public Renderable() {
+ initWidget(BINDER.createAndBindUi(this));
+ }
+ }
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.uibinder.test.LazyWidgetBuilderSuite";
+ }
+
+ public void testIsRenderable() {
+ Renderable ui = new Renderable();
+
+ try {
+ RootPanel.get().add(ui);
+ assertEquals(values.anUnsafeUri(), ui.jsAnchorFromSafeUri.getHref());
+ AnchorElement anchor = Document.get().createAnchorElement();
+ anchor.setHref("#");
+ assertEquals(anchor.getHref(), ui.jsAnchorFromString.getHref());
+ assertEquals(anchor.getHref(), ui.jsAnchorFromString.getHref());
+ assertEquals("http://www.google.com/images/logo_sm.gif", ui.inlineHttpAnchor.getHref());
+ assertEquals("javascript:void(0)", ui.inlineJavascriptAnchor.getHref());
+ assertEquals(values.aSelector() + values.aGifPath(),
+ ui.httpAnchorFromConstructedString.getHref());
+
+ assertEquals(values.anUnsafeUri(), ui.jsAnchorFromSafeUriObj.uri.asString());
+ assertEquals("http://www.google.com/images/logo_sm.gif",
+ ui.inlineHttpAnchorObj.uri.asString());
+ assertEquals("javascript:void(0)", ui.inlineJavascriptAnchorObj.uri.asString());
+ } finally {
+ ui.removeFromParent();
+ }
+ }
+
+ public void testRenderer() {
+ Renderer r = GWT.create(Renderer.class);
+ SafeHtmlBuilder b = new SafeHtmlBuilder();
+ r.render(b, values);
+
+ Element e = UiBinderUtil.fromHtml(b.toSafeHtml().asString());
+ Document.get().getBody().appendChild(e);
+ try {
+ assertEquals(values.anUnsafeUri(), r.getJsAnchorFromSafeUri(e).getHref());
+
+ AnchorElement anchor = Document.get().createAnchorElement();
+ anchor.setHref("#");
+ assertEquals(anchor.getHref(), r.getJsAnchorFromString(e).getHref());
+ assertEquals(anchor.getHref(), r.getJsAnchorFromString(e).getHref());
+
+ assertEquals("http://www.google.com/images/logo_sm.gif", r.getInlineHttpAnchor(e).getHref());
+ assertEquals("javascript:void(0)", r.getInlineJavascriptAnchor(e).getHref());
+
+ assertEquals(values.aSelector() + values.aGifPath(),
+ r.getHttpAnchorFromConstructedString(e).getHref());
+ } finally {
+ e.removeFromParent();
+ }
+ }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuilderSafeUriIntegrationTest.ui.xml b/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuilderSafeUriIntegrationTest.ui.xml
new file mode 100644
index 0000000..ced27af
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/LazyWidgetBuilderSafeUriIntegrationTest.ui.xml
@@ -0,0 +1,28 @@
+<!-- -->
+<!-- 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 -->
+<!-- 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'>
+ <ui:with field='values' />
+ <div>
+ <a ui:field='jsAnchorFromSafeUri' href='{values.aSafeUri}'>JavaScript anchor from SafeUri</a> |
+ <a ui:field='jsAnchorFromString' href='{values.anUnsafeUri}'>JavaScript anchor from String</a> |
+ <a ui:field='httpAnchorFromString' href='{values.aUrl}'>Http anchor from String</a> |
+
+ <a ui:field='inlineHttpAnchor' href='http://www.google.com/images/logo_sm.gif'>inline http anchor</a> |
+ <a ui:field='inlineJavascriptAnchor' href='javascript:void(0)'>inline javascript anchor</a> |
+
+ <a ui:field='httpAnchorFromConstructedString' href='{values.aSelector}{values.aGifPath}'>http anchor from constructed string</a>
+
+ <img src="{values.aUrl}" ui:field='myImage'/>
+ </div>
+</ui:UiBinder>
diff --git a/user/test/com/google/gwt/uibinder/test/client/SafeUriIntegrationTest.BinderUi.ui.xml b/user/test/com/google/gwt/uibinder/test/client/SafeUriIntegrationTest.BinderUi.ui.xml
new file mode 100644
index 0000000..c9c12cd
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/SafeUriIntegrationTest.BinderUi.ui.xml
@@ -0,0 +1,37 @@
+<!-- -->
+<!-- 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 -->
+<!-- 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'
+ xmlns:demo='urn:import:com.google.gwt.uibinder.test.client'
+ >
+ <ui:with field='values' type='com.google.gwt.uibinder.test.client.FakeBundle'/>
+ <gwt:HTMLPanel>
+
+ <a ui:field='jsAnchorFromSafeUri' href='{values.aSafeUri}'>JavaScript anchor from SafeUri</a> |
+ <a ui:field='jsAnchorFromString' href='{values.anUnsafeUri}'>JavaScript anchor from String</a> |
+ <a ui:field='httpAnchorFromString' href='{values.aUrl}'>Http anchor from String</a> |
+
+ <a ui:field='inlineHttpAnchor' href='http://www.google.com/images/logo_sm.gif'>inline http anchor</a> |
+ <a ui:field='inlineJavascriptAnchor' href='javascript:void(0)'>inline javascript anchor</a> |
+
+ <a ui:field='httpAnchorFromConstructedString' href='{values.aSelector}{values.aGifPath}'>http anchor from constructed string</a>
+
+ <img src="{values.aUrl}" ui:field='myImage'/>
+
+ <demo:HasUri ui:field='jsAnchorFromSafeUriObj' uri='{values.aSafeUri}'/>
+ <demo:HasUri ui:field='inlineHttpAnchorObj' uri='http://www.google.com/images/logo_sm.gif'/>
+ <demo:HasUri ui:field='inlineJavascriptAnchorObj' uri='javascript:void(0)'/>
+ </gwt:HTMLPanel>
+</ui:UiBinder>
diff --git a/user/test/com/google/gwt/uibinder/test/client/SafeUriIntegrationTest.java b/user/test/com/google/gwt/uibinder/test/client/SafeUriIntegrationTest.java
new file mode 100644
index 0000000..b80360c
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/SafeUriIntegrationTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.test.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.AnchorElement;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Test SafeUri parsing.
+ */
+public class SafeUriIntegrationTest extends GWTTestCase {
+ static class BinderUi {
+ interface Binder extends UiBinder<Widget, BinderUi> {
+ }
+
+ static final Binder binder = GWT.create(Binder.class);
+
+ @UiField
+ AnchorElement jsAnchorFromSafeUri;
+ @UiField
+ AnchorElement jsAnchorFromString;
+ @UiField
+ AnchorElement httpAnchorFromString;
+ @UiField
+ AnchorElement inlineHttpAnchor;
+ @UiField
+ AnchorElement inlineJavascriptAnchor;
+ @UiField
+ AnchorElement httpAnchorFromConstructedString;
+
+ @UiField
+ HasUri jsAnchorFromSafeUriObj;
+ @UiField
+ HasUri inlineHttpAnchorObj;
+ @UiField
+ HasUri inlineJavascriptAnchorObj;
+
+ BinderUi() {
+ binder.createAndBindUi(this);
+ }
+ }
+
+ protected FakeBundle values = new FakeBundle();
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.uibinder.test.UiBinderSuite";
+ }
+
+ public void testBinder() {
+ BinderUi ui = new BinderUi();
+
+ assertEquals(values.anUnsafeUri(), ui.jsAnchorFromSafeUri.getHref());
+ AnchorElement anchor = Document.get().createAnchorElement();
+ anchor.setHref("#");
+ assertEquals(anchor.getHref(), ui.jsAnchorFromString.getHref());
+ assertEquals(anchor.getHref(), ui.jsAnchorFromString.getHref());
+ assertEquals("http://www.google.com/images/logo_sm.gif", ui.inlineHttpAnchor.getHref());
+ assertEquals("javascript:void(0)", ui.inlineJavascriptAnchor.getHref());
+ assertEquals(values.aSelector() + values.aGifPath(), ui.httpAnchorFromConstructedString.getHref());
+
+ assertEquals(values.anUnsafeUri(), ui.jsAnchorFromSafeUriObj.uri.asString());
+ assertEquals("http://www.google.com/images/logo_sm.gif", ui.inlineHttpAnchorObj.uri.asString());
+ assertEquals("javascript:void(0)", ui.inlineJavascriptAnchorObj.uri.asString());
+ }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/UiRendererTest.java b/user/test/com/google/gwt/uibinder/test/client/UiRendererTest.java
index 33d7a81..3bddd17 100644
--- a/user/test/com/google/gwt/uibinder/test/client/UiRendererTest.java
+++ b/user/test/com/google/gwt/uibinder/test/client/UiRendererTest.java
@@ -48,7 +48,7 @@
UiRendererTestApp app = UiRendererTestApp.getInstance();
safeHtmlUi = app.getSafeHtmlUi();
renderedHtml = safeHtmlUi.render(RENDERED_VALUE, RENDERED_VALUE_TWICE);
- renderer = safeHtmlUi.getRenderer();
+ renderer = UiRendererUi.getRenderer();
docDiv = Document.get().createDivElement();
docDiv.setInnerHTML(renderedHtml.asString());
@@ -249,7 +249,7 @@
}
@Override
- protected void gwtTearDown() throws Exception {
+ protected void gwtTearDown() {
docDiv.removeFromParent();
docDiv = null;
}
diff --git a/user/test/com/google/gwt/uibinder/test/client/UiRendererUi.ui.xml b/user/test/com/google/gwt/uibinder/test/client/UiRendererUi.ui.xml
index 2634254..9a1de11 100644
--- a/user/test/com/google/gwt/uibinder/test/client/UiRendererUi.ui.xml
+++ b/user/test/com/google/gwt/uibinder/test/client/UiRendererUi.ui.xml
@@ -11,21 +11,21 @@
<!-- 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:res='urn:with:com.google.gwt.uibinder.test.client.DomBasedUi.Resources'
- >
+<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'>
<ui:with field="constants" type="com.google.gwt.uibinder.test.client.Constants"/>
+ <ui:with field="res" type="com.google.gwt.uibinder.test.client.DomBasedUi.Resources"/>
+
<ui:with field="aValue"/>
<ui:with field="aValueTwice"/>
- <div ui:field='root' res:class="style.bodyColor style.bodyFont"
+
+ <div ui:field='root' class="{res.style.bodyColor} {res.style.bodyFont}"
title="The title of this div is localizable">
<ui:attribute name='title'/>
<span><ui:text from="{constants.getText}" /></span>
Hello, <span ui:field="nameSpan"><ui:text from="{aValue.getBar}"/></span>.
<ui:msg>How goes it?</ui:msg>
<span/><span><ui:text from="{aValueTwice.getBar}{aValueTwice.getBar}"/></span>
- <h2 res:class="style.bodyColor style.bodyFont">Placeholders in localizables</h2>
+ <h2 class="{res.style.bodyColor} {res.style.bodyFont}">Placeholders in localizables</h2>
<p><ui:msg>When you mark up your text for translation, there will be bits
that you don't want the translators to mess with. You can protect
these with <span style="font-weight:bold"
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 ea5911f..93b818f 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
@@ -225,7 +225,7 @@
Here is a similarly clickable
<demo:ClickyLink text="hyperlink based on a custom widget"
ui:field="customLinkWidget" popupText="That tickles! "/>. And an
- <gwt:Anchor><i>Anchor to nowhere</i></gwt:Anchor>.
+ <gwt:Anchor ><i>Anchor to nowhere</i></gwt:Anchor>.
</p>
<p>I bet you like babies in your Image widgets.</p>
@@ -248,7 +248,7 @@
{cursorifficStyle.cursor}'>
Well how do you like <br/>
tiled sprited images...of babies!! <br/>
- Well of course you do. Who wouldn't?
+ Of course you do! Who wouldn't?
</p>
<p>