Home » openjdk-7 » javax » swing » text » html » [javadoc | source]

    1   /*
    2    * Copyright (c) 2000, 2008, 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 sublasses
  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 sublasses
  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               public int getIndexAtPoint(Point p) {
  920                   View v = getView();
  921                   if (v != null) {
  922                       return v.viewToModel(p.x, p.y, getBounds());
  923                   } else {
  924                       return -1;
  925                   }
  926               }
  927   
  928               /**
  929                * Determine the bounding box of the character at the given
  930                * index into the string.  The bounds are returned in local
  931                * coordinates.  If the index is invalid an empty rectangle is
  932                * returned.
  933                *
  934                * @param i the index into the String
  935                * @return the screen coordinates of the character's the bounding box,
  936                * if index is invalid returns an empty rectangle.
  937                */
  938               public Rectangle getCharacterBounds(int i) {
  939                   try {
  940                       return editor.getUI().modelToView(editor, i);
  941                   } catch (BadLocationException e) {
  942                       return null;
  943                   }
  944               }
  945   
  946               /**
  947                * Return the number of characters (valid indicies)
  948                *
  949                * @return the number of characters
  950                */
  951               public int getCharCount() {
  952                   if (validateIfNecessary()) {
  953                       Element elem = elementInfo.getElement();
  954                       return elem.getEndOffset() - elem.getStartOffset();
  955                   }
  956                   return 0;
  957               }
  958   
  959               /**
  960                * Return the zero-based offset of the caret.
  961                *
  962                * Note: That to the right of the caret will have the same index
  963                * value as the offset (the caret is between two characters).
  964                * @return the zero-based offset of the caret.
  965                */
  966               public int getCaretPosition() {
  967                   View v = getView();
  968                   if (v == null) {
  969                       return -1;
  970                   }
  971                   Container c = v.getContainer();
  972                   if (c == null) {
  973                       return -1;
  974                   }
  975                   if (c instanceof JTextComponent) {
  976                       return ((JTextComponent)c).getCaretPosition();
  977                   } else {
  978                       return -1;
  979                   }
  980               }
  981   
  982               /**
  983                * IndexedSegment extends Segment adding the offset into the
  984                * the model the <code>Segment</code> was asked for.
  985                */
  986               private class IndexedSegment extends Segment {
  987                   /**
  988                    * Offset into the model that the position represents.
  989                    */
  990                   public int modelOffset;
  991               }
  992   
  993               public String getAtIndex(int part, int index) {
  994                   return getAtIndex(part, index, 0);
  995               }
  996   
  997   
  998               public String getAfterIndex(int part, int index) {
  999                   return getAtIndex(part, index, 1);
 1000               }
 1001   
 1002               public String getBeforeIndex(int part, int index) {
 1003                   return getAtIndex(part, index, -1);
 1004               }
 1005   
 1006               /**
 1007                * Gets the word, sentence, or character at <code>index</code>.
 1008                * If <code>direction</code> is non-null this will find the
 1009                * next/previous word/sentence/character.
 1010                */
 1011               private String getAtIndex(int part, int index, int direction) {
 1012                   if (model instanceof AbstractDocument) {
 1013                       ((AbstractDocument)model).readLock();
 1014                   }
 1015                   try {
 1016                       if (index < 0 || index >= model.getLength()) {
 1017                           return null;
 1018                       }
 1019                       switch (part) {
 1020                       case AccessibleText.CHARACTER:
 1021                           if (index + direction < model.getLength() &&
 1022                               index + direction >= 0) {
 1023                               return model.getText(index + direction, 1);
 1024                           }
 1025                           break;
 1026   
 1027   
 1028                       case AccessibleText.WORD:
 1029                       case AccessibleText.SENTENCE:
 1030                           IndexedSegment seg = getSegmentAt(part, index);
 1031                           if (seg != null) {
 1032                               if (direction != 0) {
 1033                                   int next;
 1034   
 1035   
 1036                                   if (direction < 0) {
 1037                                       next = seg.modelOffset - 1;
 1038                                   }
 1039                                   else {
 1040                                       next = seg.modelOffset + direction * seg.count;
 1041                                   }
 1042                                   if (next >= 0 && next <= model.getLength()) {
 1043                                       seg = getSegmentAt(part, next);
 1044                                   }
 1045                                   else {
 1046                                       seg = null;
 1047                                   }
 1048                               }
 1049                               if (seg != null) {
 1050                                   return new String(seg.array, seg.offset,
 1051                                                     seg.count);
 1052                               }
 1053                           }
 1054                           break;
 1055   
 1056                       default:
 1057                           break;
 1058                       }
 1059                   } catch (BadLocationException e) {
 1060                   } finally {
 1061                       if (model instanceof AbstractDocument) {
 1062                           ((AbstractDocument)model).readUnlock();
 1063                       }
 1064                   }
 1065                   return null;
 1066               }
 1067   
 1068               /*
 1069                * Returns the paragraph element for the specified index.
 1070                */
 1071               private Element getParagraphElement(int index) {
 1072                   if (model instanceof PlainDocument ) {
 1073                       PlainDocument sdoc = (PlainDocument)model;
 1074                       return sdoc.getParagraphElement(index);
 1075                   } else if (model instanceof StyledDocument) {
 1076                       StyledDocument sdoc = (StyledDocument)model;
 1077                       return sdoc.getParagraphElement(index);
 1078                   } else {
 1079                       Element para;
 1080                       for (para = model.getDefaultRootElement(); ! para.isLeaf(); ) {
 1081                           int pos = para.getElementIndex(index);
 1082                           para = para.getElement(pos);
 1083                       }
 1084                       if (para == null) {
 1085                           return null;
 1086                       }
 1087                       return para.getParentElement();
 1088                   }
 1089               }
 1090   
 1091               /*
 1092                * Returns a <code>Segment</code> containing the paragraph text
 1093                * at <code>index</code>, or null if <code>index</code> isn't
 1094                * valid.
 1095                */
 1096               private IndexedSegment getParagraphElementText(int index)
 1097                   throws BadLocationException {
 1098                   Element para = getParagraphElement(index);
 1099   
 1100   
 1101                   if (para != null) {
 1102                       IndexedSegment segment = new IndexedSegment();
 1103                       try {
 1104                           int length = para.getEndOffset() - para.getStartOffset();
 1105                           model.getText(para.getStartOffset(), length, segment);
 1106                       } catch (BadLocationException e) {
 1107                           return null;
 1108                       }
 1109                       segment.modelOffset = para.getStartOffset();
 1110                       return segment;
 1111                   }
 1112                   return null;
 1113               }
 1114   
 1115   
 1116               /**
 1117                * Returns the Segment at <code>index</code> representing either
 1118                * the paragraph or sentence as identified by <code>part</code>, or
 1119                * null if a valid paragraph/sentence can't be found. The offset
 1120                * will point to the start of the word/sentence in the array, and
 1121                * the modelOffset will point to the location of the word/sentence
 1122                * in the model.
 1123                */
 1124               private IndexedSegment getSegmentAt(int part, int index)
 1125                   throws BadLocationException {
 1126   
 1127                   IndexedSegment seg = getParagraphElementText(index);
 1128                   if (seg == null) {
 1129                       return null;
 1130                   }
 1131                   BreakIterator iterator;
 1132                   switch (part) {
 1133                   case AccessibleText.WORD:
 1134                       iterator = BreakIterator.getWordInstance(getLocale());
 1135                       break;
 1136                   case AccessibleText.SENTENCE:
 1137                       iterator = BreakIterator.getSentenceInstance(getLocale());
 1138                       break;
 1139                   default:
 1140                       return null;
 1141                   }
 1142                   seg.first();
 1143                   iterator.setText(seg);
 1144                   int end = iterator.following(index - seg.modelOffset + seg.offset);
 1145                   if (end == BreakIterator.DONE) {
 1146                       return null;
 1147                   }
 1148                   if (end > seg.offset + seg.count) {
 1149                       return null;
 1150                   }
 1151                   int begin = iterator.previous();
 1152                   if (begin == BreakIterator.DONE ||
 1153                       begin >= seg.offset + seg.count) {
 1154                       return null;
 1155                   }
 1156                   seg.modelOffset = seg.modelOffset + begin - seg.offset;
 1157                   seg.offset = begin;
 1158                   seg.count = end - begin;
 1159                   return seg;
 1160               }
 1161   
 1162               /**
 1163                * Return the AttributeSet for a given character at a given index
 1164                *
 1165                * @param i the zero-based index into the text
 1166                * @return the AttributeSet of the character
 1167                */
 1168               public AttributeSet getCharacterAttribute(int i) {
 1169                   if (model instanceof StyledDocument) {
 1170                       StyledDocument doc = (StyledDocument)model;
 1171                       Element elem = doc.getCharacterElement(i);
 1172                       if (elem != null) {
 1173                           return elem.getAttributes();
 1174                       }
 1175                   }
 1176                   return null;
 1177               }
 1178   
 1179               /**
 1180                * Returns the start offset within the selected text.
 1181                * If there is no selection, but there is
 1182                * a caret, the start and end offsets will be the same.
 1183                *
 1184                * @return the index into the text of the start of the selection
 1185                */
 1186               public int getSelectionStart() {
 1187                   return editor.getSelectionStart();
 1188               }
 1189   
 1190               /**
 1191                * Returns the end offset within the selected text.
 1192                * If there is no selection, but there is
 1193                * a caret, the start and end offsets will be the same.
 1194                *
 1195                * @return the index into teh text of the end of the selection
 1196                */
 1197               public int getSelectionEnd() {
 1198                   return editor.getSelectionEnd();
 1199               }
 1200   
 1201               /**
 1202                * Returns the portion of the text that is selected.
 1203                *
 1204                * @return the String portion of the text that is selected
 1205                */
 1206               public String getSelectedText() {
 1207                   return editor.getSelectedText();
 1208               }
 1209   
 1210               /*
 1211                * Returns the text substring starting at the specified
 1212                * offset with the specified length.
 1213                */
 1214               private String getText(int offset, int length)
 1215                   throws BadLocationException {
 1216   
 1217                   if (model != null && model instanceof StyledDocument) {
 1218                       StyledDocument doc = (StyledDocument)model;
 1219                       return model.getText(offset, length);
 1220                   } else {
 1221                       return null;
 1222                   }
 1223               }
 1224           }
 1225       }
 1226   
 1227       /*
 1228        * ElementInfo for images
 1229        */
 1230       private class IconElementInfo extends ElementInfo implements Accessible {
 1231   
 1232           private int width = -1;
 1233           private int height = -1;
 1234   
 1235           IconElementInfo(Element element, ElementInfo parent) {
 1236               super(element, parent);
 1237           }
 1238   
 1239           protected void invalidate(boolean first) {
 1240               super.invalidate(first);
 1241               width = height = -1;
 1242           }
 1243   
 1244           private int getImageSize(Object key) {
 1245               if (validateIfNecessary()) {
 1246                   int size = getIntAttr(getAttributes(), key, -1);
 1247   
 1248                   if (size == -1) {
 1249                       View v = getView();
 1250   
 1251                       size = 0;
 1252                       if (v instanceof ImageView) {
 1253                           Image img = ((ImageView)v).getImage();
 1254                           if (img != null) {
 1255                               if (key == HTML.Attribute.WIDTH) {
 1256                                   size = img.getWidth(null);
 1257                               }
 1258                               else {
 1259                                   size = img.getHeight(null);
 1260                               }
 1261                           }
 1262                       }
 1263                   }
 1264                   return size;
 1265               }
 1266               return 0;
 1267           }
 1268   
 1269           // begin AccessibleIcon implementation ...
 1270           private AccessibleContext accessibleContext;
 1271   
 1272           public AccessibleContext getAccessibleContext() {
 1273               if (accessibleContext == null) {
 1274                   accessibleContext = new IconAccessibleContext(this);
 1275               }
 1276               return accessibleContext;
 1277           }
 1278   
 1279           /*
 1280            * AccessibleContext for images
 1281            */
 1282           protected class IconAccessibleContext extends HTMLAccessibleContext
 1283               implements AccessibleIcon  {
 1284   
 1285               public IconAccessibleContext(ElementInfo elementInfo) {
 1286                   super(elementInfo);
 1287               }
 1288   
 1289               /**
 1290                * Gets the accessibleName property of this object.  The accessibleName
 1291                * property of an object is a localized String that designates the purpose
 1292                * of the object.  For example, the accessibleName property of a label
 1293                * or button might be the text of the label or button itself.  In the
 1294                * case of an object that doesn't display its name, the accessibleName
 1295                * should still be set.  For example, in the case of a text field used
 1296                * to enter the name of a city, the accessibleName for the en_US locale
 1297                * could be 'city.'
 1298                *
 1299                * @return the localized name of the object; null if this
 1300                * object does not have a name
 1301                *
 1302                * @see #setAccessibleName
 1303                */
 1304               public String getAccessibleName() {
 1305                   return getAccessibleIconDescription();
 1306               }
 1307   
 1308               /**
 1309                * Gets the accessibleDescription property of this object.  If this
 1310                * property isn't set, returns the content type of this
 1311                * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
 1312                *
 1313                * @return the localized description of the object; <code>null</code>
 1314                *  if this object does not have a description
 1315                *
 1316                * @see #setAccessibleName
 1317                */
 1318               public String getAccessibleDescription() {
 1319                   return editor.getContentType();
 1320               }
 1321   
 1322               /**
 1323                * Gets the role of this object.  The role of the object is the generic
 1324                * purpose or use of the class of this object.  For example, the role
 1325                * of a push button is AccessibleRole.PUSH_BUTTON.  The roles in
 1326                * AccessibleRole are provided so component developers can pick from
 1327                * a set of predefined roles.  This enables assistive technologies to
 1328                * provide a consistent interface to various tweaked subclasses of
 1329                * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
 1330                * that act like a push button) as well as distinguish between sublasses
 1331                * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
 1332                * and AccessibleRole.RADIO_BUTTON for radio buttons).
 1333                * <p>Note that the AccessibleRole class is also extensible, so
 1334                * custom component developers can define their own AccessibleRole's
 1335                * if the set of predefined roles is inadequate.
 1336                *
 1337                * @return an instance of AccessibleRole describing the role of the object
 1338                * @see AccessibleRole
 1339                */
 1340               public AccessibleRole getAccessibleRole() {
 1341                   return AccessibleRole.ICON;
 1342               }
 1343   
 1344               public AccessibleIcon [] getAccessibleIcon() {
 1345                   AccessibleIcon [] icons = new AccessibleIcon[1];
 1346                   icons[0] = this;
 1347                   return icons;
 1348               }
 1349   
 1350               /**
 1351                * Gets the description of the icon.  This is meant to be a brief
 1352                * textual description of the object.  For example, it might be
 1353                * presented to a blind user to give an indication of the purpose
 1354                * of the icon.
 1355                *
 1356                * @return the description of the icon
 1357                */
 1358               public String getAccessibleIconDescription() {
 1359                   return ((ImageView)getView()).getAltText();
 1360               }
 1361   
 1362               /**
 1363                * Sets the description of the icon.  This is meant to be a brief
 1364                * textual description of the object.  For example, it might be
 1365                * presented to a blind user to give an indication of the purpose
 1366                * of the icon.
 1367                *
 1368                * @param description the description of the icon
 1369                */
 1370               public void setAccessibleIconDescription(String description) {
 1371               }
 1372   
 1373               /**
 1374                * Gets the width of the icon
 1375                *
 1376                * @return the width of the icon.
 1377                */
 1378               public int getAccessibleIconWidth() {
 1379                   if (width == -1) {
 1380                       width = getImageSize(HTML.Attribute.WIDTH);
 1381                   }
 1382                   return width;
 1383               }
 1384   
 1385               /**
 1386                * Gets the height of the icon
 1387                *
 1388                * @return the height of the icon.
 1389                */
 1390               public int getAccessibleIconHeight() {
 1391                   if (height == -1) {
 1392                       height = getImageSize(HTML.Attribute.HEIGHT);
 1393                   }
 1394                   return height;
 1395               }
 1396           }
 1397           // ... end AccessibleIconImplementation
 1398       }
 1399   
 1400   
 1401       /**
 1402        * TableElementInfo encapsulates information about a HTML.Tag.TABLE.
 1403        * To make access fast it crates a grid containing the children to
 1404        * allow for access by row, column. TableElementInfo will contain
 1405        * TableRowElementInfos, which will contain TableCellElementInfos.
 1406        * Any time one of the rows or columns becomes invalid the table is
 1407        * invalidated.  This is because any time one of the child attributes
 1408        * changes the size of the grid may have changed.
 1409        */
 1410       private class TableElementInfo extends ElementInfo
 1411           implements Accessible {
 1412   
 1413           protected ElementInfo caption;
 1414   
 1415           /**
 1416            * Allocation of the table by row x column. There may be holes (eg
 1417            * nulls) depending upon the html, any cell that has a rowspan/colspan
 1418            * > 1 will be contained multiple times in the grid.
 1419            */
 1420           private TableCellElementInfo[][] grid;
 1421   
 1422   
 1423           TableElementInfo(Element e, ElementInfo parent) {
 1424               super(e, parent);
 1425           }
 1426   
 1427           public ElementInfo getCaptionInfo() {
 1428               return caption;
 1429           }
 1430   
 1431           /**
 1432            * Overriden to update the grid when validating.
 1433            */
 1434           protected void validate() {
 1435               super.validate();
 1436               updateGrid();
 1437           }
 1438   
 1439           /**
 1440            * Overriden to only alloc instances of TableRowElementInfos.
 1441            */
 1442           protected void loadChildren(Element e) {
 1443   
 1444               for (int counter = 0; counter < e.getElementCount(); counter++) {
 1445                   Element child = e.getElement(counter);
 1446                   AttributeSet attrs = child.getAttributes();
 1447   
 1448                   if (attrs.getAttribute(StyleConstants.NameAttribute) ==
 1449                                          HTML.Tag.TR) {
 1450                       addChild(new TableRowElementInfo(child, this, counter));
 1451   
 1452                   } else if (attrs.getAttribute(StyleConstants.NameAttribute) ==
 1453                                          HTML.Tag.CAPTION) {
 1454                       // Handle captions as a special case since all other
 1455                       // children are table rows.
 1456                       caption = createElementInfo(child, this);
 1457                   }
 1458               }
 1459           }
 1460   
 1461           /**
 1462            * Updates the grid.
 1463            */
 1464           private void updateGrid() {
 1465               // Determine the max row/col count.
 1466               int delta = 0;
 1467               int maxCols = 0;
 1468               int rows;
 1469               for (int counter = 0; counter < getChildCount(); counter++) {
 1470                   TableRowElementInfo row = getRow(counter);
 1471                   int prev = 0;
 1472                   for (int y = 0; y < delta; y++) {
 1473                       prev = Math.max(prev, getRow(counter - y - 1).
 1474                                       getColumnCount(y + 2));
 1475                   }
 1476                   delta = Math.max(row.getRowCount(), delta);
 1477                   delta--;
 1478                   maxCols = Math.max(maxCols, row.getColumnCount() + prev);
 1479               }
 1480               rows = getChildCount() + delta;
 1481   
 1482               // Alloc
 1483               grid = new TableCellElementInfo[rows][];
 1484               for (int counter = 0; counter < rows; counter++) {
 1485                   grid[counter] = new TableCellElementInfo[maxCols];
 1486               }
 1487               // Update
 1488               for (int counter = 0; counter < rows; counter++) {
 1489                   getRow(counter).updateGrid(counter);
 1490               }
 1491           }
 1492   
 1493           /**
 1494            * Returns the TableCellElementInfo at the specified index.
 1495            */
 1496           public TableRowElementInfo getRow(int index) {
 1497               return (TableRowElementInfo)getChild(index);
 1498           }
 1499   
 1500           /**
 1501            * Returns the TableCellElementInfo by row and column.
 1502            */
 1503           public TableCellElementInfo getCell(int r, int c) {
 1504               if (validateIfNecessary() && r < grid.length &&
 1505                                            c < grid[0].length) {
 1506                   return grid[r][c];
 1507               }
 1508               return null;
 1509           }
 1510   
 1511           /**
 1512            * Returns the rowspan of the specified entry.
 1513            */
 1514           public int getRowExtentAt(int r, int c) {
 1515               TableCellElementInfo cell = getCell(r, c);
 1516   
 1517               if (cell != null) {
 1518                   int rows = cell.getRowCount();
 1519                   int delta = 1;
 1520   
 1521                   while ((r - delta) >= 0 && grid[r - delta][c] == cell) {
 1522                       delta++;
 1523                   }
 1524                   return rows - delta + 1;
 1525               }
 1526               return 0;
 1527           }
 1528   
 1529           /**
 1530            * Returns the colspan of the specified entry.
 1531            */
 1532           public int getColumnExtentAt(int r, int c) {
 1533               TableCellElementInfo cell = getCell(r, c);
 1534   
 1535               if (cell != null) {
 1536                   int cols = cell.getColumnCount();
 1537                   int delta = 1;
 1538   
 1539                   while ((c - delta) >= 0 && grid[r][c - delta] == cell) {
 1540                       delta++;
 1541                   }
 1542                   return cols - delta + 1;
 1543               }
 1544               return 0;
 1545           }
 1546   
 1547           /**
 1548            * Returns the number of rows in the table.
 1549            */
 1550           public int getRowCount() {
 1551               if (validateIfNecessary()) {
 1552                   return grid.length;
 1553               }
 1554               return 0;
 1555           }
 1556   
 1557           /**
 1558            * Returns the number of columns in the table.
 1559            */
 1560           public int getColumnCount() {
 1561               if (validateIfNecessary() && grid.length > 0) {
 1562                   return grid[0].length;
 1563               }
 1564               return 0;
 1565           }
 1566   
 1567           // begin AccessibleTable implementation ...
 1568           private AccessibleContext accessibleContext;
 1569   
 1570           public AccessibleContext getAccessibleContext() {
 1571               if (accessibleContext == null) {
 1572                   accessibleContext = new TableAccessibleContext(this);
 1573               }
 1574               return accessibleContext;
 1575           }
 1576   
 1577           /*
 1578            * AccessibleContext for tables
 1579            */
 1580           public class TableAccessibleContext extends HTMLAccessibleContext
 1581               implements AccessibleTable {
 1582   
 1583               private AccessibleHeadersTable rowHeadersTable;
 1584   
 1585               public TableAccessibleContext(ElementInfo elementInfo) {
 1586                   super(elementInfo);
 1587               }
 1588   
 1589               /**
 1590                * Gets the accessibleName property of this object.  The accessibleName
 1591                * property of an object is a localized String that designates the purpose
 1592                * of the object.  For example, the accessibleName property of a label
 1593                * or button might be the text of the label or button itself.  In the
 1594                * case of an object that doesn't display its name, the accessibleName
 1595                * should still be set.  For example, in the case of a text field used
 1596                * to enter the name of a city, the accessibleName for the en_US locale
 1597                * could be 'city.'
 1598                *
 1599                * @return the localized name of the object; null if this
 1600                * object does not have a name
 1601                *
 1602                * @see #setAccessibleName
 1603                */
 1604               public String getAccessibleName() {
 1605                   // return the role of the object
 1606                   return getAccessibleRole().toString();
 1607               }
 1608   
 1609               /**
 1610                * Gets the accessibleDescription property of this object.  If this
 1611                * property isn't set, returns the content type of this
 1612                * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
 1613                *
 1614                * @return the localized description of the object; <code>null</code>
 1615                *  if this object does not have a description
 1616                *
 1617                * @see #setAccessibleName
 1618                */
 1619               public String getAccessibleDescription() {
 1620                   return editor.getContentType();
 1621               }
 1622   
 1623               /**
 1624                * Gets the role of this object.  The role of the object is the generic
 1625                * purpose or use of the class of this object.  For example, the role
 1626                * of a push button is AccessibleRole.PUSH_BUTTON.  The roles in
 1627                * AccessibleRole are provided so component developers can pick from
 1628                * a set of predefined roles.  This enables assistive technologies to
 1629                * provide a consistent interface to various tweaked subclasses of
 1630                * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
 1631                * that act like a push button) as well as distinguish between sublasses
 1632                * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
 1633                * and AccessibleRole.RADIO_BUTTON for radio buttons).
 1634                * <p>Note that the AccessibleRole class is also extensible, so
 1635                * custom component developers can define their own AccessibleRole's
 1636                * if the set of predefined roles is inadequate.
 1637                *
 1638                * @return an instance of AccessibleRole describing the role of the object
 1639                * @see AccessibleRole
 1640                */
 1641               public AccessibleRole getAccessibleRole() {
 1642                   return AccessibleRole.TABLE;
 1643               }
 1644   
 1645               /**
 1646                * Gets the 0-based index of this object in its accessible parent.
 1647                *
 1648                * @return the 0-based index of this object in its parent; -1 if this
 1649                * object does not have an accessible parent.
 1650                *
 1651                * @see #getAccessibleParent
 1652                * @see #getAccessibleChildrenCount
 1653                * @gsee #getAccessibleChild
 1654                */
 1655               public int getAccessibleIndexInParent() {
 1656                   return elementInfo.getIndexInParent();
 1657               }
 1658   
 1659               /**
 1660                * Returns the number of accessible children of the object.
 1661                *
 1662                * @return the number of accessible children of the object.
 1663                */
 1664               public int getAccessibleChildrenCount() {
 1665                   return ((TableElementInfo)elementInfo).getRowCount() *
 1666                       ((TableElementInfo)elementInfo).getColumnCount();
 1667               }
 1668   
 1669               /**
 1670                * Returns the specified Accessible child of the object.  The Accessible
 1671                * children of an Accessible object are zero-based, so the first child
 1672                * of an Accessible child is at index 0, the second child is at index 1,
 1673                * and so on.
 1674                *
 1675                * @param i zero-based index of child
 1676                * @return the Accessible child of the object
 1677                * @see #getAccessibleChildrenCount
 1678                */
 1679               public Accessible getAccessibleChild(int i) {
 1680                   int rowCount = ((TableElementInfo)elementInfo).getRowCount();
 1681                   int columnCount = ((TableElementInfo)elementInfo).getColumnCount();
 1682                   int r = i / rowCount;
 1683                   int c = i % columnCount;
 1684                   if (r < 0 || r >= rowCount || c < 0 || c >= columnCount) {
 1685                       return null;
 1686                   } else {
 1687                       return getAccessibleAt(r, c);
 1688                   }
 1689               }
 1690   
 1691               public AccessibleTable getAccessibleTable() {
 1692                   return this;
 1693               }
 1694   
 1695               /**
 1696                * Returns the caption for the table.
 1697                *
 1698                * @return the caption for the table
 1699                */
 1700               public Accessible getAccessibleCaption() {
 1701                   ElementInfo captionInfo = getCaptionInfo();
 1702                   if (captionInfo instanceof Accessible) {
 1703                       return (Accessible)caption;
 1704                   } else {
 1705                       return null;
 1706                   }
 1707               }
 1708   
 1709               /**
 1710                * Sets the caption for the table.
 1711                *
 1712                * @param a the caption for the table
 1713                */
 1714               public void setAccessibleCaption(Accessible a) {
 1715               }
 1716   
 1717               /**
 1718                * Returns the summary description of the table.
 1719                *
 1720                * @return the summary description of the table
 1721                */
 1722               public Accessible getAccessibleSummary() {
 1723                   return null;
 1724               }
 1725   
 1726               /**
 1727                * Sets the summary description of the table
 1728                *
 1729                * @param a the summary description of the table
 1730                */
 1731               public void setAccessibleSummary(Accessible a) {
 1732               }
 1733   
 1734               /**
 1735                * Returns the number of rows in the table.
 1736                *
 1737                * @return the number of rows in the table
 1738                */
 1739               public int getAccessibleRowCount() {
 1740                   return ((TableElementInfo)elementInfo).getRowCount();
 1741               }
 1742   
 1743               /**
 1744                * Returns the number of columns in the table.
 1745                *
 1746                * @return the number of columns in the table
 1747                */
 1748               public int getAccessibleColumnCount() {
 1749                   return ((TableElementInfo)elementInfo).getColumnCount();
 1750               }
 1751   
 1752               /**
 1753                * Returns the Accessible at a specified row and column
 1754                * in the table.
 1755                *
 1756                * @param r zero-based row of the table
 1757                * @param c zero-based column of the table
 1758                * @return the Accessible at the specified row and column
 1759                */
 1760               public Accessible getAccessibleAt(int r, int c) {
 1761                   TableCellElementInfo cellInfo = getCell(r, c);
 1762                   if (cellInfo != null) {
 1763                       return cellInfo.getAccessible();
 1764                   } else {
 1765                       return null;
 1766                   }
 1767               }
 1768   
 1769               /**
 1770                * Returns the number of rows occupied by the Accessible at
 1771                * a specified row and column in the table.
 1772                *
 1773                * @return the number of rows occupied by the Accessible at a
 1774                * given specified (row, column)
 1775                */
 1776               public int getAccessibleRowExtentAt(int r, int c) {
 1777                   return ((TableElementInfo)elementInfo).getRowExtentAt(r, c);
 1778               }
 1779   
 1780               /**
 1781                * Returns the number of columns occupied by the Accessible at
 1782                * a specified row and column in the table.
 1783                *
 1784                * @return the number of columns occupied by the Accessible at a
 1785                * given specified row and column
 1786                */
 1787               public int getAccessibleColumnExtentAt(int r, int c) {
 1788                   return ((TableElementInfo)elementInfo).getColumnExtentAt(r, c);
 1789               }
 1790   
 1791               /**
 1792                * Returns the row headers as an AccessibleTable.
 1793                *
 1794                * @return an AccessibleTable representing the row
 1795                * headers
 1796                */
 1797               public AccessibleTable getAccessibleRowHeader() {
 1798                   return rowHeadersTable;
 1799               }
 1800   
 1801               /**
 1802                * Sets the row headers.
 1803                *
 1804                * @param table an AccessibleTable representing the
 1805                * row headers
 1806                */
 1807               public void setAccessibleRowHeader(AccessibleTable table) {
 1808               }
 1809   
 1810               /**
 1811                * Returns the column headers as an AccessibleTable.
 1812                *
 1813                * @return an AccessibleTable representing the column
 1814                * headers
 1815                */
 1816               public AccessibleTable getAccessibleColumnHeader() {
 1817                   return null;
 1818               }
 1819   
 1820               /**
 1821                * Sets the column headers.
 1822                *
 1823                * @param table an AccessibleTable representing the
 1824                * column headers
 1825                */
 1826               public void setAccessibleColumnHeader(AccessibleTable table) {
 1827               }
 1828   
 1829               /**
 1830                * Returns the description of the specified row in the table.
 1831                *
 1832                * @param r zero-based row of the table
 1833                * @return the description of the row
 1834                */
 1835               public Accessible getAccessibleRowDescription(int r) {
 1836                   return null;
 1837               }
 1838   
 1839               /**
 1840                * Sets the description text of the specified row of the table.
 1841                *
 1842                * @param r zero-based row of the table
 1843                * @param a the description of the row
 1844                */
 1845               public void setAccessibleRowDescription(int r, Accessible a) {
 1846               }
 1847   
 1848               /**
 1849                * Returns the description text of the specified column in the table.
 1850                *
 1851                * @param c zero-based column of the table
 1852                * @return the text description of the column
 1853                */
 1854               public Accessible getAccessibleColumnDescription(int c) {
 1855                   return null;
 1856               }
 1857   
 1858               /**
 1859                * Sets the description text of the specified column in the table.
 1860                *
 1861                * @param c zero-based column of the table
 1862                * @param a the text description of the column
 1863                */
 1864               public void setAccessibleColumnDescription(int c, Accessible a) {
 1865               }
 1866   
 1867               /**
 1868                * Returns a boolean value indicating whether the accessible at
 1869                * a specified row and column is selected.
 1870                *
 1871                * @param r zero-based row of the table
 1872                * @param c zero-based column of the table
 1873                * @return the boolean value true if the accessible at the
 1874                * row and column is selected. Otherwise, the boolean value
 1875                * false
 1876                */
 1877               public boolean isAccessibleSelected(int r, int c) {
 1878                   if (validateIfNecessary()) {
 1879                       if (r < 0 || r >= getAccessibleRowCount() ||
 1880                           c < 0 || c >= getAccessibleColumnCount()) {
 1881                           return false;
 1882                       }
 1883                       TableCellElementInfo cell = getCell(r, c);
 1884                       if (cell != null) {
 1885                           Element elem = cell.getElement();
 1886                           int start = elem.getStartOffset();
 1887                           int end = elem.getEndOffset();
 1888                           return start >= editor.getSelectionStart() &&
 1889                               end <= editor.getSelectionEnd();
 1890                       }
 1891                   }
 1892                   return false;
 1893               }
 1894   
 1895               /**
 1896                * Returns a boolean value indicating whether the specified row
 1897                * is selected.
 1898                *
 1899                * @param r zero-based row of the table
 1900                * @return the boolean value true if the specified row is selected.
 1901                * Otherwise, false.
 1902                */
 1903               public boolean isAccessibleRowSelected(int r) {
 1904                   if (validateIfNecessary()) {
 1905                       if (r < 0 || r >= getAccessibleRowCount()) {
 1906                           return false;
 1907                       }
 1908                       int nColumns = getAccessibleColumnCount();
 1909   
 1910                       TableCellElementInfo startCell = getCell(r, 0);
 1911                       if (startCell == null) {
 1912                           return false;
 1913                       }
 1914                       int start = startCell.getElement().getStartOffset();
 1915   
 1916                       TableCellElementInfo endCell = getCell(r, nColumns-1);
 1917                       if (endCell == null) {
 1918                           return false;
 1919                       }
 1920                       int end = endCell.getElement().getEndOffset();
 1921   
 1922                       return start >= editor.getSelectionStart() &&
 1923                           end <= editor.getSelectionEnd();
 1924                   }
 1925                   return false;
 1926               }
 1927   
 1928               /**
 1929                * Returns a boolean value indicating whether the specified column
 1930                * is selected.
 1931                *
 1932                * @param c zero-based column of the table
 1933                * @return the boolean value true if the specified column is selected.
 1934                * Otherwise, false.
 1935                */
 1936               public boolean isAccessibleColumnSelected(int c) {
 1937                   if (validateIfNecessary()) {
 1938                       if (c < 0 || c >= getAccessibleColumnCount()) {
 1939                           return false;
 1940                       }
 1941                       int nRows = getAccessibleRowCount();
 1942   
 1943                       TableCellElementInfo startCell = getCell(0, c);
 1944                       if (startCell == null) {
 1945                           return false;
 1946                       }
 1947                       int start = startCell.getElement().getStartOffset();
 1948   
 1949                       TableCellElementInfo endCell = getCell(nRows-1, c);
 1950                       if (endCell == null) {
 1951                           return false;
 1952                       }
 1953                       int end = endCell.getElement().getEndOffset();
 1954                       return start >= editor.getSelectionStart() &&
 1955                           end <= editor.getSelectionEnd();
 1956                   }
 1957                   return false;
 1958               }
 1959   
 1960               /**
 1961                * Returns the selected rows in a table.
 1962                *
 1963                * @return an array of selected rows where each element is a
 1964                * zero-based row of the table
 1965                */
 1966               public int [] getSelectedAccessibleRows() {
 1967                   if (validateIfNecessary()) {
 1968                       int nRows = getAccessibleRowCount();
 1969                       Vector<Integer> vec = new Vector<Integer>();
 1970   
 1971                       for (int i = 0; i < nRows; i++) {
 1972                           if (isAccessibleRowSelected(i)) {
 1973                               vec.addElement(Integer.valueOf(i));
 1974                           }
 1975                       }
 1976                       int retval[] = new int[vec.size()];
 1977                       for (int i = 0; i < retval.length; i++) {
 1978                           retval[i] = vec.elementAt(i).intValue();
 1979                       }
 1980                       return retval;
 1981                   }
 1982                   return new int[0];
 1983               }
 1984   
 1985               /**
 1986                * Returns the selected columns in a table.
 1987                *
 1988                * @return an array of selected columns where each element is a
 1989                * zero-based column of the table
 1990                */
 1991               public int [] getSelectedAccessibleColumns() {
 1992                   if (validateIfNecessary()) {
 1993                       int nColumns = getAccessibleRowCount();
 1994                       Vector<Integer> vec = new Vector<Integer>();
 1995   
 1996                       for (int i = 0; i < nColumns; i++) {
 1997                           if (isAccessibleColumnSelected(i)) {
 1998                               vec.addElement(Integer.valueOf(i));
 1999                           }
 2000                       }
 2001                       int retval[] = new int[vec.size()];
 2002                       for (int i = 0; i < retval.length; i++) {
 2003                           retval[i] = vec.elementAt(i).intValue();
 2004                       }
 2005                       return retval;
 2006                   }
 2007                   return new int[0];
 2008               }
 2009   
 2010               // begin AccessibleExtendedTable implementation -------------
 2011   
 2012               /**
 2013                * Returns the row number of an index in the table.
 2014                *
 2015                * @param index the zero-based index in the table
 2016                * @return the zero-based row of the table if one exists;
 2017                * otherwise -1.
 2018                */
 2019               public int getAccessibleRow(int index) {
 2020                   if (validateIfNecessary()) {
 2021                       int numCells = getAccessibleColumnCount() *
 2022                           getAccessibleRowCount();
 2023                       if (index >= numCells) {
 2024                           return -1;
 2025                       } else {
 2026                           return index / getAccessibleColumnCount();
 2027                       }
 2028                   }
 2029                   return -1;
 2030               }
 2031   
 2032               /**
 2033                * Returns the column number of an index in the table.
 2034                *
 2035                * @param index the zero-based index in the table
 2036                * @return the zero-based column of the table if one exists;
 2037                * otherwise -1.
 2038                */
 2039               public int getAccessibleColumn(int index) {
 2040                   if (validateIfNecessary()) {
 2041                       int numCells = getAccessibleColumnCount() *
 2042                           getAccessibleRowCount();
 2043                       if (index >= numCells) {
 2044                           return -1;
 2045                       } else {
 2046                           return index % getAccessibleColumnCount();
 2047                       }
 2048                   }
 2049                   return -1;
 2050               }
 2051   
 2052               /**
 2053                * Returns the index at a row and column in the table.
 2054                *
 2055                * @param r zero-based row of the table
 2056                * @param c zero-based column of the table
 2057                * @return the zero-based index in the table if one exists;
 2058                * otherwise -1.
 2059                */
 2060               public int getAccessibleIndex(int r, int c) {
 2061                   if (validateIfNecessary()) {
 2062                       if (r >= getAccessibleRowCount() ||
 2063                           c >= getAccessibleColumnCount()) {
 2064                           return -1;
 2065                       } else {
 2066                           return r * getAccessibleColumnCount() + c;
 2067                       }
 2068                   }
 2069                   return -1;
 2070               }
 2071   
 2072               /**
 2073                * Returns the row header at a row in a table.
 2074                * @param r zero-based row of the table
 2075                *
 2076                * @return a String representing the row header
 2077                * if one exists; otherwise null.
 2078                */
 2079               public String getAccessibleRowHeader(int r) {
 2080                   if (validateIfNecessary()) {
 2081                       TableCellElementInfo cellInfo = getCell(r, 0);
 2082                       if (cellInfo.isHeaderCell()) {
 2083                           View v = cellInfo.getView();
 2084                           if (v != null && model != null) {
 2085                               try {
 2086                                   return model.getText(v.getStartOffset(),
 2087                                                        v.getEndOffset() -
 2088                                                        v.getStartOffset());
 2089                               } catch (BadLocationException e) {
 2090                                   return null;
 2091                               }
 2092                           }
 2093                       }
 2094                   }
 2095                   return null;
 2096               }
 2097   
 2098               /**
 2099                * Returns the column header at a column in a table.
 2100                * @param c zero-based column of the table
 2101                *
 2102                * @return a String representing the column header
 2103                * if one exists; otherwise null.
 2104                */
 2105               public String getAccessibleColumnHeader(int c) {
 2106                   if (validateIfNecessary()) {
 2107                       TableCellElementInfo cellInfo = getCell(0, c);
 2108                       if (cellInfo.isHeaderCell()) {
 2109                           View v = cellInfo.getView();
 2110                           if (v != null && model != null) {
 2111                               try {
 2112                                   return model.getText(v.getStartOffset(),
 2113                                                        v.getEndOffset() -
 2114                                                        v.getStartOffset());
 2115                               } catch (BadLocationException e) {
 2116                                   return null;
 2117                               }
 2118                           }
 2119                       }
 2120                   }
 2121                   return null;
 2122               }
 2123   
 2124               public void addRowHeader(TableCellElementInfo cellInfo, int rowNumber) {
 2125                   if (rowHeadersTable == null) {
 2126                       rowHeadersTable = new AccessibleHeadersTable();
 2127                   }
 2128                   rowHeadersTable.addHeader(cellInfo, rowNumber);
 2129               }
 2130               // end of AccessibleExtendedTable implementation ------------
 2131   
 2132               protected class AccessibleHeadersTable implements AccessibleTable {
 2133   
 2134                   // Header information is modeled as a Hashtable of
 2135                   // ArrayLists where each Hashtable entry represents
 2136                   // a row containing one or more headers.
 2137                   private Hashtable<Integer, ArrayList<TableCellElementInfo>> headers =
 2138                           new Hashtable<Integer, ArrayList<TableCellElementInfo>>();
 2139                   private int rowCount = 0;
 2140                   private int columnCount = 0;
 2141   
 2142                   public void addHeader(TableCellElementInfo cellInfo, int rowNumber) {
 2143                       Integer rowInteger = Integer.valueOf(rowNumber);
 2144                       ArrayList<TableCellElementInfo> list = headers.get(rowInteger);
 2145                       if (list == null) {
 2146                           list = new ArrayList<TableCellElementInfo>();
 2147                           headers.put(rowInteger, list);
 2148                       }
 2149                       list.add(cellInfo);
 2150                   }
 2151   
 2152                   /**
 2153                    * Returns the caption for the table.
 2154                    *
 2155                    * @return the caption for the table
 2156                    */
 2157                   public Accessible getAccessibleCaption() {
 2158                       return null;
 2159                   }
 2160   
 2161                   /**
 2162                    * Sets the caption for the table.
 2163                    *
 2164                    * @param a the caption for the table
 2165                    */
 2166                   public void setAccessibleCaption(Accessible a) {
 2167                   }
 2168   
 2169                   /**
 2170                    * Returns the summary description of the table.
 2171                    *
 2172                    * @return the summary description of the table
 2173                    */
 2174                   public Accessible getAccessibleSummary() {
 2175                       return null;
 2176                   }
 2177   
 2178                   /**
 2179                    * Sets the summary description of the table
 2180                    *
 2181                    * @param a the summary description of the table
 2182                    */
 2183                   public void setAccessibleSummary(Accessible a) {
 2184                   }
 2185   
 2186                   /**
 2187                    * Returns the number of rows in the table.
 2188                    *
 2189                    * @return the number of rows in the table
 2190                    */
 2191                   public int getAccessibleRowCount() {
 2192                       return rowCount;
 2193                   }
 2194   
 2195                   /**
 2196                    * Returns the number of columns in the table.
 2197                    *
 2198                    * @return the number of columns in the table
 2199                    */
 2200                   public int getAccessibleColumnCount() {
 2201                       return columnCount;
 2202                   }
 2203   
 2204                   private TableCellElementInfo getElementInfoAt(int r, int c) {
 2205                       ArrayList<TableCellElementInfo> list = headers.get(Integer.valueOf(r));
 2206                       if (list != null) {
 2207                           return list.get(c);
 2208                       } else {
 2209                           return null;
 2210                       }
 2211                   }
 2212   
 2213                   /**
 2214                    * Returns the Accessible at a specified row and column
 2215                    * in the table.
 2216                    *
 2217                    * @param r zero-based row of the table
 2218                    * @param c zero-based column of the table
 2219                    * @return the Accessible at the specified row and column
 2220                    */
 2221                   public Accessible getAccessibleAt(int r, int c) {
 2222                       ElementInfo elementInfo = getElementInfoAt(r, c);
 2223                       if (elementInfo instanceof Accessible) {
 2224                           return (Accessible)elementInfo;
 2225                       } else {
 2226                           return null;
 2227                       }
 2228                   }
 2229   
 2230                   /**
 2231                    * Returns the number of rows occupied by the Accessible at
 2232                    * a specified row and column in the table.
 2233                    *
 2234                    * @return the number of rows occupied by the Accessible at a
 2235                    * given specified (row, column)
 2236                    */
 2237                   public int getAccessibleRowExtentAt(int r, int c) {
 2238                       TableCellElementInfo elementInfo = getElementInfoAt(r, c);
 2239                       if (elementInfo != null) {
 2240                           return elementInfo.getRowCount();
 2241                       } else {
 2242                           return 0;
 2243                       }
 2244                   }
 2245   
 2246                   /**
 2247                    * Returns the number of columns occupied by the Accessible at
 2248                    * a specified row and column in the table.
 2249                    *
 2250                    * @return the number of columns occupied by the Accessible at a
 2251                    * given specified row and column
 2252                    */
 2253                   public int getAccessibleColumnExtentAt(int r, int c) {
 2254                       TableCellElementInfo elementInfo = getElementInfoAt(r, c);
 2255                       if (elementInfo != null) {
 2256                           return elementInfo.getRowCount();
 2257                       } else {
 2258                           return 0;
 2259                       }
 2260                   }
 2261   
 2262                   /**
 2263                    * Returns the row headers as an AccessibleTable.
 2264                    *
 2265                    * @return an AccessibleTable representing the row
 2266                    * headers
 2267                    */
 2268                   public AccessibleTable getAccessibleRowHeader() {
 2269                       return null;
 2270                   }
 2271   
 2272                   /**
 2273                    * Sets the row headers.
 2274                    *
 2275                    * @param table an AccessibleTable representing the
 2276                    * row headers
 2277                    */
 2278                   public void setAccessibleRowHeader(AccessibleTable table) {
 2279                   }
 2280   
 2281                   /**
 2282                    * Returns the column headers as an AccessibleTable.
 2283                    *
 2284                    * @return an AccessibleTable representing the column
 2285                    * headers
 2286                    */
 2287                   public AccessibleTable getAccessibleColumnHeader() {
 2288                       return null;
 2289                   }
 2290   
 2291                   /**
 2292                    * Sets the column headers.
 2293                    *
 2294                    * @param table an AccessibleTable representing the
 2295                    * column headers
 2296                    */
 2297                   public void setAccessibleColumnHeader(AccessibleTable table) {
 2298                   }
 2299   
 2300                   /**
 2301                    * Returns the description of the specified row in the table.
 2302                    *
 2303                    * @param r zero-based row of the table
 2304                    * @return the description of the row
 2305                    */
 2306                   public Accessible getAccessibleRowDescription(int r) {
 2307                       return null;
 2308                   }
 2309   
 2310                   /**
 2311                    * Sets the description text of the specified row of the table.
 2312                    *
 2313                    * @param r zero-based row of the table
 2314                    * @param a the description of the row
 2315                    */
 2316                   public void setAccessibleRowDescription(int r, Accessible a) {
 2317                   }
 2318   
 2319                   /**
 2320                    * Returns the description text of the specified column in the table.
 2321                    *
 2322                    * @param c zero-based column of the table
 2323                    * @return the text description of the column
 2324                    */
 2325                   public Accessible getAccessibleColumnDescription(int c) {
 2326                       return null;
 2327                   }
 2328   
 2329                   /**
 2330                    * Sets the description text of the specified column in the table.
 2331                    *
 2332                    * @param c zero-based column of the table
 2333                    * @param a the text description of the column
 2334                    */
 2335                   public void setAccessibleColumnDescription(int c, Accessible a) {
 2336                   }
 2337   
 2338                   /**
 2339                    * Returns a boolean value indicating whether the accessible at
 2340                    * a specified row and column is selected.
 2341                    *
 2342                    * @param r zero-based row of the table
 2343                    * @param c zero-based column of the table
 2344                    * @return the boolean value true if the accessible at the
 2345                    * row and column is selected. Otherwise, the boolean value
 2346                    * false
 2347                    */
 2348                   public boolean isAccessibleSelected(int r, int c) {
 2349                       return false;
 2350                   }
 2351   
 2352                   /**
 2353                    * Returns a boolean value indicating whether the specified row
 2354                    * is selected.
 2355                    *
 2356                    * @param r zero-based row of the table
 2357                    * @return the boolean value true if the specified row is selected.
 2358                    * Otherwise, false.
 2359                    */
 2360                   public boolean isAccessibleRowSelected(int r) {
 2361                       return false;
 2362                   }
 2363   
 2364                   /**
 2365                    * Returns a boolean value indicating whether the specified column
 2366                    * is selected.
 2367                    *
 2368                    * @param c zero-based column of the table
 2369                    * @return the boolean value true if the specified column is selected.
 2370                    * Otherwise, false.
 2371                    */
 2372                   public boolean isAccessibleColumnSelected(int c) {
 2373                       return false;
 2374                   }
 2375   
 2376                   /**
 2377                    * Returns the selected rows in a table.
 2378                    *
 2379                    * @return an array of selected rows where each element is a
 2380                    * zero-based row of the table
 2381                    */
 2382                   public int [] getSelectedAccessibleRows() {
 2383                       return new int [0];
 2384                   }
 2385   
 2386                   /**
 2387                    * Returns the selected columns in a table.
 2388                    *
 2389                    * @return an array of selected columns where each element is a
 2390                    * zero-based column of the table
 2391                    */
 2392                   public int [] getSelectedAccessibleColumns() {
 2393                       return new int [0];
 2394                   }
 2395               }
 2396           } // ... end AccessibleHeadersTable
 2397   
 2398           /*
 2399            * ElementInfo for table rows
 2400            */
 2401           private class TableRowElementInfo extends ElementInfo {
 2402   
 2403               private TableElementInfo parent;
 2404               private int rowNumber;
 2405   
 2406               TableRowElementInfo(Element e, TableElementInfo parent, int rowNumber) {
 2407                   super(e, parent);
 2408                   this.parent = parent;
 2409                   this.rowNumber = rowNumber;
 2410               }
 2411   
 2412               protected void loadChildren(Element e) {
 2413                   for (int x = 0; x < e.getElementCount(); x++) {
 2414                       AttributeSet attrs = e.getElement(x).getAttributes();
 2415   
 2416                       if (attrs.getAttribute(StyleConstants.NameAttribute) ==
 2417                               HTML.Tag.TH) {
 2418                           TableCellElementInfo headerElementInfo =
 2419                               new TableCellElementInfo(e.getElement(x), this, true);
 2420                           addChild(headerElementInfo);
 2421   
 2422                           AccessibleTable at =
 2423                               parent.getAccessibleContext().getAccessibleTable();
 2424                           TableAccessibleContext tableElement =
 2425                               (TableAccessibleContext)at;
 2426                           tableElement.addRowHeader(headerElementInfo, rowNumber);
 2427   
 2428                       } else if (attrs.getAttribute(StyleConstants.NameAttribute) ==
 2429                               HTML.Tag.TD) {
 2430                           addChild(new TableCellElementInfo(e.getElement(x), this,
 2431                                                             false));
 2432                       }
 2433                   }
 2434               }
 2435   
 2436               /**
 2437                * Returns the max of the rowspans of the cells in this row.
 2438                */
 2439               public int getRowCount() {
 2440                   int rowCount = 1;
 2441                   if (validateIfNecessary()) {
 2442                       for (int counter = 0; counter < getChildCount();
 2443                            counter++) {
 2444   
 2445                           TableCellElementInfo cell = (TableCellElementInfo)
 2446                                                       getChild(counter);
 2447   
 2448                           if (cell.validateIfNecessary()) {
 2449                               rowCount = Math.max(rowCount, cell.getRowCount());
 2450                           }
 2451                       }
 2452                   }
 2453                   return rowCount;
 2454               }
 2455   
 2456               /**
 2457                * Returns the sum of the column spans of the individual
 2458                * cells in this row.
 2459                */
 2460               public int getColumnCount() {
 2461                   int colCount = 0;
 2462                   if (validateIfNecessary()) {
 2463                       for (int counter = 0; counter < getChildCount();
 2464                            counter++) {
 2465                           TableCellElementInfo cell = (TableCellElementInfo)
 2466                                                       getChild(counter);
 2467   
 2468                           if (cell.validateIfNecessary()) {
 2469                               colCount += cell.getColumnCount();
 2470                           }
 2471                       }
 2472                   }
 2473                   return colCount;
 2474               }
 2475   
 2476               /**
 2477                * Overriden to invalidate the table as well as
 2478                * TableRowElementInfo.
 2479                */
 2480               protected void invalidate(boolean first) {
 2481                   super.invalidate(first);
 2482                   getParent().invalidate(true);
 2483               }
 2484   
 2485               /**
 2486                * Places the TableCellElementInfos for this element in
 2487                * the grid.
 2488                */
 2489               private void updateGrid(int row) {
 2490                   if (validateIfNecessary()) {
 2491                       boolean emptyRow = false;
 2492   
 2493                       while (!emptyRow) {
 2494                           for (int counter = 0; counter < grid[row].length;
 2495                                    counter++) {
 2496                               if (grid[row][counter] == null) {
 2497                                   emptyRow = true;
 2498                                   break;
 2499                               }
 2500                           }
 2501                           if (!emptyRow) {
 2502                               row++;
 2503                           }
 2504                       }
 2505                       for (int col = 0, counter = 0; counter < getChildCount();
 2506                                counter++) {
 2507                           TableCellElementInfo cell = (TableCellElementInfo)
 2508                                                       getChild(counter);
 2509   
 2510                           while (grid[row][col] != null) {
 2511                               col++;
 2512                           }
 2513                           for (int rowCount = cell.getRowCount() - 1;
 2514                                rowCount >= 0; rowCount--) {
 2515                               for (int colCount = cell.getColumnCount() - 1;
 2516                                    colCount >= 0; colCount--) {
 2517                                   grid[row + rowCount][col + colCount] = cell;
 2518                               }
 2519                           }
 2520                           col += cell.getColumnCount();
 2521                       }
 2522                   }
 2523               }
 2524   
 2525               /**
 2526                * Returns the column count of the number of columns that have
 2527                * a rowcount >= rowspan.
 2528                */
 2529               private int getColumnCount(int rowspan) {
 2530                   if (validateIfNecessary()) {
 2531                       int cols = 0;
 2532                       for (int counter = 0; counter < getChildCount();
 2533                            counter++) {
 2534                           TableCellElementInfo cell = (TableCellElementInfo)
 2535                                                       getChild(counter);
 2536   
 2537                           if (cell.getRowCount() >= rowspan) {
 2538                               cols += cell.getColumnCount();
 2539                           }
 2540                       }
 2541                       return cols;
 2542                   }
 2543                   return 0;
 2544               }
 2545           }
 2546   
 2547           /**
 2548            * TableCellElementInfo is used to represents the cells of
 2549            * the table.
 2550            */
 2551           private class TableCellElementInfo extends ElementInfo {
 2552   
 2553               private Accessible accessible;
 2554               private boolean isHeaderCell;
 2555   
 2556               TableCellElementInfo(Element e, ElementInfo parent) {
 2557                   super(e, parent);
 2558                   this.isHeaderCell = false;
 2559               }
 2560   
 2561               TableCellElementInfo(Element e, ElementInfo parent,
 2562                                    boolean isHeaderCell) {
 2563                   super(e, parent);
 2564                   this.isHeaderCell = isHeaderCell;
 2565               }
 2566   
 2567               /*
 2568                * Returns whether this table cell is a header
 2569                */
 2570               public boolean isHeaderCell() {
 2571                   return this.isHeaderCell;
 2572               }
 2573   
 2574               /*
 2575                * Returns the Accessible representing this table cell
 2576                */
 2577               public Accessible getAccessible() {
 2578                   accessible = null;
 2579                   getAccessible(this);
 2580                   return accessible;
 2581               }
 2582   
 2583               /*
 2584                * Gets the outermost Accessible in the table cell
 2585                */
 2586               private void getAccessible(ElementInfo elementInfo) {
 2587                   if (elementInfo instanceof Accessible) {
 2588                       accessible = (Accessible)elementInfo;
 2589                   } else {
 2590                       for (int i = 0; i < elementInfo.getChildCount(); i++) {
 2591                           getAccessible(elementInfo.getChild(i));
 2592                       }
 2593                   }
 2594               }
 2595   
 2596               /**
 2597                * Returns the rowspan attribute.
 2598                */
 2599               public int getRowCount() {
 2600                   if (validateIfNecessary()) {
 2601                       return Math.max(1, getIntAttr(getAttributes(),
 2602                                                     HTML.Attribute.ROWSPAN, 1));
 2603                   }
 2604                   return 0;
 2605               }
 2606   
 2607               /**
 2608                * Returns the colspan attribute.
 2609                */
 2610               public int getColumnCount() {
 2611                   if (validateIfNecessary()) {
 2612                       return Math.max(1, getIntAttr(getAttributes(),
 2613                                                     HTML.Attribute.COLSPAN, 1));
 2614                   }
 2615                   return 0;
 2616               }
 2617   
 2618               /**
 2619                * Overriden to invalidate the TableRowElementInfo as well as
 2620                * the TableCellElementInfo.
 2621                */
 2622               protected void invalidate(boolean first) {
 2623                   super.invalidate(first);
 2624                   getParent().invalidate(true);
 2625               }
 2626           }
 2627       }
 2628   
 2629   
 2630       /**
 2631        * ElementInfo provides a slim down view of an Element.  Each ElementInfo
 2632        * can have any number of child ElementInfos that are not necessarily
 2633        * direct children of the Element. As the Document changes various
 2634        * ElementInfos become invalidated. Before accessing a particular portion
 2635        * of an ElementInfo you should make sure it is valid by invoking
 2636        * <code>validateIfNecessary</code>, this will return true if
 2637        * successful, on the other hand a false return value indicates the
 2638        * ElementInfo is not valid and can never become valid again (usually
 2639        * the result of the Element the ElementInfo encapsulates being removed).
 2640        */
 2641       private class ElementInfo {
 2642   
 2643           /**
 2644            * The children of this ElementInfo.
 2645            */
 2646           private ArrayList<ElementInfo> children;
 2647           /**
 2648            * The Element this ElementInfo is providing information for.
 2649            */
 2650           private Element element;
 2651           /**
 2652            * The parent ElementInfo, will be null for the root.
 2653            */
 2654           private ElementInfo parent;
 2655           /**
 2656            * Indicates the validity of the ElementInfo.
 2657            */
 2658           private boolean isValid;
 2659           /**
 2660            * Indicates if the ElementInfo can become valid.
 2661            */
 2662           private boolean canBeValid;
 2663   
 2664   
 2665           /**
 2666            * Creates the root ElementInfo.
 2667            */
 2668           ElementInfo(Element element) {
 2669               this(element, null);
 2670           }
 2671   
 2672           /**
 2673            * Creates an ElementInfo representing <code>element</code> with
 2674            * the specified parent.
 2675            */
 2676           ElementInfo(Element element, ElementInfo parent) {
 2677               this.element = element;
 2678               this.parent = parent;
 2679               isValid = false;
 2680               canBeValid = true;
 2681           }
 2682   
 2683           /**
 2684            * Validates the receiver. This recreates the children as well. This
 2685            * will be invoked within a <code>readLock</code>. If this is overriden
 2686            * it MUST invoke supers implementation first!
 2687            */
 2688           protected void validate() {
 2689               isValid = true;
 2690               loadChildren(getElement());
 2691           }
 2692   
 2693           /**
 2694            * Recreates the direct children of <code>info</code>.
 2695            */
 2696           protected void loadChildren(Element parent) {
 2697               if (!parent.isLeaf()) {
 2698                   for (int counter = 0, maxCounter = parent.getElementCount();
 2699                       counter < maxCounter; counter++) {
 2700                       Element e = parent.getElement(counter);
 2701                       ElementInfo childInfo = createElementInfo(e, this);
 2702   
 2703                       if (childInfo != null) {
 2704                           addChild(childInfo);
 2705                       }
 2706                       else {
 2707                           loadChildren(e);
 2708                       }
 2709                   }
 2710               }
 2711           }
 2712   
 2713           /**
 2714            * Returns the index of the child in the parent, or -1 for the
 2715            * root or if the parent isn't valid.
 2716            */
 2717           public int getIndexInParent() {
 2718               if (parent == null || !parent.isValid()) {
 2719                   return -1;
 2720               }
 2721               return parent.indexOf(this);
 2722           }
 2723   
 2724           /**
 2725            * Returns the Element this <code>ElementInfo</code> represents.
 2726            */
 2727           public Element getElement() {
 2728               return element;
 2729           }
 2730   
 2731           /**
 2732            * Returns the parent of this Element, or null for the root.
 2733            */
 2734           public ElementInfo getParent() {
 2735               return parent;
 2736           }
 2737   
 2738           /**
 2739            * Returns the index of the specified child, or -1 if
 2740            * <code>child</code> isn't a valid child.
 2741            */
 2742           public int indexOf(ElementInfo child) {
 2743               ArrayList children = this.children;
 2744   
 2745               if (children != null) {
 2746                   return children.indexOf(child);
 2747               }
 2748               return -1;
 2749           }
 2750   
 2751           /**
 2752            * Returns the child ElementInfo at <code>index</code>, or null
 2753            * if <code>index</code> isn't a valid index.
 2754            */
 2755           public ElementInfo getChild(int index) {
 2756               if (validateIfNecessary()) {
 2757                   ArrayList<ElementInfo> children = this.children;
 2758   
 2759                   if (children != null && index >= 0 &&
 2760                                           index < children.size()) {
 2761                       return children.get(index);
 2762                   }
 2763               }
 2764               return null;
 2765           }
 2766   
 2767           /**
 2768            * Returns the number of children the ElementInfo contains.
 2769            */
 2770           public int getChildCount() {
 2771               validateIfNecessary();
 2772               return (children == null) ? 0 : children.size();
 2773           }
 2774   
 2775           /**
 2776            * Adds a new child to this ElementInfo.
 2777            */
 2778           protected void addChild(ElementInfo child) {
 2779               if (children == null) {
 2780                   children = new ArrayList<ElementInfo>();
 2781               }
 2782               children.add(child);
 2783           }
 2784   
 2785           /**
 2786            * Returns the View corresponding to this ElementInfo, or null
 2787            * if the ElementInfo can't be validated.
 2788            */
 2789           protected View getView() {
 2790               if (!validateIfNecessary()) {
 2791                   return null;
 2792               }
 2793               Object lock = lock();
 2794               try {
 2795                   View rootView = getRootView();
 2796                   Element e = getElement();
 2797                   int start = e.getStartOffset();
 2798   
 2799                   if (rootView != null) {
 2800                       return getView(rootView, e, start);
 2801                   }
 2802                   return null;
 2803               } finally {
 2804                   unlock(lock);
 2805               }
 2806           }
 2807   
 2808           /**
 2809            * Returns the Bounds for this ElementInfo, or null
 2810            * if the ElementInfo can't be validated.
 2811            */
 2812           public Rectangle getBounds() {
 2813               if (!validateIfNecessary()) {
 2814                   return null;
 2815               }
 2816               Object lock = lock();
 2817               try {
 2818                   Rectangle bounds = getRootEditorRect();
 2819                   View rootView = getRootView();
 2820                   Element e = getElement();
 2821   
 2822                   if (bounds != null && rootView != null) {
 2823                       try {
 2824                           return rootView.modelToView(e.getStartOffset(),
 2825                                                       Position.Bias.Forward,
 2826                                                       e.getEndOffset(),
 2827                                                       Position.Bias.Backward,
 2828                                                       bounds).getBounds();
 2829                       } catch (BadLocationException ble) { }
 2830                   }
 2831               } finally {
 2832                   unlock(lock);
 2833               }
 2834               return null;
 2835           }
 2836   
 2837           /**
 2838            * Returns true if this ElementInfo is valid.
 2839            */
 2840           protected boolean isValid() {
 2841               return isValid;
 2842           }
 2843   
 2844           /**
 2845            * Returns the AttributeSet associated with the Element, this will
 2846            * return null if the ElementInfo can't be validated.
 2847            */
 2848           protected AttributeSet getAttributes() {
 2849               if (validateIfNecessary()) {
 2850                   return getElement().getAttributes();
 2851               }
 2852               return null;
 2853           }
 2854   
 2855           /**
 2856            * Returns the AttributeSet associated with the View that is
 2857            * representing this Element, this will
 2858            * return null if the ElementInfo can't be validated.
 2859            */
 2860           protected AttributeSet getViewAttributes() {
 2861               if (validateIfNecessary()) {
 2862                   View view = getView();
 2863   
 2864                   if (view != null) {
 2865                       return view.getElement().getAttributes();
 2866                   }
 2867                   return getElement().getAttributes();
 2868               }
 2869               return null;
 2870           }
 2871   
 2872           /**
 2873            * Convenience method for getting an integer attribute from the passed
 2874            * in AttributeSet.
 2875            */
 2876           protected int getIntAttr(AttributeSet attrs, Object key, int deflt) {
 2877               if (attrs != null && attrs.isDefined(key)) {
 2878                   int i;
 2879                   String val = (String)attrs.getAttribute(key);
 2880                   if (val == null) {
 2881                       i = deflt;
 2882                   }
 2883                   else {
 2884                       try {
 2885                           i = Math.max(0, Integer.parseInt(val));
 2886                       } catch (NumberFormatException x) {
 2887                           i = deflt;
 2888                       }
 2889                   }
 2890                   return i;
 2891               }
 2892               return deflt;
 2893           }
 2894   
 2895           /**
 2896            * Validates the ElementInfo if necessary.  Some ElementInfos may
 2897            * never be valid again.  You should check <code>isValid</code> before
 2898            * using one.  This will reload the children and invoke
 2899            * <code>validate</code> if the ElementInfo is invalid and can become
 2900            * valid again. This will return true if the receiver is valid.
 2901            */
 2902           protected boolean validateIfNecessary() {
 2903               if (!isValid() && canBeValid) {
 2904                   children = null;
 2905                   Object lock = lock();
 2906   
 2907                   try {
 2908                       validate();
 2909                   } finally {
 2910                       unlock(lock);
 2911                   }
 2912               }
 2913               return isValid();
 2914           }
 2915   
 2916           /**
 2917            * Invalidates the ElementInfo. Subclasses should override this
 2918            * if they need to reset state once invalid.
 2919            */
 2920           protected void invalidate(boolean first) {
 2921               if (!isValid()) {
 2922                   if (canBeValid && !first) {
 2923                       canBeValid = false;
 2924                   }
 2925                   return;
 2926               }
 2927               isValid = false;
 2928               canBeValid = first;
 2929               if (children != null) {
 2930                   for (ElementInfo child : children) {
 2931                       child.invalidate(false);
 2932                   }
 2933                   children = null;
 2934               }
 2935           }
 2936   
 2937           private View getView(View parent, Element e, int start) {
 2938               if (parent.getElement() == e) {
 2939                   return parent;
 2940               }
 2941               int index = parent.getViewIndex(start, Position.Bias.Forward);
 2942   
 2943               if (index != -1 && index < parent.getViewCount()) {
 2944                   return getView(parent.getView(index), e, start);
 2945               }
 2946               return null;
 2947           }
 2948   
 2949           private int getClosestInfoIndex(int index) {
 2950               for (int counter = 0; counter < getChildCount(); counter++) {
 2951                   ElementInfo info = getChild(counter);
 2952   
 2953                   if (index < info.getElement().getEndOffset() ||
 2954                       index == info.getElement().getStartOffset()) {
 2955                       return counter;
 2956                   }
 2957               }
 2958               return -1;
 2959           }
 2960   
 2961           private void update(DocumentEvent e) {
 2962               if (!isValid()) {
 2963                   return;
 2964               }
 2965               ElementInfo parent = getParent();
 2966               Element element = getElement();
 2967   
 2968               do {
 2969                   DocumentEvent.ElementChange ec = e.getChange(element);
 2970                   if (ec != null) {
 2971                       if (element == getElement()) {
 2972                           // One of our children changed.
 2973                           invalidate(true);
 2974                       }
 2975                       else if (parent != null) {
 2976                           parent.invalidate(parent == getRootInfo());
 2977                       }
 2978                       return;
 2979                   }
 2980                   element = element.getParentElement();
 2981               } while (parent != null && element != null &&
 2982                        element != parent.getElement());
 2983   
 2984               if (getChildCount() > 0) {
 2985                   Element elem = getElement();
 2986                   int pos = e.getOffset();
 2987                   int index0 = getClosestInfoIndex(pos);
 2988                   if (index0 == -1 &&
 2989                       e.getType() == DocumentEvent.EventType.REMOVE &&
 2990                       pos >= elem.getEndOffset()) {
 2991                       // Event beyond our offsets. We may have represented this,
 2992                       // that is the remove may have removed one of our child
 2993                       // Elements that represented this, so, we should foward
 2994                       // to last element.
 2995                       index0 = getChildCount() - 1;
 2996                   }
 2997                   ElementInfo info = (index0 >= 0) ? getChild(index0) : null;
 2998                   if (info != null &&
 2999                       (info.getElement().getStartOffset() == pos) && (pos > 0)) {
 3000                       // If at a boundary, forward the event to the previous
 3001                       // ElementInfo too.
 3002                       index0 = Math.max(index0 - 1, 0);
 3003                   }
 3004                   int index1;
 3005                   if (e.getType() != DocumentEvent.EventType.REMOVE) {
 3006                       index1 = getClosestInfoIndex(pos + e.getLength());
 3007                       if (index1 < 0) {
 3008                           index1 = getChildCount() - 1;
 3009                       }
 3010                   }
 3011                   else {
 3012                       index1 = index0;
 3013                       // A remove may result in empty elements.
 3014                       while ((index1 + 1) < getChildCount() &&
 3015                              getChild(index1 + 1).getElement().getEndOffset() ==
 3016                              getChild(index1 + 1).getElement().getStartOffset()){
 3017                           index1++;
 3018                       }
 3019                   }
 3020                   index0 = Math.max(index0, 0);
 3021                   // The check for isValid is here as in the process of
 3022                   // forwarding update our child may invalidate us.
 3023                   for (int i = index0; i <= index1 && isValid(); i++) {
 3024                       getChild(i).update(e);
 3025                   }
 3026               }
 3027           }
 3028       }
 3029   
 3030       /**
 3031        * DocumentListener installed on the current Document.  Will invoke
 3032        * <code>update</code> on the <code>RootInfo</code> in response to
 3033        * any event.
 3034        */
 3035       private class DocumentHandler implements DocumentListener {
 3036           public void insertUpdate(DocumentEvent e) {
 3037               getRootInfo().update(e);
 3038           }
 3039           public void removeUpdate(DocumentEvent e) {
 3040               getRootInfo().update(e);
 3041           }
 3042           public void changedUpdate(DocumentEvent e) {
 3043               getRootInfo().update(e);
 3044           }
 3045       }
 3046   
 3047       /*
 3048        * PropertyChangeListener installed on the editor.
 3049        */
 3050       private class PropertyChangeHandler implements PropertyChangeListener {
 3051           public void propertyChange(PropertyChangeEvent evt) {
 3052               if (evt.getPropertyName().equals("document")) {
 3053                   // handle the document change
 3054                   setDocument(editor.getDocument());
 3055               }
 3056           }
 3057       }
 3058   }

Home » openjdk-7 » javax » swing » text » html » [javadoc | source]