OSDN Git Service

Imported GNU Classpath 0.20
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / text / html / StyleSheet.java
1 /* StyleSheet.java -- 
2    Copyright (C) 2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package javax.swing.text.html;
40
41 import java.awt.Color;
42 import java.awt.Font;
43 import java.awt.Graphics;
44
45 import java.io.IOException;
46 import java.io.Reader;
47 import java.io.Serializable;
48 import java.io.StringReader;
49
50 import java.net.MalformedURLException;
51 import java.net.URL;
52
53 import java.util.Enumeration;
54 import java.util.Vector;
55
56 import javax.swing.text.AttributeSet;
57 import javax.swing.text.Element;
58 import javax.swing.text.MutableAttributeSet;
59 import javax.swing.text.SimpleAttributeSet;
60 import javax.swing.text.Style;
61 import javax.swing.text.StyleContext;
62 import javax.swing.text.View;
63
64
65 /**
66  * This class adds support for defining the visual characteristics of HTML views
67  * being rendered. This enables views to be customized by a look-and-feel, mulitple
68  * views over the same model can be rendered differently. Each EditorPane has its 
69  * own StyleSheet, but by default one sheet will be shared by all of the HTMLEditorKit
70  * instances. An HTMLDocument can also have a StyleSheet, which holds specific CSS
71  * specs. 
72  * 
73  *  In order for Views to store less state and therefore be more lightweight, 
74  *  the StyleSheet can act as a factory for painters that handle some of the 
75  *  rendering tasks. Since the StyleSheet may be used by views over multiple
76  *  documents the HTML attributes don't effect the selector being used.
77  *  
78  *  The rules are stored as named styles, and other information is stored to 
79  *  translate the context of an element to a rule.
80  * 
81  * @author Lillian Angel (langel@redhat.com)
82  */
83 public class StyleSheet extends StyleContext
84 {
85
86   /** The base URL */
87   URL base;
88   
89   /** Base font size (int) */
90   int baseFontSize;
91   
92   /** The style sheets stored. */
93   StyleSheet[] styleSheet;
94   
95   /**
96    * Constructs a StyleSheet.
97    */
98   public StyleSheet()
99   {
100     super();
101     baseFontSize = 4; // Default font size from CSS
102   }
103
104   /**
105    * Gets the style used to render the given tag. The element represents the tag
106    * and can be used to determine the nesting, where the attributes will differ
107    * if there is nesting inside of elements.
108    * 
109    * @param t - the tag to translate to visual attributes
110    * @param e - the element representing the tag
111    * @return the set of CSS attributes to use to render the tag.
112    */
113   public Style getRule(HTML.Tag t, Element e)
114   {
115     // FIXME: Not implemented.
116     return null;
117   }
118   
119   /**
120    * Gets the rule that best matches the selector. selector is a space
121    * separated String of element names. The attributes of the returned 
122    * Style will change as rules are added and removed.
123    * 
124    * @param selector - the element names separated by spaces
125    * @return the set of CSS attributes to use to render
126    */
127   public Style getRule(String selector)
128   {
129     // FIXME: Not implemented.
130     return null; 
131   }
132   
133   /**
134    * Adds a set if rules to the sheet. The rules are expected to be in valid
135    * CSS format. This is called as a result of parsing a <style> tag
136    * 
137    * @param rule - the rule to add to the sheet
138    */
139   public void addRule(String rule)
140   {
141     CssParser cp = new CssParser();
142     try
143     {
144       cp.parse(base, new StringReader(rule), false, false);
145     }
146     catch (IOException io)
147     {
148       // Do nothing here.
149     }
150   }
151   
152   /**
153    * Translates a CSS declaration into an AttributeSet. This is called
154    * as a result of encountering an HTML style attribute.
155    * 
156    * @param decl - the declaration to get
157    * @return the AttributeSet representing the declaration
158    */
159   public AttributeSet getDeclaration(String decl)
160   {
161     if (decl == null)
162       return SimpleAttributeSet.EMPTY;
163     // FIXME: Not implemented.
164     return null;     
165   }
166   
167   /**
168    * Loads a set of rules that have been specified in terms of CSS grammar.
169    * If there are any conflicts with existing rules, the new rule is added.
170    * 
171    * @param in - the stream to read the CSS grammar from.
172    * @param ref - the reference URL. It is the location of the stream, it may
173    * be null. All relative URLs specified in the stream will be based upon this
174    * parameter.
175    * @throws IOException - For any IO error while reading
176    */
177   public void loadRules(Reader in, URL ref) throws IOException
178   {
179     CssParser cp = new CssParser();
180     cp.parse(ref, in, false, false);
181   }
182   
183   /**
184    * Gets a set of attributes to use in the view. This is a set of
185    * attributes that can be used for View.getAttributes
186    * 
187    * @param v - the view to get the set for
188    * @return the AttributeSet to use in the view.
189    */
190   public AttributeSet getViewAttributes(View v)
191   {
192     // FIXME: Not implemented.
193     return null;
194   }
195   
196   /**
197    * Removes a style previously added.
198    * 
199    * @param nm - the name of the style to remove
200    */
201   public void removeStyle(String nm)
202   {
203     // FIXME: Not implemented.
204     super.removeStyle(nm);
205   }
206   
207   /**
208    * Adds the rules from ss to those of the receiver. ss's rules will
209    * override the old rules. An added StyleSheet will never override the rules
210    * of the receiving style sheet.
211    * 
212    * @param ss - the new StyleSheet.
213    */
214   public void addStyleSheet(StyleSheet ss)
215   {
216     if (styleSheet == null)
217       styleSheet = new StyleSheet[] {ss};
218     else
219       System.arraycopy(new StyleSheet[] {ss}, 0, styleSheet, 
220                        styleSheet.length, 1);
221   }
222   
223   /**
224    * Removes ss from those of the receiver
225    * 
226    * @param ss - the StyleSheet to remove.
227    */
228   public void removeStyleSheet(StyleSheet ss)
229   {
230     if (styleSheet.length == 1 && styleSheet[0].equals(ss))
231       styleSheet = null;
232     else
233       {
234         for (int i = 0; i < styleSheet.length; i++)
235           {
236             StyleSheet curr = styleSheet[i];
237             if (curr.equals(ss))
238               {
239                 StyleSheet[] tmp = new StyleSheet[styleSheet.length - 1];
240                 if (i != 0 && i != (styleSheet.length - 1))
241                   {
242                     System.arraycopy(styleSheet, 0, tmp, 0, i);
243                     System.arraycopy(styleSheet, i + 1, tmp, i,
244                                      styleSheet.length - i - 1);
245                   }
246                 else if (i == 0)
247                   System.arraycopy(styleSheet, 1, tmp, 0, styleSheet.length - 1);
248                 else
249                   System.arraycopy(styleSheet, 0, tmp, 0, styleSheet.length - 1);
250                 
251                 styleSheet = tmp;
252                 break;
253               }
254           }
255       }
256   }
257   
258   /**
259    * Returns an array of the linked StyleSheets. May return null.
260    * 
261    * @return - An array of the linked StyleSheets.
262    */
263   public StyleSheet[] getStyleSheets()
264   {
265     return styleSheet;
266   }
267   
268   /**
269    * Imports a style sheet from the url. The rules are directly added to the
270    * receiver.
271    * 
272    * @param url - the URL to import the StyleSheet from.
273    */
274   public void importStyleSheet(URL url)
275   {
276     // FIXME: Not implemented
277   }
278   
279   /**
280    * Sets the base url. All import statements that are relative, will be
281    * relative to base.
282    * 
283    * @param base -
284    *          the base URL.
285    */
286   public void setBase(URL base)
287   {
288     this.base = base;
289   }
290   
291   /**
292    * Gets the base url.
293    * 
294    * @return - the base
295    */
296   public URL getBase()
297   {
298     return base;
299   }
300   
301   /**
302    * Adds a CSS attribute to the given set.
303    * 
304    * @param attr - the attribute set
305    * @param key - the attribute to add
306    * @param value - the value of the key
307    */
308   public void addCSSAttribute(MutableAttributeSet attr, CSS.Attribute key,
309                               String value)
310   {
311     attr.addAttribute(key, value);
312   }
313   
314   /**
315    * Adds a CSS attribute to the given set.
316    * This method parses the value argument from HTML based on key. 
317    * Returns true if it finds a valid value for the given key, 
318    * and false otherwise.
319    * 
320    * @param attr - the attribute set
321    * @param key - the attribute to add
322    * @param value - the value of the key
323    * @return true if a valid value was found.
324    */
325   public boolean addCSSAttributeFromHTML(MutableAttributeSet attr, CSS.Attribute key,
326                                          String value)
327   {
328     // FIXME: Need to parse value from HTML based on key.
329     attr.addAttribute(key, value);
330     return attr.containsAttribute(key, value);
331   }
332   
333   /**
334    * Converts a set of HTML attributes to an equivalent set of CSS attributes.
335    * 
336    * @param htmlAttrSet - the set containing the HTML attributes.
337    * @return the set of CSS attributes
338    */
339   public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet)
340   {
341     // FIXME: Not implemented.
342     return null;    
343   }
344
345   /**
346    * Adds an attribute to the given set and returns a new set. This is implemented
347    * to convert StyleConstants attributes to CSS before forwarding them to the superclass.
348    * The StyleConstants attribute do not have corresponding CSS entry, the attribute
349    * is stored (but will likely not be used).
350    * 
351    * @param old - the old set
352    * @param key - the non-null attribute key
353    * @param value - the attribute value
354    * @return the updated set 
355    */
356   public AttributeSet addAttribute(AttributeSet old, Object key,
357                                    Object value)
358   {
359     // FIXME: Not implemented.
360     return super.addAttribute(old, key, value);       
361   }
362   
363   /**
364    * Adds a set of attributes to the element. If any of these attributes are
365    * StyleConstants, they will be converted to CSS before forwarding to the 
366    * superclass.
367    * 
368    * @param old - the old set
369    * @param attr - the attributes to add
370    * @return the updated attribute set
371    */
372   public AttributeSet addAttributes(AttributeSet old, AttributeSet attr)
373   {
374     // FIXME: Not implemented.
375     return super.addAttributes(old, attr);           
376   }
377   
378   /**
379    * Removes an attribute from the set. If the attribute is a
380    * StyleConstants, it will be converted to CSS before forwarding to the 
381    * superclass.
382    * 
383    * @param old - the old set
384    * @param key - the non-null attribute key
385    * @return the updated set 
386    */
387   public AttributeSet removeAttribute(AttributeSet old, Object key)
388   {
389     // FIXME: Not implemented.
390     return super.removeAttribute(old, key);    
391   }
392   
393   /**
394    * Removes an attribute from the set. If any of the attributes are
395    * StyleConstants, they will be converted to CSS before forwarding to the 
396    * superclass.
397    * 
398    * @param old - the old set
399    * @param attrs - the attributes to remove
400    * @return the updated set 
401    */
402   public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs)
403   {
404     // FIXME: Not implemented.
405     return super.removeAttributes(old, attrs);    
406   }
407   
408   /**
409    * Removes a set of attributes for the element. If any of the attributes is a
410    * StyleConstants, they will be converted to CSS before forwarding to the 
411    * superclass.
412    * 
413    * @param old - the old attribute set
414    * @param names - the attribute names
415    * @return the update attribute set
416    */
417   public AttributeSet removeAttributes(AttributeSet old, Enumeration names)
418   {
419     // FIXME: Not implemented.
420     return super.removeAttributes(old, names);        
421   }
422   
423   /**
424    * Creates a compact set of attributes that might be shared. This is a hook
425    * for subclasses that want to change the behaviour of SmallAttributeSet.
426    * 
427    * @param a - the set of attributes to be represented in the compact form.
428    * @return the set of attributes created
429    */
430   protected StyleContext.SmallAttributeSet createSmallAttributeSet(AttributeSet a)
431   {
432     return super.createSmallAttributeSet(a);     
433   }
434   
435   /**
436    * Creates a large set of attributes. This set is not shared. This is a hook
437    * for subclasses that want to change the behaviour of the larger attribute
438    * storage format.
439    * 
440    * @param a - the set of attributes to be represented in the larger form.
441    * @return the large set of attributes.
442    */
443   protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
444   {
445     return super.createLargeAttributeSet(a);     
446   }
447   
448   /**
449    * Gets the font to use for the given set.
450    * 
451    * @param a - the set to get the font for.
452    * @return the font for the set
453    */
454   public Font getFont(AttributeSet a)
455   {
456     return super.getFont(a);    
457   }
458   
459   /**
460    * Takes a set of attributes and turns it into a foreground
461    * color specification. This is used to specify things like, brigher, more hue
462    * etc.
463    * 
464    * @param a - the set to get the foreground color for
465    * @return the foreground color for the set
466    */
467   public Color getForeground(AttributeSet a)
468   {
469     return super.getForeground(a);     
470   }
471   
472   /**
473    * Takes a set of attributes and turns it into a background
474    * color specification. This is used to specify things like, brigher, more hue
475    * etc.
476    * 
477    * @param a - the set to get the background color for
478    * @return the background color for the set
479    */
480   public Color getBackground(AttributeSet a)
481   {
482     return super.getBackground(a);     
483   }
484   
485   /**
486    * Gets the box formatter to use for the given set of CSS attributes.
487    * 
488    * @param a - the given set
489    * @return the box formatter
490    */
491   public BoxPainter getBoxPainter(AttributeSet a)
492   {
493     return new BoxPainter(a);     
494   }
495   
496   /**
497    * Gets the list formatter to use for the given set of CSS attributes.
498    * 
499    * @param a - the given set
500    * @return the list formatter
501    */
502   public ListPainter getListPainter(AttributeSet a)
503   {
504     return new ListPainter(a);         
505   }
506   
507   /**
508    * Sets the base font size between 1 and 7.
509    * 
510    * @param sz - the new font size for the base.
511    */
512   public void setBaseFontSize(int sz)
513   {
514     if (sz <= 7 && sz >= 1)
515       baseFontSize = sz;
516   }
517   
518   /**
519    * Sets the base font size from the String. It can either identify
520    * a specific font size (between 1 and 7) or identify a relative
521    * font size such as +1 or -2.
522    * 
523    * @param size - the new font size as a String.
524    */
525   public void setBaseFontSize(String size)
526   {
527     size.trim();
528     int temp = 0;
529     try
530       {
531         if (size.length() == 2)
532           {
533             int i = new Integer(size.substring(1)).intValue();
534             if (size.startsWith("+"))
535               temp = baseFontSize + i;
536             else if (size.startsWith("-"))
537               temp = baseFontSize - i;
538           }
539         else if (size.length() == 1)
540           temp = new Integer(size.substring(0)).intValue();
541
542         if (temp <= 7 && temp >= 1)
543           baseFontSize = temp;
544       }
545     catch (NumberFormatException nfe)
546       {
547         // Do nothing here
548       }
549   }
550   
551   /**
552    * TODO
553    * 
554    * @param pt - TODO
555    * @return TODO
556    */
557   public static int getIndexOfSize(float pt)
558   {
559     // FIXME: Not implemented.
560     return 0;
561   }
562   
563   /**
564    * Gets the point size, given a size index.
565    * 
566    * @param index - the size index
567    * @return the point size.
568    */
569   public float getPointSize(int index)
570   {
571     // FIXME: Not implemented.
572     return 0;    
573   }
574   
575   /**
576    * Given the string of the size, returns the point size value.
577    * 
578    * @param size - the string representation of the size.
579    * @return - the point size value.
580    */
581   public float getPointSize(String size)
582   {
583     // FIXME: Not implemented.
584     return 0;    
585   }
586   
587   /**
588    * Converst a color string to a color. If it is not found, null is returned.
589    * 
590    * @param color - the color string such as "RED" or "#NNNNNN"
591    * @return the Color, or null if not found.
592    */
593   public Color stringToColor(String color)
594   {
595     color = color.toLowerCase();
596     if (color.equals("black") || color.equals("#000000"))
597       return Color.BLACK;
598     else if (color.equals("aqua") || color.equals("#00FFFF"))
599       return new Color(127, 255, 212);
600     else if (color.equals("gray") || color.equals("#808080"))
601       return Color.GRAY;
602     else if (color.equals("navy") || color.equals("#000080"))
603       return new Color(0, 0, 128);
604     else if (color.equals("silver") || color.equals("#C0C0C0"))
605       return Color.LIGHT_GRAY;
606     else if (color.equals("green") || color.equals("#008000"))
607       return Color.GREEN;
608     else if (color.equals("olive") || color.equals("#808000"))
609       return new Color(128, 128, 0);
610     else if (color.equals("teal") || color.equals("#008080"))
611       return new Color(0, 128, 128);
612     else if (color.equals("blue") || color.equals("#0000FF"))
613       return Color.BLUE;
614     else if (color.equals("lime") || color.equals("#00FF00"))
615       return new Color(0, 255, 0);
616     else if (color.equals("purple") || color.equals("#800080"))
617       return new Color(128, 0, 128);
618     else if (color.equals("white") || color.equals("#FFFFFF"))
619       return Color.WHITE;
620     else if (color.equals("fuchsia") || color.equals("#FF00FF"))
621       return Color.MAGENTA;
622     else if (color.equals("maroon") || color.equals("#800000"))
623       return new Color(128, 0, 0);
624     else if (color.equals("Red") || color.equals("#FF0000"))
625       return Color.RED;
626     else if (color.equals("Yellow") || color.equals("#FFFF00"))
627       return Color.YELLOW;
628     return null; 
629   }
630   
631   /**
632    * This class carries out some of the duties of CSS formatting. This enables views
633    * to present the CSS formatting while not knowing how the CSS values are cached.
634    * 
635    * This object is reponsible for the insets of a View and making sure
636    * the background is maintained according to the CSS attributes.
637    * 
638    * @author Lillian Angel (langel@redhat.com)
639    */
640   public static class BoxPainter extends Object implements Serializable
641   {
642     
643     /**
644      * Attribute set for painter
645      */
646     AttributeSet as;
647     
648     /**
649      * Package-private constructor.
650      * 
651      * @param as - AttributeSet for painter
652      */
653     BoxPainter(AttributeSet as)
654     {
655       this.as = as;
656     }
657     
658     /**
659      * Gets the inset needed on a given side to account for the margin, border
660      * and padding.
661      * 
662      * @param size - the size of the box to get the inset for. View.TOP, View.LEFT,
663      * View.BOTTOM or View.RIGHT.
664      * @param v - the view making the request. This is used to get the AttributeSet,
665      * amd may be used to resolve percentage arguments.
666      * @return the inset
667      * @throws IllegalArgumentException - for an invalid direction.
668      */
669     public float getInset(int size, View v)
670     {
671       // FIXME: Not implemented.
672       return 0;       
673     }
674     
675     /**
676      * Paints the CSS box according to the attributes given. This should
677      * paint the border, padding and background.
678      * 
679      * @param g - the graphics configuration
680      * @param x - the x coordinate
681      * @param y - the y coordinate
682      * @param w - the width of the allocated area
683      * @param h - the height of the allocated area
684      * @param v - the view making the request
685      */
686     public void paint(Graphics g, float x, float y, float w, float h, View v)
687     {
688       // FIXME: Not implemented.
689     }
690   }
691   
692   /**
693    * This class carries out some of the CSS list formatting duties. Implementations
694    * of this class enable views to present the CSS formatting while not knowing anything
695    * about how the CSS values are being cached.
696    * 
697    * @author Lillian Angel (langel@redhat.com)
698    */
699   public static class ListPainter extends Object implements Serializable
700   {
701     
702     /**
703      * Attribute set for painter
704      */
705     AttributeSet as;
706     
707     /**
708      * Package-private constructor.
709      * 
710      * @param as - AttributeSet for painter
711      */
712     ListPainter(AttributeSet as)
713     {
714       this.as = as;
715     }
716     
717     /**
718      * Paints the CSS list decoration according to the attributes given.
719      * 
720      * @param g - the graphics configuration
721      * @param x - the x coordinate
722      * @param y - the y coordinate
723      * @param w - the width of the allocated area
724      * @param h - the height of the allocated area
725      * @param v - the view making the request
726      * @param item - the list item to be painted >=0.
727      */
728     public void paint(Graphics g, float x, float y, float w, float h, View v,
729                       int item)
730     {
731       // FIXME: Not implemented.
732     }
733   }
734   
735   /**
736    * The parser callback for the CSSParser.
737    */
738   class CssParser implements CSSParser.CSSParserCallback
739   {
740     /** 
741      * A vector of all the selectors. 
742      * Each element is an array of all the selector tokens 
743      * in a single rule. 
744      */
745     Vector selectors;
746
747     /** A vector of all the selector tokens in a rule. */
748     Vector selectorTokens;
749
750     /**  Name of the current property. */
751     String propertyName;
752
753     /** The set of CSS declarations */
754     MutableAttributeSet declaration;
755
756     /** 
757      * True if parsing a declaration, that is the Reader will not 
758      * contain a selector. 
759      */
760     boolean parsingDeclaration;
761
762     /** True if the attributes are coming from a linked/imported style. */
763     boolean isLink;
764
765     /** The base URL */
766     URL base;
767
768     /** The parser */
769     CSSParser parser;
770
771     /**
772      * Constructor
773      */
774     CssParser()
775     {
776       selectors = new Vector();
777       selectorTokens = new Vector();
778       parser = new CSSParser();
779       base = StyleSheet.this.base;
780       declaration = new SimpleAttributeSet();
781     }
782
783     /**
784      * Parses the passed in CSS declaration into an AttributeSet.
785      * 
786      * @param s - the declaration
787      * @return the set of attributes containing the property and value.
788      */
789     public AttributeSet parseDeclaration(String s)
790     {
791       try
792       {
793         return parseDeclaration(new StringReader(s));
794       }
795       catch (IOException e)
796       {
797          // Do nothing here.
798       }
799       return null;
800     }
801
802     /**
803      * Parses the passed in CSS declaration into an AttributeSet.
804      * 
805      * @param r - the reader
806      * @return the attribute set
807      * @throws IOException from the reader
808      */
809     public AttributeSet parseDeclaration(Reader r) throws IOException
810     {
811       parse(base, r, true, false);
812       return declaration;
813     }
814
815     /**
816      * Parse the given CSS stream
817      * 
818      * @param base - the url
819      * @param r - the reader
820      * @param parseDec - True if parsing a declaration
821      * @param isLink - True if parsing a link
822      */
823    public void parse(URL base, Reader r, boolean parseDec, boolean isLink) throws IOException
824    {
825      parsingDeclaration = parseDec;
826      this.isLink = isLink;
827      this.base = base;
828      
829      // flush out all storage
830      propertyName = null;
831      selectors.clear();
832      selectorTokens.clear();
833      declaration.removeAttributes(declaration);
834      
835      parser.parse(r, this, parseDec);
836    }
837
838    /**
839     * Invoked when a valid @import is encountered, 
840     * will call importStyleSheet if a MalformedURLException 
841     * is not thrown in creating the URL.
842     *
843     * @param s - the string after @import
844     */ 
845    public void handleImport(String s)
846     {
847       if (s != null)
848         {
849           try
850             {
851               if (s.startsWith("url(") && s.endsWith(")"))
852                 s = s.substring(4, s.length() - 1);
853               if (s.indexOf("\"") >= 0)
854                 s = s.replaceAll("\"","");
855
856               URL url = new URL(s);
857               if (url == null && base != null)
858                 url = new URL(base, s);
859               
860               importStyleSheet(url);
861             }
862           catch (MalformedURLException e)
863             {
864               // Do nothing here.
865             }
866         }
867     }
868
869    /**
870      * A selector has been encountered.
871      * 
872      * @param s - a selector (e.g. P or UL or even P,)
873      */
874    public void handleSelector(String s)
875    {
876      if (s.endsWith(","))
877        s = s.substring(0, s.length() - 1);
878      
879      selectorTokens.addElement(s);
880      addSelector();
881    }
882
883    /**
884     * Invoked when the start of a rule is encountered.
885     */
886    public void startRule()
887    {
888      addSelector();
889    }
890
891    /**
892     * Invoked when a property name is encountered.
893     *
894     * @param s - the property
895     */
896    public void handleProperty(String s)
897    {
898      propertyName = s;
899    }
900
901   /**
902    * Invoked when a property value is encountered.
903    *
904    * @param s - the value
905    */
906    public void handleValue(String s)
907    {
908      // call addCSSAttribute
909      // FIXME: Not implemented
910    }
911    
912    /**
913     * Invoked when the end of a rule is encountered.
914     */
915    public void endRule()
916    {
917      // FIXME: Not implemented
918      // add rules
919      propertyName = null;
920    }
921
922    /**
923     * Adds the selector to the vector.
924     */
925    private void addSelector()
926    {
927      int length = selectorTokens.size();
928      if (length > 0)
929        {
930          Object[] sel = new Object[length];
931          System.arraycopy(selectorTokens.toArray(), 0, sel, 0, length);
932          selectors.add(sel);
933          selectorTokens.clear();
934        }
935    }
936   }
937 }