Source Code Cross Referenced for StyleSheet.java in  » 6.0-JDK-Core » swing » javax » swing » text » html » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing.text.html 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1997-2005 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025        package javax.swing.text.html;
0026
0027        import sun.swing.SwingUtilities2;
0028        import java.util.*;
0029        import java.awt.*;
0030        import java.io.*;
0031        import java.net.*;
0032        import javax.swing.Icon;
0033        import javax.swing.ImageIcon;
0034        import javax.swing.border.*;
0035        import javax.swing.event.ChangeListener;
0036        import javax.swing.text.*;
0037
0038        /**
0039         * Support for defining the visual characteristics of
0040         * HTML views being rendered.  The StyleSheet is used to
0041         * translate the HTML model into visual characteristics.
0042         * This enables views to be customized by a look-and-feel,
0043         * multiple views over the same model can be rendered
0044         * differently, etc.  This can be thought of as a CSS
0045         * rule repository.  The key for CSS attributes is an
0046         * object of type CSS.Attribute.  The type of the value
0047         * is up to the StyleSheet implementation, but the
0048         * <code>toString</code> method is required
0049         * to return a string representation of CSS value.
0050         * <p>
0051         * The primary entry point for HTML View implementations
0052         * to get their attributes is the 
0053         * <a href="#getViewAttributes">getViewAttributes</a>
0054         * method.  This should be implemented to establish the
0055         * desired policy used to associate attributes with the view.
0056         * Each HTMLEditorKit (i.e. and therefore each associated
0057         * JEditorPane) can have its own StyleSheet, but by default one
0058         * sheet will be shared by all of the HTMLEditorKit instances.
0059         * HTMLDocument instance can also have a StyleSheet, which
0060         * holds the document-specific CSS specifications.
0061         * <p>
0062         * In order for Views to store less state and therefore be
0063         * more lightweight, the StyleSheet can act as a factory for
0064         * painters that handle some of the rendering tasks.  This allows
0065         * implementations to determine what they want to cache
0066         * and have the sharing potentially at the level that a
0067         * selector is common to multiple views.  Since the StyleSheet
0068         * may be used by views over multiple documents and typically
0069         * the HTML attributes don't effect the selector being used,
0070         * the potential for sharing is significant.
0071         * <p>
0072         * The rules are stored as named styles, and other information
0073         * is stored to translate the context of an element to a 
0074         * rule quickly.  The following code fragment will display
0075         * the named styles, and therefore the CSS rules contained.
0076         * <code><pre>
0077         * &nbsp; 
0078         * &nbsp; import java.util.*;
0079         * &nbsp; import javax.swing.text.*;
0080         * &nbsp; import javax.swing.text.html.*;
0081         * &nbsp; 
0082         * &nbsp; public class ShowStyles {
0083         * &nbsp; 
0084         * &nbsp;     public static void main(String[] args) {
0085         * &nbsp; 	HTMLEditorKit kit = new HTMLEditorKit();
0086         * &nbsp; 	HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
0087         * &nbsp; 	StyleSheet styles = doc.getStyleSheet();
0088         * &nbsp; 	
0089         * &nbsp; 	Enumeration rules = styles.getStyleNames();
0090         * &nbsp; 	while (rules.hasMoreElements()) {
0091         * &nbsp; 	    String name = (String) rules.nextElement();
0092         * &nbsp; 	    Style rule = styles.getStyle(name);
0093         * &nbsp; 	    System.out.println(rule.toString());
0094         * &nbsp; 	}
0095         * &nbsp; 	System.exit(0);
0096         * &nbsp;     }
0097         * &nbsp; }
0098         * &nbsp; 
0099         * </pre></code>
0100         * <p>
0101         * The semantics for when a CSS style should overide visual attributes
0102         * defined by an element are not well defined. For example, the html
0103         * <code>&lt;body bgcolor=red&gt;</code> makes the body have a red
0104         * background. But if the html file also contains the CSS rule
0105         * <code>body { background: blue }</code> it becomes less clear as to
0106         * what color the background of the body should be. The current
0107         * implemention gives visual attributes defined in the element the
0108         * highest precedence, that is they are always checked before any styles.
0109         * Therefore, in the previous example the background would have a
0110         * red color as the body element defines the background color to be red.
0111         * <p>
0112         * As already mentioned this supports CSS. We don't support the full CSS
0113         * spec. Refer to the javadoc of the CSS class to see what properties
0114         * we support. The two major CSS parsing related
0115         * concepts we do not currently
0116         * support are pseudo selectors, such as <code>A:link { color: red }</code>,
0117         * and the <code>important</code> modifier.
0118         * <p>
0119         * <font color="red">Note: This implementation is currently
0120         * incomplete.  It can be replaced with alternative implementations
0121         * that are complete.  Future versions of this class will provide
0122         * better CSS support.</font>
0123         *
0124         * @author  Timothy Prinzing
0125         * @author  Sunita Mani
0126         * @author  Sara Swanson
0127         * @author  Jill Nakata
0128         * @version 1.97 05/05/07
0129         */
0130        public class StyleSheet extends StyleContext {
0131            // As the javadoc states, this class maintains a mapping between
0132            // a CSS selector (such as p.bar) and a Style.
0133            // This consists of a number of parts:
0134            // . Each selector is broken down into its constituent simple selectors,
0135            //   and stored in an inverted graph, for example:
0136            //     p { color: red } ol p { font-size: 10pt } ul p { font-size: 12pt }
0137            //   results in the graph:
0138            //          root
0139            //           |
0140            //           p
0141            //          / \
0142            //         ol ul
0143            //   each node (an instance of SelectorMapping) has an associated
0144            //   specificity and potentially a Style.
0145            // . Every rule that is asked for (either by way of getRule(String) or
0146            //   getRule(HTML.Tag, Element)) results in a unique instance of
0147            //   ResolvedStyle. ResolvedStyles contain the AttributeSets from the
0148            //   SelectorMapping.
0149            // . When a new rule is created it is inserted into the graph, and
0150            //   the AttributeSets of each ResolvedStyles are updated appropriately.
0151            // . This class creates special AttributeSets, LargeConversionSet and
0152            //   SmallConversionSet, that maintain a mapping between StyleConstants
0153            //   and CSS so that developers that wish to use the StyleConstants
0154            //   methods can do so.
0155            // . When one of the AttributeSets is mutated by way of a
0156            //   StyleConstants key, all the associated CSS keys are removed. This is
0157            //   done so that the two representations don't get out of sync. For
0158            //   example, if the developer adds StyleConsants.BOLD, FALSE to an
0159            //   AttributeSet that contains HTML.Tag.B, the HTML.Tag.B entry will
0160            //   be removed.
0161
0162            /**
0163             * Construct a StyleSheet
0164             */
0165            public StyleSheet() {
0166                super ();
0167                selectorMapping = new SelectorMapping(0);
0168                resolvedStyles = new Hashtable();
0169                if (css == null) {
0170                    css = new CSS();
0171                }
0172            }
0173
0174            /**
0175             * Fetches the style to use to render the given type
0176             * of HTML tag.  The element given is representing
0177             * the tag and can be used to determine the nesting
0178             * for situations where the attributes will differ
0179             * if nesting inside of elements.
0180             *
0181             * @param t the type to translate to visual attributes
0182             * @param e the element representing the tag; the element
0183             *  can be used to determine the nesting for situations where
0184             *  the attributes will differ if nested inside of other
0185             *  elements
0186             * @return the set of CSS attributes to use to render
0187             *  the tag
0188             */
0189            public Style getRule(HTML.Tag t, Element e) {
0190                SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
0191
0192                try {
0193                    // Build an array of all the parent elements.
0194                    Vector searchContext = sb.getVector();
0195
0196                    for (Element p = e; p != null; p = p.getParentElement()) {
0197                        searchContext.addElement(p);
0198                    }
0199
0200                    // Build a fully qualified selector.
0201                    int n = searchContext.size();
0202                    StringBuffer cacheLookup = sb.getStringBuffer();
0203                    AttributeSet attr;
0204                    String eName;
0205                    Object name;
0206
0207                    // >= 1 as the HTML.Tag for the 0th element is passed in.
0208                    for (int counter = n - 1; counter >= 1; counter--) {
0209                        e = (Element) searchContext.elementAt(counter);
0210                        attr = e.getAttributes();
0211                        name = attr.getAttribute(StyleConstants.NameAttribute);
0212                        eName = name.toString();
0213                        cacheLookup.append(eName);
0214                        if (attr != null) {
0215                            if (attr.isDefined(HTML.Attribute.ID)) {
0216                                cacheLookup.append('#');
0217                                cacheLookup.append(attr
0218                                        .getAttribute(HTML.Attribute.ID));
0219                            } else if (attr.isDefined(HTML.Attribute.CLASS)) {
0220                                cacheLookup.append('.');
0221                                cacheLookup.append(attr
0222                                        .getAttribute(HTML.Attribute.CLASS));
0223                            }
0224                        }
0225                        cacheLookup.append(' ');
0226                    }
0227                    cacheLookup.append(t.toString());
0228                    e = (Element) searchContext.elementAt(0);
0229                    attr = e.getAttributes();
0230                    if (e.isLeaf()) {
0231                        // For leafs, we use the second tier attributes.
0232                        Object testAttr = attr.getAttribute(t);
0233                        if (testAttr instanceof  AttributeSet) {
0234                            attr = (AttributeSet) testAttr;
0235                        } else {
0236                            attr = null;
0237                        }
0238                    }
0239                    if (attr != null) {
0240                        if (attr.isDefined(HTML.Attribute.ID)) {
0241                            cacheLookup.append('#');
0242                            cacheLookup.append(attr
0243                                    .getAttribute(HTML.Attribute.ID));
0244                        } else if (attr.isDefined(HTML.Attribute.CLASS)) {
0245                            cacheLookup.append('.');
0246                            cacheLookup.append(attr
0247                                    .getAttribute(HTML.Attribute.CLASS));
0248                        }
0249                    }
0250
0251                    Style style = getResolvedStyle(cacheLookup.toString(),
0252                            searchContext, t);
0253                    return style;
0254                } finally {
0255                    SearchBuffer.releaseSearchBuffer(sb);
0256                }
0257            }
0258
0259            /**
0260             * Fetches the rule that best matches the selector given
0261             * in string form. Where <code>selector</code> is a space separated
0262             * String of the element names. For example, <code>selector</code>
0263             * might be 'html body tr td''<p>
0264             * The attributes of the returned Style will change
0265             * as rules are added and removed. That is if you to ask for a rule
0266             * with a selector "table p" and a new rule was added with a selector
0267             * of "p" the returned Style would include the new attributes from
0268             * the rule "p".
0269             */
0270            public Style getRule(String selector) {
0271                selector = cleanSelectorString(selector);
0272                if (selector != null) {
0273                    Style style = getResolvedStyle(selector);
0274                    return style;
0275                }
0276                return null;
0277            }
0278
0279            /**
0280             * Adds a set of rules to the sheet.  The rules are expected to
0281             * be in valid CSS format.  Typically this would be called as
0282             * a result of parsing a &lt;style&gt; tag.
0283             */
0284            public void addRule(String rule) {
0285                if (rule != null) {
0286                    //tweaks to control display properties
0287                    //see BasicEditorPaneUI
0288                    final String baseUnitsDisable = "BASE_SIZE_DISABLE";
0289                    final String baseUnits = "BASE_SIZE ";
0290                    final String w3cLengthUnitsEnable = "W3C_LENGTH_UNITS_ENABLE";
0291                    final String w3cLengthUnitsDisable = "W3C_LENGTH_UNITS_DISABLE";
0292                    if (rule == baseUnitsDisable) {
0293                        sizeMap = sizeMapDefault;
0294                    } else if (rule.startsWith(baseUnits)) {
0295                        rebaseSizeMap(Integer.parseInt(rule.substring(baseUnits
0296                                .length())));
0297                    } else if (rule == w3cLengthUnitsEnable) {
0298                        w3cLengthUnits = true;
0299                    } else if (rule == w3cLengthUnitsDisable) {
0300                        w3cLengthUnits = false;
0301                    } else {
0302                        CssParser parser = new CssParser();
0303                        try {
0304                            parser.parse(getBase(), new StringReader(rule),
0305                                    false, false);
0306                        } catch (IOException ioe) {
0307                        }
0308                    }
0309                }
0310            }
0311
0312            /**
0313             * Translates a CSS declaration to an AttributeSet that represents
0314             * the CSS declaration.  Typically this would be called as a
0315             * result of encountering an HTML style attribute.
0316             */
0317            public AttributeSet getDeclaration(String decl) {
0318                if (decl == null) {
0319                    return SimpleAttributeSet.EMPTY;
0320                }
0321                CssParser parser = new CssParser();
0322                return parser.parseDeclaration(decl);
0323            }
0324
0325            /**
0326             * Loads a set of rules that have been specified in terms of
0327             * CSS1 grammar.  If there are collisions with existing rules,
0328             * the newly specified rule will win.
0329             *
0330             * @param in the stream to read the CSS grammar from
0331             * @param ref the reference URL.  This value represents the
0332             *  location of the stream and may be null.  All relative
0333             *  URLs specified in the stream will be based upon this
0334             *  parameter.
0335             */
0336            public void loadRules(Reader in, URL ref) throws IOException {
0337                CssParser parser = new CssParser();
0338                parser.parse(ref, in, false, false);
0339            }
0340
0341            /**
0342             * Fetches a set of attributes to use in the view for
0343             * displaying.  This is basically a set of attributes that
0344             * can be used for View.getAttributes.
0345             */
0346            public AttributeSet getViewAttributes(View v) {
0347                return new ViewAttributeSet(v);
0348            }
0349
0350            /**
0351             * Removes a named style previously added to the document.
0352             *
0353             * @param nm  the name of the style to remove
0354             */
0355            public void removeStyle(String nm) {
0356                Style aStyle = getStyle(nm);
0357
0358                if (aStyle != null) {
0359                    String selector = cleanSelectorString(nm);
0360                    String[] selectors = getSimpleSelectors(selector);
0361                    synchronized (this ) {
0362                        SelectorMapping mapping = getRootSelectorMapping();
0363                        for (int i = selectors.length - 1; i >= 0; i--) {
0364                            mapping = mapping.getChildSelectorMapping(
0365                                    selectors[i], true);
0366                        }
0367                        Style rule = mapping.getStyle();
0368                        if (rule != null) {
0369                            mapping.setStyle(null);
0370                            if (resolvedStyles.size() > 0) {
0371                                Enumeration values = resolvedStyles.elements();
0372                                while (values.hasMoreElements()) {
0373                                    ResolvedStyle style = (ResolvedStyle) values
0374                                            .nextElement();
0375                                    style.removeStyle(rule);
0376                                }
0377                            }
0378                        }
0379                    }
0380                }
0381                super .removeStyle(nm);
0382            }
0383
0384            /**
0385             * Adds the rules from the StyleSheet <code>ss</code> to those of
0386             * the receiver. <code>ss's</code> rules will override the rules of
0387             * any previously added style sheets. An added StyleSheet will never
0388             * override the rules of the receiving style sheet.
0389             *
0390             * @since 1.3
0391             */
0392            public void addStyleSheet(StyleSheet ss) {
0393                synchronized (this ) {
0394                    if (linkedStyleSheets == null) {
0395                        linkedStyleSheets = new Vector();
0396                    }
0397                    if (!linkedStyleSheets.contains(ss)) {
0398                        int index = 0;
0399                        if (ss instanceof  javax.swing.plaf.UIResource
0400                                && linkedStyleSheets.size() > 1) {
0401                            index = linkedStyleSheets.size() - 1;
0402                        }
0403                        linkedStyleSheets.insertElementAt(ss, index);
0404                        linkStyleSheetAt(ss, index);
0405                    }
0406                }
0407            }
0408
0409            /**
0410             * Removes the StyleSheet <code>ss</code> from those of the receiver.
0411             *
0412             * @since 1.3
0413             */
0414            public void removeStyleSheet(StyleSheet ss) {
0415                synchronized (this ) {
0416                    if (linkedStyleSheets != null) {
0417                        int index = linkedStyleSheets.indexOf(ss);
0418                        if (index != -1) {
0419                            linkedStyleSheets.removeElementAt(index);
0420                            unlinkStyleSheet(ss, index);
0421                            if (index == 0 && linkedStyleSheets.size() == 0) {
0422                                linkedStyleSheets = null;
0423                            }
0424                        }
0425                    }
0426                }
0427            }
0428
0429            //
0430            // The following is used to import style sheets.
0431            //
0432
0433            /**
0434             * Returns an array of the linked StyleSheets. Will return null
0435             * if there are no linked StyleSheets.
0436             *
0437             * @since 1.3
0438             */
0439            public StyleSheet[] getStyleSheets() {
0440                StyleSheet[] retValue;
0441
0442                synchronized (this ) {
0443                    if (linkedStyleSheets != null) {
0444                        retValue = new StyleSheet[linkedStyleSheets.size()];
0445                        linkedStyleSheets.copyInto(retValue);
0446                    } else {
0447                        retValue = null;
0448                    }
0449                }
0450                return retValue;
0451            }
0452
0453            /**
0454             * Imports a style sheet from <code>url</code>. The resulting rules
0455             * are directly added to the receiver. If you do not want the rules
0456             * to become part of the receiver, create a new StyleSheet and use
0457             * addStyleSheet to link it in.
0458             *
0459             * @since 1.3
0460             */
0461            public void importStyleSheet(URL url) {
0462                try {
0463                    InputStream is;
0464
0465                    is = url.openStream();
0466                    Reader r = new BufferedReader(new InputStreamReader(is));
0467                    CssParser parser = new CssParser();
0468                    parser.parse(url, r, false, true);
0469                    r.close();
0470                    is.close();
0471                } catch (Throwable e) {
0472                    // on error we simply have no styles... the html
0473                    // will look mighty wrong but still function.
0474                }
0475            }
0476
0477            /**
0478             * Sets the base. All import statements that are relative, will be
0479             * relative to <code>base</code>.
0480             *
0481             * @since 1.3
0482             */
0483            public void setBase(URL base) {
0484                this .base = base;
0485            }
0486
0487            /**
0488             * Returns the base.
0489             *
0490             * @since 1.3
0491             */
0492            public URL getBase() {
0493                return base;
0494            }
0495
0496            /**
0497             * Adds a CSS attribute to the given set.
0498             *
0499             * @since 1.3
0500             */
0501            public void addCSSAttribute(MutableAttributeSet attr,
0502                    CSS.Attribute key, String value) {
0503                css.addInternalCSSValue(attr, key, value);
0504            }
0505
0506            /**
0507             * Adds a CSS attribute to the given set.
0508             *
0509             * @since 1.3
0510             */
0511            public boolean addCSSAttributeFromHTML(MutableAttributeSet attr,
0512                    CSS.Attribute key, String value) {
0513                Object iValue = css.getCssValue(key, value);
0514                if (iValue != null) {
0515                    attr.addAttribute(key, iValue);
0516                    return true;
0517                }
0518                return false;
0519            }
0520
0521            // ---- Conversion functionality ---------------------------------
0522
0523            /**
0524             * Converts a set of HTML attributes to an equivalent
0525             * set of CSS attributes.
0526             *
0527             * @param htmlAttrSet AttributeSet containing the HTML attributes.
0528             */
0529            public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet) {
0530                AttributeSet cssAttrSet = css.translateHTMLToCSS(htmlAttrSet);
0531
0532                MutableAttributeSet cssStyleSet = addStyle(null, null);
0533                cssStyleSet.addAttributes(cssAttrSet);
0534
0535                return cssStyleSet;
0536            }
0537
0538            /**
0539             * Adds an attribute to the given set, and returns
0540             * the new representative set.  This is reimplemented to
0541             * convert StyleConstant attributes to CSS prior to forwarding
0542             * to the superclass behavior.  The StyleConstants attribute
0543             * has no corresponding CSS entry, the StyleConstants attribute
0544             * is stored (but will likely be unused).
0545             *
0546             * @param old the old attribute set
0547             * @param key the non-null attribute key
0548             * @param value the attribute value
0549             * @return the updated attribute set
0550             * @see MutableAttributeSet#addAttribute
0551             */
0552            public AttributeSet addAttribute(AttributeSet old, Object key,
0553                    Object value) {
0554                if (css == null) {
0555                    // supers constructor will call this before returning,
0556                    // and we need to make sure CSS is non null.
0557                    css = new CSS();
0558                }
0559                if (key instanceof  StyleConstants) {
0560                    HTML.Tag tag = HTML
0561                            .getTagForStyleConstantsKey((StyleConstants) key);
0562
0563                    if (tag != null && old.isDefined(tag)) {
0564                        old = removeAttribute(old, tag);
0565                    }
0566
0567                    Object cssValue = css.styleConstantsValueToCSSValue(
0568                            (StyleConstants) key, value);
0569                    if (cssValue != null) {
0570                        Object cssKey = css
0571                                .styleConstantsKeyToCSSKey((StyleConstants) key);
0572                        if (cssKey != null) {
0573                            return super .addAttribute(old, cssKey, cssValue);
0574                        }
0575                    }
0576                }
0577                return super .addAttribute(old, key, value);
0578            }
0579
0580            /**
0581             * Adds a set of attributes to the element.  If any of these attributes
0582             * are StyleConstants attributes, they will be converted to CSS prior
0583             * to forwarding to the superclass behavior.
0584             *
0585             * @param old the old attribute set
0586             * @param attr the attributes to add
0587             * @return the updated attribute set
0588             * @see MutableAttributeSet#addAttribute
0589             */
0590            public AttributeSet addAttributes(AttributeSet old,
0591                    AttributeSet attr) {
0592                if (!(attr instanceof  HTMLDocument.TaggedAttributeSet)) {
0593                    old = removeHTMLTags(old, attr);
0594                }
0595                return super .addAttributes(old, convertAttributeSet(attr));
0596            }
0597
0598            /**
0599             * Removes an attribute from the set.  If the attribute is a StyleConstants
0600             * attribute, the request will be converted to a CSS attribute prior to 
0601             * forwarding to the superclass behavior.
0602             *
0603             * @param old the old set of attributes
0604             * @param key the non-null attribute name
0605             * @return the updated attribute set
0606             * @see MutableAttributeSet#removeAttribute
0607             */
0608            public AttributeSet removeAttribute(AttributeSet old, Object key) {
0609                if (key instanceof  StyleConstants) {
0610                    HTML.Tag tag = HTML
0611                            .getTagForStyleConstantsKey((StyleConstants) key);
0612                    if (tag != null) {
0613                        old = super .removeAttribute(old, tag);
0614                    }
0615
0616                    Object cssKey = css
0617                            .styleConstantsKeyToCSSKey((StyleConstants) key);
0618                    if (cssKey != null) {
0619                        return super .removeAttribute(old, cssKey);
0620                    }
0621                }
0622                return super .removeAttribute(old, key);
0623            }
0624
0625            /**
0626             * Removes a set of attributes for the element.  If any of the attributes
0627             * is a StyleConstants attribute, the request will be converted to a CSS 
0628             * attribute prior to forwarding to the superclass behavior.
0629             *
0630             * @param old the old attribute set
0631             * @param names the attribute names
0632             * @return the updated attribute set
0633             * @see MutableAttributeSet#removeAttributes
0634             */
0635            public AttributeSet removeAttributes(AttributeSet old,
0636                    Enumeration<?> names) {
0637                // PENDING: Should really be doing something similar to 
0638                // removeHTMLTags here, but it is rather expensive to have to
0639                // clone names
0640                return super .removeAttributes(old, names);
0641            }
0642
0643            /**
0644             * Removes a set of attributes. If any of the attributes
0645             * is a StyleConstants attribute, the request will be converted to a CSS 
0646             * attribute prior to forwarding to the superclass behavior.
0647             *
0648             * @param old the old attribute set
0649             * @param attrs the attributes
0650             * @return the updated attribute set
0651             * @see MutableAttributeSet#removeAttributes
0652             */
0653            public AttributeSet removeAttributes(AttributeSet old,
0654                    AttributeSet attrs) {
0655                if (old != attrs) {
0656                    old = removeHTMLTags(old, attrs);
0657                }
0658                return super .removeAttributes(old, convertAttributeSet(attrs));
0659            }
0660
0661            /**
0662             * Creates a compact set of attributes that might be shared.
0663             * This is a hook for subclasses that want to alter the 
0664             * behavior of SmallAttributeSet.  This can be reimplemented
0665             * to return an AttributeSet that provides some sort of
0666             * attribute conversion.
0667             *
0668             * @param a The set of attributes to be represented in the
0669             *  the compact form.
0670             */
0671            protected SmallAttributeSet createSmallAttributeSet(AttributeSet a) {
0672                return new SmallConversionSet(a);
0673            }
0674
0675            /**
0676             * Creates a large set of attributes that should trade off
0677             * space for time.  This set will not be shared.  This is
0678             * a hook for subclasses that want to alter the behavior
0679             * of the larger attribute storage format (which is 
0680             * SimpleAttributeSet by default).   This can be reimplemented
0681             * to return a MutableAttributeSet that provides some sort of
0682             * attribute conversion.
0683             *
0684             * @param a The set of attributes to be represented in the
0685             *  the larger form.
0686             */
0687            protected MutableAttributeSet createLargeAttributeSet(AttributeSet a) {
0688                return new LargeConversionSet(a);
0689            }
0690
0691            /**
0692             * For any StyleConstants key in attr that has an associated HTML.Tag,
0693             * it is removed from old. The resulting AttributeSet is then returned.
0694             */
0695            private AttributeSet removeHTMLTags(AttributeSet old,
0696                    AttributeSet attr) {
0697                if (!(attr instanceof  LargeConversionSet)
0698                        && !(attr instanceof  SmallConversionSet)) {
0699                    Enumeration names = attr.getAttributeNames();
0700
0701                    while (names.hasMoreElements()) {
0702                        Object key = names.nextElement();
0703
0704                        if (key instanceof  StyleConstants) {
0705                            HTML.Tag tag = HTML
0706                                    .getTagForStyleConstantsKey((StyleConstants) key);
0707
0708                            if (tag != null && old.isDefined(tag)) {
0709                                old = super .removeAttribute(old, tag);
0710                            }
0711                        }
0712                    }
0713                }
0714                return old;
0715            }
0716
0717            /**
0718             * Converts a set of attributes (if necessary) so that
0719             * any attributes that were specified as StyleConstants
0720             * attributes and have a CSS mapping, will be converted
0721             * to CSS attributes.
0722             */
0723            AttributeSet convertAttributeSet(AttributeSet a) {
0724                if ((a instanceof  LargeConversionSet)
0725                        || (a instanceof  SmallConversionSet)) {
0726                    // known to be converted.
0727                    return a;
0728                }
0729                // in most cases, there are no StyleConstants attributes
0730                // so we iterate the collection of keys to avoid creating
0731                // a new set.
0732                Enumeration names = a.getAttributeNames();
0733                while (names.hasMoreElements()) {
0734                    Object name = names.nextElement();
0735                    if (name instanceof  StyleConstants) {
0736                        // we really need to do a conversion, iterate again
0737                        // building a new set.
0738                        MutableAttributeSet converted = new LargeConversionSet();
0739                        Enumeration keys = a.getAttributeNames();
0740                        while (keys.hasMoreElements()) {
0741                            Object key = keys.nextElement();
0742                            Object cssValue = null;
0743                            if (key instanceof  StyleConstants) {
0744                                // convert the StyleConstants attribute if possible
0745                                Object cssKey = css
0746                                        .styleConstantsKeyToCSSKey((StyleConstants) key);
0747                                if (cssKey != null) {
0748                                    Object value = a.getAttribute(key);
0749                                    cssValue = css
0750                                            .styleConstantsValueToCSSValue(
0751                                                    (StyleConstants) key, value);
0752                                    if (cssValue != null) {
0753                                        converted
0754                                                .addAttribute(cssKey, cssValue);
0755                                    }
0756                                }
0757                            }
0758                            if (cssValue == null) {
0759                                converted
0760                                        .addAttribute(key, a.getAttribute(key));
0761                            }
0762                        }
0763                        return converted;
0764                    }
0765                }
0766                return a;
0767            }
0768
0769            /**
0770             * Large set of attributes that does conversion of requests
0771             * for attributes of type StyleConstants.
0772             */
0773            class LargeConversionSet extends SimpleAttributeSet {
0774
0775                /**
0776                 * Creates a new attribute set based on a supplied set of attributes.
0777                 *
0778                 * @param source the set of attributes
0779                 */
0780                public LargeConversionSet(AttributeSet source) {
0781                    super (source);
0782                }
0783
0784                public LargeConversionSet() {
0785                    super ();
0786                }
0787
0788                /**
0789                 * Checks whether a given attribute is defined.
0790                 *
0791                 * @param key the attribute key
0792                 * @return true if the attribute is defined
0793                 * @see AttributeSet#isDefined
0794                 */
0795                public boolean isDefined(Object key) {
0796                    if (key instanceof  StyleConstants) {
0797                        Object cssKey = css
0798                                .styleConstantsKeyToCSSKey((StyleConstants) key);
0799                        if (cssKey != null) {
0800                            return super .isDefined(cssKey);
0801                        }
0802                    }
0803                    return super .isDefined(key);
0804                }
0805
0806                /**
0807                 * Gets the value of an attribute.
0808                 *
0809                 * @param key the attribute name
0810                 * @return the attribute value
0811                 * @see AttributeSet#getAttribute
0812                 */
0813                public Object getAttribute(Object key) {
0814                    if (key instanceof  StyleConstants) {
0815                        Object cssKey = css
0816                                .styleConstantsKeyToCSSKey((StyleConstants) key);
0817                        if (cssKey != null) {
0818                            Object value = super .getAttribute(cssKey);
0819                            if (value != null) {
0820                                return css.cssValueToStyleConstantsValue(
0821                                        (StyleConstants) key, value);
0822                            }
0823                        }
0824                    }
0825                    return super .getAttribute(key);
0826                }
0827            }
0828
0829            /**
0830             * Small set of attributes that does conversion of requests
0831             * for attributes of type StyleConstants.
0832             */
0833            class SmallConversionSet extends SmallAttributeSet {
0834
0835                /**
0836                 * Creates a new attribute set based on a supplied set of attributes.
0837                 *
0838                 * @param source the set of attributes
0839                 */
0840                public SmallConversionSet(AttributeSet attrs) {
0841                    super (attrs);
0842                }
0843
0844                /**
0845                 * Checks whether a given attribute is defined.
0846                 *
0847                 * @param key the attribute key
0848                 * @return true if the attribute is defined
0849                 * @see AttributeSet#isDefined
0850                 */
0851                public boolean isDefined(Object key) {
0852                    if (key instanceof  StyleConstants) {
0853                        Object cssKey = css
0854                                .styleConstantsKeyToCSSKey((StyleConstants) key);
0855                        if (cssKey != null) {
0856                            return super .isDefined(cssKey);
0857                        }
0858                    }
0859                    return super .isDefined(key);
0860                }
0861
0862                /**
0863                 * Gets the value of an attribute.
0864                 *
0865                 * @param key the attribute name
0866                 * @return the attribute value
0867                 * @see AttributeSet#getAttribute
0868                 */
0869                public Object getAttribute(Object key) {
0870                    if (key instanceof  StyleConstants) {
0871                        Object cssKey = css
0872                                .styleConstantsKeyToCSSKey((StyleConstants) key);
0873                        if (cssKey != null) {
0874                            Object value = super .getAttribute(cssKey);
0875                            if (value != null) {
0876                                return css.cssValueToStyleConstantsValue(
0877                                        (StyleConstants) key, value);
0878                            }
0879                        }
0880                    }
0881                    return super .getAttribute(key);
0882                }
0883            }
0884
0885            // ---- Resource handling ----------------------------------------
0886
0887            /**
0888             * Fetches the font to use for the given set of attributes.
0889             */
0890            public Font getFont(AttributeSet a) {
0891                return css.getFont(this , a, 12, this );
0892            }
0893
0894            /**
0895             * Takes a set of attributes and turn it into a foreground color
0896             * specification.  This might be used to specify things
0897             * like brighter, more hue, etc.
0898             *
0899             * @param a the set of attributes
0900             * @return the color
0901             */
0902            public Color getForeground(AttributeSet a) {
0903                Color c = css.getColor(a, CSS.Attribute.COLOR);
0904                if (c == null) {
0905                    return Color.black;
0906                }
0907                return c;
0908            }
0909
0910            /**
0911             * Takes a set of attributes and turn it into a background color
0912             * specification.  This might be used to specify things
0913             * like brighter, more hue, etc.
0914             *
0915             * @param a the set of attributes
0916             * @return the color
0917             */
0918            public Color getBackground(AttributeSet a) {
0919                return css.getColor(a, CSS.Attribute.BACKGROUND_COLOR);
0920            }
0921
0922            /**
0923             * Fetches the box formatter to use for the given set
0924             * of CSS attributes.
0925             */
0926            public BoxPainter getBoxPainter(AttributeSet a) {
0927                return new BoxPainter(a, css, this );
0928            }
0929
0930            /**
0931             * Fetches the list formatter to use for the given set
0932             * of CSS attributes.
0933             */
0934            public ListPainter getListPainter(AttributeSet a) {
0935                return new ListPainter(a, this );
0936            }
0937
0938            /**
0939             * Sets the base font size, with valid values between 1 and 7.
0940             */
0941            public void setBaseFontSize(int sz) {
0942                css.setBaseFontSize(sz);
0943            }
0944
0945            /**
0946             * Sets the base font size from the passed in String. The string
0947             * can either identify a specific font size, with legal values between
0948             * 1 and 7, or identifiy a relative font size such as +1 or -2.
0949             */
0950            public void setBaseFontSize(String size) {
0951                css.setBaseFontSize(size);
0952            }
0953
0954            public static int getIndexOfSize(float pt) {
0955                return CSS.getIndexOfSize(pt, sizeMapDefault);
0956            }
0957
0958            /**
0959             * Returns the point size, given a size index.
0960             */
0961            public float getPointSize(int index) {
0962                return css.getPointSize(index, this );
0963            }
0964
0965            /**
0966             *  Given a string such as "+2", "-2", or "2",
0967             *  returns a point size value.
0968             */
0969            public float getPointSize(String size) {
0970                return css.getPointSize(size, this );
0971            }
0972
0973            /**
0974             * Converts a color string such as "RED" or "#NNNNNN" to a Color.
0975             * Note: This will only convert the HTML3.2 color strings
0976             *       or a string of length 7;
0977             *       otherwise, it will return null.
0978             */
0979            public Color stringToColor(String string) {
0980                return CSS.stringToColor(string);
0981            }
0982
0983            /**
0984             * Returns the ImageIcon to draw in the background for
0985             * <code>attr</code>.
0986             */
0987            ImageIcon getBackgroundImage(AttributeSet attr) {
0988                Object value = attr
0989                        .getAttribute(CSS.Attribute.BACKGROUND_IMAGE);
0990
0991                if (value != null) {
0992                    return ((CSS.BackgroundImage) value).getImage(getBase());
0993                }
0994                return null;
0995            }
0996
0997            /**
0998             * Adds a rule into the StyleSheet.
0999             *
1000             * @param selector the selector to use for the rule.
1001             *  This will be a set of simple selectors, and must
1002             *  be a length of 1 or greater.
1003             * @param declaration the set of CSS attributes that
1004             *  make up the rule.
1005             */
1006            void addRule(String[] selector, AttributeSet declaration,
1007                    boolean isLinked) {
1008                int n = selector.length;
1009                StringBuffer sb = new StringBuffer();
1010                sb.append(selector[0]);
1011                for (int counter = 1; counter < n; counter++) {
1012                    sb.append(' ');
1013                    sb.append(selector[counter]);
1014                }
1015                String selectorName = sb.toString();
1016                Style rule = getStyle(selectorName);
1017                if (rule == null) {
1018                    // Notice how the rule is first created, and it not part of
1019                    // the synchronized block. It is done like this as creating
1020                    // a new rule will fire a ChangeEvent. We do not want to be
1021                    // holding the lock when calling to other objects, it can
1022                    // result in deadlock.
1023                    Style altRule = addStyle(selectorName, null);
1024                    synchronized (this ) {
1025                        SelectorMapping mapping = getRootSelectorMapping();
1026                        for (int i = n - 1; i >= 0; i--) {
1027                            mapping = mapping.getChildSelectorMapping(
1028                                    selector[i], true);
1029                        }
1030                        rule = mapping.getStyle();
1031                        if (rule == null) {
1032                            rule = altRule;
1033                            mapping.setStyle(rule);
1034                            refreshResolvedRules(selectorName, selector, rule,
1035                                    mapping.getSpecificity());
1036                        }
1037                    }
1038                }
1039                if (isLinked) {
1040                    rule = getLinkedStyle(rule);
1041                }
1042                rule.addAttributes(declaration);
1043            }
1044
1045            //
1046            // The following gaggle of methods is used in maintaing the rules from
1047            // the sheet.
1048            //
1049
1050            /**
1051             * Updates the attributes of the rules to reference any related
1052             * rules in <code>ss</code>.
1053             */
1054            private synchronized void linkStyleSheetAt(StyleSheet ss, int index) {
1055                if (resolvedStyles.size() > 0) {
1056                    Enumeration values = resolvedStyles.elements();
1057                    while (values.hasMoreElements()) {
1058                        ResolvedStyle rule = (ResolvedStyle) values
1059                                .nextElement();
1060                        rule.insertExtendedStyleAt(ss.getRule(rule.getName()),
1061                                index);
1062                    }
1063                }
1064            }
1065
1066            /**
1067             * Removes references to the rules in <code>ss</code>.
1068             * <code>index</code> gives the index the StyleSheet was at, that is
1069             * how many StyleSheets had been added before it.
1070             */
1071            private synchronized void unlinkStyleSheet(StyleSheet ss, int index) {
1072                if (resolvedStyles.size() > 0) {
1073                    Enumeration values = resolvedStyles.elements();
1074                    while (values.hasMoreElements()) {
1075                        ResolvedStyle rule = (ResolvedStyle) values
1076                                .nextElement();
1077                        rule.removeExtendedStyleAt(index);
1078                    }
1079                }
1080            }
1081
1082            /**
1083             * Returns the simple selectors that comprise selector.
1084             */
1085            /* protected */
1086            String[] getSimpleSelectors(String selector) {
1087                selector = cleanSelectorString(selector);
1088                SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
1089                Vector selectors = sb.getVector();
1090                int lastIndex = 0;
1091                int length = selector.length();
1092                while (lastIndex != -1) {
1093                    int newIndex = selector.indexOf(' ', lastIndex);
1094                    if (newIndex != -1) {
1095                        selectors.addElement(selector.substring(lastIndex,
1096                                newIndex));
1097                        if (++newIndex == length) {
1098                            lastIndex = -1;
1099                        } else {
1100                            lastIndex = newIndex;
1101                        }
1102                    } else {
1103                        selectors.addElement(selector.substring(lastIndex));
1104                        lastIndex = -1;
1105                    }
1106                }
1107                String[] retValue = new String[selectors.size()];
1108                selectors.copyInto(retValue);
1109                SearchBuffer.releaseSearchBuffer(sb);
1110                return retValue;
1111            }
1112
1113            /**
1114             * Returns a string that only has one space between simple selectors,
1115             * which may be the passed in String.
1116             */
1117            /*protected*/String cleanSelectorString(String selector) {
1118                boolean lastWasSpace = true;
1119                for (int counter = 0, maxCounter = selector.length(); counter < maxCounter; counter++) {
1120                    switch (selector.charAt(counter)) {
1121                    case ' ':
1122                        if (lastWasSpace) {
1123                            return _cleanSelectorString(selector);
1124                        }
1125                        lastWasSpace = true;
1126                        break;
1127                    case '\n':
1128                    case '\r':
1129                    case '\t':
1130                        return _cleanSelectorString(selector);
1131                    default:
1132                        lastWasSpace = false;
1133                    }
1134                }
1135                if (lastWasSpace) {
1136                    return _cleanSelectorString(selector);
1137                }
1138                // It was fine.
1139                return selector;
1140            }
1141
1142            /**
1143             * Returns a new String that contains only one space between non
1144             * white space characters.
1145             */
1146            private String _cleanSelectorString(String selector) {
1147                SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
1148                StringBuffer buff = sb.getStringBuffer();
1149                boolean lastWasSpace = true;
1150                int lastIndex = 0;
1151                char[] chars = selector.toCharArray();
1152                int numChars = chars.length;
1153                String retValue = null;
1154                try {
1155                    for (int counter = 0; counter < numChars; counter++) {
1156                        switch (chars[counter]) {
1157                        case ' ':
1158                            if (!lastWasSpace) {
1159                                lastWasSpace = true;
1160                                if (lastIndex < counter) {
1161                                    buff.append(chars, lastIndex, 1 + counter
1162                                            - lastIndex);
1163                                }
1164                            }
1165                            lastIndex = counter + 1;
1166                            break;
1167                        case '\n':
1168                        case '\r':
1169                        case '\t':
1170                            if (!lastWasSpace) {
1171                                lastWasSpace = true;
1172                                if (lastIndex < counter) {
1173                                    buff.append(chars, lastIndex, counter
1174                                            - lastIndex);
1175                                    buff.append(' ');
1176                                }
1177                            }
1178                            lastIndex = counter + 1;
1179                            break;
1180                        default:
1181                            lastWasSpace = false;
1182                            break;
1183                        }
1184                    }
1185                    if (lastWasSpace && buff.length() > 0) {
1186                        // Remove last space.
1187                        buff.setLength(buff.length() - 1);
1188                    } else if (lastIndex < numChars) {
1189                        buff.append(chars, lastIndex, numChars - lastIndex);
1190                    }
1191                    retValue = buff.toString();
1192                } finally {
1193                    SearchBuffer.releaseSearchBuffer(sb);
1194                }
1195                return retValue;
1196            }
1197
1198            /**
1199             * Returns the root selector mapping that all selectors are relative
1200             * to. This is an inverted graph of the selectors.
1201             */
1202            private SelectorMapping getRootSelectorMapping() {
1203                return selectorMapping;
1204            }
1205
1206            /**
1207             * Returns the specificity of the passed in String. It assumes the
1208             * passed in string doesn't contain junk, that is each selector is
1209             * separated by a space and each selector at most contains one . or one
1210             * #. A simple selector has a weight of 1, an id selector has a weight
1211             * of 100, and a class selector has a weight of 10000.
1212             */
1213            /*protected*/static int getSpecificity(String selector) {
1214                int specificity = 0;
1215                boolean lastWasSpace = true;
1216
1217                for (int counter = 0, maxCounter = selector.length(); counter < maxCounter; counter++) {
1218                    switch (selector.charAt(counter)) {
1219                    case '.':
1220                        specificity += 100;
1221                        break;
1222                    case '#':
1223                        specificity += 10000;
1224                        break;
1225                    case ' ':
1226                        lastWasSpace = true;
1227                        break;
1228                    default:
1229                        if (lastWasSpace) {
1230                            lastWasSpace = false;
1231                            specificity += 1;
1232                        }
1233                    }
1234                }
1235                return specificity;
1236            }
1237
1238            /**
1239             * Returns the style that linked attributes should be added to. This
1240             * will create the style if necessary.
1241             */
1242            private Style getLinkedStyle(Style localStyle) {
1243                // NOTE: This is not synchronized, and the caller of this does
1244                // not synchronize. There is the chance for one of the callers to
1245                // overwrite the existing resolved parent, but it is quite rare.
1246                // The reason this is left like this is because setResolveParent
1247                // will fire a ChangeEvent. It is really, REALLY bad for us to
1248                // hold a lock when calling outside of us, it may cause a deadlock.
1249                Style retStyle = (Style) localStyle.getResolveParent();
1250                if (retStyle == null) {
1251                    retStyle = addStyle(null, null);
1252                    localStyle.setResolveParent(retStyle);
1253                }
1254                return retStyle;
1255            }
1256
1257            /**
1258             * Returns the resolved style for <code>selector</code>. This will
1259             * create the resolved style, if necessary.
1260             */
1261            private synchronized Style getResolvedStyle(String selector,
1262                    Vector elements, HTML.Tag t) {
1263                Style retStyle = (Style) resolvedStyles.get(selector);
1264                if (retStyle == null) {
1265                    retStyle = createResolvedStyle(selector, elements, t);
1266                }
1267                return retStyle;
1268            }
1269
1270            /**
1271             * Returns the resolved style for <code>selector</code>. This will
1272             * create the resolved style, if necessary.
1273             */
1274            private synchronized Style getResolvedStyle(String selector) {
1275                Style retStyle = (Style) resolvedStyles.get(selector);
1276                if (retStyle == null) {
1277                    retStyle = createResolvedStyle(selector);
1278                }
1279                return retStyle;
1280            }
1281
1282            /**
1283             * Adds <code>mapping</code> to <code>elements</code>. It is added
1284             * such that <code>elements</code> will remain ordered by 
1285             * specificity.
1286             */
1287            private void addSortedStyle(SelectorMapping mapping, Vector elements) {
1288                int size = elements.size();
1289
1290                if (size > 0) {
1291                    int specificity = mapping.getSpecificity();
1292
1293                    for (int counter = 0; counter < size; counter++) {
1294                        if (specificity >= ((SelectorMapping) elements
1295                                .elementAt(counter)).getSpecificity()) {
1296                            elements.insertElementAt(mapping, counter);
1297                            return;
1298                        }
1299                    }
1300                }
1301                elements.addElement(mapping);
1302            }
1303
1304            /**
1305             * Adds <code>parentMapping</code> to <code>styles</code>, and
1306             * recursively calls this method if <code>parentMapping</code> has
1307             * any child mappings for any of the Elements in <code>elements</code>.
1308             */
1309            private synchronized void getStyles(SelectorMapping parentMapping,
1310                    Vector styles, String[] tags, String[] ids,
1311                    String[] classes, int index, int numElements,
1312                    Hashtable alreadyChecked) {
1313                // Avoid desending the same mapping twice.
1314                if (alreadyChecked.contains(parentMapping)) {
1315                    return;
1316                }
1317                alreadyChecked.put(parentMapping, parentMapping);
1318                Style style = parentMapping.getStyle();
1319                if (style != null) {
1320                    addSortedStyle(parentMapping, styles);
1321                }
1322                for (int counter = index; counter < numElements; counter++) {
1323                    String tagString = tags[counter];
1324                    if (tagString != null) {
1325                        SelectorMapping childMapping = parentMapping
1326                                .getChildSelectorMapping(tagString, false);
1327                        if (childMapping != null) {
1328                            getStyles(childMapping, styles, tags, ids, classes,
1329                                    counter + 1, numElements, alreadyChecked);
1330                        }
1331                        if (classes[counter] != null) {
1332                            String className = classes[counter];
1333                            childMapping = parentMapping
1334                                    .getChildSelectorMapping(tagString + "."
1335                                            + className, false);
1336                            if (childMapping != null) {
1337                                getStyles(childMapping, styles, tags, ids,
1338                                        classes, counter + 1, numElements,
1339                                        alreadyChecked);
1340                            }
1341                            childMapping = parentMapping
1342                                    .getChildSelectorMapping("." + className,
1343                                            false);
1344                            if (childMapping != null) {
1345                                getStyles(childMapping, styles, tags, ids,
1346                                        classes, counter + 1, numElements,
1347                                        alreadyChecked);
1348                            }
1349                        }
1350                        if (ids[counter] != null) {
1351                            String idName = ids[counter];
1352                            childMapping = parentMapping
1353                                    .getChildSelectorMapping(tagString + "#"
1354                                            + idName, false);
1355                            if (childMapping != null) {
1356                                getStyles(childMapping, styles, tags, ids,
1357                                        classes, counter + 1, numElements,
1358                                        alreadyChecked);
1359                            }
1360                            childMapping = parentMapping
1361                                    .getChildSelectorMapping("#" + idName,
1362                                            false);
1363                            if (childMapping != null) {
1364                                getStyles(childMapping, styles, tags, ids,
1365                                        classes, counter + 1, numElements,
1366                                        alreadyChecked);
1367                            }
1368                        }
1369                    }
1370                }
1371            }
1372
1373            /**
1374             * Creates and returns a Style containing all the rules that match
1375             *  <code>selector</code>.
1376             */
1377            private synchronized Style createResolvedStyle(String selector,
1378                    String[] tags, String[] ids, String[] classes) {
1379                SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
1380                Vector tempVector = sb.getVector();
1381                Hashtable tempHashtable = sb.getHashtable();
1382                // Determine all the Styles that are appropriate, placing them
1383                // in tempVector
1384                try {
1385                    SelectorMapping mapping = getRootSelectorMapping();
1386                    int numElements = tags.length;
1387                    String tagString = tags[0];
1388                    SelectorMapping childMapping = mapping
1389                            .getChildSelectorMapping(tagString, false);
1390                    if (childMapping != null) {
1391                        getStyles(childMapping, tempVector, tags, ids, classes,
1392                                1, numElements, tempHashtable);
1393                    }
1394                    if (classes[0] != null) {
1395                        String className = classes[0];
1396                        childMapping = mapping.getChildSelectorMapping(
1397                                tagString + "." + className, false);
1398                        if (childMapping != null) {
1399                            getStyles(childMapping, tempVector, tags, ids,
1400                                    classes, 1, numElements, tempHashtable);
1401                        }
1402                        childMapping = mapping.getChildSelectorMapping("."
1403                                + className, false);
1404                        if (childMapping != null) {
1405                            getStyles(childMapping, tempVector, tags, ids,
1406                                    classes, 1, numElements, tempHashtable);
1407                        }
1408                    }
1409                    if (ids[0] != null) {
1410                        String idName = ids[0];
1411                        childMapping = mapping.getChildSelectorMapping(
1412                                tagString + "#" + idName, false);
1413                        if (childMapping != null) {
1414                            getStyles(childMapping, tempVector, tags, ids,
1415                                    classes, 1, numElements, tempHashtable);
1416                        }
1417                        childMapping = mapping.getChildSelectorMapping("#"
1418                                + idName, false);
1419                        if (childMapping != null) {
1420                            getStyles(childMapping, tempVector, tags, ids,
1421                                    classes, 1, numElements, tempHashtable);
1422                        }
1423                    }
1424                    // Create a new Style that will delegate to all the matching
1425                    // Styles.
1426                    int numLinkedSS = (linkedStyleSheets != null) ? linkedStyleSheets
1427                            .size()
1428                            : 0;
1429                    int numStyles = tempVector.size();
1430                    AttributeSet[] attrs = new AttributeSet[numStyles
1431                            + numLinkedSS];
1432                    for (int counter = 0; counter < numStyles; counter++) {
1433                        attrs[counter] = ((SelectorMapping) tempVector
1434                                .elementAt(counter)).getStyle();
1435                    }
1436                    // Get the AttributeSet from linked style sheets.
1437                    for (int counter = 0; counter < numLinkedSS; counter++) {
1438                        AttributeSet attr = ((StyleSheet) linkedStyleSheets
1439                                .elementAt(counter)).getRule(selector);
1440                        if (attr == null) {
1441                            attrs[counter + numStyles] = SimpleAttributeSet.EMPTY;
1442                        } else {
1443                            attrs[counter + numStyles] = attr;
1444                        }
1445                    }
1446                    ResolvedStyle retStyle = new ResolvedStyle(selector, attrs,
1447                            numStyles);
1448                    resolvedStyles.put(selector, retStyle);
1449                    return retStyle;
1450                } finally {
1451                    SearchBuffer.releaseSearchBuffer(sb);
1452                }
1453            }
1454
1455            /**
1456             * Creates and returns a Style containing all the rules that
1457             * matches <code>selector</code>.
1458             *
1459             * @param elements  a Vector of all the Elements
1460             *                  the style is being asked for. The
1461             *                  first Element is the deepest Element, with the last Element
1462             *                  representing the root.
1463             * @param t         the Tag to use for
1464             *                  the first Element in <code>elements</code>
1465             */
1466            private Style createResolvedStyle(String selector, Vector elements,
1467                    HTML.Tag t) {
1468                int numElements = elements.size();
1469                // Build three arrays, one for tags, one for class's, and one for
1470                // id's
1471                String tags[] = new String[numElements];
1472                String ids[] = new String[numElements];
1473                String classes[] = new String[numElements];
1474                for (int counter = 0; counter < numElements; counter++) {
1475                    Element e = (Element) elements.elementAt(counter);
1476                    AttributeSet attr = e.getAttributes();
1477                    if (counter == 0 && e.isLeaf()) {
1478                        // For leafs, we use the second tier attributes.
1479                        Object testAttr = attr.getAttribute(t);
1480                        if (testAttr instanceof  AttributeSet) {
1481                            attr = (AttributeSet) testAttr;
1482                        } else {
1483                            attr = null;
1484                        }
1485                    }
1486                    if (attr != null) {
1487                        HTML.Tag tag = (HTML.Tag) attr
1488                                .getAttribute(StyleConstants.NameAttribute);
1489                        if (tag != null) {
1490                            tags[counter] = tag.toString();
1491                        } else {
1492                            tags[counter] = null;
1493                        }
1494                        if (attr.isDefined(HTML.Attribute.CLASS)) {
1495                            classes[counter] = attr.getAttribute(
1496                                    HTML.Attribute.CLASS).toString();
1497                        } else {
1498                            classes[counter] = null;
1499                        }
1500                        if (attr.isDefined(HTML.Attribute.ID)) {
1501                            ids[counter] = attr.getAttribute(HTML.Attribute.ID)
1502                                    .toString();
1503                        } else {
1504                            ids[counter] = null;
1505                        }
1506                    } else {
1507                        tags[counter] = ids[counter] = classes[counter] = null;
1508                    }
1509                }
1510                tags[0] = t.toString();
1511                return createResolvedStyle(selector, tags, ids, classes);
1512            }
1513
1514            /**
1515             * Creates and returns a Style containing all the rules that match
1516             *  <code>selector</code>. It is assumed that each simple selector
1517             * in <code>selector</code> is separated by a space.
1518             */
1519            private Style createResolvedStyle(String selector) {
1520                SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
1521                // Will contain the tags, ids, and classes, in that order.
1522                Vector elements = sb.getVector();
1523                try {
1524                    boolean done;
1525                    int dotIndex = 0;
1526                    int spaceIndex = 0;
1527                    int poundIndex = 0;
1528                    int lastIndex = 0;
1529                    int length = selector.length();
1530                    while (lastIndex < length) {
1531                        if (dotIndex == lastIndex) {
1532                            dotIndex = selector.indexOf('.', lastIndex);
1533                        }
1534                        if (poundIndex == lastIndex) {
1535                            poundIndex = selector.indexOf('#', lastIndex);
1536                        }
1537                        spaceIndex = selector.indexOf(' ', lastIndex);
1538                        if (spaceIndex == -1) {
1539                            spaceIndex = length;
1540                        }
1541                        if (dotIndex != -1 && poundIndex != -1
1542                                && dotIndex < spaceIndex
1543                                && poundIndex < spaceIndex) {
1544                            if (poundIndex < dotIndex) {
1545                                // #.
1546                                if (lastIndex == poundIndex) {
1547                                    elements.addElement("");
1548                                } else {
1549                                    elements.addElement(selector.substring(
1550                                            lastIndex, poundIndex));
1551                                }
1552                                if ((dotIndex + 1) < spaceIndex) {
1553                                    elements.addElement(selector.substring(
1554                                            dotIndex + 1, spaceIndex));
1555                                } else {
1556                                    elements.addElement(null);
1557                                }
1558                                if ((poundIndex + 1) == dotIndex) {
1559                                    elements.addElement(null);
1560                                } else {
1561                                    elements.addElement(selector.substring(
1562                                            poundIndex + 1, dotIndex));
1563                                }
1564                            } else if (poundIndex < spaceIndex) {
1565                                // .#
1566                                if (lastIndex == dotIndex) {
1567                                    elements.addElement("");
1568                                } else {
1569                                    elements.addElement(selector.substring(
1570                                            lastIndex, dotIndex));
1571                                }
1572                                if ((dotIndex + 1) < poundIndex) {
1573                                    elements.addElement(selector.substring(
1574                                            dotIndex + 1, poundIndex));
1575                                } else {
1576                                    elements.addElement(null);
1577                                }
1578                                if ((poundIndex + 1) == spaceIndex) {
1579                                    elements.addElement(null);
1580                                } else {
1581                                    elements.addElement(selector.substring(
1582                                            poundIndex + 1, spaceIndex));
1583                                }
1584                            }
1585                            dotIndex = poundIndex = spaceIndex + 1;
1586                        } else if (dotIndex != -1 && dotIndex < spaceIndex) {
1587                            // .
1588                            if (dotIndex == lastIndex) {
1589                                elements.addElement("");
1590                            } else {
1591                                elements.addElement(selector.substring(
1592                                        lastIndex, dotIndex));
1593                            }
1594                            if ((dotIndex + 1) == spaceIndex) {
1595                                elements.addElement(null);
1596                            } else {
1597                                elements.addElement(selector.substring(
1598                                        dotIndex + 1, spaceIndex));
1599                            }
1600                            elements.addElement(null);
1601                            dotIndex = spaceIndex + 1;
1602                        } else if (poundIndex != -1 && poundIndex < spaceIndex) {
1603                            // #
1604                            if (poundIndex == lastIndex) {
1605                                elements.addElement("");
1606                            } else {
1607                                elements.addElement(selector.substring(
1608                                        lastIndex, poundIndex));
1609                            }
1610                            elements.addElement(null);
1611                            if ((poundIndex + 1) == spaceIndex) {
1612                                elements.addElement(null);
1613                            } else {
1614                                elements.addElement(selector.substring(
1615                                        poundIndex + 1, spaceIndex));
1616                            }
1617                            poundIndex = spaceIndex + 1;
1618                        } else {
1619                            // id
1620                            elements.addElement(selector.substring(lastIndex,
1621                                    spaceIndex));
1622                            elements.addElement(null);
1623                            elements.addElement(null);
1624                        }
1625                        lastIndex = spaceIndex + 1;
1626                    }
1627                    // Create the tag, id, and class arrays.
1628                    int total = elements.size();
1629                    int numTags = total / 3;
1630                    String[] tags = new String[numTags];
1631                    String[] ids = new String[numTags];
1632                    String[] classes = new String[numTags];
1633                    for (int index = 0, eIndex = total - 3; index < numTags; index++, eIndex -= 3) {
1634                        tags[index] = (String) elements.elementAt(eIndex);
1635                        classes[index] = (String) elements
1636                                .elementAt(eIndex + 1);
1637                        ids[index] = (String) elements.elementAt(eIndex + 2);
1638                    }
1639                    return createResolvedStyle(selector, tags, ids, classes);
1640                } finally {
1641                    SearchBuffer.releaseSearchBuffer(sb);
1642                }
1643            }
1644
1645            /**
1646             * Should be invoked when a new rule is added that did not previously
1647             * exist. Goes through and refreshes the necessary resolved
1648             * rules.
1649             */
1650            private synchronized void refreshResolvedRules(String selectorName,
1651                    String[] selector, Style newStyle, int specificity) {
1652                if (resolvedStyles.size() > 0) {
1653                    Enumeration values = resolvedStyles.elements();
1654                    while (values.hasMoreElements()) {
1655                        ResolvedStyle style = (ResolvedStyle) values
1656                                .nextElement();
1657                        if (style.matches(selectorName)) {
1658                            style.insertStyle(newStyle, specificity);
1659                        }
1660                    }
1661                }
1662            }
1663
1664            /**
1665             * A temporary class used to hold a Vector, a StringBuffer and a
1666             * Hashtable. This is used to avoid allocing a lot of garbage when
1667             * searching for rules. Use the static method obtainSearchBuffer and
1668             * releaseSearchBuffer to get a SearchBuffer, and release it when
1669             * done.
1670             */
1671            private static class SearchBuffer {
1672                /** A stack containing instances of SearchBuffer. Used in getting
1673                 * rules. */
1674                static Stack searchBuffers = new Stack();
1675                // A set of temporary variables that can be used in whatever way.
1676                Vector vector = null;
1677                StringBuffer stringBuffer = null;
1678                Hashtable hashtable = null;
1679
1680                /**
1681                 * Returns an instance of SearchBuffer. Be sure and issue
1682                 * a releaseSearchBuffer when done with it.
1683                 */
1684                static SearchBuffer obtainSearchBuffer() {
1685                    SearchBuffer sb;
1686                    try {
1687                        if (!searchBuffers.empty()) {
1688                            sb = (SearchBuffer) searchBuffers.pop();
1689                        } else {
1690                            sb = new SearchBuffer();
1691                        }
1692                    } catch (EmptyStackException ese) {
1693                        sb = new SearchBuffer();
1694                    }
1695                    return sb;
1696                }
1697
1698                /**
1699                 * Adds <code>sb</code> to the stack of SearchBuffers that can
1700                 * be used.
1701                 */
1702                static void releaseSearchBuffer(SearchBuffer sb) {
1703                    sb.empty();
1704                    searchBuffers.push(sb);
1705                }
1706
1707                StringBuffer getStringBuffer() {
1708                    if (stringBuffer == null) {
1709                        stringBuffer = new StringBuffer();
1710                    }
1711                    return stringBuffer;
1712                }
1713
1714                Vector getVector() {
1715                    if (vector == null) {
1716                        vector = new Vector();
1717                    }
1718                    return vector;
1719                }
1720
1721                Hashtable getHashtable() {
1722                    if (hashtable == null) {
1723                        hashtable = new Hashtable();
1724                    }
1725                    return hashtable;
1726                }
1727
1728                void empty() {
1729                    if (stringBuffer != null) {
1730                        stringBuffer.setLength(0);
1731                    }
1732                    if (vector != null) {
1733                        vector.removeAllElements();
1734                    }
1735                    if (hashtable != null) {
1736                        hashtable.clear();
1737                    }
1738                }
1739            }
1740
1741            static final Border noBorder = new EmptyBorder(0, 0, 0, 0);
1742
1743            /**
1744             * Class to carry out some of the duties of
1745             * CSS formatting.  Implementations of this
1746             * class enable views to present the CSS formatting
1747             * while not knowing anything about how the CSS values
1748             * are being cached.
1749             * <p>
1750             * As a delegate of Views, this object is responsible for
1751             * the insets of a View and making sure the background
1752             * is maintained according to the CSS attributes.
1753             */
1754            public static class BoxPainter implements  Serializable {
1755
1756                BoxPainter(AttributeSet a, CSS css, StyleSheet ss) {
1757                    this .ss = ss;
1758                    this .css = css;
1759                    border = getBorder(a);
1760                    binsets = border.getBorderInsets(null);
1761                    topMargin = getLength(CSS.Attribute.MARGIN_TOP, a);
1762                    bottomMargin = getLength(CSS.Attribute.MARGIN_BOTTOM, a);
1763                    leftMargin = getLength(CSS.Attribute.MARGIN_LEFT, a);
1764                    rightMargin = getLength(CSS.Attribute.MARGIN_RIGHT, a);
1765                    bg = ss.getBackground(a);
1766                    if (ss.getBackgroundImage(a) != null) {
1767                        bgPainter = new BackgroundImagePainter(a, css, ss);
1768                    }
1769                }
1770
1771                /**
1772                 * Fetches a border to render for the given attributes.
1773                 * PENDING(prinz) This is pretty badly hacked at the 
1774                 * moment.
1775                 */
1776                Border getBorder(AttributeSet a) {
1777                    Border b = noBorder;
1778                    Object o = a.getAttribute(CSS.Attribute.BORDER_STYLE);
1779                    if (o != null) {
1780                        String bstyle = o.toString();
1781                        int bw = (int) getLength(
1782                                CSS.Attribute.BORDER_TOP_WIDTH, a);
1783                        if (bw > 0) {
1784                            if (bstyle.equals("inset")) {
1785                                Color c = getBorderColor(a);
1786                                b = new BevelBorder(BevelBorder.LOWERED, c
1787                                        .brighter(), c.darker());
1788                            } else if (bstyle.equals("outset")) {
1789                                Color c = getBorderColor(a);
1790                                b = new BevelBorder(BevelBorder.RAISED, c
1791                                        .brighter(), c.darker());
1792                            } else if (bstyle.equals("solid")) {
1793                                Color c = getBorderColor(a);
1794                                b = new LineBorder(c, bw);
1795                            }
1796                        }
1797                    }
1798                    return b;
1799                }
1800
1801                /**
1802                 * Fetches the color to use for borders.  This will either be
1803                 * the value specified by the border-color attribute (which
1804                 * is not inherited), or it will default to the color attribute
1805                 * (which is inherited).
1806                 */
1807                Color getBorderColor(AttributeSet a) {
1808                    Color color = css.getColor(a, CSS.Attribute.BORDER_COLOR);
1809                    if (color == null) {
1810                        color = css.getColor(a, CSS.Attribute.COLOR);
1811                        if (color == null) {
1812                            return Color.black;
1813                        }
1814                    }
1815                    return color;
1816                }
1817
1818                /**
1819                 * Fetches the inset needed on a given side to
1820                 * account for the margin, border, and padding.
1821                 *
1822                 * @param side The size of the box to fetch the
1823                 *  inset for.  This can be View.TOP,
1824                 *  View.LEFT, View.BOTTOM, or View.RIGHT.
1825                 * @param v the view making the request.  This is
1826                 *  used to get the AttributeSet, and may be used to
1827                 *  resolve percentage arguments.
1828                 * @exception IllegalArgumentException for an invalid direction
1829                 */
1830                public float getInset(int side, View v) {
1831                    AttributeSet a = v.getAttributes();
1832                    float inset = 0;
1833                    switch (side) {
1834                    case View.LEFT:
1835                        inset += getOrientationMargin(HorizontalMargin.LEFT,
1836                                leftMargin, a, isLeftToRight(v));
1837                        inset += binsets.left;
1838                        inset += getLength(CSS.Attribute.PADDING_LEFT, a);
1839                        break;
1840                    case View.RIGHT:
1841                        inset += getOrientationMargin(HorizontalMargin.RIGHT,
1842                                rightMargin, a, isLeftToRight(v));
1843                        inset += binsets.right;
1844                        inset += getLength(CSS.Attribute.PADDING_RIGHT, a);
1845                        break;
1846                    case View.TOP:
1847                        inset += topMargin;
1848                        inset += binsets.top;
1849                        inset += getLength(CSS.Attribute.PADDING_TOP, a);
1850                        break;
1851                    case View.BOTTOM:
1852                        inset += bottomMargin;
1853                        inset += binsets.bottom;
1854                        inset += getLength(CSS.Attribute.PADDING_BOTTOM, a);
1855                        break;
1856                    default:
1857                        throw new IllegalArgumentException("Invalid side: "
1858                                + side);
1859                    }
1860                    return inset;
1861                }
1862
1863                /**
1864                 * Paints the CSS box according to the attributes
1865                 * given.  This should paint the border, padding,
1866                 * and background.
1867                 *
1868                 * @param g the rendering surface.
1869                 * @param x the x coordinate of the allocated area to
1870                 *  render into.
1871                 * @param y the y coordinate of the allocated area to
1872                 *  render into.
1873                 * @param w the width of the allocated area to render into.
1874                 * @param h the height of the allocated area to render into.
1875                 * @param v the view making the request.  This is
1876                 *  used to get the AttributeSet, and may be used to
1877                 *  resolve percentage arguments.
1878                 */
1879                public void paint(Graphics g, float x, float y, float w,
1880                        float h, View v) {
1881                    // PENDING(prinz) implement real rendering... which would
1882                    // do full set of border and background capabilities.
1883                    // remove margin
1884
1885                    float dx = 0;
1886                    float dy = 0;
1887                    float dw = 0;
1888                    float dh = 0;
1889                    AttributeSet a = v.getAttributes();
1890                    boolean isLeftToRight = isLeftToRight(v);
1891                    float localLeftMargin = getOrientationMargin(
1892                            HorizontalMargin.LEFT, leftMargin, a, isLeftToRight);
1893                    float localRightMargin = getOrientationMargin(
1894                            HorizontalMargin.RIGHT, rightMargin, a,
1895                            isLeftToRight);
1896                    if (!(v instanceof  HTMLEditorKit.HTMLFactory.BodyBlockView)) {
1897                        dx = localLeftMargin;
1898                        dy = topMargin;
1899                        dw = -(localLeftMargin + localRightMargin);
1900                        dh = -(topMargin + bottomMargin);
1901                    }
1902                    if (bg != null) {
1903                        g.setColor(bg);
1904                        g.fillRect((int) (x + dx), (int) (y + dy),
1905                                (int) (w + dw), (int) (h + dh));
1906                    }
1907                    if (bgPainter != null) {
1908                        bgPainter.paint(g, x + dx, y + dy, w + dw, h + dh, v);
1909                    }
1910                    x += localLeftMargin;
1911                    y += topMargin;
1912                    w -= localLeftMargin + localRightMargin;
1913                    h -= topMargin + bottomMargin;
1914                    if (border instanceof  BevelBorder) {
1915                        //BevelBorder does not support border width
1916                        int bw = (int) getLength(
1917                                CSS.Attribute.BORDER_TOP_WIDTH, a);
1918                        for (int i = bw - 1; i >= 0; i--) {
1919                            border.paintBorder(null, g, (int) x + i, (int) y
1920                                    + i, (int) w - 2 * i, (int) h - 2 * i);
1921                        }
1922                    } else {
1923                        border.paintBorder(null, g, (int) x, (int) y, (int) w,
1924                                (int) h);
1925                    }
1926                }
1927
1928                float getLength(CSS.Attribute key, AttributeSet a) {
1929                    return css.getLength(a, key, ss);
1930                }
1931
1932                static boolean isLeftToRight(View v) {
1933                    boolean ret = true;
1934                    if (isOrientationAware(v)) {
1935                        Container container = null;
1936                        if (v != null && (container = v.getContainer()) != null) {
1937                            ret = container.getComponentOrientation()
1938                                    .isLeftToRight();
1939                        }
1940                    }
1941                    return ret;
1942                }
1943
1944                /*
1945                 * only certain tags are concerned about orientation
1946                 * <dir>, <menu>, <ul>, <ol>
1947                 * for all others we return true. It is implemented this way
1948                 * for performance purposes
1949                 */
1950                static boolean isOrientationAware(View v) {
1951                    boolean ret = false;
1952                    AttributeSet attr = null;
1953                    Object obj = null;
1954                    if (v != null
1955                            && (attr = v.getElement().getAttributes()) != null
1956                            && (obj = attr
1957                                    .getAttribute(StyleConstants.NameAttribute)) instanceof  HTML.Tag
1958                            && (obj == HTML.Tag.DIR || obj == HTML.Tag.MENU
1959                                    || obj == HTML.Tag.UL || obj == HTML.Tag.OL)) {
1960                        ret = true;
1961                    }
1962
1963                    return ret;
1964                }
1965
1966                static enum HorizontalMargin {
1967                    LEFT, RIGHT
1968                };
1969
1970                /**
1971                 * for <dir>, <menu>, <ul> etc.
1972                 * margins are Left-To-Right/Right-To-Left depended.
1973                 * see 5088268 for more details
1974                 * margin-(left|right)-(ltr|rtl) were introduced to describe it
1975                 * if margin-(left|right) is present we are to use it.
1976                 *
1977                 * @param side The horizontal side to fetch margin for
1978                 *  This can be HorizontalMargin.LEFT or HorizontalMargin.RIGHT
1979                 * @param cssMargin margin from css
1980                 * @param a AttributeSet for the View we getting margin for
1981                 * @param isLeftToRight 
1982                 * @return orientation depended margin
1983                 */
1984                float getOrientationMargin(HorizontalMargin side,
1985                        float cssMargin, AttributeSet a, boolean isLeftToRight) {
1986                    float margin = cssMargin;
1987                    float orientationMargin = cssMargin;
1988                    Object cssMarginValue = null;
1989                    switch (side) {
1990                    case RIGHT: {
1991                        orientationMargin = (isLeftToRight) ? getLength(
1992                                CSS.Attribute.MARGIN_RIGHT_LTR, a) : getLength(
1993                                CSS.Attribute.MARGIN_RIGHT_RTL, a);
1994                        cssMarginValue = a
1995                                .getAttribute(CSS.Attribute.MARGIN_RIGHT);
1996                    }
1997                        break;
1998                    case LEFT: {
1999                        orientationMargin = (isLeftToRight) ? getLength(
2000                                CSS.Attribute.MARGIN_LEFT_LTR, a) : getLength(
2001                                CSS.Attribute.MARGIN_LEFT_RTL, a);
2002                        cssMarginValue = a
2003                                .getAttribute(CSS.Attribute.MARGIN_LEFT);
2004                    }
2005                        break;
2006                    }
2007
2008                    if (cssMarginValue == null
2009                            && orientationMargin != Integer.MIN_VALUE) {
2010                        margin = orientationMargin;
2011                    }
2012                    return margin;
2013                }
2014
2015                float topMargin;
2016                float bottomMargin;
2017                float leftMargin;
2018                float rightMargin;
2019                // Bitmask, used to indicate what margins are relative:
2020                // bit 0 for top, 1 for bottom, 2 for left and 3 for right.
2021                short marginFlags;
2022                Border border;
2023                Insets binsets;
2024                CSS css;
2025                StyleSheet ss;
2026                Color bg;
2027                BackgroundImagePainter bgPainter;
2028            }
2029
2030            /**
2031             * Class to carry out some of the duties of CSS list
2032             * formatting.  Implementations of this
2033             * class enable views to present the CSS formatting
2034             * while not knowing anything about how the CSS values
2035             * are being cached.
2036             */
2037            public static class ListPainter implements  Serializable {
2038
2039                ListPainter(AttributeSet attr, StyleSheet ss) {
2040                    this .ss = ss;
2041                    /* Get the image to use as a list bullet */
2042                    String imgstr = (String) attr
2043                            .getAttribute(CSS.Attribute.LIST_STYLE_IMAGE);
2044                    type = null;
2045                    if (imgstr != null && !imgstr.equals("none")) {
2046                        String tmpstr = null;
2047                        try {
2048                            StringTokenizer st = new StringTokenizer(imgstr,
2049                                    "()");
2050                            if (st.hasMoreTokens())
2051                                tmpstr = st.nextToken();
2052                            if (st.hasMoreTokens())
2053                                tmpstr = st.nextToken();
2054                            URL u = new URL(tmpstr);
2055                            img = new ImageIcon(u);
2056                        } catch (MalformedURLException e) {
2057                            if (tmpstr != null && ss != null
2058                                    && ss.getBase() != null) {
2059                                try {
2060                                    URL u = new URL(ss.getBase(), tmpstr);
2061                                    img = new ImageIcon(u);
2062                                } catch (MalformedURLException murle) {
2063                                    img = null;
2064                                }
2065                            } else {
2066                                img = null;
2067                            }
2068                        }
2069                    }
2070
2071                    /* Get the type of bullet to use in the list */
2072                    if (img == null) {
2073                        type = (CSS.Value) attr
2074                                .getAttribute(CSS.Attribute.LIST_STYLE_TYPE);
2075                    }
2076                    start = 1;
2077
2078                    paintRect = new Rectangle();
2079                }
2080
2081                /**
2082                 * Returns a string that represents the value
2083                 * of the HTML.Attribute.TYPE attribute.
2084                 * If this attributes is not defined, then
2085                 * then the type defaults to "disc" unless
2086                 * the tag is on Ordered list.  In the case
2087                 * of the latter, the default type is "decimal".
2088                 */
2089                private CSS.Value getChildType(View childView) {
2090                    CSS.Value childtype = (CSS.Value) childView.getAttributes()
2091                            .getAttribute(CSS.Attribute.LIST_STYLE_TYPE);
2092
2093                    if (childtype == null) {
2094                        if (type == null) {
2095                            // Parent view.
2096                            View v = childView.getParent();
2097                            HTMLDocument doc = (HTMLDocument) v.getDocument();
2098                            if (doc.matchNameAttribute(v.getElement()
2099                                    .getAttributes(), HTML.Tag.OL)) {
2100                                childtype = CSS.Value.DECIMAL;
2101                            } else {
2102                                childtype = CSS.Value.DISC;
2103                            }
2104                        } else {
2105                            childtype = type;
2106                        }
2107                    }
2108                    return childtype;
2109                }
2110
2111                /**
2112                 * Obtains the starting index from <code>parent</code>.
2113                 */
2114                private void getStart(View parent) {
2115                    checkedForStart = true;
2116                    Element element = parent.getElement();
2117                    if (element != null) {
2118                        AttributeSet attr = element.getAttributes();
2119                        Object startValue;
2120                        if (attr != null
2121                                && attr.isDefined(HTML.Attribute.START)
2122                                && (startValue = attr
2123                                        .getAttribute(HTML.Attribute.START)) != null
2124                                && (startValue instanceof  String)) {
2125
2126                            try {
2127                                start = Integer.parseInt((String) startValue);
2128                            } catch (NumberFormatException nfe) {
2129                            }
2130                        }
2131                    }
2132                }
2133
2134                /**
2135                 * Returns an integer that should be used to render the child at
2136                 * <code>childIndex</code> with. The retValue will usually be
2137                 * <code>childIndex</code> + 1, unless <code>parentView</code>
2138                 * has some Views that do not represent LI's, or one of the views
2139                 * has a HTML.Attribute.START specified.
2140                 */
2141                private int getRenderIndex(View parentView, int childIndex) {
2142                    if (!checkedForStart) {
2143                        getStart(parentView);
2144                    }
2145                    int retIndex = childIndex;
2146                    for (int counter = childIndex; counter >= 0; counter--) {
2147                        AttributeSet as = parentView.getElement().getElement(
2148                                counter).getAttributes();
2149                        if (as.getAttribute(StyleConstants.NameAttribute) != HTML.Tag.LI) {
2150                            retIndex--;
2151                        } else if (as.isDefined(HTML.Attribute.VALUE)) {
2152                            Object value = as
2153                                    .getAttribute(HTML.Attribute.VALUE);
2154                            if (value != null && (value instanceof  String)) {
2155                                try {
2156                                    int iValue = Integer
2157                                            .parseInt((String) value);
2158                                    return retIndex - counter + iValue;
2159                                } catch (NumberFormatException nfe) {
2160                                }
2161                            }
2162                        }
2163                    }
2164                    return retIndex + start;
2165                }
2166
2167                /**
2168                 * Paints the CSS list decoration according to the
2169                 * attributes given.
2170                 *
2171                 * @param g the rendering surface.
2172                 * @param x the x coordinate of the list item allocation
2173                 * @param y the y coordinate of the list item allocation
2174                 * @param w the width of the list item allocation
2175                 * @param h the height of the list item allocation
2176                 * @param v the allocated area to paint into.
2177                 * @param item which list item is being painted.  This
2178                 *  is a number greater than or equal to 0.
2179                 */
2180                public void paint(Graphics g, float x, float y, float w,
2181                        float h, View v, int item) {
2182                    View cv = v.getView(item);
2183                    Object name = cv.getElement().getAttributes().getAttribute(
2184                            StyleConstants.NameAttribute);
2185                    // Only draw something if the View is a list item. This won't
2186                    // be the case for comments.
2187                    if (!(name instanceof  HTML.Tag) || name != HTML.Tag.LI) {
2188                        return;
2189                    }
2190                    // deside on what side draw bullets, etc.
2191                    isLeftToRight = cv.getContainer().getComponentOrientation()
2192                            .isLeftToRight();
2193
2194                    // How the list indicator is aligned is not specified, it is
2195                    // left up to the UA. IE and NS differ on this behavior.
2196                    // This is closer to NS where we align to the first line of text.
2197                    // If the child is not text we draw the indicator at the
2198                    // origin (0).
2199                    float align = 0;
2200                    if (cv.getViewCount() > 0) {
2201                        View pView = cv.getView(0);
2202                        Object cName = pView.getElement().getAttributes()
2203                                .getAttribute(StyleConstants.NameAttribute);
2204                        if ((cName == HTML.Tag.P || cName == HTML.Tag.IMPLIED)
2205                                && pView.getViewCount() > 0) {
2206                            paintRect.setBounds((int) x, (int) y, (int) w,
2207                                    (int) h);
2208                            Shape shape = cv.getChildAllocation(0, paintRect);
2209                            if (shape != null
2210                                    && (shape = pView.getView(0)
2211                                            .getChildAllocation(0, shape)) != null) {
2212                                Rectangle rect = (shape instanceof  Rectangle) ? (Rectangle) shape
2213                                        : shape.getBounds();
2214
2215                                align = pView.getView(0).getAlignment(
2216                                        View.Y_AXIS);
2217                                y = rect.y;
2218                                h = rect.height;
2219                            }
2220                        }
2221                    }
2222
2223                    // set the color of a decoration
2224                    if (ss != null) {
2225                        g.setColor(ss.getForeground(cv.getAttributes()));
2226                    } else {
2227                        g.setColor(Color.black);
2228                    }
2229
2230                    if (img != null) {
2231                        drawIcon(g, (int) x, (int) y, (int) w, (int) h, align,
2232                                v.getContainer());
2233                        return;
2234                    }
2235                    CSS.Value childtype = getChildType(cv);
2236                    Font font = ((StyledDocument) cv.getDocument()).getFont(cv
2237                            .getAttributes());
2238                    if (font != null) {
2239                        g.setFont(font);
2240                    }
2241                    if (childtype == CSS.Value.SQUARE
2242                            || childtype == CSS.Value.CIRCLE
2243                            || childtype == CSS.Value.DISC) {
2244                        drawShape(g, childtype, (int) x, (int) y, (int) w,
2245                                (int) h, align);
2246                    } else if (childtype == CSS.Value.DECIMAL) {
2247                        drawLetter(g, '1', (int) x, (int) y, (int) w, (int) h,
2248                                align, getRenderIndex(v, item));
2249                    } else if (childtype == CSS.Value.LOWER_ALPHA) {
2250                        drawLetter(g, 'a', (int) x, (int) y, (int) w, (int) h,
2251                                align, getRenderIndex(v, item));
2252                    } else if (childtype == CSS.Value.UPPER_ALPHA) {
2253                        drawLetter(g, 'A', (int) x, (int) y, (int) w, (int) h,
2254                                align, getRenderIndex(v, item));
2255                    } else if (childtype == CSS.Value.LOWER_ROMAN) {
2256                        drawLetter(g, 'i', (int) x, (int) y, (int) w, (int) h,
2257                                align, getRenderIndex(v, item));
2258                    } else if (childtype == CSS.Value.UPPER_ROMAN) {
2259                        drawLetter(g, 'I', (int) x, (int) y, (int) w, (int) h,
2260                                align, getRenderIndex(v, item));
2261                    }
2262                }
2263
2264                /**
2265                 * Draws the bullet icon specified by the list-style-image argument.
2266                 *
2267                 * @param g     the graphics context
2268                 * @param ax    x coordinate to place the bullet
2269                 * @param ay    y coordinate to place the bullet
2270                 * @param aw    width of the container the bullet is placed in
2271                 * @param ah    height of the container the bullet is placed in
2272                 * @param align preferred alignment factor for the child view
2273                 */
2274                void drawIcon(Graphics g, int ax, int ay, int aw, int ah,
2275                        float align, Component c) {
2276                    // Align to bottom of icon.
2277                    int gap = isLeftToRight ? -(img.getIconWidth() + bulletgap)
2278                            : (aw + bulletgap);
2279                    int x = ax + gap;
2280                    int y = Math.max(ay, ay + (int) (align * ah)
2281                            - img.getIconHeight());
2282
2283                    img.paintIcon(c, g, x, y);
2284                }
2285
2286                /**
2287                 * Draws the graphical bullet item specified by the type argument.
2288                 *
2289                 * @param g     the graphics context
2290                 * @param type  type of bullet to draw (circle, square, disc)
2291                 * @param ax    x coordinate to place the bullet
2292                 * @param ay    y coordinate to place the bullet
2293                 * @param aw    width of the container the bullet is placed in
2294                 * @param ah    height of the container the bullet is placed in
2295                 * @param align preferred alignment factor for the child view
2296                 */
2297                void drawShape(Graphics g, CSS.Value type, int ax, int ay,
2298                        int aw, int ah, float align) {
2299                    // Align to bottom of shape.
2300                    int gap = isLeftToRight ? -(bulletgap + 8)
2301                            : (aw + bulletgap);
2302                    int x = ax + gap;
2303                    int y = Math.max(ay, ay + (int) (align * ah) - 8);
2304
2305                    if (type == CSS.Value.SQUARE) {
2306                        g.drawRect(x, y, 8, 8);
2307                    } else if (type == CSS.Value.CIRCLE) {
2308                        g.drawOval(x, y, 8, 8);
2309                    } else {
2310                        g.fillOval(x, y, 8, 8);
2311                    }
2312                }
2313
2314                /**
2315                 * Draws the letter or number for an ordered list.
2316                 *
2317                 * @param g     the graphics context
2318                 * @param letter type of ordered list to draw
2319                 * @param ax    x coordinate to place the bullet
2320                 * @param ay    y coordinate to place the bullet
2321                 * @param aw    width of the container the bullet is placed in
2322                 * @param ah    height of the container the bullet is placed in
2323                 * @param index position of the list item in the list
2324                 */
2325                void drawLetter(Graphics g, char letter, int ax, int ay,
2326                        int aw, int ah, float align, int index) {
2327                    String str = formatItemNum(index, letter);
2328                    str = isLeftToRight ? str + "." : "." + str;
2329                    FontMetrics fm = SwingUtilities2.getFontMetrics(null, g);
2330                    int stringwidth = SwingUtilities2
2331                            .stringWidth(null, fm, str);
2332                    int gap = isLeftToRight ? -(stringwidth + bulletgap)
2333                            : (aw + bulletgap);
2334                    int x = ax + gap;
2335                    int y = Math.max(ay + fm.getAscent(), ay
2336                            + (int) (ah * align));
2337                    SwingUtilities2.drawString(null, g, str, x, y);
2338                }
2339
2340                /**
2341                 * Converts the item number into the ordered list number
2342                 * (i.e.  1 2 3, i ii iii, a b c, etc.
2343                 *
2344                 * @param itemNum number to format
2345                 * @param type    type of ordered list
2346                 */
2347                String formatItemNum(int itemNum, char type) {
2348                    String numStyle = "1";
2349
2350                    boolean uppercase = false;
2351
2352                    String formattedNum;
2353
2354                    switch (type) {
2355                    case '1':
2356                    default:
2357                        formattedNum = String.valueOf(itemNum);
2358                        break;
2359
2360                    case 'A':
2361                        uppercase = true;
2362                        // fall through
2363                    case 'a':
2364                        formattedNum = formatAlphaNumerals(itemNum);
2365                        break;
2366
2367                    case 'I':
2368                        uppercase = true;
2369                        // fall through
2370                    case 'i':
2371                        formattedNum = formatRomanNumerals(itemNum);
2372                    }
2373
2374                    if (uppercase) {
2375                        formattedNum = formattedNum.toUpperCase();
2376                    }
2377
2378                    return formattedNum;
2379                }
2380
2381                /**
2382                 * Converts the item number into an alphabetic character
2383                 *
2384                 * @param itemNum number to format
2385                 */
2386                String formatAlphaNumerals(int itemNum) {
2387                    String result = "";
2388
2389                    if (itemNum > 26) {
2390                        result = formatAlphaNumerals(itemNum / 26)
2391                                + formatAlphaNumerals(itemNum % 26);
2392                    } else {
2393                        // -1 because item is 1 based.
2394                        result = String.valueOf((char) ('a' + itemNum - 1));
2395                    }
2396
2397                    return result;
2398                }
2399
2400                /* list of roman numerals */
2401                static final char romanChars[][] = { { 'i', 'v' },
2402                        { 'x', 'l' }, { 'c', 'd' }, { 'm', '?' }, };
2403
2404                /**
2405                 * Converts the item number into a roman numeral
2406                 *
2407                 * @param num  number to format
2408                 */
2409                String formatRomanNumerals(int num) {
2410                    return formatRomanNumerals(0, num);
2411                }
2412
2413                /**
2414                 * Converts the item number into a roman numeral
2415                 *
2416                 * @param num  number to format
2417                 */
2418                String formatRomanNumerals(int level, int num) {
2419                    if (num < 10) {
2420                        return formatRomanDigit(level, num);
2421                    } else {
2422                        return formatRomanNumerals(level + 1, num / 10)
2423                                + formatRomanDigit(level, num % 10);
2424                    }
2425                }
2426
2427                /**
2428                 * Converts the item number into a roman numeral
2429                 *
2430                 * @param level position
2431                 * @param num   digit to format
2432                 */
2433                String formatRomanDigit(int level, int digit) {
2434                    String result = "";
2435                    if (digit == 9) {
2436                        result = result + romanChars[level][0];
2437                        result = result + romanChars[level + 1][0];
2438                        return result;
2439                    } else if (digit == 4) {
2440                        result = result + romanChars[level][0];
2441                        result = result + romanChars[level][1];
2442                        return result;
2443                    } else if (digit >= 5) {
2444                        result = result + romanChars[level][1];
2445                        digit -= 5;
2446                    }
2447
2448                    for (int i = 0; i < digit; i++) {
2449                        result = result + romanChars[level][0];
2450                    }
2451
2452                    return result;
2453                }
2454
2455                private Rectangle paintRect;
2456                private boolean checkedForStart;
2457                private int start;
2458                private CSS.Value type;
2459                URL imageurl;
2460                private StyleSheet ss = null;
2461                Icon img = null;
2462                private int bulletgap = 5;
2463                private boolean isLeftToRight;
2464            }
2465
2466            /**
2467             * Paints the background image.
2468             */
2469            static class BackgroundImagePainter implements  Serializable {
2470                ImageIcon backgroundImage;
2471                float hPosition;
2472                float vPosition;
2473                // bit mask: 0 for repeat x, 1 for repeat y, 2 for horiz relative,
2474                // 3 for vert relative
2475                short flags;
2476                // These are used when painting, updatePaintCoordinates updates them.
2477                private int paintX;
2478                private int paintY;
2479                private int paintMaxX;
2480                private int paintMaxY;
2481
2482                BackgroundImagePainter(AttributeSet a, CSS css, StyleSheet ss) {
2483                    backgroundImage = ss.getBackgroundImage(a);
2484                    // Determine the position.
2485                    CSS.BackgroundPosition pos = (CSS.BackgroundPosition) a
2486                            .getAttribute(CSS.Attribute.BACKGROUND_POSITION);
2487                    if (pos != null) {
2488                        hPosition = pos.getHorizontalPosition();
2489                        vPosition = pos.getVerticalPosition();
2490                        if (pos.isHorizontalPositionRelativeToSize()) {
2491                            flags |= 4;
2492                        } else if (pos.isHorizontalPositionRelativeToSize()) {
2493                            hPosition *= css.getFontSize(a, 12, ss);
2494                        }
2495                        if (pos.isVerticalPositionRelativeToSize()) {
2496                            flags |= 8;
2497                        } else if (pos.isVerticalPositionRelativeToFontSize()) {
2498                            vPosition *= css.getFontSize(a, 12, ss);
2499                        }
2500                    }
2501                    // Determine any repeating values.
2502                    CSS.Value repeats = (CSS.Value) a
2503                            .getAttribute(CSS.Attribute.BACKGROUND_REPEAT);
2504                    if (repeats == null
2505                            || repeats == CSS.Value.BACKGROUND_REPEAT) {
2506                        flags |= 3;
2507                    } else if (repeats == CSS.Value.BACKGROUND_REPEAT_X) {
2508                        flags |= 1;
2509                    } else if (repeats == CSS.Value.BACKGROUND_REPEAT_Y) {
2510                        flags |= 2;
2511                    }
2512                }
2513
2514                void paint(Graphics g, float x, float y, float w, float h,
2515                        View v) {
2516                    Rectangle clip = g.getClipRect();
2517                    if (clip != null) {
2518                        // Constrain the clip so that images don't draw outside the
2519                        // legal bounds.
2520                        g.clipRect((int) x, (int) y, (int) w, (int) h);
2521                    }
2522                    if ((flags & 3) == 0) {
2523                        // no repeating
2524                        int width = backgroundImage.getIconWidth();
2525                        int height = backgroundImage.getIconWidth();
2526                        if ((flags & 4) == 4) {
2527                            paintX = (int) (x + w * hPosition - (float) width
2528                                    * hPosition);
2529                        } else {
2530                            paintX = (int) x + (int) hPosition;
2531                        }
2532                        if ((flags & 8) == 8) {
2533                            paintY = (int) (y + h * vPosition - (float) height
2534                                    * vPosition);
2535                        } else {
2536                            paintY = (int) y + (int) vPosition;
2537                        }
2538                        if (clip == null
2539                                || !((paintX + width <= clip.x)
2540                                        || (paintY + height <= clip.y)
2541                                        || (paintX >= clip.x + clip.width) || (paintY >= clip.y
2542                                        + clip.height))) {
2543                            backgroundImage.paintIcon(null, g, paintX, paintY);
2544                        }
2545                    } else {
2546                        int width = backgroundImage.getIconWidth();
2547                        int height = backgroundImage.getIconHeight();
2548                        if (width > 0 && height > 0) {
2549                            paintX = (int) x;
2550                            paintY = (int) y;
2551                            paintMaxX = (int) (x + w);
2552                            paintMaxY = (int) (y + h);
2553                            if (updatePaintCoordinates(clip, width, height)) {
2554                                while (paintX < paintMaxX) {
2555                                    int ySpot = paintY;
2556                                    while (ySpot < paintMaxY) {
2557                                        backgroundImage.paintIcon(null, g,
2558                                                paintX, ySpot);
2559                                        ySpot += height;
2560                                    }
2561                                    paintX += width;
2562                                }
2563                            }
2564                        }
2565                    }
2566                    if (clip != null) {
2567                        // Reset clip.
2568                        g.setClip(clip.x, clip.y, clip.width, clip.height);
2569                    }
2570                }
2571
2572                private boolean updatePaintCoordinates(Rectangle clip,
2573                        int width, int height) {
2574                    if ((flags & 3) == 1) {
2575                        paintMaxY = paintY + 1;
2576                    } else if ((flags & 3) == 2) {
2577                        paintMaxX = paintX + 1;
2578                    }
2579                    if (clip != null) {
2580                        if ((flags & 3) == 1
2581                                && ((paintY + height <= clip.y) || (paintY > clip.y
2582                                        + clip.height))) {
2583                            // not visible.
2584                            return false;
2585                        }
2586                        if ((flags & 3) == 2
2587                                && ((paintX + width <= clip.x) || (paintX > clip.x
2588                                        + clip.width))) {
2589                            // not visible.
2590                            return false;
2591                        }
2592                        if ((flags & 1) == 1) {
2593                            if ((clip.x + clip.width) < paintMaxX) {
2594                                if ((clip.x + clip.width - paintX) % width == 0) {
2595                                    paintMaxX = clip.x + clip.width;
2596                                } else {
2597                                    paintMaxX = ((clip.x + clip.width - paintX)
2598                                            / width + 1)
2599                                            * width + paintX;
2600                                }
2601                            }
2602                            if (clip.x > paintX) {
2603                                paintX = (clip.x - paintX) / width * width
2604                                        + paintX;
2605                            }
2606                        }
2607                        if ((flags & 2) == 2) {
2608                            if ((clip.y + clip.height) < paintMaxY) {
2609                                if ((clip.y + clip.height - paintY) % height == 0) {
2610                                    paintMaxY = clip.y + clip.height;
2611                                } else {
2612                                    paintMaxY = ((clip.y + clip.height - paintY)
2613                                            / height + 1)
2614                                            * height + paintY;
2615                                }
2616                            }
2617                            if (clip.y > paintY) {
2618                                paintY = (clip.y - paintY) / height * height
2619                                        + paintY;
2620                            }
2621                        }
2622                    }
2623                    // Valid
2624                    return true;
2625                }
2626            }
2627
2628            /**
2629             * A subclass of MuxingAttributeSet that translates between
2630             * CSS and HTML and StyleConstants. The AttributeSets used are
2631             * the CSS rules that match the Views Elements.
2632             */
2633            class ViewAttributeSet extends MuxingAttributeSet {
2634                ViewAttributeSet(View v) {
2635                    host = v;
2636
2637                    // PENDING(prinz) fix this up to be a more realistic
2638                    // implementation.
2639                    Document doc = v.getDocument();
2640                    SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
2641                    Vector muxList = sb.getVector();
2642                    try {
2643                        if (doc instanceof  HTMLDocument) {
2644                            StyleSheet styles = StyleSheet.this ;
2645                            Element elem = v.getElement();
2646                            AttributeSet a = elem.getAttributes();
2647                            AttributeSet htmlAttr = styles
2648                                    .translateHTMLToCSS(a);
2649
2650                            if (htmlAttr.getAttributeCount() != 0) {
2651                                muxList.addElement(htmlAttr);
2652                            }
2653                            if (elem.isLeaf()) {
2654                                Enumeration keys = a.getAttributeNames();
2655                                while (keys.hasMoreElements()) {
2656                                    Object key = keys.nextElement();
2657                                    if (key instanceof  HTML.Tag) {
2658                                        if ((HTML.Tag) key == HTML.Tag.A) {
2659                                            Object o = a
2660                                                    .getAttribute((HTML.Tag) key);
2661                                            /**
2662                                               In the case of an A tag, the css rules
2663                                               apply only for tags that have their
2664                                               href attribute defined and not for
2665                                               anchors that only have their name attributes
2666                                               defined, i.e anchors that function as
2667                                               destinations.  Hence we do not add the
2668                                               attributes for that latter kind of 
2669                                               anchors.  When CSS2 support is added,
2670                                               it will be possible to specificity this
2671                                               kind of conditional behaviour in the 
2672                                               stylesheet.
2673                                             **/
2674                                            if (o != null
2675                                                    && o instanceof  AttributeSet) {
2676                                                AttributeSet attr = (AttributeSet) o;
2677                                                if (attr
2678                                                        .getAttribute(HTML.Attribute.HREF) == null) {
2679                                                    continue;
2680                                                }
2681                                            }
2682                                        }
2683                                        AttributeSet cssRule = styles.getRule(
2684                                                (HTML.Tag) key, elem);
2685                                        if (cssRule != null) {
2686                                            muxList.addElement(cssRule);
2687                                        }
2688                                    }
2689                                }
2690                            } else {
2691                                HTML.Tag t = (HTML.Tag) a
2692                                        .getAttribute(StyleConstants.NameAttribute);
2693                                AttributeSet cssRule = styles.getRule(t, elem);
2694                                if (cssRule != null) {
2695                                    muxList.addElement(cssRule);
2696                                }
2697                            }
2698                        }
2699                        AttributeSet[] attrs = new AttributeSet[muxList.size()];
2700                        muxList.copyInto(attrs);
2701                        setAttributes(attrs);
2702                    } finally {
2703                        SearchBuffer.releaseSearchBuffer(sb);
2704                    }
2705                }
2706
2707                //  --- AttributeSet methods ----------------------------
2708
2709                /**
2710                 * Checks whether a given attribute is defined.
2711                 * This will convert the key over to CSS if the
2712                 * key is a StyleConstants key that has a CSS
2713                 * mapping.
2714                 *
2715                 * @param key the attribute key
2716                 * @return true if the attribute is defined
2717                 * @see AttributeSet#isDefined
2718                 */
2719                public boolean isDefined(Object key) {
2720                    if (key instanceof  StyleConstants) {
2721                        Object cssKey = css
2722                                .styleConstantsKeyToCSSKey((StyleConstants) key);
2723                        if (cssKey != null) {
2724                            key = cssKey;
2725                        }
2726                    }
2727                    return super .isDefined(key);
2728                }
2729
2730                /**
2731                 * Gets the value of an attribute.  If the requested
2732                 * attribute is a StyleConstants attribute that has
2733                 * a CSS mapping, the request will be converted.
2734                 *
2735                 * @param key the attribute name
2736                 * @return the attribute value
2737                 * @see AttributeSet#getAttribute
2738                 */
2739                public Object getAttribute(Object key) {
2740                    if (key instanceof  StyleConstants) {
2741                        Object cssKey = css
2742                                .styleConstantsKeyToCSSKey((StyleConstants) key);
2743                        if (cssKey != null) {
2744                            Object value = doGetAttribute(cssKey);
2745                            if (value instanceof  CSS.CssValue) {
2746                                return ((CSS.CssValue) value).toStyleConstants(
2747                                        (StyleConstants) key, host);
2748                            }
2749                        }
2750                    }
2751                    return doGetAttribute(key);
2752                }
2753
2754                Object doGetAttribute(Object key) {
2755                    Object retValue = super .getAttribute(key);
2756                    if (retValue != null) {
2757                        return retValue;
2758                    }
2759                    // didn't find it... try parent if it's a css attribute
2760                    // that is inherited.
2761                    if (key instanceof  CSS.Attribute) {
2762                        CSS.Attribute css = (CSS.Attribute) key;
2763                        if (css.isInherited()) {
2764                            AttributeSet parent = getResolveParent();
2765                            if (parent != null)
2766                                return parent.getAttribute(key);
2767                        }
2768                    }
2769                    return null;
2770                }
2771
2772                /**
2773                 * If not overriden, the resolving parent defaults to
2774                 * the parent element.
2775                 *
2776                 * @return the attributes from the parent
2777                 * @see AttributeSet#getResolveParent
2778                 */
2779                public AttributeSet getResolveParent() {
2780                    if (host == null) {
2781                        return null;
2782                    }
2783                    View parent = host.getParent();
2784                    return (parent != null) ? parent.getAttributes() : null;
2785                }
2786
2787                /** View created for. */
2788                View host;
2789            }
2790
2791            /**
2792             * A subclass of MuxingAttributeSet that implements Style. Currently
2793             * the MutableAttributeSet methods are unimplemented, that is they
2794             * do nothing.
2795             */
2796            // PENDING(sky): Decide what to do with this. Either make it
2797            // contain a SimpleAttributeSet that modify methods are delegated to,
2798            // or change getRule to return an AttributeSet and then don't make this
2799            // implement Style.
2800            static class ResolvedStyle extends MuxingAttributeSet implements 
2801                    Serializable, Style {
2802                ResolvedStyle(String name, AttributeSet[] attrs,
2803                        int extendedIndex) {
2804                    super (attrs);
2805                    this .name = name;
2806                    this .extendedIndex = extendedIndex;
2807                }
2808
2809                /**
2810                 * Inserts a Style into the receiver so that the styles the
2811                 * receiver represents are still ordered by specificity.
2812                 * <code>style</code> will be added before any extended styles, that
2813                 * is before extendedIndex.
2814                 */
2815                synchronized void insertStyle(Style style, int specificity) {
2816                    AttributeSet[] attrs = getAttributes();
2817                    int maxCounter = attrs.length;
2818                    int counter = 0;
2819                    for (; counter < extendedIndex; counter++) {
2820                        if (specificity > getSpecificity(((Style) attrs[counter])
2821                                .getName())) {
2822                            break;
2823                        }
2824                    }
2825                    insertAttributeSetAt(style, counter);
2826                    extendedIndex++;
2827                }
2828
2829                /**
2830                 * Removes a previously added style. This will do nothing if
2831                 * <code>style</code> is not referenced by the receiver.
2832                 */
2833                synchronized void removeStyle(Style style) {
2834                    AttributeSet[] attrs = getAttributes();
2835
2836                    for (int counter = attrs.length - 1; counter >= 0; counter--) {
2837                        if (attrs[counter] == style) {
2838                            removeAttributeSetAt(counter);
2839                            if (counter < extendedIndex) {
2840                                extendedIndex--;
2841                            }
2842                            break;
2843                        }
2844                    }
2845                }
2846
2847                /**
2848                 * Adds <code>s</code> as one of the Attributesets to look up
2849                 * attributes in.
2850                 */
2851                synchronized void insertExtendedStyleAt(Style attr, int index) {
2852                    insertAttributeSetAt(attr, extendedIndex + index);
2853                }
2854
2855                /**
2856                 * Adds <code>s</code> as one of the AttributeSets to look up
2857                 * attributes in. It will be the AttributeSet last checked.
2858                 */
2859                synchronized void addExtendedStyle(Style attr) {
2860                    insertAttributeSetAt(attr, getAttributes().length);
2861                }
2862
2863                /**
2864                 * Removes the style at <code>index</code> +
2865                 * <code>extendedIndex</code>.
2866                 */
2867                synchronized void removeExtendedStyleAt(int index) {
2868                    removeAttributeSetAt(extendedIndex + index);
2869                }
2870
2871                /**
2872                 * Returns true if the receiver matches <code>selector</code>, where
2873                 * a match is defined by the CSS rule matching.
2874                 * Each simple selector must be separated by a single space.
2875                 */
2876                protected boolean matches(String selector) {
2877                    int sLast = selector.length();
2878
2879                    if (sLast == 0) {
2880                        return false;
2881                    }
2882                    int this Last = name.length();
2883                    int sCurrent = selector.lastIndexOf(' ');
2884                    int this Current = name.lastIndexOf(' ');
2885                    if (sCurrent >= 0) {
2886                        sCurrent++;
2887                    }
2888                    if (this Current >= 0) {
2889                        this Current++;
2890                    }
2891                    if (!matches(selector, sCurrent, sLast, this Current,
2892                            this Last)) {
2893                        return false;
2894                    }
2895                    while (sCurrent != -1) {
2896                        sLast = sCurrent - 1;
2897                        sCurrent = selector.lastIndexOf(' ', sLast - 1);
2898                        if (sCurrent >= 0) {
2899                            sCurrent++;
2900                        }
2901                        boolean match = false;
2902                        while (!match && this Current != -1) {
2903                            this Last = this Current - 1;
2904                            this Current = name.lastIndexOf(' ', this Last - 1);
2905                            if (this Current >= 0) {
2906                                this Current++;
2907                            }
2908                            match = matches(selector, sCurrent, sLast,
2909                                    this Current, this Last);
2910                        }
2911                        if (!match) {
2912                            return false;
2913                        }
2914                    }
2915                    return true;
2916                }
2917
2918                /**
2919                 * Returns true if the substring of the receiver, in the range
2920                 * thisCurrent, thisLast matches the substring of selector in
2921                 * the ranme sCurrent to sLast based on CSS selector matching.
2922                 */
2923                boolean matches(String selector, int sCurrent, int sLast,
2924                        int this Current, int this Last) {
2925                    sCurrent = Math.max(sCurrent, 0);
2926                    this Current = Math.max(this Current, 0);
2927                    int this DotIndex = boundedIndexOf(name, '.', this Current,
2928                            this Last);
2929                    int this PoundIndex = boundedIndexOf(name, '#', this Current,
2930                            this Last);
2931                    int sDotIndex = boundedIndexOf(selector, '.', sCurrent,
2932                            sLast);
2933                    int sPoundIndex = boundedIndexOf(selector, '#', sCurrent,
2934                            sLast);
2935                    if (sDotIndex != -1) {
2936                        // Selector has a '.', which indicates name must match it,
2937                        // or if the '.' starts the selector than name must have
2938                        // the same class (doesn't matter what element name).
2939                        if (this DotIndex == -1) {
2940                            return false;
2941                        }
2942                        if (sCurrent == sDotIndex) {
2943                            if ((this Last - this DotIndex) != (sLast - sDotIndex)
2944                                    || !selector.regionMatches(sCurrent, name,
2945                                            this DotIndex,
2946                                            (this Last - this DotIndex))) {
2947                                return false;
2948                            }
2949                        } else {
2950                            // Has to fully match.
2951                            if ((sLast - sCurrent) != (this Last - this Current)
2952                                    || !selector.regionMatches(sCurrent, name,
2953                                            this Current,
2954                                            (this Last - this Current))) {
2955                                return false;
2956                            }
2957                        }
2958                        return true;
2959                    }
2960                    if (sPoundIndex != -1) {
2961                        // Selector has a '#', which indicates name must match it,
2962                        // or if the '#' starts the selector than name must have
2963                        // the same id (doesn't matter what element name).
2964                        if (this PoundIndex == -1) {
2965                            return false;
2966                        }
2967                        if (sCurrent == sPoundIndex) {
2968                            if ((this Last - this PoundIndex) != (sLast - sPoundIndex)
2969                                    || !selector.regionMatches(sCurrent, name,
2970                                            this PoundIndex,
2971                                            (this Last - this PoundIndex))) {
2972                                return false;
2973                            }
2974                        } else {
2975                            // Has to fully match.
2976                            if ((sLast - sCurrent) != (this Last - this Current)
2977                                    || !selector.regionMatches(sCurrent, name,
2978                                            this Current,
2979                                            (this Last - this Current))) {
2980                                return false;
2981                            }
2982                        }
2983                        return true;
2984                    }
2985                    if (this DotIndex != -1) {
2986                        // Reciever references a class, just check element name.
2987                        return (((this DotIndex - this Current) == (sLast - sCurrent)) && selector
2988                                .regionMatches(sCurrent, name, this Current,
2989                                        this DotIndex - this Current));
2990                    }
2991                    if (this PoundIndex != -1) {
2992                        // Reciever references an id, just check element name.
2993                        return (((this PoundIndex - this Current) == (sLast - sCurrent)) && selector
2994                                .regionMatches(sCurrent, name, this Current,
2995                                        this PoundIndex - this Current));
2996                    }
2997                    // Fail through, no classes or ides, just check string.
2998                    return (((this Last - this Current) == (sLast - sCurrent)) && selector
2999                            .regionMatches(sCurrent, name, this Current,
3000                                    this Last - this Current));
3001                }
3002
3003                /**
3004                 * Similiar to String.indexOf, but allows an upper bound
3005                 * (this is slower in that it will still check string starting at
3006                 * start.
3007                 */
3008                int boundedIndexOf(String string, char search, int start,
3009                        int end) {
3010                    int retValue = string.indexOf(search, start);
3011                    if (retValue >= end) {
3012                        return -1;
3013                    }
3014                    return retValue;
3015                }
3016
3017                public void addAttribute(Object name, Object value) {
3018                }
3019
3020                public void addAttributes(AttributeSet attributes) {
3021                }
3022
3023                public void removeAttribute(Object name) {
3024                }
3025
3026                public void removeAttributes(Enumeration<?> names) {
3027                }
3028
3029                public void removeAttributes(AttributeSet attributes) {
3030                }
3031
3032                public void setResolveParent(AttributeSet parent) {
3033                }
3034
3035                public String getName() {
3036                    return name;
3037                }
3038
3039                public void addChangeListener(ChangeListener l) {
3040                }
3041
3042                public void removeChangeListener(ChangeListener l) {
3043                }
3044
3045                public ChangeListener[] getChangeListeners() {
3046                    return new ChangeListener[0];
3047                }
3048
3049                /** The name of the Style, which is the selector.
3050                 * This will NEVER change!
3051                 */
3052                String name;
3053                /** Start index of styles coming from other StyleSheets. */
3054                private int extendedIndex;
3055            }
3056
3057            /**
3058             * SelectorMapping contains a specifitiy, as an integer, and an associated
3059             * Style. It can also reference children <code>SelectorMapping</code>s,
3060             * so that it behaves like a tree.
3061             * <p>
3062             * This is not thread safe, it is assumed the caller will take the
3063             * necessary precations if this is to be used in a threaded environment.
3064             */
3065            static class SelectorMapping implements  Serializable {
3066                public SelectorMapping(int specificity) {
3067                    this .specificity = specificity;
3068                }
3069
3070                /**
3071                 * Returns the specificity this mapping represents.
3072                 */
3073                public int getSpecificity() {
3074                    return specificity;
3075                }
3076
3077                /**
3078                 * Sets the Style associated with this mapping.
3079                 */
3080                public void setStyle(Style style) {
3081                    this .style = style;
3082                }
3083
3084                /**
3085                 * Returns the Style associated with this mapping.
3086                 */
3087                public Style getStyle() {
3088                    return style;
3089                }
3090
3091                /**
3092                 * Returns the child mapping identified by the simple selector
3093                 * <code>selector</code>. If a child mapping does not exist for
3094                 *<code>selector</code>, and <code>create</code> is true, a new
3095                 * one will be created.
3096                 */
3097                public SelectorMapping getChildSelectorMapping(String selector,
3098                        boolean create) {
3099                    SelectorMapping retValue = null;
3100
3101                    if (children != null) {
3102                        retValue = (SelectorMapping) children.get(selector);
3103                    } else if (create) {
3104                        children = new HashMap(7);
3105                    }
3106                    if (retValue == null && create) {
3107                        int specificity = getChildSpecificity(selector);
3108
3109                        retValue = createChildSelectorMapping(specificity);
3110                        children.put(selector, retValue);
3111                    }
3112                    return retValue;
3113                }
3114
3115                /**
3116                 * Creates a child <code>SelectorMapping</code> with the specified
3117                 * <code>specificity</code>.
3118                 */
3119                protected SelectorMapping createChildSelectorMapping(
3120                        int specificity) {
3121                    return new SelectorMapping(specificity);
3122                }
3123
3124                /**
3125                 * Returns the specificity for the child selector
3126                 * <code>selector</code>.
3127                 */
3128                protected int getChildSpecificity(String selector) {
3129                    // class (.) 100
3130                    // id (#)    10000
3131                    char firstChar = selector.charAt(0);
3132                    int specificity = getSpecificity();
3133
3134                    if (firstChar == '.') {
3135                        specificity += 100;
3136                    } else if (firstChar == '#') {
3137                        specificity += 10000;
3138                    } else {
3139                        specificity += 1;
3140                        if (selector.indexOf('.') != -1) {
3141                            specificity += 100;
3142                        }
3143                        if (selector.indexOf('#') != -1) {
3144                            specificity += 10000;
3145                        }
3146                    }
3147                    return specificity;
3148                }
3149
3150                /**
3151                 * The specificity for this selector.
3152                 */
3153                private int specificity;
3154                /**
3155                 * Style for this selector.
3156                 */
3157                private Style style;
3158                /**
3159                 * Any sub selectors. Key will be String, and value will be
3160                 * another SelectorMapping.
3161                 */
3162                private HashMap children;
3163            }
3164
3165            // ---- Variables ---------------------------------------------
3166
3167            final static int DEFAULT_FONT_SIZE = 3;
3168
3169            private CSS css;
3170
3171            /**
3172             * An inverted graph of the selectors.
3173             */
3174            private SelectorMapping selectorMapping;
3175
3176            /** Maps from selector (as a string) to Style that includes all
3177             * relevant styles. */
3178            private Hashtable resolvedStyles;
3179
3180            /** Vector of StyleSheets that the rules are to reference.
3181             */
3182            private Vector linkedStyleSheets;
3183
3184            /** Where the style sheet was found. Used for relative imports. */
3185            private URL base;
3186
3187            /**
3188             * Default parser for CSS specifications that get loaded into
3189             * the StyleSheet.<p>
3190             * This class is NOT thread safe, do not ask it to parse while it is
3191             * in the middle of parsing.
3192             */
3193            class CssParser implements  CSSParser.CSSParserCallback {
3194
3195                /**
3196                 * Parses the passed in CSS declaration into an AttributeSet.
3197                 */
3198                public AttributeSet parseDeclaration(String string) {
3199                    try {
3200                        return parseDeclaration(new StringReader(string));
3201                    } catch (IOException ioe) {
3202                    }
3203                    return null;
3204                }
3205
3206                /**
3207                 * Parses the passed in CSS declaration into an AttributeSet.
3208                 */
3209                public AttributeSet parseDeclaration(Reader r)
3210                        throws IOException {
3211                    parse(base, r, true, false);
3212                    return declaration.copyAttributes();
3213                }
3214
3215                /**
3216                 * Parse the given CSS stream
3217                 */
3218                public void parse(URL base, Reader r, boolean parseDeclaration,
3219                        boolean isLink) throws IOException {
3220                    this .base = base;
3221                    this .isLink = isLink;
3222                    this .parsingDeclaration = parseDeclaration;
3223                    declaration.removeAttributes(declaration);
3224                    selectorTokens.removeAllElements();
3225                    selectors.removeAllElements();
3226                    propertyName = null;
3227                    parser.parse(r, this , parseDeclaration);
3228                }
3229
3230                //
3231                // CSSParserCallback methods, public to implement the interface.
3232                //
3233
3234                /**
3235                 * Invoked when a valid @import is encountered, will call
3236                 * <code>importStyleSheet</code> if a 
3237                 * <code>MalformedURLException</code> is not thrown in creating
3238                 * the URL.
3239                 */
3240                public void handleImport(String importString) {
3241                    URL url = CSS.getURL(base, importString);
3242                    if (url != null) {
3243                        importStyleSheet(url);
3244                    }
3245                }
3246
3247                /**
3248                 * A selector has been encountered.
3249                 */
3250                public void handleSelector(String selector) {
3251                    //class and index selectors are case sensitive
3252                    if (!(selector.startsWith(".") || selector.startsWith("#"))) {
3253                        selector = selector.toLowerCase();
3254                    }
3255                    int length = selector.length();
3256
3257                    if (selector.endsWith(",")) {
3258                        if (length > 1) {
3259                            selector = selector.substring(0, length - 1);
3260                            selectorTokens.addElement(selector);
3261                        }
3262                        addSelector();
3263                    } else if (length > 0) {
3264                        selectorTokens.addElement(selector);
3265                    }
3266                }
3267
3268                /**
3269                 * Invoked when the start of a rule is encountered.
3270                 */
3271                public void startRule() {
3272                    if (selectorTokens.size() > 0) {
3273                        addSelector();
3274                    }
3275                    propertyName = null;
3276                }
3277
3278                /**
3279                 * Invoked when a property name is encountered.
3280                 */
3281                public void handleProperty(String property) {
3282                    propertyName = property;
3283                }
3284
3285                /**
3286                 * Invoked when a property value is encountered.
3287                 */
3288                public void handleValue(String value) {
3289                    if (propertyName != null && value != null
3290                            && value.length() > 0) {
3291                        CSS.Attribute cssKey = CSS.getAttribute(propertyName);
3292                        if (cssKey != null) {
3293                            // There is currently no mechanism to determine real
3294                            // base that style sheet was loaded from. For the time
3295                            // being, this maps for LIST_STYLE_IMAGE, which appear
3296                            // to be the only one that currently matters. A more
3297                            // general mechanism is definately needed.
3298                            if (cssKey == CSS.Attribute.LIST_STYLE_IMAGE) {
3299                                if (value != null && !value.equals("none")) {
3300                                    URL url = CSS.getURL(base, value);
3301
3302                                    if (url != null) {
3303                                        value = url.toString();
3304                                    }
3305                                }
3306                            }
3307                            addCSSAttribute(declaration, cssKey, value);
3308                        }
3309                        propertyName = null;
3310                    }
3311                }
3312
3313                /**
3314                 * Invoked when the end of a rule is encountered.
3315                 */
3316                public void endRule() {
3317                    int n = selectors.size();
3318                    for (int i = 0; i < n; i++) {
3319                        String[] selector = (String[]) selectors.elementAt(i);
3320                        if (selector.length > 0) {
3321                            StyleSheet.this .addRule(selector, declaration,
3322                                    isLink);
3323                        }
3324                    }
3325                    declaration.removeAttributes(declaration);
3326                    selectors.removeAllElements();
3327                }
3328
3329                private void addSelector() {
3330                    String[] selector = new String[selectorTokens.size()];
3331                    selectorTokens.copyInto(selector);
3332                    selectors.addElement(selector);
3333                    selectorTokens.removeAllElements();
3334                }
3335
3336                Vector selectors = new Vector();
3337                Vector selectorTokens = new Vector();
3338                /** Name of the current property. */
3339                String propertyName;
3340                MutableAttributeSet declaration = new SimpleAttributeSet();
3341                /** True if parsing a declaration, that is the Reader will not
3342                 * contain a selector. */
3343                boolean parsingDeclaration;
3344                /** True if the attributes are coming from a linked/imported style. */
3345                boolean isLink;
3346                /** Where the CSS stylesheet lives. */
3347                URL base;
3348                CSSParser parser = new CSSParser();
3349            }
3350
3351            void rebaseSizeMap(int base) {
3352                final int minimalFontSize = 4;
3353                sizeMap = new int[sizeMapDefault.length];
3354                for (int i = 0; i < sizeMapDefault.length; i++) {
3355                    sizeMap[i] = Math.max(base * sizeMapDefault[i]
3356                            / sizeMapDefault[CSS.baseFontSizeIndex],
3357                            minimalFontSize);
3358                }
3359
3360            }
3361
3362            int[] getSizeMap() {
3363                return sizeMap;
3364            }
3365
3366            boolean isW3CLengthUnits() {
3367                return w3cLengthUnits;
3368            }
3369
3370            /**
3371             * The HTML/CSS size model has seven slots
3372             * that one can assign sizes to.
3373             */
3374            static final int sizeMapDefault[] = { 8, 10, 12, 14, 18, 24, 36 };
3375
3376            private int sizeMap[] = sizeMapDefault;
3377            private boolean w3cLengthUnits = false;
3378        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.