/* * 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; } }