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