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