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