1. /*
  2. * @(#)AccessibleHTML.java 1.13 03/12/19
  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.text.html;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import java.beans.*;
  11. import java.util.*;
  12. import javax.swing.*;
  13. import javax.swing.event.*;
  14. import javax.swing.text.*;
  15. import javax.accessibility.*;
  16. import java.text.BreakIterator;
  17. /*
  18. * The AccessibleHTML class provide information about the contents
  19. * of a HTML document to assistive technologies.
  20. *
  21. * @version 1.13 12/19/03
  22. * @author Lynn Monsanto
  23. */
  24. class AccessibleHTML implements Accessible {
  25. /**
  26. * The editor.
  27. */
  28. private JEditorPane editor;
  29. /**
  30. * Current model.
  31. */
  32. private Document model;
  33. /**
  34. * DocumentListener installed on the current model.
  35. */
  36. private DocumentListener docListener;
  37. /**
  38. * PropertyChangeListener installed on the editor
  39. */
  40. private PropertyChangeListener propChangeListener;
  41. /**
  42. * The root ElementInfo for the document
  43. */
  44. private ElementInfo rootElementInfo;
  45. /*
  46. * The root accessible context for the document
  47. */
  48. private RootHTMLAccessibleContext rootHTMLAccessibleContext;
  49. public AccessibleHTML(JEditorPane pane) {
  50. editor = pane;
  51. propChangeListener = new PropertyChangeHandler();
  52. setDocument(editor.getDocument());
  53. docListener = new DocumentHandler();
  54. }
  55. /**
  56. * Sets the document.
  57. */
  58. private void setDocument(Document document) {
  59. if (model != null) {
  60. model.removeDocumentListener(docListener);
  61. }
  62. if (editor != null) {
  63. editor.removePropertyChangeListener(propChangeListener);
  64. }
  65. this.model = document;
  66. if (model != null) {
  67. if (rootElementInfo != null) {
  68. rootElementInfo.invalidate(false);
  69. }
  70. buildInfo();
  71. model.addDocumentListener(docListener);
  72. }
  73. else {
  74. rootElementInfo = null;
  75. }
  76. if (editor != null) {
  77. editor.addPropertyChangeListener(propChangeListener);
  78. }
  79. }
  80. /**
  81. * Returns the Document currently presenting information for.
  82. */
  83. private Document getDocument() {
  84. return model;
  85. }
  86. /**
  87. * Returns the JEditorPane providing information for.
  88. */
  89. private JEditorPane getTextComponent() {
  90. return editor;
  91. }
  92. /**
  93. * Returns the ElementInfo representing the root Element.
  94. */
  95. private ElementInfo getRootInfo() {
  96. return rootElementInfo;
  97. }
  98. /**
  99. * Returns the root <code>View</code> associated with the current text
  100. * component.
  101. */
  102. private View getRootView() {
  103. return getTextComponent().getUI().getRootView(getTextComponent());
  104. }
  105. /**
  106. * Returns the bounds the root View will be rendered in.
  107. */
  108. private Rectangle getRootEditorRect() {
  109. Rectangle alloc = getTextComponent().getBounds();
  110. if ((alloc.width > 0) && (alloc.height > 0)) {
  111. alloc.x = alloc.y = 0;
  112. Insets insets = editor.getInsets();
  113. alloc.x += insets.left;
  114. alloc.y += insets.top;
  115. alloc.width -= insets.left + insets.right;
  116. alloc.height -= insets.top + insets.bottom;
  117. return alloc;
  118. }
  119. return null;
  120. }
  121. /**
  122. * If possible acquires a lock on the Document. If a lock has been
  123. * obtained a key will be retured that should be passed to
  124. * <code>unlock</code>.
  125. */
  126. private Object lock() {
  127. Document document = getDocument();
  128. if (document instanceof AbstractDocument) {
  129. ((AbstractDocument)document).readLock();
  130. return document;
  131. }
  132. return null;
  133. }
  134. /**
  135. * Releases a lock previously obtained via <code>lock</code>.
  136. */
  137. private void unlock(Object key) {
  138. if (key != null) {
  139. ((AbstractDocument)key).readUnlock();
  140. }
  141. }
  142. /**
  143. * Rebuilds the information from the current info.
  144. */
  145. private void buildInfo() {
  146. Object lock = lock();
  147. try {
  148. Document doc = getDocument();
  149. Element root = doc.getDefaultRootElement();
  150. rootElementInfo = new ElementInfo(root);
  151. rootElementInfo.validate();
  152. } finally {
  153. unlock(lock);
  154. }
  155. }
  156. /*
  157. * Create an ElementInfo subclass based on the passed in Element.
  158. */
  159. ElementInfo createElementInfo(Element e, ElementInfo parent) {
  160. AttributeSet attrs = e.getAttributes();
  161. if (attrs != null) {
  162. Object name = attrs.getAttribute(StyleConstants.NameAttribute);
  163. if (name == HTML.Tag.IMG) {
  164. return new IconElementInfo(e, parent);
  165. }
  166. else if (name == HTML.Tag.CONTENT || name == HTML.Tag.CAPTION) {
  167. return new TextElementInfo(e, parent);
  168. }
  169. else if (name == HTML.Tag.TABLE) {
  170. return new TableElementInfo(e, parent);
  171. }
  172. }
  173. return null;
  174. }
  175. /**
  176. * Returns the root AccessibleContext for the document
  177. */
  178. public AccessibleContext getAccessibleContext() {
  179. if (rootHTMLAccessibleContext == null) {
  180. rootHTMLAccessibleContext =
  181. new RootHTMLAccessibleContext(rootElementInfo);
  182. }
  183. return rootHTMLAccessibleContext;
  184. }
  185. /*
  186. * The roow AccessibleContext for the document
  187. */
  188. private class RootHTMLAccessibleContext extends HTMLAccessibleContext {
  189. public RootHTMLAccessibleContext(ElementInfo elementInfo) {
  190. super(elementInfo);
  191. }
  192. /**
  193. * Gets the accessibleName property of this object. The accessibleName
  194. * property of an object is a localized String that designates the purpose
  195. * of the object. For example, the accessibleName property of a label
  196. * or button might be the text of the label or button itself. In the
  197. * case of an object that doesn't display its name, the accessibleName
  198. * should still be set. For example, in the case of a text field used
  199. * to enter the name of a city, the accessibleName for the en_US locale
  200. * could be 'city.'
  201. *
  202. * @return the localized name of the object; null if this
  203. * object does not have a name
  204. *
  205. * @see #setAccessibleName
  206. */
  207. public String getAccessibleName() {
  208. if (model != null) {
  209. return (String)model.getProperty(Document.TitleProperty);
  210. } else {
  211. return null;
  212. }
  213. }
  214. /**
  215. * Gets the accessibleDescription property of this object. If this
  216. * property isn't set, returns the content type of this
  217. * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
  218. *
  219. * @return the localized description of the object; <code>null</code>
  220. * if this object does not have a description
  221. *
  222. * @see #setAccessibleName
  223. */
  224. public String getAccessibleDescription() {
  225. return editor.getContentType();
  226. }
  227. /**
  228. * Gets the role of this object. The role of the object is the generic
  229. * purpose or use of the class of this object. For example, the role
  230. * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
  231. * AccessibleRole are provided so component developers can pick from
  232. * a set of predefined roles. This enables assistive technologies to
  233. * provide a consistent interface to various tweaked subclasses of
  234. * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
  235. * that act like a push button) as well as distinguish between sublasses
  236. * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
  237. * and AccessibleRole.RADIO_BUTTON for radio buttons).
  238. * <p>Note that the AccessibleRole class is also extensible, so
  239. * custom component developers can define their own AccessibleRole's
  240. * if the set of predefined roles is inadequate.
  241. *
  242. * @return an instance of AccessibleRole describing the role of the object
  243. * @see AccessibleRole
  244. */
  245. public AccessibleRole getAccessibleRole() {
  246. return AccessibleRole.TEXT;
  247. }
  248. }
  249. /*
  250. * Base AccessibleContext class for HTML elements
  251. */
  252. protected abstract class HTMLAccessibleContext extends AccessibleContext
  253. implements Accessible, AccessibleComponent {
  254. protected ElementInfo elementInfo;
  255. public HTMLAccessibleContext(ElementInfo elementInfo) {
  256. this.elementInfo = elementInfo;
  257. }
  258. // begin AccessibleContext implementation ...
  259. public AccessibleContext getAccessibleContext() {
  260. return this;
  261. }
  262. /**
  263. * Gets the state set of this object.
  264. *
  265. * @return an instance of AccessibleStateSet describing the states
  266. * of the object
  267. * @see AccessibleStateSet
  268. */
  269. public AccessibleStateSet getAccessibleStateSet() {
  270. AccessibleStateSet states = new AccessibleStateSet();
  271. Component comp = getTextComponent();
  272. if (comp.isEnabled()) {
  273. states.add(AccessibleState.ENABLED);
  274. }
  275. if (comp instanceof JTextComponent &&
  276. ((JTextComponent)comp).isEditable()) {
  277. states.add(AccessibleState.EDITABLE);
  278. states.add(AccessibleState.FOCUSABLE);
  279. }
  280. if (comp.isVisible()) {
  281. states.add(AccessibleState.VISIBLE);
  282. }
  283. if (comp.isShowing()) {
  284. states.add(AccessibleState.SHOWING);
  285. }
  286. return states;
  287. }
  288. /**
  289. * Gets the 0-based index of this object in its accessible parent.
  290. *
  291. * @return the 0-based index of this object in its parent; -1 if this
  292. * object does not have an accessible parent.
  293. *
  294. * @see #getAccessibleParent
  295. * @see #getAccessibleChildrenCount
  296. * @see #getAccessibleChild
  297. */
  298. public int getAccessibleIndexInParent() {
  299. return elementInfo.getIndexInParent();
  300. }
  301. /**
  302. * Returns the number of accessible children of the object.
  303. *
  304. * @return the number of accessible children of the object.
  305. */
  306. public int getAccessibleChildrenCount() {
  307. return elementInfo.getChildCount();
  308. }
  309. /**
  310. * Returns the specified Accessible child of the object. The Accessible
  311. * children of an Accessible object are zero-based, so the first child
  312. * of an Accessible child is at index 0, the second child is at index 1,
  313. * and so on.
  314. *
  315. * @param i zero-based index of child
  316. * @return the Accessible child of the object
  317. * @see #getAccessibleChildrenCount
  318. */
  319. public Accessible getAccessibleChild(int i) {
  320. ElementInfo childInfo = elementInfo.getChild(i);
  321. if (childInfo != null && childInfo instanceof Accessible) {
  322. return (Accessible)childInfo;
  323. } else {
  324. return null;
  325. }
  326. }
  327. /**
  328. * Gets the locale of the component. If the component does not have a
  329. * locale, then the locale of its parent is returned.
  330. *
  331. * @return this component's locale. If this component does not have
  332. * a locale, the locale of its parent is returned.
  333. *
  334. * @exception IllegalComponentStateException
  335. * If the Component does not have its own locale and has not yet been
  336. * added to a containment hierarchy such that the locale can be
  337. * determined from the containing parent.
  338. */
  339. public Locale getLocale() throws IllegalComponentStateException {
  340. return editor.getLocale();
  341. }
  342. // ... end AccessibleContext implementation
  343. // begin AccessibleComponent implementation ...
  344. public AccessibleComponent getAccessibleComponent() {
  345. return this;
  346. }
  347. /**
  348. * Gets the background color of this object.
  349. *
  350. * @return the background color, if supported, of the object;
  351. * otherwise, null
  352. * @see #setBackground
  353. */
  354. public Color getBackground() {
  355. return getTextComponent().getBackground();
  356. }
  357. /**
  358. * Sets the background color of this object.
  359. *
  360. * @param c the new Color for the background
  361. * @see #setBackground
  362. */
  363. public void setBackground(Color c) {
  364. getTextComponent().setBackground(c);
  365. }
  366. /**
  367. * Gets the foreground color of this object.
  368. *
  369. * @return the foreground color, if supported, of the object;
  370. * otherwise, null
  371. * @see #setForeground
  372. */
  373. public Color getForeground() {
  374. return getTextComponent().getForeground();
  375. }
  376. /**
  377. * Sets the foreground color of this object.
  378. *
  379. * @param c the new Color for the foreground
  380. * @see #getForeground
  381. */
  382. public void setForeground(Color c) {
  383. getTextComponent().setForeground(c);
  384. }
  385. /**
  386. * Gets the Cursor of this object.
  387. *
  388. * @return the Cursor, if supported, of the object; otherwise, null
  389. * @see #setCursor
  390. */
  391. public Cursor getCursor() {
  392. return getTextComponent().getCursor();
  393. }
  394. /**
  395. * Sets the Cursor of this object.
  396. *
  397. * @param c the new Cursor for the object
  398. * @see #getCursor
  399. */
  400. public void setCursor(Cursor cursor) {
  401. getTextComponent().setCursor(cursor);
  402. }
  403. /**
  404. * Gets the Font of this object.
  405. *
  406. * @return the Font,if supported, for the object; otherwise, null
  407. * @see #setFont
  408. */
  409. public Font getFont() {
  410. return getTextComponent().getFont();
  411. }
  412. /**
  413. * Sets the Font of this object.
  414. *
  415. * @param f the new Font for the object
  416. * @see #getFont
  417. */
  418. public void setFont(Font f) {
  419. getTextComponent().setFont(f);
  420. }
  421. /**
  422. * Gets the FontMetrics of this object.
  423. *
  424. * @param f the Font
  425. * @return the FontMetrics, if supported, the object; otherwise, null
  426. * @see #getFont
  427. */
  428. public FontMetrics getFontMetrics(Font f) {
  429. return getTextComponent().getFontMetrics(f);
  430. }
  431. /**
  432. * Determines if the object is enabled. Objects that are enabled
  433. * will also have the AccessibleState.ENABLED state set in their
  434. * AccessibleStateSets.
  435. *
  436. * @return true if object is enabled; otherwise, false
  437. * @see #setEnabled
  438. * @see AccessibleContext#getAccessibleStateSet
  439. * @see AccessibleState#ENABLED
  440. * @see AccessibleStateSet
  441. */
  442. public boolean isEnabled() {
  443. return getTextComponent().isEnabled();
  444. }
  445. /**
  446. * Sets the enabled state of the object.
  447. *
  448. * @param b if true, enables this object; otherwise, disables it
  449. * @see #isEnabled
  450. */
  451. public void setEnabled(boolean b) {
  452. getTextComponent().setEnabled(b);
  453. }
  454. /**
  455. * Determines if the object is visible. Note: this means that the
  456. * object intends to be visible; however, it may not be
  457. * showing on the screen because one of the objects that this object
  458. * is contained by is currently not visible. To determine if an object
  459. * is showing on the screen, use isShowing().
  460. * <p>Objects that are visible will also have the
  461. * AccessibleState.VISIBLE state set in their AccessibleStateSets.
  462. *
  463. * @return true if object is visible; otherwise, false
  464. * @see #setVisible
  465. * @see AccessibleContext#getAccessibleStateSet
  466. * @see AccessibleState#VISIBLE
  467. * @see AccessibleStateSet
  468. */
  469. public boolean isVisible() {
  470. return getTextComponent().isVisible();
  471. }
  472. /**
  473. * Sets the visible state of the object.
  474. *
  475. * @param b if true, shows this object; otherwise, hides it
  476. * @see #isVisible
  477. */
  478. public void setVisible(boolean b) {
  479. getTextComponent().setVisible(b);
  480. }
  481. /**
  482. * Determines if the object is showing. This is determined by checking
  483. * the visibility of the object and its ancestors.
  484. * Note: this
  485. * will return true even if the object is obscured by another (for
  486. * example, it is underneath a menu that was pulled down).
  487. *
  488. * @return true if object is showing; otherwise, false
  489. */
  490. public boolean isShowing() {
  491. return getTextComponent().isShowing();
  492. }
  493. /**
  494. * Checks whether the specified point is within this object's bounds,
  495. * where the point's x and y coordinates are defined to be relative
  496. * to the coordinate system of the object.
  497. *
  498. * @param p the Point relative to the coordinate system of the object
  499. * @return true if object contains Point; otherwise false
  500. * @see #getBounds
  501. */
  502. public boolean contains(Point p) {
  503. Rectangle r = getBounds();
  504. if (r != null) {
  505. return r.contains(p.x, p.y);
  506. } else {
  507. return false;
  508. }
  509. }
  510. /**
  511. * Returns the location of the object on the screen.
  512. *
  513. * @return the location of the object on screen; null if this object
  514. * is not on the screen
  515. * @see #getBounds
  516. * @see #getLocation
  517. */
  518. public Point getLocationOnScreen() {
  519. Point editorLocation = getTextComponent().getLocationOnScreen();
  520. Rectangle r = getBounds();
  521. if (r != null) {
  522. return new Point(editorLocation.x + r.x,
  523. editorLocation.y + r.y);
  524. } else {
  525. return null;
  526. }
  527. }
  528. /**
  529. * Gets the location of the object relative to the parent in the form
  530. * of a point specifying the object's top-left corner in the screen's
  531. * coordinate space.
  532. *
  533. * @return An instance of Point representing the top-left corner of the
  534. * object's bounds in the coordinate space of the screen; null if
  535. * this object or its parent are not on the screen
  536. * @see #getBounds
  537. * @see #getLocationOnScreen
  538. */
  539. public Point getLocation() {
  540. Rectangle r = getBounds();
  541. if (r != null) {
  542. return new Point(r.x, r.y);
  543. } else {
  544. return null;
  545. }
  546. }
  547. /**
  548. * Sets the location of the object relative to the parent.
  549. * @param p the new position for the top-left corner
  550. * @see #getLocation
  551. */
  552. public void setLocation(Point p) {
  553. }
  554. /**
  555. * Gets the bounds of this object in the form of a Rectangle object.
  556. * The bounds specify this object's width, height, and location
  557. * relative to its parent.
  558. *
  559. * @return A rectangle indicating this component's bounds; null if
  560. * this object is not on the screen.
  561. * @see #contains
  562. */
  563. public Rectangle getBounds() {
  564. return elementInfo.getBounds();
  565. }
  566. /**
  567. * Sets the bounds of this object in the form of a Rectangle object.
  568. * The bounds specify this object's width, height, and location
  569. * relative to its parent.
  570. *
  571. * @param r rectangle indicating this component's bounds
  572. * @see #getBounds
  573. */
  574. public void setBounds(Rectangle r) {
  575. }
  576. /**
  577. * Returns the size of this object in the form of a Dimension object.
  578. * The height field of the Dimension object contains this object's
  579. * height, and the width field of the Dimension object contains this
  580. * object's width.
  581. *
  582. * @return A Dimension object that indicates the size of this component;
  583. * null if this object is not on the screen
  584. * @see #setSize
  585. */
  586. public Dimension getSize() {
  587. Rectangle r = getBounds();
  588. if (r != null) {
  589. return new Dimension(r.width, r.height);
  590. } else {
  591. return null;
  592. }
  593. }
  594. /**
  595. * Resizes this object so that it has width and height.
  596. *
  597. * @param d The dimension specifying the new size of the object.
  598. * @see #getSize
  599. */
  600. public void setSize(Dimension d) {
  601. Component comp = getTextComponent();
  602. comp.setSize(d);
  603. }
  604. /**
  605. * Returns the Accessible child, if one exists, contained at the local
  606. * coordinate Point.
  607. *
  608. * @param p The point relative to the coordinate system of this object.
  609. * @return the Accessible, if it exists, at the specified location;
  610. * otherwise null
  611. */
  612. public Accessible getAccessibleAt(Point p) {
  613. ElementInfo innerMostElement = getElementInfoAt(rootElementInfo, p);
  614. if (innerMostElement instanceof Accessible) {
  615. return (Accessible)innerMostElement;
  616. } else {
  617. return null;
  618. }
  619. }
  620. private ElementInfo getElementInfoAt(ElementInfo elementInfo, Point p) {
  621. if (elementInfo.getBounds() == null) {
  622. return null;
  623. }
  624. if (elementInfo.getChildCount() == 0 &&
  625. elementInfo.getBounds().contains(p)) {
  626. return elementInfo;
  627. } else {
  628. if (elementInfo instanceof TableElementInfo) {
  629. // Handle table caption as a special case since it's the
  630. // only table child that is not a table row.
  631. ElementInfo captionInfo =
  632. ((TableElementInfo)elementInfo).getCaptionInfo();
  633. if (captionInfo != null) {
  634. Rectangle bounds = captionInfo.getBounds();
  635. if (bounds != null && bounds.contains(p)) {
  636. return captionInfo;
  637. }
  638. }
  639. }
  640. for (int i = 0; i < elementInfo.getChildCount(); i++)
  641. {
  642. ElementInfo childInfo = elementInfo.getChild(i);
  643. ElementInfo retValue = getElementInfoAt(childInfo, p);
  644. if (retValue != null) {
  645. return retValue;
  646. }
  647. }
  648. }
  649. return null;
  650. }
  651. /**
  652. * Returns whether this object can accept focus or not. Objects that
  653. * can accept focus will also have the AccessibleState.FOCUSABLE state
  654. * set in their AccessibleStateSets.
  655. *
  656. * @return true if object can accept focus; otherwise false
  657. * @see AccessibleContext#getAccessibleStateSet
  658. * @see AccessibleState#FOCUSABLE
  659. * @see AccessibleState#FOCUSED
  660. * @see AccessibleStateSet
  661. */
  662. public boolean isFocusTraversable() {
  663. Component comp = getTextComponent();
  664. if (comp instanceof JTextComponent) {
  665. if (((JTextComponent)comp).isEditable()) {
  666. return true;
  667. }
  668. }
  669. return false;
  670. }
  671. /**
  672. * Requests focus for this object. If this object cannot accept focus,
  673. * nothing will happen. Otherwise, the object will attempt to take
  674. * focus.
  675. * @see #isFocusTraversable
  676. */
  677. public void requestFocus() {
  678. // TIGER - 4856191
  679. if (! isFocusTraversable()) {
  680. return;
  681. }
  682. Component comp = getTextComponent();
  683. if (comp instanceof JTextComponent) {
  684. comp.requestFocusInWindow();
  685. try {
  686. if (elementInfo.validateIfNecessary()) {
  687. // set the caret position to the start of this component
  688. Element elem = elementInfo.getElement();
  689. ((JTextComponent)comp).setCaretPosition(elem.getStartOffset());
  690. // fire a AccessibleState.FOCUSED property change event
  691. AccessibleContext ac = editor.getAccessibleContext();
  692. PropertyChangeEvent pce = new PropertyChangeEvent(this,
  693. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  694. null, AccessibleState.FOCUSED);
  695. ac.firePropertyChange(
  696. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  697. null, pce);
  698. }
  699. } catch (IllegalArgumentException e) {
  700. // don't fire property change event
  701. }
  702. }
  703. }
  704. /**
  705. * Adds the specified focus listener to receive focus events from this
  706. * component.
  707. *
  708. * @param l the focus listener
  709. * @see #removeFocusListener
  710. */
  711. public void addFocusListener(FocusListener l) {
  712. getTextComponent().addFocusListener(l);
  713. }
  714. /**
  715. * Removes the specified focus listener so it no longer receives focus
  716. * events from this component.
  717. *
  718. * @param l the focus listener
  719. * @see #addFocusListener
  720. */
  721. public void removeFocusListener(FocusListener l) {
  722. getTextComponent().removeFocusListener(l);
  723. }
  724. // ... end AccessibleComponent implementation
  725. } // ... end HTMLAccessibleContext
  726. /*
  727. * ElementInfo for text
  728. */
  729. class TextElementInfo extends ElementInfo implements Accessible {
  730. TextElementInfo(Element element, ElementInfo parent) {
  731. super(element, parent);
  732. }
  733. // begin AccessibleText implementation ...
  734. private AccessibleContext accessibleContext;
  735. public AccessibleContext getAccessibleContext() {
  736. if (accessibleContext == null) {
  737. accessibleContext = new TextAccessibleContext(this);
  738. }
  739. return accessibleContext;
  740. }
  741. /*
  742. * AccessibleContext for text elements
  743. */
  744. public class TextAccessibleContext extends HTMLAccessibleContext
  745. implements AccessibleText {
  746. public TextAccessibleContext(ElementInfo elementInfo) {
  747. super(elementInfo);
  748. }
  749. public AccessibleText getAccessibleText() {
  750. return this;
  751. }
  752. /**
  753. * Gets the accessibleName property of this object. The accessibleName
  754. * property of an object is a localized String that designates the purpose
  755. * of the object. For example, the accessibleName property of a label
  756. * or button might be the text of the label or button itself. In the
  757. * case of an object that doesn't display its name, the accessibleName
  758. * should still be set. For example, in the case of a text field used
  759. * to enter the name of a city, the accessibleName for the en_US locale
  760. * could be 'city.'
  761. *
  762. * @return the localized name of the object; null if this
  763. * object does not have a name
  764. *
  765. * @see #setAccessibleName
  766. */
  767. public String getAccessibleName() {
  768. if (model != null) {
  769. return (String)model.getProperty(Document.TitleProperty);
  770. } else {
  771. return null;
  772. }
  773. }
  774. /**
  775. * Gets the accessibleDescription property of this object. If this
  776. * property isn't set, returns the content type of this
  777. * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
  778. *
  779. * @return the localized description of the object; <code>null</code>
  780. * if this object does not have a description
  781. *
  782. * @see #setAccessibleName
  783. */
  784. public String getAccessibleDescription() {
  785. return editor.getContentType();
  786. }
  787. /**
  788. * Gets the role of this object. The role of the object is the generic
  789. * purpose or use of the class of this object. For example, the role
  790. * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
  791. * AccessibleRole are provided so component developers can pick from
  792. * a set of predefined roles. This enables assistive technologies to
  793. * provide a consistent interface to various tweaked subclasses of
  794. * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
  795. * that act like a push button) as well as distinguish between sublasses
  796. * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
  797. * and AccessibleRole.RADIO_BUTTON for radio buttons).
  798. * <p>Note that the AccessibleRole class is also extensible, so
  799. * custom component developers can define their own AccessibleRole's
  800. * if the set of predefined roles is inadequate.
  801. *
  802. * @return an instance of AccessibleRole describing the role of the object
  803. * @see AccessibleRole
  804. */
  805. public AccessibleRole getAccessibleRole() {
  806. return AccessibleRole.TEXT;
  807. }
  808. /**
  809. * Given a point in local coordinates, return the zero-based index
  810. * of the character under that Point. If the point is invalid,
  811. * this method returns -1.
  812. *
  813. * @param p the Point in local coordinates
  814. * @return the zero-based index of the character under Point p; if
  815. * Point is invalid returns -1.
  816. */
  817. public int getIndexAtPoint(Point p) {
  818. View v = getView();
  819. if (v != null) {
  820. return v.viewToModel(p.x, p.y, getBounds());
  821. } else {
  822. return -1;
  823. }
  824. }
  825. /**
  826. * Determine the bounding box of the character at the given
  827. * index into the string. The bounds are returned in local
  828. * coordinates. If the index is invalid an empty rectangle is
  829. * returned.
  830. *
  831. * @param i the index into the String
  832. * @return the screen coordinates of the character's the bounding box,
  833. * if index is invalid returns an empty rectangle.
  834. */
  835. public Rectangle getCharacterBounds(int i) {
  836. try {
  837. return editor.getUI().modelToView(editor, i);
  838. } catch (BadLocationException e) {
  839. return null;
  840. }
  841. }
  842. /**
  843. * Return the number of characters (valid indicies)
  844. *
  845. * @return the number of characters
  846. */
  847. public int getCharCount() {
  848. if (validateIfNecessary()) {
  849. Element elem = elementInfo.getElement();
  850. return elem.getEndOffset() - elem.getStartOffset();
  851. }
  852. return 0;
  853. }
  854. /**
  855. * Return the zero-based offset of the caret.
  856. *
  857. * Note: That to the right of the caret will have the same index
  858. * value as the offset (the caret is between two characters).
  859. * @return the zero-based offset of the caret.
  860. */
  861. public int getCaretPosition() {
  862. View v = getView();
  863. if (v == null) {
  864. return -1;
  865. }
  866. Container c = v.getContainer();
  867. if (c == null) {
  868. return -1;
  869. }
  870. if (c instanceof JTextComponent) {
  871. return ((JTextComponent)c).getCaretPosition();
  872. } else {
  873. return -1;
  874. }
  875. }
  876. /**
  877. * IndexedSegment extends Segment adding the offset into the
  878. * the model the <code>Segment</code> was asked for.
  879. */
  880. private class IndexedSegment extends Segment {
  881. /**
  882. * Offset into the model that the position represents.
  883. */
  884. public int modelOffset;
  885. }
  886. public String getAtIndex(int part, int index) {
  887. return getAtIndex(part, index, 0);
  888. }
  889. public String getAfterIndex(int part, int index) {
  890. return getAtIndex(part, index, 1);
  891. }
  892. public String getBeforeIndex(int part, int index) {
  893. return getAtIndex(part, index, -1);
  894. }
  895. /**
  896. * Gets the word, sentence, or character at <code>index</code>.
  897. * If <code>direction</code> is non-null this will find the
  898. * next/previous word/sentence/character.
  899. */
  900. private String getAtIndex(int part, int index, int direction) {
  901. if (model instanceof AbstractDocument) {
  902. ((AbstractDocument)model).readLock();
  903. }
  904. try {
  905. if (index < 0 || index >= model.getLength()) {
  906. return null;
  907. }
  908. switch (part) {
  909. case AccessibleText.CHARACTER:
  910. if (index + direction < model.getLength() &&
  911. index + direction >= 0) {
  912. return model.getText(index + direction, 1);
  913. }
  914. break;
  915. case AccessibleText.WORD:
  916. case AccessibleText.SENTENCE:
  917. IndexedSegment seg = getSegmentAt(part, index);
  918. if (seg != null) {
  919. if (direction != 0) {
  920. int next;
  921. if (direction < 0) {
  922. next = seg.modelOffset - 1;
  923. }
  924. else {
  925. next = seg.modelOffset + direction * seg.count;
  926. }
  927. if (next >= 0 && next <= model.getLength()) {
  928. seg = getSegmentAt(part, next);
  929. }
  930. else {
  931. seg = null;
  932. }
  933. }
  934. if (seg != null) {
  935. return new String(seg.array, seg.offset,
  936. seg.count);
  937. }
  938. }
  939. break;
  940. default:
  941. break;
  942. }
  943. } catch (BadLocationException e) {
  944. } finally {
  945. if (model instanceof AbstractDocument) {
  946. ((AbstractDocument)model).readUnlock();
  947. }
  948. }
  949. return null;
  950. }
  951. /*
  952. * Returns the paragraph element for the specified index.
  953. */
  954. private Element getParagraphElement(int index) {
  955. if (model instanceof PlainDocument ) {
  956. PlainDocument sdoc = (PlainDocument)model;
  957. return sdoc.getParagraphElement(index);
  958. } else if (model instanceof StyledDocument) {
  959. StyledDocument sdoc = (StyledDocument)model;
  960. return sdoc.getParagraphElement(index);
  961. } else {
  962. Element para = null;
  963. for (para = model.getDefaultRootElement(); ! para.isLeaf(); ) {
  964. int pos = para.getElementIndex(index);
  965. para = para.getElement(pos);
  966. }
  967. if (para == null) {
  968. return null;
  969. }
  970. return para.getParentElement();
  971. }
  972. }
  973. /*
  974. * Returns a <code>Segment</code> containing the paragraph text
  975. * at <code>index</code>, or null if <code>index</code> isn't
  976. * valid.
  977. */
  978. private IndexedSegment getParagraphElementText(int index)
  979. throws BadLocationException {
  980. Element para = getParagraphElement(index);
  981. if (para != null) {
  982. IndexedSegment segment = new IndexedSegment();
  983. try {
  984. int length = para.getEndOffset() - para.getStartOffset();
  985. model.getText(para.getStartOffset(), length, segment);
  986. } catch (BadLocationException e) {
  987. return null;
  988. }
  989. segment.modelOffset = para.getStartOffset();
  990. return segment;
  991. }
  992. return null;
  993. }
  994. /**
  995. * Returns the Segment at <code>index</code> representing either
  996. * the paragraph or sentence as identified by <code>part</code>, or
  997. * null if a valid paragraph/sentence can't be found. The offset
  998. * will point to the start of the word/sentence in the array, and
  999. * the modelOffset will point to the location of the word/sentence
  1000. * in the model.
  1001. */
  1002. private IndexedSegment getSegmentAt(int part, int index)
  1003. throws BadLocationException {
  1004. IndexedSegment seg = getParagraphElementText(index);
  1005. if (seg == null) {
  1006. return null;
  1007. }
  1008. BreakIterator iterator;
  1009. switch (part) {
  1010. case AccessibleText.WORD:
  1011. iterator = BreakIterator.getWordInstance(getLocale());
  1012. break;
  1013. case AccessibleText.SENTENCE:
  1014. iterator = BreakIterator.getSentenceInstance(getLocale());
  1015. break;
  1016. default:
  1017. return null;
  1018. }
  1019. seg.first();
  1020. iterator.setText(seg);
  1021. int end = iterator.following(index - seg.modelOffset + seg.offset);
  1022. if (end == BreakIterator.DONE) {
  1023. return null;
  1024. }
  1025. if (end > seg.offset + seg.count) {
  1026. return null;
  1027. }
  1028. int begin = iterator.previous();
  1029. if (begin == BreakIterator.DONE ||
  1030. begin >= seg.offset + seg.count) {
  1031. return null;
  1032. }
  1033. seg.modelOffset = seg.modelOffset + begin - seg.offset;
  1034. seg.offset = begin;
  1035. seg.count = end - begin;
  1036. return seg;
  1037. }
  1038. /**
  1039. * Return the AttributeSet for a given character at a given index
  1040. *
  1041. * @param i the zero-based index into the text
  1042. * @return the AttributeSet of the character
  1043. */
  1044. public AttributeSet getCharacterAttribute(int i) {
  1045. if (model instanceof StyledDocument) {
  1046. StyledDocument doc = (StyledDocument)model;
  1047. Element elem = doc.getCharacterElement(i);
  1048. if (elem != null) {
  1049. return elem.getAttributes();
  1050. }
  1051. }
  1052. return null;
  1053. }
  1054. /**
  1055. * Returns the start offset within the selected text.
  1056. * If there is no selection, but there is
  1057. * a caret, the start and end offsets will be the same.
  1058. *
  1059. * @return the index into the text of the start of the selection
  1060. */
  1061. public int getSelectionStart() {
  1062. return editor.getSelectionStart();
  1063. }
  1064. /**
  1065. * Returns the end offset within the selected text.
  1066. * If there is no selection, but there is
  1067. * a caret, the start and end offsets will be the same.
  1068. *
  1069. * @return the index into teh text of the end of the selection
  1070. */
  1071. public int getSelectionEnd() {
  1072. return editor.getSelectionEnd();
  1073. }
  1074. /**
  1075. * Returns the portion of the text that is selected.
  1076. *
  1077. * @return the String portion of the text that is selected
  1078. */
  1079. public String getSelectedText() {
  1080. return editor.getSelectedText();
  1081. }
  1082. /*
  1083. * Returns the text substring starting at the specified
  1084. * offset with the specified length.
  1085. */
  1086. private String getText(int offset, int length)
  1087. throws BadLocationException {
  1088. if (model != null && model instanceof StyledDocument) {
  1089. StyledDocument doc = (StyledDocument)model;
  1090. return model.getText(offset, length);
  1091. } else {
  1092. return null;
  1093. }
  1094. }
  1095. }
  1096. }
  1097. /*
  1098. * ElementInfo for images
  1099. */
  1100. private class IconElementInfo extends ElementInfo implements Accessible {
  1101. private int width = -1;
  1102. private int height = -1;
  1103. IconElementInfo(Element element, ElementInfo parent) {
  1104. super(element, parent);
  1105. }
  1106. protected void invalidate(boolean first) {
  1107. super.invalidate(first);
  1108. width = height = -1;
  1109. }
  1110. private int getImageSize(Object key) {
  1111. if (validateIfNecessary()) {
  1112. int size = getIntAttr(getAttributes(), key, -1);
  1113. if (size == -1) {
  1114. View v = getView();
  1115. size = 0;
  1116. if (v instanceof ImageView) {
  1117. Image img = ((ImageView)v).getImage();
  1118. if (img != null) {
  1119. if (key == HTML.Attribute.WIDTH) {
  1120. size = img.getWidth(null);
  1121. }
  1122. else {
  1123. size = img.getHeight(null);
  1124. }
  1125. }
  1126. }
  1127. }
  1128. return size;
  1129. }
  1130. return 0;
  1131. }
  1132. // begin AccessibleIcon implementation ...
  1133. private AccessibleContext accessibleContext;
  1134. public AccessibleContext getAccessibleContext() {
  1135. if (accessibleContext == null) {
  1136. accessibleContext = new IconAccessibleContext(this);
  1137. }
  1138. return accessibleContext;
  1139. }
  1140. /*
  1141. * AccessibleContext for images
  1142. */
  1143. protected class IconAccessibleContext extends HTMLAccessibleContext
  1144. implements AccessibleIcon {
  1145. public IconAccessibleContext(ElementInfo elementInfo) {
  1146. super(elementInfo);
  1147. }
  1148. /**
  1149. * Gets the accessibleName property of this object. The accessibleName
  1150. * property of an object is a localized String that designates the purpose
  1151. * of the object. For example, the accessibleName property of a label
  1152. * or button might be the text of the label or button itself. In the
  1153. * case of an object that doesn't display its name, the accessibleName
  1154. * should still be set. For example, in the case of a text field used
  1155. * to enter the name of a city, the accessibleName for the en_US locale
  1156. * could be 'city.'
  1157. *
  1158. * @return the localized name of the object; null if this
  1159. * object does not have a name
  1160. *
  1161. * @see #setAccessibleName
  1162. */
  1163. public String getAccessibleName() {
  1164. return getAccessibleIconDescription();
  1165. }
  1166. /**
  1167. * Gets the accessibleDescription property of this object. If this
  1168. * property isn't set, returns the content type of this
  1169. * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
  1170. *
  1171. * @return the localized description of the object; <code>null</code>
  1172. * if this object does not have a description
  1173. *
  1174. * @see #setAccessibleName
  1175. */
  1176. public String getAccessibleDescription() {
  1177. return editor.getContentType();
  1178. }
  1179. /**
  1180. * Gets the role of this object. The role of the object is the generic
  1181. * purpose or use of the class of this object. For example, the role
  1182. * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
  1183. * AccessibleRole are provided so component developers can pick from
  1184. * a set of predefined roles. This enables assistive technologies to
  1185. * provide a consistent interface to various tweaked subclasses of
  1186. * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
  1187. * that act like a push button) as well as distinguish between sublasses
  1188. * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
  1189. * and AccessibleRole.RADIO_BUTTON for radio buttons).
  1190. * <p>Note that the AccessibleRole class is also extensible, so
  1191. * custom component developers can define their own AccessibleRole's
  1192. * if the set of predefined roles is inadequate.
  1193. *
  1194. * @return an instance of AccessibleRole describing the role of the object
  1195. * @see AccessibleRole
  1196. */
  1197. public AccessibleRole getAccessibleRole() {
  1198. return AccessibleRole.ICON;
  1199. }
  1200. public AccessibleIcon [] getAccessibleIcon() {
  1201. AccessibleIcon [] icons = new AccessibleIcon[1];
  1202. icons[0] = this;
  1203. return icons;
  1204. }
  1205. /**
  1206. * Gets the description of the icon. This is meant to be a brief
  1207. * textual description of the object. For example, it might be
  1208. * presented to a blind user to give an indication of the purpose
  1209. * of the icon.
  1210. *
  1211. * @return the description of the icon
  1212. */
  1213. public String getAccessibleIconDescription() {
  1214. return ((ImageView)getView()).getAltText();
  1215. }
  1216. /**
  1217. * Sets the description of the icon. This is meant to be a brief
  1218. * textual description of the object. For example, it might be
  1219. * presented to a blind user to give an indication of the purpose
  1220. * of the icon.
  1221. *
  1222. * @param description the description of the icon
  1223. */
  1224. public void setAccessibleIconDescription(String description) {
  1225. }
  1226. /**
  1227. * Gets the width of the icon
  1228. *
  1229. * @return the width of the icon.
  1230. */
  1231. public int getAccessibleIconWidth() {
  1232. if (width == -1) {
  1233. width = getImageSize(HTML.Attribute.WIDTH);
  1234. }
  1235. return width;
  1236. }
  1237. /**
  1238. * Gets the height of the icon
  1239. *
  1240. * @return the height of the icon.
  1241. */
  1242. public int getAccessibleIconHeight() {
  1243. if (height == -1) {
  1244. height = getImageSize(HTML.Attribute.HEIGHT);
  1245. }
  1246. return height;
  1247. }
  1248. }
  1249. // ... end AccessibleIconImplementation
  1250. }
  1251. /**
  1252. * TableElementInfo encapsulates information about a HTML.Tag.TABLE.
  1253. * To make access fast it crates a grid containing the children to
  1254. * allow for access by row, column. TableElementInfo will contain
  1255. * TableRowElementInfos, which will contain TableCellElementInfos.
  1256. * Any time one of the rows or columns becomes invalid the table is
  1257. * invalidated. This is because any time one of the child attributes
  1258. * changes the size of the grid may have changed.
  1259. */
  1260. private class TableElementInfo extends ElementInfo
  1261. implements Accessible {
  1262. protected ElementInfo caption;
  1263. /**
  1264. * Allocation of the table by row x column. There may be holes (eg
  1265. * nulls) depending upon the html, any cell that has a rowspan/colspan
  1266. * > 1 will be contained multiple times in the grid.
  1267. */
  1268. private TableCellElementInfo[][] grid;
  1269. TableElementInfo(Element e, ElementInfo parent) {
  1270. super(e, parent);
  1271. }
  1272. public ElementInfo getCaptionInfo() {
  1273. return caption;
  1274. }
  1275. /**
  1276. * Overriden to update the grid when validating.
  1277. */
  1278. protected void validate() {
  1279. super.validate();
  1280. updateGrid();
  1281. }
  1282. /**
  1283. * Overriden to only alloc instances of TableRowElementInfos.
  1284. */
  1285. protected void loadChildren(Element e) {
  1286. for (int counter = 0; counter < e.getElementCount(); counter++) {
  1287. Element child = e.getElement(counter);
  1288. AttributeSet attrs = child.getAttributes();
  1289. if (attrs.getAttribute(StyleConstants.NameAttribute) ==
  1290. HTML.Tag.TR) {
  1291. addChild(new TableRowElementInfo(child, this, counter));
  1292. } else if (attrs.getAttribute(StyleConstants.NameAttribute) ==
  1293. HTML.Tag.CAPTION) {
  1294. // Handle captions as a special case since all other
  1295. // children are table rows.
  1296. caption = createElementInfo(child, this);
  1297. }
  1298. }
  1299. }
  1300. /**
  1301. * Updates the grid.
  1302. */
  1303. private void updateGrid() {
  1304. // Determine the max row/col count.
  1305. int delta = 0;
  1306. int maxCols = 0;
  1307. int rows = 0;
  1308. for (int counter = 0; counter < getChildCount(); counter++) {
  1309. TableRowElementInfo row = getRow(counter);
  1310. int prev = 0;
  1311. for (int y = 0; y < delta; y++) {
  1312. prev = Math.max(prev, getRow(counter - y - 1).
  1313. getColumnCount(y + 2));
  1314. }
  1315. delta = Math.max(row.getRowCount(), delta);
  1316. delta--;
  1317. maxCols = Math.max(maxCols, row.getColumnCount() + prev);
  1318. }
  1319. rows = getChildCount() + delta;
  1320. // Alloc
  1321. grid = new TableCellElementInfo[rows][];
  1322. for (int counter = 0; counter < rows; counter++) {
  1323. grid[counter] = new TableCellElementInfo[maxCols];
  1324. }
  1325. // Update
  1326. for (int counter = 0; counter < rows; counter++) {
  1327. getRow(counter).updateGrid(counter);
  1328. }
  1329. }
  1330. /**
  1331. * Returns the TableCellElementInfo at the specified index.
  1332. */
  1333. public TableRowElementInfo getRow(int index) {
  1334. return (TableRowElementInfo)getChild(index);
  1335. }
  1336. /**
  1337. * Returns the TableCellElementInfo by row and column.
  1338. */
  1339. public TableCellElementInfo getCell(int r, int c) {
  1340. if (validateIfNecessary() && r < grid.length &&
  1341. c < grid[0].length) {
  1342. return grid[r][c];
  1343. }
  1344. return null;
  1345. }
  1346. /**
  1347. * Returns the rowspan of the specified entry.
  1348. */
  1349. public int getRowExtentAt(int r, int c) {
  1350. TableCellElementInfo cell = getCell(r, c);
  1351. if (cell != null) {
  1352. int rows = cell.getRowCount();
  1353. int delta = 1;
  1354. while ((r - delta) >= 0 && grid[r - delta][c] == cell) {
  1355. delta++;
  1356. }
  1357. return rows - delta + 1;
  1358. }
  1359. return 0;
  1360. }
  1361. /**
  1362. * Returns the colspan of the specified entry.
  1363. */
  1364. public int getColumnExtentAt(int r, int c) {
  1365. TableCellElementInfo cell = getCell(r, c);
  1366. if (cell != null) {
  1367. int cols = cell.getColumnCount();
  1368. int delta = 1;
  1369. while ((c - delta) >= 0 && grid[r][c - delta] == cell) {
  1370. delta++;
  1371. }
  1372. return cols - delta + 1;
  1373. }
  1374. return 0;
  1375. }
  1376. /**
  1377. * Returns the number of rows in the table.
  1378. */
  1379. public int getRowCount() {
  1380. if (validateIfNecessary()) {
  1381. return grid.length;
  1382. }
  1383. return 0;
  1384. }
  1385. /**
  1386. * Returns the number of columns in the table.
  1387. */
  1388. public int getColumnCount() {
  1389. if (validateIfNecessary() && grid.length > 0) {
  1390. return grid[0].length;
  1391. }
  1392. return 0;
  1393. }
  1394. // begin AccessibleTable implementation ...
  1395. private AccessibleContext accessibleContext;
  1396. public AccessibleContext getAccessibleContext() {
  1397. if (accessibleContext == null) {
  1398. accessibleContext = new TableAccessibleContext(this);
  1399. }
  1400. return accessibleContext;
  1401. }
  1402. /*
  1403. * AccessibleContext for tables
  1404. */
  1405. public class TableAccessibleContext extends HTMLAccessibleContext
  1406. implements AccessibleTable {
  1407. private AccessibleHeadersTable rowHeadersTable;
  1408. public TableAccessibleContext(ElementInfo elementInfo) {
  1409. super(elementInfo);
  1410. }
  1411. /**
  1412. * Gets the accessibleName property of this object. The accessibleName
  1413. * property of an object is a localized String that designates the purpose
  1414. * of the object. For example, the accessibleName property of a label
  1415. * or button might be the text of the label or button itself. In the
  1416. * case of an object that doesn't display its name, the accessibleName
  1417. * should still be set. For example, in the case of a text field used
  1418. * to enter the name of a city, the accessibleName for the en_US locale
  1419. * could be 'city.'
  1420. *
  1421. * @return the localized name of the object; null if this
  1422. * object does not have a name
  1423. *
  1424. * @see #setAccessibleName
  1425. */
  1426. public String getAccessibleName() {
  1427. // return the role of the object
  1428. return getAccessibleRole().toString();
  1429. }
  1430. /**
  1431. * Gets the accessibleDescription property of this object. If this
  1432. * property isn't set, returns the content type of this
  1433. * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
  1434. *
  1435. * @return the localized description of the object; <code>null</code>
  1436. * if this object does not have a description
  1437. *
  1438. * @see #setAccessibleName
  1439. */
  1440. public String getAccessibleDescription() {
  1441. return editor.getContentType();
  1442. }
  1443. /**
  1444. * Gets the role of this object. The role of the object is the generic
  1445. * purpose or use of the class of this object. For example, the role
  1446. * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
  1447. * AccessibleRole are provided so component developers can pick from
  1448. * a set of predefined roles. This enables assistive technologies to
  1449. * provide a consistent interface to various tweaked subclasses of
  1450. * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
  1451. * that act like a push button) as well as distinguish between sublasses
  1452. * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
  1453. * and AccessibleRole.RADIO_BUTTON for radio buttons).
  1454. * <p>Note that the AccessibleRole class is also extensible, so
  1455. * custom component developers can define their own AccessibleRole's
  1456. * if the set of predefined roles is inadequate.
  1457. *
  1458. * @return an instance of AccessibleRole describing the role of the object
  1459. * @see AccessibleRole
  1460. */
  1461. public AccessibleRole getAccessibleRole() {
  1462. return AccessibleRole.TABLE;
  1463. }
  1464. /**
  1465. * Gets the 0-based index of this object in its accessible parent.
  1466. *
  1467. * @return the 0-based index of this object in its parent; -1 if this
  1468. * object does not have an accessible parent.
  1469. *
  1470. * @see #getAccessibleParent
  1471. * @see #getAccessibleChildrenCount
  1472. * @gsee #getAccessibleChild
  1473. */
  1474. public int getAccessibleIndexInParent() {
  1475. return elementInfo.getIndexInParent();
  1476. }
  1477. /**
  1478. * Returns the number of accessible children of the object.
  1479. *
  1480. * @return the number of accessible children of the object.
  1481. */
  1482. public int getAccessibleChildrenCount() {
  1483. return ((TableElementInfo)elementInfo).getRowCount() *
  1484. ((TableElementInfo)elementInfo).getColumnCount();
  1485. }
  1486. /**
  1487. * Returns the specified Accessible child of the object. The Accessible
  1488. * children of an Accessible object are zero-based, so the first child
  1489. * of an Accessible child is at index 0, the second child is at index 1,
  1490. * and so on.
  1491. *
  1492. * @param i zero-based index of child
  1493. * @return the Accessible child of the object
  1494. * @see #getAccessibleChildrenCount
  1495. */
  1496. public Accessible getAccessibleChild(int i) {
  1497. int rowCount = ((TableElementInfo)elementInfo).getRowCount();
  1498. int columnCount = ((TableElementInfo)elementInfo).getColumnCount();
  1499. int r = i / rowCount;
  1500. int c = i % columnCount;
  1501. if (r < 0 || r >= rowCount || c < 0 || c >= columnCount) {
  1502. return null;
  1503. } else {
  1504. return getAccessibleAt(r, c);
  1505. }
  1506. }
  1507. public AccessibleTable getAccessibleTable() {
  1508. return this;
  1509. }
  1510. /**
  1511. * Returns the caption for the table.
  1512. *
  1513. * @return the caption for the table
  1514. */
  1515. public Accessible getAccessibleCaption() {
  1516. ElementInfo captionInfo = getCaptionInfo();
  1517. if (captionInfo instanceof Accessible) {
  1518. return (Accessible)caption;
  1519. } else {
  1520. return null;
  1521. }
  1522. }
  1523. /**
  1524. * Sets the caption for the table.
  1525. *
  1526. * @param a the caption for the table
  1527. */
  1528. public void setAccessibleCaption(Accessible a) {
  1529. }
  1530. /**
  1531. * Returns the summary description of the table.
  1532. *
  1533. * @return the summary description of the table
  1534. */
  1535. public Accessible getAccessibleSummary() {
  1536. return null;
  1537. }
  1538. /**
  1539. * Sets the summary description of the table
  1540. *
  1541. * @param a the summary description of the table
  1542. */
  1543. public void setAccessibleSummary(Accessible a) {
  1544. }
  1545. /**
  1546. * Returns the number of rows in the table.
  1547. *
  1548. * @return the number of rows in the table
  1549. */
  1550. public int getAccessibleRowCount() {
  1551. return ((TableElementInfo)elementInfo).getRowCount();
  1552. }
  1553. /**
  1554. * Returns the number of columns in the table.
  1555. *
  1556. * @return the number of columns in the table
  1557. */
  1558. public int getAccessibleColumnCount() {
  1559. return ((TableElementInfo)elementInfo).getColumnCount();
  1560. }
  1561. /**
  1562. * Returns the Accessible at a specified row and column
  1563. * in the table.
  1564. *
  1565. * @param r zero-based row of the table
  1566. * @param c zero-based column of the table
  1567. * @return the Accessible at the specified row and column
  1568. */
  1569. public Accessible getAccessibleAt(int r, int c) {
  1570. TableCellElementInfo cellInfo = getCell(r, c);
  1571. if (cellInfo != null) {
  1572. return cellInfo.getAccessible();
  1573. } else {
  1574. return null;
  1575. }
  1576. }
  1577. /**
  1578. * Returns the number of rows occupied by the Accessible at
  1579. * a specified row and column in the table.
  1580. *
  1581. * @return the number of rows occupied by the Accessible at a
  1582. * given specified (row, column)
  1583. */
  1584. public int getAccessibleRowExtentAt(int r, int c) {
  1585. return ((TableElementInfo)elementInfo).getRowExtentAt(r, c);
  1586. }
  1587. /**
  1588. * Returns the number of columns occupied by the Accessible at
  1589. * a specified row and column in the table.
  1590. *
  1591. * @return the number of columns occupied by the Accessible at a
  1592. * given specified row and column
  1593. */
  1594. public int getAccessibleColumnExtentAt(int r, int c) {
  1595. return ((TableElementInfo)elementInfo).getColumnExtentAt(r, c);
  1596. }
  1597. /**
  1598. * Returns the row headers as an AccessibleTable.
  1599. *
  1600. * @return an AccessibleTable representing the row
  1601. * headers
  1602. */
  1603. public AccessibleTable getAccessibleRowHeader() {
  1604. return rowHeadersTable;
  1605. }
  1606. /**
  1607. * Sets the row headers.
  1608. *
  1609. * @param table an AccessibleTable representing the
  1610. * row headers
  1611. */
  1612. public void setAccessibleRowHeader(AccessibleTable table) {
  1613. }
  1614. /**
  1615. * Returns the column headers as an AccessibleTable.
  1616. *
  1617. * @return an AccessibleTable representing the column
  1618. * headers
  1619. */
  1620. public AccessibleTable getAccessibleColumnHeader() {
  1621. return null;
  1622. }
  1623. /**
  1624. * Sets the column headers.
  1625. *
  1626. * @param table an AccessibleTable representing the
  1627. * column headers
  1628. */
  1629. public void setAccessibleColumnHeader(AccessibleTable table) {
  1630. }
  1631. /**
  1632. * Returns the description of the specified row in the table.
  1633. *
  1634. * @param r zero-based row of the table
  1635. * @return the description of the row
  1636. */
  1637. public Accessible getAccessibleRowDescription(int r) {
  1638. return null;
  1639. }
  1640. /**
  1641. * Sets the description text of the specified row of the table.
  1642. *
  1643. * @param r zero-based row of the table
  1644. * @param a the description of the row
  1645. */
  1646. public void setAccessibleRowDescription(int r, Accessible a) {
  1647. }
  1648. /**
  1649. * Returns the description text of the specified column in the table.
  1650. *
  1651. * @param c zero-based column of the table
  1652. * @return the text description of the column
  1653. */
  1654. public Accessible getAccessibleColumnDescription(int c) {
  1655. return null;
  1656. }
  1657. /**
  1658. * Sets the description text of the specified column in the table.
  1659. *
  1660. * @param c zero-based column of the table
  1661. * @param a the text description of the column
  1662. */
  1663. public void setAccessibleColumnDescription(int c, Accessible a) {
  1664. }
  1665. /**
  1666. * Returns a boolean value indicating whether the accessible at
  1667. * a specified row and column is selected.
  1668. *
  1669. * @param r zero-based row of the table
  1670. * @param c zero-based column of the table
  1671. * @return the boolean value true if the accessible at the
  1672. * row and column is selected. Otherwise, the boolean value
  1673. * false
  1674. */
  1675. public boolean isAccessibleSelected(int r, int c) {
  1676. if (validateIfNecessary()) {
  1677. if (r < 0 || r >= getAccessibleRowCount() ||
  1678. c < 0 || c >= getAccessibleColumnCount()) {
  1679. return false;
  1680. }
  1681. TableCellElementInfo cell = getCell(r, c);
  1682. if (cell != null) {
  1683. Element elem = cell.getElement();
  1684. int start = elem.getStartOffset();
  1685. int end = elem.getEndOffset();
  1686. return start >= editor.getSelectionStart() &&
  1687. end <= editor.getSelectionEnd();
  1688. }
  1689. }
  1690. return false;
  1691. }
  1692. /**
  1693. * Returns a boolean value indicating whether the specified row
  1694. * is selected.
  1695. *
  1696. * @param r zero-based row of the table
  1697. * @return the boolean value true if the specified row is selected.
  1698. * Otherwise, false.
  1699. */
  1700. public boolean isAccessibleRowSelected(int r) {
  1701. if (validateIfNecessary()) {
  1702. if (r < 0 || r >= getAccessibleRowCount()) {
  1703. return false;
  1704. }
  1705. int nColumns = getAccessibleColumnCount();
  1706. TableCellElementInfo startCell = getCell(r, 0);
  1707. if (startCell == null) {
  1708. return false;
  1709. }
  1710. int start = startCell.getElement().getStartOffset();
  1711. TableCellElementInfo endCell = getCell(r, nColumns-1);
  1712. if (endCell == null) {
  1713. return false;
  1714. }
  1715. int end = endCell.getElement().getEndOffset();
  1716. return start >= editor.getSelectionStart() &&
  1717. end <= editor.getSelectionEnd();
  1718. }
  1719. return false;
  1720. }
  1721. /**
  1722. * Returns a boolean value indicating whether the specified column
  1723. * is selected.
  1724. *
  1725. * @param r zero-based column of the table
  1726. * @return the boolean value true if the specified column is selected.
  1727. * Otherwise, false.
  1728. */
  1729. public boolean isAccessibleColumnSelected(int c) {
  1730. if (validateIfNecessary()) {
  1731. if (c < 0 || c >= getAccessibleColumnCount()) {
  1732. return false;
  1733. }
  1734. int nRows = getAccessibleRowCount();
  1735. TableCellElementInfo startCell = getCell(0, c);
  1736. if (startCell == null) {
  1737. return false;
  1738. }
  1739. int start = startCell.getElement().getStartOffset();
  1740. TableCellElementInfo endCell = getCell(nRows-1, c);
  1741. if (endCell == null) {
  1742. return false;
  1743. }
  1744. int end = endCell.getElement().getEndOffset();
  1745. return start >= editor.getSelectionStart() &&
  1746. end <= editor.getSelectionEnd();
  1747. }
  1748. return false;
  1749. }
  1750. /**
  1751. * Returns the selected rows in a table.
  1752. *
  1753. * @return an array of selected rows where each element is a
  1754. * zero-based row of the table
  1755. */
  1756. public int [] getSelectedAccessibleRows() {
  1757. if (validateIfNecessary()) {
  1758. int nRows = getAccessibleRowCount();
  1759. Vector vec = new Vector();
  1760. for (int i = 0; i < nRows; i++) {
  1761. if (isAccessibleRowSelected(i)) {
  1762. vec.addElement(new Integer(i));
  1763. }
  1764. }
  1765. int retval[] = new int[vec.size()];
  1766. for (int i = 0; i < retval.length; i++) {
  1767. retval[i] = ((Integer)vec.elementAt(i)).intValue();
  1768. }
  1769. return retval;
  1770. }
  1771. return new int[0];
  1772. }
  1773. /**
  1774. * Returns the selected columns in a table.
  1775. *
  1776. * @return an array of selected columns where each element is a
  1777. * zero-based column of the table
  1778. */
  1779. public int [] getSelectedAccessibleColumns() {
  1780. if (validateIfNecessary()) {
  1781. int nColumns = getAccessibleRowCount();
  1782. Vector vec = new Vector();
  1783. for (int i = 0; i < nColumns; i++) {
  1784. if (isAccessibleColumnSelected(i)) {
  1785. vec.addElement(new Integer(i));
  1786. }
  1787. }
  1788. int retval[] = new int[vec.size()];
  1789. for (int i = 0; i < retval.length; i++) {
  1790. retval[i] = ((Integer)vec.elementAt(i)).intValue();
  1791. }
  1792. return retval;
  1793. }
  1794. return new int[0];
  1795. }
  1796. // begin AccessibleExtendedTable implementation -------------
  1797. /**
  1798. * Returns the row number of an index in the table.
  1799. *
  1800. * @param index the zero-based index in the table
  1801. * @return the zero-based row of the table if one exists;
  1802. * otherwise -1.
  1803. */
  1804. public int getAccessibleRow(int index) {
  1805. if (validateIfNecessary()) {
  1806. int numCells = getAccessibleColumnCount() *
  1807. getAccessibleRowCount();
  1808. if (index >= numCells) {
  1809. return -1;
  1810. } else {
  1811. return index / getAccessibleColumnCount();
  1812. }
  1813. }
  1814. return -1;
  1815. }
  1816. /**
  1817. * Returns the column number of an index in the table.
  1818. *
  1819. * @param index the zero-based index in the table
  1820. * @return the zero-based column of the table if one exists;
  1821. * otherwise -1.
  1822. */
  1823. public int getAccessibleColumn(int index) {
  1824. if (validateIfNecessary()) {
  1825. int numCells = getAccessibleColumnCount() *
  1826. getAccessibleRowCount();
  1827. if (index >= numCells) {
  1828. return -1;
  1829. } else {
  1830. return index % getAccessibleColumnCount();
  1831. }
  1832. }
  1833. return -1;
  1834. }
  1835. /*
  1836. * Returns the index at a row and column in the table.
  1837. *
  1838. * @param r zero-based row of the table
  1839. * @param c zero-based column of the table
  1840. * @return the zero-based index in the table if one exists;
  1841. * otherwise -1.
  1842. */
  1843. public int getAccessibleIndex(int r, int c) {
  1844. if (validateIfNecessary()) {
  1845. if (r >= getAccessibleRowCount() ||
  1846. c >= getAccessibleColumnCount()) {
  1847. return -1;
  1848. } else {
  1849. return r * getAccessibleColumnCount() + c;
  1850. }
  1851. }
  1852. return -1;
  1853. }
  1854. /**
  1855. * Returns the row header at a row in a table.
  1856. * @param r zero-based row of the table
  1857. *
  1858. * @return a String representing the row header
  1859. * if one exists; otherwise null.
  1860. */
  1861. public String getAccessibleRowHeader(int r) {
  1862. if (validateIfNecessary()) {
  1863. TableCellElementInfo cellInfo = getCell(r, 0);
  1864. if (cellInfo.isHeaderCell()) {
  1865. View v = cellInfo.getView();
  1866. if (v != null && model != null) {
  1867. try {
  1868. return model.getText(v.getStartOffset(),
  1869. v.getEndOffset() -
  1870. v.getStartOffset());
  1871. } catch (BadLocationException e) {
  1872. return null;
  1873. }
  1874. }
  1875. }
  1876. }
  1877. return null;
  1878. }
  1879. /**
  1880. * Returns the column header at a column in a table.
  1881. * @param c zero-based column of the table
  1882. *
  1883. * @return a String representing the column header
  1884. * if one exists; otherwise null.
  1885. */
  1886. public String getAccessibleColumnHeader(int c) {
  1887. if (validateIfNecessary()) {
  1888. TableCellElementInfo cellInfo = getCell(0, c);
  1889. if (cellInfo.isHeaderCell()) {
  1890. View v = cellInfo.getView();
  1891. if (v != null && model != null) {
  1892. try {
  1893. return model.getText(v.getStartOffset(),
  1894. v.getEndOffset() -
  1895. v.getStartOffset());
  1896. } catch (BadLocationException e) {
  1897. return null;
  1898. }
  1899. }
  1900. }
  1901. }
  1902. return null;
  1903. }
  1904. public void addRowHeader(TableCellElementInfo cellInfo, int rowNumber) {
  1905. if (rowHeadersTable == null) {
  1906. rowHeadersTable = new AccessibleHeadersTable();
  1907. }
  1908. rowHeadersTable.addHeader(cellInfo, rowNumber);
  1909. }
  1910. // end of AccessibleExtendedTable implementation ------------
  1911. protected class AccessibleHeadersTable implements AccessibleTable {
  1912. // Header information is modeled as a Hashtable of
  1913. // ArrayLists where each Hashtable entry represents
  1914. // a row containing one or more headers.
  1915. private Hashtable headers = new Hashtable();
  1916. private int rowCount = 0;
  1917. private int columnCount = 0;
  1918. public void addHeader(TableCellElementInfo cellInfo, int rowNumber) {
  1919. Integer rowInteger = new Integer(rowNumber);
  1920. ArrayList list = (ArrayList)headers.get(rowInteger);
  1921. if (list == null) {
  1922. list = new ArrayList();
  1923. headers.put(rowInteger, list);
  1924. }
  1925. list.add(cellInfo);
  1926. }
  1927. /**
  1928. * Returns the caption for the table.
  1929. *
  1930. * @return the caption for the table
  1931. */
  1932. public Accessible getAccessibleCaption() {
  1933. return null;
  1934. }
  1935. /**
  1936. * Sets the caption for the table.
  1937. *
  1938. * @param a the caption for the table
  1939. */
  1940. public void setAccessibleCaption(Accessible a) {
  1941. }
  1942. /**
  1943. * Returns the summary description of the table.
  1944. *
  1945. * @return the summary description of the table
  1946. */
  1947. public Accessible getAccessibleSummary() {
  1948. return null;
  1949. }
  1950. /**
  1951. * Sets the summary description of the table
  1952. *
  1953. * @param a the summary description of the table
  1954. */
  1955. public void setAccessibleSummary(Accessible a) {
  1956. }
  1957. /**
  1958. * Returns the number of rows in the table.
  1959. *
  1960. * @return the number of rows in the table
  1961. */
  1962. public int getAccessibleRowCount() {
  1963. return rowCount;
  1964. }
  1965. /**
  1966. * Returns the number of columns in the table.
  1967. *
  1968. * @return the number of columns in the table
  1969. */
  1970. public int getAccessibleColumnCount() {
  1971. return columnCount;
  1972. }
  1973. private TableCellElementInfo getElementInfoAt(int r, int c) {
  1974. ArrayList list = (ArrayList)headers.get(new Integer(r));
  1975. if (list != null) {
  1976. return (TableCellElementInfo)list.get(c);
  1977. } else {
  1978. return null;
  1979. }
  1980. }
  1981. /**
  1982. * Returns the Accessible at a specified row and column
  1983. * in the table.
  1984. *
  1985. * @param r zero-based row of the table
  1986. * @param c zero-based column of the table
  1987. * @return the Accessible at the specified row and column
  1988. */
  1989. public Accessible getAccessibleAt(int r, int c) {
  1990. ElementInfo elementInfo = getElementInfoAt(r, c);
  1991. if (elementInfo instanceof Accessible) {
  1992. return (Accessible)elementInfo;
  1993. } else {
  1994. return null;
  1995. }
  1996. }
  1997. /**
  1998. * Returns the number of rows occupied by the Accessible at
  1999. * a specified row and column in the table.
  2000. *
  2001. * @return the number of rows occupied by the Accessible at a
  2002. * given specified (row, column)
  2003. */
  2004. public int getAccessibleRowExtentAt(int r, int c) {
  2005. TableCellElementInfo elementInfo = getElementInfoAt(r, c);
  2006. if (elementInfo != null) {
  2007. return elementInfo.getRowCount();
  2008. } else {
  2009. return 0;
  2010. }
  2011. }
  2012. /**
  2013. * Returns the number of columns occupied by the Accessible at
  2014. * a specified row and column in the table.
  2015. *
  2016. * @return the number of columns occupied by the Accessible at a
  2017. * given specified row and column
  2018. */
  2019. public int getAccessibleColumnExtentAt(int r, int c) {
  2020. TableCellElementInfo elementInfo = getElementInfoAt(r, c);
  2021. if (elementInfo != null) {
  2022. return elementInfo.getRowCount();
  2023. } else {
  2024. return 0;
  2025. }
  2026. }
  2027. /**
  2028. * Returns the row headers as an AccessibleTable.
  2029. *
  2030. * @return an AccessibleTable representing the row
  2031. * headers
  2032. */
  2033. public AccessibleTable getAccessibleRowHeader() {
  2034. return null;
  2035. }
  2036. /**
  2037. * Sets the row headers.
  2038. *
  2039. * @param table an AccessibleTable representing the
  2040. * row headers
  2041. */
  2042. public void setAccessibleRowHeader(AccessibleTable table) {
  2043. }
  2044. /**
  2045. * Returns the column headers as an AccessibleTable.
  2046. *
  2047. * @return an AccessibleTable representing the column
  2048. * headers
  2049. */
  2050. public AccessibleTable getAccessibleColumnHeader() {
  2051. return null;
  2052. }
  2053. /**
  2054. * Sets the column headers.
  2055. *
  2056. * @param table an AccessibleTable representing the
  2057. * column headers
  2058. */
  2059. public void setAccessibleColumnHeader(AccessibleTable table) {
  2060. }
  2061. /**
  2062. * Returns the description of the specified row in the table.
  2063. *
  2064. * @param r zero-based row of the table
  2065. * @return the description of the row
  2066. */
  2067. public Accessible getAccessibleRowDescription(int r) {
  2068. return null;
  2069. }
  2070. /**
  2071. * Sets the description text of the specified row of the table.
  2072. *
  2073. * @param r zero-based row of the table
  2074. * @param a the description of the row
  2075. */
  2076. public void setAccessibleRowDescription(int r, Accessible a) {
  2077. }
  2078. /**
  2079. * Returns the description text of the specified column in the table.
  2080. *
  2081. * @param c zero-based column of the table
  2082. * @return the text description of the column
  2083. */
  2084. public Accessible getAccessibleColumnDescription(int c) {
  2085. return null;
  2086. }
  2087. /**
  2088. * Sets the description text of the specified column in the table.
  2089. *
  2090. * @param c zero-based column of the table
  2091. * @param a the text description of the column
  2092. */
  2093. public void setAccessibleColumnDescription(int c, Accessible a) {
  2094. }
  2095. /**
  2096. * Returns a boolean value indicating whether the accessible at
  2097. * a specified row and column is selected.
  2098. *
  2099. * @param r zero-based row of the table
  2100. * @param c zero-based column of the table
  2101. * @return the boolean value true if the accessible at the
  2102. * row and column is selected. Otherwise, the boolean value
  2103. * false
  2104. */
  2105. public boolean isAccessibleSelected(int r, int c) {
  2106. return false;
  2107. }
  2108. /**
  2109. * Returns a boolean value indicating whether the specified row
  2110. * is selected.
  2111. *
  2112. * @param r zero-based row of the table
  2113. * @return the boolean value true if the specified row is selected.
  2114. * Otherwise, false.
  2115. */
  2116. public boolean isAccessibleRowSelected(int r) {
  2117. return false;
  2118. }
  2119. /**
  2120. * Returns a boolean value indicating whether the specified column
  2121. * is selected.
  2122. *
  2123. * @param r zero-based column of the table
  2124. * @return the boolean value true if the specified column is selected.
  2125. * Otherwise, false.
  2126. */
  2127. public boolean isAccessibleColumnSelected(int c) {
  2128. return false;
  2129. }
  2130. /**
  2131. * Returns the selected rows in a table.
  2132. *
  2133. * @return an array of selected rows where each element is a
  2134. * zero-based row of the table
  2135. */
  2136. public int [] getSelectedAccessibleRows() {
  2137. return new int [0];
  2138. }
  2139. /**
  2140. * Returns the selected columns in a table.
  2141. *
  2142. * @return an array of selected columns where each element is a
  2143. * zero-based column of the table
  2144. */
  2145. public int [] getSelectedAccessibleColumns() {
  2146. return new int [0];
  2147. }
  2148. }
  2149. } // ... end AccessibleHeadersTable
  2150. /*
  2151. * ElementInfo for table rows
  2152. */
  2153. private class TableRowElementInfo extends ElementInfo {
  2154. private TableElementInfo parent;
  2155. private int rowNumber;
  2156. TableRowElementInfo(Element e, TableElementInfo parent, int rowNumber) {
  2157. super(e, parent);
  2158. this.parent = parent;
  2159. this.rowNumber = rowNumber;
  2160. }
  2161. protected void loadChildren(Element e) {
  2162. for (int x = 0; x < e.getElementCount(); x++) {
  2163. AttributeSet attrs = e.getElement(x).getAttributes();
  2164. if (attrs.getAttribute(StyleConstants.NameAttribute) ==
  2165. HTML.Tag.TH) {
  2166. TableCellElementInfo headerElementInfo =
  2167. new TableCellElementInfo(e.getElement(x), this, true);
  2168. addChild(headerElementInfo);
  2169. AccessibleTable at =
  2170. parent.getAccessibleContext().getAccessibleTable();
  2171. TableAccessibleContext tableElement =
  2172. (TableAccessibleContext)at;
  2173. tableElement.addRowHeader(headerElementInfo, rowNumber);
  2174. } else if (attrs.getAttribute(StyleConstants.NameAttribute) ==
  2175. HTML.Tag.TD) {
  2176. addChild(new TableCellElementInfo(e.getElement(x), this,
  2177. false));
  2178. }
  2179. }
  2180. }
  2181. /**
  2182. * Returns the max of the rowspans of the cells in this row.
  2183. */
  2184. public int getRowCount() {
  2185. int rowCount = 1;
  2186. if (validateIfNecessary()) {
  2187. for (int counter = 0; counter < getChildCount();
  2188. counter++) {
  2189. TableCellElementInfo cell = (TableCellElementInfo)
  2190. getChild(counter);
  2191. if (cell.validateIfNecessary()) {
  2192. rowCount = Math.max(rowCount, cell.getRowCount());
  2193. }
  2194. }
  2195. }
  2196. return rowCount;
  2197. }
  2198. /**
  2199. * Returns the sum of the column spans of the individual
  2200. * cells in this row.
  2201. */
  2202. public int getColumnCount() {
  2203. int colCount = 0;
  2204. if (validateIfNecessary()) {
  2205. for (int counter = 0; counter < getChildCount();
  2206. counter++) {
  2207. TableCellElementInfo cell = (TableCellElementInfo)
  2208. getChild(counter);
  2209. if (cell.validateIfNecessary()) {
  2210. colCount += cell.getColumnCount();
  2211. }
  2212. }
  2213. }
  2214. return colCount;
  2215. }
  2216. /**
  2217. * Overriden to invalidate the table as well as
  2218. * TableRowElementInfo.
  2219. */
  2220. protected void invalidate(boolean first) {
  2221. super.invalidate(first);
  2222. getParent().invalidate(true);
  2223. }
  2224. /**
  2225. * Places the TableCellElementInfos for this element in
  2226. * the grid.
  2227. */
  2228. private void updateGrid(int row) {
  2229. if (validateIfNecessary()) {
  2230. boolean emptyRow = false;
  2231. while (!emptyRow) {
  2232. for (int counter = 0; counter < grid[row].length;
  2233. counter++) {
  2234. if (grid[row][counter] == null) {
  2235. emptyRow = true;
  2236. break;
  2237. }
  2238. }
  2239. if (!emptyRow) {
  2240. row++;
  2241. }
  2242. }
  2243. for (int col = 0, counter = 0; counter < getChildCount();
  2244. counter++) {
  2245. TableCellElementInfo cell = (TableCellElementInfo)
  2246. getChild(counter);
  2247. while (grid[row][col] != null) {
  2248. col++;
  2249. }
  2250. for (int rowCount = cell.getRowCount() - 1;
  2251. rowCount >= 0; rowCount--) {
  2252. for (int colCount = cell.getColumnCount() - 1;
  2253. colCount >= 0; colCount--) {
  2254. grid[row + rowCount][col + colCount] = cell;
  2255. }
  2256. }
  2257. col += cell.getColumnCount();
  2258. }
  2259. }
  2260. }
  2261. /**
  2262. * Returns the column count of the number of columns that have
  2263. * a rowcount >= rowspan.
  2264. */
  2265. private int getColumnCount(int rowspan) {
  2266. if (validateIfNecessary()) {
  2267. int cols = 0;
  2268. for (int counter = 0; counter < getChildCount();
  2269. counter++) {
  2270. TableCellElementInfo cell = (TableCellElementInfo)
  2271. getChild(counter);
  2272. if (cell.getRowCount() >= rowspan) {
  2273. cols += cell.getColumnCount();
  2274. }
  2275. }
  2276. return cols;
  2277. }
  2278. return 0;
  2279. }
  2280. }
  2281. /**
  2282. * TableCellElementInfo is used to represents the cells of
  2283. * the table.
  2284. */
  2285. private class TableCellElementInfo extends ElementInfo {
  2286. private Accessible accessible;
  2287. private boolean isHeaderCell;
  2288. TableCellElementInfo(Element e, ElementInfo parent) {
  2289. super(e, parent);
  2290. this.isHeaderCell = false;
  2291. }
  2292. TableCellElementInfo(Element e, ElementInfo parent,
  2293. boolean isHeaderCell) {
  2294. super(e, parent);
  2295. this.isHeaderCell = isHeaderCell;
  2296. }
  2297. /*
  2298. * Returns whether this table cell is a header
  2299. */
  2300. public boolean isHeaderCell() {
  2301. return this.isHeaderCell;
  2302. }
  2303. /*
  2304. * Returns the Accessible representing this table cell
  2305. */
  2306. public Accessible getAccessible() {
  2307. accessible = null;
  2308. getAccessible(this);
  2309. return accessible;
  2310. }
  2311. /*
  2312. * Gets the outermost Accessible in the table cell
  2313. */
  2314. private void getAccessible(ElementInfo elementInfo) {
  2315. if (elementInfo instanceof Accessible) {
  2316. accessible = (Accessible)elementInfo;
  2317. return;
  2318. } else {
  2319. for (int i = 0; i < elementInfo.getChildCount(); i++) {
  2320. getAccessible(elementInfo.getChild(i));
  2321. }
  2322. }
  2323. }
  2324. /**
  2325. * Returns the rowspan attribute.
  2326. */
  2327. public int getRowCount() {
  2328. if (validateIfNecessary()) {
  2329. return Math.max(1, getIntAttr(getAttributes(),
  2330. HTML.Attribute.ROWSPAN, 1));
  2331. }
  2332. return 0;
  2333. }
  2334. /**
  2335. * Returns the colspan attribute.
  2336. */
  2337. public int getColumnCount() {
  2338. if (validateIfNecessary()) {
  2339. return Math.max(1, getIntAttr(getAttributes(),
  2340. HTML.Attribute.COLSPAN, 1));
  2341. }
  2342. return 0;
  2343. }
  2344. /**
  2345. * Overriden to invalidate the TableRowElementInfo as well as
  2346. * the TableCellElementInfo.
  2347. */
  2348. protected void invalidate(boolean first) {
  2349. super.invalidate(first);
  2350. getParent().invalidate(true);
  2351. }
  2352. }
  2353. }
  2354. /**
  2355. * ElementInfo provides a slim down view of an Element. Each ElementInfo
  2356. * can have any number of child ElementInfos that are not necessarily
  2357. * direct children of the Element. As the Document changes various
  2358. * ElementInfos become invalidated. Before accessing a particular portion
  2359. * of an ElementInfo you should make sure it is valid by invoking
  2360. * <code>validateIfNecessary</code>, this will return true if
  2361. * successful, on the other hand a false return value indicates the
  2362. * ElementInfo is not valid and can never become valid again (usually
  2363. * the result of the Element the ElementInfo encapsulates being removed).
  2364. */
  2365. private class ElementInfo {
  2366. /**
  2367. * The children of this ElementInfo.
  2368. */
  2369. private ArrayList children;
  2370. /**
  2371. * The Element this ElementInfo is providing information for.
  2372. */
  2373. private Element element;
  2374. /**
  2375. * The parent ElementInfo, will be null for the root.
  2376. */
  2377. private ElementInfo parent;
  2378. /**
  2379. * Indicates the validity of the ElementInfo.
  2380. */
  2381. private boolean isValid;
  2382. /**
  2383. * Indicates if the ElementInfo can become valid.
  2384. */
  2385. private boolean canBeValid;
  2386. /**
  2387. * Creates the root ElementInfo.
  2388. */
  2389. ElementInfo(Element element) {
  2390. this(element, null);
  2391. }
  2392. /**
  2393. * Creates an ElementInfo representing <code>element</code> with
  2394. * the specified parent.
  2395. */
  2396. ElementInfo(Element element, ElementInfo parent) {
  2397. this.element = element;
  2398. this.parent = parent;
  2399. isValid = false;
  2400. canBeValid = true;
  2401. }
  2402. /**
  2403. * Validates the receiver. This recreates the children as well. This
  2404. * will be invoked within a <code>readLock</code>. If this is overriden
  2405. * it MUST invoke supers implementation first!
  2406. */
  2407. protected void validate() {
  2408. isValid = true;
  2409. loadChildren(getElement());
  2410. }
  2411. /**
  2412. * Recreates the direct children of <code>info</code>.
  2413. */
  2414. protected void loadChildren(Element parent) {
  2415. if (!parent.isLeaf()) {
  2416. for (int counter = 0, maxCounter = parent.getElementCount();
  2417. counter < maxCounter; counter++) {
  2418. Element e = parent.getElement(counter);
  2419. ElementInfo childInfo = createElementInfo(e, this);
  2420. if (childInfo != null) {
  2421. addChild(childInfo);
  2422. }
  2423. else {
  2424. loadChildren(e);
  2425. }
  2426. }
  2427. }
  2428. }
  2429. /**
  2430. * Returns the index of the child in the parent, or -1 for the
  2431. * root or if the parent isn't valid.
  2432. */
  2433. public int getIndexInParent() {
  2434. if (parent == null || !parent.isValid()) {
  2435. return -1;
  2436. }
  2437. return parent.indexOf(this);
  2438. }
  2439. /**
  2440. * Returns the Element this <code>ElementInfo</code> represents.
  2441. */
  2442. public Element getElement() {
  2443. return element;
  2444. }
  2445. /**
  2446. * Returns the parent of this Element, or null for the root.
  2447. */
  2448. public ElementInfo getParent() {
  2449. return parent;
  2450. }
  2451. /**
  2452. * Returns the index of the specified child, or -1 if
  2453. * <code>child</code> isn't a valid child.
  2454. */
  2455. public int indexOf(ElementInfo child) {
  2456. ArrayList children = this.children;
  2457. if (children != null) {
  2458. return children.indexOf(child);
  2459. }
  2460. return -1;
  2461. }
  2462. /**
  2463. * Returns the child ElementInfo at <code>index</code>, or null
  2464. * if <code>index</code> isn't a valid index.
  2465. */
  2466. public ElementInfo getChild(int index) {
  2467. if (validateIfNecessary()) {
  2468. ArrayList children = this.children;
  2469. if (children != null && index >= 0 &&
  2470. index < children.size()) {
  2471. return (ElementInfo)children.get(index);
  2472. }
  2473. }
  2474. return null;
  2475. }
  2476. /**
  2477. * Returns the number of children the ElementInfo contains.
  2478. */
  2479. public int getChildCount() {
  2480. validateIfNecessary();
  2481. return (children == null) ? 0 : children.size();
  2482. }
  2483. /**
  2484. * Adds a new child to this ElementInfo.
  2485. */
  2486. protected void addChild(ElementInfo child) {
  2487. if (children == null) {
  2488. children = new ArrayList();
  2489. }
  2490. children.add(child);
  2491. }
  2492. /**
  2493. * Returns the View corresponding to this ElementInfo, or null
  2494. * if the ElementInfo can't be validated.
  2495. */
  2496. protected View getView() {
  2497. if (!validateIfNecessary()) {
  2498. return null;
  2499. }
  2500. Object lock = lock();
  2501. try {
  2502. View rootView = getRootView();
  2503. Element e = getElement();
  2504. int start = e.getStartOffset();
  2505. if (rootView != null) {
  2506. return getView(rootView, e, start);
  2507. }
  2508. return null;
  2509. } finally {
  2510. unlock(lock);
  2511. }
  2512. }
  2513. /**
  2514. * Returns the Bounds for this ElementInfo, or null
  2515. * if the ElementInfo can't be validated.
  2516. */
  2517. public Rectangle getBounds() {
  2518. if (!validateIfNecessary()) {
  2519. return null;
  2520. }
  2521. Object lock = lock();
  2522. try {
  2523. Rectangle bounds = getRootEditorRect();
  2524. View rootView = getRootView();
  2525. Element e = getElement();
  2526. if (bounds != null && rootView != null) {
  2527. try {
  2528. return rootView.modelToView(e.getStartOffset(),
  2529. Position.Bias.Forward,
  2530. e.getEndOffset(),
  2531. Position.Bias.Backward,
  2532. bounds).getBounds();
  2533. } catch (BadLocationException ble) { }
  2534. }
  2535. } finally {
  2536. unlock(lock);
  2537. }
  2538. return null;
  2539. }
  2540. /**
  2541. * Returns true if this ElementInfo is valid.
  2542. */
  2543. protected boolean isValid() {
  2544. return isValid;
  2545. }
  2546. /**
  2547. * Returns the AttributeSet associated with the Element, this will
  2548. * return null if the ElementInfo can't be validated.
  2549. */
  2550. protected AttributeSet getAttributes() {
  2551. if (validateIfNecessary()) {
  2552. return getElement().getAttributes();
  2553. }
  2554. return null;
  2555. }
  2556. /**
  2557. * Returns the AttributeSet associated with the View that is
  2558. * representing this Element, this will
  2559. * return null if the ElementInfo can't be validated.
  2560. */
  2561. protected AttributeSet getViewAttributes() {
  2562. if (validateIfNecessary()) {
  2563. View view = getView();
  2564. if (view != null) {
  2565. return view.getElement().getAttributes();
  2566. }
  2567. return getElement().getAttributes();
  2568. }
  2569. return null;
  2570. }
  2571. /**
  2572. * Convenience method for getting an integer attribute from the passed
  2573. * in AttributeSet.
  2574. */
  2575. protected int getIntAttr(AttributeSet attrs, Object key, int deflt) {
  2576. if (attrs != null && attrs.isDefined(key)) {
  2577. int i;
  2578. String val = (String)attrs.getAttribute(key);
  2579. if (val == null) {
  2580. i = deflt;
  2581. }
  2582. else {
  2583. try {
  2584. i = Math.max(0, Integer.parseInt(val));
  2585. } catch (NumberFormatException x) {
  2586. i = deflt;
  2587. }
  2588. }
  2589. return i;
  2590. }
  2591. return deflt;
  2592. }
  2593. /**
  2594. * Validates the ElementInfo if necessary. Some ElementInfos may
  2595. * never be valid again. You should check <code>isValid</code> before
  2596. * using one. This will reload the children and invoke
  2597. * <code>validate</code> if the ElementInfo is invalid and can become
  2598. * valid again. This will return true if the receiver is valid.
  2599. */
  2600. protected boolean validateIfNecessary() {
  2601. if (!isValid() && canBeValid) {
  2602. children = null;
  2603. Object lock = lock();
  2604. try {
  2605. validate();
  2606. } finally {
  2607. unlock(lock);
  2608. }
  2609. }
  2610. return isValid();
  2611. }
  2612. /**
  2613. * Invalidates the ElementInfo. Subclasses should override this
  2614. * if they need to reset state once invalid.
  2615. */
  2616. protected void invalidate(boolean first) {
  2617. if (!isValid()) {
  2618. if (canBeValid && !first) {
  2619. canBeValid = false;
  2620. }
  2621. return;
  2622. }
  2623. isValid = false;
  2624. canBeValid = first;
  2625. if (children != null) {
  2626. for (int counter = 0; counter < children.size(); counter++) {
  2627. ((ElementInfo)children.get(counter)).invalidate(false);
  2628. }
  2629. children = null;
  2630. }
  2631. }
  2632. private View getView(View parent, Element e, int start) {
  2633. if (parent.getElement() == e) {
  2634. return parent;
  2635. }
  2636. int index = parent.getViewIndex(start, Position.Bias.Forward);
  2637. if (index != -1 && index < parent.getViewCount()) {
  2638. return getView(parent.getView(index), e, start);
  2639. }
  2640. return null;
  2641. }
  2642. private int getClosestInfoIndex(int index) {
  2643. for (int counter = 0; counter < getChildCount(); counter++) {
  2644. ElementInfo info = getChild(counter);
  2645. if (index < info.getElement().getEndOffset() ||
  2646. index == info.getElement().getStartOffset()) {
  2647. return counter;
  2648. }
  2649. }
  2650. return -1;
  2651. }
  2652. private void update(DocumentEvent e) {
  2653. if (!isValid()) {
  2654. return;
  2655. }
  2656. ElementInfo parent = getParent();
  2657. Element element = getElement();
  2658. do {
  2659. DocumentEvent.ElementChange ec = e.getChange(element);
  2660. if (ec != null) {
  2661. if (element == getElement()) {
  2662. // One of our children changed.
  2663. invalidate(true);
  2664. }
  2665. else if (parent != null) {
  2666. parent.invalidate(parent == getRootInfo());
  2667. }
  2668. return;
  2669. }
  2670. element = element.getParentElement();
  2671. } while (parent != null && element != null &&
  2672. element != parent.getElement());
  2673. if (getChildCount() > 0) {
  2674. Element elem = getElement();
  2675. int pos = e.getOffset();
  2676. int index0 = getClosestInfoIndex(pos);
  2677. if (index0 == -1 &&
  2678. e.getType() == DocumentEvent.EventType.REMOVE &&
  2679. pos >= elem.getEndOffset()) {
  2680. // Event beyond our offsets. We may have represented this,
  2681. // that is the remove may have removed one of our child
  2682. // Elements that represented this, so, we should foward
  2683. // to last element.
  2684. index0 = getChildCount() - 1;
  2685. }
  2686. ElementInfo info = (index0 >= 0) ? getChild(index0) : null;
  2687. if (info != null &&
  2688. (info.getElement().getStartOffset() == pos) && (pos > 0)) {
  2689. // If at a boundary, forward the event to the previous
  2690. // ElementInfo too.
  2691. index0 = Math.max(index0 - 1, 0);
  2692. }
  2693. int index1;
  2694. if (e.getType() != DocumentEvent.EventType.REMOVE) {
  2695. index1 = getClosestInfoIndex(pos + e.getLength());
  2696. if (index1 < 0) {
  2697. index1 = getChildCount() - 1;
  2698. }
  2699. }
  2700. else {
  2701. index1 = index0;
  2702. // A remove may result in empty elements.
  2703. while ((index1 + 1) < getChildCount() &&
  2704. getChild(index1 + 1).getElement().getEndOffset() ==
  2705. getChild(index1 + 1).getElement().getStartOffset()){
  2706. index1++;
  2707. }
  2708. }
  2709. index0 = Math.max(index0, 0);
  2710. // The check for isValid is here as in the process of
  2711. // forwarding update our child may invalidate us.
  2712. for (int i = index0; i <= index1 && isValid(); i++) {
  2713. getChild(i).update(e);
  2714. }
  2715. }
  2716. }
  2717. }
  2718. /**
  2719. * DocumentListener installed on the current Document. Will invoke
  2720. * <code>update</code> on the <code>RootInfo</code> in response to
  2721. * any event.
  2722. */
  2723. private class DocumentHandler implements DocumentListener {
  2724. public void insertUpdate(DocumentEvent e) {
  2725. getRootInfo().update(e);
  2726. }
  2727. public void removeUpdate(DocumentEvent e) {
  2728. getRootInfo().update(e);
  2729. }
  2730. public void changedUpdate(DocumentEvent e) {
  2731. getRootInfo().update(e);
  2732. }
  2733. }
  2734. /*
  2735. * PropertyChangeListener installed on the editor.
  2736. */
  2737. private class PropertyChangeHandler implements PropertyChangeListener {
  2738. public void propertyChange(PropertyChangeEvent evt) {
  2739. if (evt.getPropertyName().equals("document")) {
  2740. // handle the document change
  2741. setDocument(editor.getDocument());
  2742. }
  2743. }
  2744. }
  2745. }