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