| /* | |
| * 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.safehtml.rebind; | |
| import com.google.gwt.dev.util.Preconditions; | |
| import java.util.Collections; | |
| import java.util.LinkedList; | |
| import java.util.List; | |
| /** | |
| * A representation of a parsed HTML template. | |
| * | |
| * <p>A parsed template is represented as a sequence of template chunks. | |
| * | |
| * <p>A chunk may correspond to a literal string or to a formal template | |
| * parameter. | |
| * | |
| * <p>Parameter chunks have an attribute that refers to the index of the | |
| * corresponding template method parameter, as well as an attribute that | |
| * represents the HTML context the template parameter appeared in. | |
| */ | |
| final class ParsedHtmlTemplate { | |
| /** | |
| * A representation of the context of a point within a HTML document. A | |
| * context consists of a type (such as "plain text", "attribute"), as well as | |
| * a HTML tag and attribute name where applicable. | |
| */ | |
| static final class HtmlContext { | |
| /** | |
| * The possible types of HTML context. | |
| */ | |
| static enum Type { | |
| /** | |
| * Regular inner text HTML context. | |
| */ | |
| TEXT, | |
| /** | |
| * Value of a HTML attribute. | |
| */ | |
| ATTRIBUTE_VALUE, | |
| /** | |
| * At the very start of a URL-valued attribute. | |
| */ | |
| URL_ATTRIBUTE_START, | |
| /** | |
| * A template parameter that comprises an entire URL-valued attribute. | |
| */ | |
| URL_ATTRIBUTE_ENTIRE, | |
| /** | |
| * CSS (style) context. | |
| */ | |
| CSS, | |
| /** | |
| * CSS (style) attribute context. | |
| */ | |
| CSS_ATTRIBUTE, | |
| /** | |
| * At the very start of a CSS (style) attribute context. | |
| */ | |
| CSS_ATTRIBUTE_START | |
| } | |
| private final Type type; | |
| private final String tag; | |
| private final String attribute; | |
| /** | |
| * Creates a HTML context. | |
| * | |
| * @param type the {@link Type} of this context | |
| */ | |
| public HtmlContext(Type type) { | |
| this(type, null, null); | |
| } | |
| /** | |
| * Creates a HTML context. | |
| * | |
| * @param type the {@link Type} of this context | |
| * @param tag the HTML tag this context corresponds to, if applicable; null | |
| * otherwise | |
| * @param attribute the HTML attribute this context corresponds to, if | |
| * applicable; null otherwise | |
| */ | |
| public HtmlContext(Type type, String tag, String attribute) { | |
| Preconditions.checkArgument((type != Type.TEXT) | |
| || ((tag == null) && (attribute == null)), | |
| "tag and attribute must be null for context \"TEXT\""); | |
| this.type = type; | |
| this.tag = tag; | |
| this.attribute = attribute; | |
| } | |
| /** | |
| * Returns the attribute of this HTML context. | |
| */ | |
| public String getAttribute() { | |
| return attribute; | |
| } | |
| /** | |
| * Returns the tag of this HTML context. | |
| */ | |
| public String getTag() { | |
| return tag; | |
| } | |
| /** | |
| * Returns the type of this HTML context. | |
| */ | |
| public Type getType() { | |
| return type; | |
| } | |
| @Override | |
| public String toString() { | |
| return "(" + getType() + "," + getTag() + "," + getAttribute() + ")"; | |
| } | |
| } | |
| /** | |
| * Represents a template chunk corresponding to a literal string. | |
| */ | |
| static final class LiteralChunk implements TemplateChunk { | |
| private final StringBuilder chunk; | |
| /** | |
| * Creates a literal chunk corresponding to an empty string. | |
| */ | |
| public LiteralChunk() { | |
| chunk = new StringBuilder(); | |
| } | |
| /** | |
| * Creates a literal chunk corresponding to the provided string. | |
| */ | |
| public LiteralChunk(String literal) { | |
| chunk = new StringBuilder(literal); | |
| } | |
| public Kind getKind() { | |
| return Kind.LITERAL; | |
| } | |
| public String getLiteral() { | |
| return chunk.toString(); | |
| } | |
| @Override | |
| public String toString() { | |
| return String.format("L(%s)", getLiteral()); | |
| } | |
| void append(String s) { | |
| chunk.append(s); | |
| } | |
| } | |
| /** | |
| * Represents a template chunk corresponding to a template parameter. | |
| */ | |
| static final class ParameterChunk implements TemplateChunk { | |
| private final HtmlContext context; | |
| private final int parameterIndex; | |
| /** | |
| * Creates a parameter chunk. | |
| * | |
| * @param context the HTML context this parameter appears in | |
| * @param parameterIndex the index of the template parameter that this chunk | |
| * refers to | |
| */ | |
| public ParameterChunk(HtmlContext context, int parameterIndex) { | |
| this.context = context; | |
| this.parameterIndex = parameterIndex; | |
| } | |
| /** | |
| * Returns the HTML context this parameter appears in. | |
| */ | |
| public HtmlContext getContext() { | |
| return context; | |
| } | |
| public Kind getKind() { | |
| return Kind.PARAMETER; | |
| } | |
| /** | |
| * Returns the index of the template parameter that this chunk refers to. | |
| */ | |
| public int getParameterIndex() { | |
| return parameterIndex; | |
| } | |
| @Override | |
| public String toString() { | |
| return String.format("P(%s,%d)", getContext(), getParameterIndex()); | |
| } | |
| } | |
| /** | |
| * Represents a parsed chunk of a template. | |
| * | |
| * <p>There are two kinds of chunks: Those representing literal strings and | |
| * those representing template parameters. | |
| */ | |
| interface TemplateChunk { | |
| /** | |
| * Distinguishes different kinds of {@link TemplateChunk}. | |
| */ | |
| public static enum Kind { | |
| LITERAL, PARAMETER, | |
| } | |
| /** | |
| * Returns the {@link Kind} of this template chunk. | |
| */ | |
| Kind getKind(); | |
| } | |
| /* | |
| * The chunks of this parsed template. | |
| */ | |
| private final LinkedList<TemplateChunk> chunks; | |
| /** | |
| * Initializes an empty parsed HTML template. | |
| */ | |
| public ParsedHtmlTemplate() { | |
| chunks = new LinkedList<TemplateChunk>(); | |
| } | |
| /** | |
| * Adds a literal string to the template. | |
| * | |
| * If the currently last chunk of the parsed template is a literal chunk, the | |
| * provided string literal will be appended to that chunk. I.e., consecutive | |
| * literal chunks are automatically coalesced. | |
| * @param literal the string to be added as a literal chunk | |
| */ | |
| public void addLiteral(String literal) { | |
| if (chunks.isEmpty() | |
| || (chunks.getLast().getKind() != TemplateChunk.Kind.LITERAL)) { | |
| chunks.add(new LiteralChunk(literal)); | |
| } else { | |
| ((LiteralChunk) chunks.getLast()).append(literal); | |
| } | |
| } | |
| /** | |
| * Adds a parameter chunk to the template. | |
| * | |
| * @param chunk the chunk to be added | |
| */ | |
| public void addParameter(ParameterChunk chunk) { | |
| chunks.add(chunk); | |
| } | |
| /** | |
| * Returns the chunks of this parsed template. | |
| * | |
| * <p>The returned list is unmodifiable. | |
| */ | |
| public List<TemplateChunk> getChunks() { | |
| return Collections.unmodifiableList(chunks); | |
| } | |
| @Override | |
| public String toString() { | |
| return chunks.toString(); | |
| } | |
| } |