View Javadoc
1   /*
2    * Copyright (c) 2005, 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   */
25  package sun.swing;
26  
27  import java.util.*;
28  import java.lang.reflect.Array;
29  import javax.swing.SwingUtilities;
30  
31  /**
32   * An abstract class to be used in the cases where we need {@code Runnable}
33   * to perform  some actions on an appendable set of data.
34   * The set of data might be appended after the {@code Runnable} is
35   * sent for the execution. Usually such {@code Runnables} are sent to
36   * the EDT.
37   *
38   * <p>
39   * Usage example:
40   *
41   * <p>
42   * Say we want to implement JLabel.setText(String text) which sends
43   * {@code text} string to the JLabel.setTextImpl(String text) on the EDT.
44   * In the event JLabel.setText is called rapidly many times off the EDT
45   * we will get many updates on the EDT but only the last one is important.
46   * (Every next updates overrides the previous one.)
47   * We might want to implement this {@code setText} in a way that only
48   * the last update is delivered.
49   * <p>
50   * Here is how one can do this using {@code AccumulativeRunnable}:
51   * <pre>
52   * AccumulativeRunnable<String> doSetTextImpl =
53   * new  AccumulativeRunnable<String>() {
54   *     @Override
55   *     protected void run(List&lt;String&gt; args) {
56   *         //set to the last string being passed
57   *         setTextImpl(args.get(args.size() - 1));
58   *     }
59   * }
60   * void setText(String text) {
61   *     //add text and send for the execution if needed.
62   *     doSetTextImpl.add(text);
63   * }
64   * </pre>
65   *
66   * <p>
67   * Say we want want to implement addDirtyRegion(Rectangle rect)
68   * which sends this region to the
69   * handleDirtyRegions(List<Rect> regiouns) on the EDT.
70   * addDirtyRegions better be accumulated before handling on the EDT.
71   *
72   * <p>
73   * Here is how it can be implemented using AccumulativeRunnable:
74   * <pre>
75   * AccumulativeRunnable<Rectangle> doHandleDirtyRegions =
76   *     new AccumulativeRunnable<Rectangle>() {
77   *         @Override
78   *         protected void run(List&lt;Rectangle&gt; args) {
79   *             handleDirtyRegions(args);
80   *         }
81   *     };
82   *  void addDirtyRegion(Rectangle rect) {
83   *      doHandleDirtyRegions.add(rect);
84   *  }
85   * </pre>
86   *
87   * @author Igor Kushnirskiy
88   *
89   * @param <T> the type this {@code Runnable} accumulates
90   *
91   * @since 1.6
92   */
93  public abstract class AccumulativeRunnable<T> implements Runnable {
94      private List<T> arguments = null;
95  
96      /**
97       * Equivalent to {@code Runnable.run} method with the
98       * accumulated arguments to process.
99       *
100      * @param args accumulated argumets to process.
101      */
102     protected abstract void run(List<T> args);
103 
104     /**
105      * {@inheritDoc}
106      *
107      * <p>
108      * This implementation calls {@code run(List<T> args)} mehtod
109      * with the list of accumulated arguments.
110      */
111     public final void run() {
112         run(flush());
113     }
114 
115     /**
116      * appends arguments and sends this {@cod Runnable} for the
117      * execution if needed.
118      * <p>
119      * This implementation uses {@see #submit} to send this
120      * {@code Runnable} for execution.
121      * @param args the arguments to accumulate
122      */
123     @SafeVarargs
124     @SuppressWarnings("varargs") // Copying args is safe
125     public final synchronized void add(T... args) {
126         boolean isSubmitted = true;
127         if (arguments == null) {
128             isSubmitted = false;
129             arguments = new ArrayList<T>();
130         }
131         Collections.addAll(arguments, args);
132         if (!isSubmitted) {
133             submit();
134         }
135     }
136 
137     /**
138      * Sends this {@code Runnable} for the execution
139      *
140      * <p>
141      * This method is to be executed only from {@code add} method.
142      *
143      * <p>
144      * This implementation uses {@code SwingWorker.invokeLater}.
145      */
146     protected void submit() {
147         SwingUtilities.invokeLater(this);
148     }
149 
150     /**
151      * Returns accumulated arguments and flashes the arguments storage.
152      *
153      * @return accumulated arguments
154      */
155     private final synchronized List<T> flush() {
156         List<T> list = arguments;
157         arguments = null;
158         return list;
159     }
160 }