CSS.java revision 11099:678faa7d1a6a
1/*
2 * Copyright (c) 1998, 2014, Oracle and/or its affiliates. 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package javax.swing.text.html;
26
27import java.awt.Color;
28import java.awt.Font;
29import java.awt.GraphicsEnvironment;
30import java.awt.Toolkit;
31import java.awt.HeadlessException;
32import java.awt.Image;
33import java.io.*;
34import java.lang.reflect.Method;
35import java.net.URL;
36import java.net.MalformedURLException;
37import java.util.Enumeration;
38import java.util.Hashtable;
39import java.util.Vector;
40import java.util.Locale;
41import javax.swing.ImageIcon;
42import javax.swing.SizeRequirements;
43import 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 supported 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>text-decoration (with the exception of blink and overline)
66 *   <li>vertical-align (only sup and super)
67 *   <li>text-align (justify is treated as center)
68 *   <li>margin-top
69 *   <li>margin-right
70 *   <li>margin-bottom
71 *   <li>margin-left
72 *   <li>margin
73 *   <li>padding-top
74 *   <li>padding-right
75 *   <li>padding-bottom
76 *   <li>padding-left
77 *   <li>padding
78 *   <li>border-top-style
79 *   <li>border-right-style
80 *   <li>border-bottom-style
81 *   <li>border-left-style
82 *   <li>border-style (only supports inset, outset and none)
83 *   <li>border-top-color
84 *   <li>border-right-color
85 *   <li>border-bottom-color
86 *   <li>border-left-color
87 *   <li>border-color
88 *   <li>list-style-image
89 *   <li>list-style-type
90 *   <li>list-style-position
91 * </ul>
92 * The following are modeled, but currently not rendered.
93 * <ul><li>font-variant
94 *   <li>background-attachment (background always treated as scroll)
95 *   <li>word-spacing
96 *   <li>letter-spacing
97 *   <li>text-indent
98 *   <li>text-transform
99 *   <li>line-height
100 *   <li>border-top-width (this is used to indicate if a border should be used)
101 *   <li>border-right-width
102 *   <li>border-bottom-width
103 *   <li>border-left-width
104 *   <li>border-width
105 *   <li>border-top
106 *   <li>border-right
107 *   <li>border-bottom
108 *   <li>border-left
109 *   <li>border
110 *   <li>width
111 *   <li>height
112 *   <li>float
113 *   <li>clear
114 *   <li>display
115 *   <li>white-space
116 *   <li>list-style
117 * </ul>
118 * <p><b>Note: for the time being we do not fully support relative units,
119 * unless noted, so that
120 * p { margin-top: 10% } will be treated as if no margin-top was specified.</b>
121 *
122 * @author  Timothy Prinzing
123 * @author  Scott Violet
124 * @see StyleSheet
125 */
126@SuppressWarnings("serial") // Same-version serialization only
127public class CSS implements Serializable {
128
129    /**
130     * Definitions to be used as a key on AttributeSet's
131     * that might hold CSS attributes.  Since this is a
132     * closed set (i.e. defined exactly by the specification),
133     * it is final and cannot be extended.
134     */
135    public static final class Attribute {
136
137        private Attribute(String name, String defaultValue, boolean inherited) {
138            this.name = name;
139            this.defaultValue = defaultValue;
140            this.inherited = inherited;
141        }
142
143        /**
144         * The string representation of the attribute.  This
145         * should exactly match the string specified in the
146         * CSS specification.
147         */
148        public String toString() {
149            return name;
150        }
151
152        /**
153         * Fetch the default value for the attribute.
154         * If there is no default value (such as for
155         * composite attributes), null will be returned.
156         *
157         * @return default value for the attribute
158         */
159        public String getDefaultValue() {
160            return defaultValue;
161        }
162
163        /**
164         * Indicates if the attribute should be inherited
165         * from the parent or not.
166         *
167         * @return true if the attribute should be inherited from the parent
168         */
169        public boolean isInherited() {
170            return inherited;
171        }
172
173        private String name;
174        private String defaultValue;
175        private boolean inherited;
176
177
178        /**
179         * CSS attribute "background".
180         */
181        public static final Attribute BACKGROUND =
182            new Attribute("background", null, false);
183
184        /**
185         * CSS attribute "background-attachment".
186         */
187        public static final Attribute BACKGROUND_ATTACHMENT =
188            new Attribute("background-attachment", "scroll", false);
189
190        /**
191         * CSS attribute "background-color".
192         */
193        public static final Attribute BACKGROUND_COLOR =
194            new Attribute("background-color", "transparent", false);
195
196        /**
197         * CSS attribute "background-image".
198         */
199        public static final Attribute BACKGROUND_IMAGE =
200            new Attribute("background-image", "none", false);
201
202        /**
203         * CSS attribute "background-position".
204         */
205        public static final Attribute BACKGROUND_POSITION =
206            new Attribute("background-position", null, false);
207
208        /**
209         * CSS attribute "background-repeat".
210         */
211        public static final Attribute BACKGROUND_REPEAT =
212            new Attribute("background-repeat", "repeat", false);
213
214        /**
215         * CSS attribute "border".
216         */
217        public static final Attribute BORDER =
218            new Attribute("border", null, false);
219
220        /**
221         * CSS attribute "border-bottom".
222         */
223        public static final Attribute BORDER_BOTTOM =
224            new Attribute("border-bottom", null, false);
225
226        /**
227         * CSS attribute "border-bottom-color".
228         */
229        public static final Attribute BORDER_BOTTOM_COLOR =
230            new Attribute("border-bottom-color", null, false);
231
232        /**
233         * CSS attribute "border-bottom-style".
234         */
235        public static final Attribute BORDER_BOTTOM_STYLE =
236            new Attribute("border-bottom-style", "none", false);
237
238        /**
239         * CSS attribute "border-bottom-width".
240         */
241        public static final Attribute BORDER_BOTTOM_WIDTH =
242            new Attribute("border-bottom-width", "medium", false);
243
244        /**
245         * CSS attribute "border-color".
246         */
247        public static final Attribute BORDER_COLOR =
248            new Attribute("border-color", null, false);
249
250        /**
251         * CSS attribute "border-left".
252         */
253        public static final Attribute BORDER_LEFT =
254            new Attribute("border-left", null, false);
255
256        /**
257         * CSS attribute "margin-right".
258         */
259        public static final Attribute BORDER_LEFT_COLOR =
260            new Attribute("border-left-color", null, false);
261
262        /**
263         * CSS attribute "border-left-style".
264         */
265        public static final Attribute BORDER_LEFT_STYLE =
266            new Attribute("border-left-style", "none", false);
267
268        /**
269         * CSS attribute "border-left-width".
270         */
271        public static final Attribute BORDER_LEFT_WIDTH =
272            new Attribute("border-left-width", "medium", false);
273
274        /**
275         * CSS attribute "border-right".
276         */
277        public static final Attribute BORDER_RIGHT =
278            new Attribute("border-right", null, false);
279
280        /**
281         * CSS attribute "border-right-color".
282         */
283        public static final Attribute BORDER_RIGHT_COLOR =
284            new Attribute("border-right-color", null, false);
285
286        /**
287         * CSS attribute "border-right-style".
288         */
289        public static final Attribute BORDER_RIGHT_STYLE =
290            new Attribute("border-right-style", "none", false);
291
292        /**
293         * CSS attribute "border-right-width".
294         */
295        public static final Attribute BORDER_RIGHT_WIDTH =
296            new Attribute("border-right-width", "medium", false);
297
298        /**
299         * CSS attribute "border-style".
300         */
301        public static final Attribute BORDER_STYLE =
302            new Attribute("border-style", "none", false);
303
304        /**
305         * CSS attribute "border-top".
306         */
307        public static final Attribute BORDER_TOP =
308            new Attribute("border-top", null, false);
309
310        /**
311         * CSS attribute "border-top-color".
312         */
313        public static final Attribute BORDER_TOP_COLOR =
314            new Attribute("border-top-color", null, false);
315
316        /**
317         * CSS attribute "border-top-style".
318         */
319        public static final Attribute BORDER_TOP_STYLE =
320            new Attribute("border-top-style", "none", false);
321
322        /**
323         * CSS attribute "border-top-width".
324         */
325        public static final Attribute BORDER_TOP_WIDTH =
326            new Attribute("border-top-width", "medium", false);
327
328        /**
329         * CSS attribute "border-width".
330         */
331        public static final Attribute BORDER_WIDTH =
332            new Attribute("border-width", "medium", false);
333
334        /**
335         * CSS attribute "clear".
336         */
337        public static final Attribute CLEAR =
338            new Attribute("clear", "none", false);
339
340        /**
341         * CSS attribute "color".
342         */
343        public static final Attribute COLOR =
344            new Attribute("color", "black", true);
345
346        /**
347         * CSS attribute "display".
348         */
349        public static final Attribute DISPLAY =
350            new Attribute("display", "block", false);
351
352        /**
353         * CSS attribute "float".
354         */
355        public static final Attribute FLOAT =
356            new Attribute("float", "none", false);
357
358        /**
359         * CSS attribute "font".
360         */
361        public static final Attribute FONT =
362            new Attribute("font", null, true);
363
364        /**
365         * CSS attribute "font-family".
366         */
367        public static final Attribute FONT_FAMILY =
368            new Attribute("font-family", null, true);
369
370        /**
371         * CSS attribute "font-size".
372         */
373        public static final Attribute FONT_SIZE =
374            new Attribute("font-size", "medium", true);
375
376        /**
377         * CSS attribute "font-style".
378         */
379        public static final Attribute FONT_STYLE =
380            new Attribute("font-style", "normal", true);
381
382        /**
383         * CSS attribute "font-variant".
384         */
385        public static final Attribute FONT_VARIANT =
386            new Attribute("font-variant", "normal", true);
387
388        /**
389         * CSS attribute "font-weight".
390         */
391        public static final Attribute FONT_WEIGHT =
392            new Attribute("font-weight", "normal", true);
393
394        /**
395         * CSS attribute "height".
396         */
397        public static final Attribute HEIGHT =
398            new Attribute("height", "auto", false);
399
400        /**
401         * CSS attribute "letter-spacing".
402         */
403        public static final Attribute LETTER_SPACING =
404            new Attribute("letter-spacing", "normal", true);
405
406        /**
407         * CSS attribute "line-height".
408         */
409        public static final Attribute LINE_HEIGHT =
410            new Attribute("line-height", "normal", true);
411
412        /**
413         * CSS attribute "list-style".
414         */
415        public static final Attribute LIST_STYLE =
416            new Attribute("list-style", null, true);
417
418        /**
419         * CSS attribute "list-style-image".
420         */
421        public static final Attribute LIST_STYLE_IMAGE =
422            new Attribute("list-style-image", "none", true);
423
424        /**
425         * CSS attribute "list-style-position".
426         */
427        public static final Attribute LIST_STYLE_POSITION =
428            new Attribute("list-style-position", "outside", true);
429
430        /**
431         * CSS attribute "list-style-type".
432         */
433        public static final Attribute LIST_STYLE_TYPE =
434            new Attribute("list-style-type", "disc", true);
435
436        /**
437         * CSS attribute "margin".
438         */
439        public static final Attribute MARGIN =
440            new Attribute("margin", null, false);
441
442        /**
443         * CSS attribute "margin-bottom".
444         */
445        public static final Attribute MARGIN_BOTTOM =
446            new Attribute("margin-bottom", "0", false);
447
448        /**
449         * CSS attribute "margin-left".
450         */
451        public static final Attribute MARGIN_LEFT =
452            new Attribute("margin-left", "0", false);
453
454        /**
455         * CSS attribute "margin-right".
456         */
457        public static final Attribute MARGIN_RIGHT =
458            new Attribute("margin-right", "0", false);
459
460        /*
461         * made up css attributes to describe orientation depended
462         * margins. used for <dir>, <menu>, <ul> etc. see
463         * 5088268 for more details
464         */
465        static final Attribute MARGIN_LEFT_LTR =
466            new Attribute("margin-left-ltr",
467                          Integer.toString(Integer.MIN_VALUE), false);
468
469        static final Attribute MARGIN_LEFT_RTL =
470            new Attribute("margin-left-rtl",
471                          Integer.toString(Integer.MIN_VALUE), false);
472
473        static final Attribute MARGIN_RIGHT_LTR =
474            new Attribute("margin-right-ltr",
475                          Integer.toString(Integer.MIN_VALUE), false);
476
477        static final Attribute MARGIN_RIGHT_RTL =
478            new Attribute("margin-right-rtl",
479                          Integer.toString(Integer.MIN_VALUE), false);
480
481
482        /**
483         * CSS attribute "margin-top".
484         */
485        public static final Attribute MARGIN_TOP =
486            new Attribute("margin-top", "0", false);
487
488        /**
489         * CSS attribute "padding".
490         */
491        public static final Attribute PADDING =
492            new Attribute("padding", null, false);
493
494        /**
495         * CSS attribute "padding-bottom".
496         */
497        public static final Attribute PADDING_BOTTOM =
498            new Attribute("padding-bottom", "0", false);
499
500        /**
501         * CSS attribute "padding-left".
502         */
503        public static final Attribute PADDING_LEFT =
504            new Attribute("padding-left", "0", false);
505
506        /**
507         * CSS attribute "padding-right".
508         */
509        public static final Attribute PADDING_RIGHT =
510            new Attribute("padding-right", "0", false);
511
512        /**
513         * CSS attribute "padding-top".
514         */
515        public static final Attribute PADDING_TOP =
516            new Attribute("padding-top", "0", false);
517
518        /**
519         * CSS attribute "text-align".
520         */
521        public static final Attribute TEXT_ALIGN =
522            new Attribute("text-align", null, true);
523
524        /**
525         * CSS attribute "text-decoration".
526         */
527        public static final Attribute TEXT_DECORATION =
528            new Attribute("text-decoration", "none", true);
529
530        /**
531         * CSS attribute "text-indent".
532         */
533        public static final Attribute TEXT_INDENT =
534            new Attribute("text-indent", "0", true);
535
536        /**
537         * CSS attribute "text-transform".
538         */
539        public static final Attribute TEXT_TRANSFORM =
540            new Attribute("text-transform", "none", true);
541
542        /**
543         * CSS attribute "vertical-align".
544         */
545        public static final Attribute VERTICAL_ALIGN =
546            new Attribute("vertical-align", "baseline", false);
547
548        /**
549         * CSS attribute "word-spacing".
550         */
551        public static final Attribute WORD_SPACING =
552            new Attribute("word-spacing", "normal", true);
553
554        /**
555         * CSS attribute "white-space".
556         */
557        public static final Attribute WHITE_SPACE =
558            new Attribute("white-space", "normal", true);
559
560        /**
561         * CSS attribute "width".
562         */
563        public static final Attribute WIDTH =
564            new Attribute("width", "auto", false);
565
566        /*public*/ static final Attribute BORDER_SPACING =
567            new Attribute("border-spacing", "0", true);
568
569        /*public*/ static final Attribute CAPTION_SIDE =
570            new Attribute("caption-side", "left", true);
571
572        // All possible CSS attribute keys.
573        static final Attribute[] allAttributes = {
574            BACKGROUND, BACKGROUND_ATTACHMENT, BACKGROUND_COLOR,
575            BACKGROUND_IMAGE, BACKGROUND_POSITION, BACKGROUND_REPEAT,
576            BORDER, BORDER_BOTTOM, BORDER_BOTTOM_WIDTH, BORDER_COLOR,
577            BORDER_LEFT, BORDER_LEFT_WIDTH, BORDER_RIGHT, BORDER_RIGHT_WIDTH,
578            BORDER_STYLE, BORDER_TOP, BORDER_TOP_WIDTH, BORDER_WIDTH,
579            BORDER_TOP_STYLE, BORDER_RIGHT_STYLE, BORDER_BOTTOM_STYLE,
580            BORDER_LEFT_STYLE,
581            BORDER_TOP_COLOR, BORDER_RIGHT_COLOR, BORDER_BOTTOM_COLOR,
582            BORDER_LEFT_COLOR,
583            CLEAR, COLOR, DISPLAY, FLOAT, FONT, FONT_FAMILY, FONT_SIZE,
584            FONT_STYLE, FONT_VARIANT, FONT_WEIGHT, HEIGHT, LETTER_SPACING,
585            LINE_HEIGHT, LIST_STYLE, LIST_STYLE_IMAGE, LIST_STYLE_POSITION,
586            LIST_STYLE_TYPE, MARGIN, MARGIN_BOTTOM, MARGIN_LEFT, MARGIN_RIGHT,
587            MARGIN_TOP, PADDING, PADDING_BOTTOM, PADDING_LEFT, PADDING_RIGHT,
588            PADDING_TOP, TEXT_ALIGN, TEXT_DECORATION, TEXT_INDENT, TEXT_TRANSFORM,
589            VERTICAL_ALIGN, WORD_SPACING, WHITE_SPACE, WIDTH,
590            BORDER_SPACING, CAPTION_SIDE,
591            MARGIN_LEFT_LTR, MARGIN_LEFT_RTL, MARGIN_RIGHT_LTR, MARGIN_RIGHT_RTL
592        };
593
594        private static final Attribute[] ALL_MARGINS =
595                { MARGIN_TOP, MARGIN_RIGHT, MARGIN_BOTTOM, MARGIN_LEFT };
596        private static final Attribute[] ALL_PADDING =
597                { PADDING_TOP, PADDING_RIGHT, PADDING_BOTTOM, PADDING_LEFT };
598        private static final Attribute[] ALL_BORDER_WIDTHS =
599                { BORDER_TOP_WIDTH, BORDER_RIGHT_WIDTH, BORDER_BOTTOM_WIDTH,
600                  BORDER_LEFT_WIDTH };
601        private static final Attribute[] ALL_BORDER_STYLES =
602                { BORDER_TOP_STYLE, BORDER_RIGHT_STYLE, BORDER_BOTTOM_STYLE,
603                  BORDER_LEFT_STYLE };
604        private static final Attribute[] ALL_BORDER_COLORS =
605                { BORDER_TOP_COLOR, BORDER_RIGHT_COLOR, BORDER_BOTTOM_COLOR,
606                  BORDER_LEFT_COLOR };
607
608    }
609
610    static final class Value {
611
612        private Value(String name) {
613            this.name = name;
614        }
615
616        /**
617         * The string representation of the attribute.  This
618         * should exactly match the string specified in the
619         * CSS specification.
620         */
621        public String toString() {
622            return name;
623        }
624
625        static final Value INHERITED = new Value("inherited");
626        static final Value NONE = new Value("none");
627        static final Value HIDDEN = new Value("hidden");
628        static final Value DOTTED = new Value("dotted");
629        static final Value DASHED = new Value("dashed");
630        static final Value SOLID = new Value("solid");
631        static final Value DOUBLE = new Value("double");
632        static final Value GROOVE = new Value("groove");
633        static final Value RIDGE = new Value("ridge");
634        static final Value INSET = new Value("inset");
635        static final Value OUTSET = new Value("outset");
636        // Lists.
637        static final Value DISC = new Value("disc");
638        static final Value CIRCLE = new Value("circle");
639        static final Value SQUARE = new Value("square");
640        static final Value DECIMAL = new Value("decimal");
641        static final Value LOWER_ROMAN = new Value("lower-roman");
642        static final Value UPPER_ROMAN = new Value("upper-roman");
643        static final Value LOWER_ALPHA = new Value("lower-alpha");
644        static final Value UPPER_ALPHA = new Value("upper-alpha");
645        // background-repeat
646        static final Value BACKGROUND_NO_REPEAT = new Value("no-repeat");
647        static final Value BACKGROUND_REPEAT = new Value("repeat");
648        static final Value BACKGROUND_REPEAT_X = new Value("repeat-x");
649        static final Value BACKGROUND_REPEAT_Y = new Value("repeat-y");
650        // background-attachment
651        static final Value BACKGROUND_SCROLL = new Value("scroll");
652        static final Value BACKGROUND_FIXED = new Value("fixed");
653
654        private String name;
655
656        static final Value[] allValues = {
657            INHERITED, NONE, DOTTED, DASHED, SOLID, DOUBLE, GROOVE,
658            RIDGE, INSET, OUTSET, DISC, CIRCLE, SQUARE, DECIMAL,
659            LOWER_ROMAN, UPPER_ROMAN, LOWER_ALPHA, UPPER_ALPHA,
660            BACKGROUND_NO_REPEAT, BACKGROUND_REPEAT,
661            BACKGROUND_REPEAT_X, BACKGROUND_REPEAT_Y,
662            BACKGROUND_FIXED, BACKGROUND_FIXED
663        };
664    }
665
666    /**
667     * Constructs a CSS object.
668     */
669    public CSS() {
670        baseFontSize = baseFontSizeIndex + 1;
671        // setup the css conversion table
672        valueConvertor = new Hashtable<Object, Object>();
673        valueConvertor.put(CSS.Attribute.FONT_SIZE, new FontSize());
674        valueConvertor.put(CSS.Attribute.FONT_FAMILY, new FontFamily());
675        valueConvertor.put(CSS.Attribute.FONT_WEIGHT, new FontWeight());
676        Object bs = new BorderStyle();
677        valueConvertor.put(CSS.Attribute.BORDER_TOP_STYLE, bs);
678        valueConvertor.put(CSS.Attribute.BORDER_RIGHT_STYLE, bs);
679        valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_STYLE, bs);
680        valueConvertor.put(CSS.Attribute.BORDER_LEFT_STYLE, bs);
681        Object cv = new ColorValue();
682        valueConvertor.put(CSS.Attribute.COLOR, cv);
683        valueConvertor.put(CSS.Attribute.BACKGROUND_COLOR, cv);
684        valueConvertor.put(CSS.Attribute.BORDER_TOP_COLOR, cv);
685        valueConvertor.put(CSS.Attribute.BORDER_RIGHT_COLOR, cv);
686        valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_COLOR, cv);
687        valueConvertor.put(CSS.Attribute.BORDER_LEFT_COLOR, cv);
688        Object lv = new LengthValue();
689        valueConvertor.put(CSS.Attribute.MARGIN_TOP, lv);
690        valueConvertor.put(CSS.Attribute.MARGIN_BOTTOM, lv);
691        valueConvertor.put(CSS.Attribute.MARGIN_LEFT, lv);
692        valueConvertor.put(CSS.Attribute.MARGIN_LEFT_LTR, lv);
693        valueConvertor.put(CSS.Attribute.MARGIN_LEFT_RTL, lv);
694        valueConvertor.put(CSS.Attribute.MARGIN_RIGHT, lv);
695        valueConvertor.put(CSS.Attribute.MARGIN_RIGHT_LTR, lv);
696        valueConvertor.put(CSS.Attribute.MARGIN_RIGHT_RTL, lv);
697        valueConvertor.put(CSS.Attribute.PADDING_TOP, lv);
698        valueConvertor.put(CSS.Attribute.PADDING_BOTTOM, lv);
699        valueConvertor.put(CSS.Attribute.PADDING_LEFT, lv);
700        valueConvertor.put(CSS.Attribute.PADDING_RIGHT, lv);
701        Object bv = new BorderWidthValue(null, 0);
702        valueConvertor.put(CSS.Attribute.BORDER_TOP_WIDTH, bv);
703        valueConvertor.put(CSS.Attribute.BORDER_BOTTOM_WIDTH, bv);
704        valueConvertor.put(CSS.Attribute.BORDER_LEFT_WIDTH, bv);
705        valueConvertor.put(CSS.Attribute.BORDER_RIGHT_WIDTH, bv);
706        Object nlv = new LengthValue(true);
707        valueConvertor.put(CSS.Attribute.TEXT_INDENT, nlv);
708        valueConvertor.put(CSS.Attribute.WIDTH, lv);
709        valueConvertor.put(CSS.Attribute.HEIGHT, lv);
710        valueConvertor.put(CSS.Attribute.BORDER_SPACING, lv);
711        Object sv = new StringValue();
712        valueConvertor.put(CSS.Attribute.FONT_STYLE, sv);
713        valueConvertor.put(CSS.Attribute.TEXT_DECORATION, sv);
714        valueConvertor.put(CSS.Attribute.TEXT_ALIGN, sv);
715        valueConvertor.put(CSS.Attribute.VERTICAL_ALIGN, sv);
716        Object valueMapper = new CssValueMapper();
717        valueConvertor.put(CSS.Attribute.LIST_STYLE_TYPE,
718                           valueMapper);
719        valueConvertor.put(CSS.Attribute.BACKGROUND_IMAGE,
720                           new BackgroundImage());
721        valueConvertor.put(CSS.Attribute.BACKGROUND_POSITION,
722                           new BackgroundPosition());
723        valueConvertor.put(CSS.Attribute.BACKGROUND_REPEAT,
724                           valueMapper);
725        valueConvertor.put(CSS.Attribute.BACKGROUND_ATTACHMENT,
726                           valueMapper);
727        Object generic = new CssValue();
728        int n = CSS.Attribute.allAttributes.length;
729        for (int i = 0; i < n; i++) {
730            CSS.Attribute key = CSS.Attribute.allAttributes[i];
731            if (valueConvertor.get(key) == null) {
732                valueConvertor.put(key, generic);
733            }
734        }
735    }
736
737    /**
738     * Sets the base font size. <code>sz</code> is a CSS value, and is
739     * not necessarily the point size. Use getPointSize to determine the
740     * point size corresponding to <code>sz</code>.
741     */
742    void setBaseFontSize(int sz) {
743        if (sz < 1)
744          baseFontSize = 0;
745        else if (sz > 7)
746          baseFontSize = 7;
747        else
748          baseFontSize = sz;
749    }
750
751    /**
752     * Sets the base font size from the passed in string.
753     */
754    void setBaseFontSize(String size) {
755        int relSize, absSize, diff;
756
757        if (size != null) {
758            if (size.startsWith("+")) {
759                relSize = Integer.valueOf(size.substring(1)).intValue();
760                setBaseFontSize(baseFontSize + relSize);
761            } else if (size.startsWith("-")) {
762                relSize = -Integer.valueOf(size.substring(1)).intValue();
763                setBaseFontSize(baseFontSize + relSize);
764            } else {
765                setBaseFontSize(Integer.valueOf(size).intValue());
766            }
767        }
768    }
769
770    /**
771     * Returns the base font size.
772     */
773    int getBaseFontSize() {
774        return baseFontSize;
775    }
776
777    /**
778     * Parses the CSS property <code>key</code> with value
779     * <code>value</code> placing the result in <code>att</code>.
780     */
781    void addInternalCSSValue(MutableAttributeSet attr,
782                             CSS.Attribute key, String value) {
783        if (key == CSS.Attribute.FONT) {
784            ShorthandFontParser.parseShorthandFont(this, value, attr);
785        }
786        else if (key == CSS.Attribute.BACKGROUND) {
787            ShorthandBackgroundParser.parseShorthandBackground
788                               (this, value, attr);
789        }
790        else if (key == CSS.Attribute.MARGIN) {
791            ShorthandMarginParser.parseShorthandMargin(this, value, attr,
792                                           CSS.Attribute.ALL_MARGINS);
793        }
794        else if (key == CSS.Attribute.PADDING) {
795            ShorthandMarginParser.parseShorthandMargin(this, value, attr,
796                                           CSS.Attribute.ALL_PADDING);
797        }
798        else if (key == CSS.Attribute.BORDER_WIDTH) {
799            ShorthandMarginParser.parseShorthandMargin(this, value, attr,
800                                           CSS.Attribute.ALL_BORDER_WIDTHS);
801        }
802        else if (key == CSS.Attribute.BORDER_COLOR) {
803            ShorthandMarginParser.parseShorthandMargin(this, value, attr,
804                                            CSS.Attribute.ALL_BORDER_COLORS);
805        }
806        else if (key == CSS.Attribute.BORDER_STYLE) {
807            ShorthandMarginParser.parseShorthandMargin(this, value, attr,
808                                            CSS.Attribute.ALL_BORDER_STYLES);
809        }
810        else if ((key == CSS.Attribute.BORDER) ||
811                   (key == CSS.Attribute.BORDER_TOP) ||
812                   (key == CSS.Attribute.BORDER_RIGHT) ||
813                   (key == CSS.Attribute.BORDER_BOTTOM) ||
814                   (key == CSS.Attribute.BORDER_LEFT)) {
815            ShorthandBorderParser.parseShorthandBorder(attr, key, value);
816        }
817        else {
818            Object iValue = getInternalCSSValue(key, value);
819            if (iValue != null) {
820                attr.addAttribute(key, iValue);
821            }
822        }
823    }
824
825    /**
826     * Gets the internal CSS representation of <code>value</code> which is
827     * a CSS value of the CSS attribute named <code>key</code>. The receiver
828     * should not modify <code>value</code>, and the first <code>count</code>
829     * strings are valid.
830     */
831    Object getInternalCSSValue(CSS.Attribute key, String value) {
832        CssValue conv = (CssValue) valueConvertor.get(key);
833        Object r = conv.parseCssValue(value);
834        return r != null ? r : conv.parseCssValue(key.getDefaultValue());
835    }
836
837    /**
838     * Maps from a StyleConstants to a CSS Attribute.
839     */
840    Attribute styleConstantsKeyToCSSKey(StyleConstants sc) {
841        return styleConstantToCssMap.get(sc);
842    }
843
844    /**
845     * Maps from a StyleConstants value to a CSS value.
846     */
847    Object styleConstantsValueToCSSValue(StyleConstants sc,
848                                         Object styleValue) {
849        Attribute cssKey = styleConstantsKeyToCSSKey(sc);
850        if (cssKey != null) {
851            CssValue conv = (CssValue)valueConvertor.get(cssKey);
852            return conv.fromStyleConstants(sc, styleValue);
853        }
854        return null;
855    }
856
857    /**
858     * Converts the passed in CSS value to a StyleConstants value.
859     * <code>key</code> identifies the CSS attribute being mapped.
860     */
861    Object cssValueToStyleConstantsValue(StyleConstants key, Object value) {
862        if (value instanceof CssValue) {
863            return ((CssValue)value).toStyleConstants(key, null);
864        }
865        return null;
866    }
867
868    /**
869     * Returns the font for the values in the passed in AttributeSet.
870     * It is assumed the keys will be CSS.Attribute keys.
871     * <code>sc</code> is the StyleContext that will be messaged to get
872     * the font once the size, name and style have been determined.
873     */
874    Font getFont(StyleContext sc, AttributeSet a, int defaultSize, StyleSheet ss) {
875        ss = getStyleSheet(ss);
876        int size = getFontSize(a, defaultSize, ss);
877
878        /*
879         * If the vertical alignment is set to either superscript or
880         * subscript we reduce the font size by 2 points.
881         */
882        StringValue vAlignV = (StringValue)a.getAttribute
883                              (CSS.Attribute.VERTICAL_ALIGN);
884        if ((vAlignV != null)) {
885            String vAlign = vAlignV.toString();
886            if ((vAlign.indexOf("sup") >= 0) ||
887                (vAlign.indexOf("sub") >= 0)) {
888                size -= 2;
889            }
890        }
891
892        FontFamily familyValue = (FontFamily)a.getAttribute
893                                            (CSS.Attribute.FONT_FAMILY);
894        String family = (familyValue != null) ? familyValue.getValue() :
895                                  Font.SANS_SERIF;
896        int style = Font.PLAIN;
897        FontWeight weightValue = (FontWeight) a.getAttribute
898                                  (CSS.Attribute.FONT_WEIGHT);
899        if ((weightValue != null) && (weightValue.getValue() > 400)) {
900            style |= Font.BOLD;
901        }
902        Object fs = a.getAttribute(CSS.Attribute.FONT_STYLE);
903        if ((fs != null) && (fs.toString().indexOf("italic") >= 0)) {
904            style |= Font.ITALIC;
905        }
906        if (family.equalsIgnoreCase("monospace")) {
907            family = Font.MONOSPACED;
908        }
909        Font f = sc.getFont(family, style, size);
910        if (f == null
911            || (f.getFamily().equals(Font.DIALOG)
912                && ! family.equalsIgnoreCase(Font.DIALOG))) {
913            family = Font.SANS_SERIF;
914            f = sc.getFont(family, style, size);
915        }
916        return f;
917    }
918
919    static int getFontSize(AttributeSet attr, int defaultSize, StyleSheet ss) {
920        // PENDING(prinz) this is a 1.1 based implementation, need to also
921        // have a 1.2 version.
922        FontSize sizeValue = (FontSize)attr.getAttribute(CSS.Attribute.
923                                                         FONT_SIZE);
924
925        return (sizeValue != null) ? sizeValue.getValue(attr, ss)
926                                   : defaultSize;
927    }
928
929    /**
930     * Takes a set of attributes and turn it into a color
931     * specification.  This might be used to specify things
932     * like brighter, more hue, etc.
933     * This will return null if there is no value for <code>key</code>.
934     *
935     * @param key CSS.Attribute identifying where color is stored.
936     * @param a the set of attributes
937     * @return the color
938     */
939    Color getColor(AttributeSet a, CSS.Attribute key) {
940        ColorValue cv = (ColorValue) a.getAttribute(key);
941        if (cv != null) {
942            return cv.getValue();
943        }
944        return null;
945    }
946
947    /**
948     * Returns the size of a font from the passed in string.
949     *
950     * @param size CSS string describing font size
951     * @param baseFontSize size to use for relative units.
952     */
953    float getPointSize(String size, StyleSheet ss) {
954        int relSize, absSize, diff, index;
955        ss = getStyleSheet(ss);
956        if (size != null) {
957            if (size.startsWith("+")) {
958                relSize = Integer.valueOf(size.substring(1)).intValue();
959                return getPointSize(baseFontSize + relSize, ss);
960            } else if (size.startsWith("-")) {
961                relSize = -Integer.valueOf(size.substring(1)).intValue();
962                return getPointSize(baseFontSize + relSize, ss);
963            } else {
964                absSize = Integer.valueOf(size).intValue();
965                return getPointSize(absSize, ss);
966            }
967        }
968        return 0;
969    }
970
971    /**
972     * Returns the length of the attribute in <code>a</code> with
973     * key <code>key</code>.
974     */
975    float getLength(AttributeSet a, CSS.Attribute key, StyleSheet ss) {
976        ss = getStyleSheet(ss);
977        LengthValue lv = (LengthValue) a.getAttribute(key);
978        boolean isW3CLengthUnits = (ss == null) ? false : ss.isW3CLengthUnits();
979        float len = (lv != null) ? lv.getValue(isW3CLengthUnits) : 0;
980        return len;
981    }
982
983    /**
984     * Convert a set of HTML attributes to an equivalent
985     * set of CSS attributes.
986     *
987     * @param htmlAttrSet AttributeSet containing the HTML attributes.
988     * @return AttributeSet containing the corresponding CSS attributes.
989     *        The AttributeSet will be empty if there are no mapping
990     *        CSS attributes.
991     */
992    AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet) {
993        MutableAttributeSet cssAttrSet = new SimpleAttributeSet();
994        Element elem = (Element)htmlAttrSet;
995        HTML.Tag tag = getHTMLTag(htmlAttrSet);
996        if ((tag == HTML.Tag.TD) || (tag == HTML.Tag.TH)) {
997            // translate border width into the cells, if it has non-zero value.
998            AttributeSet tableAttr = elem.getParentElement().
999                                     getParentElement().getAttributes();
1000
1001            int borderWidth = getTableBorder(tableAttr);
1002            if (borderWidth > 0) {
1003                // If table contains the BORDER attribute cells should have border width equals 1
1004                translateAttribute(HTML.Attribute.BORDER, "1", cssAttrSet);
1005            }
1006            String pad = (String)tableAttr.getAttribute(HTML.Attribute.CELLPADDING);
1007            if (pad != null) {
1008                LengthValue v =
1009                    (LengthValue)getInternalCSSValue(CSS.Attribute.PADDING_TOP, pad);
1010                v.span = (v.span < 0) ? 0 : v.span;
1011                cssAttrSet.addAttribute(CSS.Attribute.PADDING_TOP, v);
1012                cssAttrSet.addAttribute(CSS.Attribute.PADDING_BOTTOM, v);
1013                cssAttrSet.addAttribute(CSS.Attribute.PADDING_LEFT, v);
1014                cssAttrSet.addAttribute(CSS.Attribute.PADDING_RIGHT, v);
1015            }
1016        }
1017        if (elem.isLeaf()) {
1018            translateEmbeddedAttributes(htmlAttrSet, cssAttrSet);
1019        } else {
1020            translateAttributes(tag, htmlAttrSet, cssAttrSet);
1021        }
1022        if (tag == HTML.Tag.CAPTION) {
1023            /*
1024             * Navigator uses ALIGN for caption placement and IE uses VALIGN.
1025             */
1026            Object v = htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
1027            if ((v != null) && (v.equals("top") || v.equals("bottom"))) {
1028                cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE, v);
1029                cssAttrSet.removeAttribute(CSS.Attribute.TEXT_ALIGN);
1030            } else {
1031                v = htmlAttrSet.getAttribute(HTML.Attribute.VALIGN);
1032                if (v != null) {
1033                    cssAttrSet.addAttribute(CSS.Attribute.CAPTION_SIDE, v);
1034                }
1035            }
1036        }
1037        return cssAttrSet;
1038    }
1039
1040    private static int getTableBorder(AttributeSet tableAttr) {
1041        String borderValue = (String) tableAttr.getAttribute(HTML.Attribute.BORDER);
1042
1043        if (borderValue == HTML.NULL_ATTRIBUTE_VALUE || "".equals(borderValue)) {
1044            // Some browsers accept <TABLE BORDER> and <TABLE BORDER=""> with the same semantics as BORDER=1
1045            return 1;
1046        }
1047
1048        try {
1049            return Integer.parseInt(borderValue);
1050        } catch (NumberFormatException e) {
1051            return 0;
1052        }
1053    }
1054
1055    private static final Hashtable<String, Attribute> attributeMap = new Hashtable<String, Attribute>();
1056    private static final Hashtable<String, Value> valueMap = new Hashtable<String, Value>();
1057
1058    /**
1059     * The hashtable and the static initalization block below,
1060     * set up a mapping from well-known HTML attributes to
1061     * CSS attributes.  For the most part, there is a 1-1 mapping
1062     * between the two.  However in the case of certain HTML
1063     * attributes for example HTML.Attribute.VSPACE or
1064     * HTML.Attribute.HSPACE, end up mapping to two CSS.Attribute's.
1065     * Therefore, the value associated with each HTML.Attribute.
1066     * key ends up being an array of CSS.Attribute.* objects.
1067     */
1068    private static final Hashtable<HTML.Attribute, CSS.Attribute[]> htmlAttrToCssAttrMap = new Hashtable<HTML.Attribute, CSS.Attribute[]>(20);
1069
1070    /**
1071     * The hashtable and static initialization that follows sets
1072     * up a translation from StyleConstants (i.e. the <em>well known</em>
1073     * attributes) to the associated CSS attributes.
1074     */
1075    private static final Hashtable<Object, Attribute> styleConstantToCssMap = new Hashtable<Object, Attribute>(17);
1076    /** Maps from HTML value to a CSS value. Used in internal mapping. */
1077    private static final Hashtable<String, CSS.Value> htmlValueToCssValueMap = new Hashtable<String, CSS.Value>(8);
1078    /** Maps from CSS value (string) to internal value. */
1079    private static final Hashtable<String, CSS.Value> cssValueToInternalValueMap = new Hashtable<String, CSS.Value>(13);
1080
1081    static {
1082        // load the attribute map
1083        for (int i = 0; i < Attribute.allAttributes.length; i++ ) {
1084            attributeMap.put(Attribute.allAttributes[i].toString(),
1085                             Attribute.allAttributes[i]);
1086        }
1087        // load the value map
1088        for (int i = 0; i < Value.allValues.length; i++ ) {
1089            valueMap.put(Value.allValues[i].toString(),
1090                             Value.allValues[i]);
1091        }
1092
1093        htmlAttrToCssAttrMap.put(HTML.Attribute.COLOR,
1094                                 new CSS.Attribute[]{CSS.Attribute.COLOR});
1095        htmlAttrToCssAttrMap.put(HTML.Attribute.TEXT,
1096                                 new CSS.Attribute[]{CSS.Attribute.COLOR});
1097        htmlAttrToCssAttrMap.put(HTML.Attribute.CLEAR,
1098                                 new CSS.Attribute[]{CSS.Attribute.CLEAR});
1099        htmlAttrToCssAttrMap.put(HTML.Attribute.BACKGROUND,
1100                                 new CSS.Attribute[]{CSS.Attribute.BACKGROUND_IMAGE});
1101        htmlAttrToCssAttrMap.put(HTML.Attribute.BGCOLOR,
1102                                 new CSS.Attribute[]{CSS.Attribute.BACKGROUND_COLOR});
1103        htmlAttrToCssAttrMap.put(HTML.Attribute.WIDTH,
1104                                 new CSS.Attribute[]{CSS.Attribute.WIDTH});
1105        htmlAttrToCssAttrMap.put(HTML.Attribute.HEIGHT,
1106                                 new CSS.Attribute[]{CSS.Attribute.HEIGHT});
1107        htmlAttrToCssAttrMap.put(HTML.Attribute.BORDER,
1108                                 new CSS.Attribute[]{CSS.Attribute.BORDER_TOP_WIDTH, CSS.Attribute.BORDER_RIGHT_WIDTH, CSS.Attribute.BORDER_BOTTOM_WIDTH, CSS.Attribute.BORDER_LEFT_WIDTH});
1109        htmlAttrToCssAttrMap.put(HTML.Attribute.CELLPADDING,
1110                                 new CSS.Attribute[]{CSS.Attribute.PADDING});
1111        htmlAttrToCssAttrMap.put(HTML.Attribute.CELLSPACING,
1112                                 new CSS.Attribute[]{CSS.Attribute.BORDER_SPACING});
1113        htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINWIDTH,
1114                                 new CSS.Attribute[]{CSS.Attribute.MARGIN_LEFT,
1115                                                     CSS.Attribute.MARGIN_RIGHT});
1116        htmlAttrToCssAttrMap.put(HTML.Attribute.MARGINHEIGHT,
1117                                 new CSS.Attribute[]{CSS.Attribute.MARGIN_TOP,
1118                                                     CSS.Attribute.MARGIN_BOTTOM});
1119        htmlAttrToCssAttrMap.put(HTML.Attribute.HSPACE,
1120                                 new CSS.Attribute[]{CSS.Attribute.PADDING_LEFT,
1121                                                     CSS.Attribute.PADDING_RIGHT});
1122        htmlAttrToCssAttrMap.put(HTML.Attribute.VSPACE,
1123                                 new CSS.Attribute[]{CSS.Attribute.PADDING_BOTTOM,
1124                                                     CSS.Attribute.PADDING_TOP});
1125        htmlAttrToCssAttrMap.put(HTML.Attribute.FACE,
1126                                 new CSS.Attribute[]{CSS.Attribute.FONT_FAMILY});
1127        htmlAttrToCssAttrMap.put(HTML.Attribute.SIZE,
1128                                 new CSS.Attribute[]{CSS.Attribute.FONT_SIZE});
1129        htmlAttrToCssAttrMap.put(HTML.Attribute.VALIGN,
1130                                 new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN});
1131        htmlAttrToCssAttrMap.put(HTML.Attribute.ALIGN,
1132                                 new CSS.Attribute[]{CSS.Attribute.VERTICAL_ALIGN,
1133                                                     CSS.Attribute.TEXT_ALIGN,
1134                                                     CSS.Attribute.FLOAT});
1135        htmlAttrToCssAttrMap.put(HTML.Attribute.TYPE,
1136                                 new CSS.Attribute[]{CSS.Attribute.LIST_STYLE_TYPE});
1137        htmlAttrToCssAttrMap.put(HTML.Attribute.NOWRAP,
1138                                 new CSS.Attribute[]{CSS.Attribute.WHITE_SPACE});
1139
1140        // initialize StyleConstants mapping
1141        styleConstantToCssMap.put(StyleConstants.FontFamily,
1142                                  CSS.Attribute.FONT_FAMILY);
1143        styleConstantToCssMap.put(StyleConstants.FontSize,
1144                                  CSS.Attribute.FONT_SIZE);
1145        styleConstantToCssMap.put(StyleConstants.Bold,
1146                                  CSS.Attribute.FONT_WEIGHT);
1147        styleConstantToCssMap.put(StyleConstants.Italic,
1148                                  CSS.Attribute.FONT_STYLE);
1149        styleConstantToCssMap.put(StyleConstants.Underline,
1150                                  CSS.Attribute.TEXT_DECORATION);
1151        styleConstantToCssMap.put(StyleConstants.StrikeThrough,
1152                                  CSS.Attribute.TEXT_DECORATION);
1153        styleConstantToCssMap.put(StyleConstants.Superscript,
1154                                  CSS.Attribute.VERTICAL_ALIGN);
1155        styleConstantToCssMap.put(StyleConstants.Subscript,
1156                                  CSS.Attribute.VERTICAL_ALIGN);
1157        styleConstantToCssMap.put(StyleConstants.Foreground,
1158                                  CSS.Attribute.COLOR);
1159        styleConstantToCssMap.put(StyleConstants.Background,
1160                                  CSS.Attribute.BACKGROUND_COLOR);
1161        styleConstantToCssMap.put(StyleConstants.FirstLineIndent,
1162                                  CSS.Attribute.TEXT_INDENT);
1163        styleConstantToCssMap.put(StyleConstants.LeftIndent,
1164                                  CSS.Attribute.MARGIN_LEFT);
1165        styleConstantToCssMap.put(StyleConstants.RightIndent,
1166                                  CSS.Attribute.MARGIN_RIGHT);
1167        styleConstantToCssMap.put(StyleConstants.SpaceAbove,
1168                                  CSS.Attribute.MARGIN_TOP);
1169        styleConstantToCssMap.put(StyleConstants.SpaceBelow,
1170                                  CSS.Attribute.MARGIN_BOTTOM);
1171        styleConstantToCssMap.put(StyleConstants.Alignment,
1172                                  CSS.Attribute.TEXT_ALIGN);
1173
1174        // HTML->CSS
1175        htmlValueToCssValueMap.put("disc", CSS.Value.DISC);
1176        htmlValueToCssValueMap.put("square", CSS.Value.SQUARE);
1177        htmlValueToCssValueMap.put("circle", CSS.Value.CIRCLE);
1178        htmlValueToCssValueMap.put("1", CSS.Value.DECIMAL);
1179        htmlValueToCssValueMap.put("a", CSS.Value.LOWER_ALPHA);
1180        htmlValueToCssValueMap.put("A", CSS.Value.UPPER_ALPHA);
1181        htmlValueToCssValueMap.put("i", CSS.Value.LOWER_ROMAN);
1182        htmlValueToCssValueMap.put("I", CSS.Value.UPPER_ROMAN);
1183
1184        // CSS-> internal CSS
1185        cssValueToInternalValueMap.put("none", CSS.Value.NONE);
1186        cssValueToInternalValueMap.put("disc", CSS.Value.DISC);
1187        cssValueToInternalValueMap.put("square", CSS.Value.SQUARE);
1188        cssValueToInternalValueMap.put("circle", CSS.Value.CIRCLE);
1189        cssValueToInternalValueMap.put("decimal", CSS.Value.DECIMAL);
1190        cssValueToInternalValueMap.put("lower-roman", CSS.Value.LOWER_ROMAN);
1191        cssValueToInternalValueMap.put("upper-roman", CSS.Value.UPPER_ROMAN);
1192        cssValueToInternalValueMap.put("lower-alpha", CSS.Value.LOWER_ALPHA);
1193        cssValueToInternalValueMap.put("upper-alpha", CSS.Value.UPPER_ALPHA);
1194        cssValueToInternalValueMap.put("repeat", CSS.Value.BACKGROUND_REPEAT);
1195        cssValueToInternalValueMap.put("no-repeat",
1196                                       CSS.Value.BACKGROUND_NO_REPEAT);
1197        cssValueToInternalValueMap.put("repeat-x",
1198                                       CSS.Value.BACKGROUND_REPEAT_X);
1199        cssValueToInternalValueMap.put("repeat-y",
1200                                       CSS.Value.BACKGROUND_REPEAT_Y);
1201        cssValueToInternalValueMap.put("scroll",
1202                                       CSS.Value.BACKGROUND_SCROLL);
1203        cssValueToInternalValueMap.put("fixed",
1204                                       CSS.Value.BACKGROUND_FIXED);
1205
1206        // Register all the CSS attribute keys for archival/unarchival
1207        Object[] keys = CSS.Attribute.allAttributes;
1208        try {
1209            for (Object key : keys) {
1210                StyleContext.registerStaticAttributeKey(key);
1211            }
1212        } catch (Throwable e) {
1213            e.printStackTrace();
1214        }
1215
1216        // Register all the CSS Values for archival/unarchival
1217        keys = CSS.Value.allValues;
1218        try {
1219            for (Object key : keys) {
1220                StyleContext.registerStaticAttributeKey(key);
1221            }
1222        } catch (Throwable e) {
1223            e.printStackTrace();
1224        }
1225    }
1226
1227    /**
1228     * Return the set of all possible CSS attribute keys.
1229     *
1230     * @return the set of all possible CSS attribute keys
1231     */
1232    public static Attribute[] getAllAttributeKeys() {
1233        Attribute[] keys = new Attribute[Attribute.allAttributes.length];
1234        System.arraycopy(Attribute.allAttributes, 0, keys, 0, Attribute.allAttributes.length);
1235        return keys;
1236    }
1237
1238    /**
1239     * Translates a string to a <code>CSS.Attribute</code> object.
1240     * This will return <code>null</code> if there is no attribute
1241     * by the given name.
1242     *
1243     * @param name the name of the CSS attribute to fetch the
1244     *  typesafe enumeration for
1245     * @return the <code>CSS.Attribute</code> object,
1246     *  or <code>null</code> if the string
1247     *  doesn't represent a valid attribute key
1248     */
1249    public static final Attribute getAttribute(String name) {
1250        return attributeMap.get(name);
1251    }
1252
1253    /**
1254     * Translates a string to a <code>CSS.Value</code> object.
1255     * This will return <code>null</code> if there is no value
1256     * by the given name.
1257     *
1258     * @param name the name of the CSS value to fetch the
1259     *  typesafe enumeration for
1260     * @return the <code>CSS.Value</code> object,
1261     *  or <code>null</code> if the string
1262     *  doesn't represent a valid CSS value name; this does
1263     *  not mean that it doesn't represent a valid CSS value
1264     */
1265    static final Value getValue(String name) {
1266        return valueMap.get(name);
1267    }
1268
1269
1270    //
1271    // Conversion related methods/classes
1272    //
1273
1274    /**
1275     * Returns a URL for the given CSS url string. If relative,
1276     * <code>base</code> is used as the parent. If a valid URL can not
1277     * be found, this will not throw a MalformedURLException, instead
1278     * null will be returned.
1279     */
1280    static URL getURL(URL base, String cssString) {
1281        if (cssString == null) {
1282            return null;
1283        }
1284        if (cssString.startsWith("url(") &&
1285            cssString.endsWith(")")) {
1286            cssString = cssString.substring(4, cssString.length() - 1);
1287        }
1288        // Absolute first
1289        try {
1290            URL url = new URL(cssString);
1291            if (url != null) {
1292                return url;
1293            }
1294        } catch (MalformedURLException mue) {
1295        }
1296        // Then relative
1297        if (base != null) {
1298            // Relative URL, try from base
1299            try {
1300                URL url = new URL(base, cssString);
1301                return url;
1302            }
1303            catch (MalformedURLException muee) {
1304            }
1305        }
1306        return null;
1307    }
1308
1309    /**
1310     * Converts a type Color to a hex string
1311     * in the format "#RRGGBB"
1312     */
1313    static String colorToHex(Color color) {
1314
1315      String colorstr = "#";
1316
1317      // Red
1318      String str = Integer.toHexString(color.getRed());
1319      if (str.length() > 2)
1320        str = str.substring(0, 2);
1321      else if (str.length() < 2)
1322        colorstr += "0" + str;
1323      else
1324        colorstr += str;
1325
1326      // Green
1327      str = Integer.toHexString(color.getGreen());
1328      if (str.length() > 2)
1329        str = str.substring(0, 2);
1330      else if (str.length() < 2)
1331        colorstr += "0" + str;
1332      else
1333        colorstr += str;
1334
1335      // Blue
1336      str = Integer.toHexString(color.getBlue());
1337      if (str.length() > 2)
1338        str = str.substring(0, 2);
1339      else if (str.length() < 2)
1340        colorstr += "0" + str;
1341      else
1342        colorstr += str;
1343
1344      return colorstr;
1345    }
1346
1347     /**
1348      * Convert a "#FFFFFF" hex string to a Color.
1349      * If the color specification is bad, an attempt
1350      * will be made to fix it up.
1351      */
1352    static final Color hexToColor(String value) {
1353        String digits;
1354        int n = value.length();
1355        if (value.startsWith("#")) {
1356            digits = value.substring(1, Math.min(value.length(), 7));
1357        } else {
1358            digits = value;
1359        }
1360        String hstr = "0x" + digits;
1361        Color c;
1362        try {
1363            c = Color.decode(hstr);
1364        } catch (NumberFormatException nfe) {
1365            c = null;
1366        }
1367         return c;
1368     }
1369
1370    /**
1371     * Convert a color string such as "RED" or "#NNNNNN" or "rgb(r, g, b)"
1372     * to a Color.
1373     */
1374    static Color stringToColor(String str) {
1375      Color color;
1376
1377      if (str == null) {
1378          return null;
1379      }
1380      if (str.length() == 0)
1381        color = Color.black;
1382      else if (str.startsWith("rgb(")) {
1383          color = parseRGB(str);
1384      }
1385      else if (str.charAt(0) == '#')
1386        color = hexToColor(str);
1387      else if (str.equalsIgnoreCase("Black"))
1388        color = hexToColor("#000000");
1389      else if(str.equalsIgnoreCase("Silver"))
1390        color = hexToColor("#C0C0C0");
1391      else if(str.equalsIgnoreCase("Gray"))
1392        color = hexToColor("#808080");
1393      else if(str.equalsIgnoreCase("White"))
1394        color = hexToColor("#FFFFFF");
1395      else if(str.equalsIgnoreCase("Maroon"))
1396        color = hexToColor("#800000");
1397      else if(str.equalsIgnoreCase("Red"))
1398        color = hexToColor("#FF0000");
1399      else if(str.equalsIgnoreCase("Purple"))
1400        color = hexToColor("#800080");
1401      else if(str.equalsIgnoreCase("Fuchsia"))
1402        color = hexToColor("#FF00FF");
1403      else if(str.equalsIgnoreCase("Green"))
1404        color = hexToColor("#008000");
1405      else if(str.equalsIgnoreCase("Lime"))
1406        color = hexToColor("#00FF00");
1407      else if(str.equalsIgnoreCase("Olive"))
1408        color = hexToColor("#808000");
1409      else if(str.equalsIgnoreCase("Yellow"))
1410        color = hexToColor("#FFFF00");
1411      else if(str.equalsIgnoreCase("Navy"))
1412        color = hexToColor("#000080");
1413      else if(str.equalsIgnoreCase("Blue"))
1414        color = hexToColor("#0000FF");
1415      else if(str.equalsIgnoreCase("Teal"))
1416        color = hexToColor("#008080");
1417      else if(str.equalsIgnoreCase("Aqua"))
1418        color = hexToColor("#00FFFF");
1419      else if(str.equalsIgnoreCase("Orange"))
1420        color = hexToColor("#FF8000");
1421      else
1422          color = hexToColor(str); // sometimes get specified without leading #
1423      return color;
1424    }
1425
1426    /**
1427     * Parses a String in the format <code>rgb(r, g, b)</code> where
1428     * each of the Color components is either an integer, or a floating number
1429     * with a % after indicating a percentage value of 255. Values are
1430     * constrained to fit with 0-255. The resulting Color is returned.
1431     */
1432    private static Color parseRGB(String string) {
1433        // Find the next numeric char
1434        int[] index = new int[1];
1435
1436        index[0] = 4;
1437        int red = getColorComponent(string, index);
1438        int green = getColorComponent(string, index);
1439        int blue = getColorComponent(string, index);
1440
1441        return new Color(red, green, blue);
1442    }
1443
1444    /**
1445     * Returns the next integer value from <code>string</code> starting
1446     * at <code>index[0]</code>. The value can either can an integer, or
1447     * a percentage (floating number ending with %), in which case it is
1448     * multiplied by 255.
1449     */
1450    private static int getColorComponent(String string, int[] index) {
1451        int length = string.length();
1452        char aChar;
1453
1454        // Skip non-decimal chars
1455        while(index[0] < length && (aChar = string.charAt(index[0])) != '-' &&
1456              !Character.isDigit(aChar) && aChar != '.') {
1457            index[0]++;
1458        }
1459
1460        int start = index[0];
1461
1462        if (start < length && string.charAt(index[0]) == '-') {
1463            index[0]++;
1464        }
1465        while(index[0] < length &&
1466                         Character.isDigit(string.charAt(index[0]))) {
1467            index[0]++;
1468        }
1469        if (index[0] < length && string.charAt(index[0]) == '.') {
1470            // Decimal value
1471            index[0]++;
1472            while(index[0] < length &&
1473                  Character.isDigit(string.charAt(index[0]))) {
1474                index[0]++;
1475            }
1476        }
1477        if (start != index[0]) {
1478            try {
1479                float value = Float.parseFloat(string.substring
1480                                               (start, index[0]));
1481
1482                if (index[0] < length && string.charAt(index[0]) == '%') {
1483                    index[0]++;
1484                    value = value * 255f / 100f;
1485                }
1486                return Math.min(255, Math.max(0, (int)value));
1487            } catch (NumberFormatException nfe) {
1488                // Treat as 0
1489            }
1490        }
1491        return 0;
1492    }
1493
1494    static int getIndexOfSize(float pt, int[] sizeMap) {
1495        for (int i = 0; i < sizeMap.length; i ++ )
1496                if (pt <= sizeMap[i])
1497                        return i + 1;
1498        return sizeMap.length;
1499    }
1500
1501    static int getIndexOfSize(float pt, StyleSheet ss) {
1502        int[] sizeMap = (ss != null) ? ss.getSizeMap() :
1503            StyleSheet.sizeMapDefault;
1504        return getIndexOfSize(pt, sizeMap);
1505    }
1506
1507
1508    /**
1509     * @return an array of all the strings in <code>value</code>
1510     *         that are separated by whitespace.
1511     */
1512    static String[] parseStrings(String value) {
1513        int         current, last;
1514        int         length = (value == null) ? 0 : value.length();
1515        Vector<String> temp = new Vector<String>(4);
1516
1517        current = 0;
1518        while (current < length) {
1519            // Skip ws
1520            while (current < length && Character.isWhitespace
1521                   (value.charAt(current))) {
1522                current++;
1523            }
1524            last = current;
1525            while (current < length && !Character.isWhitespace
1526                   (value.charAt(current))) {
1527                current++;
1528            }
1529            if (last != current) {
1530                temp.addElement(value.substring(last, current));
1531            }
1532            current++;
1533        }
1534        String[] retValue = new String[temp.size()];
1535        temp.copyInto(retValue);
1536        return retValue;
1537    }
1538
1539    /**
1540     * Return the point size, given a size index. Legal HTML index sizes
1541     * are 1-7.
1542     */
1543    float getPointSize(int index, StyleSheet ss) {
1544        ss = getStyleSheet(ss);
1545        int[] sizeMap = (ss != null) ? ss.getSizeMap() :
1546            StyleSheet.sizeMapDefault;
1547        --index;
1548        if (index < 0)
1549          return sizeMap[0];
1550        else if (index > sizeMap.length - 1)
1551          return sizeMap[sizeMap.length - 1];
1552        else
1553          return sizeMap[index];
1554    }
1555
1556
1557    private void translateEmbeddedAttributes(AttributeSet htmlAttrSet,
1558                                             MutableAttributeSet cssAttrSet) {
1559        Enumeration<?> keys = htmlAttrSet.getAttributeNames();
1560        if (htmlAttrSet.getAttribute(StyleConstants.NameAttribute) ==
1561            HTML.Tag.HR) {
1562            // HR needs special handling due to us treating it as a leaf.
1563            translateAttributes(HTML.Tag.HR, htmlAttrSet, cssAttrSet);
1564        }
1565        while (keys.hasMoreElements()) {
1566            Object key = keys.nextElement();
1567            if (key instanceof HTML.Tag) {
1568                HTML.Tag tag = (HTML.Tag)key;
1569                Object o = htmlAttrSet.getAttribute(tag);
1570                if (o != null && o instanceof AttributeSet) {
1571                    translateAttributes(tag, (AttributeSet)o, cssAttrSet);
1572                }
1573            } else if (key instanceof CSS.Attribute) {
1574                cssAttrSet.addAttribute(key, htmlAttrSet.getAttribute(key));
1575            }
1576        }
1577    }
1578
1579    private void translateAttributes(HTML.Tag tag,
1580                                            AttributeSet htmlAttrSet,
1581                                            MutableAttributeSet cssAttrSet) {
1582        Enumeration<?> names = htmlAttrSet.getAttributeNames();
1583        while (names.hasMoreElements()) {
1584            Object name = names.nextElement();
1585
1586            if (name instanceof HTML.Attribute) {
1587                HTML.Attribute key = (HTML.Attribute)name;
1588
1589                /*
1590                 * HTML.Attribute.ALIGN needs special processing.
1591                 * It can map to 1 of many(3) possible CSS attributes
1592                 * depending on the nature of the tag the attribute is
1593                 * part off and depending on the value of the attribute.
1594                 */
1595                if (key == HTML.Attribute.ALIGN) {
1596                    String htmlAttrValue = (String)htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
1597                    if (htmlAttrValue != null) {
1598                        CSS.Attribute cssAttr = getCssAlignAttribute(tag, htmlAttrSet);
1599                        if (cssAttr != null) {
1600                            Object o = getCssValue(cssAttr, htmlAttrValue);
1601                            if (o != null) {
1602                                cssAttrSet.addAttribute(cssAttr, o);
1603                            }
1604                        }
1605                    }
1606                } else {
1607                    if (key == HTML.Attribute.SIZE && !isHTMLFontTag(tag)) {
1608                        /*
1609                         * The html size attribute has a mapping in the CSS world only
1610                         * if it is par of a font or base font tag.
1611                         */
1612                    } else if (tag == HTML.Tag.TABLE && key == HTML.Attribute.BORDER) {
1613                        int borderWidth = getTableBorder(htmlAttrSet);
1614
1615                        if (borderWidth > 0) {
1616                            translateAttribute(HTML.Attribute.BORDER, Integer.toString(borderWidth), cssAttrSet);
1617                        }
1618                    } else {
1619                        translateAttribute(key, (String) htmlAttrSet.getAttribute(key), cssAttrSet);
1620                    }
1621                }
1622            } else if (name instanceof CSS.Attribute) {
1623                cssAttrSet.addAttribute(name, htmlAttrSet.getAttribute(name));
1624            }
1625        }
1626    }
1627
1628    private void translateAttribute(HTML.Attribute key,
1629                                           String htmlAttrValue,
1630                                           MutableAttributeSet cssAttrSet) {
1631        /*
1632         * In the case of all remaining HTML.Attribute's they
1633         * map to 1 or more CCS.Attribute.
1634         */
1635        CSS.Attribute[] cssAttrList = getCssAttribute(key);
1636
1637        if (cssAttrList == null || htmlAttrValue == null) {
1638            return;
1639        }
1640        for (Attribute cssAttr : cssAttrList) {
1641            Object o = getCssValue(cssAttr, htmlAttrValue);
1642            if (o != null) {
1643                cssAttrSet.addAttribute(cssAttr , o);
1644            }
1645        }
1646    }
1647
1648    /**
1649     * Given a CSS.Attribute object and its corresponding HTML.Attribute's
1650     * value, this method returns a CssValue object to associate with the
1651     * CSS attribute.
1652     *
1653     * @param the CSS.Attribute
1654     * @param a String containing the value associated HTML.Attribtue.
1655     */
1656    Object getCssValue(CSS.Attribute cssAttr, String htmlAttrValue) {
1657        CssValue value = (CssValue)valueConvertor.get(cssAttr);
1658        Object o = value.parseHtmlValue(htmlAttrValue);
1659        return o;
1660    }
1661
1662    /**
1663     * Maps an HTML.Attribute object to its appropriate CSS.Attributes.
1664     *
1665     * @param HTML.Attribute
1666     * @return CSS.Attribute[]
1667     */
1668    private CSS.Attribute[] getCssAttribute(HTML.Attribute hAttr) {
1669        return htmlAttrToCssAttrMap.get(hAttr);
1670    }
1671
1672    /**
1673     * Maps HTML.Attribute.ALIGN to either:
1674     *     CSS.Attribute.TEXT_ALIGN
1675     *     CSS.Attribute.FLOAT
1676     *     CSS.Attribute.VERTICAL_ALIGN
1677     * based on the tag associated with the attribute and the
1678     * value of the attribute.
1679     *
1680     * @param AttributeSet containing HTML attributes.
1681     * @return CSS.Attribute mapping for HTML.Attribute.ALIGN.
1682     */
1683    private CSS.Attribute getCssAlignAttribute(HTML.Tag tag,
1684                                                   AttributeSet htmlAttrSet) {
1685        return CSS.Attribute.TEXT_ALIGN;
1686/*
1687        String htmlAttrValue = (String)htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
1688        CSS.Attribute cssAttr = CSS.Attribute.TEXT_ALIGN;
1689        if (htmlAttrValue != null && htmlAttrSet instanceof Element) {
1690            Element elem = (Element)htmlAttrSet;
1691            if (!elem.isLeaf() && tag.isBlock() && validTextAlignValue(htmlAttrValue)) {
1692                return CSS.Attribute.TEXT_ALIGN;
1693            } else if (isFloater(htmlAttrValue)) {
1694                return CSS.Attribute.FLOAT;
1695            } else if (elem.isLeaf()) {
1696                return CSS.Attribute.VERTICAL_ALIGN;
1697            }
1698        }
1699        return null;
1700        */
1701    }
1702
1703    /**
1704     * Fetches the tag associated with the HTML AttributeSet.
1705     *
1706     * @param  AttributeSet containing the HTML attributes.
1707     * @return HTML.Tag
1708     */
1709    private HTML.Tag getHTMLTag(AttributeSet htmlAttrSet) {
1710        Object o = htmlAttrSet.getAttribute(StyleConstants.NameAttribute);
1711        if (o instanceof HTML.Tag) {
1712            HTML.Tag tag = (HTML.Tag) o;
1713            return tag;
1714        }
1715        return null;
1716    }
1717
1718
1719    private boolean isHTMLFontTag(HTML.Tag tag) {
1720        return (tag != null && ((tag == HTML.Tag.FONT) || (tag == HTML.Tag.BASEFONT)));
1721    }
1722
1723
1724    private boolean isFloater(String alignValue) {
1725        return (alignValue.equals("left") || alignValue.equals("right"));
1726    }
1727
1728    private boolean validTextAlignValue(String alignValue) {
1729        return (isFloater(alignValue) || alignValue.equals("center"));
1730    }
1731
1732    /**
1733     * Base class to CSS values in the attribute sets.  This
1734     * is intended to act as a convertor to/from other attribute
1735     * formats.
1736     * <p>
1737     * The CSS parser uses the parseCssValue method to convert
1738     * a string to whatever format is appropriate a given key
1739     * (i.e. these convertors are stored in a map using the
1740     * CSS.Attribute as a key and the CssValue as the value).
1741     * <p>
1742     * The HTML to CSS conversion process first converts the
1743     * HTML.Attribute to a CSS.Attribute, and then calls
1744     * the parseHtmlValue method on the value of the HTML
1745     * attribute to produce the corresponding CSS value.
1746     * <p>
1747     * The StyleConstants to CSS conversion process first
1748     * converts the StyleConstants attribute to a
1749     * CSS.Attribute, and then calls the fromStyleConstants
1750     * method to convert the StyleConstants value to a
1751     * CSS value.
1752     * <p>
1753     * The CSS to StyleConstants conversion process first
1754     * converts the StyleConstants attribute to a
1755     * CSS.Attribute, and then calls the toStyleConstants
1756     * method to convert the CSS value to a StyleConstants
1757     * value.
1758     */
1759    @SuppressWarnings("serial") // Same-version serialization only
1760    static class CssValue implements Serializable {
1761
1762        /**
1763         * Convert a CSS value string to the internal format
1764         * (for fast processing) used in the attribute sets.
1765         * The fallback storage for any value that we don't
1766         * have a special binary format for is a String.
1767         */
1768        Object parseCssValue(String value) {
1769            return value;
1770        }
1771
1772        /**
1773         * Convert an HTML attribute value to a CSS attribute
1774         * value.  If there is no conversion, return null.
1775         * This is implemented to simply forward to the CSS
1776         * parsing by default (since some of the attribute
1777         * values are the same).  If the attribute value
1778         * isn't recognized as a CSS value it is generally
1779         * returned as null.
1780         */
1781        Object parseHtmlValue(String value) {
1782            return parseCssValue(value);
1783        }
1784
1785        /**
1786         * Converts a <code>StyleConstants</code> attribute value to
1787         * a CSS attribute value.  If there is no conversion,
1788         * returns <code>null</code>.  By default, there is no conversion.
1789         *
1790         * @param key the <code>StyleConstants</code> attribute
1791         * @param value the value of a <code>StyleConstants</code>
1792         *   attribute to be converted
1793         * @return the CSS value that represents the
1794         *   <code>StyleConstants</code> value
1795         */
1796        Object fromStyleConstants(StyleConstants key, Object value) {
1797            return null;
1798        }
1799
1800        /**
1801         * Converts a CSS attribute value to a
1802         * <code>StyleConstants</code>
1803         * value.  If there is no conversion, returns
1804         * <code>null</code>.
1805         * By default, there is no conversion.
1806         *
1807         * @param key the <code>StyleConstants</code> attribute
1808         * @param v the view containing <code>AttributeSet</code>
1809         * @return the <code>StyleConstants</code> attribute value that
1810         *   represents the CSS attribute value
1811         */
1812        Object toStyleConstants(StyleConstants key, View v) {
1813            return null;
1814        }
1815
1816        /**
1817         * Return the CSS format of the value
1818         */
1819        public String toString() {
1820            return svalue;
1821        }
1822
1823        /**
1824         * The value as a string... before conversion to a
1825         * binary format.
1826         */
1827        String svalue;
1828    }
1829
1830    /**
1831     * By default CSS attributes are represented as simple
1832     * strings.  They also have no conversion to/from
1833     * StyleConstants by default. This class represents the
1834     * value as a string (via the superclass), but
1835     * provides StyleConstants conversion support for the
1836     * CSS attributes that are held as strings.
1837     */
1838    @SuppressWarnings("serial") // Same-version serialization only
1839    static class StringValue extends CssValue {
1840
1841        /**
1842         * Convert a CSS value string to the internal format
1843         * (for fast processing) used in the attribute sets.
1844         * This produces a StringValue, so that it can be
1845         * used to convert from CSS to StyleConstants values.
1846         */
1847        Object parseCssValue(String value) {
1848            StringValue sv = new StringValue();
1849            sv.svalue = value;
1850            return sv;
1851        }
1852
1853        /**
1854         * Converts a <code>StyleConstants</code> attribute value to
1855         * a CSS attribute value.  If there is no conversion
1856         * returns <code>null</code>.
1857         *
1858         * @param key the <code>StyleConstants</code> attribute
1859         * @param value the value of a <code>StyleConstants</code>
1860         *   attribute to be converted
1861         * @return the CSS value that represents the
1862         *   <code>StyleConstants</code> value
1863         */
1864        Object fromStyleConstants(StyleConstants key, Object value) {
1865            if (key == StyleConstants.Italic) {
1866                if (value.equals(Boolean.TRUE)) {
1867                    return parseCssValue("italic");
1868                }
1869                return parseCssValue("");
1870            } else if (key == StyleConstants.Underline) {
1871                if (value.equals(Boolean.TRUE)) {
1872                    return parseCssValue("underline");
1873                }
1874                return parseCssValue("");
1875            } else if (key == StyleConstants.Alignment) {
1876                int align = ((Integer)value).intValue();
1877                String ta;
1878                switch(align) {
1879                case StyleConstants.ALIGN_LEFT:
1880                    ta = "left";
1881                    break;
1882                case StyleConstants.ALIGN_RIGHT:
1883                    ta = "right";
1884                    break;
1885                case StyleConstants.ALIGN_CENTER:
1886                    ta = "center";
1887                    break;
1888                case StyleConstants.ALIGN_JUSTIFIED:
1889                    ta = "justify";
1890                    break;
1891                default:
1892                    ta = "left";
1893                }
1894                return parseCssValue(ta);
1895            } else if (key == StyleConstants.StrikeThrough) {
1896                if (value.equals(Boolean.TRUE)) {
1897                    return parseCssValue("line-through");
1898                }
1899                return parseCssValue("");
1900            } else if (key == StyleConstants.Superscript) {
1901                if (value.equals(Boolean.TRUE)) {
1902                    return parseCssValue("super");
1903                }
1904                return parseCssValue("");
1905            } else if (key == StyleConstants.Subscript) {
1906                if (value.equals(Boolean.TRUE)) {
1907                    return parseCssValue("sub");
1908                }
1909                return parseCssValue("");
1910            }
1911            return null;
1912        }
1913
1914        /**
1915         * Converts a CSS attribute value to a
1916         * <code>StyleConstants</code> value.
1917         * If there is no conversion, returns <code>null</code>.
1918         * By default, there is no conversion.
1919         *
1920         * @param key the <code>StyleConstants</code> attribute
1921         * @return the <code>StyleConstants</code> attribute value that
1922         *   represents the CSS attribute value
1923         */
1924        Object toStyleConstants(StyleConstants key, View v) {
1925            if (key == StyleConstants.Italic) {
1926                if (svalue.indexOf("italic") >= 0) {
1927                    return Boolean.TRUE;
1928                }
1929                return Boolean.FALSE;
1930            } else if (key == StyleConstants.Underline) {
1931                if (svalue.indexOf("underline") >= 0) {
1932                    return Boolean.TRUE;
1933                }
1934                return Boolean.FALSE;
1935            } else if (key == StyleConstants.Alignment) {
1936                if (svalue.equals("right")) {
1937                    return StyleConstants.ALIGN_RIGHT;
1938                } else if (svalue.equals("center")) {
1939                    return StyleConstants.ALIGN_CENTER;
1940                } else if  (svalue.equals("justify")) {
1941                    return StyleConstants.ALIGN_JUSTIFIED;
1942                }
1943                return StyleConstants.ALIGN_LEFT;
1944            } else if (key == StyleConstants.StrikeThrough) {
1945                if (svalue.indexOf("line-through") >= 0) {
1946                    return Boolean.TRUE;
1947                }
1948                return Boolean.FALSE;
1949            } else if (key == StyleConstants.Superscript) {
1950                if (svalue.indexOf("super") >= 0) {
1951                    return Boolean.TRUE;
1952                }
1953                return Boolean.FALSE;
1954            } else if (key == StyleConstants.Subscript) {
1955                if (svalue.indexOf("sub") >= 0) {
1956                    return Boolean.TRUE;
1957                }
1958                return Boolean.FALSE;
1959            }
1960            return null;
1961        }
1962
1963        // Used by ViewAttributeSet
1964        boolean isItalic() {
1965            return (svalue.indexOf("italic") != -1);
1966        }
1967
1968        boolean isStrike() {
1969            return (svalue.indexOf("line-through") != -1);
1970        }
1971
1972        boolean isUnderline() {
1973            return (svalue.indexOf("underline") != -1);
1974        }
1975
1976        boolean isSub() {
1977            return (svalue.indexOf("sub") != -1);
1978        }
1979
1980        boolean isSup() {
1981            return (svalue.indexOf("sup") != -1);
1982        }
1983    }
1984
1985    /**
1986     * Represents a value for the CSS.FONT_SIZE attribute.
1987     * The binary format of the value can be one of several
1988     * types.  If the type is Float,
1989     * the value is specified in terms of point or
1990     * percentage, depending upon the ending of the
1991     * associated string.
1992     * If the type is Integer, the value is specified
1993     * in terms of a size index.
1994     */
1995    @SuppressWarnings("serial") // Same-version serialization only
1996    class FontSize extends CssValue {
1997
1998        /**
1999         * Returns the size in points.  This is ultimately
2000         * what we need for the purpose of creating/fetching
2001         * a Font object.
2002         *
2003         * @param a the attribute set the value is being
2004         *  requested from.  We may need to walk up the
2005         *  resolve hierarchy if it's relative.
2006         */
2007        int getValue(AttributeSet a, StyleSheet ss) {
2008            ss = getStyleSheet(ss);
2009            if (index) {
2010                // it's an index, translate from size table
2011                return Math.round(getPointSize((int) value, ss));
2012            }
2013            else if (lu == null) {
2014                return Math.round(value);
2015            }
2016            else {
2017                if (lu.type == 0) {
2018                    boolean isW3CLengthUnits = (ss == null) ? false : ss.isW3CLengthUnits();
2019                    return Math.round(lu.getValue(isW3CLengthUnits));
2020                }
2021                if (a != null) {
2022                    AttributeSet resolveParent = a.getResolveParent();
2023
2024                    if (resolveParent != null) {
2025                        int pValue = StyleConstants.getFontSize(resolveParent);
2026
2027                        float retValue;
2028                        if (lu.type == 1 || lu.type == 3) {
2029                            retValue = lu.value * (float)pValue;
2030                        }
2031                        else {
2032                            retValue = lu.value + (float)pValue;
2033                        }
2034                        return Math.round(retValue);
2035                    }
2036                }
2037                // a is null, or no resolve parent.
2038                return 12;
2039            }
2040        }
2041
2042        Object parseCssValue(String value) {
2043            FontSize fs = new FontSize();
2044            fs.svalue = value;
2045            try {
2046                if (value.equals("xx-small")) {
2047                    fs.value = 1;
2048                    fs.index = true;
2049                } else if (value.equals("x-small")) {
2050                    fs.value = 2;
2051                    fs.index = true;
2052                } else if (value.equals("small")) {
2053                    fs.value = 3;
2054                    fs.index = true;
2055                } else if (value.equals("medium")) {
2056                    fs.value = 4;
2057                    fs.index = true;
2058                } else if (value.equals("large")) {
2059                    fs.value = 5;
2060                    fs.index = true;
2061                } else if (value.equals("x-large")) {
2062                    fs.value = 6;
2063                    fs.index = true;
2064                } else if (value.equals("xx-large")) {
2065                    fs.value = 7;
2066                    fs.index = true;
2067                } else {
2068                    fs.lu = new LengthUnit(value, (short)1, 1f);
2069                }
2070                // relative sizes, larger | smaller (adjust from parent by
2071                // 1.5 pixels)
2072                // em, ex refer to parent sizes
2073                // lengths: pt, mm, cm, pc, in, px
2074                //          em (font height 3em would be 3 times font height)
2075                //          ex (height of X)
2076                // lengths are (+/-) followed by a number and two letter
2077                // unit identifier
2078            } catch (NumberFormatException nfe) {
2079                fs = null;
2080            }
2081            return fs;
2082        }
2083
2084        Object parseHtmlValue(String value) {
2085            if ((value == null) || (value.length() == 0)) {
2086                return null;
2087            }
2088            FontSize fs = new FontSize();
2089            fs.svalue = value;
2090
2091            try {
2092                /*
2093                 * relative sizes in the size attribute are relative
2094                 * to the <basefont>'s size.
2095                 */
2096                int baseFontSize = getBaseFontSize();
2097                if (value.charAt(0) == '+') {
2098                    int relSize = Integer.valueOf(value.substring(1)).intValue();
2099                    fs.value = baseFontSize + relSize;
2100                    fs.index = true;
2101                } else if (value.charAt(0) == '-') {
2102                    int relSize = -Integer.valueOf(value.substring(1)).intValue();
2103                    fs.value = baseFontSize + relSize;
2104                    fs.index = true;
2105                } else {
2106                    fs.value = Integer.parseInt(value);
2107                    if (fs.value > 7) {
2108                        fs.value = 7;
2109                    } else if (fs.value < 0) {
2110                        fs.value = 0;
2111                    }
2112                    fs.index = true;
2113                }
2114
2115            } catch (NumberFormatException nfe) {
2116                fs = null;
2117            }
2118            return fs;
2119        }
2120
2121        /**
2122         * Converts a <code>StyleConstants</code> attribute value to
2123         * a CSS attribute value.  If there is no conversion
2124         * returns <code>null</code>.  By default, there is no conversion.
2125         *
2126         * @param key the <code>StyleConstants</code> attribute
2127         * @param value the value of a <code>StyleConstants</code>
2128         *   attribute to be converted
2129         * @return the CSS value that represents the
2130         *   <code>StyleConstants</code> value
2131         */
2132        Object fromStyleConstants(StyleConstants key, Object value) {
2133            if (value instanceof Number) {
2134                FontSize fs = new FontSize();
2135
2136                fs.value = getIndexOfSize(((Number)value).floatValue(), StyleSheet.sizeMapDefault);
2137                fs.svalue = Integer.toString((int)fs.value);
2138                fs.index = true;
2139                return fs;
2140            }
2141            return parseCssValue(value.toString());
2142        }
2143
2144        /**
2145         * Converts a CSS attribute value to a <code>StyleConstants</code>
2146         * value.  If there is no conversion, returns <code>null</code>.
2147         * By default, there is no conversion.
2148         *
2149         * @param key the <code>StyleConstants</code> attribute
2150         * @return the <code>StyleConstants</code> attribute value that
2151         *   represents the CSS attribute value
2152         */
2153        Object toStyleConstants(StyleConstants key, View v) {
2154            if (v != null) {
2155                return Integer.valueOf(getValue(v.getAttributes(), null));
2156            }
2157            return Integer.valueOf(getValue(null, null));
2158        }
2159
2160        float value;
2161        boolean index;
2162        LengthUnit lu;
2163    }
2164
2165    @SuppressWarnings("serial") // Same-version serialization only
2166    static class FontFamily extends CssValue {
2167
2168        /**
2169         * Returns the font family to use.
2170         */
2171        String getValue() {
2172            return family;
2173        }
2174
2175        Object parseCssValue(String value) {
2176            int cIndex = value.indexOf(',');
2177            FontFamily ff = new FontFamily();
2178            ff.svalue = value;
2179            ff.family = null;
2180
2181            if (cIndex == -1) {
2182                setFontName(ff, value);
2183            }
2184            else {
2185                boolean done = false;
2186                int lastIndex;
2187                int length = value.length();
2188                cIndex = 0;
2189                while (!done) {
2190                    // skip ws.
2191                    while (cIndex < length &&
2192                           Character.isWhitespace(value.charAt(cIndex)))
2193                        cIndex++;
2194                    // Find next ','
2195                    lastIndex = cIndex;
2196                    cIndex = value.indexOf(',', cIndex);
2197                    if (cIndex == -1) {
2198                        cIndex = length;
2199                    }
2200                    if (lastIndex < length) {
2201                        if (lastIndex != cIndex) {
2202                            int lastCharIndex = cIndex;
2203                            if (cIndex > 0 && value.charAt(cIndex - 1) == ' '){
2204                                lastCharIndex--;
2205                            }
2206                            setFontName(ff, value.substring
2207                                        (lastIndex, lastCharIndex));
2208                            done = (ff.family != null);
2209                        }
2210                        cIndex++;
2211                    }
2212                    else {
2213                        done = true;
2214                    }
2215                }
2216            }
2217            if (ff.family == null) {
2218                ff.family = Font.SANS_SERIF;
2219            }
2220            return ff;
2221        }
2222
2223        private void setFontName(FontFamily ff, String fontName) {
2224            ff.family = fontName;
2225        }
2226
2227        Object parseHtmlValue(String value) {
2228            // TBD
2229            return parseCssValue(value);
2230        }
2231
2232        /**
2233         * Converts a <code>StyleConstants</code> attribute value to
2234         * a CSS attribute value.  If there is no conversion
2235         * returns <code>null</code>.  By default, there is no conversion.
2236         *
2237         * @param key the <code>StyleConstants</code> attribute
2238         * @param value the value of a <code>StyleConstants</code>
2239         *   attribute to be converted
2240         * @return the CSS value that represents the
2241         *   <code>StyleConstants</code> value
2242         */
2243        Object fromStyleConstants(StyleConstants key, Object value) {
2244            return parseCssValue(value.toString());
2245        }
2246
2247        /**
2248         * Converts a CSS attribute value to a <code>StyleConstants</code>
2249         * value.  If there is no conversion, returns <code>null</code>.
2250         * By default, there is no conversion.
2251         *
2252         * @param key the <code>StyleConstants</code> attribute
2253         * @return the <code>StyleConstants</code> attribute value that
2254         *   represents the CSS attribute value
2255         */
2256        Object toStyleConstants(StyleConstants key, View v) {
2257            return family;
2258        }
2259
2260        String family;
2261    }
2262
2263    @SuppressWarnings("serial") // Same-version serialization only
2264    static class FontWeight extends CssValue {
2265
2266        int getValue() {
2267            return weight;
2268        }
2269
2270        Object parseCssValue(String value) {
2271            FontWeight fw = new FontWeight();
2272            fw.svalue = value;
2273            if (value.equals("bold")) {
2274                fw.weight = 700;
2275            } else if (value.equals("normal")) {
2276                fw.weight = 400;
2277            } else {
2278                // PENDING(prinz) add support for relative values
2279                try {
2280                    fw.weight = Integer.parseInt(value);
2281                } catch (NumberFormatException nfe) {
2282                    fw = null;
2283                }
2284            }
2285            return fw;
2286        }
2287
2288        /**
2289         * Converts a <code>StyleConstants</code> attribute value to
2290         * a CSS attribute value.  If there is no conversion
2291         * returns <code>null</code>.  By default, there is no conversion.
2292         *
2293         * @param key the <code>StyleConstants</code> attribute
2294         * @param value the value of a <code>StyleConstants</code>
2295         *   attribute to be converted
2296         * @return the CSS value that represents the
2297         *   <code>StyleConstants</code> value
2298         */
2299        Object fromStyleConstants(StyleConstants key, Object value) {
2300            if (value.equals(Boolean.TRUE)) {
2301                return parseCssValue("bold");
2302            }
2303            return parseCssValue("normal");
2304        }
2305
2306        /**
2307         * Converts a CSS attribute value to a <code>StyleConstants</code>
2308         * value.  If there is no conversion, returns <code>null</code>.
2309         * By default, there is no conversion.
2310         *
2311         * @param key the <code>StyleConstants</code> attribute
2312         * @return the <code>StyleConstants</code> attribute value that
2313         *   represents the CSS attribute value
2314         */
2315        Object toStyleConstants(StyleConstants key, View v) {
2316            return (weight > 500) ? Boolean.TRUE : Boolean.FALSE;
2317        }
2318
2319        boolean isBold() {
2320            return (weight > 500);
2321        }
2322
2323        int weight;
2324    }
2325
2326    @SuppressWarnings("serial") // Same-version serialization only
2327    static class ColorValue extends CssValue {
2328
2329        /**
2330         * Returns the color to use.
2331         */
2332        Color getValue() {
2333            return c;
2334        }
2335
2336        Object parseCssValue(String value) {
2337
2338            Color c = stringToColor(value);
2339            if (c != null) {
2340                ColorValue cv = new ColorValue();
2341                cv.svalue = value;
2342                cv.c = c;
2343                return cv;
2344            }
2345            return null;
2346        }
2347
2348        Object parseHtmlValue(String value) {
2349            return parseCssValue(value);
2350        }
2351
2352        /**
2353         * Converts a <code>StyleConstants</code> attribute value to
2354         * a CSS attribute value.  If there is no conversion
2355         * returns <code>null</code>.  By default, there is no conversion.
2356         *
2357         * @param key the <code>StyleConstants</code> attribute
2358         * @param value the value of a <code>StyleConstants</code>
2359         *   attribute to be converted
2360         * @return the CSS value that represents the
2361         *   <code>StyleConstants</code> value
2362         */
2363        Object fromStyleConstants(StyleConstants key, Object value) {
2364            ColorValue colorValue = new ColorValue();
2365            colorValue.c = (Color)value;
2366            colorValue.svalue = colorToHex(colorValue.c);
2367            return colorValue;
2368        }
2369
2370        /**
2371         * Converts a CSS attribute value to a <code>StyleConstants</code>
2372         * value.  If there is no conversion, returns <code>null</code>.
2373         * By default, there is no conversion.
2374         *
2375         * @param key the <code>StyleConstants</code> attribute
2376         * @return the <code>StyleConstants</code> attribute value that
2377         *   represents the CSS attribute value
2378         */
2379        Object toStyleConstants(StyleConstants key, View v) {
2380            return c;
2381        }
2382
2383        Color c;
2384    }
2385
2386    @SuppressWarnings("serial") // Same-version serialization only
2387    static class BorderStyle extends CssValue {
2388
2389        CSS.Value getValue() {
2390            return style;
2391        }
2392
2393        Object parseCssValue(String value) {
2394            CSS.Value cssv = CSS.getValue(value);
2395            if (cssv != null) {
2396                if ((cssv == CSS.Value.INSET) ||
2397                    (cssv == CSS.Value.OUTSET) ||
2398                    (cssv == CSS.Value.NONE) ||
2399                    (cssv == CSS.Value.DOTTED) ||
2400                    (cssv == CSS.Value.DASHED) ||
2401                    (cssv == CSS.Value.SOLID) ||
2402                    (cssv == CSS.Value.DOUBLE) ||
2403                    (cssv == CSS.Value.GROOVE) ||
2404                    (cssv == CSS.Value.RIDGE)) {
2405
2406                    BorderStyle bs = new BorderStyle();
2407                    bs.svalue = value;
2408                    bs.style = cssv;
2409                    return bs;
2410                }
2411            }
2412            return null;
2413        }
2414
2415        private void writeObject(java.io.ObjectOutputStream s)
2416                     throws IOException {
2417            s.defaultWriteObject();
2418            if (style == null) {
2419                s.writeObject(null);
2420            }
2421            else {
2422                s.writeObject(style.toString());
2423            }
2424        }
2425
2426        private void readObject(ObjectInputStream s)
2427                throws ClassNotFoundException, IOException {
2428            s.defaultReadObject();
2429            Object value = s.readObject();
2430            if (value != null) {
2431                style = CSS.getValue((String)value);
2432            }
2433        }
2434
2435        // CSS.Values are static, don't archive it.
2436        transient private CSS.Value style;
2437    }
2438
2439    @SuppressWarnings("serial") // Same-version serialization only
2440    static class LengthValue extends CssValue {
2441
2442        /**
2443         * if this length value may be negative.
2444         */
2445        boolean mayBeNegative;
2446
2447        LengthValue() {
2448            this(false);
2449        }
2450
2451        LengthValue(boolean mayBeNegative) {
2452            this.mayBeNegative = mayBeNegative;
2453        }
2454
2455        /**
2456         * Returns the length (span) to use.
2457         */
2458        float getValue() {
2459            return getValue(false);
2460        }
2461
2462        float getValue(boolean isW3CLengthUnits) {
2463            return getValue(0, isW3CLengthUnits);
2464        }
2465
2466        /**
2467         * Returns the length (span) to use. If the value represents
2468         * a percentage, it is scaled based on <code>currentValue</code>.
2469         */
2470        float getValue(float currentValue) {
2471            return getValue(currentValue, false);
2472        }
2473        float getValue(float currentValue, boolean isW3CLengthUnits) {
2474            if (percentage) {
2475                return span * currentValue;
2476            }
2477            return LengthUnit.getValue(span, units, isW3CLengthUnits);
2478        }
2479
2480        /**
2481         * Returns true if the length represents a percentage of the
2482         * containing box.
2483         */
2484        boolean isPercentage() {
2485            return percentage;
2486        }
2487
2488        Object parseCssValue(String value) {
2489            LengthValue lv;
2490            try {
2491                // Assume pixels
2492                float absolute = Float.valueOf(value).floatValue();
2493                lv = new LengthValue();
2494                lv.span = absolute;
2495            } catch (NumberFormatException nfe) {
2496                // Not pixels, use LengthUnit
2497                LengthUnit lu = new LengthUnit(value,
2498                                               LengthUnit.UNINITALIZED_LENGTH,
2499                                               0);
2500
2501                // PENDING: currently, we only support absolute values and
2502                // percentages.
2503                switch (lu.type) {
2504                case 0:
2505                    // Absolute
2506                    lv = new LengthValue();
2507                    lv.span =
2508                        (mayBeNegative) ? lu.value : Math.max(0, lu.value);
2509                    lv.units = lu.units;
2510                    break;
2511                case 1:
2512                    // %
2513                    lv = new LengthValue();
2514                    lv.span = Math.max(0, Math.min(1, lu.value));
2515                    lv.percentage = true;
2516                    break;
2517                default:
2518                    return null;
2519                }
2520            }
2521            lv.svalue = value;
2522            return lv;
2523        }
2524
2525        Object parseHtmlValue(String value) {
2526            if (value.equals(HTML.NULL_ATTRIBUTE_VALUE)) {
2527                value = "1";
2528            }
2529            return parseCssValue(value);
2530        }
2531        /**
2532         * Converts a <code>StyleConstants</code> attribute value to
2533         * a CSS attribute value.  If there is no conversion,
2534         * returns <code>null</code>.  By default, there is no conversion.
2535         *
2536         * @param key the <code>StyleConstants</code> attribute
2537         * @param value the value of a <code>StyleConstants</code>
2538         *   attribute to be converted
2539         * @return the CSS value that represents the
2540         *   <code>StyleConstants</code> value
2541         */
2542        Object fromStyleConstants(StyleConstants key, Object value) {
2543            LengthValue v = new LengthValue();
2544            v.svalue = value.toString();
2545            v.span = ((Float)value).floatValue();
2546            return v;
2547        }
2548
2549        /**
2550         * Converts a CSS attribute value to a <code>StyleConstants</code>
2551         * value.  If there is no conversion, returns <code>null</code>.
2552         * By default, there is no conversion.
2553         *
2554         * @param key the <code>StyleConstants</code> attribute
2555         * @return the <code>StyleConstants</code> attribute value that
2556         *   represents the CSS attribute value
2557         */
2558        Object toStyleConstants(StyleConstants key, View v) {
2559            return new Float(getValue(false));
2560        }
2561
2562        /** If true, span is a percentage value, and that to determine
2563         * the length another value needs to be passed in. */
2564        boolean percentage;
2565        /** Either the absolute value (percentage == false) or
2566         * a percentage value. */
2567        float span;
2568
2569        String units = null;
2570    }
2571
2572
2573    /**
2574     * BorderWidthValue is used to model BORDER_XXX_WIDTH and adds support
2575     * for the thin/medium/thick values.
2576     */
2577    @SuppressWarnings("serial") // Same-version serialization only
2578    static class BorderWidthValue extends LengthValue {
2579        BorderWidthValue(String svalue, int index) {
2580            this.svalue = svalue;
2581            span = values[index];
2582            percentage = false;
2583        }
2584
2585        Object parseCssValue(String value) {
2586            if (value != null) {
2587                if (value.equals("thick")) {
2588                    return new BorderWidthValue(value, 2);
2589                }
2590                else if (value.equals("medium")) {
2591                    return new BorderWidthValue(value, 1);
2592                }
2593                else if (value.equals("thin")) {
2594                    return new BorderWidthValue(value, 0);
2595                }
2596            }
2597            // Assume its a length.
2598            return super.parseCssValue(value);
2599        }
2600
2601        Object parseHtmlValue(String value) {
2602            if (value == HTML.NULL_ATTRIBUTE_VALUE) {
2603                return parseCssValue("medium");
2604            }
2605            return parseCssValue(value);
2606        }
2607
2608        /** Values used to represent border width. */
2609        private static final float[] values = { 1, 2, 4 };
2610   }
2611
2612
2613    /**
2614     * Handles uniquing of CSS values, like lists, and background image
2615     * repeating.
2616     */
2617    @SuppressWarnings("serial") // Same-version serialization only
2618    static class CssValueMapper extends CssValue {
2619        Object parseCssValue(String value) {
2620            Object retValue = cssValueToInternalValueMap.get(value);
2621            if (retValue == null) {
2622                retValue = cssValueToInternalValueMap.get(value.toLowerCase());
2623            }
2624            return retValue;
2625        }
2626
2627
2628        Object parseHtmlValue(String value) {
2629            Object retValue = htmlValueToCssValueMap.get(value);
2630            if (retValue == null) {
2631                retValue = htmlValueToCssValueMap.get(value.toLowerCase());
2632            }
2633            return retValue;
2634        }
2635    }
2636
2637
2638    /**
2639     * Used for background images, to represent the position.
2640     */
2641    @SuppressWarnings("serial") // Same-version serialization only
2642    static class BackgroundPosition extends CssValue {
2643        float horizontalPosition;
2644        float verticalPosition;
2645        // bitmask: bit 0, horizontal relative, bit 1 horizontal relative to
2646        // font size, 2 vertical relative to size, 3 vertical relative to
2647        // font size.
2648        //
2649        short relative;
2650
2651        Object parseCssValue(String value) {
2652            // 'top left' and 'left top' both mean the same as '0% 0%'.
2653            // 'top', 'top center' and 'center top' mean the same as '50% 0%'.
2654            // 'right top' and 'top right' mean the same as '100% 0%'.
2655            // 'left', 'left center' and 'center left' mean the same as
2656            //        '0% 50%'.
2657            // 'center' and 'center center' mean the same as '50% 50%'.
2658            // 'right', 'right center' and 'center right' mean the same as
2659            //        '100% 50%'.
2660            // 'bottom left' and 'left bottom' mean the same as '0% 100%'.
2661            // 'bottom', 'bottom center' and 'center bottom' mean the same as
2662            //        '50% 100%'.
2663            // 'bottom right' and 'right bottom' mean the same as '100% 100%'.
2664            String[]  strings = CSS.parseStrings(value);
2665            int count = strings.length;
2666            BackgroundPosition bp = new BackgroundPosition();
2667            bp.relative = 5;
2668            bp.svalue = value;
2669
2670            if (count > 0) {
2671                // bit 0 for vert, 1 hor, 2 for center
2672                short found = 0;
2673                int index = 0;
2674                while (index < count) {
2675                    // First, check for keywords
2676                    String string = strings[index++];
2677                    if (string.equals("center")) {
2678                        found |= 4;
2679                        continue;
2680                    }
2681                    else {
2682                        if ((found & 1) == 0) {
2683                            if (string.equals("top")) {
2684                                found |= 1;
2685                            }
2686                            else if (string.equals("bottom")) {
2687                                found |= 1;
2688                                bp.verticalPosition = 1;
2689                                continue;
2690                            }
2691                        }
2692                        if ((found & 2) == 0) {
2693                            if (string.equals("left")) {
2694                                found |= 2;
2695                                bp.horizontalPosition = 0;
2696                            }
2697                            else if (string.equals("right")) {
2698                                found |= 2;
2699                                bp.horizontalPosition = 1;
2700                            }
2701                        }
2702                    }
2703                }
2704                if (found != 0) {
2705                    if ((found & 1) == 1) {
2706                        if ((found & 2) == 0) {
2707                            // vert and no horiz.
2708                            bp.horizontalPosition = .5f;
2709                        }
2710                    }
2711                    else if ((found & 2) == 2) {
2712                        // horiz and no vert.
2713                        bp.verticalPosition = .5f;
2714                    }
2715                    else {
2716                        // no horiz, no vert, but center
2717                        bp.horizontalPosition = bp.verticalPosition = .5f;
2718                    }
2719                }
2720                else {
2721                    // Assume lengths
2722                    LengthUnit lu = new LengthUnit(strings[0], (short)0, 0f);
2723
2724                    if (lu.type == 0) {
2725                        bp.horizontalPosition = lu.value;
2726                        bp.relative = (short)(1 ^ bp.relative);
2727                    }
2728                    else if (lu.type == 1) {
2729                        bp.horizontalPosition = lu.value;
2730                    }
2731                    else if (lu.type == 3) {
2732                        bp.horizontalPosition = lu.value;
2733                        bp.relative = (short)((1 ^ bp.relative) | 2);
2734                    }
2735                    if (count > 1) {
2736                        lu = new LengthUnit(strings[1], (short)0, 0f);
2737
2738                        if (lu.type == 0) {
2739                            bp.verticalPosition = lu.value;
2740                            bp.relative = (short)(4 ^ bp.relative);
2741                        }
2742                        else if (lu.type == 1) {
2743                            bp.verticalPosition = lu.value;
2744                        }
2745                        else if (lu.type == 3) {
2746                            bp.verticalPosition = lu.value;
2747                            bp.relative = (short)((4 ^ bp.relative) | 8);
2748                        }
2749                    }
2750                    else {
2751                        bp.verticalPosition = .5f;
2752                    }
2753                }
2754            }
2755            return bp;
2756        }
2757
2758        boolean isHorizontalPositionRelativeToSize() {
2759            return ((relative & 1) == 1);
2760        }
2761
2762        boolean isHorizontalPositionRelativeToFontSize() {
2763            return ((relative & 2) == 2);
2764        }
2765
2766        float getHorizontalPosition() {
2767            return horizontalPosition;
2768        }
2769
2770        boolean isVerticalPositionRelativeToSize() {
2771            return ((relative & 4) == 4);
2772        }
2773
2774        boolean isVerticalPositionRelativeToFontSize() {
2775            return ((relative & 8) == 8);
2776        }
2777
2778        float getVerticalPosition() {
2779            return verticalPosition;
2780        }
2781    }
2782
2783
2784    /**
2785     * Used for BackgroundImages.
2786     */
2787    @SuppressWarnings("serial") // Same-version serialization only
2788    static class BackgroundImage extends CssValue {
2789        private boolean    loadedImage;
2790        private ImageIcon  image;
2791
2792        Object parseCssValue(String value) {
2793            BackgroundImage retValue = new BackgroundImage();
2794            retValue.svalue = value;
2795            return retValue;
2796        }
2797
2798        Object parseHtmlValue(String value) {
2799            return parseCssValue(value);
2800        }
2801
2802        // PENDING: this base is wrong for linked style sheets.
2803        ImageIcon getImage(URL base) {
2804            if (!loadedImage) {
2805                synchronized(this) {
2806                    if (!loadedImage) {
2807                        URL url = CSS.getURL(base, svalue);
2808                        loadedImage = true;
2809                        if (url != null) {
2810                            image = new ImageIcon();
2811                            Image tmpImg = Toolkit.getDefaultToolkit().createImage(url);
2812                            if (tmpImg != null) {
2813                                image.setImage(tmpImg);
2814                            }
2815                        }
2816                    }
2817                }
2818            }
2819            return image;
2820        }
2821    }
2822
2823    /**
2824     * Parses a length value, this is used internally, and never added
2825     * to an AttributeSet or returned to the developer.
2826     */
2827    @SuppressWarnings("serial") // Same-version serialization only
2828    static class LengthUnit implements Serializable {
2829        static Hashtable<String, Float> lengthMapping = new Hashtable<String, Float>(6);
2830        static Hashtable<String, Float> w3cLengthMapping = new Hashtable<String, Float>(6);
2831        static {
2832            lengthMapping.put("pt", new Float(1f));
2833            // Not sure about 1.3, determined by experiementation.
2834            lengthMapping.put("px", new Float(1.3f));
2835            lengthMapping.put("mm", new Float(2.83464f));
2836            lengthMapping.put("cm", new Float(28.3464f));
2837            lengthMapping.put("pc", new Float(12f));
2838            lengthMapping.put("in", new Float(72f));
2839            int res = 72;
2840            try {
2841                res = Toolkit.getDefaultToolkit().getScreenResolution();
2842            } catch (HeadlessException e) {
2843            }
2844            // mapping according to the CSS2 spec
2845            w3cLengthMapping.put("pt", new Float(res/72f));
2846            w3cLengthMapping.put("px", new Float(1f));
2847            w3cLengthMapping.put("mm", new Float(res/25.4f));
2848            w3cLengthMapping.put("cm", new Float(res/2.54f));
2849            w3cLengthMapping.put("pc", new Float(res/6f));
2850            w3cLengthMapping.put("in", new Float(res));
2851        }
2852
2853        LengthUnit(String value, short defaultType, float defaultValue) {
2854            parse(value, defaultType, defaultValue);
2855        }
2856
2857        void parse(String value, short defaultType, float defaultValue) {
2858            type = defaultType;
2859            this.value = defaultValue;
2860
2861            int length = value.length();
2862            if (length > 0 && value.charAt(length - 1) == '%') {
2863                try {
2864                    this.value = Float.valueOf(value.substring(0, length - 1)).
2865                                               floatValue() / 100.0f;
2866                    type = 1;
2867                }
2868                catch (NumberFormatException nfe) { }
2869            }
2870            if (length >= 2) {
2871                units = value.substring(length - 2, length);
2872                Float scale = lengthMapping.get(units);
2873                if (scale != null) {
2874                    try {
2875                        this.value = Float.valueOf(value.substring(0,
2876                               length - 2)).floatValue();
2877                        type = 0;
2878                    }
2879                    catch (NumberFormatException nfe) { }
2880                }
2881                else if (units.equals("em") ||
2882                         units.equals("ex")) {
2883                    try {
2884                        this.value = Float.valueOf(value.substring(0,
2885                                      length - 2)).floatValue();
2886                        type = 3;
2887                    }
2888                    catch (NumberFormatException nfe) { }
2889                }
2890                else if (value.equals("larger")) {
2891                    this.value = 2f;
2892                    type = 2;
2893                }
2894                else if (value.equals("smaller")) {
2895                    this.value = -2;
2896                    type = 2;
2897                }
2898                else {
2899                    // treat like points.
2900                    try {
2901                        this.value = Float.valueOf(value).floatValue();
2902                        type = 0;
2903                    } catch (NumberFormatException nfe) {}
2904                }
2905            }
2906            else if (length > 0) {
2907                // treat like points.
2908                try {
2909                    this.value = Float.valueOf(value).floatValue();
2910                    type = 0;
2911                } catch (NumberFormatException nfe) {}
2912            }
2913        }
2914
2915        float getValue(boolean w3cLengthUnits) {
2916            Hashtable<String, Float> mapping = (w3cLengthUnits) ? w3cLengthMapping : lengthMapping;
2917            float scale = 1;
2918            if (units != null) {
2919                Float scaleFloat = mapping.get(units);
2920                if (scaleFloat != null) {
2921                    scale = scaleFloat.floatValue();
2922                }
2923            }
2924            return this.value * scale;
2925
2926        }
2927
2928        static float getValue(float value, String units, Boolean w3cLengthUnits) {
2929            Hashtable<String, Float> mapping = (w3cLengthUnits) ? w3cLengthMapping : lengthMapping;
2930            float scale = 1;
2931            if (units != null) {
2932                Float scaleFloat = mapping.get(units);
2933                if (scaleFloat != null) {
2934                    scale = scaleFloat.floatValue();
2935                }
2936            }
2937            return value * scale;
2938        }
2939
2940        public String toString() {
2941            return type + " " + value;
2942        }
2943
2944        // 0 - value indicates real value
2945        // 1 - % value, value relative to depends upon key.
2946        //     50% will have a value = .5
2947        // 2 - add value to parent value.
2948        // 3 - em/ex relative to font size of element (except for
2949        //     font-size, which is relative to parent).
2950        short type;
2951        float value;
2952        String units = null;
2953
2954
2955        static final short UNINITALIZED_LENGTH = (short)10;
2956    }
2957
2958
2959    /**
2960     * Class used to parse font property. The font property is shorthand
2961     * for the other font properties. This expands the properties, placing
2962     * them in the attributeset.
2963     */
2964    static class ShorthandFontParser {
2965        /**
2966         * Parses the shorthand font string <code>value</code>, placing the
2967         * result in <code>attr</code>.
2968         */
2969        static void parseShorthandFont(CSS css, String value,
2970                                       MutableAttributeSet attr) {
2971            // font is of the form:
2972            // [ <font-style> || <font-variant> || <font-weight> ]? <font-size>
2973            //   [ / <line-height> ]? <font-family>
2974            String[]   strings = CSS.parseStrings(value);
2975            int        count = strings.length;
2976            int        index = 0;
2977            // bitmask, 1 for style, 2 for variant, 3 for weight
2978            short      found = 0;
2979            int        maxC = Math.min(3, count);
2980
2981            // Check for font-style font-variant font-weight
2982            while (index < maxC) {
2983                if ((found & 1) == 0 && isFontStyle(strings[index])) {
2984                    css.addInternalCSSValue(attr, CSS.Attribute.FONT_STYLE,
2985                                            strings[index++]);
2986                    found |= 1;
2987                }
2988                else if ((found & 2) == 0 && isFontVariant(strings[index])) {
2989                    css.addInternalCSSValue(attr, CSS.Attribute.FONT_VARIANT,
2990                                            strings[index++]);
2991                    found |= 2;
2992                }
2993                else if ((found & 4) == 0 && isFontWeight(strings[index])) {
2994                    css.addInternalCSSValue(attr, CSS.Attribute.FONT_WEIGHT,
2995                                            strings[index++]);
2996                    found |= 4;
2997                }
2998                else if (strings[index].equals("normal")) {
2999                    index++;
3000                }
3001                else {
3002                    break;
3003                }
3004            }
3005            if ((found & 1) == 0) {
3006                css.addInternalCSSValue(attr, CSS.Attribute.FONT_STYLE,
3007                                        "normal");
3008            }
3009            if ((found & 2) == 0) {
3010                css.addInternalCSSValue(attr, CSS.Attribute.FONT_VARIANT,
3011                                        "normal");
3012            }
3013            if ((found & 4) == 0) {
3014                css.addInternalCSSValue(attr, CSS.Attribute.FONT_WEIGHT,
3015                                        "normal");
3016            }
3017
3018            // string at index should be the font-size
3019            if (index < count) {
3020                String fontSize = strings[index];
3021                int slashIndex = fontSize.indexOf('/');
3022
3023                if (slashIndex != -1) {
3024                    fontSize = fontSize.substring(0, slashIndex);
3025                    strings[index] = strings[index].substring(slashIndex);
3026                }
3027                else {
3028                    index++;
3029                }
3030                css.addInternalCSSValue(attr, CSS.Attribute.FONT_SIZE,
3031                                        fontSize);
3032            }
3033            else {
3034                css.addInternalCSSValue(attr, CSS.Attribute.FONT_SIZE,
3035                                        "medium");
3036            }
3037
3038            // Check for line height
3039            if (index < count && strings[index].startsWith("/")) {
3040                String lineHeight = null;
3041                if (strings[index].equals("/")) {
3042                    if (++index < count) {
3043                        lineHeight = strings[index++];
3044                    }
3045                }
3046                else {
3047                    lineHeight = strings[index++].substring(1);
3048                }
3049                // line height
3050                if (lineHeight != null) {
3051                    css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT,
3052                                            lineHeight);
3053                }
3054                else {
3055                    css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT,
3056                                            "normal");
3057                }
3058            }
3059            else {
3060                css.addInternalCSSValue(attr, CSS.Attribute.LINE_HEIGHT,
3061                                        "normal");
3062            }
3063
3064            // remainder of strings are font-family
3065            if (index < count) {
3066                String family = strings[index++];
3067
3068                while (index < count) {
3069                    family += " " + strings[index++];
3070                }
3071                css.addInternalCSSValue(attr, CSS.Attribute.FONT_FAMILY,
3072                                        family);
3073            }
3074            else {
3075                css.addInternalCSSValue(attr, CSS.Attribute.FONT_FAMILY,
3076                                        Font.SANS_SERIF);
3077            }
3078        }
3079
3080        private static boolean isFontStyle(String string) {
3081            return (string.equals("italic") ||
3082                    string.equals("oblique"));
3083        }
3084
3085        private static boolean isFontVariant(String string) {
3086            return (string.equals("small-caps"));
3087        }
3088
3089        private static boolean isFontWeight(String string) {
3090            if (string.equals("bold") || string.equals("bolder") ||
3091                string.equals("italic") || string.equals("lighter")) {
3092                return true;
3093            }
3094            // test for 100-900
3095            return (string.length() == 3 &&
3096                    string.charAt(0) >= '1' && string.charAt(0) <= '9' &&
3097                    string.charAt(1) == '0' && string.charAt(2) == '0');
3098        }
3099
3100    }
3101
3102
3103    /**
3104     * Parses the background property into its intrinsic values.
3105     */
3106    static class ShorthandBackgroundParser {
3107        /**
3108         * Parses the shorthand font string <code>value</code>, placing the
3109         * result in <code>attr</code>.
3110         */
3111        static void parseShorthandBackground(CSS css, String value,
3112                                             MutableAttributeSet attr) {
3113            String[] strings = parseStrings(value);
3114            int count = strings.length;
3115            int index = 0;
3116            // bitmask: 0 for image, 1 repeat, 2 attachment, 3 position,
3117            //          4 color
3118            short found = 0;
3119
3120            while (index < count) {
3121                String string = strings[index++];
3122                if ((found & 1) == 0 && isImage(string)) {
3123                    css.addInternalCSSValue(attr, CSS.Attribute.
3124                                            BACKGROUND_IMAGE, string);
3125                    found |= 1;
3126                }
3127                else if ((found & 2) == 0 && isRepeat(string)) {
3128                    css.addInternalCSSValue(attr, CSS.Attribute.
3129                                            BACKGROUND_REPEAT, string);
3130                    found |= 2;
3131                }
3132                else if ((found & 4) == 0 && isAttachment(string)) {
3133                    css.addInternalCSSValue(attr, CSS.Attribute.
3134                                            BACKGROUND_ATTACHMENT, string);
3135                    found |= 4;
3136                }
3137                else if ((found & 8) == 0 && isPosition(string)) {
3138                    if (index < count && isPosition(strings[index])) {
3139                        css.addInternalCSSValue(attr, CSS.Attribute.
3140                                                BACKGROUND_POSITION,
3141                                                string + " " +
3142                                                strings[index++]);
3143                    }
3144                    else {
3145                        css.addInternalCSSValue(attr, CSS.Attribute.
3146                                                BACKGROUND_POSITION, string);
3147                    }
3148                    found |= 8;
3149                }
3150                else if ((found & 16) == 0 && isColor(string)) {
3151                    css.addInternalCSSValue(attr, CSS.Attribute.
3152                                            BACKGROUND_COLOR, string);
3153                    found |= 16;
3154                }
3155            }
3156            if ((found & 1) == 0) {
3157                css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_IMAGE,
3158                                        null);
3159            }
3160            if ((found & 2) == 0) {
3161                css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_REPEAT,
3162                                        "repeat");
3163            }
3164            if ((found & 4) == 0) {
3165                css.addInternalCSSValue(attr, CSS.Attribute.
3166                                        BACKGROUND_ATTACHMENT, "scroll");
3167            }
3168            if ((found & 8) == 0) {
3169                css.addInternalCSSValue(attr, CSS.Attribute.
3170                                        BACKGROUND_POSITION, null);
3171            }
3172            // Currently, there is no good way to express this.
3173            /*
3174            if ((found & 16) == 0) {
3175                css.addInternalCSSValue(attr, CSS.Attribute.BACKGROUND_COLOR,
3176                                        null);
3177            }
3178            */
3179        }
3180
3181        static boolean isImage(String string) {
3182            return (string.startsWith("url(") && string.endsWith(")"));
3183        }
3184
3185        static boolean isRepeat(String string) {
3186            return (string.equals("repeat-x") || string.equals("repeat-y") ||
3187                    string.equals("repeat") || string.equals("no-repeat"));
3188        }
3189
3190        static boolean isAttachment(String string) {
3191            return (string.equals("fixed") || string.equals("scroll"));
3192        }
3193
3194        static boolean isPosition(String string) {
3195            return (string.equals("top") || string.equals("bottom") ||
3196                    string.equals("left") || string.equals("right") ||
3197                    string.equals("center") ||
3198                    (string.length() > 0 &&
3199                     Character.isDigit(string.charAt(0))));
3200        }
3201
3202        static boolean isColor(String string) {
3203            return (CSS.stringToColor(string) != null);
3204        }
3205    }
3206
3207
3208    /**
3209     * Used to parser margin and padding.
3210     */
3211    static class ShorthandMarginParser {
3212        /**
3213         * Parses the shorthand margin/padding/border string
3214         * <code>value</code>, placing the result in <code>attr</code>.
3215         * <code>names</code> give the 4 instrinsic property names.
3216         */
3217        static void parseShorthandMargin(CSS css, String value,
3218                                         MutableAttributeSet attr,
3219                                         CSS.Attribute[] names) {
3220            String[] strings = parseStrings(value);
3221            int count = strings.length;
3222            int index = 0;
3223            switch (count) {
3224            case 0:
3225                // empty string
3226                return;
3227            case 1:
3228                // Identifies all values.
3229                for (int counter = 0; counter < 4; counter++) {
3230                    css.addInternalCSSValue(attr, names[counter], strings[0]);
3231                }
3232                break;
3233            case 2:
3234                // 0 & 2 = strings[0], 1 & 3 = strings[1]
3235                css.addInternalCSSValue(attr, names[0], strings[0]);
3236                css.addInternalCSSValue(attr, names[2], strings[0]);
3237                css.addInternalCSSValue(attr, names[1], strings[1]);
3238                css.addInternalCSSValue(attr, names[3], strings[1]);
3239                break;
3240            case 3:
3241                css.addInternalCSSValue(attr, names[0], strings[0]);
3242                css.addInternalCSSValue(attr, names[1], strings[1]);
3243                css.addInternalCSSValue(attr, names[2], strings[2]);
3244                css.addInternalCSSValue(attr, names[3], strings[1]);
3245                break;
3246            default:
3247                for (int counter = 0; counter < 4; counter++) {
3248                    css.addInternalCSSValue(attr, names[counter],
3249                                            strings[counter]);
3250                }
3251                break;
3252            }
3253        }
3254    }
3255
3256    static class ShorthandBorderParser {
3257        static Attribute[] keys = {
3258            Attribute.BORDER_TOP, Attribute.BORDER_RIGHT,
3259            Attribute.BORDER_BOTTOM, Attribute.BORDER_LEFT,
3260        };
3261
3262        static void parseShorthandBorder(MutableAttributeSet attributes,
3263                                            CSS.Attribute key, String value) {
3264            Object[] parts = new Object[CSSBorder.PARSERS.length];
3265            String[] strings = parseStrings(value);
3266            for (String s : strings) {
3267                boolean valid = false;
3268                for (int i = 0; i < parts.length; i++) {
3269                    Object v = CSSBorder.PARSERS[i].parseCssValue(s);
3270                    if (v != null) {
3271                        if (parts[i] == null) {
3272                            parts[i] = v;
3273                            valid = true;
3274                        }
3275                        break;
3276                    }
3277                }
3278                if (!valid) {
3279                    // Part is non-parseable or occurred more than once.
3280                    return;
3281                }
3282            }
3283
3284            // Unspecified parts get default values.
3285            for (int i = 0; i < parts.length; i++) {
3286                if (parts[i] == null) {
3287                    parts[i] = CSSBorder.DEFAULTS[i];
3288                }
3289            }
3290
3291            // Dispatch collected values to individual properties.
3292            for (int i = 0; i < keys.length; i++) {
3293                if ((key == Attribute.BORDER) || (key == keys[i])) {
3294                    for (int k = 0; k < parts.length; k++) {
3295                        attributes.addAttribute(
3296                                        CSSBorder.ATTRIBUTES[k][i], parts[k]);
3297                    }
3298                }
3299            }
3300        }
3301    }
3302
3303    /**
3304     * Calculate the requirements needed to tile the requirements
3305     * given by the iterator that would be tiled.  The calculation
3306     * takes into consideration margin and border spacing.
3307     */
3308    static SizeRequirements calculateTiledRequirements(LayoutIterator iter, SizeRequirements r) {
3309        long minimum = 0;
3310        long maximum = 0;
3311        long preferred = 0;
3312        int lastMargin = 0;
3313        int totalSpacing = 0;
3314        int n = iter.getCount();
3315        for (int i = 0; i < n; i++) {
3316            iter.setIndex(i);
3317            int margin0 = lastMargin;
3318            int margin1 = (int) iter.getLeadingCollapseSpan();
3319            totalSpacing += Math.max(margin0, margin1);
3320            preferred += (int) iter.getPreferredSpan(0);
3321            minimum += iter.getMinimumSpan(0);
3322            maximum += iter.getMaximumSpan(0);
3323
3324            lastMargin = (int) iter.getTrailingCollapseSpan();
3325        }
3326        totalSpacing += lastMargin;
3327        totalSpacing += 2 * iter.getBorderWidth();
3328
3329        // adjust for the spacing area
3330        minimum += totalSpacing;
3331        preferred += totalSpacing;
3332        maximum += totalSpacing;
3333
3334        // set return value
3335        if (r == null) {
3336            r = new SizeRequirements();
3337        }
3338        r.minimum = (minimum > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)minimum;
3339        r.preferred = (preferred > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) preferred;
3340        r.maximum = (maximum > Integer.MAX_VALUE) ? Integer.MAX_VALUE :(int) maximum;
3341        return r;
3342    }
3343
3344    /**
3345     * Calculate a tiled layout for the given iterator.
3346     * This should be done collapsing the neighboring
3347     * margins to be a total of the maximum of the two
3348     * neighboring margin areas as described in the CSS spec.
3349     */
3350    static void calculateTiledLayout(LayoutIterator iter, int targetSpan) {
3351
3352        /*
3353         * first pass, calculate the preferred sizes, adjustments needed because
3354         * of margin collapsing, and the flexibility to adjust the sizes.
3355         */
3356        long preferred = 0;
3357        long currentPreferred;
3358        int lastMargin = 0;
3359        int totalSpacing = 0;
3360        int n = iter.getCount();
3361        int adjustmentWeightsCount = LayoutIterator.WorstAdjustmentWeight + 1;
3362        //max gain we can get adjusting elements with adjustmentWeight <= i
3363        long gain[] = new long[adjustmentWeightsCount];
3364        //max loss we can get adjusting elements with adjustmentWeight <= i
3365        long loss[] = new long[adjustmentWeightsCount];
3366
3367        for (int i = 0; i < adjustmentWeightsCount; i++) {
3368            gain[i] = loss[i] = 0;
3369        }
3370        for (int i = 0; i < n; i++) {
3371            iter.setIndex(i);
3372            int margin0 = lastMargin;
3373            int margin1 = (int) iter.getLeadingCollapseSpan();
3374
3375            iter.setOffset(Math.max(margin0, margin1));
3376            totalSpacing += iter.getOffset();
3377
3378            currentPreferred = (long)iter.getPreferredSpan(targetSpan);
3379            iter.setSpan((int) currentPreferred);
3380            preferred += currentPreferred;
3381            gain[iter.getAdjustmentWeight()] +=
3382                (long)iter.getMaximumSpan(targetSpan) - currentPreferred;
3383            loss[iter.getAdjustmentWeight()] +=
3384                currentPreferred - (long)iter.getMinimumSpan(targetSpan);
3385            lastMargin = (int) iter.getTrailingCollapseSpan();
3386        }
3387        totalSpacing += lastMargin;
3388        totalSpacing += 2 * iter.getBorderWidth();
3389
3390        for (int i = 1; i < adjustmentWeightsCount; i++) {
3391            gain[i] += gain[i - 1];
3392            loss[i] += loss[i - 1];
3393        }
3394
3395        /*
3396         * Second pass, expand or contract by as much as possible to reach
3397         * the target span.  This takes the margin collapsing into account
3398         * prior to adjusting the span.
3399         */
3400
3401        // determine the adjustment to be made
3402        int allocated = targetSpan - totalSpacing;
3403        long desiredAdjustment = allocated - preferred;
3404        long adjustmentsArray[] = (desiredAdjustment > 0) ? gain : loss;
3405        desiredAdjustment = Math.abs(desiredAdjustment);
3406        int adjustmentLevel = 0;
3407        for (;adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight;
3408             adjustmentLevel++) {
3409            // adjustmentsArray[] is sorted. I do not bother about
3410            // binary search though
3411            if (adjustmentsArray[adjustmentLevel] >= desiredAdjustment) {
3412                break;
3413            }
3414        }
3415        float adjustmentFactor = 0.0f;
3416        if (adjustmentLevel <= LayoutIterator.WorstAdjustmentWeight) {
3417            desiredAdjustment -= (adjustmentLevel > 0) ?
3418                adjustmentsArray[adjustmentLevel - 1] : 0;
3419            if (desiredAdjustment != 0) {
3420                float maximumAdjustment =
3421                    adjustmentsArray[adjustmentLevel] -
3422                    ((adjustmentLevel > 0) ?
3423                     adjustmentsArray[adjustmentLevel - 1] : 0
3424                     );
3425                adjustmentFactor = desiredAdjustment / maximumAdjustment;
3426            }
3427        }
3428        // make the adjustments
3429        int totalOffset = (int)iter.getBorderWidth();
3430        for (int i = 0; i < n; i++) {
3431            iter.setIndex(i);
3432            iter.setOffset( iter.getOffset() + totalOffset);
3433            if (iter.getAdjustmentWeight() < adjustmentLevel) {
3434                iter.setSpan((int)
3435                             ((allocated > preferred) ?
3436                              Math.floor(iter.getMaximumSpan(targetSpan)) :
3437                              Math.ceil(iter.getMinimumSpan(targetSpan))
3438                              )
3439                             );
3440            } else if (iter.getAdjustmentWeight() == adjustmentLevel) {
3441                int availableSpan = (allocated > preferred) ?
3442                    (int) iter.getMaximumSpan(targetSpan) - iter.getSpan() :
3443                    iter.getSpan() - (int) iter.getMinimumSpan(targetSpan);
3444                int adj = (int)Math.floor(adjustmentFactor * availableSpan);
3445                iter.setSpan(iter.getSpan() +
3446                             ((allocated > preferred) ? adj : -adj));
3447            }
3448            totalOffset = (int) Math.min((long) iter.getOffset() +
3449                                         (long) iter.getSpan(),
3450                                         Integer.MAX_VALUE);
3451        }
3452
3453        // while rounding we could lose several pixels.
3454        int roundError = targetSpan - totalOffset -
3455            (int)iter.getTrailingCollapseSpan() -
3456            (int)iter.getBorderWidth();
3457        int adj = (roundError > 0) ? 1 : -1;
3458        roundError *= adj;
3459
3460        boolean canAdjust = true;
3461        while (roundError > 0 && canAdjust) {
3462            // check for infinite loop
3463            canAdjust = false;
3464            int offsetAdjust = 0;
3465            // try to distribute roundError. one pixel per cell
3466            for (int i = 0; i < n; i++) {
3467                iter.setIndex(i);
3468                iter.setOffset(iter.getOffset() + offsetAdjust);
3469                int curSpan = iter.getSpan();
3470                if (roundError > 0) {
3471                    int boundGap = (adj > 0) ?
3472                        (int)Math.floor(iter.getMaximumSpan(targetSpan)) - curSpan :
3473                        curSpan - (int)Math.ceil(iter.getMinimumSpan(targetSpan));
3474                    if (boundGap >= 1) {
3475                        canAdjust = true;
3476                        iter.setSpan(curSpan + adj);
3477                        offsetAdjust += adj;
3478                        roundError--;
3479                    }
3480                }
3481            }
3482        }
3483    }
3484
3485    /**
3486     * An iterator to express the requirements to use when computing
3487     * layout.
3488     */
3489    interface LayoutIterator {
3490
3491        void setOffset(int offs);
3492
3493        int getOffset();
3494
3495        void setSpan(int span);
3496
3497        int getSpan();
3498
3499        int getCount();
3500
3501        void setIndex(int i);
3502
3503        float getMinimumSpan(float parentSpan);
3504
3505        float getPreferredSpan(float parentSpan);
3506
3507        float getMaximumSpan(float parentSpan);
3508
3509        int getAdjustmentWeight(); //0 is the best weight WorstAdjustmentWeight is a worst one
3510
3511        //float getAlignment();
3512
3513        float getBorderWidth();
3514
3515        float getLeadingCollapseSpan();
3516
3517        float getTrailingCollapseSpan();
3518        public static final int WorstAdjustmentWeight = 2;
3519    }
3520
3521    //
3522    // Serialization support
3523    //
3524
3525    private void writeObject(java.io.ObjectOutputStream s)
3526        throws IOException
3527    {
3528        s.defaultWriteObject();
3529
3530        // Determine what values in valueConvertor need to be written out.
3531        Enumeration<?> keys = valueConvertor.keys();
3532        s.writeInt(valueConvertor.size());
3533        if (keys != null) {
3534            while (keys.hasMoreElements()) {
3535                Object key = keys.nextElement();
3536                Object value = valueConvertor.get(key);
3537                if (!(key instanceof Serializable) &&
3538                    (key = StyleContext.getStaticAttributeKey(key)) == null) {
3539                    // Should we throw an exception here?
3540                    key = null;
3541                    value = null;
3542                }
3543                else if (!(value instanceof Serializable) &&
3544                    (value = StyleContext.getStaticAttributeKey(value)) == null){
3545                    // Should we throw an exception here?
3546                    key = null;
3547                    value = null;
3548                }
3549                s.writeObject(key);
3550                s.writeObject(value);
3551            }
3552        }
3553    }
3554
3555    private void readObject(ObjectInputStream s)
3556      throws ClassNotFoundException, IOException
3557    {
3558        ObjectInputStream.GetField f = s.readFields();
3559        int newBaseFontSize = f.get("baseFontSize", 0);
3560        setBaseFontSize(newBaseFontSize);
3561
3562        // Reconstruct the hashtable.
3563        int numValues = s.readInt();
3564        valueConvertor = new Hashtable<>(Math.max(1, numValues));
3565        while (numValues-- > 0) {
3566            Object key = s.readObject();
3567            Object value = s.readObject();
3568            Object staticKey = StyleContext.getStaticAttribute(key);
3569            if (staticKey != null) {
3570                key = staticKey;
3571            }
3572            Object staticValue = StyleContext.getStaticAttribute(value);
3573            if (staticValue != null) {
3574                value = staticValue;
3575            }
3576            if (key != null && value != null) {
3577                valueConvertor.put(key, value);
3578            }
3579        }
3580    }
3581
3582
3583    /*
3584     * we need StyleSheet for resolving lenght units. (see
3585     * isW3CLengthUnits)
3586     * we can not pass stylesheet for handling relative sizes. (do not
3587     * think changing public API is necessary)
3588     * CSS is not likely to be accessed from more then one thread.
3589     * Having local storage for StyleSheet for resolving relative
3590     * sizes is safe
3591     *
3592     * idk 08/30/2004
3593     */
3594    private StyleSheet getStyleSheet(StyleSheet ss) {
3595        if (ss != null) {
3596            styleSheet = ss;
3597        }
3598        return styleSheet;
3599    }
3600    //
3601    // Instance variables
3602    //
3603
3604    /** Maps from CSS key to CssValue. */
3605    private transient Hashtable<Object, Object> valueConvertor;
3606
3607    /** Size used for relative units. */
3608    private int baseFontSize;
3609
3610    private transient StyleSheet styleSheet = null;
3611
3612    static int baseFontSizeIndex = 3;
3613}
3614