1 /* 2 * Copyright (c) 2000, 2005, 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 */ 25 26 package sun.awt.datatransfer; 27 28 import java.awt.datatransfer.DataFlavor; 29 import java.awt.datatransfer.Transferable; 30 import java.awt.datatransfer.UnsupportedFlavorException; 31 32 import java.io.IOException; 33 34 import java.util.HashMap; 35 import java.util.Iterator; 36 import java.util.Map; 37 38 39 /** 40 * Reads all of the data from the system Clipboard which the data transfer 41 * subsystem knows how to translate. This includes all text data, File Lists, 42 * Serializable objects, Remote objects, and properly registered, arbitrary 43 * data as InputStreams. The data is stored in byte format until requested 44 * by client code. At that point, the data is converted, if necessary, into 45 * the proper format to deliver to the application. 46 * 47 * This hybrid pre-fetch/delayed-rendering approach allows us to circumvent 48 * the API restriction that client code cannot lock the Clipboard to discover 49 * its formats before requesting data in a particular format, while avoiding 50 * the overhead of fully rendering all data ahead of time. 51 * 52 * @author David Mendenhall 53 * @author Danila Sinopalnikov 54 * 55 * @since 1.4 (appeared in modified form as FullyRenderedTransferable in 1.3.1) 56 */ 57 public class ClipboardTransferable implements Transferable { 58 private final HashMap flavorsToData = new HashMap(); 59 private DataFlavor[] flavors = new DataFlavor[0]; 60 61 private final class DataFactory { 62 final long format; 63 final byte[] data; 64 DataFactory(long format, byte[] data) { 65 this.format = format; 66 this.data = data; 67 } 68 69 public Object getTransferData(DataFlavor flavor) throws IOException { 70 return DataTransferer.getInstance(). 71 translateBytes(data, flavor, format, 72 ClipboardTransferable.this); 73 } 74 } 75 76 public ClipboardTransferable(SunClipboard clipboard) { 77 78 clipboard.openClipboard(null); 79 80 try { 81 long[] formats = clipboard.getClipboardFormats(); 82 83 if (formats != null && formats.length > 0) { 84 // Since the SystemFlavorMap will specify many DataFlavors 85 // which map to the same format, we should cache data as we 86 // read it. 87 HashMap cached_data = new HashMap(formats.length, 1.0f); 88 89 Map flavorsForFormats = DataTransferer.getInstance(). 90 getFlavorsForFormats(formats, SunClipboard.flavorMap); 91 for (Iterator iter = flavorsForFormats.keySet().iterator(); 92 iter.hasNext(); ) 93 { 94 DataFlavor flavor = (DataFlavor)iter.next(); 95 Long lFormat = (Long)flavorsForFormats.get(flavor); 96 97 fetchOneFlavor(clipboard, flavor, lFormat, cached_data); 98 } 99 100 flavors = DataTransferer.getInstance(). 101 setToSortedDataFlavorArray(flavorsToData.keySet()); 102 } 103 } finally { 104 clipboard.closeClipboard(); 105 } 106 } 107 108 private boolean fetchOneFlavor(SunClipboard clipboard, DataFlavor flavor, 109 Long lFormat, HashMap cached_data) 110 { 111 if (!flavorsToData.containsKey(flavor)) { 112 long format = lFormat.longValue(); 113 Object data = null; 114 115 if (!cached_data.containsKey(lFormat)) { 116 try { 117 data = clipboard.getClipboardData(format); 118 } catch (IOException e) { 119 data = e; 120 } catch (Throwable e) { 121 e.printStackTrace(); 122 } 123 124 // Cache this data, even if it's null, so we don't have to go 125 // to native code again for this format. 126 cached_data.put(lFormat, data); 127 } else { 128 data = cached_data.get(lFormat); 129 } 130 131 // Casting IOException to byte array causes ClassCastException. 132 // We should handle IOException separately - do not wrap them into 133 // DataFactory and report failure. 134 if (data instanceof IOException) { 135 flavorsToData.put(flavor, data); 136 return false; 137 } else if (data != null) { 138 flavorsToData.put(flavor, new DataFactory(format, 139 (byte[])data)); 140 return true; 141 } 142 } 143 144 return false; 145 } 146 147 public DataFlavor[] getTransferDataFlavors() { 148 return (DataFlavor[])flavors.clone(); 149 } 150 151 public boolean isDataFlavorSupported(DataFlavor flavor) { 152 return flavorsToData.containsKey(flavor); 153 } 154 155 public Object getTransferData(DataFlavor flavor) 156 throws UnsupportedFlavorException, IOException 157 { 158 if (!isDataFlavorSupported(flavor)) { 159 throw new UnsupportedFlavorException(flavor); 160 } 161 Object ret = flavorsToData.get(flavor); 162 if (ret instanceof IOException) { 163 // rethrow IOExceptions generated while fetching data 164 throw (IOException)ret; 165 } else if (ret instanceof DataFactory) { 166 // Now we can render the data 167 DataFactory factory = (DataFactory)ret; 168 ret = factory.getTransferData(flavor); 169 } 170 return ret; 171 } 172 173 }