blob: e91db2eb4f04fa0d9bad5b129dc64f5590c41b33 [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.core.client.JavaScriptObject;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.core.shared.impl.StringCase;
import com.google.gwt.dom.client.Style.BorderStyle;
import com.google.gwt.dom.client.Style.Cursor;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Float;
import com.google.gwt.dom.client.Style.FontStyle;
import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.dom.client.Style.ListStyleType;
import com.google.gwt.dom.client.Style.OutlineStyle;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.TableLayout;
import com.google.gwt.dom.client.Style.TextAlign;
import com.google.gwt.dom.client.Style.TextDecoration;
import com.google.gwt.dom.client.Style.TextJustify;
import com.google.gwt.dom.client.Style.TextOverflow;
import com.google.gwt.dom.client.Style.TextTransform;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.Style.VerticalAlign;
import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.safecss.shared.SafeStylesUtils;
import com.google.gwt.safehtml.shared.SafeUri;
import java.util.HashMap;
import java.util.Map;
/**
* Builds the style object.
*/
class HtmlStylesBuilder implements StylesBuilder {
/**
* A very fast map implemented using a {@link JavaScriptObject} when running
* in production.
*/
private static interface FastStringMap {
/**
* Get a value.
*
* @return the value, or null if not in the map
*/
String get(String key);
/**
* Set a value.
*/
void put(String key, String value);
}
/**
* JRE implementation of {@link FastStringMap}.
*/
private static class FastStringMapJre implements FastStringMap {
private final Map<String, String> map = new HashMap<String, String>();
@Override
public String get(String key) {
return map.get(key);
}
@Override
public void put(String key, String value) {
map.put(key, value);
}
}
/**
* Native implementation of {@link FastStringMap}.
*
* The native implementation sets and retrieves properties on a
* {@link JavaScriptObject}, which avoids the dynamic casts associated with
* the emulated version of {@link java.util.Map}.
*/
private static class FastStringMapClient implements FastStringMap {
private final JavaScriptObject map = JavaScriptObject.createObject();
@Override
public String get(String key) {
return getImpl(map, key);
}
@Override
public void put(String key, String value) {
putImpl(map, key, value);
}
private native String getImpl(JavaScriptObject map, String key) /*-{
return map[key] || null;
}-*/;
private native void putImpl(JavaScriptObject map, String key, String value) /*-{
map[key] = value;
}-*/;
}
/**
* A map of camelCase style properties to their hyphenated equivalents.
*
* The set of style property names is limited, and common ones are reused
* frequently, so caching saves us from converting every style property name
* from camelCase to hyphenated form.
*/
private static FastStringMap camelCaseMap;
/**
* Regex to match a word in a camelCase phrase. A word starts with an
* uppercase or lowercase letter, followed by zero or more non-uppercase
* letters. For example, in the camelCase phrase backgroundUrl, the pattern
* matches "background" and "Url".
*
* This pattern is not used to validate the style property name.
* {@link SafeStylesUtils} performs a more detailed check.
*/
private static RegExp camelCaseWord;
/**
* Regex to match a word that starts with an uppercase letter.
*/
private static RegExp caseWord;
/**
* Convert a camelCase or hyphenated string to a hyphenated string.
*
* @param name the camelCase or hyphenated string to convert
* @return the hyphenated string
*/
// Visible for testing
static String toHyphenatedForm(String name) {
// Static initializers.
if (camelCaseWord == null) {
camelCaseMap = GWT.isClient() ? new FastStringMapClient() : new FastStringMapJre();
camelCaseWord = RegExp.compile("([A-Za-z])([^A-Z]*)", "g");
caseWord = RegExp.compile("[A-Z]([^A-Z]*)");
}
// Early exit if already in hyphenated form.
if (name.contains("-")) {
return name;
}
// Check for the name in the cache.
String hyphenated = camelCaseMap.get(name);
// Convert the name to hyphenated format if not in the cache.
if (hyphenated == null) {
hyphenated = "";
MatchResult matches;
while ((matches = camelCaseWord.exec(name)) != null) {
String word = matches.getGroup(0);
if (caseWord.exec(word) == null) {
// The first letter is already lowercase, probably the first word.
hyphenated += word;
} else {
// Hyphenate the first letter.
hyphenated += "-" + StringCase.toLower(matches.getGroup(1));
if (matches.getGroupCount() > 1) {
hyphenated += matches.getGroup(2);
}
}
}
camelCaseMap.put(name, hyphenated);
}
return hyphenated;
}
private final HtmlBuilderImpl delegate;
/**
* Construct a new {@link HtmlStylesBuilder}.
*
* @param delegate the delegate that builds the style
*/
HtmlStylesBuilder(HtmlBuilderImpl delegate) {
this.delegate = delegate;
}
@Override
public StylesBuilder backgroundImage(SafeUri uri) {
return delegate.styleProperty(SafeStylesUtils.forBackgroundImage(uri));
}
@Override
public StylesBuilder borderStyle(BorderStyle value) {
return delegate.styleProperty(SafeStylesUtils.forBorderStyle(value));
}
@Override
public StylesBuilder borderWidth(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forBorderWidth(value, unit));
}
@Override
public StylesBuilder bottom(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forBottom(value, unit));
}
@Override
public StylesBuilder cursor(Cursor value) {
return delegate.styleProperty(SafeStylesUtils.forCursor(value));
}
@Override
public StylesBuilder display(Display value) {
return delegate.styleProperty(SafeStylesUtils.forDisplay(value));
}
@Override
public void endStyle() {
delegate.endStyle();
}
@Override
public StylesBuilder floatprop(Float value) {
return delegate.styleProperty(SafeStylesUtils.forFloat(value));
}
@Override
public StylesBuilder fontSize(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forFontSize(value, unit));
}
@Override
public StylesBuilder fontStyle(FontStyle value) {
return delegate.styleProperty(SafeStylesUtils.forFontStyle(value));
}
@Override
public StylesBuilder fontWeight(FontWeight value) {
return delegate.styleProperty(SafeStylesUtils.forFontWeight(value));
}
@Override
public StylesBuilder height(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forHeight(value, unit));
}
@Override
public StylesBuilder left(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forLeft(value, unit));
}
@Override
public StylesBuilder lineHeight(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forLineHeight(value, unit));
}
@Override
public StylesBuilder listStyleType(ListStyleType value) {
return delegate.styleProperty(SafeStylesUtils.forListStyleType(value));
}
@Override
public StylesBuilder margin(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forMargin(value, unit));
}
@Override
public StylesBuilder marginBottom(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forMarginBottom(value, unit));
}
@Override
public StylesBuilder marginLeft(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forMarginLeft(value, unit));
}
@Override
public StylesBuilder marginRight(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forMarginRight(value, unit));
}
@Override
public StylesBuilder marginTop(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forMarginTop(value, unit));
}
@Override
public StylesBuilder opacity(double value) {
return delegate.styleProperty(SafeStylesUtils.forOpacity(value));
}
@Override
public StylesBuilder outlineStyle(OutlineStyle value) {
return delegate.styleProperty(SafeStylesUtils.forOutlineStyle(value));
}
@Override
public StylesBuilder outlineWidth(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forOutlineWidth(value, unit));
}
@Override
public StylesBuilder overflow(Overflow value) {
return delegate.styleProperty(SafeStylesUtils.forOverflow(value));
}
@Override
public StylesBuilder overflowX(Overflow value) {
return delegate.styleProperty(SafeStylesUtils.forOverflowX(value));
}
@Override
public StylesBuilder overflowY(Overflow value) {
return delegate.styleProperty(SafeStylesUtils.forOverflowY(value));
}
@Override
public StylesBuilder padding(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forPadding(value, unit));
}
@Override
public StylesBuilder paddingBottom(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forPaddingBottom(value, unit));
}
@Override
public StylesBuilder paddingLeft(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forPaddingLeft(value, unit));
}
@Override
public StylesBuilder paddingRight(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forPaddingRight(value, unit));
}
@Override
public StylesBuilder paddingTop(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forPaddingTop(value, unit));
}
@Override
public StylesBuilder position(Position value) {
return delegate.styleProperty(SafeStylesUtils.forPosition(value));
}
@Override
public StylesBuilder right(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forRight(value, unit));
}
@Override
public StylesBuilder tableLayout(TableLayout value) {
return delegate.styleProperty(SafeStylesUtils.forTableLayout(value));
}
@Override
public StylesBuilder textAlign(TextAlign value) {
return delegate.styleProperty(SafeStylesUtils.forTextAlign(value));
}
@Override
public StylesBuilder textDecoration(TextDecoration value) {
return delegate.styleProperty(SafeStylesUtils.forTextDecoration(value));
}
@Override
public StylesBuilder textIndent(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forTextIndent(value, unit));
}
@Override
public StylesBuilder textJustify(TextJustify value) {
return delegate.styleProperty(SafeStylesUtils.forTextJustify(value));
}
@Override
public StylesBuilder textOverflow(TextOverflow value) {
return delegate.styleProperty(SafeStylesUtils.forTextOverflow(value));
}
@Override
public StylesBuilder textTransform(TextTransform value) {
return delegate.styleProperty(SafeStylesUtils.forTextTransform(value));
}
@Override
public StylesBuilder top(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forTop(value, unit));
}
@Override
public StylesBuilder trustedBackgroundColor(String value) {
return delegate.styleProperty(SafeStylesUtils.forTrustedBackgroundColor(value));
}
@Override
public StylesBuilder trustedBackgroundImage(String value) {
return delegate.styleProperty(SafeStylesUtils.forTrustedBackgroundImage(value));
}
@Override
public StylesBuilder trustedBorderColor(String value) {
return delegate.styleProperty(SafeStylesUtils.forTrustedBorderColor(value));
}
@Override
public StylesBuilder trustedColor(String value) {
return delegate.styleProperty(SafeStylesUtils.forTrustedColor(value));
}
@Override
public StylesBuilder trustedOutlineColor(String value) {
return delegate.styleProperty(SafeStylesUtils.forTrustedOutlineColor(value));
}
@Override
public StylesBuilder trustedProperty(String name, double value, Unit unit) {
name = toHyphenatedForm(name);
return delegate.styleProperty(SafeStylesUtils.fromTrustedNameAndValue(name, value, unit));
}
@Override
public StylesBuilder trustedProperty(String name, String value) {
name = toHyphenatedForm(name);
return delegate.styleProperty(SafeStylesUtils.fromTrustedNameAndValue(name, value));
}
@Override
public StylesBuilder verticalAlign(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forVerticalAlign(value, unit));
}
@Override
public StylesBuilder verticalAlign(VerticalAlign value) {
return delegate.styleProperty(SafeStylesUtils.forVerticalAlign(value));
}
@Override
public StylesBuilder visibility(Visibility value) {
return delegate.styleProperty(SafeStylesUtils.forVisibility(value));
}
@Override
public StylesBuilder width(double value, Unit unit) {
return delegate.styleProperty(SafeStylesUtils.forWidth(value, unit));
}
@Override
public StylesBuilder zIndex(int value) {
return delegate.styleProperty(SafeStylesUtils.forZIndex(value));
}
}