Source Code Cross Referenced for CSS.java in  » Apache-Harmony-Java-SE » javax-package » javax » swing » text » html » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Apache Harmony Java SE » javax package » javax.swing.text.html 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *  Licensed to the Apache Software Foundation (ASF) under one or more
0003:         *  contributor license agreements.  See the NOTICE file distributed with
0004:         *  this work for additional information regarding copyright ownership.
0005:         *  The ASF licenses this file to You under the Apache License, Version 2.0
0006:         *  (the "License"); you may not use this file except in compliance with
0007:         *  the License.  You may obtain a copy of the License at
0008:         *
0009:         *     http://www.apache.org/licenses/LICENSE-2.0
0010:         *
0011:         *  Unless required by applicable law or agreed to in writing, software
0012:         *  distributed under the License is distributed on an "AS IS" BASIS,
0013:         *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014:         *  See the License for the specific language governing permissions and
0015:         *  limitations under the License.
0016:         */
0017:        /**
0018:         * @author Alexey A. Ivanov
0019:         * @version $Revision$
0020:         */package javax.swing.text.html;
0021:
0022:        import java.awt.Color;
0023:        import java.awt.GraphicsEnvironment;
0024:        import java.awt.Image;
0025:        import java.io.Serializable;
0026:        import java.net.URL;
0027:        import java.util.ArrayList;
0028:        import java.util.Arrays;
0029:        import java.util.HashMap;
0030:        import java.util.Iterator;
0031:        import java.util.List;
0032:        import java.util.Map;
0033:        import java.util.regex.Matcher;
0034:        import java.util.regex.Pattern;
0035:
0036:        import javax.swing.text.AttributeSet;
0037:        import javax.swing.text.BoxView;
0038:        import javax.swing.text.MutableAttributeSet;
0039:        import javax.swing.text.StyleConstants;
0040:        import javax.swing.text.StyleContext;
0041:        import javax.swing.text.View;
0042:
0043:        import org.apache.harmony.x.swing.Utilities;
0044:        import org.apache.harmony.x.swing.internal.nls.Messages;
0045:
0046:        public class CSS implements  Serializable {
0047:            public static final class Attribute {
0048:                public static final Attribute BACKGROUND = new Attribute(
0049:                        "background", null, false, new BackgroundExpander());
0050:                public static final Attribute BACKGROUND_ATTACHMENT = new Attribute(
0051:                        "background-attachment", "scroll", false,
0052:                        BackgroundAttachment.factory);
0053:                public static final Attribute BACKGROUND_COLOR = new Attribute(
0054:                        "background-color", "transparent", false,
0055:                        BackgroundColor.factory);
0056:                public static final Attribute BACKGROUND_IMAGE = new Attribute(
0057:                        "background-image", "none", false, ImageValue.NONE);
0058:                public static final Attribute BACKGROUND_POSITION = new Attribute(
0059:                        "background-position", null, false,
0060:                        new BackgroundPosition());
0061:                public static final Attribute BACKGROUND_REPEAT = new Attribute(
0062:                        "background-repeat", "repeat", false,
0063:                        BackgroundRepeat.factory);
0064:                public static final Attribute BORDER = new Attribute("border",
0065:                        null, false, new BorderExpander());
0066:                public static final Attribute BORDER_BOTTOM_WIDTH = new Attribute(
0067:                        "border-bottom-width", "medium", false,
0068:                        BorderWidthValue.factory);
0069:                public static final Attribute BORDER_BOTTOM = new Attribute(
0070:                        "border-bottom", null, false, new BorderSideExpander(
0071:                                BorderSideExpander.BOTTOM_SIDE,
0072:                                BORDER_BOTTOM_WIDTH));
0073:                public static final Attribute BORDER_COLOR = new Attribute(
0074:                        "border-color", null, false, new BorderColor());
0075:                public static final Attribute BORDER_LEFT_WIDTH = new Attribute(
0076:                        "border-left-width", "medium", false,
0077:                        BorderWidthValue.factory);
0078:                public static final Attribute BORDER_LEFT = new Attribute(
0079:                        "border-left", null, false,
0080:                        new BorderSideExpander(BorderSideExpander.LEFT_SIDE,
0081:                                BORDER_LEFT_WIDTH));
0082:                public static final Attribute BORDER_RIGHT_WIDTH = new Attribute(
0083:                        "border-right-width", "medium", false,
0084:                        BorderWidthValue.factory);
0085:                public static final Attribute BORDER_RIGHT = new Attribute(
0086:                        "border-right", null, false, new BorderSideExpander(
0087:                                BorderSideExpander.RIGHT_SIDE,
0088:                                BORDER_RIGHT_WIDTH));
0089:                public static final Attribute BORDER_STYLE = new Attribute(
0090:                        "border-style", "none", false, new BorderStyle());
0091:                public static final Attribute BORDER_TOP_WIDTH = new Attribute(
0092:                        "border-top-width", "medium", false,
0093:                        BorderWidthValue.factory);
0094:                public static final Attribute BORDER_TOP = new Attribute(
0095:                        "border-top", null, false, new BorderSideExpander(
0096:                                BorderSideExpander.TOP_SIDE, BORDER_TOP_WIDTH));
0097:                public static final Attribute BORDER_WIDTH = new Attribute(
0098:                        "border-width", "medium", false, new SpaceExpander(
0099:                                BORDER_TOP_WIDTH, BORDER_RIGHT_WIDTH,
0100:                                BORDER_BOTTOM_WIDTH, BORDER_LEFT_WIDTH,
0101:                                BorderWidthValue.factory));
0102:                public static final Attribute CLEAR = new Attribute("clear",
0103:                        "none", false, new Clear());
0104:                public static final Attribute COLOR = new Attribute("color",
0105:                        null, true, ColorProperty.factory);
0106:                public static final Attribute DISPLAY = new Attribute(
0107:                        "display", "block", false, new Display());
0108:                public static final Attribute FLOAT = new Attribute("float",
0109:                        "none", false, new FloatProperty());
0110:                public static final Attribute FONT = new Attribute("font",
0111:                        null, true, new FontExpander());
0112:                public static final Attribute FONT_FAMILY = new Attribute(
0113:                        "font-family", null, true, new FontFamily());
0114:                public static final Attribute FONT_SIZE = new Attribute(
0115:                        "font-size", "medium", true, new FontSize());
0116:                public static final Attribute FONT_STYLE = new Attribute(
0117:                        "font-style", "normal", true, new FontStyle());
0118:                public static final Attribute FONT_VARIANT = new Attribute(
0119:                        "font-variant", "normal", true, new FontVariant());
0120:                public static final Attribute FONT_WEIGHT = new Attribute(
0121:                        "font-weight", "normal", true, new FontWeight());
0122:                public static final Attribute HEIGHT = new Attribute("height",
0123:                        "auto", false, new Height());
0124:                public static final Attribute LETTER_SPACING = new Attribute(
0125:                        "letter-spacing", "normal", true, SpacingValue.factory);
0126:                public static final Attribute LINE_HEIGHT = new Attribute(
0127:                        "line-height", "normal", true, LineHeight.normal);
0128:                public static final Attribute LIST_STYLE = new Attribute(
0129:                        "list-style", null, true, new ListStyleExpander());
0130:                public static final Attribute LIST_STYLE_IMAGE = new Attribute(
0131:                        "list-style-image", "none", true, ImageValue.NONE);
0132:                public static final Attribute LIST_STYLE_POSITION = new Attribute(
0133:                        "list-style-position", "outside", true,
0134:                        ListStylePosition.factory);
0135:                public static final Attribute LIST_STYLE_TYPE = new Attribute(
0136:                        "list-style-type", "disc", true, ListStyleType.factory);
0137:                public static final Attribute MARGIN_BOTTOM = new Attribute(
0138:                        "margin-bottom", "0", false, FloatValue.factory);
0139:                public static final Attribute MARGIN_LEFT = new Attribute(
0140:                        "margin-left", "0", false, FloatValue.factory);
0141:                public static final Attribute MARGIN_RIGHT = new Attribute(
0142:                        "margin-right", "0", false, FloatValue.factory);
0143:                public static final Attribute MARGIN_TOP = new Attribute(
0144:                        "margin-top", "0", false, FloatValue.factory);
0145:                public static final Attribute MARGIN = new Attribute("margin",
0146:                        null, false, new SpaceExpander(MARGIN_TOP,
0147:                                MARGIN_RIGHT, MARGIN_BOTTOM, MARGIN_LEFT));
0148:                public static final Attribute PADDING_BOTTOM = new Attribute(
0149:                        "padding-bottom", "0", false, FloatValue.factory);
0150:                public static final Attribute PADDING_LEFT = new Attribute(
0151:                        "padding-left", "0", false, FloatValue.factory);
0152:                public static final Attribute PADDING_RIGHT = new Attribute(
0153:                        "padding-right", "0", false, FloatValue.factory);
0154:                public static final Attribute PADDING_TOP = new Attribute(
0155:                        "padding-top", "0", false, FloatValue.factory);
0156:                public static final Attribute PADDING = new Attribute(
0157:                        "padding", null, false, new SpaceExpander(PADDING_TOP,
0158:                                PADDING_RIGHT, PADDING_BOTTOM, PADDING_LEFT));
0159:                public static final Attribute TEXT_ALIGN = new Attribute(
0160:                        "text-align", null, true, new TextAlign());
0161:                public static final Attribute TEXT_DECORATION = new Attribute(
0162:                        "text-decoration", "none", true, new TextDecoration());
0163:                public static final Attribute TEXT_INDENT = new Attribute(
0164:                        "text-indent", "0", true, FloatValue.factory);
0165:                public static final Attribute TEXT_TRANSFORM = new Attribute(
0166:                        "text-transform", "none", true, new TextTransform());
0167:                public static final Attribute VERTICAL_ALIGN = new Attribute(
0168:                        "vertical-align", "baseline", false,
0169:                        new VerticalAlign());
0170:                public static final Attribute WHITE_SPACE = new Attribute(
0171:                        "white-space", "normal", true, WhiteSpace.factory);
0172:                public static final Attribute WIDTH = new Attribute("width",
0173:                        "auto", false, Width.auto);
0174:                public static final Attribute WORD_SPACING = new Attribute(
0175:                        "word-spacing", "normal", true, SpacingValue.factory);
0176:
0177:                private final String name;
0178:                private final String defValue;
0179:                private final boolean inherit;
0180:                private final PropertyValueConverter converter;
0181:                private final ShorthandPropertyExpander expander;
0182:
0183:                private Attribute(final String name, final String defValue,
0184:                        final boolean inherit,
0185:                        final PropertyValueConverter converter) {
0186:                    this (name, defValue, inherit, converter, null);
0187:                }
0188:
0189:                private Attribute(final String name, final String defValue,
0190:                        final boolean inherit,
0191:                        final ShorthandPropertyExpander expander) {
0192:                    this (name, defValue, inherit, null, expander);
0193:                }
0194:
0195:                private Attribute(final String name, final String defValue,
0196:                        final boolean inherit,
0197:                        final PropertyValueConverter converter,
0198:                        final ShorthandPropertyExpander expander) {
0199:                    this .name = name;
0200:                    this .defValue = defValue;
0201:                    this .inherit = inherit;
0202:                    this .converter = converter;
0203:                    this .expander = expander;
0204:                }
0205:
0206:                public String getDefaultValue() {
0207:                    return defValue;
0208:                }
0209:
0210:                public boolean isInherited() {
0211:                    return inherit;
0212:                }
0213:
0214:                public String toString() {
0215:                    return name;
0216:                }
0217:
0218:                /**
0219:                 * Returns the converter to translate property values from
0220:                 * {@link StyleConstants} to <code>CSS</code> and vice versa.
0221:                 *
0222:                 * @return the converter.
0223:                 */
0224:                PropertyValueConverter getConverter() {
0225:                    return converter;
0226:                }
0227:
0228:                /**
0229:                 * Returns the expander to convert a shorthand property to its
0230:                 * distinct parts.
0231:                 *
0232:                 * @return the expander.
0233:                 */
0234:                ShorthandPropertyExpander getExpander() {
0235:                    return expander;
0236:                }
0237:            }
0238:
0239:            interface ShorthandPropertyExpander {
0240:                void parseAndExpandProperty(MutableAttributeSet attrs,
0241:                        String value);
0242:            }
0243:
0244:            /**
0245:             * The implementation is based on CSS1 spec for
0246:             * <a href="http://www.w3.org/TR/CSS1#background">'background'</a>.
0247:             */
0248:            static final class BackgroundExpander implements 
0249:                    ShorthandPropertyExpander {
0250:
0251:                public void parseAndExpandProperty(
0252:                        final MutableAttributeSet attrs, final String value) {
0253:                    ColorProperty color = null;
0254:                    ImageValue image = null;
0255:                    BackgroundRepeat repeat = null;
0256:                    BackgroundAttachment attachment = null;
0257:                    BackgroundPosition[] position = new BackgroundPosition[2];
0258:                    String[] parts = split(value.trim());
0259:                    if (parts == null) {
0260:                        return;
0261:                    }
0262:                    for (int i = 0; i < parts.length; i++) {
0263:                        if (color == null) {
0264:                            color = (ColorProperty) BackgroundColor.factory
0265:                                    .toCSS(parts[i]);
0266:                            if (color != null) {
0267:                                continue;
0268:                            }
0269:                        }
0270:
0271:                        if (image == null) {
0272:                            image = (ImageValue) ImageValue.NONE
0273:                                    .toCSS(parts[i]);
0274:                            if (image != null) {
0275:                                continue;
0276:                            }
0277:                        }
0278:
0279:                        if (repeat == null) {
0280:                            repeat = (BackgroundRepeat) BackgroundRepeat.factory
0281:                                    .toCSS(parts[i]);
0282:                            if (repeat != null) {
0283:                                continue;
0284:                            }
0285:                        }
0286:
0287:                        if (attachment == null) {
0288:                            attachment = (BackgroundAttachment) BackgroundAttachment.factory
0289:                                    .toCSS(parts[i]);
0290:                            if (attachment != null) {
0291:                                continue;
0292:                            }
0293:                        }
0294:
0295:                        if (position[0] == null) {
0296:                            position[0] = (BackgroundPosition) Attribute.BACKGROUND_POSITION
0297:                                    .getConverter().toCSS(parts[i]);
0298:                            if (position[0] != null) {
0299:                                continue;
0300:                            }
0301:                        }
0302:
0303:                        if (position[1] == null) {
0304:                            position[1] = (BackgroundPosition) Attribute.BACKGROUND_POSITION
0305:                                    .getConverter().toCSS(parts[i]);
0306:                            if (position[1] != null) {
0307:                                continue;
0308:                            }
0309:                        }
0310:
0311:                        return;
0312:                    }
0313:
0314:                    final PropertyValueConverter bgPosConverter = Attribute.BACKGROUND_POSITION
0315:                            .getConverter();
0316:                    // Attribute.BACKGROUND_POSITION.getDefaultValue() must be "0% 0%"
0317:                    PropertyValueConverter bgPosition = position[0] == null ? bgPosConverter
0318:                            .toCSS("0% 0%"/*Attribute.BACKGROUND_POSITION
0319:                                                                            .getDefaultValue()*/)
0320:                            : (position[1] != null ? bgPosConverter
0321:                                    .toCSS(position[0] + " " + position[1])
0322:                                    : position[0]);
0323:
0324:                    if (bgPosition == null) {
0325:                        return;
0326:                    }
0327:
0328:                    attrs.addAttribute(Attribute.BACKGROUND_COLOR,
0329:                            color != null ? color : BackgroundColor.factory);
0330:                    attrs.addAttribute(Attribute.BACKGROUND_IMAGE,
0331:                            image != null ? image : ImageValue.NONE);
0332:                    attrs.addAttribute(Attribute.BACKGROUND_REPEAT,
0333:                            repeat != null ? repeat : BackgroundRepeat.factory);
0334:                    attrs.addAttribute(Attribute.BACKGROUND_ATTACHMENT,
0335:                            attachment != null ? attachment
0336:                                    : BackgroundAttachment.factory);
0337:                    attrs.addAttribute(Attribute.BACKGROUND_POSITION,
0338:                            bgPosition);
0339:                }
0340:
0341:                private static String[] split(final String value) {
0342:                    if (value.indexOf("url(") != -1) {
0343:                        Matcher matcher = ListStyleExpander.URL_PATTERN
0344:                                .matcher(value);
0345:                        if (!matcher.find()) {
0346:                            return null;
0347:                        }
0348:                        final int urlStart = matcher.start();
0349:                        final int urlEnd = matcher.end();
0350:                        final String url = value.substring(urlStart, urlEnd);
0351:                        final String rest = (value.substring(0, urlStart) + value
0352:                                .substring(urlEnd)).trim();
0353:                        if (rest.length() != 0) {
0354:                            if (rest.indexOf("url(") != -1) {
0355:                                return null;
0356:                            }
0357:
0358:                            String[] parts = BorderColor.SPLIT_PATTERN
0359:                                    .split(rest);
0360:                            String[] result = new String[parts.length + 1];
0361:                            System.arraycopy(parts, 0, result, 0, parts.length);
0362:                            result[parts.length] = url;
0363:
0364:                            return result;
0365:                        }
0366:
0367:                        return new String[] { url };
0368:                    }
0369:
0370:                    return BorderColor.SPLIT_PATTERN.split(value);
0371:                }
0372:            }
0373:
0374:            /**
0375:             * Describes converters of attribute values from {@link StyleConstants}
0376:             * notation to CSS and vice versa.
0377:             * <p>
0378:             * The convertion may be not single-valued transformation, i.e.
0379:             * the following expression is generally speaking <code>false</code>:
0380:             * <pre>
0381:             * value.equals(toCSS(value).fromCSS())
0382:             * </pre>
0383:             * <p>
0384:             * The result of convertion to CSS notation is instance of
0385:             * <code>PropertyValueConverter</code>. This object holds values for
0386:             * both notations. It returns its <code>StyleConstants</code> representation
0387:             * from {@link CSS.PropertyValueConverter#fromCSS() fromCSS()} method.
0388:             * The CSS-styled value should be returned from <code>toString()</code>.
0389:             */
0390:            interface PropertyValueConverter {
0391:                /**
0392:                 * Converts an attribute value from <code>StyleConstants</code> to
0393:                 * <code>CSS</code>.
0394:                 *
0395:                 * @param value the value to convert.
0396:                 * @return wrapper object which holds both values.
0397:                 */
0398:                PropertyValueConverter toCSS(Object value);
0399:
0400:                /**
0401:                 * Returns the <code>StyleConstants</code> representation of a CSS
0402:                 * property value stored.
0403:                 *
0404:                 * @return the converted value.
0405:                 */
0406:                Object fromCSS();
0407:            }
0408:
0409:            interface RelativeValueResolver {
0410:                Object getComputedValue(View view);
0411:            }
0412:
0413:            /**
0414:             * The implementation is based on CSS1 spec for
0415:             * <a href="http://www.w3.org/TR/CSS1#background-attachment">'background-attachment'</a>.
0416:             */
0417:            static final class BackgroundAttachment extends FixedSetValues {
0418:                static final BackgroundAttachment factory = new BackgroundAttachment(
0419:                        0);
0420:
0421:                private static final String[] VALID_VALUES = { "scroll",
0422:                        "fixed" };
0423:                private static final BackgroundAttachment[] VALUE_HOLDERS = new BackgroundAttachment[VALID_VALUES.length];
0424:
0425:                static {
0426:                    VALUE_HOLDERS[0] = factory;
0427:                }
0428:
0429:                private BackgroundAttachment(final int index) {
0430:                    super (index);
0431:                }
0432:
0433:                String[] getValidValues() {
0434:                    return VALID_VALUES;
0435:                }
0436:
0437:                PropertyValueConverter[] getValueHolders() {
0438:                    return VALUE_HOLDERS;
0439:                }
0440:
0441:                PropertyValueConverter createValueHolder(final int valueIndex) {
0442:                    return new BackgroundAttachment(valueIndex);
0443:                }
0444:            }
0445:
0446:            /**
0447:             * The implementation is based on CSS1 spec for
0448:             * <a href="http://www.w3.org/TR/CSS1#background-color">'background-color'</a>.
0449:             */
0450:            static final class BackgroundColor extends ColorProperty {
0451:                static final BackgroundColor factory = new BackgroundColor();
0452:
0453:                private BackgroundColor() {
0454:                    super ("transparent", null);
0455:                }
0456:
0457:                public PropertyValueConverter toCSS(final Object value) {
0458:                    return "transparent".equals(value) ? factory : super 
0459:                            .toCSS(value);
0460:                }
0461:            }
0462:
0463:            /**
0464:             * The implementation is based on CSS1 spec for
0465:             * <a href="http://www.w3.org/TR/CSS1#background-image">'background-image'</a>
0466:             * and
0467:             * <a href="http://www.w3.org/TR/CSS1#list-style-image">'list-style-image'</a>.
0468:             */
0469:            static final class ImageValue implements  PropertyValueConverter {
0470:                static final ImageValue NONE = new ImageValue();
0471:
0472:                private final String value;
0473:                private final String path;
0474:                private BackgroundImageLoader imageLoader;
0475:
0476:                private final List listeners = new ArrayList();
0477:
0478:                private ImageValue() {
0479:                    this ("none", null);
0480:                }
0481:
0482:                private ImageValue(final String value, final String path) {
0483:                    this .value = value;
0484:                    this .path = path;
0485:                }
0486:
0487:                public PropertyValueConverter toCSS(final Object value) {
0488:                    if ("none".equals(value)) {
0489:                        return NONE;
0490:                    }
0491:
0492:                    String path = extractPath((String) value);
0493:                    return path != null ? new ImageValue((String) value, path)
0494:                            : null;
0495:                }
0496:
0497:                public Object fromCSS() {
0498:                    return null;
0499:                }
0500:
0501:                public String toString() {
0502:                    return value;
0503:                }
0504:
0505:                void loadImage(final URL base) {
0506:                    if (path != null && imageLoader == null) {
0507:
0508:                        final URL url = HTML.resolveURL(path, base);
0509:                        imageLoader = new BackgroundImageLoader(url, true, -1,
0510:                                -1) {
0511:                            protected void onReady() {
0512:                                super .onReady();
0513:                                notifyViews();
0514:                            }
0515:                        };
0516:                    }
0517:                }
0518:
0519:                int getWidth() {
0520:                    return imageLoader != null ? imageLoader.getWidth() : -1;
0521:                }
0522:
0523:                int getHeight() {
0524:                    return imageLoader != null ? imageLoader.getHeight() : -1;
0525:                }
0526:
0527:                Image getImage() {
0528:                    return imageLoader != null && imageLoader.isReady() ? imageLoader
0529:                            .getImage()
0530:                            : null;
0531:                }
0532:
0533:                synchronized void addListener(final ViewUpdater viewUpdater) {
0534:                    if (imageLoader != null && !imageLoader.isReady()
0535:                            && !imageLoader.isError()) {
0536:
0537:                        listeners.add(viewUpdater);
0538:                    }
0539:                }
0540:
0541:                private static String extractPath(final String url) {
0542:                    if (!(url.startsWith("url(") && url.endsWith(")"))) {
0543:                        return null;
0544:                    }
0545:                    char c = url.charAt(4);
0546:                    boolean quoted = c == '\'' || c == '"';
0547:                    if (quoted && url.charAt(url.length() - 2) != c) {
0548:                        return null;
0549:                    }
0550:                    return url.substring(4 + (quoted ? 1 : 0), url.length() - 1
0551:                            - (quoted ? 1 : 0));
0552:                }
0553:
0554:                private synchronized void notifyViews() {
0555:                    Iterator it = listeners.iterator();
0556:                    while (it.hasNext()) {
0557:                        ((ViewUpdater) it.next()).updateView();
0558:                    }
0559:                    listeners.clear();
0560:                }
0561:            }
0562:
0563:            /**
0564:             * The implementation is based on CSS1 spec for
0565:             * <a href="http://www.w3.org/TR/CSS1#background-position">'background-position'</a>.
0566:             */
0567:            static final class BackgroundPosition implements 
0568:                    PropertyValueConverter {
0569:                private static final String[] KEYWORDS = new String[] { "top",
0570:                        "center", "bottom", "left", "right" };
0571:
0572:                private static final String[] KEYWORD_VALUE_STRS = new String[] {
0573:                        "0%", "50%", "100%" };
0574:                private static final FloatValue[] KEYWORD_VALUES = new FloatValue[3];
0575:
0576:                private static final int KV_0 = 0;
0577:                private static final int KV_50 = 1;
0578:                private static final int KV_100 = 2;
0579:
0580:                private String theValue;
0581:                private FloatValue horz;
0582:                private FloatValue vert;
0583:
0584:                BackgroundPosition() {
0585:                    this (null, null, null);
0586:                }
0587:
0588:                private BackgroundPosition(final String theValue, final int hz,
0589:                        final int vt) {
0590:                    this (theValue, getKeywordValue(hz), getKeywordValue(vt));
0591:                }
0592:
0593:                private BackgroundPosition(final String theValue,
0594:                        final FloatValue horz, final FloatValue vert) {
0595:                    this .theValue = theValue;
0596:                    this .horz = horz;
0597:                    this .vert = vert;
0598:                }
0599:
0600:                public PropertyValueConverter toCSS(final Object value) {
0601:                    return parseValue((String) value);
0602:                }
0603:
0604:                public Object fromCSS() {
0605:                    return null;
0606:                }
0607:
0608:                public String toString() {
0609:                    return theValue;
0610:                }
0611:
0612:                private PropertyValueConverter parseValue(final String value) {
0613:                    String[] parts = value.split("\\s+");
0614:                    if (parts.length > 2) {
0615:                        return null;
0616:                    }
0617:
0618:                    if (parts.length == 1) {
0619:                        int index = getKeywordIndex(parts[0]);
0620:                        if (index != -1) {
0621:                            switch (index) {
0622:                            case 0: // top
0623:                                horz = getKeywordValue(KV_50);
0624:                                vert = getKeywordValue(KV_0);
0625:                                break;
0626:
0627:                            case 1: // center
0628:                                horz = getKeywordValue(KV_50);
0629:                                vert = getKeywordValue(KV_50);
0630:                                break;
0631:
0632:                            case 2: // bottom
0633:                                horz = getKeywordValue(KV_50);
0634:                                vert = getKeywordValue(KV_100);
0635:                                break;
0636:
0637:                            case 3: // left
0638:                                horz = getKeywordValue(KV_0);
0639:                                vert = getKeywordValue(KV_50);
0640:                                break;
0641:
0642:                            case 4: // right
0643:                                horz = getKeywordValue(KV_100);
0644:                                vert = getKeywordValue(KV_50);
0645:                                break;
0646:
0647:                            default:
0648:                                return null;
0649:                            }
0650:                            return new BackgroundPosition(value, horz, vert);
0651:                        }
0652:                        horz = (FloatValue) FloatValue.factory.toCSS(parts[0]);
0653:                        return horz == null ? null : new BackgroundPosition(
0654:                                value, horz, getKeywordValue(KV_50));
0655:                    }
0656:
0657:                    int hzIndex = getKeywordIndex(parts[0]);
0658:                    int vtIndex = getKeywordIndex(parts[1]);
0659:                    if (hzIndex == -1 && vtIndex != -1 || hzIndex != -1
0660:                            && vtIndex == -1) {
0661:
0662:                        // Combinations of keywords and length or percentage values
0663:                        // are not allowed according to CSS1 spec, 'background-position'.
0664:                        return null;
0665:                    }
0666:
0667:                    if (hzIndex == -1 && vtIndex == -1) {
0668:                        horz = (FloatValue) FloatValue.factory.toCSS(parts[0]);
0669:                        vert = (FloatValue) FloatValue.factory.toCSS(parts[1]);
0670:                        return horz == null || vert == null ? null
0671:                                : new BackgroundPosition(value, horz, vert);
0672:                    }
0673:
0674:                    if (isHorizontalIndex(hzIndex) && isVerticalIndex(vtIndex)) {
0675:                        return new BackgroundPosition(value,
0676:                                kwToValue(hzIndex), kwToValue(vtIndex));
0677:                    }
0678:
0679:                    if (isHorizontalIndex(vtIndex) && isVerticalIndex(hzIndex)) {
0680:                        return new BackgroundPosition(value,
0681:                                kwToValue(vtIndex), kwToValue(hzIndex));
0682:                    }
0683:
0684:                    return null;
0685:                }
0686:
0687:                private static int getKeywordIndex(final String kw) {
0688:                    for (int i = 0; i < KEYWORDS.length; i++) {
0689:                        if (KEYWORDS[i].equals(kw)) {
0690:                            return i;
0691:                        }
0692:                    }
0693:                    return -1;
0694:                }
0695:
0696:                private static boolean isHorizontalIndex(final int kwIndex) {
0697:                    return kwIndex == 1 || kwIndex == 3 || kwIndex == 4;
0698:                }
0699:
0700:                private static boolean isVerticalIndex(final int kwIndex) {
0701:                    return kwIndex <= 2;
0702:                }
0703:
0704:                private static int kwToValue(final int kwIndex) {
0705:                    switch (kwIndex) {
0706:                    case 0: // top
0707:                    case 3: // left
0708:                        return 0;
0709:
0710:                    case 1: // center
0711:                        return 1;
0712:
0713:                    case 2: // bottom
0714:                    case 4: // right
0715:                        return 2;
0716:
0717:                    default:
0718:                        return -1;
0719:                    }
0720:                }
0721:
0722:                private static FloatValue getKeywordValue(final int valueIndex) {
0723:                    if (KEYWORD_VALUES[valueIndex] == null) {
0724:                        // valueIndex must be in the range 0..2
0725:                        KEYWORD_VALUES[valueIndex] = new FloatValue(
0726:                                KEYWORD_VALUE_STRS[valueIndex],
0727:                                50 * valueIndex,
0728:                                Length.RELATIVE_UNITS_PERCENTAGE);
0729:                    }
0730:                    return KEYWORD_VALUES[valueIndex];
0731:                }
0732:            }
0733:
0734:            /**
0735:             * The implementation is based on CSS1 spec for
0736:             * <a href="http://www.w3.org/TR/CSS1#background-repeat">'background-repeat'</a>.
0737:             */
0738:            static final class BackgroundRepeat extends FixedSetValues {
0739:                static final BackgroundRepeat factory = new BackgroundRepeat(0);
0740:
0741:                static final int REPEAT = 0;
0742:                static final int REPEAT_X = 1;
0743:                static final int REPEAT_Y = 2;
0744:                static final int NO_REPEAT = 3;
0745:
0746:                private static final String[] VALID_VALUES = { "repeat",
0747:                        "repeat-x", "repeat-y", "no-repeat" };
0748:                private static final BackgroundRepeat[] VALUE_HOLDERS = new BackgroundRepeat[VALID_VALUES.length];
0749:
0750:                static {
0751:                    VALUE_HOLDERS[0] = factory;
0752:                }
0753:
0754:                private BackgroundRepeat(final int index) {
0755:                    super (index);
0756:                }
0757:
0758:                String[] getValidValues() {
0759:                    return VALID_VALUES;
0760:                }
0761:
0762:                PropertyValueConverter[] getValueHolders() {
0763:                    return VALUE_HOLDERS;
0764:                }
0765:
0766:                PropertyValueConverter createValueHolder(final int valueIndex) {
0767:                    return new BackgroundRepeat(valueIndex);
0768:                }
0769:            }
0770:
0771:            /**
0772:             * The implementation is based on CSS1 spec for
0773:             * <a href="http://www.w3.org/TR/CSS1#border-color">'border-color'</a>.
0774:             */
0775:            static final class BorderColor implements  PropertyValueConverter {
0776:                static final Pattern SPLIT_PATTERN = Pattern
0777:                        .compile("(?<!,)\\s+");
0778:
0779:                private static final PropertyValueConverter converter = ColorProperty.factory;
0780:
0781:                private final ColorProperty[] colors;
0782:
0783:                private String value;
0784:
0785:                BorderColor() {
0786:                    this (null, null);
0787:                }
0788:
0789:                BorderColor(final ColorProperty[] colors) {
0790:                    this (colors, getDeclaration(colors));
0791:                }
0792:
0793:                BorderColor(final ColorProperty[] colors, final String value) {
0794:                    this .colors = colors;
0795:                    this .value = value;
0796:                }
0797:
0798:                public PropertyValueConverter toCSS(final Object value) {
0799:                    final ColorProperty[] cp = new ColorProperty[4];
0800:                    final String[] values = SPLIT_PATTERN.split((String) value);
0801:                    for (int i = 0; i < cp.length; i++) {
0802:                        if (i < values.length) {
0803:                            cp[i] = (ColorProperty) converter.toCSS(values[i]);
0804:                            if (cp[i] == null) {
0805:                                return null;
0806:                            }
0807:                        } else {
0808:                            cp[i] = cp[i >= 3 ? 1 : 0];
0809:                        }
0810:                    }
0811:                    return new BorderColor(cp, (String) value);
0812:                }
0813:
0814:                public Object fromCSS() {
0815:                    return null;
0816:                }
0817:
0818:                public String toString() {
0819:                    return value;
0820:                }
0821:
0822:                void setSideColor(final int sideIndex, final ColorProperty color) {
0823:                    colors[sideIndex] = color;
0824:                    value = getDeclaration(colors);
0825:                }
0826:
0827:                ColorProperty getSideColor(final int sideIndex) {
0828:                    return colors[sideIndex];
0829:                }
0830:
0831:                private static String getDeclaration(
0832:                        final ColorProperty[] colors) {
0833:                    StringBuffer result = new StringBuffer();
0834:                    for (int i = 0; i < colors.length; i++) {
0835:                        if (i > 0) {
0836:                            result.append(' ');
0837:                        }
0838:                        result.append(colors[i] == null ? "white" : colors[i]
0839:                                .toString());
0840:                    }
0841:                    return result.toString();
0842:                }
0843:            }
0844:
0845:            /**
0846:             * The implementation is based on CSS1 spec for
0847:             * <a href="http://www.w3.org/TR/CSS1#border-style">'border-style'</a>.
0848:             * <p>This class stores four {@link BorderStyleValue} for each side of the box.
0849:             */
0850:            static final class BorderStyle implements  PropertyValueConverter {
0851:                private static final PropertyValueConverter converter = BorderStyleValue.factory;
0852:
0853:                private final BorderStyleValue[] styles;
0854:
0855:                private String value;
0856:
0857:                BorderStyle() {
0858:                    this (null, null);
0859:                }
0860:
0861:                BorderStyle(final BorderStyleValue[] styles) {
0862:                    this (styles, getDeclaration(styles));
0863:                }
0864:
0865:                BorderStyle(final BorderStyleValue[] styles, final String value) {
0866:                    this .styles = styles;
0867:                    this .value = value;
0868:                }
0869:
0870:                public PropertyValueConverter toCSS(final Object value) {
0871:                    final BorderStyleValue[] bs = new BorderStyleValue[4];
0872:                    final String[] values = ((String) value).split("\\s+");
0873:                    for (int i = 0; i < bs.length; i++) {
0874:                        if (i < values.length) {
0875:                            bs[i] = (BorderStyleValue) converter
0876:                                    .toCSS(values[i]);
0877:                            if (bs[i] == null) {
0878:                                return null;
0879:                            }
0880:                        } else {
0881:                            bs[i] = bs[i >= 3 ? 1 : 0];
0882:                        }
0883:                    }
0884:                    return new BorderStyle(bs, (String) value);
0885:                }
0886:
0887:                public Object fromCSS() {
0888:                    return null;
0889:                }
0890:
0891:                public String toString() {
0892:                    return value;
0893:                }
0894:
0895:                void setSideStyle(final int sideIndex,
0896:                        final BorderStyleValue style) {
0897:                    styles[sideIndex] = style;
0898:                    value = getDeclaration(styles);
0899:                }
0900:
0901:                BorderStyleValue getSideStyle(final int sideIndex) {
0902:                    return styles[sideIndex] != null ? styles[sideIndex]
0903:                            : (BorderStyleValue) converter;
0904:                }
0905:
0906:                private static String getDeclaration(
0907:                        final BorderStyleValue[] styles) {
0908:                    StringBuffer result = new StringBuffer();
0909:                    for (int i = 0; i < styles.length; i++) {
0910:                        if (i > 0) {
0911:                            result.append(' ');
0912:                        }
0913:                        result.append(styles[i] == null ? "none" : styles[i]
0914:                                .toString());
0915:                    }
0916:                    return result.toString();
0917:                }
0918:            }
0919:
0920:            /**
0921:             * The implementation is based on CSS1 spec for
0922:             * <a href="http://www.w3.org/TR/CSS1#border-style">'border-style'</a>.
0923:             * <p>Defines the particular value for a side of the box.
0924:             */
0925:            static final class BorderStyleValue extends FixedSetValues {
0926:                static final int NONE = 0;
0927:                static final int SOLID = 1;
0928:                static final int DOTTED = 2;
0929:                static final int DASHED = 3;
0930:                static final int DOUBLE = 4;
0931:                static final int GROOVE = 5;
0932:                static final int RIDGE = 6;
0933:                static final int INSET = 7;
0934:                static final int OUTSET = 8;
0935:
0936:                static final BorderStyleValue factory = new BorderStyleValue(0);
0937:
0938:                private static final String[] VALID_VALUES = { "none", "solid",
0939:                        "dotted", "dashed", "double", "groove", "ridge",
0940:                        "inset", "outset" };
0941:                private static final BorderStyleValue[] VALUE_HOLDERS = new BorderStyleValue[VALID_VALUES.length];
0942:
0943:                static {
0944:                    VALUE_HOLDERS[0] = factory;
0945:                }
0946:
0947:                private BorderStyleValue(final int index) {
0948:                    super (index);
0949:                }
0950:
0951:                String[] getValidValues() {
0952:                    return VALID_VALUES;
0953:                }
0954:
0955:                PropertyValueConverter[] getValueHolders() {
0956:                    return VALUE_HOLDERS;
0957:                }
0958:
0959:                PropertyValueConverter createValueHolder(final int valueIndex) {
0960:                    return new BorderStyleValue(valueIndex);
0961:                }
0962:            }
0963:
0964:            /**
0965:             * The implementation is based on CSS1.
0966:             * It defines the particular value for a side of the box:
0967:             * <ul>
0968:             * <li><a href="http://www.w3.org/TR/CSS1#border-width-top">'border-width-top'</a>
0969:             * <li><a href="http://www.w3.org/TR/CSS1#border-width-right">'border-width-right'</a>
0970:             * <li><a href="http://www.w3.org/TR/CSS1#border-width-bottom">'border-width-bottom'</a>
0971:             * <li><a href="http://www.w3.org/TR/CSS1#border-width-left">'border-width-left'</a>
0972:             * </ul>
0973:             */
0974:            static final class BorderWidthValue extends FloatValue {
0975:                static final BorderWidthValue factory = new BorderWidthValue(
0976:                        "medium", 3);
0977:
0978:                private static final BorderWidthValue[] WIDTHS = {
0979:                        new BorderWidthValue("thin", 1), factory,
0980:                        new BorderWidthValue("thick", 5) };
0981:
0982:                private BorderWidthValue(final String value,
0983:                        final float theValue) {
0984:                    super (value, theValue, RELATIVE_UNITS_UNDEFINED);
0985:                }
0986:
0987:                private BorderWidthValue(final String value,
0988:                        final float theValue, final int rUnits) {
0989:                    super (value, theValue, rUnits);
0990:                }
0991:
0992:                public PropertyValueConverter toCSS(final Object value) {
0993:                    for (int i = 0; i < WIDTHS.length; i++) {
0994:                        if (WIDTHS[i].sValue.equals(value)) {
0995:                            return WIDTHS[i];
0996:                        }
0997:                    }
0998:
0999:                    return super .toCSS(value);
1000:                }
1001:
1002:                PropertyValueConverter create(final String strValue,
1003:                        final float theValue, final int rUnits) {
1004:                    return theValue < 0 || rUnits == RELATIVE_UNITS_PERCENTAGE ? null
1005:                            : new BorderWidthValue(strValue, theValue, rUnits);
1006:                }
1007:            }
1008:
1009:            /**
1010:             * The implementation is based on CSS1 spec for
1011:             * <ul>
1012:             * <li><a href="http://www.w3.org/TR/CSS1#border-top">'border-top'</a>
1013:             * <li><a href="http://www.w3.org/TR/CSS1#border-right">'border-right'</a>
1014:             * <li><a href="http://www.w3.org/TR/CSS1#border-bottom">'border-bottom'</a>
1015:             * <li><a href="http://www.w3.org/TR/CSS1#border-left">'border-left'</a>
1016:             * </ul>
1017:             */
1018:            static final class BorderSideExpander implements 
1019:                    ShorthandPropertyExpander {
1020:                static final int TOP_SIDE = 0;
1021:                static final int RIGHT_SIDE = 1;
1022:                static final int BOTTOM_SIDE = 2;
1023:                static final int LEFT_SIDE = 3;
1024:
1025:                private final int sideIndex;
1026:                private final Attribute widthKey;
1027:
1028:                BorderSideExpander(final int sideIndex, final Attribute widthKey) {
1029:                    this .sideIndex = sideIndex;
1030:                    this .widthKey = widthKey;
1031:                }
1032:
1033:                public void parseAndExpandProperty(
1034:                        final MutableAttributeSet attrs, final String value) {
1035:                    BorderStyleValue style = null;
1036:                    ColorProperty color = null;
1037:                    BorderWidthValue width = null;
1038:
1039:                    final String[] parts = BorderColor.SPLIT_PATTERN
1040:                            .split(value);
1041:                    for (int i = 0; i < parts.length; i++) {
1042:                        if (style == null) {
1043:                            style = (BorderStyleValue) BorderStyle.converter
1044:                                    .toCSS(parts[i]);
1045:                            if (style != null) {
1046:                                continue;
1047:                            }
1048:                        }
1049:
1050:                        if (color == null) {
1051:                            color = (ColorProperty) ColorProperty.factory
1052:                                    .toCSS(parts[i]);
1053:                            if (color != null) {
1054:                                continue;
1055:                            }
1056:                        }
1057:
1058:                        if (width == null) {
1059:                            width = (BorderWidthValue) BorderWidthValue.factory
1060:                                    .toCSS(parts[i]);
1061:                            if (width != null) {
1062:                                continue;
1063:                            }
1064:                        }
1065:
1066:                        // Contains an unknown value - ignore the entire declaration
1067:                        return;
1068:                    }
1069:
1070:                    if (style != null) {
1071:                        final BorderStyle styleValue = (BorderStyle) attrs
1072:                                .getAttribute(Attribute.BORDER_STYLE);
1073:                        if (styleValue != null) {
1074:                            styleValue.setSideStyle(sideIndex, style);
1075:                        } else {
1076:                            BorderStyleValue[] styles = new BorderStyleValue[4];
1077:                            styles[sideIndex] = style;
1078:                            attrs.addAttribute(Attribute.BORDER_STYLE,
1079:                                    new BorderStyle(styles));
1080:                        }
1081:                    }
1082:
1083:                    if (color != null) {
1084:                        final BorderColor colorValue = (BorderColor) attrs
1085:                                .getAttribute(Attribute.BORDER_COLOR);
1086:                        if (colorValue != null) {
1087:                            colorValue.setSideColor(sideIndex, color);
1088:                        } else {
1089:                            ColorProperty[] colors = new ColorProperty[4];
1090:                            colors[sideIndex] = color;
1091:                            attrs.addAttribute(Attribute.BORDER_COLOR,
1092:                                    new BorderColor(colors));
1093:                        }
1094:                    }
1095:
1096:                    if (width != null) {
1097:                        attrs.addAttribute(widthKey, width);
1098:                    }
1099:                }
1100:            }
1101:
1102:            /**
1103:             * The implementation is based on CSS1 spec for
1104:             * <a href="http://www.w3.org/TR/CSS1#border">'border'</a>.
1105:             */
1106:            static final class BorderExpander implements 
1107:                    ShorthandPropertyExpander {
1108:                public void parseAndExpandProperty(
1109:                        final MutableAttributeSet attrs, final String value) {
1110:                    BorderStyleValue style = null;
1111:                    ColorProperty color = null;
1112:                    BorderWidthValue width = null;
1113:
1114:                    final String[] parts = BorderColor.SPLIT_PATTERN
1115:                            .split(value);
1116:                    for (int i = 0; i < parts.length; i++) {
1117:                        if (style == null) {
1118:                            style = (BorderStyleValue) BorderStyle.converter
1119:                                    .toCSS(parts[i]);
1120:                            if (style != null) {
1121:                                continue;
1122:                            }
1123:                        }
1124:
1125:                        if (color == null) {
1126:                            color = (ColorProperty) ColorProperty.factory
1127:                                    .toCSS(parts[i]);
1128:                            if (color != null) {
1129:                                continue;
1130:                            }
1131:                        }
1132:
1133:                        if (width == null) {
1134:                            width = (BorderWidthValue) BorderWidthValue.factory
1135:                                    .toCSS(parts[i]);
1136:                            if (width != null) {
1137:                                continue;
1138:                            }
1139:                        }
1140:
1141:                        // Contains an unknown value - ignore the entire declaration
1142:                        return;
1143:                    }
1144:
1145:                    if (style != null) {
1146:                        attrs
1147:                                .addAttribute(Attribute.BORDER_STYLE,
1148:                                        new BorderStyle(new BorderStyleValue[] {
1149:                                                style, style, style, style },
1150:                                                style.toString()));
1151:                    }
1152:
1153:                    if (color != null) {
1154:                        attrs
1155:                                .addAttribute(Attribute.BORDER_COLOR,
1156:                                        new BorderColor(new ColorProperty[] {
1157:                                                color, color, color, color },
1158:                                                color.toString()));
1159:                    }
1160:
1161:                    if (width != null) {
1162:                        attrs.addAttribute(Attribute.BORDER_TOP_WIDTH, width);
1163:                        attrs.addAttribute(Attribute.BORDER_RIGHT_WIDTH, width);
1164:                        attrs
1165:                                .addAttribute(Attribute.BORDER_BOTTOM_WIDTH,
1166:                                        width);
1167:                        attrs.addAttribute(Attribute.BORDER_LEFT_WIDTH, width);
1168:                    }
1169:                }
1170:            }
1171:
1172:            /**
1173:             * The implementation is based on CSS1 spec for
1174:             * <a href="http://www.w3.org/TR/CSS1#color">'color'</a>.
1175:             * @see <a href="http://www.w3.org/TR/CSS1#color-units">Color Units</a>.
1176:             */
1177:            static class ColorProperty implements  PropertyValueConverter {
1178:                static final ColorProperty factory = new ColorProperty();
1179:
1180:                private static final char[] zeros = { '0', '0', '0', '0', '0' };
1181:                private static final Map colorMap = new HashMap();
1182:                private static final Pattern RGB_SEPARATOR = Pattern
1183:                        .compile(",");
1184:
1185:                private final Color color;
1186:                private final String cssValue;
1187:
1188:                static {
1189:                    Color color;
1190:
1191:                    color = Color.CYAN;
1192:                    colorMap.put("aqua", color);
1193:                    colorMap.put("00ffff", color);
1194:
1195:                    color = Color.BLACK;
1196:                    colorMap.put("black", color);
1197:                    colorMap.put("000000", color);
1198:
1199:                    color = Color.BLUE;
1200:                    colorMap.put("blue", color);
1201:                    colorMap.put("0000ff", color);
1202:
1203:                    color = Color.MAGENTA;
1204:                    colorMap.put("fuchsia", color);
1205:                    colorMap.put("ff00ff", color);
1206:
1207:                    color = Color.GRAY;
1208:                    colorMap.put("gray", color);
1209:                    colorMap.put("808080", color);
1210:
1211:                    color = new Color(0, 128, 0);
1212:                    colorMap.put("green", color);
1213:                    colorMap.put("008000", color);
1214:
1215:                    color = Color.GREEN;
1216:                    colorMap.put("lime", color);
1217:                    colorMap.put("00ff00", color);
1218:
1219:                    color = new Color(128, 0, 0);
1220:                    colorMap.put("maroon", color);
1221:                    colorMap.put("800000", color);
1222:
1223:                    color = new Color(0, 0, 128);
1224:                    colorMap.put("navy", color);
1225:                    colorMap.put("000080", color);
1226:
1227:                    color = new Color(128, 128, 0);
1228:                    colorMap.put("olive", color);
1229:                    colorMap.put("808000", color);
1230:
1231:                    color = new Color(128, 0, 128);
1232:                    colorMap.put("purple", color);
1233:                    colorMap.put("800080", color);
1234:
1235:                    color = Color.RED;
1236:                    colorMap.put("red", color);
1237:                    colorMap.put("ff0000", color);
1238:
1239:                    color = Color.LIGHT_GRAY;
1240:                    colorMap.put("silver", color);
1241:                    colorMap.put("c0c0c0", color);
1242:
1243:                    color = new Color(0, 128, 128);
1244:                    colorMap.put("teal", color);
1245:                    colorMap.put("008080", color);
1246:
1247:                    color = Color.WHITE;
1248:                    colorMap.put("white", color);
1249:                    colorMap.put("ffffff", color);
1250:
1251:                    color = Color.YELLOW;
1252:                    colorMap.put("yellow", color);
1253:                    colorMap.put("ffff00", color);
1254:                }
1255:
1256:                ColorProperty(final String cssValue, final Color color) {
1257:                    this .cssValue = cssValue;
1258:                    this .color = color;
1259:                }
1260:
1261:                private ColorProperty() {
1262:                    this (null, null);
1263:                }
1264:
1265:                private ColorProperty(final Color color) {
1266:                    this (colorToString(color), color);
1267:                }
1268:
1269:                public PropertyValueConverter toCSS(final Object value) {
1270:                    if (value instanceof  String) {
1271:                        final String cssValue = (String) value;
1272:                        Color c = parseRGB(cssValue);
1273:                        if (c == null) {
1274:                            c = stringToColor(cssValue);
1275:                        }
1276:                        return c != null ? new ColorProperty(cssValue, c)
1277:                                : null;
1278:                    }
1279:                    return new ColorProperty((Color) value);
1280:                }
1281:
1282:                public Object fromCSS() {
1283:                    return color;
1284:                }
1285:
1286:                public String toString() {
1287:                    return cssValue;
1288:                }
1289:
1290:                static Color stringToColor(final String colorName) {
1291:                    if (Utilities.isEmptyString(colorName)) {
1292:                        return null;
1293:                    }
1294:
1295:                    final String lower = colorName.toLowerCase();
1296:                    if (lower.charAt(0) == '#') {
1297:                        final StringBuffer name = new StringBuffer(6);
1298:                        if (lower.length() == 4) {
1299:                            for (int i = 1; i < 4; i++) {
1300:                                name.append(lower.charAt(i)).append(
1301:                                        lower.charAt(i));
1302:                            }
1303:                        } else if (lower.length() != 7) {
1304:                            return null;
1305:                        } else {
1306:                            name.append(lower.substring(1));
1307:                        }
1308:
1309:                        final Color result = (Color) colorMap.get(name
1310:                                .toString());
1311:                        return result != null ? result : new Color(Integer
1312:                                .parseInt(name.toString(), 16));
1313:                    } else {
1314:                        return (Color) colorMap.get(lower);
1315:                    }
1316:                }
1317:
1318:                static Color parseRGB(final String rgbColor) {
1319:                    if (Utilities.isEmptyString(rgbColor)
1320:                            || !rgbColor.startsWith("rgb(")
1321:                            && !rgbColor.endsWith(")")) {
1322:
1323:                        return null;
1324:                    }
1325:                    final String[] colorComponents = RGB_SEPARATOR
1326:                            .split(rgbColor.substring(4, rgbColor.length() - 1));
1327:                    if (colorComponents.length < 3) {
1328:                        return null;
1329:                    }
1330:                    final int[] rgb = new int[3];
1331:                    boolean percentage = false;
1332:                    for (int i = 0; i < colorComponents.length; i++) {
1333:                        final String cc = colorComponents[i].trim();
1334:                        if (Utilities.isEmptyString(cc)) {
1335:                            return null;
1336:                        }
1337:                        percentage |= cc.charAt(cc.length() - 1) == '%';
1338:                        if (percentage && cc.charAt(cc.length() - 1) != '%') {
1339:                            return null;
1340:                        }
1341:                        rgb[i] = percentage ? (int) (Double.parseDouble(cc
1342:                                .substring(0, cc.length() - 1)) * 255 / 100)
1343:                                : Integer.parseInt(cc);
1344:                        rgb[i] = Utilities.range(rgb[i], 0, 255);
1345:                    }
1346:                    return new Color(rgb[0], rgb[1], rgb[2]);
1347:                }
1348:
1349:                final Color getColor() {
1350:                    return color;
1351:                }
1352:
1353:                private static String colorToString(final Color color) {
1354:                    final StringBuffer result = new StringBuffer(7);
1355:                    final String hex = Integer
1356:                            .toHexString(color.getRGB() & 0x00FFFFFF);
1357:                    result.append('#').append(zeros, 0, 6 - hex.length())
1358:                            .append(hex);
1359:                    return result.toString();
1360:                }
1361:            }
1362:
1363:            /**
1364:             * The implementation is based on CSS1 spec for
1365:             * <a href="http://www.w3.org/TR/CSS1#clear">'clear'</a>.
1366:             */
1367:            static final class Clear extends FixedSetValues {
1368:                private static final String[] VALID_VALUES = { "none", "left",
1369:                        "right", "both" };
1370:                private static final Clear[] VALUE_HOLDERS = new Clear[VALID_VALUES.length];
1371:
1372:                Clear() {
1373:                    this (-1);
1374:                }
1375:
1376:                private Clear(final int index) {
1377:                    super (index);
1378:                }
1379:
1380:                String[] getValidValues() {
1381:                    return VALID_VALUES;
1382:                }
1383:
1384:                PropertyValueConverter[] getValueHolders() {
1385:                    return VALUE_HOLDERS;
1386:                }
1387:
1388:                PropertyValueConverter createValueHolder(final int valueIndex) {
1389:                    return new Clear(valueIndex);
1390:                }
1391:            }
1392:
1393:            /**
1394:             * The implementation is based on CSS1 spec for
1395:             * <a href="http://www.w3.org/TR/CSS1#display">'display'</a>.
1396:             */
1397:            static final class Display extends FixedSetValues {
1398:                private static final String[] VALID_VALUES = { "block",
1399:                        "inline", "list-item", "none" };
1400:                private static final Display[] VALUE_HOLDERS = new Display[VALID_VALUES.length];
1401:
1402:                Display() {
1403:                    this (-1);
1404:                }
1405:
1406:                private Display(final int index) {
1407:                    super (index);
1408:                }
1409:
1410:                String[] getValidValues() {
1411:                    return VALID_VALUES;
1412:                }
1413:
1414:                PropertyValueConverter[] getValueHolders() {
1415:                    return VALUE_HOLDERS;
1416:                }
1417:
1418:                PropertyValueConverter createValueHolder(final int valueIndex) {
1419:                    return new Display(valueIndex);
1420:                }
1421:            }
1422:
1423:            /**
1424:             * The implementation is based on CSS1 spec for
1425:             * <a href="http://www.w3.org/TR/CSS1#float">'float'</a>.
1426:             */
1427:            static final class FloatProperty extends FixedSetValues {
1428:                private static final String[] VALID_VALUES = { "none", "left",
1429:                        "right" };
1430:                private static final FloatProperty[] VALUE_HOLDERS = new FloatProperty[VALID_VALUES.length];
1431:
1432:                FloatProperty() {
1433:                    this (-1);
1434:                }
1435:
1436:                private FloatProperty(final int index) {
1437:                    super (index);
1438:                }
1439:
1440:                String[] getValidValues() {
1441:                    return VALID_VALUES;
1442:                }
1443:
1444:                PropertyValueConverter[] getValueHolders() {
1445:                    return VALUE_HOLDERS;
1446:                }
1447:
1448:                PropertyValueConverter createValueHolder(final int valueIndex) {
1449:                    return new FloatProperty(valueIndex);
1450:                }
1451:            }
1452:
1453:            /**
1454:             * The implementation is based on CSS1 spec for
1455:             * <a href="http://www.w3.org/TR/CSS1#font-family">'font-family'</a>.
1456:             */
1457:            static final class FontFamily implements  PropertyValueConverter {
1458:                private static final String SANS_SERIF_FAMILY = "sans-serif";
1459:                private static final String SERIF_FAMILY = "serif";
1460:                private static final String MONOSPACE_FAMILY = "monospace";
1461:
1462:                private static final String SANS_SERIF = "SansSerif";
1463:                private static final String SERIF = "Serif";
1464:                private static final String MONOSPACED = "Monospaced";
1465:
1466:                static final String DEFAULT = SANS_SERIF;
1467:
1468:                private static final Pattern SPLIT_PATTERN = Pattern
1469:                        .compile("\\s*,\\s*");
1470:
1471:                private static final String[] fontFamilies;
1472:
1473:                static {
1474:                    GraphicsEnvironment ge = GraphicsEnvironment
1475:                            .getLocalGraphicsEnvironment();
1476:                    fontFamilies = ge.getAvailableFontFamilyNames();
1477:                    Arrays.sort(fontFamilies);
1478:                }
1479:
1480:                private final String value;
1481:                private final String family;
1482:
1483:                FontFamily() {
1484:                    value = null;
1485:                    family = null;
1486:                }
1487:
1488:                private FontFamily(final Object value) {
1489:                    this .value = (String) value;
1490:                    this .family = init();
1491:                }
1492:
1493:                public PropertyValueConverter toCSS(final Object value) {
1494:                    return new FontFamily(value);
1495:                }
1496:
1497:                public Object fromCSS() {
1498:                    return family;
1499:                }
1500:
1501:                public String toString() {
1502:                    return value;
1503:                }
1504:
1505:                private String init() {
1506:                    final String[] familyList = SPLIT_PATTERN.split(value);
1507:                    for (int i = 0; i < familyList.length; i++) {
1508:                        if (SANS_SERIF_FAMILY.equals(familyList[i])) {
1509:                            return SANS_SERIF;
1510:                        }
1511:                        if (SERIF_FAMILY.equals(familyList[i])) {
1512:                            return SERIF;
1513:                        }
1514:                        if (MONOSPACE_FAMILY.equals(familyList[i])) {
1515:                            return MONOSPACED;
1516:                        }
1517:                        if (Arrays.binarySearch(fontFamilies, familyList[i]) >= 0) {
1518:                            return familyList[i];
1519:                        }
1520:                    }
1521:                    return SANS_SERIF;
1522:                }
1523:            }
1524:
1525:            /**
1526:             * The implementation is based on CSS1 spec for
1527:             * <a href="http://www.w3.org/TR/CSS1#font-size">'font-size'</a>.
1528:             */
1529:            static final class FontSize extends Length implements 
1530:                    RelativeValueResolver {
1531:
1532:                static final Integer[] SIZE_TABLE = { new Integer(8),
1533:                        new Integer(10), new Integer(12), new Integer(14),
1534:                        new Integer(18), new Integer(24), new Integer(36) };
1535:
1536:                static final int RELATIVE_UNITS_SMALLER = 10;
1537:                static final int RELATIVE_UNITS_LARGER = 11;
1538:
1539:                private static final String[] VALUE_TABLE = { "xx-small",
1540:                        "x-small", "small", "medium", "large", "x-large",
1541:                        "xx-large" };
1542:
1543:                private final Integer size;
1544:
1545:                FontSize() {
1546:                    super ();
1547:                    size = null;
1548:                }
1549:
1550:                private FontSize(final String strValue, final int rUnits) {
1551:                    super (strValue, rUnits);
1552:                    size = getDefaultValue();
1553:                }
1554:
1555:                private FontSize(final String strValue, final Integer theSize) {
1556:                    super (strValue, RELATIVE_UNITS_UNDEFINED);
1557:                    size = theSize;
1558:                }
1559:
1560:                private FontSize(final String strValue, final int theSize,
1561:                        final int rUnits) {
1562:                    super (strValue, rUnits);
1563:                    size = new Integer(theSize);
1564:                }
1565:
1566:                public PropertyValueConverter toCSS(final Object value) {
1567:                    if (value instanceof  String) {
1568:                        if ("smaller".equals(value)) {
1569:                            return new FontSize((String) value,
1570:                                    RELATIVE_UNITS_SMALLER);
1571:                        }
1572:                        if ("larger".equals(value)) {
1573:                            return new FontSize((String) value,
1574:                                    RELATIVE_UNITS_LARGER);
1575:                        }
1576:                        Integer theSize = valueToSize((String) value);
1577:                        return theSize != null ? new FontSize((String) value,
1578:                                theSize) : super .toCSS(value);
1579:                    }
1580:                    if (value instanceof  Integer) {
1581:                        final int index = sizeValueIndex(((Integer) value)
1582:                                .intValue());
1583:                        return new FontSize(VALUE_TABLE[index],
1584:                                SIZE_TABLE[index]);
1585:                    }
1586:                    return null;
1587:                }
1588:
1589:                public Object getComputedValue(final View view) {
1590:                    if (relativeUnits != RELATIVE_UNITS_UNDEFINED) {
1591:                        return new FontSize(sValue,
1592:                                (Integer) resolveRelativeValue(view));
1593:                    }
1594:                    return this ;
1595:                }
1596:
1597:                static Integer getDefaultValue() {
1598:                    return SIZE_TABLE[2];
1599:                }
1600:
1601:                static int sizeValueIndex(final int size) {
1602:                    for (int i = 0; i < SIZE_TABLE.length; i++) {
1603:                        if (size <= SIZE_TABLE[i].intValue()) {
1604:                            return i;
1605:                        }
1606:                    }
1607:                    return VALUE_TABLE.length - 1;
1608:                }
1609:
1610:                PropertyValueConverter create(final String strValue,
1611:                        final float theValue, final int rUnits) {
1612:                    if (theValue < 0) {
1613:                        return null;
1614:                    }
1615:                    return new FontSize(strValue, (int) theValue, rUnits);
1616:                }
1617:
1618:                Object getValue(final View view) {
1619:                    return size;
1620:                }
1621:
1622:                Object resolveRelativeValue(final View view) {
1623:                    if (view == null) {
1624:                        return getDefaultValue();
1625:                    }
1626:                    final View parent = view.getParent();
1627:                    if (parent == null) {
1628:                        return getDefaultValue();
1629:                    }
1630:
1631:                    final AttributeSet attr = parent.getAttributes();
1632:                    final Object fs = attr.getAttribute(Attribute.FONT_SIZE);
1633:                    final int fontSize = fs != null ? ((Length) fs)
1634:                            .intValue(parent) : getDefaultValue().intValue();
1635:                    int sizeValueIndex;
1636:
1637:                    // calculation is defined by CSS1, http://www.w3.org/TR/CSS1#length-units
1638:                    switch (relativeUnits) {
1639:                    case RELATIVE_UNITS_EM:
1640:                        return new Integer(fontSize * size.intValue());
1641:
1642:                    case RELATIVE_UNITS_EX:
1643:                        return new Integer(fontSize * size.intValue() / 2);
1644:
1645:                    case RELATIVE_UNITS_PERCENTAGE:
1646:                        return new Integer(fontSize * size.intValue() / 100);
1647:
1648:                    case RELATIVE_UNITS_SMALLER:
1649:                        sizeValueIndex = sizeValueIndex(fontSize);
1650:                        return SIZE_TABLE[sizeValueIndex > 0 ? sizeValueIndex - 1
1651:                                : 0];
1652:
1653:                    case RELATIVE_UNITS_LARGER:
1654:                        sizeValueIndex = sizeValueIndex(fontSize) + 1;
1655:                        return new Integer(fontSize * 120 / 100);
1656:
1657:                    default:
1658:                        System.err.println(Messages.getString("swing.err.07")); //$NON-NLS-1$
1659:                    }
1660:                    return getDefaultValue();
1661:                }
1662:
1663:                private static Integer valueToSize(final String value) {
1664:                    for (int i = 0; i < VALUE_TABLE.length; i++) {
1665:                        if (VALUE_TABLE[i].equals(value)) {
1666:                            return SIZE_TABLE[i];
1667:                        }
1668:                    }
1669:                    return null;
1670:                }
1671:            }
1672:
1673:            /**
1674:             * The implementation is based on CSS1 spec for
1675:             * <a href="http://www.w3.org/TR/CSS1#font-style">'font-style'</a>.
1676:             */
1677:            static final class FontStyle implements  PropertyValueConverter {
1678:                private static final FontStyle OBLIQUE = new FontStyle(
1679:                        "oblique", Boolean.FALSE);
1680:                private static final FontStyle NORMAL = new FontStyle("normal",
1681:                        Boolean.FALSE);
1682:                private static final FontStyle ITALIC = new FontStyle("italic",
1683:                        Boolean.TRUE);
1684:
1685:                private final String cssValue;
1686:                private final Boolean value;
1687:
1688:                FontStyle() {
1689:                    cssValue = null;
1690:                    value = null;
1691:                }
1692:
1693:                private FontStyle(final String cssValue, final Boolean value) {
1694:                    this .cssValue = cssValue;
1695:                    this .value = value;
1696:                }
1697:
1698:                public PropertyValueConverter toCSS(final Object value) {
1699:                    if (value instanceof  Boolean) {
1700:                        return ((Boolean) value).booleanValue() ? ITALIC
1701:                                : NORMAL;
1702:                    } else if (ITALIC.cssValue.equals(value)) {
1703:                        return ITALIC;
1704:                    } else if (OBLIQUE.cssValue.equals(value)) {
1705:                        return OBLIQUE;
1706:                    } else if (NORMAL.cssValue.equals(value)) {
1707:                        return NORMAL;
1708:                    }
1709:
1710:                    return null;
1711:                }
1712:
1713:                public Object fromCSS() {
1714:                    return value;
1715:                }
1716:
1717:                public String toString() {
1718:                    return cssValue;
1719:                }
1720:
1721:            }
1722:
1723:            /**
1724:             * The implementation is based on CSS1 spec for
1725:             * <a href="http://www.w3.org/TR/CSS1#font-variant">'font-variant'</a>.
1726:             */
1727:            static final class FontVariant extends FixedSetValues {
1728:                private static final String[] VALID_VALUES = { "normal",
1729:                        "small-caps" };
1730:                private static final FontVariant[] VALUE_HOLDERS = new FontVariant[VALID_VALUES.length];
1731:
1732:                FontVariant() {
1733:                    this (-1);
1734:                }
1735:
1736:                private FontVariant(final int index) {
1737:                    super (index);
1738:                }
1739:
1740:                String[] getValidValues() {
1741:                    return VALID_VALUES;
1742:                }
1743:
1744:                PropertyValueConverter[] getValueHolders() {
1745:                    return VALUE_HOLDERS;
1746:                }
1747:
1748:                PropertyValueConverter createValueHolder(final int valueIndex) {
1749:                    return new FontVariant(valueIndex);
1750:                }
1751:            }
1752:
1753:            /**
1754:             * The implementation is based on CSS1 spec for
1755:             * <a href="http://www.w3.org/TR/CSS1#font-weight">'font-weight'</a>.
1756:             */
1757:            static final class FontWeight implements  PropertyValueConverter {
1758:                private static final FontWeight BOLD = new FontWeight("bold",
1759:                        Boolean.TRUE);
1760:                private static final FontWeight NORMAL = new FontWeight(
1761:                        "normal", Boolean.FALSE);
1762:
1763:                private static final String[] RELATIVE = { "bolder", "lighter" };
1764:                private static final String[] ABSOLUTE = { "100", "200", "300",
1765:                        "400", "500", "600", "700", "800", "900" };
1766:                private static final int BOLD_THRESHOLD = 5;
1767:
1768:                private final String cssValue;
1769:                private final Boolean value;
1770:
1771:                FontWeight() {
1772:                    cssValue = null;
1773:                    value = null;
1774:                }
1775:
1776:                private FontWeight(final String cssValue, final Boolean value) {
1777:                    this .cssValue = cssValue;
1778:                    this .value = value;
1779:                }
1780:
1781:                public PropertyValueConverter toCSS(final Object value) {
1782:                    if (value instanceof  Boolean) {
1783:                        return ((Boolean) value).booleanValue() ? BOLD : NORMAL;
1784:                    }
1785:
1786:                    if (BOLD.cssValue.equals(value)) {
1787:                        return BOLD;
1788:                    } else if (NORMAL.cssValue.equals(value)) {
1789:                        return NORMAL;
1790:                    }
1791:
1792:                    for (int i = 0; i < ABSOLUTE.length; i++) {
1793:                        if (ABSOLUTE[i].equals(value)) {
1794:                            return new FontWeight((String) value, Boolean
1795:                                    .valueOf(i >= BOLD_THRESHOLD));
1796:                        }
1797:                    }
1798:
1799:                    for (int i = 0; i < RELATIVE.length; i++) {
1800:                        if (RELATIVE[i].equals(value)) {
1801:                            return new FontWeight((String) value, Boolean
1802:                                    .valueOf(i == 0));
1803:                        }
1804:                    }
1805:
1806:                    return null;
1807:                }
1808:
1809:                public Object fromCSS() {
1810:                    return value;
1811:                }
1812:
1813:                public String toString() {
1814:                    return cssValue;
1815:                }
1816:
1817:            }
1818:
1819:            /**
1820:             * The implementation is based on CSS1 spec for
1821:             * <a href="http://www.w3.org/TR/CSS1#font">'font'</a>.
1822:             */
1823:            static final class FontExpander implements 
1824:                    ShorthandPropertyExpander {
1825:                private static final Pattern WS_SPLIT = Pattern.compile("\\s+");
1826:
1827:                public void parseAndExpandProperty(
1828:                        final MutableAttributeSet attrs, final String value) {
1829:                    parse(value.trim(), attrs);
1830:                }
1831:
1832:                private void parse(final String value,
1833:                        final MutableAttributeSet attrs) {
1834:                    final String[] parts = WS_SPLIT.split(value);
1835:                    FontStyle style = null;
1836:                    FontVariant variant = null;
1837:                    FontWeight weight = null;
1838:                    FontSize size = null;
1839:                    FloatValue lineHeight = null;
1840:                    int slashIndex = -1;
1841:                    int i;
1842:                    for (i = 0; i < parts.length; i++) {
1843:                        if (i > 3) {
1844:                            return;
1845:                        }
1846:
1847:                        if ("normal".equals(parts[i])) {
1848:                            continue;
1849:                        }
1850:
1851:                        if (style == null) {
1852:                            style = (FontStyle) Attribute.FONT_STYLE
1853:                                    .getConverter().toCSS(parts[i]);
1854:                            if (style != null) {
1855:                                continue;
1856:                            }
1857:                        }
1858:
1859:                        if (variant == null) {
1860:                            variant = (FontVariant) Attribute.FONT_VARIANT
1861:                                    .getConverter().toCSS(parts[i]);
1862:                            if (variant != null) {
1863:                                continue;
1864:                            }
1865:                        }
1866:
1867:                        if (weight == null) {
1868:                            weight = (FontWeight) Attribute.FONT_WEIGHT
1869:                                    .getConverter().toCSS(parts[i]);
1870:                            if (weight != null) {
1871:                                continue;
1872:                            }
1873:                        }
1874:
1875:                        if (size == null) {
1876:                            slashIndex = parts[i].indexOf('/');
1877:                            final String sizeValue = slashIndex == -1 ? parts[i]
1878:                                    : parts[i].substring(0, slashIndex);
1879:                            size = (FontSize) Attribute.FONT_SIZE
1880:                                    .getConverter().toCSS(sizeValue);
1881:                            if (size != null) {
1882:                                break;
1883:                            }
1884:                        }
1885:
1886:                        return;
1887:                    }
1888:
1889:                    if (size == null) {
1890:                        return;
1891:                    }
1892:
1893:                    String lhValue;
1894:                    if (slashIndex > 0) {
1895:                        if (parts[i].length() > slashIndex + 1) {
1896:                            lhValue = parts[i].substring(slashIndex + 1);
1897:                            i += 1;
1898:                        } else {
1899:                            if (parts.length <= i + 1) {
1900:                                return;
1901:                            }
1902:                            lhValue = parts[i + 1];
1903:                            i += 2;
1904:                        }
1905:                    } else {
1906:                        if (parts.length <= i + 1) {
1907:                            return;
1908:                        }
1909:
1910:                        if (parts[i + 1].length() == 1
1911:                                && parts[i + 1].charAt(0) == '/') {
1912:
1913:                            if (parts.length <= i + 2) {
1914:                                return;
1915:                            }
1916:                            lhValue = parts[i + 2];
1917:                            i += 3;
1918:                        } else if (parts[i + 1].startsWith("/")) {
1919:                            lhValue = parts[i + 1].substring(1);
1920:                            i += 2;
1921:                        } else {
1922:                            lhValue = Attribute.LINE_HEIGHT.getDefaultValue();
1923:                            i += 1;
1924:                        }
1925:                    }
1926:                    lineHeight = (FloatValue) Attribute.LINE_HEIGHT
1927:                            .getConverter().toCSS(lhValue);
1928:                    if (lineHeight == null) {
1929:                        return;
1930:                    }
1931:
1932:                    StringBuffer family = new StringBuffer();
1933:                    family.append(parts[i++]);
1934:                    for (; i < parts.length; i++) {
1935:                        family.append(' ').append(parts[i]);
1936:                    }
1937:                    if (family.length() == 0) {
1938:                        return;
1939:                    }
1940:
1941:                    if (style == null) {
1942:                        style = (FontStyle) Attribute.FONT_STYLE.getConverter()
1943:                                .toCSS(Attribute.FONT_STYLE.getDefaultValue());
1944:                    }
1945:
1946:                    if (variant == null) {
1947:                        variant = (FontVariant) Attribute.FONT_VARIANT
1948:                                .getConverter().toCSS(
1949:                                        Attribute.FONT_VARIANT
1950:                                                .getDefaultValue());
1951:                    }
1952:
1953:                    if (weight == null) {
1954:                        weight = (FontWeight) Attribute.FONT_WEIGHT
1955:                                .getConverter()
1956:                                .toCSS(Attribute.FONT_WEIGHT.getDefaultValue());
1957:                    }
1958:
1959:                    //            if (lineHeight == null ) {
1960:                    //                lineHeight = (FloatValue)Attribute.LINE_HEIGHT.getConverter()
1961:                    //                                  .toCSS(Attribute.LINE_HEIGHT.getDefaultValue());
1962:                    //            }
1963:
1964:                    attrs.addAttribute(Attribute.FONT_STYLE, style);
1965:                    attrs.addAttribute(Attribute.FONT_VARIANT, variant);
1966:                    attrs.addAttribute(Attribute.FONT_WEIGHT, weight);
1967:                    attrs.addAttribute(Attribute.FONT_SIZE, size);
1968:                    attrs.addAttribute(Attribute.LINE_HEIGHT, lineHeight);
1969:                    attrs
1970:                            .addAttribute(Attribute.FONT_FAMILY, family
1971:                                    .toString());
1972:                }
1973:            }
1974:
1975:            /**
1976:             * The implementation is based on CSS1 spec for
1977:             * <a href="http://www.w3.org/TR/CSS1#text-align">'text-align'</a>.
1978:             */
1979:            static final class TextAlign implements  PropertyValueConverter {
1980:                private static final TextAlign LEFT = new TextAlign("left",
1981:                        new Integer(StyleConstants.ALIGN_LEFT));
1982:                private static final TextAlign CENTER = new TextAlign("center",
1983:                        new Integer(StyleConstants.ALIGN_CENTER));
1984:                private static final TextAlign RIGHT = new TextAlign("right",
1985:                        new Integer(StyleConstants.ALIGN_RIGHT));
1986:                private static final TextAlign JUSTIFY = new TextAlign(
1987:                        "justify", new Integer(StyleConstants.ALIGN_JUSTIFIED));
1988:
1989:                private final String align;
1990:                private final Integer justification;
1991:
1992:                TextAlign() {
1993:                    this .align = null;
1994:                    this .justification = null;
1995:                }
1996:
1997:                private TextAlign(final String align,
1998:                        final Integer justification) {
1999:                    this .align = align;
2000:                    this .justification = justification;
2001:                }
2002:
2003:                public String toString() {
2004:                    return align;
2005:                }
2006:
2007:                public PropertyValueConverter toCSS(final Object value) {
2008:                    return value instanceof  Integer ? convertIntegerValue((Integer) value)
2009:                            : convertStringValue((String) value);
2010:                }
2011:
2012:                public Object fromCSS() {
2013:                    return justification;
2014:                }
2015:
2016:                private PropertyValueConverter convertIntegerValue(
2017:                        final Integer value) {
2018:                    switch (value.intValue()) {
2019:                    case StyleConstants.ALIGN_LEFT:
2020:                        return LEFT;
2021:
2022:                    case StyleConstants.ALIGN_CENTER:
2023:                        return CENTER;
2024:
2025:                    case StyleConstants.ALIGN_RIGHT:
2026:                        return RIGHT;
2027:
2028:                    case StyleConstants.ALIGN_JUSTIFIED:
2029:                        return JUSTIFY;
2030:
2031:                    default:
2032:                        return LEFT;
2033:                    }
2034:                }
2035:
2036:                private PropertyValueConverter convertStringValue(
2037:                        final String value) {
2038:                    if (LEFT.align.equals(value)) {
2039:                        return LEFT;
2040:                    }
2041:                    if (CENTER.align.equals(value)) {
2042:                        return CENTER;
2043:                    }
2044:                    if (RIGHT.align.equals(value)) {
2045:                        return RIGHT;
2046:                    }
2047:                    if (JUSTIFY.align.equals(value)) {
2048:                        return JUSTIFY;
2049:                    }
2050:                    return null;
2051:                }
2052:            }
2053:
2054:            /**
2055:             * Storage for
2056:             * <a href="http://www.w3.org/TR/CSS1#length-units">Length Units</a>
2057:             * of float type.
2058:             */
2059:            static class FloatValue extends Length {
2060:                static final FloatValue factory = new FloatValue();
2061:
2062:                private static final Float ZERO = new Float(0);
2063:
2064:                private final Float theValue;
2065:
2066:                FloatValue() {
2067:                    super ();
2068:                    theValue = null;
2069:                }
2070:
2071:                protected FloatValue(final String strValue,
2072:                        final float theValue, final int units) {
2073:                    this (strValue, new Float(theValue), units);
2074:                }
2075:
2076:                protected FloatValue(final String strValue,
2077:                        final Float theValue, final int units) {
2078:                    super (strValue, units);
2079:                    this .theValue = theValue;
2080:                }
2081:
2082:                public PropertyValueConverter toCSS(final Object value) {
2083:                    if (value instanceof  Float) {
2084:                        return new FloatValue(value.toString() + "pt",
2085:                                (Float) value, RELATIVE_UNITS_UNDEFINED);
2086:                    }
2087:                    return super .toCSS(value);
2088:                }
2089:
2090:                PropertyValueConverter create(final String strValue,
2091:                        final float theValue, final int rUnits) {
2092:                    return new FloatValue(strValue, theValue, rUnits);
2093:                }
2094:
2095:                Object resolveRelativeValue(final View view) {
2096:                    if (view == null) {
2097:                        return ZERO;
2098:                    }
2099:
2100:                    final AttributeSet attr = view.getAttributes();
2101:
2102:                    switch (relativeUnits) {
2103:                    case RELATIVE_UNITS_EM:
2104:                    case RELATIVE_UNITS_EX:
2105:                        final Object fs = attr
2106:                                .getAttribute(Attribute.FONT_SIZE);
2107:                        final float fontSize = fs != null ? ((Length) fs)
2108:                                .floatValue(view) : FontSize.getDefaultValue()
2109:                                .floatValue();
2110:
2111:                        float result = fontSize * theValue.floatValue();
2112:                        if (relativeUnits == RELATIVE_UNITS_EX) {
2113:                            result /= 2;
2114:                        }
2115:                        return new Float(result);
2116:
2117:                    case RELATIVE_UNITS_PERCENTAGE:
2118:                        View parent = view.getParent();
2119:                        if (!(parent instanceof  BoxView)) {
2120:                            return ZERO;
2121:                        }
2122:                        float width = ((BoxView) parent).getWidth();
2123:                        if (width >= Integer.MAX_VALUE) {
2124:                            return ZERO;
2125:                        }
2126:
2127:                        return new Float(width * theValue.floatValue() / 100);
2128:
2129:                    default:
2130:                        System.err.println(Messages.getString("swing.err.07")); //$NON-NLS-1$
2131:                    }
2132:                    return ZERO;
2133:                }
2134:
2135:                Object getValue(final View view) {
2136:                    return theValue;
2137:                }
2138:            }
2139:
2140:            /**
2141:             * The implementation is based on CSS1 spec for
2142:             * <a href="http://www.w3.org/TR/CSS1#width">'width'</a>.
2143:             */
2144:            static class Width extends FloatValue {
2145:                static final Width auto = new Width("auto", 0,
2146:                        RELATIVE_UNITS_UNDEFINED);
2147:
2148:                Width() {
2149:                    super ();
2150:                }
2151:
2152:                Width(final String strValue, final float theValue,
2153:                        final int units) {
2154:                    super (strValue, theValue, units);
2155:                }
2156:
2157:                public PropertyValueConverter toCSS(final Object value) {
2158:                    if ("auto".equals(value)) {
2159:                        return auto;
2160:                    }
2161:                    return super .toCSS(value);
2162:                }
2163:
2164:                PropertyValueConverter create(final String strValue,
2165:                        final float theValue, final int rUnits) {
2166:                    if (theValue < 0) {
2167:                        return null;
2168:                    }
2169:                    return super .create(strValue, theValue, rUnits);
2170:                }
2171:            }
2172:
2173:            static final class Height extends Width {
2174:                PropertyValueConverter create(final String strValue,
2175:                        final float theValue, final int rUnits) {
2176:                    if (rUnits == RELATIVE_UNITS_PERCENTAGE) {
2177:                        return null;
2178:                    }
2179:                    return super .create(strValue, theValue, rUnits);
2180:                }
2181:            }
2182:
2183:            /**
2184:             * The implementation is based on CSS1 spec for
2185:             * <a href="http://www.w3.org/TR/CSS1#line-height">'line-height'</a>.
2186:             */
2187:            static final class LineHeight extends FloatValue implements 
2188:                    RelativeValueResolver {
2189:
2190:                static final int RELATIVE_UNITS_NUMBER = 20;
2191:
2192:                static final Length normal = new LineHeight("normal", 1,
2193:                        RELATIVE_UNITS_NUMBER);
2194:
2195:                private LineHeight(final String strValue, final float theValue,
2196:                        final int units) {
2197:                    super (strValue, theValue, units);
2198:                }
2199:
2200:                public PropertyValueConverter toCSS(final Object value) {
2201:                    if ("normal".equals(value)) {
2202:                        return normal;
2203:                    }
2204:                    if (value instanceof  String) {
2205:                        final String sValue = (String) value;
2206:
2207:                        return NUMBER_PATTERN.matcher(sValue).matches() ? create(
2208:                                sValue, Float.parseFloat(sValue),
2209:                                RELATIVE_UNITS_NUMBER)
2210:                                : super .toCSS(value);
2211:                    }
2212:                    return super .toCSS(value);
2213:                }
2214:
2215:                public Object getComputedValue(final View view) {
2216:                    if (relativeUnits == RELATIVE_UNITS_UNDEFINED
2217:                            || relativeUnits == RELATIVE_UNITS_NUMBER) {
2218:
2219:                        return this ;
2220:                    }
2221:                    return new FloatValue(sValue,
2222:                            (Float) resolveRelativeValue(view),
2223:                            RELATIVE_UNITS_UNDEFINED);
2224:                }
2225:
2226:                PropertyValueConverter create(final String strValue,
2227:                        final float theValue, final int rUnits) {
2228:                    if (theValue < 0) {
2229:                        return null;
2230:                    }
2231:                    return super .create(strValue, theValue, rUnits);
2232:                }
2233:            }
2234:
2235:            /**
2236:             * The implementation is based on CSS1 spec for
2237:             * <a href="http://www.w3.org/TR/CSS1#list-style-type">'list-style-type'</a>.
2238:             */
2239:            static final class ListStyleType extends FixedSetValues {
2240:                static final ListStyleType factory = new ListStyleType(0);
2241:
2242:                static final int LIST_STYLE_DISC = 0;
2243:                static final int LIST_STYLE_CIRCLE = 1;
2244:                static final int LIST_STYLE_SQUARE = 2;
2245:                static final int LIST_STYLE_DECIMAL = 3;
2246:
2247:                static final int LIST_STYLE_LOWER_ROMAN = 4;
2248:                static final int LIST_STYLE_UPPER_ROMAN = 5;
2249:                static final int LIST_STYLE_LOWER_ALPHA = 6;
2250:                static final int LIST_STYLE_UPPER_ALPHA = 7;
2251:
2252:                static final int LIST_STYLE_NONE = 8;
2253:
2254:                private static final String[] VALID_VALUES = { "disc",
2255:                        "circle", "square", "decimal", "lower-roman",
2256:                        "upper-roman", "lower-alpha", "upper-alpha", "none" };
2257:                private static final ListStyleType[] VALUE_HOLDERS = new ListStyleType[VALID_VALUES.length];
2258:
2259:                static {
2260:                    VALUE_HOLDERS[0] = factory;
2261:                }
2262:
2263:                private ListStyleType(final int index) {
2264:                    super (index);
2265:                }
2266:
2267:                String[] getValidValues() {
2268:                    return VALID_VALUES;
2269:                }
2270:
2271:                PropertyValueConverter[] getValueHolders() {
2272:                    return VALUE_HOLDERS;
2273:                }
2274:
2275:                PropertyValueConverter createValueHolder(final int valueIndex) {
2276:                    return new ListStyleType(valueIndex);
2277:                }
2278:            }
2279:
2280:            /**
2281:             * The implementation is based on CSS1 spec for
2282:             * <a href="http://www.w3.org/TR/CSS1#list-style-position">'list-style-position'</a>.
2283:             */
2284:            static final class ListStylePosition extends FixedSetValues {
2285:                static final ListStylePosition factory = new ListStylePosition(
2286:                        0);
2287:
2288:                private static final String[] VALID_VALUES = { "outside",
2289:                        "inside" };
2290:                private static final ListStylePosition[] VALUE_HOLDERS = new ListStylePosition[VALID_VALUES.length];
2291:
2292:                static {
2293:                    VALUE_HOLDERS[0] = factory;
2294:                }
2295:
2296:                private ListStylePosition(final int index) {
2297:                    super (index);
2298:                }
2299:
2300:                String[] getValidValues() {
2301:                    return VALID_VALUES;
2302:                }
2303:
2304:                PropertyValueConverter[] getValueHolders() {
2305:                    return VALUE_HOLDERS;
2306:                }
2307:
2308:                PropertyValueConverter createValueHolder(final int valueIndex) {
2309:                    return new ListStylePosition(valueIndex);
2310:                }
2311:            }
2312:
2313:            /**
2314:             * The implementation is based on CSS1 spec for
2315:             * <a href="http://www.w3.org/TR/CSS1#list-style">'list-style'</a>.
2316:             */
2317:            static final class ListStyleExpander implements 
2318:                    ShorthandPropertyExpander {
2319:                private static final Pattern WS_SPLIT = Pattern.compile("\\s+");
2320:                // pattern is based on CSS1, http://www.w3.org/TR/CSS1#url format
2321:                static final Pattern URL_PATTERN = Pattern
2322:                        .compile("url\\(\\s*((?:\"|')?+).+?\\1\\s*\\)");
2323:
2324:                public void parseAndExpandProperty(
2325:                        final MutableAttributeSet attrs, final String value) {
2326:                    String[] parts = split(value.trim());
2327:                    if (parts == null || parts.length > 3) {
2328:                        return;
2329:                    }
2330:
2331:                    ListStyleType type = null;
2332:                    ListStylePosition position = null;
2333:                    ImageValue image = null;
2334:
2335:                    for (int i = 0; i < parts.length; i++) {
2336:                        if (type == null) {
2337:                            type = (ListStyleType) ListStyleType.factory
2338:                                    .toCSS(parts[i]);
2339:                            if (type != null) {
2340:                                continue;
2341:                            }
2342:                        } else {
2343:                            if ("none".equals(type.toString()) && image == null) {
2344:                                ListStyleType typeTry = (ListStyleType) ListStyleType.factory
2345:                                        .toCSS(parts[i]);
2346:                                if (typeTry != null) {
2347:                                    type = typeTry;
2348:                                    image = ImageValue.NONE;
2349:                                    continue;
2350:                                }
2351:                            }
2352:                        }
2353:
2354:                        if (position == null) {
2355:                            position = (ListStylePosition) ListStylePosition.factory
2356:                                    .toCSS(parts[i]);
2357:                            if (position != null) {
2358:                                continue;
2359:                            }
2360:                        }
2361:
2362:                        if (image == null) {
2363:                            image = (ImageValue) ImageValue.NONE
2364:                                    .toCSS(parts[i]);
2365:                            if (image != null) {
2366:                                continue;
2367:                            }
2368:                        }
2369:
2370:                        // An invalid value encountered
2371:                        return;
2372:                    }
2373:
2374:                    attrs.addAttribute(Attribute.LIST_STYLE_TYPE,
2375:                            type != null ? type : ListStyleType.factory);
2376:                    attrs.addAttribute(Attribute.LIST_STYLE_POSITION,
2377:                            position != null ? position
2378:                                    : ListStylePosition.factory);
2379:                    attrs.addAttribute(Attribute.LIST_STYLE_IMAGE,
2380:                            image != null ? image : ImageValue.NONE);
2381:                }
2382:
2383:                private static String[] split(final String value) {
2384:                    if (value.indexOf("url(") != -1) {
2385:                        Matcher matcher = URL_PATTERN.matcher(value);
2386:                        if (!matcher.find()) {
2387:                            return null;
2388:                        }
2389:                        final int urlStart = matcher.start();
2390:                        final int urlEnd = matcher.end();
2391:                        final String url = value.substring(urlStart, urlEnd);
2392:                        final String rest = (value.substring(0, urlStart) + value
2393:                                .substring(urlEnd)).trim();
2394:                        if (rest.length() != 0) {
2395:                            if (rest.indexOf("url(") != -1) {
2396:                                return null;
2397:                            }
2398:
2399:                            String[] parts = WS_SPLIT.split(rest);
2400:                            String[] result = new String[parts.length + 1];
2401:                            System.arraycopy(parts, 0, result, 0, parts.length);
2402:                            result[parts.length] = url;
2403:
2404:                            return result;
2405:                        }
2406:
2407:                        return new String[] { url };
2408:                    }
2409:
2410:                    return WS_SPLIT.split(value);
2411:                }
2412:            }
2413:
2414:            /**
2415:             * The implementation is based on CSS1 spec for
2416:             * <a href="http://www.w3.org/TR/CSS1#text-transform">'text-transform'</a>.
2417:             */
2418:            static final class TextTransform extends FixedSetValues {
2419:                private static final String[] VALID_VALUES = { "none",
2420:                        "capitalize", "uppercase", "lowercase" };
2421:                private static final TextTransform[] VALUE_HOLDERS = new TextTransform[VALID_VALUES.length];
2422:
2423:                TextTransform() {
2424:                    this (-1);
2425:                }
2426:
2427:                private TextTransform(final int index) {
2428:                    super (index);
2429:                }
2430:
2431:                String[] getValidValues() {
2432:                    return VALID_VALUES;
2433:                }
2434:
2435:                PropertyValueConverter[] getValueHolders() {
2436:                    return VALUE_HOLDERS;
2437:                }
2438:
2439:                PropertyValueConverter createValueHolder(final int valueIndex) {
2440:                    return new TextTransform(valueIndex);
2441:                }
2442:            }
2443:
2444:            /**
2445:             * The implementation is based on CSS1 spec for
2446:             * <a href="http://www.w3.org/TR/CSS1#letter-spacing">'letter-spacing'</a>
2447:             * and <a href="http://www.w3.org/TR/CSS1#word-spacing">'word-spacing'</a>.
2448:             */
2449:            static final class SpacingValue extends FloatValue {
2450:                static final SpacingValue factory = new SpacingValue("normal",
2451:                        0, RELATIVE_UNITS_UNDEFINED);
2452:
2453:                private SpacingValue(final String strValue,
2454:                        final float theValue, final int units) {
2455:                    super (strValue, theValue, units);
2456:                }
2457:
2458:                public PropertyValueConverter toCSS(final Object value) {
2459:                    if ("normal".equals(value)) {
2460:                        return factory;
2461:                    }
2462:
2463:                    return super .toCSS(value);
2464:                }
2465:
2466:                PropertyValueConverter create(final String strValue,
2467:                        final float theValue, final int rUnits) {
2468:                    return rUnits == RELATIVE_UNITS_PERCENTAGE ? null
2469:                            : new FloatValue(strValue, theValue, rUnits);
2470:                }
2471:            }
2472:
2473:            /**
2474:             * The implementation is based on CSS1 spec for
2475:             * <a href="http://www.w3.org/TR/CSS1#vertical-align">'vertical-align'</a>.
2476:             */
2477:            static final class VerticalAlign extends FixedSetValues {
2478:                private static final String[] VALID_VALUES = { "baseline",
2479:                        "sub", "super", "top", "text-top", "middle", "bottom",
2480:                        "text-bottom" };
2481:                private static final VerticalAlign[] VALUE_HOLDERS = new VerticalAlign[VALID_VALUES.length];
2482:
2483:                VerticalAlign() {
2484:                    this (-1);
2485:                }
2486:
2487:                private VerticalAlign(final int index) {
2488:                    super (index);
2489:                }
2490:
2491:                public PropertyValueConverter toCSS(final Object value) {
2492:                    // TODO 'vertical-align' - implement support for percentage values
2493:                    return super .toCSS(value);
2494:                }
2495:
2496:                String[] getValidValues() {
2497:                    return VALID_VALUES;
2498:                }
2499:
2500:                PropertyValueConverter[] getValueHolders() {
2501:                    return VALUE_HOLDERS;
2502:                }
2503:
2504:                PropertyValueConverter createValueHolder(final int valueIndex) {
2505:                    return new VerticalAlign(valueIndex);
2506:                }
2507:            }
2508:
2509:            /**
2510:             * The implementation is based on CSS1 spec for
2511:             * <a href="http://www.w3.org/TR/CSS1#white-space">'white-space'</a>.
2512:             */
2513:            static final class WhiteSpace extends FixedSetValues {
2514:                static final WhiteSpace factory = new WhiteSpace(0);
2515:
2516:                static final int NORMAL = 0;
2517:                static final int PRE = 1;
2518:                static final int NOWRAP = 2;
2519:
2520:                private static final String[] VALID_VALUES = { "normal", "pre",
2521:                        "nowrap" };
2522:                private static final WhiteSpace[] VALUE_HOLDERS = new WhiteSpace[VALID_VALUES.length];
2523:
2524:                static {
2525:                    VALUE_HOLDERS[0] = factory;
2526:                }
2527:
2528:                private WhiteSpace(final int index) {
2529:                    super (index);
2530:                }
2531:
2532:                String[] getValidValues() {
2533:                    return VALID_VALUES;
2534:                }
2535:
2536:                PropertyValueConverter[] getValueHolders() {
2537:                    return VALUE_HOLDERS;
2538:                }
2539:
2540:                PropertyValueConverter createValueHolder(final int valueIndex) {
2541:                    return new WhiteSpace(valueIndex);
2542:                }
2543:            }
2544:
2545:            /**
2546:             * The implementation is based on CSS1 spec for
2547:             * <a href="http://www.w3.org/TR/CSS1#text-decoration">'text-decoration'</a>.
2548:             */
2549:            static final class TextDecoration implements  Cloneable,
2550:                    PropertyValueConverter {
2551:                private static final String[] DECORATIONS = { "underline",
2552:                        "line-through", "overline", "blink" };
2553:
2554:                private final boolean[] values = new boolean[4];
2555:
2556:                private String value;
2557:
2558:                TextDecoration() {
2559:                }
2560:
2561:                private TextDecoration(final String value,
2562:                        final boolean[] values) {
2563:                    this .value = value;
2564:                    for (int i = 0; i < this .values.length; i++) {
2565:                        this .values[i] = values[i];
2566:                    }
2567:                }
2568:
2569:                public Object clone() {
2570:                    try {
2571:                        return super .clone();
2572:                    } catch (CloneNotSupportedException e) {
2573:                        return null;
2574:                    }
2575:                }
2576:
2577:                public PropertyValueConverter toCSS(final Object value) {
2578:                    return parseValue((String) value, values) ? new TextDecoration(
2579:                            (String) value, values)
2580:                            : null;
2581:                }
2582:
2583:                public Object fromCSS() {
2584:                    return null;
2585:                }
2586:
2587:                public String toString() {
2588:                    return value;
2589:                }
2590:
2591:                void setUnderline(final boolean underline) {
2592:                    values[0] = underline;
2593:                    updateValue();
2594:                }
2595:
2596:                boolean isUnderline() {
2597:                    return values[0];
2598:                }
2599:
2600:                void setLineThrough(final boolean lineThrough) {
2601:                    values[1] = lineThrough;
2602:                    updateValue();
2603:                }
2604:
2605:                boolean isLineThrough() {
2606:                    return values[1];
2607:                }
2608:
2609:                void setOverline(final boolean overline) {
2610:                    values[2] = overline;
2611:                    updateValue();
2612:                }
2613:
2614:                boolean isOverline() {
2615:                    return values[2];
2616:                }
2617:
2618:                void setBlink(final boolean blink) {
2619:                    values[3] = blink;
2620:                    updateValue();
2621:                }
2622:
2623:                boolean isBlink() {
2624:                    return values[3];
2625:                }
2626:
2627:                boolean isNone() {
2628:                    return !isUnderline() && !isLineThrough() && !isOverline()
2629:                            && !isBlink();
2630:                }
2631:
2632:                private String getValue() {
2633:                    StringBuffer result = new StringBuffer();
2634:                    for (int i = 0; i < values.length; i++) {
2635:                        if (values[i]) {
2636:                            if (result.length() > 0) {
2637:                                result.append(' ');
2638:                            }
2639:                            result.append(DECORATIONS[i]);
2640:                        }
2641:                    }
2642:                    return result.length() > 0 ? result.toString() : "none";
2643:                }
2644:
2645:                private static boolean parseValue(final String value,
2646:                        final boolean[] values) {
2647:                    for (int i = 0; i < values.length; i++) {
2648:                        values[i] = false;
2649:                    }
2650:                    if ("none".equals(value)) {
2651:                        return true;
2652:                    }
2653:
2654:                    final String[] decorators = value.split("\\s+");
2655:                    if (decorators.length == 0) {
2656:                        return false;
2657:                    }
2658:
2659:                    for (int i = 0; i < decorators.length; i++) {
2660:                        int index = getIndex(decorators[i]);
2661:                        if (index == -1) {
2662:                            return false;
2663:                        }
2664:                        values[index] = true;
2665:                    }
2666:                    return true;
2667:                }
2668:
2669:                private void updateValue() {
2670:                    value = getValue();
2671:                }
2672:
2673:                private static int getIndex(final String decorator) {
2674:                    for (int i = 0; i < DECORATIONS.length; i++) {
2675:                        if (DECORATIONS[i].equals(decorator)) {
2676:                            return i;
2677:                        }
2678:                    }
2679:                    return -1;
2680:                }
2681:            }
2682:
2683:            /**
2684:             * The storage for
2685:             * <a href="http://www.w3.org/TR/CSS1#length-units">'Length Units'</a>.
2686:             */
2687:            abstract static class Length implements  PropertyValueConverter {
2688:                static final int RELATIVE_UNITS_UNDEFINED = -1;
2689:                static final int RELATIVE_UNITS_PERCENTAGE = 0;
2690:                static final int RELATIVE_UNITS_EM = 1;
2691:                static final int RELATIVE_UNITS_EX = 2;
2692:
2693:                // Units convertion coefficients
2694:                static final float PX_TO_PT = 1.3f; // 96 / 72 = 4 / 3 = 1.3(3),
2695:                // which is approx. 1.3
2696:                // 96 - def screen resolution
2697:                static final float MM_TO_PT = 72f / 25.4f; // 1 in = 25.4 mm
2698:                static final float CM_TO_PT = 72f / 2.54f; // 1 in = 2.54 cm
2699:                static final float PC_TO_PT = 12f; // 1 pc = 12 pt
2700:                static final float IN_TO_PT = 72f; // 1 pt = 1/72 in
2701:
2702:                // This is based on CSS1 spec, http://www.w3.org/TR/CSS1#length-units.
2703:                static final Pattern NUMBER_PATTERN = Pattern
2704:                        .compile("(?:\\+|-)?(?:\\d+|\\d*\\.\\d+)");
2705:                private static final Pattern LENGTH_PATTERN = Pattern
2706:                        .compile("(?:\\+|-)?(?:\\d+|\\d*\\.\\d+)(?:pt|px|mm|cm|pc|in)");
2707:
2708:                final String sValue;
2709:                final int relativeUnits;
2710:
2711:                Length() {
2712:                    sValue = null;
2713:                    relativeUnits = RELATIVE_UNITS_UNDEFINED;
2714:                }
2715:
2716:                Length(final String sValue, final int rUnits) {
2717:                    this .sValue = sValue;
2718:                    this .relativeUnits = rUnits;
2719:                }
2720:
2721:                abstract PropertyValueConverter create(final String strValue,
2722:                        final float theValue, final int rUnits);
2723:
2724:                abstract Object resolveRelativeValue(final View view);
2725:
2726:                abstract Object getValue(final View view);
2727:
2728:                public PropertyValueConverter toCSS(final Object value) {
2729:                    if (value instanceof  String) {
2730:                        String strValue = (String) value;
2731:                        int rUnits = getRelativeUnits(strValue);
2732:                        float theValue = rUnits != RELATIVE_UNITS_UNDEFINED ? parseRelativeValue(
2733:                                strValue, rUnits)
2734:                                : parseAbsoluteValue(strValue);
2735:                        if (!Float.isNaN(theValue)) {
2736:                            return create(strValue, theValue, rUnits);
2737:                        }
2738:                    }
2739:                    return null;
2740:                }
2741:
2742:                public final Object fromCSS() {
2743:                    return getResolvedValue(null);
2744:                }
2745:
2746:                public final String toString() {
2747:                    return sValue;
2748:                }
2749:
2750:                protected static float parseAbsoluteValue(final String value) {
2751:                    if ("0".equals(value)) {
2752:                        return 0;
2753:                    }
2754:                    if (LENGTH_PATTERN.matcher(value).matches()) {
2755:                        final String units = value.substring(
2756:                                value.length() - 2, value.length());
2757:                        final float theValue = Float.parseFloat(value
2758:                                .substring(0, value.length() - 2));
2759:
2760:                        if ("pt".equals(units)) {
2761:                            return theValue;
2762:                        } else if ("px".equals(units)) {
2763:                            return theValue * PX_TO_PT;
2764:                        } else if ("mm".equals(units)) {
2765:                            return theValue * MM_TO_PT;
2766:                        } else if ("cm".equals(units)) {
2767:                            return theValue * CM_TO_PT;
2768:                        } else if ("pc".equals(units)) {
2769:                            return theValue * PC_TO_PT;
2770:                        } else if ("in".equals(units)) {
2771:                            return theValue * IN_TO_PT;
2772:                        }
2773:                    }
2774:
2775:                    return Float.NaN;
2776:                }
2777:
2778:                protected static float parseRelativeValue(final String value,
2779:                        final int relativeUnits) {
2780:                    String number;
2781:                    switch (relativeUnits) {
2782:                    case RELATIVE_UNITS_PERCENTAGE:
2783:                        number = value.substring(0, value.length() - 1);
2784:                        break;
2785:
2786:                    case RELATIVE_UNITS_EM:
2787:                    case RELATIVE_UNITS_EX:
2788:                        number = value.substring(0, value.length() - 2);
2789:                        break;
2790:
2791:                    default:
2792:                        return Float.NaN;
2793:                    }
2794:
2795:                    if (NUMBER_PATTERN.matcher(number).matches()) {
2796:                        return Float.parseFloat(number);
2797:                    }
2798:                    return Float.NaN;
2799:                }
2800:
2801:                protected static int getRelativeUnits(final String value) {
2802:                    if (Utilities.isEmptyString(value)) {
2803:                        return RELATIVE_UNITS_UNDEFINED;
2804:                    }
2805:
2806:                    if (value.endsWith("%")) {
2807:                        return RELATIVE_UNITS_PERCENTAGE;
2808:                    }
2809:                    if (value.endsWith("em")) {
2810:                        return RELATIVE_UNITS_EM;
2811:                    }
2812:                    if (value.endsWith("ex")) {
2813:                        return RELATIVE_UNITS_EX;
2814:                    }
2815:
2816:                    return RELATIVE_UNITS_UNDEFINED;
2817:                }
2818:
2819:                protected static boolean isRelative(final String value) {
2820:                    return getRelativeUnits(value) != RELATIVE_UNITS_UNDEFINED;
2821:                }
2822:
2823:                final boolean isRelative() {
2824:                    return relativeUnits != RELATIVE_UNITS_UNDEFINED;
2825:                }
2826:
2827:                final Object resolveRelativeValue() {
2828:                    return resolveRelativeValue(null);
2829:                }
2830:
2831:                final Object getValue() {
2832:                    return getValue(null);
2833:                }
2834:
2835:                final Object getResolvedValue(final View view) {
2836:                    if (isRelative()) {
2837:                        return resolveRelativeValue(view);
2838:                    }
2839:                    return getValue(view);
2840:                }
2841:
2842:                final float floatValue() {
2843:                    return floatValue((View) null);
2844:                }
2845:
2846:                final float floatValue(final View view) {
2847:                    return ((Number) getResolvedValue(view)).floatValue();
2848:                }
2849:
2850:                final float floatValue(final AttributeSet attrs) {
2851:                    return attrs instanceof  ViewAttributeSet ? floatValue(((ViewAttributeSet) attrs).view)
2852:                            : floatValue();
2853:                }
2854:
2855:                final int intValue() {
2856:                    return intValue((View) null);
2857:                }
2858:
2859:                final int intValue(final View view) {
2860:                    return ((Number) getResolvedValue(view)).intValue();
2861:                }
2862:
2863:                final int intValue(final AttributeSet attrs) {
2864:                    return attrs instanceof  ViewAttributeSet ? intValue(((ViewAttributeSet) attrs).view)
2865:                            : intValue();
2866:                }
2867:            }
2868:
2869:            /**
2870:             * The storage for all properties with fixed set of values.
2871:             */
2872:            abstract static class FixedSetValues implements 
2873:                    PropertyValueConverter {
2874:                abstract String[] getValidValues();
2875:
2876:                abstract PropertyValueConverter[] getValueHolders();
2877:
2878:                private final int index;
2879:
2880:                FixedSetValues(final int index) {
2881:                    this .index = index;
2882:                }
2883:
2884:                public PropertyValueConverter toCSS(final Object value) {
2885:                    if (value instanceof  String) {
2886:                        final int valueIndex = getValueIndex((String) value);
2887:                        return valueIndex >= 0 ? getValueHolder(valueIndex)
2888:                                : null;
2889:                    }
2890:                    return null;
2891:                }
2892:
2893:                public Object fromCSS() {
2894:                    return null;
2895:                }
2896:
2897:                final int getIndex() {
2898:                    return index;
2899:                }
2900:
2901:                final int getValueIndex(final String value) {
2902:                    final String[] validValues = getValidValues();
2903:                    for (int i = 0; i < validValues.length; i++) {
2904:                        if (validValues[i].equals(value)) {
2905:                            return i;
2906:                        }
2907:                    }
2908:                    return -1;
2909:                }
2910:
2911:                final PropertyValueConverter getValueHolder(final int valueIndex) {
2912:                    final PropertyValueConverter[] valueHolders = getValueHolders();
2913:                    if (valueHolders[valueIndex] == null) {
2914:                        valueHolders[valueIndex] = createValueHolder(valueIndex);
2915:                    }
2916:                    return valueHolders[valueIndex];
2917:                }
2918:
2919:                PropertyValueConverter createValueHolder(final int valueIndex) {
2920:                    throw new FixedSetValuesError(valueIndex, getClass()
2921:                            .getName());
2922:                }
2923:
2924:                public final String toString() {
2925:                    return getValidValues()[index];
2926:                }
2927:            }
2928:
2929:            static final class FixedSetValuesError extends Error {
2930:                FixedSetValuesError(final int index, final String className) {
2931:                    super (Messages.getString("swing.err.07", index, className)); //$NON-NLS-1$
2932:                }
2933:            }
2934:
2935:            /**
2936:             * Expands values of shorthand <a href="http://www.w3.org/TR/CSS1#margin">'margin'</a>
2937:             * and <a href="http://www.w3.org/TR/CSS1#padding">'padding'</a> properties into
2938:             * individual properties for each side.
2939:             */
2940:            static final class SpaceExpander implements 
2941:                    ShorthandPropertyExpander {
2942:                final Attribute[] keys;
2943:                final PropertyValueConverter factory;
2944:
2945:                private Pattern splitPattern;
2946:
2947:                SpaceExpander(final Attribute topKey, final Attribute rightKey,
2948:                        final Attribute bottomKey, final Attribute leftKey) {
2949:                    this (topKey, rightKey, bottomKey, leftKey,
2950:                            FloatValue.factory);
2951:                }
2952:
2953:                SpaceExpander(final Attribute topKey, final Attribute rightKey,
2954:                        final Attribute bottomKey, final Attribute leftKey,
2955:                        final PropertyValueConverter factory) {
2956:                    keys = new Attribute[] { topKey, rightKey, bottomKey,
2957:                            leftKey };
2958:                    this .factory = factory;
2959:                }
2960:
2961:                public void parseAndExpandProperty(
2962:                        final MutableAttributeSet attrs, final String value) {
2963:                    final String[] values = getSplitPattern().split(value);
2964:                    final Object[] parsed = new Object[4];
2965:                    for (int i = 0; i < parsed.length; i++) {
2966:                        if (i < values.length) {
2967:                            parsed[i] = factory.toCSS(values[i]);
2968:                        } else {
2969:                            parsed[i] = parsed[i >= 3 ? 1 : 0];
2970:                            // parsed[1] = parsed[0], i.e. right = top if only one value
2971:                            //                                          is specified
2972:                            // parsed[2] = parsed[0], i.e. bottom = top
2973:                            // parsed[3] = parsed[1], i.e. left = right
2974:                        }
2975:                        if (parsed[i] == null) {
2976:                            // Even if one portion is not recognized as a valid value,
2977:                            // the whole declaration must be ignored.
2978:                            return;
2979:                        }
2980:                    }
2981:
2982:                    for (int i = 0; i < parsed.length; i++) {
2983:                        attrs.addAttribute(keys[i], parsed[i]);
2984:                    }
2985:                }
2986:
2987:                private Pattern getSplitPattern() {
2988:                    if (splitPattern == null) {
2989:                        splitPattern = Pattern.compile("\\s+");
2990:                    }
2991:                    return splitPattern;
2992:                }
2993:            }
2994:
2995:            interface ViewUpdater {
2996:                void updateView();
2997:            }
2998:
2999:            private static final Attribute[] allAttributeKeys = {
3000:                    Attribute.BACKGROUND, Attribute.BACKGROUND_ATTACHMENT,
3001:                    Attribute.BACKGROUND_COLOR, Attribute.BACKGROUND_IMAGE,
3002:                    Attribute.BACKGROUND_POSITION, Attribute.BACKGROUND_REPEAT,
3003:                    Attribute.BORDER, Attribute.BORDER_BOTTOM,
3004:                    Attribute.BORDER_BOTTOM_WIDTH, Attribute.BORDER_COLOR,
3005:                    Attribute.BORDER_LEFT, Attribute.BORDER_LEFT_WIDTH,
3006:                    Attribute.BORDER_RIGHT, Attribute.BORDER_RIGHT_WIDTH,
3007:                    Attribute.BORDER_STYLE, Attribute.BORDER_TOP,
3008:                    Attribute.BORDER_TOP_WIDTH, Attribute.BORDER_WIDTH,
3009:                    Attribute.CLEAR, Attribute.COLOR, Attribute.DISPLAY,
3010:                    Attribute.FLOAT, Attribute.FONT, Attribute.FONT_FAMILY,
3011:                    Attribute.FONT_SIZE, Attribute.FONT_STYLE,
3012:                    Attribute.FONT_VARIANT, Attribute.FONT_WEIGHT,
3013:                    Attribute.HEIGHT, Attribute.LETTER_SPACING,
3014:                    Attribute.LINE_HEIGHT, Attribute.LIST_STYLE,
3015:                    Attribute.LIST_STYLE_IMAGE, Attribute.LIST_STYLE_POSITION,
3016:                    Attribute.LIST_STYLE_TYPE, Attribute.MARGIN,
3017:                    Attribute.MARGIN_BOTTOM, Attribute.MARGIN_LEFT,
3018:                    Attribute.MARGIN_RIGHT, Attribute.MARGIN_TOP,
3019:                    Attribute.PADDING, Attribute.PADDING_BOTTOM,
3020:                    Attribute.PADDING_LEFT, Attribute.PADDING_RIGHT,
3021:                    Attribute.PADDING_TOP, Attribute.TEXT_ALIGN,
3022:                    Attribute.TEXT_DECORATION, Attribute.TEXT_INDENT,
3023:                    Attribute.TEXT_TRANSFORM, Attribute.VERTICAL_ALIGN,
3024:                    Attribute.WHITE_SPACE, Attribute.WIDTH,
3025:                    Attribute.WORD_SPACING };
3026:
3027:            static final int TOP_SIDE = 0;
3028:            static final int RIGHT_SIDE = 1;
3029:            static final int BOTTOM_SIDE = 2;
3030:            static final int LEFT_SIDE = 3;
3031:
3032:            private static final HashMap nameToAttribute = new HashMap();
3033:            private static final HashMap styleConstantsToCSS = new HashMap();
3034:
3035:            static {
3036:                for (int i = 0; i < allAttributeKeys.length; i++) {
3037:                    nameToAttribute.put(allAttributeKeys[i].toString(),
3038:                            allAttributeKeys[i]);
3039:                    StyleContext
3040:                            .registerStaticAttributeKey(allAttributeKeys[i]);
3041:                }
3042:
3043:                styleConstantsToCSS.put(StyleConstants.Background,
3044:                        Attribute.BACKGROUND_COLOR);
3045:
3046:                styleConstantsToCSS.put(StyleConstants.Foreground,
3047:                        Attribute.COLOR);
3048:
3049:                styleConstantsToCSS.put(StyleConstants.FontFamily,
3050:                        Attribute.FONT_FAMILY);
3051:                styleConstantsToCSS.put(StyleConstants.FontSize,
3052:                        Attribute.FONT_SIZE);
3053:                styleConstantsToCSS.put(StyleConstants.Italic,
3054:                        Attribute.FONT_STYLE);
3055:                styleConstantsToCSS.put(StyleConstants.Bold,
3056:                        Attribute.FONT_WEIGHT);
3057:
3058:                styleConstantsToCSS.put(StyleConstants.SpaceAbove,
3059:                        Attribute.MARGIN_TOP);
3060:                styleConstantsToCSS.put(StyleConstants.RightIndent,
3061:                        Attribute.MARGIN_RIGHT);
3062:                styleConstantsToCSS.put(StyleConstants.SpaceBelow,
3063:                        Attribute.MARGIN_BOTTOM);
3064:                styleConstantsToCSS.put(StyleConstants.LeftIndent,
3065:                        Attribute.MARGIN_LEFT);
3066:
3067:                styleConstantsToCSS.put(StyleConstants.Alignment,
3068:                        Attribute.TEXT_ALIGN);
3069:                styleConstantsToCSS.put(StyleConstants.FirstLineIndent,
3070:                        Attribute.TEXT_INDENT);
3071:            }
3072:
3073:            public CSS() {
3074:            }
3075:
3076:            public static Attribute[] getAllAttributeKeys() {
3077:                return allAttributeKeys;
3078:            }
3079:
3080:            public static final Attribute getAttribute(final String name) {
3081:                return (Attribute) nameToAttribute.get(name);
3082:            }
3083:
3084:            /**
3085:             * Maps a <code>StyleConstants</code> attribute key
3086:             * to {@link CSS.Attribute}.
3087:             *
3088:             * @param attrKey the key to convert.
3089:             * @return the corresponding <code>CSS.Attribute</code>,
3090:             *         or <code>null</code> if no equivalent found.
3091:             */
3092:            static Object mapToCSS(final Object attrKey) {
3093:                return styleConstantsToCSS.get(attrKey);
3094:            }
3095:
3096:            /**
3097:             * Maps a <code>StyleConstants</code> attribute key
3098:             * to {@link CSS.Attribute}.
3099:             *
3100:             * @param attrKey the key to convert.
3101:             * @return <code>attrKey</code> if it is of type <code>CSS.Attribute</code>,
3102:             *         the corresponding <code>CSS.Attribute</code>,
3103:             *         or <code>null</code> if no equivalent found.
3104:             */
3105:            static Object mapToCSSForced(final Object attrKey) {
3106:                return attrKey instanceof  Attribute ? attrKey
3107:                        : mapToCSS(attrKey);
3108:            }
3109:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.