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 */
25package sun.swing;
26
27import java.util.*;
28import java.lang.reflect.Array;
29import 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 * {@code AccumulativeRunnable<String> doSetTextImpl =
53 *  new  AccumulativeRunnable<String>()} {
54 *    {@literal @Override}
55 *    {@code protected void run(List<String> 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 to implement addDirtyRegion(Rectangle rect)
68 * which sends this region to the
69 * {@code 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 * {@code AccumulativeRunnable<Rectangle> doHandleDirtyRegions =}
76 *    {@code new AccumulativeRunnable<Rectangle>()} {
77 *        {@literal @Override}
78 *        {@code protected void run(List<Rectangle> 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 */
93public 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 {@code 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}
161