1 /*
2 * Copyright 1996-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package java.awt.datatransfer;
27
28 import java.awt.EventQueue;
29
30 import java.util.Set;
31 import java.util.HashSet;
32 import java.util.Arrays;
33
34 import java.io.IOException;
35
36 import sun.awt.EventListenerAggregate;
37
38
39 /**
40 * A class that implements a mechanism to transfer data using
41 * cut/copy/paste operations.
42 * <p>
43 * {@link FlavorListener}s may be registered on an instance of the
44 * Clipboard class to be notified about changes to the set of
45 * {@link DataFlavor}s available on this clipboard (see
46 * {@link #addFlavorListener}).
47 *
48 * @see java.awt.Toolkit#getSystemClipboard
49 * @see java.awt.Toolkit#getSystemSelection
50 *
51 * @author Amy Fowler
52 * @author Alexander Gerasimov
53 */
54 public class Clipboard {
55
56 String name;
57
58 protected ClipboardOwner owner;
59 protected Transferable contents;
60
61 /**
62 * An aggregate of flavor listeners registered on this local clipboard.
63 *
64 * @since 1.5
65 */
66 private EventListenerAggregate flavorListeners;
67
68 /**
69 * A set of <code>DataFlavor</code>s that is available on
70 * this local clipboard. It is used for tracking changes
71 * of <code>DataFlavor</code>s available on this clipboard.
72 *
73 * @since 1.5
74 */
75 private Set currentDataFlavors;
76
77 /**
78 * Creates a clipboard object.
79 *
80 * @see java.awt.Toolkit#getSystemClipboard
81 */
82 public Clipboard(String name) {
83 this.name = name;
84 }
85
86 /**
87 * Returns the name of this clipboard object.
88 *
89 * @see java.awt.Toolkit#getSystemClipboard
90 */
91 public String getName() {
92 return name;
93 }
94
95 /**
96 * Sets the current contents of the clipboard to the specified
97 * transferable object and registers the specified clipboard owner
98 * as the owner of the new contents.
99 * <p>
100 * If there is an existing owner different from the argument
101 * <code>owner</code>, that owner is notified that it no longer
102 * holds ownership of the clipboard contents via an invocation
103 * of <code>ClipboardOwner.lostOwnership()</code> on that owner.
104 * An implementation of <code>setContents()</code> is free not
105 * to invoke <code>lostOwnership()</code> directly from this method.
106 * For example, <code>lostOwnership()</code> may be invoked later on
107 * a different thread. The same applies to <code>FlavorListener</code>s
108 * registered on this clipboard.
109 * <p>
110 * The method throws <code>IllegalStateException</code> if the clipboard
111 * is currently unavailable. For example, on some platforms, the system
112 * clipboard is unavailable while it is accessed by another application.
113 *
114 * @param contents the transferable object representing the
115 * clipboard content
116 * @param owner the object which owns the clipboard content
117 * @throws IllegalStateException if the clipboard is currently unavailable
118 * @see java.awt.Toolkit#getSystemClipboard
119 */
120 public synchronized void setContents(Transferable contents, ClipboardOwner owner) {
121 final ClipboardOwner oldOwner = this.owner;
122 final Transferable oldContents = this.contents;
123
124 this.owner = owner;
125 this.contents = contents;
126
127 if (oldOwner != null && oldOwner != owner) {
128 EventQueue.invokeLater(new Runnable() {
129 public void run() {
130 oldOwner.lostOwnership(Clipboard.this, oldContents);
131 }
132 });
133 }
134 fireFlavorsChanged();
135 }
136
137 /**
138 * Returns a transferable object representing the current contents
139 * of the clipboard. If the clipboard currently has no contents,
140 * it returns <code>null</code>. The parameter Object requestor is
141 * not currently used. The method throws
142 * <code>IllegalStateException</code> if the clipboard is currently
143 * unavailable. For example, on some platforms, the system clipboard is
144 * unavailable while it is accessed by another application.
145 *
146 * @param requestor the object requesting the clip data (not used)
147 * @return the current transferable object on the clipboard
148 * @throws IllegalStateException if the clipboard is currently unavailable
149 * @see java.awt.Toolkit#getSystemClipboard
150 */
151 public synchronized Transferable getContents(Object requestor) {
152 return contents;
153 }
154
155
156 /**
157 * Returns an array of <code>DataFlavor</code>s in which the current
158 * contents of this clipboard can be provided. If there are no
159 * <code>DataFlavor</code>s available, this method returns a zero-length
160 * array.
161 *
162 * @return an array of <code>DataFlavor</code>s in which the current
163 * contents of this clipboard can be provided
164 *
165 * @throws IllegalStateException if this clipboard is currently unavailable
166 *
167 * @since 1.5
168 */
169 public DataFlavor[] getAvailableDataFlavors() {
170 Transferable cntnts = getContents(null);
171 if (cntnts == null) {
172 return new DataFlavor[0];
173 }
174 return cntnts.getTransferDataFlavors();
175 }
176
177 /**
178 * Returns whether or not the current contents of this clipboard can be
179 * provided in the specified <code>DataFlavor</code>.
180 *
181 * @param flavor the requested <code>DataFlavor</code> for the contents
182 *
183 * @return <code>true</code> if the current contents of this clipboard
184 * can be provided in the specified <code>DataFlavor</code>;
185 * <code>false</code> otherwise
186 *
187 * @throws NullPointerException if <code>flavor</code> is <code>null</code>
188 * @throws IllegalStateException if this clipboard is currently unavailable
189 *
190 * @since 1.5
191 */
192 public boolean isDataFlavorAvailable(DataFlavor flavor) {
193 if (flavor == null) {
194 throw new NullPointerException("flavor");
195 }
196
197 Transferable cntnts = getContents(null);
198 if (cntnts == null) {
199 return false;
200 }
201 return cntnts.isDataFlavorSupported(flavor);
202 }
203
204 /**
205 * Returns an object representing the current contents of this clipboard
206 * in the specified <code>DataFlavor</code>.
207 * The class of the object returned is defined by the representation
208 * class of <code>flavor</code>.
209 *
210 * @param flavor the requested <code>DataFlavor</code> for the contents
211 *
212 * @return an object representing the current contents of this clipboard
213 * in the specified <code>DataFlavor</code>
214 *
215 * @throws NullPointerException if <code>flavor</code> is <code>null</code>
216 * @throws IllegalStateException if this clipboard is currently unavailable
217 * @throws UnsupportedFlavorException if the requested <code>DataFlavor</code>
218 * is not available
219 * @throws IOException if the data in the requested <code>DataFlavor</code>
220 * can not be retrieved
221 *
222 * @see DataFlavor#getRepresentationClass
223 *
224 * @since 1.5
225 */
226 public Object getData(DataFlavor flavor)
227 throws UnsupportedFlavorException, IOException {
228 if (flavor == null) {
229 throw new NullPointerException("flavor");
230 }
231
232 Transferable cntnts = getContents(null);
233 if (cntnts == null) {
234 throw new UnsupportedFlavorException(flavor);
235 }
236 return cntnts.getTransferData(flavor);
237 }
238
239
240 /**
241 * Registers the specified <code>FlavorListener</code> to receive
242 * <code>FlavorEvent</code>s from this clipboard.
243 * If <code>listener</code> is <code>null</code>, no exception
244 * is thrown and no action is performed.
245 *
246 * @param listener the listener to be added
247 *
248 * @see #removeFlavorListener
249 * @see #getFlavorListeners
250 * @see FlavorListener
251 * @see FlavorEvent
252 * @since 1.5
253 */
254 public synchronized void addFlavorListener(FlavorListener listener) {
255 if (listener == null) {
256 return;
257 }
258 if (flavorListeners == null) {
259 currentDataFlavors = getAvailableDataFlavorSet();
260 flavorListeners = new EventListenerAggregate(FlavorListener.class);
261 }
262 flavorListeners.add(listener);
263 }
264
265 /**
266 * Removes the specified <code>FlavorListener</code> so that it no longer
267 * receives <code>FlavorEvent</code>s from this <code>Clipboard</code>.
268 * This method performs no function, nor does it throw an exception, if
269 * the listener specified by the argument was not previously added to this
270 * <code>Clipboard</code>.
271 * If <code>listener</code> is <code>null</code>, no exception
272 * is thrown and no action is performed.
273 *
274 * @param listener the listener to be removed
275 *
276 * @see #addFlavorListener
277 * @see #getFlavorListeners
278 * @see FlavorListener
279 * @see FlavorEvent
280 * @since 1.5
281 */
282 public synchronized void removeFlavorListener(FlavorListener listener) {
283 if (listener == null || flavorListeners == null) {
284 return;
285 }
286 flavorListeners.remove(listener);
287 }
288
289 /**
290 * Returns an array of all the <code>FlavorListener</code>s currently
291 * registered on this <code>Clipboard</code>.
292 *
293 * @return all of this clipboard's <code>FlavorListener</code>s or an empty
294 * array if no listeners are currently registered
295 * @see #addFlavorListener
296 * @see #removeFlavorListener
297 * @see FlavorListener
298 * @see FlavorEvent
299 * @since 1.5
300 */
301 public synchronized FlavorListener[] getFlavorListeners() {
302 return flavorListeners == null ? new FlavorListener[0] :
303 (FlavorListener[])flavorListeners.getListenersCopy();
304 }
305
306 /**
307 * Checks change of the <code>DataFlavor</code>s and, if necessary,
308 * notifies all listeners that have registered interest for notification
309 * on <code>FlavorEvent</code>s.
310 *
311 * @since 1.5
312 */
313 private void fireFlavorsChanged() {
314 if (flavorListeners == null) {
315 return;
316 }
317 Set prevDataFlavors = currentDataFlavors;
318 currentDataFlavors = getAvailableDataFlavorSet();
319 if (prevDataFlavors.equals(currentDataFlavors)) {
320 return;
321 }
322 FlavorListener[] flavorListenerArray =
323 (FlavorListener[])flavorListeners.getListenersInternal();
324 for (int i = 0; i < flavorListenerArray.length; i++) {
325 final FlavorListener listener = flavorListenerArray[i];
326 EventQueue.invokeLater(new Runnable() {
327 public void run() {
328 listener.flavorsChanged(new FlavorEvent(Clipboard.this));
329 }
330 });
331 }
332 }
333
334 /**
335 * Returns a set of <code>DataFlavor</code>s currently available
336 * on this clipboard.
337 *
338 * @return a set of <code>DataFlavor</code>s currently available
339 * on this clipboard
340 *
341 * @since 1.5
342 */
343 private Set getAvailableDataFlavorSet() {
344 Set set = new HashSet();
345 Transferable contents = getContents(null);
346 if (contents != null) {
347 DataFlavor[] flavors = contents.getTransferDataFlavors();
348 if (flavors != null) {
349 set.addAll(Arrays.asList(flavors));
350 }
351 }
352 return set;
353 }
354 }