blob: c65574bb5c5bbded1550dad7d15a407a20743804 [file] [log] [blame]
/*
* 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.thirdparty.guava.common.base.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();
}
}