jdk/src/share/classes/sun/awt/datatransfer/ClipboardTransferable.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/awt/datatransfer/ClipboardTransferable.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2000-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.awt.datatransfer;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+
+/**
+ * Reads all of the data from the system Clipboard which the data transfer
+ * subsystem knows how to translate. This includes all text data, File Lists,
+ * Serializable objects, Remote objects, and properly registered, arbitrary
+ * data as InputStreams. The data is stored in byte format until requested
+ * by client code. At that point, the data is converted, if necessary, into
+ * the proper format to deliver to the application.
+ *
+ * This hybrid pre-fetch/delayed-rendering approach allows us to circumvent
+ * the API restriction that client code cannot lock the Clipboard to discover
+ * its formats before requesting data in a particular format, while avoiding
+ * the overhead of fully rendering all data ahead of time.
+ *
+ * @author David Mendenhall
+ * @author Danila Sinopalnikov
+ *
+ * @since 1.4 (appeared in modified form as FullyRenderedTransferable in 1.3.1)
+ */
+public class ClipboardTransferable implements Transferable {
+    private final HashMap flavorsToData = new HashMap();
+    private DataFlavor[] flavors = new DataFlavor[0];
+
+    private final class DataFactory {
+        final long format;
+        final byte[] data;
+        DataFactory(long format, byte[] data) {
+            this.format = format;
+            this.data   = data;
+        }
+
+        public Object getTransferData(DataFlavor flavor) throws IOException {
+            return DataTransferer.getInstance().
+                translateBytes(data, flavor, format,
+                               ClipboardTransferable.this);
+        }
+    }
+
+    public ClipboardTransferable(SunClipboard clipboard) {
+
+        clipboard.openClipboard(null);
+
+        try {
+            long[] formats = clipboard.getClipboardFormats();
+
+            if (formats != null && formats.length > 0) {
+                // Since the SystemFlavorMap will specify many DataFlavors
+                // which map to the same format, we should cache data as we
+                // read it.
+                HashMap cached_data = new HashMap(formats.length, 1.0f);
+
+                Map flavorsForFormats = DataTransferer.getInstance().
+                    getFlavorsForFormats(formats, SunClipboard.flavorMap);
+                for (Iterator iter = flavorsForFormats.keySet().iterator();
+                     iter.hasNext(); )
+                {
+                    DataFlavor flavor = (DataFlavor)iter.next();
+                    Long lFormat = (Long)flavorsForFormats.get(flavor);
+
+                    fetchOneFlavor(clipboard, flavor, lFormat, cached_data);
+                }
+
+                flavors = DataTransferer.getInstance().
+                    setToSortedDataFlavorArray(flavorsToData.keySet(),
+                                               flavorsForFormats);
+            }
+        } finally {
+            clipboard.closeClipboard();
+        }
+    }
+
+    private boolean fetchOneFlavor(SunClipboard clipboard, DataFlavor flavor,
+                                   Long lFormat, HashMap cached_data)
+    {
+        if (!flavorsToData.containsKey(flavor)) {
+            long format = lFormat.longValue();
+            Object data = null;
+
+            if (!cached_data.containsKey(lFormat)) {
+                try {
+                    data = clipboard.getClipboardData(format);
+                } catch (IOException e) {
+                    data = e;
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                }
+
+                // Cache this data, even if it's null, so we don't have to go
+                // to native code again for this format.
+                cached_data.put(lFormat, data);
+            } else {
+                data = cached_data.get(lFormat);
+            }
+
+            // Casting IOException to byte array causes ClassCastException.
+            // We should handle IOException separately - do not wrap them into
+            // DataFactory and report failure.
+            if (data instanceof IOException) {
+                flavorsToData.put(flavor, data);
+                return false;
+            } else if (data != null) {
+                flavorsToData.put(flavor, new DataFactory(format,
+                                                          (byte[])data));
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public DataFlavor[] getTransferDataFlavors() {
+        return (DataFlavor[])flavors.clone();
+    }
+
+    public boolean isDataFlavorSupported(DataFlavor flavor) {
+        return flavorsToData.containsKey(flavor);
+    }
+
+    public Object getTransferData(DataFlavor flavor)
+        throws UnsupportedFlavorException, IOException
+    {
+        if (!isDataFlavorSupported(flavor)) {
+            throw new UnsupportedFlavorException(flavor);
+        }
+        Object ret = flavorsToData.get(flavor);
+        if (ret instanceof IOException) {
+            // rethrow IOExceptions generated while fetching data
+            throw (IOException)ret;
+        } else if (ret instanceof DataFactory) {
+            // Now we can render the data
+            DataFactory factory = (DataFactory)ret;
+            ret = factory.getTransferData(flavor);
+        }
+        return ret;
+    }
+
+}