blob: 8488cc283862342386de96c9afd4ce33358c289e [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.dom.builder.shared;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.AreaElement;
import com.google.gwt.dom.client.AudioElement;
import com.google.gwt.dom.client.BRElement;
import com.google.gwt.dom.client.BaseElement;
import com.google.gwt.dom.client.BodyElement;
import com.google.gwt.dom.client.ButtonElement;
import com.google.gwt.dom.client.CanvasElement;
import com.google.gwt.dom.client.DListElement;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.FieldSetElement;
import com.google.gwt.dom.client.FormElement;
import com.google.gwt.dom.client.FrameElement;
import com.google.gwt.dom.client.FrameSetElement;
import com.google.gwt.dom.client.HRElement;
import com.google.gwt.dom.client.HeadElement;
import com.google.gwt.dom.client.IFrameElement;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.LIElement;
import com.google.gwt.dom.client.LabelElement;
import com.google.gwt.dom.client.LegendElement;
import com.google.gwt.dom.client.LinkElement;
import com.google.gwt.dom.client.MapElement;
import com.google.gwt.dom.client.MetaElement;
import com.google.gwt.dom.client.OListElement;
import com.google.gwt.dom.client.OptGroupElement;
import com.google.gwt.dom.client.OptionElement;
import com.google.gwt.dom.client.ParagraphElement;
import com.google.gwt.dom.client.ParamElement;
import com.google.gwt.dom.client.PreElement;
import com.google.gwt.dom.client.QuoteElement;
import com.google.gwt.dom.client.ScriptElement;
import com.google.gwt.dom.client.SelectElement;
import com.google.gwt.dom.client.SourceElement;
import com.google.gwt.dom.client.SpanElement;
import com.google.gwt.dom.client.StyleElement;
import com.google.gwt.dom.client.TableCaptionElement;
import com.google.gwt.dom.client.TableCellElement;
import com.google.gwt.dom.client.TableColElement;
import com.google.gwt.dom.client.TableElement;
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.dom.client.TableSectionElement;
import com.google.gwt.dom.client.TextAreaElement;
import com.google.gwt.dom.client.TitleElement;
import com.google.gwt.dom.client.UListElement;
import com.google.gwt.dom.client.VideoElement;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
/**
* Implementation of methods in {@link ElementBuilderBase} used to render HTML
* as a string, using innerHtml to generate an element.
*/
class HtmlBuilderImpl extends ElementBuilderImpl {
/*
* Common element builders, and those most likely to appear in a loop, are
* created on initialization to avoid null checks. Less common element
* builders are created lazily to avoid unnecessary object creation.
*/
private HtmlAnchorBuilder anchorBuilder;
private HtmlAreaBuilder areaBuilder;
private HtmlAudioBuilder audioBuilder;
private HtmlBaseBuilder baseBuilder;
private HtmlBodyBuilder bodyBuilder;
private HtmlBRBuilder brBuilder;
private HtmlButtonBuilder buttonBuilder;
private HtmlCanvasBuilder canvasBuilder;
private final HtmlDivBuilder divBuilder = new HtmlDivBuilder(this);
private HtmlDListBuilder dListBuilder;
private final HtmlElementBuilder elementBuilder = new HtmlElementBuilder(this);
private HtmlFieldSetBuilder fieldSetBuilder;
private HtmlFormBuilder formBuilder;
private HtmlFrameBuilder frameBuilder;
private HtmlFrameSetBuilder frameSetBuilder;
private HtmlHeadBuilder headBuilder;
private HtmlHeadingBuilder headingBuilder;
private HtmlHRBuilder hrBuilder;
private HtmlIFrameBuilder iFrameBuilder;
private HtmlImageBuilder imageBuilder;
private final HtmlInputBuilder inputBuilder = new HtmlInputBuilder(this);
private HtmlLabelBuilder labelBuilder;
private HtmlLegendBuilder legendBuilder;
private final HtmlLIBuilder liBuilder = new HtmlLIBuilder(this);
private HtmlLinkBuilder linkBuilder;
private HtmlMapBuilder mapBuilder;
private HtmlMetaBuilder metaBuilder;
private HtmlOListBuilder oListBuilder;
private final HtmlOptionBuilder optionBuilder = new HtmlOptionBuilder(this);
private HtmlOptGroupBuilder optGroupBuilder;
private HtmlParagraphBuilder paragraphBuilder;
private HtmlParamBuilder paramBuilder;
private HtmlPreBuilder preBuilder;
private HtmlQuoteBuilder quoteBuilder;
private HtmlScriptBuilder scriptBuilder;
private HtmlSelectBuilder selectBuilder;
private HtmlSourceBuilder sourceBuilder;
private final HtmlSpanBuilder spanBuilder = new HtmlSpanBuilder(this);
private HtmlStyleBuilder styleBuilder;
private final StylesBuilder stylesBuilder = new HtmlStylesBuilder(this);
private HtmlTableBuilder tableBuilder;
private final HtmlTableCellBuilder tableCellBuilder = new HtmlTableCellBuilder(this);
private HtmlTableCaptionBuilder tableCaptionBuilder;
private HtmlTableColBuilder tableColBuilder;
private final HtmlTableRowBuilder tableRowBuilder = new HtmlTableRowBuilder(this);
private HtmlTableSectionBuilder tableSectionBuilder;
private HtmlTextAreaBuilder textAreaBuilder;
private HtmlTitleBuilder titleBuilder;
private HtmlUListBuilder uListBuilder;
private HtmlVideoBuilder videoBuilder;
/**
* Used to builder the HTML string. We cannot use
* {@link com.google.gwt.safehtml.shared.SafeHtmlBuilder} because it does some
* rudimentary checks that the HTML tags are complete. Instead, we escape
* values before appending them.
*/
private final StringBuilder sb = new StringBuilder();
/**
* Return the HTML as a {@link SafeHtml} string.
*/
public SafeHtml asSafeHtml() {
// End all open tags.
endAllTags();
/*
* sb is trusted because we only append trusted strings or escaped strings
* to it.
*/
return SafeHtmlUtils.fromTrustedString(sb.toString());
}
public void attribute(String name, int value) {
trustedAttribute(escape(name), value);
}
public void attribute(String name, String value) {
trustedAttribute(escape(name), value);
}
public HtmlAnchorBuilder startAnchor() {
if (anchorBuilder == null) {
anchorBuilder = new HtmlAnchorBuilder(this);
}
trustedStart(AnchorElement.TAG, anchorBuilder);
return anchorBuilder;
}
public HtmlAreaBuilder startArea() {
if (areaBuilder == null) {
areaBuilder = new HtmlAreaBuilder(this);
}
trustedStart(AreaElement.TAG, areaBuilder);
return areaBuilder;
}
public HtmlAudioBuilder startAudio() {
if (audioBuilder == null) {
audioBuilder = new HtmlAudioBuilder(this);
}
trustedStart(AudioElement.TAG, audioBuilder);
return audioBuilder;
}
public HtmlBaseBuilder startBase() {
if (baseBuilder == null) {
baseBuilder = new HtmlBaseBuilder(this);
}
trustedStart(BaseElement.TAG, baseBuilder);
return baseBuilder;
}
public HtmlQuoteBuilder startBlockQuote() {
return startQuote(QuoteElement.TAG_BLOCKQUOTE);
}
public HtmlBodyBuilder startBody() {
if (bodyBuilder == null) {
bodyBuilder = new HtmlBodyBuilder(this);
}
trustedStart(BodyElement.TAG, bodyBuilder);
return bodyBuilder;
}
public HtmlBRBuilder startBR() {
if (brBuilder == null) {
brBuilder = new HtmlBRBuilder(this);
}
trustedStart(BRElement.TAG, brBuilder);
return brBuilder;
}
public InputBuilder startButtonInput() {
return startInput(ButtonElement.TAG);
}
public HtmlCanvasBuilder startCanvas() {
if (canvasBuilder == null) {
canvasBuilder = new HtmlCanvasBuilder(this);
}
trustedStart(CanvasElement.TAG, canvasBuilder);
return canvasBuilder;
}
public InputBuilder startCheckboxInput() {
return startInput("checkbox");
}
public HtmlTableColBuilder startCol() {
return startTableCol(TableColElement.TAG_COL);
}
public HtmlTableColBuilder startColGroup() {
return startTableCol(TableColElement.TAG_COLGROUP);
}
public HtmlDivBuilder startDiv() {
trustedStart(DivElement.TAG, divBuilder);
return divBuilder;
}
public HtmlDListBuilder startDList() {
if (dListBuilder == null) {
dListBuilder = new HtmlDListBuilder(this);
}
trustedStart(DListElement.TAG, dListBuilder);
return dListBuilder;
}
public HtmlFieldSetBuilder startFieldSet() {
if (fieldSetBuilder == null) {
fieldSetBuilder = new HtmlFieldSetBuilder(this);
}
trustedStart(FieldSetElement.TAG, fieldSetBuilder);
return fieldSetBuilder;
}
public InputBuilder startFileInput() {
return startInput("file");
}
public HtmlFormBuilder startForm() {
if (formBuilder == null) {
formBuilder = new HtmlFormBuilder(this);
}
trustedStart(FormElement.TAG, formBuilder);
return formBuilder;
}
public HtmlFrameBuilder startFrame() {
if (frameBuilder == null) {
frameBuilder = new HtmlFrameBuilder(this);
}
trustedStart(FrameElement.TAG, frameBuilder);
return frameBuilder;
}
public HtmlFrameSetBuilder startFrameSet() {
if (frameSetBuilder == null) {
frameSetBuilder = new HtmlFrameSetBuilder(this);
}
trustedStart(FrameSetElement.TAG, frameSetBuilder);
return frameSetBuilder;
}
public HtmlHeadingBuilder startH1() {
return startHeading(1);
}
public HtmlHeadingBuilder startH2() {
return startHeading(2);
}
public HtmlHeadingBuilder startH3() {
return startHeading(3);
}
public HtmlHeadingBuilder startH4() {
return startHeading(4);
}
public HtmlHeadingBuilder startH5() {
return startHeading(5);
}
public HtmlHeadingBuilder startH6() {
return startHeading(6);
}
public HtmlHeadBuilder startHead() {
if (headBuilder == null) {
headBuilder = new HtmlHeadBuilder(this);
}
trustedStart(HeadElement.TAG, headBuilder);
return headBuilder;
}
public InputBuilder startHiddenInput() {
return startInput("hidden");
}
public HtmlHRBuilder startHR() {
if (hrBuilder == null) {
hrBuilder = new HtmlHRBuilder(this);
}
trustedStart(HRElement.TAG, hrBuilder);
return hrBuilder;
}
public HtmlIFrameBuilder startIFrame() {
if (iFrameBuilder == null) {
iFrameBuilder = new HtmlIFrameBuilder(this);
}
trustedStart(IFrameElement.TAG, iFrameBuilder);
return iFrameBuilder;
}
public HtmlImageBuilder startImage() {
if (imageBuilder == null) {
imageBuilder = new HtmlImageBuilder(this);
}
trustedStart(ImageElement.TAG, imageBuilder);
return imageBuilder;
}
public InputBuilder startImageInput() {
return startInput("image");
}
public HtmlLabelBuilder startLabel() {
if (labelBuilder == null) {
labelBuilder = new HtmlLabelBuilder(this);
}
trustedStart(LabelElement.TAG, labelBuilder);
return labelBuilder;
}
public HtmlLegendBuilder startLegend() {
if (legendBuilder == null) {
legendBuilder = new HtmlLegendBuilder(this);
}
trustedStart(LegendElement.TAG, legendBuilder);
return legendBuilder;
}
public HtmlLIBuilder startLI() {
trustedStart(LIElement.TAG, liBuilder);
return liBuilder;
}
public HtmlLinkBuilder startLink() {
if (linkBuilder == null) {
linkBuilder = new HtmlLinkBuilder(this);
}
trustedStart(LinkElement.TAG, linkBuilder);
return linkBuilder;
}
public HtmlMapBuilder startMap() {
if (mapBuilder == null) {
mapBuilder = new HtmlMapBuilder(this);
}
trustedStart(MapElement.TAG, mapBuilder);
return mapBuilder;
}
public HtmlMetaBuilder startMeta() {
if (metaBuilder == null) {
metaBuilder = new HtmlMetaBuilder(this);
}
trustedStart(MetaElement.TAG, metaBuilder);
return metaBuilder;
}
public HtmlOListBuilder startOList() {
if (oListBuilder == null) {
oListBuilder = new HtmlOListBuilder(this);
}
trustedStart(OListElement.TAG, oListBuilder);
return oListBuilder;
}
public HtmlOptGroupBuilder startOptGroup() {
if (optGroupBuilder == null) {
optGroupBuilder = new HtmlOptGroupBuilder(this);
}
trustedStart(OptGroupElement.TAG, optGroupBuilder);
return optGroupBuilder;
}
public HtmlOptionBuilder startOption() {
trustedStart(OptionElement.TAG, optionBuilder);
return optionBuilder;
}
public HtmlParagraphBuilder startParagraph() {
if (paragraphBuilder == null) {
paragraphBuilder = new HtmlParagraphBuilder(this);
}
trustedStart(ParagraphElement.TAG, paragraphBuilder);
return paragraphBuilder;
}
public HtmlParamBuilder startParam() {
if (paramBuilder == null) {
paramBuilder = new HtmlParamBuilder(this);
}
trustedStart(ParamElement.TAG, paramBuilder);
return paramBuilder;
}
public InputBuilder startPasswordInput() {
return startInput("password");
}
public HtmlPreBuilder startPre() {
if (preBuilder == null) {
preBuilder = new HtmlPreBuilder(this);
}
trustedStart(PreElement.TAG, preBuilder);
return preBuilder;
}
public HtmlButtonBuilder startPushButton() {
return startButton("button");
}
public HtmlQuoteBuilder startQuote() {
return startQuote(QuoteElement.TAG_Q);
}
public InputBuilder startRadioInput(String name) {
InputBuilder builder = startInput("radio");
attribute("name", name);
return builder;
}
public HtmlButtonBuilder startResetButton() {
return startButton("reset");
}
public InputBuilder startResetInput() {
return startInput("reset");
}
public HtmlScriptBuilder startScript() {
if (scriptBuilder == null) {
scriptBuilder = new HtmlScriptBuilder(this);
}
trustedStart(ScriptElement.TAG, scriptBuilder);
return scriptBuilder;
}
public HtmlSelectBuilder startSelect() {
if (selectBuilder == null) {
selectBuilder = new HtmlSelectBuilder(this);
}
trustedStart(SelectElement.TAG, selectBuilder);
return selectBuilder;
}
public HtmlSourceBuilder startSource() {
if (sourceBuilder == null) {
sourceBuilder = new HtmlSourceBuilder(this);
}
trustedStart(SourceElement.TAG, sourceBuilder);
return sourceBuilder;
}
public HtmlSpanBuilder startSpan() {
trustedStart(SpanElement.TAG, spanBuilder);
return spanBuilder;
}
public HtmlStyleBuilder startStyle() {
if (styleBuilder == null) {
styleBuilder = new HtmlStyleBuilder(this);
}
trustedStart(StyleElement.TAG, styleBuilder);
return styleBuilder;
}
public HtmlButtonBuilder startSubmitButton() {
return startButton("submit");
}
public InputBuilder startSubmitInput() {
return startInput("submit");
}
public HtmlTableBuilder startTable() {
if (tableBuilder == null) {
tableBuilder = new HtmlTableBuilder(this);
}
trustedStart(TableElement.TAG, tableBuilder);
return tableBuilder;
}
public HtmlTableCaptionBuilder startTableCaption() {
if (tableCaptionBuilder == null) {
tableCaptionBuilder = new HtmlTableCaptionBuilder(this);
}
trustedStart(TableCaptionElement.TAG, tableCaptionBuilder);
return tableCaptionBuilder;
}
public HtmlTableSectionBuilder startTBody() {
return startTableSection(TableSectionElement.TAG_TBODY);
}
public HtmlTableCellBuilder startTD() {
trustedStart(TableCellElement.TAG_TD, tableCellBuilder);
return tableCellBuilder;
}
public HtmlTextAreaBuilder startTextArea() {
if (textAreaBuilder == null) {
textAreaBuilder = new HtmlTextAreaBuilder(this);
}
trustedStart(TextAreaElement.TAG, textAreaBuilder);
return textAreaBuilder;
}
public InputBuilder startTextInput() {
return startInput("text");
}
public HtmlTableSectionBuilder startTFoot() {
return startTableSection(TableSectionElement.TAG_TFOOT);
}
public HtmlTableCellBuilder startTH() {
trustedStart(TableCellElement.TAG_TH, tableCellBuilder);
return tableCellBuilder;
}
public HtmlTableSectionBuilder startTHead() {
return startTableSection(TableSectionElement.TAG_THEAD);
}
public HtmlTitleBuilder startTitle() {
if (titleBuilder == null) {
titleBuilder = new HtmlTitleBuilder(this);
}
trustedStart(TitleElement.TAG, titleBuilder);
return titleBuilder;
}
public HtmlTableRowBuilder startTR() {
trustedStart(TableRowElement.TAG, tableRowBuilder);
return tableRowBuilder;
}
public HtmlUListBuilder startUList() {
if (uListBuilder == null) {
uListBuilder = new HtmlUListBuilder(this);
}
trustedStart(UListElement.TAG, uListBuilder);
return uListBuilder;
}
public HtmlVideoBuilder startVideo() {
if (videoBuilder == null) {
videoBuilder = new HtmlVideoBuilder(this);
}
trustedStart(VideoElement.TAG, videoBuilder);
return videoBuilder;
}
@Override
public StylesBuilder style() {
return stylesBuilder;
}
public StylesBuilder styleProperty(SafeStyles style) {
assertCanAddStylePropertyImpl();
sb.append(style.asString());
return style();
}
/**
* Add a trusted attribute without escaping the name.
*/
public void trustedAttribute(String name, int value) {
assertCanAddAttributeImpl();
sb.append(" ").append(name).append("=\"").append(value).append("\"");
}
/**
* Add a trusted attribute without escaping the name. The value is still
* escaped.
*/
public void trustedAttribute(String name, String value) {
assertCanAddAttributeImpl();
sb.append(" ").append(name).append("=\"").append(escape(value)).append("\"");
}
public HtmlElementBuilder trustedStart(String tagName) {
trustedStart(tagName, elementBuilder);
return elementBuilder;
}
@Override
protected void doCloseStartTagImpl() {
sb.append(">");
}
@Override
protected void doCloseStyleAttributeImpl() {
sb.append("\"");
}
@Override
protected void doEndStartTagImpl() {
sb.append(" />");
}
@Override
protected void doEndTagImpl(String tagName) {
/*
* Add an end tag.
*
* Some browsers do not behave correctly if you self close (ex <select />)
* certain tags, so we always add the end tag unless the element
* specifically forbids an end tag (see doEndStartTagImpl()).
*
* The tag name is safe because it comes from the stack, and tag names are
* checked before they are added to the stack.
*/
sb.append("</").append(tagName).append(">");
}
@Override
protected Element doFinishImpl() {
Element tmp = Document.get().createDivElement();
tmp.setInnerHTML(asSafeHtml().asString());
return tmp.getFirstChildElement();
}
@Override
protected void doHtmlImpl(SafeHtml html) {
sb.append(html.asString());
}
@Override
protected void doOpenStyleImpl() {
sb.append(" style=\"");
}
@Override
protected void doTextImpl(String text) {
sb.append(escape(text));
}
/**
* Escape a string.
*
* @param s the string to escape
*/
private String escape(String s) {
return SafeHtmlUtils.htmlEscape(s);
}
/**
* Start a button with the specified type.
*/
private HtmlButtonBuilder startButton(String type) {
if (buttonBuilder == null) {
buttonBuilder = new HtmlButtonBuilder(this);
}
trustedStart("button", buttonBuilder);
buttonBuilder.attribute("type", type);
return buttonBuilder;
}
/**
* Start one of the many heading elements.
*/
private HtmlHeadingBuilder startHeading(int level) {
if (headingBuilder == null) {
headingBuilder = new HtmlHeadingBuilder(this);
}
trustedStart("h" + level, headingBuilder);
return headingBuilder;
}
/**
* Start an input with the specified type.
*/
private HtmlInputBuilder startInput(String type) {
trustedStart("input", inputBuilder);
attribute("type", type);
return inputBuilder;
}
/**
* Start a quote or blockquote.
*/
private HtmlQuoteBuilder startQuote(String tagName) {
if (quoteBuilder == null) {
quoteBuilder = new HtmlQuoteBuilder(this);
}
trustedStart(tagName, quoteBuilder);
return quoteBuilder;
}
/**
* Start a table col or colgroup.
*/
private HtmlTableColBuilder startTableCol(String tagName) {
if (tableColBuilder == null) {
tableColBuilder = new HtmlTableColBuilder(this);
}
trustedStart(tagName, tableColBuilder);
return tableColBuilder;
}
/**
* Start a table section of the specified tag name.
*/
private HtmlTableSectionBuilder startTableSection(String tagName) {
if (tableSectionBuilder == null) {
tableSectionBuilder = new HtmlTableSectionBuilder(this);
}
trustedStart(tagName, tableSectionBuilder);
return tableSectionBuilder;
}
/**
* Start a tag using the specified builder. The tagName is not checked or
* escaped.
*/
private void trustedStart(String tagName, ElementBuilderBase<?> builder) {
onStart(tagName, builder);
sb.append("<").append(tagName);
}
}