PageRenderTime 60ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/src/macosx/classes/sun/lwawt/macosx/CDataTransferer.java

https://bitbucket.org/psandoz/lambda-jdk
Java | 524 lines | 382 code | 85 blank | 57 comment | 77 complexity | 95c7e848d27699a08fd633ed72f1d3ad MD5 | raw file
Possible License(s): BSD-3-Clause-No-Nuclear-License-2014, LGPL-3.0, GPL-2.0
  1. /*
  2. * Copyright (c) 2011, 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. package sun.lwawt.macosx;
  26. import java.awt.*;
  27. import java.awt.image.*;
  28. import sun.awt.image.ImageRepresentation;
  29. import java.io.*;
  30. import java.net.URL;
  31. import java.text.Normalizer;
  32. import java.text.Normalizer.Form;
  33. import java.util.*;
  34. import java.awt.datatransfer.*;
  35. import sun.awt.datatransfer.*;
  36. public class CDataTransferer extends DataTransferer {
  37. private static final Map<String, Long> predefinedClipboardNameMap;
  38. private static final Map<Long, String> predefinedClipboardFormatMap;
  39. // See SystemFlavorMap, or the flavormap.properties file:
  40. // We should define a few more types in flavormap.properties, it's rather slim now.
  41. private static final String[] predefinedClipboardNames = {
  42. "",
  43. "STRING",
  44. "FILE_NAME",
  45. "TIFF",
  46. "RICH_TEXT",
  47. "HTML",
  48. "PDF",
  49. "URL"
  50. };
  51. static {
  52. Map<String, Long> nameMap = new HashMap<String, Long>(predefinedClipboardNames.length, 1.0f);
  53. Map<Long, String> formatMap = new HashMap<Long, String>(predefinedClipboardNames.length, 1.0f);
  54. for (int i = 1; i < predefinedClipboardNames.length; i++) {
  55. nameMap.put(predefinedClipboardNames[i], new Long(i));
  56. formatMap.put(new Long(i), predefinedClipboardNames[i]);
  57. }
  58. predefinedClipboardNameMap = Collections.synchronizedMap(nameMap);
  59. predefinedClipboardFormatMap = Collections.synchronizedMap(formatMap);
  60. }
  61. public static final int CF_UNSUPPORTED = 0;
  62. public static final int CF_STRING = 1;
  63. public static final int CF_FILE = 2;
  64. public static final int CF_TIFF = 3;
  65. public static final int CF_RICH_TEXT = 4;
  66. public static final int CF_HTML = 5;
  67. public static final int CF_PDF = 6;
  68. public static final int CF_URL = 7;
  69. public static final int CF_PNG = 10;
  70. public static final int CF_JPEG = 11;
  71. public static final Long L_CF_TIFF = predefinedClipboardNameMap.get(predefinedClipboardNames[CF_TIFF]);
  72. // Image file formats with java.awt.Image representation:
  73. private static final Long[] imageFormats = new Long[] {
  74. L_CF_TIFF
  75. };
  76. private CDataTransferer() {}
  77. private static CDataTransferer fTransferer;
  78. public static synchronized CDataTransferer getInstanceImpl() {
  79. if (fTransferer == null) {
  80. fTransferer = new CDataTransferer();
  81. }
  82. return fTransferer;
  83. }
  84. public String getDefaultUnicodeEncoding() {
  85. return "utf-16le";
  86. }
  87. public boolean isLocaleDependentTextFormat(long format) {
  88. return format == CF_STRING;
  89. }
  90. public boolean isFileFormat(long format) {
  91. return format == CF_FILE;
  92. }
  93. public boolean isImageFormat(long format) {
  94. int ifmt = (int)format;
  95. switch(ifmt) {
  96. case CF_TIFF:
  97. case CF_PDF:
  98. case CF_PNG:
  99. case CF_JPEG:
  100. return true;
  101. default:
  102. return false;
  103. }
  104. }
  105. protected Long[] getImageFormatsAsLongArray() {
  106. return imageFormats;
  107. }
  108. public byte[] translateTransferable(Transferable contents, DataFlavor flavor, long format) throws IOException
  109. {
  110. byte[] bytes = super.translateTransferable(contents, flavor, format);
  111. // 9-12-02 VL: we may need to do something like Windows here.
  112. //if (format == CF_HTML) {
  113. // bytes = HTMLSupport.convertToHTMLFormat(bytes);
  114. //}
  115. return bytes;
  116. }
  117. protected Object translateBytesOrStream(InputStream stream, byte[] bytes, DataFlavor flavor, long format,
  118. Transferable transferable) throws IOException
  119. {
  120. // 5-28-03 VL: [Radar 3266030]
  121. // We need to do like Windows does here.
  122. if (format == CF_HTML && flavor.isFlavorTextType()) {
  123. if (stream == null) {
  124. stream = new ByteArrayInputStream(bytes);
  125. bytes = null;
  126. }
  127. stream = new HTMLDecodingInputStream(stream);
  128. }
  129. if (format == CF_URL && URL.class.equals(flavor.getRepresentationClass()))
  130. {
  131. if (bytes == null) {
  132. bytes = inputStreamToByteArray(stream);
  133. stream = null;
  134. }
  135. String charset = getDefaultTextCharset();
  136. if (transferable != null && transferable.isDataFlavorSupported(javaTextEncodingFlavor)) {
  137. try {
  138. charset = new String((byte[])transferable.getTransferData(javaTextEncodingFlavor), "UTF-8");
  139. } catch (UnsupportedFlavorException cannotHappen) {
  140. }
  141. }
  142. return new URL(new String(bytes, charset));
  143. }
  144. if (format == CF_STRING) {
  145. bytes = Normalizer.normalize(new String(bytes, "UTF8"), Form.NFC).getBytes("UTF8");
  146. }
  147. return super.translateBytesOrStream(stream, bytes, flavor, format, transferable);
  148. }
  149. synchronized protected Long getFormatForNativeAsLong(String str) {
  150. Long format = predefinedClipboardNameMap.get(str);
  151. if (format == null) {
  152. format = new Long(registerFormatWithPasteboard(str));
  153. predefinedClipboardNameMap.put(str, format);
  154. predefinedClipboardFormatMap.put(format, str);
  155. }
  156. return format;
  157. }
  158. /*
  159. * Adds type to native mapping NSDictionary.
  160. */
  161. private native long registerFormatWithPasteboard(String type);
  162. // Get registered native format string for an index, return null if unknown:
  163. private native String formatForIndex(long index);
  164. protected String getNativeForFormat(long format) {
  165. String returnValue = null;
  166. // The most common case - just index the array of predefined names:
  167. if (format >= 0 && format < predefinedClipboardNames.length) {
  168. returnValue = predefinedClipboardNames[(int) format];
  169. } else {
  170. Long formatObj = new Long(format);
  171. returnValue = predefinedClipboardFormatMap.get(formatObj);
  172. // predefinedClipboardFormatMap may not know this format:
  173. if (returnValue == null) {
  174. returnValue = formatForIndex(format);
  175. // Native clipboard may not know this format either:
  176. if (returnValue != null) {
  177. predefinedClipboardNameMap.put(returnValue, formatObj);
  178. predefinedClipboardFormatMap.put(formatObj, returnValue);
  179. }
  180. }
  181. }
  182. if (returnValue == null) {
  183. returnValue = predefinedClipboardNames[CF_UNSUPPORTED];
  184. }
  185. return returnValue;
  186. }
  187. private final ToolkitThreadBlockedHandler handler = new CToolkitThreadBlockedHandler();
  188. public ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler() {
  189. return handler;
  190. }
  191. protected byte[] imageToPlatformBytes(Image image, long format) {
  192. int w = image.getWidth(null);
  193. int h = image.getHeight(null);
  194. BufferedImage bimage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
  195. Graphics g = bimage.getGraphics();
  196. g.drawImage(image, 0, 0, w, h, null);
  197. g.dispose();
  198. Raster raster = bimage.getRaster();
  199. DataBuffer buffer = raster.getDataBuffer();
  200. return imageDataToPlatformImageBytes(((DataBufferInt)buffer).getData(),
  201. raster.getWidth(),
  202. raster.getHeight());
  203. }
  204. private static native String[] nativeDragQueryFile(final byte[] bytes);
  205. protected String[] dragQueryFile(final byte[] bytes) {
  206. if (bytes == null) return null;
  207. if (new String(bytes).startsWith("Unsupported type")) return null;
  208. return nativeDragQueryFile(bytes);
  209. }
  210. private native byte[] imageDataToPlatformImageBytes(int[] rData, int nW, int nH);
  211. /**
  212. * Translates either a byte array or an input stream which contain
  213. * platform-specific image data in the given format into an Image.
  214. */
  215. protected Image platformImageBytesOrStreamToImage(InputStream stream, byte[] bytes, long format) throws IOException {
  216. byte[] imageData = bytes;
  217. if (imageData == null)
  218. imageData = inputStreamToByteArray(stream);
  219. return getImageForByteStream(imageData);
  220. }
  221. private native Image getImageForByteStream(byte[] bytes);
  222. @Override
  223. protected ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList) throws IOException {
  224. // TODO Auto-generated method stub
  225. return null;
  226. }
  227. }
  228. // ---- Code borrowed from WDataTransferer: ----
  229. // This will come handy for supporting HTML data.
  230. final class HTMLSupport {
  231. public static final String ENCODING = "UTF-8";
  232. public static final String VERSION = "Version:";
  233. public static final String START_HTML = "StartHTML:";
  234. public static final String END_HTML = "EndHTML:";
  235. public static final String START_FRAGMENT = "StartFragment:";
  236. public static final String END_FRAGMENT = "EndFragment:";
  237. public static final String START_FRAGMENT_CMT = "<!--StartFragment-->";
  238. public static final String END_FRAGMENT_CMT = "<!--EndFragment-->";
  239. public static final String EOLN = "\r\n";
  240. private static final String VERSION_NUM = "0.9";
  241. private static final String HTML_START_END = "-1";
  242. private static final int PADDED_WIDTH = 10;
  243. private static final int HEADER_LEN =
  244. VERSION.length() + VERSION_NUM.length() + EOLN.length() +
  245. START_HTML.length() + HTML_START_END.length() + EOLN.length() +
  246. END_HTML.length() + HTML_START_END.length() + EOLN.length() +
  247. START_FRAGMENT.length() + PADDED_WIDTH + EOLN.length() +
  248. END_FRAGMENT.length() + PADDED_WIDTH + EOLN.length() +
  249. START_FRAGMENT_CMT.length() + EOLN.length();
  250. private static final String HEADER_LEN_STR =
  251. toPaddedString(HEADER_LEN, PADDED_WIDTH);
  252. private static final String TRAILER = END_FRAGMENT_CMT + EOLN + '\0';
  253. private static String toPaddedString(int n, int width) {
  254. String string = "" + n;
  255. int len = string.length();
  256. if (n >= 0 && len < width) {
  257. char[] array = new char[width - len];
  258. Arrays.fill(array, '0');
  259. StringBuffer buffer = new StringBuffer();
  260. buffer.append(array);
  261. buffer.append(string);
  262. string = buffer.toString();
  263. }
  264. return string;
  265. }
  266. public static byte[] convertToHTMLFormat(byte[] bytes) {
  267. StringBuffer header = new StringBuffer(HEADER_LEN);
  268. header.append(VERSION);
  269. header.append(VERSION_NUM);
  270. header.append(EOLN);
  271. header.append(START_HTML);
  272. header.append(HTML_START_END);
  273. header.append(EOLN);
  274. header.append(END_HTML);
  275. header.append(HTML_START_END);
  276. header.append(EOLN);
  277. header.append(START_FRAGMENT);
  278. header.append(HEADER_LEN_STR);
  279. header.append(EOLN);
  280. header.append(END_FRAGMENT);
  281. // Strip terminating NUL byte from array
  282. header.append(toPaddedString(HEADER_LEN + bytes.length - 1,
  283. PADDED_WIDTH));
  284. header.append(EOLN);
  285. header.append(START_FRAGMENT_CMT);
  286. header.append(EOLN);
  287. byte[] headerBytes = null, trailerBytes = null;
  288. try {
  289. headerBytes = new String(header).getBytes(ENCODING);
  290. trailerBytes = TRAILER.getBytes(ENCODING);
  291. } catch (UnsupportedEncodingException cannotHappen) {
  292. }
  293. byte[] retval = new byte[headerBytes.length + bytes.length - 1 +
  294. trailerBytes.length];
  295. System.arraycopy(headerBytes, 0, retval, 0, headerBytes.length);
  296. System.arraycopy(bytes, 0, retval, headerBytes.length,
  297. bytes.length - 1);
  298. System.arraycopy(trailerBytes, 0, retval,
  299. headerBytes.length + bytes.length - 1,
  300. trailerBytes.length);
  301. return retval;
  302. }
  303. }
  304. /**
  305. * This stream takes an InputStream which provides data in CF_HTML format,
  306. * strips off the description and context to extract the original HTML data.
  307. */
  308. class HTMLDecodingInputStream extends InputStream {
  309. private final BufferedInputStream bufferedStream;
  310. private boolean descriptionParsed = false;
  311. private boolean closed = false;
  312. private int index;
  313. private int end;
  314. // InputStreamReader uses an 8K buffer. The size is not customizable.
  315. public static final int BYTE_BUFFER_LEN = 8192;
  316. // CharToByteUTF8.getMaxBytesPerChar returns 3, so we should not buffer
  317. // more chars than 3 times the number of bytes we can buffer.
  318. public static final int CHAR_BUFFER_LEN = BYTE_BUFFER_LEN / 3;
  319. private static final String FAILURE_MSG =
  320. "Unable to parse HTML description: ";
  321. private static final String INVALID_MSG = " invalid";
  322. public HTMLDecodingInputStream(InputStream bytestream) throws IOException {
  323. bufferedStream = new BufferedInputStream(bytestream, BYTE_BUFFER_LEN);
  324. }
  325. private void parseDescription() throws IOException {
  326. bufferedStream.mark(BYTE_BUFFER_LEN);
  327. BufferedReader bufferedReader = new BufferedReader
  328. (new InputStreamReader(bufferedStream, HTMLSupport.ENCODING),
  329. CHAR_BUFFER_LEN);
  330. String version = bufferedReader.readLine().trim();
  331. if (version == null || !version.startsWith(HTMLSupport.VERSION)) {
  332. // Not MS-compliant HTML text. Return raw text from read().
  333. index = 0;
  334. end = -1;
  335. bufferedStream.reset();
  336. return;
  337. }
  338. String input;
  339. boolean startHTML, endHTML, startFragment, endFragment;
  340. startHTML = endHTML = startFragment = endFragment = false;
  341. try {
  342. do {
  343. input = bufferedReader.readLine().trim();
  344. if (input == null) {
  345. close();
  346. throw new IOException(FAILURE_MSG);
  347. } else if (input.startsWith(HTMLSupport.START_HTML)) {
  348. int val = Integer.parseInt
  349. (input.substring(HTMLSupport.START_HTML.length(),
  350. input.length()).trim());
  351. if (val >= 0) {
  352. index = val;
  353. startHTML = true;
  354. } else if (val != -1) {
  355. close();
  356. throw new IOException(FAILURE_MSG +
  357. HTMLSupport.START_HTML +
  358. INVALID_MSG);
  359. }
  360. } else if (input.startsWith(HTMLSupport.END_HTML)) {
  361. int val = Integer.parseInt
  362. (input.substring(HTMLSupport.END_HTML.length(),
  363. input.length()).trim());
  364. if (val >= 0) {
  365. end = val;
  366. endHTML = true;
  367. } else if (val != -1) {
  368. close();
  369. throw new IOException(FAILURE_MSG +
  370. HTMLSupport.END_HTML +
  371. INVALID_MSG);
  372. }
  373. } else if (!startHTML && !endHTML &&
  374. input.startsWith(HTMLSupport.START_FRAGMENT)) {
  375. index = Integer.parseInt
  376. (input.substring(HTMLSupport.START_FRAGMENT.length(),
  377. input.length()).trim());
  378. if (index < 0) {
  379. close();
  380. throw new IOException(FAILURE_MSG +
  381. HTMLSupport.START_FRAGMENT +
  382. INVALID_MSG);
  383. }
  384. startFragment = true;
  385. } else if (!startHTML && !endHTML &&
  386. input.startsWith(HTMLSupport.END_FRAGMENT)) {
  387. end = Integer.parseInt
  388. (input.substring(HTMLSupport.END_FRAGMENT.length(),
  389. input.length()).trim());
  390. if (end < 0) {
  391. close();
  392. throw new IOException(FAILURE_MSG +
  393. HTMLSupport.END_FRAGMENT +
  394. INVALID_MSG);
  395. }
  396. endFragment = true;
  397. }
  398. } while (!((startHTML && endHTML) ||
  399. (startFragment && endFragment)));
  400. } catch (NumberFormatException e) {
  401. close();
  402. throw new IOException(FAILURE_MSG + e);
  403. }
  404. bufferedStream.reset();
  405. for (int i = 0; i < index; i++) {
  406. if (bufferedStream.read() == -1) {
  407. close();
  408. throw new IOException(FAILURE_MSG +
  409. "Byte stream ends in description.");
  410. }
  411. }
  412. }
  413. public int read() throws IOException {
  414. if (closed) {
  415. throw new IOException("Stream closed");
  416. }
  417. if (!descriptionParsed) {
  418. parseDescription(); // initializes 'index' and 'end'
  419. descriptionParsed = true;
  420. }
  421. if (end != -1 && index >= end) {
  422. return -1;
  423. }
  424. int retval = bufferedStream.read();
  425. if (retval == -1) {
  426. index = end = 0; // so future read() calls will fail quickly
  427. return -1;
  428. }
  429. index++;
  430. // System.out.print((char)retval);
  431. return retval;
  432. }
  433. public void close() throws IOException {
  434. if (!closed) {
  435. closed = true;
  436. bufferedStream.close();
  437. }
  438. }
  439. }