/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.lwuit.html;
import com.sun.lwuit.Component;
import com.sun.lwuit.Font;
import com.sun.lwuit.plaf.Style;
import java.util.Enumeration;
/**
* The CSSElement class defines a single CSS element with its attributes and children.
* It extends Element and adds to it certain CSS-specific methods.
* Each CSSElement object is in fact a CSS selector.
*
* @author Ofir Leitner
*/
class CSSElement extends Element {
/**
* Defines the Dots-per-Inch - used when CSS length values are denoted in units like inches (in), cm, mm, points (pt) etc.
* This can be customized per device in build time. However since these units are not mandatory in WCSS so even if this is not accurate no harm done.
* In any case, always prefer using the supported length units: px, em, ex
*/
private static final int DPI=72;
/**
* Possible suffix strings for values of the CSS length type
*/
private final static String[] CSS_LENGTH_SUFFIX = {"px","em","ex","in","pt","pc","mm","cm"};
private final static String CENTER_STR = "center"; // Since this value is used in multiple CSS properties it is defined as a constant
private static final String CSS_NONE = "none"; // Since this value is used in multiple CSS properties it is defined as a constant
/**
* A constant representing both the attribute value of 'font-variant' and the font name that should be given to small-caps fonts
* Since LWUIT/J2ME doesn't support small-caps fonts, when a small-caps font varinat is requested
* the font-family is changed to "smallcaps" which should be loaded to HTMLComponent and the theme as a bitmap font
* If no smallcaps font is found at all, then the family stays the same, but if even only one is found - the best match will be used.
*/
final static String SMALL_CAPS_STRING = "small-caps";
// Background repeat strings and their corresponding values
static final String[] BG_REPEAT_STRINGS = {"repeat","repeat-x","repeat-y","no-repeat"};
private static final int[] BG_REPEAT_VALS = {Style.BACKGROUND_IMAGE_TILE_BOTH,Style.BACKGROUND_IMAGE_TILE_HORIZONTAL,Style.BACKGROUND_IMAGE_TILE_VERTICAL,0};
// Horizontal alignment strings and their corresponding values
private static final String[] TEXT_ALIGN_STRINGS = {"left","right",CENTER_STR};
private static final int[] TEXT_ALIGN_VALS = {Component.LEFT,Component.RIGHT,Component.CENTER};
// Vertical alignment strings and their corresponding values
private static final String[] VERTICAL_ALIGN_STRINGS = {"top","middle","bottom","baseline","sub","super"};
private static final int[] VERTICAL_ALIGN_VALS = {Component.TOP,Component.CENTER,Component.BOTTOM,-1,-1,-1};
// Border style strings
private static final String[] BORDER_STYLE_STRINGS = {CSS_NONE,"solid","groove","ridge","inset","outset","dotted","dashed","double"};
// Border width strings and their corresponding values
private static final String[] BORDER_WIDTH_STRINGS = {"thin","medium","thick"};
private static final int[] BORDER_WIDTH_VALS = {1,3,5};
/**
* The default border width (when not specified)
*/
static final int BORDER_DEFAULT_WIDTH = BORDER_WIDTH_VALS[1]; // medium
//
// Font strings and values constants
//
static final int FONT_SIZE_SMALLER = -3;
static final int FONT_SIZE_LARGER = -2;
// Constants defining the approximate sizes of a small/medium/large font (This can be tweaked per platform)
static final int FONT_SIZE_SMALL = 12;
static final int FONT_SIZE_MEDIUM = 15;
static final int FONT_SIZE_LARGE = 19;
// Font size strings and their corresponding values
private static final String[] FONT_SIZE_STRINGS = {"xx-small","x-small","small","medium","large","x-large","xx-large","smaller","larger"};
private static final int[] FONT_SIZE_VALS = {FONT_SIZE_SMALL-4,FONT_SIZE_SMALL-2,FONT_SIZE_SMALL,FONT_SIZE_MEDIUM,FONT_SIZE_LARGE,FONT_SIZE_LARGE+2,FONT_SIZE_LARGE+4,FONT_SIZE_SMALLER,FONT_SIZE_LARGER};
// Font style strings and their corresponding values, note that oblique is translated to italic
private static final String[] FONT_STYLE_STRINGS = {"normal","italic","oblique"};
private static final int[] FONT_STYLE_VALS = {Font.STYLE_PLAIN,Font.STYLE_ITALIC,Font.STYLE_ITALIC};
// Font weight strings and their corresponding values
// Note that since we have only two levels of boldness (plain, bold) - bolder always means bold, and lighter always means plain
private static final String[] FONT_WEIGHT_STRINGS = {"normal","bold","bolder","lighter","100","200","300","400","500","600","700","800","900"};
private static final int[] FONT_WEIGHT_VALS = {Font.STYLE_PLAIN,Font.STYLE_BOLD,Font.STYLE_BOLD,Font.STYLE_PLAIN,Font.STYLE_PLAIN,Font.STYLE_PLAIN,Font.STYLE_PLAIN,Font.STYLE_PLAIN,Font.STYLE_PLAIN,Font.STYLE_BOLD,Font.STYLE_BOLD,Font.STYLE_BOLD,Font.STYLE_BOLD};
/**
* Since the other tags are 0 based the CSS tags should start at this point
*/
static final int CSS_STYLE_ID_OFFSET = 500;
// CSS Attributes
static final int CSS_BACKGROUND_COLOR = CSS_STYLE_ID_OFFSET;
static final int CSS_BACKGROUND_IMAGE = CSS_STYLE_ID_OFFSET + 1;
static final int CSS_BACKGROUND_REPEAT = CSS_STYLE_ID_OFFSET + 2;
static final int CSS_BACKGROUND_ATTACHMENT = CSS_STYLE_ID_OFFSET + 3;
static final int CSS_BACKGROUND_POSITION_X = CSS_STYLE_ID_OFFSET + 4;
static final int CSS_BACKGROUND_POSITION_Y = CSS_STYLE_ID_OFFSET + 5;
static final int CSS_BORDER_TOP_WIDTH = CSS_STYLE_ID_OFFSET + 6;
static final int CSS_BORDER_LEFT_WIDTH = CSS_STYLE_ID_OFFSET + 7;
static final int CSS_BORDER_BOTTOM_WIDTH = CSS_STYLE_ID_OFFSET + 8;
static final int CSS_BORDER_RIGHT_WIDTH = CSS_STYLE_ID_OFFSET + 9;
static final int CSS_BORDER_TOP_STYLE = CSS_STYLE_ID_OFFSET + 10;
static final int CSS_BORDER_LEFT_STYLE = CSS_STYLE_ID_OFFSET + 11;
static final int CSS_BORDER_BOTTOM_STYLE = CSS_STYLE_ID_OFFSET + 12;
static final int CSS_BORDER_RIGHT_STYLE = CSS_STYLE_ID_OFFSET + 13;
static final int CSS_BORDER_TOP_COLOR= CSS_STYLE_ID_OFFSET + 14;
static final int CSS_BORDER_LEFT_COLOR = CSS_STYLE_ID_OFFSET + 15;
static final int CSS_BORDER_BOTTOM_COLOR = CSS_STYLE_ID_OFFSET + 16;
static final int CSS_BORDER_RIGHT_COLOR = CSS_STYLE_ID_OFFSET + 17;
static final int CSS_CLEAR = CSS_STYLE_ID_OFFSET + 18;
static final int CSS_COLOR = CSS_STYLE_ID_OFFSET + 19;
static final int CSS_VERTICAL_ALIGN = CSS_STYLE_ID_OFFSET + 20;
static final int CSS_DISPLAY = CSS_STYLE_ID_OFFSET + 21;
static final int CSS_FLOAT = CSS_STYLE_ID_OFFSET + 22;
static final int CSS_FONT_FAMILY = CSS_STYLE_ID_OFFSET + 23;
static final int CSS_FONT_SIZE = CSS_STYLE_ID_OFFSET + 24;
static final int CSS_FONT_STYLE = CSS_STYLE_ID_OFFSET + 25;
static final int CSS_FONT_WEIGHT = CSS_STYLE_ID_OFFSET + 26;
static final int CSS_FONT_VARIANT = CSS_STYLE_ID_OFFSET + 27;
static final int CSS_HEIGHT = CSS_STYLE_ID_OFFSET + 28;
static final int CSS_WIDTH = CSS_STYLE_ID_OFFSET + 29;
static final int CSS_VISIBILITY = CSS_STYLE_ID_OFFSET + 30;
static final int CSS_WHITE_SPACE = CSS_STYLE_ID_OFFSET + 31;
static final int CSS_LIST_STYLE_IMAGE = CSS_STYLE_ID_OFFSET + 32;
static final int CSS_LIST_STYLE_POSITION = CSS_STYLE_ID_OFFSET + 33;
static final int CSS_LIST_STYLE_TYPE = CSS_STYLE_ID_OFFSET + 34;
static final int CSS_MARGIN_TOP = CSS_STYLE_ID_OFFSET + 35;
static final int CSS_MARGIN_LEFT = CSS_STYLE_ID_OFFSET + 36;
static final int CSS_MARGIN_BOTTOM = CSS_STYLE_ID_OFFSET + 37;
static final int CSS_MARGIN_RIGHT = CSS_STYLE_ID_OFFSET + 38;
static final int CSS_PADDING_TOP = CSS_STYLE_ID_OFFSET + 39;
static final int CSS_PADDING_LEFT = CSS_STYLE_ID_OFFSET + 40;
static final int CSS_PADDING_BOTTOM = CSS_STYLE_ID_OFFSET + 41;
static final int CSS_PADDING_RIGHT = CSS_STYLE_ID_OFFSET + 42;
static final int CSS_TEXT_ALIGN = CSS_STYLE_ID_OFFSET + 43;
static final int CSS_TEXT_DECORATION = CSS_STYLE_ID_OFFSET + 44; // Not implemented, since the only mandatory WCSS decoration value is 'none' which is usually used to remove underlines from links - since we don't have underlines it has no meaning
static final int CSS_TEXT_INDENT = CSS_STYLE_ID_OFFSET + 45;
static final int CSS_TEXT_TRANSFORM = CSS_STYLE_ID_OFFSET + 46;
static final int CSS_WAP_ACCESSKEY = CSS_STYLE_ID_OFFSET + 47;
static final int CSS_WAP_INPUT_FORMAT = CSS_STYLE_ID_OFFSET + 48;
static final int CSS_WAP_INPUT_REQUIRED = CSS_STYLE_ID_OFFSET + 49;
static final int CSS_PAGEURL = CSS_STYLE_ID_OFFSET + 50; // This attribute is not a CSS attribute, but rather for internal LWUIT usage, to identify the stylesheet's base url
/**
* The types of the attribute
*/
static final int[] CSS_ATTRIBUTE_TYPES = {
TYPE_COLOR, // CSS_BACKGROUND_COLOR
TYPE_CSS_URL, //CSS_BACKGROUND_IMAGE
TYPE_NMTOKENS, //CSS_BACKGROUND_REPEAT
TYPE_NMTOKENS, //CSS_BACKGROUND_ATTACHMENT
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_BACKGROUND_POSITION_X
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_BACKGROUND_POSITION_Y
TYPE_CSS_LENGTH, //CSS_BORDER_TOP_WIDTH
TYPE_CSS_LENGTH, //CSS_BORDER_RIGHT_WIDTH
TYPE_CSS_LENGTH, //CSS_BORDER_BOTTOM_WIDTH
TYPE_CSS_LENGTH, //CSS_BORDER_LEFT_WIDTH
TYPE_NMTOKENS, //CSS_BORDER_TOP_STYLE
TYPE_NMTOKENS, //CSS_BORDER_RIGHT_STYLE
TYPE_NMTOKENS, //CSS_BORDER_BOTTOM_STYLE
TYPE_NMTOKENS, //CSS_BORDER_LEFT_STYLE
TYPE_COLOR, //CSS_BORDER_TOP_COLOR
TYPE_COLOR, // CSS_BORDER_RIGHT_COLOR
TYPE_COLOR, // CSS_BORDER_BOTTOM_COLOR
TYPE_COLOR, // CSS_BORDER_LEFT_COLOR
TYPE_NMTOKENS, //CSS_CLEAR
TYPE_COLOR, // CSS_COLOR
TYPE_NMTOKENS, //CSS_VERTICAL_ALIGN
TYPE_NMTOKENS, //CSS_DISPLAY
TYPE_NMTOKENS, //CSS_FLOAT
TYPE_NMTOKENS, //CSS_FONT_FAMILY
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_FONT_SIZE
TYPE_NMTOKENS, //CSS_FONT_STYLE
TYPE_NMTOKENS, //CSS_FONT_WEIGHT
TYPE_NMTOKENS, //CSS_FONT_VARIANT
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_HEIGHT
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_WIDTH
TYPE_NMTOKENS, // CSS_VISIBILITY
TYPE_NMTOKENS, //CSS_WHITE_SPACE
TYPE_CSS_URL, //CSS_LIST_STYLE_IMAGE
TYPE_NMTOKENS, //CSS_LIST_STYLE_POSITION
TYPE_NMTOKENS, //CSS_LIST_STYLE_TYPE
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_TOP
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_RIGHT
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_BOTTOM
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_LEFT
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_TOP
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_RIGHT
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_BOTTOM
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_LEFT
TYPE_ALIGN, //CSS_TEXT_ALIGN
TYPE_NMTOKENS, //CSS_TEXT_DECORATION
TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_TEXT_INDENT
TYPE_NMTOKENS, //CSS_TEXT_TRANSFORM
TYPE_NMTOKENS, // CSS_WAP_ACCESSKEY
TYPE_NMTOKENS, // CSS_WAP_INPUT_FORMAT
TYPE_BOOLEAN, // CSS_WAP_INPUT_REQUIRED
TYPE_NMTOKENS, // CSS_PAGE_URL (internal usage)
};
/**
* An array containing the allowed strings for CSS attributes.
* Note that these strings are allowed in addition for the allowed values according to the attribute types.
* Also, unlike the allowed string in Element that are matched per type, here in CSSElement each line matches an attribute (and not its type).
* This is because of the great variance of allowed strings in CSS that are really per attribute and not per type.
*/
private String[][] CSS_ALLOWED_STRINGS = {
null, //TYPE_COLOR, // CSS_BACKGROUND_COLOR
null, //TYPE_CSS_URL, //CSS_BACKGROUND_IMAGE
BG_REPEAT_STRINGS, //TYPE_NMTOKENS, //CSS_BACKGROUND_REPEAT
{"fixed","scroll"}, //TYPE_NMTOKENS, //CSS_BACKGROUND_ATTACHMENT
{"left",CENTER_STR,"right"}, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_BACKGROUND_POSITION_X
{"top",CENTER_STR,"bottom"}, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_BACKGROUND_POSITION_Y
BORDER_WIDTH_STRINGS, //TYPE_CSS_LENGTH, //CSS_BORDER_TOP_WIDTH
BORDER_WIDTH_STRINGS, //TYPE_CSS_LENGTH, //CSS_BORDER_RIGHT_WIDTH
BORDER_WIDTH_STRINGS, //TYPE_CSS_LENGTH, //CSS_BORDER_BOTTOM_WIDTH
BORDER_WIDTH_STRINGS, //TYPE_CSS_LENGTH, //CSS_BORDER_LEFT_WIDTH
BORDER_STYLE_STRINGS, //TYPE_NMTOKENS, //CSS_BORDER_TOP_STYLE
BORDER_STYLE_STRINGS, //TYPE_NMTOKENS, //CSS_BORDER_RIGHT_STYLE
BORDER_STYLE_STRINGS, //TYPE_NMTOKENS, //CSS_BORDER_BOTTOM_STYLE
BORDER_STYLE_STRINGS, //TYPE_NMTOKENS, //CSS_BORDER_LEFT_STYLE
null, //TYPE_COLOR, //CSS_BORDER_TOP_COLOR
null, //TYPE_COLOR, // CSS_BORDER_RIGHT_COLOR
null, //TYPE_COLOR, // CSS_BORDER_BOTTOM_COLOR
null, //TYPE_COLOR, // CSS_BORDER_LEFT_COLOR
{"left","right",CSS_NONE,"both"}, //TYPE_NMTOKENS, //CSS_CLEAR
null, //TYPE_COLOR, // CSS_COLOR
VERTICAL_ALIGN_STRINGS, //TYPE_NMTOKENS, //CSS_VERTICAL_ALIGN
{"inline","block","list-item",CSS_NONE,"-wap-marquee"}, //TYPE_NMTOKENS, //CSS_DISPLAY
{"left","right",CSS_NONE}, //TYPE_NMTOKENS, //CSS_FLOAT
null, //TYPE_NMTOKENS, //CSS_FONT_FAMILY
FONT_SIZE_STRINGS, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_FONT_SIZE
FONT_STYLE_STRINGS, //TYPE_NMTOKENS, //CSS_FONT_STYLE
FONT_WEIGHT_STRINGS, //TYPE_NMTOKENS, //CSS_FONT_WEIGHT
{"normal",SMALL_CAPS_STRING}, //TYPE_NMTOKENS, //CSS_FONT_VARIANT
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_HEIGHT
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_WIDTH
{"hidden","visible"}, //TYPE_NMTOKENS, // CSS_VISIBILITY
{"normal","pre","nowrap"}, //TYPE_NMTOKENS, //CSS_WHITE_SPACE
null, //TYPE_CSS_URL, //CSS_LIST_STYLE_IMAGE
{"inside","outside"}, //TYPE_NMTOKENS, //CSS_LIST_STYLE_POSITION
{CSS_NONE,"disc","circle","square","decimal","upper-alpha","lower-alpha","upper-roman","lower-roman"}, //TYPE_NMTOKENS, //CSS_LIST_STYLE_TYPE
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_TOP
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_RIGHT
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_BOTTOM
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_LEFT
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_TOP
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_RIGHT
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_BOTTOM
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_LEFT
TEXT_ALIGN_STRINGS, //TYPE_ALIGN, //CSS_TEXT_ALIGN
{CSS_NONE}, //TYPE_NMTOKENS, //CSS_TEXT_DECORATION
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_TEXT_INDENT
{CSS_NONE,"uppercase","lowercase","capitalize"}, //TYPE_NMTOKENS, //CSS_TEXT_TRANSFORM
null, //TYPE_NMTOKENS, // CSS_WAP_ACCESSKEY
null, //TYPE_NMTOKENS, // CSS_WAP_INPUT_FORMAT
{"true","false"}, //TYPE_BOOLEAN, // CSS_WAP_INPUT_REQUIRED
null, //TYPE_NMTOKENS, // baseurl (internal usage)
};
/**
* The values of each of the allowed strings.
* When adding an attribute which its value is an allowed string, the attribute value is set according to this array.
* For attributes that have no allowed strings (i.e. accpeting numeric values solely) this is not reelvant.
* For attributes that do have allowed strings and 'null' as the values, it means that the first string in the allowed list will be translated to 0, the second to 1 etc.
* The purpose of converting strings in strings that have non-null values, is to convert them to a value representing the essence of the string (FOr example thin in border is 1)
* And for those with null values, is to avoid having to parse strings all the time (so they are conerted to 0,1...,n)
*/
private final int[][] CSS_ALLOWED_STRINGS_VALS = {
null, //TYPE_COLOR, // CSS_BACKGROUND_COLOR
null, //TYPE_CSS_URL, //CSS_BACKGROUND_IMAGE
BG_REPEAT_VALS, //TYPE_NMTOKENS, //CSS_BACKGROUND_REPEAT
null, //{"fixed","scroll"}, //TYPE_NMTOKENS, //CSS_BACKGROUND_ATTACHMENT
BG_POS_PERCENTAGE, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_BACKGROUND_POSITION_X
BG_POS_PERCENTAGE, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_BACKGROUND_POSITION_Y
BORDER_WIDTH_VALS, //TYPE_CSS_LENGTH, //CSS_BORDER_TOP_WIDTH
BORDER_WIDTH_VALS, //TYPE_CSS_LENGTH, //CSS_BORDER_RIGHT_WIDTH
BORDER_WIDTH_VALS, //TYPE_CSS_LENGTH, //CSS_BORDER_BOTTOM_WIDTH
BORDER_WIDTH_VALS, //TYPE_CSS_LENGTH, //CSS_BORDER_LEFT_WIDTH
null, //BORDER_STYLE_VALS, //TYPE_NMTOKENS, //CSS_BORDER_TOP_STYLE
null, //BORDER_STYLE_VALS, //TYPE_NMTOKENS, //CSS_BORDER_RIGHT_STYLE
null, //BORDER_STYLE_VALS, //TYPE_NMTOKENS, //CSS_BORDER_BOTTOM_STYLE
null, //BORDER_STYLE_VALS, //TYPE_NMTOKENS, //CSS_BORDER_LEFT_STYLE
null, //TYPE_COLOR, //CSS_BORDER_TOP_COLOR
null, //TYPE_COLOR, // CSS_BORDER_RIGHT_COLOR
null, //TYPE_COLOR, // CSS_BORDER_BOTTOM_COLOR
null, //TYPE_COLOR, // CSS_BORDER_LEFT_COLOR
null, //CLEAR_VALS, //TYPE_NMTOKENS, //CSS_CLEAR
null, //TYPE_COLOR, // CSS_COLOR
VERTICAL_ALIGN_VALS, //TYPE_NMTOKENS, //CSS_VERTICAL_ALIGN
null, //DISPLAY_VALS, //TYPE_NMTOKENS, //CSS_DISPLAY
null, //FLOAT_VALS, //TYPE_NMTOKENS, //CSS_FLOAT
null, //TYPE_NMTOKENS, //CSS_FONT_FAMILY
FONT_SIZE_VALS, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_FONT_SIZE
FONT_STYLE_VALS, //TYPE_NMTOKENS, //CSS_FONT_STYLE
FONT_WEIGHT_VALS, //TYPE_NMTOKENS, //CSS_FONT_WEIGHT
null, //FONT_VARIANT_VALS, //TYPE_NMTOKENS, //CSS_FONT_VARIANT
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_HEIGHT
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_WIDTH
null, //VISIBILITY_VALS, //TYPE_NMTOKENS, // CSS_VISIBILITY
null, //WHITE_SPACE_VALS, //TYPE_NMTOKENS, //CSS_WHITE_SPACE
null, //TYPE_CSS_URL, //CSS_LIST_STYLE_IMAGE
null, //LIST_STYLE_POSITION_VALS, //TYPE_NMTOKENS, //CSS_LIST_STYLE_POSITION
null, //LIST_STYLE_TYPE_VALS, //TYPE_NMTOKENS, //CSS_LIST_STYLE_TYPE
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_TOP
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_RIGHT
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_BOTTOM
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_MARGIN_LEFT
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_TOP
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_RIGHT
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_BOTTOM
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_PADDING_LEFT
TEXT_ALIGN_VALS, //TYPE_ALIGN, //CSS_TEXT_ALIGN
null, //{CSS_NONE}, //TYPE_NMTOKENS, //CSS_TEXT_DECORATION
null, //TYPE_CSS_LENGTH_OR_PERCENTAGE, //CSS_TEXT_INDENT
null, //TEXT_TRANSFORM_VALS, //TYPE_NMTOKENS, //CSS_TEXT_TRANSFORM
null, //TYPE_NMTOKENS, // CSS_WAP_ACCESSKEY
null, //TYPE_NMTOKENS, // CSS_WAP_INPUT_FORMAT
null, //TYPE_BOOLEAN, // CSS_WAP_INPUT_REQUIRED
null, //TYPE_NMTOKENS, // baseurl (internal usage)
};
/**
* The attrVals array holds the numeric values of most of the attributes of this element.
* This is used for quick access, instead of parsing strings in the attributes Hashtable
* Values are set during addAttribute.
*/
int[] attrVals = new int[CSS_ATTRIBUTE_ROOTS.length];
/**
* This very high integer value is added to a numeric value stored in attrVals to denote that it is a percentage value
*/
static final int VAL_PERCENTAGE = 1<<20;
/**
* This very high integer value is added to a numeric value stored in attrVals to denote that it is a value in the CSS 'ex' unit (EX means half of the font size)
*/
static final int VAL_EX = 1<<21;
/**
* Values associated with BG_POS_STRINGS
*/
private final static int[] BG_POS_PERCENTAGE = {0+VAL_PERCENTAGE,50+VAL_PERCENTAGE,100+VAL_PERCENTAGE};
/**
* A string array with the attributes supported by the WCSS spec
* Note that shorthand attributes appear in a different array
*/
static final String[] CSS_ATTRIBUTE_ROOTS = {
"background-color", "background-image", "background-repeat", "background-attachment",
"background-position-x", "background-position-y",
"border-top-width", "border-left-width", "border-bottom-width", "border-right-width",
"border-top-style", "border-left-style", "border-bottom-style", "border-right-style",
"border-top-color", "border-left-color", "border-bottom-color", "border-right-color",
"clear", "color", "vertical-align", "display", "float",
"font-family", "font-size", "font-style", "font-weight", "font-variant",
"height", "width", "visibility", "white-space",
"list-style-image", "list-style-position", "list-style-type",
"margin-top", "margin-left", "margin-bottom", "margin-right",
"padding-top", "padding-left", "padding-bottom", "padding-right",
"text-align", "text-decoration", "text-indent", "text-transform",
"-wap-access-key","-wap-input-format","-wap-input-required",
"pageurl" // an attribute used for internal LWUIT uses
};
/**
* A string array containing all supported shorthand attributes
* Shorthand attrributes are mapped to multiple CSS base attributes are used to define several CSS attributes at once
*/
static final String[] CSS_SHORTHAND_ATTRIBUTE_LIST = {
"background",
"background-position",
"border-width",
"border-style",
"border-color",
"border-top",
"border-right",
"border-bottom",
"border-left",
"border",
"font",
"margin",
"padding",
"list-style"
};
/**
* A boolean array defining which of the CSS shorthand attributes when defined with less than the expected number of values copy the values defined to the remaining undefined attributes
* For example, margin expects 4 values (top/right/bottom/left), but can also be defined with 1 value that will be used for all 4, or with 2 values which will be used one for the vertical margins and the other for the horizontal etc.
*/
static final boolean[] CSS_IS_SHORTHAND_ATTRIBUTE_COLLATABLE = new boolean[]{
false, // "background",
false, // "background-position",
true, // "border-width",
true, // "border-style",
true, // "border-color",
false, // "border-top",
false, // "border-right",
false, // "border-bottom",
false, // "border-left",
false, // "border",
false, // "font",
true, // "margin",
true, // "padding",
false // "list-style"
};
/**
* A constant defining the offset in which the base CSS attribute relating to the TOP appears in the shorthand attribute index
*/
private static final int CSS_TOP=0;
/**
* A constant defining the offset in which the base CSS attribute relating to the RIGHT appears in the shorthand attribute index
*/
private static final int CSS_RIGHT=1;
/**
* A constant defining the offset in which the base CSS attribute relating to the BOTTOM appears in the shorthand attribute index
*/
private static final int CSS_BOTTOM=2;
/**
* A constant defining the offset in which the base CSS attribute relating to the LEFT appears in the shorthand attribute index
*/
private static final int CSS_LEFT=3;
/**
* A map defining the rules by which values of collatable shorthand attributes are assigned to the base attributes
* This map is according to the CSS specs
*/
static final int[][][] CSS_COLLATABLE_ORDER = {
{ // When one value is specified it is set to all sides
{CSS_TOP,CSS_RIGHT,CSS_BOTTOM,CSS_LEFT}
},
{ // When two value are specified:
{CSS_TOP,CSS_BOTTOM}, // The first value is set to the top and bottom
{CSS_LEFT,CSS_RIGHT} // The second value is set to the left and right
},
{ // When three values are specified:
{CSS_TOP}, // The first is set to the top
{CSS_LEFT,CSS_RIGHT}, // The second is set to the left and right
{CSS_BOTTOM} // THe third is set to the bottom
},
{ // When four values are specified - each is set to the appropriate side
{CSS_TOP},
{CSS_RIGHT},
{CSS_BOTTOM},
{CSS_LEFT}
}
};
// The following constants are used in CSS_SHORTHAND_ATTRIBUTE_INDEX when a shorthand attribute translates to a second level of shorthand attributes
// For example 'border' which translated to width/style/color. The values of the constants denote their location in the CSS_SHORTHAND_ATTRIBUTE_INDEX map
private static final int CSS_SHORTHAND_BACKGROUND_POSITION = 1;
private static final int CSS_SHORTHAND_BORDER_WIDTH = 2;
private static final int CSS_SHORTHAND_BORDER_STYLE = 3;
private static final int CSS_SHORTHAND_BORDER_COLOR = 4;
/**
* A map of CSS shorthand attributes to their corresponding base attributes (or to other shorthand attributes)
*/
static final int[][] CSS_SHORTHAND_ATTRIBUTE_INDEX = {
{CSS_BACKGROUND_COLOR,CSS_BACKGROUND_IMAGE,CSS_BACKGROUND_REPEAT, CSS_BACKGROUND_ATTACHMENT, CSS_SHORTHAND_BACKGROUND_POSITION},
{ CSS_BACKGROUND_POSITION_X, CSS_BACKGROUND_POSITION_Y},
{CSS_BORDER_TOP_WIDTH,CSS_BORDER_RIGHT_WIDTH,CSS_BORDER_BOTTOM_WIDTH,CSS_BORDER_LEFT_WIDTH},
{CSS_BORDER_TOP_STYLE,CSS_BORDER_RIGHT_STYLE,CSS_BORDER_BOTTOM_STYLE,CSS_BORDER_LEFT_STYLE},
{CSS_BORDER_TOP_COLOR,CSS_BORDER_RIGHT_COLOR,CSS_BORDER_BOTTOM_COLOR,CSS_BORDER_LEFT_COLOR},
{CSS_BORDER_TOP_WIDTH,CSS_BORDER_TOP_STYLE,CSS_BORDER_TOP_COLOR},
{CSS_BORDER_RIGHT_WIDTH,CSS_BORDER_RIGHT_STYLE,CSS_BORDER_RIGHT_COLOR},
{CSS_BORDER_BOTTOM_WIDTH,CSS_BORDER_BOTTOM_STYLE,CSS_BORDER_BOTTOM_COLOR},
{CSS_BORDER_LEFT_WIDTH,CSS_BORDER_LEFT_STYLE,CSS_BORDER_LEFT_COLOR},
{CSS_SHORTHAND_BORDER_WIDTH,CSS_SHORTHAND_BORDER_STYLE,CSS_SHORTHAND_BORDER_COLOR},
{CSS_FONT_STYLE,CSS_FONT_VARIANT,CSS_FONT_WEIGHT,CSS_FONT_SIZE,CSS_FONT_FAMILY},
{CSS_MARGIN_TOP,CSS_MARGIN_RIGHT,CSS_MARGIN_BOTTOM,CSS_MARGIN_LEFT},
{CSS_PADDING_TOP,CSS_PADDING_RIGHT,CSS_PADDING_BOTTOM,CSS_PADDING_LEFT},
{CSS_LIST_STYLE_TYPE,CSS_LIST_STYLE_POSITION,CSS_LIST_STYLE_IMAGE}
};
private int selectorSpecificity = -1; // A value used to determine how specific this selector is - the more the selector is specific (i.e. id > class > tag) the more it overrides other less specific selectors
private String selectorId=null; // The selector's ID (if it's an ID selector, i.e. '#someid')
private String selectorClass=null; // The selector's class (if it's a class selector i.e. '.someclass')
private String selectorTag=null; // The selector's tag (if it's a tag selector - i.e. 'div')
private int selectorPseudoClass=0;
/**
* A constant representing the focus pseudo-class
*/
static final int PC_FOCUS = 1;
/**
* A constant representing the active pseudo-class
*/
static final int PC_ACTIVE = 2;
/**
* A constant representing the link pseudo-class
*/
static final int PC_LINK = 4;
/**
* A constant representing the visited pseudo-class
*/
static final int PC_VISITED = 8;
/**
* The list of strings representing the various CSS pseudo-classes
*/
final static String[] PSEUDO_CLASSES_STRINGS = {"hover","focus","active","link","visited"};
/**
* The values per each string in PSEUDO_CLASSES_STRINGS
* Note that 'hover' is mapped to PC_FOCUS since we don't support hover on mobile.
*/
final static int[] PSEUDO_CLASSES_VALS = {PC_FOCUS,PC_FOCUS,PC_ACTIVE,PC_LINK,PC_VISITED}; // Since we don't have hover in most/all devices we convert hover to focus as well
/**
* Converts the given CSS length/percentage string to pixels
*
* @param units The string with CSS units (can be either percentage or px/em/ex and other sufixes from CSS_LENGTH_SUFFIX)
* @param cmp The component of the element in question
* @param originalDim The orginal dimension of the element in question
* @return
*/
static int convertUnitsOrPercentage(String units) {
if (units==null) {
return -1;
}
boolean percentage=false;
if (units.charAt(units.length()-1)=='%') {
percentage=true;
units=units.substring(0, units.length()-1);
}
int val=convertUnits(units);
if (percentage) {
val+=VAL_PERCENTAGE;
}
return val;
}
/**
* Converts the given CSS length string to pixels
*
* @param units The string with CSS units (can be px/em/ex and other sufixes from CSS_LENGTH_SUFFIX)
* @param fontHeight The font height of the current element
* @return the CSS length in pixels
*/
static int convertUnits(String units) {
if (units==null) {
return -1;
}
int factor=1;
int factors[] = {1,2,1,DPI,DPI/72,DPI/6,(int)(DPI/2.54/10),(int)(DPI/2.54)}; // unit factors for {"px","em","ex","in","pt","pc","mm","cm"};
int i=0;
for(;i<CSSElement.CSS_LENGTH_SUFFIX.length;i++) {
if (units.endsWith(CSSElement.CSS_LENGTH_SUFFIX[i])) {
factor=factors[i];
units=units.substring(0, units.length()-2);
break;
}
}
try {
int result=(int)(Float.parseFloat(units)*factor);
if ((i==1) || (i==2)) { // em & ex
result+=VAL_EX;
}
return result;
} catch (NumberFormatException nfe) {
return -1;
}
}
/**
* Construvts a CSSElement, This basically sets the name and ID and resets the attrVals array.
*
* @param name
*/
CSSElement(String name) {
id=TAG_CSS_SELECTOR;
this.name=name;
for(int i=0;i<CSS_ATTRIBUTE_ROOTS.length;i++) {
attrVals[i]=-1;
}
}
/**
* Overrides Element.getName to return the name string of this CSSElement.
* Unlike Element which discards the name string and uses the id to identify the name, the CSSElement retains the name and simply returns it here.
* This is because a CSSElement name is in fact the selector string which is any combination of tags, classes and IDs and as such cannot be converted to a simple int.
*
* @return the selector's name
*/
String getName() {
return name;
}
/**
* Checks if the specified attribute is assigned (i.e. was set with a legal value)
*
* @param attrId The attribute ID (Should be one of the CSS attributes, i.e. >= CSS_STYLE_ID_OFFSET)
* @return true if this attribute is assigned, false otherwise
*/
boolean isAttributeAssigned(int attrId) {
return ((attrVals[attrId-CSS_STYLE_ID_OFFSET]!=-1) || (attributes.get(new Integer(attrId))!=null));
}
/**
* Returns the CSSElement's child positioned at the specified index.
* This is a convenience method that is very similar to Element.getChildAt, but returns an object of the CSSElement class.
* Since all of the children of a CSSElement are CSSElements as well, this prevents redundant casting.
*
* @param index The requested child position
* @return The child at the requested position
* @throws ArrayIndexOutOfBoundsException if the index is bigger than the children's count or smaller than 0
*/
CSSElement getCSSChildAt(int index) {
if ((index<0) || (index>=children.size())) {
throw new ArrayIndexOutOfBoundsException();
}
return (CSSElement)children.elementAt(index);
}
/**
* Adds the specified attribute and value to this CSSElement if it is supported and has a valid value.
* This method overrides Element.addAttribute to provide with specific CSSElement functionality.
* Unlike Element which retains all the value strings in the attributes hashtable, In CSSElement the value is immediately converted to a numeric value if possible and placed in the attrVals array.
* The string is retained only for values that can't be converted to int (such as URLs).
*
* @param attribute The attribute's name
* @param value The attribute's value
*
* @return a positive error code or -1 if attribute is supported and valid
*/
int addAttribute(String attribute,String value) {
int attrId=-1;
int i=0;
while ((attrId==-1) && (i<CSS_ATTRIBUTE_ROOTS.length)) {
if(CSS_ATTRIBUTE_ROOTS[i].equals(attribute)) {
attrId=CSS_STYLE_ID_OFFSET + i;
break;
} else {
i++;
}
}
if (attrId==-1) {
return HTMLCallback.ERROR_CSS_ATTRIBUTE_NOT_SUPPORTED;
} else {
return addAttribute(attrId, value);
}
}
/**
* Adds the specified attribute and value to this CSSElement if it is supported and has a valid value.
* This method is used by addAttribute(String,String) and also when we already know the attribute's id (Such as in shorthand attributes)
*
* @param attrId The attribute's id
* @param value The attribute's value
*
* @return a positive error code or -1 if attribute is supported and valid
*/
int addAttribute(int attrId,String value) {
int i=attrId-CSS_STYLE_ID_OFFSET;
boolean knownType=true;
int val=-1;
switch(CSS_ATTRIBUTE_TYPES[i]) {
case TYPE_COLOR:
val=getColor(value, -1);
break;
case TYPE_CSS_LENGTH:
val=convertUnits(value);
break;
case TYPE_CSS_LENGTH_OR_PERCENTAGE:
val=convertUnitsOrPercentage(value);
break;
default:
knownType=false;
}
if (val==-1) { // Some attributes can be either a number, or a string. For example border-width may be a number/percentage but can also be "thin"/"medium"/"thick" which is translated by the strings
if (CSS_ALLOWED_STRINGS[i]!=null) {
val=Parser.getStringVal(value, CSS_ALLOWED_STRINGS[i], CSS_ALLOWED_STRINGS_VALS[i]);
if (val==-1) {
return HTMLCallback.ERROR_ATTIBUTE_VALUE_INVALID;
} else {
attrVals[i]=val;
fixBackgroundPositionDefaults(attrId);
}
} else {
if (knownType) {
return HTMLCallback.ERROR_ATTIBUTE_VALUE_INVALID;
} else {
attributes.put(new Integer(attrId), value);
}
}
} else {
attrVals[i]=val;
fixBackgroundPositionDefaults(attrId);
}
return -1; // No error code - attribute addition succeeded
}
/**
* Checks if the attribute is one of CSS_BACKGROUND_POSITION_X or CSS_BACKGROUND_POSITION_Y and if so fixes the default of the other attribute accordingly.
* Background position is a very special case, since when it is not specified the default is TOP, LEFT, but when one of the background positions is specified, the other's default turns to CENTER
*
* @param attrId The attributeID
*/
private void fixBackgroundPositionDefaults(int attrId) {
if ((attrId==CSS_BACKGROUND_POSITION_X) && (!isAttributeAssigned(CSS_BACKGROUND_POSITION_Y))) {
addAttribute(CSS_BACKGROUND_POSITION_Y, CENTER_STR);
} else if ((attrId==CSS_BACKGROUND_POSITION_Y) && (!isAttributeAssigned(CSS_BACKGROUND_POSITION_X))) {
addAttribute(CSS_BACKGROUND_POSITION_X, CENTER_STR);
}
}
/**
* {@inheritDoc}
*/
String getAttributeName(Integer attrKey) {
return CSS_ATTRIBUTE_ROOTS[attrKey.intValue()-CSS_STYLE_ID_OFFSET];
}
/**
* Returns this selector's specificity. A specificity of a selector determines the order in which it should be applied.
* The bigger the specificty, the later the selector will be applied (Which means it will overdride previously applied selectors)
* This lazily invokes calcSelectorSpecificity if it wasn't invoked before.
*
* @return this selector's specificity.
*/
int getSelectorSpecificity() {
if (selectorSpecificity==-1) {
selectorSpecificity=calcSelectorSpecificity();
}
return selectorSpecificity;
}
/**
* Returns the value of the requested attribute
*
* @param attrId The attribute's id
* @return the value of the requested attribute
*/
int getAttrVal(int attrId) {
return attrVals[attrId-CSS_STYLE_ID_OFFSET];
}
/**
* Returns the length value of the requested attribute.
* A CSS length value can be denoted in various units, or percentages. This method calculates the final value in pixels.
*
* @param attrId The attribute's id
* @param cmp The component relevant to the length calculation (Used in case of length values based on font size)
* @param origDimension The original dimension to take into account if the value is specified as a percentage value
* @return
*/
int getAttrLengthVal(int attrId,Component cmp,int origDimension) {
int val=getAttrVal(attrId);
if (val>=0) { // !=-1 is not enough, since some values as FONT_SMALLER/LARGER are negative and then & VAL_* may be true...
if ((val & VAL_PERCENTAGE)!=0) {
val-=VAL_PERCENTAGE;
val=val*origDimension/100;
} else if ((val & VAL_EX)!=0) { // 'ex' means half of the font size
val-=VAL_EX;
val=val*cmp.getStyle().getFont().getHeight()/2;
}
}
return val;
}
/**
* Calculates this selector's specificity
*
* @return this selector's specificity
*/
int calcSelectorSpecificity() {
int spec=0;
String nameStr=name;
if (nameStr.startsWith("*")) {
nameStr=nameStr.substring(1); //ignore universal selector
}
int index=nameStr.lastIndexOf(':');
while (index!=-1) {
String property=nameStr.substring(index+1);
nameStr=nameStr.substring(0, index);
int propNum=Parser.getStringVal(property, PSEUDO_CLASSES_STRINGS, PSEUDO_CLASSES_VALS);
if (propNum!=-1) {
selectorPseudoClass+=propNum;
spec++; // Psuedo-classes get extra specificity points
}
index=nameStr.lastIndexOf(':');
}
if ((selectorPseudoClass!=0) && (nameStr.equals(""))) { // :link is the same as a:link
nameStr="a";
}
name=nameStr; // The tag name should not contain the pseudo classes
index=nameStr.indexOf('#');
if (index!=-1) {
spec+=100;
selectorId=name.substring(index+1);
if (index!=0) {
spec+=1;
selectorTag=name.substring(0, index);
}
} else {
index=nameStr.indexOf('.');
if (index!=-1) {
spec+=10;
selectorClass=name.substring(index+1);
//selectorClass=selectorClass.replace('.', ' ');
if (index!=0) {
spec+=1;
selectorTag=name.substring(0, index);
}
} else {
if (nameStr.length()>0) {
spec+=1;
selectorTag=name;
}
}
}
for(int i=0;i<getNumChildren();i++) { // There should be usually just one child
spec+=(getCSSChildAt(i)).getSelectorSpecificity();
}
return spec;
}
/**
* Returns this selector's id, or null if none
* This method assumes that calcSelectorSpecificity was invoked before.
*
* @return this selector's id, or null if none
*/
String getSelectorId() {
return selectorId;
}
/**
* Returns this selector's class, or null if none
* This method assumes that calcSelectorSpecificity was invoked before.
*
* @return this selector's class, or null if none
*/
String getSelectorClass() {
return selectorClass;
}
/**
* Returns this selector's tag, or null if none
* This method assumes that calcSelectorSpecificity was invoked before.
*
* @return this selector's tag, or null if none
*/
String getSelectorTag() {
return selectorTag;
}
/**
* This method assumes that calcSelectorSpecificity was invoked before.
*
* @return the selectorPseudoClass
*/
int getSelectorPseudoClass() {
return selectorPseudoClass;
}
/**
* Copies all properties, both string and numeric values to the destination element
* This is used for grouped selectors
*
* @param dest The destination selector
*/
void copyAttributesTo(CSSElement dest) {
for (int i=0;i<attrVals.length;i++) {
dest.attrVals[i]=attrVals[i];
}
for(Enumeration e=attributes.keys();e.hasMoreElements();) {
Integer key=(Integer)e.nextElement();
String value=(String)attributes.get(key);
dest.attributes.put(key, value);
}
//dest.attributes=attributes;
}
}