1 /* 2 * Copyright (c) 2011, 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.lwawt.macosx; 27 28 import sun.awt.datatransfer.desktop.DataTransferer; 29 import sun.awt.datatransfer.desktop.ToolkitThreadBlockedHandler; 30 31 import java.awt.*; 32 33 import java.io.*; 34 import java.net.URL; 35 import java.nio.charset.Charset; 36 import java.text.Normalizer; 37 import java.text.Normalizer.Form; 38 import java.util.*; 39 40 import java.awt.datatransfer.*; 41 42 public class CDataTransferer extends DataTransferer { 43 private static final Map<String, Long> predefinedClipboardNameMap; 44 private static final Map<Long, String> predefinedClipboardFormatMap; 45 46 // See SystemFlavorMap, or the flavormap.properties file: 47 // We should define a few more types in flavormap.properties, it's rather slim now. 48 private static final String[] predefinedClipboardNames = { 49 "", 50 "STRING", 51 "FILE_NAME", 52 "TIFF", 53 "RICH_TEXT", 54 "HTML", 55 "PDF", 56 "URL", 57 "PNG", 58 "JFIF" 59 }; 60 61 static { 62 Map<String, Long> nameMap = new HashMap<>(predefinedClipboardNames.length, 1.0f); 63 Map<Long, String> formatMap = new HashMap<>(predefinedClipboardNames.length, 1.0f); 64 for (int i = 1; i < predefinedClipboardNames.length; i++) { 65 nameMap.put(predefinedClipboardNames[i], (long) i); 66 formatMap.put((long) i, predefinedClipboardNames[i]); 67 } 68 predefinedClipboardNameMap = Collections.synchronizedMap(nameMap); 69 predefinedClipboardFormatMap = Collections.synchronizedMap(formatMap); 70 } 71 72 public static final int CF_UNSUPPORTED = 0; 73 public static final int CF_STRING = 1; 74 public static final int CF_FILE = 2; 75 public static final int CF_TIFF = 3; 76 public static final int CF_RICH_TEXT = 4; 77 public static final int CF_HTML = 5; 78 public static final int CF_PDF = 6; 79 public static final int CF_URL = 7; 80 public static final int CF_PNG = 8; 81 public static final int CF_JPEG = 9; 82 83 private CDataTransferer() {} 84 85 private static CDataTransferer fTransferer; 86 87 static synchronized CDataTransferer getInstanceImpl() { 88 if (fTransferer == null) { 89 fTransferer = new CDataTransferer(); 90 } 91 92 return fTransferer; 93 } 94 95 @Override 96 public String getDefaultUnicodeEncoding() { 97 return "utf-16le"; 98 } 99 100 @Override 101 public boolean isLocaleDependentTextFormat(long format) { 102 return format == CF_STRING; 103 } 104 105 @Override 106 public boolean isFileFormat(long format) { 107 return format == CF_FILE; 108 } 109 110 @Override 111 public boolean isImageFormat(long format) { 112 int ifmt = (int)format; 113 switch(ifmt) { 114 case CF_TIFF: 115 case CF_PDF: 116 case CF_PNG: 117 case CF_JPEG: 118 return true; 119 default: 120 return false; 121 } 122 } 123 124 @Override 125 public Object translateBytes(byte[] bytes, DataFlavor flavor, 126 long format, Transferable transferable) throws IOException { 127 128 if (format == CF_URL && URL.class.equals(flavor.getRepresentationClass())) 129 { 130 String charset = Charset.defaultCharset().name(); 131 if (transferable != null && transferable.isDataFlavorSupported(javaTextEncodingFlavor)) { 132 try { 133 charset = new String((byte[])transferable.getTransferData(javaTextEncodingFlavor), "UTF-8"); 134 } catch (UnsupportedFlavorException cannotHappen) { 135 } 136 } 137 138 return new URL(new String(bytes, charset)); 139 } 140 141 if (format == CF_STRING) { 142 bytes = Normalizer.normalize(new String(bytes, "UTF8"), Form.NFC).getBytes("UTF8"); 143 } 144 145 return super.translateBytes(bytes, flavor, format, transferable); 146 } 147 148 @Override 149 synchronized protected Long getFormatForNativeAsLong(String str) { 150 Long format = predefinedClipboardNameMap.get(str); 151 152 if (format == null) { 153 if (java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance()) { 154 // Do not try to access native system for the unknown format 155 return -1L; 156 } 157 format = registerFormatWithPasteboard(str); 158 predefinedClipboardNameMap.put(str, format); 159 predefinedClipboardFormatMap.put(format, str); 160 } 161 162 return format; 163 } 164 165 /* 166 * Adds type to native mapping NSDictionary. 167 */ 168 private native long registerFormatWithPasteboard(String type); 169 170 // Get registered native format string for an index, return null if unknown: 171 private native String formatForIndex(long index); 172 173 @Override 174 protected String getNativeForFormat(long format) { 175 String returnValue = null; 176 177 // The most common case - just index the array of predefined names: 178 if (format >= 0 && format < predefinedClipboardNames.length) { 179 returnValue = predefinedClipboardNames[(int) format]; 180 } else { 181 Long formatObj = format; 182 returnValue = predefinedClipboardFormatMap.get(formatObj); 183 184 // predefinedClipboardFormatMap may not know this format: 185 if (returnValue == null) { 186 returnValue = formatForIndex(format); 187 188 // Native clipboard may not know this format either: 189 if (returnValue != null) { 190 predefinedClipboardNameMap.put(returnValue, formatObj); 191 predefinedClipboardFormatMap.put(formatObj, returnValue); 192 } 193 } 194 } 195 196 if (returnValue == null) { 197 returnValue = predefinedClipboardNames[CF_UNSUPPORTED]; 198 } 199 200 return returnValue; 201 } 202 203 private final ToolkitThreadBlockedHandler handler = new CToolkitThreadBlockedHandler(); 204 205 @Override 206 public ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler() { 207 return handler; 208 } 209 210 @Override 211 protected byte[] imageToPlatformBytes(Image image, long format) { 212 return CImage.getCreator().getPlatformImageBytes(image); 213 } 214 215 private static native String[] nativeDragQueryFile(final byte[] bytes); 216 @Override 217 protected String[] dragQueryFile(final byte[] bytes) { 218 if (bytes == null) return null; 219 if (new String(bytes).startsWith("Unsupported type")) return null; 220 return nativeDragQueryFile(bytes); 221 } 222 223 @Override 224 protected Image platformImageBytesToImage(byte[] bytes, long format) throws IOException { 225 return CImage.getCreator().createImageFromPlatformImageBytes(bytes); 226 } 227 228 @Override 229 protected ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList) throws IOException { 230 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 231 for (String file : fileList) { 232 byte[] bytes = file.getBytes(); 233 bos.write(bytes, 0, bytes.length); 234 bos.write(0); 235 } 236 return bos; 237 } 238 239 @Override 240 protected boolean isURIListFormat(long format) { 241 String nat = getNativeForFormat(format); 242 if (nat == null) { 243 return false; 244 } 245 try { 246 DataFlavor df = new DataFlavor(nat); 247 if (df.getPrimaryType().equals("text") && df.getSubType().equals("uri-list")) { 248 return true; 249 } 250 } catch (Exception e) { 251 // Not a MIME format. 252 } 253 return false; 254 } 255 } 256 257