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