KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > text > html > AccessibleHTML


1 /*
2  * @(#)AccessibleHTML.java 1.13 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing.text.html;
9
10 import java.awt.*;
11 import java.awt.event.*;
12 import java.beans.*;
13 import java.util.*;
14 import javax.swing.*;
15 import javax.swing.event.*;
16 import javax.swing.text.*;
17 import javax.accessibility.*;
18 import java.text.BreakIterator JavaDoc;
19
20 /*
21  * The AccessibleHTML class provide information about the contents
22  * of a HTML document to assistive technologies.
23  *
24  * @version 1.13 12/19/03
25  * @author Lynn Monsanto
26  */

27 class AccessibleHTML implements Accessible {
28
29     /**
30      * The editor.
31      */

32     private JEditorPane editor;
33     /**
34      * Current model.
35      */

36     private Document model;
37     /**
38      * DocumentListener installed on the current model.
39      */

40     private DocumentListener docListener;
41     /**
42      * PropertyChangeListener installed on the editor
43      */

44     private PropertyChangeListener propChangeListener;
45     /**
46      * The root ElementInfo for the document
47      */

48     private ElementInfo rootElementInfo;
49     /*
50      * The root accessible context for the document
51      */

52     private RootHTMLAccessibleContext rootHTMLAccessibleContext;
53
54     public AccessibleHTML(JEditorPane pane) {
55     editor = pane;
56     propChangeListener = new PropertyChangeHandler();
57         setDocument(editor.getDocument());
58
59         docListener = new DocumentHandler();
60     }
61
62     /**
63      * Sets the document.
64      */

65     private void setDocument(Document document) {
66         if (model != null) {
67             model.removeDocumentListener(docListener);
68         }
69     if (editor != null) {
70         editor.removePropertyChangeListener(propChangeListener);
71     }
72         this.model = document;
73         if (model != null) {
74             if (rootElementInfo != null) {
75                 rootElementInfo.invalidate(false);
76             }
77             buildInfo();
78             model.addDocumentListener(docListener);
79         }
80         else {
81             rootElementInfo = null;
82         }
83     if (editor != null) {
84         editor.addPropertyChangeListener(propChangeListener);
85     }
86     }
87
88     /**
89      * Returns the Document currently presenting information for.
90      */

91     private Document getDocument() {
92         return model;
93     }
94
95     /**
96      * Returns the JEditorPane providing information for.
97      */

98     private JEditorPane getTextComponent() {
99         return editor;
100     }
101
102     /**
103      * Returns the ElementInfo representing the root Element.
104      */

105     private ElementInfo getRootInfo() {
106         return rootElementInfo;
107     }
108
109     /**
110      * Returns the root <code>View</code> associated with the current text
111      * component.
112      */

113     private View getRootView() {
114         return getTextComponent().getUI().getRootView(getTextComponent());
115     }
116
117     /**
118      * Returns the bounds the root View will be rendered in.
119      */

120     private Rectangle getRootEditorRect() {
121         Rectangle alloc = getTextComponent().getBounds();
122         if ((alloc.width > 0) && (alloc.height > 0)) {
123             alloc.x = alloc.y = 0;
124             Insets insets = editor.getInsets();
125             alloc.x += insets.left;
126             alloc.y += insets.top;
127             alloc.width -= insets.left + insets.right;
128             alloc.height -= insets.top + insets.bottom;
129             return alloc;
130         }
131         return null;
132     }
133
134     /**
135      * If possible acquires a lock on the Document. If a lock has been
136      * obtained a key will be retured that should be passed to
137      * <code>unlock</code>.
138      */

139     private Object JavaDoc lock() {
140         Document document = getDocument();
141
142         if (document instanceof AbstractDocument) {
143             ((AbstractDocument)document).readLock();
144             return document;
145         }
146         return null;
147     }
148
149     /**
150      * Releases a lock previously obtained via <code>lock</code>.
151      */

152     private void unlock(Object JavaDoc key) {
153         if (key != null) {
154             ((AbstractDocument)key).readUnlock();
155         }
156     }
157
158     /**
159      * Rebuilds the information from the current info.
160      */

161     private void buildInfo() {
162         Object JavaDoc lock = lock();
163
164         try {
165             Document doc = getDocument();
166             Element root = doc.getDefaultRootElement();
167
168             rootElementInfo = new ElementInfo(root);
169             rootElementInfo.validate();
170         } finally {
171             unlock(lock);
172         }
173     }
174
175     /*
176      * Create an ElementInfo subclass based on the passed in Element.
177      */

178     ElementInfo createElementInfo(Element e, ElementInfo parent) {
179         AttributeSet attrs = e.getAttributes();
180
181         if (attrs != null) {
182             Object JavaDoc name = attrs.getAttribute(StyleConstants.NameAttribute);
183
184             if (name == HTML.Tag.IMG) {
185                 return new IconElementInfo(e, parent);
186             }
187             else if (name == HTML.Tag.CONTENT || name == HTML.Tag.CAPTION) {
188                 return new TextElementInfo(e, parent);
189             }
190             else if (name == HTML.Tag.TABLE) {
191                 return new TableElementInfo(e, parent);
192             }
193         }
194         return null;
195     }
196     
197     /**
198      * Returns the root AccessibleContext for the document
199      */

200     public AccessibleContext getAccessibleContext() {
201     if (rootHTMLAccessibleContext == null) {
202         rootHTMLAccessibleContext =
203         new RootHTMLAccessibleContext(rootElementInfo);
204     }
205     return rootHTMLAccessibleContext;
206     }
207     
208     /*
209      * The roow AccessibleContext for the document
210      */

211     private class RootHTMLAccessibleContext extends HTMLAccessibleContext {
212
213     public RootHTMLAccessibleContext(ElementInfo elementInfo) {
214         super(elementInfo);
215     }
216
217     /**
218      * Gets the accessibleName property of this object. The accessibleName
219      * property of an object is a localized String that designates the purpose
220      * of the object. For example, the accessibleName property of a label
221      * or button might be the text of the label or button itself. In the
222      * case of an object that doesn't display its name, the accessibleName
223      * should still be set. For example, in the case of a text field used
224      * to enter the name of a city, the accessibleName for the en_US locale
225      * could be 'city.'
226      *
227      * @return the localized name of the object; null if this
228      * object does not have a name
229      *
230      * @see #setAccessibleName
231      */

232     public String JavaDoc getAccessibleName() {
233         if (model != null) {
234         return (String JavaDoc)model.getProperty(Document.TitleProperty);
235         } else {
236         return null;
237         }
238     }
239     
240     /**
241      * Gets the accessibleDescription property of this object. If this
242      * property isn't set, returns the content type of this
243      * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
244      *
245      * @return the localized description of the object; <code>null</code>
246      * if this object does not have a description
247      *
248      * @see #setAccessibleName
249      */

250     public String JavaDoc getAccessibleDescription() {
251         return editor.getContentType();
252     }
253     
254     /**
255      * Gets the role of this object. The role of the object is the generic
256      * purpose or use of the class of this object. For example, the role
257      * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
258      * AccessibleRole are provided so component developers can pick from
259      * a set of predefined roles. This enables assistive technologies to
260      * provide a consistent interface to various tweaked subclasses of
261      * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
262      * that act like a push button) as well as distinguish between sublasses
263      * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
264      * and AccessibleRole.RADIO_BUTTON for radio buttons).
265      * <p>Note that the AccessibleRole class is also extensible, so
266      * custom component developers can define their own AccessibleRole's
267      * if the set of predefined roles is inadequate.
268      *
269      * @return an instance of AccessibleRole describing the role of the object
270      * @see AccessibleRole
271      */

272     public AccessibleRole getAccessibleRole() {
273         return AccessibleRole.TEXT;
274     }
275     }
276
277     /*
278      * Base AccessibleContext class for HTML elements
279      */

280     protected abstract class HTMLAccessibleContext extends AccessibleContext
281         implements Accessible, AccessibleComponent {
282
283     protected ElementInfo elementInfo;
284
285     public HTMLAccessibleContext(ElementInfo elementInfo) {
286         this.elementInfo = elementInfo;
287     }
288
289     // begin AccessibleContext implementation ...
290
public AccessibleContext getAccessibleContext() {
291         return this;
292     }
293
294     /**
295      * Gets the state set of this object.
296      *
297      * @return an instance of AccessibleStateSet describing the states
298      * of the object
299      * @see AccessibleStateSet
300      */

301     public AccessibleStateSet getAccessibleStateSet() {
302         AccessibleStateSet states = new AccessibleStateSet();
303         Component comp = getTextComponent();
304
305         if (comp.isEnabled()) {
306         states.add(AccessibleState.ENABLED);
307         }
308         if (comp instanceof JTextComponent &&
309         ((JTextComponent)comp).isEditable()) {
310         
311         states.add(AccessibleState.EDITABLE);
312         states.add(AccessibleState.FOCUSABLE);
313         }
314         if (comp.isVisible()) {
315         states.add(AccessibleState.VISIBLE);
316         }
317         if (comp.isShowing()) {
318         states.add(AccessibleState.SHOWING);
319         }
320         return states;
321     }
322     
323     /**
324      * Gets the 0-based index of this object in its accessible parent.
325      *
326      * @return the 0-based index of this object in its parent; -1 if this
327      * object does not have an accessible parent.
328      *
329      * @see #getAccessibleParent
330      * @see #getAccessibleChildrenCount
331      * @see #getAccessibleChild
332      */

333     public int getAccessibleIndexInParent() {
334         return elementInfo.getIndexInParent();
335     }
336     
337     /**
338      * Returns the number of accessible children of the object.
339      *
340      * @return the number of accessible children of the object.
341      */

342     public int getAccessibleChildrenCount() {
343         return elementInfo.getChildCount();
344     }
345     
346     /**
347      * Returns the specified Accessible child of the object. The Accessible
348      * children of an Accessible object are zero-based, so the first child
349      * of an Accessible child is at index 0, the second child is at index 1,
350      * and so on.
351      *
352      * @param i zero-based index of child
353      * @return the Accessible child of the object
354      * @see #getAccessibleChildrenCount
355      */

356     public Accessible getAccessibleChild(int i) {
357         ElementInfo childInfo = elementInfo.getChild(i);
358         if (childInfo != null && childInfo instanceof Accessible) {
359         return (Accessible)childInfo;
360         } else {
361         return null;
362         }
363     }
364     
365     /**
366      * Gets the locale of the component. If the component does not have a
367      * locale, then the locale of its parent is returned.
368      *
369      * @return this component's locale. If this component does not have
370      * a locale, the locale of its parent is returned.
371      *
372      * @exception IllegalComponentStateException
373      * If the Component does not have its own locale and has not yet been
374      * added to a containment hierarchy such that the locale can be
375      * determined from the containing parent.
376      */

377     public Locale getLocale() throws IllegalComponentStateException {
378         return editor.getLocale();
379     }
380     // ... end AccessibleContext implementation
381

382     // begin AccessibleComponent implementation ...
383
public AccessibleComponent getAccessibleComponent() {
384         return this;
385     }
386     
387     /**
388      * Gets the background color of this object.
389      *
390      * @return the background color, if supported, of the object;
391      * otherwise, null
392      * @see #setBackground
393      */

394     public Color getBackground() {
395         return getTextComponent().getBackground();
396     }
397     
398     /**
399      * Sets the background color of this object.
400      *
401      * @param c the new Color for the background
402      * @see #setBackground
403      */

404     public void setBackground(Color c) {
405         getTextComponent().setBackground(c);
406     }
407     
408     /**
409      * Gets the foreground color of this object.
410      *
411      * @return the foreground color, if supported, of the object;
412      * otherwise, null
413      * @see #setForeground
414      */

415     public Color getForeground() {
416         return getTextComponent().getForeground();
417     }
418     
419     /**
420      * Sets the foreground color of this object.
421      *
422      * @param c the new Color for the foreground
423      * @see #getForeground
424      */

425     public void setForeground(Color c) {
426         getTextComponent().setForeground(c);
427     }
428     
429     /**
430      * Gets the Cursor of this object.
431      *
432      * @return the Cursor, if supported, of the object; otherwise, null
433      * @see #setCursor
434      */

435     public Cursor getCursor() {
436         return getTextComponent().getCursor();
437     }
438     
439     /**
440      * Sets the Cursor of this object.
441      *
442      * @param c the new Cursor for the object
443      * @see #getCursor
444      */

445     public void setCursor(Cursor cursor) {
446         getTextComponent().setCursor(cursor);
447     }
448     
449     /**
450      * Gets the Font of this object.
451      *
452      * @return the Font,if supported, for the object; otherwise, null
453      * @see #setFont
454      */

455     public Font getFont() {
456         return getTextComponent().getFont();
457     }
458     
459     /**
460      * Sets the Font of this object.
461      *
462      * @param f the new Font for the object
463      * @see #getFont
464      */

465     public void setFont(Font f) {
466         getTextComponent().setFont(f);
467     }
468     
469     /**
470      * Gets the FontMetrics of this object.
471      *
472      * @param f the Font
473      * @return the FontMetrics, if supported, the object; otherwise, null
474      * @see #getFont
475      */

476     public FontMetrics getFontMetrics(Font f) {
477         return getTextComponent().getFontMetrics(f);
478     }
479     
480     /**
481      * Determines if the object is enabled. Objects that are enabled
482      * will also have the AccessibleState.ENABLED state set in their
483      * AccessibleStateSets.
484      *
485      * @return true if object is enabled; otherwise, false
486      * @see #setEnabled
487      * @see AccessibleContext#getAccessibleStateSet
488      * @see AccessibleState#ENABLED
489      * @see AccessibleStateSet
490      */

491     public boolean isEnabled() {
492         return getTextComponent().isEnabled();
493     }
494     
495     /**
496      * Sets the enabled state of the object.
497      *
498      * @param b if true, enables this object; otherwise, disables it
499      * @see #isEnabled
500      */

501     public void setEnabled(boolean b) {
502         getTextComponent().setEnabled(b);
503     }
504     
505     /**
506      * Determines if the object is visible. Note: this means that the
507      * object intends to be visible; however, it may not be
508      * showing on the screen because one of the objects that this object
509      * is contained by is currently not visible. To determine if an object
510      * is showing on the screen, use isShowing().
511      * <p>Objects that are visible will also have the
512      * AccessibleState.VISIBLE state set in their AccessibleStateSets.
513      *
514      * @return true if object is visible; otherwise, false
515      * @see #setVisible
516      * @see AccessibleContext#getAccessibleStateSet
517      * @see AccessibleState#VISIBLE
518      * @see AccessibleStateSet
519      */

520     public boolean isVisible() {
521         return getTextComponent().isVisible();
522     }
523     
524     /**
525      * Sets the visible state of the object.
526      *
527      * @param b if true, shows this object; otherwise, hides it
528      * @see #isVisible
529      */

530     public void setVisible(boolean b) {
531         getTextComponent().setVisible(b);
532     }
533     
534     /**
535      * Determines if the object is showing. This is determined by checking
536      * the visibility of the object and its ancestors.
537      * Note: this
538      * will return true even if the object is obscured by another (for
539      * example, it is underneath a menu that was pulled down).
540      *
541      * @return true if object is showing; otherwise, false
542      */

543     public boolean isShowing() {
544         return getTextComponent().isShowing();
545     }
546     
547     /**
548      * Checks whether the specified point is within this object's bounds,
549      * where the point's x and y coordinates are defined to be relative
550      * to the coordinate system of the object.
551      *
552      * @param p the Point relative to the coordinate system of the object
553      * @return true if object contains Point; otherwise false
554      * @see #getBounds
555      */

556     public boolean contains(Point p) {
557         Rectangle r = getBounds();
558         if (r != null) {
559         return r.contains(p.x, p.y);
560         } else {
561         return false;
562         }
563     }
564     
565     /**
566      * Returns the location of the object on the screen.
567      *
568      * @return the location of the object on screen; null if this object
569      * is not on the screen
570      * @see #getBounds
571      * @see #getLocation
572      */

573     public Point getLocationOnScreen() {
574         Point editorLocation = getTextComponent().getLocationOnScreen();
575         Rectangle r = getBounds();
576         if (r != null) {
577         return new Point(editorLocation.x + r.x,
578                  editorLocation.y + r.y);
579         } else {
580         return null;
581         }
582     }
583     
584     /**
585      * Gets the location of the object relative to the parent in the form
586      * of a point specifying the object's top-left corner in the screen's
587      * coordinate space.
588      *
589      * @return An instance of Point representing the top-left corner of the
590      * object's bounds in the coordinate space of the screen; null if
591      * this object or its parent are not on the screen
592      * @see #getBounds
593      * @see #getLocationOnScreen
594      */

595     public Point getLocation() {
596         Rectangle r = getBounds();
597         if (r != null) {
598         return new Point(r.x, r.y);
599         } else {
600         return null;
601         }
602     }
603     
604     /**
605      * Sets the location of the object relative to the parent.
606      * @param p the new position for the top-left corner
607      * @see #getLocation
608      */

609     public void setLocation(Point p) {
610     }
611     
612     /**
613      * Gets the bounds of this object in the form of a Rectangle object.
614      * The bounds specify this object's width, height, and location
615      * relative to its parent.
616      *
617      * @return A rectangle indicating this component's bounds; null if
618      * this object is not on the screen.
619      * @see #contains
620      */

621     public Rectangle getBounds() {
622         return elementInfo.getBounds();
623     }
624     
625     /**
626      * Sets the bounds of this object in the form of a Rectangle object.
627      * The bounds specify this object's width, height, and location
628      * relative to its parent.
629      *
630      * @param r rectangle indicating this component's bounds
631      * @see #getBounds
632      */

633     public void setBounds(Rectangle r) {
634     }
635     
636     /**
637      * Returns the size of this object in the form of a Dimension object.
638      * The height field of the Dimension object contains this object's
639      * height, and the width field of the Dimension object contains this
640      * object's width.
641      *
642      * @return A Dimension object that indicates the size of this component;
643      * null if this object is not on the screen
644      * @see #setSize
645      */

646     public Dimension getSize() {
647         Rectangle r = getBounds();
648         if (r != null) {
649         return new Dimension(r.width, r.height);
650         } else {
651         return null;
652         }
653     }
654     
655     /**
656      * Resizes this object so that it has width and height.
657      *
658      * @param d The dimension specifying the new size of the object.
659      * @see #getSize
660      */

661     public void setSize(Dimension d) {
662         Component comp = getTextComponent();
663         comp.setSize(d);
664     }
665     
666     /**
667      * Returns the Accessible child, if one exists, contained at the local
668      * coordinate Point.
669      *
670      * @param p The point relative to the coordinate system of this object.
671      * @return the Accessible, if it exists, at the specified location;
672      * otherwise null
673      */

674     public Accessible getAccessibleAt(Point p) {
675         ElementInfo innerMostElement = getElementInfoAt(rootElementInfo, p);
676         if (innerMostElement instanceof Accessible) {
677         return (Accessible)innerMostElement;
678         } else {
679         return null;
680         }
681     }
682
683         private ElementInfo getElementInfoAt(ElementInfo elementInfo, Point p) {
684         if (elementInfo.getBounds() == null) {
685         return null;
686         }
687         if (elementInfo.getChildCount() == 0 &&
688         elementInfo.getBounds().contains(p)) {
689         return elementInfo;
690         
691         } else {
692         if (elementInfo instanceof TableElementInfo) {
693             // Handle table caption as a special case since it's the
694
// only table child that is not a table row.
695
ElementInfo captionInfo =
696             ((TableElementInfo)elementInfo).getCaptionInfo();
697             if (captionInfo != null) {
698             Rectangle bounds = captionInfo.getBounds();
699             if (bounds != null && bounds.contains(p)) {
700                 return captionInfo;
701             }
702             }
703         }
704         for (int i = 0; i < elementInfo.getChildCount(); i++)
705 {
706             ElementInfo childInfo = elementInfo.getChild(i);
707                     ElementInfo retValue = getElementInfoAt(childInfo, p);
708                     if (retValue != null) {
709                         return retValue;
710                     }
711         }
712         }
713             return null;
714     }
715     
716     /**
717      * Returns whether this object can accept focus or not. Objects that
718      * can accept focus will also have the AccessibleState.FOCUSABLE state
719      * set in their AccessibleStateSets.
720      *
721      * @return true if object can accept focus; otherwise false
722      * @see AccessibleContext#getAccessibleStateSet
723      * @see AccessibleState#FOCUSABLE
724      * @see AccessibleState#FOCUSED
725      * @see AccessibleStateSet
726      */

727     public boolean isFocusTraversable() {
728         Component comp = getTextComponent();
729         if (comp instanceof JTextComponent) {
730         if (((JTextComponent)comp).isEditable()) {
731             return true;
732         }
733         }
734         return false;
735     }
736     
737     /**
738      * Requests focus for this object. If this object cannot accept focus,
739      * nothing will happen. Otherwise, the object will attempt to take
740      * focus.
741      * @see #isFocusTraversable
742      */

743     public void requestFocus() {
744             // TIGER - 4856191
745
if (! isFocusTraversable()) {
746                 return;
747             }
748
749         Component comp = getTextComponent();
750         if (comp instanceof JTextComponent) {
751
752         comp.requestFocusInWindow();
753
754         try {
755             if (elementInfo.validateIfNecessary()) {
756             // set the caret position to the start of this component
757
Element elem = elementInfo.getElement();
758             ((JTextComponent)comp).setCaretPosition(elem.getStartOffset());
759             
760             // fire a AccessibleState.FOCUSED property change event
761
AccessibleContext ac = editor.getAccessibleContext();
762             PropertyChangeEvent pce = new PropertyChangeEvent(this,
763                 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
764                 null, AccessibleState.FOCUSED);
765             ac.firePropertyChange(
766                 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
767                 null, pce);
768             }
769         } catch (IllegalArgumentException JavaDoc e) {
770             // don't fire property change event
771
}
772         }
773     }
774     
775     /**
776      * Adds the specified focus listener to receive focus events from this
777      * component.
778      *
779      * @param l the focus listener
780      * @see #removeFocusListener
781      */

782     public void addFocusListener(FocusListener l) {
783         getTextComponent().addFocusListener(l);
784     }
785     
786     /**
787      * Removes the specified focus listener so it no longer receives focus
788      * events from this component.
789      *
790      * @param l the focus listener
791      * @see #addFocusListener
792      */

793     public void removeFocusListener(FocusListener l) {
794         getTextComponent().removeFocusListener(l);
795     }
796     // ... end AccessibleComponent implementation
797
} // ... end HTMLAccessibleContext
798

799
800  
801     /*
802      * ElementInfo for text
803      */

804     class TextElementInfo extends ElementInfo implements Accessible {
805
806         TextElementInfo(Element element, ElementInfo parent) {
807             super(element, parent);
808         }
809
810     // begin AccessibleText implementation ...
811
private AccessibleContext accessibleContext;
812     
813     public AccessibleContext getAccessibleContext() {
814         if (accessibleContext == null) {
815         accessibleContext = new TextAccessibleContext(this);
816         }
817         return accessibleContext;
818     }
819     
820     /*
821      * AccessibleContext for text elements
822      */

823     public class TextAccessibleContext extends HTMLAccessibleContext
824             implements AccessibleText {
825
826         public TextAccessibleContext(ElementInfo elementInfo) {
827         super(elementInfo);
828         }
829         
830         public AccessibleText getAccessibleText() {
831         return this;
832         }
833         
834         /**
835          * Gets the accessibleName property of this object. The accessibleName
836          * property of an object is a localized String that designates the purpose
837          * of the object. For example, the accessibleName property of a label
838          * or button might be the text of the label or button itself. In the
839          * case of an object that doesn't display its name, the accessibleName
840          * should still be set. For example, in the case of a text field used
841          * to enter the name of a city, the accessibleName for the en_US locale
842          * could be 'city.'
843          *
844          * @return the localized name of the object; null if this
845          * object does not have a name
846          *
847          * @see #setAccessibleName
848          */

849         public String JavaDoc getAccessibleName() {
850         if (model != null) {
851             return (String JavaDoc)model.getProperty(Document.TitleProperty);
852         } else {
853             return null;
854         }
855         }
856         
857         /**
858          * Gets the accessibleDescription property of this object. If this
859          * property isn't set, returns the content type of this
860          * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
861          *
862          * @return the localized description of the object; <code>null</code>
863          * if this object does not have a description
864          *
865          * @see #setAccessibleName
866          */

867         public String JavaDoc getAccessibleDescription() {
868         return editor.getContentType();
869         }
870         
871         /**
872          * Gets the role of this object. The role of the object is the generic
873          * purpose or use of the class of this object. For example, the role
874          * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
875          * AccessibleRole are provided so component developers can pick from
876          * a set of predefined roles. This enables assistive technologies to
877          * provide a consistent interface to various tweaked subclasses of
878          * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
879          * that act like a push button) as well as distinguish between sublasses
880          * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
881          * and AccessibleRole.RADIO_BUTTON for radio buttons).
882          * <p>Note that the AccessibleRole class is also extensible, so
883          * custom component developers can define their own AccessibleRole's
884          * if the set of predefined roles is inadequate.
885          *
886          * @return an instance of AccessibleRole describing the role of the object
887          * @see AccessibleRole
888          */

889         public AccessibleRole getAccessibleRole() {
890         return AccessibleRole.TEXT;
891         }
892         
893         /**
894          * Given a point in local coordinates, return the zero-based index
895          * of the character under that Point. If the point is invalid,
896          * this method returns -1.
897          *
898          * @param p the Point in local coordinates
899          * @return the zero-based index of the character under Point p; if
900          * Point is invalid returns -1.
901          */

902         public int getIndexAtPoint(Point p) {
903         View v = getView();
904         if (v != null) {
905             return v.viewToModel(p.x, p.y, getBounds());
906         } else {
907             return -1;
908         }
909         }
910         
911         /**
912          * Determine the bounding box of the character at the given
913          * index into the string. The bounds are returned in local
914          * coordinates. If the index is invalid an empty rectangle is
915          * returned.
916          *
917          * @param i the index into the String
918          * @return the screen coordinates of the character's the bounding box,
919          * if index is invalid returns an empty rectangle.
920          */

921         public Rectangle getCharacterBounds(int i) {
922         try {
923             return editor.getUI().modelToView(editor, i);
924         } catch (BadLocationException e) {
925             return null;
926         }
927         }
928         
929         /**
930          * Return the number of characters (valid indicies)
931          *
932          * @return the number of characters
933          */

934         public int getCharCount() {
935         if (validateIfNecessary()) {
936             Element elem = elementInfo.getElement();
937             return elem.getEndOffset() - elem.getStartOffset();
938         }
939         return 0;
940         }
941         
942         /**
943          * Return the zero-based offset of the caret.
944          *
945          * Note: That to the right of the caret will have the same index
946          * value as the offset (the caret is between two characters).
947          * @return the zero-based offset of the caret.
948          */

949         public int getCaretPosition() {
950         View v = getView();
951         if (v == null) {
952             return -1;
953         }
954         Container c = v.getContainer();
955         if (c == null) {
956             return -1;
957         }
958         if (c instanceof JTextComponent) {
959             return ((JTextComponent)c).getCaretPosition();
960         } else {
961             return -1;
962         }
963         }
964         
965         /**
966          * IndexedSegment extends Segment adding the offset into the
967          * the model the <code>Segment</code> was asked for.
968          */

969         private class IndexedSegment extends Segment {
970         /**
971          * Offset into the model that the position represents.
972          */

973         public int modelOffset;
974         }
975         
976         public String JavaDoc getAtIndex(int part, int index) {
977         return getAtIndex(part, index, 0);
978         }
979         
980         
981         public String JavaDoc getAfterIndex(int part, int index) {
982         return getAtIndex(part, index, 1);
983         }
984         
985         public String JavaDoc getBeforeIndex(int part, int index) {
986         return getAtIndex(part, index, -1);
987         }
988         
989         /**
990          * Gets the word, sentence, or character at <code>index</code>.
991          * If <code>direction</code> is non-null this will find the
992          * next/previous word/sentence/character.
993          */

994         private String JavaDoc getAtIndex(int part, int index, int direction) {
995         if (model instanceof AbstractDocument) {
996             ((AbstractDocument)model).readLock();
997         }
998         try {
999             if (index < 0 || index >= model.getLength()) {
1000            return null;
1001            }
1002            switch (part) {
1003            case AccessibleText.CHARACTER:
1004            if (index + direction < model.getLength() &&
1005                index + direction >= 0) {
1006                return model.getText(index + direction, 1);
1007            }
1008            break;
1009            
1010            
1011            case AccessibleText.WORD:
1012            case AccessibleText.SENTENCE:
1013            IndexedSegment seg = getSegmentAt(part, index);
1014            if (seg != null) {
1015                if (direction != 0) {
1016                int next;
1017                
1018                
1019                if (direction < 0) {
1020                    next = seg.modelOffset - 1;
1021                }
1022                else {
1023                    next = seg.modelOffset + direction * seg.count;
1024                }
1025                if (next >= 0 && next <= model.getLength()) {
1026                    seg = getSegmentAt(part, next);
1027                }
1028                else {
1029                    seg = null;
1030                }
1031                }
1032                if (seg != null) {
1033                return new String JavaDoc(seg.array, seg.offset,
1034                                                  seg.count);
1035                }
1036            }
1037            break;
1038            
1039            default:
1040            break;
1041            }
1042        } catch (BadLocationException e) {
1043        } finally {
1044            if (model instanceof AbstractDocument) {
1045            ((AbstractDocument)model).readUnlock();
1046            }
1047        }
1048        return null;
1049        }
1050        
1051        /*
1052         * Returns the paragraph element for the specified index.
1053         */

1054        private Element getParagraphElement(int index) {
1055        if (model instanceof PlainDocument ) {
1056            PlainDocument sdoc = (PlainDocument)model;
1057            return sdoc.getParagraphElement(index);
1058        } else if (model instanceof StyledDocument) {
1059            StyledDocument sdoc = (StyledDocument)model;
1060            return sdoc.getParagraphElement(index);
1061        } else {
1062            Element para = null;
1063            for (para = model.getDefaultRootElement(); ! para.isLeaf(); ) {
1064            int pos = para.getElementIndex(index);
1065            para = para.getElement(pos);
1066            }
1067            if (para == null) {
1068            return null;
1069            }
1070            return para.getParentElement();
1071        }
1072        }
1073        
1074        /*
1075         * Returns a <code>Segment</code> containing the paragraph text
1076         * at <code>index</code>, or null if <code>index</code> isn't
1077         * valid.
1078         */

1079        private IndexedSegment getParagraphElementText(int index)
1080        throws BadLocationException {
1081        Element para = getParagraphElement(index);
1082        
1083        
1084        if (para != null) {
1085            IndexedSegment segment = new IndexedSegment();
1086            try {
1087            int length = para.getEndOffset() - para.getStartOffset();
1088            model.getText(para.getStartOffset(), length, segment);
1089            } catch (BadLocationException e) {
1090            return null;
1091            }
1092            segment.modelOffset = para.getStartOffset();
1093            return segment;
1094        }
1095        return null;
1096        }
1097        
1098        
1099        /**
1100         * Returns the Segment at <code>index</code> representing either
1101         * the paragraph or sentence as identified by <code>part</code>, or
1102         * null if a valid paragraph/sentence can't be found. The offset
1103         * will point to the start of the word/sentence in the array, and
1104         * the modelOffset will point to the location of the word/sentence
1105         * in the model.
1106         */

1107        private IndexedSegment getSegmentAt(int part, int index)
1108                throws BadLocationException {
1109
1110        IndexedSegment seg = getParagraphElementText(index);
1111        if (seg == null) {
1112            return null;
1113        }
1114        BreakIterator JavaDoc iterator;
1115        switch (part) {
1116        case AccessibleText.WORD:
1117            iterator = BreakIterator.getWordInstance(getLocale());
1118            break;
1119        case AccessibleText.SENTENCE:
1120            iterator = BreakIterator.getSentenceInstance(getLocale());
1121            break;
1122        default:
1123            return null;
1124        }
1125        seg.first();
1126        iterator.setText(seg);
1127        int end = iterator.following(index - seg.modelOffset + seg.offset);
1128        if (end == BreakIterator.DONE) {
1129            return null;
1130        }
1131        if (end > seg.offset + seg.count) {
1132            return null;
1133        }
1134        int begin = iterator.previous();
1135        if (begin == BreakIterator.DONE ||
1136            begin >= seg.offset + seg.count) {
1137            return null;
1138        }
1139        seg.modelOffset = seg.modelOffset + begin - seg.offset;
1140        seg.offset = begin;
1141        seg.count = end - begin;
1142        return seg;
1143        }
1144        
1145        /**
1146         * Return the AttributeSet for a given character at a given index
1147         *
1148         * @param i the zero-based index into the text
1149         * @return the AttributeSet of the character
1150         */

1151        public AttributeSet getCharacterAttribute(int i) {
1152        if (model instanceof StyledDocument) {
1153            StyledDocument doc = (StyledDocument)model;
1154            Element elem = doc.getCharacterElement(i);
1155            if (elem != null) {
1156            return elem.getAttributes();
1157            }
1158        }
1159        return null;
1160        }
1161        
1162        /**
1163         * Returns the start offset within the selected text.
1164         * If there is no selection, but there is
1165         * a caret, the start and end offsets will be the same.
1166         *
1167         * @return the index into the text of the start of the selection
1168         */

1169        public int getSelectionStart() {
1170        return editor.getSelectionStart();
1171        }
1172        
1173        /**
1174         * Returns the end offset within the selected text.
1175         * If there is no selection, but there is
1176         * a caret, the start and end offsets will be the same.
1177         *
1178         * @return the index into teh text of the end of the selection
1179         */

1180        public int getSelectionEnd() {
1181        return editor.getSelectionEnd();
1182        }
1183        
1184        /**
1185         * Returns the portion of the text that is selected.
1186         *
1187         * @return the String portion of the text that is selected
1188         */

1189        public String JavaDoc getSelectedText() {
1190        return editor.getSelectedText();
1191        }
1192        
1193        /*
1194         * Returns the text substring starting at the specified
1195         * offset with the specified length.
1196         */

1197        private String JavaDoc getText(int offset, int length)
1198        throws BadLocationException {
1199        
1200        if (model != null && model instanceof StyledDocument) {
1201            StyledDocument doc = (StyledDocument)model;
1202            return model.getText(offset, length);
1203        } else {
1204            return null;
1205        }
1206        }
1207    }
1208    }
1209    
1210    /*
1211     * ElementInfo for images
1212     */

1213    private class IconElementInfo extends ElementInfo implements Accessible {
1214
1215        private int width = -1;
1216        private int height = -1;
1217
1218        IconElementInfo(Element element, ElementInfo parent) {
1219            super(element, parent);
1220        }
1221
1222        protected void invalidate(boolean first) {
1223            super.invalidate(first);
1224            width = height = -1;
1225        }
1226
1227    private int getImageSize(Object JavaDoc key) {
1228            if (validateIfNecessary()) {
1229                int size = getIntAttr(getAttributes(), key, -1);
1230
1231                if (size == -1) {
1232                    View v = getView();
1233
1234                    size = 0;
1235                    if (v instanceof ImageView JavaDoc) {
1236                        Image img = ((ImageView JavaDoc)v).getImage();
1237                        if (img != null) {
1238                            if (key == HTML.Attribute.WIDTH) {
1239                                size = img.getWidth(null);
1240                            }
1241                            else {
1242                                size = img.getHeight(null);
1243                            }
1244                        }
1245                    }
1246                }
1247                return size;
1248            }
1249            return 0;
1250    }
1251
1252    // begin AccessibleIcon implementation ...
1253
private AccessibleContext accessibleContext;
1254    
1255    public AccessibleContext getAccessibleContext() {
1256        if (accessibleContext == null) {
1257        accessibleContext = new IconAccessibleContext(this);
1258        }
1259        return accessibleContext;
1260    }
1261    
1262    /*
1263     * AccessibleContext for images
1264     */

1265    protected class IconAccessibleContext extends HTMLAccessibleContext
1266            implements AccessibleIcon {
1267
1268        public IconAccessibleContext(ElementInfo elementInfo) {
1269        super(elementInfo);
1270        }
1271        
1272        /**
1273         * Gets the accessibleName property of this object. The accessibleName
1274         * property of an object is a localized String that designates the purpose
1275         * of the object. For example, the accessibleName property of a label
1276         * or button might be the text of the label or button itself. In the
1277         * case of an object that doesn't display its name, the accessibleName
1278         * should still be set. For example, in the case of a text field used
1279         * to enter the name of a city, the accessibleName for the en_US locale
1280         * could be 'city.'
1281         *
1282         * @return the localized name of the object; null if this
1283         * object does not have a name
1284         *
1285         * @see #setAccessibleName
1286         */

1287        public String JavaDoc getAccessibleName() {
1288        return getAccessibleIconDescription();
1289        }
1290        
1291        /**
1292         * Gets the accessibleDescription property of this object. If this
1293         * property isn't set, returns the content type of this
1294         * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
1295         *
1296         * @return the localized description of the object; <code>null</code>
1297         * if this object does not have a description
1298         *
1299         * @see #setAccessibleName
1300         */

1301        public String JavaDoc getAccessibleDescription() {
1302        return editor.getContentType();
1303        }
1304        
1305        /**
1306         * Gets the role of this object. The role of the object is the generic
1307         * purpose or use of the class of this object. For example, the role
1308         * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
1309         * AccessibleRole are provided so component developers can pick from
1310         * a set of predefined roles. This enables assistive technologies to
1311         * provide a consistent interface to various tweaked subclasses of
1312         * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
1313         * that act like a push button) as well as distinguish between sublasses
1314         * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
1315         * and AccessibleRole.RADIO_BUTTON for radio buttons).
1316         * <p>Note that the AccessibleRole class is also extensible, so
1317         * custom component developers can define their own AccessibleRole's
1318         * if the set of predefined roles is inadequate.
1319         *
1320         * @return an instance of AccessibleRole describing the role of the object
1321         * @see AccessibleRole
1322         */

1323        public AccessibleRole getAccessibleRole() {
1324        return AccessibleRole.ICON;
1325        }
1326        
1327        public AccessibleIcon [] getAccessibleIcon() {
1328        AccessibleIcon [] icons = new AccessibleIcon[1];
1329        icons[0] = this;
1330        return icons;
1331        }
1332
1333        /**
1334         * Gets the description of the icon. This is meant to be a brief
1335         * textual description of the object. For example, it might be
1336         * presented to a blind user to give an indication of the purpose
1337         * of the icon.
1338         *
1339         * @return the description of the icon
1340         */

1341        public String JavaDoc getAccessibleIconDescription() {
1342        return ((ImageView JavaDoc)getView()).getAltText();
1343        }
1344    
1345        /**
1346         * Sets the description of the icon. This is meant to be a brief
1347         * textual description of the object. For example, it might be
1348         * presented to a blind user to give an indication of the purpose
1349         * of the icon.
1350         *
1351         * @param description the description of the icon
1352         */

1353        public void setAccessibleIconDescription(String JavaDoc description) {
1354        }
1355    
1356        /**
1357         * Gets the width of the icon
1358         *
1359         * @return the width of the icon.
1360         */

1361        public int getAccessibleIconWidth() {
1362        if (width == -1) {
1363            width = getImageSize(HTML.Attribute.WIDTH);
1364        }
1365        return width;
1366        }
1367        
1368        /**
1369         * Gets the height of the icon
1370         *
1371         * @return the height of the icon.
1372         */

1373        public int getAccessibleIconHeight() {
1374        if (height == -1) {
1375            height = getImageSize(HTML.Attribute.HEIGHT);
1376        }
1377        return height;
1378        }
1379    }
1380    // ... end AccessibleIconImplementation
1381
}
1382
1383
1384    /**
1385     * TableElementInfo encapsulates information about a HTML.Tag.TABLE.
1386     * To make access fast it crates a grid containing the children to
1387     * allow for access by row, column. TableElementInfo will contain
1388     * TableRowElementInfos, which will contain TableCellElementInfos.
1389     * Any time one of the rows or columns becomes invalid the table is
1390     * invalidated. This is because any time one of the child attributes
1391     * changes the size of the grid may have changed.
1392     */

1393    private class TableElementInfo extends ElementInfo
1394        implements Accessible {
1395
1396    protected ElementInfo caption;
1397
1398        /**
1399         * Allocation of the table by row x column. There may be holes (eg
1400         * nulls) depending upon the html, any cell that has a rowspan/colspan
1401         * > 1 will be contained multiple times in the grid.
1402         */

1403        private TableCellElementInfo[][] grid;
1404
1405
1406        TableElementInfo(Element e, ElementInfo parent) {
1407            super(e, parent);
1408        }
1409
1410    public ElementInfo getCaptionInfo() {
1411        return caption;
1412    }
1413
1414        /**
1415         * Overriden to update the grid when validating.
1416         */

1417        protected void validate() {
1418            super.validate();
1419            updateGrid();
1420        }
1421
1422        /**
1423         * Overriden to only alloc instances of TableRowElementInfos.
1424         */

1425        protected void loadChildren(Element e) {
1426
1427            for (int counter = 0; counter < e.getElementCount(); counter++) {
1428                Element child = e.getElement(counter);
1429                AttributeSet attrs = child.getAttributes();
1430
1431                if (attrs.getAttribute(StyleConstants.NameAttribute) ==
1432                                       HTML.Tag.TR) {
1433                    addChild(new TableRowElementInfo(child, this, counter));
1434
1435                } else if (attrs.getAttribute(StyleConstants.NameAttribute) ==
1436                                       HTML.Tag.CAPTION) {
1437            // Handle captions as a special case since all other
1438
// children are table rows.
1439
caption = createElementInfo(child, this);
1440        }
1441            }
1442        }
1443
1444        /**
1445         * Updates the grid.
1446         */

1447        private void updateGrid() {
1448            // Determine the max row/col count.
1449
int delta = 0;
1450            int maxCols = 0;
1451            int rows = 0;
1452            for (int counter = 0; counter < getChildCount(); counter++) {
1453                TableRowElementInfo row = getRow(counter);
1454                int prev = 0;
1455                for (int y = 0; y < delta; y++) {
1456                    prev = Math.max(prev, getRow(counter - y - 1).
1457                                    getColumnCount(y + 2));
1458                }
1459                delta = Math.max(row.getRowCount(), delta);
1460                delta--;
1461                maxCols = Math.max(maxCols, row.getColumnCount() + prev);
1462            }
1463            rows = getChildCount() + delta;
1464
1465            // Alloc
1466
grid = new TableCellElementInfo[rows][];
1467            for (int counter = 0; counter < rows; counter++) {
1468                grid[counter] = new TableCellElementInfo[maxCols];
1469            }
1470            // Update
1471
for (int counter = 0; counter < rows; counter++) {
1472                getRow(counter).updateGrid(counter);
1473            }
1474        }
1475
1476        /**
1477         * Returns the TableCellElementInfo at the specified index.
1478         */

1479        public TableRowElementInfo getRow(int index) {
1480            return (TableRowElementInfo)getChild(index);
1481        }
1482
1483        /**
1484         * Returns the TableCellElementInfo by row and column.
1485         */

1486        public TableCellElementInfo getCell(int r, int c) {
1487            if (validateIfNecessary() && r < grid.length &&
1488                                         c < grid[0].length) {
1489                return grid[r][c];
1490            }
1491            return null;
1492        }
1493
1494        /**
1495         * Returns the rowspan of the specified entry.
1496         */

1497        public int getRowExtentAt(int r, int c) {
1498            TableCellElementInfo cell = getCell(r, c);
1499
1500            if (cell != null) {
1501                int rows = cell.getRowCount();
1502                int delta = 1;
1503
1504                while ((r - delta) >= 0 && grid[r - delta][c] == cell) {
1505                    delta++;
1506                }
1507                return rows - delta + 1;
1508            }
1509            return 0;
1510        }
1511
1512        /**
1513         * Returns the colspan of the specified entry.
1514         */

1515        public int getColumnExtentAt(int r, int c) {
1516            TableCellElementInfo cell = getCell(r, c);
1517
1518            if (cell != null) {
1519                int cols = cell.getColumnCount();
1520                int delta = 1;
1521
1522                while ((c - delta) >= 0 && grid[r][c - delta] == cell) {
1523                    delta++;
1524                }
1525                return cols - delta + 1;
1526            }
1527            return 0;
1528        }
1529
1530        /**
1531         * Returns the number of rows in the table.
1532         */

1533        public int getRowCount() {
1534            if (validateIfNecessary()) {
1535                return grid.length;
1536            }
1537            return 0;
1538        }
1539
1540        /**
1541         * Returns the number of columns in the table.
1542         */

1543        public int getColumnCount() {
1544            if (validateIfNecessary() && grid.length > 0) {
1545                return grid[0].length;
1546            }
1547            return 0;
1548        }
1549
1550    // begin AccessibleTable implementation ...
1551
private AccessibleContext accessibleContext;
1552    
1553    public AccessibleContext getAccessibleContext() {
1554        if (accessibleContext == null) {
1555        accessibleContext = new TableAccessibleContext(this);
1556        }
1557        return accessibleContext;
1558    }
1559    
1560    /*
1561     * AccessibleContext for tables
1562     */

1563    public class TableAccessibleContext extends HTMLAccessibleContext
1564            implements AccessibleTable {
1565
1566        private AccessibleHeadersTable rowHeadersTable;
1567
1568        public TableAccessibleContext(ElementInfo elementInfo) {
1569        super(elementInfo);
1570        }
1571        
1572        /**
1573         * Gets the accessibleName property of this object. The accessibleName
1574         * property of an object is a localized String that designates the purpose
1575         * of the object. For example, the accessibleName property of a label
1576         * or button might be the text of the label or button itself. In the
1577         * case of an object that doesn't display its name, the accessibleName
1578         * should still be set. For example, in the case of a text field used
1579         * to enter the name of a city, the accessibleName for the en_US locale
1580         * could be 'city.'
1581         *
1582         * @return the localized name of the object; null if this
1583         * object does not have a name
1584         *
1585         * @see #setAccessibleName
1586         */

1587        public String JavaDoc getAccessibleName() {
1588        // return the role of the object
1589
return getAccessibleRole().toString();
1590        }
1591        
1592        /**
1593         * Gets the accessibleDescription property of this object. If this
1594         * property isn't set, returns the content type of this
1595         * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
1596         *
1597         * @return the localized description of the object; <code>null</code>
1598         * if this object does not have a description
1599         *
1600         * @see #setAccessibleName
1601         */

1602        public String JavaDoc getAccessibleDescription() {
1603        return editor.getContentType();
1604        }
1605        
1606        /**
1607         * Gets the role of this object. The role of the object is the generic
1608         * purpose or use of the class of this object. For example, the role
1609         * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
1610         * AccessibleRole are provided so component developers can pick from
1611         * a set of predefined roles. This enables assistive technologies to
1612         * provide a consistent interface to various tweaked subclasses of
1613         * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
1614         * that act like a push button) as well as distinguish between sublasses
1615         * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
1616         * and AccessibleRole.RADIO_BUTTON for radio buttons).
1617         * <p>Note that the AccessibleRole class is also extensible, so
1618         * custom component developers can define their own AccessibleRole's
1619         * if the set of predefined roles is inadequate.
1620         *
1621         * @return an instance of AccessibleRole describing the role of the object
1622         * @see AccessibleRole
1623         */

1624        public AccessibleRole getAccessibleRole() {
1625        return AccessibleRole.TABLE;
1626        }
1627        
1628        /**
1629         * Gets the 0-based index of this object in its accessible parent.
1630         *
1631         * @return the 0-based index of this object in its parent; -1 if this
1632         * object does not have an accessible parent.
1633         *
1634         * @see #getAccessibleParent
1635         * @see #getAccessibleChildrenCount
1636         * @gsee #getAccessibleChild
1637         */

1638        public int getAccessibleIndexInParent() {
1639        return elementInfo.getIndexInParent();
1640        }
1641        
1642        /**
1643         * Returns the number of accessible children of the object.
1644         *
1645         * @return the number of accessible children of the object.
1646         */

1647        public int getAccessibleChildrenCount() {
1648        return ((TableElementInfo)elementInfo).getRowCount() *
1649            ((TableElementInfo)elementInfo).getColumnCount();
1650        }
1651        
1652        /**
1653         * Returns the specified Accessible child of the object. The Accessible
1654         * children of an Accessible object are zero-based, so the first child
1655         * of an Accessible child is at index 0, the second child is at index 1,
1656         * and so on.
1657         *
1658         * @param i zero-based index of child
1659         * @return the Accessible child of the object
1660         * @see #getAccessibleChildrenCount
1661         */

1662        public Accessible getAccessibleChild(int i) {
1663        int rowCount = ((TableElementInfo)elementInfo).getRowCount();
1664        int columnCount = ((TableElementInfo)elementInfo).getColumnCount();
1665        int r = i / rowCount;
1666        int c = i % columnCount;
1667        if (r < 0 || r >= rowCount || c < 0 || c >= columnCount) {
1668            return null;
1669        } else {
1670            return getAccessibleAt(r, c);
1671        }
1672        }
1673        
1674        public AccessibleTable getAccessibleTable() {
1675        return this;
1676        }
1677        
1678        /**
1679         * Returns the caption for the table.
1680         *
1681         * @return the caption for the table
1682         */

1683        public Accessible getAccessibleCaption() {
1684        ElementInfo captionInfo = getCaptionInfo();
1685        if (captionInfo instanceof Accessible) {
1686            return (Accessible)caption;
1687        } else {
1688            return null;
1689        }
1690        }
1691        
1692        /**
1693         * Sets the caption for the table.
1694         *
1695         * @param a the caption for the table
1696         */

1697        public void setAccessibleCaption(Accessible a) {
1698        }
1699        
1700        /**
1701         * Returns the summary description of the table.
1702         *
1703         * @return the summary description of the table
1704         */

1705        public Accessible getAccessibleSummary() {
1706        return null;
1707        }
1708        
1709        /**
1710         * Sets the summary description of the table
1711         *
1712         * @param a the summary description of the table
1713         */

1714        public void setAccessibleSummary(Accessible a) {
1715        }
1716        
1717        /**
1718         * Returns the number of rows in the table.
1719         *
1720         * @return the number of rows in the table
1721         */

1722        public int getAccessibleRowCount() {
1723        return ((TableElementInfo)elementInfo).getRowCount();
1724        }
1725        
1726        /**
1727         * Returns the number of columns in the table.
1728         *
1729         * @return the number of columns in the table
1730         */

1731        public int getAccessibleColumnCount() {
1732        return ((TableElementInfo)elementInfo).getColumnCount();
1733        }
1734        
1735        /**
1736         * Returns the Accessible at a specified row and column
1737         * in the table.
1738         *
1739         * @param r zero-based row of the table
1740         * @param c zero-based column of the table
1741         * @return the Accessible at the specified row and column
1742         */

1743        public Accessible getAccessibleAt(int r, int c) {
1744        TableCellElementInfo cellInfo = getCell(r, c);
1745        if (cellInfo != null) {
1746            return cellInfo.getAccessible();
1747        } else {
1748            return null;
1749        }
1750        }
1751        
1752        /**
1753         * Returns the number of rows occupied by the Accessible at
1754         * a specified row and column in the table.
1755         *
1756         * @return the number of rows occupied by the Accessible at a
1757         * given specified (row, column)
1758         */

1759        public int getAccessibleRowExtentAt(int r, int c) {
1760        return ((TableElementInfo)elementInfo).getRowExtentAt(r, c);
1761        }
1762        
1763        /**
1764         * Returns the number of columns occupied by the Accessible at
1765         * a specified row and column in the table.
1766         *
1767         * @return the number of columns occupied by the Accessible at a
1768         * given specified row and column
1769         */

1770        public int getAccessibleColumnExtentAt(int r, int c) {
1771        return ((TableElementInfo)elementInfo).getColumnExtentAt(r, c);
1772        }
1773        
1774        /**
1775         * Returns the row headers as an AccessibleTable.
1776         *
1777         * @return an AccessibleTable representing the row
1778         * headers
1779         */

1780        public AccessibleTable getAccessibleRowHeader() {
1781        return rowHeadersTable;
1782        }
1783        
1784        /**
1785         * Sets the row headers.
1786         *
1787         * @param table an AccessibleTable representing the
1788         * row headers
1789         */

1790        public void setAccessibleRowHeader(AccessibleTable table) {
1791        }
1792        
1793        /**
1794         * Returns the column headers as an AccessibleTable.
1795         *
1796         * @return an AccessibleTable representing the column
1797         * headers
1798         */

1799        public AccessibleTable getAccessibleColumnHeader() {
1800        return null;
1801        }
1802        
1803        /**
1804         * Sets the column headers.
1805         *
1806         * @param table an AccessibleTable representing the
1807         * column headers
1808         */

1809        public void setAccessibleColumnHeader(AccessibleTable table) {
1810        }
1811        
1812        /**
1813         * Returns the description of the specified row in the table.
1814         *
1815         * @param r zero-based row of the table
1816         * @return the description of the row
1817         */

1818        public Accessible getAccessibleRowDescription(int r) {
1819        return null;
1820        }
1821        
1822        /**
1823         * Sets the description text of the specified row of the table.
1824         *
1825         * @param r zero-based row of the table
1826         * @param a the description of the row
1827         */

1828        public void setAccessibleRowDescription(int r, Accessible a) {
1829        }
1830        
1831        /**
1832         * Returns the description text of the specified column in the table.
1833         *
1834         * @param c zero-based column of the table
1835         * @return the text description of the column
1836         */

1837        public Accessible getAccessibleColumnDescription(int c) {
1838        return null;
1839        }
1840        
1841        /**
1842         * Sets the description text of the specified column in the table.
1843         *
1844         * @param c zero-based column of the table
1845         * @param a the text description of the column
1846         */

1847        public void setAccessibleColumnDescription(int c, Accessible a) {
1848        }
1849        
1850        /**
1851         * Returns a boolean value indicating whether the accessible at
1852         * a specified row and column is selected.
1853         *
1854         * @param r zero-based row of the table
1855         * @param c zero-based column of the table
1856         * @return the boolean value true if the accessible at the
1857         * row and column is selected. Otherwise, the boolean value
1858         * false
1859         */

1860        public boolean isAccessibleSelected(int r, int c) {
1861        if (validateIfNecessary()) {
1862            if (r < 0 || r >= getAccessibleRowCount() ||
1863            c < 0 || c >= getAccessibleColumnCount()) {
1864            return false;
1865            }
1866            TableCellElementInfo cell = getCell(r, c);
1867            if (cell != null) {
1868            Element elem = cell.getElement();
1869            int start = elem.getStartOffset();
1870            int end = elem.getEndOffset();
1871            return start >= editor.getSelectionStart() &&
1872                end <= editor.getSelectionEnd();
1873            }
1874        }
1875        return false;
1876        }
1877        
1878        /**
1879         * Returns a boolean value indicating whether the specified row
1880         * is selected.
1881         *
1882         * @param r zero-based row of the table
1883         * @return the boolean value true if the specified row is selected.
1884         * Otherwise, false.
1885         */

1886        public boolean isAccessibleRowSelected(int r) {
1887        if (validateIfNecessary()) {
1888            if (r < 0 || r >= getAccessibleRowCount()) {
1889            return false;
1890            }
1891            int nColumns = getAccessibleColumnCount();
1892
1893            TableCellElementInfo startCell = getCell(r, 0);
1894            if (startCell == null) {
1895            return false;
1896            }
1897            int start = startCell.getElement().getStartOffset();
1898
1899            TableCellElementInfo endCell = getCell(r, nColumns-1);
1900            if (endCell == null) {
1901            return false;
1902            }
1903            int end = endCell.getElement().getEndOffset();
1904
1905            return start >= editor.getSelectionStart() &&
1906            end <= editor.getSelectionEnd();
1907        }
1908        return false;
1909        }
1910        
1911        /**
1912         * Returns a boolean value indicating whether the specified column
1913         * is selected.
1914         *
1915         * @param r zero-based column of the table
1916         * @return the boolean value true if the specified column is selected.
1917         * Otherwise, false.
1918         */

1919        public boolean isAccessibleColumnSelected(int c) {
1920        if (validateIfNecessary()) {
1921            if (c < 0 || c >= getAccessibleColumnCount()) {
1922            return false;
1923            }
1924            int nRows = getAccessibleRowCount();
1925
1926            TableCellElementInfo startCell = getCell(0, c);
1927            if (startCell == null) {
1928            return false;
1929            }
1930            int start = startCell.getElement().getStartOffset();
1931
1932            TableCellElementInfo endCell = getCell(nRows-1, c);
1933            if (endCell == null) {
1934            return false;
1935            }
1936            int end = endCell.getElement().getEndOffset();
1937            return start >= editor.getSelectionStart() &&
1938            end <= editor.getSelectionEnd();
1939        }
1940        return false;
1941        }
1942        
1943        /**
1944         * Returns the selected rows in a table.
1945         *
1946         * @return an array of selected rows where each element is a
1947         * zero-based row of the table
1948         */

1949        public int [] getSelectedAccessibleRows() {
1950        if (validateIfNecessary()) {
1951            int nRows = getAccessibleRowCount();
1952            Vector vec = new Vector();
1953            
1954            for (int i = 0; i < nRows; i++) {
1955            if (isAccessibleRowSelected(i)) {
1956                vec.addElement(new Integer JavaDoc(i));
1957            }
1958            }
1959            int retval[] = new int[vec.size()];
1960            for (int i = 0; i < retval.length; i++) {
1961            retval[i] = ((Integer JavaDoc)vec.elementAt(i)).intValue();
1962            }
1963            return retval;
1964        }
1965        return new int[0];
1966        }
1967        
1968        /**
1969         * Returns the selected columns in a table.
1970         *
1971         * @return an array of selected columns where each element is a
1972         * zero-based column of the table
1973         */

1974        public int [] getSelectedAccessibleColumns() {
1975        if (validateIfNecessary()) {
1976            int nColumns = getAccessibleRowCount();
1977            Vector vec = new Vector();
1978            
1979            for (int i = 0; i < nColumns; i++) {
1980            if (isAccessibleColumnSelected(i)) {
1981                vec.addElement(new Integer JavaDoc(i));
1982            }
1983            }
1984            int retval[] = new int[vec.size()];
1985            for (int i = 0; i < retval.length; i++) {
1986            retval[i] = ((Integer JavaDoc)vec.elementAt(i)).intValue();
1987            }
1988            return retval;
1989        }
1990        return new int[0];
1991        }
1992        
1993        // begin AccessibleExtendedTable implementation -------------
1994

1995        /**
1996         * Returns the row number of an index in the table.
1997         *
1998         * @param index the zero-based index in the table
1999         * @return the zero-based row of the table if one exists;
2000         * otherwise -1.
2001         */

2002        public int getAccessibleRow(int index) {
2003        if (validateIfNecessary()) {
2004            int numCells = getAccessibleColumnCount() *
2005            getAccessibleRowCount();
2006            if (index >= numCells) {
2007            return -1;
2008            } else {
2009            return index / getAccessibleColumnCount();
2010            }
2011        }
2012        return -1;
2013        }
2014        
2015        /**
2016         * Returns the column number of an index in the table.
2017         *
2018         * @param index the zero-based index in the table
2019         * @return the zero-based column of the table if one exists;
2020         * otherwise -1.
2021         */

2022        public int getAccessibleColumn(int index) {
2023        if (validateIfNecessary()) {
2024            int numCells = getAccessibleColumnCount() *
2025            getAccessibleRowCount();
2026            if (index >= numCells) {
2027            return -1;
2028            } else {
2029            return index % getAccessibleColumnCount();
2030            }
2031        }
2032        return -1;
2033        }
2034        
2035        /*
2036         * Returns the index at a row and column in the table.
2037         *
2038         * @param r zero-based row of the table
2039         * @param c zero-based column of the table
2040         * @return the zero-based index in the table if one exists;
2041         * otherwise -1.
2042         */

2043        public int getAccessibleIndex(int r, int c) {
2044        if (validateIfNecessary()) {
2045            if (r >= getAccessibleRowCount() ||
2046            c >= getAccessibleColumnCount()) {
2047            return -1;
2048            } else {
2049            return r * getAccessibleColumnCount() + c;
2050            }
2051        }
2052        return -1;
2053        }
2054        
2055        /**
2056         * Returns the row header at a row in a table.
2057         * @param r zero-based row of the table
2058         *
2059         * @return a String representing the row header
2060         * if one exists; otherwise null.
2061         */

2062        public String JavaDoc getAccessibleRowHeader(int r) {
2063        if (validateIfNecessary()) {
2064            TableCellElementInfo cellInfo = getCell(r, 0);
2065            if (cellInfo.isHeaderCell()) {
2066            View v = cellInfo.getView();
2067            if (v != null && model != null) {
2068                try {
2069                return model.getText(v.getStartOffset(),
2070                             v.getEndOffset() -
2071                             v.getStartOffset());
2072                } catch (BadLocationException e) {
2073                return null;
2074                }
2075            }
2076            }
2077        }
2078        return null;
2079        }
2080        
2081        /**
2082         * Returns the column header at a column in a table.
2083         * @param c zero-based column of the table
2084         *
2085         * @return a String representing the column header
2086         * if one exists; otherwise null.
2087         */

2088        public String JavaDoc getAccessibleColumnHeader(int c) {
2089        if (validateIfNecessary()) {
2090            TableCellElementInfo cellInfo = getCell(0, c);
2091            if (cellInfo.isHeaderCell()) {
2092            View v = cellInfo.getView();
2093            if (v != null && model != null) {
2094                try {
2095                return model.getText(v.getStartOffset(),
2096                             v.getEndOffset() -
2097                             v.getStartOffset());
2098                } catch (BadLocationException e) {
2099                return null;
2100                }
2101            }
2102            }
2103        }
2104        return null;
2105        }
2106
2107        public void addRowHeader(TableCellElementInfo cellInfo, int rowNumber) {
2108        if (rowHeadersTable == null) {
2109            rowHeadersTable = new AccessibleHeadersTable();
2110        }
2111        rowHeadersTable.addHeader(cellInfo, rowNumber);
2112        }
2113        // end of AccessibleExtendedTable implementation ------------
2114

2115        protected class AccessibleHeadersTable implements AccessibleTable {
2116            
2117        // Header information is modeled as a Hashtable of
2118
// ArrayLists where each Hashtable entry represents
2119
// a row containing one or more headers.
2120
private Hashtable headers = new Hashtable();
2121        private int rowCount = 0;
2122        private int columnCount = 0;
2123
2124        public void addHeader(TableCellElementInfo cellInfo, int rowNumber) {
2125            Integer JavaDoc rowInteger = new Integer JavaDoc(rowNumber);
2126            ArrayList list = (ArrayList)headers.get(rowInteger);
2127            if (list == null) {
2128            list = new ArrayList();
2129            headers.put(rowInteger, list);
2130            }
2131            list.add(cellInfo);
2132        }
2133
2134        /**
2135         * Returns the caption for the table.
2136         *
2137         * @return the caption for the table
2138         */

2139        public Accessible getAccessibleCaption() {
2140            return null;
2141        }
2142        
2143        /**
2144         * Sets the caption for the table.
2145         *
2146         * @param a the caption for the table
2147         */

2148        public void setAccessibleCaption(Accessible a) {
2149        }
2150        
2151        /**
2152         * Returns the summary description of the table.
2153         *
2154         * @return the summary description of the table
2155         */

2156        public Accessible getAccessibleSummary() {
2157            return null;
2158        }
2159        
2160        /**
2161         * Sets the summary description of the table
2162         *
2163         * @param a the summary description of the table
2164         */

2165        public void setAccessibleSummary(Accessible a) {
2166        }
2167        
2168        /**
2169         * Returns the number of rows in the table.
2170         *
2171         * @return the number of rows in the table
2172         */

2173        public int getAccessibleRowCount() {
2174            return rowCount;
2175        }
2176        
2177        /**
2178         * Returns the number of columns in the table.
2179         *
2180         * @return the number of columns in the table
2181         */

2182        public int getAccessibleColumnCount() {
2183            return columnCount;
2184        }
2185        
2186        private TableCellElementInfo getElementInfoAt(int r, int c) {
2187            ArrayList list = (ArrayList)headers.get(new Integer JavaDoc(r));
2188            if (list != null) {
2189            return (TableCellElementInfo)list.get(c);
2190            } else {
2191            return null;
2192            }
2193        }
2194
2195        /**
2196         * Returns the Accessible at a specified row and column
2197         * in the table.
2198         *
2199         * @param r zero-based row of the table
2200         * @param c zero-based column of the table
2201         * @return the Accessible at the specified row and column
2202         */

2203        public Accessible getAccessibleAt(int r, int c) {
2204            ElementInfo elementInfo = getElementInfoAt(r, c);
2205            if (elementInfo instanceof Accessible) {
2206            return (Accessible)elementInfo;
2207            } else {
2208            return null;
2209            }
2210        }
2211        
2212        /**
2213         * Returns the number of rows occupied by the Accessible at
2214         * a specified row and column in the table.
2215         *
2216         * @return the number of rows occupied by the Accessible at a
2217         * given specified (row, column)
2218         */

2219        public int getAccessibleRowExtentAt(int r, int c) {
2220            TableCellElementInfo elementInfo = getElementInfoAt(r, c);
2221            if (elementInfo != null) {
2222            return elementInfo.getRowCount();
2223            } else {
2224            return 0;
2225            }
2226        }
2227        
2228        /**
2229         * Returns the number of columns occupied by the Accessible at
2230         * a specified row and column in the table.
2231         *
2232         * @return the number of columns occupied by the Accessible at a
2233         * given specified row and column
2234         */

2235        public int getAccessibleColumnExtentAt(int r, int c) {
2236            TableCellElementInfo elementInfo = getElementInfoAt(r, c);
2237            if (elementInfo != null) {
2238            return elementInfo.getRowCount();
2239            } else {
2240            return 0;
2241            }
2242        }
2243        
2244        /**
2245         * Returns the row headers as an AccessibleTable.
2246         *
2247         * @return an AccessibleTable representing the row
2248         * headers
2249         */

2250        public AccessibleTable getAccessibleRowHeader() {
2251            return null;
2252        }
2253        
2254        /**
2255         * Sets the row headers.
2256         *
2257         * @param table an AccessibleTable representing the
2258         * row headers
2259         */

2260        public void setAccessibleRowHeader(AccessibleTable table) {
2261        }
2262        
2263        /**
2264         * Returns the column headers as an AccessibleTable.
2265         *
2266         * @return an AccessibleTable representing the column
2267         * headers
2268         */

2269        public AccessibleTable getAccessibleColumnHeader() {
2270            return null;
2271        }
2272        
2273        /**
2274         * Sets the column headers.
2275         *
2276         * @param table an AccessibleTable representing the
2277         * column headers
2278         */

2279        public void setAccessibleColumnHeader(AccessibleTable table) {
2280        }
2281        
2282        /**
2283         * Returns the description of the specified row in the table.
2284         *
2285         * @param r zero-based row of the table
2286         * @return the description of the row
2287         */

2288        public Accessible getAccessibleRowDescription(int r) {
2289            return null;
2290        }
2291        
2292        /**
2293         * Sets the description text of the specified row of the table.
2294         *
2295         * @param r zero-based row of the table
2296         * @param a the description of the row
2297         */

2298        public void setAccessibleRowDescription(int r, Accessible a) {
2299        }
2300        
2301        /**
2302         * Returns the description text of the specified column in the table.
2303         *
2304         * @param c zero-based column of the table
2305         * @return the text description of the column
2306         */

2307        public Accessible getAccessibleColumnDescription(int c) {
2308            return null;
2309        }
2310        
2311        /**
2312         * Sets the description text of the specified column in the table.
2313         *
2314         * @param c zero-based column of the table
2315         * @param a the text description of the column
2316         */

2317        public void setAccessibleColumnDescription(int c, Accessible a) {
2318        }
2319        
2320        /**
2321         * Returns a boolean value indicating whether the accessible at
2322         * a specified row and column is selected.
2323         *
2324         * @param r zero-based row of the table
2325         * @param c zero-based column of the table
2326         * @return the boolean value true if the accessible at the
2327         * row and column is selected. Otherwise, the boolean value
2328         * false
2329         */

2330        public boolean isAccessibleSelected(int r, int c) {
2331            return false;
2332        }
2333        
2334        /**
2335         * Returns a boolean value indicating whether the specified row
2336         * is selected.
2337         *
2338         * @param r zero-based row of the table
2339         * @return the boolean value true if the specified row is selected.
2340         * Otherwise, false.
2341         */

2342        public boolean isAccessibleRowSelected(int r) {
2343            return false;
2344        }
2345        
2346        /**
2347         * Returns a boolean value indicating whether the specified column
2348         * is selected.
2349         *
2350         * @param r zero-based column of the table
2351         * @return the boolean value true if the specified column is selected.
2352         * Otherwise, false.
2353         */

2354        public boolean isAccessibleColumnSelected(int c) {
2355            return false;
2356        }
2357        
2358        /**
2359         * Returns the selected rows in a table.
2360         *
2361         * @return an array of selected rows where each element is a
2362         * zero-based row of the table
2363         */

2364        public int [] getSelectedAccessibleRows() {
2365            return new int [0];
2366        }
2367        
2368        /**
2369         * Returns the selected columns in a table.
2370         *
2371         * @return an array of selected columns where each element is a
2372         * zero-based column of the table
2373         */

2374        public int [] getSelectedAccessibleColumns() {
2375            return new int [0];
2376        }
2377        }
2378        } // ... end AccessibleHeadersTable
2379

2380    /*
2381     * ElementInfo for table rows
2382     */

2383        private class TableRowElementInfo extends ElementInfo {
2384
2385        private TableElementInfo parent;
2386        private int rowNumber;
2387
2388            TableRowElementInfo(Element e, TableElementInfo parent, int rowNumber) {
2389                super(e, parent);
2390        this.parent = parent;
2391        this.rowNumber = rowNumber;
2392            }
2393
2394            protected void loadChildren(Element e) {
2395                for (int x = 0; x < e.getElementCount(); x++) {
2396                    AttributeSet attrs = e.getElement(x).getAttributes();
2397
2398                    if (attrs.getAttribute(StyleConstants.NameAttribute) ==
2399                HTML.Tag.TH) {
2400            TableCellElementInfo headerElementInfo =
2401                new TableCellElementInfo(e.getElement(x), this, true);
2402            addChild(headerElementInfo);
2403
2404            AccessibleTable at =
2405                parent.getAccessibleContext().getAccessibleTable();
2406            TableAccessibleContext tableElement =
2407                (TableAccessibleContext)at;
2408            tableElement.addRowHeader(headerElementInfo, rowNumber);
2409
2410            } else if (attrs.getAttribute(StyleConstants.NameAttribute) ==
2411                HTML.Tag.TD) {
2412                        addChild(new TableCellElementInfo(e.getElement(x), this,
2413                              false));
2414                    }
2415                }
2416            }
2417
2418            /**
2419             * Returns the max of the rowspans of the cells in this row.
2420             */

2421            public int getRowCount() {
2422                int rowCount = 1;
2423                if (validateIfNecessary()) {
2424                    for (int counter = 0; counter < getChildCount();
2425             counter++) {
2426
2427                        TableCellElementInfo cell = (TableCellElementInfo)
2428                                                    getChild(counter);
2429
2430                        if (cell.validateIfNecessary()) {
2431                rowCount = Math.max(rowCount, cell.getRowCount());
2432                        }
2433                    }
2434                }
2435                return rowCount;
2436            }
2437
2438            /**
2439             * Returns the sum of the column spans of the individual
2440             * cells in this row.
2441             */

2442            public int getColumnCount() {
2443                int colCount = 0;
2444                if (validateIfNecessary()) {
2445                    for (int counter = 0; counter < getChildCount();
2446                         counter++) {
2447                        TableCellElementInfo cell = (TableCellElementInfo)
2448                                                    getChild(counter);
2449
2450                        if (cell.validateIfNecessary()) {
2451                colCount += cell.getColumnCount();
2452                        }
2453                    }
2454                }
2455                return colCount;
2456            }
2457
2458            /**
2459             * Overriden to invalidate the table as well as
2460             * TableRowElementInfo.
2461             */

2462            protected void invalidate(boolean first) {
2463                super.invalidate(first);
2464                getParent().invalidate(true);
2465            }
2466
2467            /**
2468             * Places the TableCellElementInfos for this element in
2469             * the grid.
2470             */

2471            private void updateGrid(int row) {
2472                if (validateIfNecessary()) {
2473                    boolean emptyRow = false;
2474
2475                    while (!emptyRow) {
2476                        for (int counter = 0; counter < grid[row].length;
2477                                 counter++) {
2478                            if (grid[row][counter] == null) {
2479                                emptyRow = true;
2480                                break;
2481                            }
2482                        }
2483                        if (!emptyRow) {
2484                            row++;
2485                        }
2486                    }
2487                    for (int col = 0, counter = 0; counter < getChildCount();
2488                             counter++) {
2489                        TableCellElementInfo cell = (TableCellElementInfo)
2490                                                    getChild(counter);
2491
2492                        while (grid[row][col] != null) {
2493                            col++;
2494                        }
2495                        for (int rowCount = cell.getRowCount() - 1;
2496                             rowCount >= 0; rowCount--) {
2497                            for (int colCount = cell.getColumnCount() - 1;
2498                                 colCount >= 0; colCount--) {
2499                                grid[row + rowCount][col + colCount] = cell;
2500                            }
2501                        }
2502                        col += cell.getColumnCount();
2503                    }
2504                }
2505            }
2506
2507            /**
2508             * Returns the column count of the number of columns that have
2509             * a rowcount >= rowspan.
2510             */

2511            private int getColumnCount(int rowspan) {
2512                if (validateIfNecessary()) {
2513                    int cols = 0;
2514                    for (int counter = 0; counter < getChildCount();
2515                         counter++) {
2516                        TableCellElementInfo cell = (TableCellElementInfo)
2517                                                    getChild(counter);
2518
2519                        if (cell.getRowCount() >= rowspan) {
2520                            cols += cell.getColumnCount();
2521                        }
2522                    }
2523                    return cols;
2524                }
2525                return 0;
2526            }
2527    }
2528
2529        /**
2530         * TableCellElementInfo is used to represents the cells of
2531         * the table.
2532         */

2533        private class TableCellElementInfo extends ElementInfo {
2534
2535        private Accessible accessible;
2536        private boolean isHeaderCell;
2537
2538            TableCellElementInfo(Element e, ElementInfo parent) {
2539                super(e, parent);
2540        this.isHeaderCell = false;
2541            }
2542
2543            TableCellElementInfo(Element e, ElementInfo parent,
2544                 boolean isHeaderCell) {
2545                super(e, parent);
2546        this.isHeaderCell = isHeaderCell;
2547            }
2548
2549        /*
2550         * Returns whether this table cell is a header
2551         */

2552        public boolean isHeaderCell() {
2553        return this.isHeaderCell;
2554        }
2555
2556        /*
2557         * Returns the Accessible representing this table cell
2558         */

2559        public Accessible getAccessible() {
2560        accessible = null;
2561        getAccessible(this);
2562        return accessible;
2563        }
2564
2565        /*
2566         * Gets the outermost Accessible in the table cell
2567         */

2568        private void getAccessible(ElementInfo elementInfo) {
2569        if (elementInfo instanceof Accessible) {
2570            accessible = (Accessible)elementInfo;
2571            return;
2572        } else {
2573            for (int i = 0; i < elementInfo.getChildCount(); i++) {
2574            getAccessible(elementInfo.getChild(i));
2575            }
2576        }
2577        }
2578
2579            /**
2580             * Returns the rowspan attribute.
2581             */

2582            public int getRowCount() {
2583                if (validateIfNecessary()) {
2584                    return Math.max(1, getIntAttr(getAttributes(),
2585                                                  HTML.Attribute.ROWSPAN, 1));
2586                }
2587                return 0;
2588            }
2589
2590            /**
2591             * Returns the colspan attribute.
2592             */

2593            public int getColumnCount() {
2594                if (validateIfNecessary()) {
2595                    return Math.max(1, getIntAttr(getAttributes(),
2596                                                  HTML.Attribute.COLSPAN, 1));
2597                }
2598                return 0;
2599            }
2600
2601            /**
2602             * Overriden to invalidate the TableRowElementInfo as well as
2603             * the TableCellElementInfo.
2604             */

2605            protected void invalidate(boolean first) {
2606                super.invalidate(first);
2607                getParent().invalidate(true);
2608            }
2609    }
2610    }
2611
2612
2613    /**
2614     * ElementInfo provides a slim down view of an Element. Each ElementInfo
2615     * can have any number of child ElementInfos that are not necessarily
2616     * direct children of the Element. As the Document changes various
2617     * ElementInfos become invalidated. Before accessing a particular portion
2618     * of an ElementInfo you should make sure it is valid by invoking
2619     * <code>validateIfNecessary</code>, this will return true if
2620     * successful, on the other hand a false return value indicates the
2621     * ElementInfo is not valid and can never become valid again (usually
2622     * the result of the Element the ElementInfo encapsulates being removed).
2623     */

2624    private class ElementInfo {
2625
2626        /**
2627         * The children of this ElementInfo.
2628         */

2629        private ArrayList children;
2630        /**
2631         * The Element this ElementInfo is providing information for.
2632         */

2633        private Element element;
2634        /**
2635         * The parent ElementInfo, will be null for the root.
2636         */

2637        private ElementInfo parent;
2638        /**
2639         * Indicates the validity of the ElementInfo.
2640         */

2641        private boolean isValid;
2642        /**
2643         * Indicates if the ElementInfo can become valid.
2644         */

2645        private boolean canBeValid;
2646
2647
2648        /**
2649         * Creates the root ElementInfo.
2650         */

2651        ElementInfo(Element element) {
2652            this(element, null);
2653        }
2654
2655        /**
2656         * Creates an ElementInfo representing <code>element</code> with
2657         * the specified parent.
2658         */

2659        ElementInfo(Element element, ElementInfo parent) {
2660            this.element = element;
2661            this.parent = parent;
2662            isValid = false;
2663            canBeValid = true;
2664        }
2665
2666        /**
2667         * Validates the receiver. This recreates the children as well. This
2668         * will be invoked within a <code>readLock</code>. If this is overriden
2669         * it MUST invoke supers implementation first!
2670         */

2671        protected void validate() {
2672            isValid = true;
2673            loadChildren(getElement());
2674        }
2675
2676        /**
2677         * Recreates the direct children of <code>info</code>.
2678         */

2679        protected void loadChildren(Element parent) {
2680            if (!parent.isLeaf()) {
2681                for (int counter = 0, maxCounter = parent.getElementCount();
2682                    counter < maxCounter; counter++) {
2683                    Element e = parent.getElement(counter);
2684                    ElementInfo childInfo = createElementInfo(e, this);
2685
2686                    if (childInfo != null) {
2687                        addChild(childInfo);
2688                    }
2689                    else {
2690                        loadChildren(e);
2691                    }
2692                }
2693            }
2694        }
2695
2696        /**
2697         * Returns the index of the child in the parent, or -1 for the
2698         * root or if the parent isn't valid.
2699         */

2700        public int getIndexInParent() {
2701            if (parent == null || !parent.isValid()) {
2702                return -1;
2703            }
2704            return parent.indexOf(this);
2705        }
2706
2707        /**
2708         * Returns the Element this <code>ElementInfo</code> represents.
2709         */

2710        public Element getElement() {
2711            return element;
2712        }
2713
2714        /**
2715         * Returns the parent of this Element, or null for the root.
2716         */

2717        public ElementInfo getParent() {
2718            return parent;
2719        }
2720
2721        /**
2722         * Returns the index of the specified child, or -1 if
2723         * <code>child</code> isn't a valid child.
2724         */

2725        public int indexOf(ElementInfo child) {
2726            ArrayList children = this.children;
2727
2728            if (children != null) {
2729                return children.indexOf(child);
2730            }
2731            return -1;
2732        }
2733
2734        /**
2735         * Returns the child ElementInfo at <code>index</code>, or null
2736         * if <code>index</code> isn't a valid index.
2737         */

2738        public ElementInfo getChild(int index) {
2739            if (validateIfNecessary()) {
2740                ArrayList children = this.children;
2741
2742                if (children != null && index >= 0 &&
2743                                        index < children.size()) {
2744                    return (ElementInfo)children.get(index);
2745                }
2746            }
2747            return null;
2748        }
2749
2750        /**
2751         * Returns the number of children the ElementInfo contains.
2752         */

2753        public int getChildCount() {
2754            validateIfNecessary();
2755            return (children == null) ? 0 : children.size();
2756        }
2757
2758        /**
2759         * Adds a new child to this ElementInfo.
2760         */

2761        protected void addChild(ElementInfo child) {
2762            if (children == null) {
2763                children = new ArrayList();
2764            }
2765            children.add(child);
2766        }
2767
2768        /**
2769         * Returns the View corresponding to this ElementInfo, or null
2770         * if the ElementInfo can't be validated.
2771         */

2772        protected View getView() {
2773            if (!validateIfNecessary()) {
2774                return null;
2775            }
2776            Object JavaDoc lock = lock();
2777            try {
2778                View rootView = getRootView();
2779                Element e = getElement();
2780                int start = e.getStartOffset();
2781
2782                if (rootView != null) {
2783                    return getView(rootView, e, start);
2784                }
2785                return null;
2786            } finally {
2787                unlock(lock);
2788            }
2789        }
2790
2791        /**
2792         * Returns the Bounds for this ElementInfo, or null
2793         * if the ElementInfo can't be validated.
2794         */

2795    public Rectangle getBounds() {
2796            if (!validateIfNecessary()) {
2797                return null;
2798            }
2799            Object JavaDoc lock = lock();
2800            try {
2801                Rectangle bounds = getRootEditorRect();
2802                View rootView = getRootView();
2803                Element e = getElement();
2804
2805                if (bounds != null && rootView != null) {
2806                    try {
2807                        return rootView.modelToView(e.getStartOffset(),
2808                                                    Position.Bias.Forward,
2809                                                    e.getEndOffset(),
2810                                                    Position.Bias.Backward,
2811                                                    bounds).getBounds();
2812                    } catch (BadLocationException ble) { }
2813                }
2814            } finally {
2815                unlock(lock);
2816            }
2817            return null;
2818    }
2819
2820        /**
2821         * Returns true if this ElementInfo is valid.
2822         */

2823        protected boolean isValid() {
2824            return isValid;
2825        }
2826
2827        /**
2828         * Returns the AttributeSet associated with the Element, this will
2829         * return null if the ElementInfo can't be validated.
2830         */

2831        protected AttributeSet getAttributes() {
2832            if (validateIfNecessary()) {
2833                return getElement().getAttributes();
2834            }
2835            return null;
2836        }
2837
2838        /**
2839         * Returns the AttributeSet associated with the View that is
2840         * representing this Element, this will
2841         * return null if the ElementInfo can't be validated.
2842         */

2843        protected AttributeSet getViewAttributes() {
2844            if (validateIfNecessary()) {
2845                View view = getView();
2846
2847                if (view != null) {
2848                    return view.getElement().getAttributes();
2849                }
2850                return getElement().getAttributes();
2851            }
2852            return null;
2853        }
2854
2855        /**
2856         * Convenience method for getting an integer attribute from the passed
2857         * in AttributeSet.
2858         */

2859        protected int getIntAttr(AttributeSet attrs, Object JavaDoc key, int deflt) {
2860            if (attrs != null && attrs.isDefined(key)) {
2861                int i;
2862                String JavaDoc val = (String JavaDoc)attrs.getAttribute(key);
2863                if (val == null) {
2864                    i = deflt;
2865                }
2866                else {
2867                    try {
2868                        i = Math.max(0, Integer.parseInt(val));
2869                    } catch (NumberFormatException JavaDoc x) {
2870                        i = deflt;
2871                    }
2872                }
2873                return i;
2874            }
2875            return deflt;
2876        }
2877
2878        /**
2879         * Validates the ElementInfo if necessary. Some ElementInfos may
2880         * never be valid again. You should check <code>isValid</code> before
2881         * using one. This will reload the children and invoke
2882         * <code>validate</code> if the ElementInfo is invalid and can become
2883         * valid again. This will return true if the receiver is valid.
2884         */

2885        protected boolean validateIfNecessary() {
2886            if (!isValid() && canBeValid) {
2887                children = null;
2888                Object JavaDoc lock = lock();
2889
2890                try {
2891                    validate();
2892                } finally {
2893                    unlock(lock);
2894                }
2895            }
2896            return isValid();
2897        }
2898
2899        /**
2900         * Invalidates the ElementInfo. Subclasses should override this
2901         * if they need to reset state once invalid.
2902         */

2903        protected void invalidate(boolean first) {
2904            if (!isValid()) {
2905                if (canBeValid && !first) {
2906                    canBeValid = false;
2907                }
2908                return;
2909            }
2910            isValid = false;
2911            canBeValid = first;
2912            if (children != null) {
2913                for (int counter = 0; counter < children.size(); counter++) {
2914                    ((ElementInfo)children.get(counter)).invalidate(false);
2915                }
2916                children = null;
2917            }
2918        }
2919
2920        private View getView(View parent, Element e, int start) {
2921            if (parent.getElement() == e) {
2922                return parent;
2923            }
2924            int index = parent.getViewIndex(start, Position.Bias.Forward);
2925
2926            if (index != -1 && index < parent.getViewCount()) {
2927                return getView(parent.getView(index), e, start);
2928            }
2929            return null;
2930        }
2931
2932        private int getClosestInfoIndex(int index) {
2933            for (int counter = 0; counter < getChildCount(); counter++) {
2934                ElementInfo info = getChild(counter);
2935
2936                if (index < info.getElement().getEndOffset() ||
2937                    index == info.getElement().getStartOffset()) {
2938                    return counter;
2939                }
2940            }
2941            return -1;
2942        }
2943
2944        private void update(DocumentEvent e) {
2945            if (!isValid()) {
2946                return;
2947            }
2948            ElementInfo parent = getParent();
2949            Element element = getElement();
2950
2951            do {
2952                DocumentEvent.ElementChange ec = e.getChange(element);
2953                if (ec != null) {
2954                    if (element == getElement()) {
2955                        // One of our children changed.
2956
invalidate(true);
2957                    }
2958                    else if (parent != null) {
2959                        parent.invalidate(parent == getRootInfo());
2960                    }
2961                    return;
2962                }
2963                element = element.getParentElement();
2964            } while (parent != null && element != null &&
2965                     element != parent.getElement());
2966
2967            if (getChildCount() > 0) {
2968                Element elem = getElement();
2969                int pos = e.getOffset();
2970                int index0 = getClosestInfoIndex(pos);
2971                if (index0 == -1 &&
2972                    e.getType() == DocumentEvent.EventType.REMOVE &&
2973                    pos >= elem.getEndOffset()) {
2974                    // Event beyond our offsets. We may have represented this,
2975
// that is the remove may have removed one of our child
2976
// Elements that represented this, so, we should foward
2977
// to last element.
2978
index0 = getChildCount() - 1;
2979                }
2980                ElementInfo info = (index0 >= 0) ? getChild(index0) : null;
2981                if (info != null &&
2982                    (info.getElement().getStartOffset() == pos) && (pos > 0)) {
2983                    // If at a boundary, forward the event to the previous
2984
// ElementInfo too.
2985
index0 = Math.max(index0 - 1, 0);
2986                }
2987                int index1;
2988                if (e.getType() != DocumentEvent.EventType.REMOVE) {
2989                    index1 = getClosestInfoIndex(pos + e.getLength());
2990                    if (index1 < 0) {
2991                        index1 = getChildCount() - 1;
2992                    }
2993                }
2994                else {
2995                    index1 = index0;
2996                    // A remove may result in empty elements.
2997
while ((index1 + 1) < getChildCount() &&
2998                           getChild(index1 + 1).getElement().getEndOffset() ==
2999                           getChild(index1 + 1).getElement().getStartOffset()){
3000                        index1++;
3001                    }
3002                }
3003                index0 = Math.max(index0, 0);
3004                // The check for isValid is here as in the process of
3005
// forwarding update our child may invalidate us.
3006
for (int i = index0; i <= index1 && isValid(); i++) {
3007                    getChild(i).update(e);
3008                }
3009            }
3010        }
3011    }
3012
3013    /**
3014     * DocumentListener installed on the current Document. Will invoke
3015     * <code>update</code> on the <code>RootInfo</code> in response to
3016     * any event.
3017     */

3018    private class DocumentHandler implements DocumentListener {
3019        public void insertUpdate(DocumentEvent e) {
3020            getRootInfo().update(e);
3021        }
3022        public void removeUpdate(DocumentEvent e) {
3023            getRootInfo().update(e);
3024        }
3025        public void changedUpdate(DocumentEvent e) {
3026            getRootInfo().update(e);
3027        }
3028    }
3029
3030    /*
3031     * PropertyChangeListener installed on the editor.
3032     */

3033    private class PropertyChangeHandler implements PropertyChangeListener {
3034    public void propertyChange(PropertyChangeEvent evt) {
3035        if (evt.getPropertyName().equals("document")) {
3036        // handle the document change
3037
setDocument(editor.getDocument());
3038        }
3039    }
3040    }
3041}
3042
Popular Tags