0001 /*
0002 * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025 package javax.swing.plaf.synth;
0026
0027 import java.awt.Color;
0028 import java.awt.Component;
0029 import java.awt.Font;
0030 import java.awt.Graphics;
0031 import java.awt.Image;
0032 import java.awt.Insets;
0033 import java.awt.Toolkit;
0034 import java.io.BufferedInputStream;
0035 import java.io.IOException;
0036 import java.io.InputStream;
0037 import java.net.MalformedURLException;
0038 import java.net.URL;
0039 import java.net.URLClassLoader;
0040 import java.text.ParseException;
0041 import java.util.ArrayList;
0042 import java.util.HashMap;
0043 import java.util.Locale;
0044 import java.util.Map;
0045 import java.util.StringTokenizer;
0046 import java.util.regex.PatternSyntaxException;
0047
0048 import javax.swing.ImageIcon;
0049 import javax.swing.JSplitPane;
0050 import javax.swing.SwingConstants;
0051 import javax.swing.UIDefaults;
0052 import javax.swing.plaf.ColorUIResource;
0053 import javax.swing.plaf.DimensionUIResource;
0054 import javax.swing.plaf.FontUIResource;
0055 import javax.swing.plaf.InsetsUIResource;
0056 import javax.swing.plaf.UIResource;
0057 import javax.xml.parsers.ParserConfigurationException;
0058 import javax.xml.parsers.SAXParser;
0059 import javax.xml.parsers.SAXParserFactory;
0060
0061 import org.xml.sax.AttributeList;
0062 import org.xml.sax.HandlerBase;
0063 import org.xml.sax.InputSource;
0064 import org.xml.sax.Locator;
0065 import org.xml.sax.SAXException;
0066 import org.xml.sax.SAXParseException;
0067
0068 import com.sun.beans.ObjectHandler;
0069
0070 /**
0071 * @version 1.23, 09/12/05
0072 */
0073 class SynthParser extends HandlerBase {
0074 //
0075 // Known element names
0076 //
0077 private static final String ELEMENT_SYNTH = "synth";
0078 private static final String ELEMENT_STYLE = "style";
0079 private static final String ELEMENT_STATE = "state";
0080 private static final String ELEMENT_FONT = "font";
0081 private static final String ELEMENT_COLOR = "color";
0082 private static final String ELEMENT_IMAGE_PAINTER = "imagePainter";
0083 private static final String ELEMENT_PAINTER = "painter";
0084 private static final String ELEMENT_PROPERTY = "property";
0085 private static final String ELEMENT_SYNTH_GRAPHICS = "graphicsUtils";
0086 private static final String ELEMENT_IMAGE_ICON = "imageIcon";
0087 private static final String ELEMENT_BIND = "bind";
0088 private static final String ELEMENT_BIND_KEY = "bindKey";
0089 private static final String ELEMENT_INSETS = "insets";
0090 private static final String ELEMENT_OPAQUE = "opaque";
0091 private static final String ELEMENT_DEFAULTS_PROPERTY = "defaultsProperty";
0092 private static final String ELEMENT_INPUT_MAP = "inputMap";
0093
0094 //
0095 // Known attribute names
0096 //
0097 private static final String ATTRIBUTE_ACTION = "action";
0098 private static final String ATTRIBUTE_ID = "id";
0099 private static final String ATTRIBUTE_IDREF = "idref";
0100 private static final String ATTRIBUTE_CLONE = "clone";
0101 private static final String ATTRIBUTE_VALUE = "value";
0102 private static final String ATTRIBUTE_NAME = "name";
0103 private static final String ATTRIBUTE_STYLE = "style";
0104 private static final String ATTRIBUTE_SIZE = "size";
0105 private static final String ATTRIBUTE_TYPE = "type";
0106 private static final String ATTRIBUTE_TOP = "top";
0107 private static final String ATTRIBUTE_LEFT = "left";
0108 private static final String ATTRIBUTE_BOTTOM = "bottom";
0109 private static final String ATTRIBUTE_RIGHT = "right";
0110 private static final String ATTRIBUTE_KEY = "key";
0111 private static final String ATTRIBUTE_SOURCE_INSETS = "sourceInsets";
0112 private static final String ATTRIBUTE_DEST_INSETS = "destinationInsets";
0113 private static final String ATTRIBUTE_PATH = "path";
0114 private static final String ATTRIBUTE_STRETCH = "stretch";
0115 private static final String ATTRIBUTE_PAINT_CENTER = "paintCenter";
0116 private static final String ATTRIBUTE_METHOD = "method";
0117 private static final String ATTRIBUTE_DIRECTION = "direction";
0118 private static final String ATTRIBUTE_CENTER = "center";
0119
0120 /**
0121 * Lazily created, used for anything we don't understand.
0122 */
0123 private ObjectHandler _handler;
0124
0125 /**
0126 * Indicates the depth of how many elements we've encountered but don't
0127 * understand. This is used when forwarding to beans persistance to know
0128 * when we hsould stop forwarding.
0129 */
0130 private int _depth;
0131
0132 /**
0133 * Factory that new styles are added to.
0134 */
0135 private DefaultSynthStyleFactory _factory;
0136
0137 /**
0138 * Array of state infos for the current style. These are pushed to the
0139 * style when </style> is received.
0140 */
0141 private java.util.List _stateInfos;
0142
0143 /**
0144 * Current style.
0145 */
0146 private ParsedSynthStyle _style;
0147
0148 /**
0149 * Current state info.
0150 */
0151 private ParsedSynthStyle.StateInfo _stateInfo;
0152
0153 /**
0154 * Bindings for the current InputMap
0155 */
0156 private java.util.List _inputMapBindings;
0157
0158 /**
0159 * ID for the input map. This is cached as
0160 * the InputMap is created AFTER the inputMapProperty has ended.
0161 */
0162 private String _inputMapID;
0163
0164 /**
0165 * Object references outside the scope of persistance.
0166 */
0167 private Map<String, Object> _mapping;
0168
0169 /**
0170 * Based URL used to resolve paths.
0171 */
0172 private URL _urlResourceBase;
0173
0174 /**
0175 * Based class used to resolve paths.
0176 */
0177 private Class<?> _classResourceBase;
0178
0179 /**
0180 * List of ColorTypes. This is populated in startColorType.
0181 */
0182 private java.util.List _colorTypes;
0183
0184 /**
0185 * defaultsPropertys are placed here.
0186 */
0187 private Map _defaultsMap;
0188
0189 /**
0190 * List of SynthStyle.Painters that will be applied to the current style.
0191 */
0192 private java.util.List _stylePainters;
0193
0194 /**
0195 * List of SynthStyle.Painters that will be applied to the current state.
0196 */
0197 private java.util.List _statePainters;
0198
0199 SynthParser() {
0200 _mapping = new HashMap<String, Object>();
0201 _stateInfos = new ArrayList();
0202 _colorTypes = new ArrayList();
0203 _inputMapBindings = new ArrayList();
0204 _stylePainters = new ArrayList();
0205 _statePainters = new ArrayList();
0206 }
0207
0208 /**
0209 * Parses a set of styles from <code>inputStream</code>, adding the
0210 * resulting styles to the passed in DefaultSynthStyleFactory.
0211 * Resources are resolved either from a URL or from a Class. When calling
0212 * this method, one of the URL or the Class must be null but not both at
0213 * the same time.
0214 *
0215 * @param inputStream XML document containing the styles to read
0216 * @param factory DefaultSynthStyleFactory that new styles are added to
0217 * @param urlResourceBase the URL used to resolve any resources, such as Images
0218 * @param classResourceBase the Class used to resolve any resources, such as Images
0219 * @param defaultsMap Map that UIDefaults properties are placed in
0220 */
0221 public void parse(InputStream inputStream,
0222 DefaultSynthStyleFactory factory, URL urlResourceBase,
0223 Class<?> classResourceBase, Map defaultsMap)
0224 throws ParseException, IllegalArgumentException {
0225 if (inputStream == null
0226 || factory == null
0227 || (urlResourceBase == null && classResourceBase == null)) {
0228 throw new IllegalArgumentException(
0229 "You must supply an InputStream, StyleFactory and Class or URL");
0230 }
0231
0232 assert (!(urlResourceBase != null && classResourceBase != null));
0233
0234 _factory = factory;
0235 _classResourceBase = classResourceBase;
0236 _urlResourceBase = urlResourceBase;
0237 _defaultsMap = defaultsMap;
0238 try {
0239 try {
0240 SAXParser saxParser = SAXParserFactory.newInstance()
0241 .newSAXParser();
0242 saxParser.parse(new BufferedInputStream(inputStream),
0243 this );
0244 } catch (ParserConfigurationException e) {
0245 throw new ParseException("Error parsing: " + e, 0);
0246 } catch (SAXException se) {
0247 throw new ParseException("Error parsing: " + se + " "
0248 + se.getException(), 0);
0249 } catch (IOException ioe) {
0250 throw new ParseException("Error parsing: " + ioe, 0);
0251 }
0252 } finally {
0253 reset();
0254 }
0255 }
0256
0257 /**
0258 * Returns the path to a resource.
0259 */
0260 private URL getResource(String path) {
0261 if (_classResourceBase != null) {
0262 return _classResourceBase.getResource(path);
0263 } else {
0264 try {
0265 return new URL(_urlResourceBase, path);
0266 } catch (MalformedURLException mue) {
0267 return null;
0268 }
0269 }
0270 }
0271
0272 /**
0273 * Clears our internal state.
0274 */
0275 private void reset() {
0276 _handler = null;
0277 _depth = 0;
0278 _mapping.clear();
0279 _stateInfos.clear();
0280 _colorTypes.clear();
0281 _statePainters.clear();
0282 _stylePainters.clear();
0283 }
0284
0285 /**
0286 * Returns true if we are forwarding to persistance.
0287 */
0288 private boolean isForwarding() {
0289 return (_depth > 0);
0290 }
0291
0292 /**
0293 * Handles beans persistance.
0294 */
0295 private ObjectHandler getHandler() {
0296 if (_handler == null) {
0297 if (_urlResourceBase != null) {
0298 // getHandler() is never called before parse() so it is safe
0299 // to create a URLClassLoader with _resourceBase.
0300 //
0301 // getResource(".") is called to ensure we have the directory
0302 // containing the resources in the case the resource base is a
0303 // .class file.
0304 URL[] urls = new URL[] { getResource(".") };
0305 ClassLoader parent = Thread.currentThread()
0306 .getContextClassLoader();
0307 ClassLoader urlLoader = new URLClassLoader(urls, parent);
0308 _handler = new ObjectHandler(null, urlLoader);
0309 } else {
0310 _handler = new ObjectHandler(null, _classResourceBase
0311 .getClassLoader());
0312 }
0313
0314 for (String key : _mapping.keySet()) {
0315 _handler.register(key, _mapping.get(key));
0316 }
0317 }
0318 return _handler;
0319 }
0320
0321 /**
0322 * If <code>value</code> is an instance of <code>type</code> it is
0323 * returned, otherwise a SAXException is thrown.
0324 */
0325 private Object checkCast(Object value, Class type)
0326 throws SAXException {
0327 if (!type.isInstance(value)) {
0328 throw new SAXException("Expected type " + type + " got "
0329 + value.getClass());
0330 }
0331 return value;
0332 }
0333
0334 /**
0335 * Returns an object created with id=key. If the object is not of
0336 * type type, this will throw an exception.
0337 */
0338 private Object lookup(String key, Class type) throws SAXException {
0339 Object value = null;
0340 if (_handler != null) {
0341 if ((value = _handler.lookup(key)) != null) {
0342 return checkCast(value, type);
0343 }
0344 }
0345 value = _mapping.get(key);
0346 if (value == null) {
0347 throw new SAXException("ID " + key
0348 + " has not been defined");
0349 }
0350 return checkCast(value, type);
0351 }
0352
0353 /**
0354 * Registers an object by name. This will throw an exception if an
0355 * object has already been registered under the given name.
0356 */
0357 private void register(String key, Object value) throws SAXException {
0358 if (key != null) {
0359 if (_mapping.get(key) != null
0360 || (_handler != null && _handler.lookup(key) != null)) {
0361 throw new SAXException("ID " + key
0362 + " is already defined");
0363 }
0364 if (_handler != null) {
0365 _handler.register(key, value);
0366 } else {
0367 _mapping.put(key, value);
0368 }
0369 }
0370 }
0371
0372 /**
0373 * Convenience method to return the next int, or throw if there are no
0374 * more valid ints.
0375 */
0376 private int nextInt(StringTokenizer tok, String errorMsg)
0377 throws SAXException {
0378 if (!tok.hasMoreTokens()) {
0379 throw new SAXException(errorMsg);
0380 }
0381 try {
0382 return Integer.parseInt(tok.nextToken());
0383 } catch (NumberFormatException nfe) {
0384 throw new SAXException(errorMsg);
0385 }
0386 }
0387
0388 /**
0389 * Convenience method to return an Insets object.
0390 */
0391 private Insets parseInsets(String insets, String errorMsg)
0392 throws SAXException {
0393 StringTokenizer tokenizer = new StringTokenizer(insets);
0394 return new Insets(nextInt(tokenizer, errorMsg), nextInt(
0395 tokenizer, errorMsg), nextInt(tokenizer, errorMsg),
0396 nextInt(tokenizer, errorMsg));
0397 }
0398
0399 //
0400 // The following methods are invoked from startElement/stopElement
0401 //
0402
0403 private void startStyle(AttributeList attributes)
0404 throws SAXException {
0405 String id = null;
0406
0407 _style = null;
0408 for (int i = attributes.getLength() - 1; i >= 0; i--) {
0409 String key = attributes.getName(i);
0410 if (key.equals(ATTRIBUTE_CLONE)) {
0411 _style = (ParsedSynthStyle) ((ParsedSynthStyle) lookup(
0412 attributes.getValue(i), ParsedSynthStyle.class))
0413 .clone();
0414 } else if (key.equals(ATTRIBUTE_ID)) {
0415 id = attributes.getValue(i);
0416 }
0417 }
0418 if (_style == null) {
0419 _style = new ParsedSynthStyle();
0420 }
0421 register(id, _style);
0422 }
0423
0424 private void endStyle() throws SAXException {
0425 int size = _stylePainters.size();
0426 if (size > 0) {
0427 _style
0428 .setPainters((ParsedSynthStyle.PainterInfo[]) _stylePainters
0429 .toArray(new ParsedSynthStyle.PainterInfo[size]));
0430 _stylePainters.clear();
0431 }
0432 size = _stateInfos.size();
0433 if (size > 0) {
0434 _style
0435 .setStateInfo((ParsedSynthStyle.StateInfo[]) _stateInfos
0436 .toArray(new ParsedSynthStyle.StateInfo[size]));
0437 _stateInfos.clear();
0438 }
0439 _style = null;
0440 }
0441
0442 private void startState(AttributeList attributes)
0443 throws SAXException {
0444 ParsedSynthStyle.StateInfo stateInfo = null;
0445 int state = 0;
0446 String id = null;
0447
0448 _stateInfo = null;
0449 for (int i = attributes.getLength() - 1; i >= 0; i--) {
0450 String key = attributes.getName(i);
0451 if (key.equals(ATTRIBUTE_ID)) {
0452 id = attributes.getValue(i);
0453 } else if (key.equals(ATTRIBUTE_IDREF)) {
0454 _stateInfo = (ParsedSynthStyle.StateInfo) lookup(
0455 attributes.getValue(i),
0456 ParsedSynthStyle.StateInfo.class);
0457 } else if (key.equals(ATTRIBUTE_CLONE)) {
0458 _stateInfo = (ParsedSynthStyle.StateInfo) ((ParsedSynthStyle.StateInfo) lookup(
0459 attributes.getValue(i),
0460 ParsedSynthStyle.StateInfo.class)).clone();
0461 } else if (key.equals(ATTRIBUTE_VALUE)) {
0462 StringTokenizer tokenizer = new StringTokenizer(
0463 attributes.getValue(i));
0464 while (tokenizer.hasMoreTokens()) {
0465 String stateString = tokenizer.nextToken()
0466 .toUpperCase().intern();
0467 if (stateString == "ENABLED") {
0468 state |= SynthConstants.ENABLED;
0469 } else if (stateString == "MOUSE_OVER") {
0470 state |= SynthConstants.MOUSE_OVER;
0471 } else if (stateString == "PRESSED") {
0472 state |= SynthConstants.PRESSED;
0473 } else if (stateString == "DISABLED") {
0474 state |= SynthConstants.DISABLED;
0475 } else if (stateString == "FOCUSED") {
0476 state |= SynthConstants.FOCUSED;
0477 } else if (stateString == "SELECTED") {
0478 state |= SynthConstants.SELECTED;
0479 } else if (stateString == "DEFAULT") {
0480 state |= SynthConstants.DEFAULT;
0481 } else if (stateString != "AND") {
0482 throw new SAXException("Unknown state: "
0483 + state);
0484 }
0485 }
0486 }
0487 }
0488 if (_stateInfo == null) {
0489 _stateInfo = new ParsedSynthStyle.StateInfo();
0490 }
0491 _stateInfo.setComponentState(state);
0492 register(id, _stateInfo);
0493 _stateInfos.add(_stateInfo);
0494 }
0495
0496 private void endState() throws SAXException {
0497 int size = _statePainters.size();
0498 if (size > 0) {
0499 _stateInfo
0500 .setPainters((ParsedSynthStyle.PainterInfo[]) _statePainters
0501 .toArray(new ParsedSynthStyle.PainterInfo[size]));
0502 _statePainters.clear();
0503 }
0504 _stateInfo = null;
0505 }
0506
0507 private void startFont(AttributeList attributes)
0508 throws SAXException {
0509 Font font = null;
0510 int style = Font.PLAIN;
0511 int size = 0;
0512 String id = null;
0513 String name = null;
0514
0515 for (int i = attributes.getLength() - 1; i >= 0; i--) {
0516 String key = attributes.getName(i);
0517 if (key.equals(ATTRIBUTE_ID)) {
0518 id = attributes.getValue(i);
0519 } else if (key.equals(ATTRIBUTE_IDREF)) {
0520 font = (Font) lookup(attributes.getValue(i), Font.class);
0521 } else if (key.equals(ATTRIBUTE_NAME)) {
0522 name = attributes.getValue(i);
0523 } else if (key.equals(ATTRIBUTE_SIZE)) {
0524 try {
0525 size = Integer.parseInt(attributes.getValue(i));
0526 } catch (NumberFormatException nfe) {
0527 throw new SAXException("Invalid font size: "
0528 + attributes.getValue(i));
0529 }
0530 } else if (key.equals(ATTRIBUTE_STYLE)) {
0531 StringTokenizer tok = new StringTokenizer(attributes
0532 .getValue(i));
0533 while (tok.hasMoreTokens()) {
0534 String token = tok.nextToken().intern();
0535 if (token == "BOLD") {
0536 style = ((style | Font.PLAIN) ^ Font.PLAIN)
0537 | Font.BOLD;
0538 } else if (token == "ITALIC") {
0539 style |= Font.ITALIC;
0540 }
0541 }
0542 }
0543 }
0544 if (font == null) {
0545 if (name == null) {
0546 throw new SAXException(
0547 "You must define a name for the font");
0548 }
0549 if (size == 0) {
0550 throw new SAXException(
0551 "You must define a size for the font");
0552 }
0553 font = new FontUIResource(name, style, size);
0554 } else if (name != null || size != 0 || style != Font.PLAIN) {
0555 throw new SAXException(
0556 "Name, size and style are not for use "
0557 + "with idref");
0558 }
0559 register(id, font);
0560 if (_stateInfo != null) {
0561 _stateInfo.setFont(font);
0562 } else if (_style != null) {
0563 _style.setFont(font);
0564 }
0565 }
0566
0567 private void startColor(AttributeList attributes)
0568 throws SAXException {
0569 Color color = null;
0570 String id = null;
0571
0572 _colorTypes.clear();
0573 for (int i = attributes.getLength() - 1; i >= 0; i--) {
0574 String key = attributes.getName(i);
0575 if (key.equals(ATTRIBUTE_ID)) {
0576 id = attributes.getValue(i);
0577 } else if (key.equals(ATTRIBUTE_IDREF)) {
0578 color = (Color) lookup(attributes.getValue(i),
0579 Color.class);
0580 } else if (key.equals(ATTRIBUTE_NAME)) {
0581 } else if (key.equals(ATTRIBUTE_VALUE)) {
0582 String value = attributes.getValue(i);
0583
0584 if (value.startsWith("#")) {
0585 try {
0586 int argb;
0587 boolean hasAlpha;
0588
0589 int length = value.length();
0590 if (length < 8) {
0591 // Just RGB, or some portion of it.
0592 argb = Integer.decode(value);
0593 hasAlpha = false;
0594 } else if (length == 8) {
0595 // Single character alpha: #ARRGGBB.
0596 argb = Integer.decode(value);
0597 hasAlpha = true;
0598 } else if (length == 9) {
0599 // Color has alpha and is of the form
0600 // #AARRGGBB.
0601 // The following split decoding is mandatory due to
0602 // Integer.decode() behavior which won't decode
0603 // hexadecimal values higher than #7FFFFFFF.
0604 // Thus, when an alpha channel is detected, it is
0605 // decoded separately from the RGB channels.
0606 int rgb = Integer.decode('#' + value
0607 .substring(3, 9));
0608 int a = Integer.decode(value
0609 .substring(0, 3));
0610 argb = (a << 24) | rgb;
0611 hasAlpha = true;
0612 } else {
0613 throw new SAXException(
0614 "Invalid Color value: " + value);
0615 }
0616
0617 color = new ColorUIResource(new Color(argb,
0618 hasAlpha));
0619 } catch (NumberFormatException nfe) {
0620 throw new SAXException("Invalid Color value: "
0621 + value);
0622 }
0623 } else {
0624 try {
0625 color = new ColorUIResource((Color) Color.class
0626 .getField(value.toUpperCase()).get(
0627 Color.class));
0628 } catch (NoSuchFieldException nsfe) {
0629 throw new SAXException("Invalid color name: "
0630 + value);
0631 } catch (IllegalAccessException iae) {
0632 throw new SAXException("Invalid color name: "
0633 + value);
0634 }
0635 }
0636 } else if (key.equals(ATTRIBUTE_TYPE)) {
0637 StringTokenizer tokenizer = new StringTokenizer(
0638 attributes.getValue(i));
0639 while (tokenizer.hasMoreTokens()) {
0640 String typeName = tokenizer.nextToken();
0641 int classIndex = typeName.lastIndexOf('.');
0642 Class typeClass;
0643
0644 if (classIndex == -1) {
0645 typeClass = ColorType.class;
0646 classIndex = 0;
0647 } else {
0648 try {
0649 typeClass = Class.forName(typeName
0650 .substring(0, classIndex));
0651 } catch (ClassNotFoundException cnfe) {
0652 throw new SAXException("Unknown class: "
0653 + typeName.substring(0, classIndex));
0654 }
0655 classIndex++;
0656 }
0657 try {
0658 _colorTypes.add((ColorType) checkCast(typeClass
0659 .getField(
0660 typeName.substring(classIndex,
0661 typeName.length()
0662 - classIndex))
0663 .get(typeClass), ColorType.class));
0664 } catch (NoSuchFieldException nsfe) {
0665 throw new SAXException(
0666 "Unable to find color type: "
0667 + typeName);
0668 } catch (IllegalAccessException iae) {
0669 throw new SAXException(
0670 "Unable to find color type: "
0671 + typeName);
0672 }
0673 }
0674 }
0675 }
0676 if (color == null) {
0677 throw new SAXException("color: you must specificy a value");
0678 }
0679 register(id, color);
0680 if (_stateInfo != null && _colorTypes.size() > 0) {
0681 Color[] colors = _stateInfo.getColors();
0682 int max = 0;
0683 for (int counter = _colorTypes.size() - 1; counter >= 0; counter--) {
0684 max = Math.max(max, ((ColorType) _colorTypes
0685 .get(counter)).getID());
0686 }
0687 if (colors == null || colors.length <= max) {
0688 Color[] newColors = new Color[max + 1];
0689 if (colors != null) {
0690 System.arraycopy(colors, 0, newColors, 0,
0691 colors.length);
0692 }
0693 colors = newColors;
0694 }
0695 for (int counter = _colorTypes.size() - 1; counter >= 0; counter--) {
0696 colors[((ColorType) _colorTypes.get(counter)).getID()] = color;
0697 }
0698 _stateInfo.setColors(colors);
0699 }
0700 }
0701
0702 private void startProperty(AttributeList attributes, Object property)
0703 throws SAXException {
0704 Object value = null;
0705 Object key = null;
0706 // Type of the value: 0=idref, 1=boolean, 2=dimension, 3=insets,
0707 // 4=integer,5=string
0708 int iType = 0;
0709 String aValue = null;
0710
0711 for (int i = attributes.getLength() - 1; i >= 0; i--) {
0712 String aName = attributes.getName(i);
0713 if (aName.equals(ATTRIBUTE_TYPE)) {
0714 String type = attributes.getValue(i).toUpperCase();
0715 if (type.equals("IDREF")) {
0716 iType = 0;
0717 } else if (type.equals("BOOLEAN")) {
0718 iType = 1;
0719 } else if (type.equals("DIMENSION")) {
0720 iType = 2;
0721 } else if (type.equals("INSETS")) {
0722 iType = 3;
0723 } else if (type.equals("INTEGER")) {
0724 iType = 4;
0725 } else if (type.equals("STRING")) {
0726 iType = 5;
0727 } else {
0728 throw new SAXException(
0729 property
0730 + " unknown type, use"
0731 + "idref, boolean, dimension, insets or integer");
0732 }
0733 } else if (aName.equals(ATTRIBUTE_VALUE)) {
0734 aValue = attributes.getValue(i);
0735 } else if (aName.equals(ATTRIBUTE_KEY)) {
0736 key = attributes.getValue(i);
0737 }
0738 }
0739 if (aValue != null) {
0740 switch (iType) {
0741 case 0: // idref
0742 value = lookup(aValue, Object.class);
0743 break;
0744 case 1: // boolean
0745 if (aValue.toUpperCase().equals("TRUE")) {
0746 value = Boolean.TRUE;
0747 } else {
0748 value = Boolean.FALSE;
0749 }
0750 break;
0751 case 2: // dimension
0752 StringTokenizer tok = new StringTokenizer(aValue);
0753 value = new DimensionUIResource(nextInt(tok,
0754 "Invalid dimension"), nextInt(tok,
0755 "Invalid dimension"));
0756 break;
0757 case 3: // insets
0758 value = parseInsets(aValue, property
0759 + " invalid insets");
0760 break;
0761 case 4: // integer
0762 try {
0763 value = new Integer(Integer.parseInt(aValue));
0764 } catch (NumberFormatException nfe) {
0765 throw new SAXException(property + " invalid value");
0766 }
0767 break;
0768 case 5: //string
0769 value = aValue;
0770 break;
0771 }
0772 }
0773 if (value == null || key == null) {
0774 throw new SAXException(property + ": you must supply a "
0775 + "key and value");
0776 }
0777 if (property == ELEMENT_DEFAULTS_PROPERTY) {
0778 _defaultsMap.put(key, value);
0779 } else if (_stateInfo != null) {
0780 if (_stateInfo.getData() == null) {
0781 _stateInfo.setData(new HashMap());
0782 }
0783 _stateInfo.getData().put(key, value);
0784 } else if (_style != null) {
0785 if (_style.getData() == null) {
0786 _style.setData(new HashMap());
0787 }
0788 _style.getData().put(key, value);
0789 }
0790 }
0791
0792 private void startGraphics(AttributeList attributes)
0793 throws SAXException {
0794 SynthGraphicsUtils graphics = null;
0795
0796 for (int i = attributes.getLength() - 1; i >= 0; i--) {
0797 String key = attributes.getName(i);
0798 if (key.equals(ATTRIBUTE_IDREF)) {
0799 graphics = (SynthGraphicsUtils) lookup(attributes
0800 .getValue(i), SynthGraphicsUtils.class);
0801 }
0802 }
0803 if (graphics == null) {
0804 throw new SAXException(
0805 "graphicsUtils: you must supply an idref");
0806 }
0807 if (_style != null) {
0808 _style.setGraphicsUtils(graphics);
0809 }
0810 }
0811
0812 private void startInsets(AttributeList attributes)
0813 throws SAXException {
0814 int top = 0;
0815 int bottom = 0;
0816 int left = 0;
0817 int right = 0;
0818 Insets insets = null;
0819 String id = null;
0820
0821 for (int i = attributes.getLength() - 1; i >= 0; i--) {
0822 String key = attributes.getName(i);
0823
0824 try {
0825 if (key.equals(ATTRIBUTE_IDREF)) {
0826 insets = (Insets) lookup(attributes.getValue(i),
0827 Insets.class);
0828 } else if (key.equals(ATTRIBUTE_ID)) {
0829 id = attributes.getValue(i);
0830 } else if (key.equals(ATTRIBUTE_TOP)) {
0831 top = Integer.parseInt(attributes.getValue(i));
0832 } else if (key.equals(ATTRIBUTE_LEFT)) {
0833 left = Integer.parseInt(attributes.getValue(i));
0834 } else if (key.equals(ATTRIBUTE_BOTTOM)) {
0835 bottom = Integer.parseInt(attributes.getValue(i));
0836 } else if (key.equals(ATTRIBUTE_RIGHT)) {
0837 right = Integer.parseInt(attributes.getValue(i));
0838 }
0839 } catch (NumberFormatException nfe) {
0840 throw new SAXException("insets: bad integer value for "
0841 + attributes.getValue(i));
0842 }
0843 }
0844 if (insets == null) {
0845 insets = new InsetsUIResource(top, left, bottom, right);
0846 }
0847 register(id, insets);
0848 if (_style != null) {
0849 _style.setInsets(insets);
0850 }
0851 }
0852
0853 private void startBind(AttributeList attributes)
0854 throws SAXException {
0855 ParsedSynthStyle style = null;
0856 String path = null;
0857 int type = -1;
0858
0859 for (int i = attributes.getLength() - 1; i >= 0; i--) {
0860 String key = attributes.getName(i);
0861
0862 if (key.equals(ATTRIBUTE_STYLE)) {
0863 style = (ParsedSynthStyle) lookup(attributes
0864 .getValue(i), ParsedSynthStyle.class);
0865 } else if (key.equals(ATTRIBUTE_TYPE)) {
0866 String typeS = attributes.getValue(i).toUpperCase();
0867
0868 if (typeS.equals("NAME")) {
0869 type = DefaultSynthStyleFactory.NAME;
0870 } else if (typeS.equals("REGION")) {
0871 type = DefaultSynthStyleFactory.REGION;
0872 } else {
0873 throw new SAXException("bind: unknown type "
0874 + typeS);
0875 }
0876 } else if (key.equals(ATTRIBUTE_KEY)) {
0877 path = attributes.getValue(i);
0878 }
0879 }
0880 if (style == null || path == null || type == -1) {
0881 throw new SAXException(
0882 "bind: you must specify a style, type " + "and key");
0883 }
0884 try {
0885 _factory.addStyle(style, path, type);
0886 } catch (PatternSyntaxException pse) {
0887 throw new SAXException("bind: " + path + " is not a valid "
0888 + "regular expression");
0889 }
0890 }
0891
0892 private void startPainter(AttributeList attributes, String type)
0893 throws SAXException {
0894 Insets sourceInsets = null;
0895 Insets destInsets = null;
0896 String path = null;
0897 boolean paintCenter = true;
0898 boolean stretch = true;
0899 SynthPainter painter = null;
0900 String method = null;
0901 String id = null;
0902 int direction = -1;
0903 boolean center = false;
0904
0905 boolean stretchSpecified = false;
0906 boolean paintCenterSpecified = false;
0907
0908 for (int i = attributes.getLength() - 1; i >= 0; i--) {
0909 String key = attributes.getName(i);
0910 String value = attributes.getValue(i);
0911
0912 if (key.equals(ATTRIBUTE_ID)) {
0913 id = value;
0914 } else if (key.equals(ATTRIBUTE_METHOD)) {
0915 method = value.toLowerCase(Locale.ENGLISH);
0916 } else if (key.equals(ATTRIBUTE_IDREF)) {
0917 painter = (SynthPainter) lookup(value,
0918 SynthPainter.class);
0919 } else if (key.equals(ATTRIBUTE_PATH)) {
0920 path = value;
0921 } else if (key.equals(ATTRIBUTE_SOURCE_INSETS)) {
0922 sourceInsets = parseInsets(
0923 value,
0924 type
0925 + ": sourceInsets must be top left bottom right");
0926 } else if (key.equals(ATTRIBUTE_DEST_INSETS)) {
0927 destInsets = parseInsets(
0928 value,
0929 type
0930 + ": destinationInsets must be top left bottom right");
0931 } else if (key.equals(ATTRIBUTE_PAINT_CENTER)) {
0932 paintCenter = value.toLowerCase().equals("true");
0933 paintCenterSpecified = true;
0934 } else if (key.equals(ATTRIBUTE_STRETCH)) {
0935 stretch = value.toLowerCase().equals("true");
0936 stretchSpecified = true;
0937 } else if (key.equals(ATTRIBUTE_DIRECTION)) {
0938 value = value.toUpperCase().intern();
0939 if (value == "EAST") {
0940 direction = SwingConstants.EAST;
0941 } else if (value == "NORTH") {
0942 direction = SwingConstants.NORTH;
0943 } else if (value == "SOUTH") {
0944 direction = SwingConstants.SOUTH;
0945 } else if (value == "WEST") {
0946 direction = SwingConstants.WEST;
0947 } else if (value == "TOP") {
0948 direction = SwingConstants.TOP;
0949 } else if (value == "LEFT") {
0950 direction = SwingConstants.LEFT;
0951 } else if (value == "BOTTOM") {
0952 direction = SwingConstants.BOTTOM;
0953 } else if (value == "RIGHT") {
0954 direction = SwingConstants.RIGHT;
0955 } else if (value == "HORIZONTAL") {
0956 direction = SwingConstants.HORIZONTAL;
0957 } else if (value == "VERTICAL") {
0958 direction = SwingConstants.VERTICAL;
0959 } else if (value == "HORIZONTAL_SPLIT") {
0960 direction = JSplitPane.HORIZONTAL_SPLIT;
0961 } else if (value == "VERTICAL_SPLIT") {
0962 direction = JSplitPane.VERTICAL_SPLIT;
0963 } else {
0964 throw new SAXException(type + ": unknown direction");
0965 }
0966 } else if (key.equals(ATTRIBUTE_CENTER)) {
0967 center = value.toLowerCase().equals("true");
0968 }
0969 }
0970 if (painter == null) {
0971 if (type == ELEMENT_PAINTER) {
0972 throw new SAXException(type
0973 + ": you must specify an idref");
0974 }
0975 if (sourceInsets == null && !center) {
0976 throw new SAXException(
0977 "property: you must specify sourceInsets");
0978 }
0979 if (path == null) {
0980 throw new SAXException(
0981 "property: you must specify a path");
0982 }
0983 if (center
0984 && (sourceInsets != null || destInsets != null
0985 || paintCenterSpecified || stretchSpecified)) {
0986 throw new SAXException("The attributes: sourceInsets, "
0987 + "destinationInsets, paintCenter and stretch "
0988 + " are not legal when center is true");
0989 }
0990 painter = new ImagePainter(!stretch, paintCenter,
0991 sourceInsets, destInsets, getResource(path), center);
0992 }
0993 register(id, painter);
0994 if (_stateInfo != null) {
0995 addPainterOrMerge(_statePainters, method, painter,
0996 direction);
0997 } else if (_style != null) {
0998 addPainterOrMerge(_stylePainters, method, painter,
0999 direction);
1000 }
1001 }
1002
1003 private void addPainterOrMerge(java.util.List painters,
1004 String method, SynthPainter painter, int direction) {
1005 ParsedSynthStyle.PainterInfo painterInfo;
1006 painterInfo = new ParsedSynthStyle.PainterInfo(method, painter,
1007 direction);
1008
1009 for (Object infoObject : painters) {
1010 ParsedSynthStyle.PainterInfo info;
1011 info = (ParsedSynthStyle.PainterInfo) infoObject;
1012
1013 if (painterInfo.equalsPainter(info)) {
1014 info.addPainter(painter);
1015 return;
1016 }
1017 }
1018
1019 painters.add(painterInfo);
1020 }
1021
1022 private void startImageIcon(AttributeList attributes)
1023 throws SAXException {
1024 String path = null;
1025 String id = null;
1026
1027 for (int i = attributes.getLength() - 1; i >= 0; i--) {
1028 String key = attributes.getName(i);
1029
1030 if (key.equals(ATTRIBUTE_ID)) {
1031 id = attributes.getValue(i);
1032 } else if (key.equals(ATTRIBUTE_PATH)) {
1033 path = attributes.getValue(i);
1034 }
1035 }
1036 if (path == null) {
1037 throw new SAXException("imageIcon: you must specify a path");
1038 }
1039 register(id, new LazyImageIcon(getResource(path)));
1040 }
1041
1042 private void startOpaque(AttributeList attributes)
1043 throws SAXException {
1044 if (_style != null) {
1045 _style.setOpaque(true);
1046 for (int i = attributes.getLength() - 1; i >= 0; i--) {
1047 String key = attributes.getName(i);
1048
1049 if (key.equals(ATTRIBUTE_VALUE)) {
1050 _style.setOpaque("true".equals(attributes.getValue(
1051 i).toLowerCase()));
1052 }
1053 }
1054 }
1055 }
1056
1057 private void startInputMap(AttributeList attributes)
1058 throws SAXException {
1059 _inputMapBindings.clear();
1060 _inputMapID = null;
1061 if (_style != null) {
1062 for (int i = attributes.getLength() - 1; i >= 0; i--) {
1063 String key = attributes.getName(i);
1064
1065 if (key.equals(ATTRIBUTE_ID)) {
1066 _inputMapID = attributes.getValue(i);
1067 }
1068 }
1069 }
1070 }
1071
1072 private void endInputMap() throws SAXException {
1073 if (_inputMapID != null) {
1074 register(_inputMapID, new UIDefaults.LazyInputMap(
1075 _inputMapBindings
1076 .toArray(new Object[_inputMapBindings
1077 .size()])));
1078 }
1079 _inputMapBindings.clear();
1080 _inputMapID = null;
1081 }
1082
1083 private void startBindKey(AttributeList attributes)
1084 throws SAXException {
1085 if (_inputMapID == null) {
1086 // Not in an inputmap, bail.
1087 return;
1088 }
1089 if (_style != null) {
1090 String key = null;
1091 String value = null;
1092 for (int i = attributes.getLength() - 1; i >= 0; i--) {
1093 String aKey = attributes.getName(i);
1094
1095 if (aKey.equals(ATTRIBUTE_KEY)) {
1096 key = attributes.getValue(i);
1097 } else if (aKey.equals(ATTRIBUTE_ACTION)) {
1098 value = attributes.getValue(i);
1099 }
1100 }
1101 if (key == null || value == null) {
1102 throw new SAXException(
1103 "bindKey: you must supply a key and action");
1104 }
1105 _inputMapBindings.add(key);
1106 _inputMapBindings.add(value);
1107 }
1108 }
1109
1110 //
1111 // SAX methods, these forward to the ObjectHandler if we don't know
1112 // the element name.
1113 //
1114
1115 public InputSource resolveEntity(String publicId, String systemId)
1116 throws SAXException {
1117 if (isForwarding()) {
1118 return getHandler().resolveEntity(publicId, systemId);
1119 }
1120 return null;
1121 }
1122
1123 public void notationDecl(String name, String publicId,
1124 String systemId) {
1125 if (isForwarding()) {
1126 getHandler().notationDecl(name, publicId, systemId);
1127 }
1128 }
1129
1130 public void unparsedEntityDecl(String name, String publicId,
1131 String systemId, String notationName) {
1132 if (isForwarding()) {
1133 getHandler().unparsedEntityDecl(name, publicId, systemId,
1134 notationName);
1135 }
1136 }
1137
1138 public void setDocumentLocator(Locator locator) {
1139 if (isForwarding()) {
1140 getHandler().setDocumentLocator(locator);
1141 }
1142 }
1143
1144 public void startDocument() throws SAXException {
1145 if (isForwarding()) {
1146 getHandler().startDocument();
1147 }
1148 }
1149
1150 public void endDocument() throws SAXException {
1151 if (isForwarding()) {
1152 getHandler().endDocument();
1153 }
1154 }
1155
1156 public void startElement(String name, AttributeList attributes)
1157 throws SAXException {
1158 name = name.intern();
1159 if (name == ELEMENT_STYLE) {
1160 startStyle(attributes);
1161 } else if (name == ELEMENT_STATE) {
1162 startState(attributes);
1163 } else if (name == ELEMENT_FONT) {
1164 startFont(attributes);
1165 } else if (name == ELEMENT_COLOR) {
1166 startColor(attributes);
1167 } else if (name == ELEMENT_PAINTER) {
1168 startPainter(attributes, name);
1169 } else if (name == ELEMENT_IMAGE_PAINTER) {
1170 startPainter(attributes, name);
1171 } else if (name == ELEMENT_PROPERTY) {
1172 startProperty(attributes, ELEMENT_PROPERTY);
1173 } else if (name == ELEMENT_DEFAULTS_PROPERTY) {
1174 startProperty(attributes, ELEMENT_DEFAULTS_PROPERTY);
1175 } else if (name == ELEMENT_SYNTH_GRAPHICS) {
1176 startGraphics(attributes);
1177 } else if (name == ELEMENT_INSETS) {
1178 startInsets(attributes);
1179 } else if (name == ELEMENT_BIND) {
1180 startBind(attributes);
1181 } else if (name == ELEMENT_BIND_KEY) {
1182 startBindKey(attributes);
1183 } else if (name == ELEMENT_IMAGE_ICON) {
1184 startImageIcon(attributes);
1185 } else if (name == ELEMENT_OPAQUE) {
1186 startOpaque(attributes);
1187 } else if (name == ELEMENT_INPUT_MAP) {
1188 startInputMap(attributes);
1189 } else if (name != ELEMENT_SYNTH) {
1190 if (_depth++ == 0) {
1191 getHandler().reset();
1192 }
1193 getHandler().startElement(name, attributes);
1194 }
1195 }
1196
1197 public void endElement(String name) throws SAXException {
1198 if (isForwarding()) {
1199 getHandler().endElement(name);
1200 _depth--;
1201 if (!isForwarding()) {
1202 getHandler().reset();
1203 }
1204 } else {
1205 name = name.intern();
1206 if (name == ELEMENT_STYLE) {
1207 endStyle();
1208 } else if (name == ELEMENT_STATE) {
1209 endState();
1210 } else if (name == ELEMENT_INPUT_MAP) {
1211 endInputMap();
1212 }
1213 }
1214 }
1215
1216 public void characters(char ch[], int start, int length)
1217 throws SAXException {
1218 if (isForwarding()) {
1219 getHandler().characters(ch, start, length);
1220 }
1221 }
1222
1223 public void ignorableWhitespace(char ch[], int start, int length)
1224 throws SAXException {
1225 if (isForwarding()) {
1226 getHandler().ignorableWhitespace(ch, start, length);
1227 }
1228 }
1229
1230 public void processingInstruction(String target, String data)
1231 throws SAXException {
1232 if (isForwarding()) {
1233 getHandler().processingInstruction(target, data);
1234 }
1235 }
1236
1237 public void warning(SAXParseException e) throws SAXException {
1238 if (isForwarding()) {
1239 getHandler().warning(e);
1240 }
1241 }
1242
1243 public void error(SAXParseException e) throws SAXException {
1244 if (isForwarding()) {
1245 getHandler().error(e);
1246 }
1247 }
1248
1249 public void fatalError(SAXParseException e) throws SAXException {
1250 if (isForwarding()) {
1251 getHandler().fatalError(e);
1252 }
1253 throw e;
1254 }
1255
1256 /**
1257 * ImageIcon that lazily loads the image until needed.
1258 */
1259 private static class LazyImageIcon extends ImageIcon implements
1260 UIResource {
1261 private URL location;
1262
1263 public LazyImageIcon(URL location) {
1264 super ();
1265 this .location = location;
1266 }
1267
1268 public void paintIcon(Component c, Graphics g, int x, int y) {
1269 if (getImage() != null) {
1270 super .paintIcon(c, g, x, y);
1271 }
1272 }
1273
1274 public int getIconWidth() {
1275 if (getImage() != null) {
1276 return super .getIconWidth();
1277 }
1278 return 0;
1279 }
1280
1281 public int getIconHeight() {
1282 if (getImage() != null) {
1283 return super .getIconHeight();
1284 }
1285 return 0;
1286 }
1287
1288 public Image getImage() {
1289 if (location != null) {
1290 setImage(Toolkit.getDefaultToolkit().getImage(location));
1291 location = null;
1292 }
1293 return super.getImage();
1294 }
1295 }
1296 }
|