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