1. /*
  2. * @(#)AccessibleHTML.java 1.11 03/01/23
  3. *
  4. * Copyright 2003 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.11 01/23/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 != null && comp.isEnabled()) {
  273. states.add(AccessibleState.ENABLED);
  274. }
  275. if (comp != null && comp.isFocusTraversable()) {
  276. states.add(AccessibleState.FOCUSABLE);
  277. }
  278. if (comp != null && comp.isVisible()) {
  279. states.add(AccessibleState.VISIBLE);
  280. }
  281. if (comp != null && comp.isShowing()) {
  282. states.add(AccessibleState.SHOWING);
  283. }
  284. return states;
  285. }
  286. /**
  287. * Gets the 0-based index of this object in its accessible parent.
  288. *
  289. * @return the 0-based index of this object in its parent; -1 if this
  290. * object does not have an accessible parent.
  291. *
  292. * @see #getAccessibleParent
  293. * @see #getAccessibleChildrenCount
  294. * @see #getAccessibleChild
  295. */
  296. public int getAccessibleIndexInParent() {
  297. return elementInfo.getIndexInParent();
  298. }
  299. /**
  300. * Returns the number of accessible children of the object.
  301. *
  302. * @return the number of accessible children of the object.
  303. */
  304. public int getAccessibleChildrenCount() {
  305. return elementInfo.getChildCount();
  306. }
  307. /**
  308. * Returns the specified Accessible child of the object. The Accessible
  309. * children of an Accessible object are zero-based, so the first child
  310. * of an Accessible child is at index 0, the second child is at index 1,
  311. * and so on.
  312. *
  313. * @param i zero-based index of child
  314. * @return the Accessible child of the object
  315. * @see #getAccessibleChildrenCount
  316. */
  317. public Accessible getAccessibleChild(int i) {
  318. ElementInfo childInfo = elementInfo.getChild(i);
  319. if (childInfo != null && childInfo instanceof Accessible) {
  320. return (Accessible)childInfo;
  321. } else {
  322. return null;
  323. }
  324. }
  325. /**
  326. * Gets the locale of the component. If the component does not have a
  327. * locale, then the locale of its parent is returned.
  328. *
  329. * @return this component's locale. If this component does not have
  330. * a locale, the locale of its parent is returned.
  331. *
  332. * @exception IllegalComponentStateException
  333. * If the Component does not have its own locale and has not yet been
  334. * added to a containment hierarchy such that the locale can be
  335. * determined from the containing parent.
  336. */
  337. public Locale getLocale() throws IllegalComponentStateException {
  338. return editor.getLocale();
  339. }
  340. // ... end AccessibleContext implementation
  341. // begin AccessibleComponent implementation ...
  342. public AccessibleComponent getAccessibleComponent() {
  343. return this;
  344. }
  345. /**
  346. * Gets the background color of this object.
  347. *
  348. * @return the background color, if supported, of the object;
  349. * otherwise, null
  350. * @see #setBackground
  351. */
  352. public Color getBackground() {
  353. return getTextComponent().getBackground();
  354. }
  355. /**
  356. * Sets the background color of this object.
  357. *
  358. * @param c the new Color for the background
  359. * @see #setBackground
  360. */
  361. public void setBackground(Color c) {
  362. getTextComponent().setBackground(c);
  363. }
  364. /**
  365. * Gets the foreground color of this object.
  366. *
  367. * @return the foreground color, if supported, of the object;
  368. * otherwise, null
  369. * @see #setForeground
  370. */
  371. public Color getForeground() {
  372. return getTextComponent().getForeground();
  373. }
  374. /**
  375. * Sets the foreground color of this object.
  376. *
  377. * @param c the new Color for the foreground
  378. * @see #getForeground
  379. */
  380. public void setForeground(Color c) {
  381. getTextComponent().setForeground(c);
  382. }
  383. /**
  384. * Gets the Cursor of this object.
  385. *
  386. * @return the Cursor, if supported, of the object; otherwise, null
  387. * @see #setCursor
  388. */
  389. public Cursor getCursor() {
  390. return getTextComponent().getCursor();
  391. }
  392. /**
  393. * Sets the Cursor of this object.
  394. *
  395. * @param c the new Cursor for the object
  396. * @see #getCursor
  397. */
  398. public void setCursor(Cursor cursor) {
  399. getTextComponent().setCursor(cursor);
  400. }
  401. /**
  402. * Gets the Font of this object.
  403. *
  404. * @return the Font,if supported, for the object; otherwise, null
  405. * @see #setFont
  406. */
  407. public Font getFont() {
  408. return getTextComponent().getFont();
  409. }
  410. /**
  411. * Sets the Font of this object.
  412. *
  413. * @param f the new Font for the object
  414. * @see #getFont
  415. */
  416. public void setFont(Font f) {
  417. getTextComponent().setFont(f);
  418. }
  419. /**
  420. * Gets the FontMetrics of this object.
  421. *
  422. * @param f the Font
  423. * @return the FontMetrics, if supported, the object; otherwise, null
  424. * @see #getFont
  425. */
  426. public FontMetrics getFontMetrics(Font f) {
  427. return getTextComponent().getFontMetrics(f);
  428. }
  429. /**
  430. * Determines if the object is enabled. Objects that are enabled
  431. * will also have the AccessibleState.ENABLED state set in their
  432. * AccessibleStateSets.
  433. *
  434. * @return true if object is enabled; otherwise, false
  435. * @see #setEnabled
  436. * @see AccessibleContext#getAccessibleStateSet
  437. * @see AccessibleState#ENABLED
  438. * @see AccessibleStateSet
  439. */
  440. public boolean isEnabled() {
  441. return getTextComponent().isEnabled();
  442. }
  443. /**
  444. * Sets the enabled state of the object.
  445. *
  446. * @param b if true, enables this object; otherwise, disables it
  447. * @see #isEnabled
  448. */
  449. public void setEnabled(boolean b) {
  450. getTextComponent().setEnabled(b);
  451. }
  452. /**
  453. * Determines if the object is visible. Note: this means that the
  454. * object intends to be visible; however, it may not be
  455. * showing on the screen because one of the objects that this object
  456. * is contained by is currently not visible. To determine if an object
  457. * is showing on the screen, use isShowing().
  458. * <p>Objects that are visible will also have the
  459. * AccessibleState.VISIBLE state set in their AccessibleStateSets.
  460. *
  461. * @return true if object is visible; otherwise, false
  462. * @see #setVisible
  463. * @see AccessibleContext#getAccessibleStateSet
  464. * @see AccessibleState#VISIBLE
  465. * @see AccessibleStateSet
  466. */
  467. public boolean isVisible() {
  468. return getTextComponent().isVisible();
  469. }
  470. /**
  471. * Sets the visible state of the object.
  472. *
  473. * @param b if true, shows this object; otherwise, hides it
  474. * @see #isVisible
  475. */
  476. public void setVisible(boolean b) {
  477. getTextComponent().setVisible(b);
  478. }
  479. /**
  480. * Determines if the object is showing. This is determined by checking
  481. * the visibility of the object and its ancestors.
  482. * Note: this
  483. * will return true even if the object is obscured by another (for
  484. * example, it is underneath a menu that was pulled down).
  485. *
  486. * @return true if object is showing; otherwise, false
  487. */
  488. public boolean isShowing() {
  489. return getTextComponent().isShowing();
  490. }
  491. /**
  492. * Checks whether the specified point is within this object's bounds,
  493. * where the point's x and y coordinates are defined to be relative
  494. * to the coordinate system of the object.
  495. *
  496. * @param p the Point relative to the coordinate system of the object
  497. * @return true if object contains Point; otherwise false
  498. * @see #getBounds
  499. */
  500. public boolean contains(Point p) {
  501. Rectangle r = getBounds();
  502. if (r != null) {
  503. return r.contains(p.x, p.y);
  504. } else {
  505. return false;
  506. }
  507. }
  508. /**
  509. * Returns the location of the object on the screen.
  510. *
  511. * @return the location of the object on screen; null if this object
  512. * is not on the screen
  513. * @see #getBounds
  514. * @see #getLocation
  515. */
  516. public Point getLocationOnScreen() {
  517. Point editorLocation = getTextComponent().getLocationOnScreen();
  518. Rectangle r = getBounds();
  519. if (r != null) {
  520. return new Point(editorLocation.x + r.x,
  521. editorLocation.y + r.y);
  522. } else {
  523. return null;
  524. }
  525. }
  526. /**
  527. * Gets the location of the object relative to the parent in the form
  528. * of a point specifying the object's top-left corner in the screen's
  529. * coordinate space.
  530. *
  531. * @return An instance of Point representing the top-left corner of the
  532. * object's bounds in the coordinate space of the screen; null if
  533. * this object or its parent are not on the screen
  534. * @see #getBounds
  535. * @see #getLocationOnScreen
  536. */
  537. public Point getLocation() {
  538. Rectangle r = getBounds();
  539. if (r != null) {
  540. return new Point(r.x, r.y);
  541. } else {
  542. return null;
  543. }
  544. }
  545. /**
  546. * Sets the location of the object relative to the parent.
  547. * @param p the new position for the top-left corner
  548. * @see #getLocation
  549. */
  550. public void setLocation(Point p) {
  551. }
  552. /**
  553. * Gets the bounds of this object in the form of a Rectangle object.
  554. * The bounds specify this object's width, height, and location
  555. * relative to its parent.
  556. *
  557. * @return A rectangle indicating this component's bounds; null if
  558. * this object is not on the screen.
  559. * @see #contains
  560. */
  561. public Rectangle getBounds() {
  562. return elementInfo.getBounds();
  563. }
  564. /**
  565. * Sets the bounds of this object in the form of a Rectangle object.
  566. * The bounds specify this object's width, height, and location
  567. * relative to its parent.
  568. *
  569. * @param r rectangle indicating this component's bounds
  570. * @see #getBounds
  571. */
  572. public void setBounds(Rectangle r) {
  573. getTextComponent().setBounds(r);
  574. }
  575. /**
  576. * Returns the size of this object in the form of a Dimension object.
  577. * The height field of the Dimension object contains this object's
  578. * height, and the width field of the Dimension object contains this
  579. * object's width.
  580. *
  581. * @return A Dimension object that indicates the size of this component;
  582. * null if this object is not on the screen
  583. * @see #setSize
  584. */
  585. public Dimension getSize() {
  586. Rectangle r = getBounds();
  587. if (r != null) {
  588. return new Dimension(r.width, r.height);
  589. } else {
  590. return null;
  591. }
  592. }
  593. /**
  594. * Resizes this object so that it has width and height.
  595. *
  596. * @param d The dimension specifying the new size of the object.
  597. * @see #getSize
  598. */
  599. public void setSize(Dimension d) {
  600. getTextComponent().setSize(d);
  601. }
  602. /**
  603. * Returns the Accessible child, if one exists, contained at the local
  604. * coordinate Point.
  605. *
  606. * @param p The point relative to the coordinate system of this object.
  607. * @return the Accessible, if it exists, at the specified location;
  608. * otherwise null
  609. */
  610. public Accessible getAccessibleAt(Point p) {
  611. ElementInfo innerMostElement = getElementInfoAt(rootElementInfo, p);
  612. if (innerMostElement instanceof Accessible) {
  613. return (Accessible)innerMostElement;
  614. } else {
  615. return null;
  616. }
  617. }
  618. private ElementInfo getElementInfoAt(ElementInfo elementInfo, Point p) {
  619. if (elementInfo.getBounds() == null) {
  620. return null;
  621. }
  622. if (elementInfo.getChildCount() == 0 &&
  623. elementInfo.getBounds().contains(p)) {
  624. return elementInfo;
  625. } else {
  626. if (elementInfo instanceof TableElementInfo) {
  627. // Handle table caption as a special case since it's the
  628. // only table child that is not a table row.
  629. ElementInfo captionInfo =
  630. ((TableElementInfo)elementInfo).getCaptionInfo();
  631. if (captionInfo != null) {
  632. Rectangle bounds = captionInfo.getBounds();
  633. if (bounds != null && bounds.contains(p)) {
  634. return captionInfo;
  635. }
  636. }
  637. }
  638. for (int i = 0; i < elementInfo.getChildCount(); i++)
  639. {
  640. ElementInfo childInfo = elementInfo.getChild(i);
  641. ElementInfo retValue = getElementInfoAt(childInfo, p);
  642. if (retValue != null) {
  643. return retValue;
  644. }
  645. }
  646. }
  647. return null;
  648. }
  649. /**
  650. * Returns whether this object can accept focus or not. Objects that
  651. * can accept focus will also have the AccessibleState.FOCUSABLE state
  652. * set in their AccessibleStateSets.
  653. *
  654. * @return true if object can accept focus; otherwise false
  655. * @see AccessibleContext#getAccessibleStateSet
  656. * @see AccessibleState#FOCUSABLE
  657. * @see AccessibleState#FOCUSED
  658. * @see AccessibleStateSet
  659. */
  660. public boolean isFocusTraversable() {
  661. return getTextComponent().isFocusTraversable();
  662. }
  663. /**
  664. * Requests focus for this object. If this object cannot accept focus,
  665. * nothing will happen. Otherwise, the object will attempt to take
  666. * focus.
  667. * @see #isFocusTraversable
  668. */
  669. public void requestFocus() {
  670. getTextComponent().requestFocus();
  671. }
  672. /**
  673. * Adds the specified focus listener to receive focus events from this
  674. * component.
  675. *
  676. * @param l the focus listener
  677. * @see #removeFocusListener
  678. */
  679. public void addFocusListener(FocusListener l) {
  680. getTextComponent().addFocusListener(l);
  681. }
  682. /**
  683. * Removes the specified focus listener so it no longer receives focus
  684. * events from this component.
  685. *
  686. * @param l the focus listener
  687. * @see #addFocusListener
  688. */
  689. public void removeFocusListener(FocusListener l) {
  690. getTextComponent().removeFocusListener(l);
  691. }
  692. // ... end AccessibleComponent implementation
  693. } // ... end HTMLAccessibleContext
  694. /*
  695. * ElementInfo for text
  696. */
  697. class TextElementInfo extends ElementInfo implements Accessible {
  698. TextElementInfo(Element element, ElementInfo parent) {
  699. super(element, parent);
  700. }
  701. // begin AccessibleText implementation ...
  702. private AccessibleContext accessibleContext;
  703. public AccessibleContext getAccessibleContext() {
  704. if (accessibleContext == null) {
  705. accessibleContext = new TextAccessibleContext(this);
  706. }
  707. return accessibleContext;
  708. }
  709. /*
  710. * AccessibleContext for text elements
  711. */
  712. public class TextAccessibleContext extends HTMLAccessibleContext
  713. implements AccessibleText {
  714. public TextAccessibleContext(ElementInfo elementInfo) {
  715. super(elementInfo);
  716. }
  717. public AccessibleText getAccessibleText() {
  718. return this;
  719. }
  720. /**
  721. * Gets the accessibleName property of this object. The accessibleName
  722. * property of an object is a localized String that designates the purpose
  723. * of the object. For example, the accessibleName property of a label
  724. * or button might be the text of the label or button itself. In the
  725. * case of an object that doesn't display its name, the accessibleName
  726. * should still be set. For example, in the case of a text field used
  727. * to enter the name of a city, the accessibleName for the en_US locale
  728. * could be 'city.'
  729. *
  730. * @return the localized name of the object; null if this
  731. * object does not have a name
  732. *
  733. * @see #setAccessibleName
  734. */
  735. public String getAccessibleName() {
  736. if (model != null) {
  737. return (String)model.getProperty(Document.TitleProperty);
  738. } else {
  739. return null;
  740. }
  741. }
  742. /**
  743. * Gets the accessibleDescription property of this object. If this
  744. * property isn't set, returns the content type of this
  745. * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
  746. *
  747. * @return the localized description of the object; <code>null</code>
  748. * if this object does not have a description
  749. *
  750. * @see #setAccessibleName
  751. */
  752. public String getAccessibleDescription() {
  753. return editor.getContentType();
  754. }
  755. /**
  756. * Gets the role of this object. The role of the object is the generic
  757. * purpose or use of the class of this object. For example, the role
  758. * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
  759. * AccessibleRole are provided so component developers can pick from
  760. * a set of predefined roles. This enables assistive technologies to
  761. * provide a consistent interface to various tweaked subclasses of
  762. * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
  763. * that act like a push button) as well as distinguish between sublasses
  764. * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
  765. * and AccessibleRole.RADIO_BUTTON for radio buttons).
  766. * <p>Note that the AccessibleRole class is also extensible, so
  767. * custom component developers can define their own AccessibleRole's
  768. * if the set of predefined roles is inadequate.
  769. *
  770. * @return an instance of AccessibleRole describing the role of the object
  771. * @see AccessibleRole
  772. */
  773. public AccessibleRole getAccessibleRole() {
  774. return AccessibleRole.TEXT;
  775. }
  776. /**
  777. * Given a point in local coordinates, return the zero-based index
  778. * of the character under that Point. If the point is invalid,
  779. * this method returns -1.
  780. *
  781. * @param p the Point in local coordinates
  782. * @return the zero-based index of the character under Point p; if
  783. * Point is invalid returns -1.
  784. */
  785. public int getIndexAtPoint(Point p) {
  786. View v = getView();
  787. if (v != null) {
  788. return v.viewToModel(p.x, p.y, getBounds());
  789. } else {
  790. return -1;
  791. }
  792. }
  793. /**
  794. * Determine the bounding box of the character at the given
  795. * index into the string. The bounds are returned in local
  796. * coordinates. If the index is invalid an empty rectangle is
  797. * returned.
  798. *
  799. * @param i the index into the String
  800. * @return the screen coordinates of the character's the bounding box,
  801. * if index is invalid returns an empty rectangle.
  802. */
  803. public Rectangle getCharacterBounds(int i) {
  804. try {
  805. return editor.getUI().modelToView(editor, i);
  806. } catch (BadLocationException e) {
  807. return null;
  808. }
  809. }
  810. /**
  811. * Return the number of characters (valid indicies)
  812. *
  813. * @return the number of characters
  814. */
  815. public int getCharCount() {
  816. if (validateIfNecessary()) {
  817. Element elem = elementInfo.getElement();
  818. return elem.getEndOffset() - elem.getStartOffset();
  819. }
  820. return 0;
  821. }
  822. /**
  823. * Return the zero-based offset of the caret.
  824. *
  825. * Note: That to the right of the caret will have the same index
  826. * value as the offset (the caret is between two characters).
  827. * @return the zero-based offset of the caret.
  828. */
  829. public int getCaretPosition() {
  830. View v = getView();
  831. if (v == null) {
  832. return -1;
  833. }
  834. Container c = v.getContainer();
  835. if (c == null) {
  836. return -1;
  837. }
  838. if (c instanceof JTextComponent) {
  839. return ((JTextComponent)c).getCaretPosition();
  840. } else {
  841. return -1;
  842. }
  843. }
  844. /**
  845. * IndexedSegment extends Segment adding the offset into the
  846. * the model the <code>Segment</code> was asked for.
  847. */
  848. private class IndexedSegment extends Segment {
  849. /**
  850. * Offset into the model that the position represents.
  851. */
  852. public int modelOffset;
  853. }
  854. public String getAtIndex(int part, int index) {
  855. return getAtIndex(part, index, 0);
  856. }
  857. public String getAfterIndex(int part, int index) {
  858. return getAtIndex(part, index, 1);
  859. }
  860. public String getBeforeIndex(int part, int index) {
  861. return getAtIndex(part, index, -1);
  862. }
  863. /**
  864. * Gets the word, sentence, or character at <code>index</code>.
  865. * If <code>direction</code> is non-null this will find the
  866. * next/previous word/sentence/character.
  867. */
  868. private String getAtIndex(int part, int index, int direction) {
  869. if (model instanceof AbstractDocument) {
  870. ((AbstractDocument)model).readLock();
  871. }
  872. try {
  873. if (index < 0 || index >= model.getLength()) {
  874. return null;
  875. }
  876. switch (part) {
  877. case AccessibleText.CHARACTER:
  878. if (index + direction < model.getLength() &&
  879. index + direction >= 0) {
  880. return model.getText(index + direction, 1);
  881. }
  882. break;
  883. case AccessibleText.WORD:
  884. case AccessibleText.SENTENCE:
  885. IndexedSegment seg = getSegmentAt(part, index);
  886. if (seg != null) {
  887. if (direction != 0) {
  888. int next;
  889. if (direction < 0) {
  890. next = seg.modelOffset - 1;
  891. }
  892. else {
  893. next = seg.modelOffset + direction * seg.count;
  894. }
  895. if (next >= 0 && next <= model.getLength()) {
  896. seg = getSegmentAt(part, next);
  897. }
  898. else {
  899. seg = null;
  900. }
  901. }
  902. if (seg != null) {
  903. return new String(seg.array, seg.offset,
  904. seg.count);
  905. }
  906. }
  907. break;
  908. default:
  909. break;
  910. }
  911. } catch (BadLocationException e) {
  912. } finally {
  913. if (model instanceof AbstractDocument) {
  914. ((AbstractDocument)model).readUnlock();
  915. }
  916. }
  917. return null;
  918. }
  919. /*
  920. * Returns the paragraph element for the specified index.
  921. */
  922. private Element getParagraphElement(int index) {
  923. if (model instanceof PlainDocument ) {
  924. PlainDocument sdoc = (PlainDocument)model;
  925. return sdoc.getParagraphElement(index);
  926. } else if (model instanceof StyledDocument) {
  927. StyledDocument sdoc = (StyledDocument)model;
  928. return sdoc.getParagraphElement(index);
  929. } else {
  930. Element para = null;
  931. for (para = model.getDefaultRootElement(); ! para.isLeaf(); ) {
  932. int pos = para.getElementIndex(index);
  933. para = para.getElement(pos);
  934. }
  935. if (para == null) {
  936. return null;
  937. }
  938. return para.getParentElement();
  939. }
  940. }
  941. /*
  942. * Returns a <code>Segment</code> containing the paragraph text
  943. * at <code>index</code>, or null if <code>index</code> isn't
  944. * valid.
  945. */
  946. private IndexedSegment getParagraphElementText(int index)
  947. throws BadLocationException {
  948. Element para = getParagraphElement(index);
  949. if (para != null) {
  950. IndexedSegment segment = new IndexedSegment();
  951. try {
  952. int length = para.getEndOffset() - para.getStartOffset();
  953. model.getText(para.getStartOffset(), length, segment);
  954. } catch (BadLocationException e) {
  955. return null;
  956. }
  957. segment.modelOffset = para.getStartOffset();
  958. return segment;
  959. }
  960. return null;
  961. }
  962. /**
  963. * Returns the Segment at <code>index</code> representing either
  964. * the paragraph or sentence as identified by <code>part</code>, or
  965. * null if a valid paragraph/sentence can't be found. The offset
  966. * will point to the start of the word/sentence in the array, and
  967. * the modelOffset will point to the location of the word/sentence
  968. * in the model.
  969. */
  970. private IndexedSegment getSegmentAt(int part, int index)
  971. throws BadLocationException {
  972. IndexedSegment seg = getParagraphElementText(index);
  973. if (seg == null) {
  974. return null;
  975. }
  976. BreakIterator iterator;
  977. switch (part) {
  978. case AccessibleText.WORD:
  979. iterator = BreakIterator.getWordInstance(getLocale());
  980. break;
  981. case AccessibleText.SENTENCE:
  982. iterator = BreakIterator.getSentenceInstance(getLocale());
  983. break;
  984. default:
  985. return null;
  986. }
  987. seg.first();
  988. iterator.setText(seg);
  989. int end = iterator.following(index - seg.modelOffset + seg.offset);
  990. if (end == BreakIterator.DONE) {
  991. return null;
  992. }
  993. if (end > seg.offset + seg.count) {
  994. return null;
  995. }
  996. int begin = iterator.previous();
  997. if (begin == BreakIterator.DONE ||
  998. begin >= seg.offset + seg.count) {
  999. return null;
  1000. }
  1001. seg.modelOffset = seg.modelOffset + begin - seg.offset;
  1002. seg.offset = begin;
  1003. seg.count = end - begin;
  1004. return seg;
  1005. }
  1006. /**
  1007. * Return the AttributeSet for a given character at a given index
  1008. *
  1009. * @param i the zero-based index into the text
  1010. * @return the AttributeSet of the character
  1011. */
  1012. public AttributeSet getCharacterAttribute(int i) {
  1013. if (model instanceof StyledDocument) {
  1014. StyledDocument doc = (StyledDocument)model;
  1015. Element elem = doc.getCharacterElement(i);
  1016. if (elem != null) {
  1017. return elem.getAttributes();
  1018. }
  1019. }
  1020. return null;
  1021. }
  1022. /**
  1023. * Returns the start offset within the selected text.
  1024. * If there is no selection, but there is
  1025. * a caret, the start and end offsets will be the same.
  1026. *
  1027. * @return the index into the text of the start of the selection
  1028. */
  1029. public int getSelectionStart() {
  1030. return editor.getSelectionStart();
  1031. }
  1032. /**
  1033. * Returns the end offset within the selected text.
  1034. * If there is no selection, but there is
  1035. * a caret, the start and end offsets will be the same.
  1036. *
  1037. * @return the index into teh text of the end of the selection
  1038. */
  1039. public int getSelectionEnd() {
  1040. return editor.getSelectionEnd();
  1041. }
  1042. /**
  1043. * Returns the portion of the text that is selected.
  1044. *
  1045. * @return the String portion of the text that is selected
  1046. */
  1047. public String getSelectedText() {
  1048. return editor.getSelectedText();
  1049. }
  1050. /*
  1051. * Returns the text substring starting at the specified
  1052. * offset with the specified length.
  1053. */
  1054. private String getText(int offset, int length)
  1055. throws BadLocationException {
  1056. if (model != null && model instanceof StyledDocument) {
  1057. StyledDocument doc = (StyledDocument)model;
  1058. return model.getText(offset, length);
  1059. } else {
  1060. return null;
  1061. }
  1062. }
  1063. }
  1064. }
  1065. /*
  1066. * ElementInfo for images
  1067. */
  1068. private class IconElementInfo extends ElementInfo implements Accessible {
  1069. private int width = -1;
  1070. private int height = -1;
  1071. IconElementInfo(Element element, ElementInfo parent) {
  1072. super(element, parent);
  1073. }
  1074. protected void invalidate(boolean first) {
  1075. super.invalidate(first);
  1076. width = height = -1;
  1077. }
  1078. private int getImageSize(Object key) {
  1079. if (validateIfNecessary()) {
  1080. int size = getIntAttr(getAttributes(), key, -1);
  1081. if (size == -1) {
  1082. View v = getView();
  1083. size = 0;
  1084. if (v instanceof ImageView) {
  1085. Image img = ((ImageView)v).getImage();
  1086. if (img != null) {
  1087. if (key == HTML.Attribute.WIDTH) {
  1088. size = img.getWidth(null);
  1089. }
  1090. else {
  1091. size = img.getHeight(null);
  1092. }
  1093. }
  1094. }
  1095. }
  1096. return size;
  1097. }
  1098. return 0;
  1099. }
  1100. // begin AccessibleIcon implementation ...
  1101. private AccessibleContext accessibleContext;
  1102. public AccessibleContext getAccessibleContext() {
  1103. if (accessibleContext == null) {
  1104. accessibleContext = new IconAccessibleContext(this);
  1105. }
  1106. return accessibleContext;
  1107. }
  1108. /*
  1109. * AccessibleContext for images
  1110. */
  1111. protected class IconAccessibleContext extends HTMLAccessibleContext
  1112. implements AccessibleIcon {
  1113. public IconAccessibleContext(ElementInfo elementInfo) {
  1114. super(elementInfo);
  1115. }
  1116. /**
  1117. * Gets the accessibleName property of this object. The accessibleName
  1118. * property of an object is a localized String that designates the purpose
  1119. * of the object. For example, the accessibleName property of a label
  1120. * or button might be the text of the label or button itself. In the
  1121. * case of an object that doesn't display its name, the accessibleName
  1122. * should still be set. For example, in the case of a text field used
  1123. * to enter the name of a city, the accessibleName for the en_US locale
  1124. * could be 'city.'
  1125. *
  1126. * @return the localized name of the object; null if this
  1127. * object does not have a name
  1128. *
  1129. * @see #setAccessibleName
  1130. */
  1131. public String getAccessibleName() {
  1132. if (model != null) {
  1133. return (String)model.getProperty(Document.TitleProperty);
  1134. } else {
  1135. return null;
  1136. }
  1137. }
  1138. /**
  1139. * Gets the accessibleDescription property of this object. If this
  1140. * property isn't set, returns the content type of this
  1141. * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
  1142. *
  1143. * @return the localized description of the object; <code>null</code>
  1144. * if this object does not have a description
  1145. *
  1146. * @see #setAccessibleName
  1147. */
  1148. public String getAccessibleDescription() {
  1149. return editor.getContentType();
  1150. }
  1151. /**
  1152. * Gets the role of this object. The role of the object is the generic
  1153. * purpose or use of the class of this object. For example, the role
  1154. * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
  1155. * AccessibleRole are provided so component developers can pick from
  1156. * a set of predefined roles. This enables assistive technologies to
  1157. * provide a consistent interface to various tweaked subclasses of
  1158. * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
  1159. * that act like a push button) as well as distinguish between sublasses
  1160. * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
  1161. * and AccessibleRole.RADIO_BUTTON for radio buttons).
  1162. * <p>Note that the AccessibleRole class is also extensible, so
  1163. * custom component developers can define their own AccessibleRole's
  1164. * if the set of predefined roles is inadequate.
  1165. *
  1166. * @return an instance of AccessibleRole describing the role of the object
  1167. * @see AccessibleRole
  1168. */
  1169. public AccessibleRole getAccessibleRole() {
  1170. return AccessibleRole.ICON;
  1171. }
  1172. public AccessibleIcon [] getAccessibleIcon() {
  1173. AccessibleIcon [] icons = new AccessibleIcon[1];
  1174. icons[0] = this;
  1175. return icons;
  1176. }
  1177. /**
  1178. * Gets the description of the icon. This is meant to be a brief
  1179. * textual description of the object. For example, it might be
  1180. * presented to a blind user to give an indication of the purpose
  1181. * of the icon.
  1182. *
  1183. * @return the description of the icon
  1184. */
  1185. public String getAccessibleIconDescription() {
  1186. return ((ImageView)getView()).getAltText();
  1187. }
  1188. /**
  1189. * Sets the description of the icon. This is meant to be a brief
  1190. * textual description of the object. For example, it might be
  1191. * presented to a blind user to give an indication of the purpose
  1192. * of the icon.
  1193. *
  1194. * @param description the description of the icon
  1195. */
  1196. public void setAccessibleIconDescription(String description) {
  1197. }
  1198. /**
  1199. * Gets the width of the icon
  1200. *
  1201. * @return the width of the icon.
  1202. */
  1203. public int getAccessibleIconWidth() {
  1204. if (width == -1) {
  1205. width = getImageSize(HTML.Attribute.WIDTH);
  1206. }
  1207. return width;
  1208. }
  1209. /**
  1210. * Gets the height of the icon
  1211. *
  1212. * @return the height of the icon.
  1213. */
  1214. public int getAccessibleIconHeight() {
  1215. if (height == -1) {
  1216. height = getImageSize(HTML.Attribute.HEIGHT);
  1217. }
  1218. return height;
  1219. }
  1220. }
  1221. // ... end AccessibleIconImplementation
  1222. }
  1223. /**
  1224. * TableElementInfo encapsulates information about a HTML.Tag.TABLE.
  1225. * To make access fast it crates a grid containing the children to
  1226. * allow for access by row, column. TableElementInfo will contain
  1227. * TableRowElementInfos, which will contain TableCellElementInfos.
  1228. * Any time one of the rows or columns becomes invalid the table is
  1229. * invalidated. This is because any time one of the child attributes
  1230. * changes the size of the grid may have changed.
  1231. */
  1232. private class TableElementInfo extends ElementInfo
  1233. implements Accessible {
  1234. protected ElementInfo caption;
  1235. /**
  1236. * Allocation of the table by row x column. There may be holes (eg
  1237. * nulls) depending upon the html, any cell that has a rowspan/colspan
  1238. * > 1 will be contained multiple times in the grid.
  1239. */
  1240. private TableCellElementInfo[][] grid;
  1241. TableElementInfo(Element e, ElementInfo parent) {
  1242. super(e, parent);
  1243. }
  1244. public ElementInfo getCaptionInfo() {
  1245. return caption;
  1246. }
  1247. /**
  1248. * Overriden to update the grid when validating.
  1249. */
  1250. protected void validate() {
  1251. super.validate();
  1252. updateGrid();
  1253. }
  1254. /**
  1255. * Overriden to only alloc instances of TableRowElementInfos.
  1256. */
  1257. protected void loadChildren(Element e) {
  1258. for (int counter = 0; counter < e.getElementCount(); counter++) {
  1259. Element child = e.getElement(counter);
  1260. AttributeSet attrs = child.getAttributes();
  1261. if (attrs.getAttribute(StyleConstants.NameAttribute) ==
  1262. HTML.Tag.TR) {
  1263. addChild(new TableRowElementInfo(child, this, counter));
  1264. } else if (attrs.getAttribute(StyleConstants.NameAttribute) ==
  1265. HTML.Tag.CAPTION) {
  1266. // Handle captions as a special case since all other
  1267. // children are table rows.
  1268. caption = createElementInfo(child, this);
  1269. }
  1270. }
  1271. }
  1272. /**
  1273. * Updates the grid.
  1274. */
  1275. private void updateGrid() {
  1276. // Determine the max row/col count.
  1277. int delta = 0;
  1278. int maxCols = 0;
  1279. int rows = 0;
  1280. for (int counter = 0; counter < getChildCount(); counter++) {
  1281. TableRowElementInfo row = getRow(counter);
  1282. int prev = 0;
  1283. for (int y = 0; y < delta; y++) {
  1284. prev = Math.max(prev, getRow(counter - y - 1).
  1285. getColumnCount(y + 2));
  1286. }
  1287. delta = Math.max(row.getRowCount(), delta);
  1288. delta--;
  1289. maxCols = Math.max(maxCols, row.getColumnCount() + prev);
  1290. }
  1291. rows = getChildCount() + delta;
  1292. // Alloc
  1293. grid = new TableCellElementInfo[rows][];
  1294. for (int counter = 0; counter < rows; counter++) {
  1295. grid[counter] = new TableCellElementInfo[maxCols];
  1296. }
  1297. // Update
  1298. for (int counter = 0; counter < rows; counter++) {
  1299. getRow(counter).updateGrid(counter);
  1300. }
  1301. }
  1302. /**
  1303. * Returns the TableCellElementInfo at the specified index.
  1304. */
  1305. public TableRowElementInfo getRow(int index) {
  1306. return (TableRowElementInfo)getChild(index);
  1307. }
  1308. /**
  1309. * Returns the TableCellElementInfo by row and column.
  1310. */
  1311. public TableCellElementInfo getCell(int r, int c) {
  1312. if (validateIfNecessary() && r < grid.length &&
  1313. c < grid[0].length) {
  1314. return grid[r][c];
  1315. }
  1316. return null;
  1317. }
  1318. /**
  1319. * Returns the rowspan of the specified entry.
  1320. */
  1321. public int getRowExtentAt(int r, int c) {
  1322. TableCellElementInfo cell = getCell(r, c);
  1323. if (cell != null) {
  1324. int rows = cell.getRowCount();
  1325. int delta = 1;
  1326. while ((r - delta) >= 0 && grid[r - delta][c] == cell) {
  1327. delta++;
  1328. }
  1329. return rows - delta + 1;
  1330. }
  1331. return 0;
  1332. }
  1333. /**
  1334. * Returns the colspan of the specified entry.
  1335. */
  1336. public int getColumnExtentAt(int r, int c) {
  1337. TableCellElementInfo cell = getCell(r, c);
  1338. if (cell != null) {
  1339. int cols = cell.getColumnCount();
  1340. int delta = 1;
  1341. while ((c - delta) >= 0 && grid[r][c - delta] == cell) {
  1342. delta++;
  1343. }
  1344. return cols - delta + 1;
  1345. }
  1346. return 0;
  1347. }
  1348. /**
  1349. * Returns the number of rows in the table.
  1350. */
  1351. public int getRowCount() {
  1352. if (validateIfNecessary()) {
  1353. return grid.length;
  1354. }
  1355. return 0;
  1356. }
  1357. /**
  1358. * Returns the number of columns in the table.
  1359. */
  1360. public int getColumnCount() {
  1361. if (validateIfNecessary() && grid.length > 0) {
  1362. return grid[0].length;
  1363. }
  1364. return 0;
  1365. }
  1366. // begin AccessibleTable implementation ...
  1367. private AccessibleContext accessibleContext;
  1368. public AccessibleContext getAccessibleContext() {
  1369. if (accessibleContext == null) {
  1370. accessibleContext = new TableAccessibleContext(this);
  1371. }
  1372. return accessibleContext;
  1373. }
  1374. /*
  1375. * AccessibleContext for tables
  1376. */
  1377. public class TableAccessibleContext extends HTMLAccessibleContext
  1378. implements AccessibleTable {
  1379. private AccessibleHeadersTable rowHeadersTable;
  1380. public TableAccessibleContext(ElementInfo elementInfo) {
  1381. super(elementInfo);
  1382. }
  1383. /**
  1384. * Gets the accessibleName property of this object. The accessibleName
  1385. * property of an object is a localized String that designates the purpose
  1386. * of the object. For example, the accessibleName property of a label
  1387. * or button might be the text of the label or button itself. In the
  1388. * case of an object that doesn't display its name, the accessibleName
  1389. * should still be set. For example, in the case of a text field used
  1390. * to enter the name of a city, the accessibleName for the en_US locale
  1391. * could be 'city.'
  1392. *
  1393. * @return the localized name of the object; null if this
  1394. * object does not have a name
  1395. *
  1396. * @see #setAccessibleName
  1397. */
  1398. public String getAccessibleName() {
  1399. // return the role of the object
  1400. return getAccessibleRole().toString();
  1401. }
  1402. /**
  1403. * Gets the accessibleDescription property of this object. If this
  1404. * property isn't set, returns the content type of this
  1405. * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
  1406. *
  1407. * @return the localized description of the object; <code>null</code>
  1408. * if this object does not have a description
  1409. *
  1410. * @see #setAccessibleName
  1411. */
  1412. public String getAccessibleDescription() {
  1413. return editor.getContentType();
  1414. }
  1415. /**
  1416. * Gets the role of this object. The role of the object is the generic
  1417. * purpose or use of the class of this object. For example, the role
  1418. * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
  1419. * AccessibleRole are provided so component developers can pick from
  1420. * a set of predefined roles. This enables assistive technologies to
  1421. * provide a consistent interface to various tweaked subclasses of
  1422. * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
  1423. * that act like a push button) as well as distinguish between sublasses
  1424. * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
  1425. * and AccessibleRole.RADIO_BUTTON for radio buttons).
  1426. * <p>Note that the AccessibleRole class is also extensible, so
  1427. * custom component developers can define their own AccessibleRole's
  1428. * if the set of predefined roles is inadequate.
  1429. *
  1430. * @return an instance of AccessibleRole describing the role of the object
  1431. * @see AccessibleRole
  1432. */
  1433. public AccessibleRole getAccessibleRole() {
  1434. return AccessibleRole.TABLE;
  1435. }
  1436. /**
  1437. * Gets the 0-based index of this object in its accessible parent.
  1438. *
  1439. * @return the 0-based index of this object in its parent; -1 if this
  1440. * object does not have an accessible parent.
  1441. *
  1442. * @see #getAccessibleParent
  1443. * @see #getAccessibleChildrenCount
  1444. * @gsee #getAccessibleChild
  1445. */
  1446. public int getAccessibleIndexInParent() {
  1447. return elementInfo.getIndexInParent();
  1448. }
  1449. /**
  1450. * Returns the number of accessible children of the object.
  1451. *
  1452. * @return the number of accessible children of the object.
  1453. */
  1454. public int getAccessibleChildrenCount() {
  1455. return ((TableElementInfo)elementInfo).getRowCount() *
  1456. ((TableElementInfo)elementInfo).getColumnCount();
  1457. }
  1458. /**
  1459. * Returns the specified Accessible child of the object. The Accessible
  1460. * children of an Accessible object are zero-based, so the first child
  1461. * of an Accessible child is at index 0, the second child is at index 1,
  1462. * and so on.
  1463. *
  1464. * @param i zero-based index of child
  1465. * @return the Accessible child of the object
  1466. * @see #getAccessibleChildrenCount
  1467. */
  1468. public Accessible getAccessibleChild(int i) {
  1469. int rowCount = ((TableElementInfo)elementInfo).getRowCount();
  1470. int columnCount = ((TableElementInfo)elementInfo).getColumnCount();
  1471. int r = i / rowCount;
  1472. int c = i % columnCount;
  1473. if (r < 0 || r >= rowCount || c < 0 || c >= columnCount) {
  1474. return null;
  1475. } else {
  1476. return getAccessibleAt(r, c);
  1477. }
  1478. }
  1479. public AccessibleTable getAccessibleTable() {
  1480. return this;
  1481. }
  1482. /**
  1483. * Returns the caption for the table.
  1484. *
  1485. * @return the caption for the table
  1486. */
  1487. public Accessible getAccessibleCaption() {
  1488. ElementInfo captionInfo = getCaptionInfo();
  1489. if (captionInfo instanceof Accessible) {
  1490. return (Accessible)caption;
  1491. } else {
  1492. return null;
  1493. }
  1494. }
  1495. /**
  1496. * Sets the caption for the table.
  1497. *
  1498. * @param a the caption for the table
  1499. */
  1500. public void setAccessibleCaption(Accessible<