| /* | |
| * Copyright (c) 2000, 2014, Oracle and/or its affiliates. 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. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.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.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 Map<DataFlavor, Object> 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. | |
| Map<Long, Object> cached_data = new HashMap<>(formats.length, 1.0f); | |
| DataTransferer.getInstance() | |
| .getFlavorsForFormats(formats, SunClipboard.getDefaultFlavorTable()) | |
| .entrySet() | |
| .forEach(entry -> fetchOneFlavor(clipboard, entry.getKey(), entry.getValue(), cached_data)); | |
| flavors = DataTransferer.setToSortedDataFlavorArray(flavorsToData.keySet()); | |
| } | |
| } finally { | |
| clipboard.closeClipboard(); | |
| } | |
| } | |
| private boolean fetchOneFlavor(SunClipboard clipboard, DataFlavor flavor, | |
| long format, Map<Long, Object> cached_data) | |
| { | |
| if (!flavorsToData.containsKey(flavor)) { | |
| Object data = null; | |
| if (!cached_data.containsKey(format)) { | |
| 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(format, data); | |
| } else { | |
| data = cached_data.get(format); | |
| } | |
| // 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; | |
| } | |
| @Override | |
| public DataFlavor[] getTransferDataFlavors() { | |
| return flavors.clone(); | |
| } | |
| @Override | |
| public boolean isDataFlavorSupported(DataFlavor flavor) { | |
| return flavorsToData.containsKey(flavor); | |
| } | |
| @Override | |
| 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 new IOException("Exception fetching data: ", (IOException)ret); | |
| } else if (ret instanceof DataFactory) { | |
| // Now we can render the data | |
| DataFactory factory = (DataFactory)ret; | |
| ret = factory.getTransferData(flavor); | |
| } | |
| return ret; | |
| } | |
| } |