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