1/*
2 * Copyright (c) 2002, 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
26
27package com.sun.java.accessibility.util;
28
29import com.sun.java.accessibility.util.internal.*;
30import java.beans.*;
31import java.util.*;
32import java.awt.*;
33import java.awt.event.*;
34// Do not import Swing classes.  This module is intended to work
35// with both Swing and AWT.
36// import javax.swing.*;
37import javax.accessibility.*;
38
39/**
40 * <p>The {@code Translator} class provides a translation to interface
41 * {@link javax.accessibility.Accessible Accessible}
42 * for objects that do not implement interface {@code Accessible}.  Assistive
43 * technologies can use the {@link #getAccessible getAccessible} class method of
44 * {@code Translator} to obtain an object that implements interface {@code Accessible}.
45 * If the object passed in already implements interface {@code Accessible},
46 * {@code getAccessible} merely returns the object.
47 *
48 * <p>An example of how an assistive technology might use the {@code Translator}
49 * class is as follows:
50 *
51 * <PRE>
52 *    Accessible accessible = Translator.getAccessible(someObj);
53 *    // obtain information from the 'accessible' object.
54 * </PRE>
55 *
56 * <P>Note:  This implementation is missing many things and is not a recommended way
57 * to implement accessibility features for a toolkit.  Instead of relying upon this
58 * code, a toolkit's components should implement interface {@code Accessible} directly.
59 */
60public class Translator extends AccessibleContext
61        implements Accessible, AccessibleComponent {
62
63    /** The source object needing translating. */
64    protected Object source;
65
66    /**
67     * Find a translator for this class.  If one doesn't exist for this
68     * class explicitly, try its superclass and so on.
69     *
70     * @param c a Class
71     * @return the {@code Translator} Class for the Class passed in
72     */
73    protected static Class<?> getTranslatorClass(Class<?> c) {
74        Class<?> t = null;
75        if (c == null) {
76            return null;
77        }
78        switch (c.getSimpleName()) {
79            case "Button":
80                t = ButtonTranslator.class;
81                break;
82            case "Checkbox":
83                t = CheckboxTranslator.class;
84                break;
85            case "Label":
86                t = LabelTranslator.class;
87                break;
88            case "List":
89                t = ListTranslator.class;
90                break;
91            case "TextComponent":
92                t = TextComponentTranslator.class;
93                break;
94        }
95        if (t != null) {
96            return t;
97        } else {
98            return getTranslatorClass(c.getSuperclass());
99        }
100    }
101
102    /**
103     * Obtain an object that implements interface {@code Accessible}.  If the object
104     * passed in already implements interface {@code Accessible}, {@code getAccessible}
105     * merely returns the object.
106     *
107     * @param o an Object; if a null is passed in a null is returned
108     * @return an {@code Object}, possibly the {@code Object} passed in, that
109     *     implements the {@code Accessible} interface for the {@code Object}
110     *     which was passed in
111     */
112    public static Accessible getAccessible(Object o) {
113        Accessible a = null;
114
115        if (o == null) {
116            return null;
117        }
118        if (o instanceof Accessible) {
119            a = (Accessible)o;
120        } else {
121            Class<?> translatorClass = getTranslatorClass(o.getClass());
122            if (translatorClass != null) {
123                try {
124                    @SuppressWarnings("deprecation")
125                    Translator t = (Translator)translatorClass.newInstance();
126                    t.setSource(o);
127                    a = t;
128                } catch (Exception e) {
129                }
130            }
131        }
132        if (a == null) {
133            a = new Translator(o);
134        }
135        return a;
136    }
137
138    /**
139     * Create a new {@code Translator}.  You must call the {@link #setSource setSource}
140     * method to set the object to be translated after calling this constructor.
141     */
142    public Translator() {
143    }
144
145    /**
146     * Create a new {@code Translator} with the source object o.
147     *
148     * @param o the Component that does not implement interface
149     *     {@link javax.accessibility.Accessible Accessible}
150     */
151    public Translator(Object o) {
152        source = o;
153    }
154
155    /**
156     * Get the source {@code Object} of the {@code Translator}.
157     *
158     * @return the source {@code Object} of the {@code Translator}
159     */
160    public Object getSource() {
161        return source;
162    }
163
164    /**
165     * Set the source object of the {@code Translator}.
166     *
167     * @param o the Component that does not implement interface Accessible
168     */
169    public void setSource(Object o) {
170        source = o;
171    }
172
173    /**
174     * Returns true if this object is the same as the one passed in.
175     *
176     * @param o the {@code Object} to check against
177     * @return true if this is the same object
178     */
179    public boolean equals(Object o) {
180        if (o instanceof Translator) {
181            return java.util.Objects.equals(source, o);
182        } else {
183            return false;
184        }
185    }
186
187    /**
188     * Return hashcode.
189     *
190     * @return hashcode
191     */
192    public int hashCode() {
193        return java.util.Objects.hashCode(source);
194    }
195
196
197// Accessible methods
198
199    /**
200     * Returns this object.
201     */
202    public AccessibleContext getAccessibleContext() {
203        return this;
204    }
205
206// AccessibleContext methods
207
208    /**
209     * Get the accessible name of this object.
210     *
211     * @return the localized name of the object; can be null if this object
212     *     does not have a name
213     */
214    public String getAccessibleName() {
215        if (source instanceof MenuItem) {
216            return ((MenuItem) source).getLabel();
217        } else if (source instanceof Component) {
218            return ((Component) source).getName();
219        } else {
220            return null;
221        }
222    }
223
224    /**
225     * Set the name of this object.
226     */
227    public void setAccessibleName(String s) {
228        if (source instanceof MenuItem) {
229            ((MenuItem) source).setLabel(s);
230        } else if (source instanceof Component) {
231            ((Component) source).setName(s);
232        }
233    }
234
235    /**
236     * Get the accessible description of this object.
237     *
238     * @return the description of the object; can be null if this object does
239     * not have a description
240     */
241    public String getAccessibleDescription() {
242        return null;
243    }
244
245    /**
246     * Set the accessible description of this object.
247     *
248     * @param s the new localized description of the object
249     */
250    public void setAccessibleDescription(String s) {
251    }
252
253    /**
254     * Get the role of this object.
255     *
256     * @return an instance of AccessibleRole describing the role of the object
257     */
258    public AccessibleRole getAccessibleRole() {
259        return AccessibleRole.UNKNOWN;
260    }
261
262
263    /**
264     * Get the state of this object, given an already populated state.
265     * This method is intended for use by subclasses so they don't have
266     * to check for everything.
267     *
268     * @return an instance of {@code AccessibleStateSet}
269     *     containing the current state of the object
270     */
271    public AccessibleStateSet getAccessibleStateSet() {
272        AccessibleStateSet states = new AccessibleStateSet();
273        if (source instanceof Component) {
274            Component c = (Component) source;
275            for (Container p = c.getParent(); p != null; p = p.getParent()) {
276                if (p instanceof Window) {
277                    if (((Window)p).getFocusOwner() == c) {
278                        states.add(AccessibleState.FOCUSED);
279                    }
280                }
281            }
282        }
283        if (isEnabled()) {
284            states.add(AccessibleState.ENABLED);
285        }
286        if (isFocusTraversable()) {
287            states.add(AccessibleState.FOCUSABLE);
288        }
289        if (source instanceof MenuItem) {
290            states.add(AccessibleState.FOCUSABLE);
291        }
292        return states;
293    }
294
295    /**
296     * Get the accessible parent of this object.
297     *
298     * @return the accessible parent of this object; can be null if this
299     *     object does not have an accessible parent
300     */
301    public Accessible getAccessibleParent() {
302        if (accessibleParent != null) {
303            return accessibleParent;
304        } else if (source instanceof Component) {
305            return getAccessible(((Component) source).getParent());
306        } else {
307            return null;
308        }
309    }
310
311    /**
312     * Get the index of this object in its accessible parent.
313     *
314     * @return -1 of this object does not have an accessible parent; otherwise,
315     * the index of the child in its accessible parent
316     */
317    public int getAccessibleIndexInParent() {
318        if (source instanceof Component) {
319            Container parent = ((Component) source).getParent();
320            if (parent != null) {
321                Component ca[] = parent.getComponents();
322                for (int i = 0; i < ca.length; i++) {
323                    if (source.equals(ca[i])) {
324                        return i;
325                    }
326                }
327            }
328        }
329        return -1;
330    }
331
332    /**
333     * Returns the number of accessible children in the object.
334     *
335     * @return the number of accessible children in the object
336     */
337    public int getAccessibleChildrenCount() {
338        if (source instanceof Container) {
339            Component[] children = ((Container) source).getComponents();
340            int count = 0;
341            for (int i = 0; i < children.length; i++) {
342                Accessible a = getAccessible(children[i]);
343                if (a != null) {
344                    count++;
345                }
346            }
347            return count;
348        } else {
349            return 0;
350        }
351    }
352
353    /**
354     * Return the nth accessible child of the object.
355     *
356     * @param i zero-based index of child
357     * @return the nth accessible child of the object
358     */
359    public Accessible getAccessibleChild(int i) {
360        if (source instanceof Container) {
361            Component[] children = ((Container) source).getComponents();
362            int count = 0;
363
364            for (int j = 0; j < children.length; j++) {
365                Accessible a = getAccessible(children[j]);
366                if (a != null) {
367                    if (count == i) {
368                        AccessibleContext ac = a.getAccessibleContext();
369                        if (ac != null) {
370                            ac.setAccessibleParent(this);
371                        }
372                        return a;
373                    } else {
374                        count++;
375                    }
376                }
377            }
378        }
379        return null;
380    }
381
382    /**
383     * Gets the {@code Locale} of the component. If the component does not have a
384     * locale, the locale of its parent is returned.
385     *
386     * @return the {@code Locale} of the object
387     */
388    public Locale getLocale() throws IllegalComponentStateException {
389        if (source instanceof Component) {
390            return ((Component) source).getLocale();
391        } else {
392            return null;
393        }
394    }
395
396    /**
397     * Add a {@code PropertyChangeListener} to the listener list.  The listener
398     * is registered for all properties.
399     */
400    public void addPropertyChangeListener(PropertyChangeListener l) {
401    }
402
403    /**
404     * Remove the {@code PropertyChangeListener} from the listener list.
405     */
406    public void removePropertyChangeListener(PropertyChangeListener l) {
407    }
408
409// AccessibleComponent methods
410
411    /**
412     * Get the background {@code Color} of this object.
413     *
414     * @return if supported, the background {@code Color} of the object;
415     *     otherwise, null
416     *
417     */
418    public Color getBackground() {
419        if (source instanceof Component) { // MenuComponent doesn't do background
420            return ((Component) source).getBackground();
421        } else {
422            return null;
423        }
424    }
425
426    /**
427     * Set the background {@code Color} of this object.
428     *
429     * @param c the new {@code Color} for the background
430     */
431    public void setBackground(Color c) {
432        if (source instanceof Component) { // MenuComponent doesn't do background
433            ((Component) source).setBackground(c);
434        }
435    }
436
437    /**
438     * Get the foreground {@code Color} of this object.
439     *
440     * @return if supported, the foreground {@code Color} of the object; otherwise, null
441     */
442    public Color getForeground() {
443        if (source instanceof Component) { // MenuComponent doesn't do foreground
444            return ((Component) source).getForeground();
445        } else {
446            return null;
447        }
448    }
449
450    /**
451     * Set the foreground {@code Color} of this object.
452     *
453     * @param c the new {@code Color} for the foreground
454     */
455    public void setForeground(Color c) {
456        if (source instanceof Component) { // MenuComponent doesn't do foreground
457            ((Component) source).setForeground(c);
458        }
459    }
460
461    /**
462     * Get the {@code Cursor} of this object.
463     *
464     * @return if supported, the Cursor of the object; otherwise, null
465     */
466    public Cursor getCursor() {
467        if (source instanceof Component) { // MenuComponent doesn't do cursor
468            return ((Component) source).getCursor();
469        } else {
470            return null;
471        }
472    }
473
474    /**
475     * Set the {@code Cursor} of this object.
476     * @param c the new {@code Cursor} for the object
477     */
478    public void setCursor(Cursor c) {
479        if (source instanceof Component) { // MenuComponent doesn't do cursor
480            ((Component) source).setCursor(c);
481        }
482    }
483
484    /**
485     * Get the {@code Font} of this object.
486     *
487     * @return if supported, the {@code Font} for the object; otherwise, null
488     */
489    public Font getFont() {
490        if (source instanceof Component) {
491            return ((Component) source).getFont();
492        } else if (source instanceof MenuComponent) {
493            return ((MenuComponent) source).getFont();
494        } else {
495            return null;
496        }
497    }
498
499    /**
500     * Set the {@code Font} of this object.
501     *
502     * @param f the new {@code Font} for the object
503     */
504    public void setFont(Font f) {
505        if (source instanceof Component) {
506            ((Component) source).setFont(f);
507        } else if (source instanceof MenuComponent) {
508            ((MenuComponent) source).setFont(f);
509        }
510    }
511
512    /**
513     * Get the {@code FontMetrics} of this object.
514     *
515     * @param f the {@code Font}
516     * @return if supported, the {@code FontMetrics} the object; otherwise, null
517     * @see #getFont
518     */
519    public FontMetrics getFontMetrics(Font f) {
520        if (source instanceof Component) {
521            return ((Component) source).getFontMetrics(f);
522        } else {
523            return null;
524        }
525    }
526
527    /**
528     * Determine if the object is enabled.
529     *
530     * @return true if object is enabled; otherwise, false
531     */
532    public boolean isEnabled() {
533        if (source instanceof Component) {
534            return ((Component) source).isEnabled();
535        } else if (source instanceof MenuItem) {
536            return ((MenuItem) source).isEnabled();
537        } else {
538            return true;
539        }
540    }
541
542    /**
543     * Set the enabled state of the object.
544     *
545     * @param b if true, enables this object; otherwise, disables it
546     */
547    public void setEnabled(boolean b) {
548        if (source instanceof Component) {
549            ((Component) source).setEnabled(b);
550        } else if (source instanceof MenuItem) {
551            ((MenuItem) source).setEnabled(b);
552        }
553    }
554
555    /**
556     * Determine if the object is visible.
557     *
558     * @return true if object is visible; otherwise, false
559     */
560    public boolean isVisible() {
561        if (source instanceof Component) {
562            return ((Component) source).isVisible();
563        } else {
564            return false;
565        }
566    }
567
568    /**
569     * Set the visible state of the object.
570     *
571     * @param b if true, shows this object; otherwise, hides it
572     */
573    public void setVisible(boolean b) {
574        if (source instanceof Component) {
575            ((Component) source).setVisible(b);
576        }
577    }
578
579    /**
580     * Determine if the object is showing.  This is determined by checking
581     * the visibility of the object and ancestors of the object.
582     *
583     * @return true if object is showing; otherwise, false
584     */
585    public boolean isShowing() {
586        if (source instanceof Component) {
587            return ((Component) source).isShowing();
588        } else {
589            return false;
590        }
591    }
592
593    /**
594     * Checks whether the specified {@code Point} is within this
595     * object's bounds, where the {@code Point} is relative to the coordinate
596     * system of the object.
597     *
598     * @param p the {@code Point} relative to the coordinate system of the object
599     * @return true if object contains {@code Point}; otherwise false
600     */
601    public boolean contains(Point p) {
602        if (source instanceof Component) {
603            return ((Component) source).contains(p);
604        } else {
605            return false;
606        }
607    }
608
609    /**
610     * Returns the location of the object on the screen.
611     *
612     * @return location of object on screen; can be null if this object
613     *     is not on the screen
614     */
615    public Point getLocationOnScreen() {
616        if (source instanceof Component) {
617            return ((Component) source).getLocationOnScreen();
618        } else {
619            return null;
620        }
621    }
622
623    /**
624     * Returns the location of the object relative to parent.
625     *
626     * @return location of object relative to parent; can be null if
627     *     this object or its parent are not on the screen
628     */
629    public Point getLocation() {
630        if (source instanceof Component) {
631            return ((Component) source).getLocation();
632        } else {
633            return null;
634        }
635    }
636
637    /**
638     * Sets the location of the object relative to parent.
639     */
640    public void setLocation(Point p) {
641        if (source instanceof Component) {
642            ((Component) source).setLocation(p);
643        }
644    }
645
646    /**
647     * Returns the current bounds of this object.
648     *
649     * @return current bounds of object; can be null if this object
650     *     is not on the screen
651     */
652    public Rectangle getBounds() {
653        if (source instanceof Component) {
654            return ((Component) source).getBounds();
655        } else {
656            return null;
657        }
658    }
659
660    /**
661     * Sets the current bounds of this object.
662     */
663    public void setBounds(Rectangle r) {
664        if (source instanceof Component) {
665            ((Component) source).setBounds(r);
666        }
667    }
668
669    /**
670     * Returns the current size of this object.
671     *
672     * @return current size of object; can be null if this object is
673     *     not on the screen
674     */
675    public Dimension getSize() {
676        if (source instanceof Component) {
677            return ((Component) source).getSize();
678        } else {
679            return null;
680        }
681    }
682
683    /**
684     * Sets the current size of this object.
685     */
686    public void setSize(Dimension d) {
687        if (source instanceof Component) {
688            ((Component) source).setSize(d);
689        }
690    }
691
692    /**
693     * Returns the accessible child contained at the local coordinate
694     * Point, if one exists.
695     *
696     * @return the Accessible at the specified location, if it exists
697     */
698    public Accessible getAccessibleAt(Point p) {
699        if (source instanceof Component) {
700            Component c = ((Component) source).getComponentAt(p);
701            if (c != null) {
702                return (getAccessible(c));
703            }
704        }
705        return null;
706    }
707
708    /**
709     * Returns whether this object can accept focus or not.
710     *
711     * @return true if object can accept focus; otherwise false
712     */
713    @SuppressWarnings("deprecation")
714    public boolean isFocusTraversable() {
715        if (source instanceof Component) {
716            return ((Component) source).isFocusTraversable();
717        } else {
718            return false;
719        }
720    }
721
722    /**
723     * Requests focus for this object.
724     */
725    public void requestFocus() {
726        if (source instanceof Component) {
727            ((Component) source).requestFocus();
728        }
729    }
730
731    /**
732     * Adds the specified {@code FocusListener} to receive focus events from
733     * this component.
734     *
735     * @param l the focus listener
736     */
737    public synchronized void addFocusListener(FocusListener l) {
738        if (source instanceof Component) {
739            ((Component) source).addFocusListener(l);
740        }
741    }
742
743    /**
744     * Removes the specified focus listener so it no longer receives focus
745     * events from this component.
746     *
747     * @param l the focus listener; this method performs no function, nor does it
748     *     throw an exception if the listener specified was not previously added
749     *     to this component; if listener is null, no exception is thrown and no
750     *     action is performed.
751     */
752    public synchronized void removeFocusListener(FocusListener l) {
753        if (source instanceof Component) {
754            ((Component) source).removeFocusListener(l);
755        }
756    }
757}
758