0001 /*
0002 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.swing.text.html;
0027
0028 import java.awt.*;
0029 import java.awt.event.*;
0030 import java.beans.*;
0031 import java.util.*;
0032 import javax.swing.*;
0033 import javax.swing.event.*;
0034 import javax.swing.text.*;
0035 import javax.accessibility.*;
0036 import java.text.BreakIterator;
0037
0038 /*
0039 * The AccessibleHTML class provide information about the contents
0040 * of a HTML document to assistive technologies.
0041 *
0042 * @version 1.21 05/05/07
0043 * @author Lynn Monsanto
0044 */
0045 class AccessibleHTML implements Accessible {
0046
0047 /**
0048 * The editor.
0049 */
0050 private JEditorPane editor;
0051 /**
0052 * Current model.
0053 */
0054 private Document model;
0055 /**
0056 * DocumentListener installed on the current model.
0057 */
0058 private DocumentListener docListener;
0059 /**
0060 * PropertyChangeListener installed on the editor
0061 */
0062 private PropertyChangeListener propChangeListener;
0063 /**
0064 * The root ElementInfo for the document
0065 */
0066 private ElementInfo rootElementInfo;
0067 /*
0068 * The root accessible context for the document
0069 */
0070 private RootHTMLAccessibleContext rootHTMLAccessibleContext;
0071
0072 public AccessibleHTML(JEditorPane pane) {
0073 editor = pane;
0074 propChangeListener = new PropertyChangeHandler();
0075 setDocument(editor.getDocument());
0076
0077 docListener = new DocumentHandler();
0078 }
0079
0080 /**
0081 * Sets the document.
0082 */
0083 private void setDocument(Document document) {
0084 if (model != null) {
0085 model.removeDocumentListener(docListener);
0086 }
0087 if (editor != null) {
0088 editor.removePropertyChangeListener(propChangeListener);
0089 }
0090 this .model = document;
0091 if (model != null) {
0092 if (rootElementInfo != null) {
0093 rootElementInfo.invalidate(false);
0094 }
0095 buildInfo();
0096 model.addDocumentListener(docListener);
0097 } else {
0098 rootElementInfo = null;
0099 }
0100 if (editor != null) {
0101 editor.addPropertyChangeListener(propChangeListener);
0102 }
0103 }
0104
0105 /**
0106 * Returns the Document currently presenting information for.
0107 */
0108 private Document getDocument() {
0109 return model;
0110 }
0111
0112 /**
0113 * Returns the JEditorPane providing information for.
0114 */
0115 private JEditorPane getTextComponent() {
0116 return editor;
0117 }
0118
0119 /**
0120 * Returns the ElementInfo representing the root Element.
0121 */
0122 private ElementInfo getRootInfo() {
0123 return rootElementInfo;
0124 }
0125
0126 /**
0127 * Returns the root <code>View</code> associated with the current text
0128 * component.
0129 */
0130 private View getRootView() {
0131 return getTextComponent().getUI().getRootView(
0132 getTextComponent());
0133 }
0134
0135 /**
0136 * Returns the bounds the root View will be rendered in.
0137 */
0138 private Rectangle getRootEditorRect() {
0139 Rectangle alloc = getTextComponent().getBounds();
0140 if ((alloc.width > 0) && (alloc.height > 0)) {
0141 alloc.x = alloc.y = 0;
0142 Insets insets = editor.getInsets();
0143 alloc.x += insets.left;
0144 alloc.y += insets.top;
0145 alloc.width -= insets.left + insets.right;
0146 alloc.height -= insets.top + insets.bottom;
0147 return alloc;
0148 }
0149 return null;
0150 }
0151
0152 /**
0153 * If possible acquires a lock on the Document. If a lock has been
0154 * obtained a key will be retured that should be passed to
0155 * <code>unlock</code>.
0156 */
0157 private Object lock() {
0158 Document document = getDocument();
0159
0160 if (document instanceof AbstractDocument) {
0161 ((AbstractDocument) document).readLock();
0162 return document;
0163 }
0164 return null;
0165 }
0166
0167 /**
0168 * Releases a lock previously obtained via <code>lock</code>.
0169 */
0170 private void unlock(Object key) {
0171 if (key != null) {
0172 ((AbstractDocument) key).readUnlock();
0173 }
0174 }
0175
0176 /**
0177 * Rebuilds the information from the current info.
0178 */
0179 private void buildInfo() {
0180 Object lock = lock();
0181
0182 try {
0183 Document doc = getDocument();
0184 Element root = doc.getDefaultRootElement();
0185
0186 rootElementInfo = new ElementInfo(root);
0187 rootElementInfo.validate();
0188 } finally {
0189 unlock(lock);
0190 }
0191 }
0192
0193 /*
0194 * Create an ElementInfo subclass based on the passed in Element.
0195 */
0196 ElementInfo createElementInfo(Element e, ElementInfo parent) {
0197 AttributeSet attrs = e.getAttributes();
0198
0199 if (attrs != null) {
0200 Object name = attrs
0201 .getAttribute(StyleConstants.NameAttribute);
0202
0203 if (name == HTML.Tag.IMG) {
0204 return new IconElementInfo(e, parent);
0205 } else if (name == HTML.Tag.CONTENT
0206 || name == HTML.Tag.CAPTION) {
0207 return new TextElementInfo(e, parent);
0208 } else if (name == HTML.Tag.TABLE) {
0209 return new TableElementInfo(e, parent);
0210 }
0211 }
0212 return null;
0213 }
0214
0215 /**
0216 * Returns the root AccessibleContext for the document
0217 */
0218 public AccessibleContext getAccessibleContext() {
0219 if (rootHTMLAccessibleContext == null) {
0220 rootHTMLAccessibleContext = new RootHTMLAccessibleContext(
0221 rootElementInfo);
0222 }
0223 return rootHTMLAccessibleContext;
0224 }
0225
0226 /*
0227 * The roow AccessibleContext for the document
0228 */
0229 private class RootHTMLAccessibleContext extends
0230 HTMLAccessibleContext {
0231
0232 public RootHTMLAccessibleContext(ElementInfo elementInfo) {
0233 super (elementInfo);
0234 }
0235
0236 /**
0237 * Gets the accessibleName property of this object. The accessibleName
0238 * property of an object is a localized String that designates the purpose
0239 * of the object. For example, the accessibleName property of a label
0240 * or button might be the text of the label or button itself. In the
0241 * case of an object that doesn't display its name, the accessibleName
0242 * should still be set. For example, in the case of a text field used
0243 * to enter the name of a city, the accessibleName for the en_US locale
0244 * could be 'city.'
0245 *
0246 * @return the localized name of the object; null if this
0247 * object does not have a name
0248 *
0249 * @see #setAccessibleName
0250 */
0251 public String getAccessibleName() {
0252 if (model != null) {
0253 return (String) model
0254 .getProperty(Document.TitleProperty);
0255 } else {
0256 return null;
0257 }
0258 }
0259
0260 /**
0261 * Gets the accessibleDescription property of this object. If this
0262 * property isn't set, returns the content type of this
0263 * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
0264 *
0265 * @return the localized description of the object; <code>null</code>
0266 * if this object does not have a description
0267 *
0268 * @see #setAccessibleName
0269 */
0270 public String getAccessibleDescription() {
0271 return editor.getContentType();
0272 }
0273
0274 /**
0275 * Gets the role of this object. The role of the object is the generic
0276 * purpose or use of the class of this object. For example, the role
0277 * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
0278 * AccessibleRole are provided so component developers can pick from
0279 * a set of predefined roles. This enables assistive technologies to
0280 * provide a consistent interface to various tweaked subclasses of
0281 * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
0282 * that act like a push button) as well as distinguish between sublasses
0283 * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
0284 * and AccessibleRole.RADIO_BUTTON for radio buttons).
0285 * <p>Note that the AccessibleRole class is also extensible, so
0286 * custom component developers can define their own AccessibleRole's
0287 * if the set of predefined roles is inadequate.
0288 *
0289 * @return an instance of AccessibleRole describing the role of the object
0290 * @see AccessibleRole
0291 */
0292 public AccessibleRole getAccessibleRole() {
0293 return AccessibleRole.TEXT;
0294 }
0295 }
0296
0297 /*
0298 * Base AccessibleContext class for HTML elements
0299 */
0300 protected abstract class HTMLAccessibleContext extends
0301 AccessibleContext implements Accessible,
0302 AccessibleComponent {
0303
0304 protected ElementInfo elementInfo;
0305
0306 public HTMLAccessibleContext(ElementInfo elementInfo) {
0307 this .elementInfo = elementInfo;
0308 }
0309
0310 // begin AccessibleContext implementation ...
0311 public AccessibleContext getAccessibleContext() {
0312 return this ;
0313 }
0314
0315 /**
0316 * Gets the state set of this object.
0317 *
0318 * @return an instance of AccessibleStateSet describing the states
0319 * of the object
0320 * @see AccessibleStateSet
0321 */
0322 public AccessibleStateSet getAccessibleStateSet() {
0323 AccessibleStateSet states = new AccessibleStateSet();
0324 Component comp = getTextComponent();
0325
0326 if (comp.isEnabled()) {
0327 states.add(AccessibleState.ENABLED);
0328 }
0329 if (comp instanceof JTextComponent
0330 && ((JTextComponent) comp).isEditable()) {
0331
0332 states.add(AccessibleState.EDITABLE);
0333 states.add(AccessibleState.FOCUSABLE);
0334 }
0335 if (comp.isVisible()) {
0336 states.add(AccessibleState.VISIBLE);
0337 }
0338 if (comp.isShowing()) {
0339 states.add(AccessibleState.SHOWING);
0340 }
0341 return states;
0342 }
0343
0344 /**
0345 * Gets the 0-based index of this object in its accessible parent.
0346 *
0347 * @return the 0-based index of this object in its parent; -1 if this
0348 * object does not have an accessible parent.
0349 *
0350 * @see #getAccessibleParent
0351 * @see #getAccessibleChildrenCount
0352 * @see #getAccessibleChild
0353 */
0354 public int getAccessibleIndexInParent() {
0355 return elementInfo.getIndexInParent();
0356 }
0357
0358 /**
0359 * Returns the number of accessible children of the object.
0360 *
0361 * @return the number of accessible children of the object.
0362 */
0363 public int getAccessibleChildrenCount() {
0364 return elementInfo.getChildCount();
0365 }
0366
0367 /**
0368 * Returns the specified Accessible child of the object. The Accessible
0369 * children of an Accessible object are zero-based, so the first child
0370 * of an Accessible child is at index 0, the second child is at index 1,
0371 * and so on.
0372 *
0373 * @param i zero-based index of child
0374 * @return the Accessible child of the object
0375 * @see #getAccessibleChildrenCount
0376 */
0377 public Accessible getAccessibleChild(int i) {
0378 ElementInfo childInfo = elementInfo.getChild(i);
0379 if (childInfo != null && childInfo instanceof Accessible) {
0380 return (Accessible) childInfo;
0381 } else {
0382 return null;
0383 }
0384 }
0385
0386 /**
0387 * Gets the locale of the component. If the component does not have a
0388 * locale, then the locale of its parent is returned.
0389 *
0390 * @return this component's locale. If this component does not have
0391 * a locale, the locale of its parent is returned.
0392 *
0393 * @exception IllegalComponentStateException
0394 * If the Component does not have its own locale and has not yet been
0395 * added to a containment hierarchy such that the locale can be
0396 * determined from the containing parent.
0397 */
0398 public Locale getLocale() throws IllegalComponentStateException {
0399 return editor.getLocale();
0400 }
0401
0402 // ... end AccessibleContext implementation
0403
0404 // begin AccessibleComponent implementation ...
0405 public AccessibleComponent getAccessibleComponent() {
0406 return this ;
0407 }
0408
0409 /**
0410 * Gets the background color of this object.
0411 *
0412 * @return the background color, if supported, of the object;
0413 * otherwise, null
0414 * @see #setBackground
0415 */
0416 public Color getBackground() {
0417 return getTextComponent().getBackground();
0418 }
0419
0420 /**
0421 * Sets the background color of this object.
0422 *
0423 * @param c the new Color for the background
0424 * @see #setBackground
0425 */
0426 public void setBackground(Color c) {
0427 getTextComponent().setBackground(c);
0428 }
0429
0430 /**
0431 * Gets the foreground color of this object.
0432 *
0433 * @return the foreground color, if supported, of the object;
0434 * otherwise, null
0435 * @see #setForeground
0436 */
0437 public Color getForeground() {
0438 return getTextComponent().getForeground();
0439 }
0440
0441 /**
0442 * Sets the foreground color of this object.
0443 *
0444 * @param c the new Color for the foreground
0445 * @see #getForeground
0446 */
0447 public void setForeground(Color c) {
0448 getTextComponent().setForeground(c);
0449 }
0450
0451 /**
0452 * Gets the Cursor of this object.
0453 *
0454 * @return the Cursor, if supported, of the object; otherwise, null
0455 * @see #setCursor
0456 */
0457 public Cursor getCursor() {
0458 return getTextComponent().getCursor();
0459 }
0460
0461 /**
0462 * Sets the Cursor of this object.
0463 *
0464 * @param c the new Cursor for the object
0465 * @see #getCursor
0466 */
0467 public void setCursor(Cursor cursor) {
0468 getTextComponent().setCursor(cursor);
0469 }
0470
0471 /**
0472 * Gets the Font of this object.
0473 *
0474 * @return the Font,if supported, for the object; otherwise, null
0475 * @see #setFont
0476 */
0477 public Font getFont() {
0478 return getTextComponent().getFont();
0479 }
0480
0481 /**
0482 * Sets the Font of this object.
0483 *
0484 * @param f the new Font for the object
0485 * @see #getFont
0486 */
0487 public void setFont(Font f) {
0488 getTextComponent().setFont(f);
0489 }
0490
0491 /**
0492 * Gets the FontMetrics of this object.
0493 *
0494 * @param f the Font
0495 * @return the FontMetrics, if supported, the object; otherwise, null
0496 * @see #getFont
0497 */
0498 public FontMetrics getFontMetrics(Font f) {
0499 return getTextComponent().getFontMetrics(f);
0500 }
0501
0502 /**
0503 * Determines if the object is enabled. Objects that are enabled
0504 * will also have the AccessibleState.ENABLED state set in their
0505 * AccessibleStateSets.
0506 *
0507 * @return true if object is enabled; otherwise, false
0508 * @see #setEnabled
0509 * @see AccessibleContext#getAccessibleStateSet
0510 * @see AccessibleState#ENABLED
0511 * @see AccessibleStateSet
0512 */
0513 public boolean isEnabled() {
0514 return getTextComponent().isEnabled();
0515 }
0516
0517 /**
0518 * Sets the enabled state of the object.
0519 *
0520 * @param b if true, enables this object; otherwise, disables it
0521 * @see #isEnabled
0522 */
0523 public void setEnabled(boolean b) {
0524 getTextComponent().setEnabled(b);
0525 }
0526
0527 /**
0528 * Determines if the object is visible. Note: this means that the
0529 * object intends to be visible; however, it may not be
0530 * showing on the screen because one of the objects that this object
0531 * is contained by is currently not visible. To determine if an object
0532 * is showing on the screen, use isShowing().
0533 * <p>Objects that are visible will also have the
0534 * AccessibleState.VISIBLE state set in their AccessibleStateSets.
0535 *
0536 * @return true if object is visible; otherwise, false
0537 * @see #setVisible
0538 * @see AccessibleContext#getAccessibleStateSet
0539 * @see AccessibleState#VISIBLE
0540 * @see AccessibleStateSet
0541 */
0542 public boolean isVisible() {
0543 return getTextComponent().isVisible();
0544 }
0545
0546 /**
0547 * Sets the visible state of the object.
0548 *
0549 * @param b if true, shows this object; otherwise, hides it
0550 * @see #isVisible
0551 */
0552 public void setVisible(boolean b) {
0553 getTextComponent().setVisible(b);
0554 }
0555
0556 /**
0557 * Determines if the object is showing. This is determined by checking
0558 * the visibility of the object and its ancestors.
0559 * Note: this
0560 * will return true even if the object is obscured by another (for
0561 * example, it is underneath a menu that was pulled down).
0562 *
0563 * @return true if object is showing; otherwise, false
0564 */
0565 public boolean isShowing() {
0566 return getTextComponent().isShowing();
0567 }
0568
0569 /**
0570 * Checks whether the specified point is within this object's bounds,
0571 * where the point's x and y coordinates are defined to be relative
0572 * to the coordinate system of the object.
0573 *
0574 * @param p the Point relative to the coordinate system of the object
0575 * @return true if object contains Point; otherwise false
0576 * @see #getBounds
0577 */
0578 public boolean contains(Point p) {
0579 Rectangle r = getBounds();
0580 if (r != null) {
0581 return r.contains(p.x, p.y);
0582 } else {
0583 return false;
0584 }
0585 }
0586
0587 /**
0588 * Returns the location of the object on the screen.
0589 *
0590 * @return the location of the object on screen; null if this object
0591 * is not on the screen
0592 * @see #getBounds
0593 * @see #getLocation
0594 */
0595 public Point getLocationOnScreen() {
0596 Point editorLocation = getTextComponent()
0597 .getLocationOnScreen();
0598 Rectangle r = getBounds();
0599 if (r != null) {
0600 return new Point(editorLocation.x + r.x,
0601 editorLocation.y + r.y);
0602 } else {
0603 return null;
0604 }
0605 }
0606
0607 /**
0608 * Gets the location of the object relative to the parent in the form
0609 * of a point specifying the object's top-left corner in the screen's
0610 * coordinate space.
0611 *
0612 * @return An instance of Point representing the top-left corner of the
0613 * object's bounds in the coordinate space of the screen; null if
0614 * this object or its parent are not on the screen
0615 * @see #getBounds
0616 * @see #getLocationOnScreen
0617 */
0618 public Point getLocation() {
0619 Rectangle r = getBounds();
0620 if (r != null) {
0621 return new Point(r.x, r.y);
0622 } else {
0623 return null;
0624 }
0625 }
0626
0627 /**
0628 * Sets the location of the object relative to the parent.
0629 * @param p the new position for the top-left corner
0630 * @see #getLocation
0631 */
0632 public void setLocation(Point p) {
0633 }
0634
0635 /**
0636 * Gets the bounds of this object in the form of a Rectangle object.
0637 * The bounds specify this object's width, height, and location
0638 * relative to its parent.
0639 *
0640 * @return A rectangle indicating this component's bounds; null if
0641 * this object is not on the screen.
0642 * @see #contains
0643 */
0644 public Rectangle getBounds() {
0645 return elementInfo.getBounds();
0646 }
0647
0648 /**
0649 * Sets the bounds of this object in the form of a Rectangle object.
0650 * The bounds specify this object's width, height, and location
0651 * relative to its parent.
0652 *
0653 * @param r rectangle indicating this component's bounds
0654 * @see #getBounds
0655 */
0656 public void setBounds(Rectangle r) {
0657 }
0658
0659 /**
0660 * Returns the size of this object in the form of a Dimension object.
0661 * The height field of the Dimension object contains this object's
0662 * height, and the width field of the Dimension object contains this
0663 * object's width.
0664 *
0665 * @return A Dimension object that indicates the size of this component;
0666 * null if this object is not on the screen
0667 * @see #setSize
0668 */
0669 public Dimension getSize() {
0670 Rectangle r = getBounds();
0671 if (r != null) {
0672 return new Dimension(r.width, r.height);
0673 } else {
0674 return null;
0675 }
0676 }
0677
0678 /**
0679 * Resizes this object so that it has width and height.
0680 *
0681 * @param d The dimension specifying the new size of the object.
0682 * @see #getSize
0683 */
0684 public void setSize(Dimension d) {
0685 Component comp = getTextComponent();
0686 comp.setSize(d);
0687 }
0688
0689 /**
0690 * Returns the Accessible child, if one exists, contained at the local
0691 * coordinate Point.
0692 *
0693 * @param p The point relative to the coordinate system of this object.
0694 * @return the Accessible, if it exists, at the specified location;
0695 * otherwise null
0696 */
0697 public Accessible getAccessibleAt(Point p) {
0698 ElementInfo innerMostElement = getElementInfoAt(
0699 rootElementInfo, p);
0700 if (innerMostElement instanceof Accessible) {
0701 return (Accessible) innerMostElement;
0702 } else {
0703 return null;
0704 }
0705 }
0706
0707 private ElementInfo getElementInfoAt(ElementInfo elementInfo,
0708 Point p) {
0709 if (elementInfo.getBounds() == null) {
0710 return null;
0711 }
0712 if (elementInfo.getChildCount() == 0
0713 && elementInfo.getBounds().contains(p)) {
0714 return elementInfo;
0715
0716 } else {
0717 if (elementInfo instanceof TableElementInfo) {
0718 // Handle table caption as a special case since it's the
0719 // only table child that is not a table row.
0720 ElementInfo captionInfo = ((TableElementInfo) elementInfo)
0721 .getCaptionInfo();
0722 if (captionInfo != null) {
0723 Rectangle bounds = captionInfo.getBounds();
0724 if (bounds != null && bounds.contains(p)) {
0725 return captionInfo;
0726 }
0727 }
0728 }
0729 for (int i = 0; i < elementInfo.getChildCount(); i++) {
0730 ElementInfo childInfo = elementInfo.getChild(i);
0731 ElementInfo retValue = getElementInfoAt(childInfo,
0732 p);
0733 if (retValue != null) {
0734 return retValue;
0735 }
0736 }
0737 }
0738 return null;
0739 }
0740
0741 /**
0742 * Returns whether this object can accept focus or not. Objects that
0743 * can accept focus will also have the AccessibleState.FOCUSABLE state
0744 * set in their AccessibleStateSets.
0745 *
0746 * @return true if object can accept focus; otherwise false
0747 * @see AccessibleContext#getAccessibleStateSet
0748 * @see AccessibleState#FOCUSABLE
0749 * @see AccessibleState#FOCUSED
0750 * @see AccessibleStateSet
0751 */
0752 public boolean isFocusTraversable() {
0753 Component comp = getTextComponent();
0754 if (comp instanceof JTextComponent) {
0755 if (((JTextComponent) comp).isEditable()) {
0756 return true;
0757 }
0758 }
0759 return false;
0760 }
0761
0762 /**
0763 * Requests focus for this object. If this object cannot accept focus,
0764 * nothing will happen. Otherwise, the object will attempt to take
0765 * focus.
0766 * @see #isFocusTraversable
0767 */
0768 public void requestFocus() {
0769 // TIGER - 4856191
0770 if (!isFocusTraversable()) {
0771 return;
0772 }
0773
0774 Component comp = getTextComponent();
0775 if (comp instanceof JTextComponent) {
0776
0777 comp.requestFocusInWindow();
0778
0779 try {
0780 if (elementInfo.validateIfNecessary()) {
0781 // set the caret position to the start of this component
0782 Element elem = elementInfo.getElement();
0783 ((JTextComponent) comp).setCaretPosition(elem
0784 .getStartOffset());
0785
0786 // fire a AccessibleState.FOCUSED property change event
0787 AccessibleContext ac = editor
0788 .getAccessibleContext();
0789 PropertyChangeEvent pce = new PropertyChangeEvent(
0790 this ,
0791 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
0792 null, AccessibleState.FOCUSED);
0793 ac
0794 .firePropertyChange(
0795 AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
0796 null, pce);
0797 }
0798 } catch (IllegalArgumentException e) {
0799 // don't fire property change event
0800 }
0801 }
0802 }
0803
0804 /**
0805 * Adds the specified focus listener to receive focus events from this
0806 * component.
0807 *
0808 * @param l the focus listener
0809 * @see #removeFocusListener
0810 */
0811 public void addFocusListener(FocusListener l) {
0812 getTextComponent().addFocusListener(l);
0813 }
0814
0815 /**
0816 * Removes the specified focus listener so it no longer receives focus
0817 * events from this component.
0818 *
0819 * @param l the focus listener
0820 * @see #addFocusListener
0821 */
0822 public void removeFocusListener(FocusListener l) {
0823 getTextComponent().removeFocusListener(l);
0824 }
0825 // ... end AccessibleComponent implementation
0826 } // ... end HTMLAccessibleContext
0827
0828 /*
0829 * ElementInfo for text
0830 */
0831 class TextElementInfo extends ElementInfo implements Accessible {
0832
0833 TextElementInfo(Element element, ElementInfo parent) {
0834 super (element, parent);
0835 }
0836
0837 // begin AccessibleText implementation ...
0838 private AccessibleContext accessibleContext;
0839
0840 public AccessibleContext getAccessibleContext() {
0841 if (accessibleContext == null) {
0842 accessibleContext = new TextAccessibleContext(this );
0843 }
0844 return accessibleContext;
0845 }
0846
0847 /*
0848 * AccessibleContext for text elements
0849 */
0850 public class TextAccessibleContext extends
0851 HTMLAccessibleContext implements AccessibleText {
0852
0853 public TextAccessibleContext(ElementInfo elementInfo) {
0854 super (elementInfo);
0855 }
0856
0857 public AccessibleText getAccessibleText() {
0858 return this ;
0859 }
0860
0861 /**
0862 * Gets the accessibleName property of this object. The accessibleName
0863 * property of an object is a localized String that designates the purpose
0864 * of the object. For example, the accessibleName property of a label
0865 * or button might be the text of the label or button itself. In the
0866 * case of an object that doesn't display its name, the accessibleName
0867 * should still be set. For example, in the case of a text field used
0868 * to enter the name of a city, the accessibleName for the en_US locale
0869 * could be 'city.'
0870 *
0871 * @return the localized name of the object; null if this
0872 * object does not have a name
0873 *
0874 * @see #setAccessibleName
0875 */
0876 public String getAccessibleName() {
0877 if (model != null) {
0878 return (String) model
0879 .getProperty(Document.TitleProperty);
0880 } else {
0881 return null;
0882 }
0883 }
0884
0885 /**
0886 * Gets the accessibleDescription property of this object. If this
0887 * property isn't set, returns the content type of this
0888 * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
0889 *
0890 * @return the localized description of the object; <code>null</code>
0891 * if this object does not have a description
0892 *
0893 * @see #setAccessibleName
0894 */
0895 public String getAccessibleDescription() {
0896 return editor.getContentType();
0897 }
0898
0899 /**
0900 * Gets the role of this object. The role of the object is the generic
0901 * purpose or use of the class of this object. For example, the role
0902 * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
0903 * AccessibleRole are provided so component developers can pick from
0904 * a set of predefined roles. This enables assistive technologies to
0905 * provide a consistent interface to various tweaked subclasses of
0906 * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
0907 * that act like a push button) as well as distinguish between sublasses
0908 * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
0909 * and AccessibleRole.RADIO_BUTTON for radio buttons).
0910 * <p>Note that the AccessibleRole class is also extensible, so
0911 * custom component developers can define their own AccessibleRole's
0912 * if the set of predefined roles is inadequate.
0913 *
0914 * @return an instance of AccessibleRole describing the role of the object
0915 * @see AccessibleRole
0916 */
0917 public AccessibleRole getAccessibleRole() {
0918 return AccessibleRole.TEXT;
0919 }
0920
0921 /**
0922 * Given a point in local coordinates, return the zero-based index
0923 * of the character under that Point. If the point is invalid,
0924 * this method returns -1.
0925 *
0926 * @param p the Point in local coordinates
0927 * @return the zero-based index of the character under Point p; if
0928 * Point is invalid returns -1.
0929 */
0930 public int getIndexAtPoint(Point p) {
0931 View v = getView();
0932 if (v != null) {
0933 return v.viewToModel(p.x, p.y, getBounds());
0934 } else {
0935 return -1;
0936 }
0937 }
0938
0939 /**
0940 * Determine the bounding box of the character at the given
0941 * index into the string. The bounds are returned in local
0942 * coordinates. If the index is invalid an empty rectangle is
0943 * returned.
0944 *
0945 * @param i the index into the String
0946 * @return the screen coordinates of the character's the bounding box,
0947 * if index is invalid returns an empty rectangle.
0948 */
0949 public Rectangle getCharacterBounds(int i) {
0950 try {
0951 return editor.getUI().modelToView(editor, i);
0952 } catch (BadLocationException e) {
0953 return null;
0954 }
0955 }
0956
0957 /**
0958 * Return the number of characters (valid indicies)
0959 *
0960 * @return the number of characters
0961 */
0962 public int getCharCount() {
0963 if (validateIfNecessary()) {
0964 Element elem = elementInfo.getElement();
0965 return elem.getEndOffset() - elem.getStartOffset();
0966 }
0967 return 0;
0968 }
0969
0970 /**
0971 * Return the zero-based offset of the caret.
0972 *
0973 * Note: That to the right of the caret will have the same index
0974 * value as the offset (the caret is between two characters).
0975 * @return the zero-based offset of the caret.
0976 */
0977 public int getCaretPosition() {
0978 View v = getView();
0979 if (v == null) {
0980 return -1;
0981 }
0982 Container c = v.getContainer();
0983 if (c == null) {
0984 return -1;
0985 }
0986 if (c instanceof JTextComponent) {
0987 return ((JTextComponent) c).getCaretPosition();
0988 } else {
0989 return -1;
0990 }
0991 }
0992
0993 /**
0994 * IndexedSegment extends Segment adding the offset into the
0995 * the model the <code>Segment</code> was asked for.
0996 */
0997 private class IndexedSegment extends Segment {
0998 /**
0999 * Offset into the model that the position represents.
1000 */
1001 public int modelOffset;
1002 }
1003
1004 public String getAtIndex(int part, int index) {
1005 return getAtIndex(part, index, 0);
1006 }
1007
1008 public String getAfterIndex(int part, int index) {
1009 return getAtIndex(part, index, 1);
1010 }
1011
1012 public String getBeforeIndex(int part, int index) {
1013 return getAtIndex(part, index, -1);
1014 }
1015
1016 /**
1017 * Gets the word, sentence, or character at <code>index</code>.
1018 * If <code>direction</code> is non-null this will find the
1019 * next/previous word/sentence/character.
1020 */
1021 private String getAtIndex(int part, int index, int direction) {
1022 if (model instanceof AbstractDocument) {
1023 ((AbstractDocument) model).readLock();
1024 }
1025 try {
1026 if (index < 0 || index >= model.getLength()) {
1027 return null;
1028 }
1029 switch (part) {
1030 case AccessibleText.CHARACTER:
1031 if (index + direction < model.getLength()
1032 && index + direction >= 0) {
1033 return model.getText(index + direction, 1);
1034 }
1035 break;
1036
1037 case AccessibleText.WORD:
1038 case AccessibleText.SENTENCE:
1039 IndexedSegment seg = getSegmentAt(part, index);
1040 if (seg != null) {
1041 if (direction != 0) {
1042 int next;
1043
1044 if (direction < 0) {
1045 next = seg.modelOffset - 1;
1046 } else {
1047 next = seg.modelOffset + direction
1048 * seg.count;
1049 }
1050 if (next >= 0
1051 && next <= model.getLength()) {
1052 seg = getSegmentAt(part, next);
1053 } else {
1054 seg = null;
1055 }
1056 }
1057 if (seg != null) {
1058 return new String(seg.array,
1059 seg.offset, seg.count);
1060 }
1061 }
1062 break;
1063
1064 default:
1065 break;
1066 }
1067 } catch (BadLocationException e) {
1068 } finally {
1069 if (model instanceof AbstractDocument) {
1070 ((AbstractDocument) model).readUnlock();
1071 }
1072 }
1073 return null;
1074 }
1075
1076 /*
1077 * Returns the paragraph element for the specified index.
1078 */
1079 private Element getParagraphElement(int index) {
1080 if (model instanceof PlainDocument) {
1081 PlainDocument sdoc = (PlainDocument) model;
1082 return sdoc.getParagraphElement(index);
1083 } else if (model instanceof StyledDocument) {
1084 StyledDocument sdoc = (StyledDocument) model;
1085 return sdoc.getParagraphElement(index);
1086 } else {
1087 Element para = null;
1088 for (para = model.getDefaultRootElement(); !para
1089 .isLeaf();) {
1090 int pos = para.getElementIndex(index);
1091 para = para.getElement(pos);
1092 }
1093 if (para == null) {
1094 return null;
1095 }
1096 return para.getParentElement();
1097 }
1098 }
1099
1100 /*
1101 * Returns a <code>Segment</code> containing the paragraph text
1102 * at <code>index</code>, or null if <code>index</code> isn't
1103 * valid.
1104 */
1105 private IndexedSegment getParagraphElementText(int index)
1106 throws BadLocationException {
1107 Element para = getParagraphElement(index);
1108
1109 if (para != null) {
1110 IndexedSegment segment = new IndexedSegment();
1111 try {
1112 int length = para.getEndOffset()
1113 - para.getStartOffset();
1114 model.getText(para.getStartOffset(), length,
1115 segment);
1116 } catch (BadLocationException e) {
1117 return null;
1118 }
1119 segment.modelOffset = para.getStartOffset();
1120 return segment;
1121 }
1122 return null;
1123 }
1124
1125 /**
1126 * Returns the Segment at <code>index</code> representing either
1127 * the paragraph or sentence as identified by <code>part</code>, or
1128 * null if a valid paragraph/sentence can't be found. The offset
1129 * will point to the start of the word/sentence in the array, and
1130 * the modelOffset will point to the location of the word/sentence
1131 * in the model.
1132 */
1133 private IndexedSegment getSegmentAt(int part, int index)
1134 throws BadLocationException {
1135
1136 IndexedSegment seg = getParagraphElementText(index);
1137 if (seg == null) {
1138 return null;
1139 }
1140 BreakIterator iterator;
1141 switch (part) {
1142 case AccessibleText.WORD:
1143 iterator = BreakIterator
1144 .getWordInstance(getLocale());
1145 break;
1146 case AccessibleText.SENTENCE:
1147 iterator = BreakIterator
1148 .getSentenceInstance(getLocale());
1149 break;
1150 default:
1151 return null;
1152 }
1153 seg.first();
1154 iterator.setText(seg);
1155 int end = iterator.following(index - seg.modelOffset
1156 + seg.offset);
1157 if (end == BreakIterator.DONE) {
1158 return null;
1159 }
1160 if (end > seg.offset + seg.count) {
1161 return null;
1162 }
1163 int begin = iterator.previous();
1164 if (begin == BreakIterator.DONE
1165 || begin >= seg.offset + seg.count) {
1166 return null;
1167 }
1168 seg.modelOffset = seg.modelOffset + begin - seg.offset;
1169 seg.offset = begin;
1170 seg.count = end - begin;
1171 return seg;
1172 }
1173
1174 /**
1175 * Return the AttributeSet for a given character at a given index
1176 *
1177 * @param i the zero-based index into the text
1178 * @return the AttributeSet of the character
1179 */
1180 public AttributeSet getCharacterAttribute(int i) {
1181 if (model instanceof StyledDocument) {
1182 StyledDocument doc = (StyledDocument) model;
1183 Element elem = doc.getCharacterElement(i);
1184 if (elem != null) {
1185 return elem.getAttributes();
1186 }
1187 }
1188 return null;
1189 }
1190
1191 /**
1192 * Returns the start offset within the selected text.
1193 * If there is no selection, but there is
1194 * a caret, the start and end offsets will be the same.
1195 *
1196 * @return the index into the text of the start of the selection
1197 */
1198 public int getSelectionStart() {
1199 return editor.getSelectionStart();
1200 }
1201
1202 /**
1203 * Returns the end offset within the selected text.
1204 * If there is no selection, but there is
1205 * a caret, the start and end offsets will be the same.
1206 *
1207 * @return the index into teh text of the end of the selection
1208 */
1209 public int getSelectionEnd() {
1210 return editor.getSelectionEnd();
1211 }
1212
1213 /**
1214 * Returns the portion of the text that is selected.
1215 *
1216 * @return the String portion of the text that is selected
1217 */
1218 public String getSelectedText() {
1219 return editor.getSelectedText();
1220 }
1221
1222 /*
1223 * Returns the text substring starting at the specified
1224 * offset with the specified length.
1225 */
1226 private String getText(int offset, int length)
1227 throws BadLocationException {
1228
1229 if (model != null && model instanceof StyledDocument) {
1230 StyledDocument doc = (StyledDocument) model;
1231 return model.getText(offset, length);
1232 } else {
1233 return null;
1234 }
1235 }
1236 }
1237 }
1238
1239 /*
1240 * ElementInfo for images
1241 */
1242 private class IconElementInfo extends ElementInfo implements
1243 Accessible {
1244
1245 private int width = -1;
1246 private int height = -1;
1247
1248 IconElementInfo(Element element, ElementInfo parent) {
1249 super (element, parent);
1250 }
1251
1252 protected void invalidate(boolean first) {
1253 super .invalidate(first);
1254 width = height = -1;
1255 }
1256
1257 private int getImageSize(Object key) {
1258 if (validateIfNecessary()) {
1259 int size = getIntAttr(getAttributes(), key, -1);
1260
1261 if (size == -1) {
1262 View v = getView();
1263
1264 size = 0;
1265 if (v instanceof ImageView) {
1266 Image img = ((ImageView) v).getImage();
1267 if (img != null) {
1268 if (key == HTML.Attribute.WIDTH) {
1269 size = img.getWidth(null);
1270 } else {
1271 size = img.getHeight(null);
1272 }
1273 }
1274 }
1275 }
1276 return size;
1277 }
1278 return 0;
1279 }
1280
1281 // begin AccessibleIcon implementation ...
1282 private AccessibleContext accessibleContext;
1283
1284 public AccessibleContext getAccessibleContext() {
1285 if (accessibleContext == null) {
1286 accessibleContext = new IconAccessibleContext(this );
1287 }
1288 return accessibleContext;
1289 }
1290
1291 /*
1292 * AccessibleContext for images
1293 */
1294 protected class IconAccessibleContext extends
1295 HTMLAccessibleContext implements AccessibleIcon {
1296
1297 public IconAccessibleContext(ElementInfo elementInfo) {
1298 super (elementInfo);
1299 }
1300
1301 /**
1302 * Gets the accessibleName property of this object. The accessibleName
1303 * property of an object is a localized String that designates the purpose
1304 * of the object. For example, the accessibleName property of a label
1305 * or button might be the text of the label or button itself. In the
1306 * case of an object that doesn't display its name, the accessibleName
1307 * should still be set. For example, in the case of a text field used
1308 * to enter the name of a city, the accessibleName for the en_US locale
1309 * could be 'city.'
1310 *
1311 * @return the localized name of the object; null if this
1312 * object does not have a name
1313 *
1314 * @see #setAccessibleName
1315 */
1316 public String getAccessibleName() {
1317 return getAccessibleIconDescription();
1318 }
1319
1320 /**
1321 * Gets the accessibleDescription property of this object. If this
1322 * property isn't set, returns the content type of this
1323 * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
1324 *
1325 * @return the localized description of the object; <code>null</code>
1326 * if this object does not have a description
1327 *
1328 * @see #setAccessibleName
1329 */
1330 public String getAccessibleDescription() {
1331 return editor.getContentType();
1332 }
1333
1334 /**
1335 * Gets the role of this object. The role of the object is the generic
1336 * purpose or use of the class of this object. For example, the role
1337 * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
1338 * AccessibleRole are provided so component developers can pick from
1339 * a set of predefined roles. This enables assistive technologies to
1340 * provide a consistent interface to various tweaked subclasses of
1341 * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
1342 * that act like a push button) as well as distinguish between sublasses
1343 * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
1344 * and AccessibleRole.RADIO_BUTTON for radio buttons).
1345 * <p>Note that the AccessibleRole class is also extensible, so
1346 * custom component developers can define their own AccessibleRole's
1347 * if the set of predefined roles is inadequate.
1348 *
1349 * @return an instance of AccessibleRole describing the role of the object
1350 * @see AccessibleRole
1351 */
1352 public AccessibleRole getAccessibleRole() {
1353 return AccessibleRole.ICON;
1354 }
1355
1356 public AccessibleIcon[] getAccessibleIcon() {
1357 AccessibleIcon[] icons = new AccessibleIcon[1];
1358 icons[0] = this ;
1359 return icons;
1360 }
1361
1362 /**
1363 * Gets the description of the icon. This is meant to be a brief
1364 * textual description of the object. For example, it might be
1365 * presented to a blind user to give an indication of the purpose
1366 * of the icon.
1367 *
1368 * @return the description of the icon
1369 */
1370 public String getAccessibleIconDescription() {
1371 return ((ImageView) getView()).getAltText();
1372 }
1373
1374 /**
1375 * Sets the description of the icon. This is meant to be a brief
1376 * textual description of the object. For example, it might be
1377 * presented to a blind user to give an indication of the purpose
1378 * of the icon.
1379 *
1380 * @param description the description of the icon
1381 */
1382 public void setAccessibleIconDescription(String description) {
1383 }
1384
1385 /**
1386 * Gets the width of the icon
1387 *
1388 * @return the width of the icon.
1389 */
1390 public int getAccessibleIconWidth() {
1391 if (width == -1) {
1392 width = getImageSize(HTML.Attribute.WIDTH);
1393 }
1394 return width;
1395 }
1396
1397 /**
1398 * Gets the height of the icon
1399 *
1400 * @return the height of the icon.
1401 */
1402 public int getAccessibleIconHeight() {
1403 if (height == -1) {
1404 height = getImageSize(HTML.Attribute.HEIGHT);
1405 }
1406 return height;
1407 }
1408 }
1409 // ... end AccessibleIconImplementation
1410 }
1411
1412 /**
1413 * TableElementInfo encapsulates information about a HTML.Tag.TABLE.
1414 * To make access fast it crates a grid containing the children to
1415 * allow for access by row, column. TableElementInfo will contain
1416 * TableRowElementInfos, which will contain TableCellElementInfos.
1417 * Any time one of the rows or columns becomes invalid the table is
1418 * invalidated. This is because any time one of the child attributes
1419 * changes the size of the grid may have changed.
1420 */
1421 private class TableElementInfo extends ElementInfo implements
1422 Accessible {
1423
1424 protected ElementInfo caption;
1425
1426 /**
1427 * Allocation of the table by row x column. There may be holes (eg
1428 * nulls) depending upon the html, any cell that has a rowspan/colspan
1429 * > 1 will be contained multiple times in the grid.
1430 */
1431 private TableCellElementInfo[][] grid;
1432
1433 TableElementInfo(Element e, ElementInfo parent) {
1434 super (e, parent);
1435 }
1436
1437 public ElementInfo getCaptionInfo() {
1438 return caption;
1439 }
1440
1441 /**
1442 * Overriden to update the grid when validating.
1443 */
1444 protected void validate() {
1445 super .validate();
1446 updateGrid();
1447 }
1448
1449 /**
1450 * Overriden to only alloc instances of TableRowElementInfos.
1451 */
1452 protected void loadChildren(Element e) {
1453
1454 for (int counter = 0; counter < e.getElementCount(); counter++) {
1455 Element child = e.getElement(counter);
1456 AttributeSet attrs = child.getAttributes();
1457
1458 if (attrs.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.TR) {
1459 addChild(new TableRowElementInfo(child, this ,
1460 counter));
1461
1462 } else if (attrs
1463 .getAttribute(StyleConstants.NameAttribute) == HTML.Tag.CAPTION) {
1464 // Handle captions as a special case since all other
1465 // children are table rows.
1466 caption = createElementInfo(child, this );
1467 }
1468 }
1469 }
1470
1471 /**
1472 * Updates the grid.
1473 */
1474 private void updateGrid() {
1475 // Determine the max row/col count.
1476 int delta = 0;
1477 int maxCols = 0;
1478 int rows = 0;
1479 for (int counter = 0; counter < getChildCount(); counter++) {
1480 TableRowElementInfo row = getRow(counter);
1481 int prev = 0;
1482 for (int y = 0; y < delta; y++) {
1483 prev = Math.max(prev, getRow(counter - y - 1)
1484 .getColumnCount(y + 2));
1485 }
1486 delta = Math.max(row.getRowCount(), delta);
1487 delta--;
1488 maxCols = Math
1489 .max(maxCols, row.getColumnCount() + prev);
1490 }
1491 rows = getChildCount() + delta;
1492
1493 // Alloc
1494 grid = new TableCellElementInfo[rows][];
1495 for (int counter = 0; counter < rows; counter++) {
1496 grid[counter] = new TableCellElementInfo[maxCols];
1497 }
1498 // Update
1499 for (int counter = 0; counter < rows; counter++) {
1500 getRow(counter).updateGrid(counter);
1501 }
1502 }
1503
1504 /**
1505 * Returns the TableCellElementInfo at the specified index.
1506 */
1507 public TableRowElementInfo getRow(int index) {
1508 return (TableRowElementInfo) getChild(index);
1509 }
1510
1511 /**
1512 * Returns the TableCellElementInfo by row and column.
1513 */
1514 public TableCellElementInfo getCell(int r, int c) {
1515 if (validateIfNecessary() && r < grid.length
1516 && c < grid[0].length) {
1517 return grid[r][c];
1518 }
1519 return null;
1520 }
1521
1522 /**
1523 * Returns the rowspan of the specified entry.
1524 */
1525 public int getRowExtentAt(int r, int c) {
1526 TableCellElementInfo cell = getCell(r, c);
1527
1528 if (cell != null) {
1529 int rows = cell.getRowCount();
1530 int delta = 1;
1531
1532 while ((r - delta) >= 0 && grid[r - delta][c] == cell) {
1533 delta++;
1534 }
1535 return rows - delta + 1;
1536 }
1537 return 0;
1538 }
1539
1540 /**
1541 * Returns the colspan of the specified entry.
1542 */
1543 public int getColumnExtentAt(int r, int c) {
1544 TableCellElementInfo cell = getCell(r, c);
1545
1546 if (cell != null) {
1547 int cols = cell.getColumnCount();
1548 int delta = 1;
1549
1550 while ((c - delta) >= 0 && grid[r][c - delta] == cell) {
1551 delta++;
1552 }
1553 return cols - delta + 1;
1554 }
1555 return 0;
1556 }
1557
1558 /**
1559 * Returns the number of rows in the table.
1560 */
1561 public int getRowCount() {
1562 if (validateIfNecessary()) {
1563 return grid.length;
1564 }
1565 return 0;
1566 }
1567
1568 /**
1569 * Returns the number of columns in the table.
1570 */
1571 public int getColumnCount() {
1572 if (validateIfNecessary() && grid.length > 0) {
1573 return grid[0].length;
1574 }
1575 return 0;
1576 }
1577
1578 // begin AccessibleTable implementation ...
1579 private AccessibleContext accessibleContext;
1580
1581 public AccessibleContext getAccessibleContext() {
1582 if (accessibleContext == null) {
1583 accessibleContext = new TableAccessibleContext(this );
1584 }
1585 return accessibleContext;
1586 }
1587
1588 /*
1589 * AccessibleContext for tables
1590 */
1591 public class TableAccessibleContext extends
1592 HTMLAccessibleContext implements AccessibleTable {
1593
1594 private AccessibleHeadersTable rowHeadersTable;
1595
1596 public TableAccessibleContext(ElementInfo elementInfo) {
1597 super (elementInfo);
1598 }
1599
1600 /**
1601 * Gets the accessibleName property of this object. The accessibleName
1602 * property of an object is a localized String that designates the purpose
1603 * of the object. For example, the accessibleName property of a label
1604 * or button might be the text of the label or button itself. In the
1605 * case of an object that doesn't display its name, the accessibleName
1606 * should still be set. For example, in the case of a text field used
1607 * to enter the name of a city, the accessibleName for the en_US locale
1608 * could be 'city.'
1609 *
1610 * @return the localized name of the object; null if this
1611 * object does not have a name
1612 *
1613 * @see #setAccessibleName
1614 */
1615 public String getAccessibleName() {
1616 // return the role of the object
1617 return getAccessibleRole().toString();
1618 }
1619
1620 /**
1621 * Gets the accessibleDescription property of this object. If this
1622 * property isn't set, returns the content type of this
1623 * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
1624 *
1625 * @return the localized description of the object; <code>null</code>
1626 * if this object does not have a description
1627 *
1628 * @see #setAccessibleName
1629 */
1630 public String getAccessibleDescription() {
1631 return editor.getContentType();
1632 }
1633
1634 /**
1635 * Gets the role of this object. The role of the object is the generic
1636 * purpose or use of the class of this object. For example, the role
1637 * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
1638 * AccessibleRole are provided so component developers can pick from
1639 * a set of predefined roles. This enables assistive technologies to
1640 * provide a consistent interface to various tweaked subclasses of
1641 * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
1642 * that act like a push button) as well as distinguish between sublasses
1643 * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
1644 * and AccessibleRole.RADIO_BUTTON for radio buttons).
1645 * <p>Note that the AccessibleRole class is also extensible, so
1646 * custom component developers can define their own AccessibleRole's
1647 * if the set of predefined roles is inadequate.
1648 *
1649 * @return an instance of AccessibleRole describing the role of the object
1650 * @see AccessibleRole
1651 */
1652 public AccessibleRole getAccessibleRole() {
1653 return AccessibleRole.TABLE;
1654 }
1655
1656 /**
1657 * Gets the 0-based index of this object in its accessible parent.
1658 *
1659 * @return the 0-based index of this object in its parent; -1 if this
1660 * object does not have an accessible parent.
1661 *
1662 * @see #getAccessibleParent
1663 * @see #getAccessibleChildrenCount
1664 * @gsee #getAccessibleChild
1665 */
1666 public int getAccessibleIndexInParent() {
1667 return elementInfo.getIndexInParent();
1668 }
1669
1670 /**
1671 * Returns the number of accessible children of the object.
1672 *
1673 * @return the number of accessible children of the object.
1674 */
1675 public int getAccessibleChildrenCount() {
1676 return ((TableElementInfo) elementInfo).getRowCount()
1677 * ((TableElementInfo) elementInfo)
1678 .getColumnCount();
1679 }
1680
1681 /**
1682 * Returns the specified Accessible child of the object. The Accessible
1683 * children of an Accessible object are zero-based, so the first child
1684 * of an Accessible child is at index 0, the second child is at index 1,
1685 * and so on.
1686 *
1687 * @param i zero-based index of child
1688 * @return the Accessible child of the object
1689 * @see #getAccessibleChildrenCount
1690 */
1691 public Accessible getAccessibleChild(int i) {
1692 int rowCount = ((TableElementInfo) elementInfo)
1693 .getRowCount();
1694 int columnCount = ((TableElementInfo) elementInfo)
1695 .getColumnCount();
1696 int r = i / rowCount;
1697 int c = i % columnCount;
1698 if (r < 0 || r >= rowCount || c < 0 || c >= columnCount) {
1699 return null;
1700 } else {
1701 return getAccessibleAt(r, c);
1702 }
1703 }
1704
1705 public AccessibleTable getAccessibleTable() {
1706 return this ;
1707 }
1708
1709 /**
1710 * Returns the caption for the table.
1711 *
1712 * @return the caption for the table
1713 */
1714 public Accessible getAccessibleCaption() {
1715 ElementInfo captionInfo = getCaptionInfo();
1716 if (captionInfo instanceof Accessible) {
1717 return (Accessible) caption;
1718 } else {
1719 return null;
1720 }
1721 }
1722
1723 /**
1724 * Sets the caption for the table.
1725 *
1726 * @param a the caption for the table
1727 */
1728 public void setAccessibleCaption(Accessible a) {
1729 }
1730
1731 /**
1732 * Returns the summary description of the table.
1733 *
1734 * @return the summary description of the table
1735 */
1736 public Accessible getAccessibleSummary() {
1737 return null;
1738 }
1739
1740 /**
1741 * Sets the summary description of the table
1742 *
1743 * @param a the summary description of the table
1744 */
1745 public void setAccessibleSummary(Accessible a) {
1746 }
1747
1748 /**
1749 * Returns the number of rows in the table.
1750 *
1751 * @return the number of rows in the table
1752 */
1753 public int getAccessibleRowCount() {
1754 return ((TableElementInfo) elementInfo).getRowCount();
1755 }
1756
1757 /**
1758 * Returns the number of columns in the table.
1759 *
1760 * @return the number of columns in the table
1761 */
1762 public int getAccessibleColumnCount() {
1763 return ((TableElementInfo) elementInfo)
1764 .getColumnCount();
1765 }
1766
1767 /**
1768 * Returns the Accessible at a specified row and column
1769 * in the table.
1770 *
1771 * @param r zero-based row of the table
1772 * @param c zero-based column of the table
1773 * @return the Accessible at the specified row and column
1774 */
1775 public Accessible getAccessibleAt(int r, int c) {
1776 TableCellElementInfo cellInfo = getCell(r, c);
1777 if (cellInfo != null) {
1778 return cellInfo.getAccessible();
1779 } else {
1780 return null;
1781 }
1782 }
1783
1784 /**
1785 * Returns the number of rows occupied by the Accessible at
1786 * a specified row and column in the table.
1787 *
1788 * @return the number of rows occupied by the Accessible at a
1789 * given specified (row, column)
1790 */
1791 public int getAccessibleRowExtentAt(int r, int c) {
1792 return ((TableElementInfo) elementInfo).getRowExtentAt(
1793 r, c);
1794 }
1795
1796 /**
1797 * Returns the number of columns occupied by the Accessible at
1798 * a specified row and column in the table.
1799 *
1800 * @return the number of columns occupied by the Accessible at a
1801 * given specified row and column
1802 */
1803 public int getAccessibleColumnExtentAt(int r, int c) {
1804 return ((TableElementInfo) elementInfo)
1805 .getColumnExtentAt(r, c);
1806 }
1807
1808 /**
1809 * Returns the row headers as an AccessibleTable.
1810 *
1811 * @return an AccessibleTable representing the row
1812 * headers
1813 */
1814 public AccessibleTable getAccessibleRowHeader() {
1815 return rowHeadersTable;
1816 }
1817
1818 /**
1819 * Sets the row headers.
1820 *
1821 * @param table an AccessibleTable representing the
1822 * row headers
1823 */
1824 public void setAccessibleRowHeader(AccessibleTable table) {
1825 }
1826
1827 /**
1828 * Returns the column headers as an AccessibleTable.
1829 *
1830 * @return an AccessibleTable representing the column
1831 * headers
1832 */
1833 public AccessibleTable getAccessibleColumnHeader() {
1834 return null;
1835 }
1836
1837 /**
1838 * Sets the column headers.
1839 *
1840 * @param table an AccessibleTable representing the
1841 * column headers
1842 */
1843 public void setAccessibleColumnHeader(AccessibleTable table) {
1844 }
1845
1846 /**
1847 * Returns the description of the specified row in the table.
1848 *
1849 * @param r zero-based row of the table
1850 * @return the description of the row
1851 */
1852 public Accessible getAccessibleRowDescription(int r) {
1853 return null;
1854 }
1855
1856 /**
1857 * Sets the description text of the specified row of the table.
1858 *
1859 * @param r zero-based row of the table
1860 * @param a the description of the row
1861 */
1862 public void setAccessibleRowDescription(int r, Accessible a) {
1863 }
1864
1865 /**
1866 * Returns the description text of the specified column in the table.
1867 *
1868 * @param c zero-based column of the table
1869 * @return the text description of the column
1870 */
1871 public Accessible getAccessibleColumnDescription(int c) {
1872 return null;
1873 }
1874
1875 /**
1876 * Sets the description text of the specified column in the table.
1877 *
1878 * @param c zero-based column of the table
1879 * @param a the text description of the column
1880 */
1881 public void setAccessibleColumnDescription(int c,
1882 Accessible a) {
1883 }
1884
1885 /**
1886 * Returns a boolean value indicating whether the accessible at
1887 * a specified row and column is selected.
1888 *
1889 * @param r zero-based row of the table
1890 * @param c zero-based column of the table
1891 * @return the boolean value true if the accessible at the
1892 * row and column is selected. Otherwise, the boolean value
1893 * false
1894 */
1895 public boolean isAccessibleSelected(int r, int c) {
1896 if (validateIfNecessary()) {
1897 if (r < 0 || r >= getAccessibleRowCount() || c < 0
1898 || c >= getAccessibleColumnCount()) {
1899 return false;
1900 }
1901 TableCellElementInfo cell = getCell(r, c);
1902 if (cell != null) {
1903 Element elem = cell.getElement();
1904 int start = elem.getStartOffset();
1905 int end = elem.getEndOffset();
1906 return start >= editor.getSelectionStart()
1907 && end <= editor.getSelectionEnd();
1908 }
1909 }
1910 return false;
1911 }
1912
1913 /**
1914 * Returns a boolean value indicating whether the specified row
1915 * is selected.
1916 *
1917 * @param r zero-based row of the table
1918 * @return the boolean value true if the specified row is selected.
1919 * Otherwise, false.
1920 */
1921 public boolean isAccessibleRowSelected(int r) {
1922 if (validateIfNecessary()) {
1923 if (r < 0 || r >= getAccessibleRowCount()) {
1924 return false;
1925 }
1926 int nColumns = getAccessibleColumnCount();
1927
1928 TableCellElementInfo startCell = getCell(r, 0);
1929 if (startCell == null) {
1930 return false;
1931 }
1932 int start = startCell.getElement().getStartOffset();
1933
1934 TableCellElementInfo endCell = getCell(r,
1935 nColumns - 1);
1936 if (endCell == null) {
1937 return false;
1938 }
1939 int end = endCell.getElement().getEndOffset();
1940
1941 return start >= editor.getSelectionStart()
1942 && end <= editor.getSelectionEnd();
1943 }
1944 return false;
1945 }
1946
1947 /**
1948 * Returns a boolean value indicating whether the specified column
1949 * is selected.
1950 *
1951 * @param r zero-based column of the table
1952 * @return the boolean value true if the specified column is selected.
1953 * Otherwise, false.
1954 */
1955 public boolean isAccessibleColumnSelected(int c) {
1956 if (validateIfNecessary()) {
1957 if (c < 0 || c >= getAccessibleColumnCount()) {
1958 return false;
1959 }
1960 int nRows = getAccessibleRowCount();
1961
1962 TableCellElementInfo startCell = getCell(0, c);
1963 if (startCell == null) {
1964 return false;
1965 }
1966 int start = startCell.getElement().getStartOffset();
1967
1968 TableCellElementInfo endCell = getCell(nRows - 1, c);
1969 if (endCell == null) {
1970 return false;
1971 }
1972 int end = endCell.getElement().getEndOffset();
1973 return start >= editor.getSelectionStart()
1974 && end <= editor.getSelectionEnd();
1975 }
1976 return false;
1977 }
1978
1979 /**
1980 * Returns the selected rows in a table.
1981 *
1982 * @return an array of selected rows where each element is a
1983 * zero-based row of the table
1984 */
1985 public int[] getSelectedAccessibleRows() {
1986 if (validateIfNecessary()) {
1987 int nRows = getAccessibleRowCount();
1988 Vector vec = new Vector();
1989
1990 for (int i = 0; i < nRows; i++) {
1991 if (isAccessibleRowSelected(i)) {
1992 vec.addElement(new Integer(i));
1993 }
1994 }
1995 int retval[] = new int[vec.size()];
1996 for (int i = 0; i < retval.length; i++) {
1997 retval[i] = ((Integer) vec.elementAt(i))
1998 .intValue();
1999 }
2000 return retval;
2001 }
2002 return new int[0];
2003 }
2004
2005 /**
2006 * Returns the selected columns in a table.
2007 *
2008 * @return an array of selected columns where each element is a
2009 * zero-based column of the table
2010 */
2011 public int[] getSelectedAccessibleColumns() {
2012 if (validateIfNecessary()) {
2013 int nColumns = getAccessibleRowCount();
2014 Vector vec = new Vector();
2015
2016 for (int i = 0; i < nColumns; i++) {
2017 if (isAccessibleColumnSelected(i)) {
2018 vec.addElement(new Integer(i));
2019 }
2020 }
2021 int retval[] = new int[vec.size()];
2022 for (int i = 0; i < retval.length; i++) {
2023 retval[i] = ((Integer) vec.elementAt(i))
2024 .intValue();
2025 }
2026 return retval;
2027 }
2028 return new int[0];
2029 }
2030
2031 // begin AccessibleExtendedTable implementation -------------
2032
2033 /**
2034 * Returns the row number of an index in the table.
2035 *
2036 * @param index the zero-based index in the table
2037 * @return the zero-based row of the table if one exists;
2038 * otherwise -1.
2039 */
2040 public int getAccessibleRow(int index) {
2041 if (validateIfNecessary()) {
2042 int numCells = getAccessibleColumnCount()
2043 * getAccessibleRowCount();
2044 if (index >= numCells) {
2045 return -1;
2046 } else {
2047 return index / getAccessibleColumnCount();
2048 }
2049 }
2050 return -1;
2051 }
2052
2053 /**
2054 * Returns the column number of an index in the table.
2055 *
2056 * @param index the zero-based index in the table
2057 * @return the zero-based column of the table if one exists;
2058 * otherwise -1.
2059 */
2060 public int getAccessibleColumn(int index) {
2061 if (validateIfNecessary()) {
2062 int numCells = getAccessibleColumnCount()
2063 * getAccessibleRowCount();
2064 if (index >= numCells) {
2065 return -1;
2066 } else {
2067 return index % getAccessibleColumnCount();
2068 }
2069 }
2070 return -1;
2071 }
2072
2073 /**
2074 * Returns the index at a row and column in the table.
2075 *
2076 * @param r zero-based row of the table
2077 * @param c zero-based column of the table
2078 * @return the zero-based index in the table if one exists;
2079 * otherwise -1.
2080 */
2081 public int getAccessibleIndex(int r, int c) {
2082 if (validateIfNecessary()) {
2083 if (r >= getAccessibleRowCount()
2084 || c >= getAccessibleColumnCount()) {
2085 return -1;
2086 } else {
2087 return r * getAccessibleColumnCount() + c;
2088 }
2089 }
2090 return -1;
2091 }
2092
2093 /**
2094 * Returns the row header at a row in a table.
2095 * @param r zero-based row of the table
2096 *
2097 * @return a String representing the row header
2098 * if one exists; otherwise null.
2099 */
2100 public String getAccessibleRowHeader(int r) {
2101 if (validateIfNecessary()) {
2102 TableCellElementInfo cellInfo = getCell(r, 0);
2103 if (cellInfo.isHeaderCell()) {
2104 View v = cellInfo.getView();
2105 if (v != null && model != null) {
2106 try {
2107 return model.getText(
2108 v.getStartOffset(), v
2109 .getEndOffset()
2110 - v.getStartOffset());
2111 } catch (BadLocationException e) {
2112 return null;
2113 }
2114 }
2115 }
2116 }
2117 return null;
2118 }
2119
2120 /**
2121 * Returns the column header at a column in a table.
2122 * @param c zero-based column of the table
2123 *
2124 * @return a String representing the column header
2125 * if one exists; otherwise null.
2126 */
2127 public String getAccessibleColumnHeader(int c) {
2128 if (validateIfNecessary()) {
2129 TableCellElementInfo cellInfo = getCell(0, c);
2130 if (cellInfo.isHeaderCell()) {
2131 View v = cellInfo.getView();
2132 if (v != null && model != null) {
2133 try {
2134 return model.getText(
2135 v.getStartOffset(), v
2136 .getEndOffset()
2137 - v.getStartOffset());
2138 } catch (BadLocationException e) {
2139 return null;
2140 }
2141 }
2142 }
2143 }
2144 return null;
2145 }
2146
2147 public void addRowHeader(TableCellElementInfo cellInfo,
2148 int rowNumber) {
2149 if (rowHeadersTable == null) {
2150 rowHeadersTable = new AccessibleHeadersTable();
2151 }
2152 rowHeadersTable.addHeader(cellInfo, rowNumber);
2153 }
2154
2155 // end of AccessibleExtendedTable implementation ------------
2156
2157 protected class AccessibleHeadersTable implements
2158 AccessibleTable {
2159
2160 // Header information is modeled as a Hashtable of
2161 // ArrayLists where each Hashtable entry represents
2162 // a row containing one or more headers.
2163 private Hashtable headers = new Hashtable();
2164 private int rowCount = 0;
2165 private int columnCount = 0;
2166
2167 public void addHeader(TableCellElementInfo cellInfo,
2168 int rowNumber) {
2169 Integer rowInteger = new Integer(rowNumber);
2170 ArrayList list = (ArrayList) headers
2171 .get(rowInteger);
2172 if (list == null) {
2173 list = new ArrayList();
2174 headers.put(rowInteger, list);
2175 }
2176 list.add(cellInfo);
2177 }
2178
2179 /**
2180 * Returns the caption for the table.
2181 *
2182 * @return the caption for the table
2183 */
2184 public Accessible getAccessibleCaption() {
2185 return null;
2186 }
2187
2188 /**
2189 * Sets the caption for the table.
2190 *
2191 * @param a the caption for the table
2192 */
2193 public void setAccessibleCaption(Accessible a) {
2194 }
2195
2196 /**
2197 * Returns the summary description of the table.
2198 *
2199 * @return the summary description of the table
2200 */
2201 public Accessible getAccessibleSummary() {
2202 return null;
2203 }
2204
2205 /**
2206 * Sets the summary description of the table
2207 *
2208 * @param a the summary description of the table
2209 */
2210 public void setAccessibleSummary(Accessible a) {
2211 }
2212
2213 /**
2214 * Returns the number of rows in the table.
2215 *
2216 * @return the number of rows in the table
2217 */
2218 public int getAccessibleRowCount() {
2219 return rowCount;
2220 }
2221
2222 /**
2223 * Returns the number of columns in the table.
2224 *
2225 * @return the number of columns in the table
2226 */
2227 public int getAccessibleColumnCount() {
2228 return columnCount;
2229 }
2230
2231 private TableCellElementInfo getElementInfoAt(int r,
2232 int c) {
2233 ArrayList list = (ArrayList) headers
2234 .get(new Integer(r));
2235 if (list != null) {
2236 return (TableCellElementInfo) list.get(c);
2237 } else {
2238 return null;
2239 }
2240 }
2241
2242 /**
2243 * Returns the Accessible at a specified row and column
2244 * in the table.
2245 *
2246 * @param r zero-based row of the table
2247 * @param c zero-based column of the table
2248 * @return the Accessible at the specified row and column
2249 */
2250 public Accessible getAccessibleAt(int r, int c) {
2251 ElementInfo elementInfo = getElementInfoAt(r, c);
2252 if (elementInfo instanceof Accessible) {
2253 return (Accessible) elementInfo;
2254 } else {
2255 return null;
2256 }
2257 }
2258
2259 /**
2260 * Returns the number of rows occupied by the Accessible at
2261 * a specified row and column in the table.
2262 *
2263 * @return the number of rows occupied by the Accessible at a
2264 * given specified (row, column)
2265 */
2266 public int getAccessibleRowExtentAt(int r, int c) {
2267 TableCellElementInfo elementInfo = getElementInfoAt(
2268 r, c);
2269 if (elementInfo != null) {
2270 return elementInfo.getRowCount();
2271 } else {
2272 return 0;
2273 }
2274 }
2275
2276 /**
2277 * Returns the number of columns occupied by the Accessible at
2278 * a specified row and column in the table.
2279 *
2280 * @return the number of columns occupied by the Accessible at a
2281 * given specified row and column
2282 */
2283 public int getAccessibleColumnExtentAt(int r, int c) {
2284 TableCellElementInfo elementInfo = getElementInfoAt(
2285 r, c);
2286 if (elementInfo != null) {
2287 return elementInfo.getRowCount();
2288 } else {
2289 return 0;
2290 }
2291 }
2292
2293 /**
2294 * Returns the row headers as an AccessibleTable.
2295 *
2296 * @return an AccessibleTable representing the row
2297 * headers
2298 */
2299 public AccessibleTable getAccessibleRowHeader() {
2300 return null;
2301 }
2302
2303 /**
2304 * Sets the row headers.
2305 *
2306 * @param table an AccessibleTable representing the
2307 * row headers
2308 */
2309 public void setAccessibleRowHeader(AccessibleTable table) {
2310 }
2311
2312 /**
2313 * Returns the column headers as an AccessibleTable.
2314 *
2315 * @return an AccessibleTable representing the column
2316 * headers
2317 */
2318 public AccessibleTable getAccessibleColumnHeader() {
2319 return null;
2320 }
2321
2322 /**
2323 * Sets the column headers.
2324 *
2325 * @param table an AccessibleTable representing the
2326 * column headers
2327 */
2328 public void setAccessibleColumnHeader(
2329 AccessibleTable table) {
2330 }
2331
2332 /**
2333 * Returns the description of the specified row in the table.
2334 *
2335 * @param r zero-based row of the table
2336 * @return the description of the row
2337 */
2338 public Accessible getAccessibleRowDescription(int r) {
2339 return null;
2340 }
2341
2342 /**
2343 * Sets the description text of the specified row of the table.
2344 *
2345 * @param r zero-based row of the table
2346 * @param a the description of the row
2347 */
2348 public void setAccessibleRowDescription(int r,
2349 Accessible a) {
2350 }
2351
2352 /**
2353 * Returns the description text of the specified column in the table.
2354 *
2355 * @param c zero-based column of the table
2356 * @return the text description of the column
2357 */
2358 public Accessible getAccessibleColumnDescription(int c) {
2359 return null;
2360 }
2361
2362 /**
2363 * Sets the description text of the specified column in the table.
2364 *
2365 * @param c zero-based column of the table
2366 * @param a the text description of the column
2367 */
2368 public void setAccessibleColumnDescription(int c,
2369 Accessible a) {
2370 }
2371
2372 /**
2373 * Returns a boolean value indicating whether the accessible at
2374 * a specified row and column is selected.
2375 *
2376 * @param r zero-based row of the table
2377 * @param c zero-based column of the table
2378 * @return the boolean value true if the accessible at the
2379 * row and column is selected. Otherwise, the boolean value
2380 * false
2381 */
2382 public boolean isAccessibleSelected(int r, int c) {
2383 return false;
2384 }
2385
2386 /**
2387 * Returns a boolean value indicating whether the specified row
2388 * is selected.
2389 *
2390 * @param r zero-based row of the table
2391 * @return the boolean value true if the specified row is selected.
2392 * Otherwise, false.
2393 */
2394 public boolean isAccessibleRowSelected(int r) {
2395 return false;
2396 }
2397
2398 /**
2399 * Returns a boolean value indicating whether the specified column
2400 * is selected.
2401 *
2402 * @param r zero-based column of the table
2403 * @return the boolean value true if the specified column is selected.
2404 * Otherwise, false.
2405 */
2406 public boolean isAccessibleColumnSelected(int c) {
2407 return false;
2408 }
2409
2410 /**
2411 * Returns the selected rows in a table.
2412 *
2413 * @return an array of selected rows where each element is a
2414 * zero-based row of the table
2415 */
2416 public int[] getSelectedAccessibleRows() {
2417 return new int[0];
2418 }
2419
2420 /**
2421 * Returns the selected columns in a table.
2422 *
2423 * @return an array of selected columns where each element is a
2424 * zero-based column of the table
2425 */
2426 public int[] getSelectedAccessibleColumns() {
2427 return new int[0];
2428 }
2429 }
2430 } // ... end AccessibleHeadersTable
2431
2432 /*
2433 * ElementInfo for table rows
2434 */
2435 private class TableRowElementInfo extends ElementInfo {
2436
2437 private TableElementInfo parent;
2438 private int rowNumber;
2439
2440 TableRowElementInfo(Element e, TableElementInfo parent,
2441 int rowNumber) {
2442 super (e, parent);
2443 this .parent = parent;
2444 this .rowNumber = rowNumber;
2445 }
2446
2447 protected void loadChildren(Element e) {
2448 for (int x = 0; x < e.getElementCount(); x++) {
2449 AttributeSet attrs = e.getElement(x)
2450 .getAttributes();
2451
2452 if (attrs
2453 .getAttribute(StyleConstants.NameAttribute) == HTML.Tag.TH) {
2454 TableCellElementInfo headerElementInfo = new TableCellElementInfo(
2455 e.getElement(x), this , true);
2456 addChild(headerElementInfo);
2457
2458 AccessibleTable at = parent
2459 .getAccessibleContext()
2460 .getAccessibleTable();
2461 TableAccessibleContext tableElement = (TableAccessibleContext) at;
2462 tableElement.addRowHeader(headerElementInfo,
2463 rowNumber);
2464
2465 } else if (attrs
2466 .getAttribute(StyleConstants.NameAttribute) == HTML.Tag.TD) {
2467 addChild(new TableCellElementInfo(e
2468 .getElement(x), this , false));
2469 }
2470 }
2471 }
2472
2473 /**
2474 * Returns the max of the rowspans of the cells in this row.
2475 */
2476 public int getRowCount() {
2477 int rowCount = 1;
2478 if (validateIfNecessary()) {
2479 for (int counter = 0; counter < getChildCount(); counter++) {
2480
2481 TableCellElementInfo cell = (TableCellElementInfo) getChild(counter);
2482
2483 if (cell.validateIfNecessary()) {
2484 rowCount = Math.max(rowCount, cell
2485 .getRowCount());
2486 }
2487 }
2488 }
2489 return rowCount;
2490 }
2491
2492 /**
2493 * Returns the sum of the column spans of the individual
2494 * cells in this row.
2495 */
2496 public int getColumnCount() {
2497 int colCount = 0;
2498 if (validateIfNecessary()) {
2499 for (int counter = 0; counter < getChildCount(); counter++) {
2500 TableCellElementInfo cell = (TableCellElementInfo) getChild(counter);
2501
2502 if (cell.validateIfNecessary()) {
2503 colCount += cell.getColumnCount();
2504 }
2505 }
2506 }
2507 return colCount;
2508 }
2509
2510 /**
2511 * Overriden to invalidate the table as well as
2512 * TableRowElementInfo.
2513 */
2514 protected void invalidate(boolean first) {
2515 super .invalidate(first);
2516 getParent().invalidate(true);
2517 }
2518
2519 /**
2520 * Places the TableCellElementInfos for this element in
2521 * the grid.
2522 */
2523 private void updateGrid(int row) {
2524 if (validateIfNecessary()) {
2525 boolean emptyRow = false;
2526
2527 while (!emptyRow) {
2528 for (int counter = 0; counter < grid[row].length; counter++) {
2529 if (grid[row][counter] == null) {
2530 emptyRow = true;
2531 break;
2532 }
2533 }
2534 if (!emptyRow) {
2535 row++;
2536 }
2537 }
2538 for (int col = 0, counter = 0; counter < getChildCount(); counter++) {
2539 TableCellElementInfo cell = (TableCellElementInfo) getChild(counter);
2540
2541 while (grid[row][col] != null) {
2542 col++;
2543 }
2544 for (int rowCount = cell.getRowCount() - 1; rowCount >= 0; rowCount--) {
2545 for (int colCount = cell.getColumnCount() - 1; colCount >= 0; colCount--) {
2546 grid[row + rowCount][col + colCount] = cell;
2547 }
2548 }
2549 col += cell.getColumnCount();
2550 }
2551 }
2552 }
2553
2554 /**
2555 * Returns the column count of the number of columns that have
2556 * a rowcount >= rowspan.
2557 */
2558 private int getColumnCount(int rowspan) {
2559 if (validateIfNecessary()) {
2560 int cols = 0;
2561 for (int counter = 0; counter < getChildCount(); counter++) {
2562 TableCellElementInfo cell = (TableCellElementInfo) getChild(counter);
2563
2564 if (cell.getRowCount() >= rowspan) {
2565 cols += cell.getColumnCount();
2566 }
2567 }
2568 return cols;
2569 }
2570 return 0;
2571 }
2572 }
2573
2574 /**
2575 * TableCellElementInfo is used to represents the cells of
2576 * the table.
2577 */
2578 private class TableCellElementInfo extends ElementInfo {
2579
2580 private Accessible accessible;
2581 private boolean isHeaderCell;
2582
2583 TableCellElementInfo(Element e, ElementInfo parent) {
2584 super (e, parent);
2585 this .isHeaderCell = false;
2586 }
2587
2588 TableCellElementInfo(Element e, ElementInfo parent,
2589 boolean isHeaderCell) {
2590 super (e, parent);
2591 this .isHeaderCell = isHeaderCell;
2592 }
2593
2594 /*
2595 * Returns whether this table cell is a header
2596 */
2597 public boolean isHeaderCell() {
2598 return this .isHeaderCell;
2599 }
2600
2601 /*
2602 * Returns the Accessible representing this table cell
2603 */
2604 public Accessible getAccessible() {
2605 accessible = null;
2606 getAccessible(this );
2607 return accessible;
2608 }
2609
2610 /*
2611 * Gets the outermost Accessible in the table cell
2612 */
2613 private void getAccessible(ElementInfo elementInfo) {
2614 if (elementInfo instanceof Accessible) {
2615 accessible = (Accessible) elementInfo;
2616 return;
2617 } else {
2618 for (int i = 0; i < elementInfo.getChildCount(); i++) {
2619 getAccessible(elementInfo.getChild(i));
2620 }
2621 }
2622 }
2623
2624 /**
2625 * Returns the rowspan attribute.
2626 */
2627 public int getRowCount() {
2628 if (validateIfNecessary()) {
2629 return Math.max(1, getIntAttr(getAttributes(),
2630 HTML.Attribute.ROWSPAN, 1));
2631 }
2632 return 0;
2633 }
2634
2635 /**
2636 * Returns the colspan attribute.
2637 */
2638 public int getColumnCount() {
2639 if (validateIfNecessary()) {
2640 return Math.max(1, getIntAttr(getAttributes(),
2641 HTML.Attribute.COLSPAN, 1));
2642 }
2643 return 0;
2644 }
2645
2646 /**
2647 * Overriden to invalidate the TableRowElementInfo as well as
2648 * the TableCellElementInfo.
2649 */
2650 protected void invalidate(boolean first) {
2651 super .invalidate(first);
2652 getParent().invalidate(true);
2653 }
2654 }
2655 }
2656
2657 /**
2658 * ElementInfo provides a slim down view of an Element. Each ElementInfo
2659 * can have any number of child ElementInfos that are not necessarily
2660 * direct children of the Element. As the Document changes various
2661 * ElementInfos become invalidated. Before accessing a particular portion
2662 * of an ElementInfo you should make sure it is valid by invoking
2663 * <code>validateIfNecessary</code>, this will return true if
2664 * successful, on the other hand a false return value indicates the
2665 * ElementInfo is not valid and can never become valid again (usually
2666 * the result of the Element the ElementInfo encapsulates being removed).
2667 */
2668 private class ElementInfo {
2669
2670 /**
2671 * The children of this ElementInfo.
2672 */
2673 private ArrayList children;
2674 /**
2675 * The Element this ElementInfo is providing information for.
2676 */
2677 private Element element;
2678 /**
2679 * The parent ElementInfo, will be null for the root.
2680 */
2681 private ElementInfo parent;
2682 /**
2683 * Indicates the validity of the ElementInfo.
2684 */
2685 private boolean isValid;
2686 /**
2687 * Indicates if the ElementInfo can become valid.
2688 */
2689 private boolean canBeValid;
2690
2691 /**
2692 * Creates the root ElementInfo.
2693 */
2694 ElementInfo(Element element) {
2695 this (element, null);
2696 }
2697
2698 /**
2699 * Creates an ElementInfo representing <code>element</code> with
2700 * the specified parent.
2701 */
2702 ElementInfo(Element element, ElementInfo parent) {
2703 this .element = element;
2704 this .parent = parent;
2705 isValid = false;
2706 canBeValid = true;
2707 }
2708
2709 /**
2710 * Validates the receiver. This recreates the children as well. This
2711 * will be invoked within a <code>readLock</code>. If this is overriden
2712 * it MUST invoke supers implementation first!
2713 */
2714 protected void validate() {
2715 isValid = true;
2716 loadChildren(getElement());
2717 }
2718
2719 /**
2720 * Recreates the direct children of <code>info</code>.
2721 */
2722 protected void loadChildren(Element parent) {
2723 if (!parent.isLeaf()) {
2724 for (int counter = 0, maxCounter = parent
2725 .getElementCount(); counter < maxCounter; counter++) {
2726 Element e = parent.getElement(counter);
2727 ElementInfo childInfo = createElementInfo(e, this );
2728
2729 if (childInfo != null) {
2730 addChild(childInfo);
2731 } else {
2732 loadChildren(e);
2733 }
2734 }
2735 }
2736 }
2737
2738 /**
2739 * Returns the index of the child in the parent, or -1 for the
2740 * root or if the parent isn't valid.
2741 */
2742 public int getIndexInParent() {
2743 if (parent == null || !parent.isValid()) {
2744 return -1;
2745 }
2746 return parent.indexOf(this );
2747 }
2748
2749 /**
2750 * Returns the Element this <code>ElementInfo</code> represents.
2751 */
2752 public Element getElement() {
2753 return element;
2754 }
2755
2756 /**
2757 * Returns the parent of this Element, or null for the root.
2758 */
2759 public ElementInfo getParent() {
2760 return parent;
2761 }
2762
2763 /**
2764 * Returns the index of the specified child, or -1 if
2765 * <code>child</code> isn't a valid child.
2766 */
2767 public int indexOf(ElementInfo child) {
2768 ArrayList children = this .children;
2769
2770 if (children != null) {
2771 return children.indexOf(child);
2772 }
2773 return -1;
2774 }
2775
2776 /**
2777 * Returns the child ElementInfo at <code>index</code>, or null
2778 * if <code>index</code> isn't a valid index.
2779 */
2780 public ElementInfo getChild(int index) {
2781 if (validateIfNecessary()) {
2782 ArrayList children = this .children;
2783
2784 if (children != null && index >= 0
2785 && index < children.size()) {
2786 return (ElementInfo) children.get(index);
2787 }
2788 }
2789 return null;
2790 }
2791
2792 /**
2793 * Returns the number of children the ElementInfo contains.
2794 */
2795 public int getChildCount() {
2796 validateIfNecessary();
2797 return (children == null) ? 0 : children.size();
2798 }
2799
2800 /**
2801 * Adds a new child to this ElementInfo.
2802 */
2803 protected void addChild(ElementInfo child) {
2804 if (children == null) {
2805 children = new ArrayList();
2806 }
2807 children.add(child);
2808 }
2809
2810 /**
2811 * Returns the View corresponding to this ElementInfo, or null
2812 * if the ElementInfo can't be validated.
2813 */
2814 protected View getView() {
2815 if (!validateIfNecessary()) {
2816 return null;
2817 }
2818 Object lock = lock();
2819 try {
2820 View rootView = getRootView();
2821 Element e = getElement();
2822 int start = e.getStartOffset();
2823
2824 if (rootView != null) {
2825 return getView(rootView, e, start);
2826 }
2827 return null;
2828 } finally {
2829 unlock(lock);
2830 }
2831 }
2832
2833 /**
2834 * Returns the Bounds for this ElementInfo, or null
2835 * if the ElementInfo can't be validated.
2836 */
2837 public Rectangle getBounds() {
2838 if (!validateIfNecessary()) {
2839 return null;
2840 }
2841 Object lock = lock();
2842 try {
2843 Rectangle bounds = getRootEditorRect();
2844 View rootView = getRootView();
2845 Element e = getElement();
2846
2847 if (bounds != null && rootView != null) {
2848 try {
2849 return rootView.modelToView(e.getStartOffset(),
2850 Position.Bias.Forward,
2851 e.getEndOffset(),
2852 Position.Bias.Backward, bounds)
2853 .getBounds();
2854 } catch (BadLocationException ble) {
2855 }
2856 }
2857 } finally {
2858 unlock(lock);
2859 }
2860 return null;
2861 }
2862
2863 /**
2864 * Returns true if this ElementInfo is valid.
2865 */
2866 protected boolean isValid() {
2867 return isValid;
2868 }
2869
2870 /**
2871 * Returns the AttributeSet associated with the Element, this will
2872 * return null if the ElementInfo can't be validated.
2873 */
2874 protected AttributeSet getAttributes() {
2875 if (validateIfNecessary()) {
2876 return getElement().getAttributes();
2877 }
2878 return null;
2879 }
2880
2881 /**
2882 * Returns the AttributeSet associated with the View that is
2883 * representing this Element, this will
2884 * return null if the ElementInfo can't be validated.
2885 */
2886 protected AttributeSet getViewAttributes() {
2887 if (validateIfNecessary()) {
2888 View view = getView();
2889
2890 if (view != null) {
2891 return view.getElement().getAttributes();
2892 }
2893 return getElement().getAttributes();
2894 }
2895 return null;
2896 }
2897
2898 /**
2899 * Convenience method for getting an integer attribute from the passed
2900 * in AttributeSet.
2901 */
2902 protected int getIntAttr(AttributeSet attrs, Object key,
2903 int deflt) {
2904 if (attrs != null && attrs.isDefined(key)) {
2905 int i;
2906 String val = (String) attrs.getAttribute(key);
2907 if (val == null) {
2908 i = deflt;
2909 } else {
2910 try {
2911 i = Math.max(0, Integer.parseInt(val));
2912 } catch (NumberFormatException x) {
2913 i = deflt;
2914 }
2915 }
2916 return i;
2917 }
2918 return deflt;
2919 }
2920
2921 /**
2922 * Validates the ElementInfo if necessary. Some ElementInfos may
2923 * never be valid again. You should check <code>isValid</code> before
2924 * using one. This will reload the children and invoke
2925 * <code>validate</code> if the ElementInfo is invalid and can become
2926 * valid again. This will return true if the receiver is valid.
2927 */
2928 protected boolean validateIfNecessary() {
2929 if (!isValid() && canBeValid) {
2930 children = null;
2931 Object lock = lock();
2932
2933 try {
2934 validate();
2935 } finally {
2936 unlock(lock);
2937 }
2938 }
2939 return isValid();
2940 }
2941
2942 /**
2943 * Invalidates the ElementInfo. Subclasses should override this
2944 * if they need to reset state once invalid.
2945 */
2946 protected void invalidate(boolean first) {
2947 if (!isValid()) {
2948 if (canBeValid && !first) {
2949 canBeValid = false;
2950 }
2951 return;
2952 }
2953 isValid = false;
2954 canBeValid = first;
2955 if (children != null) {
2956 for (int counter = 0; counter < children.size(); counter++) {
2957 ((ElementInfo) children.get(counter))
2958 .invalidate(false);
2959 }
2960 children = null;
2961 }
2962 }
2963
2964 private View getView(View parent, Element e, int start) {
2965 if (parent.getElement() == e) {
2966 return parent;
2967 }
2968 int index = parent.getViewIndex(start,
2969 Position.Bias.Forward);
2970
2971 if (index != -1 && index < parent.getViewCount()) {
2972 return getView(parent.getView(index), e, start);
2973 }
2974 return null;
2975 }
2976
2977 private int getClosestInfoIndex(int index) {
2978 for (int counter = 0; counter < getChildCount(); counter++) {
2979 ElementInfo info = getChild(counter);
2980
2981 if (index < info.getElement().getEndOffset()
2982 || index == info.getElement().getStartOffset()) {
2983 return counter;
2984 }
2985 }
2986 return -1;
2987 }
2988
2989 private void update(DocumentEvent e) {
2990 if (!isValid()) {
2991 return;
2992 }
2993 ElementInfo parent = getParent();
2994 Element element = getElement();
2995
2996 do {
2997 DocumentEvent.ElementChange ec = e.getChange(element);
2998 if (ec != null) {
2999 if (element == getElement()) {
3000 // One of our children changed.
3001 invalidate(true);
3002 } else if (parent != null) {
3003 parent.invalidate(parent == getRootInfo());
3004 }
3005 return;
3006 }
3007 element = element.getParentElement();
3008 } while (parent != null && element != null
3009 && element != parent.getElement());
3010
3011 if (getChildCount() > 0) {
3012 Element elem = getElement();
3013 int pos = e.getOffset();
3014 int index0 = getClosestInfoIndex(pos);
3015 if (index0 == -1
3016 && e.getType() == DocumentEvent.EventType.REMOVE
3017 && pos >= elem.getEndOffset()) {
3018 // Event beyond our offsets. We may have represented this,
3019 // that is the remove may have removed one of our child
3020 // Elements that represented this, so, we should foward
3021 // to last element.
3022 index0 = getChildCount() - 1;
3023 }
3024 ElementInfo info = (index0 >= 0) ? getChild(index0)
3025 : null;
3026 if (info != null
3027 && (info.getElement().getStartOffset() == pos)
3028 && (pos > 0)) {
3029 // If at a boundary, forward the event to the previous
3030 // ElementInfo too.
3031 index0 = Math.max(index0 - 1, 0);
3032 }
3033 int index1;
3034 if (e.getType() != DocumentEvent.EventType.REMOVE) {
3035 index1 = getClosestInfoIndex(pos + e.getLength());
3036 if (index1 < 0) {
3037 index1 = getChildCount() - 1;
3038 }
3039 } else {
3040 index1 = index0;
3041 // A remove may result in empty elements.
3042 while ((index1 + 1) < getChildCount()
3043 && getChild(index1 + 1).getElement()
3044 .getEndOffset() == getChild(
3045 index1 + 1).getElement()
3046 .getStartOffset()) {
3047 index1++;
3048 }
3049 }
3050 index0 = Math.max(index0, 0);
3051 // The check for isValid is here as in the process of
3052 // forwarding update our child may invalidate us.
3053 for (int i = index0; i <= index1 && isValid(); i++) {
3054 getChild(i).update(e);
3055 }
3056 }
3057 }
3058 }
3059
3060 /**
3061 * DocumentListener installed on the current Document. Will invoke
3062 * <code>update</code> on the <code>RootInfo</code> in response to
3063 * any event.
3064 */
3065 private class DocumentHandler implements DocumentListener {
3066 public void insertUpdate(DocumentEvent e) {
3067 getRootInfo().update(e);
3068 }
3069
3070 public void removeUpdate(DocumentEvent e) {
3071 getRootInfo().update(e);
3072 }
3073
3074 public void changedUpdate(DocumentEvent e) {
3075 getRootInfo().update(e);
3076 }
3077 }
3078
3079 /*
3080 * PropertyChangeListener installed on the editor.
3081 */
3082 private class PropertyChangeHandler implements
3083 PropertyChangeListener {
3084 public void propertyChange(PropertyChangeEvent evt) {
3085 if (evt.getPropertyName().equals("document")) {
3086 // handle the document change
3087 setDocument(editor.getDocument());
3088 }
3089 }
3090 }
3091 }
|