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.<