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