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