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