1 /*
   2  * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.java.accessibility.internal;
  27 
  28 import java.awt.*;
  29 import java.awt.event.*;
  30 import java.util.*;
  31 import java.lang.*;
  32 import java.lang.reflect.*;
  33 
  34 import java.beans.*;
  35 import javax.swing.*;
  36 import javax.swing.event.*;
  37 import javax.swing.text.*;
  38 import javax.swing.tree.*;
  39 import javax.swing.table.*;
  40 import javax.swing.plaf.TreeUI;
  41 
  42 import javax.accessibility.*;
  43 import com.sun.java.accessibility.util.*;
  44 import java.awt.geom.Rectangle2D;
  45 import sun.awt.AWTAccessor;
  46 import sun.awt.AppContext;
  47 import sun.awt.SunToolkit;
  48 
  49 import java.util.concurrent.Callable;
  50 import java.util.concurrent.ConcurrentHashMap;
  51 
  52 /*
  53  * Note: This class has to be public.  It's loaded from the VM like this:
  54  *       Class.forName(atName).newInstance();
  55  */
  56 final public class AccessBridge {
  57 
  58     private static AccessBridge theAccessBridge;
  59     private ObjectReferences references;
  60     private EventHandler eventHandler;
  61 
  62     // Maps AccessibleRoles strings to AccessibleRoles.
  63     private ConcurrentHashMap<String,AccessibleRole> accessibleRoleMap = new ConcurrentHashMap<>();
  64 
  65     /**
  66        If the object's role is in the following array getVirtualAccessibleName
  67        will use the extended search algorithm.
  68     */
  69     private ArrayList<AccessibleRole> extendedVirtualNameSearchRoles = new ArrayList<>();
  70     /**
  71        If the role of the object's parent is in the following array
  72        getVirtualAccessibleName will NOT use the extended search
  73        algorithm even if the object's role is in the
  74        extendedVirtualNameSearchRoles array.
  75     */
  76     private ArrayList<AccessibleRole> noExtendedVirtualNameSearchParentRoles = new ArrayList<>();
  77 
  78     private static native boolean isSysWow();
  79 
  80 
  81     /**
  82      * Load DLLs
  83      */
  84     static {
  85         // Load the appropriate DLLs
  86         boolean is32on64 = false;
  87         if (System.getProperty("os.arch").equals("x86")) {
  88             // 32 bit JRE
  89             // Load jabsysinfo.dll so can determine Win bitness
  90             java.security.AccessController.doPrivileged(
  91                 new java.security.PrivilegedAction<Void>() {
  92                     public Void run() {
  93                         System.loadLibrary("jabsysinfo");
  94                         return null;
  95                     }
  96                 }, null, new java.lang.RuntimePermission("loadLibrary.jabsysinfo")
  97             );
  98             if (isSysWow()) {
  99                 // 32 bit JRE on 64 bit OS
 100                 is32on64 = true;
 101                 java.security.AccessController.doPrivileged(
 102                     new java.security.PrivilegedAction<Void>() {
 103                         public Void run() {
 104                             System.loadLibrary("javaaccessbridge-32");
 105                             return null;
 106                         }
 107                     }, null, new java.lang.RuntimePermission("loadLibrary.javaaccessbridge-32")
 108                 );
 109             }
 110         }
 111         if (!is32on64) {
 112             // 32 bit JRE on 32 bit OS or 64 bit JRE on 64 bit OS
 113             java.security.AccessController.doPrivileged(
 114                 new java.security.PrivilegedAction<Void>() {
 115                     public Void run() {
 116                         System.loadLibrary("javaaccessbridge");
 117                         return null;
 118                     }
 119                 }, null, new java.lang.RuntimePermission("loadLibrary.javaaccessbridge")
 120             );
 121         }
 122     }
 123 
 124     /**
 125      * AccessBridge constructor
 126      *
 127      * Note: This constructor has to be public.  It's called from the VM like this:
 128      *       Class.forName(atName).newInstance();
 129      */
 130     public AccessBridge() {
 131         theAccessBridge = this;
 132         references = new ObjectReferences();
 133 
 134         // initialize shutdown hook
 135         Runtime runTime = Runtime.getRuntime();
 136         shutdownHook hook = new shutdownHook();
 137         runTime.addShutdownHook(new Thread(hook));
 138 
 139         // initialize AccessibleRole map
 140         initAccessibleRoleMap();
 141 
 142         // initialize the methods that map HWNDs and Java top-level
 143         // windows
 144         initHWNDcalls();
 145 
 146         // is this a JVM we can use?
 147         // install JDK 1.2 and later Swing ToolKit listener
 148         EventQueueMonitor.isGUIInitialized();
 149 
 150         // start the Java event handler
 151         eventHandler = new EventHandler(this);
 152 
 153         // register for menu selection events
 154         MenuSelectionManager.defaultManager().addChangeListener(eventHandler);
 155 
 156         // register as a NativeWindowHandler
 157         addNativeWindowHandler(new DefaultNativeWindowHandler());
 158 
 159         // start in a new thread
 160         Thread abthread = new Thread(new dllRunner());
 161         abthread.setDaemon(true);
 162         abthread.start();
 163         debugString("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 != null ? role.toDisplayString(Locale.US) : "null") );
 706                 /*
 707                 Step 3:
 708                 =======
 709                 We have determined that we should not use the extended name
 710                 search algorithm for this object (we must obtain the name of
 711                 the object from the object itself and not from neighboring
 712                 objects).  However the object name cannot be obtained from
 713                 the Accessible Name or Accessible Description of the object.
 714 
 715                 Handle several special cases here that might yield a value for
 716                 the Virtual Accessible Name.  Return null if the object does
 717                 not match the criteria for any of these special cases.
 718                 */
 719                 if (AccessibleRole.LABEL == role) {
 720                     /*
 721                     Does the label support the Accessible Text Interface?
 722                     */
 723                     final AccessibleText at = InvocationUtils.invokeAndWait(new Callable<AccessibleText>() {
 724                         @Override
 725                         public AccessibleText call() throws Exception {
 726                             return ac.getAccessibleText();
 727                         }
 728                     }, ac);
 729                     if (null != at) {
 730                         int charCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
 731                             @Override
 732                             public Integer call() throws Exception {
 733                                 return at.getCharCount();
 734                             }
 735                         }, ac);
 736                         String text = getAccessibleTextRangeFromContext (ac, 0, charCount);
 737                         if (null != text) {
 738                             debugString ("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         if(ac instanceof AccessibleExtendedTable) {
4531             _getVisibleChildrenCount((AccessibleExtendedTable)ac);
4532             return;
4533         }
4534         int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4535             @Override
4536             public Integer call() throws Exception {
4537                 return ac.getAccessibleChildrenCount();
4538             }
4539         }, ac);
4540         for (int i = 0; i < numChildren; i++) {
4541             final int idx = i;
4542             final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4543                 @Override
4544                 public AccessibleContext call() throws Exception {
4545                     Accessible a = ac.getAccessibleChild(idx);
4546                     if (a != null)
4547                         return a.getAccessibleContext();
4548                     else
4549                         return null;
4550                 }
4551             }, ac);
4552             if ( ac2 == null ||
4553                  (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4554                      @Override
4555                      public Boolean call() throws Exception {
4556                          return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4557                      }
4558                  }, ac))
4559                ) {
4560                 continue;
4561             }
4562             _visibleChildrenCount++;
4563 
4564             if (InvocationUtils.invokeAndWait(new Callable<Integer>() {
4565                 @Override
4566                 public Integer call() throws Exception {
4567                     return ac2.getAccessibleChildrenCount();
4568                 }
4569             }, ac) > 0 ) {
4570                 _getVisibleChildrenCount(ac2);
4571             }
4572         }
4573     }
4574 
4575     /*
4576     * Recursively descends AccessibleContext and gets the number
4577     * of visible children. Stops search if get to invisible part of table.
4578     */
4579     private void _getVisibleChildrenCount(final AccessibleExtendedTable acTable) {
4580         if (acTable == null)
4581             return;
4582         int lastVisibleRow = -1;
4583         int lastVisibleColumn = -1;
4584         boolean foundVisible = false;
4585         int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4586             @Override
4587             public Integer call() throws Exception {
4588                 return acTable.getAccessibleRowCount();
4589             }
4590         }, acTable);
4591         int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4592             @Override
4593             public Integer call() throws Exception {
4594                 return acTable.getAccessibleColumnCount();
4595             }
4596         }, acTable);
4597         for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) {
4598             for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) {
4599                 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) {
4600                     continue;
4601                 }
4602                 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) {
4603                     continue;
4604                 }
4605                 int finalRowIdx = rowIdx;
4606                 int finalColumnIdx = columnIdx;
4607                 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4608                     @Override
4609                     public AccessibleContext call() throws Exception {
4610                         Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx);
4611                         if (a == null)
4612                             return null;
4613                         else
4614                             return a.getAccessibleContext();
4615                     }
4616                 }, acTable);
4617                 if (ac2 == null ||
4618                         (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4619                             @Override
4620                             public Boolean call() throws Exception {
4621                                 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4622                             }
4623                         }, acTable))
4624                         ) {
4625                     if (foundVisible) {
4626                         if (columnIdx != 0 && lastVisibleColumn == -1) {
4627                             //the same row, so we found the last visible column
4628                             lastVisibleColumn = columnIdx - 1;
4629                         } else if (columnIdx == 0 && lastVisibleRow == -1) {
4630                             lastVisibleRow = rowIdx - 1;
4631                         }
4632                     }
4633                     continue;
4634                 }
4635 
4636                 foundVisible = true;
4637 
4638                 _visibleChildrenCount++;
4639 
4640                 if (InvocationUtils.invokeAndWait(new Callable<Integer>() {
4641                     @Override
4642                     public Integer call() throws Exception {
4643                         return ac2.getAccessibleChildrenCount();
4644                     }
4645                 }, acTable) > 0) {
4646                     _getVisibleChildrenCount(ac2);
4647                 }
4648             }
4649         }
4650     }
4651 
4652     /**
4653      * Gets the visible child of an AccessibleContext at the
4654      * specified index
4655      *
4656      * Bug ID 4944762- getVisibleChildren for list-like components needed
4657      */
4658     private AccessibleContext getVisibleChild(AccessibleContext ac, int index) {
4659         debugString("getVisibleChild: index = "+index);
4660         if (ac == null) {
4661             return null;
4662         }
4663         _visibleChild = null;
4664         _currentVisibleIndex = 0;
4665         _foundVisibleChild = false;
4666         _getVisibleChild(ac, index);
4667 
4668         if (_visibleChild != null) {
4669             debugString( "    getVisibleChild: found child = " +
4670                          InvocationUtils.invokeAndWait(new Callable<String>() {
4671                              @Override
4672                              public String call() throws Exception {
4673                                  return AccessBridge.this._visibleChild.getAccessibleName();
4674                              }
4675                          }, ac) );
4676         }
4677         return _visibleChild;
4678     }
4679 
4680     /*
4681      * Recursively searchs AccessibleContext and finds the visible component
4682      * at the specified index
4683      */
4684     private void _getVisibleChild(final AccessibleContext ac, final int index) {
4685         if (_visibleChild != null) {
4686             return;
4687         }
4688         if(ac instanceof AccessibleExtendedTable) {
4689             _getVisibleChild((AccessibleExtendedTable)ac, index);
4690             return;
4691         }
4692         int numChildren = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4693             @Override
4694             public Integer call() throws Exception {
4695                 return ac.getAccessibleChildrenCount();
4696             }
4697         }, ac);
4698         for (int i = 0; i < numChildren; i++) {
4699             final int idx=i;
4700             final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4701                 @Override
4702                 public AccessibleContext call() throws Exception {
4703                     Accessible a = ac.getAccessibleChild(idx);
4704                     if (a == null)
4705                         return null;
4706                     else
4707                         return a.getAccessibleContext();
4708                 }
4709             }, ac);
4710             if (ac2 == null ||
4711             (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4712                 @Override
4713                 public Boolean call() throws Exception {
4714                     return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4715                 }
4716             }, ac))) {
4717                 continue;
4718             }
4719             if (!_foundVisibleChild && _currentVisibleIndex == index) {
4720             _visibleChild = ac2;
4721             _foundVisibleChild = true;
4722             return;
4723             }
4724             _currentVisibleIndex++;
4725 
4726             if ( InvocationUtils.invokeAndWait(new Callable<Integer>() {
4727                 @Override
4728                 public Integer call() throws Exception {
4729                     return ac2.getAccessibleChildrenCount();
4730                 }
4731             }, ac) > 0 ) {
4732                 _getVisibleChild(ac2, index);
4733             }
4734         }
4735     }
4736 
4737     private void _getVisibleChild(final AccessibleExtendedTable acTable, final int index) {
4738         if (_visibleChild != null) {
4739             return;
4740         }
4741         int lastVisibleRow = -1;
4742         int lastVisibleColumn = -1;
4743         boolean foundVisible = false;
4744         int rowCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4745             @Override
4746             public Integer call() throws Exception {
4747                 return acTable.getAccessibleRowCount();
4748             }
4749         }, acTable);
4750         int columnCount = InvocationUtils.invokeAndWait(new Callable<Integer>() {
4751             @Override
4752             public Integer call() throws Exception {
4753                 return acTable.getAccessibleColumnCount();
4754             }
4755         }, acTable);
4756         for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) {
4757             for (int columnIdx = 0; columnIdx < columnCount; columnIdx++) {
4758                 if (lastVisibleRow != -1 && rowIdx > lastVisibleRow) {
4759                     continue;
4760                 }
4761                 if (lastVisibleColumn != -1 && columnIdx > lastVisibleColumn) {
4762                     continue;
4763                 }
4764                 int finalRowIdx = rowIdx;
4765                 int finalColumnIdx = columnIdx;
4766                 final AccessibleContext ac2 = InvocationUtils.invokeAndWait(new Callable<AccessibleContext>() {
4767                     @Override
4768                     public AccessibleContext call() throws Exception {
4769                         Accessible a = acTable.getAccessibleAt(finalRowIdx, finalColumnIdx);
4770                         if (a == null)
4771                             return null;
4772                         else
4773                             return a.getAccessibleContext();
4774                     }
4775                 }, acTable);
4776                 if (ac2 == null ||
4777                         (!InvocationUtils.invokeAndWait(new Callable<Boolean>() {
4778                             @Override
4779                             public Boolean call() throws Exception {
4780                                 return ac2.getAccessibleStateSet().contains(AccessibleState.SHOWING);
4781                             }
4782                         }, acTable))) {
4783                     if (foundVisible) {
4784                         if (columnIdx != 0 && lastVisibleColumn == -1) {
4785                             //the same row, so we found the last visible column
4786                             lastVisibleColumn = columnIdx - 1;
4787                         } else if (columnIdx == 0 && lastVisibleRow == -1) {
4788                             lastVisibleRow = rowIdx - 1;
4789                         }
4790                     }
4791                     continue;
4792                 }
4793                 foundVisible = true;
4794 
4795                 if (!_foundVisibleChild && _currentVisibleIndex == index) {
4796                     _visibleChild = ac2;
4797                     _foundVisibleChild = true;
4798                     return;
4799                 }
4800                 _currentVisibleIndex++;
4801 
4802                 if (InvocationUtils.invokeAndWait(new Callable<Integer>() {
4803                     @Override
4804                     public Integer call() throws Exception {
4805                         return ac2.getAccessibleChildrenCount();
4806                     }
4807                 }, acTable) > 0) {
4808                     _getVisibleChild(ac2, index);
4809                 }
4810             }
4811         }
4812     }
4813 
4814     /* ===== Java object memory management code ===== */
4815 
4816     /**
4817      * Class to track object references to ensure the
4818      * Java VM doesn't garbage collect them
4819      */
4820     private class ObjectReferences {
4821 
4822         private class Reference {
4823             private int value;
4824 
4825             Reference(int i) {
4826                 value = i;
4827             }
4828 
4829             public String toString() {
4830                 return ("refCount: " + value);
4831             }
4832         }
4833 
4834         /**
4835         * table object references, to keep 'em from being garbage collected
4836         */
4837         private ConcurrentHashMap<Object,Reference> refs;
4838 
4839         /**
4840         * Constructor
4841         */
4842         ObjectReferences() {
4843             refs = new ConcurrentHashMap<>(4);
4844         }
4845 
4846         /**
4847         * Debugging: dump the contents of ObjectReferences' refs Hashtable
4848         */
4849         String dump() {
4850             return refs.toString();
4851         }
4852 
4853         /**
4854         * Increment ref count; set to 1 if we have no references for it
4855         */
4856         void increment(Object o) {
4857             if (o == null){
4858                 debugString("ObjectReferences::increment - Passed in object is null");
4859                 return;
4860             }
4861 
4862             if (refs.containsKey(o)) {
4863                 (refs.get(o)).value++;
4864             } else {
4865                 refs.put(o, new Reference(1));
4866             }
4867         }
4868 
4869         /**
4870         * Decrement ref count; remove if count drops to 0
4871         */
4872         void decrement(Object o) {
4873             Reference aRef = refs.get(o);
4874             if (aRef != null) {
4875                 aRef.value--;
4876                 if (aRef.value == 0) {
4877                     refs.remove(o);
4878                 } else if (aRef.value < 0) {
4879                     debugString("ERROR: decrementing reference count below 0");
4880                 }
4881             } else {
4882                 debugString("ERROR: object to decrement not in ObjectReferences table");
4883             }
4884         }
4885 
4886     }
4887 
4888     /* ===== event handling code ===== */
4889 
4890    /**
4891      * native method for handling property change events
4892      */
4893     private native void propertyCaretChange(PropertyChangeEvent e,
4894                         AccessibleContext src,
4895                         int oldValue, int newValue);
4896     private native void propertyDescriptionChange(PropertyChangeEvent e,
4897                         AccessibleContext src,
4898                         String oldValue, String newValue);
4899     private native void propertyNameChange(PropertyChangeEvent e,
4900                         AccessibleContext src,
4901                         String oldValue, String newValue);
4902     private native void propertySelectionChange(PropertyChangeEvent e,
4903                         AccessibleContext src);
4904     private native void propertyStateChange(PropertyChangeEvent e,
4905                         AccessibleContext src,
4906                         String oldValue, String newValue);
4907     private native void propertyTextChange(PropertyChangeEvent e,
4908                         AccessibleContext src);
4909     private native void propertyValueChange(PropertyChangeEvent e,
4910                         AccessibleContext src,
4911                         String oldValue, String newValue);
4912     private native void propertyVisibleDataChange(PropertyChangeEvent e,
4913                         AccessibleContext src);
4914     private native void propertyChildChange(PropertyChangeEvent e,
4915                         AccessibleContext src,
4916                         AccessibleContext oldValue,
4917                         AccessibleContext newValue);
4918     private native void propertyActiveDescendentChange(PropertyChangeEvent e,
4919                         AccessibleContext src,
4920                         AccessibleContext oldValue,
4921                         AccessibleContext newValue);
4922 
4923     private native void javaShutdown();
4924 
4925     /**
4926      * native methods for handling focus events
4927      */
4928     private native void focusGained(FocusEvent e, AccessibleContext src);
4929     private native void focusLost(FocusEvent e, AccessibleContext src);
4930 
4931     /**
4932      * native method for handling caret events
4933      */
4934     private native void caretUpdate(CaretEvent e, AccessibleContext src);
4935 
4936     /**
4937      * native methods for handling mouse events
4938      */
4939     private native void mouseClicked(MouseEvent e, AccessibleContext src);
4940     private native void mouseEntered(MouseEvent e, AccessibleContext src);
4941     private native void mouseExited(MouseEvent e, AccessibleContext src);
4942     private native void mousePressed(MouseEvent e, AccessibleContext src);
4943     private native void mouseReleased(MouseEvent e, AccessibleContext src);
4944 
4945     /**
4946      * native methods for handling menu & popupMenu events
4947      */
4948     private native void menuCanceled(MenuEvent e, AccessibleContext src);
4949     private native void menuDeselected(MenuEvent e, AccessibleContext src);
4950     private native void menuSelected(MenuEvent e, AccessibleContext src);
4951     private native void popupMenuCanceled(PopupMenuEvent e, AccessibleContext src);
4952     private native void popupMenuWillBecomeInvisible(PopupMenuEvent e,
4953                                                      AccessibleContext src);
4954     private native void popupMenuWillBecomeVisible(PopupMenuEvent e,
4955                                                    AccessibleContext src);
4956 
4957     /* ===== event definitions ===== */
4958 
4959     private static final long PROPERTY_CHANGE_EVENTS = 1;
4960     private static final long FOCUS_GAINED_EVENTS = 2;
4961     private static final long FOCUS_LOST_EVENTS = 4;
4962     private static final long FOCUS_EVENTS = (FOCUS_GAINED_EVENTS | FOCUS_LOST_EVENTS);
4963 
4964     private static final long CARET_UPATE_EVENTS = 8;
4965     private static final long CARET_EVENTS = CARET_UPATE_EVENTS;
4966 
4967     private static final long MOUSE_CLICKED_EVENTS = 16;
4968     private static final long MOUSE_ENTERED_EVENTS = 32;
4969     private static final long MOUSE_EXITED_EVENTS = 64;
4970     private static final long MOUSE_PRESSED_EVENTS = 128;
4971     private static final long MOUSE_RELEASED_EVENTS = 256;
4972     private static final long MOUSE_EVENTS = (MOUSE_CLICKED_EVENTS | MOUSE_ENTERED_EVENTS |
4973                                              MOUSE_EXITED_EVENTS | MOUSE_PRESSED_EVENTS |
4974                                              MOUSE_RELEASED_EVENTS);
4975 
4976     private static final long MENU_CANCELED_EVENTS = 512;
4977     private static final long MENU_DESELECTED_EVENTS = 1024;
4978     private static final long MENU_SELECTED_EVENTS = 2048;
4979     private static final long MENU_EVENTS = (MENU_CANCELED_EVENTS | MENU_DESELECTED_EVENTS |
4980                                             MENU_SELECTED_EVENTS);
4981 
4982     private static final long POPUPMENU_CANCELED_EVENTS = 4096;
4983     private static final long POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS = 8192;
4984     private static final long POPUPMENU_WILL_BECOME_VISIBLE_EVENTS = 16384;
4985     private static final long POPUPMENU_EVENTS = (POPUPMENU_CANCELED_EVENTS |
4986                                                  POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS |
4987                                                  POPUPMENU_WILL_BECOME_VISIBLE_EVENTS);
4988 
4989     /* These use their own numbering scheme, to ensure sufficient expansion room */
4990     private static final long PROPERTY_NAME_CHANGE_EVENTS = 1;
4991     private static final long PROPERTY_DESCRIPTION_CHANGE_EVENTS = 2;
4992     private static final long PROPERTY_STATE_CHANGE_EVENTS = 4;
4993     private static final long PROPERTY_VALUE_CHANGE_EVENTS = 8;
4994     private static final long PROPERTY_SELECTION_CHANGE_EVENTS = 16;
4995     private static final long PROPERTY_TEXT_CHANGE_EVENTS = 32;
4996     private static final long PROPERTY_CARET_CHANGE_EVENTS = 64;
4997     private static final long PROPERTY_VISIBLEDATA_CHANGE_EVENTS = 128;
4998     private static final long PROPERTY_CHILD_CHANGE_EVENTS = 256;
4999     private static final long PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS = 512;
5000 
5001 
5002     private static final long PROPERTY_EVENTS = (PROPERTY_NAME_CHANGE_EVENTS |
5003                                                 PROPERTY_DESCRIPTION_CHANGE_EVENTS |
5004                                                 PROPERTY_STATE_CHANGE_EVENTS |
5005                                                 PROPERTY_VALUE_CHANGE_EVENTS |
5006                                                 PROPERTY_SELECTION_CHANGE_EVENTS |
5007                                                 PROPERTY_TEXT_CHANGE_EVENTS |
5008                                                 PROPERTY_CARET_CHANGE_EVENTS |
5009                                                 PROPERTY_VISIBLEDATA_CHANGE_EVENTS |
5010                                                 PROPERTY_CHILD_CHANGE_EVENTS |
5011                                                 PROPERTY_ACTIVEDESCENDENT_CHANGE_EVENTS);
5012 
5013     /**
5014      * The EventHandler class listens for Java events and
5015      * forwards them to the AT
5016      */
5017     private class EventHandler implements PropertyChangeListener,
5018                                           FocusListener, CaretListener,
5019                                           MenuListener, PopupMenuListener,
5020                                           MouseListener, WindowListener,
5021                                           ChangeListener {
5022 
5023         private AccessBridge accessBridge;
5024         private long javaEventMask = 0;
5025         private long accessibilityEventMask = 0;
5026 
5027         EventHandler(AccessBridge bridge) {
5028             accessBridge = bridge;
5029 
5030             // Register to receive WINDOW_OPENED and WINDOW_CLOSED
5031             // events.  Add the event source as a native window
5032             // handler is it implements NativeWindowHandler.
5033             // SwingEventMonitor.addWindowListener(this);
5034         }
5035 
5036         // --------- Event Notification Registration methods
5037 
5038         /**
5039          * Invoked the first time a window is made visible.
5040          */
5041         public void windowOpened(WindowEvent e) {
5042             // If the window is a NativeWindowHandler, add it.
5043             Object o = null;
5044             if (e != null)
5045                 o = e.getSource();
5046             if (o instanceof NativeWindowHandler) {
5047                 addNativeWindowHandler((NativeWindowHandler)o);
5048             }
5049         }
5050 
5051         /**
5052          * Invoked when the user attempts to close the window
5053          * from the window's system menu.  If the program does not
5054          * explicitly hide or dispose the window while processing
5055          * this event, the window close operation will be canceled.
5056          */
5057         public void windowClosing(WindowEvent e) {}
5058 
5059         /**
5060          * Invoked when a window has been closed as the result
5061          * of calling dispose on the window.
5062          */
5063         public void windowClosed(WindowEvent e) {
5064             // If the window is a NativeWindowHandler, remove it.
5065             Object o = null;
5066             if (e != null)
5067                 o = e.getSource();
5068             if (o instanceof NativeWindowHandler) {
5069                 removeNativeWindowHandler((NativeWindowHandler)o);
5070             }
5071         }
5072 
5073         /**
5074          * Invoked when a window is changed from a normal to a
5075          * minimized state. For many platforms, a minimized window
5076          * is displayed as the icon specified in the window's
5077          * iconImage property.
5078          * @see java.awt.Frame#setIconImage
5079          */
5080         public void windowIconified(WindowEvent e) {}
5081 
5082         /**
5083          * Invoked when a window is changed from a minimized
5084          * to a normal state.
5085          */
5086         public void windowDeiconified(WindowEvent e) {}
5087 
5088         /**
5089          * Invoked when the Window is set to be the active Window. Only a Frame or
5090          * a Dialog can be the active Window. The native windowing system may
5091          * denote the active Window or its children with special decorations, such
5092          * as a highlighted title bar. The active Window is always either the
5093          * focused Window, or the first Frame or Dialog that is an owner of the
5094          * focused Window.
5095          */
5096         public void windowActivated(WindowEvent e) {}
5097 
5098         /**
5099          * Invoked when a Window is no longer the active Window. Only a Frame or a
5100          * Dialog can be the active Window. The native windowing system may denote
5101          * the active Window or its children with special decorations, such as a
5102          * highlighted title bar. The active Window is always either the focused
5103          * Window, or the first Frame or Dialog that is an owner of the focused
5104          * Window.
5105          */
5106         public void windowDeactivated(WindowEvent e) {}
5107 
5108         /**
5109          * Turn on event monitoring for the event type passed in
5110          * If necessary, add the appropriate event listener (if
5111          * no other event of that type is being listened for)
5112          */
5113         void addJavaEventNotification(long type) {
5114             long newEventMask = javaEventMask | type;
5115             /*
5116             if ( ((javaEventMask & PROPERTY_EVENTS) == 0) &&
5117                  ((newEventMask & PROPERTY_EVENTS) != 0) ) {
5118                 AccessibilityEventMonitor.addPropertyChangeListener(this);
5119             }
5120             */
5121             if ( ((javaEventMask & FOCUS_EVENTS) == 0) &&
5122                 ((newEventMask & FOCUS_EVENTS) != 0) ) {
5123                 SwingEventMonitor.addFocusListener(this);
5124             }
5125             if ( ((javaEventMask & CARET_EVENTS) == 0) &&
5126                 ((newEventMask & CARET_EVENTS) != 0) ) {
5127                 SwingEventMonitor.addCaretListener(this);
5128             }
5129             if ( ((javaEventMask & MOUSE_EVENTS) == 0) &&
5130                 ((newEventMask & MOUSE_EVENTS) != 0) ) {
5131                 SwingEventMonitor.addMouseListener(this);
5132             }
5133             if ( ((javaEventMask & MENU_EVENTS) == 0) &&
5134                 ((newEventMask & MENU_EVENTS) != 0) ) {
5135                 SwingEventMonitor.addMenuListener(this);
5136                 SwingEventMonitor.addPopupMenuListener(this);
5137             }
5138             if ( ((javaEventMask & POPUPMENU_EVENTS) == 0) &&
5139                 ((newEventMask & POPUPMENU_EVENTS) != 0) ) {
5140                 SwingEventMonitor.addPopupMenuListener(this);
5141             }
5142 
5143             javaEventMask = newEventMask;
5144         }
5145 
5146         /**
5147          * Turn off event monitoring for the event type passed in
5148          * If necessary, remove the appropriate event listener (if
5149          * no other event of that type is being listened for)
5150          */
5151         void removeJavaEventNotification(long type) {
5152             long newEventMask = javaEventMask & (~type);
5153             /*
5154             if ( ((javaEventMask & PROPERTY_EVENTS) != 0) &&
5155                  ((newEventMask & PROPERTY_EVENTS) == 0) ) {
5156                 AccessibilityEventMonitor.removePropertyChangeListener(this);
5157             }
5158             */
5159             if (((javaEventMask & FOCUS_EVENTS) != 0) &&
5160                 ((newEventMask & FOCUS_EVENTS) == 0)) {
5161                 SwingEventMonitor.removeFocusListener(this);
5162             }
5163             if (((javaEventMask & CARET_EVENTS) != 0) &&
5164                 ((newEventMask & CARET_EVENTS) == 0)) {
5165                 SwingEventMonitor.removeCaretListener(this);
5166             }
5167             if (((javaEventMask & MOUSE_EVENTS) == 0) &&
5168                 ((newEventMask & MOUSE_EVENTS) != 0)) {
5169                 SwingEventMonitor.removeMouseListener(this);
5170             }
5171             if (((javaEventMask & MENU_EVENTS) == 0) &&
5172                 ((newEventMask & MENU_EVENTS) != 0)) {
5173                 SwingEventMonitor.removeMenuListener(this);
5174             }
5175             if (((javaEventMask & POPUPMENU_EVENTS) == 0) &&
5176                 ((newEventMask & POPUPMENU_EVENTS) != 0)) {
5177                 SwingEventMonitor.removePopupMenuListener(this);
5178             }
5179 
5180             javaEventMask = newEventMask;
5181         }
5182 
5183         /**
5184          * Turn on event monitoring for the event type passed in
5185          * If necessary, add the appropriate event listener (if
5186          * no other event of that type is being listened for)
5187          */
5188         void addAccessibilityEventNotification(long type) {
5189             long newEventMask = accessibilityEventMask | type;
5190             if ( ((accessibilityEventMask & PROPERTY_EVENTS) == 0) &&
5191                  ((newEventMask & PROPERTY_EVENTS) != 0) ) {
5192                 AccessibilityEventMonitor.addPropertyChangeListener(this);
5193             }
5194             accessibilityEventMask = newEventMask;
5195         }
5196 
5197         /**
5198          * Turn off event monitoring for the event type passed in
5199          * If necessary, remove the appropriate event listener (if
5200          * no other event of that type is being listened for)
5201          */
5202         void removeAccessibilityEventNotification(long type) {
5203             long newEventMask = accessibilityEventMask & (~type);
5204             if ( ((accessibilityEventMask & PROPERTY_EVENTS) != 0) &&
5205                  ((newEventMask & PROPERTY_EVENTS) == 0) ) {
5206                 AccessibilityEventMonitor.removePropertyChangeListener(this);
5207             }
5208             accessibilityEventMask = newEventMask;
5209         }
5210 
5211         /**
5212          *  ------- property change event glue
5213          */
5214         // This is invoked on the EDT , as
5215         public void propertyChange(PropertyChangeEvent e) {
5216 
5217             accessBridge.debugString("propertyChange(" + e.toString() + ") called");
5218 
5219             if (e != null && (accessibilityEventMask & PROPERTY_EVENTS) != 0) {
5220                 Object o = e.getSource();
5221                 AccessibleContext ac;
5222 
5223                 if (o instanceof AccessibleContext) {
5224                     ac = (AccessibleContext) o;
5225                 } else {
5226                     Accessible a = Translator.getAccessible(e.getSource());
5227                     if (a == null)
5228                         return;
5229                     else
5230                         ac = a.getAccessibleContext();
5231                 }
5232                 if (ac != null) {
5233                     InvocationUtils.registerAccessibleContext(ac, AppContext.getAppContext());
5234 
5235                     accessBridge.debugString("AccessibleContext: " + ac);
5236                     String propertyName = e.getPropertyName();
5237 
5238                     if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CARET_PROPERTY) == 0) {
5239                         int oldValue = 0;
5240                         int newValue = 0;
5241 
5242                         if (e.getOldValue() instanceof Integer) {
5243                             oldValue = ((Integer) e.getOldValue()).intValue();
5244                         }
5245                         if (e.getNewValue() instanceof Integer) {
5246                             newValue = ((Integer) e.getNewValue()).intValue();
5247                         }
5248                         accessBridge.debugString(" - about to call propertyCaretChange()");
5249                         accessBridge.debugString("   old value: " + oldValue + "new value: " + newValue);
5250                         accessBridge.propertyCaretChange(e, ac, oldValue, newValue);
5251 
5252                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_DESCRIPTION_PROPERTY) == 0) {
5253                         String oldValue = null;
5254                         String newValue = null;
5255 
5256                         if (e.getOldValue() != null) {
5257                             oldValue = e.getOldValue().toString();
5258                         }
5259                         if (e.getNewValue() != null) {
5260                             newValue = e.getNewValue().toString();
5261                         }
5262                         accessBridge.debugString(" - about to call propertyDescriptionChange()");
5263                         accessBridge.debugString("   old value: " + oldValue + "new value: " + newValue);
5264                         accessBridge.propertyDescriptionChange(e, ac, oldValue, newValue);
5265 
5266                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_NAME_PROPERTY) == 0) {
5267                         String oldValue = null;
5268                         String newValue = null;
5269 
5270                         if (e.getOldValue() != null) {
5271                             oldValue = e.getOldValue().toString();
5272                         }
5273                         if (e.getNewValue() != null) {
5274                             newValue = e.getNewValue().toString();
5275                         }
5276                         accessBridge.debugString(" - about to call propertyNameChange()");
5277                         accessBridge.debugString("   old value: " + oldValue + " new value: " + newValue);
5278                         accessBridge.propertyNameChange(e, ac, oldValue, newValue);
5279 
5280                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY) == 0) {
5281                         accessBridge.debugString(" - about to call propertySelectionChange() " + ac +  "   " + Thread.currentThread() + "   " + e.getSource());
5282 
5283                         accessBridge.propertySelectionChange(e, ac);
5284 
5285                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_STATE_PROPERTY) == 0) {
5286                         String oldValue = null;
5287                         String newValue = null;
5288 
5289                         // Localization fix requested by Oliver for EA-1
5290                         if (e.getOldValue() != null) {
5291                             AccessibleState oldState = (AccessibleState) e.getOldValue();
5292                             oldValue = oldState.toDisplayString(Locale.US);
5293                         }
5294                         if (e.getNewValue() != null) {
5295                             AccessibleState newState = (AccessibleState) e.getNewValue();
5296                             newValue = newState.toDisplayString(Locale.US);
5297                         }
5298 
5299                         accessBridge.debugString(" - about to call propertyStateChange()");
5300                         accessBridge.propertyStateChange(e, ac, oldValue, newValue);
5301 
5302                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_TEXT_PROPERTY) == 0) {
5303                         accessBridge.debugString(" - about to call propertyTextChange()");
5304                         accessBridge.propertyTextChange(e, ac);
5305 
5306                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY) == 0) {  // strings 'cause of floating point, etc.
5307                         String oldValue = null;
5308                         String newValue = null;
5309 
5310                         if (e.getOldValue() != null) {
5311                             oldValue = e.getOldValue().toString();
5312                         }
5313                         if (e.getNewValue() != null) {
5314                             newValue = e.getNewValue().toString();
5315                         }
5316                         accessBridge.debugString(" - about to call propertyDescriptionChange()");
5317                         accessBridge.propertyValueChange(e, ac, oldValue, newValue);
5318 
5319                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY) == 0) {
5320                         accessBridge.propertyVisibleDataChange(e, ac);
5321 
5322                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY) == 0) {
5323                         AccessibleContext oldAC = null;
5324                         AccessibleContext newAC = null;
5325                         Accessible a;
5326 
5327                         if (e.getOldValue() instanceof AccessibleContext) {
5328                             oldAC = (AccessibleContext) e.getOldValue();
5329                             InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext());
5330                         }
5331                         if (e.getNewValue() instanceof AccessibleContext) {
5332                             newAC = (AccessibleContext) e.getNewValue();
5333                             InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext());
5334                         }
5335                         accessBridge.debugString(" - about to call propertyChildChange()");
5336                         accessBridge.debugString("   old AC: " + oldAC + "new AC: " + newAC);
5337                         accessBridge.propertyChildChange(e, ac, oldAC, newAC);
5338 
5339                     } else if (propertyName.compareTo(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY) == 0) {
5340                         handleActiveDescendentEvent(e, ac);
5341                     }
5342                 }
5343             }
5344         }
5345 
5346         /*
5347         * Handle an ActiveDescendent PropertyChangeEvent.  This
5348         * method works around a JTree bug where ActiveDescendent
5349         * PropertyChangeEvents have the wrong parent.
5350         */
5351         private AccessibleContext prevAC = null; // previous AccessibleContext
5352 
5353         private void handleActiveDescendentEvent(PropertyChangeEvent e,
5354                                                  AccessibleContext ac) {
5355             if (e == null || ac == null)
5356                 return;
5357             AccessibleContext oldAC = null;
5358             AccessibleContext newAC = null;
5359             Accessible a;
5360 
5361             // get the old active descendent
5362             if (e.getOldValue() instanceof Accessible) {
5363                 oldAC = ((Accessible) e.getOldValue()).getAccessibleContext();
5364             } else if (e.getOldValue() instanceof Component) {
5365                 a = Translator.getAccessible(e.getOldValue());
5366                 if (a != null) {
5367                     oldAC = a.getAccessibleContext();
5368                 }
5369             }
5370             if (oldAC != null) {
5371                 Accessible parent = oldAC.getAccessibleParent();
5372                 if (parent instanceof JTree) {
5373                     // use the previous AccessibleJTreeNode
5374                     oldAC = prevAC;
5375                 }
5376             }
5377 
5378             // get the new active descendent
5379             if (e.getNewValue() instanceof Accessible) {
5380                 newAC = ((Accessible) e.getNewValue()).getAccessibleContext();
5381             } else if (e.getNewValue() instanceof Component) {
5382                 a = Translator.getAccessible(e.getNewValue());
5383                 if (a != null) {
5384                     newAC = a.getAccessibleContext();
5385                 }
5386             }
5387             if (newAC != null) {
5388                 Accessible parent = newAC.getAccessibleParent();
5389                 if (parent instanceof JTree) {
5390                     // use a new AccessibleJTreeNode with the right parent
5391                     JTree tree = (JTree)parent;
5392                     newAC = new AccessibleJTreeNode(tree,
5393                                                     tree.getSelectionPath(),
5394                                                     null);
5395                 }
5396             }
5397             prevAC = newAC;
5398 
5399             accessBridge.debugString("  - about to call propertyActiveDescendentChange()");
5400             accessBridge.debugString("   AC: " + ac);
5401             accessBridge.debugString("   old AC: " + oldAC + "new AC: " + newAC);
5402 
5403             InvocationUtils.registerAccessibleContext(oldAC, AppContext.getAppContext());
5404             InvocationUtils.registerAccessibleContext(newAC, AppContext.getAppContext());
5405             accessBridge.propertyActiveDescendentChange(e, ac, oldAC, newAC);
5406         }
5407 
5408         /**
5409         *  ------- focus event glue
5410         */
5411         private boolean stateChangeListenerAdded = false;
5412 
5413         public void focusGained(FocusEvent e) {
5414             processFocusGained();
5415         }
5416 
5417         public void stateChanged(ChangeEvent e) {
5418             processFocusGained();
5419         }
5420 
5421         private void processFocusGained() {
5422             Component focusOwner = KeyboardFocusManager.
5423             getCurrentKeyboardFocusManager().getFocusOwner();
5424             if (focusOwner == null) {
5425                 return;
5426             }
5427 
5428             // Only menus and popup selections are handled by the JRootPane.
5429             if (focusOwner instanceof JRootPane) {
5430                 MenuElement [] path =
5431                 MenuSelectionManager.defaultManager().getSelectedPath();
5432                 if (path.length > 1) {
5433                     Component penult = path[path.length-2].getComponent();
5434                     Component last = path[path.length-1].getComponent();
5435 
5436                     if (last instanceof JPopupMenu) {
5437                         // This is a popup with nothing in the popup
5438                         // selected. The menu itself is selected.
5439                         FocusEvent e = new FocusEvent(penult, FocusEvent.FOCUS_GAINED);
5440                         AccessibleContext context = penult.getAccessibleContext();
5441                         InvocationUtils.registerAccessibleContext(context, SunToolkit.targetToAppContext(penult));
5442                         accessBridge.focusGained(e, context);
5443                     } else if (penult instanceof JPopupMenu) {
5444                         // This is a popup with an item selected
5445                         FocusEvent e =
5446                         new FocusEvent(last, FocusEvent.FOCUS_GAINED);
5447                         accessBridge.debugString(" - about to call focusGained()");
5448                         AccessibleContext focusedAC = last.getAccessibleContext();
5449                         InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(last));
5450                         accessBridge.debugString("   AC: " + focusedAC);
5451                         accessBridge.focusGained(e, focusedAC);
5452                     }
5453                 }
5454             } else {
5455                 // The focus owner has the selection.
5456                 if (focusOwner instanceof Accessible) {
5457                     FocusEvent e = new FocusEvent(focusOwner,
5458                                                   FocusEvent.FOCUS_GAINED);
5459                     accessBridge.debugString(" - about to call focusGained()");
5460                     AccessibleContext focusedAC = focusOwner.getAccessibleContext();
5461                     InvocationUtils.registerAccessibleContext(focusedAC, SunToolkit.targetToAppContext(focusOwner));
5462                     accessBridge.debugString("   AC: " + focusedAC);
5463                     accessBridge.focusGained(e, focusedAC);
5464                 }
5465             }
5466         }
5467 
5468         public void focusLost(FocusEvent e) {
5469             if (e != null && (javaEventMask & FOCUS_LOST_EVENTS) != 0) {
5470                 Accessible a = Translator.getAccessible(e.getSource());
5471                 if (a != null) {
5472                     accessBridge.debugString(" - about to call focusLost()");
5473                     accessBridge.debugString("   AC: " + a.getAccessibleContext());
5474                     AccessibleContext context = a.getAccessibleContext();
5475                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5476                     accessBridge.focusLost(e, context);
5477                 }
5478             }
5479         }
5480 
5481         /**
5482          *  ------- caret event glue
5483          */
5484         public void caretUpdate(CaretEvent e) {
5485             if (e != null && (javaEventMask & CARET_UPATE_EVENTS) != 0) {
5486                 Accessible a = Translator.getAccessible(e.getSource());
5487                 if (a != null) {
5488                     AccessibleContext context = a.getAccessibleContext();
5489                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5490                     accessBridge.caretUpdate(e, context);
5491                 }
5492             }
5493         }
5494 
5495     /**
5496      *  ------- mouse event glue
5497      */
5498 
5499         public void mouseClicked(MouseEvent e) {
5500             if (e != null && (javaEventMask & MOUSE_CLICKED_EVENTS) != 0) {
5501                 Accessible a = Translator.getAccessible(e.getSource());
5502                 if (a != null) {
5503                     AccessibleContext context = a.getAccessibleContext();
5504                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5505                     accessBridge.mouseClicked(e, context);
5506                 }
5507             }
5508         }
5509 
5510         public void mouseEntered(MouseEvent e) {
5511             if (e != null && (javaEventMask & MOUSE_ENTERED_EVENTS) != 0) {
5512                 Accessible a = Translator.getAccessible(e.getSource());
5513                 if (a != null) {
5514                     AccessibleContext context = a.getAccessibleContext();
5515                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5516                     accessBridge.mouseEntered(e, context);
5517                 }
5518             }
5519         }
5520 
5521         public void mouseExited(MouseEvent e) {
5522             if (e != null && (javaEventMask & MOUSE_EXITED_EVENTS) != 0) {
5523                 Accessible a = Translator.getAccessible(e.getSource());
5524                 if (a != null) {
5525                     AccessibleContext context = a.getAccessibleContext();
5526                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5527                     accessBridge.mouseExited(e, context);
5528                 }
5529             }
5530         }
5531 
5532         public void mousePressed(MouseEvent e) {
5533             if (e != null && (javaEventMask & MOUSE_PRESSED_EVENTS) != 0) {
5534                 Accessible a = Translator.getAccessible(e.getSource());
5535                 if (a != null) {
5536                     AccessibleContext context = a.getAccessibleContext();
5537                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5538                     accessBridge.mousePressed(e, context);
5539                 }
5540             }
5541         }
5542 
5543         public void mouseReleased(MouseEvent e) {
5544             if (e != null && (javaEventMask & MOUSE_RELEASED_EVENTS) != 0) {
5545                 Accessible a = Translator.getAccessible(e.getSource());
5546                 if (a != null) {
5547                     AccessibleContext context = a.getAccessibleContext();
5548                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5549                     accessBridge.mouseReleased(e, context);
5550                 }
5551             }
5552         }
5553 
5554         /**
5555          *  ------- menu event glue
5556          */
5557         public void menuCanceled(MenuEvent e) {
5558             if (e != null && (javaEventMask & MENU_CANCELED_EVENTS) != 0) {
5559                 Accessible a = Translator.getAccessible(e.getSource());
5560                 if (a != null) {
5561                     AccessibleContext context = a.getAccessibleContext();
5562                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5563                     accessBridge.menuCanceled(e, context);
5564                 }
5565             }
5566         }
5567 
5568         public void menuDeselected(MenuEvent e) {
5569             if (e != null && (javaEventMask & MENU_DESELECTED_EVENTS) != 0) {
5570                 Accessible a = Translator.getAccessible(e.getSource());
5571                 if (a != null) {
5572                     AccessibleContext context = a.getAccessibleContext();
5573                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5574                     accessBridge.menuDeselected(e, context);
5575                 }
5576             }
5577         }
5578 
5579         public void menuSelected(MenuEvent e) {
5580             if (e != null && (javaEventMask & MENU_SELECTED_EVENTS) != 0) {
5581                 Accessible a = Translator.getAccessible(e.getSource());
5582                 if (a != null) {
5583                     AccessibleContext context = a.getAccessibleContext();
5584                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5585                     accessBridge.menuSelected(e, context);
5586                 }
5587             }
5588         }
5589 
5590         public void popupMenuCanceled(PopupMenuEvent e) {
5591             if (e != null && (javaEventMask & POPUPMENU_CANCELED_EVENTS) != 0) {
5592                 Accessible a = Translator.getAccessible(e.getSource());
5593                 if (a != null) {
5594                     AccessibleContext context = a.getAccessibleContext();
5595                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5596                     accessBridge.popupMenuCanceled(e, context);
5597                 }
5598             }
5599         }
5600 
5601         public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
5602             if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_INVISIBLE_EVENTS) != 0) {
5603                 Accessible a = Translator.getAccessible(e.getSource());
5604                 if (a != null) {
5605                     AccessibleContext context = a.getAccessibleContext();
5606                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5607                     accessBridge.popupMenuWillBecomeInvisible(e, context);
5608                 }
5609             }
5610         }
5611 
5612         public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
5613             if (e != null && (javaEventMask & POPUPMENU_WILL_BECOME_VISIBLE_EVENTS) != 0) {
5614                 Accessible a = Translator.getAccessible(e.getSource());
5615                 if (a != null) {
5616                     AccessibleContext context = a.getAccessibleContext();
5617                     InvocationUtils.registerAccessibleContext(context, AppContext.getAppContext());
5618                     accessBridge.popupMenuWillBecomeVisible(e, context);
5619                 }
5620             }
5621         }
5622 
5623     } // End of EventHandler Class
5624 
5625     // --------- Event Notification Registration methods
5626 
5627     /**
5628      *  Wrapper method around eventHandler.addJavaEventNotification()
5629      */
5630     private void addJavaEventNotification(final long type) {
5631         EventQueue.invokeLater(new Runnable() {
5632             public void run(){
5633                 eventHandler.addJavaEventNotification(type);
5634             }
5635         });
5636     }
5637 
5638     /**
5639      *  Wrapper method around eventHandler.removeJavaEventNotification()
5640      */
5641     private void removeJavaEventNotification(final long type) {
5642         EventQueue.invokeLater(new Runnable() {
5643             public void run(){
5644                 eventHandler.removeJavaEventNotification(type);
5645             }
5646         });
5647     }
5648 
5649 
5650     /**
5651      *  Wrapper method around eventHandler.addAccessibilityEventNotification()
5652      */
5653     private void addAccessibilityEventNotification(final long type) {
5654         EventQueue.invokeLater(new Runnable() {
5655             public void run(){
5656                 eventHandler.addAccessibilityEventNotification(type);
5657             }
5658         });
5659     }
5660 
5661     /**
5662      *  Wrapper method around eventHandler.removeAccessibilityEventNotification()
5663      */
5664     private void removeAccessibilityEventNotification(final long type) {
5665         EventQueue.invokeLater(new Runnable() {
5666             public void run(){
5667                 eventHandler.removeAccessibilityEventNotification(type);
5668             }
5669         });
5670     }
5671 
5672     /**
5673      ******************************************************
5674      * All AccessibleRoles
5675      *
5676      * We shouldn't have to do this since it requires us
5677      * to synchronize the allAccessibleRoles array when
5678      * the AccessibleRoles class interface changes. However,
5679      * there is no Accessibility API method to get all
5680      * AccessibleRoles
5681      ******************************************************
5682      */
5683     private AccessibleRole [] allAccessibleRoles = {
5684     /**
5685      * Object is used to alert the user about something.
5686      */
5687     AccessibleRole.ALERT,
5688 
5689     /**
5690      * The header for a column of data.
5691      */
5692     AccessibleRole.COLUMN_HEADER,
5693 
5694     /**
5695      * Object that can be drawn into and is used to trap
5696      * events.
5697      * @see #FRAME
5698      * @see #GLASS_PANE
5699      * @see #LAYERED_PANE
5700      */
5701     AccessibleRole.CANVAS,
5702 
5703     /**
5704      * A list of choices the user can select from.  Also optionally
5705      * allows the user to enter a choice of their own.
5706      */
5707     AccessibleRole.COMBO_BOX,
5708 
5709     /**
5710      * An iconified internal frame in a DESKTOP_PANE.
5711      * @see #DESKTOP_PANE
5712      * @see #INTERNAL_FRAME
5713      */
5714     AccessibleRole.DESKTOP_ICON,
5715 
5716     /**
5717      * A frame-like object that is clipped by a desktop pane.  The
5718      * desktop pane, internal frame, and desktop icon objects are
5719      * often used to create multiple document interfaces within an
5720      * application.
5721      * @see #DESKTOP_ICON
5722      * @see #DESKTOP_PANE
5723      * @see #FRAME
5724      */
5725     AccessibleRole.INTERNAL_FRAME,
5726 
5727     /**
5728      * A pane that supports internal frames and
5729      * iconified versions of those internal frames.
5730      * @see #DESKTOP_ICON
5731      * @see #INTERNAL_FRAME
5732      */
5733     AccessibleRole.DESKTOP_PANE,
5734 
5735     /**
5736      * A specialized pane whose primary use is inside a DIALOG
5737      * @see #DIALOG
5738      */
5739     AccessibleRole.OPTION_PANE,
5740 
5741     /**
5742      * A top level window with no title or border.
5743      * @see #FRAME
5744      * @see #DIALOG
5745      */
5746     AccessibleRole.WINDOW,
5747 
5748     /**
5749      * A top level window with a title bar, border, menu bar, etc.  It is
5750      * often used as the primary window for an application.
5751      * @see #DIALOG
5752      * @see #CANVAS
5753      * @see #WINDOW
5754      */
5755     AccessibleRole.FRAME,
5756 
5757     /**
5758      * A top level window with title bar and a border.  A dialog is similar
5759      * to a frame, but it has fewer properties and is often used as a
5760      * secondary window for an application.
5761      * @see #FRAME
5762      * @see #WINDOW
5763      */
5764     AccessibleRole.DIALOG,
5765 
5766     /**
5767      * A specialized dialog that lets the user choose a color.
5768      */
5769     AccessibleRole.COLOR_CHOOSER,
5770 
5771 
5772     /**
5773      * A pane that allows the user to navigate through
5774      * and select the contents of a directory.  May be used
5775      * by a file chooser.
5776      * @see #FILE_CHOOSER
5777      */
5778     AccessibleRole.DIRECTORY_PANE,
5779 
5780     /**
5781      * A specialized dialog that displays the files in the directory
5782      * and lets the user select a file, browse a different directory,
5783      * or specify a filename.  May use the directory pane to show the
5784      * contents of a directory.
5785      * @see #DIRECTORY_PANE
5786      */
5787     AccessibleRole.FILE_CHOOSER,
5788 
5789     /**
5790      * An object that fills up space in a user interface.  It is often
5791      * used in interfaces to tweak the spacing between components,
5792      * but serves no other purpose.
5793      */
5794     AccessibleRole.FILLER,
5795 
5796     /**
5797      * A hypertext anchor
5798      */
5799     // AccessibleRole.HYPERLINK,
5800 
5801     /**
5802      * A small fixed size picture, typically used to decorate components.
5803      */
5804     AccessibleRole.ICON,
5805 
5806     /**
5807      * An object used to present an icon or short string in an interface.
5808      */
5809     AccessibleRole.LABEL,
5810 
5811     /**
5812      * A specialized pane that has a glass pane and a layered pane as its
5813      * children.
5814      * @see #GLASS_PANE
5815      * @see #LAYERED_PANE
5816      */
5817     AccessibleRole.ROOT_PANE,
5818 
5819     /**
5820      * A pane that is guaranteed to be painted on top
5821      * of all panes beneath it.
5822      * @see #ROOT_PANE
5823      * @see #CANVAS
5824      */
5825     AccessibleRole.GLASS_PANE,
5826 
5827     /**
5828      * A specialized pane that allows its children to be drawn in layers,
5829      * providing a form of stacking order.  This is usually the pane that
5830      * holds the menu bar as well as the pane that contains most of the
5831      * visual components in a window.
5832      * @see #GLASS_PANE
5833      * @see #ROOT_PANE
5834      */
5835     AccessibleRole.LAYERED_PANE,
5836 
5837     /**
5838      * An object that presents a list of objects to the user and allows the
5839      * user to select one or more of them.  A list is usually contained
5840      * within a scroll pane.
5841      * @see #SCROLL_PANE
5842      * @see #LIST_ITEM
5843      */
5844     AccessibleRole.LIST,
5845 
5846     /**
5847      * An object that presents an element in a list.  A list is usually
5848      * contained within a scroll pane.
5849      * @see #SCROLL_PANE
5850      * @see #LIST
5851      */
5852     AccessibleRole.LIST_ITEM,
5853 
5854     /**
5855      * An object usually drawn at the top of the primary dialog box of
5856      * an application that contains a list of menus the user can choose
5857      * from.  For example, a menu bar might contain menus for "File,"
5858      * "Edit," and "Help."
5859      * @see #MENU
5860      * @see #POPUP_MENU
5861      * @see #LAYERED_PANE
5862      */
5863     AccessibleRole.MENU_BAR,
5864 
5865     /**
5866      * A temporary window that is usually used to offer the user a
5867      * list of choices, and then hides when the user selects one of
5868      * those choices.
5869      * @see #MENU
5870      * @see #MENU_ITEM
5871      */
5872     AccessibleRole.POPUP_MENU,
5873 
5874     /**
5875      * An object usually found inside a menu bar that contains a list
5876      * of actions the user can choose from.  A menu can have any object
5877      * as its children, but most often they are menu items, other menus,
5878      * or rudimentary objects such as radio buttons, check boxes, or
5879      * separators.  For example, an application may have an "Edit" menu
5880      * that contains menu items for "Cut" and "Paste."
5881      * @see #MENU_BAR
5882      * @see #MENU_ITEM
5883      * @see #SEPARATOR
5884      * @see #RADIO_BUTTON
5885      * @see #CHECK_BOX
5886      * @see #POPUP_MENU
5887      */
5888     AccessibleRole.MENU,
5889 
5890     /**
5891      * An object usually contained in a menu that presents an action
5892      * the user can choose.  For example, the "Cut" menu item in an
5893      * "Edit" menu would be an action the user can select to cut the
5894      * selected area of text in a document.
5895      * @see #MENU_BAR
5896      * @see #SEPARATOR
5897      * @see #POPUP_MENU
5898      */
5899     AccessibleRole.MENU_ITEM,
5900 
5901     /**
5902      * An object usually contained in a menu to provide a visual
5903      * and logical separation of the contents in a menu.  For example,
5904      * the "File" menu of an application might contain menu items for
5905      * "Open," "Close," and "Exit," and will place a separator between
5906      * "Close" and "Exit" menu items.
5907      * @see #MENU
5908      * @see #MENU_ITEM
5909      */
5910     AccessibleRole.SEPARATOR,
5911 
5912     /**
5913      * An object that presents a series of panels (or page tabs), one at a
5914      * time, through some mechanism provided by the object.  The most common
5915      * mechanism is a list of tabs at the top of the panel.  The children of
5916      * a page tab list are all page tabs.
5917      * @see #PAGE_TAB
5918      */
5919     AccessibleRole.PAGE_TAB_LIST,
5920 
5921     /**
5922      * An object that is a child of a page tab list.  Its sole child is
5923      * the panel that is to be presented to the user when the user
5924      * selects the page tab from the list of tabs in the page tab list.
5925      * @see #PAGE_TAB_LIST
5926      */
5927     AccessibleRole.PAGE_TAB,
5928 
5929     /**
5930      * A generic container that is often used to group objects.
5931      */
5932     AccessibleRole.PANEL,
5933 
5934     /**
5935      * An object used to indicate how much of a task has been completed.
5936      */
5937     AccessibleRole.PROGRESS_BAR,
5938 
5939     /**
5940      * A text object used for passwords, or other places where the
5941      * text contents is not shown visibly to the user
5942      */
5943     AccessibleRole.PASSWORD_TEXT,
5944 
5945     /**
5946      * An object the user can manipulate to tell the application to do
5947      * something.
5948      * @see #CHECK_BOX
5949      * @see #TOGGLE_BUTTON
5950      * @see #RADIO_BUTTON
5951      */
5952     AccessibleRole.PUSH_BUTTON,
5953 
5954     /**
5955      * A specialized push button that can be checked or unchecked, but
5956      * does not provide a separate indicator for the current state.
5957      * @see #PUSH_BUTTON
5958      * @see #CHECK_BOX
5959      * @see #RADIO_BUTTON
5960      */
5961     AccessibleRole.TOGGLE_BUTTON,
5962 
5963     /**
5964      * A choice that can be checked or unchecked and provides a
5965      * separate indicator for the current state.
5966      * @see #PUSH_BUTTON
5967      * @see #TOGGLE_BUTTON
5968      * @see #RADIO_BUTTON
5969      */
5970     AccessibleRole.CHECK_BOX,
5971 
5972     /**
5973      * A specialized check box that will cause other radio buttons in the
5974      * same group to become unchecked when this one is checked.
5975      * @see #PUSH_BUTTON
5976      * @see #TOGGLE_BUTTON
5977      * @see #CHECK_BOX
5978      */
5979     AccessibleRole.RADIO_BUTTON,
5980 
5981     /**
5982      * The header for a row of data.
5983      */
5984     AccessibleRole.ROW_HEADER,
5985 
5986     /**
5987      * An object that allows a user to incrementally view a large amount
5988      * of information.  Its children can include scroll bars and a viewport.
5989      * @see #SCROLL_BAR
5990      * @see #VIEWPORT
5991      */
5992     AccessibleRole.SCROLL_PANE,
5993 
5994     /**
5995      * An object usually used to allow a user to incrementally view a
5996      * large amount of data.  Usually used only by a scroll pane.
5997      * @see #SCROLL_PANE
5998      */
5999     AccessibleRole.SCROLL_BAR,
6000 
6001     /**
6002      * An object usually used in a scroll pane.  It represents the portion
6003      * of the entire data that the user can see.  As the user manipulates
6004      * the scroll bars, the contents of the viewport can change.
6005      * @see #SCROLL_PANE
6006      */
6007     AccessibleRole.VIEWPORT,
6008 
6009     /**
6010      * An object that allows the user to select from a bounded range.  For
6011      * example, a slider might be used to select a number between 0 and 100.
6012      */
6013     AccessibleRole.SLIDER,
6014 
6015     /**
6016      * A specialized panel that presents two other panels at the same time.
6017      * Between the two panels is a divider the user can manipulate to make
6018      * one panel larger and the other panel smaller.
6019      */
6020     AccessibleRole.SPLIT_PANE,
6021 
6022     /**
6023      * An object used to present information in terms of rows and columns.
6024      * An example might include a spreadsheet application.
6025      */
6026     AccessibleRole.TABLE,
6027 
6028     /**
6029      * An object that presents text to the user.  The text is usually
6030      * editable by the user as opposed to a label.
6031      * @see #LABEL
6032      */
6033     AccessibleRole.TEXT,
6034 
6035     /**
6036      * An object used to present hierarchical information to the user.
6037      * The individual nodes in the tree can be collapsed and expanded
6038      * to provide selective disclosure of the tree's contents.
6039      */
6040     AccessibleRole.TREE,
6041 
6042     /**
6043      * A bar or palette usually composed of push buttons or toggle buttons.
6044      * It is often used to provide the most frequently used functions for an
6045      * application.
6046      */
6047     AccessibleRole.TOOL_BAR,
6048 
6049     /**
6050      * An object that provides information about another object.  The
6051      * accessibleDescription property of the tool tip is often displayed
6052      * to the user in a small "help bubble" when the user causes the
6053      * mouse to hover over the object associated with the tool tip.
6054      */
6055     AccessibleRole.TOOL_TIP,
6056 
6057     /**
6058      * An AWT component, but nothing else is known about it.
6059      * @see #SWING_COMPONENT
6060      * @see #UNKNOWN
6061      */
6062     AccessibleRole.AWT_COMPONENT,
6063 
6064     /**
6065      * A Swing component, but nothing else is known about it.
6066      * @see #AWT_COMPONENT
6067      * @see #UNKNOWN
6068      */
6069     AccessibleRole.SWING_COMPONENT,
6070 
6071     /**
6072      * The object contains some Accessible information, but its role is
6073      * not known.
6074      * @see #AWT_COMPONENT
6075      * @see #SWING_COMPONENT
6076      */
6077     AccessibleRole.UNKNOWN,
6078 
6079     // These roles are available since JDK 1.4
6080 
6081     /**
6082      * A STATUS_BAR is an simple component that can contain
6083      * multiple labels of status information to the user.
6084      AccessibleRole.STATUS_BAR,
6085 
6086      /**
6087      * A DATE_EDITOR is a component that allows users to edit
6088      * java.util.Date and java.util.Time objects
6089      AccessibleRole.DATE_EDITOR,
6090 
6091      /**
6092      * A SPIN_BOX is a simple spinner component and its main use
6093      * is for simple numbers.
6094      AccessibleRole.SPIN_BOX,
6095 
6096      /**
6097      * A FONT_CHOOSER is a component that lets the user pick various
6098      * attributes for fonts.
6099      AccessibleRole.FONT_CHOOSER,
6100 
6101      /**
6102      * A GROUP_BOX is a simple container that contains a border
6103      * around it and contains components inside it.
6104      AccessibleRole.GROUP_BOX
6105 
6106      /**
6107      * Since JDK 1.5
6108      *
6109      * A text header
6110 
6111      AccessibleRole.HEADER,
6112 
6113      /**
6114      * A text footer
6115 
6116      AccessibleRole.FOOTER,
6117 
6118      /**
6119      * A text paragraph
6120 
6121      AccessibleRole.PARAGRAPH,
6122 
6123      /**
6124      * A ruler is an object used to measure distance
6125 
6126      AccessibleRole.RULER,
6127 
6128      /**
6129      * A role indicating the object acts as a formula for
6130      * calculating a value.  An example is a formula in
6131      * a spreadsheet cell.
6132      AccessibleRole.EDITBAR
6133     */
6134     };
6135 
6136     /**
6137      * This class implements accessibility support for the
6138      * <code>JTree</code> child.  It provides an implementation of the
6139      * Java Accessibility API appropriate to tree nodes.
6140      *
6141      * Copied from JTree.java to work around a JTree bug where
6142      * ActiveDescendent PropertyChangeEvents contain the wrong
6143      * parent.
6144      */
6145     /**
6146      * This class in invoked on the EDT as its part of ActiveDescendant,
6147      * hence the calls do not need to be specifically made on the EDT
6148      */
6149     private class AccessibleJTreeNode extends AccessibleContext
6150         implements Accessible, AccessibleComponent, AccessibleSelection,
6151                    AccessibleAction {
6152 
6153         private JTree tree = null;
6154         private TreeModel treeModel = null;
6155         private Object obj = null;
6156         private TreePath path = null;
6157         private Accessible accessibleParent = null;
6158         private int index = 0;
6159         private boolean isLeaf = false;
6160 
6161         /**
6162          *  Constructs an AccessibleJTreeNode
6163          */
6164         AccessibleJTreeNode(JTree t, TreePath p, Accessible ap) {
6165             tree = t;
6166             path = p;
6167             accessibleParent = ap;
6168             if (t != null)
6169                 treeModel = t.getModel();
6170             if (p != null) {
6171                 obj = p.getLastPathComponent();
6172                 if (treeModel != null && obj != null) {
6173                     isLeaf = treeModel.isLeaf(obj);
6174                 }
6175             }
6176             debugString("AccessibleJTreeNode: name = "+getAccessibleName()+"; TreePath = "+p+"; parent = "+ap);
6177         }
6178 
6179         private TreePath getChildTreePath(int i) {
6180             // Tree nodes can't be so complex that they have
6181             // two sets of children -> we're ignoring that case
6182             if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) {
6183                 return null;
6184             } else {
6185                 Object childObj = treeModel.getChild(obj, i);
6186                 Object[] objPath = path.getPath();
6187                 Object[] objChildPath = new Object[objPath.length+1];
6188                 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
6189                 objChildPath[objChildPath.length-1] = childObj;
6190                 return new TreePath(objChildPath);
6191             }
6192         }
6193 
6194         /**
6195          * Get the AccessibleContext associated with this tree node.
6196          * In the implementation of the Java Accessibility API for
6197          * this class, return this object, which is its own
6198          * AccessibleContext.
6199          *
6200          * @return this object
6201         */
6202         public AccessibleContext getAccessibleContext() {
6203             return this;
6204         }
6205 
6206         private AccessibleContext getCurrentAccessibleContext() {
6207             Component c = getCurrentComponent();
6208             if (c instanceof Accessible) {
6209                return (c.getAccessibleContext());
6210             } else {
6211                 return null;
6212             }
6213         }
6214 
6215         private Component getCurrentComponent() {
6216             debugString("AccessibleJTreeNode: getCurrentComponent");
6217             // is the object visible?
6218             // if so, get row, selected, focus & leaf state,
6219             // and then get the renderer component and return it
6220             if (tree != null && tree.isVisible(path)) {
6221                 TreeCellRenderer r = tree.getCellRenderer();
6222                 if (r == null) {
6223                     debugString("  returning null 1");
6224                     return null;
6225                 }
6226                 TreeUI ui = tree.getUI();
6227                 if (ui != null) {
6228                     int row = ui.getRowForPath(tree, path);
6229                     boolean selected = tree.isPathSelected(path);
6230                     boolean expanded = tree.isExpanded(path);
6231                     boolean hasFocus = false; // how to tell?? -PK
6232                     Component retval = r.getTreeCellRendererComponent(tree, obj,
6233                                                                       selected, expanded,
6234                                                                       isLeaf, row, hasFocus);
6235                     debugString("  returning = "+retval.getClass());
6236                     return retval;
6237                 }
6238             }
6239             debugString("  returning null 2");
6240             return null;
6241         }
6242 
6243         // AccessibleContext methods
6244 
6245         /**
6246          * Get the accessible name of this object.
6247          *
6248          * @return the localized name of the object; null if this
6249          * object does not have a name
6250          */
6251         public String getAccessibleName() {
6252             debugString("AccessibleJTreeNode: getAccessibleName");
6253             AccessibleContext ac = getCurrentAccessibleContext();
6254             if (ac != null) {
6255                 String name = ac.getAccessibleName();
6256                 if ((name != null) && (!name.isEmpty())) {
6257                     String retval = ac.getAccessibleName();
6258                     debugString("    returning "+retval);
6259                     return retval;
6260                 } else {
6261                     return null;
6262                 }
6263             }
6264             if ((accessibleName != null) && (accessibleName.isEmpty())) {
6265                 return accessibleName;
6266             } else {
6267                 return null;
6268             }
6269         }
6270 
6271         /**
6272          * Set the localized accessible name of this object.
6273          *
6274          * @param s the new localized name of the object.
6275          */
6276         public void setAccessibleName(String s) {
6277             AccessibleContext ac = getCurrentAccessibleContext();
6278             if (ac != null) {
6279                 ac.setAccessibleName(s);
6280             } else {
6281                 super.setAccessibleName(s);
6282             }
6283         }
6284 
6285         //
6286         // *** should check tooltip text for desc. (needs MouseEvent)
6287         //
6288         /**
6289          * Get the accessible description of this object.
6290          *
6291          * @return the localized description of the object; null if
6292          * this object does not have a description
6293          */
6294         public String getAccessibleDescription() {
6295             AccessibleContext ac = getCurrentAccessibleContext();
6296             if (ac != null) {
6297                 return ac.getAccessibleDescription();
6298             } else {
6299                 return super.getAccessibleDescription();
6300             }
6301         }
6302 
6303         /**
6304          * Set the accessible description of this object.
6305          *
6306          * @param s the new localized description of the object
6307          */
6308         public void setAccessibleDescription(String s) {
6309             AccessibleContext ac = getCurrentAccessibleContext();
6310             if (ac != null) {
6311                 ac.setAccessibleDescription(s);
6312             } else {
6313                 super.setAccessibleDescription(s);
6314             }
6315         }
6316 
6317         /**
6318          * Get the role of this object.
6319          *
6320          * @return an instance of AccessibleRole describing the role of the object
6321          * @see AccessibleRole
6322          */
6323         public AccessibleRole getAccessibleRole() {
6324             AccessibleContext ac = getCurrentAccessibleContext();
6325             if (ac != null) {
6326                 return ac.getAccessibleRole();
6327             } else {
6328                 return AccessibleRole.UNKNOWN;
6329             }
6330         }
6331 
6332         /**
6333          * Get the state set of this object.
6334          *
6335          * @return an instance of AccessibleStateSet containing the
6336          * current state set of the object
6337          * @see AccessibleState
6338          */
6339         public AccessibleStateSet getAccessibleStateSet() {
6340             if (tree == null)
6341                 return null;
6342             AccessibleContext ac = getCurrentAccessibleContext();
6343             AccessibleStateSet states;
6344             int row = tree.getUI().getRowForPath(tree,path);
6345             int lsr = tree.getLeadSelectionRow();
6346             if (ac != null) {
6347                 states = ac.getAccessibleStateSet();
6348             } else {
6349                 states = new AccessibleStateSet();
6350             }
6351             // need to test here, 'cause the underlying component
6352             // is a cellRenderer, which is never showing...
6353             if (isShowing()) {
6354                 states.add(AccessibleState.SHOWING);
6355             } else if (states.contains(AccessibleState.SHOWING)) {
6356                 states.remove(AccessibleState.SHOWING);
6357             }
6358             if (isVisible()) {
6359                 states.add(AccessibleState.VISIBLE);
6360             } else if (states.contains(AccessibleState.VISIBLE)) {
6361                 states.remove(AccessibleState.VISIBLE);
6362             }
6363             if (tree.isPathSelected(path)){
6364                 states.add(AccessibleState.SELECTED);
6365             }
6366             if (lsr == row) {
6367                 states.add(AccessibleState.ACTIVE);
6368             }
6369             if (!isLeaf) {
6370                 states.add(AccessibleState.EXPANDABLE);
6371             }
6372             if (tree.isExpanded(path)) {
6373                 states.add(AccessibleState.EXPANDED);
6374             } else {
6375                 states.add(AccessibleState.COLLAPSED);
6376             }
6377             if (tree.isEditable()) {
6378                 states.add(AccessibleState.EDITABLE);
6379             }
6380             return states;
6381         }
6382 
6383         /**
6384          * Get the Accessible parent of this object.
6385          *
6386          * @return the Accessible parent of this object; null if this
6387          * object does not have an Accessible parent
6388          */
6389         public Accessible getAccessibleParent() {
6390             // someone wants to know, so we need to create our parent
6391             // if we don't have one (hey, we're a talented kid!)
6392             if (accessibleParent == null && path != null) {
6393                 Object[] objPath = path.getPath();
6394                 if (objPath.length > 1) {
6395                     Object objParent = objPath[objPath.length-2];
6396                     if (treeModel != null) {
6397                         index = treeModel.getIndexOfChild(objParent, obj);
6398                     }
6399                     Object[] objParentPath = new Object[objPath.length-1];
6400                     java.lang.System.arraycopy(objPath, 0, objParentPath,
6401                                                0, objPath.length-1);
6402                     TreePath parentPath = new TreePath(objParentPath);
6403                     accessibleParent = new AccessibleJTreeNode(tree,
6404                                                                parentPath,
6405                                                                null);
6406                     this.setAccessibleParent(accessibleParent);
6407                 } else if (treeModel != null) {
6408                     accessibleParent = tree; // we're the top!
6409                     index = 0; // we're an only child!
6410                     this.setAccessibleParent(accessibleParent);
6411                 }
6412             }
6413             return accessibleParent;
6414         }
6415 
6416         /**
6417          * Get the index of this object in its accessible parent.
6418          *
6419          * @return the index of this object in its parent; -1 if this
6420          * object does not have an accessible parent.
6421          * @see #getAccessibleParent
6422          */
6423         public int getAccessibleIndexInParent() {
6424             // index is invalid 'till we have an accessibleParent...
6425             if (accessibleParent == null) {
6426                 getAccessibleParent();
6427             }
6428             if (path != null) {
6429                 Object[] objPath = path.getPath();
6430                 if (objPath.length > 1) {
6431                     Object objParent = objPath[objPath.length-2];
6432                     if (treeModel != null) {
6433                         index = treeModel.getIndexOfChild(objParent, obj);
6434                     }
6435                 }
6436             }
6437             return index;
6438         }
6439 
6440         /**
6441          * Returns the number of accessible children in the object.
6442          *
6443          * @return the number of accessible children in the object.
6444          */
6445         public int getAccessibleChildrenCount() {
6446             // Tree nodes can't be so complex that they have
6447             // two sets of children -> we're ignoring that case
6448             if (obj != null && treeModel != null) {
6449                 return treeModel.getChildCount(obj);
6450             }
6451             return 0;
6452         }
6453 
6454         /**
6455          * Return the specified Accessible child of the object.
6456          *
6457          * @param i zero-based index of child
6458          * @return the Accessible child of the object
6459          */
6460         public Accessible getAccessibleChild(int i) {
6461             // Tree nodes can't be so complex that they have
6462             // two sets of children -> we're ignoring that case
6463             if (i < 0 || i >= getAccessibleChildrenCount() || path == null || treeModel == null) {
6464                 return null;
6465             } else {
6466                 Object childObj = treeModel.getChild(obj, i);
6467                 Object[] objPath = path.getPath();
6468                 Object[] objChildPath = new Object[objPath.length+1];
6469                 java.lang.System.arraycopy(objPath, 0, objChildPath, 0, objPath.length);
6470                 objChildPath[objChildPath.length-1] = childObj;
6471                 TreePath childPath = new TreePath(objChildPath);
6472                 return new AccessibleJTreeNode(tree, childPath, this);
6473             }
6474         }
6475 
6476         /**
6477          * Gets the locale of the component. If the component does not have
6478          * a locale, then the locale of its parent is returned.
6479          *
6480          * @return This component's locale. If this component does not have
6481          * a locale, the locale of its parent is returned.
6482          * @exception IllegalComponentStateException
6483          * If the Component does not have its own locale and has not yet
6484          * been added to a containment hierarchy such that the locale can be
6485          * determined from the containing parent.
6486          * @see #setLocale
6487          */
6488         public Locale getLocale() {
6489             if (tree == null)
6490                 return null;
6491             AccessibleContext ac = getCurrentAccessibleContext();
6492             if (ac != null) {
6493                 return ac.getLocale();
6494             } else {
6495                 return tree.getLocale();
6496             }
6497         }
6498 
6499         /**
6500          * Add a PropertyChangeListener to the listener list.
6501          * The listener is registered for all properties.
6502          *
6503          * @param l  The PropertyChangeListener to be added
6504          */
6505         public void addPropertyChangeListener(PropertyChangeListener l) {
6506             AccessibleContext ac = getCurrentAccessibleContext();
6507             if (ac != null) {
6508                 ac.addPropertyChangeListener(l);
6509             } else {
6510                 super.addPropertyChangeListener(l);
6511             }
6512         }
6513 
6514         /**
6515          * Remove a PropertyChangeListener from the listener list.
6516          * This removes a PropertyChangeListener that was registered
6517          * for all properties.
6518          *
6519          * @param l  The PropertyChangeListener to be removed
6520          */
6521         public void removePropertyChangeListener(PropertyChangeListener l) {
6522             AccessibleContext ac = getCurrentAccessibleContext();
6523             if (ac != null) {
6524                 ac.removePropertyChangeListener(l);
6525             } else {
6526                 super.removePropertyChangeListener(l);
6527             }
6528         }
6529 
6530         /**
6531          * Get the AccessibleAction associated with this object.  In the
6532          * implementation of the Java Accessibility API for this class,
6533          * return this object, which is responsible for implementing the
6534          * AccessibleAction interface on behalf of itself.
6535          *
6536          * @return this object
6537          */
6538         public AccessibleAction getAccessibleAction() {
6539             return this;
6540         }
6541 
6542         /**
6543          * Get the AccessibleComponent associated with this object.  In the
6544          * implementation of the Java Accessibility API for this class,
6545          * return this object, which is responsible for implementing the
6546          * AccessibleComponent interface on behalf of itself.
6547          *
6548          * @return this object
6549          */
6550         public AccessibleComponent getAccessibleComponent() {
6551             return this; // to override getBounds()
6552         }
6553 
6554         /**
6555          * Get the AccessibleSelection associated with this object if one
6556          * exists.  Otherwise return null.
6557          *
6558          * @return the AccessibleSelection, or null
6559          */
6560         public AccessibleSelection getAccessibleSelection() {
6561             AccessibleContext ac = getCurrentAccessibleContext();
6562             if (ac != null && isLeaf) {
6563                 return getCurrentAccessibleContext().getAccessibleSelection();
6564             } else {
6565                 return this;
6566             }
6567         }
6568 
6569         /**
6570          * Get the AccessibleText associated with this object if one
6571          * exists.  Otherwise return null.
6572          *
6573          * @return the AccessibleText, or null
6574          */
6575         public AccessibleText getAccessibleText() {
6576             AccessibleContext ac = getCurrentAccessibleContext();
6577             if (ac != null) {
6578                 return getCurrentAccessibleContext().getAccessibleText();
6579             } else {
6580                 return null;
6581             }
6582         }
6583 
6584         /**
6585          * Get the AccessibleValue associated with this object if one
6586          * exists.  Otherwise return null.
6587          *
6588          * @return the AccessibleValue, or null
6589          */
6590         public AccessibleValue getAccessibleValue() {
6591             AccessibleContext ac = getCurrentAccessibleContext();
6592             if (ac != null) {
6593                 return getCurrentAccessibleContext().getAccessibleValue();
6594             } else {
6595                 return null;
6596             }
6597         }
6598 
6599 
6600             // AccessibleComponent methods
6601 
6602         /**
6603          * Get the background color of this object.
6604          *
6605          * @return the background color, if supported, of the object;
6606          * otherwise, null
6607          */
6608         public Color getBackground() {
6609             AccessibleContext ac = getCurrentAccessibleContext();
6610             if (ac instanceof AccessibleComponent) {
6611                 return ((AccessibleComponent) ac).getBackground();
6612             } else {
6613                 Component c = getCurrentComponent();
6614                 if (c != null) {
6615                     return c.getBackground();
6616                 } else {
6617                     return null;
6618                 }
6619             }
6620         }
6621 
6622         /**
6623          * Set the background color of this object.
6624          *
6625          * @param c the new Color for the background
6626          */
6627         public void setBackground(Color c) {
6628             AccessibleContext ac = getCurrentAccessibleContext();
6629             if (ac instanceof AccessibleComponent) {
6630                 ((AccessibleComponent) ac).setBackground(c);
6631             } else {
6632                 Component cp = getCurrentComponent();
6633                 if (    cp != null) {
6634                     cp.setBackground(c);
6635                 }
6636             }
6637         }
6638 
6639 
6640         /**
6641          * Get the foreground color of this object.
6642          *
6643          * @return the foreground color, if supported, of the object;
6644          * otherwise, null
6645          */
6646         public Color getForeground() {
6647             AccessibleContext ac = getCurrentAccessibleContext();
6648             if (ac instanceof AccessibleComponent) {
6649                 return ((AccessibleComponent) ac).getForeground();
6650             } else {
6651                 Component c = getCurrentComponent();
6652                 if (c != null) {
6653                     return c.getForeground();
6654                 } else {
6655                     return null;
6656                 }
6657             }
6658         }
6659 
6660         public void setForeground(Color c) {
6661             AccessibleContext ac = getCurrentAccessibleContext();
6662             if (ac instanceof AccessibleComponent) {
6663                 ((AccessibleComponent) ac).setForeground(c);
6664             } else {
6665                 Component cp = getCurrentComponent();
6666                 if (cp != null) {
6667                     cp.setForeground(c);
6668                 }
6669             }
6670         }
6671 
6672         public Cursor getCursor() {
6673             AccessibleContext ac = getCurrentAccessibleContext();
6674             if (ac instanceof AccessibleComponent) {
6675                 return ((AccessibleComponent) ac).getCursor();
6676             } else {
6677                 Component c = getCurrentComponent();
6678                 if (c != null) {
6679                     return c.getCursor();
6680                 } else {
6681                     Accessible ap = getAccessibleParent();
6682                     if (ap instanceof AccessibleComponent) {
6683                         return ((AccessibleComponent) ap).getCursor();
6684                     } else {
6685                         return null;
6686                     }
6687                 }
6688             }
6689         }
6690 
6691         public void setCursor(Cursor c) {
6692             AccessibleContext ac = getCurrentAccessibleContext();
6693             if (ac instanceof AccessibleComponent) {
6694                 ((AccessibleComponent) ac).setCursor(c);
6695             } else {
6696                 Component cp = getCurrentComponent();
6697                 if (cp != null) {
6698                     cp.setCursor(c);
6699                 }
6700             }
6701         }
6702 
6703         public Font getFont() {
6704             AccessibleContext ac = getCurrentAccessibleContext();
6705             if (ac instanceof AccessibleComponent) {
6706                 return ((AccessibleComponent) ac).getFont();
6707             } else {
6708                 Component c = getCurrentComponent();
6709                 if (c != null) {
6710                     return c.getFont();
6711                 } else {
6712                     return null;
6713                 }
6714             }
6715         }
6716 
6717         public void setFont(Font f) {
6718             AccessibleContext ac = getCurrentAccessibleContext();
6719             if (ac instanceof AccessibleComponent) {
6720                 ((AccessibleComponent) ac).setFont(f);
6721             } else {
6722                 Component c = getCurrentComponent();
6723                 if (c != null) {
6724                     c.setFont(f);
6725                 }
6726             }
6727         }
6728 
6729         public FontMetrics getFontMetrics(Font f) {
6730             AccessibleContext ac = getCurrentAccessibleContext();
6731             if (ac instanceof AccessibleComponent) {
6732                 return ((AccessibleComponent) ac).getFontMetrics(f);
6733             } else {
6734                 Component c = getCurrentComponent();
6735                 if (c != null) {
6736                     return c.getFontMetrics(f);
6737                 } else {
6738                     return null;
6739                 }
6740             }
6741         }
6742 
6743         public boolean isEnabled() {
6744             AccessibleContext ac = getCurrentAccessibleContext();
6745             if (ac instanceof AccessibleComponent) {
6746                 return ((AccessibleComponent) ac).isEnabled();
6747             } else {
6748                 Component c = getCurrentComponent();
6749                 if (c != null) {
6750                     return c.isEnabled();
6751                 } else {
6752                     return false;
6753                 }
6754             }
6755         }
6756 
6757         public void setEnabled(boolean b) {
6758             AccessibleContext ac = getCurrentAccessibleContext();
6759             if (ac instanceof AccessibleComponent) {
6760                 ((AccessibleComponent) ac).setEnabled(b);
6761             } else {
6762                 Component c = getCurrentComponent();
6763                 if (c != null) {
6764                     c.setEnabled(b);
6765                 }
6766             }
6767         }
6768 
6769         public boolean isVisible() {
6770             if (tree == null)
6771                 return false;
6772             Rectangle pathBounds = tree.getPathBounds(path);
6773             Rectangle parentBounds = tree.getVisibleRect();
6774             if ( pathBounds != null && parentBounds != null &&
6775                  parentBounds.intersects(pathBounds) ) {
6776                 return true;
6777             } else {
6778                 return false;
6779             }
6780         }
6781 
6782         public void setVisible(boolean b) {
6783         }
6784 
6785         public boolean isShowing() {
6786             return (tree.isShowing() && isVisible());
6787         }
6788 
6789         public boolean contains(Point p) {
6790             AccessibleContext ac = getCurrentAccessibleContext();
6791             if (ac instanceof AccessibleComponent) {
6792                 Rectangle r = ((AccessibleComponent) ac).getBounds();
6793                 return r.contains(p);
6794             } else {
6795                 Component c = getCurrentComponent();
6796                 if (c != null) {
6797                     Rectangle r = c.getBounds();
6798                     return r.contains(p);
6799                 } else {
6800                     return getBounds().contains(p);
6801                 }
6802             }
6803         }
6804 
6805         public Point getLocationOnScreen() {
6806             if (tree != null) {
6807                 Point treeLocation = tree.getLocationOnScreen();
6808                 Rectangle pathBounds = tree.getPathBounds(path);
6809                 if (treeLocation != null && pathBounds != null) {
6810                     Point nodeLocation = new Point(pathBounds.x,
6811                                                    pathBounds.y);
6812                     nodeLocation.translate(treeLocation.x, treeLocation.y);
6813                     return nodeLocation;
6814                 } else {
6815                     return null;
6816                 }
6817             } else {
6818                 return null;
6819             }
6820         }
6821 
6822         private Point getLocationInJTree() {
6823             Rectangle r = tree.getPathBounds(path);
6824             if (r != null) {
6825                 return r.getLocation();
6826             } else {
6827                 return null;
6828             }
6829         }
6830 
6831         public Point getLocation() {
6832             Rectangle r = getBounds();
6833             if (r != null) {
6834                 return r.getLocation();
6835             } else {
6836                 return null;
6837             }
6838         }
6839 
6840         public void setLocation(Point p) {
6841         }
6842 
6843         public Rectangle getBounds() {
6844             if (tree == null)
6845                 return null;
6846             Rectangle r = tree.getPathBounds(path);
6847             Accessible parent = getAccessibleParent();
6848             if (parent instanceof AccessibleJTreeNode) {
6849                 Point parentLoc = ((AccessibleJTreeNode) parent).getLocationInJTree();
6850                 if (parentLoc != null && r != null) {
6851                     r.translate(-parentLoc.x, -parentLoc.y);
6852                 } else {
6853                     return null;        // not visible!
6854                 }
6855             }
6856             return r;
6857         }
6858 
6859         public void setBounds(Rectangle r) {
6860             AccessibleContext ac = getCurrentAccessibleContext();
6861             if (ac instanceof AccessibleComponent) {
6862                 ((AccessibleComponent) ac).setBounds(r);
6863             } else {
6864                 Component c = getCurrentComponent();
6865                 if (c != null) {
6866                     c.setBounds(r);
6867                 }
6868             }
6869         }
6870 
6871         public Dimension getSize() {
6872             return getBounds().getSize();
6873         }
6874 
6875         public void setSize (Dimension d) {
6876             AccessibleContext ac = getCurrentAccessibleContext();
6877             if (ac instanceof AccessibleComponent) {
6878                 ((AccessibleComponent) ac).setSize(d);
6879             } else {
6880                 Component c = getCurrentComponent();
6881                 if (c != null) {
6882                     c.setSize(d);
6883                 }
6884             }
6885         }
6886 
6887         /**
6888         * Returns the <code>Accessible</code> child, if one exists,
6889         * contained at the local coordinate <code>Point</code>.
6890         * Otherwise returns <code>null</code>.
6891         *
6892         * @param p point in local coordinates of this
6893         *    <code>Accessible</code>
6894         * @return the <code>Accessible</code>, if it exists,
6895         *    at the specified location; else <code>null</code>
6896         */
6897         public Accessible getAccessibleAt(Point p) {
6898             AccessibleContext ac = getCurrentAccessibleContext();
6899             if (ac instanceof AccessibleComponent) {
6900                 return ((AccessibleComponent) ac).getAccessibleAt(p);
6901             } else {
6902                 return null;
6903             }
6904         }
6905 
6906         public boolean isFocusTraversable() {
6907             AccessibleContext ac = getCurrentAccessibleContext();
6908             if (ac instanceof AccessibleComponent) {
6909                 return ((AccessibleComponent) ac).isFocusTraversable();
6910             } else {
6911                 Component c = getCurrentComponent();
6912                 if (c != null) {
6913                     return c.isFocusable();
6914                 } else {
6915                     return false;
6916                 }
6917             }
6918         }
6919 
6920         public void requestFocus() {
6921             AccessibleContext ac = getCurrentAccessibleContext();
6922             if (ac instanceof AccessibleComponent) {
6923                 ((AccessibleComponent) ac).requestFocus();
6924             } else {
6925                 Component c = getCurrentComponent();
6926                 if (c != null) {
6927                     c.requestFocus();
6928                 }
6929             }
6930         }
6931 
6932         public void addFocusListener(FocusListener l) {
6933             AccessibleContext ac = getCurrentAccessibleContext();
6934             if (ac instanceof AccessibleComponent) {
6935                 ((AccessibleComponent) ac).addFocusListener(l);
6936             } else {
6937                 Component c = getCurrentComponent();
6938                 if (c != null) {
6939                     c.addFocusListener(l);
6940                 }
6941             }
6942         }
6943 
6944         public void removeFocusListener(FocusListener l) {
6945             AccessibleContext ac = getCurrentAccessibleContext();
6946             if (ac instanceof AccessibleComponent) {
6947                 ((AccessibleComponent) ac).removeFocusListener(l);
6948             } else {
6949                 Component c = getCurrentComponent();
6950                 if (c != null) {
6951                     c.removeFocusListener(l);
6952                 }
6953             }
6954         }
6955 
6956             // AccessibleSelection methods
6957 
6958         /**
6959          * Returns the number of items currently selected.
6960          * If no items are selected, the return value will be 0.
6961          *
6962          * @return the number of items currently selected.
6963          */
6964         public int getAccessibleSelectionCount() {
6965             int count = 0;
6966             int childCount = getAccessibleChildrenCount();
6967             for (int i = 0; i < childCount; i++) {
6968                 TreePath childPath = getChildTreePath(i);
6969                 if (tree.isPathSelected(childPath)) {
6970                     count++;
6971                 }
6972             }
6973             return count;
6974         }
6975 
6976         /**
6977          * Returns an Accessible representing the specified selected item
6978          * in the object.  If there isn't a selection, or there are
6979          * fewer items selected than the integer passed in, the return
6980          * value will be null.
6981          *
6982          * @param i the zero-based index of selected items
6983          * @return an Accessible containing the selected item
6984          */
6985         public Accessible getAccessibleSelection(int i) {
6986             int childCount = getAccessibleChildrenCount();
6987             if (i < 0 || i >= childCount) {
6988                 return null;        // out of range
6989             }
6990             int count = 0;
6991             for (int j = 0; j < childCount && i >= count; j++) {
6992                 TreePath childPath = getChildTreePath(j);
6993                 if (tree.isPathSelected(childPath)) {
6994                     if (count == i) {
6995                         return new AccessibleJTreeNode(tree, childPath, this);
6996                     } else {
6997                         count++;
6998                     }
6999                 }
7000             }
7001             return null;
7002         }
7003 
7004         /**
7005          * Returns true if the current child of this object is selected.
7006          *
7007          * @param i the zero-based index of the child in this Accessible
7008          * object.
7009          * @see AccessibleContext#getAccessibleChild
7010          */
7011         public boolean isAccessibleChildSelected(int i) {
7012             int childCount = getAccessibleChildrenCount();
7013             if (i < 0 || i >= childCount) {
7014                 return false;       // out of range
7015             } else {
7016                 TreePath childPath = getChildTreePath(i);
7017                 return tree.isPathSelected(childPath);
7018             }
7019         }
7020 
7021          /**
7022          * Adds the specified selected item in the object to the object's
7023          * selection.  If the object supports multiple selections,
7024          * the specified item is added to any existing selection, otherwise
7025          * it replaces any existing selection in the object.  If the
7026          * specified item is already selected, this method has no effect.
7027          *
7028          * @param i the zero-based index of selectable items
7029          */
7030         public void addAccessibleSelection(int i) {
7031             if (tree == null)
7032                 return;
7033             TreeModel model = tree.getModel();
7034             if (model != null) {
7035                 if (i >= 0 && i < getAccessibleChildrenCount()) {
7036                     TreePath path = getChildTreePath(i);
7037                     tree.addSelectionPath(path);
7038                 }
7039             }
7040         }
7041 
7042         /**
7043          * Removes the specified selected item in the object from the
7044          * object's
7045          * selection.  If the specified item isn't currently selected, this
7046          * method has no effect.
7047          *
7048          * @param i the zero-based index of selectable items
7049          */
7050         public void removeAccessibleSelection(int i) {
7051             if (tree == null)
7052                 return;
7053             TreeModel model = tree.getModel();
7054             if (model != null) {
7055                 if (i >= 0 && i < getAccessibleChildrenCount()) {
7056                     TreePath path = getChildTreePath(i);
7057                     tree.removeSelectionPath(path);
7058                 }
7059             }
7060         }
7061 
7062         /**
7063          * Clears the selection in the object, so that nothing in the
7064          * object is selected.
7065          */
7066         public void clearAccessibleSelection() {
7067             int childCount = getAccessibleChildrenCount();
7068             for (int i = 0; i < childCount; i++) {
7069                 removeAccessibleSelection(i);
7070             }
7071         }
7072 
7073         /**
7074          * Causes every selected item in the object to be selected
7075          * if the object supports multiple selections.
7076          */
7077         public void selectAllAccessibleSelection() {
7078             if (tree == null)
7079                 return;
7080             TreeModel model = tree.getModel();
7081             if (model != null) {
7082                 int childCount = getAccessibleChildrenCount();
7083                 TreePath path;
7084                 for (int i = 0; i < childCount; i++) {
7085                     path = getChildTreePath(i);
7086                     tree.addSelectionPath(path);
7087                 }
7088             }
7089         }
7090 
7091             // AccessibleAction methods
7092 
7093         /**
7094          * Returns the number of accessible actions available in this
7095          * tree node.  If this node is not a leaf, there is at least
7096          * one action (toggle expand), in addition to any available
7097          * on the object behind the TreeCellRenderer.
7098          *
7099          * @return the number of Actions in this object
7100          */
7101         public int getAccessibleActionCount() {
7102             AccessibleContext ac = getCurrentAccessibleContext();
7103             if (ac != null) {
7104                 AccessibleAction aa = ac.getAccessibleAction();
7105                 if (aa != null) {
7106                     return (aa.getAccessibleActionCount() + (isLeaf ? 0 : 1));
7107                 }
7108             }
7109             return isLeaf ? 0 : 1;
7110         }
7111 
7112         /**
7113          * Return a description of the specified action of the tree node.
7114          * If this node is not a leaf, there is at least one action
7115          * description (toggle expand), in addition to any available
7116          * on the object behind the TreeCellRenderer.
7117          *
7118          * @param i zero-based index of the actions
7119          * @return a description of the action
7120          */
7121         public String getAccessibleActionDescription(int i) {
7122             if (i < 0 || i >= getAccessibleActionCount()) {
7123                 return null;
7124             }
7125             AccessibleContext ac = getCurrentAccessibleContext();
7126             if (i == 0) {
7127                 // TIGER - 4766636
7128                 // return AccessibleAction.TOGGLE_EXPAND;
7129                 return "toggle expand";
7130             } else if (ac != null) {
7131                 AccessibleAction aa = ac.getAccessibleAction();
7132                 if (aa != null) {
7133                     return aa.getAccessibleActionDescription(i - 1);
7134                 }
7135             }
7136             return null;
7137         }
7138 
7139         /**
7140          * Perform the specified Action on the tree node.  If this node
7141          * is not a leaf, there is at least one action which can be
7142          * done (toggle expand), in addition to any available on the
7143          * object behind the TreeCellRenderer.
7144          *
7145          * @param i zero-based index of actions
7146          * @return true if the the action was performed; else false.
7147          */
7148         public boolean doAccessibleAction(int i) {
7149             if (i < 0 || i >= getAccessibleActionCount()) {
7150                 return false;
7151             }
7152             AccessibleContext ac = getCurrentAccessibleContext();
7153             if (i == 0) {
7154                 if (tree.isExpanded(path)) {
7155                     tree.collapsePath(path);
7156                 } else {
7157                     tree.expandPath(path);
7158                 }
7159                 return true;
7160             } else if (ac != null) {
7161                 AccessibleAction aa = ac.getAccessibleAction();
7162                 if (aa != null) {
7163                     return aa.doAccessibleAction(i - 1);
7164                 }
7165             }
7166             return false;
7167         }
7168 
7169     } // inner class AccessibleJTreeNode
7170 
7171     /**
7172      * A helper class to perform {@code Callable} objects on the event dispatch thread appropriate
7173      * for the provided {@code AccessibleContext}.
7174      */
7175     private static class InvocationUtils {
7176 
7177         /**
7178          * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible}
7179          * and waits for it to finish blocking the caller thread.
7180          *
7181          * @param callable   the {@code Callable} to invoke
7182          * @param accessibleTable the {@code AccessibleExtendedTable} which would be used to find the right context
7183          *                   for the task execution
7184          * @param <T> type parameter for the result value
7185          *
7186          * @return the result of the {@code Callable} execution
7187          */
7188         public static <T> T invokeAndWait(final Callable<T> callable,
7189                                           final AccessibleExtendedTable accessibleTable) {
7190             if (accessibleTable instanceof AccessibleContext) {
7191                 return invokeAndWait(callable, (AccessibleContext)accessibleTable);
7192             }
7193             throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleTable);
7194         }
7195 
7196         /**
7197          * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Accessible}
7198          * and waits for it to finish blocking the caller thread.
7199          *
7200          * @param callable   the {@code Callable} to invoke
7201          * @param accessible the {@code Accessible} which would be used to find the right context
7202          *                   for the task execution
7203          * @param <T> type parameter for the result value
7204          *
7205          * @return the result of the {@code Callable} execution
7206          */
7207         public static <T> T invokeAndWait(final Callable<T> callable,
7208                                           final Accessible accessible) {
7209             if (accessible instanceof Component) {
7210                 return invokeAndWait(callable, (Component)accessible);
7211             }
7212             if (accessible instanceof AccessibleContext) {
7213                 // This case also covers the Translator
7214                 return invokeAndWait(callable, (AccessibleContext)accessible);
7215             }
7216             throw new RuntimeException("Unmapped Accessible used to dispatch event: " + accessible);
7217         }
7218 
7219         /**
7220          * Invokes a {@code Callable} in the {@code AppContext} of the given {@code Component}
7221          * and waits for it to finish blocking the caller thread.
7222          *
7223          * @param callable  the {@code Callable} to invoke
7224          * @param component the {@code Component} which would be used to find the right context
7225          *                  for the task execution
7226          * @param <T> type parameter for the result value
7227          *
7228          * @return the result of the {@code Callable} execution
7229          */
7230         public static <T> T invokeAndWait(final Callable<T> callable,
7231                                           final Component component) {
7232             return invokeAndWait(callable, SunToolkit.targetToAppContext(component));
7233         }
7234 
7235         /**
7236          * Invokes a {@code Callable} in the {@code AppContext} mapped to the given {@code AccessibleContext}
7237          * and waits for it to finish blocking the caller thread.
7238          *
7239          * @param callable the {@code Callable} to invoke
7240          * @param accessibleContext the {@code AccessibleContext} which would be used to determine the right
7241          *                          context for the task execution.
7242          * @param <T> type parameter for the result value
7243          *
7244          * @return the result of the {@code Callable} execution
7245          */
7246         public static <T> T invokeAndWait(final Callable<T> callable,
7247                                           final AccessibleContext accessibleContext) {
7248             AppContext targetContext = AWTAccessor.getAccessibleContextAccessor()
7249                     .getAppContext(accessibleContext);
7250             if (targetContext != null) {
7251                 return invokeAndWait(callable, targetContext);
7252             } else {
7253                 // Normally this should not happen, unmapped context provided and
7254                 // the target AppContext is unknown.
7255 
7256                 // Try to recover in case the context is a translator.
7257                 if (accessibleContext instanceof Translator) {
7258                     Object source = ((Translator)accessibleContext).getSource();
7259                     if (source instanceof Component) {
7260                         return invokeAndWait(callable, (Component)source);
7261                     }
7262                 }
7263             }
7264             throw new RuntimeException("Unmapped AccessibleContext used to dispatch event: " + accessibleContext);
7265         }
7266 
7267         private static <T> T invokeAndWait(final Callable<T> callable,
7268                                            final AppContext targetAppContext) {
7269             final CallableWrapper<T> wrapper = new CallableWrapper<T>(callable);
7270             try {
7271                 invokeAndWait(wrapper, targetAppContext);
7272                 T result = wrapper.getResult();
7273                 updateAppContextMap(result, targetAppContext);
7274                 return result;
7275             } catch (final Exception e) {
7276                 throw new RuntimeException(e);
7277             }
7278         }
7279 
7280         private static void invokeAndWait(final Runnable runnable,
7281                                         final AppContext appContext)
7282                 throws InterruptedException, InvocationTargetException {
7283 
7284             EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext);
7285             Object lock = new Object();
7286             Toolkit source = Toolkit.getDefaultToolkit();
7287             InvocationEvent event =
7288                     new InvocationEvent(source, runnable, lock, true);
7289             synchronized (lock) {
7290                 eq.postEvent(event);
7291                 lock.wait();
7292             }
7293 
7294             Throwable eventThrowable = event.getThrowable();
7295             if (eventThrowable != null) {
7296                 throw new InvocationTargetException(eventThrowable);
7297             }
7298         }
7299 
7300         /**
7301          * Maps the {@code AccessibleContext} to the {@code AppContext} which should be used
7302          * to dispatch events related to the {@code AccessibleContext}
7303          * @param accessibleContext the {@code AccessibleContext} for the mapping
7304          * @param targetContext the {@code AppContext} for the mapping
7305          */
7306         public static void registerAccessibleContext(final AccessibleContext accessibleContext,
7307                                                      final AppContext targetContext) {
7308             if (accessibleContext != null) {
7309                 AWTAccessor.getAccessibleContextAccessor().setAppContext(accessibleContext, targetContext);
7310             }
7311         }
7312 
7313         private static <T> void updateAppContextMap(final T accessibleContext,
7314                                                     final AppContext targetContext) {
7315             if (accessibleContext instanceof AccessibleContext) {
7316                 registerAccessibleContext((AccessibleContext)accessibleContext, targetContext);
7317             }
7318         }
7319 
7320         private static class CallableWrapper<T> implements Runnable {
7321             private final Callable<T> callable;
7322             private volatile T object;
7323             private Exception e;
7324 
7325             CallableWrapper(final Callable<T> callable) {
7326                 this.callable = callable;
7327             }
7328 
7329             public void run() {
7330                 try {
7331                     if (callable != null) {
7332                         object = callable.call();
7333                     }
7334                 } catch (final Exception e) {
7335                     this.e = e;
7336                 }
7337             }
7338 
7339             T getResult() throws Exception {
7340                 if (e != null)
7341                     throw e;
7342                 return object;
7343             }
7344         }
7345     }
7346 }