/* * Copyright 2008 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.elementparsers; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.uibinder.rebind.UiBinderWriter; import com.google.gwt.uibinder.rebind.XMLElement; import com.google.gwt.uibinder.rebind.messages.MessageWriter; import com.google.gwt.uibinder.rebind.messages.MessagesWriter; import com.google.gwt.uibinder.rebind.messages.PlaceholderInterpreter; /** * Interprets the interior of an html message. Extends the basic * PlaceholderInterpreter to handle dom elements with field names and computed * attributes--basically any dom attribute that cannot live in a static value * and so must be wrapped in a placeholder. (Note that the interior of such an * element is processed with a recursive call to * {@link XMLElement#consumeInnerHtml}.) */ class HtmlPlaceholderInterpreter extends PlaceholderInterpreter { private static final String EXAMPLE_OPEN_TAG = "<tag>"; private static final String EXAMPLE_CLOSE_TAG = "</tag>"; private int serial = 0; private final XMLElement.Interpreter<String> fieldAndComputed; HtmlPlaceholderInterpreter(UiBinderWriter writer, MessageWriter message, String ancestorExpression) { super(writer, message); fieldAndComputed = InterpreterPipe.newPipe(new FieldInterpreter(writer, ancestorExpression), new ComputedAttributeInterpreter(writer)); } @Override public String interpretElement(XMLElement elem) throws UnableToCompleteException { fieldAndComputed.interpretElement(elem); if (isDomPlaceholder(elem)) { MessagesWriter mw = uiWriter.getMessages(); String name = mw.consumeMessageAttribute("ph", elem); if ("".equals(name)) { name = "htmlElement" + (++serial); } String openTag = elem.consumeOpeningTag(); String openPlaceholder = nextOpenPlaceholder(name + "Begin", uiWriter.detokenate(openTag)); /* * This recursive innerHtml call has already been escaped. Hide it in a * token to avoid double escaping */ String body = tokenator.nextToken(elem.consumeInnerHtml(this)); String closeTag = elem.getClosingTag(); String closePlaceholder = nextClosePlaceholder(name + "End", closeTag); return openPlaceholder + body + closePlaceholder; } return super.interpretElement(elem); } @Override protected String consumePlaceholderInnards(XMLElement elem) throws UnableToCompleteException { return elem.consumeInnerHtml(fieldAndComputed); } /** * Returns the {@link #nextPlaceholder(String, String, String)}, using the * given {@code name} and {@code value} and a standard closing tag as example * text. */ protected String nextClosePlaceholder(String name, String value) { return nextPlaceholder(name, EXAMPLE_CLOSE_TAG, value); } /** * Returns the {@link #nextPlaceholder(String, String, String)}, using the * given {@code name} and {@code value} and a standard opening tag as example * text. */ protected String nextOpenPlaceholder(String name, String value) { return nextPlaceholder(name, EXAMPLE_OPEN_TAG, value); } /** * An element will require a placeholder if the user has called it out with a * ui:ph attribute, or if it will require run time swizzling (e.g. has a * ui:field). These latter we can identify easily because they'll have an * attribute that holds a tokenator token that was vended by * {@link UiBinderWriter}, typically in id. * * @return true if it has an ui:ph attribute, or has a token in any attribute */ private boolean isDomPlaceholder(XMLElement elem) { MessagesWriter mw = uiWriter.getMessages(); if (mw.hasMessageAttribute("ph", elem)) { return true; } for (int i = elem.getAttributeCount() - 1; i >= 0; i--) { if (elem.getAttribute(i).hasToken()) { return true; } } return false; } }