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