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
3783      */
3784     private KeyStroke getAccelerator(final AccessibleContext ac) {
3785         // workaround for getAccessibleKeyBinding not returning the
3786         // JMenuItem accelerator
3787         if (ac == null)
3788             return null;
3789         return InvocationUtils.invokeAndWait(new Callable<KeyStroke>() {
3790             @Override
3791             public KeyStroke call() throws Exception {
3792                 Accessible parent = ac.getAccessibleParent();
3793                 if (parent instanceof Accessible) {
3794                     int indexInParent = ac.getAccessibleIndexInParent();
3795                     Accessible child =
3796                             parent.getAccessibleContext().getAccessibleChild(indexInParent);
3797                     if (child instanceof JMenuItem) {
3798                         JMenuItem menuItem = (JMenuItem) child;
3799                         if (menuItem == null)
3800                             return null;
3801                         KeyStroke keyStroke = menuItem.getAccelerator();
3802                         return keyStroke;
3803                     }
3804                 }
3805                 return null;
3806             }
3807         }, ac);
3808     }
3809 
3810     /*
3811      * returns 1-24 to indicate which F key is being used for a shortcut or 0 otherwise
3812      */
3813     private int fKeyNumber(KeyStroke keyStroke) {
3814         if (keyStroke == null)
3815             return 0;
3816         int fKey = 0;
3817         String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode());
3818         if (keyText != null && (keyText.length() == 2 || keyText.length() == 3)) {
3819             String prefix = keyText.substring(0, 1);
3820             if (prefix.equals("F")) {
3821                 try {
3822                     int suffix = Integer.parseInt(keyText.substring(1));
3823                     if (suffix >= 1 && suffix <= 24) {
3824                         fKey = suffix;
3825                     }
3826                 } catch (Exception e) { // ignore NumberFormatException
3827                 }
3828             }
3829         }
3830         return fKey;
3831     }
3832 
3833     /*
3834      * returns one of several important control characters or 0 otherwise
3835      */
3836     private int controlCode(KeyStroke keyStroke) {
3837         if (keyStroke == null)
3838             return 0;
3839         int code = keyStroke.getKeyCode();
3840         switch (code) {
3841             case KeyEvent.VK_BACK_SPACE:
3842             case KeyEvent.VK_DELETE:
3843             case KeyEvent.VK_DOWN:
3844             case KeyEvent.VK_END:
3845             case KeyEvent.VK_HOME:
3846             case KeyEvent.VK_INSERT:
3847             case KeyEvent.VK_KP_DOWN:
3848             case KeyEvent.VK_KP_LEFT:
3849             case KeyEvent.VK_KP_RIGHT:
3850             case KeyEvent.VK_KP_UP:
3851             case KeyEvent.VK_LEFT:
3852             case KeyEvent.VK_PAGE_DOWN:
3853             case KeyEvent.VK_PAGE_UP:
3854             case KeyEvent.VK_RIGHT:
3855             case KeyEvent.VK_UP:
3856                 break;
3857             default:
3858                 code = 0;
3859                 break;
3860         }
3861         return code;
3862     }
3863 
3864     /*
3865      * returns the KeyStoke character
3866      */
3867     private char getKeyChar(KeyStroke keyStroke) {
3868         // If the shortcut is an FKey return 1-24
3869         if (keyStroke == null)
3870             return 0;
3871         int fKey = fKeyNumber(keyStroke);
3872         if (fKey != 0) {
3873             // return 0x00000001 through 0x00000018
3874             debugString("[INFO]:   Shortcut is: F" + fKey);
3875             return (char)fKey;
3876         }
3877         // If the accelerator is a control character, return it
3878         int keyCode = controlCode(keyStroke);
3879         if (keyCode != 0) {
3880             debugString("[INFO]:   Shortcut is control character: " + Integer.toHexString(keyCode));
3881             return (char)keyCode;
3882         }
3883         String keyText = KeyEvent.getKeyText(keyStroke.getKeyCode());
3884         debugString("[INFO]:   Shortcut is: " + keyText);
3885         if (keyText != null || keyText.length() > 0) {
3886             CharSequence seq = keyText.subSequence(0, 1);
3887             if (seq != null || seq.length() > 0) {
3888                 return seq.charAt(0);
3889             }
3890         }
3891         return 0;
3892     }
3893 
3894     /*
3895      * returns the KeyStroke modifiers as an int
3896      */
3897     private int getModifiers(KeyStroke keyStroke) {
3898         if (keyStroke == null)
3899             return 0;
3900         debugString("[INFO]: In AccessBridge.getModifiers");
3901         // modifiers is a bit strip where bits 0-7 indicate a traditional modifier
3902         // such as Ctrl/Alt/Shift, bit 8 indicates an F key shortcut, and bit 9 indicates
3903         // a control code shortcut such as the delete key.
3904 
3905         int modifiers = 0;
3906         // Is the shortcut an FKey?
3907         if (fKeyNumber(keyStroke) != 0) {
3908             modifiers |= 1 << 8;
3909         }
3910         // Is the shortcut a control code?
3911         if (controlCode(keyStroke) != 0) {
3912             modifiers |= 1 << 9;
3913         }
3914         // The following is needed in order to handle translated modifiers.
3915         // getKeyModifiersText doesn't work because for example in German Strg is
3916         // returned for Ctrl.
3917 
3918         // There can be more than one modifier, e.g. if the modifier is ctrl + shift + B
3919         // the toString text is "shift ctrl pressed B". Need to parse through that.
3920         StringTokenizer st = new StringTokenizer(keyStroke.toString());
3921         while (st.hasMoreTokens()) {
3922             String text = st.nextToken();
3923             // Meta+Ctrl+Alt+Shift
3924             // 0-3 are shift, ctrl, meta, alt
3925             // 4-7 are for Solaris workstations (though not being used)
3926             if (text.startsWith("met")) {
3927                 debugString("[INFO]:   found meta");
3928                 modifiers |= ActionEvent.META_MASK;
3929             }
3930             if (text.startsWith("ctr")) {
3931                 debugString("[INFO]:   found ctrl");
3932                 modifiers |= ActionEvent.CTRL_MASK;
3933             }
3934             if (text.startsWith("alt")) {
3935                 debugString("[INFO]:   found alt");
3936                 modifiers |= ActionEvent.ALT_MASK;
3937             }
3938             if (text.startsWith("shi")) {
3939                 debugString("   found shift");
3940                 modifiers |= ActionEvent.SHIFT_MASK;
3941             }
3942         }
3943         debugString("[INFO]:   returning modifiers: 0x" + Integer.toHexString(modifiers));
3944         return modifiers;
3945     }
3946 
3947     /*
3948      * returns the number of key bindings associated with this context
3949      */
3950     private int getAccessibleKeyBindingsCount(AccessibleContext ac) {
3951         if (ac == null)
3952             return 0;
3953         int count = 0;
3954 
3955         if (getMnemonic(ac) != null) {
3956             count++;
3957         }
3958         if (getAccelerator(ac) != null) {
3959             count++;
3960         }
3961         return count;
3962     }
3963 
3964     /*
3965      * returns the key binding character at the specified index
3966      */
3967     private char getAccessibleKeyBindingChar(AccessibleContext ac, int index) {
3968         if (ac == null)
3969             return 0;
3970         if((index == 0) && getMnemonic(ac)==null) {// special case when there is no mnemonic
3971             KeyStroke keyStroke = getAccelerator(ac);
3972             if (keyStroke != null) {
3973                 return getKeyChar(keyStroke);
3974             }
3975         }
3976         if (index == 0) {   // mnemonic
3977             KeyStroke keyStroke = getMnemonic(ac);
3978             if (keyStroke != null) {
3979                 return getKeyChar(keyStroke);
3980             }
3981         } else if (index == 1) { // accelerator
3982             KeyStroke keyStroke = getAccelerator(ac);
3983             if (keyStroke != null) {
3984                 return getKeyChar(keyStroke);
3985             }
3986         }
3987         return 0;
3988     }
3989 
3990     /*
3991      * returns the key binding modifiers at the specified index
3992      */
3993     private int getAccessibleKeyBindingModifiers(AccessibleContext ac, int index) {
3994         if (ac == null)
3995             return 0;
3996         if((index == 0) && getMnemonic(ac)==null) {// special case when there is no mnemonic
3997             KeyStroke keyStroke = getAccelerator(ac);
3998             if (keyStroke != null) {
3999                 return getModifiers(keyStroke);
4000             }
4001         }
4002         if (index == 0) {   // mnemonic
4003             KeyStroke keyStroke = getMnemonic(ac);
4004             if (keyStroke != null) {
4005                 return getModifiers(keyStroke);
4006             }
4007         } else if (index == 1) { // accelerator
4008             KeyStroke keyStroke = getAccelerator(ac);
4009             if (keyStroke != null) {
4010                 return getModifiers(keyStroke);
4011             }
4012         }
4013         return 0;
4014     }
4015 
4016     // ========== AccessibleIcon ============
4017 
4018     /*
4019      * return the number of icons associated with this context
4020      */
4021     private int getAccessibleIconsCount(final AccessibleContext ac) {
4022         debugString("[INFO]: getAccessibleIconsCount");
4023         if (ac == null) {
4024             return 0;
4025         }
4026         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4027             @Override
4028             public Integer call() throws Exception {
4029                 AccessibleIcon[] ai = ac.getAccessibleIcon();
4030                 if (ai == null) {
4031                     return 0;
4032                 }
4033                 return ai.length;
4034             }
4035         }, ac);
4036     }
4037 
4038     /*
4039      * return icon description at the specified index
4040      */
4041     private String getAccessibleIconDescription(final AccessibleContext ac, final int index) {
4042         debugString("[INFO]: getAccessibleIconDescription: index = "+index);
4043         if (ac == null) {
4044             return null;
4045         }
4046         return InvocationUtils.invokeAndWait(new Callable<String>() {
4047             @Override
4048             public String call() throws Exception {
4049                 AccessibleIcon[] ai = ac.getAccessibleIcon();
4050                 if (ai == null || index < 0 || index >= ai.length) {
4051                     return null;
4052                 }
4053                 return ai[index].getAccessibleIconDescription();
4054             }
4055         }, ac);
4056     }
4057 
4058     /*
4059      * return icon height at the specified index
4060      */
4061     private int getAccessibleIconHeight(final AccessibleContext ac, final int index) {
4062         debugString("[INFO]: getAccessibleIconHeight: index = "+index);
4063         if (ac == null) {
4064             return 0;
4065         }
4066         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4067             @Override
4068             public Integer call() throws Exception {
4069                 AccessibleIcon[] ai = ac.getAccessibleIcon();
4070                 if (ai == null || index < 0 || index >= ai.length) {
4071                     return 0;
4072                 }
4073                 return ai[index].getAccessibleIconHeight();
4074             }
4075         }, ac);
4076     }
4077 
4078     /*
4079      * return icon width at the specified index
4080      */
4081     private int getAccessibleIconWidth(final AccessibleContext ac, final int index) {
4082         debugString("[INFO]: getAccessibleIconWidth: index = "+index);
4083         if (ac == null) {
4084             return 0;
4085         }
4086         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4087             @Override
4088             public Integer call() throws Exception {
4089                 AccessibleIcon[] ai = ac.getAccessibleIcon();
4090                 if (ai == null || index < 0 || index >= ai.length) {
4091                     return 0;
4092                 }
4093                 return ai[index].getAccessibleIconWidth();
4094             }
4095         }, ac);
4096     }
4097 
4098     // ========= AccessibleAction ===========
4099 
4100     /*
4101      * return the number of icons associated with this context
4102      */
4103     private int getAccessibleActionsCount(final AccessibleContext ac) {
4104         debugString("[INFO]: getAccessibleActionsCount");
4105         if (ac == null) {
4106             return 0;
4107         }
4108         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4109             @Override
4110             public Integer call() throws Exception {
4111                 AccessibleAction aa = ac.getAccessibleAction();
4112                 if (aa == null)
4113                     return 0;
4114                 return aa.getAccessibleActionCount();
4115             }
4116         }, ac);
4117     }
4118 
4119     /*
4120      * return icon description at the specified index
4121      */
4122     private String getAccessibleActionName(final AccessibleContext ac, final int index) {
4123         debugString("[INFO]: getAccessibleActionName: index = "+index);
4124         if (ac == null) {
4125             return null;
4126         }
4127         return InvocationUtils.invokeAndWait(new Callable<String>() {
4128             @Override
4129             public String call() throws Exception {
4130                 AccessibleAction aa = ac.getAccessibleAction();
4131                 if (aa == null) {
4132                     return null;
4133                 }
4134                 return aa.getAccessibleActionDescription(index);
4135             }
4136         }, ac);
4137     }
4138     /*
4139      * return icon description at the specified index
4140      */
4141     private boolean doAccessibleActions(final AccessibleContext ac, final String name) {
4142         debugString("[INFO]: doAccessibleActions: action name = "+name);
4143         if (ac == null || name == null) {
4144             return false;
4145         }
4146         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4147             @Override
4148             public Boolean call() throws Exception {
4149                 AccessibleAction aa = ac.getAccessibleAction();
4150                 if (aa == null) {
4151                     return false;
4152                 }
4153                 int index = -1;
4154                 int numActions = aa.getAccessibleActionCount();
4155                 for (int i = 0; i < numActions; i++) {
4156                     String actionName = aa.getAccessibleActionDescription(i);
4157                     if (name.equals(actionName)) {
4158                         index = i;
4159                         break;
4160                     }
4161                 }
4162                 if (index == -1) {
4163                     return false;
4164                 }
4165                 boolean retval = aa.doAccessibleAction(index);
4166                 return retval;
4167             }
4168         }, ac);
4169     }
4170 
4171     /* ===== AT utility methods ===== */
4172 
4173     /**
4174      * Sets the contents of an AccessibleContext that
4175      * implements AccessibleEditableText with the
4176      * specified text string.
4177      * Returns whether successful.
4178      */
4179     private boolean setTextContents(final AccessibleContext ac, final String text) {
4180         debugString("[INFO]: setTextContents: ac = "+ac+"; text = "+text);
4181 
4182         if (! (ac instanceof AccessibleEditableText)) {
4183             debugString("[WARN]:   ac not instanceof AccessibleEditableText: "+ac);
4184             return false;
4185         }
4186         if (text == null) {
4187             debugString("[WARN]:   text is null");
4188             return false;
4189         }
4190 
4191         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4192             @Override
4193             public Boolean call() throws Exception {
4194                 // check whether the text field is editable
4195                 AccessibleStateSet ass = ac.getAccessibleStateSet();
4196                 if (!ass.contains(AccessibleState.ENABLED)) {
4197                     return false;
4198                 }
4199                 ((AccessibleEditableText) ac).setTextContents(text);
4200                 return true;
4201             }
4202         }, ac);
4203     }
4204 
4205     /**
4206      * Returns the Accessible Context of an Internal Frame object that is
4207      * the ancestor of a given object.  If the object is an Internal Frame
4208      * object or an Internal Frame ancestor object was found, returns the
4209      * object's AccessibleContext.
4210      * If there is no ancestor object that has an Accessible Role of
4211      * Internal Frame, returns (AccessibleContext)0.
4212      */
4213     private AccessibleContext getInternalFrame (AccessibleContext ac) {
4214         return getParentWithRole(ac, AccessibleRole.INTERNAL_FRAME.toString());
4215     }
4216 
4217     /**
4218      * Returns the Accessible Context for the top level object in
4219      * a Java Window.  This is same Accessible Context that is obtained
4220      * from GetAccessibleContextFromHWND for that window.  Returns
4221      * (AccessibleContext)0 on error.
4222      */
4223     private AccessibleContext getTopLevelObject (final AccessibleContext ac) {
4224         debugString("[INFO]: getTopLevelObject; ac = "+ac);
4225         if (ac == null) {
4226             return null;
4227         }
4228         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4229             @Override
4230             public AccessibleContext call() throws Exception {
4231                 if (ac.getAccessibleRole() == AccessibleRole.DIALOG) {
4232                     // return the dialog, not the parent window
4233                     return ac;
4234                 }
4235 
4236                 Accessible parent = ac.getAccessibleParent();
4237                 if (parent == null) {
4238                     return ac;
4239                 }
4240                 Accessible tmp = parent;
4241                 while (tmp != null && tmp.getAccessibleContext() != null) {
4242                     AccessibleContext ac2 = tmp.getAccessibleContext();
4243                     if (ac2 != null && ac2.getAccessibleRole() == AccessibleRole.DIALOG) {
4244                         // return the dialog, not the parent window
4245                         return ac2;
4246                     }
4247                     parent = tmp;
4248                     tmp = parent.getAccessibleContext().getAccessibleParent();
4249                 }
4250                 return parent.getAccessibleContext();
4251             }
4252         }, ac);
4253     }
4254 
4255     /**
4256      * Returns the parent AccessibleContext that has the specified AccessibleRole.
4257      * Returns null on error or if the AccessibleContext does not exist.
4258      */
4259     private AccessibleContext getParentWithRole (final AccessibleContext ac,
4260                                                  final String roleName) {
4261         debugString("[INFO]: getParentWithRole; ac = "+ac + "\n role = "+roleName);
4262         if (ac == null || roleName == null) {
4263             return null;
4264         }
4265 
4266         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4267             @Override
4268             public AccessibleContext call() throws Exception {
4269                 AccessibleRole role = AccessBridge.this.accessibleRoleMap.get(roleName);
4270                 if (role == null) {
4271                     return ac;
4272                 }
4273 
4274                 Accessible parent = ac.getAccessibleParent();
4275                 if (parent == null && ac.getAccessibleRole() == role) {
4276                     return ac;
4277                 }
4278 
4279                 Accessible tmp = parent;
4280                 AccessibleContext tmp_ac = null;
4281 
4282                 while (tmp != null && (tmp_ac = tmp.getAccessibleContext()) != null) {
4283                     AccessibleRole ar = tmp_ac.getAccessibleRole();
4284                     if (ar == role) {
4285                         // found
4286                         return tmp_ac;
4287                     }
4288                     parent = tmp;
4289                     tmp = parent.getAccessibleContext().getAccessibleParent();
4290                 }
4291                 // not found
4292                 return null;
4293             }
4294         }, ac);
4295     }
4296 
4297     /**
4298      * Returns the parent AccessibleContext that has the specified AccessibleRole.
4299      * Otherwise, returns the top level object for the Java Window.
4300      * Returns (AccessibleContext)0 on error.
4301      */
4302     private AccessibleContext getParentWithRoleElseRoot (AccessibleContext ac,
4303                                                          String roleName) {
4304         AccessibleContext retval = getParentWithRole(ac, roleName);
4305         if (retval == null) {
4306             retval = getTopLevelObject(ac);
4307         }
4308         return retval;
4309     }
4310 
4311     /**
4312      * Returns how deep in the object hierarchy a given object is.
4313      * The top most object in the object hierarchy has an object depth of 0.
4314      * Returns -1 on error.
4315      */
4316     private int getObjectDepth(final AccessibleContext ac) {
4317         debugString("[INFO]: getObjectDepth: ac = "+ac);
4318 
4319         if (ac == null) {
4320             return -1;
4321         }
4322         return InvocationUtils.invokeAndWait(new Callable<Integer>() {
4323             @Override
4324             public Integer call() throws Exception {
4325                 int count = 0;
4326                 Accessible parent = ac.getAccessibleParent();
4327                 if (parent == null) {
4328                     return count;
4329                 }
4330                 Accessible tmp = parent;
4331                 while (tmp != null && tmp.getAccessibleContext() != null) {
4332                     parent = tmp;
4333                     tmp = parent.getAccessibleContext().getAccessibleParent();
4334                     count++;
4335                 }
4336                 return count;
4337             }
4338         }, ac);
4339     }
4340 
4341     /**
4342      * Returns the Accessible Context of the current ActiveDescendent of an object.
4343      * Returns (AccessibleContext)0 on error.
4344      */
4345     private AccessibleContext getActiveDescendent (final AccessibleContext ac) {
4346         debugString("[INFO]: getActiveDescendent: ac = "+ac);
4347         if (ac == null) {
4348             return null;
4349         }
4350         // workaround for JTree bug where the only possible active
4351         // descendent is the JTree root
4352         final Accessible parent = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
4353             @Override
4354             public Accessible call() throws Exception {
4355                 return ac.getAccessibleParent();
4356             }
4357         }, ac);
4358 
4359         if (parent != null) {
4360             Accessible child = InvocationUtils.invokeAndWait(new Callable<Accessible>() {
4361                 @Override
4362                 public Accessible call() throws Exception {
4363                     int indexInParent = ac.getAccessibleIndexInParent();
4364                     return parent.getAccessibleContext().getAccessibleChild(indexInParent);
4365                 }
4366             }, ac);
4367 
4368             if (child instanceof JTree) {
4369                 // return the selected node
4370                 final JTree tree = (JTree)child;
4371                 return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4372                     @Override
4373                     public AccessibleContext call() throws Exception {
4374                         return new AccessibleJTreeNode(tree,
4375                                 tree.getSelectionPath(),
4376                                 null);
4377                     }
4378                 }, child);
4379             }
4380         }
4381 
4382         return InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4383             @Override
4384             public AccessibleContext call() throws Exception {
4385                 AccessibleSelection as = ac.getAccessibleSelection();
4386                 if (as == null) {
4387                     return null;
4388                 }
4389                 // assume single selection
4390                 if (as.getAccessibleSelectionCount() != 1) {
4391                     return null;
4392                 }
4393                 Accessible a = as.getAccessibleSelection(0);
4394                 if (a == null) {
4395                     return null;
4396                 }
4397                 return a.getAccessibleContext();
4398             }
4399         }, ac);
4400     }
4401 
4402 
4403     /**
4404      * Additional methods for Teton
4405      */
4406 
4407     /**
4408      * Gets the AccessibleName for a component based upon the JAWS algorithm.
4409      * Returns whether successful.
4410      *
4411      * Bug ID 4916682 - Implement JAWS AccessibleName policy
4412      */
4413     private String getJAWSAccessibleName(final AccessibleContext ac) {
4414         debugString("[INFO]:  getJAWSAccessibleName");
4415         if (ac == null) {
4416             return null;
4417         }
4418         // placeholder
4419         return InvocationUtils.invokeAndWait(new Callable<String>() {
4420             @Override
4421             public String call() throws Exception {
4422                 return ac.getAccessibleName();
4423             }
4424         }, ac);
4425     }
4426 
4427     /**
4428      * Request focus for a component. Returns whether successful;
4429      *
4430      * Bug ID 4944757 - requestFocus method needed
4431      */
4432     private boolean requestFocus(final AccessibleContext ac) {
4433         debugString("[INFO]:  requestFocus");
4434         if (ac == null) {
4435             return false;
4436         }
4437         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4438             @Override
4439             public Boolean call() throws Exception {
4440                 AccessibleComponent acomp = ac.getAccessibleComponent();
4441                 if (acomp == null) {
4442                     return false;
4443                 }
4444                 acomp.requestFocus();
4445                 return ac.getAccessibleStateSet().contains(AccessibleState.FOCUSED);
4446             }
4447         }, ac);
4448     }
4449 
4450     /**
4451      * Selects text between two indices.  Selection includes the
4452      * text at the start index and the text at the end index. Returns
4453      * whether successful;
4454      *
4455      * Bug ID 4944758 - selectTextRange method needed
4456      */
4457     private boolean selectTextRange(final AccessibleContext ac, final int startIndex, final int endIndex) {
4458         debugString("[INFO]:  selectTextRange: start = "+startIndex+"; end = "+endIndex);
4459         if (ac == null) {
4460             return false;
4461         }
4462         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4463             @Override
4464             public Boolean call() throws Exception {
4465                 AccessibleText at = ac.getAccessibleText();
4466                 if (!(at instanceof AccessibleEditableText)) {
4467                     return false;
4468                 }
4469                 ((AccessibleEditableText) at).selectText(startIndex, endIndex);
4470 
4471                 boolean result = at.getSelectionStart() == startIndex &&
4472                         at.getSelectionEnd() == endIndex;
4473                 return result;
4474             }
4475         }, ac);
4476     }
4477 
4478     /**
4479      * Set the caret to a text position. Returns whether successful;
4480      *
4481      * Bug ID 4944770 - setCaretPosition method needed
4482      */
4483     private boolean setCaretPosition(final AccessibleContext ac, final int position) {
4484         debugString("[INFO]: setCaretPosition: position = "+position);
4485         if (ac == null) {
4486             return false;
4487         }
4488         return InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4489             @Override
4490             public Boolean call() throws Exception {
4491                 AccessibleText at = ac.getAccessibleText();
4492                 if (!(at instanceof AccessibleEditableText)) {
4493                     return false;
4494                 }
4495                 ((AccessibleEditableText) at).selectText(position, position);
4496                 return at.getCaretPosition() == position;
4497             }
4498         }, ac);
4499     }
4500 
4501     /**
4502      * Gets the number of visible children of an AccessibleContext.
4503      *
4504      * Bug ID 4944762- getVisibleChildren for list-like components needed
4505      */
4506     private int _visibleChildrenCount;
4507     private AccessibleContext _visibleChild;
4508     private int _currentVisibleIndex;
4509     private boolean _foundVisibleChild;
4510 
4511     private int getVisibleChildrenCount(AccessibleContext ac) {
4512         debugString("[INFO]: getVisibleChildrenCount");
4513         if (ac == null) {
4514             return -1;
4515         }
4516         _visibleChildrenCount = 0;
4517         _getVisibleChildrenCount(ac);
4518         debugString("[INFO]:   _visibleChildrenCount = "+_visibleChildrenCount);
4519         return _visibleChildrenCount;
4520     }
4521 
4522     /*
4523      * Recursively descends AccessibleContext and gets the number
4524      * of visible children
4525      */
4526     private void _getVisibleChildrenCount(final AccessibleContext ac) {
4527         if (ac == null)
4528             return;
4529         if(ac instanceof AccessibleExtendedTable) {
4530             _getVisibleChildrenCount((AccessibleExtendedTable)ac);
4531             return;
4532         }
4533         int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4534             @Override
4535             public Integer call() throws Exception {
4536                 return ac.getAccessibleChildrenCount();
4537             }
4538         }, ac);
4539         for (int i = 0; i < numChildren; i++) {
4540             final int idx = i;
4541             final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4542                 @Override
4543                 public AccessibleContext call() throws Exception {
4544                     Accessible a = ac.getAccessibleChild(idx);
4545                     if (a != null)
4546                         return a.getAccessibleContext();
4547                     else
4548                         return null;
4549                 }
4550             }, ac);
4551             if ( ac2 == null ||
4552                  (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4553                      @Override
4554                      public Boolean call() throws Exception {
4555                          return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4556                      }
4557                  }, ac))
4558                ) {
4559                 continue;
4560             }
4561             _visibleChildrenCount++;
4562 
4563             if (InvocationUtils.invokeAndWait(new Callable<Integer>() {
4564                 @Override
4565                 public Integer call() throws Exception {
4566                     return ac2.getAccessibleChildrenCount();
4567                 }
4568             }, ac) > 0 ) {
4569                 _getVisibleChildrenCount(ac2);
4570             }
4571         }
4572     }
4573 
4574     /*
4575     * Recursively descends AccessibleContext and gets the number
4576     * of visible children. Stops search if get to invisible part of table.
4577     */
4578     private void _getVisibleChildrenCount(final AccessibleExtendedTable acTable) {
4579         if (acTable == null)
4580             return;
4581         int lastVisibleRow = -1;
4582         int lastVisibleColumn = -1;
4583         boolean foundVisible = false;
4584         int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4585             @Override
4586             public Integer call() throws Exception {
4587                 return acTable.getAccessibleRowCount();
4588             }
4589         }, acTable);
4590         int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4591             @Override
4592             public Integer call() throws Exception {
4593                 return acTable.getAccessibleColumnCount();
4594             }
4595         }, acTable);
4596         for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) {
4597             for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) {
4598                 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) {
4599                     continue;
4600                 }
4601                 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) {
4602                     continue;
4603                 }
4604                 int finalRowIdx = rowIdx;
4605                 int finalColumnIdx = columnIdx;
4606                 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4607                     @Override
4608                     public AccessibleContext call() throws Exception {
4609                         Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx);
4610                         if (a == null)
4611                             return null;
4612                         else
4613                             return a.getAccessibleContext();
4614                     }
4615                 }, acTable);
4616                 if (ac2 == null ||
4617                         (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4618                             @Override
4619                             public Boolean call() throws Exception {
4620                                 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4621                             }
4622                         }, acTable))
4623                         ) {
4624                     if (foundVisible) {
4625                         if (columnIdx != 0 && lastVisibleColumn == -1) {
4626                             //the same row, so we found the last visible column
4627                             lastVisibleColumn = columnIdx - 1;
4628                         } else if (columnIdx == 0 && lastVisibleRow == -1) {
4629                             lastVisibleRow = rowIdx - 1;
4630                         }
4631                     }
4632                     continue;
4633                 }
4634 
4635                 foundVisible = true;
4636 
4637                 _visibleChildrenCount++;
4638 
4639                 if (InvocationUtils.invokeAndWait(new Callable<Integer>() {
4640                     @Override
4641                     public Integer call() throws Exception {
4642                         return ac2.getAccessibleChildrenCount();
4643                     }
4644                 }, acTable) > 0) {
4645                     _getVisibleChildrenCount(ac2);
4646                 }
4647             }
4648         }
4649     }
4650 
4651     /**
4652      * Gets the visible child of an AccessibleContext at the
4653      * specified index
4654      *
4655      * Bug ID 4944762- getVisibleChildren for list-like components needed
4656      */
4657     private AccessibleContext getVisibleChild(AccessibleContext ac, int index) {
4658         debugString("[INFO]: getVisibleChild: index = "+index);
4659         if (ac == null) {
4660             return null;
4661         }
4662         _visibleChild = null;
4663         _currentVisibleIndex = 0;
4664         _foundVisibleChild = false;
4665         _getVisibleChild(ac, index);
4666 
4667         if (_visibleChild != null) {
4668             debugString( "[INFO]:     getVisibleChild: found child = " +
4669                          InvocationUtils.invokeAndWait(new Callable<String>() {
4670                              @Override
4671                              public String call() throws Exception {
4672                                  return AccessBridge.this._visibleChild.getAccessibleName();
4673                              }
4674                          }, ac) );
4675         }
4676         return _visibleChild;
4677     }
4678 
4679     /*
4680      * Recursively searchs AccessibleContext and finds the visible component
4681      * at the specified index
4682      */
4683     private void _getVisibleChild(final AccessibleContext ac, final int index) {
4684         if (_visibleChild != null) {
4685             return;
4686         }
4687         if(ac instanceof AccessibleExtendedTable) {
4688             _getVisibleChild((AccessibleExtendedTable)ac, index);
4689             return;
4690         }
4691         int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4692             @Override
4693             public Integer call() throws Exception {
4694                 return ac.getAccessibleChildrenCount();
4695             }
4696         }, ac);
4697         for (int i = 0; i < numChildren; i++) {
4698             final int idx=i;
4699             final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4700                 @Override
4701                 public AccessibleContext call() throws Exception {
4702                     Accessible a = ac.getAccessibleChild(idx);
4703                     if (a == null)
4704                         return null;
4705                     else
4706                         return a.getAccessibleContext();
4707                 }
4708             }, ac);
4709             if (ac2 == null ||
4710             (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4711                 @Override
4712                 public Boolean call() throws Exception {
4713                     return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4714                 }
4715             }, ac))) {
4716                 continue;
4717             }
4718             if (!_foundVisibleChild && _currentVisibleIndex == index) {
4719             _visibleChild = ac2;
4720             _foundVisibleChild = true;
4721             return;
4722             }
4723             _currentVisibleIndex++;
4724 
4725             if ( InvocationUtils.invokeAndWait(new Callable<Integer>() {
4726                 @Override
4727                 public Integer call() throws Exception {
4728                     return ac2.getAccessibleChildrenCount();
4729                 }
4730             }, ac) > 0 ) {
4731                 _getVisibleChild(ac2, index);
4732             }
4733         }
4734     }
4735 
4736     private void _getVisibleChild(final AccessibleExtendedTable acTable, final int index) {
4737         if (_visibleChild != null) {
4738             return;
4739         }
4740         int lastVisibleRow = -1;
4741         int lastVisibleColumn = -1;
4742         boolean foundVisible = false;
4743         int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4744             @Override
4745             public Integer call() throws Exception {
4746                 return acTable.getAccessibleRowCount();
4747             }
4748         }, acTable);
4749         int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4750             @Override
4751             public Integer call() throws Exception {
4752                 return acTable.getAccessibleColumnCount();
4753             }
4754         }, acTable);
4755         for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) {
4756             for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) {
4757                 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) {
4758                     continue;
4759                 }
4760                 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) {
4761                     continue;
4762                 }
4763                 int finalRowIdx = rowIdx;
4764                 int finalColumnIdx = columnIdx;
4765                 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4766                     @Override
4767                     public AccessibleContext call() throws Exception {
4768                         Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx);
4769                         if (a == null)
4770                             return null;
4771                         else
4772                             return a.getAccessibleContext();
4773                     }
4774                 }, acTable);
4775                 if (ac2 == null ||
4776                         (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4777                             @Override
4778                             public Boolean call() throws Exception {
4779                                 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4780                             }
4781                         }, acTable))) {
4782                     if (foundVisible) {
4783                         if (columnIdx != 0 && lastVisibleColumn == -1) {
4784                             //the same row, so we found the last visible column
4785                             lastVisibleColumn = columnIdx - 1;
4786                         } else if (columnIdx == 0 && lastVisibleRow == -1) {
4787                             lastVisibleRow = rowIdx - 1;
4788                         }
4789                     }
4790                     continue;
4791                 }
4792                 foundVisible = true;
4793 
4794                 if (!_foundVisibleChild && _currentVisibleIndex == index) {
4795                     _visibleChild = ac2;
4796                     _foundVisibleChild = true;
4797                     return;
4798                 }
4799                 _currentVisibleIndex++;
4800 
4801                 if (InvocationUtils.invokeAndWait(new Callable<Integer>() {
4802                     @Override
4803                     public Integer call() throws Exception {
4804                         return ac2.getAccessibleChildrenCount();
4805                     }
4806                 }, acTable) > 0) {
4807                     _getVisibleChild(ac2, index);
4808                 }
4809             }
4810         }
4811     }
4812 
4813     /* ===== Java object memory management code ===== */
4814 
4815     /**
4816      * Class to track object references to ensure the
4817      * Java VM doesn't garbage collect them
4818      */
4819     private class ObjectReferences {
4820 
4821         private class Reference {
4822             private int value;
4823 
4824             Reference(int i) {
4825                 value = i;
4826             }
4827 
4828             public String toString() {
4829                 return ("refCount: " + value);
4830             }
4831         }
4832 
4833         /**
4834         * table object references, to keep 'em from being garbage collected
4835         */
4836         private ConcurrentHashMap<Object,Reference> refs;
4837 
4838         /**
4839         * Constructor
4840         */
4841         ObjectReferences() {
4842             refs = new ConcurrentHashMap<>(4);
4843         }
4844 
4845         /**
4846         * Debugging: dump the contents of ObjectReferences' refs Hashtable
4847         */
4848         String dump() {
4849             return refs.toString();
4850         }
4851 
4852         /**
4853         * Increment ref count; set to 1 if we have no references for it
4854         */
4855         void increment(Object o) {
4856             if (o == null){
4857                 debugString("[WARN]: ObjectReferences::increment - Passed in object is null");
4858                 return;
4859             }
4860 
4861             if (refs.containsKey(o)) {
4862                 (refs.get(o)).value++;
4863             } else {
4864                 refs.put(o, new Reference(1));
4865             }
4866         }
4867 
4868         /**
4869         * Decrement ref count; remove if count drops to 0
4870         */
4871         void decrement(Object o) {
4872             Reference aRef = refs.get(o);
4873             if (aRef != null) {
4874                 aRef.value--;
4875                 if (aRef.value == 0) {
4876                     refs.remove(o);
4877                 } else if (aRef.value < 0) {
4878                     debugString("[ERROR]: decrementing reference count below 0");
4879                 }
4880             } else {
4881                 debugString("[ERROR]: object to decrement not in ObjectReferences table");
4882             }
4883         }
4884 
4885     }
4886 
4887     /* ===== event handling code ===== */
4888 
4889    /**
4890      * native method for handling property change events
4891      */
4892     private native void propertyCaretChange(PropertyChangeEvent e,
4893                         AccessibleContext src,
4894                         int oldValue, int newValue);
4895     private native void propertyDescriptionChange(PropertyChangeEvent e,
4896                         AccessibleContext src,
4897                         String oldValue, String newValue);
4898     private native void propertyNameChange(PropertyChangeEvent e,
4899                         AccessibleContext src,
4900                         String oldValue, String newValue);
4901     private native void propertySelectionChange(PropertyChangeEvent e,
4902                         AccessibleContext src);
4903     private native void propertyStateChange(PropertyChangeEvent e,
4904                         AccessibleContext src,
4905                         String oldValue, String newValue);
4906     private native void propertyTextChange(PropertyChangeEvent e,
4907                         AccessibleContext src);
4908     private native void propertyValueChange(PropertyChangeEvent e,
4909                         AccessibleContext src,
4910                         String oldValue, String newValue);
4911     private native void propertyVisibleDataChange(PropertyChangeEvent e,
4912                         AccessibleContext src);
4913     private native void propertyChildChange(PropertyChangeEvent e,
4914                         AccessibleContext src,
4915                         AccessibleContext oldValue,
4916                         AccessibleContext newValue);
4917     private native void propertyActiveDescendentChange(PropertyChangeEvent e,
4918                         AccessibleContext src,
4919                         AccessibleContext oldValue,
4920                         AccessibleContext newValue);
4921 
4922     private native void javaShutdown();
4923 
4924     /**
4925      * native methods for handling focus events
4926      */
4927     private native void focusGained(FocusEvent e, AccessibleContext src);
4928     private native void focusLost(FocusEvent e, AccessibleContext src);
4929 
4930     /**
4931      * native method for handling caret events
4932      */
4933     private native void caretUpdate(CaretEvent e, AccessibleContext src);
4934 
4935     /**
4936      * native methods for handling mouse events
4937      */
4938     private native void mouseClicked(MouseEvent e, AccessibleContext src);
4939     private native void mouseEntered(MouseEvent e, AccessibleContext src);
4940     private native void mouseExited(MouseEvent e, AccessibleContext src);
4941     private native void mousePressed(MouseEvent e, AccessibleContext src);
4942     private native void mouseReleased(MouseEvent e, AccessibleContext src);
4943 
4944     /**
4945      * native methods for handling menu & popupMenu events
4946      */
4947     private native void menuCanceled(MenuEvent e, AccessibleContext src);
4948     private native void menuDeselected(MenuEvent e, AccessibleContext src);
4949     private native void menuSelected(MenuEvent e, AccessibleContext src);
4950     private native void popupMenuCanceled(PopupMenuEvent e, AccessibleContext src);
4951     private native void popupMenuWillBecomeInvisible(PopupMenuEvent e,
4952                                                      AccessibleContext src);
4953     private native void popupMenuWillBecomeVisible(PopupMenuEvent e,
4954                                                    AccessibleContext src);
4955 
4956     /* ===== event definitions ===== */
4957 
4958     private static final long PROPERTY_CHANGE_EVENTS = 1;
4959     private static final long FOCUS_GAINED_EVENTS = 2;
4960     private static final long FOCUS_LOST_EVENTS = 4;
4961     private static final long FOCUS_EVENTS = (FOCUS_GAINED_EVENTS | FOCUS_LOST_EVENTS);
4962 
4963     private static final long CARET_UPATE_EVENTS = 8;
4964     private static final long CARET_EVENTS = CARET_UPATE_EVENTS;
4965 
4966     private static final long MOUSE_CLICKED_EVENTS = 16;
4967     private static final long MOUSE_ENTERED_EVENTS = 32;
4968     private static final long MOUSE_EXITED_EVENTS = 64;
4969     private static final long MOUSE_PRESSED_EVENTS = 128;
4970     private static final long MOUSE_RELEASED_EVENTS = 256;
4971     private static final long MOUSE_EVENTS = (MOUSE_CLICKED_EVENTS | MOUSE_ENTERED_EVENTS |
4972                                              MOUSE_EXITED_EVENTS | MOUSE_PRESSED_EVENTS |
4973                                              MOUSE_RELEASED_EVENTS);
4974 
4975     private static final long MENU_CANCELED_EVENTS = 512;
4976     private static final long MENU_DESELECTED_EVENTS = 1024;
4977     private static final long MENU_SELECTED_EVENTS = 2048;
4978     private static final long MENU_EVENTS = (MENU_CANCELED_EVENTS | MENU_DESELECTED_EVENTS |
4979                                             MENU_SELECTED_EVENTS);
4980 
4981     private static final long POPUPMENU_CANCELED_EVENTS = 4096;
4982     private static final long POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS = 8192;
4983     private static final long POPUPMENU_WILL_BECOME_VISIBLE_EVENTS = 16384;
4984     private static final long POPUPMENU_EVENTS = (POPUPMENU_CANCELED_EVENTS |
4985                                                  POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS |
4986                                                  POPUPMENU_WILL_BECOME_VISIBLE_EVENTS);
4987 
4988     /* These use their own numbering scheme, to ensure sufficient expansion room */
4989     private static final long PROPERTY_NAME_CHANGE_EVENTS = 1;
4990     private static final long PROPERTY_DESCRIPTION_CHANGE_EVENTS = 2;
4991     private static final long PROPERTY_STATE_CHANGE_EVENTS = 4;
4992     private static final long PROPERTY_VALUE_CHANGE_EVENTS = 8;
4993     private static final long PROPERTY_SELECTION_CHANGE_EVENTS = 16;
4994     private static final long PROPERTY_TEXT_CHANGE_EVENTS = 32;
4995     private static final long PROPERTY_CARET_CHANGE_EVENTS = 64;
4996     private static final long PROPERTY_VISIBLEDATA_CHANGE_EVENTS = 128;
4997     private static final long PROPERTY_CHILD_CHANGE_EVENTS = 256;
4998     private static final long PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS = 512;
4999 
5000 
5001     private static final long PROPERTY_EVENTS = (PROPERTY_NAME_CHANGE_EVENTS |
5002                                                 PROPERTY_DESCRIPTION_CHANGE_EVENTS |
5003                                                 PROPERTY_STATE_CHANGE_EVENTS |
5004                                                 PROPERTY_VALUE_CHANGE_EVENTS |
5005                                                 PROPERTY_SELECTION_CHANGE_EVENTS |
5006                                                 PROPERTY_TEXT_CHANGE_EVENTS |
5007                                                 PROPERTY_CARET_CHANGE_EVENTS |
5008                                                 PROPERTY_VISIBLEDATA_CHANGE_EVENTS |
5009                                                 PROPERTY_CHILD_CHANGE_EVENTS |
5010                                                 PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS);
5011 
5012     /**
5013      * The EventHandler class listens for Java events and
5014      * forwards them to the AT
5015      */
5016     private class EventHandler implements PropertyChangeListener,
5017                                           FocusListener, CaretListener,
5018                                           MenuListener, PopupMenuListener,
5019                                           MouseListener, WindowListener,
5020                                           ChangeListener {
5021 
5022         private AccessBridge accessBridge;
5023         private long javaEventMask = 0;
5024         private long accessibilityEventMask = 0;
5025 
5026         EventHandler(AccessBridge bridge) {
5027             accessBridge = bridge;
5028 
5029             // Register to receive WINDOW_OPENED and WINDOW_CLOSED
5030             // events.  Add the event source as a native window
5031             // handler is it implements NativeWindowHandler.
5032             // SwingEventMonitor.addWindowListener(this);
5033         }
5034 
5035         // --------- Event Notification Registration methods
5036 
5037         /**
5038          * Invoked the first time a window is made visible.
5039          */
5040         public void windowOpened(WindowEvent e) {
5041             // If the window is a NativeWindowHandler, add it.
5042             Object o = null;
5043             if (e != null)
5044                 o = e.getSource();
5045             if (o instanceof NativeWindowHandler) {
5046                 addNativeWindowHandler((NativeWindowHandler)o);
5047             }
5048         }
5049 
5050         /**
5051          * Invoked when the user attempts to close the window
5052          * from the window's system menu.  If the program does not
5053          * explicitly hide or dispose the window while processing
5054          * this event, the window close operation will be canceled.
5055          */
5056         public void windowClosing(WindowEvent e) {}
5057 
5058         /**
5059          * Invoked when a window has been closed as the result
5060          * of calling dispose on the window.
5061          */
5062         public void windowClosed(WindowEvent e) {
5063             // If the window is a NativeWindowHandler, remove it.
5064             Object o = null;
5065             if (e != null)
5066                 o = e.getSource();
5067             if (o instanceof NativeWindowHandler) {
5068                 removeNativeWindowHandler((NativeWindowHandler)o);
5069             }
5070         }
5071 
5072         /**
5073          * Invoked when a window is changed from a normal to a
5074          * minimized state. For many platforms, a minimized window
5075          * is displayed as the icon specified in the window's
5076          * iconImage property.
5077          * @see java.awt.Frame#setIconImage
5078          */
5079         public void windowIconified(WindowEvent e) {}
5080 
5081         /**
5082          * Invoked when a window is changed from a minimized
5083          * to a normal state.
5084          */
5085         public void windowDeiconified(WindowEvent e) {}
5086 
5087         /**
5088          * Invoked when the Window is set to be the active Window. Only a Frame or
5089          * a Dialog can be the active Window. The native windowing system may
5090          * denote the active Window or its children with special decorations, such
5091          * as a highlighted title bar. The active Window is always either the
5092          * focused Window, or the first Frame or Dialog that is an owner of the
5093          * focused Window.
5094          */
5095         public void windowActivated(WindowEvent e) {}
5096 
5097         /**
5098          * Invoked when a Window is no longer the active Window. Only a Frame or a
5099          * Dialog can be the active Window. The native windowing system may denote
5100          * the active Window or its children with special decorations, such as a
5101          * highlighted title bar. The active Window is always either the focused
5102          * Window, or the first Frame or Dialog that is an owner of the focused
5103          * Window.
5104          */
5105         public void windowDeactivated(WindowEvent e) {}
5106 
5107         /**
5108          * Turn on event monitoring for the event type passed in
5109          * If necessary, add the appropriate event listener (if
5110          * no other event of that type is being listened for)
5111          */
5112         void addJavaEventNotification(long type) {
5113             long newEventMask = javaEventMask | type;
5114             /*
5115             if ( ((javaEventMask & PROPERTY_EVENTS) == 0) &&
5116                  ((newEventMask & PROPERTY_EVENTS) != 0) ) {
5117                 AccessibilityEventMonitor.addPropertyChangeListener(this);
5118             }
5119             */
5120             if ( ((javaEventMask & FOCUS_EVENTS) == 0) &&
5121                 ((newEventMask & FOCUS_EVENTS) != 0) ) {
5122                 SwingEventMonitor.addFocusListener(this);
5123             }
5124             if ( ((javaEventMask & CARET_EVENTS) == 0) &&
5125                 ((newEventMask & CARET_EVENTS) != 0) ) {
5126                 SwingEventMonitor.addCaretListener(this);
5127             }
5128             if ( ((javaEventMask & MOUSE_EVENTS) == 0) &&
5129                 ((newEventMask & MOUSE_EVENTS) != 0) ) {
5130                 SwingEventMonitor.addMouseListener(this);
5131             }
5132             if ( ((javaEventMask & MENU_EVENTS) == 0) &&
5133                 ((newEventMask & MENU_EVENTS) != 0) ) {
5134                 SwingEventMonitor.addMenuListener(this);
5135                 SwingEventMonitor.addPopupMenuListener(this);
5136             }
5137             if ( ((javaEventMask & POPUPMENU_EVENTS) == 0) &&
5138                 ((newEventMask & POPUPMENU_EVENTS) != 0) ) {
5139                 SwingEventMonitor.addPopupMenuListener(this);
5140             }
5141 
5142             javaEventMask = newEventMask;
5143         }
5144 
5145         /**
5146          * Turn off event monitoring for the event type passed in
5147          * If necessary, remove the appropriate event listener (if
5148          * no other event of that type is being listened for)
5149          */
5150         void removeJavaEventNotification(long type) {
5151             long newEventMask = javaEventMask & (~type);
5152             /*
5153             if ( ((javaEventMask & PROPERTY_EVENTS) != 0) &&
5154                  ((newEventMask & PROPERTY_EVENTS) == 0) ) {
5155                 AccessibilityEventMonitor.removePropertyChangeListener(this);
5156             }
5157             */
5158             if (((javaEventMask & FOCUS_EVENTS) != 0) &&
5159                 ((newEventMask & FOCUS_EVENTS) == 0)) {
5160                 SwingEventMonitor.removeFocusListener(this);
5161             }
5162             if (((javaEventMask & CARET_EVENTS) != 0) &&
5163                 ((newEventMask & CARET_EVENTS) == 0)) {
5164                 SwingEventMonitor.removeCaretListener(this);
5165             }
5166             if (((javaEventMask & MOUSE_EVENTS) == 0) &&
5167                 ((newEventMask & MOUSE_EVENTS) != 0)) {
5168                 SwingEventMonitor.removeMouseListener(this);
5169             }
5170             if (((javaEventMask & MENU_EVENTS) == 0) &&
5171                 ((newEventMask & MENU_EVENTS) != 0)) {
5172                 SwingEventMonitor.removeMenuListener(this);
5173             }
5174             if (((javaEventMask & POPUPMENU_EVENTS) == 0) &&
5175                 ((newEventMask & POPUPMENU_EVENTS) != 0)) {
5176                 SwingEventMonitor.removePopupMenuListener(this);
5177             }
5178 
5179             javaEventMask = newEventMask;
5180         }
5181 
5182         /**
5183          * Turn on event monitoring for the event type passed in
5184          * If necessary, add the appropriate event listener (if
5185          * no other event of that type is being listened for)
5186          */
5187         void addAccessibilityEventNotification(long type) {
5188             long newEventMask = accessibilityEventMask | type;
5189             if ( ((accessibilityEventMask & PROPERTY_EVENTS) == 0) &&
5190                  ((newEventMask & PROPERTY_EVENTS) != 0) ) {
5191                 AccessibilityEventMonitor.addPropertyChangeListener(this);
5192             }
5193             accessibilityEventMask = newEventMask;
5194         }
5195 
5196         /**
5197          * Turn off event monitoring for the event type passed in
5198          * If necessary, remove the appropriate event listener (if
5199          * no other event of that type is being listened for)
5200          */
5201         void removeAccessibilityEventNotification(long type) {
5202             long newEventMask = accessibilityEventMask & (~type);
5203             if ( ((accessibilityEventMask & PROPERTY_EVENTS) != 0) &&
5204                  ((newEventMask & PROPERTY_EVENTS) == 0) ) {
5205                 AccessibilityEventMonitor.removePropertyChangeListener(this);
5206             }
5207             accessibilityEventMask = newEventMask;
5208         }
5209 
5210         /**
5211          *  ------- property change event glue
5212          */
5213         // This is invoked on the EDT , as
5214         public void propertyChange(PropertyChangeEvent e) {
5215 
5216             accessBridge.debugString("[INFO]: propertyChange(" + e.toString() + ") called");
5217 
5218             if (e != null && (accessibilityEventMask & PROPERTY_EVENTS) != 0) {
5219                 Object o = e.getSource();
5220                 AccessibleContext ac;
5221 
5222                 if (o instanceof AccessibleContext) {
5223                     ac = (AccessibleContext) o;
5224                 } else {
5225                     Accessible a = Translator.getAccessible(e.getSource());
5226                     if (a == null)
5227                         return;
5228                     else
5229                         ac = a.getAccessibleContext();
5230                 }
5231                 if (ac != null) {
5232                     InvocationUtils.registerAccessibleContext(ac, AppContext.getAppContext());
5233 
5234                     accessBridge.debugString("[INFO]: AccessibleContext: " + ac);
5235                     String propertyName = e.getPropertyName();
5236 
5237                     if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CARET_PROPERTY) == 0) {
5238                         int oldValue = 0;
5239                         int newValue = 0;
5240 
5241                         if (e.getOldValue() instanceof Integer) {
5242                             oldValue = ((Integer) e.getOldValue()).intValue();
5243                         }
5244                         if (e.getNewValue() instanceof Integer) {
5245                             newValue = ((Integer) e.getNewValue()).intValue();
5246                         }
5247                         accessBridge.debugString("[INFO]:  - about to call propertyCaretChange()   old value: " + oldValue + "new value: " + newValue);
5248                         accessBridge.propertyCaretChange(e, ac, oldValue, newValue);
5249 
5250                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY) == 0) {
5251                         String oldValue = null;
5252                         String newValue = null;
5253 
5254                         if (e.getOldValue() != null) {
5255                             oldValue = e.getOldValue().toString();
5256                         }
5257                         if (e.getNewValue() != null) {
5258                             newValue = e.getNewValue().toString();
5259                         }
5260                         accessBridge.debugString("[INFO]:  - about to call propertyDescriptionChange()   old value: " + oldValue + "new value: " + newValue);
5261                         accessBridge.propertyDescriptionChange(e, ac, oldValue, newValue);
5262 
5263                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_NAME_PROPERTY) == 0) {
5264                         String oldValue = null;
5265                         String newValue = null;
5266 
5267                         if (e.getOldValue() != null) {
5268                             oldValue = e.getOldValue().toString();
5269                         }
5270                         if (e.getNewValue() != null) {
5271                             newValue = e.getNewValue().toString();
5272                         }
5273                         accessBridge.debugString("[INFO]:  - about to call propertyNameChange()   old value: " + oldValue + " new value: " + newValue);
5274                         accessBridge.propertyNameChange(e, ac, oldValue, newValue);
5275 
5276                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY) == 0) {
5277                         accessBridge.debugString("[INFO]:  - about to call propertySelectionChange() " + ac +  "   " + Thread.currentThread() + "   " + e.getSource());
5278 
5279                         accessBridge.propertySelectionChange(e, ac);
5280 
5281                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_STATE_PROPERTY) == 0) {
5282                         String oldValue = null;
5283                         String newValue = null;
5284 
5285                         // Localization fix requested by Oliver for EA-1
5286                         if (e.getOldValue() != null) {
5287                             AccessibleState oldState = (AccessibleState) e.getOldValue();
5288                             oldValue = oldState.toDisplayString(Locale.US);
5289                         }
5290                         if (e.getNewValue() != null) {
5291                             AccessibleState newState = (AccessibleState) e.getNewValue();
5292                             newValue = newState.toDisplayString(Locale.US);
5293                         }
5294 
5295                         accessBridge.debugString("[INFO]:  - about to call propertyStateChange()");
5296                         accessBridge.propertyStateChange(e, ac, oldValue, newValue);
5297 
5298                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_TEXT_PROPERTY) == 0) {
5299                         accessBridge.debugString("[INFO]:  - about to call propertyTextChange()");
5300                         accessBridge.propertyTextChange(e, ac);
5301 
5302                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY) == 0) {  // strings 'cause of floating point, etc.
5303                         String oldValue = null;
5304                         String newValue = null;
5305 
5306                         if (e.getOldValue() != null) {
5307                             oldValue = e.getOldValue().toString();
5308                         }
5309                         if (e.getNewValue() != null) {
5310                             newValue = e.getNewValue().toString();
5311                         }
5312                         accessBridge.debugString("[INFO]:  - about to call propertyDescriptionChange()");
5313                         accessBridge.propertyValueChange(e, ac, oldValue, newValue);
5314 
5315                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY) == 0) {
5316                         accessBridge.propertyVisibleDataChange(e, ac);
5317 
5318                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) {
5319                         AccessibleContext oldAC = null;
5320                         AccessibleContext newAC = null;
5321                         Accessible a;
5322 
5323                         if (e.getOldValue() instanceof AccessibleContext) {
5324                             oldAC = (AccessibleContext) e.getOldValue();
5325                             InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext());
5326                         }
5327                         if (e.getNewValue() instanceof AccessibleContext) {
5328                             newAC = (AccessibleContext) e.getNewValue();
5329                             InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext());
5330                         }
5331                         accessBridge.debugString("[INFO]:  - about to call propertyChildChange()   old AC: " + oldAC + "new AC: " + newAC);
5332                         accessBridge.propertyChildChange(e, ac, oldAC, newAC);
5333 
5334                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0) {
5335                         handleActiveDescendentEvent(e, ac);
5336                     }
5337                 }
5338             }
5339         }
5340 
5341         /*
5342         * Handle an ActiveDescendent PropertyChangeEvent.  This
5343         * method works around a JTree bug where ActiveDescendent
5344         * PropertyChangeEvents have the wrong parent.
5345         */
5346         private AccessibleContext prevAC = null; // previous AccessibleContext
5347 
5348         private void handleActiveDescendentEvent(PropertyChangeEvent e,
5349                                                  AccessibleContext ac) {
5350             if (e == null || ac == null)
5351                 return;
5352             AccessibleContext oldAC = null;
5353             AccessibleContext newAC = null;
5354             Accessible a;
5355 
5356             // get the old active descendent
5357             if (e.getOldValue() instanceof Accessible) {
5358                 oldAC = ((Accessible) e.getOldValue()).getAccessibleContext();
5359             } else if (e.getOldValue() instanceof Component) {
5360                 a = Translator.getAccessible(e.getOldValue());
5361                 if (a != null) {
5362                     oldAC = a.getAccessibleContext();
5363                 }
5364             }
5365             if (oldAC != null) {
5366                 Accessible parent = oldAC.getAccessibleParent();
5367                 if (parent instanceof JTree) {
5368                     // use the previous AccessibleJTreeNode
5369                     oldAC = prevAC;
5370                 }
5371             }
5372 
5373             // get the new active descendent
5374             if (e.getNewValue() instanceof Accessible) {
5375                 newAC = ((Accessible) e.getNewValue()).getAccessibleContext();
5376             } else if (e.getNewValue() instanceof Component) {
5377                 a = Translator.getAccessible(e.getNewValue());
5378                 if (a != null) {
5379                     newAC = a.getAccessibleContext();
5380                 }
5381             }
5382             if (newAC != null) {
5383                 Accessible parent = newAC.getAccessibleParent();
5384                 if (parent instanceof JTree) {
5385                     // use a new AccessibleJTreeNode with the right parent
5386                     JTree tree = (JTree)parent;
5387                     newAC = new AccessibleJTreeNode(tree,
5388                                                     tree.getSelectionPath(),
5389                                                     null);
5390                 }
5391             }
5392             prevAC = newAC;
5393 
5394             accessBridge.debugString("[INFO]:   - about to call propertyActiveDescendentChange()   AC: " + ac + "   old AC: " + oldAC + "new AC: " + newAC);
5395             InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext());
5396             InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext());
5397             accessBridge.propertyActiveDescendentChange(e, ac, oldAC, newAC);
5398         }
5399 
5400         /**
5401         *  ------- focus event glue
5402         */
5403         private boolean stateChangeListenerAdded = false;
5404 
5405         public void focusGained(FocusEvent e) {
5406             processFocusGained();
5407         }
5408 
5409         public void stateChanged(ChangeEvent e) {
5410             processFocusGained();
5411         }
5412 
5413         private void processFocusGained() {
5414             Component focusOwner = KeyboardFocusManager.
5415             getCurrentKeyboardFocusManager().getFocusOwner();
5416             if (focusOwner == null) {
5417                 return;
5418             }
5419 
5420             // Only menus and popup selections are handled by the JRootPane.
5421             if (focusOwner instanceof JRootPane) {
5422                 MenuElement [] path =
5423                 MenuSelectionManager.defaultManager().getSelectedPath();
5424                 if (path.length > 1) {
5425                     Component penult = path[path.length-2].getComponent();
5426                     Component last = path[path.length-1].getComponent();
5427 
5428                     if (last instanceof JPopupMenu) {
5429                         // This is a popup with nothing in the popup
5430                         // selected. The menu itself is selected.
5431                         FocusEvent e = new FocusEvent(penult, FocusEvent.FOCUS_GAINED);
5432                         AccessibleContext context = penult.getAccessibleContext();
5433                         InvocationUtils.registerAccessibleContext(context, SunToolkit.targetToAppContext(penult));
5434                         accessBridge.focusGained(e, context);
5435                     } else if (penult instanceof JPopupMenu) {
5436                         // This is a popup with an item selected
5437                         FocusEvent e =
5438                         new FocusEvent(last, FocusEvent.FOCUS_GAINED);
5439                         AccessibleContext focusedAC = last.getAccessibleContext();
5440                         InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(last));
5441                         accessBridge.debugString("[INFO]:  - about to call focusGained()   AC: " + focusedAC);
5442                         accessBridge.focusGained(e, focusedAC);
5443                     }
5444                 }
5445             } else {
5446                 // The focus owner has the selection.
5447                 if (focusOwner instanceof Accessible) {
5448                     FocusEvent e = new FocusEvent(focusOwner,
5449                                                   FocusEvent.FOCUS_GAINED);
5450                     AccessibleContext focusedAC = focusOwner.getAccessibleContext();
5451                     InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(focusOwner));
5452                     accessBridge.debugString("[INFO]:  - about to call focusGained()   AC: " + focusedAC);
5453                     accessBridge.focusGained(e, focusedAC);
5454                 }
5455             }
5456         }
5457 
5458         public void focusLost(FocusEvent e) {
5459             if (e != null && (javaEventMask & FOCUS_LOST_EVENTS) != 0) {
5460                 Accessible a = Translator.getAccessible(e.getSource());
5461                 if (a != null) {
5462                     accessBridge.debugString("[INFO]:  - about to call focusLost()   AC: " + a.getAccessibleContext());
5463                     AccessibleContext context = a.getAccessibleContext();
5464                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5465                     accessBridge.focusLost(e, context);
5466                 }
5467             }
5468         }
5469 
5470         /**
5471          *  ------- caret event glue
5472          */
5473         public void caretUpdate(CaretEvent e) {
5474             if (e != null && (javaEventMask & CARET_UPATE_EVENTS) != 0) {
5475                 Accessible a = Translator.getAccessible(e.getSource());
5476                 if (a != null) {
5477                     AccessibleContext context = a.getAccessibleContext();
5478                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5479                     accessBridge.caretUpdate(e, context);
5480                 }
5481             }
5482         }
5483 
5484     /**
5485      *  ------- mouse event glue
5486      */
5487 
5488         public void mouseClicked(MouseEvent e) {
5489             if (e != null && (javaEventMask & MOUSE_CLICKED_EVENTS) != 0) {
5490                 Accessible a = Translator.getAccessible(e.getSource());
5491                 if (a != null) {
5492                     AccessibleContext context = a.getAccessibleContext();
5493                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5494                     accessBridge.mouseClicked(e, context);
5495                 }
5496             }
5497         }
5498 
5499         public void mouseEntered(MouseEvent e) {
5500             if (e != null && (javaEventMask & MOUSE_ENTERED_EVENTS) != 0) {
5501                 Accessible a = Translator.getAccessible(e.getSource());
5502                 if (a != null) {
5503                     AccessibleContext context = a.getAccessibleContext();
5504                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5505                     accessBridge.mouseEntered(e, context);
5506                 }
5507             }
5508         }
5509 
5510         public void mouseExited(MouseEvent e) {
5511             if (e != null && (javaEventMask & MOUSE_EXITED_EVENTS) != 0) {
5512                 Accessible a = Translator.getAccessible(e.getSource());
5513                 if (a != null) {
5514                     AccessibleContext context = a.getAccessibleContext();
5515                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5516                     accessBridge.mouseExited(e, context);
5517                 }
5518             }
5519         }
5520 
5521         public void mousePressed(MouseEvent e) {
5522             if (e != null && (javaEventMask & MOUSE_PRESSED_EVENTS) != 0) {
5523                 Accessible a = Translator.getAccessible(e.getSource());
5524                 if (a != null) {
5525                     AccessibleContext context = a.getAccessibleContext();
5526                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5527                     accessBridge.mousePressed(e, context);
5528                 }
5529             }
5530         }
5531 
5532         public void mouseReleased(MouseEvent e) {
5533             if (e != null && (javaEventMask & MOUSE_RELEASED_EVENTS) != 0) {
5534                 Accessible a = Translator.getAccessible(e.getSource());
5535                 if (a != null) {
5536                     AccessibleContext context = a.getAccessibleContext();
5537                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5538                     accessBridge.mouseReleased(e, context);
5539                 }
5540             }
5541         }
5542 
5543         /**
5544          *  ------- menu event glue
5545          */
5546         public void menuCanceled(MenuEvent e) {
5547             if (e != null && (javaEventMask & MENU_CANCELED_EVENTS) != 0) {
5548                 Accessible a = Translator.getAccessible(e.getSource());
5549                 if (a != null) {
5550                     AccessibleContext context = a.getAccessibleContext();
5551                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5552                     accessBridge.menuCanceled(e, context);
5553                 }
5554             }
5555         }
5556 
5557         public void menuDeselected(MenuEvent e) {
5558             if (e != null && (javaEventMask & MENU_DESELECTED_EVENTS) != 0) {
5559                 Accessible a = Translator.getAccessible(e.getSource());
5560                 if (a != null) {
5561                     AccessibleContext context = a.getAccessibleContext();
5562                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5563                     accessBridge.menuDeselected(e, context);
5564                 }
5565             }
5566         }
5567 
5568         public void menuSelected(MenuEvent e) {
5569             if (e != null && (javaEventMask & MENU_SELECTED_EVENTS) != 0) {
5570                 Accessible a = Translator.getAccessible(e.getSource());
5571                 if (a != null) {
5572                     AccessibleContext context = a.getAccessibleContext();
5573                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5574                     accessBridge.menuSelected(e, context);
5575                 }
5576             }
5577         }
5578 
5579         public void popupMenuCanceled(PopupMenuEvent e) {
5580             if (e != null && (javaEventMask & POPUPMENU_CANCELED_EVENTS) != 0) {
5581                 Accessible a = Translator.getAccessible(e.getSource());
5582                 if (a != null) {
5583                     AccessibleContext context = a.getAccessibleContext();
5584                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5585                     accessBridge.popupMenuCanceled(e, context);
5586                 }
5587             }
5588         }
5589 
5590         public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
5591             if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS) != 0) {
5592                 Accessible a = Translator.getAccessible(e.getSource());
5593                 if (a != null) {
5594                     AccessibleContext context = a.getAccessibleContext();
5595                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5596                     accessBridge.popupMenuWillBecomeInvisible(e, context);
5597                 }
5598             }
5599         }
5600 
5601         public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
5602             if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_VISIBLE_EVENTS) != 0) {
5603                 Accessible a = Translator.getAccessible(e.getSource());
5604                 if (a != null) {
5605                     AccessibleContext context = a.getAccessibleContext();
5606                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5607                     accessBridge.popupMenuWillBecomeVisible(e, context);
5608                 }
5609             }
5610         }
5611 
5612     } // End of EventHandler Class
5613 
5614     // --------- Event Notification Registration methods
5615 
5616     /**
5617      *  Wrapper method around eventHandler.addJavaEventNotification()
5618      */
5619     private void addJavaEventNotification(final long type) {
5620         EventQueue.invokeLater(new Runnable() {
5621             public void run(){
5622                 eventHandler.addJavaEventNotification(type);
5623             }
5624         });
5625     }
5626 
5627     /**
5628      *  Wrapper method around eventHandler.removeJavaEventNotification()
5629      */
5630     private void removeJavaEventNotification(final long type) {
5631         EventQueue.invokeLater(new Runnable() {
5632             public void run(){
5633                 eventHandler.removeJavaEventNotification(type);
5634             }
5635         });
5636     }
5637 
5638 
5639     /**
5640      *  Wrapper method around eventHandler.addAccessibilityEventNotification()
5641      */
5642     private void addAccessibilityEventNotification(final long type) {
5643         EventQueue.invokeLater(new Runnable() {
5644             public void run(){
5645                 eventHandler.addAccessibilityEventNotification(type);
5646             }
5647         });
5648     }
5649 
5650     /**
5651      *  Wrapper method around eventHandler.removeAccessibilityEventNotification()
5652      */
5653     private void removeAccessibilityEventNotification(final long type) {
5654         EventQueue.invokeLater(new Runnable() {
5655             public void run(){
5656                 eventHandler.removeAccessibilityEventNotification(type);
5657             }
5658         });
5659     }
5660 
5661     /**
5662      ******************************************************
5663      * All AccessibleRoles
5664      *
5665      * We shouldn't have to do this since it requires us
5666      * to synchronize the allAccessibleRoles array when
5667      * the AccessibleRoles class interface changes. However,
5668      * there is no Accessibility API method to get all
5669      * AccessibleRoles
5670      ******************************************************
5671      */
5672     private AccessibleRole [] allAccessibleRoles = {
5673     /**
5674      * Object is used to alert the user about something.
5675      */
5676     AccessibleRole.ALERT,
5677 
5678     /**
5679      * The header for a column of data.
5680      */
5681     AccessibleRole.COLUMN_HEADER,
5682 
5683     /**
5684      * Object that can be drawn into and is used to trap
5685      * events.
5686      * @see #FRAME
5687      * @see #GLASS_PANE
5688      * @see #LAYERED_PANE
5689      */
5690     AccessibleRole.CANVAS,
5691 
5692     /**
5693      * A list of choices the user can select from.  Also optionally
5694      * allows the user to enter a choice of their own.
5695      */
5696     AccessibleRole.COMBO_BOX,
5697 
5698     /**
5699      * An iconified internal frame in a DESKTOP_PANE.
5700      * @see #DESKTOP_PANE
5701      * @see #INTERNAL_FRAME
5702      */
5703     AccessibleRole.DESKTOP_ICON,
5704 
5705     /**
5706      * A frame-like object that is clipped by a desktop pane.  The
5707      * desktop pane, internal frame, and desktop icon objects are
5708      * often used to create multiple document interfaces within an
5709      * application.
5710      * @see #DESKTOP_ICON
5711      * @see #DESKTOP_PANE
5712      * @see #FRAME
5713      */
5714     AccessibleRole.INTERNAL_FRAME,
5715 
5716     /**
5717      * A pane that supports internal frames and
5718      * iconified versions of those internal frames.
5719      * @see #DESKTOP_ICON
5720      * @see #INTERNAL_FRAME
5721      */
5722     AccessibleRole.DESKTOP_PANE,
5723 
5724     /**
5725      * A specialized pane whose primary use is inside a DIALOG
5726      * @see #DIALOG
5727      */
5728     AccessibleRole.OPTION_PANE,
5729 
5730     /**
5731      * A top level window with no title or border.
5732      * @see #FRAME
5733      * @see #DIALOG
5734      */
5735     AccessibleRole.WINDOW,
5736 
5737     /**
5738      * A top level window with a title bar, border, menu bar, etc.  It is
5739      * often used as the primary window for an application.
5740      * @see #DIALOG
5741      * @see #CANVAS
5742      * @see #WINDOW
5743      */
5744     AccessibleRole.FRAME,
5745 
5746     /**
5747      * A top level window with title bar and a border.  A dialog is similar
5748      * to a frame, but it has fewer properties and is often used as a
5749      * secondary window for an application.
5750      * @see #FRAME
5751      * @see #WINDOW
5752      */
5753     AccessibleRole.DIALOG,
5754 
5755     /**
5756      * A specialized dialog that lets the user choose a color.
5757      */
5758     AccessibleRole.COLOR_CHOOSER,
5759 
5760 
5761     /**
5762      * A pane that allows the user to navigate through
5763      * and select the contents of a directory.  May be used
5764      * by a file chooser.
5765      * @see #FILE_CHOOSER
5766      */
5767     AccessibleRole.DIRECTORY_PANE,
5768 
5769     /**
5770      * A specialized dialog that displays the files in the directory
5771      * and lets the user select a file, browse a different directory,
5772      * or specify a filename.  May use the directory pane to show the
5773      * contents of a directory.
5774      * @see #DIRECTORY_PANE
5775      */
5776     AccessibleRole.FILE_CHOOSER,
5777 
5778     /**
5779      * An object that fills up space in a user interface.  It is often
5780      * used in interfaces to tweak the spacing between components,
5781      * but serves no other purpose.
5782      */
5783     AccessibleRole.FILLER,
5784 
5785     /**
5786      * A hypertext anchor
5787      */
5788     // AccessibleRole.HYPERLINK,
5789 
5790     /**
5791      * A small fixed size picture, typically used to decorate components.
5792      */
5793     AccessibleRole.ICON,
5794 
5795     /**
5796      * An object used to present an icon or short string in an interface.
5797      */
5798     AccessibleRole.LABEL,
5799 
5800     /**
5801      * A specialized pane that has a glass pane and a layered pane as its
5802      * children.
5803      * @see #GLASS_PANE
5804      * @see #LAYERED_PANE
5805      */
5806     AccessibleRole.ROOT_PANE,
5807 
5808     /**
5809      * A pane that is guaranteed to be painted on top
5810      * of all panes beneath it.
5811      * @see #ROOT_PANE
5812      * @see #CANVAS
5813      */
5814     AccessibleRole.GLASS_PANE,
5815 
5816     /**
5817      * A specialized pane that allows its children to be drawn in layers,
5818      * providing a form of stacking order.  This is usually the pane that
5819      * holds the menu bar as well as the pane that contains most of the
5820      * visual components in a window.
5821      * @see #GLASS_PANE
5822      * @see #ROOT_PANE
5823      */
5824     AccessibleRole.LAYERED_PANE,
5825 
5826     /**
5827      * An object that presents a list of objects to the user and allows the
5828      * user to select one or more of them.  A list is usually contained
5829      * within a scroll pane.
5830      * @see #SCROLL_PANE
5831      * @see #LIST_ITEM
5832      */
5833     AccessibleRole.LIST,
5834 
5835     /**
5836      * An object that presents an element in a list.  A list is usually
5837      * contained within a scroll pane.
5838      * @see #SCROLL_PANE
5839      * @see #LIST
5840      */
5841     AccessibleRole.LIST_ITEM,
5842 
5843     /**
5844      * An object usually drawn at the top of the primary dialog box of
5845      * an application that contains a list of menus the user can choose
5846      * from.  For example, a menu bar might contain menus for "File,"
5847      * "Edit," and "Help."
5848      * @see #MENU
5849      * @see #POPUP_MENU
5850      * @see #LAYERED_PANE
5851      */
5852     AccessibleRole.MENU_BAR,
5853 
5854     /**
5855      * A temporary window that is usually used to offer the user a
5856      * list of choices, and then hides when the user selects one of
5857      * those choices.
5858      * @see #MENU
5859      * @see #MENU_ITEM
5860      */
5861     AccessibleRole.POPUP_MENU,
5862 
5863     /**
5864      * An object usually found inside a menu bar that contains a list
5865      * of actions the user can choose from.  A menu can have any object
5866      * as its children, but most often they are menu items, other menus,
5867      * or rudimentary objects such as radio buttons, check boxes, or
5868      * separators.  For example, an application may have an "Edit" menu
5869      * that contains menu items for "Cut" and "Paste."
5870      * @see #MENU_BAR
5871      * @see #MENU_ITEM
5872      * @see #SEPARATOR
5873      * @see #RADIO_BUTTON
5874      * @see #CHECK_BOX
5875      * @see #POPUP_MENU
5876      */
5877     AccessibleRole.MENU,
5878 
5879     /**
5880      * An object usually contained in a menu that presents an action
5881      * the user can choose.  For example, the "Cut" menu item in an
5882      * "Edit" menu would be an action the user can select to cut the
5883      * selected area of text in a document.
5884      * @see #MENU_BAR
5885      * @see #SEPARATOR
5886      * @see #POPUP_MENU
5887      */
5888     AccessibleRole.MENU_ITEM,
5889 
5890     /**
5891      * An object usually contained in a menu to provide a visual
5892      * and logical separation of the contents in a menu.  For example,
5893      * the "File" menu of an application might contain menu items for
5894      * "Open," "Close," and "Exit," and will place a separator between
5895      * "Close" and "Exit" menu items.
5896      * @see #MENU
5897      * @see #MENU_ITEM
5898      */
5899     AccessibleRole.SEPARATOR,
5900 
5901     /**
5902      * An object that presents a series of panels (or page tabs), one at a
5903      * time, through some mechanism provided by the object.  The most common
5904      * mechanism is a list of tabs at the top of the panel.  The children of
5905      * a page tab list are all page tabs.
5906      * @see #PAGE_TAB
5907      */
5908     AccessibleRole.PAGE_TAB_LIST,
5909 
5910     /**
5911      * An object that is a child of a page tab list.  Its sole child is
5912      * the panel that is to be presented to the user when the user
5913      * selects the page tab from the list of tabs in the page tab list.
5914      * @see #PAGE_TAB_LIST
5915      */
5916     AccessibleRole.PAGE_TAB,
5917 
5918     /**
5919      * A generic container that is often used to group objects.
5920      */
5921     AccessibleRole.PANEL,
5922 
5923     /**
5924      * An object used to indicate how much of a task has been completed.
5925      */
5926     AccessibleRole.PROGRESS_BAR,
5927 
5928     /**
5929      * A text object used for passwords, or other places where the
5930      * text contents is not shown visibly to the user
5931      */
5932     AccessibleRole.PASSWORD_TEXT,
5933 
5934     /**
5935      * An object the user can manipulate to tell the application to do
5936      * something.
5937      * @see #CHECK_BOX
5938      * @see #TOGGLE_BUTTON
5939      * @see #RADIO_BUTTON
5940      */
5941     AccessibleRole.PUSH_BUTTON,
5942 
5943     /**
5944      * A specialized push button that can be checked or unchecked, but
5945      * does not provide a separate indicator for the current state.
5946      * @see #PUSH_BUTTON
5947      * @see #CHECK_BOX
5948      * @see #RADIO_BUTTON
5949      */
5950     AccessibleRole.TOGGLE_BUTTON,
5951 
5952     /**
5953      * A choice that can be checked or unchecked and provides a
5954      * separate indicator for the current state.
5955      * @see #PUSH_BUTTON
5956      * @see #TOGGLE_BUTTON
5957      * @see #RADIO_BUTTON
5958      */
5959     AccessibleRole.CHECK_BOX,
5960 
5961     /**
5962      * A specialized check box that will cause other radio buttons in the
5963      * same group to become unchecked when this one is checked.
5964      * @see #PUSH_BUTTON
5965      * @see #TOGGLE_BUTTON
5966      * @see #CHECK_BOX
5967      */
5968     AccessibleRole.RADIO_BUTTON,
5969 
5970     /**
5971      * The header for a row of data.
5972      */
5973     AccessibleRole.ROW_HEADER,
5974 
5975     /**
5976      * An object that allows a user to incrementally view a large amount
5977      * of information.  Its children can include scroll bars and a viewport.
5978      * @see #SCROLL_BAR
5979      * @see #VIEWPORT
5980      */
5981     AccessibleRole.SCROLL_PANE,
5982 
5983     /**
5984      * An object usually used to allow a user to incrementally view a
5985      * large amount of data.  Usually used only by a scroll pane.
5986      * @see #SCROLL_PANE
5987      */
5988     AccessibleRole.SCROLL_BAR,
5989 
5990     /**
5991      * An object usually used in a scroll pane.  It represents the portion
5992      * of the entire data that the user can see.  As the user manipulates
5993      * the scroll bars, the contents of the viewport can change.
5994      * @see #SCROLL_PANE
5995      */
5996     AccessibleRole.VIEWPORT,
5997 
5998     /**
5999      * An object that allows the user to select from a bounded range.  For
6000      * example, a slider might be used to select a number between 0 and 100.
6001      */
6002     AccessibleRole.SLIDER,
6003 
6004     /**
6005      * A specialized panel that presents two other panels at the same time.
6006      * Between the two panels is a divider the user can manipulate to make
6007      * one panel larger and the other panel smaller.
6008      */
6009     AccessibleRole.SPLIT_PANE,
6010 
6011     /**
6012      * An object used to present information in terms of rows and columns.
6013      * An example might include a spreadsheet application.
6014      */
6015     AccessibleRole.TABLE,
6016 
6017     /**
6018      * An object that presents text to the user.  The text is usually
6019      * editable by the user as opposed to a label.
6020      * @see #LABEL
6021      */
6022     AccessibleRole.TEXT,
6023 
6024     /**
6025      * An object used to present hierarchical information to the user.
6026      * The individual nodes in the tree can be collapsed and expanded
6027      * to provide selective disclosure of the tree's contents.
6028      */
6029     AccessibleRole.TREE,
6030 
6031     /**
6032      * A bar or palette usually composed of push buttons or toggle buttons.
6033      * It is often used to provide the most frequently used functions for an
6034      * application.
6035      */
6036     AccessibleRole.TOOL_BAR,
6037 
6038     /**
6039      * An object that provides information about another object.  The
6040      * accessibleDescription property of the tool tip is often displayed
6041      * to the user in a small "help bubble" when the user causes the
6042      * mouse to hover over the object associated with the tool tip.
6043      */
6044     AccessibleRole.TOOL_TIP,
6045 
6046     /**
6047      * An AWT component, but nothing else is known about it.
6048      * @see #SWING_COMPONENT
6049      * @see #UNKNOWN
6050      */
6051     AccessibleRole.AWT_COMPONENT,
6052 
6053     /**
6054      * A Swing component, but nothing else is known about it.
6055      * @see #AWT_COMPONENT
6056      * @see #UNKNOWN
6057      */
6058     AccessibleRole.SWING_COMPONENT,
6059 
6060     /**
6061      * The object contains some Accessible information, but its role is
6062      * not known.
6063      * @see #AWT_COMPONENT
6064      * @see #SWING_COMPONENT
6065      */
6066     AccessibleRole.UNKNOWN,
6067 
6068     // These roles are available since JDK 1.4
6069 
6070     /**
6071      * A STATUS_BAR is an simple component that can contain
6072      * multiple labels of status information to the user.
6073      AccessibleRole.STATUS_BAR,
6074 
6075      /**
6076      * A DATE_EDITOR is a component that allows users to edit
6077      * java.util.Date and java.util.Time objects
6078      AccessibleRole.DATE_EDITOR,
6079 
6080      /**
6081      * A SPIN_BOX is a simple spinner component and its main use
6082      * is for simple numbers.
6083      AccessibleRole.SPIN_BOX,
6084 
6085      /**
6086      * A FONT_CHOOSER is a component that lets the user pick various
6087      * attributes for fonts.
6088      AccessibleRole.FONT_CHOOSER,
6089 
6090      /**
6091      * A GROUP_BOX is a simple container that contains a border
6092      * around it and contains components inside it.
6093      AccessibleRole.GROUP_BOX
6094 
6095      /**
6096      * Since JDK 1.5
6097      *
6098      * A text header
6099 
6100      AccessibleRole.HEADER,
6101 
6102      /**
6103      * A text footer
6104 
6105      AccessibleRole.FOOTER,
6106 
6107      /**
6108      * A text paragraph
6109 
6110      AccessibleRole.PARAGRAPH,
6111 
6112      /**
6113      * A ruler is an object used to measure distance
6114 
6115      AccessibleRole.RULER,
6116 
6117      /**
6118      * A role indicating the object acts as a formula for
6119      * calculating a value.  An example is a formula in
6120      * a spreadsheet cell.
6121      AccessibleRole.EDITBAR
6122     */
6123     };
6124 
6125     /**
6126      * This class implements accessibility support for the
6127      * <code>JTree</code> child.  It provides an implementation of the
6128      * Java Accessibility API appropriate to tree nodes.
6129      *
6130      * Copied from JTree.java to work around a JTree bug where
6131      * ActiveDescendent PropertyChangeEvents contain the wrong
6132      * parent.
6133      */
6134     /**
6135      * This class in invoked on the EDT as its part of ActiveDescendant,
6136      * hence the calls do not need to be specifically made on the EDT
6137      */
6138     private class AccessibleJTreeNode extends AccessibleContext
6139         implements Accessible, AccessibleComponent, AccessibleSelection,
6140                    AccessibleAction {
6141 
6142         private JTree tree = null;
6143         private TreeModel treeModel = null;
6144         private Object obj = null;
6145         private TreePath path = null;
6146         private Accessible accessibleParent = null;
6147         private int index = 0;
6148         private boolean isLeaf = false;
6149 
6150         /**
6151          *  Constructs an AccessibleJTreeNode
6152          */
6153         AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) {
6154             tree = t;
6155             path = p;
6156             accessibleParent = ap;
6157             if (t != null)
6158                 treeModel = t.getModel();
6159             if (p != null) {
6160                 obj = p.getLastPathComponent();
6161                 if (treeModel != null && obj != null) {
6162                     isLeaf = treeModel.isLeaf(obj);
6163                 }
6164             }
6165             debugString("[INFO]: AccessibleJTreeNode: name = "+getAccessibleName()+"; TreePath = "+p+"; parent = "+ap);
6166         }
6167 
6168         private TreePath getChildTreePath(int i) {
6169             // Tree nodes can't be so complex that they have
6170             // two sets of children -> we're ignoring that case
6171             if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) {
6172                 return null;
6173             } else {
6174                 Object childObj = treeModel.getChild(obj, i);
6175                 Object[] objPath = path.getPath();
6176                 Object[] objChildPath = new Object[objPath.length+1];
6177                 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
6178                 objChildPath[objChildPath.length-1] = childObj;
6179                 return new TreePath(objChildPath);
6180             }
6181         }
6182 
6183         /**
6184          * Get the AccessibleContext associated with this tree node.
6185          * In the implementation of the Java Accessibility API for
6186          * this class, return this object, which is its own
6187          * AccessibleContext.
6188          *
6189          * @return this object
6190         */
6191         public AccessibleContext getAccessibleContext() {
6192             return this;
6193         }
6194 
6195         private AccessibleContext getCurrentAccessibleContext() {
6196             Component c = getCurrentComponent();
6197             if (c instanceof Accessible) {
6198                return (c.getAccessibleContext());
6199             } else {
6200                 return null;
6201             }
6202         }
6203 
6204         private Component getCurrentComponent() {
6205             debugString("[INFO]: AccessibleJTreeNode: getCurrentComponent");
6206             // is the object visible?
6207             // if so, get row, selected, focus & leaf state,
6208             // and then get the renderer component and return it
6209             if (tree != null && tree.isVisible(path)) {
6210                 TreeCellRenderer r = tree.getCellRenderer();
6211                 if (r == null) {
6212                     debugString("[WARN]:  returning null 1");
6213                     return null;
6214                 }
6215                 TreeUI ui = tree.getUI();
6216                 if (ui != null) {
6217                     int row = ui.getRowForPath(tree, path);
6218                     boolean selected = tree.isPathSelected(path);
6219                     boolean expanded = tree.isExpanded(path);
6220                     boolean hasFocus = false; // how to tell?? -PK
6221                     Component retval = r.getTreeCellRendererComponent(tree, obj,
6222                                                                       selected, expanded,
6223                                                                       isLeaf, row, hasFocus);
6224                     debugString("[INFO]:   returning = "+retval.getClass());
6225                     return retval;
6226                 }
6227             }
6228             debugString("[WARN]:  returning null 2");
6229             return null;
6230         }
6231 
6232         // AccessibleContext methods
6233 
6234         /**
6235          * Get the accessible name of this object.
6236          *
6237          * @return the localized name of the object; null if this
6238          * object does not have a name
6239          */
6240         public String getAccessibleName() {
6241             debugString("[INFO]: AccessibleJTreeNode: getAccessibleName");
6242             AccessibleContext ac = getCurrentAccessibleContext();
6243             if (ac != null) {
6244                 String name = ac.getAccessibleName();
6245                 if ((name != null) && (!name.isEmpty())) {
6246                     String retval = ac.getAccessibleName();
6247                     debugString("[INFO]:     returning "+retval);
6248                     return retval;
6249                 } else {
6250                     return null;
6251                 }
6252             }
6253             if ((accessibleName != null) && (accessibleName.isEmpty())) {
6254                 return accessibleName;
6255             } else {
6256                 return null;
6257             }
6258         }
6259 
6260         /**
6261          * Set the localized accessible name of this object.
6262          *
6263          * @param s the new localized name of the object.
6264          */
6265         public void setAccessibleName(String s) {
6266             AccessibleContext ac = getCurrentAccessibleContext();
6267             if (ac != null) {
6268                 ac.setAccessibleName(s);
6269             } else {
6270                 super.setAccessibleName(s);
6271             }
6272         }
6273 
6274         //
6275         // *** should check tooltip text for desc. (needs MouseEvent)
6276         //
6277         /**
6278          * Get the accessible description of this object.
6279          *
6280          * @return the localized description of the object; null if
6281          * this object does not have a description
6282          */
6283         public String getAccessibleDescription() {
6284             AccessibleContext ac = getCurrentAccessibleContext();
6285             if (ac != null) {
6286                 return ac.getAccessibleDescription();
6287             } else {
6288                 return super.getAccessibleDescription();
6289             }
6290         }
6291 
6292         /**
6293          * Set the accessible description of this object.
6294          *
6295          * @param s the new localized description of the object
6296          */
6297         public void setAccessibleDescription(String s) {
6298             AccessibleContext ac = getCurrentAccessibleContext();
6299             if (ac != null) {
6300                 ac.setAccessibleDescription(s);
6301             } else {
6302                 super.setAccessibleDescription(s);
6303             }
6304         }
6305 
6306         /**
6307          * Get the role of this object.
6308          *
6309          * @return an instance of AccessibleRole describing the role of the object
6310          * @see AccessibleRole
6311          */
6312         public AccessibleRole getAccessibleRole() {
6313             AccessibleContext ac = getCurrentAccessibleContext();
6314             if (ac != null) {
6315                 return ac.getAccessibleRole();
6316             } else {
6317                 return AccessibleRole.UNKNOWN;
6318             }
6319         }
6320 
6321         /**
6322          * Get the state set of this object.
6323          *
6324          * @return an instance of AccessibleStateSet containing the
6325          * current state set of the object
6326          * @see AccessibleState
6327          */
6328         public AccessibleStateSet getAccessibleStateSet() {
6329             if (tree == null)
6330                 return null;
6331             AccessibleContext ac = getCurrentAccessibleContext();
6332             AccessibleStateSet states;
6333             int row = tree.getUI().getRowForPath(tree,path);
6334             int lsr = tree.getLeadSelectionRow();
6335             if (ac != null) {
6336                 states = ac.getAccessibleStateSet();
6337             } else {
6338                 states = new AccessibleStateSet();
6339             }
6340             // need to test here, 'cause the underlying component
6341             // is a cellRenderer, which is never showing...
6342             if (isShowing()) {
6343                 states.add(AccessibleState.SHOWING);
6344             } else if (states.contains(AccessibleState.SHOWING)) {
6345                 states.remove(AccessibleState.SHOWING);
6346             }
6347             if (isVisible()) {
6348                 states.add(AccessibleState.VISIBLE);
6349             } else if (states.contains(AccessibleState.VISIBLE)) {
6350                 states.remove(AccessibleState.VISIBLE);
6351             }
6352             if (tree.isPathSelected(path)){
6353                 states.add(AccessibleState.SELECTED);
6354             }
6355             if (lsr == row) {
6356                 states.add(AccessibleState.ACTIVE);
6357             }
6358             if (!isLeaf) {
6359                 states.add(AccessibleState.EXPANDABLE);
6360             }
6361             if (tree.isExpanded(path)) {
6362                 states.add(AccessibleState.EXPANDED);
6363             } else {
6364                 states.add(AccessibleState.COLLAPSED);
6365             }
6366             if (tree.isEditable()) {
6367                 states.add(AccessibleState.EDITABLE);
6368             }
6369             return states;
6370         }
6371 
6372         /**
6373          * Get the Accessible parent of this object.
6374          *
6375          * @return the Accessible parent of this object; null if this
6376          * object does not have an Accessible parent
6377          */
6378         public Accessible getAccessibleParent() {
6379             // someone wants to know, so we need to create our parent
6380             // if we don't have one (hey, we're a talented kid!)
6381             if (accessibleParent == null && path != null) {
6382                 Object[] objPath = path.getPath();
6383                 if (objPath.length > 1) {
6384                     Object objParent = objPath[objPath.length-2];
6385                     if (treeModel != null) {
6386                         index = treeModel.getIndexOfChild(objParent, obj);
6387                     }
6388                     Object[] objParentPath = new Object[objPath.length-1];
6389                     java.lang.System.arraycopy(objPath, 0, objParentPath,
6390                                                0, objPath.length-1);
6391                     TreePath parentPath = new TreePath(objParentPath);
6392                     accessibleParent = new AccessibleJTreeNode(tree,
6393                                                                parentPath,
6394                                                                null);
6395                     this.setAccessibleParent(accessibleParent);
6396                 } else if (treeModel != null) {
6397                     accessibleParent = tree; // we're the top!
6398                     index = 0; // we're an only child!
6399                     this.setAccessibleParent(accessibleParent);
6400                 }
6401             }
6402             return accessibleParent;
6403         }
6404 
6405         /**
6406          * Get the index of this object in its accessible parent.
6407          *
6408          * @return the index of this object in its parent; -1 if this
6409          * object does not have an accessible parent.
6410          * @see #getAccessibleParent
6411          */
6412         public int getAccessibleIndexInParent() {
6413             // index is invalid 'till we have an accessibleParent...
6414             if (accessibleParent == null) {
6415                 getAccessibleParent();
6416             }
6417             if (path != null) {
6418                 Object[] objPath = path.getPath();
6419                 if (objPath.length > 1) {
6420                     Object objParent = objPath[objPath.length-2];
6421                     if (treeModel != null) {
6422                         index = treeModel.getIndexOfChild(objParent, obj);
6423                     }
6424                 }
6425             }
6426             return index;
6427         }
6428 
6429         /**
6430          * Returns the number of accessible children in the object.
6431          *
6432          * @return the number of accessible children in the object.
6433          */
6434         public int getAccessibleChildrenCount() {
6435             // Tree nodes can't be so complex that they have
6436             // two sets of children -> we're ignoring that case
6437             if (obj != null && treeModel != null) {
6438                 return treeModel.getChildCount(obj);
6439             }
6440             return 0;
6441         }
6442 
6443         /**
6444          * Return the specified Accessible child of the object.
6445          *
6446          * @param i zero-based index of child
6447          * @return the Accessible child of the object
6448          */
6449         public Accessible getAccessibleChild(int i) {
6450             // Tree nodes can't be so complex that they have
6451             // two sets of children -> we're ignoring that case
6452             if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) {
6453                 return null;
6454             } else {
6455                 Object childObj = treeModel.getChild(obj, i);
6456                 Object[] objPath = path.getPath();
6457                 Object[] objChildPath = new Object[objPath.length+1];
6458                 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
6459                 objChildPath[objChildPath.length-1] = childObj;
6460                 TreePath childPath = new TreePath(objChildPath);
6461                 return new AccessibleJTreeNode(tree, childPath, this);
6462             }
6463         }
6464 
6465         /**
6466          * Gets the locale of the component. If the component does not have
6467          * a locale, then the locale of its parent is returned.
6468          *
6469          * @return This component's locale. If this component does not have
6470          * a locale, the locale of its parent is returned.
6471          * @exception IllegalComponentStateException
6472          * If the Component does not have its own locale and has not yet
6473          * been added to a containment hierarchy such that the locale can be
6474          * determined from the containing parent.
6475          * @see #setLocale
6476          */
6477         public Locale getLocale() {
6478             if (tree == null)
6479                 return null;
6480             AccessibleContext ac = getCurrentAccessibleContext();
6481             if (ac != null) {
6482                 return ac.getLocale();
6483             } else {
6484                 return tree.getLocale();
6485             }
6486         }
6487 
6488         /**
6489          * Add a PropertyChangeListener to the listener list.
6490          * The listener is registered for all properties.
6491          *
6492          * @param l  The PropertyChangeListener to be added
6493          */
6494         public void addPropertyChangeListener(PropertyChangeListener l) {
6495             AccessibleContext ac = getCurrentAccessibleContext();
6496             if (ac != null) {
6497                 ac.addPropertyChangeListener(l);
6498             } else {
6499                 super.addPropertyChangeListener(l);
6500             }
6501         }
6502 
6503         /**
6504          * Remove a PropertyChangeListener from the listener list.
6505          * This removes a PropertyChangeListener that was registered
6506          * for all properties.
6507          *
6508          * @param l  The PropertyChangeListener to be removed
6509          */
6510         public void removePropertyChangeListener(PropertyChangeListener l) {
6511             AccessibleContext ac = getCurrentAccessibleContext();
6512             if (ac != null) {
6513                 ac.removePropertyChangeListener(l);
6514             } else {
6515                 super.removePropertyChangeListener(l);
6516             }
6517         }
6518 
6519         /**
6520          * Get the AccessibleAction associated with this object.  In the
6521          * implementation of the Java Accessibility API for this class,
6522          * return this object, which is responsible for implementing the
6523          * AccessibleAction interface on behalf of itself.
6524          *
6525          * @return this object
6526          */
6527         public AccessibleAction getAccessibleAction() {
6528             return this;
6529         }
6530 
6531         /**
6532          * Get the AccessibleComponent associated with this object.  In the
6533          * implementation of the Java Accessibility API for this class,
6534          * return this object, which is responsible for implementing the
6535          * AccessibleComponent interface on behalf of itself.
6536          *
6537          * @return this object
6538          */
6539         public AccessibleComponent getAccessibleComponent() {
6540             return this; // to override getBounds()
6541         }
6542 
6543         /**
6544          * Get the AccessibleSelection associated with this object if one
6545          * exists.  Otherwise return null.
6546          *
6547          * @return the AccessibleSelection, or null
6548          */
6549         public AccessibleSelection getAccessibleSelection() {
6550             AccessibleContext ac = getCurrentAccessibleContext();
6551             if (ac != null && isLeaf) {
6552                 return getCurrentAccessibleContext().getAccessibleSelection();
6553             } else {
6554                 return this;
6555             }
6556         }
6557 
6558         /**
6559          * Get the AccessibleText associated with this object if one
6560          * exists.  Otherwise return null.
6561          *
6562          * @return the AccessibleText, or null
6563          */
6564         public AccessibleText getAccessibleText() {
6565             AccessibleContext ac = getCurrentAccessibleContext();
6566             if (ac != null) {
6567                 return getCurrentAccessibleContext().getAccessibleText();
6568             } else {
6569                 return null;
6570             }
6571         }
6572 
6573         /**
6574          * Get the AccessibleValue associated with this object if one
6575          * exists.  Otherwise return null.
6576          *
6577          * @return the AccessibleValue, or null
6578          */
6579         public AccessibleValue getAccessibleValue() {
6580             AccessibleContext ac = getCurrentAccessibleContext();
6581             if (ac != null) {
6582                 return getCurrentAccessibleContext().getAccessibleValue();
6583             } else {
6584                 return null;
6585             }
6586         }
6587 
6588 
6589             // AccessibleComponent methods
6590 
6591         /**
6592          * Get the background color of this object.
6593          *
6594          * @return the background color, if supported, of the object;
6595          * otherwise, null
6596          */
6597         public Color getBackground() {
6598             AccessibleContext ac = getCurrentAccessibleContext();
6599             if (ac instanceof AccessibleComponent) {
6600                 return ((AccessibleComponent) ac).getBackground();
6601             } else {
6602                 Component c = getCurrentComponent();
6603                 if (c != null) {
6604                     return c.getBackground();
6605                 } else {
6606                     return null;
6607                 }
6608             }
6609         }
6610 
6611         /**
6612          * Set the background color of this object.
6613          *
6614          * @param c the new Color for the background
6615          */
6616         public void setBackground(Color c) {
6617             AccessibleContext ac = getCurrentAccessibleContext();
6618             if (ac instanceof AccessibleComponent) {
6619                 ((AccessibleComponent) ac).setBackground(c);
6620             } else {
6621                 Component cp = getCurrentComponent();
6622                 if (    cp != null) {
6623                     cp.setBackground(c);
6624                 }
6625             }
6626         }
6627 
6628 
6629         /**
6630          * Get the foreground color of this object.
6631          *
6632          * @return the foreground color, if supported, of the object;
6633          * otherwise, null
6634          */
6635         public Color getForeground() {
6636             AccessibleContext ac = getCurrentAccessibleContext();
6637             if (ac instanceof AccessibleComponent) {
6638                 return ((AccessibleComponent) ac).getForeground();
6639             } else {
6640                 Component c = getCurrentComponent();
6641                 if (c != null) {
6642                     return c.getForeground();
6643                 } else {
6644                     return null;
6645                 }
6646             }
6647         }
6648 
6649         public void setForeground(Color c) {
6650             AccessibleContext ac = getCurrentAccessibleContext();
6651             if (ac instanceof AccessibleComponent) {
6652                 ((AccessibleComponent) ac).setForeground(c);
6653             } else {
6654                 Component cp = getCurrentComponent();
6655                 if (cp != null) {
6656                     cp.setForeground(c);
6657                 }
6658             }
6659         }
6660 
6661         public Cursor getCursor() {
6662             AccessibleContext ac = getCurrentAccessibleContext();
6663             if (ac instanceof AccessibleComponent) {
6664                 return ((AccessibleComponent) ac).getCursor();
6665             } else {
6666                 Component c = getCurrentComponent();
6667                 if (c != null) {
6668                     return c.getCursor();
6669                 } else {
6670                     Accessible ap = getAccessibleParent();
6671                     if (ap instanceof AccessibleComponent) {
6672                         return ((AccessibleComponent) ap).getCursor();
6673                     } else {
6674                         return null;
6675                     }
6676                 }
6677             }
6678         }
6679 
6680         public void setCursor(Cursor c) {
6681             AccessibleContext ac = getCurrentAccessibleContext();
6682             if (ac instanceof AccessibleComponent) {
6683                 ((AccessibleComponent) ac).setCursor(c);
6684             } else {
6685                 Component cp = getCurrentComponent();
6686                 if (cp != null) {
6687                     cp.setCursor(c);
6688                 }
6689             }
6690         }
6691 
6692         public Font getFont() {
6693             AccessibleContext ac = getCurrentAccessibleContext();
6694             if (ac instanceof AccessibleComponent) {
6695                 return ((AccessibleComponent) ac).getFont();
6696             } else {
6697                 Component c = getCurrentComponent();
6698                 if (c != null) {
6699                     return c.getFont();
6700                 } else {
6701                     return null;
6702                 }
6703             }
6704         }
6705 
6706         public void setFont(Font f) {
6707             AccessibleContext ac = getCurrentAccessibleContext();
6708             if (ac instanceof AccessibleComponent) {
6709                 ((AccessibleComponent) ac).setFont(f);
6710             } else {
6711                 Component c = getCurrentComponent();
6712                 if (c != null) {
6713                     c.setFont(f);
6714                 }
6715             }
6716         }
6717 
6718         public FontMetrics getFontMetrics(Font f) {
6719             AccessibleContext ac = getCurrentAccessibleContext();
6720             if (ac instanceof AccessibleComponent) {
6721                 return ((AccessibleComponent) ac).getFontMetrics(f);
6722             } else {
6723                 Component c = getCurrentComponent();
6724                 if (c != null) {
6725                     return c.getFontMetrics(f);
6726                 } else {
6727                     return null;
6728                 }
6729             }
6730         }
6731 
6732         public boolean isEnabled() {
6733             AccessibleContext ac = getCurrentAccessibleContext();
6734             if (ac instanceof AccessibleComponent) {
6735                 return ((AccessibleComponent) ac).isEnabled();
6736             } else {
6737                 Component c = getCurrentComponent();
6738                 if (c != null) {
6739                     return c.isEnabled();
6740                 } else {
6741                     return false;
6742                 }
6743             }
6744         }
6745 
6746         public void setEnabled(boolean b) {
6747             AccessibleContext ac = getCurrentAccessibleContext();
6748             if (ac instanceof AccessibleComponent) {
6749                 ((AccessibleComponent) ac).setEnabled(b);
6750             } else {
6751                 Component c = getCurrentComponent();
6752                 if (c != null) {
6753                     c.setEnabled(b);
6754                 }
6755             }
6756         }
6757 
6758         public boolean isVisible() {
6759             if (tree == null)
6760                 return false;
6761             Rectangle pathBounds = tree.getPathBounds(path);
6762             Rectangle parentBounds = tree.getVisibleRect();
6763             if ( pathBounds != null && parentBounds != null &&
6764                  parentBounds.intersects(pathBounds) ) {
6765                 return true;
6766             } else {
6767                 return false;
6768             }
6769         }
6770 
6771         public void setVisible(boolean b) {
6772         }
6773 
6774         public boolean isShowing() {
6775             return (tree.isShowing() && isVisible());
6776         }
6777 
6778         public boolean contains(Point p) {
6779             AccessibleContext ac = getCurrentAccessibleContext();
6780             if (ac instanceof AccessibleComponent) {
6781                 Rectangle r = ((AccessibleComponent) ac).getBounds();
6782                 return r.contains(p);
6783             } else {
6784                 Component c = getCurrentComponent();
6785                 if (c != null) {
6786                     Rectangle r = c.getBounds();
6787                     return r.contains(p);
6788                 } else {
6789                     return getBounds().contains(p);
6790                 }
6791             }
6792         }
6793 
6794         public Point getLocationOnScreen() {
6795             if (tree != null) {
6796                 Point treeLocation = tree.getLocationOnScreen();
6797                 Rectangle pathBounds = tree.getPathBounds(path);
6798                 if (treeLocation != null && pathBounds != null) {
6799                     Point nodeLocation = new Point(pathBounds.x,
6800                                                    pathBounds.y);
6801                     nodeLocation.translate(treeLocation.x, treeLocation.y);
6802                     return nodeLocation;
6803                 } else {
6804                     return null;
6805                 }
6806             } else {
6807                 return null;
6808             }
6809         }
6810 
6811         private Point getLocationInJTree() {
6812             Rectangle r = tree.getPathBounds(path);
6813             if (r != null) {
6814                 return r.getLocation();
6815             } else {
6816                 return null;
6817             }
6818         }
6819 
6820         public Point getLocation() {
6821             Rectangle r = getBounds();
6822             if (r != null) {
6823                 return r.getLocation();
6824             } else {
6825                 return null;
6826             }
6827         }
6828 
6829         public void setLocation(Point p) {
6830         }
6831 
6832         public Rectangle getBounds() {
6833             if (tree == null)
6834                 return null;
6835             Rectangle r = tree.getPathBounds(path);
6836             Accessible parent = getAccessibleParent();
6837             if (parent instanceof AccessibleJTreeNode) {
6838                 Point parentLoc = ((AccessibleJTreeNode) parent).getLocationInJTree();
6839                 if (parentLoc != null && r != null) {
6840                     r.translate(-parentLoc.x, -parentLoc.y);
6841                 } else {
6842                     return null;        // not visible!
6843                 }
6844             }
6845             return r;
6846         }
6847 
6848         public void setBounds(Rectangle r) {
6849             AccessibleContext ac = getCurrentAccessibleContext();
6850             if (ac instanceof AccessibleComponent) {
6851                 ((AccessibleComponent) ac).setBounds(r);
6852             } else {
6853                 Component c = getCurrentComponent();
6854                 if (c != null) {
6855                     c.setBounds(r);
6856                 }
6857             }
6858         }
6859 
6860         public Dimension getSize() {
6861             return getBounds().getSize();
6862         }
6863 
6864         public void setSize (Dimension d) {
6865             AccessibleContext ac = getCurrentAccessibleContext();
6866             if (ac instanceof AccessibleComponent) {
6867                 ((AccessibleComponent) ac).setSize(d);
6868             } else {
6869                 Component c = getCurrentComponent();
6870                 if (c != null) {
6871                     c.setSize(d);
6872                 }
6873             }
6874         }
6875 
6876         /**
6877         * Returns the <code>Accessible</code> child, if one exists,
6878         * contained at the local coordinate <code>Point</code>.
6879         * Otherwise returns <code>null</code>.
6880         *
6881         * @param p point in local coordinates of this
6882         *    <code>Accessible</code>
6883         * @return the <code>Accessible</code>, if it exists,
6884         *    at the specified location; else <code>null</code>
6885         */
6886         public Accessible getAccessibleAt(Point p) {
6887             AccessibleContext ac = getCurrentAccessibleContext();
6888             if (ac instanceof AccessibleComponent) {
6889                 return ((AccessibleComponent) ac).getAccessibleAt(p);
6890             } else {
6891                 return null;
6892             }
6893         }
6894 
6895         public boolean isFocusTraversable() {
6896             AccessibleContext ac = getCurrentAccessibleContext();
6897             if (ac instanceof AccessibleComponent) {
6898                 return ((AccessibleComponent) ac).isFocusTraversable();
6899             } else {
6900                 Component c = getCurrentComponent();
6901                 if (c != null) {
6902                     return c.isFocusable();
6903                 } else {
6904                     return false;
6905                 }
6906             }
6907         }
6908 
6909         public void requestFocus() {
6910             AccessibleContext ac = getCurrentAccessibleContext();
6911             if (ac instanceof AccessibleComponent) {
6912                 ((AccessibleComponent) ac).requestFocus();
6913             } else {
6914                 Component c = getCurrentComponent();
6915                 if (c != null) {
6916                     c.requestFocus();
6917                 }
6918             }
6919         }
6920 
6921         public void addFocusListener(FocusListener l) {
6922             AccessibleContext ac = getCurrentAccessibleContext();
6923             if (ac instanceof AccessibleComponent) {
6924                 ((AccessibleComponent) ac).addFocusListener(l);
6925             } else {
6926                 Component c = getCurrentComponent();
6927                 if (c != null) {
6928                     c.addFocusListener(l);
6929                 }
6930             }
6931         }
6932 
6933         public void removeFocusListener(FocusListener l) {
6934             AccessibleContext ac = getCurrentAccessibleContext();
6935             if (ac instanceof AccessibleComponent) {
6936                 ((AccessibleComponent) ac).removeFocusListener(l);
6937             } else {
6938                 Component c = getCurrentComponent();
6939                 if (c != null) {
6940                     c.removeFocusListener(l);
6941                 }
6942             }
6943         }
6944 
6945             // AccessibleSelection methods
6946 
6947         /**
6948          * Returns the number of items currently selected.
6949          * If no items are selected, the return value will be 0.
6950          *
6951          * @return the number of items currently selected.
6952          */
6953         public int getAccessibleSelectionCount() {
6954             int count = 0;
6955             int childCount = getAccessibleChildrenCount();
6956             for (int i = 0; i < childCount; i++) {
6957                 TreePath childPath = getChildTreePath(i);
6958                 if (tree.isPathSelected(childPath)) {
6959                     count++;
6960                 }
6961             }
6962             return count;
6963         }
6964 
6965         /**
6966          * Returns an Accessible representing the specified selected item
6967          * in the object.  If there isn't a selection, or there are
6968          * fewer items selected than the integer passed in, the return
6969          * value will be null.
6970          *
6971          * @param i the zero-based index of selected items
6972          * @return an Accessible containing the selected item
6973          */
6974         public Accessible getAccessibleSelection(int i) {
6975             int childCount = getAccessibleChildrenCount();
6976             if (i < 0 || i >= childCount) {
6977                 return null;        // out of range
6978             }
6979             int count = 0;
6980             for (int j = 0; j < childCount && i >= count; j++) {
6981                 TreePath childPath = getChildTreePath(j);
6982                 if (tree.isPathSelected(childPath)) {
6983                     if (count == i) {
6984                         return new AccessibleJTreeNode(tree, childPath, this);
6985                     } else {
6986                         count++;
6987                     }
6988                 }
6989             }
6990             return null;
6991         }
6992 
6993         /**
6994          * Returns true if the current child of this object is selected.
6995          *
6996          * @param i the zero-based index of the child in this Accessible
6997          * object.
6998          * @see AccessibleContext#getAccessibleChild
6999          */
7000         public boolean isAccessibleChildSelected(int i) {
7001             int childCount = getAccessibleChildrenCount();
7002             if (i < 0 || i >= childCount) {
7003                 return false;       // out of range
7004             } else {
7005                 TreePath childPath = getChildTreePath(i);
7006                 return tree.isPathSelected(childPath);
7007             }
7008         }
7009 
7010          /**
7011          * Adds the specified selected item in the object to the object's
7012          * selection.  If the object supports multiple selections,
7013          * the specified item is added to any existing selection, otherwise
7014          * it replaces any existing selection in the object.  If the
7015          * specified item is already selected, this method has no effect.
7016          *
7017          * @param i the zero-based index of selectable items
7018          */
7019         public void addAccessibleSelection(int i) {
7020             if (tree == null)
7021                 return;
7022             TreeModel model = tree.getModel();
7023             if (model != null) {
7024                 if (i >= 0 && i < getAccessibleChildrenCount()) {
7025                     TreePath path = getChildTreePath(i);
7026                     tree.addSelectionPath(path);
7027                 }
7028             }
7029         }
7030 
7031         /**
7032          * Removes the specified selected item in the object from the
7033          * object's
7034          * selection.  If the specified item isn't currently selected, this
7035          * method has no effect.
7036          *
7037          * @param i the zero-based index of selectable items
7038          */
7039         public void removeAccessibleSelection(int i) {
7040             if (tree == null)
7041                 return;
7042             TreeModel model = tree.getModel();
7043             if (model != null) {
7044                 if (i >= 0 && i < getAccessibleChildrenCount()) {
7045                     TreePath path = getChildTreePath(i);
7046                     tree.removeSelectionPath(path);
7047                 }
7048             }
7049         }
7050 
7051         /**
7052          * Clears the selection in the object, so that nothing in the
7053          * object is selected.
7054          */
7055         public void clearAccessibleSelection() {
7056             int childCount = getAccessibleChildrenCount();
7057             for (int i = 0; i < childCount; i++) {
7058                 removeAccessibleSelection(i);
7059             }
7060         }
7061 
7062         /**
7063          * Causes every selected item in the object to be selected
7064          * if the object supports multiple selections.
7065          */
7066         public void selectAllAccessibleSelection() {
7067             if (tree == null)
7068                 return;
7069             TreeModel model = tree.getModel();
7070             if (model != null) {
7071                 int childCount = getAccessibleChildrenCount();
7072                 TreePath path;
7073                 for (int i = 0; i < childCount; i++) {
7074                     path = getChildTreePath(i);
7075                     tree.addSelectionPath(path);
7076                 }
7077             }
7078         }
7079 
7080             // AccessibleAction methods
7081 
7082         /**
7083          * Returns the number of accessible actions available in this
7084          * tree node.  If this node is not a leaf, there is at least
7085          * one action (toggle expand), in addition to any available
7086          * on the object behind the TreeCellRenderer.
7087          *
7088          * @return the number of Actions in this object
7089          */
7090         public int getAccessibleActionCount() {
7091             AccessibleContext ac = getCurrentAccessibleContext();
7092             if (ac != null) {
7093                 AccessibleAction aa = ac.getAccessibleAction();
7094                 if (aa != null) {
7095                     return (aa.getAccessibleActionCount() + (isLeaf ? 0 : 1));
7096                 }
7097             }
7098             return isLeaf ? 0 : 1;
7099         }
7100 
7101         /**
7102          * Return a description of the specified action of the tree node.
7103          * If this node is not a leaf, there is at least one action
7104          * description (toggle expand), in addition to any available
7105          * on the object behind the TreeCellRenderer.
7106          *
7107          * @param i zero-based index of the actions
7108          * @return a description of the action
7109          */
7110         public String getAccessibleActionDescription(int i) {
7111             if (i < 0 || i >= getAccessibleActionCount()) {
7112                 return null;
7113             }
7114             AccessibleContext ac = getCurrentAccessibleContext();
7115             if (i == 0) {
7116                 // TIGER - 4766636
7117                 // return AccessibleAction.TOGGLE_EXPAND;
7118                 return "toggle expand";
7119             } else if (ac != null) {
7120                 AccessibleAction aa = ac.getAccessibleAction();
7121                 if (aa != null) {
7122                     return aa.getAccessibleActionDescription(i - 1);
7123                 }
7124             }
7125             return null;
7126         }
7127 
7128         /**
7129          * Perform the specified Action on the tree node.  If this node
7130          * is not a leaf, there is at least one action which can be
7131          * done (toggle expand), in addition to any available on the
7132          * object behind the TreeCellRenderer.
7133          *
7134          * @param i zero-based index of actions
7135          * @return true if the the action was performed; else false.
7136          */
7137         public boolean doAccessibleAction(int i) {
7138             if (i < 0 || i >= getAccessibleActionCount()) {
7139                 return false;
7140             }
7141             AccessibleContext ac = getCurrentAccessibleContext();
7142             if (i == 0) {
7143                 if (tree.isExpanded(path)) {
7144                     tree.collapsePath(path);
7145                 } else {
7146                     tree.expandPath(path);
7147                 }
7148                 return true;
7149             } else if (ac != null) {
7150                 AccessibleAction aa = ac.getAccessibleAction();
7151                 if (aa != null) {
7152                     return aa.doAccessibleAction(i - 1);
7153                 }
7154             }
7155             return false;
7156         }
7157 
7158     } // inner class AccessibleJTreeNode
7159 
7160     /**
7161      * A helper class to perform {@code Callable} objects on the event dispatch thread appropriate
7162      * for the provided {@code AccessibleContext}.
7163      */
7164     private static class InvocationUtils {
7165 
7166         /**
7167          * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible}
7168          * and waits for it to finish blocking the caller thread.
7169          *
7170          * @param callable   the {@code Callable} to invoke
7171          * @param accessibleTable the {@code AccessibleExtendedTable} which would be used to find the right context
7172          *                   for the task execution
7173          * @param <T> type parameter for the result value
7174          *
7175          * @return the result of the {@code Callable} execution
7176          */
7177         public static <T> T invokeAndWait(final Callable<T> callable,
7178                                           final AccessibleExtendedTable accessibleTable) {
7179             if (accessibleTable instanceof AccessibleContext) {
7180                 return invokeAndWait(callable, (AccessibleContext)accessibleTable);
7181             }
7182             throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleTable);
7183         }
7184 
7185         /**
7186          * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible}
7187          * and waits for it to finish blocking the caller thread.
7188          *
7189          * @param callable   the {@code Callable} to invoke
7190          * @param accessible the {@code Accessible} which would be used to find the right context
7191          *                   for the task execution
7192          * @param <T> type parameter for the result value
7193          *
7194          * @return the result of the {@code Callable} execution
7195          */
7196         public static <T> T invokeAndWait(final Callable<T> callable,
7197                                           final Accessible accessible) {
7198             if (accessible instanceof Component) {
7199                 return invokeAndWait(callable, (Component)accessible);
7200             }
7201             if (accessible instanceof AccessibleContext) {
7202                 // This case also covers the Translator
7203                 return invokeAndWait(callable, (AccessibleContext)accessible);
7204             }
7205             throw new RuntimeException("Unmapped Accessible used to dispatch event: " + accessible);
7206         }
7207 
7208         /**
7209          * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Component}
7210          * and waits for it to finish blocking the caller thread.
7211          *
7212          * @param callable  the {@code Callable} to invoke
7213          * @param component the {@code Component} which would be used to find the right context
7214          *                  for the task execution
7215          * @param <T> type parameter for the result value
7216          *
7217          * @return the result of the {@code Callable} execution
7218          */
7219         public static <T> T invokeAndWait(final Callable<T> callable,
7220                                           final Component component) {
7221             return invokeAndWait(callable, SunToolkit.targetToAppContext(component));
7222         }
7223 
7224         /**
7225          * Invokes a {@code Callable} in the {@code AppContext} mapped to the given {@code AccessibleContext}
7226          * and waits for it to finish blocking the caller thread.
7227          *
7228          * @param callable the {@code Callable} to invoke
7229          * @param accessibleContext the {@code AccessibleContext} which would be used to determine the right
7230          *                          context for the task execution.
7231          * @param <T> type parameter for the result value
7232          *
7233          * @return the result of the {@code Callable} execution
7234          */
7235         public static <T> T invokeAndWait(final Callable<T> callable,
7236                                           final AccessibleContext accessibleContext) {
7237             AppContext targetContext = AWTAccessor.getAccessibleContextAccessor()
7238                     .getAppContext(accessibleContext);
7239             if (targetContext != null) {
7240                 return invokeAndWait(callable, targetContext);
7241             } else {
7242                 // Normally this should not happen, unmapped context provided and
7243                 // the target AppContext is unknown.
7244 
7245                 // Try to recover in case the context is a translator.
7246                 if (accessibleContext instanceof Translator) {
7247                     Object source = ((Translator)accessibleContext).getSource();
7248                     if (source instanceof Component) {
7249                         return invokeAndWait(callable, (Component)source);
7250                     }
7251                 }
7252             }
7253             throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleContext);
7254         }
7255 
7256         private static <T> T invokeAndWait(final Callable<T> callable,
7257                                            final AppContext targetAppContext) {
7258             final CallableWrapper<T> wrapper = new CallableWrapper<T>(callable);
7259             try {
7260                 invokeAndWait(wrapper, targetAppContext);
7261                 T result = wrapper.getResult();
7262                 updateAppContextMap(result, targetAppContext);
7263                 return result;
7264             } catch (final Exception e) {
7265                 throw new RuntimeException(e);
7266             }
7267         }
7268 
7269         private static void invokeAndWait(final Runnable runnable,
7270                                         final AppContext appContext)
7271                 throws InterruptedException, InvocationTargetException {
7272 
7273             EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext);
7274             Object lock = new Object();
7275             Toolkit source = Toolkit.getDefaultToolkit();
7276             InvocationEvent event =
7277                     new InvocationEvent(source, runnable, lock, true);
7278             synchronized (lock) {
7279                 eq.postEvent(event);
7280                 lock.wait();
7281             }
7282 
7283             Throwable eventThrowable = event.getThrowable();
7284             if (eventThrowable != null) {
7285                 throw new InvocationTargetException(eventThrowable);
7286             }
7287         }
7288 
7289         /**
7290          * Maps the {@code AccessibleContext} to the {@code AppContext} which should be used
7291          * to dispatch events related to the {@code AccessibleContext}
7292          * @param accessibleContext the {@code AccessibleContext} for the mapping
7293          * @param targetContext the {@code AppContext} for the mapping
7294          */
7295         public static void registerAccessibleContext(final AccessibleContext accessibleContext,
7296                                                      final AppContext targetContext) {
7297             if (accessibleContext != null) {
7298                 AWTAccessor.getAccessibleContextAccessor().setAppContext(accessibleContext, targetContext);
7299             }
7300         }
7301 
7302         private static <T> void updateAppContextMap(final T accessibleContext,
7303                                                     final AppContext targetContext) {
7304             if (accessibleContext instanceof AccessibleContext) {
7305                 registerAccessibleContext((AccessibleContext)accessibleContext, targetContext);
7306             }
7307         }
7308 
7309         private static class CallableWrapper<T> implements Runnable {
7310             private final Callable<T> callable;
7311             private volatile T object;
7312             private Exception e;
7313 
7314             CallableWrapper(final Callable<T> callable) {
7315                 this.callable = callable;
7316             }
7317 
7318             public void run() {
7319                 try {
7320                     if (callable != null) {
7321                         object = callable.call();
7322                     }
7323                 } catch (final Exception e) {
7324                     this.e = e;
7325                 }
7326             }
7327 
7328             T getResult() throws Exception {
7329                 if (e != null)
7330                     throw e;
7331                 return object;
7332             }
7333         }
7334     }
7335 }