1/*
2 * Copyright (c) 2000, 2013, 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 */
25package java.awt;
26
27import java.util.List;
28import java.util.ArrayList;
29import sun.util.logging.PlatformLogger;
30
31/**
32 * A FocusTraversalPolicy that determines traversal order based on the order
33 * of child Components in a Container. From a particular focus cycle root, the
34 * policy makes a pre-order traversal of the Component hierarchy, and traverses
35 * a Container's children according to the ordering of the array returned by
36 * {@code Container.getComponents()}. Portions of the hierarchy that are
37 * not visible and displayable will not be searched.
38 * <p>
39 * By default, ContainerOrderFocusTraversalPolicy implicitly transfers focus
40 * down-cycle. That is, during normal forward focus traversal, the Component
41 * traversed after a focus cycle root will be the focus-cycle-root's default
42 * Component to focus. This behavior can be disabled using the
43 * {@code setImplicitDownCycleTraversal} method.
44 * <p>
45 * By default, methods of this class will return a Component only if it is
46 * visible, displayable, enabled, and focusable. Subclasses can modify this
47 * behavior by overriding the {@code accept} method.
48 * <p>
49 * This policy takes into account <a
50 * href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal
51 * policy providers</a>.  When searching for first/last/next/previous Component,
52 * if a focus traversal policy provider is encountered, its focus traversal
53 * policy is used to perform the search operation.
54 *
55 * @author David Mendenhall
56 *
57 * @see Container#getComponents
58 * @since 1.4
59 */
60public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy
61    implements java.io.Serializable
62{
63    private static final PlatformLogger log = PlatformLogger.getLogger("java.awt.ContainerOrderFocusTraversalPolicy");
64
65    private final int FORWARD_TRAVERSAL = 0;
66    private final int BACKWARD_TRAVERSAL = 1;
67
68    /*
69     * JDK 1.4 serialVersionUID
70     */
71    private static final long serialVersionUID = 486933713763926351L;
72
73    private boolean implicitDownCycleTraversal = true;
74
75    /**
76     * Used by getComponentAfter and getComponentBefore for efficiency. In
77     * order to maintain compliance with the specification of
78     * FocusTraversalPolicy, if traversal wraps, we should invoke
79     * getFirstComponent or getLastComponent. These methods may be overriden in
80     * subclasses to behave in a non-generic way. However, in the generic case,
81     * these methods will simply return the first or last Components of the
82     * sorted list, respectively. Since getComponentAfter and
83     * getComponentBefore have already built the list before determining
84     * that they need to invoke getFirstComponent or getLastComponent, the
85     * list should be reused if possible.
86     */
87    private transient Container cachedRoot;
88    private transient List<Component> cachedCycle;
89
90    /*
91     * We suppose to use getFocusTraversalCycle & getComponentIndex methods in order
92     * to divide the policy into two parts:
93     * 1) Making the focus traversal cycle.
94     * 2) Traversing the cycle.
95     * The 1st point assumes producing a list of components representing the focus
96     * traversal cycle. The two methods mentioned above should implement this logic.
97     * The 2nd point assumes implementing the common concepts of operating on the
98     * cycle: traversing back and forth, retrieving the initial/default/first/last
99     * component. These concepts are described in the AWT Focus Spec and they are
100     * applied to the FocusTraversalPolicy in general.
101     * Thus, a descendant of this policy may wish to not reimplement the logic of
102     * the 2nd point but just override the implementation of the 1st one.
103     * A striking example of such a descendant is the javax.swing.SortingFocusTraversalPolicy.
104     */
105    /*protected*/ private List<Component> getFocusTraversalCycle(Container aContainer) {
106        List<Component> cycle = new ArrayList<Component>();
107        enumerateCycle(aContainer, cycle);
108        return cycle;
109    }
110    /*protected*/ private int getComponentIndex(List<Component> cycle, Component aComponent) {
111        return cycle.indexOf(aComponent);
112    }
113
114    private void enumerateCycle(Container container, List<Component> cycle) {
115        if (!(container.isVisible() && container.isDisplayable())) {
116            return;
117        }
118
119        cycle.add(container);
120
121        Component[] components = container.getComponents();
122        for (int i = 0; i < components.length; i++) {
123            Component comp = components[i];
124            if (comp instanceof Container) {
125                Container cont = (Container)comp;
126
127                if (!cont.isFocusCycleRoot() && !cont.isFocusTraversalPolicyProvider()) {
128                    enumerateCycle(cont, cycle);
129                    continue;
130                }
131            }
132            cycle.add(comp);
133        }
134    }
135
136    private Container getTopmostProvider(Container focusCycleRoot, Component aComponent) {
137        Container aCont = aComponent.getParent();
138        Container ftp = null;
139        while (aCont  != focusCycleRoot && aCont != null) {
140            if (aCont.isFocusTraversalPolicyProvider()) {
141                ftp = aCont;
142            }
143            aCont = aCont.getParent();
144        }
145        if (aCont == null) {
146            return null;
147        }
148        return ftp;
149    }
150
151    /*
152     * Checks if a new focus cycle takes place and returns a Component to traverse focus to.
153     * @param comp a possible focus cycle root or policy provider
154     * @param traversalDirection the direction of the traversal
155     * @return a Component to traverse focus to if {@code comp} is a root or provider
156     *         and implicit down-cycle is set, otherwise {@code null}
157     */
158    private Component getComponentDownCycle(Component comp, int traversalDirection) {
159        Component retComp = null;
160
161        if (comp instanceof Container) {
162            Container cont = (Container)comp;
163
164            if (cont.isFocusCycleRoot()) {
165                if (getImplicitDownCycleTraversal()) {
166                    retComp = cont.getFocusTraversalPolicy().getDefaultComponent(cont);
167
168                    if (retComp != null && log.isLoggable(PlatformLogger.Level.FINE)) {
169                        log.fine("### Transferred focus down-cycle to " + retComp +
170                                 " in the focus cycle root " + cont);
171                    }
172                } else {
173                    return null;
174                }
175            } else if (cont.isFocusTraversalPolicyProvider()) {
176                retComp = (traversalDirection == FORWARD_TRAVERSAL ?
177                           cont.getFocusTraversalPolicy().getDefaultComponent(cont) :
178                           cont.getFocusTraversalPolicy().getLastComponent(cont));
179
180                if (retComp != null && log.isLoggable(PlatformLogger.Level.FINE)) {
181                    log.fine("### Transferred focus to " + retComp + " in the FTP provider " + cont);
182                }
183            }
184        }
185        return retComp;
186    }
187
188    /**
189     * Returns the Component that should receive the focus after aComponent.
190     * aContainer must be a focus cycle root of aComponent or a focus traversal policy provider.
191     * <p>
192     * By default, ContainerOrderFocusTraversalPolicy implicitly transfers
193     * focus down-cycle. That is, during normal forward focus traversal, the
194     * Component traversed after a focus cycle root will be the focus-cycle-
195     * root's default Component to focus. This behavior can be disabled using
196     * the {@code setImplicitDownCycleTraversal} method.
197     * <p>
198     * If aContainer is <a href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus
199     * traversal policy provider</a>, the focus is always transferred down-cycle.
200     *
201     * @param aContainer a focus cycle root of aComponent or a focus traversal policy provider
202     * @param aComponent a (possibly indirect) child of aContainer, or
203     *        aContainer itself
204     * @return the Component that should receive the focus after aComponent, or
205     *         null if no suitable Component can be found
206     * @throws IllegalArgumentException if aContainer is not a focus cycle
207     *         root of aComponent or focus traversal policy provider, or if either aContainer or
208     *         aComponent is null
209     */
210    public Component getComponentAfter(Container aContainer, Component aComponent) {
211        if (log.isLoggable(PlatformLogger.Level.FINE)) {
212            log.fine("### Searching in " + aContainer + " for component after " + aComponent);
213        }
214
215        if (aContainer == null || aComponent == null) {
216            throw new IllegalArgumentException("aContainer and aComponent cannot be null");
217        }
218        if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {
219            throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");
220
221        } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
222            throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
223        }
224
225        synchronized(aContainer.getTreeLock()) {
226
227            if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
228                return null;
229            }
230
231            // Before all the checks below we first see if it's an FTP provider or a focus cycle root.
232            // If it's the case just go down cycle (if it's set to "implicit").
233            Component comp = getComponentDownCycle(aComponent, FORWARD_TRAVERSAL);
234            // Check if aComponent is focus-cycle-root's default Component, i.e.
235            // focus cycle root & focus-cycle-root's default Component is same.
236            if (comp != null && comp != aComponent) {
237                return comp;
238            }
239
240            // See if the component is inside of policy provider.
241            Container provider = getTopmostProvider(aContainer, aComponent);
242            if (provider != null) {
243                if (log.isLoggable(PlatformLogger.Level.FINE)) {
244                    log.fine("### Asking FTP " + provider + " for component after " + aComponent);
245                }
246
247                // FTP knows how to find component after the given. We don't.
248                FocusTraversalPolicy policy = provider.getFocusTraversalPolicy();
249                Component afterComp = policy.getComponentAfter(provider, aComponent);
250
251                // Null result means that we overstepped the limit of the FTP's cycle.
252                // In that case we must quit the cycle, otherwise return the component found.
253                if (afterComp != null) {
254                    if (log.isLoggable(PlatformLogger.Level.FINE)) {
255                        log.fine("### FTP returned " + afterComp);
256                    }
257                    return afterComp;
258                }
259                aComponent = provider;
260            }
261
262            List<Component> cycle = getFocusTraversalCycle(aContainer);
263
264            if (log.isLoggable(PlatformLogger.Level.FINE)) {
265                log.fine("### Cycle is " + cycle + ", component is " + aComponent);
266            }
267
268            int index = getComponentIndex(cycle, aComponent);
269
270            if (index < 0) {
271                if (log.isLoggable(PlatformLogger.Level.FINE)) {
272                    log.fine("### Didn't find component " + aComponent + " in a cycle " + aContainer);
273                }
274                return getFirstComponent(aContainer);
275            }
276
277            for (index++; index < cycle.size(); index++) {
278                comp = cycle.get(index);
279                if (accept(comp)) {
280                    return comp;
281                } else if ((comp = getComponentDownCycle(comp, FORWARD_TRAVERSAL)) != null) {
282                    return comp;
283                }
284            }
285
286            if (aContainer.isFocusCycleRoot()) {
287                this.cachedRoot = aContainer;
288                this.cachedCycle = cycle;
289
290                comp = getFirstComponent(aContainer);
291
292                this.cachedRoot = null;
293                this.cachedCycle = null;
294
295                return comp;
296            }
297        }
298        return null;
299    }
300
301    /**
302     * Returns the Component that should receive the focus before aComponent.
303     * aContainer must be a focus cycle root of aComponent or a <a
304     * href="doc-files/FocusSpec.html#FocusTraversalPolicyProviders">focus traversal policy
305     * provider</a>.
306     *
307     * @param aContainer a focus cycle root of aComponent or focus traversal policy provider
308     * @param aComponent a (possibly indirect) child of aContainer, or
309     *        aContainer itself
310     * @return the Component that should receive the focus before aComponent,
311     *         or null if no suitable Component can be found
312     * @throws IllegalArgumentException if aContainer is not a focus cycle
313     *         root of aComponent or focus traversal policy provider, or if either aContainer or
314     *         aComponent is null
315     */
316    public Component getComponentBefore(Container aContainer, Component aComponent) {
317        if (aContainer == null || aComponent == null) {
318            throw new IllegalArgumentException("aContainer and aComponent cannot be null");
319        }
320        if (!aContainer.isFocusTraversalPolicyProvider() && !aContainer.isFocusCycleRoot()) {
321            throw new IllegalArgumentException("aContainer should be focus cycle root or focus traversal policy provider");
322
323        } else if (aContainer.isFocusCycleRoot() && !aComponent.isFocusCycleRoot(aContainer)) {
324            throw new IllegalArgumentException("aContainer is not a focus cycle root of aComponent");
325        }
326
327        synchronized(aContainer.getTreeLock()) {
328
329            if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
330                return null;
331            }
332
333            // See if the component is inside of policy provider.
334            Container provider = getTopmostProvider(aContainer, aComponent);
335            if (provider != null) {
336                if (log.isLoggable(PlatformLogger.Level.FINE)) {
337                    log.fine("### Asking FTP " + provider + " for component after " + aComponent);
338                }
339
340                // FTP knows how to find component after the given. We don't.
341                FocusTraversalPolicy policy = provider.getFocusTraversalPolicy();
342                Component beforeComp = policy.getComponentBefore(provider, aComponent);
343
344                // Null result means that we overstepped the limit of the FTP's cycle.
345                // In that case we must quit the cycle, otherwise return the component found.
346                if (beforeComp != null) {
347                    if (log.isLoggable(PlatformLogger.Level.FINE)) {
348                        log.fine("### FTP returned " + beforeComp);
349                    }
350                    return beforeComp;
351                }
352                aComponent = provider;
353
354                // If the provider is traversable it's returned.
355                if (accept(aComponent)) {
356                    return aComponent;
357                }
358            }
359
360            List<Component> cycle = getFocusTraversalCycle(aContainer);
361
362            if (log.isLoggable(PlatformLogger.Level.FINE)) {
363                log.fine("### Cycle is " + cycle + ", component is " + aComponent);
364            }
365
366            int index = getComponentIndex(cycle, aComponent);
367
368            if (index < 0) {
369                if (log.isLoggable(PlatformLogger.Level.FINE)) {
370                    log.fine("### Didn't find component " + aComponent + " in a cycle " + aContainer);
371                }
372                return getLastComponent(aContainer);
373            }
374
375            Component comp = null;
376            Component tryComp = null;
377
378            for (index--; index>=0; index--) {
379                comp = cycle.get(index);
380                if (comp != aContainer && (tryComp = getComponentDownCycle(comp, BACKWARD_TRAVERSAL)) != null) {
381                    return tryComp;
382                } else if (accept(comp)) {
383                    return comp;
384                }
385            }
386
387            if (aContainer.isFocusCycleRoot()) {
388                this.cachedRoot = aContainer;
389                this.cachedCycle = cycle;
390
391                comp = getLastComponent(aContainer);
392
393                this.cachedRoot = null;
394                this.cachedCycle = null;
395
396                return comp;
397            }
398        }
399        return null;
400    }
401
402    /**
403     * Returns the first Component in the traversal cycle. This method is used
404     * to determine the next Component to focus when traversal wraps in the
405     * forward direction.
406     *
407     * @param aContainer the focus cycle root or focus traversal policy provider whose first
408     *        Component is to be returned
409     * @return the first Component in the traversal cycle of aContainer,
410     *         or null if no suitable Component can be found
411     * @throws IllegalArgumentException if aContainer is null
412     */
413    public Component getFirstComponent(Container aContainer) {
414        List<Component> cycle;
415
416        if (log.isLoggable(PlatformLogger.Level.FINE)) {
417            log.fine("### Getting first component in " + aContainer);
418        }
419        if (aContainer == null) {
420            throw new IllegalArgumentException("aContainer cannot be null");
421
422        }
423
424        synchronized(aContainer.getTreeLock()) {
425
426            if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
427                return null;
428            }
429
430            if (this.cachedRoot == aContainer) {
431                cycle = this.cachedCycle;
432            } else {
433                cycle = getFocusTraversalCycle(aContainer);
434            }
435
436            if (cycle.size() == 0) {
437                if (log.isLoggable(PlatformLogger.Level.FINE)) {
438                    log.fine("### Cycle is empty");
439                }
440                return null;
441            }
442            if (log.isLoggable(PlatformLogger.Level.FINE)) {
443                log.fine("### Cycle is " + cycle);
444            }
445
446            for (Component comp : cycle) {
447                if (accept(comp)) {
448                    return comp;
449                } else if (comp != aContainer &&
450                           (comp = getComponentDownCycle(comp, FORWARD_TRAVERSAL)) != null)
451                {
452                    return comp;
453                }
454            }
455        }
456        return null;
457    }
458
459    /**
460     * Returns the last Component in the traversal cycle. This method is used
461     * to determine the next Component to focus when traversal wraps in the
462     * reverse direction.
463     *
464     * @param aContainer the focus cycle root or focus traversal policy provider whose last
465     *        Component is to be returned
466     * @return the last Component in the traversal cycle of aContainer,
467     *         or null if no suitable Component can be found
468     * @throws IllegalArgumentException if aContainer is null
469     */
470    public Component getLastComponent(Container aContainer) {
471        List<Component> cycle;
472        if (log.isLoggable(PlatformLogger.Level.FINE)) {
473            log.fine("### Getting last component in " + aContainer);
474        }
475
476        if (aContainer == null) {
477            throw new IllegalArgumentException("aContainer cannot be null");
478        }
479
480        synchronized(aContainer.getTreeLock()) {
481
482            if (!(aContainer.isVisible() && aContainer.isDisplayable())) {
483                return null;
484            }
485
486            if (this.cachedRoot == aContainer) {
487                cycle = this.cachedCycle;
488            } else {
489                cycle = getFocusTraversalCycle(aContainer);
490            }
491
492            if (cycle.size() == 0) {
493                if (log.isLoggable(PlatformLogger.Level.FINE)) {
494                    log.fine("### Cycle is empty");
495                }
496                return null;
497            }
498            if (log.isLoggable(PlatformLogger.Level.FINE)) {
499                log.fine("### Cycle is " + cycle);
500            }
501
502            for (int i= cycle.size() - 1; i >= 0; i--) {
503                Component comp = cycle.get(i);
504                if (accept(comp)) {
505                    return comp;
506                } else if (comp instanceof Container && comp != aContainer) {
507                    Container cont = (Container)comp;
508                    if (cont.isFocusTraversalPolicyProvider()) {
509                        Component retComp = cont.getFocusTraversalPolicy().getLastComponent(cont);
510                        if (retComp != null) {
511                            return retComp;
512                        }
513                    }
514                }
515            }
516        }
517        return null;
518    }
519
520    /**
521     * Returns the default Component to focus. This Component will be the first
522     * to receive focus when traversing down into a new focus traversal cycle
523     * rooted at aContainer. The default implementation of this method
524     * returns the same Component as {@code getFirstComponent}.
525     *
526     * @param aContainer the focus cycle root or focus traversal policy provider whose default
527     *        Component is to be returned
528     * @return the default Component in the traversal cycle of aContainer,
529     *         or null if no suitable Component can be found
530     * @see #getFirstComponent
531     * @throws IllegalArgumentException if aContainer is null
532     */
533    public Component getDefaultComponent(Container aContainer) {
534        return getFirstComponent(aContainer);
535    }
536
537    /**
538     * Sets whether this ContainerOrderFocusTraversalPolicy transfers focus
539     * down-cycle implicitly. If {@code true}, during normal forward focus
540     * traversal, the Component traversed after a focus cycle root will be the
541     * focus-cycle-root's default Component to focus. If {@code false},
542     * the next Component in the focus traversal cycle rooted at the specified
543     * focus cycle root will be traversed instead. The default value for this
544     * property is {@code true}.
545     *
546     * @param implicitDownCycleTraversal whether this
547     *        ContainerOrderFocusTraversalPolicy transfers focus down-cycle
548     *        implicitly
549     * @see #getImplicitDownCycleTraversal
550     * @see #getFirstComponent
551     */
552    public void setImplicitDownCycleTraversal(boolean implicitDownCycleTraversal) {
553        this.implicitDownCycleTraversal = implicitDownCycleTraversal;
554    }
555
556    /**
557     * Returns whether this ContainerOrderFocusTraversalPolicy transfers focus
558     * down-cycle implicitly. If {@code true}, during normal forward focus
559     * traversal, the Component traversed after a focus cycle root will be the
560     * focus-cycle-root's default Component to focus. If {@code false},
561     * the next Component in the focus traversal cycle rooted at the specified
562     * focus cycle root will be traversed instead.
563     *
564     * @return whether this ContainerOrderFocusTraversalPolicy transfers focus
565     *         down-cycle implicitly
566     * @see #setImplicitDownCycleTraversal
567     * @see #getFirstComponent
568     */
569    public boolean getImplicitDownCycleTraversal() {
570        return implicitDownCycleTraversal;
571    }
572
573    /**
574     * Determines whether a Component is an acceptable choice as the new
575     * focus owner. By default, this method will accept a Component if and
576     * only if it is visible, displayable, enabled, and focusable.
577     *
578     * @param aComponent the Component whose fitness as a focus owner is to
579     *        be tested
580     * @return {@code true} if aComponent is visible, displayable,
581     *         enabled, and focusable; {@code false} otherwise
582     */
583    protected boolean accept(Component aComponent) {
584        if (!aComponent.canBeFocusOwner()) {
585            return false;
586        }
587
588        // Verify that the Component is recursively enabled. Disabling a
589        // heavyweight Container disables its children, whereas disabling
590        // a lightweight Container does not.
591        if (!(aComponent instanceof Window)) {
592            for (Container enableTest = aComponent.getParent();
593                 enableTest != null;
594                 enableTest = enableTest.getParent())
595            {
596                if (!(enableTest.isEnabled() || enableTest.isLightweight())) {
597                    return false;
598                }
599                if (enableTest instanceof Window) {
600                    break;
601                }
602            }
603        }
604
605        return true;
606    }
607}
608