Save This Page
Home » openjdk-7 » javax » swing » text » html » [javadoc | source]
    1   /*
    2    * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   package javax.swing.text.html;
   26   
   27   import java.awt.Color;
   28   import java.awt.Font;
   29   import java.awt.GraphicsEnvironment;
   30   import java.awt.Toolkit;
   31   import java.awt.HeadlessException;
   32   import java.awt.Image;
   33   import java.io;
   34   import java.lang.reflect.Method;
   35   import java.net.URL;
   36   import java.net.MalformedURLException;
   37   import java.util.Enumeration;
   38   import java.util.Hashtable;
   39   import java.util.Vector;
   40   import java.util.Locale;
   41   import javax.swing.ImageIcon;
   42   import javax.swing.SizeRequirements;
   43   import javax.swing.text;
   44   
   45   /**
   46    * Defines a set of
   47    * <a href="http://www.w3.org/TR/REC-CSS1">CSS attributes</a>
   48    * as a typesafe enumeration.  The HTML View implementations use
   49    * CSS attributes to determine how they will render. This also defines
   50    * methods to map between CSS/HTML/StyleConstants. Any shorthand
   51    * properties, such as font, are mapped to the intrinsic properties.
   52    * <p>The following describes the CSS properties that are suppored by the
   53    * rendering engine:
   54    * <ul><li>font-family
   55    *   <li>font-style
   56    *   <li>font-size (supports relative units)
   57    *   <li>font-weight
   58    *   <li>font
   59    *   <li>color
   60    *   <li>background-color (with the exception of transparent)
   61    *   <li>background-image
   62    *   <li>background-repeat
   63    *   <li>background-position
   64    *   <li>background
   65    *   <li>background-repeat
   66    *   <li>text-decoration (with the exception of blink and overline)
   67    *   <li>vertical-align (only sup and super)
   68    *   <li>text-align (justify is treated as center)
   69    *   <li>margin-top
   70    *   <li>margin-right
   71    *   <li>margin-bottom
   72    *   <li>margin-left
   73    *   <li>margin
   74    *   <li>padding-top
   75    *   <li>padding-right
   76    *   <li>padding-bottom
   77    *   <li>padding-left
   78    *   <li>border-style (only supports inset, outset and none)
   79    *   <li>list-style-type
   80    *   <li>list-style-position
   81    * </ul>
   82    * The following are modeled, but currently not rendered.
   83    * <ul><li>font-variant
   84    *   <li>background-attachment (background always treated as scroll)
   85    *   <li>word-spacing
   86    *   <li>letter-spacing
   87    *   <li>text-indent
   88    *   <li>text-transform
   89    *   <li>line-height
   90    *   <li>border-top-width (this is used to indicate if a border should be used)
   91    *   <li>border-right-width
   92    *   <li>border-bottom-width
   93    *   <li>border-left-width
   94    *   <li>border-width
   95    *   <li>border-top
   96    *   <li>border-right
   97    *   <li>border-bottom
   98    *   <li>border-left
   99    *   <li>border
  100    *   <li>width
  101    *   <li>height
  102    *   <li>float
  103    *   <li>clear
  104    *   <li>display
  105    *   <li>white-space
  106    *   <li>list-style
  107    * </ul>
  108    * <p><b>Note: for the time being we do not fully support relative units,
  109    * unless noted, so that
  110    * p { margin-top: 10% } will be treated as if no margin-top was specified.
  111    *
  112    * @author  Timothy Prinzing
  113    * @author  Scott Violet
  114    * @see StyleSheet
  115    */
  116   public class CSS implements Serializable {
  117   
  118       /**
  119        * Definitions to be used as a key on AttributeSet's
  120        * that might hold CSS attributes.  Since this is a
  121        * closed set (i.e. defined exactly by the specification),
  122        * it is final and cannot be extended.
  123        */
  124       public static final class Attribute {
  125   
  126           private Attribute(String name, String defaultValue, boolean inherited) {
  127               this.name = name;
  128               this.defaultValue = defaultValue;
  129               this.inherited = inherited;
  130           }
  131   
  132           /**
  133            * The string representation of the attribute.  This
  134            * should exactly match the string specified in the
  135            * CSS specification.
  136            */
  137           public String toString() {
  138               return name;
  139           }
  140   
  141           /**
  142            * Fetch the default value for the attribute.
  143            * If there is no default value (such as for
  144            * composite attributes), null will be returned.
  145            */
  146           public String getDefaultValue() {
  147               return defaultValue;
  148           }
  149   
  150           /**
  151            * Indicates if the attribute should be inherited
  152            * from the parent or not.
  153            */
  154           public boolean isInherited() {
  155               return inherited;
  156           }
  157   
  158           private String name;
  159           private String defaultValue;
  160           private boolean inherited;
  161   
  162   
  163           public static final Attribute BACKGROUND =
  164               new Attribute("background", null, false);
  165   
  166           public static final Attribute BACKGROUND_ATTACHMENT =
  167               new Attribute("background-attachment", "scroll", false);
  168   
  169           public static final Attribute BACKGROUND_COLOR =
  170               new Attribute("background-color", "transparent", false);
  171   
  172           public static final Attribute BACKGROUND_IMAGE =
  173               new Attribute("background-image", "none", false);
  174   
  175           public static final Attribute BACKGROUND_POSITION =
  176               new Attribute("background-position", null, false);
  177   
  178           public static final Attribute BACKGROUND_REPEAT =
  179               new Attribute("background-repeat", "repeat", false);
  180   
  181           public static final Attribute BORDER =
  182               new Attribute("border", null, false);
  183   
  184           public static final Attribute BORDER_BOTTOM =
  185               new Attribute("border-bottom", null, false);
  186   
  187           public static final Attribute BORDER_BOTTOM_COLOR =
  188               new Attribute("border-bottom-color", null, false);
  189   
  190           public static final Attribute BORDER_BOTTOM_STYLE =
  191               new Attribute("border-bottom-style", "none", false);
  192   
  193           public static final Attribute BORDER_BOTTOM_WIDTH =
  194               new Attribute("border-bottom-width", "medium", false);
  195   
  196           public static final Attribute BORDER_COLOR =
  197               new Attribute("border-color", null, false);
  198   
  199           public static final Attribute BORDER_LEFT =
  200               new Attribute("border-left", null, false);
  201   
  202           public static final Attribute BORDER_LEFT_COLOR =
  203               new Attribute("border-left-color", null, false);
  204   
  205           public static final Attribute BORDER_LEFT_STYLE =
  206               new Attribute("border-left-style", "none", false);
  207   
  208           public static final Attribute BORDER_LEFT_WIDTH =
  209               new Attribute("border-left-width", "medium", false);
  210   
  211           public static final Attribute BORDER_RIGHT =
  212               new Attribute("border-right", null, false);
  213   
  214           public static final Attribute BORDER_RIGHT_COLOR =
  215               new Attribute("border-right-color", null, false);
  216   
  217           public static final Attribute BORDER_RIGHT_STYLE =
  218               new Attribute("border-right-style", "none", false);
  219   
  220           public static final Attribute BORDER_RIGHT_WIDTH =
  221               new Attribute("border-right-width", "medium", false);
  222   
  223           public static final Attribute BORDER_STYLE =
  224               new Attribute("border-style", "none", false);
  225   
  226           public static final Attribute BORDER_TOP =
  227               new Attribute("border-top", null, false);
  228   
  229           public static final Attribute BORDER_TOP_COLOR =
  230               new Attribute("border-top-color", null, false);
  231   
  232           public static final Attribute BORDER_TOP_STYLE =
  233               new Attribute("border-top-style", "none", false);
  234   
  235           public static final Attribute BORDER_TOP_WIDTH =
  236               new Attribute("border-top-width", "medium", false);
  237   
  238           public static final Attribute BORDER_WIDTH =
  239               new Attribute("border-width", "medium", false);
  240   
  241           public static final Attribute CLEAR =
  242               new Attribute("clear", "none", false);
  243   
  244           public static final Attribute COLOR =
  245               new Attribute("color", "black", true);
  246   
  247           public static final Attribute DISPLAY =
  248               new Attribute("display", "block", false);
  249   
  250           public static final Attribute FLOAT =
  251               new Attribute("float", "none", false);
  252   
  253           public static final Attribute FONT =
  254               new Attribute("font", null, true);
  255   
  256           public static final Attribute FONT_FAMILY =
  257               new Attribute("font-family", null, true);
  258   
  259           public static final Attribute FONT_SIZE =
  260               new Attribute("font-size", "medium", true);
  261   
  262           public static final Attribute FONT_STYLE =
  263               new Attribute("font-style", "normal", true);
  264   
  265           public static final Attribute FONT_VARIANT =
  266               new Attribute("font-variant", "normal", true);
  267   
  268           public static final Attribute FONT_WEIGHT =
  269               new Attribute("font-weight", "normal", true);
  270   
  271           public static final Attribute HEIGHT =
  272               new Attribute("height", "auto", false);
  273   
  274           public static final Attribute LETTER_SPACING =
  275               new Attribute("letter-spacing", "normal", true);
  276   
  277           public static final Attribute LINE_HEIGHT =
  278               new Attribute("line-height", "normal", true);
  279   
  280           public static final Attribute LIST_STYLE =
  281               new Attribute("list-style", null, true);
  282   
  283           public static final Attribute LIST_STYLE_IMAGE =
  284               new Attribute("list-style-image", "none", true);
  285   
  286           public static final Attribute LIST_STYLE_POSITION =
  287               new Attribute("list-style-position", "outside", true);
  288   
  289           public static final Attribute LIST_STYLE_TYPE =
  290               new Attribute("list-style-type", "disc", true);
  291   
  292           public static final Attribute MARGIN =
  293               new Attribute("margin", null, false);
  294   
  295           public static final Attribute MARGIN_BOTTOM =
  296               new Attribute("margin-bottom", "0", false);
  297   
  298           public static final Attribute MARGIN_LEFT =
  299               new Attribute("margin-left", "0", false);
  300   
  301           public static final Attribute MARGIN_RIGHT =
  302               new Attribute("margin-right", "0", false);
  303   
  304           /*
  305            * made up css attributes to describe orientation depended
  306            * margins. used for <dir>, <menu>, <ul> etc. see
  307            * 5088268 for more details
  308            */
  309           static final Attribute MARGIN_LEFT_LTR =
  310               new Attribute("margin-left-ltr",
  311                             Integer.toString(Integer.MIN_VALUE), false);
  312   
  313           static final Attribute MARGIN_LEFT_RTL =
  314               new Attribute("margin-left-rtl",
  315                             Integer.toString(Integer.MIN_VALUE), false);
  316   
  317           static final Attribute MARGIN_RIGHT_LTR =
  318               new Attribute("margin-right-ltr",
  319                             Integer.toString(Integer.MIN_VALUE), false);
  320   
  321           static final Attribute MARGIN_RIGHT_RTL =
  322               new Attribute("margin-right-rtl",
  323                             Integer.toString(Integer.MIN_VALUE), false);
  324   
  325   
  326           public static final Attribute MARGIN_TOP =
  327               new Attribute("margin-top", "0", false);
  328   
  329           public static final Attribute PADDING =
  330               new Attribute("padding", null, false);
  331   
  332           public static final Attribute PADDING_BOTTOM =
  333               new Attribute("padding-bottom", "0", false);
  334   
  335           public static final Attribute PADDING_LEFT =
  336               new Attribute("padding-left", "0", false);
  337   
  338           public static final Attribute PADDING_RIGHT =
  339               new Attribute("padding-right", "0", false);
  340   
  341           public static final Attribute PADDING_TOP =
  342               new Attribute("padding-top", "0", false);
  343   
  344           public static final Attribute TEXT_ALIGN =
  345               new Attribute("text-align", null, true);
  346   
  347           public static final Attribute TEXT_DECORATION =
  348               new Attribute("text-decoration", "none", true);
  349   
  350           public static final Attribute TEXT_INDENT =
  351               new Attribute("text-indent", "0", true);
  352   
  353           public static final Attribute TEXT_TRANSFORM =
  354               new Attribute("text-transform", "none", true);
  355   
  356           public static final Attribute VERTICAL_ALIGN =
  357               new Attribute("vertical-align", "baseline", false);
  358   
  359           public static final Attribute WORD_SPACING =
  360               new Attribute("word-spacing", "normal", true);
  361   
  362           public static final Attribute WHITE_SPACE =
  363               new Attribute("white-space", "normal", true);
  364   
  365           public static final Attribute WIDTH =
  366               new Attribute("width", "auto", false);
  367   
  368           /*public*/ static final Attribute BORDER_SPACING =
  369               new Attribute("border-spacing", "0", true);
  370   
  371           /*public*/ static final Attribute CAPTION_SIDE =
  372               new Attribute("caption-side", "left", true);
  373   
  374           // All possible CSS attribute keys.
  375           static final Attribute[] allAttributes = {
  376               BACKGROUND, BACKGROUND_ATTACHMENT, BACKGROUND_COLOR,
  377               BACKGROUND_IMAGE, BACKGROUND_POSITION, BACKGROUND_REPEAT,
  378               BORDER, BORDER_BOTTOM, BORDER_BOTTOM_WIDTH, BORDER_COLOR,
  379               BORDER_LEFT, BORDER_LEFT_WIDTH, BORDER_RIGHT, BORDER_RIGHT_WIDTH,
  380               BORDER_STYLE, BORDER_TOP, BORDER_TOP_WIDTH, BORDER_WIDTH,
  381               BORDER_TOP_STYLE, BORDER_RIGHT_STYLE, BORDER_BOTTOM_STYLE,
  382               BORDER_LEFT_STYLE,
  383               BORDER_TOP_COLOR, BORDER_RIGHT_COLOR, BORDER_BOTTOM_COLOR,
  384               BORDER_LEFT_COLOR,
  385               CLEAR, COLOR, DISPLAY, FLOAT, FONT, FONT_FAMILY, FONT_SIZE,
  386               FONT_STYLE, FONT_VARIANT, FONT_WEIGHT, HEIGHT, LETTER_SPACING,
  387               LINE_HEIGHT, LIST_STYLE, LIST_STYLE_IMAGE, LIST_STYLE_POSITION,
  388               LIST_STYLE_TYPE, MARGIN, MARGIN_BOTTOM, MARGIN_LEFT, MARGIN_RIGHT,
  389               MARGIN_TOP, PADDING, PADDING_BOTTOM, PADDING_LEFT, PADDING_RIGHT,
  390               PADDING_TOP, TEXT_ALIGN, TEXT_DECORATION, TEXT_INDENT, TEXT_TRANSFORM,
  391               VERTICAL_ALIGN, WORD_SPACING, WHITE_SPACE, WIDTH,
  392               BORDER_SPACING, CAPTION_SIDE,
  393               MARGIN_LEFT_LTR, MARGIN_LEFT_RTL, MARGIN_RIGHT_LTR, MARGIN_RIGHT_RTL
  394           };
  395   
  396           private static final Attribute[] ALL_MARGINS =
  397                   { MARGIN_TOP, MARGIN_RIGHT, MARGIN_BOTTOM, MARGIN_LEFT };
  398           private static final Attribute[] ALL_PADDING =
  399                   { PADDING_TOP, PADDING_RIGHT, PADDING_BOTTOM, PADDING_LEFT };
  400           private static final Attribute[] ALL_BORDER_WIDTHS =
  401                   { BORDER_TOP_WIDTH, BORDER_RIGHT_WIDTH, BORDER_BOTTOM_WIDTH,
  402                     BORDER_LEFT_WIDTH };
  403           private static final Attribute[] ALL_BORDER_STYLES =
  404                   { BORDER_TOP_STYLE, BORDER_RIGHT_STYLE, BORDER_BOTTOM_STYLE,
  405                     BORDER_LEFT_STYLE };
  406           private static final Attribute[] ALL_BORDER_COLORS =
  407                   { BORDER_TOP_COLOR, BORDER_RIGHT_COLOR, BORDER_BOTTOM_COLOR,
  408                     BORDER_LEFT_COLOR };
  409   
  410       }
  411   
  412       static final class Value {
  413   
  414           private Value(String name) {
  415               this.name = name;
  416           }
  417   
  418           /**
  419            * The string representation of the attribute.  This
  420            * should exactly match the string specified in the
  421            * CSS specification.
  422            */
  423           public String toString() {
  424               return name;
  425           }
  426   
  427           static final Value INHERITED = new Value("inherited");
  428           static final Value NONE = new Value("none");
  429           static final Value HIDDEN = new Value("hidden");
  430           static final Value DOTTED = new Value("dotted");
  431           static final Value DASHED = new Value("dashed");
  432           static final Value SOLID = new Value("solid");
  433           static final Value DOUBLE = new Value("double");
  434           static final Value GROOVE = new Value("groove");
  435           static final Value RIDGE = new Value("ridge");
  436           static final Value INSET = new Value("inset");
  437           static final Value OUTSET = new Value("outset");
  438           // Lists.
  439           static final Value DISC = new Value("disc");
  440           static final Value CIRCLE = new Value("circle");
  441           static final Value SQUARE = new Value("square");
  442           static final Value DECIMAL = new Value("decimal");
  443           static final Value LOWER_ROMAN = new Value("lower-roman");
  444           static final Value UPPER_ROMAN = new Value("upper-roman");
  445           static final Value LOWER_ALPHA = new Value("lower-alpha");
  446           static final Value UPPER_ALPHA = new Value("upper-alpha");
  447           // background-repeat
  448           static final Value BACKGROUND_NO_REPEAT = new Value("no-repeat");
  449           static final Value BACKGROUND_REPEAT = new Value("repeat");
  450           static final Value BACKGROUND_REPEAT_X = new Value("repeat-x");
  451           static final Value BACKGROUND_REPEAT_Y = new Value("repeat-y");
  452           // background-attachment
  453           static final Value BACKGROUND_SCROLL = new Value("scroll");
  454           static final Value BACKGROUND_FIXED = new Value("fixed");
  455   
  456           private String name;
  457   
  458           static final Value[] allValues = {
  459               INHERITED, NONE, DOTTED, DASHED, SOLID, DOUBLE, GROOVE,
  460               RIDGE, INSET, OUTSET, DISC, CIRCLE, SQUARE, DECIMAL,
  461               LOWER_ROMAN, UPPER_ROMAN, LOWER_ALPHA, UPPER_ALPHA,
  462               BACKGROUND_NO_REPEAT, BACKGROUND_REPEAT,
  463               BACKGROUND_REPEAT_X, BACKGROUND_REPEAT_Y,
  464               BACKGROUND_FIXED, BACKGROUND_FIXED
  465           };
  466       }
  467   
  468       public CSS() {
  469           baseFontSize = baseFontSizeIndex + 1;
  470           // setup the css conversion table
  471           valueConvertor = new Hashtable();
  472           valueConvertor.put(CSS.Attribute.FONT_SIZE, new FontSize());
  473           valueConvertor.put(CSS.Attribute.FONT_FAMILY, new FontFamily());
  474           valueConvertor.put(CSS.Attribute.FONT_WEIGHT, new FontWeight());
  475           Object bs = new BorderStyle();
  476           valueConvertor.put(CSS.Attribute.BORDER_TOP_STYLE, bs);
  477           valueConvertor.put(CSS.Attribute.BORDER_RIGHT_STYLE, bs);
  478           valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_STYLE, bs);
  479           valueConvertor.put(CSS.Attribute.BORDER_LEFT_STYLE, bs);
  480           Object cv = new ColorValue();
  481           valueConvertor.put(CSS.Attribute.COLOR, cv);
  482           valueConvertor.put(CSS.Attribute.BACKGROUND_COLOR, cv);
  483           valueConvertor.put(CSS.Attribute.BORDER_TOP_COLOR, cv);
  484           valueConvertor.put(CSS.Attribute.BORDER_RIGHT_COLOR, cv);
  485           valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_COLOR, cv);
  486           valueConvertor.put(CSS.Attribute.BORDER_LEFT_COLOR, cv);
  487           Object lv = new LengthValue();
  488           valueConvertor.put(CSS.Attribute.MARGIN_TOP, lv);
  489           valueConvertor.put(CSS.Attribute.MARGIN_BOTTOM, lv);
  490           valueConvertor.put(CSS.Attribute.MARGIN_LEFT, lv);
  491           valueConvertor.put(CSS.Attribute.MARGIN_LEFT_LTR, lv);
  492           valueConvertor.put(CSS.Attribute.MARGIN_LEFT_RTL, lv);
  493           valueConvertor.put(CSS.Attribute.MARGIN_RIGHT, lv);
  494           valueConvertor.put(CSS.Attribute.MARGIN_RIGHT_LTR, lv);
  495           valueConvertor.put(CSS.Attribute.MARGIN_RIGHT_RTL, lv);
  496           valueConvertor.put(CSS.Attribute.PADDING_TOP, lv);
  497           valueConvertor.put(CSS.Attribute.PADDING_BOTTOM, lv);
  498           valueConvertor.put(CSS.Attribute.PADDING_LEFT, lv);
  499           valueConvertor.put(CSS.Attribute.PADDING_RIGHT, lv);
  500           Object bv = new BorderWidthValue(null, 0);
  501           valueConvertor.put(CSS.Attribute.BORDER_TOP_WIDTH, bv);
  502           valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_WIDTH, bv);
  503           valueConvertor.put(CSS.Attribute.BORDER_LEFT_WIDTH, bv);
  504           valueConvertor.put(CSS.Attribute.BORDER_RIGHT_WIDTH, bv);
  505           Object nlv = new LengthValue(true);
  506           valueConvertor.put(CSS.Attribute.TEXT_INDENT, nlv);
  507           valueConvertor.put(CSS.Attribute.WIDTH, lv);
  508           valueConvertor.put(CSS.Attribute.HEIGHT, lv);
  509           valueConvertor.put(CSS.Attribute.BORDER_SPACING, lv);
  510           Object sv = new StringValue();
  511           valueConvertor.put(CSS.Attribute.FONT_STYLE, sv);
  512           valueConvertor.put(CSS.Attribute.TEXT_DECORATION, sv);
  513           valueConvertor.put(CSS.Attribute.TEXT_ALIGN, sv);
  514           valueConvertor.put(CSS.Attribute.VERTICAL_ALIGN, sv);
  515           Object valueMapper = new CssValueMapper();
  516           valueConvertor.put(CSS.Attribute.LIST_STYLE_TYPE,
  517                              valueMapper);
  518           valueConvertor.put(CSS.Attribute.BACKGROUND_IMAGE,
  519                              new BackgroundImage());
  520           valueConvertor.put(CSS.Attribute.BACKGROUND_POSITION,
  521                              new BackgroundPosition());
  522           valueConvertor.put(CSS.Attribute.BACKGROUND_REPEAT,
  523                              valueMapper);
  524           valueConvertor.put(CSS.Attribute.BACKGROUND_ATTACHMENT,
  525                              valueMapper);
  526           Object generic = new CssValue();
  527           int n = CSS.Attribute.allAttributes.length;
  528           for (int i = 0; i < n; i++) {
  529               CSS.Attribute key = CSS.Attribute.allAttributes[i];
  530               if (valueConvertor.get(key) == null) {
  531                   valueConvertor.put(key, generic);
  532               }
  533           }
  534       }
  535   
  536       /**
  537        * Sets the base font size. <code>sz</code> is a CSS value, and is
  538        * not necessarily the point size. Use getPointSize to determine the
  539        * point size corresponding to <code>sz</code>.
  540        */
  541       void setBaseFontSize(int sz) {
  542           if (sz < 1)
  543             baseFontSize = 0;
  544           else if (sz > 7)
  545             baseFontSize = 7;
  546           else
  547             baseFontSize = sz;
  548       }
  549   
  550       /**
  551        * Sets the base font size from the passed in string.
  552        */
  553       void setBaseFontSize(String size) {
  554           int relSize, absSize, diff;
  555   
  556           if (size != null) {
  557               if (size.startsWith("+")) {
  558                   relSize = Integer.valueOf(size.substring(1)).intValue();
  559                   setBaseFontSize(baseFontSize + relSize);
  560               } else if (size.startsWith("-")) {
  561                   relSize = -Integer.valueOf(size.substring(1)).intValue();
  562                   setBaseFontSize(baseFontSize + relSize);
  563               } else {
  564                   setBaseFontSize(Integer.valueOf(size).intValue());
  565               }
  566           }
  567       }
  568   
  569       /**
  570        * Returns the base font size.
  571        */
  572       int getBaseFontSize() {
  573           return baseFontSize;
  574       }
  575   
  576       /**
  577        * Parses the CSS property <code>key</code> with value
  578        * <code>value</code> placing the result in <code>att</code>.
  579        */
  580       void addInternalCSSValue(MutableAttributeSet attr,
  581                                CSS.Attribute key, String value) {
  582           if (key == CSS.Attribute.FONT) {
  583               ShorthandFontParser.parseShorthandFont(this, value, attr);
  584           }
  585           else if (key == CSS.Attribute.BACKGROUND) {
  586               ShorthandBackgroundParser.parseShorthandBackground
  587                                  (this, value, attr);
  588           }
  589           else if (key == CSS.Attribute.MARGIN) {
  590               ShorthandMarginParser.parseShorthandMargin(this, value, attr,
  591                                              CSS.Attribute.ALL_MARGINS);
  592           }
  593           else if (key == CSS.Attribute.PADDING) {
  594               ShorthandMarginParser.parseShorthandMargin(this, value, attr,
  595                                              CSS.Attribute.ALL_PADDING);
  596           }
  597           else if (key == CSS.Attribute.BORDER_WIDTH) {
  598               ShorthandMarginParser.parseShorthandMargin(this, value, attr,
  599                                              CSS.Attribute.ALL_BORDER_WIDTHS);
  600           }
  601           else if (key == CSS.Attribute.BORDER_COLOR) {
  602               ShorthandMarginParser.parseShorthandMargin(this, value, attr,
  603                                               CSS.Attribute.ALL_BORDER_COLORS);
  604           }
  605           else if (key == CSS.Attribute.BORDER_STYLE) {
  606               ShorthandMarginParser.parseShorthandMargin(this, value, attr,
  607                                               CSS.Attribute.ALL_BORDER_STYLES);
  608           }
  609           else if ((key == CSS.Attribute.BORDER) ||
  610                      (key == CSS.Attribute.BORDER_TOP) ||
  611                      (key == CSS.Attribute.BORDER_RIGHT) ||
  612                      (key == CSS.Attribute.BORDER_BOTTOM) ||
  613                      (key == CSS.Attribute.BORDER_LEFT)) {
  614               ShorthandBorderParser.parseShorthandBorder(attr, key, value);
  615           }
  616           else {
  617               Object iValue = getInternalCSSValue(key, value);
  618               if (iValue != null) {
  619                   attr.addAttribute(key, iValue);
  620               }
  621           }
  622       }
  623   
  624       /**
  625        * Gets the internal CSS representation of <code>value</code> which is
  626        * a CSS value of the CSS attribute named <code>key</code>. The receiver
  627        * should not modify <code>value</code>, and the first <code>count</code>
  628        * strings are valid.
  629        */
  630       Object getInternalCSSValue(CSS.Attribute key, String value) {
  631           CssValue conv = (CssValue) valueConvertor.get(key);
  632           Object r = conv.parseCssValue(value);
  633           return r != null ? r : conv.parseCssValue(key.getDefaultValue());
  634       }
  635   
  636       /**
  637        * Maps from a StyleConstants to a CSS Attribute.
  638        */
  639       Attribute styleConstantsKeyToCSSKey(StyleConstants sc) {
  640           return (Attribute)styleConstantToCssMap.get(sc);
  641       }
  642   
  643       /**
  644        * Maps from a StyleConstants value to a CSS value.
  645        */
  646       Object styleConstantsValueToCSSValue(StyleConstants sc,
  647                                            Object styleValue) {
  648           Object cssKey = styleConstantsKeyToCSSKey(sc);
  649           if (cssKey != null) {
  650               CssValue conv = (CssValue)valueConvertor.get(cssKey);
  651               return conv.fromStyleConstants(sc, styleValue);
  652           }
  653           return null;
  654       }
  655   
  656       /**
  657        * Converts the passed in CSS value to a StyleConstants value.
  658        * <code>key</code> identifies the CSS attribute being mapped.
  659        */
  660       Object cssValueToStyleConstantsValue(StyleConstants key, Object value) {
  661           if (value instanceof CssValue) {
  662               return ((CssValue)value).toStyleConstants((StyleConstants)key,
  663                                                         null);
  664           }
  665           return null;
  666       }
  667   
  668       /**
  669        * Returns the font for the values in the passed in AttributeSet.
  670        * It is assumed the keys will be CSS.Attribute keys.
  671        * <code>sc</code> is the StyleContext that will be messaged to get
  672        * the font once the size, name and style have been determined.
  673        */
  674       Font getFont(StyleContext sc, AttributeSet a, int defaultSize, StyleSheet ss) {
  675           ss = getStyleSheet(ss);
  676           int size = getFontSize(a, defaultSize, ss);
  677   
  678           /*
  679            * If the vertical alignment is set to either superscirpt or
  680            * subscript we reduce the font size by 2 points.
  681            */
  682           StringValue vAlignV = (StringValue)a.getAttribute
  683                                 (CSS.Attribute.VERTICAL_ALIGN);
  684           if ((vAlignV != null)) {
  685               String vAlign = vAlignV.toString();
  686               if ((vAlign.indexOf("sup") >= 0) ||
  687                   (vAlign.indexOf("sub") >= 0)) {
  688                   size -= 2;
  689               }
  690           }
  691   
  692           FontFamily familyValue = (FontFamily)a.getAttribute
  693                                               (CSS.Attribute.FONT_FAMILY);
  694           String family = (familyValue != null) ? familyValue.getValue() :
  695                                     Font.SANS_SERIF;
  696           int style = Font.PLAIN;
  697           FontWeight weightValue = (FontWeight) a.getAttribute
  698                                     (CSS.Attribute.FONT_WEIGHT);
  699           if ((weightValue != null) && (weightValue.getValue() > 400)) {
  700               style |= Font.BOLD;
  701           }
  702           Object fs = a.getAttribute(CSS.Attribute.FONT_STYLE);
  703           if ((fs != null) && (fs.toString().indexOf("italic") >= 0)) {
  704               style |= Font.ITALIC;
  705           }
  706           if (family.equalsIgnoreCase("monospace")) {
  707               family = Font.MONOSPACED;
  708           }
  709           Font f = sc.getFont(family, style, size);
  710           if (f == null
  711               || (f.getFamily().equals(Font.DIALOG)
  712                   && ! family.equalsIgnoreCase(Font.DIALOG))) {
  713               family = Font.SANS_SERIF;
  714               f = sc.getFont(family, style, size);
  715           }
  716           return f;
  717       }
  718   
  719       static int getFontSize(AttributeSet attr, int defaultSize, StyleSheet ss) {
  720           // PENDING(prinz) this is a 1.1 based implementation, need to also
  721           // have a 1.2 version.
  722           FontSize sizeValue = (FontSize)attr.getAttribute(CSS.Attribute.
  723                                                            FONT_SIZE);
  724   
  725           return (sizeValue != null) ? sizeValue.getValue(attr, ss)
  726                                      : defaultSize;
  727       }
  728   
  729       /**
  730        * Takes a set of attributes and turn it into a color
  731        * specification.  This might be used to specify things
  732        * like brighter, more hue, etc.
  733        * This will return null if there is no value for <code>key</code>.
  734        *
  735        * @param key CSS.Attribute identifying where color is stored.
  736        * @param a the set of attributes
  737        * @return the color
  738        */
  739       Color getColor(AttributeSet a, CSS.Attribute key) {
  740           ColorValue cv = (ColorValue) a.getAttribute(key);
  741           if (cv != null) {
  742               return cv.getValue();
  743           }
  744           return null;
  745       }
  746   
  747       /**
  748        * Returns the size of a font from the passed in string.
  749        *
  750        * @param size CSS string describing font size
  751        * @param baseFontSize size to use for relative units.
  752        */
  753       float getPointSize(String size, StyleSheet ss) {
  754           int relSize, absSize, diff, index;
  755           ss = getStyleSheet(ss);
  756           if (size != null) {
  757               if (size.startsWith("+")) {
  758                   relSize = Integer.valueOf(size.substring(1)).intValue();
  759                   return getPointSize(baseFontSize + relSize, ss);
  760               } else if (size.startsWith("-")) {
  761                   relSize = -Integer.valueOf(size.substring(1)).intValue();
  762                   return getPointSize(baseFontSize + relSize, ss);
  763               } else {
  764                   absSize = Integer.valueOf(size).intValue();
  765                   return getPointSize(absSize, ss);
  766               }
  767           }
  768           return 0;
  769       }
  770   
  771       /**
  772        * Returns the length of the attribute in <code>a</code> with
  773        * key <code>key</code>.
  774        */
  775       float getLength(AttributeSet a, CSS.Attribute key, StyleSheet ss) {
  776           ss = getStyleSheet(ss);
  777           LengthValue lv = (LengthValue) a.getAttribute(key);
  778           boolean isW3CLengthUnits = (ss == null) ? false : ss.isW3CLengthUnits();
  779           float len = (lv != null) ? lv.getValue(isW3CLengthUnits) : 0;
  780           return len;
  781       }
  782   
  783       /**
  784        * Convert a set of HTML attributes to an equivalent
  785        * set of CSS attributes.
  786        *
  787        * @param AttributeSet containing the HTML attributes.
  788        * @return AttributeSet containing the corresponding CSS attributes.
  789        *        The AttributeSet will be empty if there are no mapping
  790        *        CSS attributes.
  791        */
  792       AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet) {
  793           MutableAttributeSet cssAttrSet = new SimpleAttributeSet();
  794           Element elem = (Element)htmlAttrSet;
  795           HTML.Tag tag = getHTMLTag(htmlAttrSet);
  796           if ((tag == HTML.Tag.TD) || (tag == HTML.Tag.TH)) {
  797               // translate border width into the cells, if it has non-zero value.
  798               AttributeSet tableAttr = elem.getParentElement().
  799                                        getParentElement().getAttributes();
  800               int borderWidth;
  801               try {
  802                   borderWidth = Integer.parseInt(
  803                       (String) tableAttr.getAttribute(HTML.Attribute.BORDER));
  804               } catch (NumberFormatException e) {
  805                   borderWidth = 0;
  806               }
  807               if (borderWidth > 0) {
  808                   translateAttribute(HTML.Attribute.BORDER, tableAttr, cssAttrSet);
  809               }
  810               String pad = (String)tableAttr.getAttribute(HTML.Attribute.CELLPADDING);
  811               if (pad != null) {
  812                   LengthValue v =
  813                       (LengthValue)getInternalCSSValue(CSS.Attribute.PADDING_TOP, pad);
  814                   v.span = (v.span < 0) ? 0 : v.span;
  815                   cssAttrSet.addAttribute(CSS.Attribute.PADDING_TOP, v);
  816                   cssAttrSet.addAttribute(CSS.Attribute.PADDING_BOTTOM, v);
  817                   cssAttrSet.addAttribute(CSS.Attribute.PADDING_LEFT, v);
  818                   cssAttrSet.addAttribute(CSS.Attribute.PADDING_RIGHT, v);
  819               }
  820           }
  821           if (elem.isLeaf()) {
  822               translateEmbeddedAttributes(htmlAttrSet, cssAttrSet);
  823           } else {
  824               translateAttributes(tag, htmlAttrSet, cssAttrSet);
  825           }
  826           if (tag == HTML.Tag.CAPTION) {
  827               /*
  828                * Navigator uses ALIGN for caption placement and IE uses VALIGN.
  829                */
  830               Object v = htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
  831               if ((v != null) && (v.equals("top") || v.equals("bottom"))) {
  832                   cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE, v);
  833                   cssAttrSet.removeAttribute(CSS.Attribute.TEXT_ALIGN);
  834               } else {
  835                   v = htmlAttrSet.getAttribute(HTML.Attribute.VALIGN);
  836                   if (v != null) {
  837                       cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE, v);
  838                   }
  839               }
  840           }
  841           return cssAttrSet;
  842       }
  843   
  844       private static final Hashtable attributeMap = new Hashtable();
  845       private static final Hashtable valueMap = new Hashtable();
  846   
  847       /**
  848        * The hashtable and the static initalization block below,
  849        * set up a mapping from well-known HTML attributes to
  850        * CSS attributes.  For the most part, there is a 1-1 mapping
  851        * between the two.  However in the case of certain HTML
  852        * attributes for example HTML.Attribute.VSPACE or
  853        * HTML.Attribute.HSPACE, end up mapping to two CSS.Attribute's.
  854        * Therefore, the value associated with each HTML.Attribute.
  855        * key ends up being an array of CSS.Attribute.* objects.
  856        */
  857       private static final Hashtable htmlAttrToCssAttrMap = new Hashtable(20);
  858   
  859       /**
  860        * The hashtable and static initialization that follows sets
  861        * up a translation from StyleConstants (i.e. the <em>well known</em>
  862        * attributes) to the associated CSS attributes.
  863        */
  864       private static final Hashtable styleConstantToCssMap = new Hashtable(17);
  865       /** Maps from HTML value to a CSS value. Used in internal mapping. */
  866       private static final Hashtable htmlValueToCssValueMap = new Hashtable(8);
  867       /** Maps from CSS value (string) to internal value. */
  868       private static final Hashtable cssValueToInternalValueMap = new Hashtable(13);
  869   
  870       static {
  871           // load the attribute map
  872           for (int i = 0; i < Attribute.allAttributes.length; i++ ) {
  873               attributeMap.put(Attribute.allAttributes[i].toString(),
  874                                Attribute.allAttributes[i]);
  875           }
  876           // load the value map
  877           for (int i = 0; i < Value.allValues.length; i++ ) {
  878               valueMap.put(Value.allValues[i].toString(),
  879                                Value.allValues[i]);
  880           }
  881   
  882           htmlAttrToCssAttrMap.put(HTML.Attribute.COLOR,
  883                                    new CSS.Attribute[]{CSS.Attribute.COLOR});
  884           htmlAttrToCssAttrMap.put(HTML.Attribute.TEXT,
  885                                    new CSS.Attribute[]{CSS.Attribute.COLOR});
  886           htmlAttrToCssAttrMap.put(HTML.Attribute.CLEAR,
  887                                    new CSS.Attribute[]{CSS.Attribute.CLEAR});
  888           htmlAttrToCssAttrMap.put(HTML.Attribute.BACKGROUND,
  889                                    new CSS.Attribute[]{CSS.Attribute.BACKGROUND_IMAGE});
  890           htmlAttrToCssAttrMap.put(HTML.Attribute.BGCOLOR,
  891                                    new CSS.Attribute[]{CSS.Attribute.BACKGROUND_COLOR});
  892           htmlAttrToCssAttrMap.put(HTML.Attribute.WIDTH,
  893                                    new CSS.Attribute[]{CSS.Attribute.WIDTH});
  894           htmlAttrToCssAttrMap.put(HTML.Attribute.HEIGHT,
  895                                    new CSS.Attribute[]{CSS.Attribute.HEIGHT});
  896           htmlAttrToCssAttrMap.put(HTML.Attribute.BORDER,
  897                                    new CSS.Attribute[]{CSS.Attribute.BORDER_TOP_WIDTH, CSS.Attribute.BORDER_RIGHT_WIDTH, CSS.Attribute.BORDER_BOTTOM_WIDTH, CSS.Attribute.BORDER_LEFT_WIDTH});
  898           htmlAttrToCssAttrMap.put(HTML.Attribute.CELLPADDING,
  899                                    new CSS.Attribute[]{CSS.Attribute.PADDING});
  900           htmlAttrToCssAttrMap.put(HTML.Attribute.CELLSPACING,
  901                                    new CSS.Attribute[]{CSS.Attribute.BORDER_SPACING});
  902           htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINWIDTH,
  903                                    new CSS.Attribute[]{CSS.Attribute.MARGIN_LEFT,
  904                                                        CSS.Attribute.MARGIN_RIGHT});
  905           htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINHEIGHT,
  906                                    new CSS.Attribute[]{CSS.Attribute.MARGIN_TOP,
  907                                                        CSS.Attribute.MARGIN_BOTTOM});
  908           htmlAttrToCssAttrMap.put(HTML.Attribute.HSPACE,
  909                                    new CSS.Attribute[]{CSS.Attribute.PADDING_LEFT,
  910                                                        CSS.Attribute.PADDING_RIGHT});
  911           htmlAttrToCssAttrMap.put(HTML.Attribute.VSPACE,
  912                                    new CSS.Attribute[]{CSS.Attribute.PADDING_BOTTOM,
  913                                                        CSS.Attribute.PADDING_TOP});
  914           htmlAttrToCssAttrMap.put(HTML.Attribute.FACE,
  915                                    new CSS.Attribute[]{CSS.Attribute.FONT_FAMILY});
  916           htmlAttrToCssAttrMap.put(HTML.Attribute.SIZE,
  917                                    new CSS.Attribute[]{CSS.Attribute.FONT_SIZE});
  918           htmlAttrToCssAttrMap.put(HTML.Attribute.VALIGN,
  919                                    new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN});
  920           htmlAttrToCssAttrMap.put(HTML.Attribute.ALIGN,
  921                                    new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN,
  922                                                        CSS.Attribute.TEXT_ALIGN,
  923                                                        CSS.Attribute.FLOAT});
  924           htmlAttrToCssAttrMap.put(HTML.Attribute.TYPE,
  925                                    new CSS.Attribute[]{CSS.Attribute.LIST_STYLE_TYPE});
  926           htmlAttrToCssAttrMap.put(HTML.Attribute.NOWRAP,
  927                                    new CSS.Attribute[]{CSS.Attribute.WHITE_SPACE});
  928   
  929           // initialize StyleConstants mapping
  930           styleConstantToCssMap.put(StyleConstants.FontFamily,
  931                                     CSS.Attribute.FONT_FAMILY);
  932           styleConstantToCssMap.put(StyleConstants.FontSize,
  933                                     CSS.Attribute.FONT_SIZE);
  934           styleConstantToCssMap.put(StyleConstants.Bold,
  935                                     CSS.Attribute.FONT_WEIGHT);
  936           styleConstantToCssMap.put(StyleConstants.Italic,
  937                                     CSS.Attribute.FONT_STYLE);
  938           styleConstantToCssMap.put(StyleConstants.Underline,
  939                                     CSS.Attribute.TEXT_DECORATION);
  940           styleConstantToCssMap.put(StyleConstants.StrikeThrough,
  941                                     CSS.Attribute.TEXT_DECORATION);
  942           styleConstantToCssMap.put(StyleConstants.Superscript,
  943                                     CSS.Attribute.VERTICAL_ALIGN);
  944           styleConstantToCssMap.put(StyleConstants.Subscript,
  945                                     CSS.Attribute.VERTICAL_ALIGN);
  946           styleConstantToCssMap.put(StyleConstants.Foreground,
  947                                     CSS.Attribute.COLOR);
  948           styleConstantToCssMap.put(StyleConstants.Background,
  949                                     CSS.Attribute.BACKGROUND_COLOR);
  950           styleConstantToCssMap.put(StyleConstants.FirstLineIndent,
  951                                     CSS.Attribute.TEXT_INDENT);
  952           styleConstantToCssMap.put(StyleConstants.LeftIndent,
  953                                     CSS.Attribute.MARGIN_LEFT);
  954           styleConstantToCssMap.put(StyleConstants.RightIndent,
  955                                     CSS.Attribute.MARGIN_RIGHT);
  956           styleConstantToCssMap.put(StyleConstants.SpaceAbove,
  957                                     CSS.Attribute.MARGIN_TOP);
  958           styleConstantToCssMap.put(StyleConstants.SpaceBelow,
  959                                     CSS.Attribute.MARGIN_BOTTOM);
  960           styleConstantToCssMap.put(StyleConstants.Alignment,
  961                                     CSS.Attribute.TEXT_ALIGN);
  962   
  963           // HTML->CSS
  964           htmlValueToCssValueMap.put("disc", CSS.Value.DISC);
  965           htmlValueToCssValueMap.put("square", CSS.Value.SQUARE);
  966           htmlValueToCssValueMap.put("circle", CSS.Value.CIRCLE);
  967           htmlValueToCssValueMap.put("1", CSS.Value.DECIMAL);
  968           htmlValueToCssValueMap.put("a", CSS.Value.LOWER_ALPHA);
  969           htmlValueToCssValueMap.put("A", CSS.Value.UPPER_ALPHA);
  970           htmlValueToCssValueMap.put("i", CSS.Value.LOWER_ROMAN);
  971           htmlValueToCssValueMap.put("I", CSS.Value.UPPER_ROMAN);
  972   
  973           // CSS-> internal CSS
  974           cssValueToInternalValueMap.put("none", CSS.Value.NONE);
  975           cssValueToInternalValueMap.put("disc", CSS.Value.DISC);
  976           cssValueToInternalValueMap.put("square", CSS.Value.SQUARE);
  977           cssValueToInternalValueMap.put("circle", CSS.Value.CIRCLE);
  978           cssValueToInternalValueMap.put("decimal", CSS.Value.DECIMAL);
  979           cssValueToInternalValueMap.put("lower-roman", CSS.Value.LOWER_ROMAN);
  980           cssValueToInternalValueMap.put("upper-roman", CSS.Value.UPPER_ROMAN);
  981           cssValueToInternalValueMap.put("lower-alpha", CSS.Value.LOWER_ALPHA);
  982           cssValueToInternalValueMap.put("upper-alpha", CSS.Value.UPPER_ALPHA);
  983           cssValueToInternalValueMap.put("repeat", CSS.Value.BACKGROUND_REPEAT);
  984           cssValueToInternalValueMap.put("no-repeat",
  985                                          CSS.Value.BACKGROUND_NO_REPEAT);
  986           cssValueToInternalValueMap.put("repeat-x",
  987                                          CSS.Value.BACKGROUND_REPEAT_X);
  988           cssValueToInternalValueMap.put("repeat-y",
  989                                          CSS.Value.BACKGROUND_REPEAT_Y);
  990           cssValueToInternalValueMap.put("scroll",
  991                                          CSS.Value.BACKGROUND_SCROLL);
  992           cssValueToInternalValueMap.put("fixed",
  993                                          CSS.Value.BACKGROUND_FIXED);
  994   
  995           // Register all the CSS attribute keys for archival/unarchival
  996           Object[] keys = CSS.Attribute.allAttributes;
  997           try {
  998               for (int i = 0; i < keys.length; i++) {
  999                   StyleContext.registerStaticAttributeKey(keys[i]);
 1000               }
 1001           } catch (Throwable e) {
 1002               e.printStackTrace();
 1003           }
 1004   
 1005           // Register all the CSS Values for archival/unarchival
 1006           keys = CSS.Value.allValues;
 1007           try {
 1008               for (int i = 0; i < keys.length; i++) {
 1009                   StyleContext.registerStaticAttributeKey(keys[i]);
 1010               }
 1011           } catch (Throwable e) {
 1012               e.printStackTrace();
 1013           }
 1014       }
 1015   
 1016       /**
 1017        * Return the set of all possible CSS attribute keys.
 1018        */
 1019       public static Attribute[] getAllAttributeKeys() {
 1020           Attribute[] keys = new Attribute[Attribute.allAttributes.length];
 1021           System.arraycopy(Attribute.allAttributes, 0, keys, 0, Attribute.allAttributes.length);
 1022           return keys;
 1023       }
 1024   
 1025       /**
 1026        * Translates a string to a <code>CSS.Attribute</code> object.
 1027        * This will return <code>null</code> if there is no attribute
 1028        * by the given name.
 1029        *
 1030        * @param name the name of the CSS attribute to fetch the
 1031        *  typesafe enumeration for
 1032        * @return the <code>CSS.Attribute</code> object,
 1033        *  or <code>null</code> if the string
 1034        *  doesn't represent a valid attribute key
 1035        */
 1036       public static final Attribute getAttribute(String name) {
 1037           return (Attribute) attributeMap.get(name);
 1038       }
 1039   
 1040       /**
 1041        * Translates a string to a <code>CSS.Value</code> object.
 1042        * This will return <code>null</code> if there is no value
 1043        * by the given name.
 1044        *
 1045        * @param name the name of the CSS value to fetch the
 1046        *  typesafe enumeration for
 1047        * @return the <code>CSS.Value</code> object,
 1048        *  or <code>null</code> if the string
 1049        *  doesn't represent a valid CSS value name; this does
 1050        *  not mean that it doesn't represent a valid CSS value
 1051        */
 1052       static final Value getValue(String name) {
 1053           return (Value) valueMap.get(name);
 1054       }
 1055   
 1056   
 1057       //
 1058       // Conversion related methods/classes
 1059       //
 1060   
 1061       /**
 1062        * Returns a URL for the given CSS url string. If relative,
 1063        * <code>base</code> is used as the parent. If a valid URL can not
 1064        * be found, this will not throw a MalformedURLException, instead
 1065        * null will be returned.
 1066        */
 1067       static URL getURL(URL base, String cssString) {
 1068           if (cssString == null) {
 1069               return null;
 1070           }
 1071           if (cssString.startsWith("url(") &&
 1072               cssString.endsWith(")")) {
 1073               cssString = cssString.substring(4, cssString.length() - 1);
 1074           }
 1075           // Absolute first
 1076           try {
 1077               URL url = new URL(cssString);
 1078               if (url != null) {
 1079                   return url;
 1080               }
 1081           } catch (MalformedURLException mue) {
 1082           }
 1083           // Then relative
 1084           if (base != null) {
 1085               // Relative URL, try from base
 1086               try {
 1087                   URL url = new URL(base, cssString);
 1088                   return url;
 1089               }
 1090               catch (MalformedURLException muee) {
 1091               }
 1092           }
 1093           return null;
 1094       }
 1095   
 1096       /**
 1097        * Converts a type Color to a hex string
 1098        * in the format "#RRGGBB"
 1099        */
 1100       static String colorToHex(Color color) {
 1101   
 1102         String colorstr = "#";
 1103   
 1104         // Red
 1105         String str = Integer.toHexString(color.getRed());
 1106         if (str.length() > 2)
 1107           str = str.substring(0, 2);
 1108         else if (str.length() < 2)
 1109           colorstr += "0" + str;
 1110         else
 1111           colorstr += str;
 1112   
 1113         // Green
 1114         str = Integer.toHexString(color.getGreen());
 1115         if (str.length() > 2)
 1116           str = str.substring(0, 2);
 1117         else if (str.length() < 2)
 1118           colorstr += "0" + str;
 1119         else
 1120           colorstr += str;
 1121   
 1122         // Blue
 1123         str = Integer.toHexString(color.getBlue());
 1124         if (str.length() > 2)
 1125           str = str.substring(0, 2);
 1126         else if (str.length() < 2)
 1127           colorstr += "0" + str;
 1128         else
 1129           colorstr += str;
 1130   
 1131         return colorstr;
 1132       }
 1133   
 1134        /**
 1135         * Convert a "#FFFFFF" hex string to a Color.
 1136         * If the color specification is bad, an attempt
 1137         * will be made to fix it up.
 1138         */
 1139       static final Color hexToColor(String value) {
 1140           String digits;
 1141           int n = value.length();
 1142           if (value.startsWith("#")) {
 1143               digits = value.substring(1, Math.min(value.length(), 7));
 1144           } else {
 1145               digits = value;
 1146           }
 1147           String hstr = "0x" + digits;
 1148           Color c;
 1149           try {
 1150               c = Color.decode(hstr);
 1151           } catch (NumberFormatException nfe) {
 1152               c = null;
 1153           }
 1154            return c;
 1155        }
 1156   
 1157       /**
 1158        * Convert a color string such as "RED" or "#NNNNNN" or "rgb(r, g, b)"
 1159        * to a Color.
 1160        */
 1161       static Color stringToColor(String str) {
 1162         Color color = null;
 1163   
 1164         if (str == null) {
 1165             return null;
 1166         }
 1167         if (str.length() == 0)
 1168           color = Color.black;
 1169         else if (str.startsWith("rgb(")) {
 1170             color = parseRGB(str);
 1171         }
 1172         else if (str.charAt(0) == '#')
 1173           color = hexToColor(str);
 1174         else if (str.equalsIgnoreCase("Black"))
 1175           color = hexToColor("#000000");
 1176         else if(str.equalsIgnoreCase("Silver"))
 1177           color = hexToColor("#C0C0C0");
 1178         else if(str.equalsIgnoreCase("Gray"))
 1179           color = hexToColor("#808080");
 1180         else if(str.equalsIgnoreCase("White"))
 1181           color = hexToColor("#FFFFFF");
 1182         else if(str.equalsIgnoreCase("Maroon"))
 1183           color = hexToColor("#800000");
 1184         else if(str.equalsIgnoreCase("Red"))
 1185           color = hexToColor("#FF0000");
 1186         else if(str.equalsIgnoreCase("Purple"))
 1187           color = hexToColor("#800080");
 1188         else if(str.equalsIgnoreCase("Fuchsia"))
 1189           color = hexToColor("#FF00FF");
 1190         else if(str.equalsIgnoreCase("Green"))
 1191           color = hexToColor("#008000");
 1192         else if(str.equalsIgnoreCase("Lime"))
 1193           color = hexToColor("#00FF00");
 1194         else if(str.equalsIgnoreCase("Olive"))
 1195           color = hexToColor("#808000");
 1196         else if(str.equalsIgnoreCase("Yellow"))
 1197           color = hexToColor("#FFFF00");
 1198         else if(str.equalsIgnoreCase("Navy"))
 1199           color = hexToColor("#000080");
 1200         else if(str.equalsIgnoreCase("Blue"))
 1201           color = hexToColor("#0000FF");
 1202         else if(str.equalsIgnoreCase("Teal"))
 1203           color = hexToColor("#008080");
 1204         else if(str.equalsIgnoreCase("Aqua"))
 1205           color = hexToColor("#00FFFF");
 1206         else if(str.equalsIgnoreCase("Orange"))
 1207           color = hexToColor("#FF8000");
 1208         else
 1209             color = hexToColor(str); // sometimes get specified without leading #
 1210         return color;
 1211       }
 1212   
 1213       /**
 1214        * Parses a String in the format <code>rgb(r, g, b)</code> where
 1215        * each of the Color components is either an integer, or a floating number
 1216        * with a % after indicating a percentage value of 255. Values are
 1217        * constrained to fit with 0-255. The resulting Color is returned.
 1218        */
 1219       private static Color parseRGB(String string) {
 1220           // Find the next numeric char
 1221           int[] index = new int[1];
 1222   
 1223           index[0] = 4;
 1224           int red = getColorComponent(string, index);
 1225           int green = getColorComponent(string, index);
 1226           int blue = getColorComponent(string, index);
 1227   
 1228           return new Color(red, green, blue);
 1229       }
 1230   
 1231       /**
 1232        * Returns the next integer value from <code>string</code> starting
 1233        * at <code>index[0]</code>. The value can either can an integer, or
 1234        * a percentage (floating number ending with %), in which case it is
 1235        * multiplied by 255.
 1236        */
 1237       private static int getColorComponent(String string, int[] index) {
 1238           int length = string.length();
 1239           char aChar;
 1240   
 1241           // Skip non-decimal chars
 1242           while(index[0] < length && (aChar = string.charAt(index[0])) != '-' &&
 1243                 !Character.isDigit(aChar) && aChar != '.') {
 1244               index[0]++;
 1245           }
 1246   
 1247           int start = index[0];
 1248   
 1249           if (start < length && string.charAt(index[0]) == '-') {
 1250               index[0]++;
 1251           }
 1252           while(index[0] < length &&
 1253                            Character.isDigit(string.charAt(index[0]))) {
 1254               index[0]++;
 1255           }
 1256           if (index[0] < length && string.charAt(index[0]) == '.') {
 1257               // Decimal value
 1258               index[0]++;
 1259               while(index[0] < length &&
 1260                     Character.isDigit(string.charAt(index[0]))) {
 1261                   index[0]++;
 1262               }
 1263           }
 1264           if (start != index[0]) {
 1265               try {
 1266                   float value = Float.parseFloat(string.substring
 1267                                                  (start, index[0]));
 1268   
 1269                   if (index[0] < length && string.charAt(index[0]) == '%') {
 1270                       index[0]++;
 1271                       value = value * 255f / 100f;
 1272                   }
 1273                   return Math.min(255, Math.max(0, (int)value));
 1274               } catch (NumberFormatException nfe) {
 1275                   // Treat as 0
 1276               }
 1277           }
 1278           return 0;
 1279       }
 1280   
 1281       static int getIndexOfSize(float pt, int[] sizeMap) {
 1282           for (int i = 0; i < sizeMap.length; i ++ )
 1283                   if (pt <= sizeMap[i])
 1284                           return i + 1;
 1285           return sizeMap.length;
 1286       }
 1287   
 1288       static int getIndexOfSize(float pt, StyleSheet ss) {
 1289           int[] sizeMap = (ss != null) ? ss.getSizeMap() :
 1290               StyleSheet.sizeMapDefault;
 1291           return getIndexOfSize(pt, sizeMap);
 1292       }
 1293   
 1294   
 1295       /**
 1296        * @return an array of all the strings in <code>value</code>
 1297        *         that are separated by whitespace.
 1298        */
 1299       static String[] parseStrings(String value) {
 1300           int         current, last;
 1301           int         length = (value == null) ? 0 : value.length();
 1302           Vector      temp = new Vector(4);
 1303   
 1304           current = 0;
 1305           while (current < length) {
 1306               // Skip ws
 1307               while (current < length && Character.isWhitespace
 1308                      (value.charAt(current))) {
 1309                   current++;
 1310               }
 1311               last = current;
 1312               while (current < length && !Character.isWhitespace
 1313                      (value.charAt(current))) {
 1314                   current++;
 1315               }
 1316               if (last != current) {
 1317                   temp.addElement(value.substring(last, current));
 1318               }
 1319               current++;
 1320           }
 1321           String[] retValue = new String[temp.size()];
 1322           temp.copyInto(retValue);
 1323           return retValue;
 1324       }
 1325   
 1326       /**
 1327        * Return the point size, given a size index. Legal HTML index sizes
 1328        * are 1-7.
 1329        */
 1330       float getPointSize(int index, StyleSheet ss) {
 1331           ss = getStyleSheet(ss);
 1332           int[] sizeMap = (ss != null) ? ss.getSizeMap() :
 1333               StyleSheet.sizeMapDefault;
 1334           --index;
 1335           if (index < 0)
 1336             return sizeMap[0];
 1337           else if (index > sizeMap.length - 1)
 1338             return sizeMap[sizeMap.length - 1];
 1339           else
 1340             return sizeMap[index];
 1341       }
 1342   
 1343   
 1344       private void translateEmbeddedAttributes(AttributeSet htmlAttrSet,
 1345                                                MutableAttributeSet cssAttrSet) {
 1346           Enumeration keys = htmlAttrSet.getAttributeNames();
 1347           if (htmlAttrSet.getAttribute(StyleConstants.NameAttribute) ==
 1348               HTML.Tag.HR) {
 1349               // HR needs special handling due to us treating it as a leaf.
 1350               translateAttributes(HTML.Tag.HR, htmlAttrSet, cssAttrSet);
 1351           }
 1352           while (keys.hasMoreElements()) {
 1353               Object key = keys.nextElement();
 1354               if (key instanceof HTML.Tag) {
 1355                   HTML.Tag tag = (HTML.Tag)key;
 1356                   Object o = htmlAttrSet.getAttribute(tag);
 1357                   if (o != null && o instanceof AttributeSet) {
 1358                       translateAttributes(tag, (AttributeSet)o, cssAttrSet);
 1359                   }
 1360               } else if (key instanceof CSS.Attribute) {
 1361                   cssAttrSet.addAttribute(key, htmlAttrSet.getAttribute(key));
 1362               }
 1363           }
 1364       }
 1365   
 1366       private void translateAttributes(HTML.Tag tag,
 1367                                               AttributeSet htmlAttrSet,
 1368                                               MutableAttributeSet cssAttrSet) {
 1369           Enumeration names = htmlAttrSet.getAttributeNames();
 1370           while (names.hasMoreElements()) {
 1371               Object name = names.nextElement();
 1372   
 1373               if (name instanceof HTML.Attribute) {
 1374                   HTML.Attribute key = (HTML.Attribute)name;
 1375   
 1376                   /*
 1377                    * HTML.Attribute.ALIGN needs special processing.
 1378                    * It can map to to 1 of many(3) possible CSS attributes
 1379                    * depending on the nature of the tag the attribute is
 1380                    * part off and depending on the value of the attribute.
 1381                    */
 1382                   if (key == HTML.Attribute.ALIGN) {
 1383                       String htmlAttrValue = (String)htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
 1384                       if (htmlAttrValue != null) {
 1385                           CSS.Attribute cssAttr = getCssAlignAttribute(tag, htmlAttrSet);
 1386                           if (cssAttr != null) {
 1387                               Object o = getCssValue(cssAttr, htmlAttrValue);
 1388                               if (o != null) {
 1389                                   cssAttrSet.addAttribute(cssAttr, o);
 1390                               }
 1391                           }
 1392                       }
 1393                   } else {
 1394   
 1395                       /*
 1396                        * The html size attribute has a mapping in the CSS world only
 1397                        * if it is par of a font or base font tag.
 1398                        */
 1399   
 1400                       if (key == HTML.Attribute.SIZE && !isHTMLFontTag(tag)) {
 1401                           continue;
 1402                       }
 1403   
 1404                       translateAttribute(key, htmlAttrSet, cssAttrSet);
 1405                   }
 1406               } else if (name instanceof CSS.Attribute) {
 1407                   cssAttrSet.addAttribute(name, htmlAttrSet.getAttribute(name));
 1408               }
 1409           }
 1410       }
 1411   
 1412       private void translateAttribute(HTML.Attribute key,
 1413                                              AttributeSet htmlAttrSet,
 1414                                              MutableAttributeSet cssAttrSet) {
 1415           /*
 1416            * In the case of all remaining HTML.Attribute's they
 1417            * map to 1 or more CCS.Attribute.
 1418            */
 1419           CSS.Attribute[] cssAttrList = getCssAttribute(key);
 1420   
 1421           String htmlAttrValue = (String)htmlAttrSet.getAttribute(key);
 1422   
 1423           if (cssAttrList == null || htmlAttrValue == null) {
 1424               return;
 1425           }
 1426           for (int i = 0; i < cssAttrList.length; i++) {
 1427               Object o = getCssValue(cssAttrList[i], htmlAttrValue);
 1428               if (o != null) {
 1429                   cssAttrSet.addAttribute(cssAttrList[i], o);
 1430               }
 1431           }
 1432       }
 1433   
 1434       /**
 1435        * Given a CSS.Attribute object and its corresponding HTML.Attribute's
 1436        * value, this method returns a CssValue object to associate with the
 1437        * CSS attribute.
 1438        *
 1439        * @param the CSS.Attribute
 1440        * @param a String containing the value associated HTML.Attribtue.
 1441        */
 1442       Object getCssValue(CSS.Attribute cssAttr, String htmlAttrValue) {
 1443           CssValue value = (CssValue)valueConvertor.get(cssAttr);
 1444           Object o = value.parseHtmlValue(htmlAttrValue);
 1445           return o;
 1446       }
 1447   
 1448       /**
 1449        * Maps an HTML.Attribute object to its appropriate CSS.Attributes.
 1450        *
 1451        * @param HTML.Attribute
 1452        * @return CSS.Attribute[]
 1453        */
 1454       private CSS.Attribute[] getCssAttribute(HTML.Attribute hAttr) {
 1455           return (CSS.Attribute[])htmlAttrToCssAttrMap.get(hAttr);
 1456       }
 1457   
 1458       /**
 1459        * Maps HTML.Attribute.ALIGN to either:
 1460        *     CSS.Attribute.TEXT_ALIGN
 1461        *     CSS.Attribute.FLOAT
 1462        *     CSS.Attribute.VERTICAL_ALIGN
 1463        * based on the tag associated with the attribute and the
 1464        * value of the attribute.
 1465        *
 1466        * @param AttributeSet containing HTML attributes.
 1467        * @return CSS.Attribute mapping for HTML.Attribute.ALIGN.
 1468        */
 1469       private CSS.Attribute getCssAlignAttribute(HTML.Tag tag,
 1470                                                      AttributeSet htmlAttrSet) {
 1471           return CSS.Attribute.TEXT_ALIGN;
 1472   /*
 1473           String htmlAttrValue = (String)htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
 1474           CSS.Attribute cssAttr = CSS.Attribute.TEXT_ALIGN;
 1475           if (htmlAttrValue != null && htmlAttrSet instanceof Element) {
 1476               Element elem = (Element)htmlAttrSet;
 1477               if (!elem.isLeaf() && tag.isBlock() && validTextAlignValue(htmlAttrValue)) {
 1478                   return CSS.Attribute.TEXT_ALIGN;
 1479               } else if (isFloater(htmlAttrValue)) {
 1480                   return CSS.Attribute.FLOAT;
 1481               } else if (elem.isLeaf()) {
 1482                   return CSS.Attribute.VERTICAL_ALIGN;
 1483               }
 1484           }
 1485           return null;
 1486           */
 1487       }
 1488   
 1489       /**
 1490        * Fetches the tag associated with the HTML AttributeSet.
 1491        *
 1492        * @param  AttributeSet containing the HTML attributes.
 1493        * @return HTML.Tag
 1494        */
 1495       private HTML.Tag getHTMLTag(AttributeSet htmlAttrSet) {
 1496           Object o = htmlAttrSet.getAttribute(StyleConstants.NameAttribute);
 1497           if (o instanceof HTML.Tag) {
 1498               HTML.Tag tag = (HTML.Tag) o;
 1499               return tag;
 1500           }
 1501           return null;
 1502       }
 1503   
 1504   
 1505       private boolean isHTMLFontTag(HTML.Tag tag) {
 1506           return (tag != null && ((tag == HTML.Tag.FONT) || (tag == HTML.Tag.BASEFONT)));
 1507       }
 1508   
 1509   
 1510       private boolean isFloater(String alignValue) {
 1511           return (alignValue.equals("left") || alignValue.equals("right"));
 1512       }
 1513   
 1514       private boolean validTextAlignValue(String alignValue) {
 1515           return (isFloater(alignValue) || alignValue.equals("center"));
 1516       }
 1517   
 1518       /**
 1519        * Base class to CSS values in the attribute sets.  This
 1520        * is intended to act as a convertor to/from other attribute
 1521        * formats.
 1522        * <p>
 1523        * The CSS parser uses the parseCssValue method to convert
 1524        * a string to whatever format is appropriate a given key
 1525        * (i.e. these convertors are stored in a map using the
 1526        * CSS.Attribute as a key and the CssValue as the value).
 1527        * <p>
 1528        * The HTML to CSS conversion process first converts the
 1529        * HTML.Attribute to a CSS.Attribute, and then calls
 1530        * the parseHtmlValue method on the value of the HTML
 1531        * attribute to produce the corresponding CSS value.
 1532        * <p>
 1533        * The StyleConstants to CSS conversion process first
 1534        * converts the StyleConstants attribute to a
 1535        * CSS.Attribute, and then calls the fromStyleConstants
 1536        * method to convert the StyleConstants value to a
 1537        * CSS value.
 1538        * <p>
 1539        * The CSS to StyleConstants conversion process first
 1540        * converts the StyleConstants attribute to a
 1541        * CSS.Attribute, and then calls the toStyleConstants
 1542        * method to convert the CSS value to a StyleConstants
 1543        * value.
 1544        */
 1545       static class CssValue implements Serializable {
 1546   
 1547           /**
 1548            * Convert a CSS value string to the internal format
 1549            * (for fast processing) used in the attribute sets.
 1550            * The fallback storage for any value that we don't
 1551            * have a special binary format for is a String.
 1552            */
 1553           Object parseCssValue(String value) {
 1554               return value;
 1555           }
 1556   
 1557           /**
 1558            * Convert an HTML attribute value to a CSS attribute
 1559            * value.  If there is no conversion, return null.
 1560            * This is implemented to simply forward to the CSS
 1561            * parsing by default (since some of the attribute
 1562            * values are the same).  If the attribute value
 1563            * isn't recognized as a CSS value it is generally
 1564            * returned as null.
 1565            */
 1566           Object parseHtmlValue(String value) {
 1567               return parseCssValue(value);
 1568           }
 1569   
 1570           /**
 1571            * Converts a <code>StyleConstants</code> attribute value to
 1572            * a CSS attribute value.  If there is no conversion,
 1573            * returns <code>null</code>.  By default, there is no conversion.
 1574            *
 1575            * @param key the <code>StyleConstants</code> attribute
 1576            * @param value the value of a <code>StyleConstants</code>
 1577            *   attribute to be converted
 1578            * @return the CSS value that represents the
 1579            *   <code>StyleConstants</code> value
 1580            */
 1581           Object fromStyleConstants(StyleConstants key, Object value) {
 1582               return null;
 1583           }
 1584   
 1585           /**
 1586            * Converts a CSS attribute value to a
 1587            * <code>StyleConstants</code>
 1588            * value.  If there is no conversion, returns
 1589            * <code>null</code>.
 1590            * By default, there is no conversion.
 1591            *
 1592            * @param key the <code>StyleConstants</code> attribute
 1593            * @param v the view containing <code>AttributeSet</code>
 1594            * @return the <code>StyleConstants</code> attribute value that
 1595            *   represents the CSS attribute value
 1596            */
 1597           Object toStyleConstants(StyleConstants key, View v) {
 1598               return null;
 1599           }
 1600   
 1601           /**
 1602            * Return the CSS format of the value
 1603            */
 1604           public String toString() {
 1605               return svalue;
 1606           }
 1607   
 1608           /**
 1609            * The value as a string... before conversion to a
 1610            * binary format.
 1611            */
 1612           String svalue;
 1613       }
 1614   
 1615       /**
 1616        * By default CSS attributes are represented as simple
 1617        * strings.  They also have no conversion to/from
 1618        * StyleConstants by default. This class represents the
 1619        * value as a string (via the superclass), but
 1620        * provides StyleConstants conversion support for the
 1621        * CSS attributes that are held as strings.
 1622        */
 1623       static class StringValue extends CssValue {
 1624   
 1625           /**
 1626            * Convert a CSS value string to the internal format
 1627            * (for fast processing) used in the attribute sets.
 1628            * This produces a StringValue, so that it can be
 1629            * used to convert from CSS to StyleConstants values.
 1630            */
 1631           Object parseCssValue(String value) {
 1632               StringValue sv = new StringValue();
 1633               sv.svalue = value;
 1634               return sv;
 1635           }
 1636   
 1637           /**
 1638            * Converts a <code>StyleConstants</code> attribute value to
 1639            * a CSS attribute value.  If there is no conversion
 1640            * returns <code>null</code>.
 1641            *
 1642            * @param key the <code>StyleConstants</code> attribute
 1643            * @param value the value of a <code>StyleConstants</code>
 1644            *   attribute to be converted
 1645            * @return the CSS value that represents the
 1646            *   <code>StyleConstants</code> value
 1647            */
 1648           Object fromStyleConstants(StyleConstants key, Object value) {
 1649               if (key == StyleConstants.Italic) {
 1650                   if (value.equals(Boolean.TRUE)) {
 1651                       return parseCssValue("italic");
 1652                   }
 1653                   return parseCssValue("");
 1654               } else if (key == StyleConstants.Underline) {
 1655                   if (value.equals(Boolean.TRUE)) {
 1656                       return parseCssValue("underline");
 1657                   }
 1658                   return parseCssValue("");
 1659               } else if (key == StyleConstants.Alignment) {
 1660                   int align = ((Integer)value).intValue();
 1661                   String ta;
 1662                   switch(align) {
 1663                   case StyleConstants.ALIGN_LEFT:
 1664                       ta = "left";
 1665                       break;
 1666                   case StyleConstants.ALIGN_RIGHT:
 1667                       ta = "right";
 1668                       break;
 1669                   case StyleConstants.ALIGN_CENTER:
 1670                       ta = "center";
 1671                       break;
 1672                   case StyleConstants.ALIGN_JUSTIFIED:
 1673                       ta = "justify";
 1674                       break;
 1675                   default:
 1676                       ta = "left";
 1677                   }
 1678                   return parseCssValue(ta);
 1679               } else if (key == StyleConstants.StrikeThrough) {
 1680                   if (value.equals(Boolean.TRUE)) {
 1681                       return parseCssValue("line-through");
 1682                   }
 1683                   return parseCssValue("");
 1684               } else if (key == StyleConstants.Superscript) {
 1685                   if (value.equals(Boolean.TRUE)) {
 1686                       return parseCssValue("super");
 1687                   }
 1688                   return parseCssValue("");
 1689               } else if (key == StyleConstants.Subscript) {
 1690                   if (value.equals(Boolean.TRUE)) {
 1691                       return parseCssValue("sub");
 1692                   }
 1693                   return parseCssValue("");
 1694               }
 1695               return null;
 1696           }
 1697   
 1698           /**
 1699            * Converts a CSS attribute value to a
 1700            * <code>StyleConstants</code> value.
 1701            * If there is no conversion, returns <code>null</code>.
 1702            * By default, there is no conversion.
 1703            *
 1704            * @param key the <code>StyleConstants</code> attribute
 1705            * @return the <code>StyleConstants</code> attribute value that
 1706            *   represents the CSS attribute value
 1707            */
 1708           Object toStyleConstants(StyleConstants key, View v) {
 1709               if (key == StyleConstants.Italic) {
 1710                   if (svalue.indexOf("italic") >= 0) {
 1711                       return Boolean.TRUE;
 1712                   }
 1713                   return Boolean.FALSE;
 1714               } else if (key == StyleConstants.Underline) {
 1715                   if (svalue.indexOf("underline") >= 0) {
 1716                       return Boolean.TRUE;
 1717                   }
 1718                   return Boolean.FALSE;
 1719               } else if (key == StyleConstants.Alignment) {
 1720                   if (svalue.equals("right")) {
 1721                       return new Integer(StyleConstants.ALIGN_RIGHT);
 1722                   } else if (svalue.equals("center")) {
 1723                       return new Integer(StyleConstants.ALIGN_CENTER);
 1724                   } else if  (svalue.equals("justify")) {
 1725                       return new Integer(StyleConstants.ALIGN_JUSTIFIED);
 1726                   }
 1727                   return new Integer(StyleConstants.ALIGN_LEFT);
 1728               } else if (key == StyleConstants.StrikeThrough) {
 1729                   if (svalue.indexOf("line-through") >= 0) {
 1730                       return Boolean.TRUE;
 1731                   }
 1732                   return Boolean.FALSE;
 1733               } else if (key == StyleConstants.Superscript) {
 1734                   if (svalue.indexOf("super") >= 0) {
 1735                       return Boolean.TRUE;
 1736                   }
 1737                   return Boolean.FALSE;
 1738               } else if (key == StyleConstants.Subscript) {
 1739                   if (svalue.indexOf("sub") >= 0) {
 1740                       return Boolean.TRUE;
 1741                   }
 1742                   return Boolean.FALSE;
 1743               }
 1744               return null;
 1745           }
 1746   
 1747           // Used by ViewAttributeSet
 1748           boolean isItalic() {
 1749               return (svalue.indexOf("italic") != -1);
 1750           }
 1751   
 1752           boolean isStrike() {
 1753               return (svalue.indexOf("line-through") != -1);
 1754           }
 1755   
 1756           boolean isUnderline() {
 1757               return (svalue.indexOf("underline") != -1);
 1758           }
 1759   
 1760           boolean isSub() {
 1761               return (svalue.indexOf("sub") != -1);
 1762           }
 1763   
 1764           boolean isSup() {
 1765               return (svalue.indexOf("sup") != -1);
 1766           }
 1767       }
 1768   
 1769       /**
 1770        * Represents a value for the CSS.FONT_SIZE attribute.
 1771        * The binary format of the value can be one of several
 1772        * types.  If the type is Float,
 1773        * the value is specified in terms of point or
 1774        * percentage, depending upon the ending of the
 1775        * associated string.
 1776        * If the type is Integer, the value is specified
 1777        * in terms of a size index.
 1778        */
 1779       class FontSize extends CssValue {
 1780   
 1781           /**
 1782            * Returns the size in points.  This is ultimately
 1783            * what we need for the purpose of creating/fetching
 1784            * a Font object.
 1785            *
 1786            * @param a the attribute set the value is being
 1787            *  requested from.  We may need to walk up the
 1788            *  resolve hierarchy if it's relative.
 1789            */
 1790           int getValue(AttributeSet a, StyleSheet ss) {
 1791               ss = getStyleSheet(ss);
 1792               if (index) {
 1793                   // it's an index, translate from size table
 1794                   return Math.round(getPointSize((int) value, ss));
 1795               }
 1796               else if (lu == null) {
 1797                   return Math.round(value);
 1798               }
 1799               else {
 1800                   if (lu.type == 0) {
 1801                       boolean isW3CLengthUnits = (ss == null) ? false : ss.isW3CLengthUnits();
 1802                       return Math.round(lu.getValue(isW3CLengthUnits));
 1803                   }
 1804                   if (a != null) {
 1805                       AttributeSet resolveParent = a.getResolveParent();
 1806   
 1807                       if (resolveParent != null) {
 1808                           int pValue = StyleConstants.getFontSize(resolveParent);
 1809   
 1810                           float retValue;
 1811                           if (lu.type == 1 || lu.type == 3) {
 1812                               retValue = lu.value * (float)pValue;
 1813                           }
 1814                           else {
 1815                               retValue = lu.value + (float)pValue;
 1816                           }
 1817                           return Math.round(retValue);
 1818                       }
 1819                   }
 1820                   // a is null, or no resolve parent.
 1821                   return 12;
 1822               }
 1823           }
 1824   
 1825           Object parseCssValue(String value) {
 1826               FontSize fs = new FontSize();
 1827               fs.svalue = value;
 1828               try {
 1829                   if (value.equals("xx-small")) {
 1830                       fs.value = 1;
 1831                       fs.index = true;
 1832                   } else if (value.equals("x-small")) {
 1833                       fs.value = 2;
 1834                       fs.index = true;
 1835                   } else if (value.equals("small")) {
 1836                       fs.value = 3;
 1837                       fs.index = true;
 1838                   } else if (value.equals("medium")) {
 1839                       fs.value = 4;
 1840                       fs.index = true;
 1841                   } else if (value.equals("large")) {
 1842                       fs.value = 5;
 1843                       fs.index = true;
 1844                   } else if (value.equals("x-large")) {
 1845                       fs.value = 6;
 1846                       fs.index = true;
 1847                   } else if (value.equals("xx-large")) {
 1848                       fs.value = 7;
 1849                       fs.index = true;
 1850                   } else {
 1851                       fs.lu = new LengthUnit(value, (short)1, 1f);
 1852                   }
 1853                   // relative sizes, larger | smaller (adjust from parent by
 1854                   // 1.5 pixels)
 1855                   // em, ex refer to parent sizes
 1856                   // lengths: pt, mm, cm, pc, in, px
 1857                   //          em (font height 3em would be 3 times font height)
 1858                   //          ex (height of X)
 1859                   // lengths are (+/-) followed by a number and two letter
 1860                   // unit identifier
 1861               } catch (NumberFormatException nfe) {
 1862                   fs = null;
 1863               }
 1864               return fs;
 1865           }
 1866   
 1867           Object parseHtmlValue(String value) {
 1868               if ((value == null) || (value.length() == 0)) {
 1869                   return null;
 1870               }
 1871               FontSize fs = new FontSize();
 1872               fs.svalue = value;
 1873   
 1874               try {
 1875                   /*
 1876                    * relative sizes in the size attribute are relative
 1877                    * to the <basefont>'s size.
 1878                    */
 1879                   int baseFontSize = getBaseFontSize();
 1880                   if (value.charAt(0) == '+') {
 1881                       int relSize = Integer.valueOf(value.substring(1)).intValue();
 1882                       fs.value = baseFontSize + relSize;
 1883                       fs.index = true;
 1884                   } else if (value.charAt(0) == '-') {
 1885                       int relSize = -Integer.valueOf(value.substring(1)).intValue();
 1886                       fs.value = baseFontSize + relSize;
 1887                       fs.index = true;
 1888                   } else {
 1889                       fs.value = Integer.parseInt(value);
 1890                       if (fs.value > 7) {
 1891                           fs.value = 7;
 1892                       } else if (fs.value < 0) {
 1893                           fs.value = 0;
 1894                       }
 1895                       fs.index = true;
 1896                   }
 1897   
 1898               } catch (NumberFormatException nfe) {
 1899                   fs = null;
 1900               }
 1901               return fs;
 1902           }
 1903   
 1904           /**
 1905            * Converts a <code>StyleConstants</code> attribute value to
 1906            * a CSS attribute value.  If there is no conversion
 1907            * returns <code>null</code>.  By default, there is no conversion.
 1908            *
 1909            * @param key the <code>StyleConstants</code> attribute
 1910            * @param value the value of a <code>StyleConstants</code>
 1911            *   attribute to be converted
 1912            * @return the CSS value that represents the
 1913            *   <code>StyleConstants</code> value
 1914            */
 1915           Object fromStyleConstants(StyleConstants key, Object value) {
 1916               if (value instanceof Number) {
 1917                   FontSize fs = new FontSize();
 1918   
 1919                   fs.value = getIndexOfSize(((Number)value).floatValue(), StyleSheet.sizeMapDefault);
 1920                   fs.svalue = Integer.toString((int)fs.value);
 1921                   fs.index = true;
 1922                   return fs;
 1923               }
 1924               return parseCssValue(value.toString());
 1925           }
 1926   
 1927           /**
 1928            * Converts a CSS attribute value to a <code>StyleConstants</code>
 1929            * value.  If there is no conversion, returns <code>null</code>.
 1930            * By default, there is no conversion.
 1931            *
 1932            * @param key the <code>StyleConstants</code> attribute
 1933            * @return the <code>StyleConstants</code> attribute value that
 1934            *   represents the CSS attribute value
 1935            */
 1936           Object toStyleConstants(StyleConstants key, View v) {
 1937               if (v != null) {
 1938                   return Integer.valueOf(getValue(v.getAttributes(), null));
 1939               }
 1940               return Integer.valueOf(getValue(null, null));
 1941           }
 1942   
 1943           float value;
 1944           boolean index;
 1945           LengthUnit lu;
 1946       }
 1947   
 1948       static class FontFamily extends CssValue {
 1949   
 1950           /**
 1951            * Returns the font family to use.
 1952            */
 1953           String getValue() {
 1954               return family;
 1955           }
 1956   
 1957           Object parseCssValue(String value) {
 1958               int cIndex = value.indexOf(',');
 1959               FontFamily ff = new FontFamily();
 1960               ff.svalue = value;
 1961               ff.family = null;
 1962   
 1963               if (cIndex == -1) {
 1964                   setFontName(ff, value);
 1965               }
 1966               else {
 1967                   boolean done = false;
 1968                   int lastIndex;
 1969                   int length = value.length();
 1970                   cIndex = 0;
 1971                   while (!done) {
 1972                       // skip ws.
 1973                       while (cIndex < length &&
 1974                              Character.isWhitespace(value.charAt(cIndex)))
 1975                           cIndex++;
 1976                       // Find next ','
 1977                       lastIndex = cIndex;
 1978                       cIndex = value.indexOf(',', cIndex);
 1979                       if (cIndex == -1) {
 1980                           cIndex = length;
 1981                       }
 1982                       if (lastIndex < length) {
 1983                           if (lastIndex != cIndex) {
 1984                               int lastCharIndex = cIndex;
 1985                               if (cIndex > 0 && value.charAt(cIndex - 1) == ' '){
 1986                                   lastCharIndex--;
 1987                               }
 1988                               setFontName(ff, value.substring
 1989                                           (lastIndex, lastCharIndex));
 1990                               done = (ff.family != null);
 1991                           }
 1992                           cIndex++;
 1993                       }
 1994                       else {
 1995                           done = true;
 1996                       }
 1997                   }
 1998               }
 1999               if (ff.family == null) {
 2000                   ff.family = Font.SANS_SERIF;
 2001               }
 2002               return ff;
 2003           }
 2004   
 2005           private void setFontName(FontFamily ff, String fontName) {
 2006               ff.family = fontName;
 2007           }
 2008   
 2009           Object parseHtmlValue(String value) {
 2010               // TBD
 2011               return parseCssValue(value);
 2012           }
 2013   
 2014           /**
 2015            * Converts a <code>StyleConstants</code> attribute value to
 2016            * a CSS attribute value.  If there is no conversion
 2017            * returns <code>null</code>.  By default, there is no conversion.
 2018            *
 2019            * @param key the <code>StyleConstants</code> attribute
 2020            * @param value the value of a <code>StyleConstants</code>
 2021            *   attribute to be converted
 2022            * @return the CSS value that represents the
 2023            *   <code>StyleConstants</code> value
 2024            */
 2025           Object fromStyleConstants(StyleConstants key, Object value) {
 2026               return parseCssValue(value.toString());
 2027           }
 2028   
 2029           /**
 2030            * Converts a CSS attribute value to a <code>StyleConstants</code>
 2031            * value.  If there is no conversion, returns <code>null</code>.
 2032            * By default, there is no conversion.
 2033            *
 2034            * @param key the <code>StyleConstants</code> attribute
 2035            * @return the <code>StyleConstants</code> attribute value that
 2036            *   represents the CSS attribute value
 2037            */
 2038           Object toStyleConstants(StyleConstants key, View v) {
 2039               return family;
 2040           }
 2041   
 2042           String family;
 2043       }
 2044   
 2045       static class FontWeight extends CssValue {
 2046   
 2047           int getValue() {
 2048               return weight;
 2049           }
 2050   
 2051           Object parseCssValue(String value) {
 2052               FontWeight fw = new FontWeight();
 2053               fw.svalue = value;
 2054               if (value.equals("bold")) {
 2055                   fw.weight = 700;
 2056               } else if (value.equals("normal")) {
 2057                   fw.weight = 400;
 2058               } else {
 2059                   // PENDING(prinz) add support for relative values
 2060                   try {
 2061                       fw.weight = Integer.parseInt(value);
 2062                   } catch (NumberFormatException nfe) {
 2063                       fw = null;
 2064                   }
 2065               }
 2066               return fw;
 2067           }
 2068   
 2069           /**
 2070            * Converts a <code>StyleConstants</code> attribute value to
 2071            * a CSS attribute value.  If there is no conversion
 2072            * returns <code>null</code>.  By default, there is no conversion.
 2073            *
 2074            * @param key the <code>StyleConstants</code> attribute
 2075            * @param value the value of a <code>StyleConstants</code>
 2076            *   attribute to be converted
 2077            * @return the CSS value that represents the
 2078            *   <code>StyleConstants</code> value
 2079            */
 2080           Object fromStyleConstants(StyleConstants key, Object value) {
 2081               if (value.equals(Boolean.TRUE)) {
 2082                   return parseCssValue("bold");
 2083               }
 2084               return parseCssValue("normal");
 2085           }
 2086   
 2087           /**
 2088            * Converts a CSS attribute value to a <code>StyleConstants</code>
 2089            * value.  If there is no conversion, returns <code>null</code>.
 2090            * By default, there is no conversion.
 2091            *
 2092            * @param key the <code>StyleConstants</code> attribute
 2093            * @return the <code>StyleConstants</code> attribute value that
 2094            *   represents the CSS attribute value
 2095            */
 2096           Object toStyleConstants(StyleConstants key, View v) {
 2097               return (weight > 500) ? Boolean.TRUE : Boolean.FALSE;
 2098           }
 2099   
 2100           boolean isBold() {
 2101               return (weight > 500);
 2102           }
 2103   
 2104           int weight;
 2105       }
 2106   
 2107       static class ColorValue extends CssValue {
 2108   
 2109           /**
 2110            * Returns the color to use.
 2111            */
 2112           Color getValue() {
 2113               return c;
 2114           }
 2115   
 2116           Object parseCssValue(String value) {
 2117   
 2118               Color c = stringToColor(value);
 2119               if (c != null) {
 2120                   ColorValue cv = new ColorValue();
 2121                   cv.svalue = value;
 2122                   cv.c = c;
 2123                   return cv;
 2124               }
 2125               return null;
 2126           }
 2127   
 2128           Object parseHtmlValue(String value) {
 2129               return parseCssValue(value);
 2130           }
 2131   
 2132           /**
 2133            * Converts a <code>StyleConstants</code> attribute value to
 2134            * a CSS attribute value.  If there is no conversion
 2135            * returns <code>null</code>.  By default, there is no conversion.
 2136            *
 2137            * @param key the <code>StyleConstants</code> attribute
 2138            * @param value the value of a <code>StyleConstants</code>
 2139            *   attribute to be converted
 2140            * @return the CSS value that represents the
 2141            *   <code>StyleConstants</code> value
 2142            */
 2143           Object fromStyleConstants(StyleConstants key, Object value) {
 2144               ColorValue colorValue = new ColorValue();
 2145               colorValue.c = (Color)value;
 2146               colorValue.svalue = colorToHex(colorValue.c);
 2147               return colorValue;
 2148           }
 2149   
 2150           /**
 2151            * Converts a CSS attribute value to a <code>StyleConstants</code>
 2152            * value.  If there is no conversion, returns <code>null</code>.
 2153            * By default, there is no conversion.
 2154            *
 2155            * @param key the <code>StyleConstants</code> attribute
 2156            * @return the <code>StyleConstants</code> attribute value that
 2157            *   represents the CSS attribute value
 2158            */
 2159           Object toStyleConstants(StyleConstants key, View v) {
 2160               return c;
 2161           }
 2162   
 2163           Color c;
 2164       }
 2165   
 2166       static class BorderStyle extends CssValue {
 2167   
 2168           CSS.Value getValue() {
 2169               return style;
 2170           }
 2171   
 2172           Object parseCssValue(String value) {
 2173               CSS.Value cssv = CSS.getValue(value);
 2174               if (cssv != null) {
 2175                   if ((cssv == CSS.Value.INSET) ||
 2176                       (cssv == CSS.Value.OUTSET) ||
 2177                       (cssv == CSS.Value.NONE) ||
 2178                       (cssv == CSS.Value.DOTTED) ||
 2179                       (cssv == CSS.Value.DASHED) ||
 2180                       (cssv == CSS.Value.SOLID) ||
 2181                       (cssv == CSS.Value.DOUBLE) ||
 2182                       (cssv == CSS.Value.GROOVE) ||
 2183                       (cssv == CSS.Value.RIDGE)) {
 2184   
 2185                       BorderStyle bs = new BorderStyle();
 2186                       bs.svalue = value;
 2187                       bs.style = cssv;
 2188                       return bs;
 2189                   }
 2190               }
 2191               return null;
 2192           }
 2193   
 2194           private void writeObject(java.io.ObjectOutputStream s)
 2195                        throws IOException {
 2196               s.defaultWriteObject();
 2197               if (style == null) {
 2198                   s.writeObject(null);
 2199               }
 2200               else {
 2201                   s.writeObject(style.toString());
 2202               }
 2203           }
 2204   
 2205           private void readObject(ObjectInputStream s)
 2206                   throws ClassNotFoundException, IOException {
 2207               s.defaultReadObject();
 2208               Object value = s.readObject();
 2209               if (value != null) {
 2210                   style = CSS.getValue((String)value);
 2211               }
 2212           }
 2213   
 2214           // CSS.Values are static, don't archive it.
 2215           transient private CSS.Value style;
 2216       }
 2217   
 2218       static class LengthValue extends CssValue {
 2219   
 2220           /**
 2221            * if this length value may be negative.
 2222            */
 2223           boolean mayBeNegative;
 2224   
 2225           LengthValue() {
 2226               this(false);
 2227           }
 2228   
 2229           LengthValue(boolean mayBeNegative) {
 2230               this.mayBeNegative = mayBeNegative;
 2231           }
 2232   
 2233           /**
 2234            * Returns the length (span) to use.
 2235            */
 2236           float getValue() {
 2237               return getValue(false);
 2238           }
 2239   
 2240           float getValue(boolean isW3CLengthUnits) {
 2241               return getValue(0, isW3CLengthUnits);
 2242           }
 2243   
 2244           /**
 2245            * Returns the length (span) to use. If the value represents
 2246            * a percentage, it is scaled based on <code>currentValue</code>.
 2247            */
 2248           float getValue(float currentValue) {
 2249               return getValue(currentValue, false);
 2250           }
 2251           float getValue(float currentValue, boolean isW3CLengthUnits) {
 2252               if (percentage) {
 2253                   return span * currentValue;
 2254               }
 2255               return LengthUnit.getValue(span, units, isW3CLengthUnits);
 2256           }
 2257   
 2258           /**
 2259            * Returns true if the length represents a percentage of the
 2260            * containing box.
 2261            */
 2262           boolean isPercentage() {
 2263               return percentage;
 2264           }
 2265   
 2266           Object parseCssValue(String value) {
 2267               LengthValue lv;
 2268               try {
 2269                   // Assume pixels
 2270                   float absolute = Float.valueOf(value).floatValue();
 2271                   lv = new LengthValue();
 2272                   lv.span = absolute;
 2273               } catch (NumberFormatException nfe) {
 2274                   // Not pixels, use LengthUnit
 2275                   LengthUnit lu = new LengthUnit(value,
 2276                                                  LengthUnit.UNINITALIZED_LENGTH,
 2277                                                  0);
 2278   
 2279                   // PENDING: currently, we only support absolute values and
 2280                   // percentages.
 2281                   switch (lu.type) {
 2282                   case 0:
 2283                       // Absolute
 2284                       lv = new LengthValue();
 2285                       lv.span =
 2286                           (mayBeNegative) ? lu.value : Math.max(0, lu.value);
 2287                       lv.units = lu.units;
 2288                       break;
 2289                   case 1:
 2290                       // %
 2291                       lv = new LengthValue();
 2292                       lv.span = Math.max(0, Math.min(1, lu.value));
 2293                       lv.percentage = true;
 2294                       break;
 2295                   default:
 2296                       return null;
 2297                   }
 2298               }
 2299               lv.svalue = value;
 2300               return lv;
 2301           }
 2302   
 2303           Object parseHtmlValue(String value) {
 2304               if (value.equals(HTML.NULL_ATTRIBUTE_VALUE)) {
 2305                   value = "1";
 2306               }
 2307               return parseCssValue(value);
 2308           }
 2309           /**
 2310            * Converts a <code>StyleConstants</code> attribute value to
 2311            * a CSS attribute value.  If there is no conversion,
 2312            * returns <code>null</code>.  By default, there is no conversion.
 2313            *
 2314            * @param key the <code>StyleConstants</code> attribute
 2315            * @param value the value of a <code>StyleConstants</code>
 2316            *   attribute to be converted
 2317            * @return the CSS value that represents the
 2318            *   <code>StyleConstants</code> value
 2319            */
 2320           Object fromStyleConstants(StyleConstants key, Object value) {
 2321               LengthValue v = new LengthValue();
 2322               v.svalue = value.toString();
 2323               v.span = ((Float)value).floatValue();
 2324               return v;
 2325           }
 2326   
 2327           /**
 2328            * Converts a CSS attribute value to a <code>StyleConstants</code>
 2329            * value.  If there is no conversion, returns <code>null</code>.
 2330            * By default, there is no conversion.
 2331            *
 2332            * @param key the <code>StyleConstants</code> attribute
 2333            * @return the <code>StyleConstants</code> attribute value that
 2334            *   represents the CSS attribute value
 2335            */
 2336           Object toStyleConstants(StyleConstants key, View v) {
 2337               return new Float(getValue(false));
 2338           }
 2339   
 2340           /** If true, span is a percentage value, and that to determine
 2341            * the length another value needs to be passed in. */
 2342           boolean percentage;
 2343           /** Either the absolute value (percentage == false) or
 2344            * a percentage value. */
 2345           float span;
 2346   
 2347           String units = null;
 2348       }
 2349   
 2350   
 2351       /**
 2352        * BorderWidthValue is used to model BORDER_XXX_WIDTH and adds support
 2353        * for the thin/medium/thick values.
 2354        */
 2355       static class BorderWidthV