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