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: }
|