< prev index next >

src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java

Print this page




  29 import java.awt.*;
  30 import java.io.*;
  31 import java.net.*;
  32 import javax.swing.Icon;
  33 import javax.swing.ImageIcon;
  34 import javax.swing.UIManager;
  35 import javax.swing.border.*;
  36 import javax.swing.event.ChangeListener;
  37 import javax.swing.text.*;
  38 
  39 /**
  40  * Support for defining the visual characteristics of
  41  * HTML views being rendered.  The StyleSheet is used to
  42  * translate the HTML model into visual characteristics.
  43  * This enables views to be customized by a look-and-feel,
  44  * multiple views over the same model can be rendered
  45  * differently, etc.  This can be thought of as a CSS
  46  * rule repository.  The key for CSS attributes is an
  47  * object of type CSS.Attribute.  The type of the value
  48  * is up to the StyleSheet implementation, but the
  49  * <code>toString</code> method is required
  50  * to return a string representation of CSS value.
  51  * <p>
  52  * The primary entry point for HTML View implementations
  53  * to get their attributes is the
  54  * {@link #getViewAttributes getViewAttributes}
  55  * method.  This should be implemented to establish the
  56  * desired policy used to associate attributes with the view.
  57  * Each HTMLEditorKit (i.e. and therefore each associated
  58  * JEditorPane) can have its own StyleSheet, but by default one
  59  * sheet will be shared by all of the HTMLEditorKit instances.
  60  * HTMLDocument instance can also have a StyleSheet, which
  61  * holds the document-specific CSS specifications.
  62  * <p>
  63  * In order for Views to store less state and therefore be
  64  * more lightweight, the StyleSheet can act as a factory for
  65  * painters that handle some of the rendering tasks.  This allows
  66  * implementations to determine what they want to cache
  67  * and have the sharing potentially at the level that a
  68  * selector is common to multiple views.  Since the StyleSheet
  69  * may be used by views over multiple documents and typically


  84  * &nbsp;
  85  * &nbsp;     public static void main(String[] args) {
  86  * &nbsp;       HTMLEditorKit kit = new HTMLEditorKit();
  87  * &nbsp;       HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
  88  * &nbsp;       StyleSheet styles = doc.getStyleSheet();
  89  * &nbsp;
  90  * &nbsp;       Enumeration rules = styles.getStyleNames();
  91  * &nbsp;       while (rules.hasMoreElements()) {
  92  * &nbsp;           String name = (String) rules.nextElement();
  93  * &nbsp;           Style rule = styles.getStyle(name);
  94  * &nbsp;           System.out.println(rule.toString());
  95  * &nbsp;       }
  96  * &nbsp;       System.exit(0);
  97  * &nbsp;     }
  98  * &nbsp; }
  99  * &nbsp;
 100  * </code></pre>
 101  * <p>
 102  * The semantics for when a CSS style should overide visual attributes
 103  * defined by an element are not well defined. For example, the html
 104  * <code>&lt;body bgcolor=red&gt;</code> makes the body have a red
 105  * background. But if the html file also contains the CSS rule
 106  * <code>body { background: blue }</code> it becomes less clear as to
 107  * what color the background of the body should be. The current
 108  * implementation gives visual attributes defined in the element the
 109  * highest precedence, that is they are always checked before any styles.
 110  * Therefore, in the previous example the background would have a
 111  * red color as the body element defines the background color to be red.
 112  * <p>
 113  * As already mentioned this supports CSS. We don't support the full CSS
 114  * spec. Refer to the javadoc of the CSS class to see what properties
 115  * we support. The two major CSS parsing related
 116  * concepts we do not currently
 117  * support are pseudo selectors, such as <code>A:link { color: red }</code>,
 118  * and the <code>important</code> modifier.
 119  *
 120  * @implNote This implementation is currently
 121  * incomplete.  It can be replaced with alternative implementations
 122  * that are complete.  Future versions of this class will provide
 123  * better CSS support.
 124  *
 125  * @author  Timothy Prinzing
 126  * @author  Sunita Mani
 127  * @author  Sara Swanson
 128  * @author  Jill Nakata
 129  */
 130 @SuppressWarnings("serial") // Superclass is not serializable across versions
 131 public class StyleSheet extends StyleContext {
 132     // As the javadoc states, this class maintains a mapping between
 133     // a CSS selector (such as p.bar) and a Style.
 134     // This consists of a number of parts:
 135     // . Each selector is broken down into its constituent simple selectors,
 136     //   and stored in an inverted graph, for example:
 137     //     p { color: red } ol p { font-size: 10pt } ul p { font-size: 12pt }
 138     //   results in the graph:


 246                     cacheLookup.append(attr.getAttribute(HTML.Attribute.ID));
 247                 }
 248                 else if (attr.isDefined(HTML.Attribute.CLASS)) {
 249                     cacheLookup.append('.');
 250                     cacheLookup.append(attr.getAttribute
 251                                        (HTML.Attribute.CLASS));
 252                 }
 253             }
 254 
 255             Style style = getResolvedStyle(cacheLookup.toString(),
 256                                            searchContext, t);
 257             return style;
 258         }
 259         finally {
 260             SearchBuffer.releaseSearchBuffer(sb);
 261         }
 262     }
 263 
 264     /**
 265      * Fetches the rule that best matches the selector given
 266      * in string form. Where <code>selector</code> is a space separated
 267      * String of the element names. For example, <code>selector</code>
 268      * might be 'html body tr td''<p>
 269      * The attributes of the returned Style will change
 270      * as rules are added and removed. That is if you to ask for a rule
 271      * with a selector "table p" and a new rule was added with a selector
 272      * of "p" the returned Style would include the new attributes from
 273      * the rule "p".
 274      *
 275      * @param selector a space separated String of the element names.
 276      * @return the rule that best matches the selector.
 277      */
 278     public Style getRule(String selector) {
 279         selector = cleanSelectorString(selector);
 280         if (selector != null) {
 281             Style style = getResolvedStyle(selector);
 282             return style;
 283         }
 284         return null;
 285     }
 286 
 287     /**


 379                     mapping = mapping.getChildSelectorMapping(selectors[i],
 380                                                               true);
 381                 }
 382                 Style rule = mapping.getStyle();
 383                 if (rule != null) {
 384                     mapping.setStyle(null);
 385                     if (resolvedStyles.size() > 0) {
 386                         Enumeration<ResolvedStyle> values = resolvedStyles.elements();
 387                         while (values.hasMoreElements()) {
 388                             ResolvedStyle style = values.nextElement();
 389                             style.removeStyle(rule);
 390                         }
 391                     }
 392                 }
 393             }
 394         }
 395         super.removeStyle(nm);
 396     }
 397 
 398     /**
 399      * Adds the rules from the StyleSheet <code>ss</code> to those of
 400      * the receiver. <code>ss's</code> rules will override the rules of
 401      * any previously added style sheets. An added StyleSheet will never
 402      * override the rules of the receiving style sheet.
 403      *
 404      * @param ss a StyleSheet
 405      * @since 1.3
 406      */
 407     public void addStyleSheet(StyleSheet ss) {
 408         synchronized(this) {
 409             if (linkedStyleSheets == null) {
 410                 linkedStyleSheets = new Vector<StyleSheet>();
 411             }
 412             if (!linkedStyleSheets.contains(ss)) {
 413                 int index = 0;
 414                 if (ss instanceof javax.swing.plaf.UIResource
 415                     && linkedStyleSheets.size() > 1) {
 416                     index = linkedStyleSheets.size() - 1;
 417                 }
 418                 linkedStyleSheets.insertElementAt(ss, index);
 419                 linkStyleSheetAt(ss, index);
 420             }
 421         }
 422     }
 423 
 424     /**
 425      * Removes the StyleSheet <code>ss</code> from those of the receiver.
 426      *
 427      * @param ss a StyleSheet
 428      * @since 1.3
 429      */
 430     public void removeStyleSheet(StyleSheet ss) {
 431         synchronized(this) {
 432             if (linkedStyleSheets != null) {
 433                 int index = linkedStyleSheets.indexOf(ss);
 434                 if (index != -1) {
 435                     linkedStyleSheets.removeElementAt(index);
 436                     unlinkStyleSheet(ss, index);
 437                     if (index == 0 && linkedStyleSheets.size() == 0) {
 438                         linkedStyleSheets = null;
 439                     }
 440                 }
 441             }
 442         }
 443     }
 444 
 445     //


 452      *
 453      * @return an array of StyleSheets.
 454      * @since 1.3
 455      */
 456     public StyleSheet[] getStyleSheets() {
 457         StyleSheet[] retValue;
 458 
 459         synchronized(this) {
 460             if (linkedStyleSheets != null) {
 461                 retValue = new StyleSheet[linkedStyleSheets.size()];
 462                 linkedStyleSheets.copyInto(retValue);
 463             }
 464             else {
 465                 retValue = null;
 466             }
 467         }
 468         return retValue;
 469     }
 470 
 471     /**
 472      * Imports a style sheet from <code>url</code>. The resulting rules
 473      * are directly added to the receiver. If you do not want the rules
 474      * to become part of the receiver, create a new StyleSheet and use
 475      * addStyleSheet to link it in.
 476      *
 477      * @param url an url
 478      * @since 1.3
 479      */
 480     public void importStyleSheet(URL url) {
 481         try {
 482             InputStream is;
 483 
 484             is = url.openStream();
 485             Reader r = new BufferedReader(new InputStreamReader(is));
 486             CssParser parser = new CssParser();
 487             parser.parse(url, r, false, true);
 488             r.close();
 489             is.close();
 490         } catch (Throwable e) {
 491             // on error we simply have no styles... the html
 492             // will look mighty wrong but still function.
 493         }
 494     }
 495 
 496     /**
 497      * Sets the base. All import statements that are relative, will be
 498      * relative to <code>base</code>.
 499      *
 500      * @param base a base.
 501      * @since 1.3
 502      */
 503     public void setBase(URL base) {
 504         this.base = base;
 505     }
 506 
 507     /**
 508      * Returns the base.
 509      *
 510      * @return the base.
 511      * @since 1.3
 512      */
 513     public URL getBase() {
 514         return base;
 515     }
 516 
 517     /**
 518      * Adds a CSS attribute to the given set.


1014      */
1015     public float getPointSize(String size) {
1016         return css.getPointSize(size, this);
1017     }
1018 
1019     /**
1020      * Converts a color string such as "RED" or "#NNNNNN" to a Color.
1021      * Note: This will only convert the HTML3.2 color strings
1022      *       or a string of length 7;
1023      *       otherwise, it will return null.
1024      *
1025      * @param string color string such as "RED" or "#NNNNNN"
1026      * @return the color
1027      */
1028     public Color stringToColor(String string) {
1029         return CSS.stringToColor(string);
1030     }
1031 
1032     /**
1033      * Returns the ImageIcon to draw in the background for
1034      * <code>attr</code>.
1035      */
1036     ImageIcon getBackgroundImage(AttributeSet attr) {
1037         Object value = attr.getAttribute(CSS.Attribute.BACKGROUND_IMAGE);
1038 
1039         if (value != null) {
1040             return ((CSS.BackgroundImage)value).getImage(getBase());
1041         }
1042         return null;
1043     }
1044 
1045     /**
1046      * Adds a rule into the StyleSheet.
1047      *
1048      * @param selector the selector to use for the rule.
1049      *  This will be a set of simple selectors, and must
1050      *  be a length of 1 or greater.
1051      * @param declaration the set of CSS attributes that
1052      *  make up the rule.
1053      */
1054     void addRule(String[] selector, AttributeSet declaration,


1080                     rule = altRule;
1081                     mapping.setStyle(rule);
1082                     refreshResolvedRules(selectorName, selector, rule,
1083                                          mapping.getSpecificity());
1084                 }
1085             }
1086         }
1087         if (isLinked) {
1088             rule = getLinkedStyle(rule);
1089         }
1090         rule.addAttributes(declaration);
1091     }
1092 
1093     //
1094     // The following gaggle of methods is used in maintaining the rules from
1095     // the sheet.
1096     //
1097 
1098     /**
1099      * Updates the attributes of the rules to reference any related
1100      * rules in <code>ss</code>.
1101      */
1102     private synchronized void linkStyleSheetAt(StyleSheet ss, int index) {
1103         if (resolvedStyles.size() > 0) {
1104             Enumeration<ResolvedStyle> values = resolvedStyles.elements();
1105             while (values.hasMoreElements()) {
1106                 ResolvedStyle rule = values.nextElement();
1107                 rule.insertExtendedStyleAt(ss.getRule(rule.getName()),
1108                                            index);
1109             }
1110         }
1111     }
1112 
1113     /**
1114      * Removes references to the rules in <code>ss</code>.
1115      * <code>index</code> gives the index the StyleSheet was at, that is
1116      * how many StyleSheets had been added before it.
1117      */
1118     private synchronized void unlinkStyleSheet(StyleSheet ss, int index) {
1119         if (resolvedStyles.size() > 0) {
1120             Enumeration<ResolvedStyle> values = resolvedStyles.elements();
1121             while (values.hasMoreElements()) {
1122                 ResolvedStyle rule = values.nextElement();
1123                 rule.removeExtendedStyleAt(index);
1124             }
1125         }
1126     }
1127 
1128     /**
1129      * Returns the simple selectors that comprise selector.
1130      */
1131     /* protected */
1132     String[] getSimpleSelectors(String selector) {
1133         selector = cleanSelectorString(selector);
1134         SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
1135         @SuppressWarnings("unchecked")


1290     /**
1291      * Returns the style that linked attributes should be added to. This
1292      * will create the style if necessary.
1293      */
1294     private Style getLinkedStyle(Style localStyle) {
1295         // NOTE: This is not synchronized, and the caller of this does
1296         // not synchronize. There is the chance for one of the callers to
1297         // overwrite the existing resolved parent, but it is quite rare.
1298         // The reason this is left like this is because setResolveParent
1299         // will fire a ChangeEvent. It is really, REALLY bad for us to
1300         // hold a lock when calling outside of us, it may cause a deadlock.
1301         Style retStyle = (Style)localStyle.getResolveParent();
1302         if (retStyle == null) {
1303             retStyle = addStyle(null, null);
1304             localStyle.setResolveParent(retStyle);
1305         }
1306         return retStyle;
1307     }
1308 
1309     /**
1310      * Returns the resolved style for <code>selector</code>. This will
1311      * create the resolved style, if necessary.
1312      */
1313     private synchronized Style getResolvedStyle(String selector,
1314                                                 Vector<Element> elements,
1315                                                 HTML.Tag t) {
1316         Style retStyle = resolvedStyles.get(selector);
1317         if (retStyle == null) {
1318             retStyle = createResolvedStyle(selector, elements, t);
1319         }
1320         return retStyle;
1321     }
1322 
1323     /**
1324      * Returns the resolved style for <code>selector</code>. This will
1325      * create the resolved style, if necessary.
1326      */
1327     private synchronized Style getResolvedStyle(String selector) {
1328         Style retStyle = resolvedStyles.get(selector);
1329         if (retStyle == null) {
1330             retStyle = createResolvedStyle(selector);
1331         }
1332         return retStyle;
1333     }
1334 
1335     /**
1336      * Adds <code>mapping</code> to <code>elements</code>. It is added
1337      * such that <code>elements</code> will remain ordered by
1338      * specificity.
1339      */
1340     private void addSortedStyle(SelectorMapping mapping, Vector<SelectorMapping> elements) {
1341         int       size = elements.size();
1342 
1343         if (size > 0) {
1344             int     specificity = mapping.getSpecificity();
1345 
1346             for (int counter = 0; counter < size; counter++) {
1347                 if (specificity >= elements.elementAt(counter).getSpecificity()) {
1348                     elements.insertElementAt(mapping, counter);
1349                     return;
1350                 }
1351             }
1352         }
1353         elements.addElement(mapping);
1354     }
1355 
1356     /**
1357      * Adds <code>parentMapping</code> to <code>styles</code>, and
1358      * recursively calls this method if <code>parentMapping</code> has
1359      * any child mappings for any of the Elements in <code>elements</code>.
1360      */
1361     private synchronized void getStyles(SelectorMapping parentMapping,
1362                            Vector<SelectorMapping> styles,
1363                            String[] tags, String[] ids, String[] classes,
1364                            int index, int numElements,
1365                            Hashtable<SelectorMapping, SelectorMapping> alreadyChecked) {
1366         // Avoid desending the same mapping twice.
1367         if (alreadyChecked.contains(parentMapping)) {
1368             return;
1369         }
1370         alreadyChecked.put(parentMapping, parentMapping);
1371         Style style = parentMapping.getStyle();
1372         if (style != null) {
1373             addSortedStyle(parentMapping, styles);
1374         }
1375         for (int counter = index; counter < numElements; counter++) {
1376             String tagString = tags[counter];
1377             if (tagString != null) {
1378                 SelectorMapping childMapping = parentMapping.
1379                                 getChildSelectorMapping(tagString, false);


1400                     String idName = ids[counter];
1401                     childMapping = parentMapping.getChildSelectorMapping(
1402                                          tagString + "#" + idName, false);
1403                     if (childMapping != null) {
1404                         getStyles(childMapping, styles, tags, ids, classes,
1405                                   counter + 1, numElements, alreadyChecked);
1406                     }
1407                     childMapping = parentMapping.getChildSelectorMapping(
1408                                    "#" + idName, false);
1409                     if (childMapping != null) {
1410                         getStyles(childMapping, styles, tags, ids, classes,
1411                                   counter + 1, numElements, alreadyChecked);
1412                     }
1413                 }
1414             }
1415         }
1416     }
1417 
1418     /**
1419      * Creates and returns a Style containing all the rules that match
1420      *  <code>selector</code>.
1421      */
1422     private synchronized Style createResolvedStyle(String selector,
1423                                       String[] tags,
1424                                       String[] ids, String[] classes) {
1425         SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
1426         @SuppressWarnings("unchecked")
1427         Vector<SelectorMapping> tempVector = sb.getVector();
1428         @SuppressWarnings("unchecked")
1429         Hashtable<SelectorMapping, SelectorMapping> tempHashtable = sb.getHashtable();
1430         // Determine all the Styles that are appropriate, placing them
1431         // in tempVector
1432         try {
1433             SelectorMapping mapping = getRootSelectorMapping();
1434             int numElements = tags.length;
1435             String tagString = tags[0];
1436             SelectorMapping childMapping = mapping.getChildSelectorMapping(
1437                                                    tagString, false);
1438             if (childMapping != null) {
1439                 getStyles(childMapping, tempVector, tags, ids, classes, 1,
1440                           numElements, tempHashtable);


1483                 AttributeSet attr = linkedStyleSheets.elementAt(counter).getRule(selector);
1484                 if (attr == null) {
1485                     attrs[counter + numStyles] = SimpleAttributeSet.EMPTY;
1486                 }
1487                 else {
1488                     attrs[counter + numStyles] = attr;
1489                 }
1490             }
1491             ResolvedStyle retStyle = new ResolvedStyle(selector, attrs,
1492                                                        numStyles);
1493             resolvedStyles.put(selector, retStyle);
1494             return retStyle;
1495         }
1496         finally {
1497             SearchBuffer.releaseSearchBuffer(sb);
1498         }
1499     }
1500 
1501     /**
1502      * Creates and returns a Style containing all the rules that
1503      * matches <code>selector</code>.
1504      *
1505      * @param elements  a Vector of all the Elements
1506      *                  the style is being asked for. The
1507      *                  first Element is the deepest Element, with the last Element
1508      *                  representing the root.
1509      * @param t         the Tag to use for
1510      *                  the first Element in <code>elements</code>
1511      */
1512     private Style createResolvedStyle(String selector, Vector<Element> elements,
1513                                       HTML.Tag t) {
1514         int numElements = elements.size();
1515         // Build three arrays, one for tags, one for class's, and one for
1516         // id's
1517         String tags[] = new String[numElements];
1518         String ids[] = new String[numElements];
1519         String classes[] = new String[numElements];
1520         for (int counter = 0; counter < numElements; counter++) {
1521             Element e = elements.elementAt(counter);
1522             AttributeSet attr = e.getAttributes();
1523             if (counter == 0 && e.isLeaf()) {
1524                 // For leafs, we use the second tier attributes.
1525                 Object testAttr = attr.getAttribute(t);
1526                 if (testAttr instanceof AttributeSet) {
1527                     attr = (AttributeSet)testAttr;
1528                 }
1529                 else {
1530                     attr = null;


1547                     classes[counter] = null;
1548                 }
1549                 if (attr.isDefined(HTML.Attribute.ID)) {
1550                     ids[counter] = attr.getAttribute(HTML.Attribute.ID).
1551                                         toString();
1552                 }
1553                 else {
1554                     ids[counter] = null;
1555                 }
1556             }
1557             else {
1558                 tags[counter] = ids[counter] = classes[counter] = null;
1559             }
1560         }
1561         tags[0] = t.toString();
1562         return createResolvedStyle(selector, tags, ids, classes);
1563     }
1564 
1565     /**
1566      * Creates and returns a Style containing all the rules that match
1567      *  <code>selector</code>. It is assumed that each simple selector
1568      * in <code>selector</code> is separated by a space.
1569      */
1570     private Style createResolvedStyle(String selector) {
1571         SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
1572         // Will contain the tags, ids, and classes, in that order.
1573         @SuppressWarnings("unchecked")
1574         Vector<String> elements = sb.getVector();
1575         try {
1576             boolean done;
1577             int dotIndex = 0;
1578             int spaceIndex;
1579             int poundIndex = 0;
1580             int lastIndex = 0;
1581             int length = selector.length();
1582             while (lastIndex < length) {
1583                 if (dotIndex == lastIndex) {
1584                     dotIndex = selector.indexOf('.', lastIndex);
1585                 }
1586                 if (poundIndex == lastIndex) {
1587                     poundIndex = selector.indexOf('#', lastIndex);
1588                 }


1748 
1749         /**
1750          * Returns an instance of SearchBuffer. Be sure and issue
1751          * a releaseSearchBuffer when done with it.
1752          */
1753         static SearchBuffer obtainSearchBuffer() {
1754             SearchBuffer sb;
1755             try {
1756                 if(!searchBuffers.empty()) {
1757                    sb = searchBuffers.pop();
1758                 } else {
1759                    sb = new SearchBuffer();
1760                 }
1761             } catch (EmptyStackException ese) {
1762                 sb = new SearchBuffer();
1763             }
1764             return sb;
1765         }
1766 
1767         /**
1768          * Adds <code>sb</code> to the stack of SearchBuffers that can
1769          * be used.
1770          */
1771         static void releaseSearchBuffer(SearchBuffer sb) {
1772             sb.empty();
1773             searchBuffers.push(sb);
1774         }
1775 
1776         StringBuffer getStringBuffer() {
1777             if (stringBuffer == null) {
1778                 stringBuffer = new StringBuffer();
1779             }
1780             return stringBuffer;
1781         }
1782 
1783         Vector getVector() {
1784             if (vector == null) {
1785                 vector = new Vector();
1786             }
1787             return vector;
1788         }


2140 
2141             if (childtype == null) {
2142                 if (type == null) {
2143                     // Parent view.
2144                     View v = childView.getParent();
2145                     HTMLDocument doc = (HTMLDocument)v.getDocument();
2146                     if (HTMLDocument.matchNameAttribute(v.getElement().getAttributes(),
2147                                                         HTML.Tag.OL)) {
2148                         childtype = CSS.Value.DECIMAL;
2149                     } else {
2150                         childtype = CSS.Value.DISC;
2151                     }
2152                 } else {
2153                     childtype = type;
2154                 }
2155             }
2156             return childtype;
2157         }
2158 
2159         /**
2160          * Obtains the starting index from <code>parent</code>.
2161          */
2162         private void getStart(View parent) {
2163             checkedForStart = true;
2164             Element element = parent.getElement();
2165             if (element != null) {
2166                 AttributeSet attr = element.getAttributes();
2167                 Object startValue;
2168                 if (attr != null && attr.isDefined(HTML.Attribute.START) &&
2169                     (startValue = attr.getAttribute
2170                      (HTML.Attribute.START)) != null &&
2171                     (startValue instanceof String)) {
2172 
2173                     try {
2174                         start = Integer.parseInt((String)startValue);
2175                     }
2176                     catch (NumberFormatException nfe) {}
2177                 }
2178             }
2179         }
2180 
2181         /**
2182          * Returns an integer that should be used to render the child at
2183          * <code>childIndex</code> with. The retValue will usually be
2184          * <code>childIndex</code> + 1, unless <code>parentView</code>
2185          * has some Views that do not represent LI's, or one of the views
2186          * has a HTML.Attribute.START specified.
2187          */
2188         private int getRenderIndex(View parentView, int childIndex) {
2189             if (!checkedForStart) {
2190                 getStart(parentView);
2191             }
2192             int retIndex = childIndex;
2193             for (int counter = childIndex; counter >= 0; counter--) {
2194                 AttributeSet as = parentView.getElement().getElement(counter).
2195                                   getAttributes();
2196                 if (as.getAttribute(StyleConstants.NameAttribute) !=
2197                     HTML.Tag.LI) {
2198                     retIndex--;
2199                 } else if (as.isDefined(HTML.Attribute.VALUE)) {
2200                     Object value = as.getAttribute(HTML.Attribute.VALUE);
2201                     if (value != null &&
2202                         (value instanceof String)) {
2203                         try {
2204                             int iValue = Integer.parseInt((String)value);


2843      * A subclass of MuxingAttributeSet that implements Style. Currently
2844      * the MutableAttributeSet methods are unimplemented, that is they
2845      * do nothing.
2846      */
2847     // PENDING(sky): Decide what to do with this. Either make it
2848     // contain a SimpleAttributeSet that modify methods are delegated to,
2849     // or change getRule to return an AttributeSet and then don't make this
2850     // implement Style.
2851     @SuppressWarnings("serial") // Same-version serialization only
2852     static class ResolvedStyle extends MuxingAttributeSet implements
2853                   Serializable, Style {
2854         ResolvedStyle(String name, AttributeSet[] attrs, int extendedIndex) {
2855             super(attrs);
2856             this.name = name;
2857             this.extendedIndex = extendedIndex;
2858         }
2859 
2860         /**
2861          * Inserts a Style into the receiver so that the styles the
2862          * receiver represents are still ordered by specificity.
2863          * <code>style</code> will be added before any extended styles, that
2864          * is before extendedIndex.
2865          */
2866         synchronized void insertStyle(Style style, int specificity) {
2867             AttributeSet[] attrs = getAttributes();
2868             int maxCounter = attrs.length;
2869             int counter = 0;
2870             for (;counter < extendedIndex; counter++) {
2871                 if (specificity > getSpecificity(((Style)attrs[counter]).
2872                                                  getName())) {
2873                     break;
2874                 }
2875             }
2876             insertAttributeSetAt(style, counter);
2877             extendedIndex++;
2878         }
2879 
2880         /**
2881          * Removes a previously added style. This will do nothing if
2882          * <code>style</code> is not referenced by the receiver.
2883          */
2884         synchronized void removeStyle(Style style) {
2885             AttributeSet[] attrs = getAttributes();
2886 
2887             for (int counter = attrs.length - 1; counter >= 0; counter--) {
2888                 if (attrs[counter] == style) {
2889                     removeAttributeSetAt(counter);
2890                     if (counter < extendedIndex) {
2891                         extendedIndex--;
2892                     }
2893                     break;
2894                 }
2895             }
2896         }
2897 
2898         /**
2899          * Adds <code>s</code> as one of the Attributesets to look up
2900          * attributes in.
2901          */
2902         synchronized void insertExtendedStyleAt(Style attr, int index) {
2903             insertAttributeSetAt(attr, extendedIndex + index);
2904         }
2905 
2906         /**
2907          * Adds <code>s</code> as one of the AttributeSets to look up
2908          * attributes in. It will be the AttributeSet last checked.
2909          */
2910         synchronized void addExtendedStyle(Style attr) {
2911             insertAttributeSetAt(attr, getAttributes().length);
2912         }
2913 
2914         /**
2915          * Removes the style at <code>index</code> +
2916          * <code>extendedIndex</code>.
2917          */
2918         synchronized void removeExtendedStyleAt(int index) {
2919             removeAttributeSetAt(extendedIndex + index);
2920         }
2921 
2922         /**
2923          * Returns true if the receiver matches <code>selector</code>, where
2924          * a match is defined by the CSS rule matching.
2925          * Each simple selector must be separated by a single space.
2926          */
2927         protected boolean matches(String selector) {
2928             int sLast = selector.length();
2929 
2930             if (sLast == 0) {
2931                 return false;
2932             }
2933             int thisLast = name.length();
2934             int sCurrent = selector.lastIndexOf(' ');
2935             int thisCurrent = name.lastIndexOf(' ');
2936             if (sCurrent >= 0) {
2937                 sCurrent++;
2938             }
2939             if (thisCurrent >= 0) {
2940                 thisCurrent++;
2941             }
2942             if (!matches(selector, sCurrent, sLast, thisCurrent, thisLast)) {
2943                 return false;


3067         public void removeAttributes(AttributeSet attributes) {}
3068         public void setResolveParent(AttributeSet parent) {}
3069         public String getName() {return name;}
3070         public void addChangeListener(ChangeListener l) {}
3071         public void removeChangeListener(ChangeListener l) {}
3072         public ChangeListener[] getChangeListeners() {
3073             return new ChangeListener[0];
3074         }
3075 
3076         /** The name of the Style, which is the selector.
3077          * This will NEVER change!
3078          */
3079         String name;
3080         /** Start index of styles coming from other StyleSheets. */
3081         private int extendedIndex;
3082     }
3083 
3084 
3085     /**
3086      * SelectorMapping contains a specifitiy, as an integer, and an associated
3087      * Style. It can also reference children <code>SelectorMapping</code>s,
3088      * so that it behaves like a tree.
3089      * <p>
3090      * This is not thread safe, it is assumed the caller will take the
3091      * necessary precations if this is to be used in a threaded environment.
3092      */
3093     @SuppressWarnings("serial") // Same-version serialization only
3094     static class SelectorMapping implements Serializable {
3095         public SelectorMapping(int specificity) {
3096             this.specificity = specificity;
3097         }
3098 
3099         /**
3100          * Returns the specificity this mapping represents.
3101          */
3102         public int getSpecificity() {
3103             return specificity;
3104         }
3105 
3106         /**
3107          * Sets the Style associated with this mapping.
3108          */
3109         public void setStyle(Style style) {
3110             this.style = style;
3111         }
3112 
3113         /**
3114          * Returns the Style associated with this mapping.
3115          */
3116         public Style getStyle() {
3117             return style;
3118         }
3119 
3120         /**
3121          * Returns the child mapping identified by the simple selector
3122          * <code>selector</code>. If a child mapping does not exist for
3123          *<code>selector</code>, and <code>create</code> is true, a new
3124          * one will be created.
3125          */
3126         public SelectorMapping getChildSelectorMapping(String selector,
3127                                                        boolean create) {
3128             SelectorMapping retValue = null;
3129 
3130             if (children != null) {
3131                 retValue = children.get(selector);
3132             }
3133             else if (create) {
3134                 children = new HashMap<String, SelectorMapping>(7);
3135             }
3136             if (retValue == null && create) {
3137                 int specificity = getChildSpecificity(selector);
3138 
3139                 retValue = createChildSelectorMapping(specificity);
3140                 children.put(selector, retValue);
3141             }
3142             return retValue;
3143         }
3144 
3145         /**
3146          * Creates a child <code>SelectorMapping</code> with the specified
3147          * <code>specificity</code>.
3148          */
3149         protected SelectorMapping createChildSelectorMapping(int specificity) {
3150             return new SelectorMapping(specificity);
3151         }
3152 
3153         /**
3154          * Returns the specificity for the child selector
3155          * <code>selector</code>.
3156          */
3157         protected int getChildSpecificity(String selector) {
3158             // class (.) 100
3159             // id (#)    10000
3160             char    firstChar = selector.charAt(0);
3161             int     specificity = getSpecificity();
3162 
3163             if (firstChar == '.') {
3164                 specificity += 100;
3165             }
3166             else if (firstChar == '#') {
3167                 specificity += 10000;
3168             }
3169             else {
3170                 specificity += 1;
3171                 if (selector.indexOf('.') != -1) {
3172                     specificity += 100;
3173                 }
3174                 if (selector.indexOf('#') != -1) {
3175                     specificity += 10000;


3247          * Parse the given CSS stream
3248          */
3249         public void parse(URL base, Reader r, boolean parseDeclaration,
3250                           boolean isLink) throws IOException {
3251             this.base = base;
3252             this.isLink = isLink;
3253             this.parsingDeclaration = parseDeclaration;
3254             declaration.removeAttributes(declaration);
3255             selectorTokens.removeAllElements();
3256             selectors.removeAllElements();
3257             propertyName = null;
3258             parser.parse(r, this, parseDeclaration);
3259         }
3260 
3261         //
3262         // CSSParserCallback methods, public to implement the interface.
3263         //
3264 
3265         /**
3266          * Invoked when a valid @import is encountered, will call
3267          * <code>importStyleSheet</code> if a
3268          * <code>MalformedURLException</code> is not thrown in creating
3269          * the URL.
3270          */
3271         public void handleImport(String importString) {
3272             URL url = CSS.getURL(base, importString);
3273             if (url != null) {
3274                 importStyleSheet(url);
3275             }
3276         }
3277 
3278         /**
3279          * A selector has been encountered.
3280          */
3281         public void handleSelector(String selector) {
3282             //class and index selectors are case sensitive
3283             if (!(selector.startsWith(".")
3284                   || selector.startsWith("#"))) {
3285                 selector = selector.toLowerCase();
3286             }
3287             int length = selector.length();
3288 




  29 import java.awt.*;
  30 import java.io.*;
  31 import java.net.*;
  32 import javax.swing.Icon;
  33 import javax.swing.ImageIcon;
  34 import javax.swing.UIManager;
  35 import javax.swing.border.*;
  36 import javax.swing.event.ChangeListener;
  37 import javax.swing.text.*;
  38 
  39 /**
  40  * Support for defining the visual characteristics of
  41  * HTML views being rendered.  The StyleSheet is used to
  42  * translate the HTML model into visual characteristics.
  43  * This enables views to be customized by a look-and-feel,
  44  * multiple views over the same model can be rendered
  45  * differently, etc.  This can be thought of as a CSS
  46  * rule repository.  The key for CSS attributes is an
  47  * object of type CSS.Attribute.  The type of the value
  48  * is up to the StyleSheet implementation, but the
  49  * {@code toString} method is required
  50  * to return a string representation of CSS value.
  51  * <p>
  52  * The primary entry point for HTML View implementations
  53  * to get their attributes is the
  54  * {@link #getViewAttributes getViewAttributes}
  55  * method.  This should be implemented to establish the
  56  * desired policy used to associate attributes with the view.
  57  * Each HTMLEditorKit (i.e. and therefore each associated
  58  * JEditorPane) can have its own StyleSheet, but by default one
  59  * sheet will be shared by all of the HTMLEditorKit instances.
  60  * HTMLDocument instance can also have a StyleSheet, which
  61  * holds the document-specific CSS specifications.
  62  * <p>
  63  * In order for Views to store less state and therefore be
  64  * more lightweight, the StyleSheet can act as a factory for
  65  * painters that handle some of the rendering tasks.  This allows
  66  * implementations to determine what they want to cache
  67  * and have the sharing potentially at the level that a
  68  * selector is common to multiple views.  Since the StyleSheet
  69  * may be used by views over multiple documents and typically


  84  * &nbsp;
  85  * &nbsp;     public static void main(String[] args) {
  86  * &nbsp;       HTMLEditorKit kit = new HTMLEditorKit();
  87  * &nbsp;       HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
  88  * &nbsp;       StyleSheet styles = doc.getStyleSheet();
  89  * &nbsp;
  90  * &nbsp;       Enumeration rules = styles.getStyleNames();
  91  * &nbsp;       while (rules.hasMoreElements()) {
  92  * &nbsp;           String name = (String) rules.nextElement();
  93  * &nbsp;           Style rule = styles.getStyle(name);
  94  * &nbsp;           System.out.println(rule.toString());
  95  * &nbsp;       }
  96  * &nbsp;       System.exit(0);
  97  * &nbsp;     }
  98  * &nbsp; }
  99  * &nbsp;
 100  * </code></pre>
 101  * <p>
 102  * The semantics for when a CSS style should overide visual attributes
 103  * defined by an element are not well defined. For example, the html
 104  * {@code <body bgcolor=red>} makes the body have a red
 105  * background. But if the html file also contains the CSS rule
 106  * {@code body { background: blue }} it becomes less clear as to
 107  * what color the background of the body should be. The current
 108  * implementation gives visual attributes defined in the element the
 109  * highest precedence, that is they are always checked before any styles.
 110  * Therefore, in the previous example the background would have a
 111  * red color as the body element defines the background color to be red.
 112  * <p>
 113  * As already mentioned this supports CSS. We don't support the full CSS
 114  * spec. Refer to the javadoc of the CSS class to see what properties
 115  * we support. The two major CSS parsing related
 116  * concepts we do not currently
 117  * support are pseudo selectors, such as {@code A:link { color: red }},
 118  * and the {@code important} modifier.
 119  *
 120  * @implNote This implementation is currently
 121  * incomplete.  It can be replaced with alternative implementations
 122  * that are complete.  Future versions of this class will provide
 123  * better CSS support.
 124  *
 125  * @author  Timothy Prinzing
 126  * @author  Sunita Mani
 127  * @author  Sara Swanson
 128  * @author  Jill Nakata
 129  */
 130 @SuppressWarnings("serial") // Superclass is not serializable across versions
 131 public class StyleSheet extends StyleContext {
 132     // As the javadoc states, this class maintains a mapping between
 133     // a CSS selector (such as p.bar) and a Style.
 134     // This consists of a number of parts:
 135     // . Each selector is broken down into its constituent simple selectors,
 136     //   and stored in an inverted graph, for example:
 137     //     p { color: red } ol p { font-size: 10pt } ul p { font-size: 12pt }
 138     //   results in the graph:


 246                     cacheLookup.append(attr.getAttribute(HTML.Attribute.ID));
 247                 }
 248                 else if (attr.isDefined(HTML.Attribute.CLASS)) {
 249                     cacheLookup.append('.');
 250                     cacheLookup.append(attr.getAttribute
 251                                        (HTML.Attribute.CLASS));
 252                 }
 253             }
 254 
 255             Style style = getResolvedStyle(cacheLookup.toString(),
 256                                            searchContext, t);
 257             return style;
 258         }
 259         finally {
 260             SearchBuffer.releaseSearchBuffer(sb);
 261         }
 262     }
 263 
 264     /**
 265      * Fetches the rule that best matches the selector given
 266      * in string form. Where {@code selector} is a space separated
 267      * String of the element names. For example, {@code selector}
 268      * might be 'html body tr td''<p>
 269      * The attributes of the returned Style will change
 270      * as rules are added and removed. That is if you to ask for a rule
 271      * with a selector "table p" and a new rule was added with a selector
 272      * of "p" the returned Style would include the new attributes from
 273      * the rule "p".
 274      *
 275      * @param selector a space separated String of the element names.
 276      * @return the rule that best matches the selector.
 277      */
 278     public Style getRule(String selector) {
 279         selector = cleanSelectorString(selector);
 280         if (selector != null) {
 281             Style style = getResolvedStyle(selector);
 282             return style;
 283         }
 284         return null;
 285     }
 286 
 287     /**


 379                     mapping = mapping.getChildSelectorMapping(selectors[i],
 380                                                               true);
 381                 }
 382                 Style rule = mapping.getStyle();
 383                 if (rule != null) {
 384                     mapping.setStyle(null);
 385                     if (resolvedStyles.size() > 0) {
 386                         Enumeration<ResolvedStyle> values = resolvedStyles.elements();
 387                         while (values.hasMoreElements()) {
 388                             ResolvedStyle style = values.nextElement();
 389                             style.removeStyle(rule);
 390                         }
 391                     }
 392                 }
 393             }
 394         }
 395         super.removeStyle(nm);
 396     }
 397 
 398     /**
 399      * Adds the rules from the StyleSheet {@code ss} to those of
 400      * the receiver. {@code ss's} rules will override the rules of
 401      * any previously added style sheets. An added StyleSheet will never
 402      * override the rules of the receiving style sheet.
 403      *
 404      * @param ss a StyleSheet
 405      * @since 1.3
 406      */
 407     public void addStyleSheet(StyleSheet ss) {
 408         synchronized(this) {
 409             if (linkedStyleSheets == null) {
 410                 linkedStyleSheets = new Vector<StyleSheet>();
 411             }
 412             if (!linkedStyleSheets.contains(ss)) {
 413                 int index = 0;
 414                 if (ss instanceof javax.swing.plaf.UIResource
 415                     && linkedStyleSheets.size() > 1) {
 416                     index = linkedStyleSheets.size() - 1;
 417                 }
 418                 linkedStyleSheets.insertElementAt(ss, index);
 419                 linkStyleSheetAt(ss, index);
 420             }
 421         }
 422     }
 423 
 424     /**
 425      * Removes the StyleSheet {@code ss} from those of the receiver.
 426      *
 427      * @param ss a StyleSheet
 428      * @since 1.3
 429      */
 430     public void removeStyleSheet(StyleSheet ss) {
 431         synchronized(this) {
 432             if (linkedStyleSheets != null) {
 433                 int index = linkedStyleSheets.indexOf(ss);
 434                 if (index != -1) {
 435                     linkedStyleSheets.removeElementAt(index);
 436                     unlinkStyleSheet(ss, index);
 437                     if (index == 0 && linkedStyleSheets.size() == 0) {
 438                         linkedStyleSheets = null;
 439                     }
 440                 }
 441             }
 442         }
 443     }
 444 
 445     //


 452      *
 453      * @return an array of StyleSheets.
 454      * @since 1.3
 455      */
 456     public StyleSheet[] getStyleSheets() {
 457         StyleSheet[] retValue;
 458 
 459         synchronized(this) {
 460             if (linkedStyleSheets != null) {
 461                 retValue = new StyleSheet[linkedStyleSheets.size()];
 462                 linkedStyleSheets.copyInto(retValue);
 463             }
 464             else {
 465                 retValue = null;
 466             }
 467         }
 468         return retValue;
 469     }
 470 
 471     /**
 472      * Imports a style sheet from {@code url}. The resulting rules
 473      * are directly added to the receiver. If you do not want the rules
 474      * to become part of the receiver, create a new StyleSheet and use
 475      * addStyleSheet to link it in.
 476      *
 477      * @param url an url
 478      * @since 1.3
 479      */
 480     public void importStyleSheet(URL url) {
 481         try {
 482             InputStream is;
 483 
 484             is = url.openStream();
 485             Reader r = new BufferedReader(new InputStreamReader(is));
 486             CssParser parser = new CssParser();
 487             parser.parse(url, r, false, true);
 488             r.close();
 489             is.close();
 490         } catch (Throwable e) {
 491             // on error we simply have no styles... the html
 492             // will look mighty wrong but still function.
 493         }
 494     }
 495 
 496     /**
 497      * Sets the base. All import statements that are relative, will be
 498      * relative to {@code base}.
 499      *
 500      * @param base a base.
 501      * @since 1.3
 502      */
 503     public void setBase(URL base) {
 504         this.base = base;
 505     }
 506 
 507     /**
 508      * Returns the base.
 509      *
 510      * @return the base.
 511      * @since 1.3
 512      */
 513     public URL getBase() {
 514         return base;
 515     }
 516 
 517     /**
 518      * Adds a CSS attribute to the given set.


1014      */
1015     public float getPointSize(String size) {
1016         return css.getPointSize(size, this);
1017     }
1018 
1019     /**
1020      * Converts a color string such as "RED" or "#NNNNNN" to a Color.
1021      * Note: This will only convert the HTML3.2 color strings
1022      *       or a string of length 7;
1023      *       otherwise, it will return null.
1024      *
1025      * @param string color string such as "RED" or "#NNNNNN"
1026      * @return the color
1027      */
1028     public Color stringToColor(String string) {
1029         return CSS.stringToColor(string);
1030     }
1031 
1032     /**
1033      * Returns the ImageIcon to draw in the background for
1034      * {@code attr}.
1035      */
1036     ImageIcon getBackgroundImage(AttributeSet attr) {
1037         Object value = attr.getAttribute(CSS.Attribute.BACKGROUND_IMAGE);
1038 
1039         if (value != null) {
1040             return ((CSS.BackgroundImage)value).getImage(getBase());
1041         }
1042         return null;
1043     }
1044 
1045     /**
1046      * Adds a rule into the StyleSheet.
1047      *
1048      * @param selector the selector to use for the rule.
1049      *  This will be a set of simple selectors, and must
1050      *  be a length of 1 or greater.
1051      * @param declaration the set of CSS attributes that
1052      *  make up the rule.
1053      */
1054     void addRule(String[] selector, AttributeSet declaration,


1080                     rule = altRule;
1081                     mapping.setStyle(rule);
1082                     refreshResolvedRules(selectorName, selector, rule,
1083                                          mapping.getSpecificity());
1084                 }
1085             }
1086         }
1087         if (isLinked) {
1088             rule = getLinkedStyle(rule);
1089         }
1090         rule.addAttributes(declaration);
1091     }
1092 
1093     //
1094     // The following gaggle of methods is used in maintaining the rules from
1095     // the sheet.
1096     //
1097 
1098     /**
1099      * Updates the attributes of the rules to reference any related
1100      * rules in {@code ss}.
1101      */
1102     private synchronized void linkStyleSheetAt(StyleSheet ss, int index) {
1103         if (resolvedStyles.size() > 0) {
1104             Enumeration<ResolvedStyle> values = resolvedStyles.elements();
1105             while (values.hasMoreElements()) {
1106                 ResolvedStyle rule = values.nextElement();
1107                 rule.insertExtendedStyleAt(ss.getRule(rule.getName()),
1108                                            index);
1109             }
1110         }
1111     }
1112 
1113     /**
1114      * Removes references to the rules in {@code ss}.
1115      * {@code index} gives the index the StyleSheet was at, that is
1116      * how many StyleSheets had been added before it.
1117      */
1118     private synchronized void unlinkStyleSheet(StyleSheet ss, int index) {
1119         if (resolvedStyles.size() > 0) {
1120             Enumeration<ResolvedStyle> values = resolvedStyles.elements();
1121             while (values.hasMoreElements()) {
1122                 ResolvedStyle rule = values.nextElement();
1123                 rule.removeExtendedStyleAt(index);
1124             }
1125         }
1126     }
1127 
1128     /**
1129      * Returns the simple selectors that comprise selector.
1130      */
1131     /* protected */
1132     String[] getSimpleSelectors(String selector) {
1133         selector = cleanSelectorString(selector);
1134         SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
1135         @SuppressWarnings("unchecked")


1290     /**
1291      * Returns the style that linked attributes should be added to. This
1292      * will create the style if necessary.
1293      */
1294     private Style getLinkedStyle(Style localStyle) {
1295         // NOTE: This is not synchronized, and the caller of this does
1296         // not synchronize. There is the chance for one of the callers to
1297         // overwrite the existing resolved parent, but it is quite rare.
1298         // The reason this is left like this is because setResolveParent
1299         // will fire a ChangeEvent. It is really, REALLY bad for us to
1300         // hold a lock when calling outside of us, it may cause a deadlock.
1301         Style retStyle = (Style)localStyle.getResolveParent();
1302         if (retStyle == null) {
1303             retStyle = addStyle(null, null);
1304             localStyle.setResolveParent(retStyle);
1305         }
1306         return retStyle;
1307     }
1308 
1309     /**
1310      * Returns the resolved style for {@code selector}. This will
1311      * create the resolved style, if necessary.
1312      */
1313     private synchronized Style getResolvedStyle(String selector,
1314                                                 Vector<Element> elements,
1315                                                 HTML.Tag t) {
1316         Style retStyle = resolvedStyles.get(selector);
1317         if (retStyle == null) {
1318             retStyle = createResolvedStyle(selector, elements, t);
1319         }
1320         return retStyle;
1321     }
1322 
1323     /**
1324      * Returns the resolved style for {@code selector}. This will
1325      * create the resolved style, if necessary.
1326      */
1327     private synchronized Style getResolvedStyle(String selector) {
1328         Style retStyle = resolvedStyles.get(selector);
1329         if (retStyle == null) {
1330             retStyle = createResolvedStyle(selector);
1331         }
1332         return retStyle;
1333     }
1334 
1335     /**
1336      * Adds {@code mapping} to {@code elements}. It is added
1337      * such that {@code elements} will remain ordered by
1338      * specificity.
1339      */
1340     private void addSortedStyle(SelectorMapping mapping, Vector<SelectorMapping> elements) {
1341         int       size = elements.size();
1342 
1343         if (size > 0) {
1344             int     specificity = mapping.getSpecificity();
1345 
1346             for (int counter = 0; counter < size; counter++) {
1347                 if (specificity >= elements.elementAt(counter).getSpecificity()) {
1348                     elements.insertElementAt(mapping, counter);
1349                     return;
1350                 }
1351             }
1352         }
1353         elements.addElement(mapping);
1354     }
1355 
1356     /**
1357      * Adds {@code parentMapping} to {@code styles}, and
1358      * recursively calls this method if {@code parentMapping} has
1359      * any child mappings for any of the Elements in {@code elements}.
1360      */
1361     private synchronized void getStyles(SelectorMapping parentMapping,
1362                            Vector<SelectorMapping> styles,
1363                            String[] tags, String[] ids, String[] classes,
1364                            int index, int numElements,
1365                            Hashtable<SelectorMapping, SelectorMapping> alreadyChecked) {
1366         // Avoid desending the same mapping twice.
1367         if (alreadyChecked.contains(parentMapping)) {
1368             return;
1369         }
1370         alreadyChecked.put(parentMapping, parentMapping);
1371         Style style = parentMapping.getStyle();
1372         if (style != null) {
1373             addSortedStyle(parentMapping, styles);
1374         }
1375         for (int counter = index; counter < numElements; counter++) {
1376             String tagString = tags[counter];
1377             if (tagString != null) {
1378                 SelectorMapping childMapping = parentMapping.
1379                                 getChildSelectorMapping(tagString, false);


1400                     String idName = ids[counter];
1401                     childMapping = parentMapping.getChildSelectorMapping(
1402                                          tagString + "#" + idName, false);
1403                     if (childMapping != null) {
1404                         getStyles(childMapping, styles, tags, ids, classes,
1405                                   counter + 1, numElements, alreadyChecked);
1406                     }
1407                     childMapping = parentMapping.getChildSelectorMapping(
1408                                    "#" + idName, false);
1409                     if (childMapping != null) {
1410                         getStyles(childMapping, styles, tags, ids, classes,
1411                                   counter + 1, numElements, alreadyChecked);
1412                     }
1413                 }
1414             }
1415         }
1416     }
1417 
1418     /**
1419      * Creates and returns a Style containing all the rules that match
1420      *  {@code selector}.
1421      */
1422     private synchronized Style createResolvedStyle(String selector,
1423                                       String[] tags,
1424                                       String[] ids, String[] classes) {
1425         SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
1426         @SuppressWarnings("unchecked")
1427         Vector<SelectorMapping> tempVector = sb.getVector();
1428         @SuppressWarnings("unchecked")
1429         Hashtable<SelectorMapping, SelectorMapping> tempHashtable = sb.getHashtable();
1430         // Determine all the Styles that are appropriate, placing them
1431         // in tempVector
1432         try {
1433             SelectorMapping mapping = getRootSelectorMapping();
1434             int numElements = tags.length;
1435             String tagString = tags[0];
1436             SelectorMapping childMapping = mapping.getChildSelectorMapping(
1437                                                    tagString, false);
1438             if (childMapping != null) {
1439                 getStyles(childMapping, tempVector, tags, ids, classes, 1,
1440                           numElements, tempHashtable);


1483                 AttributeSet attr = linkedStyleSheets.elementAt(counter).getRule(selector);
1484                 if (attr == null) {
1485                     attrs[counter + numStyles] = SimpleAttributeSet.EMPTY;
1486                 }
1487                 else {
1488                     attrs[counter + numStyles] = attr;
1489                 }
1490             }
1491             ResolvedStyle retStyle = new ResolvedStyle(selector, attrs,
1492                                                        numStyles);
1493             resolvedStyles.put(selector, retStyle);
1494             return retStyle;
1495         }
1496         finally {
1497             SearchBuffer.releaseSearchBuffer(sb);
1498         }
1499     }
1500 
1501     /**
1502      * Creates and returns a Style containing all the rules that
1503      * matches {@code selector}.
1504      *
1505      * @param elements  a Vector of all the Elements
1506      *                  the style is being asked for. The
1507      *                  first Element is the deepest Element, with the last Element
1508      *                  representing the root.
1509      * @param t         the Tag to use for
1510      *                  the first Element in {@code elements}
1511      */
1512     private Style createResolvedStyle(String selector, Vector<Element> elements,
1513                                       HTML.Tag t) {
1514         int numElements = elements.size();
1515         // Build three arrays, one for tags, one for class's, and one for
1516         // id's
1517         String tags[] = new String[numElements];
1518         String ids[] = new String[numElements];
1519         String classes[] = new String[numElements];
1520         for (int counter = 0; counter < numElements; counter++) {
1521             Element e = elements.elementAt(counter);
1522             AttributeSet attr = e.getAttributes();
1523             if (counter == 0 && e.isLeaf()) {
1524                 // For leafs, we use the second tier attributes.
1525                 Object testAttr = attr.getAttribute(t);
1526                 if (testAttr instanceof AttributeSet) {
1527                     attr = (AttributeSet)testAttr;
1528                 }
1529                 else {
1530                     attr = null;


1547                     classes[counter] = null;
1548                 }
1549                 if (attr.isDefined(HTML.Attribute.ID)) {
1550                     ids[counter] = attr.getAttribute(HTML.Attribute.ID).
1551                                         toString();
1552                 }
1553                 else {
1554                     ids[counter] = null;
1555                 }
1556             }
1557             else {
1558                 tags[counter] = ids[counter] = classes[counter] = null;
1559             }
1560         }
1561         tags[0] = t.toString();
1562         return createResolvedStyle(selector, tags, ids, classes);
1563     }
1564 
1565     /**
1566      * Creates and returns a Style containing all the rules that match
1567      *  {@code selector}. It is assumed that each simple selector
1568      * in {@code selector} is separated by a space.
1569      */
1570     private Style createResolvedStyle(String selector) {
1571         SearchBuffer sb = SearchBuffer.obtainSearchBuffer();
1572         // Will contain the tags, ids, and classes, in that order.
1573         @SuppressWarnings("unchecked")
1574         Vector<String> elements = sb.getVector();
1575         try {
1576             boolean done;
1577             int dotIndex = 0;
1578             int spaceIndex;
1579             int poundIndex = 0;
1580             int lastIndex = 0;
1581             int length = selector.length();
1582             while (lastIndex < length) {
1583                 if (dotIndex == lastIndex) {
1584                     dotIndex = selector.indexOf('.', lastIndex);
1585                 }
1586                 if (poundIndex == lastIndex) {
1587                     poundIndex = selector.indexOf('#', lastIndex);
1588                 }


1748 
1749         /**
1750          * Returns an instance of SearchBuffer. Be sure and issue
1751          * a releaseSearchBuffer when done with it.
1752          */
1753         static SearchBuffer obtainSearchBuffer() {
1754             SearchBuffer sb;
1755             try {
1756                 if(!searchBuffers.empty()) {
1757                    sb = searchBuffers.pop();
1758                 } else {
1759                    sb = new SearchBuffer();
1760                 }
1761             } catch (EmptyStackException ese) {
1762                 sb = new SearchBuffer();
1763             }
1764             return sb;
1765         }
1766 
1767         /**
1768          * Adds {@code sb} to the stack of SearchBuffers that can
1769          * be used.
1770          */
1771         static void releaseSearchBuffer(SearchBuffer sb) {
1772             sb.empty();
1773             searchBuffers.push(sb);
1774         }
1775 
1776         StringBuffer getStringBuffer() {
1777             if (stringBuffer == null) {
1778                 stringBuffer = new StringBuffer();
1779             }
1780             return stringBuffer;
1781         }
1782 
1783         Vector getVector() {
1784             if (vector == null) {
1785                 vector = new Vector();
1786             }
1787             return vector;
1788         }


2140 
2141             if (childtype == null) {
2142                 if (type == null) {
2143                     // Parent view.
2144                     View v = childView.getParent();
2145                     HTMLDocument doc = (HTMLDocument)v.getDocument();
2146                     if (HTMLDocument.matchNameAttribute(v.getElement().getAttributes(),
2147                                                         HTML.Tag.OL)) {
2148                         childtype = CSS.Value.DECIMAL;
2149                     } else {
2150                         childtype = CSS.Value.DISC;
2151                     }
2152                 } else {
2153                     childtype = type;
2154                 }
2155             }
2156             return childtype;
2157         }
2158 
2159         /**
2160          * Obtains the starting index from {@code parent}.
2161          */
2162         private void getStart(View parent) {
2163             checkedForStart = true;
2164             Element element = parent.getElement();
2165             if (element != null) {
2166                 AttributeSet attr = element.getAttributes();
2167                 Object startValue;
2168                 if (attr != null && attr.isDefined(HTML.Attribute.START) &&
2169                     (startValue = attr.getAttribute
2170                      (HTML.Attribute.START)) != null &&
2171                     (startValue instanceof String)) {
2172 
2173                     try {
2174                         start = Integer.parseInt((String)startValue);
2175                     }
2176                     catch (NumberFormatException nfe) {}
2177                 }
2178             }
2179         }
2180 
2181         /**
2182          * Returns an integer that should be used to render the child at
2183          * {@code childIndex} with. The retValue will usually be
2184          * {@code childIndex} + 1, unless {@code parentView}
2185          * has some Views that do not represent LI's, or one of the views
2186          * has a HTML.Attribute.START specified.
2187          */
2188         private int getRenderIndex(View parentView, int childIndex) {
2189             if (!checkedForStart) {
2190                 getStart(parentView);
2191             }
2192             int retIndex = childIndex;
2193             for (int counter = childIndex; counter >= 0; counter--) {
2194                 AttributeSet as = parentView.getElement().getElement(counter).
2195                                   getAttributes();
2196                 if (as.getAttribute(StyleConstants.NameAttribute) !=
2197                     HTML.Tag.LI) {
2198                     retIndex--;
2199                 } else if (as.isDefined(HTML.Attribute.VALUE)) {
2200                     Object value = as.getAttribute(HTML.Attribute.VALUE);
2201                     if (value != null &&
2202                         (value instanceof String)) {
2203                         try {
2204                             int iValue = Integer.parseInt((String)value);


2843      * A subclass of MuxingAttributeSet that implements Style. Currently
2844      * the MutableAttributeSet methods are unimplemented, that is they
2845      * do nothing.
2846      */
2847     // PENDING(sky): Decide what to do with this. Either make it
2848     // contain a SimpleAttributeSet that modify methods are delegated to,
2849     // or change getRule to return an AttributeSet and then don't make this
2850     // implement Style.
2851     @SuppressWarnings("serial") // Same-version serialization only
2852     static class ResolvedStyle extends MuxingAttributeSet implements
2853                   Serializable, Style {
2854         ResolvedStyle(String name, AttributeSet[] attrs, int extendedIndex) {
2855             super(attrs);
2856             this.name = name;
2857             this.extendedIndex = extendedIndex;
2858         }
2859 
2860         /**
2861          * Inserts a Style into the receiver so that the styles the
2862          * receiver represents are still ordered by specificity.
2863          * {@code style} will be added before any extended styles, that
2864          * is before extendedIndex.
2865          */
2866         synchronized void insertStyle(Style style, int specificity) {
2867             AttributeSet[] attrs = getAttributes();
2868             int maxCounter = attrs.length;
2869             int counter = 0;
2870             for (;counter < extendedIndex; counter++) {
2871                 if (specificity > getSpecificity(((Style)attrs[counter]).
2872                                                  getName())) {
2873                     break;
2874                 }
2875             }
2876             insertAttributeSetAt(style, counter);
2877             extendedIndex++;
2878         }
2879 
2880         /**
2881          * Removes a previously added style. This will do nothing if
2882          * {@code style} is not referenced by the receiver.
2883          */
2884         synchronized void removeStyle(Style style) {
2885             AttributeSet[] attrs = getAttributes();
2886 
2887             for (int counter = attrs.length - 1; counter >= 0; counter--) {
2888                 if (attrs[counter] == style) {
2889                     removeAttributeSetAt(counter);
2890                     if (counter < extendedIndex) {
2891                         extendedIndex--;
2892                     }
2893                     break;
2894                 }
2895             }
2896         }
2897 
2898         /**
2899          * Adds {@code s} as one of the Attributesets to look up
2900          * attributes in.
2901          */
2902         synchronized void insertExtendedStyleAt(Style attr, int index) {
2903             insertAttributeSetAt(attr, extendedIndex + index);
2904         }
2905 
2906         /**
2907          * Adds {@code s} as one of the AttributeSets to look up
2908          * attributes in. It will be the AttributeSet last checked.
2909          */
2910         synchronized void addExtendedStyle(Style attr) {
2911             insertAttributeSetAt(attr, getAttributes().length);
2912         }
2913 
2914         /**
2915          * Removes the style at {@code index} +
2916          * {@code extendedIndex}.
2917          */
2918         synchronized void removeExtendedStyleAt(int index) {
2919             removeAttributeSetAt(extendedIndex + index);
2920         }
2921 
2922         /**
2923          * Returns true if the receiver matches {@code selector}, where
2924          * a match is defined by the CSS rule matching.
2925          * Each simple selector must be separated by a single space.
2926          */
2927         protected boolean matches(String selector) {
2928             int sLast = selector.length();
2929 
2930             if (sLast == 0) {
2931                 return false;
2932             }
2933             int thisLast = name.length();
2934             int sCurrent = selector.lastIndexOf(' ');
2935             int thisCurrent = name.lastIndexOf(' ');
2936             if (sCurrent >= 0) {
2937                 sCurrent++;
2938             }
2939             if (thisCurrent >= 0) {
2940                 thisCurrent++;
2941             }
2942             if (!matches(selector, sCurrent, sLast, thisCurrent, thisLast)) {
2943                 return false;


3067         public void removeAttributes(AttributeSet attributes) {}
3068         public void setResolveParent(AttributeSet parent) {}
3069         public String getName() {return name;}
3070         public void addChangeListener(ChangeListener l) {}
3071         public void removeChangeListener(ChangeListener l) {}
3072         public ChangeListener[] getChangeListeners() {
3073             return new ChangeListener[0];
3074         }
3075 
3076         /** The name of the Style, which is the selector.
3077          * This will NEVER change!
3078          */
3079         String name;
3080         /** Start index of styles coming from other StyleSheets. */
3081         private int extendedIndex;
3082     }
3083 
3084 
3085     /**
3086      * SelectorMapping contains a specifitiy, as an integer, and an associated
3087      * Style. It can also reference children {@code SelectorMapping}s,
3088      * so that it behaves like a tree.
3089      * <p>
3090      * This is not thread safe, it is assumed the caller will take the
3091      * necessary precations if this is to be used in a threaded environment.
3092      */
3093     @SuppressWarnings("serial") // Same-version serialization only
3094     static class SelectorMapping implements Serializable {
3095         public SelectorMapping(int specificity) {
3096             this.specificity = specificity;
3097         }
3098 
3099         /**
3100          * Returns the specificity this mapping represents.
3101          */
3102         public int getSpecificity() {
3103             return specificity;
3104         }
3105 
3106         /**
3107          * Sets the Style associated with this mapping.
3108          */
3109         public void setStyle(Style style) {
3110             this.style = style;
3111         }
3112 
3113         /**
3114          * Returns the Style associated with this mapping.
3115          */
3116         public Style getStyle() {
3117             return style;
3118         }
3119 
3120         /**
3121          * Returns the child mapping identified by the simple selector
3122          * {@code selector}. If a child mapping does not exist for
3123          *{@code selector}, and {@code create} is true, a new
3124          * one will be created.
3125          */
3126         public SelectorMapping getChildSelectorMapping(String selector,
3127                                                        boolean create) {
3128             SelectorMapping retValue = null;
3129 
3130             if (children != null) {
3131                 retValue = children.get(selector);
3132             }
3133             else if (create) {
3134                 children = new HashMap<String, SelectorMapping>(7);
3135             }
3136             if (retValue == null && create) {
3137                 int specificity = getChildSpecificity(selector);
3138 
3139                 retValue = createChildSelectorMapping(specificity);
3140                 children.put(selector, retValue);
3141             }
3142             return retValue;
3143         }
3144 
3145         /**
3146          * Creates a child {@code SelectorMapping} with the specified
3147          * {@code specificity}.
3148          */
3149         protected SelectorMapping createChildSelectorMapping(int specificity) {
3150             return new SelectorMapping(specificity);
3151         }
3152 
3153         /**
3154          * Returns the specificity for the child selector
3155          * {@code selector}.
3156          */
3157         protected int getChildSpecificity(String selector) {
3158             // class (.) 100
3159             // id (#)    10000
3160             char    firstChar = selector.charAt(0);
3161             int     specificity = getSpecificity();
3162 
3163             if (firstChar == '.') {
3164                 specificity += 100;
3165             }
3166             else if (firstChar == '#') {
3167                 specificity += 10000;
3168             }
3169             else {
3170                 specificity += 1;
3171                 if (selector.indexOf('.') != -1) {
3172                     specificity += 100;
3173                 }
3174                 if (selector.indexOf('#') != -1) {
3175                     specificity += 10000;


3247          * Parse the given CSS stream
3248          */
3249         public void parse(URL base, Reader r, boolean parseDeclaration,
3250                           boolean isLink) throws IOException {
3251             this.base = base;
3252             this.isLink = isLink;
3253             this.parsingDeclaration = parseDeclaration;
3254             declaration.removeAttributes(declaration);
3255             selectorTokens.removeAllElements();
3256             selectors.removeAllElements();
3257             propertyName = null;
3258             parser.parse(r, this, parseDeclaration);
3259         }
3260 
3261         //
3262         // CSSParserCallback methods, public to implement the interface.
3263         //
3264 
3265         /**
3266          * Invoked when a valid @import is encountered, will call
3267          * {@code importStyleSheet} if a
3268          * {@code MalformedURLException} is not thrown in creating
3269          * the URL.
3270          */
3271         public void handleImport(String importString) {
3272             URL url = CSS.getURL(base, importString);
3273             if (url != null) {
3274                 importStyleSheet(url);
3275             }
3276         }
3277 
3278         /**
3279          * A selector has been encountered.
3280          */
3281         public void handleSelector(String selector) {
3282             //class and index selectors are case sensitive
3283             if (!(selector.startsWith(".")
3284                   || selector.startsWith("#"))) {
3285                 selector = selector.toLowerCase();
3286             }
3287             int length = selector.length();
3288 


< prev index next >