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