001 /*
002 * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.awt.datatransfer;
027
028 import java.awt.EventQueue;
029
030 import java.util.Set;
031 import java.util.HashSet;
032 import java.util.Arrays;
033
034 import java.io.IOException;
035
036 import sun.awt.EventListenerAggregate;
037
038 /**
039 * A class that implements a mechanism to transfer data using
040 * cut/copy/paste operations.
041 * <p>
042 * {@link FlavorListener}s may be registered on an instance of the
043 * Clipboard class to be notified about changes to the set of
044 * {@link DataFlavor}s available on this clipboard (see
045 * {@link #addFlavorListener}).
046 *
047 * @see java.awt.Toolkit#getSystemClipboard
048 * @see java.awt.Toolkit#getSystemSelection
049 *
050 * @version 1.30, 05/05/07
051 * @author Amy Fowler
052 * @author Alexander Gerasimov
053 */
054 public class Clipboard {
055
056 String name;
057
058 protected ClipboardOwner owner;
059 protected Transferable contents;
060
061 /**
062 * An aggregate of flavor listeners registered on this local clipboard.
063 *
064 * @since 1.5
065 */
066 private EventListenerAggregate flavorListeners;
067
068 /**
069 * A set of <code>DataFlavor</code>s that is available on
070 * this local clipboard. It is used for tracking changes
071 * of <code>DataFlavor</code>s available on this clipboard.
072 *
073 * @since 1.5
074 */
075 private Set currentDataFlavors;
076
077 /**
078 * Creates a clipboard object.
079 *
080 * @see java.awt.Toolkit#getSystemClipboard
081 */
082 public Clipboard(String name) {
083 this .name = name;
084 }
085
086 /**
087 * Returns the name of this clipboard object.
088 *
089 * @see java.awt.Toolkit#getSystemClipboard
090 */
091 public String getName() {
092 return name;
093 }
094
095 /**
096 * Sets the current contents of the clipboard to the specified
097 * transferable object and registers the specified clipboard owner
098 * as the owner of the new contents.
099 * <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,
121 ClipboardOwner owner) {
122 final ClipboardOwner oldOwner = this .owner;
123 final Transferable oldContents = this .contents;
124
125 this .owner = owner;
126 this .contents = contents;
127
128 if (oldOwner != null && oldOwner != owner) {
129 EventQueue.invokeLater(new Runnable() {
130 public void run() {
131 oldOwner.lostOwnership(Clipboard.this , oldContents);
132 }
133 });
134 }
135 fireFlavorsChanged();
136 }
137
138 /**
139 * Returns a transferable object representing the current contents
140 * of the clipboard. If the clipboard currently has no contents,
141 * it returns <code>null</code>. The parameter Object requestor is
142 * not currently used. The method throws
143 * <code>IllegalStateException</code> if the clipboard is currently
144 * unavailable. For example, on some platforms, the system clipboard is
145 * unavailable while it is accessed by another application.
146 *
147 * @param requestor the object requesting the clip data (not used)
148 * @return the current transferable object on the clipboard
149 * @throws IllegalStateException if the clipboard is currently unavailable
150 * @see java.awt.Toolkit#getSystemClipboard
151 */
152 public synchronized Transferable getContents(Object requestor) {
153 return contents;
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 * Registers the specified <code>FlavorListener</code> to receive
241 * <code>FlavorEvent</code>s from this clipboard.
242 * If <code>listener</code> is <code>null</code>, no exception
243 * is thrown and no action is performed.
244 *
245 * @param listener the listener to be added
246 *
247 * @see #removeFlavorListener
248 * @see #getFlavorListeners
249 * @see FlavorListener
250 * @see FlavorEvent
251 * @since 1.5
252 */
253 public synchronized void addFlavorListener(FlavorListener listener) {
254 if (listener == null) {
255 return;
256 }
257 if (flavorListeners == null) {
258 currentDataFlavors = getAvailableDataFlavorSet();
259 flavorListeners = new EventListenerAggregate(
260 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(
283 FlavorListener listener) {
284 if (listener == null || flavorListeners == null) {
285 return;
286 }
287 flavorListeners.remove(listener);
288 }
289
290 /**
291 * Returns an array of all the <code>FlavorListener</code>s currently
292 * registered on this <code>Clipboard</code>.
293 *
294 * @return all of this clipboard's <code>FlavorListener</code>s or an empty
295 * array if no listeners are currently registered
296 * @see #addFlavorListener
297 * @see #removeFlavorListener
298 * @see FlavorListener
299 * @see FlavorEvent
300 * @since 1.5
301 */
302 public synchronized FlavorListener[] getFlavorListeners() {
303 return flavorListeners == null ? new FlavorListener[0]
304 : (FlavorListener[]) flavorListeners.getListenersCopy();
305 }
306
307 /**
308 * Checks change of the <code>DataFlavor</code>s and, if necessary,
309 * notifies all listeners that have registered interest for notification
310 * on <code>FlavorEvent</code>s.
311 *
312 * @since 1.5
313 */
314 private void fireFlavorsChanged() {
315 if (flavorListeners == null) {
316 return;
317 }
318 Set prevDataFlavors = currentDataFlavors;
319 currentDataFlavors = getAvailableDataFlavorSet();
320 if (prevDataFlavors.equals(currentDataFlavors)) {
321 return;
322 }
323 FlavorListener[] flavorListenerArray = (FlavorListener[]) flavorListeners
324 .getListenersInternal();
325 for (int i = 0; i < flavorListenerArray.length; i++) {
326 final FlavorListener listener = flavorListenerArray[i];
327 EventQueue.invokeLater(new Runnable() {
328 public void run() {
329 listener.flavorsChanged(new FlavorEvent(
330 Clipboard.this ));
331 }
332 });
333 }
334 }
335
336 /**
337 * Returns a set of <code>DataFlavor</code>s currently available
338 * on this clipboard.
339 *
340 * @return a set of <code>DataFlavor</code>s currently available
341 * on this clipboard
342 *
343 * @since 1.5
344 */
345 private Set getAvailableDataFlavorSet() {
346 Set set = new HashSet();
347 Transferable contents = getContents(null);
348 if (contents != null) {
349 DataFlavor[] flavors = contents.getTransferDataFlavors();
350 if (flavors != null) {
351 set.addAll(Arrays.asList(flavors));
352 }
353 }
354 return set;
355 }
356 }
|