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