1 /*
   2  * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.lwawt.macosx;
  27 
  28 import java.awt.*;
  29 import java.beans.*;
  30 import java.lang.reflect.Field;
  31 import java.lang.reflect.InvocationTargetException;
  32 import java.util.*;
  33 import java.util.concurrent.Callable;
  34 
  35 import javax.accessibility.*;
  36 import javax.swing.*;
  37 
  38 class CAccessibility implements PropertyChangeListener {
  39     private static Set<String> ignoredRoles;
  40 
  41     static {
  42         // Need to load the native library for this code.
  43         java.security.AccessController.doPrivileged(
  44             new java.security.PrivilegedAction<Void>() {
  45                 public Void run() {
  46                     System.loadLibrary("awt");
  47                     return null;
  48                 }
  49             });
  50     }
  51 
  52     static CAccessibility sAccessibility;
  53     static synchronized CAccessibility getAccessibility(final String[] roles) {
  54         if (sAccessibility != null) return sAccessibility;
  55         sAccessibility = new CAccessibility();
  56 
  57         if (roles != null) {
  58             ignoredRoles = new HashSet<String>(roles.length);
  59             for (final String role : roles) ignoredRoles.add(role);
  60         } else {
  61             ignoredRoles = new HashSet<String>();
  62         }
  63 
  64         return sAccessibility;
  65     }
  66 
  67     private CAccessibility() {
  68         KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("focusOwner", this);
  69     }
  70 
  71     public void propertyChange(final PropertyChangeEvent evt) {
  72         if (evt.getNewValue() == null) return;
  73         focusChanged();
  74     }
  75 
  76     private native void focusChanged();
  77 
  78     static <T> T invokeAndWait(final Callable<T> callable, final Component c) {
  79         try {
  80             return LWCToolkit.invokeAndWait(callable, c);
  81         } catch (final Exception e) { e.printStackTrace(); }
  82         return null;
  83     }
  84 
  85     static void invokeLater(final Runnable runnable, final Component c) {
  86         try {
  87             LWCToolkit.invokeLater(runnable, c);
  88         } catch (InvocationTargetException e) { e.printStackTrace(); }
  89     }
  90 
  91     public static String getAccessibleActionDescription(final AccessibleAction aa, final int index, final Component c) {
  92         if (aa == null) return null;
  93 
  94         return invokeAndWait(new Callable<String>() {
  95             public String call() throws Exception {
  96                 return aa.getAccessibleActionDescription(index);
  97             }
  98         }, c);
  99     }
 100 
 101     public static void doAccessibleAction(final AccessibleAction aa, final int index, final Component c) {
 102         // We make this an invokeLater because we don't need a reply.
 103         if (aa == null) return;
 104 
 105         invokeLater(new Runnable() {
 106             public void run() {
 107                 aa.doAccessibleAction(index);
 108             }
 109         }, c);
 110     }
 111 
 112     public static Dimension getSize(final AccessibleComponent ac, final Component c) {
 113         if (ac == null) return null;
 114 
 115         return invokeAndWait(new Callable<Dimension>() {
 116             public Dimension call() throws Exception {
 117                 return ac.getSize();
 118             }
 119         }, c);
 120     }
 121 
 122     public static AccessibleSelection getAccessibleSelection(final AccessibleContext ac, final Component c) {
 123         if (ac == null) return null;
 124 
 125         return invokeAndWait(new Callable<AccessibleSelection>() {
 126             public AccessibleSelection call() throws Exception {
 127                 return ac.getAccessibleSelection();
 128             }
 129         }, c);
 130     }
 131 
 132     public static Accessible ax_getAccessibleSelection(final AccessibleContext ac, final int index, final Component c) {
 133         if (ac == null) return null;
 134 
 135         return invokeAndWait(new Callable<Accessible>() {
 136             public Accessible call() throws Exception {
 137                 final AccessibleSelection as = ac.getAccessibleSelection();
 138                 if (as == null) return null;
 139                 return as.getAccessibleSelection(index);
 140             }
 141         }, c);
 142     }
 143 
 144     // KCH - can we make this a postEvent?
 145     public static void addAccessibleSelection(final AccessibleContext ac, final int index, final Component c) {
 146         if (ac == null) return;
 147 
 148         invokeLater(new Runnable() {
 149             public void run() {
 150                 final AccessibleSelection as = ac.getAccessibleSelection();
 151                 if (as == null) return;
 152                 as.addAccessibleSelection(index);
 153             }
 154         }, c);
 155     }
 156 
 157     public static AccessibleContext getAccessibleContext(final Accessible a, final Component c) {
 158         if (a == null) return null;
 159 
 160         return invokeAndWait(new Callable<AccessibleContext>() {
 161             public AccessibleContext call() throws Exception {
 162                 return a.getAccessibleContext();
 163             }
 164         }, c);
 165     }
 166 
 167     public static boolean isAccessibleChildSelected(final Accessible a, final int index, final Component c) {
 168         if (a == null) return false;
 169 
 170         return invokeAndWait(new Callable<Boolean>() {
 171             public Boolean call() throws Exception {
 172                 final AccessibleContext ac = a.getAccessibleContext();
 173                 if (ac == null) return Boolean.FALSE;
 174 
 175                 final AccessibleSelection as = ac.getAccessibleSelection();
 176                 if (as == null) return Boolean.FALSE;
 177 
 178                 return new Boolean(as.isAccessibleChildSelected(index));
 179             }
 180         }, c);
 181     }
 182 
 183     public static AccessibleStateSet getAccessibleStateSet(final AccessibleContext ac, final Component c) {
 184         if (ac == null) return null;
 185 
 186         return invokeAndWait(new Callable<AccessibleStateSet>() {
 187             public AccessibleStateSet call() throws Exception {
 188                 return ac.getAccessibleStateSet();
 189             }
 190         }, c);
 191     }
 192 
 193     public static boolean contains(final AccessibleContext ac, final AccessibleState as, final Component c) {
 194         if (ac == null || as == null) return false;
 195 
 196         return invokeAndWait(new Callable<Boolean>() {
 197             public Boolean call() throws Exception {
 198                 final AccessibleStateSet ass = ac.getAccessibleStateSet();
 199                 if (ass == null) return null;
 200                 return ass.contains(as);
 201             }
 202         }, c);
 203     }
 204 
 205     static Field getAccessibleBundleKeyFieldWithReflection() {
 206         try {
 207             final Field fieldKey = AccessibleBundle.class.getDeclaredField("key");
 208             fieldKey.setAccessible(true);
 209             return fieldKey;
 210         } catch (final SecurityException e) {
 211             e.printStackTrace();
 212         } catch (final NoSuchFieldException e) {
 213             e.printStackTrace();
 214         }
 215         return null;
 216     }
 217     private static final Field FIELD_KEY = getAccessibleBundleKeyFieldWithReflection();
 218 
 219     static String getAccessibleRoleFor(final Accessible a) {
 220         final AccessibleContext ac = a.getAccessibleContext();
 221         if (ac == null) return null;
 222 
 223         final AccessibleRole role = ac.getAccessibleRole();
 224         try {
 225             return (String)FIELD_KEY.get(role);
 226         } catch (final IllegalArgumentException e) {
 227             e.printStackTrace();
 228         } catch (final IllegalAccessException e) {
 229             e.printStackTrace();
 230         }
 231         return null;
 232     }
 233 
 234     public static String getAccessibleRole(final Accessible a, final Component c) {
 235         if (a == null) return null;
 236 
 237         return invokeAndWait(new Callable<String>() {
 238             public String call() throws Exception {
 239                 final Accessible sa = CAccessible.getSwingAccessible(a);
 240                 final String role = getAccessibleRoleFor(a);
 241 
 242                 if (!"text".equals(role)) return role;
 243                 if (sa instanceof JTextArea || sa instanceof JEditorPane) {
 244                     return "textarea";
 245                 }
 246                 return role;
 247             }
 248         }, c);
 249     }
 250 
 251     public static Point getLocationOnScreen(final AccessibleComponent ac, final Component c) {
 252         if (ac == null) return null;
 253 
 254         return invokeAndWait(new Callable<Point>() {
 255             public Point call() throws Exception {
 256                 return ac.getLocationOnScreen();
 257             }
 258         }, c);
 259     }
 260 
 261     public static int getCharCount(final AccessibleText at, final Component c) {
 262         if (at == null) return 0;
 263 
 264         return invokeAndWait(new Callable<Integer>() {
 265             public Integer call() throws Exception {
 266                 return at.getCharCount();
 267             }
 268         }, c);
 269     }
 270 
 271     // Accessibility Threadsafety for JavaComponentAccessibility.m
 272     public static Accessible getAccessibleParent(final Accessible a, final Component c) {
 273         if (a == null) return null;
 274 
 275         return invokeAndWait(new Callable<Accessible>() {
 276             public Accessible call() throws Exception {
 277                 final AccessibleContext ac = a.getAccessibleContext();
 278                 if (ac == null) return null;
 279                 return ac.getAccessibleParent();
 280             }
 281         }, c);
 282     }
 283 
 284     public static int getAccessibleIndexInParent(final Accessible a, final Component c) {
 285         if (a == null) return 0;
 286 
 287         return invokeAndWait(new Callable<Integer>() {
 288             public Integer call() throws Exception {
 289                 final AccessibleContext ac = a.getAccessibleContext();
 290                 if (ac == null) return null;
 291                 return ac.getAccessibleIndexInParent();
 292             }
 293         }, c);
 294     }
 295 
 296     public static AccessibleComponent getAccessibleComponent(final Accessible a, final Component c) {
 297         if (a == null) return null;
 298 
 299         return invokeAndWait(new Callable<AccessibleComponent>() {
 300             public AccessibleComponent call() throws Exception {
 301                 final AccessibleContext ac = a.getAccessibleContext();
 302                 if (ac == null) return null;
 303                 return ac.getAccessibleComponent();
 304             }
 305         }, c);
 306     }
 307 
 308     public static AccessibleValue getAccessibleValue(final Accessible a, final Component c) {
 309         if (a == null) return null;
 310 
 311         return invokeAndWait(new Callable<AccessibleValue>() {
 312             public AccessibleValue call() throws Exception {
 313                 final AccessibleContext ac = a.getAccessibleContext();
 314                 if (ac == null) return null;
 315 
 316                 AccessibleValue accessibleValue = ac.getAccessibleValue();
 317                 return accessibleValue;
 318             }
 319         }, c);
 320     }
 321 
 322     public static String getAccessibleName(final Accessible a, final Component c) {
 323         if (a == null) return null;
 324 
 325         return invokeAndWait(new Callable<String>() {
 326             public String call() throws Exception {
 327                 final AccessibleContext ac = a.getAccessibleContext();
 328                 if (ac == null) return null;
 329 
 330                 final String accessibleName = ac.getAccessibleName();
 331                 if (accessibleName == null) {
 332                     return ac.getAccessibleDescription();
 333                 }
 334                 return accessibleName;
 335             }
 336         }, c);
 337     }
 338 
 339     public static AccessibleText getAccessibleText(final Accessible a, final Component c) {
 340         if (a == null) return null;
 341 
 342         return invokeAndWait(new Callable<AccessibleText>() {
 343             public AccessibleText call() throws Exception {
 344                 final AccessibleContext ac = a.getAccessibleContext();
 345                 if (ac == null) return null;
 346 
 347                 AccessibleText accessibleText = ac.getAccessibleText();
 348                 return accessibleText;
 349             }
 350         }, c);
 351     }
 352 
 353     public static String getAccessibleDescription(final Accessible a, final Component c) {
 354         if (a == null) return null;
 355 
 356         return invokeAndWait(new Callable<String>() {
 357             public String call() throws Exception {
 358                 final AccessibleContext ac = a.getAccessibleContext();
 359                 if (ac == null) return null;
 360 
 361                 final String accessibleDescription = ac.getAccessibleDescription();
 362                 if (accessibleDescription == null) {
 363                     if (c instanceof JComponent) {
 364                         String toolTipText = ((JComponent)c).getToolTipText();
 365                         if (toolTipText != null) {
 366                             return toolTipText;
 367                         }
 368                     }
 369                 }
 370 
 371                 return accessibleDescription;
 372             }
 373         }, c);
 374     }
 375 
 376     public static boolean isFocusTraversable(final Accessible a, final Component c) {
 377         if (a == null) return false;
 378 
 379         return invokeAndWait(new Callable<Boolean>() {
 380             public Boolean call() throws Exception {
 381                 final AccessibleContext ac = a.getAccessibleContext();
 382                 if (ac == null) return null;
 383 
 384                 final AccessibleComponent aComp = ac.getAccessibleComponent();
 385                 if (aComp == null) return null;
 386 
 387                 return aComp.isFocusTraversable();
 388             }
 389         }, c);
 390     }
 391 
 392     public static Accessible accessibilityHitTest(final Container parent, final float hitPointX, final float hitPointY) {
 393         return invokeAndWait(new Callable<Accessible>() {
 394             public Accessible call() throws Exception {
 395                 final Point p = parent.getLocationOnScreen();
 396 
 397                 // Make it into local coords
 398                 final Point localPoint = new Point((int)(hitPointX - p.getX()), (int)(hitPointY - p.getY()));
 399 
 400                 final Component component = parent.findComponentAt(localPoint);
 401                 if (component == null) return null;
 402 
 403                 final AccessibleContext axContext = component.getAccessibleContext();
 404                 if (axContext == null) return null;
 405 
 406                 final AccessibleComponent axComponent = axContext.getAccessibleComponent();
 407                 if (axComponent == null) return null;
 408 
 409                 final int numChildren = axContext.getAccessibleChildrenCount();
 410                 if (numChildren > 0) {
 411                     // It has children, check to see which one is hit.
 412                     final Point p2 = axComponent.getLocationOnScreen();
 413                     final Point localP2 = new Point((int)(hitPointX - p2.getX()), (int)(hitPointY - p2.getY()));
 414                     return CAccessible.getCAccessible(axComponent.getAccessibleAt(localP2));
 415                 }
 416 
 417                 if (!(component instanceof Accessible)) return null;
 418                 return CAccessible.getCAccessible((Accessible)component);
 419             }
 420         }, parent);
 421     }
 422 
 423     public static AccessibleAction getAccessibleAction(final Accessible a, final Component c) {
 424         return invokeAndWait(new Callable<AccessibleAction>() {
 425             public AccessibleAction call() throws Exception {
 426                 final AccessibleContext ac = a.getAccessibleContext();
 427                 if (ac == null) return null;
 428                 return ac.getAccessibleAction();
 429             }
 430         }, c);
 431     }
 432 
 433     public static boolean isEnabled(final Accessible a, final Component c) {
 434         if (a == null) return false;
 435 
 436         return invokeAndWait(new Callable<Boolean>() {
 437             public Boolean call() throws Exception {
 438                 final AccessibleContext ac = a.getAccessibleContext();
 439                 if (ac == null) return null;
 440 
 441                 final AccessibleComponent aComp = ac.getAccessibleComponent();
 442                 if (aComp == null) return null;
 443 
 444                 return aComp.isEnabled();
 445             }
 446         }, c);
 447     }
 448 
 449     // KCH - can we make this a postEvent instead?
 450     public static void requestFocus(final Accessible a, final Component c) {
 451         if (a == null) return;
 452 
 453         invokeLater(new Runnable() {
 454             public void run() {
 455                 final AccessibleContext ac = a.getAccessibleContext();
 456                 if (ac == null) return;
 457 
 458                 final AccessibleComponent aComp = ac.getAccessibleComponent();
 459                 if (aComp == null) return;
 460 
 461                 aComp.requestFocus();
 462             }
 463         }, c);
 464     }
 465 
 466     public static Number getMaximumAccessibleValue(final Accessible a, final Component c) {
 467         if (a == null) return null;
 468 
 469         return invokeAndWait(new Callable<Number>() {
 470             public Number call() throws Exception {
 471                 final AccessibleContext ac = a.getAccessibleContext();
 472                 if (ac == null) return null;
 473 
 474                 final AccessibleValue av = ac.getAccessibleValue();
 475                 if (av == null) return null;
 476 
 477                 return av.getMaximumAccessibleValue();
 478             }
 479         }, c);
 480     }
 481 
 482     public static Number getMinimumAccessibleValue(final Accessible a, final Component c) {
 483         if (a == null) return null;
 484 
 485         return invokeAndWait(new Callable<Number>() {
 486             public Number call() throws Exception {
 487                 final AccessibleContext ac = a.getAccessibleContext();
 488                 if (ac == null) return null;
 489 
 490                 final AccessibleValue av = ac.getAccessibleValue();
 491                 if (av == null) return null;
 492 
 493                 return av.getMinimumAccessibleValue();
 494             }
 495         }, c);
 496     }
 497 
 498     public static String getAccessibleRoleDisplayString(final Accessible a, final Component c) {
 499         if (a == null) return null;
 500 
 501         return invokeAndWait(new Callable<String>() {
 502             public String call() throws Exception {
 503                 final AccessibleContext ac = a.getAccessibleContext();
 504                 if (ac == null) return null;
 505 
 506                 final AccessibleRole ar = ac.getAccessibleRole();
 507                 if (ar == null) return null;
 508 
 509                 return ar.toDisplayString();
 510             }
 511         }, c);
 512     }
 513 
 514     public static Number getCurrentAccessibleValue(final AccessibleValue av, final Component c) {
 515         if (av == null) return null;
 516 
 517         return invokeAndWait(new Callable<Number>() {
 518             public Number call() throws Exception {
 519                 Number currentAccessibleValue = av.getCurrentAccessibleValue();
 520                 return currentAccessibleValue;
 521             }
 522         }, c);
 523     }
 524 
 525     public static Accessible getFocusOwner(final Component c) {
 526         return invokeAndWait(new Callable<Accessible>() {
 527             public Accessible call() throws Exception {
 528                 Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
 529                 if (c == null || !(c instanceof Accessible)) return null;
 530                 return CAccessible.getCAccessible((Accessible)c);
 531             }
 532         }, c);
 533     }
 534 
 535     public static boolean[] getInitialAttributeStates(final Accessible a, final Component c) {
 536         final boolean[] ret = new boolean[7];
 537         if (a == null) return ret;
 538 
 539         return invokeAndWait(new Callable<boolean[]>() {
 540             public boolean[] call() throws Exception {
 541                 final AccessibleContext aContext = a.getAccessibleContext();
 542                 if (aContext == null) return ret;
 543 
 544                 final AccessibleComponent aComponent = aContext.getAccessibleComponent();
 545                 ret[0] = (aComponent != null);
 546                 ret[1] = ((aComponent != null) && (aComponent.isFocusTraversable()));
 547                 ret[2] = (aContext.getAccessibleValue() != null);
 548                 ret[3] = (aContext.getAccessibleText() != null);
 549 
 550                 final AccessibleStateSet aStateSet = aContext.getAccessibleStateSet();
 551                 ret[4] = (aStateSet.contains(AccessibleState.HORIZONTAL) || aStateSet.contains(AccessibleState.VERTICAL));
 552                 ret[5] = (aContext.getAccessibleName() != null);
 553                 ret[6] = (aContext.getAccessibleChildrenCount() > 0);
 554                 return ret;
 555             }
 556         }, c);
 557     }
 558 
 559     // Duplicated from JavaComponentAccessibility
 560     // Note that values >=0 are indexes into the child array
 561     final static int JAVA_AX_ALL_CHILDREN = -1;
 562     final static int JAVA_AX_SELECTED_CHILDREN = -2;
 563     final static int JAVA_AX_VISIBLE_CHILDREN = -3;
 564 
 565     // Each child takes up two entries in the array: one for itself and one for its role
 566     public static Object[] getChildrenAndRoles(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) {
 567         if (a == null) return null;
 568         return invokeAndWait(new Callable<Object[]>() {
 569             public Object[] call() throws Exception {
 570                 final ArrayList<Object> childrenAndRoles = new ArrayList<Object>();
 571                 _addChildren(a, whichChildren, allowIgnored, childrenAndRoles);
 572 
 573                 if ((whichChildren < 0) || (whichChildren * 2 >= childrenAndRoles.size())) {
 574                     return childrenAndRoles.toArray();
 575                 }
 576 
 577                 return new Object[] { childrenAndRoles.get(whichChildren * 2), childrenAndRoles.get((whichChildren * 2) + 1) };
 578             }
 579         }, c);
 580     }
 581 
 582     private static AccessibleRole getAccessibleRoleForLabel(JLabel l, AccessibleRole fallback) {
 583         String text = l.getText();
 584         if (text != null && text.length() > 0) {
 585             return fallback;
 586         }
 587         Icon icon = l.getIcon();
 588         if (icon != null) {
 589             return AccessibleRole.ICON;
 590         }
 591         return fallback;
 592     }
 593 
 594     private static AccessibleRole getAccessibleRole(Accessible a) {
 595         AccessibleContext ac = a.getAccessibleContext();
 596         AccessibleRole role = ac.getAccessibleRole();
 597         Object component = CAccessible.getSwingAccessible(a);
 598         if (role == null) return null;
 599         String roleString = role.toString();
 600         if ("label".equals(roleString) && component instanceof JLabel) {
 601             return getAccessibleRoleForLabel((JLabel) component, role);
 602         }
 603         return role;
 604     }
 605 
 606 
 607     // Either gets the immediate children of a, or recursively gets all unignored children of a
 608     private static void _addChildren(final Accessible a, final int whichChildren, final boolean allowIgnored, final ArrayList<Object> childrenAndRoles) {
 609         if (a == null) return;
 610 
 611         final AccessibleContext ac = a.getAccessibleContext();
 612         if (ac == null) return;
 613 
 614         final int numChildren = ac.getAccessibleChildrenCount();
 615 
 616         // each child takes up two entries in the array: itself, and its role
 617         // so the array holds alternating Accessible and AccessibleRole objects
 618         for (int i = 0; i < numChildren; i++) {
 619             final Accessible child = ac.getAccessibleChild(i);
 620             if (child == null) continue;
 621 
 622             final AccessibleContext context = child.getAccessibleContext();
 623             if (context == null) continue;
 624 
 625             if (whichChildren == JAVA_AX_VISIBLE_CHILDREN) {
 626                 if (!context.getAccessibleComponent().isVisible()) continue;
 627             } else if (whichChildren == JAVA_AX_SELECTED_CHILDREN) {
 628                 if (!ac.getAccessibleSelection().isAccessibleChildSelected(i)) continue;
 629             }
 630 
 631             if (!allowIgnored) {
 632                 final AccessibleRole role = context.getAccessibleRole();
 633                 if (role != null && ignoredRoles.contains(roleKey(role))) {
 634                     // Get the child's unignored children.
 635                     _addChildren(child, whichChildren, false, childrenAndRoles);
 636                 } else {
 637                     childrenAndRoles.add(child);
 638                     childrenAndRoles.add(getAccessibleRole(child));
 639                 }
 640             } else {
 641                 childrenAndRoles.add(child);
 642                 childrenAndRoles.add(getAccessibleRole(child));
 643             }
 644 
 645             // If there is an index, and we are beyond it, time to finish up
 646             if ((whichChildren >= 0) && (childrenAndRoles.size() / 2) >= (whichChildren + 1)) {
 647                 return;
 648             }
 649         }
 650     }
 651 
 652     private static native String roleKey(AccessibleRole aRole);
 653 
 654     public static Object[] getChildren(final Accessible a, final Component c) {
 655         if (a == null) return null;
 656         return invokeAndWait(new Callable<Object[]>() {
 657             public Object[] call() throws Exception {
 658                 final AccessibleContext ac = a.getAccessibleContext();
 659                 if (ac == null) return null;
 660 
 661                 final int numChildren = ac.getAccessibleChildrenCount();
 662                 final Object[] children = new Object[numChildren];
 663                 for (int i = 0; i < numChildren; i++) {
 664                     children[i] = ac.getAccessibleChild(i);
 665                 }
 666                 return children;
 667             }
 668         }, c);
 669     }
 670 }