PageRenderTime 57ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/adoptopenjdk/jdk8-jdk
Java | 548 lines | 406 code | 84 blank | 58 comment | 81 complexity | 0c967aea9be27a5bacd70b562fd15fa0 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0, BSD-3-Clause-No-Nuclear-License-2014, BSD-3-Clause
  1. /*
  2. * Copyright (c) 2011, 2013, 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.translateBytes(bytes, flavor, format, transferable);
  148. }
  149. synchronized protected Long getFormatForNativeAsLong(String str) {
  150. Long format = predefinedClipboardNameMap.get(str);
  151. if (format == null) {
  152. if (java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().isHeadlessInstance()) {
  153. // Do not try to access native system for the unknown format
  154. return -1L;
  155. }
  156. format = registerFormatWithPasteboard(str);
  157. predefinedClipboardNameMap.put(str, format);
  158. predefinedClipboardFormatMap.put(format, str);
  159. }
  160. return format;
  161. }
  162. /*
  163. * Adds type to native mapping NSDictionary.
  164. */
  165. private native long registerFormatWithPasteboard(String type);
  166. // Get registered native format string for an index, return null if unknown:
  167. private native String formatForIndex(long index);
  168. protected String getNativeForFormat(long format) {
  169. String returnValue = null;
  170. // The most common case - just index the array of predefined names:
  171. if (format >= 0 && format < predefinedClipboardNames.length) {
  172. returnValue = predefinedClipboardNames[(int) format];
  173. } else {
  174. Long formatObj = new Long(format);
  175. returnValue = predefinedClipboardFormatMap.get(formatObj);
  176. // predefinedClipboardFormatMap may not know this format:
  177. if (returnValue == null) {
  178. returnValue = formatForIndex(format);
  179. // Native clipboard may not know this format either:
  180. if (returnValue != null) {
  181. predefinedClipboardNameMap.put(returnValue, formatObj);
  182. predefinedClipboardFormatMap.put(formatObj, returnValue);
  183. }
  184. }
  185. }
  186. if (returnValue == null) {
  187. returnValue = predefinedClipboardNames[CF_UNSUPPORTED];
  188. }
  189. return returnValue;
  190. }
  191. private final ToolkitThreadBlockedHandler handler = new CToolkitThreadBlockedHandler();
  192. public ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler() {
  193. return handler;
  194. }
  195. protected byte[] imageToPlatformBytes(Image image, long format) {
  196. int w = image.getWidth(null);
  197. int h = image.getHeight(null);
  198. BufferedImage bimage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
  199. Graphics g = bimage.getGraphics();
  200. g.drawImage(image, 0, 0, w, h, null);
  201. g.dispose();
  202. Raster raster = bimage.getRaster();
  203. DataBuffer buffer = raster.getDataBuffer();
  204. return imageDataToPlatformImageBytes(((DataBufferInt)buffer).getData(),
  205. raster.getWidth(),
  206. raster.getHeight());
  207. }
  208. private static native String[] nativeDragQueryFile(final byte[] bytes);
  209. protected String[] dragQueryFile(final byte[] bytes) {
  210. if (bytes == null) return null;
  211. if (new String(bytes).startsWith("Unsupported type")) return null;
  212. return nativeDragQueryFile(bytes);
  213. }
  214. private native byte[] imageDataToPlatformImageBytes(int[] rData, int nW, int nH);
  215. /**
  216. * Translates a byte array which contains
  217. * platform-specific image data in the given format into an Image.
  218. */
  219. protected Image platformImageBytesToImage(byte[] bytes, long format)
  220. throws IOException
  221. {
  222. return getImageForByteStream(bytes);
  223. }
  224. private native Image getImageForByteStream(byte[] bytes);
  225. @Override
  226. protected ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList) throws IOException {
  227. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  228. for (int i = 0; i < fileList.size(); i++)
  229. {
  230. byte[] bytes = fileList.get(i).getBytes();
  231. bos.write(bytes, 0, bytes.length);
  232. bos.write(0);
  233. }
  234. return bos;
  235. }
  236. @Override
  237. protected boolean isURIListFormat(long format) {
  238. String nat = getNativeForFormat(format);
  239. if (nat == null) {
  240. return false;
  241. }
  242. try {
  243. DataFlavor df = new DataFlavor(nat);
  244. if (df.getPrimaryType().equals("text") && df.getSubType().equals("uri-list")) {
  245. return true;
  246. }
  247. } catch (Exception e) {
  248. // Not a MIME format.
  249. }
  250. return false;
  251. }
  252. }
  253. // ---- Code borrowed from WDataTransferer: ----
  254. // This will come handy for supporting HTML data.
  255. final class HTMLSupport {
  256. public static final String ENCODING = "UTF-8";
  257. public static final String VERSION = "Version:";
  258. public static final String START_HTML = "StartHTML:";
  259. public static final String END_HTML = "EndHTML:";
  260. public static final String START_FRAGMENT = "StartFragment:";
  261. public static final String END_FRAGMENT = "EndFragment:";
  262. public static final String START_FRAGMENT_CMT = "<!--StartFragment-->";
  263. public static final String END_FRAGMENT_CMT = "<!--EndFragment-->";
  264. public static final String EOLN = "\r\n";
  265. private static final String VERSION_NUM = "0.9";
  266. private static final String HTML_START_END = "-1";
  267. private static final int PADDED_WIDTH = 10;
  268. private static final int HEADER_LEN =
  269. VERSION.length() + VERSION_NUM.length() + EOLN.length() +
  270. START_HTML.length() + HTML_START_END.length() + EOLN.length() +
  271. END_HTML.length() + HTML_START_END.length() + EOLN.length() +
  272. START_FRAGMENT.length() + PADDED_WIDTH + EOLN.length() +
  273. END_FRAGMENT.length() + PADDED_WIDTH + EOLN.length() +
  274. START_FRAGMENT_CMT.length() + EOLN.length();
  275. private static final String HEADER_LEN_STR =
  276. toPaddedString(HEADER_LEN, PADDED_WIDTH);
  277. private static final String TRAILER = END_FRAGMENT_CMT + EOLN + '\0';
  278. private static String toPaddedString(int n, int width) {
  279. String string = "" + n;
  280. int len = string.length();
  281. if (n >= 0 && len < width) {
  282. char[] array = new char[width - len];
  283. Arrays.fill(array, '0');
  284. StringBuffer buffer = new StringBuffer();
  285. buffer.append(array);
  286. buffer.append(string);
  287. string = buffer.toString();
  288. }
  289. return string;
  290. }
  291. public static byte[] convertToHTMLFormat(byte[] bytes) {
  292. StringBuffer header = new StringBuffer(HEADER_LEN);
  293. header.append(VERSION);
  294. header.append(VERSION_NUM);
  295. header.append(EOLN);
  296. header.append(START_HTML);
  297. header.append(HTML_START_END);
  298. header.append(EOLN);
  299. header.append(END_HTML);
  300. header.append(HTML_START_END);
  301. header.append(EOLN);
  302. header.append(START_FRAGMENT);
  303. header.append(HEADER_LEN_STR);
  304. header.append(EOLN);
  305. header.append(END_FRAGMENT);
  306. // Strip terminating NUL byte from array
  307. header.append(toPaddedString(HEADER_LEN + bytes.length - 1,
  308. PADDED_WIDTH));
  309. header.append(EOLN);
  310. header.append(START_FRAGMENT_CMT);
  311. header.append(EOLN);
  312. byte[] headerBytes = null, trailerBytes = null;
  313. try {
  314. headerBytes = new String(header).getBytes(ENCODING);
  315. trailerBytes = TRAILER.getBytes(ENCODING);
  316. } catch (UnsupportedEncodingException cannotHappen) {
  317. }
  318. byte[] retval = new byte[headerBytes.length + bytes.length - 1 +
  319. trailerBytes.length];
  320. System.arraycopy(headerBytes, 0, retval, 0, headerBytes.length);
  321. System.arraycopy(bytes, 0, retval, headerBytes.length,
  322. bytes.length - 1);
  323. System.arraycopy(trailerBytes, 0, retval,
  324. headerBytes.length + bytes.length - 1,
  325. trailerBytes.length);
  326. return retval;
  327. }
  328. }
  329. /**
  330. * This stream takes an InputStream which provides data in CF_HTML format,
  331. * strips off the description and context to extract the original HTML data.
  332. */
  333. class HTMLDecodingInputStream extends InputStream {
  334. private final BufferedInputStream bufferedStream;
  335. private boolean descriptionParsed = false;
  336. private boolean closed = false;
  337. private int index;
  338. private int end;
  339. // InputStreamReader uses an 8K buffer. The size is not customizable.
  340. public static final int BYTE_BUFFER_LEN = 8192;
  341. // CharToByteUTF8.getMaxBytesPerChar returns 3, so we should not buffer
  342. // more chars than 3 times the number of bytes we can buffer.
  343. public static final int CHAR_BUFFER_LEN = BYTE_BUFFER_LEN / 3;
  344. private static final String FAILURE_MSG =
  345. "Unable to parse HTML description: ";
  346. private static final String INVALID_MSG = " invalid";
  347. public HTMLDecodingInputStream(InputStream bytestream) throws IOException {
  348. bufferedStream = new BufferedInputStream(bytestream, BYTE_BUFFER_LEN);
  349. }
  350. private void parseDescription() throws IOException {
  351. bufferedStream.mark(BYTE_BUFFER_LEN);
  352. BufferedReader bufferedReader = new BufferedReader
  353. (new InputStreamReader(bufferedStream, HTMLSupport.ENCODING),
  354. CHAR_BUFFER_LEN);
  355. String version = bufferedReader.readLine().trim();
  356. if (version == null || !version.startsWith(HTMLSupport.VERSION)) {
  357. // Not MS-compliant HTML text. Return raw text from read().
  358. index = 0;
  359. end = -1;
  360. bufferedStream.reset();
  361. return;
  362. }
  363. String input;
  364. boolean startHTML, endHTML, startFragment, endFragment;
  365. startHTML = endHTML = startFragment = endFragment = false;
  366. try {
  367. do {
  368. input = bufferedReader.readLine().trim();
  369. if (input == null) {
  370. close();
  371. throw new IOException(FAILURE_MSG);
  372. } else if (input.startsWith(HTMLSupport.START_HTML)) {
  373. int val = Integer.parseInt
  374. (input.substring(HTMLSupport.START_HTML.length(),
  375. input.length()).trim());
  376. if (val >= 0) {
  377. index = val;
  378. startHTML = true;
  379. } else if (val != -1) {
  380. close();
  381. throw new IOException(FAILURE_MSG +
  382. HTMLSupport.START_HTML +
  383. INVALID_MSG);
  384. }
  385. } else if (input.startsWith(HTMLSupport.END_HTML)) {
  386. int val = Integer.parseInt
  387. (input.substring(HTMLSupport.END_HTML.length(),
  388. input.length()).trim());
  389. if (val >= 0) {
  390. end = val;
  391. endHTML = true;
  392. } else if (val != -1) {
  393. close();
  394. throw new IOException(FAILURE_MSG +
  395. HTMLSupport.END_HTML +
  396. INVALID_MSG);
  397. }
  398. } else if (!startHTML && !endHTML &&
  399. input.startsWith(HTMLSupport.START_FRAGMENT)) {
  400. index = Integer.parseInt
  401. (input.substring(HTMLSupport.START_FRAGMENT.length(),
  402. input.length()).trim());
  403. if (index < 0) {
  404. close();
  405. throw new IOException(FAILURE_MSG +
  406. HTMLSupport.START_FRAGMENT +
  407. INVALID_MSG);
  408. }
  409. startFragment = true;
  410. } else if (!startHTML && !endHTML &&
  411. input.startsWith(HTMLSupport.END_FRAGMENT)) {
  412. end = Integer.parseInt
  413. (input.substring(HTMLSupport.END_FRAGMENT.length(),
  414. input.length()).trim());
  415. if (end < 0) {
  416. close();
  417. throw new IOException(FAILURE_MSG +
  418. HTMLSupport.END_FRAGMENT +
  419. INVALID_MSG);
  420. }
  421. endFragment = true;
  422. }
  423. } while (!((startHTML && endHTML) ||
  424. (startFragment && endFragment)));
  425. } catch (NumberFormatException e) {
  426. close();
  427. throw new IOException(FAILURE_MSG + e);
  428. }
  429. bufferedStream.reset();
  430. for (int i = 0; i < index; i++) {
  431. if (bufferedStream.read() == -1) {
  432. close();
  433. throw new IOException(FAILURE_MSG +
  434. "Byte stream ends in description.");
  435. }
  436. }
  437. }
  438. public int read() throws IOException {
  439. if (closed) {
  440. throw new IOException("Stream closed");
  441. }
  442. if (!descriptionParsed) {
  443. parseDescription(); // initializes 'index' and 'end'
  444. descriptionParsed = true;
  445. }
  446. if (end != -1 && index >= end) {
  447. return -1;
  448. }
  449. int retval = bufferedStream.read();
  450. if (retval == -1) {
  451. index = end = 0; // so future read() calls will fail quickly
  452. return -1;
  453. }
  454. index++;
  455. // System.out.print((char)retval);
  456. return retval;
  457. }
  458. public void close() throws IOException {
  459. if (!closed) {
  460. closed = true;
  461. bufferedStream.close();
  462. }
  463. }
  464. }