blob: 0a6dea028347cf691701a6f043ba084284cb7174 [file] [log] [blame]
/*
* 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;
}
}