KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > wings > style > CSSStyleSheet


1 /*
2  * $Id: CSSStyleSheet.java,v 1.5 2005/05/27 09:17:35 blueshift Exp $
3  * Copyright 2000,2005 wingS development team.
4  *
5  * This file is part of wingS (http://www.j-wings.org).
6  *
7  * wingS is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1
10  * of the License, or (at your option) any later version.
11  *
12  * Please see COPYING for the complete licence.
13  */

14 package org.wings.style;
15
16 import org.wings.SFont;
17 import org.wings.io.Device;
18
19 import java.awt.*;
20 import java.io.*;
21 import java.net.MalformedURLException JavaDoc;
22 import java.net.URL JavaDoc;
23 import java.util.*;
24 import java.util.List JavaDoc;
25
26 public class CSSStyleSheet
27         implements StyleSheet {
28     private static final Map lengthMapping = new HashMap();
29
30     static {
31         lengthMapping.put("pt", new Float JavaDoc(1f));
32         lengthMapping.put("px", new Float JavaDoc(1.3f));
33         lengthMapping.put("mm", new Float JavaDoc(2.83464f));
34         lengthMapping.put("cm", new Float JavaDoc(28.3464f));
35         lengthMapping.put("pc", new Float JavaDoc(12f));
36         lengthMapping.put("in", new Float JavaDoc(72f));
37     }
38
39     private final Map map;
40
41     public CSSStyleSheet() {
42         map = new HashMap();
43     }
44
45     public CSSStyleSheet(InputStream in) throws IOException {
46         this();
47         read(in);
48     }
49
50     public void putStyle(Style style) {
51         map.put(style.getSelector(), style);
52         style.setSheet(this);
53     }
54
55     public Set styles() {
56         return new HashSet(map.values());
57     }
58
59     /**
60      * Write each style in set to the device.
61      */

62     public void write(Device out) throws IOException {
63         Iterator iterator = map.entrySet().iterator();
64         while (iterator.hasNext()) {
65             Map.Entry entry = (Map.Entry) iterator.next();
66             /*
67             out.print((String)entry.getKey());
68             out.print(" { ");
69             out.print(entry.getValue().toString());
70             out.print(" }\n");
71             */

72             ((Style) entry.getValue()).write(out);
73         }
74         out.flush();
75     }
76
77     public void read(InputStream is)
78             throws IOException {
79         Reader r = new BufferedReader(new InputStreamReader(is));
80         CssParser parser = new CssParser();
81         parser.parse(null, r, false, false);
82     }
83
84     public void importStyleSheet(URL JavaDoc url) {
85         try {
86             InputStream is = url.openStream();
87             Reader r = new BufferedReader(new InputStreamReader(is));
88             CssParser parser = new CssParser();
89             parser.parse(url, r, false, true);
90             r.close();
91             is.close();
92         } catch (Throwable JavaDoc e) {
93             e.printStackTrace();
94             // on error we simply have no styles... the html
95
// will look mighty wrong but still function.
96
}
97     }
98
99     public InputStream getInputStream() throws IOException {
100         return null;
101     };
102     
103     public boolean isFinal() {
104         return false;
105     }
106
107     /**
108      * Fetches the font to use for the given set of attributes.
109      */

110     public static SFont getFont(CSSAttributeSet a) {
111         boolean anyFontAttribute = false;
112         int size = getFontSize(a);
113         anyFontAttribute |= (size > 0);
114
115         /*
116          * If the vertical alignment is set to either superscirpt or
117          * subscript we reduce the font size by 2 points.
118          */

119         String JavaDoc vAlign = (String JavaDoc) a.get(CSSProperty.VERTICAL_ALIGN);
120
121         if (vAlign != null) {
122             if ((vAlign.indexOf("sup") >= 0) ||
123                     (vAlign.indexOf("sub") >= 0)) {
124                 size -= 2;
125             }
126         }
127
128         String JavaDoc family = (String JavaDoc) a.get(CSSProperty.FONT_FAMILY);
129         anyFontAttribute |= (family != null);
130
131         int style = Font.PLAIN;
132         String JavaDoc weight = (String JavaDoc) a.get(CSSProperty.FONT_WEIGHT);
133         if (weight == null)
134             ;
135         else if (weight.equals("bold")) {
136             style |= Font.BOLD;
137         } else if (weight.equals("normal"))
138             ;
139         else {
140             try {
141                 int w = Integer.parseInt(weight);
142                 if (w > 400)
143                     style |= Font.BOLD;
144             } catch (NumberFormatException JavaDoc nfe) {
145             }
146         }
147         anyFontAttribute |= (weight != null);
148
149         String JavaDoc styleValue = (String JavaDoc) a.get(CSSProperty.FONT_STYLE);
150         if ((styleValue != null) && (styleValue.toLowerCase().indexOf("italic") >= 0)) style |= Font.ITALIC;
151         anyFontAttribute |= (styleValue != null);
152         return anyFontAttribute ? new SFont(family, style, size) : null;
153     }
154
155     static int sizeMap[] = {8, 10, 12, 14, 18, 24, 36};
156
157     /**
158      * parses the font size attribute. return -1, if no font size
159      * is specified.
160      */

161     private static int getFontSize(CSSAttributeSet attr) {
162         String JavaDoc value = (String JavaDoc) attr.get(CSSProperty.FONT_SIZE);
163         if (value == null)
164             return -1;
165         try {
166             if (value.equals("xx-small")) {
167                 return 8;
168             } else if (value.equals("x-small")) {
169                 return 10;
170             } else if (value.equals("small")) {
171                 return 12;
172             } else if (value.equals("medium")) {
173                 return 14;
174             } else if (value.equals("large")) {
175                 return 18;
176             } else if (value.equals("x-large")) {
177                 return 24;
178             } else if (value.equals("xx-large")) {
179                 return 36;
180             } else {
181                 return new Float JavaDoc(getLength(value)).intValue();
182             }
183         } catch (NumberFormatException JavaDoc nfe) {
184             return -1;
185         }
186     }
187
188     public static float getLength(String JavaDoc value) {
189         int length = value.length();
190         if (length >= 2) {
191             String JavaDoc units = value.substring(length - 2, length);
192             Float JavaDoc scale = (Float JavaDoc) lengthMapping.get(units);
193
194             if (scale != null) {
195                 try {
196                     return Float.valueOf(value.substring(0, length - 2)).floatValue() *
197                             scale.floatValue();
198                 } catch (NumberFormatException JavaDoc nfe) {
199                 }
200             } else {
201                 // treat like points.
202
try {
203                     return Float.valueOf(value).floatValue();
204                 } catch (NumberFormatException JavaDoc nfe) {
205                 }
206             }
207         } else if (length > 0) {
208             // treat like points.
209
try {
210                 return Float.valueOf(value).floatValue();
211             } catch (NumberFormatException JavaDoc nfe) {
212             }
213         }
214         return 12.0f;
215     }
216
217     /**
218      * Takes a set of attributes and turn it into a foreground color
219      * specification. This might be used to specify things
220      * like brighter, more hue, etc.
221      *
222      * @param a the set of attributes
223      * @return the color
224      */

225     public static Color getForeground(CSSAttributeSet a) {
226         return getColor(a, CSSProperty.COLOR);
227     }
228
229     /**
230      * Takes a set of attributes and turn it into a background color
231      * specification. This might be used to specify things
232      * like brighter, more hue, etc.
233      *
234      * @param a the set of attributes
235      * @return the color
236      */

237     public static Color getBackground(CSSAttributeSet a) {
238         return getColor(a, CSSProperty.BACKGROUND_COLOR);
239     }
240
241     static Color getColor(CSSAttributeSet a, CSSProperty cssProperty) {
242         String JavaDoc cv = (String JavaDoc) a.get(cssProperty);
243         if (cv != null) {
244             return stringToColor(cv);
245         }
246         return null;
247     }
248
249     /**
250      * Converts a type Color to a hex string
251      * in the format "#RRGGBB"
252      */

253     static String JavaDoc colorToHex(Color color) {
254         String JavaDoc colorstr = "#";
255
256         // Red
257
String JavaDoc str = Integer.toHexString(color.getRed());
258         if (str.length() > 2)
259             str = str.substring(0, 2);
260         else if (str.length() < 2)
261             colorstr += "0" + str;
262         else
263             colorstr += str;
264
265         // Green
266
str = Integer.toHexString(color.getGreen());
267         if (str.length() > 2)
268             str = str.substring(0, 2);
269         else if (str.length() < 2)
270             colorstr += "0" + str;
271         else
272             colorstr += str;
273
274         // Blue
275
str = Integer.toHexString(color.getBlue());
276         if (str.length() > 2)
277             str = str.substring(0, 2);
278         else if (str.length() < 2)
279             colorstr += "0" + str;
280         else
281             colorstr += str;
282
283         return colorstr;
284     }
285
286     /**
287      * Convert a "#FFFFFF" hex string to a Color.
288      * If the color specification is bad, an attempt
289      * will be made to fix it up.
290      */

291     static final Color hexToColor(String JavaDoc value) {
292         String JavaDoc digits;
293         if (value.startsWith("#")) {
294             digits = value.substring(1, Math.min(value.length(), 7));
295         } else {
296             digits = value;
297         }
298         String JavaDoc hstr = "0x" + digits;
299         Color c;
300         try {
301             c = Color.decode(hstr);
302         } catch (NumberFormatException JavaDoc nfe) {
303             c = null;
304         }
305         return c;
306     }
307
308     /**
309      * Convert a color string such as "RED" or "#NNNNNN" or "rgb(r, g, b)"
310      * to a Color.
311      */

312     static Color stringToColor(String JavaDoc str) {
313         Color color = null;
314
315         if (str.length() == 0)
316             color = Color.black;
317         else if (str.startsWith("rgb(")) {
318             color = parseRGB(str);
319         } else if (str.charAt(0) == '#')
320             color = hexToColor(str);
321         else if (str.equalsIgnoreCase("Black"))
322             color = hexToColor("#000000");
323         else if (str.equalsIgnoreCase("Silver"))
324             color = hexToColor("#C0C0C0");
325         else if (str.equalsIgnoreCase("Gray"))
326             color = hexToColor("#808080");
327         else if (str.equalsIgnoreCase("White"))
328             color = hexToColor("#FFFFFF");
329         else if (str.equalsIgnoreCase("Maroon"))
330             color = hexToColor("#800000");
331         else if (str.equalsIgnoreCase("Red"))
332             color = hexToColor("#FF0000");
333         else if (str.equalsIgnoreCase("Purple"))
334             color = hexToColor("#800080");
335         else if (str.equalsIgnoreCase("Fuchsia"))
336             color = hexToColor("#FF00FF");
337         else if (str.equalsIgnoreCase("Green"))
338             color = hexToColor("#008000");
339         else if (str.equalsIgnoreCase("Lime"))
340             color = hexToColor("#00FF00");
341         else if (str.equalsIgnoreCase("Olive"))
342             color = hexToColor("#808000");
343         else if (str.equalsIgnoreCase("Yellow"))
344             color = hexToColor("#FFFF00");
345         else if (str.equalsIgnoreCase("Navy"))
346             color = hexToColor("#000080");
347         else if (str.equalsIgnoreCase("Blue"))
348             color = hexToColor("#0000FF");
349         else if (str.equalsIgnoreCase("Teal"))
350             color = hexToColor("#008080");
351         else if (str.equalsIgnoreCase("Aqua"))
352             color = hexToColor("#00FFFF");
353         else
354             color = hexToColor(str); // sometimes get specified without leading #
355
return color;
356     }
357
358     /**
359      * Parses a String in the format <code>rgb(r, g, b)</code> where
360      * each of the Color components is either an integer, or a floating number
361      * with a % after indicating a percentage value of 255. Values are
362      * constrained to fit with 0-255. The resulting Color is returned.
363      */

364     private static Color parseRGB(String JavaDoc string) {
365         // Find the next numeric char
366
int[] index = new int[1];
367
368         index[0] = 4;
369         int red = getColorComponent(string, index);
370         int green = getColorComponent(string, index);
371         int blue = getColorComponent(string, index);
372
373         return new Color(red, green, blue);
374     }
375
376     /**
377      * Returns the next integer value from <code>string</code> starting
378      * at <code>index[0]</code>. The value can either can an integer, or
379      * a percentage (floating number ending with %), in which case it is
380      * multiplied by 255.
381      */

382     private static int getColorComponent(String JavaDoc string, int[] index) {
383         int length = string.length();
384         char aChar;
385
386         // Skip non-decimal chars
387
while (index[0] < length && (aChar = string.charAt(index[0])) != '-' &&
388                 !Character.isDigit(aChar) && aChar != '.') {
389             index[0]++;
390         }
391
392         int start = index[0];
393
394         if (start < length && string.charAt(index[0]) == '-') {
395             index[0]++;
396         }
397         while (index[0] < length &&
398                 Character.isDigit(string.charAt(index[0]))) {
399             index[0]++;
400         }
401         if (index[0] < length && string.charAt(index[0]) == '.') {
402             // Decimal value
403
index[0]++;
404             while (index[0] < length &&
405                     Character.isDigit(string.charAt(index[0]))) {
406                 index[0]++;
407             }
408         }
409         if (start != index[0]) {
410             try {
411                 float value = Float.parseFloat(string.substring
412                         (start, index[0]));
413
414                 if (index[0] < length && string.charAt(index[0]) == '%') {
415                     index[0]++;
416                     value = value * 255f / 100f;
417                 }
418                 return Math.min(255, Math.max(0, (int) value));
419             } catch (NumberFormatException JavaDoc nfe) {
420                 // Treat as 0
421
}
422         }
423         return 0;
424     }
425
426     public static URL JavaDoc getURL(URL JavaDoc base, String JavaDoc cssString) {
427         if (cssString == null) {
428             return null;
429         }
430         if (cssString.startsWith("url(") &&
431                 cssString.endsWith(")")) {
432             cssString = cssString.substring(4, cssString.length() - 1);
433         }
434         // Absolute first
435
try {
436             URL JavaDoc url = new URL JavaDoc(cssString);
437             if (url != null) {
438                 return url;
439             }
440         } catch (MalformedURLException JavaDoc mue) {
441         }
442         // Then relative
443
if (base != null) {
444             // Relative URL, try from base
445
try {
446                 URL JavaDoc url = new URL JavaDoc(base, cssString);
447                 return url;
448             } catch (MalformedURLException JavaDoc muee) {
449             }
450         }
451         return null;
452     }
453
454     public static CSSAttributeSet getAttributes(SFont font) {
455         CSSAttributeSet attributes = new CSSAttributeSet();
456         if (font == null)
457             return attributes;
458         attributes.put(CSSProperty.FONT_FAMILY, font.getFace());
459
460         if ((font.getStyle() & Font.ITALIC) > 0)
461             attributes.put(CSSProperty.FONT_STYLE, "italic");
462
463         if ((font.getStyle() & Font.BOLD) > 0)
464             attributes.put(CSSProperty.FONT_WEIGHT, "bold");
465
466         attributes.put(CSSProperty.FONT_SIZE, font.getSize() + "pt");
467         return attributes;
468     }
469
470     public static CSSAttributeSet getAttributes(Color color, CSSProperty cssProperty) {
471         CSSAttributeSet attributes = new CSSAttributeSet();
472         if (color != null)
473             attributes.put(cssProperty, colorToHex(color));
474         return attributes;
475     }
476
477     public static String JavaDoc getAttribute(Color color) {
478         if (color != null)
479             return colorToHex(color);
480         return null;
481     }
482
483     class CssParser
484             implements CSSParser.CSSParserCallback {
485         /**
486          * Parses the passed in CSS declaration into an CSSPropertySet.
487          */

488         public CSSAttributeSet parseDeclaration(String JavaDoc string) {
489             try {
490                 return parseDeclaration(new StringReader(string));
491             } catch (IOException ioe) {
492             }
493             return null;
494         }
495
496         /**
497          * Parses the passed in CSS declaration into an CSSPropertySet.
498          */

499         public CSSAttributeSet parseDeclaration(Reader r) throws IOException {
500             parse(base, r, true, false);
501             return new CSSAttributeSet(declaration);
502         }
503
504         /**
505          * Parse the given CSS stream
506          */

507         public void parse(URL JavaDoc base, Reader r, boolean parseDeclaration, boolean isLink)
508                 throws IOException {
509             this.base = base;
510 // this.isLink = isLink;
511
// this.parsingDeclaration = parseDeclaration;
512
declaration.clear();
513             selectorTokens.clear();
514             selectors.clear();
515             propertyName = null;
516             parser.parse(r, this, parseDeclaration);
517         }
518
519         //
520
// CSSParserCallback methods, public to implement the interface.
521
//
522

523         /**
524          * Invoked when a valid @import is encountered, will call
525          * <code>importStyleSheet</code> if a
526          * <code>MalformedURLException</code> is not thrown in creating
527          * the URL.
528          */

529         public void handleImport(String JavaDoc importString) {
530             URL JavaDoc url = CSSStyleSheet.getURL(base, importString);
531             if (url != null) {
532                 importStyleSheet(url);
533             }
534         }
535
536         /**
537          * A selector has been encountered.
538          */

539         public void handleSelector(String JavaDoc selector) {
540             selector = selector.toLowerCase();
541
542             int length = selector.length();
543
544             if (selector.endsWith(",")) {
545                 if (length > 1) {
546                     selector = selector.substring(0, length - 1);
547                     selectorTokens.add(selector);
548                 }
549                 addSelector();
550             } else if (length > 0) {
551                 selectorTokens.add(selector);
552             }
553         }
554
555         /**
556          * Invoked when the start of a rule is encountered.
557          */

558         public void startRule() {
559             if (selectorTokens.size() > 0) {
560                 addSelector();
561             }
562             propertyName = null;
563         }
564
565         /**
566          * Invoked when a property name is encountered.
567          */

568         public void handleProperty(String JavaDoc property) {
569             propertyName = property;
570         }
571
572         /**
573          * Invoked when a property value is encountered.
574          */

575         public void handleValue(String JavaDoc value) {
576             if (propertyName != null) {
577                 declaration.put(new CSSProperty(propertyName), value);
578             }
579             propertyName = null;
580         }
581
582         /**
583          * Invoked when the end of a rule is encountered.
584          */

585         public void endRule() {
586             int n = selectors.size();
587             for (int i = 0; i < n; i++) {
588                 String JavaDoc[] selector = (String JavaDoc[]) selectors.get(i);
589                 for (int j = selector.length - 1; j >= 0; --j) {
590                     CSSStyleSheet.this.putStyle(new CSSStyle(new CSSSelector(selector[j]), declaration));
591                 }
592             }
593             declaration.clear();
594             selectors.clear();
595         }
596
597         private void addSelector() {
598             String JavaDoc[] selector = new String JavaDoc[selectorTokens.size()];
599             selector = (String JavaDoc[]) selectorTokens.toArray(selector);
600             selectors.add(selector);
601             selectorTokens.clear();
602         }
603
604         List JavaDoc selectors = new LinkedList();
605         List JavaDoc selectorTokens = new LinkedList();
606         /**
607          * Name of the current property.
608          */

609         String JavaDoc propertyName;
610         CSSAttributeSet declaration = new CSSAttributeSet();
611         /** True if parsing a declaration, that is the Reader will not
612          * contain a selector. */

613         // boolean parsingDeclaration;
614
/** True if the attributes are coming from a linked/imported style. */
615 // boolean isLink;
616
/**
617          * Where the CSS stylesheet lives.
618          */

619         URL JavaDoc base;
620         CSSParser parser = new CSSParser();
621     }
622 }
623
624
625
Popular Tags