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