1. /*
  2. * @(#)SynthParser.java 1.15 04/04/16
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.synth;
  8. import org.xml.sax.*;
  9. import java.awt.*;
  10. import java.io.*;
  11. import java.lang.reflect.*;
  12. import java.net.*;
  13. import java.text.*;
  14. import java.util.*;
  15. import java.util.regex.*;
  16. import javax.swing.*;
  17. import javax.swing.plaf.*;
  18. import javax.xml.parsers.*;
  19. import com.sun.beans.ObjectHandler;
  20. import sun.swing.plaf.synth.*;
  21. /**
  22. * @version 1.15, 04/16/04
  23. */
  24. class SynthParser extends HandlerBase {
  25. //
  26. // Known element names
  27. //
  28. private static final String ELEMENT_SYNTH = "synth";
  29. private static final String ELEMENT_STYLE = "style";
  30. private static final String ELEMENT_STATE = "state";
  31. private static final String ELEMENT_FONT = "font";
  32. private static final String ELEMENT_COLOR = "color";
  33. private static final String ELEMENT_IMAGE_PAINTER = "imagePainter";
  34. private static final String ELEMENT_PAINTER = "painter";
  35. private static final String ELEMENT_PROPERTY = "property";
  36. private static final String ELEMENT_SYNTH_GRAPHICS = "graphicsUtils";
  37. private static final String ELEMENT_IMAGE_ICON = "imageIcon";
  38. private static final String ELEMENT_BIND = "bind";
  39. private static final String ELEMENT_BIND_KEY = "bindKey";
  40. private static final String ELEMENT_INSETS = "insets";
  41. private static final String ELEMENT_OPAQUE = "opaque";
  42. private static final String ELEMENT_DEFAULTS_PROPERTY =
  43. "defaultsProperty";
  44. private static final String ELEMENT_INPUT_MAP = "inputMap";
  45. private static final String ELEMENT_BACKGROUND_IMAGE = "backgroundImage";
  46. //
  47. // Known attribute names
  48. //
  49. private static final String ATTRIBUTE_ACTION = "action";
  50. private static final String ATTRIBUTE_ID = "id";
  51. private static final String ATTRIBUTE_IDREF = "idref";
  52. private static final String ATTRIBUTE_CLONE = "clone";
  53. private static final String ATTRIBUTE_VALUE = "value";
  54. private static final String ATTRIBUTE_NAME = "name";
  55. private static final String ATTRIBUTE_STYLE = "style";
  56. private static final String ATTRIBUTE_SIZE = "size";
  57. private static final String ATTRIBUTE_TYPE = "type";
  58. private static final String ATTRIBUTE_TOP = "top";
  59. private static final String ATTRIBUTE_LEFT = "left";
  60. private static final String ATTRIBUTE_BOTTOM = "bottom";
  61. private static final String ATTRIBUTE_RIGHT = "right";
  62. private static final String ATTRIBUTE_KEY = "key";
  63. private static final String ATTRIBUTE_SOURCE_INSETS = "sourceInsets";
  64. private static final String ATTRIBUTE_DEST_INSETS = "destinationInsets";
  65. private static final String ATTRIBUTE_PATH = "path";
  66. private static final String ATTRIBUTE_STRETCH = "stretch";
  67. private static final String ATTRIBUTE_PAINT_CENTER = "paintCenter";
  68. private static final String ATTRIBUTE_METHOD = "method";
  69. private static final String ATTRIBUTE_DIRECTION = "direction";
  70. /**
  71. * Lazily created, used for anything we don't understand.
  72. */
  73. private ObjectHandler _handler;
  74. /**
  75. * Indicates the depth of how many elements we've encountered but don't
  76. * understand. This is used when forwarding to beans persistance to know
  77. * when we hsould stop forwarding.
  78. */
  79. private int _depth;
  80. /**
  81. * Factory that new styles are added to.
  82. */
  83. private DefaultSynthStyleFactory _factory;
  84. /**
  85. * Array of state infos for the current style. These are pushed to the
  86. * style when </style> is received.
  87. */
  88. private java.util.List _stateInfos;
  89. /**
  90. * Current style.
  91. */
  92. private ParsedSynthStyle _style;
  93. /**
  94. * Current state info.
  95. */
  96. private ParsedSynthStyle.StateInfo _stateInfo;
  97. /**
  98. * Bindings for the current InputMap
  99. */
  100. private java.util.List _inputMapBindings;
  101. /**
  102. * ID for the input map. This is cached as
  103. * the InputMap is created AFTER the inputMapProperty has ended.
  104. */
  105. private String _inputMapID;
  106. /**
  107. * Object references outside the scope of persistance.
  108. */
  109. private Map _mapping;
  110. /**
  111. * Based used to resolve paths.
  112. */
  113. private Class _resourceBase;
  114. /**
  115. * List of ColorTypes. This is populated in startColorType.
  116. */
  117. private java.util.List _colorTypes;
  118. /**
  119. * defaultsPropertys are placed here.
  120. */
  121. private Map _defaultsMap;
  122. /**
  123. * List of SynthStyle.Painters that will be applied to the current style.
  124. */
  125. private java.util.List _stylePainters;
  126. /**
  127. * List of SynthStyle.Painters that will be applied to the current state.
  128. */
  129. private java.util.List _statePainters;
  130. SynthParser() {
  131. _mapping = new HashMap();
  132. _stateInfos = new ArrayList();
  133. _colorTypes = new ArrayList();
  134. _inputMapBindings = new ArrayList();
  135. _stylePainters = new ArrayList();
  136. _statePainters = new ArrayList();
  137. }
  138. /**
  139. * Parses a set of styles from <code>inputStream</code>, adding the
  140. * resulting styles to the passed in DefaultSynthStyleFactory.
  141. *
  142. * @param inputStream XML document containing the styles to read
  143. * @param factory DefaultSynthStyleFactory that new styles are added to
  144. * @param resourceBase Class used to resolve any resources, such as Images
  145. * @param defaultsMap Map that UIDefaults properties are placed in
  146. */
  147. public void parse(InputStream inputStream,
  148. DefaultSynthStyleFactory factory,
  149. Class resourceBase, Map defaultsMap)
  150. throws ParseException, IllegalArgumentException {
  151. if (inputStream == null || factory == null || resourceBase == null) {
  152. throw new IllegalArgumentException(
  153. "You must supply an InputStream;, Class and StyleFactory");
  154. }
  155. _factory = factory;
  156. _resourceBase = resourceBase;
  157. _defaultsMap = defaultsMap;
  158. try {
  159. try {
  160. SAXParser saxParser = SAXParserFactory.newInstance().
  161. newSAXParser();
  162. saxParser.parse(new BufferedInputStream(inputStream), this);
  163. } catch (ParserConfigurationException e) {
  164. throw new ParseException("Error parsing: " + e, 0);
  165. }
  166. catch (SAXException se) {
  167. throw new ParseException("Error parsing: " + se + " " +
  168. se.getException(), 0);
  169. }
  170. catch (IOException ioe) {
  171. throw new ParseException("Error parsing: " + ioe, 0);
  172. }
  173. } finally {
  174. reset();
  175. }
  176. }
  177. /**
  178. * Returns the path to a resource.
  179. */
  180. private URL getResource(String path) {
  181. return _resourceBase.getResource(path);
  182. }
  183. /**
  184. * Clears our internal state.
  185. */
  186. private void reset() {
  187. _handler = null;
  188. _depth = 0;
  189. _mapping.clear();
  190. _stateInfos.clear();
  191. _colorTypes.clear();
  192. _statePainters.clear();
  193. _stylePainters.clear();
  194. }
  195. /**
  196. * Returns true if we are forwarding to persistance.
  197. */
  198. private boolean isForwarding() {
  199. return (_depth > 0);
  200. }
  201. /**
  202. * Handles beans persistance.
  203. */
  204. private ObjectHandler getHandler() {
  205. if (_handler == null) {
  206. _handler = new ObjectHandler();
  207. }
  208. return _handler;
  209. }
  210. /**
  211. * If <code>value</code> is an instance of <code>type</code> it is
  212. * returned, otherwise a SAXException is thrown.
  213. */
  214. private Object checkCast(Object value, Class type) throws SAXException {
  215. if (!type.isInstance(value)) {
  216. throw new SAXException("Expected type " + type + " got " +
  217. value.getClass());
  218. }
  219. return value;
  220. }
  221. /**
  222. * Returns an object created with id=key. If the object is not of
  223. * type type, this will throw an exception.
  224. */
  225. private Object lookup(String key, Class type) throws SAXException {
  226. Object value = null;
  227. if (_handler != null) {
  228. if ((value = _handler.lookup(key)) != null) {
  229. return checkCast(value, type);
  230. }
  231. }
  232. value = _mapping.get(key);
  233. if (value == null) {
  234. throw new SAXException("ID " + key + " has not been defined");
  235. }
  236. return checkCast(value, type);
  237. }
  238. /**
  239. * Registers an object by name. This will throw an exception if an
  240. * object has already been registered under the given name.
  241. */
  242. private void register(String key, Object value) throws SAXException {
  243. if (key != null) {
  244. if (_mapping.get(key) != null ||
  245. (_handler != null && _handler.lookup(key) != null)) {
  246. throw new SAXException("ID " + key + " is already defined");
  247. }
  248. _mapping.put(key, value);
  249. }
  250. }
  251. /**
  252. * Convenience method to return the next int, or throw if there are no
  253. * more valid ints.
  254. */
  255. private int nextInt(StringTokenizer tok, String errorMsg) throws
  256. SAXException {
  257. if (!tok.hasMoreTokens()) {
  258. throw new SAXException(errorMsg);
  259. }
  260. try {
  261. return Integer.parseInt(tok.nextToken());
  262. } catch (NumberFormatException nfe) {
  263. throw new SAXException(errorMsg);
  264. }
  265. }
  266. /**
  267. * Convenience method to return an Insets object.
  268. */
  269. private Insets parseInsets(String insets, String errorMsg) throws
  270. SAXException {
  271. StringTokenizer tokenizer = new StringTokenizer(insets);
  272. return new Insets(nextInt(tokenizer, errorMsg),
  273. nextInt(tokenizer, errorMsg),
  274. nextInt(tokenizer, errorMsg),
  275. nextInt(tokenizer, errorMsg));
  276. }
  277. //
  278. // The following methods are invoked from startElement/stopElement
  279. //
  280. private void startStyle(AttributeList attributes) throws SAXException {
  281. String id = null;
  282. _style = null;
  283. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  284. String key = attributes.getName(i);
  285. if (key.equals(ATTRIBUTE_CLONE)) {
  286. _style = (ParsedSynthStyle)((ParsedSynthStyle)lookup(
  287. attributes.getValue(i), ParsedSynthStyle.class)).
  288. clone();
  289. }
  290. else if (key.equals(ATTRIBUTE_ID)) {
  291. id = attributes.getValue(i);
  292. }
  293. }
  294. if (_style == null) {
  295. _style = new ParsedSynthStyle();
  296. }
  297. register(id, _style);
  298. }
  299. private void endStyle() throws SAXException {
  300. int size = _stylePainters.size();
  301. if (size > 0) {
  302. _style.setPainters((ParsedSynthStyle.PainterInfo[])
  303. _stylePainters.toArray(new ParsedSynthStyle.
  304. PainterInfo[size]));
  305. _stylePainters.clear();
  306. }
  307. size = _stateInfos.size();
  308. if (size > 0) {
  309. _style.setStateInfo((ParsedSynthStyle.StateInfo[])_stateInfos.
  310. toArray(new ParsedSynthStyle.StateInfo[size]));
  311. _stateInfos.clear();
  312. }
  313. _style = null;
  314. }
  315. private void startState(AttributeList attributes) throws SAXException {
  316. ParsedSynthStyle.StateInfo stateInfo = null;
  317. int state = 0;
  318. String id = null;
  319. _stateInfo = null;
  320. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  321. String key = attributes.getName(i);
  322. if (key.equals(ATTRIBUTE_ID)) {
  323. id = attributes.getValue(i);
  324. }
  325. else if (key.equals(ATTRIBUTE_IDREF)) {
  326. _stateInfo = (ParsedSynthStyle.StateInfo)lookup(
  327. attributes.getValue(i), ParsedSynthStyle.StateInfo.class);
  328. }
  329. else if (key.equals(ATTRIBUTE_CLONE)) {
  330. _stateInfo = (ParsedSynthStyle.StateInfo)((ParsedSynthStyle.
  331. StateInfo)lookup(attributes.getValue(i),
  332. ParsedSynthStyle.StateInfo.class)).clone();
  333. }
  334. else if (key.equals(ATTRIBUTE_VALUE)) {
  335. StringTokenizer tokenizer = new StringTokenizer(
  336. attributes.getValue(i));
  337. while (tokenizer.hasMoreTokens()) {
  338. String stateString = tokenizer.nextToken().toUpperCase().
  339. intern();
  340. if (stateString == "ENABLED") {
  341. state |= SynthConstants.ENABLED;
  342. }
  343. else if (stateString == "MOUSE_OVER") {
  344. state |= SynthConstants.MOUSE_OVER;
  345. }
  346. else if (stateString == "PRESSED") {
  347. state |= SynthConstants.PRESSED;
  348. }
  349. else if (stateString == "DISABLED") {
  350. state |= SynthConstants.DISABLED;
  351. }
  352. else if (stateString == "FOCUSED") {
  353. state |= SynthConstants.FOCUSED;
  354. }
  355. else if (stateString == "SELECTED") {
  356. state |= SynthConstants.SELECTED;
  357. }
  358. else if (stateString == "DEFAULT") {
  359. state |= SynthConstants.DEFAULT;
  360. }
  361. else if (stateString != "AND") {
  362. throw new SAXException("Unknown state: " + state);
  363. }
  364. }
  365. }
  366. }
  367. if (_stateInfo == null) {
  368. _stateInfo = new ParsedSynthStyle.StateInfo();
  369. }
  370. _stateInfo.setComponentState(state);
  371. register(id, _stateInfo);
  372. _stateInfos.add(_stateInfo);
  373. }
  374. private void endState() throws SAXException {
  375. int size = _statePainters.size();
  376. if (size > 0) {
  377. _stateInfo.setPainters((ParsedSynthStyle.PainterInfo[])
  378. _statePainters.toArray(new ParsedSynthStyle.
  379. PainterInfo[size]));
  380. _statePainters.clear();
  381. }
  382. _stateInfo = null;
  383. }
  384. private void startFont(AttributeList attributes) throws SAXException {
  385. Font font = null;
  386. int style = Font.PLAIN;
  387. int size = 0;
  388. String id = null;
  389. String name = null;
  390. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  391. String key = attributes.getName(i);
  392. if (key.equals(ATTRIBUTE_ID)) {
  393. id = attributes.getValue(i);
  394. }
  395. else if (key.equals(ATTRIBUTE_IDREF)) {
  396. font = (Font)lookup(attributes.getValue(i), Font.class);
  397. }
  398. else if (key.equals(ATTRIBUTE_NAME)) {
  399. name = attributes.getValue(i);
  400. }
  401. else if (key.equals(ATTRIBUTE_SIZE)) {
  402. try {
  403. size = Integer.parseInt(attributes.getValue(i));
  404. } catch (NumberFormatException nfe) {
  405. throw new SAXException("Invalid font size: " +
  406. attributes.getValue(i));
  407. }
  408. }
  409. else if (key.equals(ATTRIBUTE_STYLE)) {
  410. StringTokenizer tok = new StringTokenizer(
  411. attributes.getValue(i));
  412. while (tok.hasMoreTokens()) {
  413. String token = tok.nextToken().intern();
  414. if (token == "BOLD") {
  415. style = ((style | Font.PLAIN) ^ Font.PLAIN) |
  416. Font.BOLD;
  417. }
  418. else if (token == "ITALIC") {
  419. style |= Font.ITALIC;
  420. }
  421. }
  422. }
  423. }
  424. if (font == null) {
  425. if (name == null) {
  426. throw new SAXException("You must define a name for the font");
  427. }
  428. if (size == 0) {
  429. throw new SAXException("You must define a size for the font");
  430. }
  431. font = new FontUIResource(name, style, size);
  432. }
  433. else if (name != null || size != 0 || style != Font.PLAIN) {
  434. throw new SAXException("Name, size and style are not for use " +
  435. "with idref");
  436. }
  437. register(id, font);
  438. if (_stateInfo != null) {
  439. _stateInfo.setFont(font);
  440. }
  441. else if (_style != null) {
  442. _style.setFont(font);
  443. }
  444. }
  445. private void startColor(AttributeList attributes) throws SAXException {
  446. Color color = null;
  447. String id = null;
  448. _colorTypes.clear();
  449. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  450. String key = attributes.getName(i);
  451. if (key.equals(ATTRIBUTE_ID)) {
  452. id = attributes.getValue(i);
  453. }
  454. else if (key.equals(ATTRIBUTE_IDREF)) {
  455. color = (Color)lookup(attributes.getValue(i), Color.class);
  456. }
  457. else if (key.equals(ATTRIBUTE_NAME)) {
  458. }
  459. else if (key.equals(ATTRIBUTE_VALUE)) {
  460. String value = attributes.getValue(i);
  461. if (value.startsWith("#")) {
  462. try {
  463. int rgba = Integer.decode(value).intValue();
  464. color = new ColorUIResource(new Color(
  465. rgba, value.length() > 7));
  466. } catch (NumberFormatException nfe) {
  467. throw new SAXException("Invalid Color value: " +value);
  468. }
  469. }
  470. else {
  471. try {
  472. color = new ColorUIResource((Color)Color.class.
  473. getField(value.toUpperCase()).get(Color.class));
  474. } catch (NoSuchFieldException nsfe) {
  475. throw new SAXException("Invalid color name: " + value);
  476. } catch (IllegalAccessException iae) {
  477. throw new SAXException("Invalid color name: " + value);
  478. }
  479. }
  480. }
  481. else if (key.equals(ATTRIBUTE_TYPE)) {
  482. StringTokenizer tokenizer = new StringTokenizer(
  483. attributes.getValue(i));
  484. while (tokenizer.hasMoreTokens()) {
  485. String typeName = tokenizer.nextToken();
  486. int classIndex = typeName.lastIndexOf('.');
  487. Class typeClass;
  488. if (classIndex == -1) {
  489. typeClass = ColorType.class;
  490. classIndex = 0;
  491. }
  492. else {
  493. try {
  494. typeClass = Class.forName(typeName.substring(
  495. 0, classIndex));
  496. } catch (ClassNotFoundException cnfe) {
  497. throw new SAXException("Unknown class: " +
  498. typeName.substring(0, classIndex));
  499. }
  500. classIndex++;
  501. }
  502. try {
  503. _colorTypes.add((ColorType)checkCast(typeClass.
  504. getField(typeName.substring(classIndex,
  505. typeName.length() - classIndex)).
  506. get(typeClass), ColorType.class));
  507. } catch (NoSuchFieldException nsfe) {
  508. throw new SAXException("Unable to find color type: " +
  509. typeName);
  510. } catch (IllegalAccessException iae) {
  511. throw new SAXException("Unable to find color type: " +
  512. typeName);
  513. }
  514. }
  515. }
  516. }
  517. if (color == null) {
  518. throw new SAXException("color: you must specificy a value");
  519. }
  520. register(id, color);
  521. if (_stateInfo != null && _colorTypes.size() > 0) {
  522. Color[] colors = _stateInfo.getColors();
  523. int max = 0;
  524. for (int counter = _colorTypes.size() - 1; counter >= 0;
  525. counter--) {
  526. max = Math.max(max, ((ColorType)_colorTypes.get(counter)).
  527. getID());
  528. }
  529. if (colors == null || colors.length <= max) {
  530. Color[] newColors = new Color[max + 1];
  531. if (colors != null) {
  532. System.arraycopy(colors, 0, newColors, 0, colors.length);
  533. }
  534. colors = newColors;
  535. }
  536. for (int counter = _colorTypes.size() - 1; counter >= 0;
  537. counter--) {
  538. colors[((ColorType)_colorTypes.get(counter)).getID()] = color;
  539. }
  540. _stateInfo.setColors(colors);
  541. }
  542. }
  543. private void startProperty(AttributeList attributes,
  544. Object property) throws SAXException {
  545. Object value = null;
  546. Object key = null;
  547. // Type of the value: 0=idref, 1=boolean, 2=dimension, 3=insets,
  548. // 4=integer
  549. int iType = 0;
  550. String aValue = null;
  551. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  552. String aName = attributes.getName(i);
  553. if (aName.equals(ATTRIBUTE_TYPE)) {
  554. String type = attributes.getValue(i).toUpperCase();
  555. if (type.equals("IDREF")) {
  556. iType = 0;
  557. }
  558. else if (type.equals("BOOLEAN")) {
  559. iType = 1;
  560. }
  561. else if (type.equals("DIMENSION")) {
  562. iType = 2;
  563. }
  564. else if (type.equals("INSETS")) {
  565. iType = 3;
  566. }
  567. else if (type.equals("INTEGER")) {
  568. iType = 4;
  569. }
  570. else {
  571. throw new SAXException(property + " unknown type, use" +
  572. "idref, boolean, dimension, insets or integer");
  573. }
  574. }
  575. else if (aName.equals(ATTRIBUTE_VALUE)) {
  576. aValue = attributes.getValue(i);
  577. }
  578. else if (aName.equals(ATTRIBUTE_KEY)) {
  579. key = attributes.getValue(i);
  580. }
  581. }
  582. if (aValue != null) {
  583. switch (iType) {
  584. case 0: // idref
  585. value = lookup(aValue, Object.class);
  586. break;
  587. case 1: // boolean
  588. if (aValue.toUpperCase().equals("TRUE")) {
  589. value = Boolean.TRUE;
  590. }
  591. else {
  592. value = Boolean.FALSE;
  593. }
  594. break;
  595. case 2: // dimension
  596. StringTokenizer tok = new StringTokenizer(aValue);
  597. value = new DimensionUIResource(
  598. nextInt(tok, "Invalid dimension"),
  599. nextInt(tok, "Invalid dimension"));
  600. break;
  601. case 3: // insets
  602. value = parseInsets(aValue, property + " invalid insets");
  603. break;
  604. case 4: // integer
  605. try {
  606. value = new Integer(Integer.parseInt(aValue));
  607. } catch (NumberFormatException nfe) {
  608. throw new SAXException(property + " invalid value");
  609. }
  610. break;
  611. }
  612. }
  613. if (value == null || key == null) {
  614. throw new SAXException(property + ": you must supply a " +
  615. "key and value");
  616. }
  617. if (property == ELEMENT_DEFAULTS_PROPERTY) {
  618. _defaultsMap.put(key, value);
  619. }
  620. else if (_stateInfo != null) {
  621. if (_stateInfo.getData() == null) {
  622. _stateInfo.setData(new HashMap());
  623. }
  624. _stateInfo.getData().put(key, value);
  625. }
  626. else if (_style != null) {
  627. if (_style.getData() == null) {
  628. _style.setData(new HashMap());
  629. }
  630. _style.getData().put(key, value);
  631. }
  632. }
  633. private void startGraphics(AttributeList attributes) throws SAXException {
  634. SynthGraphicsUtils graphics = null;
  635. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  636. String key = attributes.getName(i);
  637. if (key.equals(ATTRIBUTE_IDREF)) {
  638. graphics = (SynthGraphicsUtils)lookup(attributes.getValue(i),
  639. SynthGraphicsUtils.class);
  640. }
  641. }
  642. if (graphics == null) {
  643. throw new SAXException("graphicsUtils: you must supply an idref");
  644. }
  645. if (_style != null) {
  646. _style.setGraphicsUtils(graphics);
  647. }
  648. }
  649. private void startInsets(AttributeList attributes) throws SAXException {
  650. int top = 0;
  651. int bottom = 0;
  652. int left = 0;
  653. int right = 0;
  654. Insets insets = null;
  655. String id = null;
  656. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  657. String key = attributes.getName(i);
  658. try {
  659. if (key.equals(ATTRIBUTE_IDREF)) {
  660. insets = (Insets)lookup(attributes.getValue(i),
  661. Insets.class);
  662. }
  663. else if (key.equals(ATTRIBUTE_ID)) {
  664. id = attributes.getValue(i);
  665. }
  666. else if (key.equals(ATTRIBUTE_TOP)) {
  667. top = Integer.parseInt(attributes.getValue(i));
  668. }
  669. else if (key.equals(ATTRIBUTE_LEFT)) {
  670. left = Integer.parseInt(attributes.getValue(i));
  671. }
  672. else if (key.equals(ATTRIBUTE_BOTTOM)) {
  673. bottom = Integer.parseInt(attributes.getValue(i));
  674. }
  675. else if (key.equals(ATTRIBUTE_RIGHT)) {
  676. right = Integer.parseInt(attributes.getValue(i));
  677. }
  678. } catch (NumberFormatException nfe) {
  679. throw new SAXException("insets: bad integer value for " +
  680. attributes.getValue(i));
  681. }
  682. }
  683. if (insets == null) {
  684. insets = new InsetsUIResource(top, left, bottom, right);
  685. }
  686. register(id, insets);
  687. if (_style != null) {
  688. _style.setInsets(insets);
  689. }
  690. }
  691. private void startBind(AttributeList attributes) throws SAXException {
  692. ParsedSynthStyle style = null;
  693. String path = null;
  694. int type = -1;
  695. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  696. String key = attributes.getName(i);
  697. if (key.equals(ATTRIBUTE_STYLE)) {
  698. style = (ParsedSynthStyle)lookup(attributes.getValue(i),
  699. ParsedSynthStyle.class);
  700. }
  701. else if (key.equals(ATTRIBUTE_TYPE)) {
  702. String typeS = attributes.getValue(i).toUpperCase();
  703. if (typeS.equals("NAME")) {
  704. type = DefaultSynthStyleFactory.NAME;
  705. }
  706. else if (typeS.equals("REGION")) {
  707. type = DefaultSynthStyleFactory.REGION;
  708. }
  709. else {
  710. throw new SAXException("bind: unknown type " + typeS);
  711. }
  712. }
  713. else if (key.equals(ATTRIBUTE_KEY)) {
  714. path = attributes.getValue(i);
  715. }
  716. }
  717. if (style == null || path == null || type == -1) {
  718. throw new SAXException("bind: you must specify a style, type " +
  719. "and key");
  720. }
  721. try {
  722. _factory.addStyle(style, path, type);
  723. } catch (PatternSyntaxException pse) {
  724. throw new SAXException("bind: " + path + " is not a valid " +
  725. "regular expression");
  726. }
  727. }
  728. private void startPainter(AttributeList attributes, String type) throws SAXException {
  729. Insets sourceInsets = null;
  730. Insets destInsets = null;
  731. String path = null;
  732. boolean paintCenter = true;
  733. boolean stretch = true;
  734. SynthPainter painter = null;
  735. String method = null;
  736. String id = null;
  737. int direction = -1;
  738. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  739. String key = attributes.getName(i);
  740. String value = attributes.getValue(i);
  741. if (key.equals(ATTRIBUTE_ID)) {
  742. id = value;
  743. }
  744. else if (key.equals(ATTRIBUTE_METHOD)) {
  745. method = value;
  746. }
  747. else if (key.equals(ATTRIBUTE_IDREF)) {
  748. painter = (SynthPainter)lookup(value, SynthPainter.class);
  749. }
  750. else if (key.equals(ATTRIBUTE_PATH)) {
  751. path = value;
  752. }
  753. else if (key.equals(ATTRIBUTE_SOURCE_INSETS)) {
  754. sourceInsets = parseInsets(value, type +
  755. ": sourceInsets must be top left bottom right");
  756. }
  757. else if (key.equals(ATTRIBUTE_DEST_INSETS)) {
  758. destInsets = parseInsets(value, type +
  759. ": destinationInsets must be top left bottom right");
  760. }
  761. else if (key.equals(ATTRIBUTE_PAINT_CENTER)) {
  762. paintCenter = value.toLowerCase().equals("true");
  763. }
  764. else if (key.equals(ATTRIBUTE_STRETCH)) {
  765. stretch = value.toLowerCase().equals("true");
  766. }
  767. else if (key.equals(ATTRIBUTE_DIRECTION)) {
  768. value = value.toUpperCase().intern();
  769. if (value == "EAST") {
  770. direction = SwingConstants.EAST;
  771. }
  772. else if (value == "NORTH") {
  773. direction = SwingConstants.NORTH;
  774. }
  775. else if (value == "SOUTH") {
  776. direction = SwingConstants.SOUTH;
  777. }
  778. else if (value == "WEST") {
  779. direction = SwingConstants.WEST;
  780. }
  781. else if (value == "HORIZONTAL") {
  782. direction = SwingConstants.HORIZONTAL;
  783. }
  784. else if (value == "VERTICAL") {
  785. direction = SwingConstants.VERTICAL;
  786. }
  787. else if (value == "HORIZONTAL_SPLIT") {
  788. direction = JSplitPane.HORIZONTAL_SPLIT;
  789. }
  790. else if (value == "VERTICAL_SPLIT") {
  791. direction = JSplitPane.VERTICAL_SPLIT;
  792. }
  793. else {
  794. throw new SAXException(type + ": unknown direction");
  795. }
  796. }
  797. }
  798. if (painter == null) {
  799. if (type == ELEMENT_PAINTER) {
  800. throw new SAXException(type +
  801. ": you must specify an idref");
  802. }
  803. if (sourceInsets == null) {
  804. throw new SAXException(
  805. "property: you must specify sourceInsets");
  806. }
  807. if (path == null) {
  808. throw new SAXException("property: you must specify a path");
  809. }
  810. painter = new ImagePainter(!stretch, paintCenter, null,
  811. sourceInsets, destInsets, getResource(path));
  812. }
  813. register(id, painter);
  814. if (_stateInfo != null) {
  815. _statePainters.add(new ParsedSynthStyle.PainterInfo(
  816. method, painter, direction));
  817. }
  818. else if (_style != null) {
  819. _stylePainters.add(new ParsedSynthStyle.PainterInfo(
  820. method, painter, direction));
  821. }
  822. }
  823. private void startImageIcon(AttributeList attributes) throws SAXException {
  824. String path = null;
  825. String id = null;
  826. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  827. String key = attributes.getName(i);
  828. if (key.equals(ATTRIBUTE_ID)) {
  829. id = attributes.getValue(i);
  830. }
  831. else if (key.equals(ATTRIBUTE_PATH)) {
  832. path = attributes.getValue(i);
  833. }
  834. }
  835. if (path == null) {
  836. throw new SAXException("imageIcon: you must specify a path");
  837. }
  838. register(id, new LazyImageIcon(getResource(path)));
  839. }
  840. private void startOpaque(AttributeList attributes) throws
  841. SAXException {
  842. if (_style != null) {
  843. _style.setOpaque(true);
  844. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  845. String key = attributes.getName(i);
  846. if (key.equals(ATTRIBUTE_VALUE)) {
  847. _style.setOpaque("true".equals(attributes.getValue(i).
  848. toLowerCase()));
  849. }
  850. }
  851. }
  852. }
  853. private void startInputMap(AttributeList attributes) throws SAXException {
  854. _inputMapBindings.clear();
  855. _inputMapID = null;
  856. if (_style != null) {
  857. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  858. String key = attributes.getName(i);
  859. if (key.equals(ATTRIBUTE_ID)) {
  860. _inputMapID = attributes.getValue(i);
  861. }
  862. }
  863. }
  864. }
  865. private void endInputMap() throws SAXException {
  866. if (_inputMapID != null) {
  867. register(_inputMapID, new UIDefaults.LazyInputMap(
  868. _inputMapBindings.toArray(new Object[_inputMapBindings.
  869. size()])));
  870. }
  871. _inputMapBindings.clear();
  872. _inputMapID = null;
  873. }
  874. private void startBindKey(AttributeList attributes) throws SAXException {
  875. if (_inputMapID == null) {
  876. // Not in an inputmap, bail.
  877. return;
  878. }
  879. if (_style != null) {
  880. String key = null;
  881. String value = null;
  882. for(int i = attributes.getLength() - 1; i >= 0; i--) {
  883. String aKey = attributes.getName(i);
  884. if (aKey.equals(ATTRIBUTE_KEY)) {
  885. key = attributes.getValue(i);
  886. }
  887. else if (aKey.equals(ATTRIBUTE_ACTION)) {
  888. value = attributes.getValue(i);
  889. }
  890. }
  891. if (key == null || value == null) {
  892. throw new SAXException(
  893. "bindKey: you must supply a key and action");
  894. }
  895. _inputMapBindings.add(key);
  896. _inputMapBindings.add(value);
  897. }
  898. }
  899. //
  900. // SAX methods, these forward to the ObjectHandler if we don't know
  901. // the element name.
  902. //
  903. public InputSource resolveEntity(String publicId, String systemId)
  904. throws SAXException {
  905. if (isForwarding()) {
  906. return getHandler().resolveEntity(publicId, systemId);
  907. }
  908. return null;
  909. }
  910. public void notationDecl(String name, String publicId, String systemId) {
  911. if (isForwarding()) {
  912. getHandler().notationDecl(name, publicId, systemId);
  913. }
  914. }
  915. public void unparsedEntityDecl(String name, String publicId,
  916. String systemId, String notationName) {
  917. if (isForwarding()) {
  918. getHandler().unparsedEntityDecl(name, publicId, systemId,
  919. notationName);
  920. }
  921. }
  922. public void setDocumentLocator(Locator locator) {
  923. if (isForwarding()) {
  924. getHandler().setDocumentLocator(locator);
  925. }
  926. }
  927. public void startDocument() throws SAXException {
  928. if (isForwarding()) {
  929. getHandler().startDocument();
  930. }
  931. }
  932. public void endDocument() throws SAXException {
  933. if (isForwarding()) {
  934. getHandler().endDocument();
  935. }
  936. }
  937. public void startElement(String name, AttributeList attributes)
  938. throws SAXException {
  939. name = name.intern();
  940. if (name == ELEMENT_STYLE) {
  941. startStyle(attributes);
  942. }
  943. else if (name == ELEMENT_STATE) {
  944. startState(attributes);
  945. }
  946. else if (name == ELEMENT_FONT) {
  947. startFont(attributes);
  948. }
  949. else if (name == ELEMENT_COLOR) {
  950. startColor(attributes);
  951. }
  952. else if (name == ELEMENT_PAINTER) {
  953. startPainter(attributes, name);
  954. }
  955. else if (name == ELEMENT_IMAGE_PAINTER) {
  956. startPainter(attributes, name);
  957. }
  958. else if (name == ELEMENT_PROPERTY) {
  959. startProperty(attributes, ELEMENT_PROPERTY);
  960. }
  961. else if (name == ELEMENT_DEFAULTS_PROPERTY) {
  962. startProperty(attributes, ELEMENT_DEFAULTS_PROPERTY);
  963. }
  964. else if (name == ELEMENT_SYNTH_GRAPHICS) {
  965. startGraphics(attributes);
  966. }
  967. else if (name == ELEMENT_INSETS) {
  968. startInsets(attributes);
  969. }
  970. else if (name == ELEMENT_BIND) {
  971. startBind(attributes);
  972. }
  973. else if (name == ELEMENT_BIND_KEY) {
  974. startBindKey(attributes);
  975. }
  976. else if (name == ELEMENT_IMAGE_ICON) {
  977. startImageIcon(attributes);
  978. }
  979. else if (name == ELEMENT_OPAQUE) {
  980. startOpaque(attributes);
  981. }
  982. else if (name == ELEMENT_INPUT_MAP) {
  983. startInputMap(attributes);
  984. }
  985. else if (name != ELEMENT_SYNTH) {
  986. if (_depth++ == 0) {
  987. getHandler().reset();
  988. }
  989. getHandler().startElement(name, attributes);
  990. }
  991. }
  992. public void endElement(String name) throws SAXException {
  993. if (isForwarding()) {
  994. getHandler().endElement(name);
  995. _depth--;
  996. if (!isForwarding()) {
  997. getHandler().reset();
  998. }
  999. }
  1000. else {
  1001. name = name.intern();
  1002. if (name == ELEMENT_STYLE) {
  1003. endStyle();
  1004. }
  1005. else if (name == ELEMENT_STATE) {
  1006. endState();
  1007. }
  1008. else if (name == ELEMENT_INPUT_MAP) {
  1009. endInputMap();
  1010. }
  1011. }
  1012. }
  1013. public void characters(char ch[], int start, int length)
  1014. throws SAXException {
  1015. if (isForwarding()) {
  1016. getHandler().characters(ch, start, length);
  1017. }
  1018. }
  1019. public void ignorableWhitespace (char ch[], int start, int length)
  1020. throws SAXException {
  1021. if (isForwarding()) {
  1022. getHandler().ignorableWhitespace(ch, start, length);
  1023. }
  1024. }
  1025. public void processingInstruction(String target, String data)
  1026. throws SAXException {
  1027. if (isForwarding()) {
  1028. getHandler().processingInstruction(target, data);
  1029. }
  1030. }
  1031. public void warning(SAXParseException e) throws SAXException {
  1032. if (isForwarding()) {
  1033. getHandler().warning(e);
  1034. }
  1035. }
  1036. public void error(SAXParseException e) throws SAXException {
  1037. if (isForwarding()) {
  1038. getHandler().error(e);
  1039. }
  1040. }
  1041. public void fatalError(SAXParseException e) throws SAXException {
  1042. if (isForwarding()) {
  1043. getHandler().fatalError(e);
  1044. }
  1045. throw e;
  1046. }
  1047. /**
  1048. * ImageIcon that lazily loads the image until needed.
  1049. */
  1050. private static class LazyImageIcon extends ImageIcon implements UIResource {
  1051. private URL location;
  1052. public LazyImageIcon(URL location) {
  1053. super();
  1054. this.location = location;
  1055. }
  1056. public void paintIcon(Component c, Graphics g, int x, int y) {
  1057. if (getImage() != null) {
  1058. super.paintIcon(c, g, x, y);
  1059. }
  1060. }
  1061. public int getIconWidth() {
  1062. if (getImage() != null) {
  1063. return super.getIconWidth();
  1064. }
  1065. return 0;
  1066. }
  1067. public int getIconHeight() {
  1068. if (getImage() != null) {
  1069. return super.getIconHeight();
  1070. }
  1071. return 0;
  1072. }
  1073. public Image getImage() {
  1074. if (location != null) {
  1075. setImage(Toolkit.getDefaultToolkit().getImage(location));
  1076. location = null;
  1077. }
  1078. return super.getImage();
  1079. }
  1080. }
  1081. }