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      */
run(List<T> args)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      */
run()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
add(T... args)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      */
submit()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      */
flush()155     private final synchronized List<T> flush() {
156         List<T> list = arguments;
157         arguments = null;
158         return list;
159     }
160 }
161