1 /*
   2  * Copyright (c) 2005, 2020, 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 com.sun.java.accessibility.internal;
  27 
  28 import java.awt.*;
  29 import java.awt.event.*;
  30 import java.util.*;
  31 import java.lang.*;
  32 import java.lang.reflect.*;
  33 
  34 import java.beans.*;
  35 import javax.swing.*;
  36 import javax.swing.event.*;
  37 import javax.swing.text.*;
  38 import javax.swing.tree.*;
  39 import javax.swing.table.*;
  40 import javax.swing.plaf.TreeUI;
  41 
  42 import javax.accessibility.*;
  43 import com.sun.java.accessibility.util.*;
  44 import java.awt.geom.Rectangle2D;
  45 import sun.awt.AWTAccessor;
  46 import sun.awt.AppContext;
  47 import sun.awt.SunToolkit;
  48 
  49 import java.util.concurrent.Callable;
  50 import java.util.concurrent.ConcurrentHashMap;
  51 
  52 /*
  53  * Note: This class has to be public.  It's loaded from the VM like this:
  54  *       Class.forName(atName).newInstance();
  55  */
  56 final public class AccessBridge {
  57 
  58     private static AccessBridge theAccessBridge;
  59     private ObjectReferences references;
  60     private EventHandler eventHandler;
  61 
  62     // Maps AccessibleRoles strings to AccessibleRoles.
  63     private ConcurrentHashMap<String,AccessibleRole> accessibleRoleMap = new ConcurrentHashMap<>();
  64 
  65     /**
  66        If the object's role is in the following array getVirtualAccessibleName
  67        will use the extended search algorithm.
  68     */
  69     private ArrayList<AccessibleRole> extendedVirtualNameSearchRoles = new ArrayList<>();
  70     /**
  71        If the role of the object's parent is in the following array
  72        getVirtualAccessibleName will NOT use the extended search
  73        algorithm even if the object's role is in the
  74        extendedVirtualNameSearchRoles array.
  75     */
  76     private ArrayList<AccessibleRole> noExtendedVirtualNameSearchParentRoles = new ArrayList<>();
  77 
  78     private static native boolean isSysWow();
  79 
  80 
  81     /**
  82      * Load DLLs
  83      */
  84     static {
  85         // Load the appropriate DLLs
  86         boolean is32on64 = false;
  87         if (System.getProperty("os.arch").equals("x86")) {
  88             // 32 bit JRE
  89             // Load jabsysinfo.dll so can determine Win bitness
  90             java.security.AccessController.doPrivileged(
  91                 new java.security.PrivilegedAction<Void>() {
  92                     public Void run() {
  93                         System.loadLibrary("jabsysinfo");
  94                         return null;
  95                     }
  96                 }, null, new java.lang.RuntimePermission("loadLibrary.jabsysinfo")
  97             );
  98             if (isSysWow()) {
  99                 // 32 bit JRE on 64 bit OS
 100                 is32on64 = true;
 101                 java.security.AccessController.doPrivileged(
 102                     new java.security.PrivilegedAction<Void>() {
 103                         public Void run() {
 104                             System.loadLibrary("javaaccessbridge-32");
 105                             return null;
 106                         }
 107                     }, null, new java.lang.RuntimePermission("loadLibrary.javaaccessbridge-32")
 108                 );
 109             }
 110         }
 111         if (!is32on64) {
 112             // 32 bit JRE on 32 bit OS or 64 bit JRE on 64 bit OS
 113             java.security.AccessController.doPrivileged(
 114                 new java.security.PrivilegedAction<Void>() {
 115                     public Void run() {
 116                         System.loadLibrary("javaaccessbridge");
 117                         return null;
 118                     }
 119                 }, null, new java.lang.RuntimePermission("loadLibrary.javaaccessbridge")
 120             );
 121         }
 122     }
 123 
 124     /**
 125      * AccessBridge constructor
 126      *
 127      * Note: This constructor has to be public.  It's called from the VM like this:
 128      *       Class.forName(atName).newInstance();
 129      */
 130     public AccessBridge() {
 131         theAccessBridge = this;
 132         references = new ObjectReferences();
 133 
 134         // initialize shutdown hook
 135         Runtime runTime = Runtime.getRuntime();
 136         shutdownHook hook = new shutdownHook();
 137         runTime.addShutdownHook(new Thread(hook));
 138 
 139         // initialize AccessibleRole map
 140         initAccessibleRoleMap();
 141 
 142         // initialize the methods that map HWNDs and Java top-level
 143         // windows
 144         initHWNDcalls();
 145 
 146         // is this a JVM we can use?
 147         // install JDK 1.2 and later Swing ToolKit listener
 148         EventQueueMonitor.isGUIInitialized();
 149 
 150         // start the Java event handler
 151         eventHandler = new EventHandler(this);
 152 
 153         // register for menu selection events
 154         MenuSelectionManager.defaultManager().addChangeListener(eventHandler);
 155 
 156         // register as a NativeWindowHandler
 157         addNativeWindowHandler(new DefaultNativeWindowHandler());
 158 
 159         // start in a new thread
 160         Thread abthread = new Thread(new dllRunner());
 161         abthread.setDaemon(true);
 162         abthread.start();
 163         debugString("[INFO]:AccessBridge started");
 164     }
 165 
 166     /*
 167      * adaptor to run the AccessBridge DLL
 168      */
 169     private class dllRunner implements Runnable {
 170         public void run() {
 171             runDLL();
 172         }
 173     }
 174 
 175     /*
 176      * shutdown hook
 177      */
 178     private class shutdownHook implements Runnable {
 179 
 180         public void run() {
 181             debugString("[INFO]:***** shutdownHook: shutting down...");
 182             javaShutdown();
 183         }
 184     }
 185 
 186 
 187     /*
 188      * Initialize the hashtable that maps Strings to AccessibleRoles.
 189      */
 190     private void initAccessibleRoleMap() {
 191         /*
 192          * Initialize the AccessibleRoles map. This code uses methods in
 193          * java.lang.reflect.* to build the map.
 194          */
 195         try {
 196             Class<?> clAccessibleRole = Class.forName ("javax.accessibility.AccessibleRole");
 197             if (null != clAccessibleRole) {
 198                 AccessibleRole roleUnknown = AccessibleRole.UNKNOWN;
 199                 Field [] fields = clAccessibleRole.getFields ();
 200                 int i = 0;
 201                 for (i = 0; i < fields.length; i ++) {
 202                     Field f = fields [i];
 203                     if (javax.accessibility.AccessibleRole.class == f.getType ()) {
 204                         AccessibleRole nextRole = (AccessibleRole) (f.get (roleUnknown));
 205                         String nextRoleString = nextRole.toDisplayString (Locale.US);
 206                         accessibleRoleMap.put (nextRoleString, nextRole);
 207                     }
 208                 }
 209             }
 210         } catch (Exception e) {}
 211 
 212     /*
 213       Build the extendedVirtualNameSearchRoles array list.
 214     */
 215     extendedVirtualNameSearchRoles.add (AccessibleRole.COMBO_BOX);
 216     try {
 217         /*
 218           Added in J2SE 1.4
 219         */
 220         extendedVirtualNameSearchRoles.add (AccessibleRole.DATE_EDITOR);
 221     } catch (NoSuchFieldError e) {}
 222     extendedVirtualNameSearchRoles.add (AccessibleRole.LIST);
 223     extendedVirtualNameSearchRoles.add (AccessibleRole.PASSWORD_TEXT);
 224     extendedVirtualNameSearchRoles.add (AccessibleRole.SLIDER);
 225     try {
 226         /*
 227           Added in J2SE 1.3
 228         */
 229         extendedVirtualNameSearchRoles.add (AccessibleRole.SPIN_BOX);
 230     } catch (NoSuchFieldError e) {}
 231     extendedVirtualNameSearchRoles.add (AccessibleRole.TABLE);
 232     extendedVirtualNameSearchRoles.add (AccessibleRole.TEXT);
 233     extendedVirtualNameSearchRoles.add (AccessibleRole.UNKNOWN);
 234 
 235     noExtendedVirtualNameSearchParentRoles.add (AccessibleRole.TABLE);
 236     noExtendedVirtualNameSearchParentRoles.add (AccessibleRole.TOOL_BAR);
 237     }
 238 
 239     /**
 240      * start the AccessBridge DLL running in its own thread
 241      */
 242     private native void runDLL();
 243 
 244     /**
 245      * debugging output (goes to OutputDebugStr())
 246      */
 247     private native void sendDebugString(String debugStr);
 248 
 249     /**
 250      * debugging output (goes to OutputDebugStr())
 251      */
 252     private void debugString(String debugStr) {
 253     sendDebugString(debugStr);
 254     }
 255 
 256     /* ===== utility methods ===== */
 257 
 258     /**
 259      * decrement the reference to the object (called by native code)
 260      */
 261     private void decrementReference(Object o) {
 262     references.decrement(o);
 263     }
 264 
 265     /**
 266      * get the java.version property from the JVM
 267      */
 268     private String getJavaVersionProperty() {
 269         String s = System.getProperty("java.version");
 270         if (s != null) {
 271             references.increment(s);
 272             return s;
 273         }
 274         return null;
 275     }
 276 
 277     /* ===== HWND/Java window mapping methods ===== */
 278 
 279     // Java toolkit methods for mapping HWNDs to Java components
 280     private Method javaGetComponentFromNativeWindowHandleMethod;
 281     private Method javaGetNativeWindowHandleFromComponentMethod;
 282 
 283     // native jawt methods for mapping HWNDs to Java components
 284     private native int jawtGetNativeWindowHandleFromComponent(Component comp);
 285 
 286     private native Component jawtGetComponentFromNativeWindowHandle(int handle);
 287 
 288     Toolkit toolkit;
 289 
 290     /**
 291      * map an HWND to an AWT Component
 292      */
 293     private void initHWNDcalls() {
 294         Class<?>[] integerParemter = new Class<?>[1];
 295         integerParemter[0] = Integer.TYPE;
 296         Class<?>[] componentParemter = new Class<?>[1];
 297         try {
 298             componentParemter[0] = Class.forName("java.awt.Component");
 299         } catch (ClassNotFoundException e) {
 300             debugString("[ERROR]:Exception: " + e.toString());
 301         }
 302         toolkit = Toolkit.getDefaultToolkit();
 303         return;
 304     }
 305 
 306     // native window handler interface
 307     private interface NativeWindowHandler {
 308         public Accessible getAccessibleFromNativeWindowHandle(int nativeHandle);
 309     }
 310 
 311     // hash table of native window handle to AccessibleContext mappings
 312     static private ConcurrentHashMap<Integer,AccessibleContext> windowHandleToContextMap = new ConcurrentHashMap<>();
 313 
 314     // hash table of AccessibleContext to native window handle mappings
 315     static private ConcurrentHashMap<AccessibleContext,Integer> contextToWindowHandleMap = new ConcurrentHashMap<>();
 316 
 317     /*
 318      * adds a virtual window handler to our hash tables
 319      */
 320     static private void registerVirtualFrame(final Accessible a,
 321                                              Integer nativeWindowHandle ) {
 322         if (a != null) {
 323             AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
 324                 @Override
 325                 public AccessibleContext call() throws Exception {
 326                     return a.getAccessibleContext();
 327                 }
 328             }, a);
 329             windowHandleToContextMap.put(nativeWindowHandle, ac);
 330             contextToWindowHandleMap.put(ac, nativeWindowHandle);
 331         }
 332     }
 333 
 334     /*
 335      * removes a virtual window handler to our hash tables
 336      */
 337     static private void revokeVirtualFrame(final Accessible a,
 338                                            Integer nativeWindowHandle ) {
 339         AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
 340             @Override
 341             public AccessibleContext call() throws Exception {
 342                 return a.getAccessibleContext();
 343             }
 344         }, a);
 345         windowHandleToContextMap.remove(nativeWindowHandle);
 346         contextToWindowHandleMap.remove(ac);
 347     }
 348 
 349     // vector of native window handlers
 350     private static Vector<NativeWindowHandler> nativeWindowHandlers = new Vector<>();
 351 
 352     /*
 353     * adds a native window handler to our list
 354     */
 355     private static void addNativeWindowHandler(NativeWindowHandler handler) {
 356         if (handler == null) {
 357             throw new IllegalArgumentException();
 358         }
 359         nativeWindowHandlers.addElement(handler);
 360     }
 361 
 362     /*
 363      * removes a native window handler to our list
 364      */
 365     private static boolean removeNativeWindowHandler(NativeWindowHandler handler) {
 366         if (handler == null) {
 367             throw new IllegalArgumentException();
 368         }
 369         return nativeWindowHandlers.removeElement(handler);
 370     }
 371 
 372     /**
 373      * verifies that a native window handle is a Java window
 374      */
 375     private boolean isJavaWindow(int nativeHandle) {
 376         AccessibleContext ac = getContextFromNativeWindowHandle(nativeHandle);
 377         if (ac != null) {
 378             saveContextToWindowHandleMapping(ac, nativeHandle);
 379             return true;
 380         }
 381         return false;
 382     }
 383 
 384     /*
 385      * saves the mapping between an AccessibleContext and a window handle
 386      */
 387     private void saveContextToWindowHandleMapping(AccessibleContext ac,
 388                                                   int nativeHandle) {
 389         debugString("[INFO]:saveContextToWindowHandleMapping...");
 390         if (ac == null) {
 391             return;
 392         }
 393         if (! contextToWindowHandleMap.containsKey(ac)) {
 394             debugString("[INFO]: saveContextToWindowHandleMapping: ac = "+ac+"; handle = "+nativeHandle);
 395             contextToWindowHandleMap.put(ac, nativeHandle);
 396         }
 397     }
 398 
 399     /**
 400      * maps a native window handle to an Accessible Context
 401      */
 402     private AccessibleContext getContextFromNativeWindowHandle(int nativeHandle) {
 403         // First, look for the Accessible in our hash table of
 404         // virtual window handles.
 405         AccessibleContext ac = windowHandleToContextMap.get(nativeHandle);
 406         if(ac!=null) {
 407             saveContextToWindowHandleMapping(ac, nativeHandle);
 408             return ac;
 409         }
 410 
 411         // Next, look for the native window handle in our vector
 412         // of native window handles.
 413         int numHandlers = nativeWindowHandlers.size();
 414         for (int i = 0; i < numHandlers; i++) {
 415             NativeWindowHandler nextHandler = nativeWindowHandlers.elementAt(i);
 416             final Accessible a = nextHandler.getAccessibleFromNativeWindowHandle(nativeHandle);
 417             if (a != null) {
 418                 ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
 419                     @Override
 420                     public AccessibleContext call() throws Exception {
 421                         return a.getAccessibleContext();
 422                     }
 423                 }, a);
 424                 saveContextToWindowHandleMapping(ac, nativeHandle);
 425                 return ac;
 426             }
 427         }
 428         // Not found.
 429         return null;
 430     }
 431 
 432     /**
 433      * maps an AccessibleContext to a native window handle
 434      *     returns 0 on error
 435      */
 436     private int getNativeWindowHandleFromContext(AccessibleContext ac) {
 437     debugString("[INFO]: getNativeWindowHandleFromContext: ac = "+ac);
 438         try {
 439             return contextToWindowHandleMap.get(ac);
 440         } catch (Exception ex) {
 441             return 0;
 442         }
 443     }
 444 
 445     private class DefaultNativeWindowHandler implements NativeWindowHandler {
 446         /*
 447         * returns the Accessible associated with a native window
 448         */
 449         public Accessible getAccessibleFromNativeWindowHandle(int nativeHandle) {
 450             final Component c = jawtGetComponentFromNativeWindowHandle(nativeHandle);
 451             if (c instanceof Accessible) {
 452                 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
 453                     @Override
 454                     public AccessibleContext call() throws Exception {
 455                         return c.getAccessibleContext();
 456                     }
 457                 }, c);
 458                 saveContextToWindowHandleMapping(ac, nativeHandle);
 459                 return (Accessible)c;
 460             } else {
 461                 return null;
 462             }
 463         }
 464     }
 465 
 466     /* ===== AccessibleContext methods =====*/
 467 
 468     /*
 469      * returns the inner-most AccessibleContext in parent at Point(x, y)
 470      */
 471     private AccessibleContext getAccessibleContextAt(int x, int y,
 472                                                     AccessibleContext parent) {
 473         if (parent == null) {
 474             return null;
 475         }
 476         if (windowHandleToContextMap != null &&
 477             windowHandleToContextMap.containsValue(getRootAccessibleContext(parent))) {
 478             // Path for applications that register their top-level
 479             // windows with the AccessBridge (e.g., StarOffice 6.1)
 480             return getAccessibleContextAt_1(x, y, parent);
 481         } else {
 482             // Path for applications that do not register
 483             // their top-level windows with the AccessBridge
 484             // (e.g., Swing/AWT applications)
 485             return getAccessibleContextAt_2(x, y, parent);
 486         }
 487     }
 488 
 489     /*
 490      * returns the root accessible context
 491      */
 492     private AccessibleContext getRootAccessibleContext(final AccessibleContext ac) {
 493         if (ac == null) {
 494             return null;
 495         }
 496         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
 497             @Override
 498             public AccessibleContext call() throws Exception {
 499                 Accessible parent = ac.getAccessibleParent();
 500                 if (parent == null) {
 501                     return ac;
 502                 }
 503                 Accessible tmp = parent.getAccessibleContext().getAccessibleParent();
 504                 while (tmp != null) {
 505                     parent = tmp;
 506                     tmp = parent.getAccessibleContext().getAccessibleParent();
 507                 }
 508                 return parent.getAccessibleContext();
 509             }
 510         }, ac);
 511     }
 512 
 513     /*
 514      * StarOffice version that does not use the EventQueueMonitor
 515      */
 516     private AccessibleContext getAccessibleContextAt_1(final int x, final int y,
 517                                                       final AccessibleContext parent) {
 518         debugString("[INFO]: getAccessibleContextAt_1 called");
 519         debugString("   -> x = " + x + " y = " + y + " parent = " + parent);
 520 
 521         if (parent == null) return null;
 522             final AccessibleComponent acmp = InvocationUtils.invokeAndWait(new Callable<AccessibleComponent>() {
 523                 @Override
 524                 public AccessibleComponent call() throws Exception {
 525                     return parent.getAccessibleComponent();
 526                 }
 527             }, parent);
 528         if (acmp!=null) {
 529             final Point loc = InvocationUtils.invokeAndWait(new Callable<Point>() {
 530                 @Override
 531                 public Point call() throws Exception {
 532                     return acmp.getLocation();
 533                 }
 534             }, parent);
 535             final Accessible a = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
 536                 @Override
 537                 public Accessible call() throws Exception {
 538                     return acmp.getAccessibleAt(new Point(x - loc.x, y - loc.y));
 539                 }
 540             }, parent);
 541             if (a != null) {
 542                 AccessibleContext foundAC = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
 543                     @Override
 544                     public AccessibleContext call() throws Exception {
 545                         return a.getAccessibleContext();
 546                     }
 547                 }, parent);
 548                 if (foundAC != null) {
 549                     if (foundAC != parent) {
 550                         // recurse down into the child
 551                         return getAccessibleContextAt_1(x - loc.x, y - loc.y,
 552                                                         foundAC);
 553                     } else
 554                         return foundAC;
 555                 }
 556             }
 557         }
 558         return parent;
 559     }
 560 
 561     /*
 562      * AWT/Swing version
 563      */
 564     private AccessibleContext getAccessibleContextAt_2(final int x, final int y,
 565                                                       AccessibleContext parent) {
 566         debugString("[INFO]: getAccessibleContextAt_2 called");
 567         debugString("   -> x = " + x + " y = " + y + " parent = " + parent);
 568 
 569         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
 570             @Override
 571             public AccessibleContext call() throws Exception {
 572                 Accessible a = EventQueueMonitor.getAccessibleAt(new Point(x, y));
 573                 if (a != null) {
 574                     AccessibleContext childAC = a.getAccessibleContext();
 575                     if (childAC != null) {
 576                         debugString("[INFO]:   returning childAC = " + childAC);
 577                         return childAC;
 578                     }
 579                 }
 580                 return null;
 581             }
 582         }, parent);
 583     }
 584 
 585     /**
 586      * returns the Accessible that has focus
 587      */
 588     private AccessibleContext getAccessibleContextWithFocus() {
 589         Component c = AWTEventMonitor.getComponentWithFocus();
 590         if (c != null) {
 591             final Accessible a = Translator.getAccessible(c);
 592             if (a != null) {
 593                 AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
 594                     @Override
 595                     public AccessibleContext call() throws Exception {
 596                         return a.getAccessibleContext();
 597                     }
 598                 }, c);
 599                 if (ac != null) {
 600                     return ac;
 601                 }
 602             }
 603         }
 604         return null;
 605     }
 606 
 607     /**
 608      * returns the AccessibleName from an AccessibleContext
 609      */
 610     private String getAccessibleNameFromContext(final AccessibleContext ac) {
 611         debugString("[INFO]: ***** ac = "+ac.getClass());
 612         if (ac != null) {
 613             String s = InvocationUtils.invokeAndWait(new Callable<String>() {
 614                 @Override
 615                 public String call() throws Exception {
 616                     return ac.getAccessibleName();
 617                 }
 618             }, ac);
 619             if (s != null) {
 620                 references.increment(s);
 621                 debugString("[INFO]: Returning AccessibleName from Context: " + s);
 622                 return s;
 623             } else {
 624                 return null;
 625             }
 626         } else {
 627             debugString("[INFO]: getAccessibleNameFromContext; ac = null!");
 628             return null;
 629         }
 630     }
 631 
 632     /**
 633      * Returns an AccessibleName for a component using an algorithm optimized
 634      * for the JAWS screen reader.  This method is only intended for JAWS. All
 635      * other uses are entirely optional.
 636      */
 637     private String getVirtualAccessibleNameFromContext(final AccessibleContext ac) {
 638         if (null != ac) {
 639             /*
 640             Step 1:
 641             =======
 642             Determine if we can obtain the Virtual Accessible Name from the
 643             Accessible Name or Accessible Description of the object.
 644             */
 645             String nameString = InvocationUtils.invokeAndWait(new Callable<String>() {
 646                 @Override
 647                 public String call() throws Exception {
 648                     return ac.getAccessibleName();
 649                 }
 650             }, ac);
 651             if ( ( null != nameString ) && ( 0 != nameString.length () ) ) {
 652                 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from AccessibleContext::getAccessibleName.");
 653                 references.increment (nameString);
 654                 return nameString;
 655             }
 656             String descriptionString = InvocationUtils.invokeAndWait(new Callable<String>() {
 657                 @Override
 658                 public String call() throws Exception {
 659                     return ac.getAccessibleDescription();
 660                 }
 661             }, ac);
 662             if ( ( null != descriptionString ) && ( 0 != descriptionString.length () ) ) {
 663                 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from AccessibleContext::getAccessibleDescription.");
 664                 references.increment (descriptionString);
 665                 return descriptionString;
 666             }
 667 
 668             debugString ("[WARN]: The Virtual Accessible Name was not found using AccessibleContext::getAccessibleDescription. or getAccessibleName");
 669             /*
 670             Step 2:
 671             =======
 672             Decide whether the extended name search algorithm should be
 673             used for this object.
 674             */
 675             boolean bExtendedSearch = false;
 676             AccessibleRole role = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
 677                 @Override
 678                 public AccessibleRole call() throws Exception {
 679                     return ac.getAccessibleRole();
 680                 }
 681             }, ac);
 682             AccessibleContext parentContext = null;
 683             AccessibleRole parentRole = AccessibleRole.UNKNOWN;
 684 
 685             if ( extendedVirtualNameSearchRoles.contains (role) ) {
 686                 parentContext = getAccessibleParentFromContext (ac);
 687                 if ( null != parentContext ) {
 688                     final AccessibleContext parentContextInnerTemp = parentContext;
 689                     parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
 690                         @Override
 691                         public AccessibleRole call() throws Exception {
 692                             return parentContextInnerTemp.getAccessibleRole();
 693                         }
 694                     }, ac);
 695                     if ( AccessibleRole.UNKNOWN != parentRole ) {
 696                         bExtendedSearch = true;
 697                         if ( noExtendedVirtualNameSearchParentRoles.contains (parentRole) ) {
 698                             bExtendedSearch = false;
 699                         }
 700                     }
 701                 }
 702             }
 703 
 704             if (false == bExtendedSearch) {
 705                 debugString ("[INFO]: bk -- getVirtualAccessibleNameFromContext will not use the extended name search algorithm.  role = " + ( role != null ? role.toDisplayString(Locale.US) : "null") );
 706                 /*
 707                 Step 3:
 708                 =======
 709                 We have determined that we should not use the extended name
 710                 search algorithm for this object (we must obtain the name of
 711                 the object from the object itself and not from neighboring
 712                 objects).  However the object name cannot be obtained from
 713                 the Accessible Name or Accessible Description of the object.
 714 
 715                 Handle several special cases here that might yield a value for
 716                 the Virtual Accessible Name.  Return null if the object does
 717                 not match the criteria for any of these special cases.
 718                 */
 719                 if (AccessibleRole.LABEL == role) {
 720                     /*
 721                     Does the label support the Accessible Text Interface?
 722                     */
 723                     final AccessibleText at = InvocationUtils.invokeAndWait(new Callable<AccessibleText>() {
 724                         @Override
 725                         public AccessibleText call() throws Exception {
 726                             return ac.getAccessibleText();
 727                         }
 728                     }, ac);
 729                     if (null != at) {
 730                         int charCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
 731                             @Override
 732                             public Integer call() throws Exception {
 733                                 return at.getCharCount();
 734                             }
 735                         }, ac);
 736                         String text = getAccessibleTextRangeFromContext (ac, 0, charCount);
 737                         if (null != text) {
 738                             debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the Accessible Text of the LABEL object.");
 739                             references.increment (text);
 740                             return text;
 741                         }
 742                     }
 743                     /*
 744                     Does the label support the Accessible Icon Interface?
 745                     */
 746                     debugString ("[INFO]: bk -- Attempting to obtain the Virtual Accessible Name from the Accessible Icon information.");
 747                     final AccessibleIcon [] ai = InvocationUtils.invokeAndWait(new Callable<AccessibleIcon[]>() {
 748                         @Override
 749                         public AccessibleIcon[] call() throws Exception {
 750                             return ac.getAccessibleIcon();
 751                         }
 752                     }, ac);
 753                     if ( (null != ai) && (ai.length > 0) ) {
 754                         String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
 755                             @Override
 756                             public String call() throws Exception {
 757                                 return ai[0].getAccessibleIconDescription();
 758                             }
 759                         }, ac);
 760                         if (iconDescription != null){
 761                             debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the LABEL object.");
 762                             references.increment (iconDescription);
 763                             return iconDescription;
 764                         }
 765                     } else {
 766                         parentContext = getAccessibleParentFromContext (ac);
 767                         if ( null != parentContext ) {
 768                             final AccessibleContext parentContextInnerTemp = parentContext;
 769                             parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
 770                                 @Override
 771                                 public AccessibleRole call() throws Exception {
 772                                     return parentContextInnerTemp.getAccessibleRole();
 773                                 }
 774                             }, ac);
 775                             if ( AccessibleRole.TABLE == parentRole ) {
 776                                 int indexInParent = InvocationUtils.invokeAndWait(new Callable<Integer>() {
 777                                     @Override
 778                                     public Integer call() throws Exception {
 779                                         return ac.getAccessibleIndexInParent();
 780                                     }
 781                                 }, ac);
 782                                 final AccessibleContext acTableCell = getAccessibleChildFromContext (parentContext, indexInParent);
 783                                 debugString ("[INFO]: bk -- Making a second attempt to obtain the Virtual Accessible Name from the Accessible Icon information for the Table Cell.");
 784                                 if (acTableCell != null) {
 785                                     final AccessibleIcon [] aiRet =InvocationUtils.invokeAndWait(new Callable<AccessibleIcon[]>() {
 786                                         @Override
 787                                         public AccessibleIcon[] call() throws Exception {
 788                                             return acTableCell.getAccessibleIcon();
 789                                         }
 790                                     }, ac);
 791                                     if ( (null != aiRet) && (aiRet.length > 0) ) {
 792                                         String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
 793                                             @Override
 794                                             public String call() throws Exception {
 795                                                 return aiRet[0].getAccessibleIconDescription();
 796                                             }
 797                                         }, ac);
 798                                         if (iconDescription != null){
 799                                             debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the Table Cell object.");
 800                                             references.increment (iconDescription);
 801                                             return iconDescription;
 802                                         }
 803                                     }
 804                                 }
 805                             }
 806                         }
 807                     }
 808                 } else if ( (AccessibleRole.TOGGLE_BUTTON == role) ||
 809                             (AccessibleRole.PUSH_BUTTON == role) ) {
 810                     /*
 811                     Does the button support the Accessible Icon Interface?
 812                     */
 813                     debugString ("[INFO]: bk -- Attempting to obtain the Virtual Accessible Name from the Accessible Icon information.");
 814                     final AccessibleIcon [] ai = InvocationUtils.invokeAndWait(new Callable<AccessibleIcon[]>() {
 815                         @Override
 816                         public AccessibleIcon[] call() throws Exception {
 817                             return ac.getAccessibleIcon();
 818                         }
 819                     }, ac);
 820                     if ( (null != ai) && (ai.length > 0) ) {
 821                         String iconDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
 822                             @Override
 823                             public String call() throws Exception {
 824                                 return ai[0].getAccessibleIconDescription();
 825                             }
 826                         }, ac);
 827                         if (iconDescription != null){
 828                             debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the description of the first Accessible Icon found in the TOGGLE_BUTTON or PUSH_BUTTON object.");
 829                             references.increment (iconDescription);
 830                             return iconDescription;
 831                         }
 832                     }
 833                 } else if ( AccessibleRole.CHECK_BOX == role ) {
 834                     /*
 835                     NOTE: The only case I know of in which a check box does not
 836                     have a name is when that check box is contained in a table.
 837 
 838                     In this case it would be appropriate to use the display string
 839                     of the check box object as the name (in US English the display
 840                     string is typically either "true" or "false").
 841 
 842                     I am using the AccessibleValue interface to obtain the display
 843                     string of the check box.  If the Accessible Value is 1, I am
 844                     returning Boolean.TRUE.toString (),  If the Accessible Value is
 845                     0, I am returning Boolean.FALSE.toString ().  If the Accessible
 846                     Value is some other number, I will return the display string of
 847                     the current numerical value of the check box.
 848                     */
 849                     final AccessibleValue av = InvocationUtils.invokeAndWait(new Callable<AccessibleValue>() {
 850                         @Override
 851                         public AccessibleValue call() throws Exception {
 852                             return ac.getAccessibleValue();
 853                         }
 854                     }, ac);
 855                     if ( null != av ) {
 856                         nameString = null;
 857                         Number value = InvocationUtils.invokeAndWait(new Callable<Number>() {
 858                             @Override
 859                             public Number call() throws Exception {
 860                                 return av.getCurrentAccessibleValue();
 861                             }
 862                         }, ac);
 863                         if ( null != value ) {
 864                             if ( 1 == value.intValue () ) {
 865                                 nameString = Boolean.TRUE.toString ();
 866                             } else if ( 0 == value.intValue () ) {
 867                                 nameString = Boolean.FALSE.toString ();
 868                             } else {
 869                                 nameString = value.toString ();
 870                             }
 871                             if ( null != nameString ) {
 872                                 references.increment (nameString);
 873                                 return nameString;
 874                             }
 875                         }
 876                     }
 877                 }
 878                 return null;
 879             }
 880 
 881             /*
 882             +
 883             Beginning of the extended name search
 884             +
 885             */
 886             final AccessibleContext parentContextOuterTemp = parentContext;
 887             String parentName = InvocationUtils.invokeAndWait(new Callable<String>() {
 888                 @Override
 889                 public String call() throws Exception {
 890                     return parentContextOuterTemp.getAccessibleName();
 891                 }
 892             }, ac);
 893             String parentDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
 894                 @Override
 895                 public String call() throws Exception {
 896                     return parentContextOuterTemp.getAccessibleDescription();
 897                 }
 898             }, ac);
 899 
 900             /*
 901             Step 4:
 902             =======
 903             Special case for Slider Bar objects.
 904             */
 905             if ( (AccessibleRole.SLIDER == role) &&
 906                  (AccessibleRole.PANEL == parentRole) &&
 907                  (null != parentName) ) {
 908                 debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from the Accessible Name of the SLIDER object's parent object.");
 909                 references.increment (parentName);
 910                 return parentName;
 911             }
 912 
 913             boolean bIsEditCombo = false;
 914 
 915             AccessibleContext testContext = ac;
 916             /*
 917             Step 5:
 918             =======
 919             Special case for Edit Combo Boxes
 920             */
 921             if ( (AccessibleRole.TEXT == role) &&
 922                  (AccessibleRole.COMBO_BOX == parentRole) ) {
 923                 bIsEditCombo = true;
 924                 if (null != parentName) {
 925                     debugString ("[INFO]: bk -- The Virtual Accessible Name for this Edit Combo box was obtained from the Accessible Name of the object's parent object.");
 926                     references.increment (parentName);
 927                     return parentName;
 928                 } else if (null != parentDescription) {
 929                     debugString ("[INFO]: bk -- The Virtual Accessible Name for this Edit Combo box was obtained from the Accessible Description of the object's parent object.");
 930                     references.increment (parentDescription);
 931                     return parentDescription;
 932                 }
 933                 testContext = parentContext;
 934                 parentRole = AccessibleRole.UNKNOWN;
 935                 parentContext = getAccessibleParentFromContext (testContext);
 936                 if ( null != parentContext ) {
 937                     final AccessibleContext parentContextInnerTemp = parentContext;
 938                     parentRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
 939                         @Override
 940                         public AccessibleRole call() throws Exception {
 941                             return parentContextInnerTemp.getAccessibleRole();
 942                         }
 943                     }, ac);
 944                 }
 945             }
 946 
 947             /*
 948             Step 6:
 949             =======
 950             Attempt to get the Virtual Accessible Name of the object using the
 951             Accessible Relation Set Info (the LABELED_BY Accessible Relation).
 952             */
 953             {
 954                 final AccessibleContext parentContextTempInner = parentContext;
 955                 AccessibleRelationSet ars = InvocationUtils.invokeAndWait(new Callable<AccessibleRelationSet>() {
 956                     @Override
 957                     public AccessibleRelationSet call() throws Exception {
 958                         return parentContextTempInner.getAccessibleRelationSet();
 959                     }
 960                 }, ac);
 961                 if ( ars != null && (ars.size () > 0) && (ars.contains (AccessibleRelation.LABELED_BY)) ) {
 962                     AccessibleRelation labeledByRelation = ars.get (AccessibleRelation.LABELED_BY);
 963                     if (labeledByRelation != null) {
 964                         Object [] targets = labeledByRelation.getTarget ();
 965                         Object o = targets [0];
 966                         if (o instanceof Accessible) {
 967                             AccessibleContext labelContext = ((Accessible)o).getAccessibleContext ();
 968                             if (labelContext != null) {
 969                                 String labelName = labelContext.getAccessibleName ();
 970                                 String labelDescription = labelContext.getAccessibleDescription ();
 971                                 if (null != labelName) {
 972                                     debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained using the LABELED_BY AccessibleRelation -- Name Case.");
 973                                     references.increment (labelName);
 974                                     return labelName;
 975                                 } else if (null != labelDescription) {
 976                                     debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained using the LABELED_BY AccessibleRelation -- Description Case.");
 977                                     references.increment (labelDescription);
 978                                     return labelDescription;
 979                                 }
 980                             }
 981                         }
 982                     }
 983                 }
 984             }
 985 
 986             //Note: add AccessibleContext to use InvocationUtils.invokeAndWait
 987             /*
 988             Step 7:
 989             =======
 990             Search for a label object that is positioned either just to the left
 991             or just above the object and get the Accessible Name of the Label
 992             object.
 993             */
 994             int testIndexMax = 0;
 995             int testX = 0;
 996             int testY = 0;
 997             int testWidth = 0;
 998             int testHeight = 0;
 999             int targetX = 0;
1000             int targetY = 0;
1001             final AccessibleContext tempContext = testContext;
1002             int testIndex = InvocationUtils.invokeAndWait(new Callable<Integer>() {
1003                 @Override
1004                 public Integer call() throws Exception {
1005                     return tempContext.getAccessibleIndexInParent();
1006                 }
1007             }, ac);
1008             if ( null != parentContext ) {
1009                 final AccessibleContext parentContextInnerTemp = parentContext;
1010                 testIndexMax =  InvocationUtils.invokeAndWait(new Callable<Integer>() {
1011                     @Override
1012                     public Integer call() throws Exception {
1013                         return parentContextInnerTemp.getAccessibleChildrenCount() - 1;
1014                     }
1015                 }, ac);
1016             }
1017             testX = getAccessibleXcoordFromContext (testContext);
1018             testY = getAccessibleYcoordFromContext (testContext);
1019             testWidth = getAccessibleWidthFromContext (testContext);
1020             testHeight = getAccessibleHeightFromContext (testContext);
1021             targetX = testX + 2;
1022             targetY = testY + 2;
1023 
1024             int childIndex = testIndex - 1;
1025             /*Accessible child = null;
1026             AccessibleContext childContext = null;
1027             AccessibleRole childRole = AccessibleRole.UNKNOWN;*/
1028             int childX = 0;
1029             int childY = 0;
1030             int childWidth = 0;
1031             int childHeight = 0;
1032             String childName = null;
1033             String childDescription = null;
1034             while (childIndex >= 0) {
1035                 final int childIndexTemp = childIndex;
1036                 final AccessibleContext parentContextInnerTemp = parentContext;
1037                 final Accessible child  =  InvocationUtils.invokeAndWait(new Callable<Accessible>() {
1038                     @Override
1039                     public Accessible call() throws Exception {
1040                         return parentContextInnerTemp.getAccessibleChild(childIndexTemp);
1041                     }
1042                 }, ac);
1043                 if ( null != child ) {
1044                     final AccessibleContext childContext = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
1045                         @Override
1046                         public AccessibleContext call() throws Exception {
1047                             return child.getAccessibleContext();
1048                         }
1049                     }, ac);
1050                     if ( null != childContext ) {
1051                         AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
1052                             @Override
1053                             public AccessibleRole call() throws Exception {
1054                                 return childContext.getAccessibleRole();
1055                             }
1056                         }, ac);
1057                         if ( AccessibleRole.LABEL == childRole ) {
1058                             childX = getAccessibleXcoordFromContext (childContext);
1059                             childY = getAccessibleYcoordFromContext (childContext);
1060                             childWidth = getAccessibleWidthFromContext (childContext);
1061                             childHeight = getAccessibleHeightFromContext (childContext);
1062                             if ( (childX < testX) &&
1063                                  ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1064                                 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1065                                     @Override
1066                                     public String call() throws Exception {
1067                                         return childContext.getAccessibleName();
1068                                     }
1069                                 }, ac);
1070                                 if ( null != childName ) {
1071                                     debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned to the left of the object.");
1072                                     references.increment (childName);
1073                                     return childName;
1074                                 }
1075                                 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1076                                     @Override
1077                                     public String call() throws Exception {
1078                                         return childContext.getAccessibleDescription();
1079                                     }
1080                                 }, ac);
1081                                 if ( null != childDescription ) {
1082                                     debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned to the left of the object.");
1083                                     references.increment (childDescription);
1084                                     return childDescription;
1085                                 }
1086                             } else if ( (childY < targetY) &&
1087                                         ((childX <= targetX) && (targetX <= (childX + childWidth))) ) {
1088                                 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1089                                     @Override
1090                                     public String call() throws Exception {
1091                                         return childContext.getAccessibleName();
1092                                     }
1093                                 }, ac);
1094                                 if ( null != childName ) {
1095                                     debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned above the object.");
1096                                     references.increment (childName);
1097                                     return childName;
1098                                 }
1099                                 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1100                                     @Override
1101                                     public String call() throws Exception {
1102                                         return childContext.getAccessibleDescription();
1103                                     }
1104                                 }, ac);
1105                                 if ( null != childDescription ) {
1106                                     debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned above the object.");
1107                                     references.increment (childDescription);
1108                                     return childDescription;
1109                                 }
1110                             }
1111                         }
1112                     }
1113                 }
1114                 childIndex --;
1115             }
1116             childIndex = testIndex + 1;
1117             while (childIndex <= testIndexMax) {
1118                 final int childIndexTemp = childIndex;
1119                 final AccessibleContext parentContextInnerTemp = parentContext;
1120                 final Accessible child  =  InvocationUtils.invokeAndWait(new Callable<Accessible>() {
1121                     @Override
1122                     public Accessible call() throws Exception {
1123                         return parentContextInnerTemp.getAccessibleChild(childIndexTemp);
1124                     }
1125                 }, ac);
1126                 if ( null != child ) {
1127                     final AccessibleContext childContext = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
1128                         @Override
1129                         public AccessibleContext call() throws Exception {
1130                             return child.getAccessibleContext();
1131                         }
1132                     }, ac);
1133                     if ( null != childContext ) {
1134                         AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
1135                             @Override
1136                             public AccessibleRole call() throws Exception {
1137                                 return childContext.getAccessibleRole();
1138                             }
1139                         }, ac);
1140                         if ( AccessibleRole.LABEL == childRole ) {
1141                             childX = getAccessibleXcoordFromContext (childContext);
1142                             childY = getAccessibleYcoordFromContext (childContext);
1143                             childWidth = getAccessibleWidthFromContext (childContext);
1144                             childHeight = getAccessibleHeightFromContext (childContext);
1145                             if ( (childX < testX) &&
1146                                  ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1147                                 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1148                                     @Override
1149                                     public String call() throws Exception {
1150                                         return childContext.getAccessibleName();
1151                                     }
1152                                 }, ac);
1153                                 if ( null != childName ) {
1154                                     debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned to the left of the object.");
1155                                     references.increment (childName);
1156                                     return childName;
1157                                 }
1158                                 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1159                                     @Override
1160                                     public String call() throws Exception {
1161                                         return childContext.getAccessibleDescription();
1162                                     }
1163                                 }, ac);
1164                                 if ( null != childDescription ) {
1165                                     debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned to the left of the object.");
1166                                     references.increment (childDescription);
1167                                     return childDescription;
1168                                 }
1169                             } else if ( (childY < targetY) &&
1170                                         ((childX <= targetX) && (targetX <= (childX + childWidth))) ) {
1171                                 childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1172                                     @Override
1173                                     public String call() throws Exception {
1174                                         return childContext.getAccessibleName();
1175                                     }
1176                                 }, ac);
1177                                 if ( null != childName ) {
1178                                     debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a LABEL object positioned above the object.");
1179                                     references.increment (childName);
1180                                     return childName;
1181                                 }
1182                                 childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1183                                     @Override
1184                                     public String call() throws Exception {
1185                                         return childContext.getAccessibleDescription();
1186                                     }
1187                                 }, ac);
1188                                 if ( null != childDescription ) {
1189                                     debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a LABEL object positioned above the object.");
1190                                     references.increment (childDescription);
1191                                     return childDescription;
1192                                 }
1193                             }
1194                         }
1195                     }
1196                 }
1197                 childIndex ++;
1198             }
1199             /*
1200             Step 8:
1201             =======
1202             Special case for combo boxes and text objects, based on a
1203             similar special case I found in some of our internal JAWS code.
1204 
1205             Search for a button object that is positioned either just to the left
1206             or just above the object and get the Accessible Name of the button
1207             object.
1208             */
1209             if ( (AccessibleRole.TEXT == role) ||
1210                  (AccessibleRole.COMBO_BOX == role) ||
1211                  (bIsEditCombo) ) {
1212                 childIndex = testIndex - 1;
1213                 while (childIndex >= 0) {
1214                     final int childIndexTemp = childIndex;
1215                     final AccessibleContext parentContextInnerTemp = parentContext;
1216                     final Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
1217                         @Override
1218                         public Accessible call() throws Exception {
1219                             return parentContextInnerTemp.getAccessibleChild(childIndexTemp);
1220                         }
1221                     }, ac);
1222                     if ( null != child ) {
1223                         final AccessibleContext childContext = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
1224                             @Override
1225                             public AccessibleContext call() throws Exception {
1226                                 return child.getAccessibleContext();
1227                             }
1228                         }, ac);
1229                         if ( null != childContext ) {
1230                             AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
1231                                 @Override
1232                                 public AccessibleRole call() throws Exception {
1233                                     return childContext.getAccessibleRole();
1234                                 }
1235                             }, ac);
1236                             if ( ( AccessibleRole.PUSH_BUTTON == childRole ) ||
1237                                  ( AccessibleRole.TOGGLE_BUTTON == childRole )) {
1238                                 childX = getAccessibleXcoordFromContext (childContext);
1239                                 childY = getAccessibleYcoordFromContext (childContext);
1240                                 childWidth = getAccessibleWidthFromContext (childContext);
1241                                 childHeight = getAccessibleHeightFromContext (childContext);
1242                                 if ( (childX < testX) &&
1243                                      ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1244                                     childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1245                                         @Override
1246                                         public String call() throws Exception {
1247                                             return childContext.getAccessibleName();
1248                                         }
1249                                     }, ac);
1250                                     if ( null != childName ) {
1251                                         debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1252                                         references.increment (childName);
1253                                         return childName;
1254                                     }
1255                                     childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1256                                         @Override
1257                                         public String call() throws Exception {
1258                                             return childContext.getAccessibleDescription();
1259                                         }
1260                                     }, ac);
1261                                     if ( null != childDescription ) {
1262                                         debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1263                                         references.increment (childDescription);
1264                                         return childDescription;
1265                                     }
1266                                 }
1267                             }
1268                         }
1269                     }
1270                     childIndex --;
1271                 }
1272                 childIndex = testIndex + 1;
1273                 while (childIndex <= testIndexMax) {
1274                     final int childIndexTemp = childIndex;
1275                     final AccessibleContext parentContextInnerTemp = parentContext;
1276                     final Accessible child  =  InvocationUtils.invokeAndWait(new Callable<Accessible>() {
1277                         @Override
1278                         public Accessible call() throws Exception {
1279                             return parentContextInnerTemp.getAccessibleChild(childIndexTemp);
1280                         }
1281                     }, ac);
1282                     if ( null != child ) {
1283                         final AccessibleContext childContext = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
1284                             @Override
1285                             public AccessibleContext call() throws Exception {
1286                                 return child.getAccessibleContext();
1287                             }
1288                         }, ac);
1289                         if ( null != childContext ) {
1290                             AccessibleRole childRole = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
1291                                 @Override
1292                                 public AccessibleRole call() throws Exception {
1293                                     return childContext.getAccessibleRole();
1294                                 }
1295                             }, ac);
1296                             if ( ( AccessibleRole.PUSH_BUTTON == childRole ) ||
1297                                     ( AccessibleRole.TOGGLE_BUTTON == childRole ) ) {
1298                                 childX = getAccessibleXcoordFromContext (childContext);
1299                                 childY = getAccessibleYcoordFromContext (childContext);
1300                                 childWidth = getAccessibleWidthFromContext (childContext);
1301                                 childHeight = getAccessibleHeightFromContext (childContext);
1302                                 if ( (childX < testX) &&
1303                                      ((childY <= targetY) && (targetY <= (childY + childHeight))) ) {
1304                                     childName = InvocationUtils.invokeAndWait(new Callable<String>() {
1305                                         @Override
1306                                         public String call() throws Exception {
1307                                             return childContext.getAccessibleName();
1308                                         }
1309                                     }, ac);
1310                                     if ( null != childName ) {
1311                                         debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Name of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1312                                         references.increment (childName);
1313                                         return childName;
1314                                     }
1315                                     childDescription = InvocationUtils.invokeAndWait(new Callable<String>() {
1316                                         @Override
1317                                         public String call() throws Exception {
1318                                             return childContext.getAccessibleDescription();
1319                                         }
1320                                     }, ac);
1321                                     if ( null != childDescription ) {
1322                                         debugString ("[INFO]: bk -- The Virtual Accessible Name was obtained from Accessible Description of a PUSH_BUTTON or TOGGLE_BUTTON object positioned to the left of the object.");
1323                                         references.increment (childDescription);
1324                                         return childDescription;
1325                                     }
1326                                 }
1327                             }
1328                         }
1329                     }
1330                     childIndex ++;
1331                 }
1332             }
1333             return null;
1334         } else {
1335             debugString ("[ERROR]: AccessBridge::getVirtualAccessibleNameFromContext error - ac == null.");
1336             return null;
1337         }
1338     }
1339 
1340     /**
1341      * returns the AccessibleDescription from an AccessibleContext
1342      */
1343     private String getAccessibleDescriptionFromContext(final AccessibleContext ac) {
1344         if (ac != null) {
1345             String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1346                 @Override
1347                 public String call() throws Exception {
1348                     return ac.getAccessibleDescription();
1349                 }
1350             }, ac);
1351             if (s != null) {
1352                 references.increment(s);
1353                 debugString("[INFO]: Returning AccessibleDescription from Context: " + s);
1354                 return s;
1355             }
1356         } else {
1357             debugString("[ERROR]: getAccessibleDescriptionFromContext; ac = null");
1358         }
1359         return null;
1360     }
1361 
1362     /**
1363      * returns the AccessibleRole from an AccessibleContext
1364      */
1365     private String getAccessibleRoleStringFromContext(final AccessibleContext ac) {
1366         if (ac != null) {
1367             AccessibleRole role = InvocationUtils.invokeAndWait(new Callable<AccessibleRole>() {
1368                 @Override
1369                 public AccessibleRole call() throws Exception {
1370                     return ac.getAccessibleRole();
1371                 }
1372             }, ac);
1373             if (role != null) {
1374                 String s = role.toDisplayString(Locale.US);
1375                 if (s != null) {
1376                     references.increment(s);
1377                     debugString("[INFO]: Returning AccessibleRole from Context: " + s);
1378                     return s;
1379                 }
1380             }
1381         } else {
1382             debugString("[ERROR]: getAccessibleRoleStringFromContext; ac = null");
1383         }
1384         return null;
1385     }
1386 
1387     /**
1388      * return the AccessibleRole from an AccessibleContext in the en_US locale
1389      */
1390     private String getAccessibleRoleStringFromContext_en_US(final AccessibleContext ac) {
1391         return getAccessibleRoleStringFromContext(ac);
1392     }
1393 
1394     /**
1395      * return the AccessibleStates from an AccessibleContext
1396      */
1397     private String getAccessibleStatesStringFromContext(final AccessibleContext ac) {
1398         if (ac != null) {
1399             AccessibleStateSet stateSet = InvocationUtils.invokeAndWait(new Callable<AccessibleStateSet>() {
1400                 @Override
1401                 public AccessibleStateSet call() throws Exception {
1402                     return ac.getAccessibleStateSet();
1403                 }
1404             }, ac);
1405             if (stateSet != null) {
1406                 String s = stateSet.toString();
1407                 if (s != null &&
1408                     s.indexOf(AccessibleState.MANAGES_DESCENDANTS.toDisplayString(Locale.US)) == -1) {
1409                     // Indicate whether this component manages its own
1410                     // children
1411                     AccessibleRole role = InvocationUtils.invokeAndWait(() -> {
1412                             return ac.getAccessibleRole();
1413                         }, ac);
1414                     if (role == AccessibleRole.LIST ||
1415                         role == AccessibleRole.TABLE ||
1416                         role == AccessibleRole.TREE) {
1417                         s += ",";
1418                         s += AccessibleState.MANAGES_DESCENDANTS.toDisplayString(Locale.US);
1419                     }
1420                     references.increment(s);
1421                     debugString("[INFO]: Returning AccessibleStateSet from Context: " + s);
1422                     return s;
1423                 }
1424             }
1425         } else {
1426             debugString("[ERROR]: getAccessibleStatesStringFromContext; ac = null");
1427         }
1428         return null;
1429     }
1430 
1431     /**
1432      * returns the AccessibleStates from an AccessibleContext in the en_US locale
1433      */
1434     private String getAccessibleStatesStringFromContext_en_US(final AccessibleContext ac) {
1435         if (ac != null) {
1436             AccessibleStateSet stateSet = InvocationUtils.invokeAndWait(new Callable<AccessibleStateSet>() {
1437                 @Override
1438                 public AccessibleStateSet call() throws Exception {
1439                     return ac.getAccessibleStateSet();
1440                 }
1441             }, ac);
1442             if (stateSet != null) {
1443                 String s = "";
1444                 AccessibleState[] states = stateSet.toArray();
1445                 if (states != null && states.length > 0) {
1446                     s = states[0].toDisplayString(Locale.US);
1447                     for (int i = 1; i < states.length; i++) {
1448                         s = s + "," + states[i].toDisplayString(Locale.US);
1449                     }
1450                 }
1451                 references.increment(s);
1452                 debugString("[INFO]: Returning AccessibleStateSet en_US from Context: " + s);
1453                 return s;
1454             }
1455         }
1456         debugString("[ERROR]: getAccessibleStatesStringFromContext; ac = null");
1457         return null;
1458     }
1459 
1460     private int getNonVisibleChildrenCountTillIndex(AccessibleContext parentAC, int index) {
1461         if (parentAC != null && index >= 0 && index < parentAC.getAccessibleChildrenCount()) {
1462             int nonVisibleChildrenCount = 0;
1463             for (int i = 0; i <= index; i++) {
1464                 if (!parentAC.getAccessibleChild(i).getAccessibleContext().getAccessibleStateSet().contains(AccessibleState.VISIBLE)) {
1465                     nonVisibleChildrenCount++;
1466                 }
1467             }
1468             return nonVisibleChildrenCount;
1469         }
1470         return 0;
1471     }
1472 
1473     private Accessible getVisibleChildAtIndex(AccessibleContext parentAC, int index) {
1474         if (parentAC != null && index >= 0 && index < parentAC.getAccessibleChildrenCount()) {
1475             int visibleIndex = -1;
1476             int childrenCount = parentAC.getAccessibleChildrenCount();
1477             for (int i = 0; i <= childrenCount; i++) {
1478                 Accessible child = parentAC.getAccessibleChild(i);
1479                 if (child != null) {
1480                     AccessibleContext ac = child.getAccessibleContext();
1481                     if (ac != null && ac.getAccessibleStateSet().contains(AccessibleState.VISIBLE)) {
1482                         visibleIndex++;
1483                     }
1484                     if (visibleIndex == index) {
1485                         return child;
1486                     }
1487                 }
1488             }
1489         }
1490         return null;
1491     }
1492     /**
1493      * returns the AccessibleParent from an AccessibleContext
1494      */
1495     private AccessibleContext getAccessibleParentFromContext(final AccessibleContext ac) {
1496         if (ac==null)
1497             return null;
1498         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
1499             @Override
1500             public AccessibleContext call() throws Exception {
1501                 Accessible a = ac.getAccessibleParent();
1502                 if (a != null) {
1503                     AccessibleContext apc = a.getAccessibleContext();
1504                     if (apc != null) {
1505                         return apc;
1506                     }
1507                 }
1508                 return null;
1509             }
1510         }, ac);
1511     }
1512 
1513     /**
1514      * returns the AccessibleIndexInParent from an AccessibleContext
1515      */
1516     private int getAccessibleIndexInParentFromContext(final AccessibleContext ac) {
1517         if (ac==null)
1518             return -1;
1519         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
1520             @Override
1521             public Integer call() throws Exception {
1522                 int indexInParent = ac.getAccessibleIndexInParent();
1523                 Accessible parent = ac.getAccessibleParent();
1524                 if (parent != null) {
1525                     indexInParent -= getNonVisibleChildrenCountTillIndex(parent.getAccessibleContext(), indexInParent);
1526                 }
1527                 return indexInParent;
1528             }
1529         }, ac);
1530     }
1531 
1532     /**
1533      * returns the AccessibleChild count from an AccessibleContext
1534      */
1535     private int getAccessibleChildrenCountFromContext(final AccessibleContext ac) {
1536         if (ac==null)
1537             return -1;
1538         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
1539             @Override
1540             public Integer call() throws Exception {
1541                 int childrenCount = ac.getAccessibleChildrenCount();
1542                 return childrenCount - getNonVisibleChildrenCountTillIndex(ac, childrenCount - 1);
1543             }
1544         }, ac);
1545     }
1546 
1547     /**
1548      * returns the AccessibleChild Context from an AccessibleContext
1549      */
1550     private AccessibleContext getAccessibleChildFromContext(final AccessibleContext ac, final int index) {
1551 
1552         if (ac == null) {
1553             return null;
1554         }
1555 
1556         final JTable table = InvocationUtils.invokeAndWait(new Callable<JTable>() {
1557             @Override
1558             public JTable call() throws Exception {
1559                 // work-around for AccessibleJTable.getCurrentAccessibleContext returning
1560                 // wrong renderer component when cell contains more than one component
1561                 Accessible parent = ac.getAccessibleParent();
1562                 if (parent != null) {
1563                     int indexInParent = ac.getAccessibleIndexInParent();
1564                     Accessible child =
1565                             parent.getAccessibleContext().getAccessibleChild(indexInParent);
1566                     if (child instanceof JTable) {
1567                         return (JTable) child;
1568                     }
1569                 }
1570                 return null;
1571             }
1572         }, ac);
1573 
1574         if (table == null) {
1575             return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
1576                 @Override
1577                 public AccessibleContext call() throws Exception {
1578                     Accessible a = getVisibleChildAtIndex(ac, index);
1579                     if (a != null) {
1580                         return a.getAccessibleContext();
1581                     }
1582                     return null;
1583                 }
1584             }, ac);
1585         }
1586 
1587         final AccessibleTable at = getAccessibleTableFromContext(ac);
1588 
1589         final int row = getAccessibleTableRow(at, index);
1590         final int column = getAccessibleTableColumn(at, index);
1591 
1592         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
1593             @Override
1594             public AccessibleContext call() throws Exception {
1595                 TableCellRenderer renderer = table.getCellRenderer(row, column);
1596                 if (renderer == null) {
1597                     Class<?> columnClass = table.getColumnClass(column);
1598                     renderer = table.getDefaultRenderer(columnClass);
1599                 }
1600                 Component component =
1601                         renderer.getTableCellRendererComponent(table, table.getValueAt(row, column),
1602                                 false, false, row, column);
1603                 if (component instanceof Accessible) {
1604                     return component.getAccessibleContext();
1605                 }
1606                 return null;
1607             }
1608         }, ac);
1609     }
1610 
1611     /**
1612      * returns the AccessibleComponent bounds on screen from an AccessibleContext
1613      */
1614     private Rectangle getAccessibleBoundsOnScreenFromContext(final AccessibleContext ac) {
1615         if(ac==null)
1616             return null;
1617         return InvocationUtils.invokeAndWait(new Callable<Rectangle>() {
1618             @Override
1619             public Rectangle call() throws Exception {
1620                 AccessibleComponent acmp = ac.getAccessibleComponent();
1621                 if (acmp != null) {
1622                     Rectangle r = acmp.getBounds();
1623                     if (r != null) {
1624                         try {
1625                             Point p = acmp.getLocationOnScreen();
1626                             if (p != null) {
1627                                 r.x = p.x;
1628                                 r.y = p.y;
1629                                 return r;
1630                             }
1631                         } catch (Exception e) {
1632                             return null;
1633                         }
1634                     }
1635                 }
1636                 return null;
1637             }
1638         }, ac);
1639     }
1640 
1641     /**
1642      * returns the AccessibleComponent x-coord from an AccessibleContext
1643      */
1644     private int getAccessibleXcoordFromContext(AccessibleContext ac) {
1645         if (ac != null) {
1646             Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1647             if (r != null) {
1648                 debugString("[INFO]: Returning Accessible x coord from Context: " + r.x);
1649                 return r.x;
1650             }
1651         } else {
1652             debugString("[ERROR]: getAccessibleXcoordFromContext ac = null");
1653         }
1654         return -1;
1655     }
1656 
1657     /**
1658      * returns the AccessibleComponent y-coord from an AccessibleContext
1659      */
1660     private int getAccessibleYcoordFromContext(AccessibleContext ac) {
1661         debugString("[INFO]: getAccessibleYcoordFromContext() called");
1662         if (ac != null) {
1663             Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1664             if (r != null) {
1665                 return r.y;
1666             }
1667         } else {
1668         debugString("[ERROR]: getAccessibleYcoordFromContext; ac = null");
1669         }
1670         return -1;
1671     }
1672 
1673     /**
1674      * returns the AccessibleComponent height from an AccessibleContext
1675      */
1676     private int getAccessibleHeightFromContext(AccessibleContext ac) {
1677         if (ac != null) {
1678             Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1679             if (r != null) {
1680                 return r.height;
1681             }
1682         } else {
1683             debugString("[ERROR]: getAccessibleHeightFromContext; ac = null");
1684         }
1685         return -1;
1686     }
1687 
1688     /**
1689      * returns the AccessibleComponent width from an AccessibleContext
1690      */
1691     private int getAccessibleWidthFromContext(AccessibleContext ac) {
1692         if (ac != null) {
1693             Rectangle r = getAccessibleBoundsOnScreenFromContext(ac);
1694             if (r != null) {
1695                 return r.width;
1696             }
1697         } else {
1698             debugString("[ERROR]: getAccessibleWidthFromContext; ac = null");
1699         }
1700         return -1;
1701     }
1702 
1703 
1704     /**
1705      * returns the AccessibleComponent from an AccessibleContext
1706      */
1707     private AccessibleComponent getAccessibleComponentFromContext(AccessibleContext ac) {
1708         if (ac != null) {
1709             AccessibleComponent acmp = InvocationUtils.invokeAndWait(() -> {
1710                     return ac.getAccessibleComponent();
1711                 }, ac);
1712             if (acmp != null) {
1713                 debugString("[INFO]: Returning AccessibleComponent Context");
1714                 return acmp;
1715             }
1716         } else {
1717             debugString("[ERROR]: getAccessibleComponentFromContext; ac = null");
1718         }
1719         return null;
1720     }
1721 
1722     /**
1723      * returns the AccessibleAction from an AccessibleContext
1724      */
1725     private AccessibleAction getAccessibleActionFromContext(final AccessibleContext ac) {
1726         debugString("[INFO]: Returning AccessibleAction Context");
1727         return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleAction>() {
1728             @Override
1729             public AccessibleAction call() throws Exception {
1730                 return ac.getAccessibleAction();
1731             }
1732         }, ac);
1733     }
1734 
1735     /**
1736      * returns the AccessibleSelection from an AccessibleContext
1737      */
1738     private AccessibleSelection getAccessibleSelectionFromContext(final AccessibleContext ac) {
1739         return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleSelection>() {
1740             @Override
1741             public AccessibleSelection call() throws Exception {
1742                 return ac.getAccessibleSelection();
1743             }
1744         }, ac);
1745     }
1746 
1747     /**
1748      * return the AccessibleText from an AccessibleContext
1749      */
1750     private AccessibleText getAccessibleTextFromContext(final AccessibleContext ac) {
1751         return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleText>() {
1752             @Override
1753             public AccessibleText call() throws Exception {
1754                 return ac.getAccessibleText();
1755             }
1756         }, ac);
1757     }
1758 
1759     /**
1760      * return the AccessibleComponent from an AccessibleContext
1761      */
1762     private AccessibleValue getAccessibleValueFromContext(final AccessibleContext ac) {
1763         return ac == null ? null : InvocationUtils.invokeAndWait(new Callable<AccessibleValue>() {
1764             @Override
1765             public AccessibleValue call() throws Exception {
1766                 return ac.getAccessibleValue();
1767             }
1768         }, ac);
1769     }
1770 
1771     /* ===== AccessibleText methods ===== */
1772 
1773     /**
1774      * returns the bounding rectangle for the text cursor
1775      * XXX
1776      */
1777     private Rectangle getCaretLocation(final AccessibleContext ac) {
1778     debugString("[INFO]: getCaretLocation");
1779         if (ac==null)
1780             return null;
1781         return InvocationUtils.invokeAndWait(new Callable<Rectangle>() {
1782             @Override
1783             public Rectangle call() throws Exception {
1784                 // workaround for JAAPI not returning cursor bounding rectangle
1785                 Rectangle r = null;
1786                 Accessible parent = ac.getAccessibleParent();
1787                 if (parent instanceof Accessible) {
1788                     int indexInParent = ac.getAccessibleIndexInParent();
1789                     Accessible child =
1790                             parent.getAccessibleContext().getAccessibleChild(indexInParent);
1791 
1792                     if (child instanceof JTextComponent) {
1793                         JTextComponent text = (JTextComponent) child;
1794                         try {
1795                             r = text.modelToView2D(text.getCaretPosition()).getBounds();
1796                             if (r != null) {
1797                                 Point p = text.getLocationOnScreen();
1798                                 r.translate(p.x, p.y);
1799                             }
1800                         } catch (BadLocationException ble) {
1801                         }
1802                     }
1803                 }
1804                 return r;
1805             }
1806         }, ac);
1807     }
1808 
1809     /**
1810      * returns the x-coordinate for the text cursor rectangle
1811      */
1812     private int getCaretLocationX(AccessibleContext ac) {
1813         Rectangle r = getCaretLocation(ac);
1814         if (r != null) {
1815             return r.x;
1816         } else {
1817             return -1;
1818         }
1819     }
1820 
1821     /**
1822      * returns the y-coordinate for the text cursor rectangle
1823      */
1824     private int getCaretLocationY(AccessibleContext ac) {
1825         Rectangle r = getCaretLocation(ac);
1826         if (r != null) {
1827             return r.y;
1828         } else {
1829             return -1;
1830         }
1831     }
1832 
1833     /**
1834      * returns the height for the text cursor rectangle
1835      */
1836     private int getCaretLocationHeight(AccessibleContext ac) {
1837         Rectangle r = getCaretLocation(ac);
1838         if (r != null) {
1839             return r.height;
1840         } else {
1841             return -1;
1842         }
1843     }
1844 
1845     /**
1846      * returns the width for the text cursor rectangle
1847      */
1848     private int getCaretLocationWidth(AccessibleContext ac) {
1849         Rectangle r = getCaretLocation(ac);
1850         if (r != null) {
1851             return r.width;
1852         } else {
1853             return -1;
1854         }
1855     }
1856 
1857     /**
1858      * returns the character count from an AccessibleContext
1859      */
1860     private int getAccessibleCharCountFromContext(final AccessibleContext ac) {
1861         if (ac==null)
1862             return -1;
1863         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
1864             @Override
1865             public Integer call() throws Exception {
1866                 AccessibleText at = ac.getAccessibleText();
1867                 if (at != null) {
1868                     return at.getCharCount();
1869                 }
1870                 return -1;
1871             }
1872         }, ac);
1873     }
1874 
1875     /**
1876      * returns the caret position from an AccessibleContext
1877      */
1878     private int getAccessibleCaretPositionFromContext(final AccessibleContext ac) {
1879         if (ac==null)
1880             return -1;
1881         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
1882             @Override
1883             public Integer call() throws Exception {
1884                 AccessibleText at = ac.getAccessibleText();
1885                 if (at != null) {
1886                     return at.getCaretPosition();
1887                 }
1888                 return -1;
1889             }
1890         }, ac);
1891     }
1892 
1893     /**
1894      * Return the index at a specific point from an AccessibleContext
1895      * Point(x, y) is in screen coordinates.
1896      */
1897     private int getAccessibleIndexAtPointFromContext(final AccessibleContext ac,
1898                                                     final int x, final int y) {
1899         debugString("[INFO]: getAccessibleIndexAtPointFromContext: x = "+x+"; y = "+y);
1900         if (ac==null)
1901             return -1;
1902         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
1903             @Override
1904             public Integer call() throws Exception {
1905                 AccessibleText at = ac.getAccessibleText();
1906                 AccessibleComponent acomp = ac.getAccessibleComponent();
1907                 if (at != null && acomp != null) {
1908                     // Convert x and y from screen coordinates to
1909                     // local coordinates.
1910                     try {
1911                         Point p = acomp.getLocationOnScreen();
1912                         int x1, y1;
1913                         if (p != null) {
1914                             x1 = x - p.x;
1915                             if (x1 < 0) {
1916                                 x1 = 0;
1917                             }
1918                             y1 = y - p.y;
1919                             if (y1 < 0) {
1920                                 y1 = 0;
1921                             }
1922 
1923                             Point newPoint = new Point(x1, y1);
1924                             int indexAtPoint = at.getIndexAtPoint(new Point(x1, y1));
1925                             return indexAtPoint;
1926                         }
1927                     } catch (Exception e) {
1928                     }
1929                 }
1930                 return -1;
1931             }
1932         }, ac);
1933     }
1934 
1935     /**
1936      * return the letter at a specific point from an AccessibleContext
1937      */
1938     private String getAccessibleLetterAtIndexFromContext(final AccessibleContext ac, final int index) {
1939         if (ac != null) {
1940             String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1941                 @Override
1942                 public String call() throws Exception {
1943                     AccessibleText at = ac.getAccessibleText();
1944                     if (at == null) return null;
1945                     return at.getAtIndex(AccessibleText.CHARACTER, index);
1946                 }
1947             }, ac);
1948             if (s != null) {
1949                 references.increment(s);
1950                 return s;
1951             }
1952         } else {
1953             debugString("[ERROR]: getAccessibleLetterAtIndexFromContext; ac = null");
1954         }
1955         return null;
1956     }
1957 
1958     /**
1959      * return the word at a specific point from an AccessibleContext
1960      */
1961     private String getAccessibleWordAtIndexFromContext(final AccessibleContext ac, final int index) {
1962         if (ac != null) {
1963             String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1964                 @Override
1965                 public String call() throws Exception {
1966                     AccessibleText at = ac.getAccessibleText();
1967                     if (at == null) return null;
1968                     return at.getAtIndex(AccessibleText.WORD, index);
1969                 }
1970             }, ac);
1971             if (s != null) {
1972                 references.increment(s);
1973                 return s;
1974             }
1975         } else {
1976             debugString("[ERROR]: getAccessibleWordAtIndexFromContext; ac = null");
1977         }
1978         return null;
1979     }
1980 
1981     /**
1982      * return the sentence at a specific point from an AccessibleContext
1983      */
1984     private String getAccessibleSentenceAtIndexFromContext(final AccessibleContext ac, final int index) {
1985         if (ac != null) {
1986             String s = InvocationUtils.invokeAndWait(new Callable<String>() {
1987                 @Override
1988                 public String call() throws Exception {
1989                     AccessibleText at = ac.getAccessibleText();
1990                     if (at == null) return null;
1991                     return at.getAtIndex(AccessibleText.SENTENCE, index);
1992                 }
1993             }, ac);
1994             if (s != null) {
1995                 references.increment(s);
1996                 return s;
1997             }
1998         } else {
1999             debugString("[ERROR]: getAccessibleSentenceAtIndexFromContext; ac = null");
2000         }
2001         return null;
2002     }
2003 
2004     /**
2005      * return the text selection start from an AccessibleContext
2006      */
2007     private int getAccessibleTextSelectionStartFromContext(final AccessibleContext ac) {
2008         if (ac == null) return -1;
2009         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
2010             @Override
2011             public Integer call() throws Exception {
2012                 AccessibleText at = ac.getAccessibleText();
2013                 if (at != null) {
2014                     return at.getSelectionStart();
2015                 }
2016                 return -1;
2017             }
2018         }, ac);
2019     }
2020 
2021     /**
2022      * return the text selection end from an AccessibleContext
2023      */
2024     private int getAccessibleTextSelectionEndFromContext(final AccessibleContext ac) {
2025         if (ac == null)
2026             return -1;
2027         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
2028             @Override
2029             public Integer call() throws Exception {
2030                 AccessibleText at = ac.getAccessibleText();
2031                 if (at != null) {
2032                     return at.getSelectionEnd();
2033                 }
2034                 return -1;
2035             }
2036         }, ac);
2037     }
2038 
2039     /**
2040      * return the selected text from an AccessibleContext
2041      */
2042     private String getAccessibleTextSelectedTextFromContext(final AccessibleContext ac) {
2043         if (ac != null) {
2044             String s = InvocationUtils.invokeAndWait(new Callable<String>() {
2045                 @Override
2046                 public String call() throws Exception {
2047                     AccessibleText at = ac.getAccessibleText();
2048                     if (at == null) return null;
2049                     return at.getSelectedText();
2050                 }
2051             }, ac);
2052             if (s != null) {
2053                 references.increment(s);
2054                 return s;
2055             }
2056         } else {
2057             debugString("[ERROR]: getAccessibleTextSelectedTextFromContext; ac = null");
2058         }
2059         return null;
2060     }
2061 
2062     /**
2063      * return the attribute string at a given index from an AccessibleContext
2064      */
2065     private String getAccessibleAttributesAtIndexFromContext(final AccessibleContext ac,
2066                                                              final int index) {
2067         if (ac == null)
2068             return null;
2069         AttributeSet as = InvocationUtils.invokeAndWait(new Callable<AttributeSet>() {
2070             @Override
2071             public AttributeSet call() throws Exception {
2072                 AccessibleText at = ac.getAccessibleText();
2073                 if (at != null) {
2074                     return at.getCharacterAttribute(index);
2075                 }
2076                 return null;
2077             }
2078         }, ac);
2079         String s = expandStyleConstants(as);
2080         if (s != null) {
2081             references.increment(s);
2082             return s;
2083         }
2084         return null;
2085     }
2086 
2087     /**
2088      * Get line info: left index of line
2089      *
2090      * algorithm:  cast back, doubling each time,
2091      *             'till find line boundaries
2092      *
2093      * return -1 if we can't get the info (e.g. index or at passed in
2094      * is bogus; etc.)
2095      */
2096     private int getAccessibleTextLineLeftBoundsFromContext(final AccessibleContext ac,
2097                                                           final int index) {
2098         if (ac == null)
2099             return -1;
2100         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
2101             @Override
2102             public Integer call() throws Exception {
2103                 AccessibleText at = ac.getAccessibleText();
2104                 if (at != null) {
2105                     int lineStart;
2106                     int offset;
2107                     Rectangle charRect;
2108                     Rectangle indexRect = at.getCharacterBounds(index);
2109                     int textLen = at.getCharCount();
2110                     if (indexRect == null) {
2111                         return -1;
2112                     }
2113                     // find the start of the line
2114                     //
2115                     offset = 1;
2116                     lineStart = index - offset < 0 ? 0 : index - offset;
2117                     charRect = at.getCharacterBounds(lineStart);
2118                     // slouch behind beginning of line
2119                     while (charRect != null
2120                             && charRect.y >= indexRect.y
2121                             && lineStart > 0) {
2122                         offset = offset << 1;
2123                         lineStart = index - offset < 0 ? 0 : index - offset;
2124                         charRect = at.getCharacterBounds(lineStart);
2125                     }
2126                     if (lineStart == 0) {    // special case: we're on the first line!
2127                         // we found it!
2128                     } else {
2129                         offset = offset >> 1;   // know boundary within last expansion
2130                         // ground forward to beginning of line
2131                         while (offset > 0) {
2132                             charRect = at.getCharacterBounds(lineStart + offset);
2133                             if (charRect.y < indexRect.y) { // still before line
2134                                 lineStart += offset;
2135                             } else {
2136                                 // leave lineStart alone, it's close!
2137                             }
2138                             offset = offset >> 1;
2139                         }
2140                         // subtract one 'cause we're already too far...
2141                         lineStart += 1;
2142                     }
2143                     return lineStart;
2144                 }
2145                 return -1;
2146             }
2147         }, ac);
2148     }
2149 
2150     /**
2151      * Get line info: right index of line
2152      *
2153      * algorithm:  cast back, doubling each time,
2154      *             'till find line boundaries
2155      *
2156      * return -1 if we can't get the info (e.g. index or at passed in
2157      * is bogus; etc.)
2158      */
2159     private int getAccessibleTextLineRightBoundsFromContext(final AccessibleContext ac, final int index) {
2160         if(ac == null)
2161             return -1;
2162         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
2163             @Override
2164             public Integer call() throws Exception {
2165                 AccessibleText at = ac.getAccessibleText();
2166                 if (at != null) {
2167                     int lineEnd;
2168                     int offset;
2169                     Rectangle charRect;
2170                     Rectangle indexRect = at.getCharacterBounds(index);
2171                     int textLen = at.getCharCount();
2172                     if (indexRect == null) {
2173                         return -1;
2174                     }
2175                     // find the end of the line
2176                     //
2177                     offset = 1;
2178                     lineEnd = index + offset > textLen - 1
2179                             ? textLen - 1 : index + offset;
2180                     charRect = at.getCharacterBounds(lineEnd);
2181                     // push past end of line
2182                     while (charRect != null &&
2183                             charRect.y <= indexRect.y &&
2184                             lineEnd < textLen - 1) {
2185                         offset = offset << 1;
2186                         lineEnd = index + offset > textLen - 1
2187                                 ? textLen - 1 : index + offset;
2188                         charRect = at.getCharacterBounds(lineEnd);
2189                     }
2190                     if (lineEnd == textLen - 1) {    // special case: on the last line!
2191                         // we found it!
2192                     } else {
2193                         offset = offset >> 1;   // know boundary within last expansion
2194                         // pull back to end of line
2195                         while (offset > 0) {
2196                             charRect = at.getCharacterBounds(lineEnd - offset);
2197                             if (charRect.y > indexRect.y) { // still beyond line
2198                                 lineEnd -= offset;
2199                             } else {
2200                                 // leave lineEnd alone, it's close!
2201                             }
2202                             offset = offset >> 1;
2203                         }
2204                         // subtract one 'cause we're already too far...
2205                         lineEnd -= 1;
2206                     }
2207                     return lineEnd;
2208                 }
2209                 return -1;
2210             }
2211         }, ac);
2212     }
2213 
2214     /**
2215      * Get a range of text; null if indicies are bogus
2216      */
2217     private String getAccessibleTextRangeFromContext(final AccessibleContext ac,
2218                                                     final int start, final int end) {
2219         String s = InvocationUtils.invokeAndWait(new Callable<String>() {
2220             @Override
2221             public String call() throws Exception {
2222                 if (ac != null) {
2223                     AccessibleText at = ac.getAccessibleText();
2224                     if (at != null) {
2225                         // start - end is inclusive
2226                         if (start > end) {
2227                             return null;
2228                         }
2229                         if (end >= at.getCharCount()) {
2230                             return null;
2231                         }
2232                         StringBuffer buf = new StringBuffer(end - start + 1);
2233                         for (int i = start; i <= end; i++) {
2234                             buf.append(at.getAtIndex(AccessibleText.CHARACTER, i));
2235                         }
2236                         return buf.toString();
2237                     }
2238                 }
2239                 return null;
2240             }
2241         }, ac);
2242         if (s != null) {
2243             references.increment(s);
2244             return s;
2245         } else {
2246             return null;
2247         }
2248     }
2249 
2250     /**
2251      * return the AttributeSet object at a given index from an AccessibleContext
2252      */
2253     private AttributeSet getAccessibleAttributeSetAtIndexFromContext(final AccessibleContext ac,
2254                                                                     final int index) {
2255         return InvocationUtils.invokeAndWait(new Callable<AttributeSet>() {
2256             @Override
2257             public AttributeSet call() throws Exception {
2258                 if (ac != null) {
2259                     AccessibleText at = ac.getAccessibleText();
2260                     if (at != null) {
2261                         AttributeSet as = at.getCharacterAttribute(index);
2262                         if (as != null) {
2263                             AccessBridge.this.references.increment(as);
2264                             return as;
2265                         }
2266                     }
2267                 }
2268                 return null;
2269             }
2270         }, ac);
2271     }
2272 
2273 
2274     /**
2275      * return the bounding rectangle at index from an AccessibleContext
2276      */
2277     private Rectangle getAccessibleTextRectAtIndexFromContext(final AccessibleContext ac,
2278                                                         final int index) {
2279         // want to do this in global coords, so need to combine w/ac global coords
2280         Rectangle r = InvocationUtils.invokeAndWait(new Callable<Rectangle>() {
2281             @Override
2282             public Rectangle call() throws Exception {
2283                 // want to do this in global coords, so need to combine w/ac global coords
2284                 if (ac != null) {
2285                     AccessibleText at = ac.getAccessibleText();
2286                     if (at != null) {
2287                         Rectangle rect = at.getCharacterBounds(index);
2288                         if (rect != null) {
2289                             String s = at.getAtIndex(AccessibleText.CHARACTER, index);
2290                             if (s != null && s.equals("\n")) {
2291                                 rect.width = 0;
2292                             }
2293                             return rect;
2294                         }
2295                     }
2296                 }
2297                 return null;
2298             }
2299         }, ac);
2300         Rectangle acRect = getAccessibleBoundsOnScreenFromContext(ac);
2301         if (r != null && acRect != null) {
2302             r.translate(acRect.x, acRect.y);
2303             return r;
2304         }
2305         return null;
2306     }
2307 
2308     /**
2309      * return the AccessibleText character x-coord at index from an AccessibleContext
2310      */
2311     private int getAccessibleXcoordTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2312         if (ac != null) {
2313             Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2314             if (r != null) {
2315                 return r.x;
2316             }
2317         } else {
2318             debugString("[ERROR]: getAccessibleXcoordTextRectAtIndexFromContext; ac = null");
2319         }
2320         return -1;
2321     }
2322 
2323     /**
2324      * return the AccessibleText character y-coord at index from an AccessibleContext
2325      */
2326     private int getAccessibleYcoordTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2327         if (ac != null) {
2328             Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2329             if (r != null) {
2330                 return r.y;
2331             }
2332         } else {
2333             debugString("[ERROR]: getAccessibleYcoordTextRectAtIndexFromContext; ac = null");
2334         }
2335         return -1;
2336     }
2337 
2338     /**
2339      * return the AccessibleText character height at index from an AccessibleContext
2340      */
2341     private int getAccessibleHeightTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2342         if (ac != null) {
2343             Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2344             if (r != null) {
2345                 return r.height;
2346             }
2347         } else {
2348             debugString("[ERROR]: getAccessibleHeightTextRectAtIndexFromContext; ac = null");
2349         }
2350         return -1;
2351     }
2352 
2353     /**
2354      * return the AccessibleText character width at index from an AccessibleContext
2355      */
2356     private int getAccessibleWidthTextRectAtIndexFromContext(AccessibleContext ac, int index) {
2357         if (ac != null) {
2358             Rectangle r = getAccessibleTextRectAtIndexFromContext(ac, index);
2359             if (r != null) {
2360                 return r.width;
2361             }
2362         } else {
2363             debugString("[ERROR]: getAccessibleWidthTextRectAtIndexFromContext; ac = null");
2364         }
2365         return -1;
2366     }
2367 
2368     /* ===== AttributeSet methods for AccessibleText ===== */
2369 
2370     /**
2371      * return the bold setting from an AttributeSet
2372      */
2373     private boolean getBoldFromAttributeSet(AttributeSet as) {
2374         if (as != null) {
2375             return StyleConstants.isBold(as);
2376         } else {
2377             debugString("[ERROR]: getBoldFromAttributeSet; as = null");
2378         }
2379         return false;
2380     }
2381 
2382     /**
2383      * return the italic setting from an AttributeSet
2384      */
2385     private boolean getItalicFromAttributeSet(AttributeSet as) {
2386         if (as != null) {
2387             return StyleConstants.isItalic(as);
2388         } else {
2389             debugString("[ERROR]: getItalicFromAttributeSet; as = null");
2390         }
2391         return false;
2392     }
2393 
2394     /**
2395      * return the underline setting from an AttributeSet
2396      */
2397     private boolean getUnderlineFromAttributeSet(AttributeSet as) {
2398         if (as != null) {
2399             return StyleConstants.isUnderline(as);
2400         } else {
2401             debugString("[ERROR]: getUnderlineFromAttributeSet; as = null");
2402         }
2403         return false;
2404     }
2405 
2406     /**
2407      * return the strikethrough setting from an AttributeSet
2408      */
2409     private boolean getStrikethroughFromAttributeSet(AttributeSet as) {
2410         if (as != null) {
2411             return StyleConstants.isStrikeThrough(as);
2412         } else {
2413             debugString("[ERROR]: getStrikethroughFromAttributeSet; as = null");
2414         }
2415         return false;
2416     }
2417 
2418     /**
2419      * return the superscript setting from an AttributeSet
2420      */
2421     private boolean getSuperscriptFromAttributeSet(AttributeSet as) {
2422         if (as != null) {
2423             return StyleConstants.isSuperscript(as);
2424         } else {
2425             debugString("[ERROR]: getSuperscriptFromAttributeSet; as = null");
2426         }
2427         return false;
2428     }
2429 
2430     /**
2431      * return the subscript setting from an AttributeSet
2432      */
2433     private boolean getSubscriptFromAttributeSet(AttributeSet as) {
2434         if (as != null) {
2435             return StyleConstants.isSubscript(as);
2436         } else {
2437             debugString("[ERROR]: getSubscriptFromAttributeSet; as = null");
2438         }
2439         return false;
2440     }
2441 
2442     /**
2443      * return the background color from an AttributeSet
2444      */
2445     private String getBackgroundColorFromAttributeSet(AttributeSet as) {
2446         if (as != null) {
2447             String s = StyleConstants.getBackground(as).toString();
2448             if (s != null) {
2449                 references.increment(s);
2450                 return s;
2451             }
2452         } else {
2453             debugString("[ERROR]: getBackgroundColorFromAttributeSet; as = null");
2454         }
2455         return null;
2456     }
2457 
2458     /**
2459      * return the foreground color from an AttributeSet
2460      */
2461     private String getForegroundColorFromAttributeSet(AttributeSet as) {
2462         if (as != null) {
2463             String s = StyleConstants.getForeground(as).toString();
2464             if (s != null) {
2465                 references.increment(s);
2466                 return s;
2467             }
2468         } else {
2469             debugString("[ERROR]: getForegroundColorFromAttributeSet; as = null");
2470         }
2471         return null;
2472     }
2473 
2474     /**
2475      * return the font family from an AttributeSet
2476      */
2477     private String getFontFamilyFromAttributeSet(AttributeSet as) {
2478         if (as != null) {
2479             String s = StyleConstants.getFontFamily(as).toString();
2480             if (s != null) {
2481                 references.increment(s);
2482                 return s;
2483             }
2484         } else {
2485             debugString("[ERROR]: getFontFamilyFromAttributeSet; as = null");
2486         }
2487         return null;
2488     }
2489 
2490     /**
2491      * return the font size from an AttributeSet
2492      */
2493     private int getFontSizeFromAttributeSet(AttributeSet as) {
2494         if (as != null) {
2495             return StyleConstants.getFontSize(as);
2496         } else {
2497             debugString("[ERROR]: getFontSizeFromAttributeSet; as = null");
2498         }
2499         return -1;
2500     }
2501 
2502     /**
2503      * return the alignment from an AttributeSet
2504      */
2505     private int getAlignmentFromAttributeSet(AttributeSet as) {
2506         if (as != null) {
2507             return StyleConstants.getAlignment(as);
2508         } else {
2509             debugString("[ERROR]: getAlignmentFromAttributeSet; as = null");
2510         }
2511         return -1;
2512     }
2513 
2514     /**
2515      * return the BiDi level from an AttributeSet
2516      */
2517     private int getBidiLevelFromAttributeSet(AttributeSet as) {
2518         if (as != null) {
2519             return StyleConstants.getBidiLevel(as);
2520         } else {
2521             debugString("[ERROR]: getBidiLevelFromAttributeSet; as = null");
2522         }
2523         return -1;
2524     }
2525 
2526 
2527     /**
2528      * return the first line indent from an AttributeSet
2529      */
2530     private float getFirstLineIndentFromAttributeSet(AttributeSet as) {
2531         if (as != null) {
2532             return StyleConstants.getFirstLineIndent(as);
2533         } else {
2534             debugString("[ERROR]: getFirstLineIndentFromAttributeSet; as = null");
2535         }
2536         return -1;
2537     }
2538 
2539     /**
2540      * return the left indent from an AttributeSet
2541      */
2542     private float getLeftIndentFromAttributeSet(AttributeSet as) {
2543         if (as != null) {
2544             return StyleConstants.getLeftIndent(as);
2545         } else {
2546             debugString("[ERROR]: getLeftIndentFromAttributeSet; as = null");
2547         }
2548         return -1;
2549     }
2550 
2551     /**
2552      * return the right indent from an AttributeSet
2553      */
2554     private float getRightIndentFromAttributeSet(AttributeSet as) {
2555         if (as != null) {
2556             return StyleConstants.getRightIndent(as);
2557         } else {
2558             debugString("[ERROR]: getRightIndentFromAttributeSet; as = null");
2559         }
2560         return -1;
2561     }
2562 
2563     /**
2564      * return the line spacing from an AttributeSet
2565      */
2566     private float getLineSpacingFromAttributeSet(AttributeSet as) {
2567         if (as != null) {
2568             return StyleConstants.getLineSpacing(as);
2569         } else {
2570             debugString("[ERROR]: getLineSpacingFromAttributeSet; as = null");
2571         }
2572         return -1;
2573     }
2574 
2575     /**
2576      * return the space above from an AttributeSet
2577      */
2578     private float getSpaceAboveFromAttributeSet(AttributeSet as) {
2579         if (as != null) {
2580             return StyleConstants.getSpaceAbove(as);
2581         } else {
2582             debugString("[ERROR]: getSpaceAboveFromAttributeSet; as = null");
2583         }
2584         return -1;
2585     }
2586 
2587     /**
2588      * return the space below from an AttributeSet
2589      */
2590     private float getSpaceBelowFromAttributeSet(AttributeSet as) {
2591         if (as != null) {
2592             return StyleConstants.getSpaceBelow(as);
2593         } else {
2594             debugString("[ERROR]: getSpaceBelowFromAttributeSet; as = null");
2595         }
2596         return -1;
2597     }
2598 
2599     /**
2600      * Enumerate all StyleConstants in the AttributeSet
2601      *
2602      * We need to check explicitly, 'cause of the HTML package conversion
2603      * mechanism (they may not be stored as StyleConstants, just translated
2604      * to them when asked).
2605      *
2606      * (Use convenience methods where they are defined...)
2607      *
2608      * Not checking the following (which the IBM SNS guidelines says
2609      * should be defined):
2610      *    - ComponentElementName
2611      *    - IconElementName
2612      *    - NameAttribute
2613      *    - ResolveAttribute
2614      */
2615     private String expandStyleConstants(AttributeSet as) {
2616         Color c;
2617         Object o;
2618         String attrString = "";
2619 
2620         // ---------- check for various Character Constants
2621 
2622         attrString += "BidiLevel = " + StyleConstants.getBidiLevel(as);
2623 
2624         final Component comp = StyleConstants.getComponent(as);
2625         if (comp != null) {
2626             if (comp instanceof Accessible) {
2627                 final AccessibleContext ac = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
2628                     @Override
2629                     public AccessibleContext call() throws Exception {
2630                         return comp.getAccessibleContext();
2631                     }
2632                 }, comp);
2633                 if (ac != null) {
2634                     attrString += "; Accessible Component = " + InvocationUtils.invokeAndWait(new Callable<String>() {
2635                         @Override
2636                         public String call() throws Exception {
2637                             return ac.getAccessibleName();
2638                         }
2639                     }, ac);
2640                 } else {
2641                     attrString += "; Innaccessible Component = " + comp;
2642                 }
2643             } else {
2644                 attrString += "; Innaccessible Component = " + comp;
2645             }
2646         }
2647 
2648         Icon i = StyleConstants.getIcon(as);
2649         if (i != null) {
2650             if (i instanceof ImageIcon) {
2651                 attrString += "; ImageIcon = " + ((ImageIcon) i).getDescription();
2652             } else {
2653                 attrString += "; Icon = " + i;
2654             }
2655         }
2656 
2657         attrString += "; FontFamily = " + StyleConstants.getFontFamily(as);
2658 
2659         attrString += "; FontSize = " + StyleConstants.getFontSize(as);
2660 
2661         if (StyleConstants.isBold(as)) {
2662             attrString += "; bold";
2663         }
2664 
2665         if (StyleConstants.isItalic(as)) {
2666             attrString += "; italic";
2667         }
2668 
2669         if (StyleConstants.isUnderline(as)) {
2670             attrString += "; underline";
2671         }
2672 
2673         if (StyleConstants.isStrikeThrough(as)) {
2674             attrString += "; strikethrough";
2675         }
2676 
2677         if (StyleConstants.isSuperscript(as)) {
2678             attrString += "; superscript";
2679         }
2680 
2681         if (StyleConstants.isSubscript(as)) {
2682             attrString += "; subscript";
2683         }
2684 
2685         c = StyleConstants.getForeground(as);
2686         if (c != null) {
2687             attrString += "; Foreground = " + c;
2688         }
2689 
2690         c = StyleConstants.getBackground(as);
2691         if (c != null) {
2692             attrString += "; Background = " + c;
2693         }
2694 
2695         attrString += "; FirstLineIndent = " + StyleConstants.getFirstLineIndent(as);
2696 
2697         attrString += "; RightIndent = " + StyleConstants.getRightIndent(as);
2698 
2699         attrString += "; LeftIndent = " + StyleConstants.getLeftIndent(as);
2700 
2701         attrString += "; LineSpacing = " + StyleConstants.getLineSpacing(as);
2702 
2703         attrString += "; SpaceAbove = " + StyleConstants.getSpaceAbove(as);
2704 
2705         attrString += "; SpaceBelow = " + StyleConstants.getSpaceBelow(as);
2706 
2707         attrString += "; Alignment = " + StyleConstants.getAlignment(as);
2708 
2709         TabSet ts = StyleConstants.getTabSet(as);
2710         if (ts != null) {
2711             attrString += "; TabSet = " + ts;
2712         }
2713 
2714         return attrString;
2715     }
2716 
2717 
2718     /* ===== AccessibleValue methods ===== */
2719 
2720     /**
2721      * return the AccessibleValue current value from an AccessibleContext
2722      * returned using a String 'cause the value is a java Number
2723      *
2724      */
2725     private String getCurrentAccessibleValueFromContext(final AccessibleContext ac) {
2726         if (ac != null) {
2727             final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() {
2728                 @Override
2729                 public Number call() throws Exception {
2730                     AccessibleValue av = ac.getAccessibleValue();
2731                     if (av == null) return null;
2732                     return av.getCurrentAccessibleValue();
2733                 }
2734             }, ac);
2735             if (value != null) {
2736                 String s = value.toString();
2737                 if (s != null) {
2738                     references.increment(s);
2739                     return s;
2740                 }
2741             }
2742         } else {
2743             debugString("[ERROR]: getCurrentAccessibleValueFromContext; ac = null");
2744         }
2745         return null;
2746     }
2747 
2748     /**
2749      * return the AccessibleValue maximum value from an AccessibleContext
2750      * returned using a String 'cause the value is a java Number
2751      *
2752      */
2753     private String getMaximumAccessibleValueFromContext(final AccessibleContext ac) {
2754         if (ac != null) {
2755             final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() {
2756                 @Override
2757                 public Number call() throws Exception {
2758                     AccessibleValue av = ac.getAccessibleValue();
2759                     if (av == null) return null;
2760                     return av.getMaximumAccessibleValue();
2761                 }
2762             }, ac);
2763             if (value != null) {
2764                 String s = value.toString();
2765                 if (s != null) {
2766                     references.increment(s);
2767                     return s;
2768                 }
2769             }
2770         } else {
2771             debugString("[ERROR]: getMaximumAccessibleValueFromContext; ac = null");
2772         }
2773         return null;
2774     }
2775 
2776     /**
2777      * return the AccessibleValue minimum value from an AccessibleContext
2778      * returned using a String 'cause the value is a java Number
2779      *
2780      */
2781     private String getMinimumAccessibleValueFromContext(final AccessibleContext ac) {
2782         if (ac != null) {
2783             final Number value = InvocationUtils.invokeAndWait(new Callable<Number>() {
2784                 @Override
2785                 public Number call() throws Exception {
2786                     AccessibleValue av = ac.getAccessibleValue();
2787                     if (av == null) return null;
2788                     return av.getMinimumAccessibleValue();
2789                 }
2790             }, ac);
2791             if (value != null) {
2792                 String s = value.toString();
2793                 if (s != null) {
2794                     references.increment(s);
2795                     return s;
2796                 }
2797             }
2798         } else {
2799             debugString("[ERROR]: getMinimumAccessibleValueFromContext; ac = null");
2800         }
2801         return null;
2802     }
2803 
2804 
2805     /* ===== AccessibleSelection methods ===== */
2806 
2807     /**
2808      * add to the AccessibleSelection of an AccessibleContext child i
2809      *
2810      */
2811     private void addAccessibleSelectionFromContext(final AccessibleContext ac, final int i) {
2812         try {
2813             InvocationUtils.invokeAndWait(new Callable<Object>() {
2814                 @Override
2815                 public Object call() throws Exception {
2816                     if (ac != null) {
2817                         AccessibleSelection as = ac.getAccessibleSelection();
2818                         if (as != null) {
2819                             as.addAccessibleSelection(i);
2820                         }
2821                     }
2822                     return null;
2823                 }
2824             }, ac);
2825         } catch(Exception e){}
2826     }
2827 
2828     /**
2829      * clear all of the AccessibleSelection of an AccessibleContex
2830      *
2831      */
2832     private void clearAccessibleSelectionFromContext(final AccessibleContext ac) {
2833         try {
2834             InvocationUtils.invokeAndWait(new Callable<Object>() {
2835                 @Override
2836                 public Object call() throws Exception {
2837                     AccessibleSelection as = ac.getAccessibleSelection();
2838                     if (as != null) {
2839                         as.clearAccessibleSelection();
2840                     }
2841                     return null;
2842                 }
2843             }, ac);
2844         } catch(Exception e){}
2845 
2846     }
2847 
2848     /**
2849      * get the AccessibleContext of the i-th AccessibleSelection of an AccessibleContext
2850      *
2851      */
2852     private AccessibleContext getAccessibleSelectionFromContext(final AccessibleContext ac, final int i) {
2853         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
2854             @Override
2855             public AccessibleContext call() throws Exception {
2856                 if (ac != null) {
2857                     AccessibleSelection as = ac.getAccessibleSelection();
2858                     if (as != null) {
2859                         Accessible a = as.getAccessibleSelection(i);
2860                         if (a == null)
2861                             return null;
2862                         else
2863                             return a.getAccessibleContext();
2864                     }
2865                 }
2866                 return null;
2867             }
2868         }, ac);
2869     }
2870 
2871     /**
2872      * get number of things selected in the AccessibleSelection of an AccessibleContext
2873      *
2874      */
2875     private int getAccessibleSelectionCountFromContext(final AccessibleContext ac) {
2876         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
2877             @Override
2878             public Integer call() throws Exception {
2879                 if (ac != null) {
2880                     AccessibleSelection as = ac.getAccessibleSelection();
2881                     if (as != null) {
2882                         return as.getAccessibleSelectionCount();
2883                     }
2884                 }
2885                 return -1;
2886             }
2887         }, ac);
2888     }
2889 
2890     /**
2891      * return true if the i-th child of the AccessibleSelection of an AccessibleContext is selected
2892      *
2893      */
2894     private boolean isAccessibleChildSelectedFromContext(final AccessibleContext ac, final int i) {
2895         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
2896             @Override
2897             public Boolean call() throws Exception {
2898                 if (ac != null) {
2899                     AccessibleSelection as = ac.getAccessibleSelection();
2900                     if (as != null) {
2901                         return as.isAccessibleChildSelected(i);
2902                     }
2903                 }
2904                 return false;
2905             }
2906         }, ac);
2907     }
2908 
2909     /**
2910      * remove the i-th child from the AccessibleSelection of an AccessibleContext
2911      *
2912      */
2913     private void removeAccessibleSelectionFromContext(final AccessibleContext ac, final int i) {
2914         InvocationUtils.invokeAndWait(new Callable<Object>() {
2915             @Override
2916             public Object call() throws Exception {
2917                 if (ac != null) {
2918                     AccessibleSelection as = ac.getAccessibleSelection();
2919                     if (as != null) {
2920                         as.removeAccessibleSelection(i);
2921                     }
2922                 }
2923                 return null;
2924             }
2925         }, ac);
2926     }
2927 
2928     /**
2929      * select all (if possible) of the children of the AccessibleSelection of an AccessibleContext
2930      *
2931      */
2932     private void selectAllAccessibleSelectionFromContext(final AccessibleContext ac) {
2933             InvocationUtils.invokeAndWait(new Callable<Object>() {
2934                 @Override
2935                 public Object call() throws Exception {
2936                     if (ac != null) {
2937                         AccessibleSelection as = ac.getAccessibleSelection();
2938                         if (as != null) {
2939                             as.selectAllAccessibleSelection();
2940                         }
2941                     }
2942                     return null;
2943                 }
2944             }, ac);
2945     }
2946 
2947     // ======== AccessibleTable ========
2948 
2949     ConcurrentHashMap<AccessibleTable,AccessibleContext> hashtab = new ConcurrentHashMap<>();
2950 
2951     /**
2952      * returns the AccessibleTable for an AccessibleContext
2953      */
2954     private AccessibleTable getAccessibleTableFromContext(final AccessibleContext ac) {
2955         return InvocationUtils.invokeAndWait(new Callable<AccessibleTable>() {
2956             @Override
2957             public AccessibleTable call() throws Exception {
2958                 if (ac != null) {
2959                     AccessibleTable at = ac.getAccessibleTable();
2960                     if (at != null) {
2961                         AccessBridge.this.hashtab.put(at, ac);
2962                         return at;
2963                     }
2964                 }
2965                 return null;
2966             }
2967         }, ac);
2968     }
2969 
2970 
2971     /*
2972      * returns the AccessibleContext that contains an AccessibleTable
2973      */
2974     private AccessibleContext getContextFromAccessibleTable(AccessibleTable at) {
2975         return hashtab.get(at);
2976     }
2977 
2978     /*
2979      * returns the row count for an AccessibleTable
2980      */
2981     private int getAccessibleTableRowCount(final AccessibleContext ac) {
2982         debugString("[INFO]: ##### getAccessibleTableRowCount");
2983         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
2984             @Override
2985             public Integer call() throws Exception {
2986                 if (ac != null) {
2987                     AccessibleTable at = ac.getAccessibleTable();
2988                     if (at != null) {
2989                         return at.getAccessibleRowCount();
2990                     }
2991                 }
2992                 return -1;
2993             }
2994         }, ac);
2995     }
2996 
2997     /*
2998      * returns the column count for an AccessibleTable
2999      */
3000     private int getAccessibleTableColumnCount(final AccessibleContext ac) {
3001         debugString("[INFO]: ##### getAccessibleTableColumnCount");
3002         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3003             @Override
3004             public Integer call() throws Exception {
3005                 if (ac != null) {
3006                     AccessibleTable at = ac.getAccessibleTable();
3007                     if (at != null) {
3008                         return at.getAccessibleColumnCount();
3009                     }
3010                 }
3011                 return -1;
3012             }
3013         }, ac);
3014     }
3015 
3016     /*
3017      * returns the AccessibleContext for an AccessibleTable cell
3018      */
3019     private AccessibleContext getAccessibleTableCellAccessibleContext(final AccessibleTable at,
3020                                                                       final int row, final int column) {
3021         debugString("[INFO]: getAccessibleTableCellAccessibleContext: at = "+at.getClass());
3022         if (at == null) return null;
3023         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
3024             @Override
3025             public AccessibleContext call() throws Exception {
3026                 if (!(at instanceof AccessibleContext)) {
3027                     Accessible a = at.getAccessibleAt(row, column);
3028                     if (a != null) {
3029                         return a.getAccessibleContext();
3030                     }
3031                 } else {
3032                     // work-around for AccessibleJTable.getCurrentAccessibleContext returning
3033                     // wrong renderer component when cell contains more than one component
3034                     AccessibleContext ac = (AccessibleContext) at;
3035                     Accessible parent = ac.getAccessibleParent();
3036                     if (parent != null) {
3037                         int indexInParent = ac.getAccessibleIndexInParent();
3038                         Accessible child =
3039                                 parent.getAccessibleContext().getAccessibleChild(indexInParent);
3040                         if (child instanceof JTable) {
3041                             JTable table = (JTable) child;
3042 
3043                             TableCellRenderer renderer = table.getCellRenderer(row, column);
3044                             if (renderer == null) {
3045                                 Class<?> columnClass = table.getColumnClass(column);
3046                                 renderer = table.getDefaultRenderer(columnClass);
3047                             }
3048                             Component component =
3049                                     renderer.getTableCellRendererComponent(table, table.getValueAt(row, column),
3050                                             false, false, row, column);
3051                             if (component instanceof Accessible) {
3052                                 return component.getAccessibleContext();
3053                             }
3054                         }
3055                     }
3056                 }
3057                 return null;
3058             }
3059         }, getContextFromAccessibleTable(at));
3060     }
3061 
3062     /*
3063      * returns the index of a cell at a given row and column in an AccessibleTable
3064      */
3065     private int getAccessibleTableCellIndex(final AccessibleTable at, int row, int column) {
3066         debugString("[INFO]: ##### getAccessibleTableCellIndex: at="+at);
3067         if (at != null) {
3068             int cellIndex = row *
3069                 InvocationUtils.invokeAndWait(new Callable<Integer>() {
3070                     @Override
3071                     public Integer call() throws Exception {
3072                         return at.getAccessibleColumnCount();
3073                     }
3074                 }, getContextFromAccessibleTable(at)) +
3075                 column;
3076             debugString("[INFO]:    ##### getAccessibleTableCellIndex="+cellIndex);
3077             return cellIndex;
3078         }
3079         debugString("[ERROR]: ##### getAccessibleTableCellIndex FAILED");
3080         return -1;
3081     }
3082 
3083     /*
3084      * returns the row extent of a cell at a given row and column in an AccessibleTable
3085      */
3086     private int getAccessibleTableCellRowExtent(final AccessibleTable at, final int row, final int column) {
3087         debugString("[INFO]: ##### getAccessibleTableCellRowExtent");
3088         if (at != null) {
3089             int rowExtent = InvocationUtils.invokeAndWait(new Callable<Integer>() {
3090                                                               @Override
3091                                                               public Integer call() throws Exception {
3092                                                                   return at.getAccessibleRowExtentAt(row, column);
3093                                                               }
3094                                                           },
3095                     getContextFromAccessibleTable(at));
3096             debugString("[INFO]:   ##### getAccessibleTableCellRowExtent="+rowExtent);
3097             return rowExtent;
3098         }
3099         debugString("[ERROR]: ##### getAccessibleTableCellRowExtent FAILED");
3100         return -1;
3101     }
3102 
3103     /*
3104      * returns the column extent of a cell at a given row and column in an AccessibleTable
3105      */
3106     private int getAccessibleTableCellColumnExtent(final AccessibleTable at, final int row, final int column) {
3107         debugString("[INFO]: ##### getAccessibleTableCellColumnExtent");
3108         if (at != null) {
3109             int columnExtent = InvocationUtils.invokeAndWait(new Callable<Integer>() {
3110                                                                  @Override
3111                                                                  public Integer call() throws Exception {
3112                                                                      return at.getAccessibleColumnExtentAt(row, column);
3113                                                                  }
3114                                                              },
3115                     getContextFromAccessibleTable(at));
3116             debugString("[INFO]:   ##### getAccessibleTableCellColumnExtent="+columnExtent);
3117             return columnExtent;
3118         }
3119         debugString("[ERROR]: ##### getAccessibleTableCellColumnExtent FAILED");
3120         return -1;
3121     }
3122 
3123     /*
3124      * returns whether a cell is selected at a given row and column in an AccessibleTable
3125      */
3126     private boolean isAccessibleTableCellSelected(final AccessibleTable at, final int row,
3127                          final int column) {
3128         debugString("[INFO]: ##### isAccessibleTableCellSelected: ["+row+"]["+column+"]");
3129         if (at == null)
3130             return false;
3131         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
3132             @Override
3133             public Boolean call() throws Exception {
3134                 boolean isSelected = false;
3135                 Accessible a = at.getAccessibleAt(row, column);
3136                 if (a != null) {
3137                     AccessibleContext ac = a.getAccessibleContext();
3138                     if (ac == null)
3139                         return false;
3140                     AccessibleStateSet as = ac.getAccessibleStateSet();
3141                     if (as != null) {
3142                         isSelected = as.contains(AccessibleState.SELECTED);
3143                     }
3144                 }
3145                 return isSelected;
3146             }
3147         }, getContextFromAccessibleTable(at));
3148     }
3149 
3150     /*
3151      * returns an AccessibleTable that represents the row header in an
3152      * AccessibleTable
3153      */
3154     private AccessibleTable getAccessibleTableRowHeader(final AccessibleContext ac) {
3155         debugString("[INFO]: #####  getAccessibleTableRowHeader called");
3156         AccessibleTable at = InvocationUtils.invokeAndWait(new Callable<AccessibleTable>() {
3157             @Override
3158             public AccessibleTable call() throws Exception {
3159                 if (ac != null) {
3160                     AccessibleTable at = ac.getAccessibleTable();
3161                     if (at != null) {
3162                         return at.getAccessibleRowHeader();
3163                     }
3164                 }
3165                 return null;
3166             }
3167         }, ac);
3168         if (at != null) {
3169             hashtab.put(at, ac);
3170         }
3171         return at;
3172     }
3173 
3174     /*
3175      * returns an AccessibleTable that represents the column header in an
3176      * AccessibleTable
3177      */
3178     private AccessibleTable getAccessibleTableColumnHeader(final AccessibleContext ac) {
3179     debugString("[INFO]: ##### getAccessibleTableColumnHeader");
3180         if (ac == null)
3181             return null;
3182         AccessibleTable at = InvocationUtils.invokeAndWait(new Callable<AccessibleTable>() {
3183             @Override
3184             public AccessibleTable call() throws Exception {
3185                 // workaround for getAccessibleColumnHeader NPE
3186                 // when the table header is null
3187                 Accessible parent = ac.getAccessibleParent();
3188                 if (parent != null) {
3189                     int indexInParent = ac.getAccessibleIndexInParent();
3190                     Accessible child =
3191                             parent.getAccessibleContext().getAccessibleChild(indexInParent);
3192                     if (child instanceof JTable) {
3193                         JTable table = (JTable) child;
3194                         if (table.getTableHeader() == null) {
3195                             return null;
3196                         }
3197                     }
3198                 }
3199                 AccessibleTable at = ac.getAccessibleTable();
3200                 if (at != null) {
3201                     return at.getAccessibleColumnHeader();
3202                 }
3203                 return null;
3204             }
3205         }, ac);
3206         if (at != null) {
3207             hashtab.put(at, ac);
3208         }
3209         return at;
3210     }
3211 
3212     /*
3213      * returns the number of row headers in an AccessibleTable that represents
3214      * the row header in an AccessibleTable
3215      */
3216     private int getAccessibleTableRowHeaderRowCount(AccessibleContext ac) {
3217 
3218     debugString("[INFO]: #####  getAccessibleTableRowHeaderRowCount called");
3219         if (ac != null) {
3220             final AccessibleTable atRowHeader = getAccessibleTableRowHeader(ac);
3221             if (atRowHeader != null) {
3222                 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3223                     @Override
3224                     public Integer call() throws Exception {
3225                         if (atRowHeader != null) {
3226                             return atRowHeader.getAccessibleRowCount();
3227                         }
3228                         return -1;
3229                     }
3230                 }, ac);
3231             }
3232         }
3233         return -1;
3234     }
3235 
3236     /*
3237      * returns the number of column headers in an AccessibleTable that represents
3238      * the row header in an AccessibleTable
3239      */
3240     private int getAccessibleTableRowHeaderColumnCount(AccessibleContext ac) {
3241         debugString("[INFO]: #####  getAccessibleTableRowHeaderColumnCount called");
3242         if (ac != null) {
3243             final AccessibleTable atRowHeader = getAccessibleTableRowHeader(ac);
3244             if (atRowHeader != null) {
3245                 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3246                     @Override
3247                     public Integer call() throws Exception {
3248                         if (atRowHeader != null) {
3249                             return atRowHeader.getAccessibleColumnCount();
3250                         }
3251                         return -1;
3252                     }
3253                 }, ac);
3254             }
3255         }
3256         debugString("[ERROR]: ##### getAccessibleTableRowHeaderColumnCount FAILED");
3257         return -1;
3258     }
3259 
3260     /*
3261      * returns the number of row headers in an AccessibleTable that represents
3262      * the column header in an AccessibleTable
3263      */
3264     private int getAccessibleTableColumnHeaderRowCount(AccessibleContext ac) {
3265 
3266     debugString("[INFO]: ##### getAccessibleTableColumnHeaderRowCount");
3267         if (ac != null) {
3268             final AccessibleTable atColumnHeader = getAccessibleTableColumnHeader(ac);
3269             if (atColumnHeader != null) {
3270                 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3271                     @Override
3272                     public Integer call() throws Exception {
3273                         if (atColumnHeader != null) {
3274                             return atColumnHeader.getAccessibleRowCount();
3275                         }
3276                         return -1;
3277                     }
3278                 }, ac);
3279             }
3280         }
3281         debugString("[ERROR]: ##### getAccessibleTableColumnHeaderRowCount FAILED");
3282         return -1;
3283     }
3284 
3285     /*
3286      * returns the number of column headers in an AccessibleTable that represents
3287      * the column header in an AccessibleTable
3288      */
3289     private int getAccessibleTableColumnHeaderColumnCount(AccessibleContext ac) {
3290 
3291     debugString("[ERROR]: #####  getAccessibleTableColumnHeaderColumnCount");
3292         if (ac != null) {
3293             final AccessibleTable atColumnHeader = getAccessibleTableColumnHeader(ac);
3294             if (atColumnHeader != null) {
3295                 return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3296                     @Override
3297                     public Integer call() throws Exception {
3298                         if (atColumnHeader != null) {
3299                             return atColumnHeader.getAccessibleColumnCount();
3300                         }
3301                         return -1;
3302                     }
3303                 }, ac);
3304             }
3305         }
3306         debugString("[ERROR]: ##### getAccessibleTableColumnHeaderColumnCount FAILED");
3307         return -1;
3308     }
3309 
3310     /*
3311      * returns the description of a row header in an AccessibleTable
3312      */
3313     private AccessibleContext getAccessibleTableRowDescription(final AccessibleTable table,
3314                                                               final int row) {
3315         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
3316             @Override
3317             public AccessibleContext call() throws Exception {
3318                 if (table != null) {
3319                     Accessible a = table.getAccessibleRowDescription(row);
3320                     if (a != null) {
3321                         return a.getAccessibleContext();
3322                     }
3323                 }
3324                 return null;
3325             }
3326         }, getContextFromAccessibleTable(table));
3327     }
3328 
3329     /*
3330      * returns the description of a column header in an AccessibleTable
3331      */
3332     private AccessibleContext getAccessibleTableColumnDescription(final AccessibleTable at,
3333                                                                  final int column) {
3334         if (at == null)
3335             return null;
3336         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
3337             @Override
3338             public AccessibleContext call() throws Exception {
3339                 Accessible a = at.getAccessibleColumnDescription(column);
3340                 if (a != null) {
3341                     return a.getAccessibleContext();
3342                 }
3343                 return null;
3344             }
3345         }, getContextFromAccessibleTable(at));
3346     }
3347 
3348     /*
3349      * returns the number of rows selected in an AccessibleTable
3350      */
3351     private int getAccessibleTableRowSelectionCount(final AccessibleTable at) {
3352         if (at != null) {
3353             return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3354                 @Override
3355                 public Integer call() throws Exception {
3356                     int[] selections = at.getSelectedAccessibleRows();
3357                     if (selections != null)
3358                         return selections.length;
3359                     else
3360                         return -1;
3361                 }
3362             }, getContextFromAccessibleTable(at));
3363         }
3364         return -1;
3365     }
3366 
3367     /*
3368      * returns the row number of the i-th selected row in an AccessibleTable
3369      */
3370     private int getAccessibleTableRowSelections(final AccessibleTable at, final int i) {
3371         if (at != null) {
3372             return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3373                 @Override
3374                 public Integer call() throws Exception {
3375                     int[] selections = at.getSelectedAccessibleRows();
3376                     if (selections.length > i) {
3377                         return selections[i];
3378                     }
3379                     return -1;
3380                 }
3381             }, getContextFromAccessibleTable(at));
3382         }
3383         return -1;
3384     }
3385 
3386     /*
3387      * returns whether a row is selected in an AccessibleTable
3388      */
3389     private boolean isAccessibleTableRowSelected(final AccessibleTable at,
3390                                                  final int row) {
3391         if (at == null)
3392             return false;
3393         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
3394             @Override
3395             public Boolean call() throws Exception {
3396                 return at.isAccessibleRowSelected(row);
3397             }
3398          }, getContextFromAccessibleTable(at));
3399     }
3400 
3401     /*
3402      * returns whether a column is selected in an AccessibleTable
3403      */
3404     private boolean isAccessibleTableColumnSelected(final AccessibleTable at,
3405                                                    final int column) {
3406         if (at == null)
3407             return false;
3408         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
3409             @Override
3410             public Boolean call() throws Exception {
3411                 return at.isAccessibleColumnSelected(column);
3412             }
3413          }, getContextFromAccessibleTable(at));
3414     }
3415 
3416     /*
3417      * returns the number of columns selected in an AccessibleTable
3418      */
3419     private int getAccessibleTableColumnSelectionCount(final AccessibleTable at) {
3420         if (at == null)
3421             return -1;
3422         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3423             @Override
3424             public Integer call() throws Exception {
3425                 int[] selections = at.getSelectedAccessibleColumns();
3426                 if (selections != null)
3427                     return selections.length;
3428                 else
3429                     return -1;
3430             }
3431         }, getContextFromAccessibleTable(at));
3432     }
3433 
3434     /*
3435      * returns the row number of the i-th selected row in an AccessibleTable
3436      */
3437     private int getAccessibleTableColumnSelections(final AccessibleTable at, final int i) {
3438         if (at == null)
3439             return -1;
3440         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3441             @Override
3442             public Integer call() throws Exception {
3443                 int[] selections = at.getSelectedAccessibleColumns();
3444                 if (selections != null && selections.length > i) {
3445                     return selections[i];
3446                 }
3447                 return -1;
3448             }
3449         }, getContextFromAccessibleTable(at));
3450     }
3451 
3452     /* ===== AccessibleExtendedTable (since 1.4) ===== */
3453 
3454     /*
3455      * returns the row number for a cell at a given index in an AccessibleTable
3456      */
3457     private int getAccessibleTableRow(final AccessibleTable at, int index) {
3458         if (at == null)
3459             return -1;
3460         int colCount=InvocationUtils.invokeAndWait(new Callable<Integer>() {
3461             @Override
3462             public Integer call() throws Exception {
3463                 return at.getAccessibleColumnCount();
3464             }
3465         }, getContextFromAccessibleTable(at));
3466         return index / colCount;
3467     }
3468 
3469     /*
3470      * returns the column number for a cell at a given index in an AccessibleTable
3471      */
3472     private int getAccessibleTableColumn(final AccessibleTable at, int index) {
3473         if (at == null)
3474             return -1;
3475         int colCount=InvocationUtils.invokeAndWait(new Callable<Integer>() {
3476             @Override
3477             public Integer call() throws Exception {
3478                 return at.getAccessibleColumnCount();
3479             }
3480         }, getContextFromAccessibleTable(at));
3481         return index % colCount;
3482     }
3483 
3484     /*
3485      * returns the index for a cell at a given row and column in an
3486      * AccessibleTable
3487      */
3488     private int getAccessibleTableIndex(final AccessibleTable at, int row, int column) {
3489         if (at == null)
3490             return -1;
3491         int colCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
3492             @Override
3493             public Integer call() throws Exception {
3494                 return at.getAccessibleColumnCount();
3495             }
3496          }, getContextFromAccessibleTable(at));
3497         return row * colCount + column;
3498     }
3499 
3500     // ===== AccessibleRelationSet =====
3501 
3502     /*
3503      * returns the number of relations in the AccessibleContext's
3504      * AccessibleRelationSet
3505      */
3506     private int getAccessibleRelationCount(final AccessibleContext ac) {
3507         {
3508             if (ac != null) {
3509                 AccessibleRelationSet ars = InvocationUtils.invokeAndWait(new Callable<AccessibleRelationSet>() {
3510                     @Override
3511                     public AccessibleRelationSet call() throws Exception {
3512                         return ac.getAccessibleRelationSet();
3513                     }
3514                 }, ac);
3515                 if (ars != null)
3516                     return ars.size();
3517             }
3518         }
3519         return 0;
3520     }
3521 
3522     /*
3523      * returns the ith relation key in the AccessibleContext's
3524      * AccessibleRelationSet
3525      */
3526     private String getAccessibleRelationKey(final AccessibleContext ac, final int i) {
3527         return InvocationUtils.invokeAndWait(new Callable<String>() {
3528             @Override
3529             public String call() throws Exception {
3530                 if (ac != null) {
3531                     AccessibleRelationSet ars = ac.getAccessibleRelationSet();
3532                     if (ars != null) {
3533                         AccessibleRelation[] relations = ars.toArray();
3534                         if (relations != null && i >= 0 && i < relations.length) {
3535                             return relations[i].getKey();
3536                         }
3537                     }
3538                 }
3539                 return null;
3540             }
3541         }, ac);
3542     }
3543 
3544     /*
3545      * returns the number of targets in a relation in the AccessibleContext's
3546      * AccessibleRelationSet
3547      */
3548     private int getAccessibleRelationTargetCount(final AccessibleContext ac, final int i) {
3549         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3550             @Override
3551             public Integer call() throws Exception {
3552                 if (ac != null) {
3553                     AccessibleRelationSet ars = ac.getAccessibleRelationSet();
3554                     if (ars != null) {
3555                         AccessibleRelation[] relations = ars.toArray();
3556                         if (relations != null && i >= 0 && i < relations.length) {
3557                             Object[] targets = relations[i].getTarget();
3558                             if (targets != null) {
3559                                 int targetCount = targets.length -
3560                                         getNonVisibleTargetCountTillIndex(targets, targets.length - 1);
3561                                 return targetCount;
3562                             }
3563                         }
3564                     }
3565                 }
3566                 return -1;
3567             }
3568         }, ac);
3569     }
3570 
3571     /*
3572      * returns the jth target in the ith relation in the AccessibleContext's
3573      * AccessibleRelationSet
3574      */
3575     private AccessibleContext getAccessibleRelationTarget(final AccessibleContext ac,
3576                                                          final int i, final int j) {
3577         debugString("[INFO]: ***** getAccessibleRelationTarget");
3578         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
3579             @Override
3580             public AccessibleContext call() throws Exception {
3581                 if (ac != null) {
3582                     AccessibleRelationSet ars = ac.getAccessibleRelationSet();
3583                     if (ars != null) {
3584                         AccessibleRelation[] relations = ars.toArray();
3585                         if (relations != null && i >= 0 && i < relations.length) {
3586                             Object[] targets = relations[i].getTarget();
3587                             if (targets != null && j >= 0 & j < targets.length) {
3588                                 Object o = getVisibleTargetAtIndex(targets, j);
3589                                 if (o instanceof Accessible) {
3590                                     return ((Accessible) o).getAccessibleContext();
3591                                 }
3592                             }
3593                         }
3594                     }
3595                 }
3596                 return null;
3597             }
3598         }, ac);
3599     }
3600 
3601     private Object getVisibleTargetAtIndex(Object[] targets, int index) {
3602         if (index >= 0 && index < targets.length) {
3603             int visibleTargetIndex = -1;
3604             for (int i = 0; i < targets.length; i++) {
3605                 if (targets[i] instanceof Accessible) {
3606                     AccessibleContext ac = ((Accessible) targets[i]).getAccessibleContext();
3607                     if (ac != null && ac.getAccessibleStateSet().contains(AccessibleState.VISIBLE)) {
3608                         visibleTargetIndex++;
3609                     }
3610                     if (visibleTargetIndex == index) {
3611                         return targets[i];
3612                     }
3613                 }
3614             }
3615         }
3616         return null;
3617     }
3618 
3619     private int getNonVisibleTargetCountTillIndex(Object[] targets, int index) {
3620         if (index >= 0 && index < targets.length) {
3621             int nonVisibleTargetsCount = 0;
3622             for (int i = 0; i <= index; i++) {
3623                 if (targets[i] instanceof Accessible) {
3624                     AccessibleContext ac = ((Accessible) targets[i]).getAccessibleContext();
3625                     if (ac != null && !ac.getAccessibleStateSet().contains(AccessibleState.VISIBLE)) {
3626                         nonVisibleTargetsCount++;
3627                     }
3628                 }
3629             }
3630             return nonVisibleTargetsCount;
3631         }
3632         return 0;
3633     }
3634 
3635     // ========= AccessibleHypertext =========
3636 
3637     private Map<AccessibleHypertext, AccessibleContext> hyperTextContextMap = new WeakHashMap<>();
3638     private Map<AccessibleHyperlink, AccessibleContext> hyperLinkContextMap = new WeakHashMap<>();
3639 
3640     /*
3641      * Returns the AccessibleHypertext
3642      */
3643     private AccessibleHypertext getAccessibleHypertext(final AccessibleContext ac) {
3644         debugString("[INFO]: getAccessibleHyperlink");
3645         if (ac==null)
3646             return null;
3647         AccessibleHypertext hypertext = InvocationUtils.invokeAndWait(new Callable<AccessibleHypertext>() {
3648             @Override
3649             public AccessibleHypertext call() throws Exception {
3650                 AccessibleText at = ac.getAccessibleText();
3651                 if (!(at instanceof AccessibleHypertext)) {
3652                     return null;
3653                 }
3654                 return ((AccessibleHypertext) at);
3655             }
3656         }, ac);
3657         hyperTextContextMap.put(hypertext, ac);
3658         return hypertext;
3659     }
3660 
3661     /*
3662      * Returns the number of AccessibleHyperlinks
3663      */
3664     private int getAccessibleHyperlinkCount(AccessibleContext ac) {
3665         debugString("[INFO]: getAccessibleHyperlinkCount");
3666         if (ac == null) {
3667             return 0;
3668         }
3669         final AccessibleHypertext hypertext = getAccessibleHypertext(ac);
3670         if (hypertext == null) {
3671             return 0;
3672         }
3673         //return hypertext.getLinkCount();
3674         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
3675             @Override
3676             public Integer call() throws Exception {
3677                 return hypertext.getLinkCount();
3678             }
3679         }, ac);
3680     }
3681 
3682     /*
3683      * Returns the hyperlink at the specified index
3684      */
3685     private AccessibleHyperlink getAccessibleHyperlink(final AccessibleHypertext hypertext, final int i) {
3686         debugString("[INFO]: getAccessibleHyperlink");
3687         if (hypertext == null) {
3688             return null;
3689         }
3690         AccessibleContext ac = hyperTextContextMap.get(hypertext);
3691         if ( i < 0 || i >=
3692              InvocationUtils.invokeAndWait(new Callable<Integer>() {
3693                  @Override
3694                  public Integer call() throws Exception {
3695                      return hypertext.getLinkCount();
3696                  }
3697              }, ac) ) {
3698             return null;
3699         }
3700         AccessibleHyperlink acLink = InvocationUtils.invokeAndWait(new Callable<AccessibleHyperlink>() {
3701             @Override
3702             public AccessibleHyperlink call() throws Exception {
3703                 AccessibleHyperlink link = hypertext.getLink(i);
3704                 if (link == null || (!link.isValid())) {
3705                     return null;
3706                 }
3707                 return link;
3708             }
3709         }, ac);
3710         hyperLinkContextMap.put(acLink, ac);
3711         return acLink;
3712     }
3713 
3714     /*
3715      * Returns the hyperlink object description
3716      */
3717     private String getAccessibleHyperlinkText(final AccessibleHyperlink link) {
3718         debugString("[INFO]: getAccessibleHyperlinkText");
3719         if (link == null) {
3720             return null;
3721         }
3722         return InvocationUtils.invokeAndWait(new Callable<String>() {
3723             @Override
3724             public String call() throws Exception {
3725                 Object o = link.getAccessibleActionDescription(0);
3726                 if (o != null) {
3727                     return o.toString();
3728                 }
3729                 return null;
3730             }
3731         }, hyperLinkContextMap.get(link));
3732     }
3733 
3734     /*
3735      * Returns the hyperlink URL
3736      */
3737     private String getAccessibleHyperlinkURL(final AccessibleHyperlink link) {
3738         debugString("[INFO]: getAccessibleHyperlinkURL");
3739         if (link == null) {
3740             return null;
3741         }
3742         return InvocationUtils.invokeAndWait(new Callable<String>() {
3743             @Override
3744             public String call() throws Exception {
3745                 Object o = link.getAccessibleActionObject(0);
3746                 if (o != null) {
3747                     return o.toString();
3748                 } else {
3749                     return null;
3750                 }
3751             }
3752         }, hyperLinkContextMap.get(link));
3753     }
3754 
3755     /*
3756      * Returns the start index of the hyperlink text
3757      */
3758     private int getAccessibleHyperlinkStartIndex(final AccessibleHyperlink link) {
3759         debugString("[INFO]: getAccessibleHyperlinkStartIndex");
3760         if (link == null) {
3761             return -1;
3762         }
3763         return  InvocationUtils.invokeAndWait(new Callable<Integer>() {
3764             @Override
3765             public Integer call() throws Exception {
3766                 return link.getStartIndex();
3767             }
3768         }, hyperLinkContextMap.get(link));
3769     }
3770 
3771     /*
3772      * Returns the end index of the hyperlink text
3773      */
3774     private int getAccessibleHyperlinkEndIndex(final AccessibleHyperlink link) {
3775         debugString("[INFO]: getAccessibleHyperlinkEndIndex");
3776         if (link == null) {
3777             return -1;
3778         }
3779         return  InvocationUtils.invokeAndWait(new Callable<Integer>() {
3780             @Override
3781             public Integer call() throws Exception {
3782                 return link.getEndIndex();
3783             }
3784         }, hyperLinkContextMap.get(link));
3785     }
3786 
3787     /*
3788      * Returns the index into an array of hyperlinks that
3789      * is associated with this character index, or -1 if there
3790      * is no hyperlink associated with this index.
3791      */
3792     private int getAccessibleHypertextLinkIndex(final AccessibleHypertext hypertext, final int charIndex) {
3793         debugString("[INFO]: getAccessibleHypertextLinkIndex: charIndex = "+charIndex);
3794         if (hypertext == null) {
3795             return -1;
3796         }
3797         int linkIndex = InvocationUtils.invokeAndWait(new Callable<Integer>() {
3798             @Override
3799             public Integer call() throws Exception {
3800                 return hypertext.getLinkIndex(charIndex);
3801             }
3802         }, hyperTextContextMap.get(hypertext));
3803         debugString("[INFO]: getAccessibleHypertextLinkIndex returning "+linkIndex);
3804         return linkIndex;
3805     }
3806 
3807     /*
3808      * Actives the hyperlink
3809      */
3810     private boolean activateAccessibleHyperlink(final AccessibleContext ac,
3811                                                 final AccessibleHyperlink link) {
3812         //debugString("activateAccessibleHyperlink: link = "+link.getClass());
3813         if (link == null) {
3814             return false;
3815         }
3816         boolean retval = InvocationUtils.invokeAndWait(new Callable<Boolean>() {
3817             @Override
3818             public Boolean call() throws Exception {
3819                 return link.doAccessibleAction(0);
3820             }
3821         }, ac);
3822         debugString("[INFO]: activateAccessibleHyperlink: returning = "+retval);
3823         return retval;
3824     }
3825 
3826 
3827     // ============ AccessibleKeyBinding =============
3828 
3829     /*
3830      * returns the component mnemonic
3831      */
3832     private KeyStroke getMnemonic(final AccessibleContext ac) {
3833         if (ac == null)
3834             return null;
3835         return InvocationUtils.invokeAndWait(new Callable<KeyStroke>() {
3836             @Override
3837             public KeyStroke call() throws Exception {
3838                 AccessibleComponent comp = ac.getAccessibleComponent();
3839                 if (!(comp instanceof AccessibleExtendedComponent)) {
3840                     return null;
3841                 }
3842                 AccessibleExtendedComponent aec = (AccessibleExtendedComponent) comp;
3843                 if (aec != null) {
3844                     AccessibleKeyBinding akb = aec.getAccessibleKeyBinding();
3845                     if (akb != null) {
3846                         Object o = akb.getAccessibleKeyBinding(0);
3847                         if (o instanceof KeyStroke) {
3848                             return (KeyStroke) o;
3849                         }
3850                     }
3851                 }
3852                 return null;
3853             }
3854         }, ac);
3855     }
3856 
3857     /*
3858      * Returns the JMenuItem accelerator. Similar implementation is used on
3859      * macOS, see CAccessibility.getAcceleratorText(AccessibleContext).
3860      */
3861     private KeyStroke getAccelerator(final AccessibleContext ac) {
3862         // workaround for getAccessibleKeyBinding not returning the
3863         // JMenuItem accelerator
3864         if (ac == null)
3865             return null;
3866         return InvocationUtils.invokeAndWait(new Callable<KeyStroke>() {
3867             @Override
3868             public KeyStroke call() throws Exception {
3869                 Accessible parent = ac.getAccessibleParent();
3870                 if (parent instanceof Accessible) {
3871                     int indexInParent = ac.getAccessibleIndexInParent();
3872                     Accessible child =
3873                             parent.getAccessibleContext().getAccessibleChild(indexInParent);
3874                     if (child instanceof JMenuItem) {
3875                         JMenuItem menuItem = (JMenuItem) child;
3876                         if (menuItem == null)
3877                             return null;
3878                         KeyStroke keyStroke = menuItem.getAccelerator();
3879                         return keyStroke;
3880                     }
3881                 }
3882                 return null;
3883             }
3884         }, ac);
3885     }
3886 
3887     /*
3888      * returns 1-24 to indicate which F key is being used for a shortcut or 0 otherwise
3889      */
3890     private int fKeyNumber(KeyStroke keyStroke) {
3891         if (keyStroke == null)
3892             return 0;
3893         int fKey = 0;
3894         String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode());
3895         if (keyText != null && (keyText.length() == 2 || keyText.length() == 3)) {
3896             String prefix = keyText.substring(0, 1);
3897             if (prefix.equals("F")) {
3898                 try {
3899                     int suffix = Integer.parseInt(keyText.substring(1));
3900                     if (suffix >= 1 && suffix <= 24) {
3901                         fKey = suffix;
3902                     }
3903                 } catch (Exception e) { // ignore NumberFormatException
3904                 }
3905             }
3906         }
3907         return fKey;
3908     }
3909 
3910     /*
3911      * returns one of several important control characters or 0 otherwise
3912      */
3913     private int controlCode(KeyStroke keyStroke) {
3914         if (keyStroke == null)
3915             return 0;
3916         int code = keyStroke.getKeyCode();
3917         switch (code) {
3918             case KeyEvent.VK_BACK_SPACE:
3919             case KeyEvent.VK_DELETE:
3920             case KeyEvent.VK_DOWN:
3921             case KeyEvent.VK_END:
3922             case KeyEvent.VK_HOME:
3923             case KeyEvent.VK_INSERT:
3924             case KeyEvent.VK_KP_DOWN:
3925             case KeyEvent.VK_KP_LEFT:
3926             case KeyEvent.VK_KP_RIGHT:
3927             case KeyEvent.VK_KP_UP:
3928             case KeyEvent.VK_LEFT:
3929             case KeyEvent.VK_PAGE_DOWN:
3930             case KeyEvent.VK_PAGE_UP:
3931             case KeyEvent.VK_RIGHT:
3932             case KeyEvent.VK_UP:
3933                 break;
3934             default:
3935                 code = 0;
3936                 break;
3937         }
3938         return code;
3939     }
3940 
3941     /*
3942      * returns the KeyStoke character
3943      */
3944     private char getKeyChar(KeyStroke keyStroke) {
3945         // If the shortcut is an FKey return 1-24
3946         if (keyStroke == null)
3947             return 0;
3948         int fKey = fKeyNumber(keyStroke);
3949         if (fKey != 0) {
3950             // return 0x00000001 through 0x00000018
3951             debugString("[INFO]:   Shortcut is: F" + fKey);
3952             return (char)fKey;
3953         }
3954         // If the accelerator is a control character, return it
3955         int keyCode = controlCode(keyStroke);
3956         if (keyCode != 0) {
3957             debugString("[INFO]:   Shortcut is control character: " + Integer.toHexString(keyCode));
3958             return (char)keyCode;
3959         }
3960         String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode());
3961         debugString("[INFO]:   Shortcut is: " + keyText);
3962         if (keyText != null || keyText.length() > 0) {
3963             CharSequence seq = keyText.subSequence(0, 1);
3964             if (seq != null || seq.length() > 0) {
3965                 return seq.charAt(0);
3966             }
3967         }
3968         return 0;
3969     }
3970 
3971     /*
3972      * returns the KeyStroke modifiers as an int
3973      */
3974     private int getModifiers(KeyStroke keyStroke) {
3975         if (keyStroke == null)
3976             return 0;
3977         debugString("[INFO]: In AccessBridge.getModifiers");
3978         // modifiers is a bit strip where bits 0-7 indicate a traditional modifier
3979         // such as Ctrl/Alt/Shift, bit 8 indicates an F key shortcut, and bit 9 indicates
3980         // a control code shortcut such as the delete key.
3981 
3982         int modifiers = 0;
3983         // Is the shortcut an FKey?
3984         if (fKeyNumber(keyStroke) != 0) {
3985             modifiers |= 1 << 8;
3986         }
3987         // Is the shortcut a control code?
3988         if (controlCode(keyStroke) != 0) {
3989             modifiers |= 1 << 9;
3990         }
3991         // The following is needed in order to handle translated modifiers.
3992         // getKeyModifiersText doesn't work because for example in German Strg is
3993         // returned for Ctrl.
3994 
3995         // There can be more than one modifier, e.g. if the modifier is ctrl + shift + B
3996         // the toString text is "shift ctrl pressed B". Need to parse through that.
3997         StringTokenizer st = new StringTokenizer(keyStroke.toString());
3998         while (st.hasMoreTokens()) {
3999             String text = st.nextToken();
4000             // Meta+Ctrl+Alt+Shift
4001             // 0-3 are shift, ctrl, meta, alt
4002             // 4-7 are for Solaris workstations (though not being used)
4003             if (text.startsWith("met")) {
4004                 debugString("[INFO]:   found meta");
4005                 modifiers |= ActionEvent.META_MASK;
4006             }
4007             if (text.startsWith("ctr")) {
4008                 debugString("[INFO]:   found ctrl");
4009                 modifiers |= ActionEvent.CTRL_MASK;
4010             }
4011             if (text.startsWith("alt")) {
4012                 debugString("[INFO]:   found alt");
4013                 modifiers |= ActionEvent.ALT_MASK;
4014             }
4015             if (text.startsWith("shi")) {
4016                 debugString("   found shift");
4017                 modifiers |= ActionEvent.SHIFT_MASK;
4018             }
4019         }
4020         debugString("[INFO]:   returning modifiers: 0x" + Integer.toHexString(modifiers));
4021         return modifiers;
4022     }
4023 
4024     /*
4025      * returns the number of key bindings associated with this context
4026      */
4027     private int getAccessibleKeyBindingsCount(AccessibleContext ac) {
4028         if (ac == null)
4029             return 0;
4030         int count = 0;
4031 
4032         if (getMnemonic(ac) != null) {
4033             count++;
4034         }
4035         if (getAccelerator(ac) != null) {
4036             count++;
4037         }
4038         return count;
4039     }
4040 
4041     /*
4042      * returns the key binding character at the specified index
4043      */
4044     private char getAccessibleKeyBindingChar(AccessibleContext ac, int index) {
4045         if (ac == null)
4046             return 0;
4047         if((index == 0) && getMnemonic(ac)==null) {// special case when there is no mnemonic
4048             KeyStroke keyStroke = getAccelerator(ac);
4049             if (keyStroke != null) {
4050                 return getKeyChar(keyStroke);
4051             }
4052         }
4053         if (index == 0) {   // mnemonic
4054             KeyStroke keyStroke = getMnemonic(ac);
4055             if (keyStroke != null) {
4056                 return getKeyChar(keyStroke);
4057             }
4058         } else if (index == 1) { // accelerator
4059             KeyStroke keyStroke = getAccelerator(ac);
4060             if (keyStroke != null) {
4061                 return getKeyChar(keyStroke);
4062             }
4063         }
4064         return 0;
4065     }
4066 
4067     /*
4068      * returns the key binding modifiers at the specified index
4069      */
4070     private int getAccessibleKeyBindingModifiers(AccessibleContext ac, int index) {
4071         if (ac == null)
4072             return 0;
4073         if((index == 0) && getMnemonic(ac)==null) {// special case when there is no mnemonic
4074             KeyStroke keyStroke = getAccelerator(ac);
4075             if (keyStroke != null) {
4076                 return getModifiers(keyStroke);
4077             }
4078         }
4079         if (index == 0) {   // mnemonic
4080             KeyStroke keyStroke = getMnemonic(ac);
4081             if (keyStroke != null) {
4082                 return getModifiers(keyStroke);
4083             }
4084         } else if (index == 1) { // accelerator
4085             KeyStroke keyStroke = getAccelerator(ac);
4086             if (keyStroke != null) {
4087                 return getModifiers(keyStroke);
4088             }
4089         }
4090         return 0;
4091     }
4092 
4093     // ========== AccessibleIcon ============
4094 
4095     /*
4096      * return the number of icons associated with this context
4097      */
4098     private int getAccessibleIconsCount(final AccessibleContext ac) {
4099         debugString("[INFO]: getAccessibleIconsCount");
4100         if (ac == null) {
4101             return 0;
4102         }
4103         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4104             @Override
4105             public Integer call() throws Exception {
4106                 AccessibleIcon[] ai = ac.getAccessibleIcon();
4107                 if (ai == null) {
4108                     return 0;
4109                 }
4110                 return ai.length;
4111             }
4112         }, ac);
4113     }
4114 
4115     /*
4116      * return icon description at the specified index
4117      */
4118     private String getAccessibleIconDescription(final AccessibleContext ac, final int index) {
4119         debugString("[INFO]: getAccessibleIconDescription: index = "+index);
4120         if (ac == null) {
4121             return null;
4122         }
4123         return InvocationUtils.invokeAndWait(new Callable<String>() {
4124             @Override
4125             public String call() throws Exception {
4126                 AccessibleIcon[] ai = ac.getAccessibleIcon();
4127                 if (ai == null || index < 0 || index >= ai.length) {
4128                     return null;
4129                 }
4130                 return ai[index].getAccessibleIconDescription();
4131             }
4132         }, ac);
4133     }
4134 
4135     /*
4136      * return icon height at the specified index
4137      */
4138     private int getAccessibleIconHeight(final AccessibleContext ac, final int index) {
4139         debugString("[INFO]: getAccessibleIconHeight: index = "+index);
4140         if (ac == null) {
4141             return 0;
4142         }
4143         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4144             @Override
4145             public Integer call() throws Exception {
4146                 AccessibleIcon[] ai = ac.getAccessibleIcon();
4147                 if (ai == null || index < 0 || index >= ai.length) {
4148                     return 0;
4149                 }
4150                 return ai[index].getAccessibleIconHeight();
4151             }
4152         }, ac);
4153     }
4154 
4155     /*
4156      * return icon width at the specified index
4157      */
4158     private int getAccessibleIconWidth(final AccessibleContext ac, final int index) {
4159         debugString("[INFO]: getAccessibleIconWidth: index = "+index);
4160         if (ac == null) {
4161             return 0;
4162         }
4163         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4164             @Override
4165             public Integer call() throws Exception {
4166                 AccessibleIcon[] ai = ac.getAccessibleIcon();
4167                 if (ai == null || index < 0 || index >= ai.length) {
4168                     return 0;
4169                 }
4170                 return ai[index].getAccessibleIconWidth();
4171             }
4172         }, ac);
4173     }
4174 
4175     // ========= AccessibleAction ===========
4176 
4177     /*
4178      * return the number of icons associated with this context
4179      */
4180     private int getAccessibleActionsCount(final AccessibleContext ac) {
4181         debugString("[INFO]: getAccessibleActionsCount");
4182         if (ac == null) {
4183             return 0;
4184         }
4185         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4186             @Override
4187             public Integer call() throws Exception {
4188                 AccessibleAction aa = ac.getAccessibleAction();
4189                 if (aa == null)
4190                     return 0;
4191                 return aa.getAccessibleActionCount();
4192             }
4193         }, ac);
4194     }
4195 
4196     /*
4197      * return icon description at the specified index
4198      */
4199     private String getAccessibleActionName(final AccessibleContext ac, final int index) {
4200         debugString("[INFO]: getAccessibleActionName: index = "+index);
4201         if (ac == null) {
4202             return null;
4203         }
4204         return InvocationUtils.invokeAndWait(new Callable<String>() {
4205             @Override
4206             public String call() throws Exception {
4207                 AccessibleAction aa = ac.getAccessibleAction();
4208                 if (aa == null) {
4209                     return null;
4210                 }
4211                 return aa.getAccessibleActionDescription(index);
4212             }
4213         }, ac);
4214     }
4215     /*
4216      * return icon description at the specified index
4217      */
4218     private boolean doAccessibleActions(final AccessibleContext ac, final String name) {
4219         debugString("[INFO]: doAccessibleActions: action name = "+name);
4220         if (ac == null || name == null) {
4221             return false;
4222         }
4223         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4224             @Override
4225             public Boolean call() throws Exception {
4226                 AccessibleAction aa = ac.getAccessibleAction();
4227                 if (aa == null) {
4228                     return false;
4229                 }
4230                 int index = -1;
4231                 int numActions = aa.getAccessibleActionCount();
4232                 for (int i = 0; i < numActions; i++) {
4233                     String actionName = aa.getAccessibleActionDescription(i);
4234                     if (name.equals(actionName)) {
4235                         index = i;
4236                         break;
4237                     }
4238                 }
4239                 if (index == -1) {
4240                     return false;
4241                 }
4242                 boolean retval = aa.doAccessibleAction(index);
4243                 return retval;
4244             }
4245         }, ac);
4246     }
4247 
4248     /* ===== AT utility methods ===== */
4249 
4250     /**
4251      * Sets the contents of an AccessibleContext that
4252      * implements AccessibleEditableText with the
4253      * specified text string.
4254      * Returns whether successful.
4255      */
4256     private boolean setTextContents(final AccessibleContext ac, final String text) {
4257         debugString("[INFO]: setTextContents: ac = "+ac+"; text = "+text);
4258 
4259         if (! (ac instanceof AccessibleEditableText)) {
4260             debugString("[WARN]:   ac not instanceof AccessibleEditableText: "+ac);
4261             return false;
4262         }
4263         if (text == null) {
4264             debugString("[WARN]:   text is null");
4265             return false;
4266         }
4267 
4268         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4269             @Override
4270             public Boolean call() throws Exception {
4271                 // check whether the text field is editable
4272                 AccessibleStateSet ass = ac.getAccessibleStateSet();
4273                 if (!ass.contains(AccessibleState.ENABLED)) {
4274                     return false;
4275                 }
4276                 ((AccessibleEditableText) ac).setTextContents(text);
4277                 return true;
4278             }
4279         }, ac);
4280     }
4281 
4282     /**
4283      * Returns the Accessible Context of an Internal Frame object that is
4284      * the ancestor of a given object.  If the object is an Internal Frame
4285      * object or an Internal Frame ancestor object was found, returns the
4286      * object's AccessibleContext.
4287      * If there is no ancestor object that has an Accessible Role of
4288      * Internal Frame, returns (AccessibleContext)0.
4289      */
4290     private AccessibleContext getInternalFrame (AccessibleContext ac) {
4291         return getParentWithRole(ac, AccessibleRole.INTERNAL_FRAME.toString());
4292     }
4293 
4294     /**
4295      * Returns the Accessible Context for the top level object in
4296      * a Java Window.  This is same Accessible Context that is obtained
4297      * from GetAccessibleContextFromHWND for that window.  Returns
4298      * (AccessibleContext)0 on error.
4299      */
4300     private AccessibleContext getTopLevelObject (final AccessibleContext ac) {
4301         debugString("[INFO]: getTopLevelObject; ac = "+ac);
4302         if (ac == null) {
4303             return null;
4304         }
4305         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4306             @Override
4307             public AccessibleContext call() throws Exception {
4308                 if (ac.getAccessibleRole() == AccessibleRole.DIALOG) {
4309                     // return the dialog, not the parent window
4310                     return ac;
4311                 }
4312 
4313                 Accessible parent = ac.getAccessibleParent();
4314                 if (parent == null) {
4315                     return ac;
4316                 }
4317                 Accessible tmp = parent;
4318                 while (tmp != null && tmp.getAccessibleContext() != null) {
4319                     AccessibleContext ac2 = tmp.getAccessibleContext();
4320                     if (ac2 != null && ac2.getAccessibleRole() == AccessibleRole.DIALOG) {
4321                         // return the dialog, not the parent window
4322                         return ac2;
4323                     }
4324                     parent = tmp;
4325                     tmp = parent.getAccessibleContext().getAccessibleParent();
4326                 }
4327                 return parent.getAccessibleContext();
4328             }
4329         }, ac);
4330     }
4331 
4332     /**
4333      * Returns the parent AccessibleContext that has the specified AccessibleRole.
4334      * Returns null on error or if the AccessibleContext does not exist.
4335      */
4336     private AccessibleContext getParentWithRole (final AccessibleContext ac,
4337                                                  final String roleName) {
4338         debugString("[INFO]: getParentWithRole; ac = "+ac + "\n role = "+roleName);
4339         if (ac == null || roleName == null) {
4340             return null;
4341         }
4342 
4343         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4344             @Override
4345             public AccessibleContext call() throws Exception {
4346                 AccessibleRole role = AccessBridge.this.accessibleRoleMap.get(roleName);
4347                 if (role == null) {
4348                     return ac;
4349                 }
4350 
4351                 Accessible parent = ac.getAccessibleParent();
4352                 if (parent == null && ac.getAccessibleRole() == role) {
4353                     return ac;
4354                 }
4355 
4356                 Accessible tmp = parent;
4357                 AccessibleContext tmp_ac = null;
4358 
4359                 while (tmp != null && (tmp_ac = tmp.getAccessibleContext()) != null) {
4360                     AccessibleRole ar = tmp_ac.getAccessibleRole();
4361                     if (ar == role) {
4362                         // found
4363                         return tmp_ac;
4364                     }
4365                     parent = tmp;
4366                     tmp = parent.getAccessibleContext().getAccessibleParent();
4367                 }
4368                 // not found
4369                 return null;
4370             }
4371         }, ac);
4372     }
4373 
4374     /**
4375      * Returns the parent AccessibleContext that has the specified AccessibleRole.
4376      * Otherwise, returns the top level object for the Java Window.
4377      * Returns (AccessibleContext)0 on error.
4378      */
4379     private AccessibleContext getParentWithRoleElseRoot (AccessibleContext ac,
4380                                                          String roleName) {
4381         AccessibleContext retval = getParentWithRole(ac, roleName);
4382         if (retval == null) {
4383             retval = getTopLevelObject(ac);
4384         }
4385         return retval;
4386     }
4387 
4388     /**
4389      * Returns how deep in the object hierarchy a given object is.
4390      * The top most object in the object hierarchy has an object depth of 0.
4391      * Returns -1 on error.
4392      */
4393     private int getObjectDepth(final AccessibleContext ac) {
4394         debugString("[INFO]: getObjectDepth: ac = "+ac);
4395 
4396         if (ac == null) {
4397             return -1;
4398         }
4399         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4400             @Override
4401             public Integer call() throws Exception {
4402                 int count = 0;
4403                 Accessible parent = ac.getAccessibleParent();
4404                 if (parent == null) {
4405                     return count;
4406                 }
4407                 Accessible tmp = parent;
4408                 while (tmp != null && tmp.getAccessibleContext() != null) {
4409                     parent = tmp;
4410                     tmp = parent.getAccessibleContext().getAccessibleParent();
4411                     count++;
4412                 }
4413                 return count;
4414             }
4415         }, ac);
4416     }
4417 
4418     /**
4419      * Returns the Accessible Context of the current ActiveDescendent of an object.
4420      * Returns (AccessibleContext)0 on error.
4421      */
4422     private AccessibleContext getActiveDescendent (final AccessibleContext ac) {
4423         debugString("[INFO]: getActiveDescendent: ac = "+ac);
4424         if (ac == null) {
4425             return null;
4426         }
4427         // workaround for JTree bug where the only possible active
4428         // descendent is the JTree root
4429         final Accessible parent = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
4430             @Override
4431             public Accessible call() throws Exception {
4432                 return ac.getAccessibleParent();
4433             }
4434         }, ac);
4435 
4436         if (parent != null) {
4437             Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
4438                 @Override
4439                 public Accessible call() throws Exception {
4440                     int indexInParent = ac.getAccessibleIndexInParent();
4441                     return parent.getAccessibleContext().getAccessibleChild(indexInParent);
4442                 }
4443             }, ac);
4444 
4445             if (child instanceof JTree) {
4446                 // return the selected node
4447                 final JTree tree = (JTree)child;
4448                 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4449                     @Override
4450                     public AccessibleContext call() throws Exception {
4451                         return new AccessibleJTreeNode(tree,
4452                                 tree.getSelectionPath(),
4453                                 null);
4454                     }
4455                 }, child);
4456             }
4457         }
4458 
4459         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4460             @Override
4461             public AccessibleContext call() throws Exception {
4462                 AccessibleSelection as = ac.getAccessibleSelection();
4463                 if (as == null) {
4464                     return null;
4465                 }
4466                 // assume single selection
4467                 if (as.getAccessibleSelectionCount() != 1) {
4468                     return null;
4469                 }
4470                 Accessible a = as.getAccessibleSelection(0);
4471                 if (a == null) {
4472                     return null;
4473                 }
4474                 return a.getAccessibleContext();
4475             }
4476         }, ac);
4477     }
4478 
4479 
4480     /**
4481      * Additional methods for Teton
4482      */
4483 
4484     /**
4485      * Gets the AccessibleName for a component based upon the JAWS algorithm.
4486      * Returns whether successful.
4487      *
4488      * Bug ID 4916682 - Implement JAWS AccessibleName policy
4489      */
4490     private String getJAWSAccessibleName(final AccessibleContext ac) {
4491         debugString("[INFO]:  getJAWSAccessibleName");
4492         if (ac == null) {
4493             return null;
4494         }
4495         // placeholder
4496         return InvocationUtils.invokeAndWait(new Callable<String>() {
4497             @Override
4498             public String call() throws Exception {
4499                 return ac.getAccessibleName();
4500             }
4501         }, ac);
4502     }
4503 
4504     /**
4505      * Request focus for a component. Returns whether successful;
4506      *
4507      * Bug ID 4944757 - requestFocus method needed
4508      */
4509     private boolean requestFocus(final AccessibleContext ac) {
4510         debugString("[INFO]:  requestFocus");
4511         if (ac == null) {
4512             return false;
4513         }
4514         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4515             @Override
4516             public Boolean call() throws Exception {
4517                 AccessibleComponent acomp = ac.getAccessibleComponent();
4518                 if (acomp == null) {
4519                     return false;
4520                 }
4521                 acomp.requestFocus();
4522                 return ac.getAccessibleStateSet().contains(AccessibleState.FOCUSED);
4523             }
4524         }, ac);
4525     }
4526 
4527     /**
4528      * Selects text between two indices.  Selection includes the
4529      * text at the start index and the text at the end index. Returns
4530      * whether successful;
4531      *
4532      * Bug ID 4944758 - selectTextRange method needed
4533      */
4534     private boolean selectTextRange(final AccessibleContext ac, final int startIndex, final int endIndex) {
4535         debugString("[INFO]:  selectTextRange: start = "+startIndex+"; end = "+endIndex);
4536         if (ac == null) {
4537             return false;
4538         }
4539         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4540             @Override
4541             public Boolean call() throws Exception {
4542                 AccessibleText at = ac.getAccessibleText();
4543                 if (!(at instanceof AccessibleEditableText)) {
4544                     return false;
4545                 }
4546                 ((AccessibleEditableText) at).selectText(startIndex, endIndex);
4547 
4548                 boolean result = at.getSelectionStart() == startIndex &&
4549                         at.getSelectionEnd() == endIndex;
4550                 return result;
4551             }
4552         }, ac);
4553     }
4554 
4555     /**
4556      * Set the caret to a text position. Returns whether successful;
4557      *
4558      * Bug ID 4944770 - setCaretPosition method needed
4559      */
4560     private boolean setCaretPosition(final AccessibleContext ac, final int position) {
4561         debugString("[INFO]: setCaretPosition: position = "+position);
4562         if (ac == null) {
4563             return false;
4564         }
4565         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4566             @Override
4567             public Boolean call() throws Exception {
4568                 AccessibleText at = ac.getAccessibleText();
4569                 if (!(at instanceof AccessibleEditableText)) {
4570                     return false;
4571                 }
4572                 ((AccessibleEditableText) at).selectText(position, position);
4573                 return at.getCaretPosition() == position;
4574             }
4575         }, ac);
4576     }
4577 
4578     /**
4579      * Gets the number of visible children of an AccessibleContext.
4580      *
4581      * Bug ID 4944762- getVisibleChildren for list-like components needed
4582      */
4583     private int _visibleChildrenCount;
4584     private AccessibleContext _visibleChild;
4585     private int _currentVisibleIndex;
4586     private boolean _foundVisibleChild;
4587 
4588     private int getVisibleChildrenCount(AccessibleContext ac) {
4589         debugString("[INFO]: getVisibleChildrenCount");
4590         if (ac == null) {
4591             return -1;
4592         }
4593         _visibleChildrenCount = 0;
4594         _getVisibleChildrenCount(ac);
4595         debugString("[INFO]:   _visibleChildrenCount = "+_visibleChildrenCount);
4596         return _visibleChildrenCount;
4597     }
4598 
4599     /*
4600      * Recursively descends AccessibleContext and gets the number
4601      * of visible children
4602      */
4603     private void _getVisibleChildrenCount(final AccessibleContext ac) {
4604         if (ac == null)
4605             return;
4606         if(ac instanceof AccessibleExtendedTable) {
4607             _getVisibleChildrenCount((AccessibleExtendedTable)ac);
4608             return;
4609         }
4610         int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4611             @Override
4612             public Integer call() throws Exception {
4613                 return ac.getAccessibleChildrenCount();
4614             }
4615         }, ac);
4616         for (int i = 0; i < numChildren; i++) {
4617             final int idx = i;
4618             final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4619                 @Override
4620                 public AccessibleContext call() throws Exception {
4621                     Accessible a = ac.getAccessibleChild(idx);
4622                     if (a != null)
4623                         return a.getAccessibleContext();
4624                     else
4625                         return null;
4626                 }
4627             }, ac);
4628             if ( ac2 == null ||
4629                  (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4630                      @Override
4631                      public Boolean call() throws Exception {
4632                          return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4633                      }
4634                  }, ac))
4635                ) {
4636                 continue;
4637             }
4638             _visibleChildrenCount++;
4639 
4640             if (InvocationUtils.invokeAndWait(new Callable<Integer>() {
4641                 @Override
4642                 public Integer call() throws Exception {
4643                     return ac2.getAccessibleChildrenCount();
4644                 }
4645             }, ac) > 0 ) {
4646                 _getVisibleChildrenCount(ac2);
4647             }
4648         }
4649     }
4650 
4651     /*
4652     * Recursively descends AccessibleContext and gets the number
4653     * of visible children. Stops search if get to invisible part of table.
4654     */
4655     private void _getVisibleChildrenCount(final AccessibleExtendedTable acTable) {
4656         if (acTable == null)
4657             return;
4658         int lastVisibleRow = -1;
4659         int lastVisibleColumn = -1;
4660         boolean foundVisible = false;
4661         int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4662             @Override
4663             public Integer call() throws Exception {
4664                 return acTable.getAccessibleRowCount();
4665             }
4666         }, acTable);
4667         int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4668             @Override
4669             public Integer call() throws Exception {
4670                 return acTable.getAccessibleColumnCount();
4671             }
4672         }, acTable);
4673         for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) {
4674             for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) {
4675                 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) {
4676                     continue;
4677                 }
4678                 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) {
4679                     continue;
4680                 }
4681                 int finalRowIdx = rowIdx;
4682                 int finalColumnIdx = columnIdx;
4683                 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4684                     @Override
4685                     public AccessibleContext call() throws Exception {
4686                         Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx);
4687                         if (a == null)
4688                             return null;
4689                         else
4690                             return a.getAccessibleContext();
4691                     }
4692                 }, acTable);
4693                 if (ac2 == null ||
4694                         (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4695                             @Override
4696                             public Boolean call() throws Exception {
4697                                 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4698                             }
4699                         }, acTable))
4700                         ) {
4701                     if (foundVisible) {
4702                         if (columnIdx != 0 && lastVisibleColumn == -1) {
4703                             //the same row, so we found the last visible column
4704                             lastVisibleColumn = columnIdx - 1;
4705                         } else if (columnIdx == 0 && lastVisibleRow == -1) {
4706                             lastVisibleRow = rowIdx - 1;
4707                         }
4708                     }
4709                     continue;
4710                 }
4711 
4712                 foundVisible = true;
4713 
4714                 _visibleChildrenCount++;
4715 
4716                 if (InvocationUtils.invokeAndWait(new Callable<Integer>() {
4717                     @Override
4718                     public Integer call() throws Exception {
4719                         return ac2.getAccessibleChildrenCount();
4720                     }
4721                 }, acTable) > 0) {
4722                     _getVisibleChildrenCount(ac2);
4723                 }
4724             }
4725         }
4726     }
4727 
4728     /**
4729      * Gets the visible child of an AccessibleContext at the
4730      * specified index
4731      *
4732      * Bug ID 4944762- getVisibleChildren for list-like components needed
4733      */
4734     private AccessibleContext getVisibleChild(AccessibleContext ac, int index) {
4735         debugString("[INFO]: getVisibleChild: index = "+index);
4736         if (ac == null) {
4737             return null;
4738         }
4739         _visibleChild = null;
4740         _currentVisibleIndex = 0;
4741         _foundVisibleChild = false;
4742         _getVisibleChild(ac, index);
4743 
4744         if (_visibleChild != null) {
4745             debugString( "[INFO]:     getVisibleChild: found child = " +
4746                          InvocationUtils.invokeAndWait(new Callable<String>() {
4747                              @Override
4748                              public String call() throws Exception {
4749                                  return AccessBridge.this._visibleChild.getAccessibleName();
4750                              }
4751                          }, ac) );
4752         }
4753         return _visibleChild;
4754     }
4755 
4756     /*
4757      * Recursively searchs AccessibleContext and finds the visible component
4758      * at the specified index
4759      */
4760     private void _getVisibleChild(final AccessibleContext ac, final int index) {
4761         if (_visibleChild != null) {
4762             return;
4763         }
4764         if(ac instanceof AccessibleExtendedTable) {
4765             _getVisibleChild((AccessibleExtendedTable)ac, index);
4766             return;
4767         }
4768         int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4769             @Override
4770             public Integer call() throws Exception {
4771                 return ac.getAccessibleChildrenCount();
4772             }
4773         }, ac);
4774         for (int i = 0; i < numChildren; i++) {
4775             final int idx=i;
4776             final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4777                 @Override
4778                 public AccessibleContext call() throws Exception {
4779                     Accessible a = ac.getAccessibleChild(idx);
4780                     if (a == null)
4781                         return null;
4782                     else
4783                         return a.getAccessibleContext();
4784                 }
4785             }, ac);
4786             if (ac2 == null ||
4787             (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4788                 @Override
4789                 public Boolean call() throws Exception {
4790                     return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4791                 }
4792             }, ac))) {
4793                 continue;
4794             }
4795             if (!_foundVisibleChild && _currentVisibleIndex == index) {
4796             _visibleChild = ac2;
4797             _foundVisibleChild = true;
4798             return;
4799             }
4800             _currentVisibleIndex++;
4801 
4802             if ( InvocationUtils.invokeAndWait(new Callable<Integer>() {
4803                 @Override
4804                 public Integer call() throws Exception {
4805                     return ac2.getAccessibleChildrenCount();
4806                 }
4807             }, ac) > 0 ) {
4808                 _getVisibleChild(ac2, index);
4809             }
4810         }
4811     }
4812 
4813     private void _getVisibleChild(final AccessibleExtendedTable acTable, final int index) {
4814         if (_visibleChild != null) {
4815             return;
4816         }
4817         int lastVisibleRow = -1;
4818         int lastVisibleColumn = -1;
4819         boolean foundVisible = false;
4820         int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4821             @Override
4822             public Integer call() throws Exception {
4823                 return acTable.getAccessibleRowCount();
4824             }
4825         }, acTable);
4826         int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4827             @Override
4828             public Integer call() throws Exception {
4829                 return acTable.getAccessibleColumnCount();
4830             }
4831         }, acTable);
4832         for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) {
4833             for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) {
4834                 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) {
4835                     continue;
4836                 }
4837                 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) {
4838                     continue;
4839                 }
4840                 int finalRowIdx = rowIdx;
4841                 int finalColumnIdx = columnIdx;
4842                 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4843                     @Override
4844                     public AccessibleContext call() throws Exception {
4845                         Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx);
4846                         if (a == null)
4847                             return null;
4848                         else
4849                             return a.getAccessibleContext();
4850                     }
4851                 }, acTable);
4852                 if (ac2 == null ||
4853                         (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4854                             @Override
4855                             public Boolean call() throws Exception {
4856                                 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4857                             }
4858                         }, acTable))) {
4859                     if (foundVisible) {
4860                         if (columnIdx != 0 && lastVisibleColumn == -1) {
4861                             //the same row, so we found the last visible column
4862                             lastVisibleColumn = columnIdx - 1;
4863                         } else if (columnIdx == 0 && lastVisibleRow == -1) {
4864                             lastVisibleRow = rowIdx - 1;
4865                         }
4866                     }
4867                     continue;
4868                 }
4869                 foundVisible = true;
4870 
4871                 if (!_foundVisibleChild && _currentVisibleIndex == index) {
4872                     _visibleChild = ac2;
4873                     _foundVisibleChild = true;
4874                     return;
4875                 }
4876                 _currentVisibleIndex++;
4877 
4878                 if (InvocationUtils.invokeAndWait(new Callable<Integer>() {
4879                     @Override
4880                     public Integer call() throws Exception {
4881                         return ac2.getAccessibleChildrenCount();
4882                     }
4883                 }, acTable) > 0) {
4884                     _getVisibleChild(ac2, index);
4885                 }
4886             }
4887         }
4888     }
4889 
4890     /* ===== Java object memory management code ===== */
4891 
4892     /**
4893      * Class to track object references to ensure the
4894      * Java VM doesn't garbage collect them
4895      */
4896     private class ObjectReferences {
4897 
4898         private class Reference {
4899             private int value;
4900 
4901             Reference(int i) {
4902                 value = i;
4903             }
4904 
4905             public String toString() {
4906                 return ("refCount: " + value);
4907             }
4908         }
4909 
4910         /**
4911         * table object references, to keep 'em from being garbage collected
4912         */
4913         private ConcurrentHashMap<Object,Reference> refs;
4914 
4915         /**
4916         * Constructor
4917         */
4918         ObjectReferences() {
4919             refs = new ConcurrentHashMap<>(4);
4920         }
4921 
4922         /**
4923         * Debugging: dump the contents of ObjectReferences' refs Hashtable
4924         */
4925         String dump() {
4926             return refs.toString();
4927         }
4928 
4929         /**
4930         * Increment ref count; set to 1 if we have no references for it
4931         */
4932         void increment(Object o) {
4933             if (o == null){
4934                 debugString("[WARN]: ObjectReferences::increment - Passed in object is null");
4935                 return;
4936             }
4937 
4938             if (refs.containsKey(o)) {
4939                 (refs.get(o)).value++;
4940             } else {
4941                 refs.put(o, new Reference(1));
4942             }
4943         }
4944 
4945         /**
4946         * Decrement ref count; remove if count drops to 0
4947         */
4948         void decrement(Object o) {
4949             Reference aRef = refs.get(o);
4950             if (aRef != null) {
4951                 aRef.value--;
4952                 if (aRef.value == 0) {
4953                     refs.remove(o);
4954                 } else if (aRef.value < 0) {
4955                     debugString("[ERROR]: decrementing reference count below 0");
4956                 }
4957             } else {
4958                 debugString("[ERROR]: object to decrement not in ObjectReferences table");
4959             }
4960         }
4961 
4962     }
4963 
4964     /* ===== event handling code ===== */
4965 
4966    /**
4967      * native method for handling property change events
4968      */
4969     private native void propertyCaretChange(PropertyChangeEvent e,
4970                         AccessibleContext src,
4971                         int oldValue, int newValue);
4972     private native void propertyDescriptionChange(PropertyChangeEvent e,
4973                         AccessibleContext src,
4974                         String oldValue, String newValue);
4975     private native void propertyNameChange(PropertyChangeEvent e,
4976                         AccessibleContext src,
4977                         String oldValue, String newValue);
4978     private native void propertySelectionChange(PropertyChangeEvent e,
4979                         AccessibleContext src);
4980     private native void propertyStateChange(PropertyChangeEvent e,
4981                         AccessibleContext src,
4982                         String oldValue, String newValue);
4983     private native void propertyTextChange(PropertyChangeEvent e,
4984                         AccessibleContext src);
4985     private native void propertyValueChange(PropertyChangeEvent e,
4986                         AccessibleContext src,
4987                         String oldValue, String newValue);
4988     private native void propertyVisibleDataChange(PropertyChangeEvent e,
4989                         AccessibleContext src);
4990     private native void propertyChildChange(PropertyChangeEvent e,
4991                         AccessibleContext src,
4992                         AccessibleContext oldValue,
4993                         AccessibleContext newValue);
4994     private native void propertyActiveDescendentChange(PropertyChangeEvent e,
4995                         AccessibleContext src,
4996                         AccessibleContext oldValue,
4997                         AccessibleContext newValue);
4998 
4999     private native void javaShutdown();
5000 
5001     /**
5002      * native methods for handling focus events
5003      */
5004     private native void focusGained(FocusEvent e, AccessibleContext src);
5005     private native void focusLost(FocusEvent e, AccessibleContext src);
5006 
5007     /**
5008      * native method for handling caret events
5009      */
5010     private native void caretUpdate(CaretEvent e, AccessibleContext src);
5011 
5012     /**
5013      * native methods for handling mouse events
5014      */
5015     private native void mouseClicked(MouseEvent e, AccessibleContext src);
5016     private native void mouseEntered(MouseEvent e, AccessibleContext src);
5017     private native void mouseExited(MouseEvent e, AccessibleContext src);
5018     private native void mousePressed(MouseEvent e, AccessibleContext src);
5019     private native void mouseReleased(MouseEvent e, AccessibleContext src);
5020 
5021     /**
5022      * native methods for handling menu & popupMenu events
5023      */
5024     private native void menuCanceled(MenuEvent e, AccessibleContext src);
5025     private native void menuDeselected(MenuEvent e, AccessibleContext src);
5026     private native void menuSelected(MenuEvent e, AccessibleContext src);
5027     private native void popupMenuCanceled(PopupMenuEvent e, AccessibleContext src);
5028     private native void popupMenuWillBecomeInvisible(PopupMenuEvent e,
5029                                                      AccessibleContext src);
5030     private native void popupMenuWillBecomeVisible(PopupMenuEvent e,
5031                                                    AccessibleContext src);
5032 
5033     /* ===== event definitions ===== */
5034 
5035     private static final long PROPERTY_CHANGE_EVENTS = 1;
5036     private static final long FOCUS_GAINED_EVENTS = 2;
5037     private static final long FOCUS_LOST_EVENTS = 4;
5038     private static final long FOCUS_EVENTS = (FOCUS_GAINED_EVENTS | FOCUS_LOST_EVENTS);
5039 
5040     private static final long CARET_UPATE_EVENTS = 8;
5041     private static final long CARET_EVENTS = CARET_UPATE_EVENTS;
5042 
5043     private static final long MOUSE_CLICKED_EVENTS = 16;
5044     private static final long MOUSE_ENTERED_EVENTS = 32;
5045     private static final long MOUSE_EXITED_EVENTS = 64;
5046     private static final long MOUSE_PRESSED_EVENTS = 128;
5047     private static final long MOUSE_RELEASED_EVENTS = 256;
5048     private static final long MOUSE_EVENTS = (MOUSE_CLICKED_EVENTS | MOUSE_ENTERED_EVENTS |
5049                                              MOUSE_EXITED_EVENTS | MOUSE_PRESSED_EVENTS |
5050                                              MOUSE_RELEASED_EVENTS);
5051 
5052     private static final long MENU_CANCELED_EVENTS = 512;
5053     private static final long MENU_DESELECTED_EVENTS = 1024;
5054     private static final long MENU_SELECTED_EVENTS = 2048;
5055     private static final long MENU_EVENTS = (MENU_CANCELED_EVENTS | MENU_DESELECTED_EVENTS |
5056                                             MENU_SELECTED_EVENTS);
5057 
5058     private static final long POPUPMENU_CANCELED_EVENTS = 4096;
5059     private static final long POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS = 8192;
5060     private static final long POPUPMENU_WILL_BECOME_VISIBLE_EVENTS = 16384;
5061     private static final long POPUPMENU_EVENTS = (POPUPMENU_CANCELED_EVENTS |
5062                                                  POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS |
5063                                                  POPUPMENU_WILL_BECOME_VISIBLE_EVENTS);
5064 
5065     /* These use their own numbering scheme, to ensure sufficient expansion room */
5066     private static final long PROPERTY_NAME_CHANGE_EVENTS = 1;
5067     private static final long PROPERTY_DESCRIPTION_CHANGE_EVENTS = 2;
5068     private static final long PROPERTY_STATE_CHANGE_EVENTS = 4;
5069     private static final long PROPERTY_VALUE_CHANGE_EVENTS = 8;
5070     private static final long PROPERTY_SELECTION_CHANGE_EVENTS = 16;
5071     private static final long PROPERTY_TEXT_CHANGE_EVENTS = 32;
5072     private static final long PROPERTY_CARET_CHANGE_EVENTS = 64;
5073     private static final long PROPERTY_VISIBLEDATA_CHANGE_EVENTS = 128;
5074     private static final long PROPERTY_CHILD_CHANGE_EVENTS = 256;
5075     private static final long PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS = 512;
5076 
5077 
5078     private static final long PROPERTY_EVENTS = (PROPERTY_NAME_CHANGE_EVENTS |
5079                                                 PROPERTY_DESCRIPTION_CHANGE_EVENTS |
5080                                                 PROPERTY_STATE_CHANGE_EVENTS |
5081                                                 PROPERTY_VALUE_CHANGE_EVENTS |
5082                                                 PROPERTY_SELECTION_CHANGE_EVENTS |
5083                                                 PROPERTY_TEXT_CHANGE_EVENTS |
5084                                                 PROPERTY_CARET_CHANGE_EVENTS |
5085                                                 PROPERTY_VISIBLEDATA_CHANGE_EVENTS |
5086                                                 PROPERTY_CHILD_CHANGE_EVENTS |
5087                                                 PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS);
5088 
5089     /**
5090      * The EventHandler class listens for Java events and
5091      * forwards them to the AT
5092      */
5093     private class EventHandler implements PropertyChangeListener,
5094                                           FocusListener, CaretListener,
5095                                           MenuListener, PopupMenuListener,
5096                                           MouseListener, WindowListener,
5097                                           ChangeListener {
5098 
5099         private AccessBridge accessBridge;
5100         private long javaEventMask = 0;
5101         private long accessibilityEventMask = 0;
5102 
5103         EventHandler(AccessBridge bridge) {
5104             accessBridge = bridge;
5105 
5106             // Register to receive WINDOW_OPENED and WINDOW_CLOSED
5107             // events.  Add the event source as a native window
5108             // handler is it implements NativeWindowHandler.
5109             // SwingEventMonitor.addWindowListener(this);
5110         }
5111 
5112         // --------- Event Notification Registration methods
5113 
5114         /**
5115          * Invoked the first time a window is made visible.
5116          */
5117         public void windowOpened(WindowEvent e) {
5118             // If the window is a NativeWindowHandler, add it.
5119             Object o = null;
5120             if (e != null)
5121                 o = e.getSource();
5122             if (o instanceof NativeWindowHandler) {
5123                 addNativeWindowHandler((NativeWindowHandler)o);
5124             }
5125         }
5126 
5127         /**
5128          * Invoked when the user attempts to close the window
5129          * from the window's system menu.  If the program does not
5130          * explicitly hide or dispose the window while processing
5131          * this event, the window close operation will be canceled.
5132          */
5133         public void windowClosing(WindowEvent e) {}
5134 
5135         /**
5136          * Invoked when a window has been closed as the result
5137          * of calling dispose on the window.
5138          */
5139         public void windowClosed(WindowEvent e) {
5140             // If the window is a NativeWindowHandler, remove it.
5141             Object o = null;
5142             if (e != null)
5143                 o = e.getSource();
5144             if (o instanceof NativeWindowHandler) {
5145                 removeNativeWindowHandler((NativeWindowHandler)o);
5146             }
5147         }
5148 
5149         /**
5150          * Invoked when a window is changed from a normal to a
5151          * minimized state. For many platforms, a minimized window
5152          * is displayed as the icon specified in the window's
5153          * iconImage property.
5154          * @see java.awt.Frame#setIconImage
5155          */
5156         public void windowIconified(WindowEvent e) {}
5157 
5158         /**
5159          * Invoked when a window is changed from a minimized
5160          * to a normal state.
5161          */
5162         public void windowDeiconified(WindowEvent e) {}
5163 
5164         /**
5165          * Invoked when the Window is set to be the active Window. Only a Frame or
5166          * a Dialog can be the active Window. The native windowing system may
5167          * denote the active Window or its children with special decorations, such
5168          * as a highlighted title bar. The active Window is always either the
5169          * focused Window, or the first Frame or Dialog that is an owner of the
5170          * focused Window.
5171          */
5172         public void windowActivated(WindowEvent e) {}
5173 
5174         /**
5175          * Invoked when a Window is no longer the active Window. Only a Frame or a
5176          * Dialog can be the active Window. The native windowing system may denote
5177          * the active Window or its children with special decorations, such as a
5178          * highlighted title bar. The active Window is always either the focused
5179          * Window, or the first Frame or Dialog that is an owner of the focused
5180          * Window.
5181          */
5182         public void windowDeactivated(WindowEvent e) {}
5183 
5184         /**
5185          * Turn on event monitoring for the event type passed in
5186          * If necessary, add the appropriate event listener (if
5187          * no other event of that type is being listened for)
5188          */
5189         void addJavaEventNotification(long type) {
5190             long newEventMask = javaEventMask | type;
5191             /*
5192             if ( ((javaEventMask & PROPERTY_EVENTS) == 0) &&
5193                  ((newEventMask & PROPERTY_EVENTS) != 0) ) {
5194                 AccessibilityEventMonitor.addPropertyChangeListener(this);
5195             }
5196             */
5197             if ( ((javaEventMask & FOCUS_EVENTS) == 0) &&
5198                 ((newEventMask & FOCUS_EVENTS) != 0) ) {
5199                 SwingEventMonitor.addFocusListener(this);
5200             }
5201             if ( ((javaEventMask & CARET_EVENTS) == 0) &&
5202                 ((newEventMask & CARET_EVENTS) != 0) ) {
5203                 SwingEventMonitor.addCaretListener(this);
5204             }
5205             if ( ((javaEventMask & MOUSE_EVENTS) == 0) &&
5206                 ((newEventMask & MOUSE_EVENTS) != 0) ) {
5207                 SwingEventMonitor.addMouseListener(this);
5208             }
5209             if ( ((javaEventMask & MENU_EVENTS) == 0) &&
5210                 ((newEventMask & MENU_EVENTS) != 0) ) {
5211                 SwingEventMonitor.addMenuListener(this);
5212                 SwingEventMonitor.addPopupMenuListener(this);
5213             }
5214             if ( ((javaEventMask & POPUPMENU_EVENTS) == 0) &&
5215                 ((newEventMask & POPUPMENU_EVENTS) != 0) ) {
5216                 SwingEventMonitor.addPopupMenuListener(this);
5217             }
5218 
5219             javaEventMask = newEventMask;
5220         }
5221 
5222         /**
5223          * Turn off event monitoring for the event type passed in
5224          * If necessary, remove the appropriate event listener (if
5225          * no other event of that type is being listened for)
5226          */
5227         void removeJavaEventNotification(long type) {
5228             long newEventMask = javaEventMask & (~type);
5229             /*
5230             if ( ((javaEventMask & PROPERTY_EVENTS) != 0) &&
5231                  ((newEventMask & PROPERTY_EVENTS) == 0) ) {
5232                 AccessibilityEventMonitor.removePropertyChangeListener(this);
5233             }
5234             */
5235             if (((javaEventMask & FOCUS_EVENTS) != 0) &&
5236                 ((newEventMask & FOCUS_EVENTS) == 0)) {
5237                 SwingEventMonitor.removeFocusListener(this);
5238             }
5239             if (((javaEventMask & CARET_EVENTS) != 0) &&
5240                 ((newEventMask & CARET_EVENTS) == 0)) {
5241                 SwingEventMonitor.removeCaretListener(this);
5242             }
5243             if (((javaEventMask & MOUSE_EVENTS) == 0) &&
5244                 ((newEventMask & MOUSE_EVENTS) != 0)) {
5245                 SwingEventMonitor.removeMouseListener(this);
5246             }
5247             if (((javaEventMask & MENU_EVENTS) == 0) &&
5248                 ((newEventMask & MENU_EVENTS) != 0)) {
5249                 SwingEventMonitor.removeMenuListener(this);
5250             }
5251             if (((javaEventMask & POPUPMENU_EVENTS) == 0) &&
5252                 ((newEventMask & POPUPMENU_EVENTS) != 0)) {
5253                 SwingEventMonitor.removePopupMenuListener(this);
5254             }
5255 
5256             javaEventMask = newEventMask;
5257         }
5258 
5259         /**
5260          * Turn on event monitoring for the event type passed in
5261          * If necessary, add the appropriate event listener (if
5262          * no other event of that type is being listened for)
5263          */
5264         void addAccessibilityEventNotification(long type) {
5265             long newEventMask = accessibilityEventMask | type;
5266             if ( ((accessibilityEventMask & PROPERTY_EVENTS) == 0) &&
5267                  ((newEventMask & PROPERTY_EVENTS) != 0) ) {
5268                 AccessibilityEventMonitor.addPropertyChangeListener(this);
5269             }
5270             accessibilityEventMask = newEventMask;
5271         }
5272 
5273         /**
5274          * Turn off event monitoring for the event type passed in
5275          * If necessary, remove the appropriate event listener (if
5276          * no other event of that type is being listened for)
5277          */
5278         void removeAccessibilityEventNotification(long type) {
5279             long newEventMask = accessibilityEventMask & (~type);
5280             if ( ((accessibilityEventMask & PROPERTY_EVENTS) != 0) &&
5281                  ((newEventMask & PROPERTY_EVENTS) == 0) ) {
5282                 AccessibilityEventMonitor.removePropertyChangeListener(this);
5283             }
5284             accessibilityEventMask = newEventMask;
5285         }
5286 
5287         /**
5288          *  ------- property change event glue
5289          */
5290         // This is invoked on the EDT , as
5291         public void propertyChange(PropertyChangeEvent e) {
5292 
5293             accessBridge.debugString("[INFO]: propertyChange(" + e.toString() + ") called");
5294 
5295             if (e != null && (accessibilityEventMask & PROPERTY_EVENTS) != 0) {
5296                 Object o = e.getSource();
5297                 AccessibleContext ac;
5298 
5299                 if (o instanceof AccessibleContext) {
5300                     ac = (AccessibleContext) o;
5301                 } else {
5302                     Accessible a = Translator.getAccessible(e.getSource());
5303                     if (a == null)
5304                         return;
5305                     else
5306                         ac = a.getAccessibleContext();
5307                 }
5308                 if (ac != null) {
5309                     InvocationUtils.registerAccessibleContext(ac, AppContext.getAppContext());
5310 
5311                     accessBridge.debugString("[INFO]: AccessibleContext: " + ac);
5312                     String propertyName = e.getPropertyName();
5313 
5314                     if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CARET_PROPERTY) == 0) {
5315                         int oldValue = 0;
5316                         int newValue = 0;
5317 
5318                         if (e.getOldValue() instanceof Integer) {
5319                             oldValue = ((Integer) e.getOldValue()).intValue();
5320                         }
5321                         if (e.getNewValue() instanceof Integer) {
5322                             newValue = ((Integer) e.getNewValue()).intValue();
5323                         }
5324                         accessBridge.debugString("[INFO]:  - about to call propertyCaretChange()   old value: " + oldValue + "new value: " + newValue);
5325                         accessBridge.propertyCaretChange(e, ac, oldValue, newValue);
5326 
5327                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY) == 0) {
5328                         String oldValue = null;
5329                         String newValue = null;
5330 
5331                         if (e.getOldValue() != null) {
5332                             oldValue = e.getOldValue().toString();
5333                         }
5334                         if (e.getNewValue() != null) {
5335                             newValue = e.getNewValue().toString();
5336                         }
5337                         accessBridge.debugString("[INFO]:  - about to call propertyDescriptionChange()   old value: " + oldValue + "new value: " + newValue);
5338                         accessBridge.propertyDescriptionChange(e, ac, oldValue, newValue);
5339 
5340                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_NAME_PROPERTY) == 0) {
5341                         String oldValue = null;
5342                         String newValue = null;
5343 
5344                         if (e.getOldValue() != null) {
5345                             oldValue = e.getOldValue().toString();
5346                         }
5347                         if (e.getNewValue() != null) {
5348                             newValue = e.getNewValue().toString();
5349                         }
5350                         accessBridge.debugString("[INFO]:  - about to call propertyNameChange()   old value: " + oldValue + " new value: " + newValue);
5351                         accessBridge.propertyNameChange(e, ac, oldValue, newValue);
5352 
5353                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY) == 0) {
5354                         accessBridge.debugString("[INFO]:  - about to call propertySelectionChange() " + ac +  "   " + Thread.currentThread() + "   " + e.getSource());
5355 
5356                         accessBridge.propertySelectionChange(e, ac);
5357 
5358                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_STATE_PROPERTY) == 0) {
5359                         String oldValue = null;
5360                         String newValue = null;
5361 
5362                         // Localization fix requested by Oliver for EA-1
5363                         if (e.getOldValue() != null) {
5364                             AccessibleState oldState = (AccessibleState) e.getOldValue();
5365                             oldValue = oldState.toDisplayString(Locale.US);
5366                         }
5367                         if (e.getNewValue() != null) {
5368                             AccessibleState newState = (AccessibleState) e.getNewValue();
5369                             newValue = newState.toDisplayString(Locale.US);
5370                         }
5371 
5372                         accessBridge.debugString("[INFO]:  - about to call propertyStateChange()");
5373                         accessBridge.propertyStateChange(e, ac, oldValue, newValue);
5374 
5375                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_TEXT_PROPERTY) == 0) {
5376                         accessBridge.debugString("[INFO]:  - about to call propertyTextChange()");
5377                         accessBridge.propertyTextChange(e, ac);
5378 
5379                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY) == 0) {  // strings 'cause of floating point, etc.
5380                         String oldValue = null;
5381                         String newValue = null;
5382 
5383                         if (e.getOldValue() != null) {
5384                             oldValue = e.getOldValue().toString();
5385                         }
5386                         if (e.getNewValue() != null) {
5387                             newValue = e.getNewValue().toString();
5388                         }
5389                         accessBridge.debugString("[INFO]:  - about to call propertyDescriptionChange()");
5390                         accessBridge.propertyValueChange(e, ac, oldValue, newValue);
5391 
5392                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY) == 0) {
5393                         accessBridge.propertyVisibleDataChange(e, ac);
5394 
5395                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) {
5396                         AccessibleContext oldAC = null;
5397                         AccessibleContext newAC = null;
5398                         Accessible a;
5399 
5400                         if (e.getOldValue() instanceof AccessibleContext) {
5401                             oldAC = (AccessibleContext) e.getOldValue();
5402                             InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext());
5403                         }
5404                         if (e.getNewValue() instanceof AccessibleContext) {
5405                             newAC = (AccessibleContext) e.getNewValue();
5406                             InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext());
5407                         }
5408                         accessBridge.debugString("[INFO]:  - about to call propertyChildChange()   old AC: " + oldAC + "new AC: " + newAC);
5409                         accessBridge.propertyChildChange(e, ac, oldAC, newAC);
5410 
5411                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0) {
5412                         handleActiveDescendentEvent(e, ac);
5413                     }
5414                 }
5415             }
5416         }
5417 
5418         /*
5419         * Handle an ActiveDescendent PropertyChangeEvent.  This
5420         * method works around a JTree bug where ActiveDescendent
5421         * PropertyChangeEvents have the wrong parent.
5422         */
5423         private AccessibleContext prevAC = null; // previous AccessibleContext
5424 
5425         private void handleActiveDescendentEvent(PropertyChangeEvent e,
5426                                                  AccessibleContext ac) {
5427             if (e == null || ac == null)
5428                 return;
5429             AccessibleContext oldAC = null;
5430             AccessibleContext newAC = null;
5431             Accessible a;
5432 
5433             // get the old active descendent
5434             if (e.getOldValue() instanceof Accessible) {
5435                 oldAC = ((Accessible) e.getOldValue()).getAccessibleContext();
5436             } else if (e.getOldValue() instanceof Component) {
5437                 a = Translator.getAccessible(e.getOldValue());
5438                 if (a != null) {
5439                     oldAC = a.getAccessibleContext();
5440                 }
5441             }
5442             if (oldAC != null) {
5443                 Accessible parent = oldAC.getAccessibleParent();
5444                 if (parent instanceof JTree) {
5445                     // use the previous AccessibleJTreeNode
5446                     oldAC = prevAC;
5447                 }
5448             }
5449 
5450             // get the new active descendent
5451             if (e.getNewValue() instanceof Accessible) {
5452                 newAC = ((Accessible) e.getNewValue()).getAccessibleContext();
5453             } else if (e.getNewValue() instanceof Component) {
5454                 a = Translator.getAccessible(e.getNewValue());
5455                 if (a != null) {
5456                     newAC = a.getAccessibleContext();
5457                 }
5458             }
5459             if (newAC != null) {
5460                 Accessible parent = newAC.getAccessibleParent();
5461                 if (parent instanceof JTree) {
5462                     // use a new AccessibleJTreeNode with the right parent
5463                     JTree tree = (JTree)parent;
5464                     newAC = new AccessibleJTreeNode(tree,
5465                                                     tree.getSelectionPath(),
5466                                                     null);
5467                 }
5468             }
5469             prevAC = newAC;
5470 
5471             accessBridge.debugString("[INFO]:   - about to call propertyActiveDescendentChange()   AC: " + ac + "   old AC: " + oldAC + "new AC: " + newAC);
5472             InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext());
5473             InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext());
5474             accessBridge.propertyActiveDescendentChange(e, ac, oldAC, newAC);
5475         }
5476 
5477         /**
5478         *  ------- focus event glue
5479         */
5480         private boolean stateChangeListenerAdded = false;
5481 
5482         public void focusGained(FocusEvent e) {
5483             processFocusGained();
5484         }
5485 
5486         public void stateChanged(ChangeEvent e) {
5487             processFocusGained();
5488         }
5489 
5490         private void processFocusGained() {
5491             Component focusOwner = KeyboardFocusManager.
5492             getCurrentKeyboardFocusManager().getFocusOwner();
5493             if (focusOwner == null) {
5494                 return;
5495             }
5496 
5497             // Only menus and popup selections are handled by the JRootPane.
5498             if (focusOwner instanceof JRootPane) {
5499                 MenuElement [] path =
5500                 MenuSelectionManager.defaultManager().getSelectedPath();
5501                 if (path.length > 1) {
5502                     Component penult = path[path.length-2].getComponent();
5503                     Component last = path[path.length-1].getComponent();
5504 
5505                     if (last instanceof JPopupMenu) {
5506                         // This is a popup with nothing in the popup
5507                         // selected. The menu itself is selected.
5508                         FocusEvent e = new FocusEvent(penult, FocusEvent.FOCUS_GAINED);
5509                         AccessibleContext context = penult.getAccessibleContext();
5510                         InvocationUtils.registerAccessibleContext(context, SunToolkit.targetToAppContext(penult));
5511                         accessBridge.focusGained(e, context);
5512                     } else if (penult instanceof JPopupMenu) {
5513                         // This is a popup with an item selected
5514                         FocusEvent e =
5515                         new FocusEvent(last, FocusEvent.FOCUS_GAINED);
5516                         AccessibleContext focusedAC = last.getAccessibleContext();
5517                         InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(last));
5518                         accessBridge.debugString("[INFO]:  - about to call focusGained()   AC: " + focusedAC);
5519                         accessBridge.focusGained(e, focusedAC);
5520                     }
5521                 }
5522             } else {
5523                 // The focus owner has the selection.
5524                 if (focusOwner instanceof Accessible) {
5525                     FocusEvent e = new FocusEvent(focusOwner,
5526                                                   FocusEvent.FOCUS_GAINED);
5527                     AccessibleContext focusedAC = focusOwner.getAccessibleContext();
5528                     InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(focusOwner));
5529                     accessBridge.debugString("[INFO]:  - about to call focusGained()   AC: " + focusedAC);
5530                     accessBridge.focusGained(e, focusedAC);
5531                 }
5532             }
5533         }
5534 
5535         public void focusLost(FocusEvent e) {
5536             if (e != null && (javaEventMask & FOCUS_LOST_EVENTS) != 0) {
5537                 Accessible a = Translator.getAccessible(e.getSource());
5538                 if (a != null) {
5539                     accessBridge.debugString("[INFO]:  - about to call focusLost()   AC: " + a.getAccessibleContext());
5540                     AccessibleContext context = a.getAccessibleContext();
5541                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5542                     accessBridge.focusLost(e, context);
5543                 }
5544             }
5545         }
5546 
5547         /**
5548          *  ------- caret event glue
5549          */
5550         public void caretUpdate(CaretEvent e) {
5551             if (e != null && (javaEventMask & CARET_UPATE_EVENTS) != 0) {
5552                 Accessible a = Translator.getAccessible(e.getSource());
5553                 if (a != null) {
5554                     AccessibleContext context = a.getAccessibleContext();
5555                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5556                     accessBridge.caretUpdate(e, context);
5557                 }
5558             }
5559         }
5560 
5561     /**
5562      *  ------- mouse event glue
5563      */
5564 
5565         public void mouseClicked(MouseEvent e) {
5566             if (e != null && (javaEventMask & MOUSE_CLICKED_EVENTS) != 0) {
5567                 Accessible a = Translator.getAccessible(e.getSource());
5568                 if (a != null) {
5569                     AccessibleContext context = a.getAccessibleContext();
5570                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5571                     accessBridge.mouseClicked(e, context);
5572                 }
5573             }
5574         }
5575 
5576         public void mouseEntered(MouseEvent e) {
5577             if (e != null && (javaEventMask & MOUSE_ENTERED_EVENTS) != 0) {
5578                 Accessible a = Translator.getAccessible(e.getSource());
5579                 if (a != null) {
5580                     AccessibleContext context = a.getAccessibleContext();
5581                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5582                     accessBridge.mouseEntered(e, context);
5583                 }
5584             }
5585         }
5586 
5587         public void mouseExited(MouseEvent e) {
5588             if (e != null && (javaEventMask & MOUSE_EXITED_EVENTS) != 0) {
5589                 Accessible a = Translator.getAccessible(e.getSource());
5590                 if (a != null) {
5591                     AccessibleContext context = a.getAccessibleContext();
5592                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5593                     accessBridge.mouseExited(e, context);
5594                 }
5595             }
5596         }
5597 
5598         public void mousePressed(MouseEvent e) {
5599             if (e != null && (javaEventMask & MOUSE_PRESSED_EVENTS) != 0) {
5600                 Accessible a = Translator.getAccessible(e.getSource());
5601                 if (a != null) {
5602                     AccessibleContext context = a.getAccessibleContext();
5603                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5604                     accessBridge.mousePressed(e, context);
5605                 }
5606             }
5607         }
5608 
5609         public void mouseReleased(MouseEvent e) {
5610             if (e != null && (javaEventMask & MOUSE_RELEASED_EVENTS) != 0) {
5611                 Accessible a = Translator.getAccessible(e.getSource());
5612                 if (a != null) {
5613                     AccessibleContext context = a.getAccessibleContext();
5614                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5615                     accessBridge.mouseReleased(e, context);
5616                 }
5617             }
5618         }
5619 
5620         /**
5621          *  ------- menu event glue
5622          */
5623         public void menuCanceled(MenuEvent e) {
5624             if (e != null && (javaEventMask & MENU_CANCELED_EVENTS) != 0) {
5625                 Accessible a = Translator.getAccessible(e.getSource());
5626                 if (a != null) {
5627                     AccessibleContext context = a.getAccessibleContext();
5628                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5629                     accessBridge.menuCanceled(e, context);
5630                 }
5631             }
5632         }
5633 
5634         public void menuDeselected(MenuEvent e) {
5635             if (e != null && (javaEventMask & MENU_DESELECTED_EVENTS) != 0) {
5636                 Accessible a = Translator.getAccessible(e.getSource());
5637                 if (a != null) {
5638                     AccessibleContext context = a.getAccessibleContext();
5639                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5640                     accessBridge.menuDeselected(e, context);
5641                 }
5642             }
5643         }
5644 
5645         public void menuSelected(MenuEvent e) {
5646             if (e != null && (javaEventMask & MENU_SELECTED_EVENTS) != 0) {
5647                 Accessible a = Translator.getAccessible(e.getSource());
5648                 if (a != null) {
5649                     AccessibleContext context = a.getAccessibleContext();
5650                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5651                     accessBridge.menuSelected(e, context);
5652                 }
5653             }
5654         }
5655 
5656         public void popupMenuCanceled(PopupMenuEvent e) {
5657             if (e != null && (javaEventMask & POPUPMENU_CANCELED_EVENTS) != 0) {
5658                 Accessible a = Translator.getAccessible(e.getSource());
5659                 if (a != null) {
5660                     AccessibleContext context = a.getAccessibleContext();
5661                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5662                     accessBridge.popupMenuCanceled(e, context);
5663                 }
5664             }
5665         }
5666 
5667         public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
5668             if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS) != 0) {
5669                 Accessible a = Translator.getAccessible(e.getSource());
5670                 if (a != null) {
5671                     AccessibleContext context = a.getAccessibleContext();
5672                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5673                     accessBridge.popupMenuWillBecomeInvisible(e, context);
5674                 }
5675             }
5676         }
5677 
5678         public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
5679             if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_VISIBLE_EVENTS) != 0) {
5680                 Accessible a = Translator.getAccessible(e.getSource());
5681                 if (a != null) {
5682                     AccessibleContext context = a.getAccessibleContext();
5683                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5684                     accessBridge.popupMenuWillBecomeVisible(e, context);
5685                 }
5686             }
5687         }
5688 
5689     } // End of EventHandler Class
5690 
5691     // --------- Event Notification Registration methods
5692 
5693     /**
5694      *  Wrapper method around eventHandler.addJavaEventNotification()
5695      */
5696     private void addJavaEventNotification(final long type) {
5697         EventQueue.invokeLater(new Runnable() {
5698             public void run(){
5699                 eventHandler.addJavaEventNotification(type);
5700             }
5701         });
5702     }
5703 
5704     /**
5705      *  Wrapper method around eventHandler.removeJavaEventNotification()
5706      */
5707     private void removeJavaEventNotification(final long type) {
5708         EventQueue.invokeLater(new Runnable() {
5709             public void run(){
5710                 eventHandler.removeJavaEventNotification(type);
5711             }
5712         });
5713     }
5714 
5715 
5716     /**
5717      *  Wrapper method around eventHandler.addAccessibilityEventNotification()
5718      */
5719     private void addAccessibilityEventNotification(final long type) {
5720         EventQueue.invokeLater(new Runnable() {
5721             public void run(){
5722                 eventHandler.addAccessibilityEventNotification(type);
5723             }
5724         });
5725     }
5726 
5727     /**
5728      *  Wrapper method around eventHandler.removeAccessibilityEventNotification()
5729      */
5730     private void removeAccessibilityEventNotification(final long type) {
5731         EventQueue.invokeLater(new Runnable() {
5732             public void run(){
5733                 eventHandler.removeAccessibilityEventNotification(type);
5734             }
5735         });
5736     }
5737 
5738     /**
5739      ******************************************************
5740      * All AccessibleRoles
5741      *
5742      * We shouldn't have to do this since it requires us
5743      * to synchronize the allAccessibleRoles array when
5744      * the AccessibleRoles class interface changes. However,
5745      * there is no Accessibility API method to get all
5746      * AccessibleRoles
5747      ******************************************************
5748      */
5749     private AccessibleRole [] allAccessibleRoles = {
5750     /**
5751      * Object is used to alert the user about something.
5752      */
5753     AccessibleRole.ALERT,
5754 
5755     /**
5756      * The header for a column of data.
5757      */
5758     AccessibleRole.COLUMN_HEADER,
5759 
5760     /**
5761      * Object that can be drawn into and is used to trap
5762      * events.
5763      * @see #FRAME
5764      * @see #GLASS_PANE
5765      * @see #LAYERED_PANE
5766      */
5767     AccessibleRole.CANVAS,
5768 
5769     /**
5770      * A list of choices the user can select from.  Also optionally
5771      * allows the user to enter a choice of their own.
5772      */
5773     AccessibleRole.COMBO_BOX,
5774 
5775     /**
5776      * An iconified internal frame in a DESKTOP_PANE.
5777      * @see #DESKTOP_PANE
5778      * @see #INTERNAL_FRAME
5779      */
5780     AccessibleRole.DESKTOP_ICON,
5781 
5782     /**
5783      * A frame-like object that is clipped by a desktop pane.  The
5784      * desktop pane, internal frame, and desktop icon objects are
5785      * often used to create multiple document interfaces within an
5786      * application.
5787      * @see #DESKTOP_ICON
5788      * @see #DESKTOP_PANE
5789      * @see #FRAME
5790      */
5791     AccessibleRole.INTERNAL_FRAME,
5792 
5793     /**
5794      * A pane that supports internal frames and
5795      * iconified versions of those internal frames.
5796      * @see #DESKTOP_ICON
5797      * @see #INTERNAL_FRAME
5798      */
5799     AccessibleRole.DESKTOP_PANE,
5800 
5801     /**
5802      * A specialized pane whose primary use is inside a DIALOG
5803      * @see #DIALOG
5804      */
5805     AccessibleRole.OPTION_PANE,
5806 
5807     /**
5808      * A top level window with no title or border.
5809      * @see #FRAME
5810      * @see #DIALOG
5811      */
5812     AccessibleRole.WINDOW,
5813 
5814     /**
5815      * A top level window with a title bar, border, menu bar, etc.  It is
5816      * often used as the primary window for an application.
5817      * @see #DIALOG
5818      * @see #CANVAS
5819      * @see #WINDOW
5820      */
5821     AccessibleRole.FRAME,
5822 
5823     /**
5824      * A top level window with title bar and a border.  A dialog is similar
5825      * to a frame, but it has fewer properties and is often used as a
5826      * secondary window for an application.
5827      * @see #FRAME
5828      * @see #WINDOW
5829      */
5830     AccessibleRole.DIALOG,
5831 
5832     /**
5833      * A specialized dialog that lets the user choose a color.
5834      */
5835     AccessibleRole.COLOR_CHOOSER,
5836 
5837 
5838     /**
5839      * A pane that allows the user to navigate through
5840      * and select the contents of a directory.  May be used
5841      * by a file chooser.
5842      * @see #FILE_CHOOSER
5843      */
5844     AccessibleRole.DIRECTORY_PANE,
5845 
5846     /**
5847      * A specialized dialog that displays the files in the directory
5848      * and lets the user select a file, browse a different directory,
5849      * or specify a filename.  May use the directory pane to show the
5850      * contents of a directory.
5851      * @see #DIRECTORY_PANE
5852      */
5853     AccessibleRole.FILE_CHOOSER,
5854 
5855     /**
5856      * An object that fills up space in a user interface.  It is often
5857      * used in interfaces to tweak the spacing between components,
5858      * but serves no other purpose.
5859      */
5860     AccessibleRole.FILLER,
5861 
5862     /**
5863      * A hypertext anchor
5864      */
5865     // AccessibleRole.HYPERLINK,
5866 
5867     /**
5868      * A small fixed size picture, typically used to decorate components.
5869      */
5870     AccessibleRole.ICON,
5871 
5872     /**
5873      * An object used to present an icon or short string in an interface.
5874      */
5875     AccessibleRole.LABEL,
5876 
5877     /**
5878      * A specialized pane that has a glass pane and a layered pane as its
5879      * children.
5880      * @see #GLASS_PANE
5881      * @see #LAYERED_PANE
5882      */
5883     AccessibleRole.ROOT_PANE,
5884 
5885     /**
5886      * A pane that is guaranteed to be painted on top
5887      * of all panes beneath it.
5888      * @see #ROOT_PANE
5889      * @see #CANVAS
5890      */
5891     AccessibleRole.GLASS_PANE,
5892 
5893     /**
5894      * A specialized pane that allows its children to be drawn in layers,
5895      * providing a form of stacking order.  This is usually the pane that
5896      * holds the menu bar as well as the pane that contains most of the
5897      * visual components in a window.
5898      * @see #GLASS_PANE
5899      * @see #ROOT_PANE
5900      */
5901     AccessibleRole.LAYERED_PANE,
5902 
5903     /**
5904      * An object that presents a list of objects to the user and allows the
5905      * user to select one or more of them.  A list is usually contained
5906      * within a scroll pane.
5907      * @see #SCROLL_PANE
5908      * @see #LIST_ITEM
5909      */
5910     AccessibleRole.LIST,
5911 
5912     /**
5913      * An object that presents an element in a list.  A list is usually
5914      * contained within a scroll pane.
5915      * @see #SCROLL_PANE
5916      * @see #LIST
5917      */
5918     AccessibleRole.LIST_ITEM,
5919 
5920     /**
5921      * An object usually drawn at the top of the primary dialog box of
5922      * an application that contains a list of menus the user can choose
5923      * from.  For example, a menu bar might contain menus for "File,"
5924      * "Edit," and "Help."
5925      * @see #MENU
5926      * @see #POPUP_MENU
5927      * @see #LAYERED_PANE
5928      */
5929     AccessibleRole.MENU_BAR,
5930 
5931     /**
5932      * A temporary window that is usually used to offer the user a
5933      * list of choices, and then hides when the user selects one of
5934      * those choices.
5935      * @see #MENU
5936      * @see #MENU_ITEM
5937      */
5938     AccessibleRole.POPUP_MENU,
5939 
5940     /**
5941      * An object usually found inside a menu bar that contains a list
5942      * of actions the user can choose from.  A menu can have any object
5943      * as its children, but most often they are menu items, other menus,
5944      * or rudimentary objects such as radio buttons, check boxes, or
5945      * separators.  For example, an application may have an "Edit" menu
5946      * that contains menu items for "Cut" and "Paste."
5947      * @see #MENU_BAR
5948      * @see #MENU_ITEM
5949      * @see #SEPARATOR
5950      * @see #RADIO_BUTTON
5951      * @see #CHECK_BOX
5952      * @see #POPUP_MENU
5953      */
5954     AccessibleRole.MENU,
5955 
5956     /**
5957      * An object usually contained in a menu that presents an action
5958      * the user can choose.  For example, the "Cut" menu item in an
5959      * "Edit" menu would be an action the user can select to cut the
5960      * selected area of text in a document.
5961      * @see #MENU_BAR
5962      * @see #SEPARATOR
5963      * @see #POPUP_MENU
5964      */
5965     AccessibleRole.MENU_ITEM,
5966 
5967     /**
5968      * An object usually contained in a menu to provide a visual
5969      * and logical separation of the contents in a menu.  For example,
5970      * the "File" menu of an application might contain menu items for
5971      * "Open," "Close," and "Exit," and will place a separator between
5972      * "Close" and "Exit" menu items.
5973      * @see #MENU
5974      * @see #MENU_ITEM
5975      */
5976     AccessibleRole.SEPARATOR,
5977 
5978     /**
5979      * An object that presents a series of panels (or page tabs), one at a
5980      * time, through some mechanism provided by the object.  The most common
5981      * mechanism is a list of tabs at the top of the panel.  The children of
5982      * a page tab list are all page tabs.
5983      * @see #PAGE_TAB
5984      */
5985     AccessibleRole.PAGE_TAB_LIST,
5986 
5987     /**
5988      * An object that is a child of a page tab list.  Its sole child is
5989      * the panel that is to be presented to the user when the user
5990      * selects the page tab from the list of tabs in the page tab list.
5991      * @see #PAGE_TAB_LIST
5992      */
5993     AccessibleRole.PAGE_TAB,
5994 
5995     /**
5996      * A generic container that is often used to group objects.
5997      */
5998     AccessibleRole.PANEL,
5999 
6000     /**
6001      * An object used to indicate how much of a task has been completed.
6002      */
6003     AccessibleRole.PROGRESS_BAR,
6004 
6005     /**
6006      * A text object used for passwords, or other places where the
6007      * text contents is not shown visibly to the user
6008      */
6009     AccessibleRole.PASSWORD_TEXT,
6010 
6011     /**
6012      * An object the user can manipulate to tell the application to do
6013      * something.
6014      * @see #CHECK_BOX
6015      * @see #TOGGLE_BUTTON
6016      * @see #RADIO_BUTTON
6017      */
6018     AccessibleRole.PUSH_BUTTON,
6019 
6020     /**
6021      * A specialized push button that can be checked or unchecked, but
6022      * does not provide a separate indicator for the current state.
6023      * @see #PUSH_BUTTON
6024      * @see #CHECK_BOX
6025      * @see #RADIO_BUTTON
6026      */
6027     AccessibleRole.TOGGLE_BUTTON,
6028 
6029     /**
6030      * A choice that can be checked or unchecked and provides a
6031      * separate indicator for the current state.
6032      * @see #PUSH_BUTTON
6033      * @see #TOGGLE_BUTTON
6034      * @see #RADIO_BUTTON
6035      */
6036     AccessibleRole.CHECK_BOX,
6037 
6038     /**
6039      * A specialized check box that will cause other radio buttons in the
6040      * same group to become unchecked when this one is checked.
6041      * @see #PUSH_BUTTON
6042      * @see #TOGGLE_BUTTON
6043      * @see #CHECK_BOX
6044      */
6045     AccessibleRole.RADIO_BUTTON,
6046 
6047     /**
6048      * The header for a row of data.
6049      */
6050     AccessibleRole.ROW_HEADER,
6051 
6052     /**
6053      * An object that allows a user to incrementally view a large amount
6054      * of information.  Its children can include scroll bars and a viewport.
6055      * @see #SCROLL_BAR
6056      * @see #VIEWPORT
6057      */
6058     AccessibleRole.SCROLL_PANE,
6059 
6060     /**
6061      * An object usually used to allow a user to incrementally view a
6062      * large amount of data.  Usually used only by a scroll pane.
6063      * @see #SCROLL_PANE
6064      */
6065     AccessibleRole.SCROLL_BAR,
6066 
6067     /**
6068      * An object usually used in a scroll pane.  It represents the portion
6069      * of the entire data that the user can see.  As the user manipulates
6070      * the scroll bars, the contents of the viewport can change.
6071      * @see #SCROLL_PANE
6072      */
6073     AccessibleRole.VIEWPORT,
6074 
6075     /**
6076      * An object that allows the user to select from a bounded range.  For
6077      * example, a slider might be used to select a number between 0 and 100.
6078      */
6079     AccessibleRole.SLIDER,
6080 
6081     /**
6082      * A specialized panel that presents two other panels at the same time.
6083      * Between the two panels is a divider the user can manipulate to make
6084      * one panel larger and the other panel smaller.
6085      */
6086     AccessibleRole.SPLIT_PANE,
6087 
6088     /**
6089      * An object used to present information in terms of rows and columns.
6090      * An example might include a spreadsheet application.
6091      */
6092     AccessibleRole.TABLE,
6093 
6094     /**
6095      * An object that presents text to the user.  The text is usually
6096      * editable by the user as opposed to a label.
6097      * @see #LABEL
6098      */
6099     AccessibleRole.TEXT,
6100 
6101     /**
6102      * An object used to present hierarchical information to the user.
6103      * The individual nodes in the tree can be collapsed and expanded
6104      * to provide selective disclosure of the tree's contents.
6105      */
6106     AccessibleRole.TREE,
6107 
6108     /**
6109      * A bar or palette usually composed of push buttons or toggle buttons.
6110      * It is often used to provide the most frequently used functions for an
6111      * application.
6112      */
6113     AccessibleRole.TOOL_BAR,
6114 
6115     /**
6116      * An object that provides information about another object.  The
6117      * accessibleDescription property of the tool tip is often displayed
6118      * to the user in a small "help bubble" when the user causes the
6119      * mouse to hover over the object associated with the tool tip.
6120      */
6121     AccessibleRole.TOOL_TIP,
6122 
6123     /**
6124      * An AWT component, but nothing else is known about it.
6125      * @see #SWING_COMPONENT
6126      * @see #UNKNOWN
6127      */
6128     AccessibleRole.AWT_COMPONENT,
6129 
6130     /**
6131      * A Swing component, but nothing else is known about it.
6132      * @see #AWT_COMPONENT
6133      * @see #UNKNOWN
6134      */
6135     AccessibleRole.SWING_COMPONENT,
6136 
6137     /**
6138      * The object contains some Accessible information, but its role is
6139      * not known.
6140      * @see #AWT_COMPONENT
6141      * @see #SWING_COMPONENT
6142      */
6143     AccessibleRole.UNKNOWN,
6144 
6145     // These roles are available since JDK 1.4
6146 
6147     /**
6148      * A STATUS_BAR is an simple component that can contain
6149      * multiple labels of status information to the user.
6150      AccessibleRole.STATUS_BAR,
6151 
6152      /**
6153      * A DATE_EDITOR is a component that allows users to edit
6154      * java.util.Date and java.util.Time objects
6155      AccessibleRole.DATE_EDITOR,
6156 
6157      /**
6158      * A SPIN_BOX is a simple spinner component and its main use
6159      * is for simple numbers.
6160      AccessibleRole.SPIN_BOX,
6161 
6162      /**
6163      * A FONT_CHOOSER is a component that lets the user pick various
6164      * attributes for fonts.
6165      AccessibleRole.FONT_CHOOSER,
6166 
6167      /**
6168      * A GROUP_BOX is a simple container that contains a border
6169      * around it and contains components inside it.
6170      AccessibleRole.GROUP_BOX
6171 
6172      /**
6173      * Since JDK 1.5
6174      *
6175      * A text header
6176 
6177      AccessibleRole.HEADER,
6178 
6179      /**
6180      * A text footer
6181 
6182      AccessibleRole.FOOTER,
6183 
6184      /**
6185      * A text paragraph
6186 
6187      AccessibleRole.PARAGRAPH,
6188 
6189      /**
6190      * A ruler is an object used to measure distance
6191 
6192      AccessibleRole.RULER,
6193 
6194      /**
6195      * A role indicating the object acts as a formula for
6196      * calculating a value.  An example is a formula in
6197      * a spreadsheet cell.
6198      AccessibleRole.EDITBAR
6199     */
6200     };
6201 
6202     /**
6203      * This class implements accessibility support for the
6204      * <code>JTree</code> child.  It provides an implementation of the
6205      * Java Accessibility API appropriate to tree nodes.
6206      *
6207      * Copied from JTree.java to work around a JTree bug where
6208      * ActiveDescendent PropertyChangeEvents contain the wrong
6209      * parent.
6210      */
6211     /**
6212      * This class in invoked on the EDT as its part of ActiveDescendant,
6213      * hence the calls do not need to be specifically made on the EDT
6214      */
6215     private class AccessibleJTreeNode extends AccessibleContext
6216         implements Accessible, AccessibleComponent, AccessibleSelection,
6217                    AccessibleAction {
6218 
6219         private JTree tree = null;
6220         private TreeModel treeModel = null;
6221         private Object obj = null;
6222         private TreePath path = null;
6223         private Accessible accessibleParent = null;
6224         private int index = 0;
6225         private boolean isLeaf = false;
6226 
6227         /**
6228          *  Constructs an AccessibleJTreeNode
6229          */
6230         AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) {
6231             tree = t;
6232             path = p;
6233             accessibleParent = ap;
6234             if (t != null)
6235                 treeModel = t.getModel();
6236             if (p != null) {
6237                 obj = p.getLastPathComponent();
6238                 if (treeModel != null && obj != null) {
6239                     isLeaf = treeModel.isLeaf(obj);
6240                 }
6241             }
6242             debugString("[INFO]: AccessibleJTreeNode: name = "+getAccessibleName()+"; TreePath = "+p+"; parent = "+ap);
6243         }
6244 
6245         private TreePath getChildTreePath(int i) {
6246             // Tree nodes can't be so complex that they have
6247             // two sets of children -> we're ignoring that case
6248             if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) {
6249                 return null;
6250             } else {
6251                 Object childObj = treeModel.getChild(obj, i);
6252                 Object[] objPath = path.getPath();
6253                 Object[] objChildPath = new Object[objPath.length+1];
6254                 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
6255                 objChildPath[objChildPath.length-1] = childObj;
6256                 return new TreePath(objChildPath);
6257             }
6258         }
6259 
6260         /**
6261          * Get the AccessibleContext associated with this tree node.
6262          * In the implementation of the Java Accessibility API for
6263          * this class, return this object, which is its own
6264          * AccessibleContext.
6265          *
6266          * @return this object
6267         */
6268         public AccessibleContext getAccessibleContext() {
6269             return this;
6270         }
6271 
6272         private AccessibleContext getCurrentAccessibleContext() {
6273             Component c = getCurrentComponent();
6274             if (c instanceof Accessible) {
6275                return (c.getAccessibleContext());
6276             } else {
6277                 return null;
6278             }
6279         }
6280 
6281         private Component getCurrentComponent() {
6282             debugString("[INFO]: AccessibleJTreeNode: getCurrentComponent");
6283             // is the object visible?
6284             // if so, get row, selected, focus & leaf state,
6285             // and then get the renderer component and return it
6286             if (tree != null && tree.isVisible(path)) {
6287                 TreeCellRenderer r = tree.getCellRenderer();
6288                 if (r == null) {
6289                     debugString("[WARN]:  returning null 1");
6290                     return null;
6291                 }
6292                 TreeUI ui = tree.getUI();
6293                 if (ui != null) {
6294                     int row = ui.getRowForPath(tree, path);
6295                     boolean selected = tree.isPathSelected(path);
6296                     boolean expanded = tree.isExpanded(path);
6297                     boolean hasFocus = false; // how to tell?? -PK
6298                     Component retval = r.getTreeCellRendererComponent(tree, obj,
6299                                                                       selected, expanded,
6300                                                                       isLeaf, row, hasFocus);
6301                     debugString("[INFO]:   returning = "+retval.getClass());
6302                     return retval;
6303                 }
6304             }
6305             debugString("[WARN]:  returning null 2");
6306             return null;
6307         }
6308 
6309         // AccessibleContext methods
6310 
6311         /**
6312          * Get the accessible name of this object.
6313          *
6314          * @return the localized name of the object; null if this
6315          * object does not have a name
6316          */
6317         public String getAccessibleName() {
6318             debugString("[INFO]: AccessibleJTreeNode: getAccessibleName");
6319             AccessibleContext ac = getCurrentAccessibleContext();
6320             if (ac != null) {
6321                 String name = ac.getAccessibleName();
6322                 if ((name != null) && (!name.isEmpty())) {
6323                     String retval = ac.getAccessibleName();
6324                     debugString("[INFO]:     returning "+retval);
6325                     return retval;
6326                 } else {
6327                     return null;
6328                 }
6329             }
6330             if ((accessibleName != null) && (accessibleName.isEmpty())) {
6331                 return accessibleName;
6332             } else {
6333                 return null;
6334             }
6335         }
6336 
6337         /**
6338          * Set the localized accessible name of this object.
6339          *
6340          * @param s the new localized name of the object.
6341          */
6342         public void setAccessibleName(String s) {
6343             AccessibleContext ac = getCurrentAccessibleContext();
6344             if (ac != null) {
6345                 ac.setAccessibleName(s);
6346             } else {
6347                 super.setAccessibleName(s);
6348             }
6349         }
6350 
6351         //
6352         // *** should check tooltip text for desc. (needs MouseEvent)
6353         //
6354         /**
6355          * Get the accessible description of this object.
6356          *
6357          * @return the localized description of the object; null if
6358          * this object does not have a description
6359          */
6360         public String getAccessibleDescription() {
6361             AccessibleContext ac = getCurrentAccessibleContext();
6362             if (ac != null) {
6363                 return ac.getAccessibleDescription();
6364             } else {
6365                 return super.getAccessibleDescription();
6366             }
6367         }
6368 
6369         /**
6370          * Set the accessible description of this object.
6371          *
6372          * @param s the new localized description of the object
6373          */
6374         public void setAccessibleDescription(String s) {
6375             AccessibleContext ac = getCurrentAccessibleContext();
6376             if (ac != null) {
6377                 ac.setAccessibleDescription(s);
6378             } else {
6379                 super.setAccessibleDescription(s);
6380             }
6381         }
6382 
6383         /**
6384          * Get the role of this object.
6385          *
6386          * @return an instance of AccessibleRole describing the role of the object
6387          * @see AccessibleRole
6388          */
6389         public AccessibleRole getAccessibleRole() {
6390             AccessibleContext ac = getCurrentAccessibleContext();
6391             if (ac != null) {
6392                 return ac.getAccessibleRole();
6393             } else {
6394                 return AccessibleRole.UNKNOWN;
6395             }
6396         }
6397 
6398         /**
6399          * Get the state set of this object.
6400          *
6401          * @return an instance of AccessibleStateSet containing the
6402          * current state set of the object
6403          * @see AccessibleState
6404          */
6405         public AccessibleStateSet getAccessibleStateSet() {
6406             if (tree == null)
6407                 return null;
6408             AccessibleContext ac = getCurrentAccessibleContext();
6409             AccessibleStateSet states;
6410             int row = tree.getUI().getRowForPath(tree,path);
6411             int lsr = tree.getLeadSelectionRow();
6412             if (ac != null) {
6413                 states = ac.getAccessibleStateSet();
6414             } else {
6415                 states = new AccessibleStateSet();
6416             }
6417             // need to test here, 'cause the underlying component
6418             // is a cellRenderer, which is never showing...
6419             if (isShowing()) {
6420                 states.add(AccessibleState.SHOWING);
6421             } else if (states.contains(AccessibleState.SHOWING)) {
6422                 states.remove(AccessibleState.SHOWING);
6423             }
6424             if (isVisible()) {
6425                 states.add(AccessibleState.VISIBLE);
6426             } else if (states.contains(AccessibleState.VISIBLE)) {
6427                 states.remove(AccessibleState.VISIBLE);
6428             }
6429             if (tree.isPathSelected(path)){
6430                 states.add(AccessibleState.SELECTED);
6431             }
6432             if (lsr == row) {
6433                 states.add(AccessibleState.ACTIVE);
6434             }
6435             if (!isLeaf) {
6436                 states.add(AccessibleState.EXPANDABLE);
6437             }
6438             if (tree.isExpanded(path)) {
6439                 states.add(AccessibleState.EXPANDED);
6440             } else {
6441                 states.add(AccessibleState.COLLAPSED);
6442             }
6443             if (tree.isEditable()) {
6444                 states.add(AccessibleState.EDITABLE);
6445             }
6446             return states;
6447         }
6448 
6449         /**
6450          * Get the Accessible parent of this object.
6451          *
6452          * @return the Accessible parent of this object; null if this
6453          * object does not have an Accessible parent
6454          */
6455         public Accessible getAccessibleParent() {
6456             // someone wants to know, so we need to create our parent
6457             // if we don't have one (hey, we're a talented kid!)
6458             if (accessibleParent == null && path != null) {
6459                 Object[] objPath = path.getPath();
6460                 if (objPath.length > 1) {
6461                     Object objParent = objPath[objPath.length-2];
6462                     if (treeModel != null) {
6463                         index = treeModel.getIndexOfChild(objParent, obj);
6464                     }
6465                     Object[] objParentPath = new Object[objPath.length-1];
6466                     java.lang.System.arraycopy(objPath, 0, objParentPath,
6467                                                0, objPath.length-1);
6468                     TreePath parentPath = new TreePath(objParentPath);
6469                     accessibleParent = new AccessibleJTreeNode(tree,
6470                                                                parentPath,
6471                                                                null);
6472                     this.setAccessibleParent(accessibleParent);
6473                 } else if (treeModel != null) {
6474                     accessibleParent = tree; // we're the top!
6475                     index = 0; // we're an only child!
6476                     this.setAccessibleParent(accessibleParent);
6477                 }
6478             }
6479             return accessibleParent;
6480         }
6481 
6482         /**
6483          * Get the index of this object in its accessible parent.
6484          *
6485          * @return the index of this object in its parent; -1 if this
6486          * object does not have an accessible parent.
6487          * @see #getAccessibleParent
6488          */
6489         public int getAccessibleIndexInParent() {
6490             // index is invalid 'till we have an accessibleParent...
6491             if (accessibleParent == null) {
6492                 getAccessibleParent();
6493             }
6494             if (path != null) {
6495                 Object[] objPath = path.getPath();
6496                 if (objPath.length > 1) {
6497                     Object objParent = objPath[objPath.length-2];
6498                     if (treeModel != null) {
6499                         index = treeModel.getIndexOfChild(objParent, obj);
6500                     }
6501                 }
6502             }
6503             return index;
6504         }
6505 
6506         /**
6507          * Returns the number of accessible children in the object.
6508          *
6509          * @return the number of accessible children in the object.
6510          */
6511         public int getAccessibleChildrenCount() {
6512             // Tree nodes can't be so complex that they have
6513             // two sets of children -> we're ignoring that case
6514             if (obj != null && treeModel != null) {
6515                 return treeModel.getChildCount(obj);
6516             }
6517             return 0;
6518         }
6519 
6520         /**
6521          * Return the specified Accessible child of the object.
6522          *
6523          * @param i zero-based index of child
6524          * @return the Accessible child of the object
6525          */
6526         public Accessible getAccessibleChild(int i) {
6527             // Tree nodes can't be so complex that they have
6528             // two sets of children -> we're ignoring that case
6529             if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) {
6530                 return null;
6531             } else {
6532                 Object childObj = treeModel.getChild(obj, i);
6533                 Object[] objPath = path.getPath();
6534                 Object[] objChildPath = new Object[objPath.length+1];
6535                 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
6536                 objChildPath[objChildPath.length-1] = childObj;
6537                 TreePath childPath = new TreePath(objChildPath);
6538                 return new AccessibleJTreeNode(tree, childPath, this);
6539             }
6540         }
6541 
6542         /**
6543          * Gets the locale of the component. If the component does not have
6544          * a locale, then the locale of its parent is returned.
6545          *
6546          * @return This component's locale. If this component does not have
6547          * a locale, the locale of its parent is returned.
6548          * @exception IllegalComponentStateException
6549          * If the Component does not have its own locale and has not yet
6550          * been added to a containment hierarchy such that the locale can be
6551          * determined from the containing parent.
6552          * @see #setLocale
6553          */
6554         public Locale getLocale() {
6555             if (tree == null)
6556                 return null;
6557             AccessibleContext ac = getCurrentAccessibleContext();
6558             if (ac != null) {
6559                 return ac.getLocale();
6560             } else {
6561                 return tree.getLocale();
6562             }
6563         }
6564 
6565         /**
6566          * Add a PropertyChangeListener to the listener list.
6567          * The listener is registered for all properties.
6568          *
6569          * @param l  The PropertyChangeListener to be added
6570          */
6571         public void addPropertyChangeListener(PropertyChangeListener l) {
6572             AccessibleContext ac = getCurrentAccessibleContext();
6573             if (ac != null) {
6574                 ac.addPropertyChangeListener(l);
6575             } else {
6576                 super.addPropertyChangeListener(l);
6577             }
6578         }
6579 
6580         /**
6581          * Remove a PropertyChangeListener from the listener list.
6582          * This removes a PropertyChangeListener that was registered
6583          * for all properties.
6584          *
6585          * @param l  The PropertyChangeListener to be removed
6586          */
6587         public void removePropertyChangeListener(PropertyChangeListener l) {
6588             AccessibleContext ac = getCurrentAccessibleContext();
6589             if (ac != null) {
6590                 ac.removePropertyChangeListener(l);
6591             } else {
6592                 super.removePropertyChangeListener(l);
6593             }
6594         }
6595 
6596         /**
6597          * Get the AccessibleAction associated with this object.  In the
6598          * implementation of the Java Accessibility API for this class,
6599          * return this object, which is responsible for implementing the
6600          * AccessibleAction interface on behalf of itself.
6601          *
6602          * @return this object
6603          */
6604         public AccessibleAction getAccessibleAction() {
6605             return this;
6606         }
6607 
6608         /**
6609          * Get the AccessibleComponent associated with this object.  In the
6610          * implementation of the Java Accessibility API for this class,
6611          * return this object, which is responsible for implementing the
6612          * AccessibleComponent interface on behalf of itself.
6613          *
6614          * @return this object
6615          */
6616         public AccessibleComponent getAccessibleComponent() {
6617             return this; // to override getBounds()
6618         }
6619 
6620         /**
6621          * Get the AccessibleSelection associated with this object if one
6622          * exists.  Otherwise return null.
6623          *
6624          * @return the AccessibleSelection, or null
6625          */
6626         public AccessibleSelection getAccessibleSelection() {
6627             AccessibleContext ac = getCurrentAccessibleContext();
6628             if (ac != null && isLeaf) {
6629                 return getCurrentAccessibleContext().getAccessibleSelection();
6630             } else {
6631                 return this;
6632             }
6633         }
6634 
6635         /**
6636          * Get the AccessibleText associated with this object if one
6637          * exists.  Otherwise return null.
6638          *
6639          * @return the AccessibleText, or null
6640          */
6641         public AccessibleText getAccessibleText() {
6642             AccessibleContext ac = getCurrentAccessibleContext();
6643             if (ac != null) {
6644                 return getCurrentAccessibleContext().getAccessibleText();
6645             } else {
6646                 return null;
6647             }
6648         }
6649 
6650         /**
6651          * Get the AccessibleValue associated with this object if one
6652          * exists.  Otherwise return null.
6653          *
6654          * @return the AccessibleValue, or null
6655          */
6656         public AccessibleValue getAccessibleValue() {
6657             AccessibleContext ac = getCurrentAccessibleContext();
6658             if (ac != null) {
6659                 return getCurrentAccessibleContext().getAccessibleValue();
6660             } else {
6661                 return null;
6662             }
6663         }
6664 
6665 
6666             // AccessibleComponent methods
6667 
6668         /**
6669          * Get the background color of this object.
6670          *
6671          * @return the background color, if supported, of the object;
6672          * otherwise, null
6673          */
6674         public Color getBackground() {
6675             AccessibleContext ac = getCurrentAccessibleContext();
6676             if (ac instanceof AccessibleComponent) {
6677                 return ((AccessibleComponent) ac).getBackground();
6678             } else {
6679                 Component c = getCurrentComponent();
6680                 if (c != null) {
6681                     return c.getBackground();
6682                 } else {
6683                     return null;
6684                 }
6685             }
6686         }
6687 
6688         /**
6689          * Set the background color of this object.
6690          *
6691          * @param c the new Color for the background
6692          */
6693         public void setBackground(Color c) {
6694             AccessibleContext ac = getCurrentAccessibleContext();
6695             if (ac instanceof AccessibleComponent) {
6696                 ((AccessibleComponent) ac).setBackground(c);
6697             } else {
6698                 Component cp = getCurrentComponent();
6699                 if (    cp != null) {
6700                     cp.setBackground(c);
6701                 }
6702             }
6703         }
6704 
6705 
6706         /**
6707          * Get the foreground color of this object.
6708          *
6709          * @return the foreground color, if supported, of the object;
6710          * otherwise, null
6711          */
6712         public Color getForeground() {
6713             AccessibleContext ac = getCurrentAccessibleContext();
6714             if (ac instanceof AccessibleComponent) {
6715                 return ((AccessibleComponent) ac).getForeground();
6716             } else {
6717                 Component c = getCurrentComponent();
6718                 if (c != null) {
6719                     return c.getForeground();
6720                 } else {
6721                     return null;
6722                 }
6723             }
6724         }
6725 
6726         public void setForeground(Color c) {
6727             AccessibleContext ac = getCurrentAccessibleContext();
6728             if (ac instanceof AccessibleComponent) {
6729                 ((AccessibleComponent) ac).setForeground(c);
6730             } else {
6731                 Component cp = getCurrentComponent();
6732                 if (cp != null) {
6733                     cp.setForeground(c);
6734                 }
6735             }
6736         }
6737 
6738         public Cursor getCursor() {
6739             AccessibleContext ac = getCurrentAccessibleContext();
6740             if (ac instanceof AccessibleComponent) {
6741                 return ((AccessibleComponent) ac).getCursor();
6742             } else {
6743                 Component c = getCurrentComponent();
6744                 if (c != null) {
6745                     return c.getCursor();
6746                 } else {
6747                     Accessible ap = getAccessibleParent();
6748                     if (ap instanceof AccessibleComponent) {
6749                         return ((AccessibleComponent) ap).getCursor();
6750                     } else {
6751                         return null;
6752                     }
6753                 }
6754             }
6755         }
6756 
6757         public void setCursor(Cursor c) {
6758             AccessibleContext ac = getCurrentAccessibleContext();
6759             if (ac instanceof AccessibleComponent) {
6760                 ((AccessibleComponent) ac).setCursor(c);
6761             } else {
6762                 Component cp = getCurrentComponent();
6763                 if (cp != null) {
6764                     cp.setCursor(c);
6765                 }
6766             }
6767         }
6768 
6769         public Font getFont() {
6770             AccessibleContext ac = getCurrentAccessibleContext();
6771             if (ac instanceof AccessibleComponent) {
6772                 return ((AccessibleComponent) ac).getFont();
6773             } else {
6774                 Component c = getCurrentComponent();
6775                 if (c != null) {
6776                     return c.getFont();
6777                 } else {
6778                     return null;
6779                 }
6780             }
6781         }
6782 
6783         public void setFont(Font f) {
6784             AccessibleContext ac = getCurrentAccessibleContext();
6785             if (ac instanceof AccessibleComponent) {
6786                 ((AccessibleComponent) ac).setFont(f);
6787             } else {
6788                 Component c = getCurrentComponent();
6789                 if (c != null) {
6790                     c.setFont(f);
6791                 }
6792             }
6793         }
6794 
6795         public FontMetrics getFontMetrics(Font f) {
6796             AccessibleContext ac = getCurrentAccessibleContext();
6797             if (ac instanceof AccessibleComponent) {
6798                 return ((AccessibleComponent) ac).getFontMetrics(f);
6799             } else {
6800                 Component c = getCurrentComponent();
6801                 if (c != null) {
6802                     return c.getFontMetrics(f);
6803                 } else {
6804                     return null;
6805                 }
6806             }
6807         }
6808 
6809         public boolean isEnabled() {
6810             AccessibleContext ac = getCurrentAccessibleContext();
6811             if (ac instanceof AccessibleComponent) {
6812                 return ((AccessibleComponent) ac).isEnabled();
6813             } else {
6814                 Component c = getCurrentComponent();
6815                 if (c != null) {
6816                     return c.isEnabled();
6817                 } else {
6818                     return false;
6819                 }
6820             }
6821         }
6822 
6823         public void setEnabled(boolean b) {
6824             AccessibleContext ac = getCurrentAccessibleContext();
6825             if (ac instanceof AccessibleComponent) {
6826                 ((AccessibleComponent) ac).setEnabled(b);
6827             } else {
6828                 Component c = getCurrentComponent();
6829                 if (c != null) {
6830                     c.setEnabled(b);
6831                 }
6832             }
6833         }
6834 
6835         public boolean isVisible() {
6836             if (tree == null)
6837                 return false;
6838             Rectangle pathBounds = tree.getPathBounds(path);
6839             Rectangle parentBounds = tree.getVisibleRect();
6840             if ( pathBounds != null && parentBounds != null &&
6841                  parentBounds.intersects(pathBounds) ) {
6842                 return true;
6843             } else {
6844                 return false;
6845             }
6846         }
6847 
6848         public void setVisible(boolean b) {
6849         }
6850 
6851         public boolean isShowing() {
6852             return (tree.isShowing() && isVisible());
6853         }
6854 
6855         public boolean contains(Point p) {
6856             AccessibleContext ac = getCurrentAccessibleContext();
6857             if (ac instanceof AccessibleComponent) {
6858                 Rectangle r = ((AccessibleComponent) ac).getBounds();
6859                 return r.contains(p);
6860             } else {
6861                 Component c = getCurrentComponent();
6862                 if (c != null) {
6863                     Rectangle r = c.getBounds();
6864                     return r.contains(p);
6865                 } else {
6866                     return getBounds().contains(p);
6867                 }
6868             }
6869         }
6870 
6871         public Point getLocationOnScreen() {
6872             if (tree != null) {
6873                 Point treeLocation = tree.getLocationOnScreen();
6874                 Rectangle pathBounds = tree.getPathBounds(path);
6875                 if (treeLocation != null && pathBounds != null) {
6876                     Point nodeLocation = new Point(pathBounds.x,
6877                                                    pathBounds.y);
6878                     nodeLocation.translate(treeLocation.x, treeLocation.y);
6879                     return nodeLocation;
6880                 } else {
6881                     return null;
6882                 }
6883             } else {
6884                 return null;
6885             }
6886         }
6887 
6888         private Point getLocationInJTree() {
6889             Rectangle r = tree.getPathBounds(path);
6890             if (r != null) {
6891                 return r.getLocation();
6892             } else {
6893                 return null;
6894             }
6895         }
6896 
6897         public Point getLocation() {
6898             Rectangle r = getBounds();
6899             if (r != null) {
6900                 return r.getLocation();
6901             } else {
6902                 return null;
6903             }
6904         }
6905 
6906         public void setLocation(Point p) {
6907         }
6908 
6909         public Rectangle getBounds() {
6910             if (tree == null)
6911                 return null;
6912             Rectangle r = tree.getPathBounds(path);
6913             Accessible parent = getAccessibleParent();
6914             if (parent instanceof AccessibleJTreeNode) {
6915                 Point parentLoc = ((AccessibleJTreeNode) parent).getLocationInJTree();
6916                 if (parentLoc != null && r != null) {
6917                     r.translate(-parentLoc.x, -parentLoc.y);
6918                 } else {
6919                     return null;        // not visible!
6920                 }
6921             }
6922             return r;
6923         }
6924 
6925         public void setBounds(Rectangle r) {
6926             AccessibleContext ac = getCurrentAccessibleContext();
6927             if (ac instanceof AccessibleComponent) {
6928                 ((AccessibleComponent) ac).setBounds(r);
6929             } else {
6930                 Component c = getCurrentComponent();
6931                 if (c != null) {
6932                     c.setBounds(r);
6933                 }
6934             }
6935         }
6936 
6937         public Dimension getSize() {
6938             return getBounds().getSize();
6939         }
6940 
6941         public void setSize (Dimension d) {
6942             AccessibleContext ac = getCurrentAccessibleContext();
6943             if (ac instanceof AccessibleComponent) {
6944                 ((AccessibleComponent) ac).setSize(d);
6945             } else {
6946                 Component c = getCurrentComponent();
6947                 if (c != null) {
6948                     c.setSize(d);
6949                 }
6950             }
6951         }
6952 
6953         /**
6954         * Returns the <code>Accessible</code> child, if one exists,
6955         * contained at the local coordinate <code>Point</code>.
6956         * Otherwise returns <code>null</code>.
6957         *
6958         * @param p point in local coordinates of this
6959         *    <code>Accessible</code>
6960         * @return the <code>Accessible</code>, if it exists,
6961         *    at the specified location; else <code>null</code>
6962         */
6963         public Accessible getAccessibleAt(Point p) {
6964             AccessibleContext ac = getCurrentAccessibleContext();
6965             if (ac instanceof AccessibleComponent) {
6966                 return ((AccessibleComponent) ac).getAccessibleAt(p);
6967             } else {
6968                 return null;
6969             }
6970         }
6971 
6972         public boolean isFocusTraversable() {
6973             AccessibleContext ac = getCurrentAccessibleContext();
6974             if (ac instanceof AccessibleComponent) {
6975                 return ((AccessibleComponent) ac).isFocusTraversable();
6976             } else {
6977                 Component c = getCurrentComponent();
6978                 if (c != null) {
6979                     return c.isFocusable();
6980                 } else {
6981                     return false;
6982                 }
6983             }
6984         }
6985 
6986         public void requestFocus() {
6987             AccessibleContext ac = getCurrentAccessibleContext();
6988             if (ac instanceof AccessibleComponent) {
6989                 ((AccessibleComponent) ac).requestFocus();
6990             } else {
6991                 Component c = getCurrentComponent();
6992                 if (c != null) {
6993                     c.requestFocus();
6994                 }
6995             }
6996         }
6997 
6998         public void addFocusListener(FocusListener l) {
6999             AccessibleContext ac = getCurrentAccessibleContext();
7000             if (ac instanceof AccessibleComponent) {
7001                 ((AccessibleComponent) ac).addFocusListener(l);
7002             } else {
7003                 Component c = getCurrentComponent();
7004                 if (c != null) {
7005                     c.addFocusListener(l);
7006                 }
7007             }
7008         }
7009 
7010         public void removeFocusListener(FocusListener l) {
7011             AccessibleContext ac = getCurrentAccessibleContext();
7012             if (ac instanceof AccessibleComponent) {
7013                 ((AccessibleComponent) ac).removeFocusListener(l);
7014             } else {
7015                 Component c = getCurrentComponent();
7016                 if (c != null) {
7017                     c.removeFocusListener(l);
7018                 }
7019             }
7020         }
7021 
7022             // AccessibleSelection methods
7023 
7024         /**
7025          * Returns the number of items currently selected.
7026          * If no items are selected, the return value will be 0.
7027          *
7028          * @return the number of items currently selected.
7029          */
7030         public int getAccessibleSelectionCount() {
7031             int count = 0;
7032             int childCount = getAccessibleChildrenCount();
7033             for (int i = 0; i < childCount; i++) {
7034                 TreePath childPath = getChildTreePath(i);
7035                 if (tree.isPathSelected(childPath)) {
7036                     count++;
7037                 }
7038             }
7039             return count;
7040         }
7041 
7042         /**
7043          * Returns an Accessible representing the specified selected item
7044          * in the object.  If there isn't a selection, or there are
7045          * fewer items selected than the integer passed in, the return
7046          * value will be null.
7047          *
7048          * @param i the zero-based index of selected items
7049          * @return an Accessible containing the selected item
7050          */
7051         public Accessible getAccessibleSelection(int i) {
7052             int childCount = getAccessibleChildrenCount();
7053             if (i < 0 || i >= childCount) {
7054                 return null;        // out of range
7055             }
7056             int count = 0;
7057             for (int j = 0; j < childCount && i >= count; j++) {
7058                 TreePath childPath = getChildTreePath(j);
7059                 if (tree.isPathSelected(childPath)) {
7060                     if (count == i) {
7061                         return new AccessibleJTreeNode(tree, childPath, this);
7062                     } else {
7063                         count++;
7064                     }
7065                 }
7066             }
7067             return null;
7068         }
7069 
7070         /**
7071          * Returns true if the current child of this object is selected.
7072          *
7073          * @param i the zero-based index of the child in this Accessible
7074          * object.
7075          * @see AccessibleContext#getAccessibleChild
7076          */
7077         public boolean isAccessibleChildSelected(int i) {
7078             int childCount = getAccessibleChildrenCount();
7079             if (i < 0 || i >= childCount) {
7080                 return false;       // out of range
7081             } else {
7082                 TreePath childPath = getChildTreePath(i);
7083                 return tree.isPathSelected(childPath);
7084             }
7085         }
7086 
7087          /**
7088          * Adds the specified selected item in the object to the object's
7089          * selection.  If the object supports multiple selections,
7090          * the specified item is added to any existing selection, otherwise
7091          * it replaces any existing selection in the object.  If the
7092          * specified item is already selected, this method has no effect.
7093          *
7094          * @param i the zero-based index of selectable items
7095          */
7096         public void addAccessibleSelection(int i) {
7097             if (tree == null)
7098                 return;
7099             TreeModel model = tree.getModel();
7100             if (model != null) {
7101                 if (i >= 0 && i < getAccessibleChildrenCount()) {
7102                     TreePath path = getChildTreePath(i);
7103                     tree.addSelectionPath(path);
7104                 }
7105             }
7106         }
7107 
7108         /**
7109          * Removes the specified selected item in the object from the
7110          * object's
7111          * selection.  If the specified item isn't currently selected, this
7112          * method has no effect.
7113          *
7114          * @param i the zero-based index of selectable items
7115          */
7116         public void removeAccessibleSelection(int i) {
7117             if (tree == null)
7118                 return;
7119             TreeModel model = tree.getModel();
7120             if (model != null) {
7121                 if (i >= 0 && i < getAccessibleChildrenCount()) {
7122                     TreePath path = getChildTreePath(i);
7123                     tree.removeSelectionPath(path);
7124                 }
7125             }
7126         }
7127 
7128         /**
7129          * Clears the selection in the object, so that nothing in the
7130          * object is selected.
7131          */
7132         public void clearAccessibleSelection() {
7133             int childCount = getAccessibleChildrenCount();
7134             for (int i = 0; i < childCount; i++) {
7135                 removeAccessibleSelection(i);
7136             }
7137         }
7138 
7139         /**
7140          * Causes every selected item in the object to be selected
7141          * if the object supports multiple selections.
7142          */
7143         public void selectAllAccessibleSelection() {
7144             if (tree == null)
7145                 return;
7146             TreeModel model = tree.getModel();
7147             if (model != null) {
7148                 int childCount = getAccessibleChildrenCount();
7149                 TreePath path;
7150                 for (int i = 0; i < childCount; i++) {
7151                     path = getChildTreePath(i);
7152                     tree.addSelectionPath(path);
7153                 }
7154             }
7155         }
7156 
7157             // AccessibleAction methods
7158 
7159         /**
7160          * Returns the number of accessible actions available in this
7161          * tree node.  If this node is not a leaf, there is at least
7162          * one action (toggle expand), in addition to any available
7163          * on the object behind the TreeCellRenderer.
7164          *
7165          * @return the number of Actions in this object
7166          */
7167         public int getAccessibleActionCount() {
7168             AccessibleContext ac = getCurrentAccessibleContext();
7169             if (ac != null) {
7170                 AccessibleAction aa = ac.getAccessibleAction();
7171                 if (aa != null) {
7172                     return (aa.getAccessibleActionCount() + (isLeaf ? 0 : 1));
7173                 }
7174             }
7175             return isLeaf ? 0 : 1;
7176         }
7177 
7178         /**
7179          * Return a description of the specified action of the tree node.
7180          * If this node is not a leaf, there is at least one action
7181          * description (toggle expand), in addition to any available
7182          * on the object behind the TreeCellRenderer.
7183          *
7184          * @param i zero-based index of the actions
7185          * @return a description of the action
7186          */
7187         public String getAccessibleActionDescription(int i) {
7188             if (i < 0 || i >= getAccessibleActionCount()) {
7189                 return null;
7190             }
7191             AccessibleContext ac = getCurrentAccessibleContext();
7192             if (i == 0) {
7193                 // TIGER - 4766636
7194                 // return AccessibleAction.TOGGLE_EXPAND;
7195                 return "toggle expand";
7196             } else if (ac != null) {
7197                 AccessibleAction aa = ac.getAccessibleAction();
7198                 if (aa != null) {
7199                     return aa.getAccessibleActionDescription(i - 1);
7200                 }
7201             }
7202             return null;
7203         }
7204 
7205         /**
7206          * Perform the specified Action on the tree node.  If this node
7207          * is not a leaf, there is at least one action which can be
7208          * done (toggle expand), in addition to any available on the
7209          * object behind the TreeCellRenderer.
7210          *
7211          * @param i zero-based index of actions
7212          * @return true if the the action was performed; else false.
7213          */
7214         public boolean doAccessibleAction(int i) {
7215             if (i < 0 || i >= getAccessibleActionCount()) {
7216                 return false;
7217             }
7218             AccessibleContext ac = getCurrentAccessibleContext();
7219             if (i == 0) {
7220                 if (tree.isExpanded(path)) {
7221                     tree.collapsePath(path);
7222                 } else {
7223                     tree.expandPath(path);
7224                 }
7225                 return true;
7226             } else if (ac != null) {
7227                 AccessibleAction aa = ac.getAccessibleAction();
7228                 if (aa != null) {
7229                     return aa.doAccessibleAction(i - 1);
7230                 }
7231             }
7232             return false;
7233         }
7234 
7235     } // inner class AccessibleJTreeNode
7236 
7237     /**
7238      * A helper class to perform {@code Callable} objects on the event dispatch thread appropriate
7239      * for the provided {@code AccessibleContext}.
7240      */
7241     private static class InvocationUtils {
7242 
7243         /**
7244          * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible}
7245          * and waits for it to finish blocking the caller thread.
7246          *
7247          * @param callable   the {@code Callable} to invoke
7248          * @param accessibleTable the {@code AccessibleExtendedTable} which would be used to find the right context
7249          *                   for the task execution
7250          * @param <T> type parameter for the result value
7251          *
7252          * @return the result of the {@code Callable} execution
7253          */
7254         public static <T> T invokeAndWait(final Callable<T> callable,
7255                                           final AccessibleExtendedTable accessibleTable) {
7256             if (accessibleTable instanceof AccessibleContext) {
7257                 return invokeAndWait(callable, (AccessibleContext)accessibleTable);
7258             }
7259             throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleTable);
7260         }
7261 
7262         /**
7263          * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible}
7264          * and waits for it to finish blocking the caller thread.
7265          *
7266          * @param callable   the {@code Callable} to invoke
7267          * @param accessible the {@code Accessible} which would be used to find the right context
7268          *                   for the task execution
7269          * @param <T> type parameter for the result value
7270          *
7271          * @return the result of the {@code Callable} execution
7272          */
7273         public static <T> T invokeAndWait(final Callable<T> callable,
7274                                           final Accessible accessible) {
7275             if (accessible instanceof Component) {
7276                 return invokeAndWait(callable, (Component)accessible);
7277             }
7278             if (accessible instanceof AccessibleContext) {
7279                 // This case also covers the Translator
7280                 return invokeAndWait(callable, (AccessibleContext)accessible);
7281             }
7282             throw new RuntimeException("Unmapped Accessible used to dispatch event: " + accessible);
7283         }
7284 
7285         /**
7286          * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Component}
7287          * and waits for it to finish blocking the caller thread.
7288          *
7289          * @param callable  the {@code Callable} to invoke
7290          * @param component the {@code Component} which would be used to find the right context
7291          *                  for the task execution
7292          * @param <T> type parameter for the result value
7293          *
7294          * @return the result of the {@code Callable} execution
7295          */
7296         public static <T> T invokeAndWait(final Callable<T> callable,
7297                                           final Component component) {
7298             return invokeAndWait(callable, SunToolkit.targetToAppContext(component));
7299         }
7300 
7301         /**
7302          * Invokes a {@code Callable} in the {@code AppContext} mapped to the given {@code AccessibleContext}
7303          * and waits for it to finish blocking the caller thread.
7304          *
7305          * @param callable the {@code Callable} to invoke
7306          * @param accessibleContext the {@code AccessibleContext} which would be used to determine the right
7307          *                          context for the task execution.
7308          * @param <T> type parameter for the result value
7309          *
7310          * @return the result of the {@code Callable} execution
7311          */
7312         public static <T> T invokeAndWait(final Callable<T> callable,
7313                                           final AccessibleContext accessibleContext) {
7314             AppContext targetContext = AWTAccessor.getAccessibleContextAccessor()
7315                     .getAppContext(accessibleContext);
7316             if (targetContext != null) {
7317                 return invokeAndWait(callable, targetContext);
7318             } else {
7319                 // Normally this should not happen, unmapped context provided and
7320                 // the target AppContext is unknown.
7321 
7322                 // Try to recover in case the context is a translator.
7323                 if (accessibleContext instanceof Translator) {
7324                     Object source = ((Translator)accessibleContext).getSource();
7325                     if (source instanceof Component) {
7326                         return invokeAndWait(callable, (Component)source);
7327                     }
7328                 }
7329             }
7330             throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleContext);
7331         }
7332 
7333         private static <T> T invokeAndWait(final Callable<T> callable,
7334                                            final AppContext targetAppContext) {
7335             final CallableWrapper<T> wrapper = new CallableWrapper<T>(callable);
7336             try {
7337                 invokeAndWait(wrapper, targetAppContext);
7338                 T result = wrapper.getResult();
7339                 updateAppContextMap(result, targetAppContext);
7340                 return result;
7341             } catch (final Exception e) {
7342                 throw new RuntimeException(e);
7343             }
7344         }
7345 
7346         private static void invokeAndWait(final Runnable runnable,
7347                                         final AppContext appContext)
7348                 throws InterruptedException, InvocationTargetException {
7349 
7350             EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext);
7351             Object lock = new Object();
7352             Toolkit source = Toolkit.getDefaultToolkit();
7353             InvocationEvent event =
7354                     new InvocationEvent(source, runnable, lock, true);
7355             synchronized (lock) {
7356                 eq.postEvent(event);
7357                 lock.wait();
7358             }
7359 
7360             Throwable eventThrowable = event.getThrowable();
7361             if (eventThrowable != null) {
7362                 throw new InvocationTargetException(eventThrowable);
7363             }
7364         }
7365 
7366         /**
7367          * Maps the {@code AccessibleContext} to the {@code AppContext} which should be used
7368          * to dispatch events related to the {@code AccessibleContext}
7369          * @param accessibleContext the {@code AccessibleContext} for the mapping
7370          * @param targetContext the {@code AppContext} for the mapping
7371          */
7372         public static void registerAccessibleContext(final AccessibleContext accessibleContext,
7373                                                      final AppContext targetContext) {
7374             if (accessibleContext != null) {
7375                 AWTAccessor.getAccessibleContextAccessor().setAppContext(accessibleContext, targetContext);
7376             }
7377         }
7378 
7379         private static <T> void updateAppContextMap(final T accessibleContext,
7380                                                     final AppContext targetContext) {
7381             if (accessibleContext instanceof AccessibleContext) {
7382                 registerAccessibleContext((AccessibleContext)accessibleContext, targetContext);
7383             }
7384         }
7385 
7386         private static class CallableWrapper<T> implements Runnable {
7387             private final Callable<T> callable;
7388             private volatile T object;
7389             private Exception e;
7390 
7391             CallableWrapper(final Callable<T> callable) {
7392                 this.callable = callable;
7393             }
7394 
7395             public void run() {
7396                 try {
7397                     if (callable != null) {
7398                         object = callable.call();
7399                     }
7400                 } catch (final Exception e) {
7401                     this.e = e;
7402                 }
7403             }
7404 
7405             T getResult() throws Exception {
7406                 if (e != null)
7407                     throw e;
7408                 return object;
7409             }
7410         }
7411     }
7412 }