/* * 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.GWT; import com.google.gwt.core.client.JavaScriptObject; 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.TextDecoration; 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 += "-" + matches.getGroup(1).toLowerCase(); 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 textDecoration(TextDecoration value) { return delegate.styleProperty(SafeStylesUtils.forTextDecoration(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)); } }