PageRenderTime 70ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/src/share/classes/sun/awt/datatransfer/DataTransferer.java

https://bitbucket.org/screenconnect/openjdk8-jdk
Java | 3067 lines | 1902 code | 414 blank | 751 comment | 403 complexity | 8a996103975d531e1bc296128a2fa160 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-3.0
  1. /*
  2. * Copyright (c) 2000, 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. package sun.awt.datatransfer;
  26. import java.awt.EventQueue;
  27. import java.awt.Graphics;
  28. import java.awt.Image;
  29. import java.awt.Toolkit;
  30. import java.awt.datatransfer.DataFlavor;
  31. import java.awt.datatransfer.FlavorMap;
  32. import java.awt.datatransfer.FlavorTable;
  33. import java.awt.datatransfer.Transferable;
  34. import java.awt.datatransfer.UnsupportedFlavorException;
  35. import java.io.BufferedReader;
  36. import java.io.ByteArrayInputStream;
  37. import java.io.ByteArrayOutputStream;
  38. import java.io.File;
  39. import java.io.InputStream;
  40. import java.io.InputStreamReader;
  41. import java.io.IOException;
  42. import java.io.ObjectInputStream;
  43. import java.io.ObjectOutputStream;
  44. import java.io.Reader;
  45. import java.io.SequenceInputStream;
  46. import java.io.StringReader;
  47. import java.net.URI;
  48. import java.net.URISyntaxException;
  49. import java.nio.ByteBuffer;
  50. import java.nio.CharBuffer;
  51. import java.nio.charset.Charset;
  52. import java.nio.charset.CharsetEncoder;
  53. import java.nio.charset.IllegalCharsetNameException;
  54. import java.nio.charset.UnsupportedCharsetException;
  55. import java.lang.reflect.Constructor;
  56. import java.lang.reflect.InvocationTargetException;
  57. import java.lang.reflect.Method;
  58. import java.lang.reflect.Modifier;
  59. import java.security.AccessController;
  60. import java.security.PrivilegedAction;
  61. import java.security.PrivilegedActionException;
  62. import java.security.PrivilegedExceptionAction;
  63. import java.security.ProtectionDomain;
  64. import java.util.ArrayList;
  65. import java.util.Arrays;
  66. import java.util.Collections;
  67. import java.util.Comparator;
  68. import java.util.HashMap;
  69. import java.util.HashSet;
  70. import java.util.Iterator;
  71. import java.util.LinkedHashSet;
  72. import java.util.List;
  73. import java.util.Map;
  74. import java.util.SortedMap;
  75. import java.util.SortedSet;
  76. import java.util.Set;
  77. import java.util.Stack;
  78. import java.util.TreeMap;
  79. import java.util.TreeSet;
  80. import sun.awt.ComponentFactory;
  81. import sun.util.logging.PlatformLogger;
  82. import sun.awt.AppContext;
  83. import sun.awt.SunToolkit;
  84. import java.awt.image.BufferedImage;
  85. import java.awt.image.ImageObserver;
  86. import java.awt.image.RenderedImage;
  87. import java.awt.image.WritableRaster;
  88. import java.awt.image.ColorModel;
  89. import javax.imageio.ImageIO;
  90. import javax.imageio.ImageReader;
  91. import javax.imageio.ImageReadParam;
  92. import javax.imageio.ImageWriter;
  93. import javax.imageio.ImageTypeSpecifier;
  94. import javax.imageio.spi.ImageWriterSpi;
  95. import javax.imageio.stream.ImageInputStream;
  96. import javax.imageio.stream.ImageOutputStream;
  97. import sun.awt.image.ImageRepresentation;
  98. import sun.awt.image.ToolkitImage;
  99. import java.io.FilePermission;
  100. /**
  101. * Provides a set of functions to be shared among the DataFlavor class and
  102. * platform-specific data transfer implementations.
  103. *
  104. * The concept of "flavors" and "natives" is extended to include "formats",
  105. * which are the numeric values Win32 and X11 use to express particular data
  106. * types. Like FlavorMap, which provides getNativesForFlavors(DataFlavor[]) and
  107. * getFlavorsForNatives(String[]) functions, DataTransferer provides a set
  108. * of getFormatsFor(Transferable|Flavor|Flavors) and
  109. * getFlavorsFor(Format|Formats) functions.
  110. *
  111. * Also provided are functions for translating a Transferable into a byte
  112. * array, given a source DataFlavor and a target format, and for translating
  113. * a byte array or InputStream into an Object, given a source format and
  114. * a target DataFlavor.
  115. *
  116. * @author David Mendenhall
  117. * @author Danila Sinopalnikov
  118. *
  119. * @since 1.3.1
  120. */
  121. public abstract class DataTransferer {
  122. /**
  123. * The <code>DataFlavor</code> representing plain text with Unicode
  124. * encoding, where:
  125. * <pre>
  126. * representationClass = java.lang.String
  127. * mimeType = "text/plain; charset=Unicode"
  128. * </pre>
  129. */
  130. public static final DataFlavor plainTextStringFlavor;
  131. /**
  132. * The <code>DataFlavor</code> representing a Java text encoding String
  133. * encoded in UTF-8, where
  134. * <pre>
  135. * representationClass = [B
  136. * mimeType = "application/x-java-text-encoding"
  137. * </pre>
  138. */
  139. public static final DataFlavor javaTextEncodingFlavor;
  140. /**
  141. * Lazy initialization of Standard Encodings.
  142. */
  143. private static class StandardEncodingsHolder {
  144. private static final SortedSet<String> standardEncodings = load();
  145. private static SortedSet<String> load() {
  146. final Comparator comparator =
  147. new CharsetComparator(IndexedComparator.SELECT_WORST);
  148. final SortedSet<String> tempSet = new TreeSet<String>(comparator);
  149. tempSet.add("US-ASCII");
  150. tempSet.add("ISO-8859-1");
  151. tempSet.add("UTF-8");
  152. tempSet.add("UTF-16BE");
  153. tempSet.add("UTF-16LE");
  154. tempSet.add("UTF-16");
  155. tempSet.add(getDefaultTextCharset());
  156. return Collections.unmodifiableSortedSet(tempSet);
  157. }
  158. }
  159. /**
  160. * Tracks whether a particular text/* MIME type supports the charset
  161. * parameter. The Map is initialized with all of the standard MIME types
  162. * listed in the DataFlavor.selectBestTextFlavor method comment. Additional
  163. * entries may be added during the life of the JRE for text/<other> types.
  164. */
  165. private static final Map textMIMESubtypeCharsetSupport;
  166. /**
  167. * Cache of the platform default encoding as specified in the
  168. * "file.encoding" system property.
  169. */
  170. private static String defaultEncoding;
  171. /**
  172. * A collection of all natives listed in flavormap.properties with
  173. * a primary MIME type of "text".
  174. */
  175. private static final Set textNatives =
  176. Collections.synchronizedSet(new HashSet());
  177. /**
  178. * The native encodings/charsets for the Set of textNatives.
  179. */
  180. private static final Map nativeCharsets =
  181. Collections.synchronizedMap(new HashMap());
  182. /**
  183. * The end-of-line markers for the Set of textNatives.
  184. */
  185. private static final Map nativeEOLNs =
  186. Collections.synchronizedMap(new HashMap());
  187. /**
  188. * The number of terminating NUL bytes for the Set of textNatives.
  189. */
  190. private static final Map nativeTerminators =
  191. Collections.synchronizedMap(new HashMap());
  192. /**
  193. * The key used to store pending data conversion requests for an AppContext.
  194. */
  195. private static final String DATA_CONVERTER_KEY = "DATA_CONVERTER_KEY";
  196. /**
  197. * The singleton DataTransferer instance. It is created during MToolkit
  198. * or WToolkit initialization.
  199. */
  200. private static DataTransferer transferer;
  201. private static final PlatformLogger dtLog = PlatformLogger.getLogger("sun.awt.datatransfer.DataTransfer");
  202. static {
  203. DataFlavor tPlainTextStringFlavor = null;
  204. try {
  205. tPlainTextStringFlavor = new DataFlavor
  206. ("text/plain;charset=Unicode;class=java.lang.String");
  207. } catch (ClassNotFoundException cannotHappen) {
  208. }
  209. plainTextStringFlavor = tPlainTextStringFlavor;
  210. DataFlavor tJavaTextEncodingFlavor = null;
  211. try {
  212. tJavaTextEncodingFlavor = new DataFlavor
  213. ("application/x-java-text-encoding;class=\"[B\"");
  214. } catch (ClassNotFoundException cannotHappen) {
  215. }
  216. javaTextEncodingFlavor = tJavaTextEncodingFlavor;
  217. Map tempMap = new HashMap(17);
  218. tempMap.put("sgml", Boolean.TRUE);
  219. tempMap.put("xml", Boolean.TRUE);
  220. tempMap.put("html", Boolean.TRUE);
  221. tempMap.put("enriched", Boolean.TRUE);
  222. tempMap.put("richtext", Boolean.TRUE);
  223. tempMap.put("uri-list", Boolean.TRUE);
  224. tempMap.put("directory", Boolean.TRUE);
  225. tempMap.put("css", Boolean.TRUE);
  226. tempMap.put("calendar", Boolean.TRUE);
  227. tempMap.put("plain", Boolean.TRUE);
  228. tempMap.put("rtf", Boolean.FALSE);
  229. tempMap.put("tab-separated-values", Boolean.FALSE);
  230. tempMap.put("t140", Boolean.FALSE);
  231. tempMap.put("rfc822-headers", Boolean.FALSE);
  232. tempMap.put("parityfec", Boolean.FALSE);
  233. textMIMESubtypeCharsetSupport = Collections.synchronizedMap(tempMap);
  234. }
  235. /**
  236. * The accessor method for the singleton DataTransferer instance. Note
  237. * that in a headless environment, there may be no DataTransferer instance;
  238. * instead, null will be returned.
  239. */
  240. public static synchronized DataTransferer getInstance() {
  241. return ((ComponentFactory) Toolkit.getDefaultToolkit()).getDataTransferer();
  242. }
  243. /**
  244. * Converts an arbitrary text encoding to its canonical name.
  245. */
  246. public static String canonicalName(String encoding) {
  247. if (encoding == null) {
  248. return null;
  249. }
  250. try {
  251. return Charset.forName(encoding).name();
  252. } catch (IllegalCharsetNameException icne) {
  253. return encoding;
  254. } catch (UnsupportedCharsetException uce) {
  255. return encoding;
  256. }
  257. }
  258. /**
  259. * If the specified flavor is a text flavor which supports the "charset"
  260. * parameter, then this method returns that parameter, or the default
  261. * charset if no such parameter was specified at construction. For non-
  262. * text DataFlavors, and for non-charset text flavors, this method returns
  263. * null.
  264. */
  265. public static String getTextCharset(DataFlavor flavor) {
  266. if (!isFlavorCharsetTextType(flavor)) {
  267. return null;
  268. }
  269. String encoding = flavor.getParameter("charset");
  270. return (encoding != null) ? encoding : getDefaultTextCharset();
  271. }
  272. /**
  273. * Returns the platform's default character encoding.
  274. */
  275. public static String getDefaultTextCharset() {
  276. if (defaultEncoding != null) {
  277. return defaultEncoding;
  278. }
  279. return defaultEncoding = Charset.defaultCharset().name();
  280. }
  281. /**
  282. * Tests only whether the flavor's MIME type supports the charset
  283. * parameter. Must only be called for flavors with a primary type of
  284. * "text".
  285. */
  286. public static boolean doesSubtypeSupportCharset(DataFlavor flavor) {
  287. if (dtLog.isLoggable(PlatformLogger.Level.FINE)) {
  288. if (!"text".equals(flavor.getPrimaryType())) {
  289. dtLog.fine("Assertion (\"text\".equals(flavor.getPrimaryType())) failed");
  290. }
  291. }
  292. String subType = flavor.getSubType();
  293. if (subType == null) {
  294. return false;
  295. }
  296. Object support = textMIMESubtypeCharsetSupport.get(subType);
  297. if (support != null) {
  298. return (support == Boolean.TRUE);
  299. }
  300. boolean ret_val = (flavor.getParameter("charset") != null);
  301. textMIMESubtypeCharsetSupport.put
  302. (subType, (ret_val) ? Boolean.TRUE : Boolean.FALSE);
  303. return ret_val;
  304. }
  305. public static boolean doesSubtypeSupportCharset(String subType,
  306. String charset)
  307. {
  308. Object support = textMIMESubtypeCharsetSupport.get(subType);
  309. if (support != null) {
  310. return (support == Boolean.TRUE);
  311. }
  312. boolean ret_val = (charset != null);
  313. textMIMESubtypeCharsetSupport.put
  314. (subType, (ret_val) ? Boolean.TRUE : Boolean.FALSE);
  315. return ret_val;
  316. }
  317. /**
  318. * Returns whether this flavor is a text type which supports the
  319. * 'charset' parameter.
  320. */
  321. public static boolean isFlavorCharsetTextType(DataFlavor flavor) {
  322. // Although stringFlavor doesn't actually support the charset
  323. // parameter (because its primary MIME type is not "text"), it should
  324. // be treated as though it does. stringFlavor is semantically
  325. // equivalent to "text/plain" data.
  326. if (DataFlavor.stringFlavor.equals(flavor)) {
  327. return true;
  328. }
  329. if (!"text".equals(flavor.getPrimaryType()) ||
  330. !doesSubtypeSupportCharset(flavor))
  331. {
  332. return false;
  333. }
  334. Class rep_class = flavor.getRepresentationClass();
  335. if (flavor.isRepresentationClassReader() ||
  336. String.class.equals(rep_class) ||
  337. flavor.isRepresentationClassCharBuffer() ||
  338. char[].class.equals(rep_class))
  339. {
  340. return true;
  341. }
  342. if (!(flavor.isRepresentationClassInputStream() ||
  343. flavor.isRepresentationClassByteBuffer() ||
  344. byte[].class.equals(rep_class))) {
  345. return false;
  346. }
  347. String charset = flavor.getParameter("charset");
  348. return (charset != null)
  349. ? DataTransferer.isEncodingSupported(charset)
  350. : true; // null equals default encoding which is always supported
  351. }
  352. /**
  353. * Returns whether this flavor is a text type which does not support the
  354. * 'charset' parameter.
  355. */
  356. public static boolean isFlavorNoncharsetTextType(DataFlavor flavor) {
  357. if (!"text".equals(flavor.getPrimaryType()) ||
  358. doesSubtypeSupportCharset(flavor))
  359. {
  360. return false;
  361. }
  362. return (flavor.isRepresentationClassInputStream() ||
  363. flavor.isRepresentationClassByteBuffer() ||
  364. byte[].class.equals(flavor.getRepresentationClass()));
  365. }
  366. /**
  367. * Determines whether this JRE can both encode and decode text in the
  368. * specified encoding.
  369. */
  370. public static boolean isEncodingSupported(String encoding) {
  371. if (encoding == null) {
  372. return false;
  373. }
  374. try {
  375. return Charset.isSupported(encoding);
  376. } catch (IllegalCharsetNameException icne) {
  377. return false;
  378. }
  379. }
  380. /**
  381. * Returns {@code true} if the given type is a java.rmi.Remote.
  382. */
  383. public static boolean isRemote(Class<?> type) {
  384. return RMI.isRemote(type);
  385. }
  386. /**
  387. * Returns an Iterator which traverses a SortedSet of Strings which are
  388. * a total order of the standard character sets supported by the JRE. The
  389. * ordering follows the same principles as DataFlavor.selectBestTextFlavor.
  390. * So as to avoid loading all available character converters, optional,
  391. * non-standard, character sets are not included.
  392. */
  393. public static Set <String> standardEncodings() {
  394. return StandardEncodingsHolder.standardEncodings;
  395. }
  396. /**
  397. * Converts a FlavorMap to a FlavorTable.
  398. */
  399. public static FlavorTable adaptFlavorMap(final FlavorMap map) {
  400. if (map instanceof FlavorTable) {
  401. return (FlavorTable)map;
  402. }
  403. return new FlavorTable() {
  404. public Map getNativesForFlavors(DataFlavor[] flavors) {
  405. return map.getNativesForFlavors(flavors);
  406. }
  407. public Map getFlavorsForNatives(String[] natives) {
  408. return map.getFlavorsForNatives(natives);
  409. }
  410. public List getNativesForFlavor(DataFlavor flav) {
  411. Map natives =
  412. getNativesForFlavors(new DataFlavor[] { flav } );
  413. String nat = (String)natives.get(flav);
  414. if (nat != null) {
  415. List list = new ArrayList(1);
  416. list.add(nat);
  417. return list;
  418. } else {
  419. return Collections.EMPTY_LIST;
  420. }
  421. }
  422. public List getFlavorsForNative(String nat) {
  423. Map flavors =
  424. getFlavorsForNatives(new String[] { nat } );
  425. DataFlavor flavor = (DataFlavor)flavors.get(nat);
  426. if (flavor != null) {
  427. List list = new ArrayList(1);
  428. list.add(flavor);
  429. return list;
  430. } else {
  431. return Collections.EMPTY_LIST;
  432. }
  433. }
  434. };
  435. }
  436. /**
  437. * Returns the default Unicode encoding for the platform. The encoding
  438. * need not be canonical. This method is only used by the archaic function
  439. * DataFlavor.getTextPlainUnicodeFlavor().
  440. */
  441. public abstract String getDefaultUnicodeEncoding();
  442. /**
  443. * This method is called for text flavor mappings established while parsing
  444. * the flavormap.properties file. It stores the "eoln" and "terminators"
  445. * parameters which are not officially part of the MIME type. They are
  446. * MIME parameters specific to the flavormap.properties file format.
  447. */
  448. public void registerTextFlavorProperties(String nat, String charset,
  449. String eoln, String terminators) {
  450. Long format = getFormatForNativeAsLong(nat);
  451. textNatives.add(format);
  452. nativeCharsets.put(format, (charset != null && charset.length() != 0)
  453. ? charset : getDefaultTextCharset());
  454. if (eoln != null && eoln.length() != 0 && !eoln.equals("\n")) {
  455. nativeEOLNs.put(format, eoln);
  456. }
  457. if (terminators != null && terminators.length() != 0) {
  458. Integer iTerminators = Integer.valueOf(terminators);
  459. if (iTerminators.intValue() > 0) {
  460. nativeTerminators.put(format, iTerminators);
  461. }
  462. }
  463. }
  464. /**
  465. * Determines whether the native corresponding to the specified long format
  466. * was listed in the flavormap.properties file.
  467. */
  468. protected boolean isTextFormat(long format) {
  469. return textNatives.contains(Long.valueOf(format));
  470. }
  471. protected String getCharsetForTextFormat(Long lFormat) {
  472. return (String)nativeCharsets.get(lFormat);
  473. }
  474. /**
  475. * Specifies whether text imported from the native system in the specified
  476. * format is locale-dependent. If so, when decoding such text,
  477. * 'nativeCharsets' should be ignored, and instead, the Transferable should
  478. * be queried for its javaTextEncodingFlavor data for the correct encoding.
  479. */
  480. public abstract boolean isLocaleDependentTextFormat(long format);
  481. /**
  482. * Determines whether the DataFlavor corresponding to the specified long
  483. * format is DataFlavor.javaFileListFlavor.
  484. */
  485. public abstract boolean isFileFormat(long format);
  486. /**
  487. * Determines whether the DataFlavor corresponding to the specified long
  488. * format is DataFlavor.imageFlavor.
  489. */
  490. public abstract boolean isImageFormat(long format);
  491. /**
  492. * Determines whether the format is a URI list we can convert to
  493. * a DataFlavor.javaFileListFlavor.
  494. */
  495. protected boolean isURIListFormat(long format) {
  496. return false;
  497. }
  498. /**
  499. * Returns a Map whose keys are all of the possible formats into which the
  500. * Transferable's transfer data flavors can be translated. The value of
  501. * each key is the DataFlavor in which the Transferable's data should be
  502. * requested when converting to the format.
  503. * <p>
  504. * The map keys are sorted according to the native formats preference
  505. * order.
  506. */
  507. public SortedMap<Long,DataFlavor> getFormatsForTransferable(
  508. Transferable contents, FlavorTable map)
  509. {
  510. DataFlavor[] flavors = contents.getTransferDataFlavors();
  511. if (flavors == null) {
  512. return new TreeMap();
  513. }
  514. return getFormatsForFlavors(flavors, map);
  515. }
  516. /**
  517. * Returns a Map whose keys are all of the possible formats into which data
  518. * in the specified DataFlavor can be translated. The value of each key
  519. * is the DataFlavor in which a Transferable's data should be requested
  520. * when converting to the format.
  521. * <p>
  522. * The map keys are sorted according to the native formats preference
  523. * order.
  524. */
  525. public SortedMap getFormatsForFlavor(DataFlavor flavor, FlavorTable map) {
  526. return getFormatsForFlavors(new DataFlavor[] { flavor },
  527. map);
  528. }
  529. /**
  530. * Returns a Map whose keys are all of the possible formats into which data
  531. * in the specified DataFlavors can be translated. The value of each key
  532. * is the DataFlavor in which the Transferable's data should be requested
  533. * when converting to the format.
  534. * <p>
  535. * The map keys are sorted according to the native formats preference
  536. * order.
  537. *
  538. * @param flavors the data flavors
  539. * @param map the FlavorTable which contains mappings between
  540. * DataFlavors and data formats
  541. * @throws NullPointerException if flavors or map is <code>null</code>
  542. */
  543. public SortedMap <Long, DataFlavor> getFormatsForFlavors(
  544. DataFlavor[] flavors, FlavorTable map)
  545. {
  546. Map <Long,DataFlavor> formatMap =
  547. new HashMap <> (flavors.length);
  548. Map <Long,DataFlavor> textPlainMap =
  549. new HashMap <> (flavors.length);
  550. // Maps formats to indices that will be used to sort the formats
  551. // according to the preference order.
  552. // Larger index value corresponds to the more preferable format.
  553. Map indexMap = new HashMap(flavors.length);
  554. Map textPlainIndexMap = new HashMap(flavors.length);
  555. int currentIndex = 0;
  556. // Iterate backwards so that preferred DataFlavors are used over
  557. // other DataFlavors. (See javadoc for
  558. // Transferable.getTransferDataFlavors.)
  559. for (int i = flavors.length - 1; i >= 0; i--) {
  560. DataFlavor flavor = flavors[i];
  561. if (flavor == null) continue;
  562. // Don't explicitly test for String, since it is just a special
  563. // case of Serializable
  564. if (flavor.isFlavorTextType() ||
  565. flavor.isFlavorJavaFileListType() ||
  566. DataFlavor.imageFlavor.equals(flavor) ||
  567. flavor.isRepresentationClassSerializable() ||
  568. flavor.isRepresentationClassInputStream() ||
  569. flavor.isRepresentationClassRemote())
  570. {
  571. List natives = map.getNativesForFlavor(flavor);
  572. currentIndex += natives.size();
  573. for (Iterator iter = natives.iterator(); iter.hasNext(); ) {
  574. Long lFormat =
  575. getFormatForNativeAsLong((String)iter.next());
  576. Integer index = Integer.valueOf(currentIndex--);
  577. formatMap.put(lFormat, flavor);
  578. indexMap.put(lFormat, index);
  579. // SystemFlavorMap.getNativesForFlavor will return
  580. // text/plain natives for all text/*. While this is good
  581. // for a single text/* flavor, we would prefer that
  582. // text/plain native data come from a text/plain flavor.
  583. if (("text".equals(flavor.getPrimaryType()) &&
  584. "plain".equals(flavor.getSubType())) ||
  585. flavor.equals(DataFlavor.stringFlavor))
  586. {
  587. textPlainMap.put(lFormat, flavor);
  588. textPlainIndexMap.put(lFormat, index);
  589. }
  590. }
  591. currentIndex += natives.size();
  592. }
  593. }
  594. formatMap.putAll(textPlainMap);
  595. indexMap.putAll(textPlainIndexMap);
  596. // Sort the map keys according to the formats preference order.
  597. Comparator comparator =
  598. new IndexOrderComparator(indexMap, IndexedComparator.SELECT_WORST);
  599. SortedMap sortedMap = new TreeMap(comparator);
  600. sortedMap.putAll(formatMap);
  601. return sortedMap;
  602. }
  603. /**
  604. * Reduces the Map output for the root function to an array of the
  605. * Map's keys.
  606. */
  607. public long[] getFormatsForTransferableAsArray(Transferable contents,
  608. FlavorTable map) {
  609. return keysToLongArray(getFormatsForTransferable(contents, map));
  610. }
  611. public long[] getFormatsForFlavorAsArray(DataFlavor flavor,
  612. FlavorTable map) {
  613. return keysToLongArray(getFormatsForFlavor(flavor, map));
  614. }
  615. public long[] getFormatsForFlavorsAsArray(DataFlavor[] flavors,
  616. FlavorTable map) {
  617. return keysToLongArray(getFormatsForFlavors(flavors, map));
  618. }
  619. /**
  620. * Returns a Map whose keys are all of the possible DataFlavors into which
  621. * data in the specified format can be translated. The value of each key
  622. * is the format in which the Clipboard or dropped data should be requested
  623. * when converting to the DataFlavor.
  624. */
  625. public Map getFlavorsForFormat(long format, FlavorTable map) {
  626. return getFlavorsForFormats(new long[] { format }, map);
  627. }
  628. /**
  629. * Returns a Map whose keys are all of the possible DataFlavors into which
  630. * data in the specified formats can be translated. The value of each key
  631. * is the format in which the Clipboard or dropped data should be requested
  632. * when converting to the DataFlavor.
  633. */
  634. public Map getFlavorsForFormats(long[] formats, FlavorTable map) {
  635. Map flavorMap = new HashMap(formats.length);
  636. Set mappingSet = new HashSet(formats.length);
  637. Set flavorSet = new HashSet(formats.length);
  638. // First step: build flavorSet, mappingSet and initial flavorMap
  639. // flavorSet - the set of all the DataFlavors into which
  640. // data in the specified formats can be translated;
  641. // mappingSet - the set of all the mappings from the specified formats
  642. // into any DataFlavor;
  643. // flavorMap - after this step, this map maps each of the DataFlavors
  644. // from flavorSet to any of the specified formats.
  645. for (int i = 0; i < formats.length; i++) {
  646. long format = formats[i];
  647. String nat = getNativeForFormat(format);
  648. List flavors = map.getFlavorsForNative(nat);
  649. for (Iterator iter = flavors.iterator(); iter.hasNext(); ) {
  650. DataFlavor flavor = (DataFlavor)iter.next();
  651. // Don't explicitly test for String, since it is just a special
  652. // case of Serializable
  653. if (flavor.isFlavorTextType() ||
  654. flavor.isFlavorJavaFileListType() ||
  655. DataFlavor.imageFlavor.equals(flavor) ||
  656. flavor.isRepresentationClassSerializable() ||
  657. flavor.isRepresentationClassInputStream() ||
  658. flavor.isRepresentationClassRemote())
  659. {
  660. Long lFormat = Long.valueOf(format);
  661. Object mapping =
  662. DataTransferer.createMapping(lFormat, flavor);
  663. flavorMap.put(flavor, lFormat);
  664. mappingSet.add(mapping);
  665. flavorSet.add(flavor);
  666. }
  667. }
  668. }
  669. // Second step: for each DataFlavor try to figure out which of the
  670. // specified formats is the best to translate to this flavor.
  671. // Then map each flavor to the best format.
  672. // For the given flavor, FlavorTable indicates which native will
  673. // best reflect data in the specified flavor to the underlying native
  674. // platform. We assume that this native is the best to translate
  675. // to this flavor.
  676. // Note: FlavorTable allows one-way mappings, so we can occasionally
  677. // map a flavor to the format for which the corresponding
  678. // format-to-flavor mapping doesn't exist. For this reason we have built
  679. // a mappingSet of all format-to-flavor mappings for the specified formats
  680. // and check if the format-to-flavor mapping exists for the
  681. // (flavor,format) pair being added.
  682. for (Iterator flavorIter = flavorSet.iterator();
  683. flavorIter.hasNext(); ) {
  684. DataFlavor flavor = (DataFlavor)flavorIter.next();
  685. List natives = map.getNativesForFlavor(flavor);
  686. for (Iterator nativeIter = natives.iterator();
  687. nativeIter.hasNext(); ) {
  688. Long lFormat =
  689. getFormatForNativeAsLong((String)nativeIter.next());
  690. Object mapping = DataTransferer.createMapping(lFormat, flavor);
  691. if (mappingSet.contains(mapping)) {
  692. flavorMap.put(flavor, lFormat);
  693. break;
  694. }
  695. }
  696. }
  697. return flavorMap;
  698. }
  699. /**
  700. * Returns a Set of all DataFlavors for which
  701. * 1) a mapping from at least one of the specified formats exists in the
  702. * specified map and
  703. * 2) the data translation for this mapping can be performed by the data
  704. * transfer subsystem.
  705. *
  706. * @param formats the data formats
  707. * @param map the FlavorTable which contains mappings between
  708. * DataFlavors and data formats
  709. * @throws NullPointerException if formats or map is <code>null</code>
  710. */
  711. public Set getFlavorsForFormatsAsSet(long[] formats, FlavorTable map) {
  712. Set flavorSet = new HashSet(formats.length);
  713. for (int i = 0; i < formats.length; i++) {
  714. String nat = getNativeForFormat(formats[i]);
  715. List flavors = map.getFlavorsForNative(nat);
  716. for (Iterator iter = flavors.iterator(); iter.hasNext(); ) {
  717. DataFlavor flavor = (DataFlavor)iter.next();
  718. // Don't explicitly test for String, since it is just a special
  719. // case of Serializable
  720. if (flavor.isFlavorTextType() ||
  721. flavor.isFlavorJavaFileListType() ||
  722. DataFlavor.imageFlavor.equals(flavor) ||
  723. flavor.isRepresentationClassSerializable() ||
  724. flavor.isRepresentationClassInputStream() ||
  725. flavor.isRepresentationClassRemote())
  726. {
  727. flavorSet.add(flavor);
  728. }
  729. }
  730. }
  731. return flavorSet;
  732. }
  733. /**
  734. * Returns an array of all DataFlavors for which
  735. * 1) a mapping from the specified format exists in the specified map and
  736. * 2) the data translation for this mapping can be performed by the data
  737. * transfer subsystem.
  738. * The array will be sorted according to a
  739. * <code>DataFlavorComparator</code> created with the specified
  740. * map as an argument.
  741. *
  742. * @param format the data format
  743. * @param map the FlavorTable which contains mappings between
  744. * DataFlavors and data formats
  745. * @throws NullPointerException if map is <code>null</code>
  746. */
  747. public DataFlavor[] getFlavorsForFormatAsArray(long format,
  748. FlavorTable map) {
  749. return getFlavorsForFormatsAsArray(new long[] { format }, map);
  750. }
  751. /**
  752. * Returns an array of all DataFlavors for which
  753. * 1) a mapping from at least one of the specified formats exists in the
  754. * specified map and
  755. * 2) the data translation for this mapping can be performed by the data
  756. * transfer subsystem.
  757. * The array will be sorted according to a
  758. * <code>DataFlavorComparator</code> created with the specified
  759. * map as an argument.
  760. *
  761. * @param formats the data formats
  762. * @param map the FlavorTable which contains mappings between
  763. * DataFlavors and data formats
  764. * @throws NullPointerException if formats or map is <code>null</code>
  765. */
  766. public DataFlavor[] getFlavorsForFormatsAsArray(long[] formats,
  767. FlavorTable map) {
  768. // getFlavorsForFormatsAsSet() is less expensive than
  769. // getFlavorsForFormats().
  770. return setToSortedDataFlavorArray(getFlavorsForFormatsAsSet(formats, map));
  771. }
  772. /**
  773. * Returns an object that represents a mapping between the specified
  774. * key and value. <tt>null</tt> values and the <tt>null</tt> keys are
  775. * permitted. The internal representation of the mapping object is
  776. * irrelevant. The only requrement is that the two mapping objects are equal
  777. * if and only if their keys are equal and their values are equal.
  778. * More formally, the two mapping objects are equal if and only if
  779. * <tt>(value1 == null ? value2 == null : value1.equals(value2))
  780. * && (key1 == null ? key2 == null : key1.equals(key2))</tt>.
  781. */
  782. private static Object createMapping(Object key, Object value) {
  783. // NOTE: Should be updated to use AbstractMap.SimpleEntry as
  784. // soon as it is made public.
  785. return Arrays.asList(new Object[] { key, value });
  786. }
  787. /**
  788. * Looks-up or registers the String native with the native data transfer
  789. * system and returns a long format corresponding to that native.
  790. */
  791. protected abstract Long getFormatForNativeAsLong(String str);
  792. /**
  793. * Looks-up the String native corresponding to the specified long format in
  794. * the native data transfer system.
  795. */
  796. protected abstract String getNativeForFormat(long format);
  797. /* Contains common code for finding the best charset for
  798. * clipboard string encoding/decoding, basing on clipboard
  799. * format and localeTransferable(on decoding, if available)
  800. */
  801. private String getBestCharsetForTextFormat(Long lFormat,
  802. Transferable localeTransferable) throws IOException
  803. {
  804. String charset = null;
  805. if (localeTransferable != null &&
  806. isLocaleDependentTextFormat(lFormat) &&
  807. localeTransferable.isDataFlavorSupported(javaTextEncodingFlavor))
  808. {
  809. try {
  810. charset = new String(
  811. (byte[])localeTransferable.getTransferData(javaTextEncodingFlavor),
  812. "UTF-8"
  813. );
  814. } catch (UnsupportedFlavorException cannotHappen) {
  815. }
  816. } else {
  817. charset = getCharsetForTextFormat(lFormat);
  818. }
  819. if (charset == null) {
  820. // Only happens when we have a custom text type.
  821. charset = getDefaultTextCharset();
  822. }
  823. return charset;
  824. }
  825. /**
  826. * Translation function for converting string into
  827. * a byte array. Search-and-replace EOLN. Encode into the
  828. * target format. Append terminating NUL bytes.
  829. *
  830. * Java to Native string conversion
  831. */
  832. private byte[] translateTransferableString(String str,
  833. long format) throws IOException
  834. {
  835. Long lFormat = Long.valueOf(format);
  836. String charset = getBestCharsetForTextFormat(lFormat, null);
  837. // Search and replace EOLN. Note that if EOLN is "\n", then we
  838. // never added an entry to nativeEOLNs anyway, so we'll skip this
  839. // code altogether.
  840. // windows: "abc\nde"->"abc\r\nde"
  841. String eoln = (String)nativeEOLNs.get(lFormat);
  842. if (eoln != null) {
  843. int length = str.length();
  844. StringBuffer buffer =
  845. new StringBuffer(length * 2); // 2 is a heuristic
  846. for (int i = 0; i < length; i++) {
  847. // Fix for 4914613 - skip native EOLN
  848. if (str.startsWith(eoln, i)) {
  849. buffer.append(eoln);
  850. i += eoln.length() - 1;
  851. continue;
  852. }
  853. char c = str.charAt(i);
  854. if (c == '\n') {
  855. buffer.append(eoln);
  856. } else {
  857. buffer.append(c);
  858. }
  859. }
  860. str = buffer.toString();
  861. }
  862. // Encode text in target format.
  863. byte[] bytes = str.getBytes(charset);
  864. // Append terminating NUL bytes. Note that if terminators is 0,
  865. // the we never added an entry to nativeTerminators anyway, so
  866. // we'll skip code altogether.
  867. // "abcde" -> "abcde\0"
  868. Integer terminators = (Integer)nativeTerminators.get(lFormat);
  869. if (terminators != null) {
  870. int numTerminators = terminators.intValue();
  871. byte[] terminatedBytes =
  872. new byte[bytes.length + numTerminators];
  873. System.arraycopy(bytes, 0, terminatedBytes, 0, bytes.length);
  874. for (int i = bytes.length; i < terminatedBytes.length; i++) {
  875. terminatedBytes[i] = 0x0;
  876. }
  877. bytes = terminatedBytes;
  878. }
  879. return bytes;
  880. }
  881. /**
  882. * Translating either a byte array or an InputStream into an String.
  883. * Strip terminators and search-and-replace EOLN.
  884. *
  885. * Native to Java string conversion
  886. */
  887. private String translateBytesToString(byte[] bytes, long format,
  888. Transferable localeTransferable)
  889. throws IOException
  890. {
  891. Long lFormat = Long.valueOf(format);
  892. String charset = getBestCharsetForTextFormat(lFormat, localeTransferable);
  893. // Locate terminating NUL bytes. Note that if terminators is 0,
  894. // the we never added an entry to nativeTerminators anyway, so
  895. // we'll skip code altogether.
  896. // In other words: we are doing char alignment here basing on suggestion
  897. // that count of zero-'terminators' is a number of bytes in one symbol
  898. // for selected charset (clipboard format). It is not complitly true for
  899. // multibyte coding like UTF-8, but helps understand the procedure.
  900. // "abcde\0" -> "abcde"
  901. String eoln = (String)nativeEOLNs.get(lFormat);
  902. Integer terminators = (Integer)nativeTerminators.get(lFormat);
  903. int count;
  904. if (terminators != null) {
  905. int numTerminators = terminators.intValue();
  906. search:
  907. for (count = 0; count < (bytes.length - numTerminators + 1); count += numTerminators) {
  908. for (int i = count; i < count + numTerminators; i++) {
  909. if (bytes[i] != 0x0) {
  910. continue search;
  911. }
  912. }
  913. // found terminators
  914. break search;
  915. }
  916. } else {
  917. count = bytes.length;
  918. }
  919. // Decode text to chars. Don't include any terminators.
  920. String converted = new String(bytes, 0, count, charset);
  921. // Search and replace EOLN. Note that if EOLN is "\n", then we
  922. // never added an entry to nativeEOLNs anyway, so we'll skip this
  923. // code altogether.
  924. // Count of NUL-terminators and EOLN coding are platform-specific and
  925. // loaded from flavormap.properties file
  926. // windows: "abc\r\nde" -> "abc\nde"
  927. if (eoln != null) {
  928. /* Fix for 4463560: replace EOLNs symbol-by-symbol instead
  929. * of using buf.replace()
  930. */
  931. char[] buf = converted.toCharArray();
  932. char[] eoln_arr = eoln.toCharArray();
  933. converted = null;
  934. int j = 0;
  935. boolean match;
  936. for (int i = 0; i < buf.length; ) {
  937. // Catch last few bytes
  938. if (i + eoln_arr.length > buf.length) {
  939. buf[j++] = buf[i++];
  940. continue;
  941. }
  942. match = true;
  943. for (int k = 0, l = i; k < eoln_arr.length; k++, l++) {
  944. if (eoln_arr[k] != buf[l]) {
  945. match = false;
  946. break;
  947. }
  948. }
  949. if (match) {
  950. buf[j++] = '\n';
  951. i += eoln_arr.length;
  952. } else {
  953. buf[j++] = buf[i++];
  954. }
  955. }
  956. converted = new String(buf, 0, j);
  957. }
  958. return converted;
  959. }
  960. /**
  961. * Primary translation function for translating a Transferable into
  962. * a byte array, given a source DataFlavor and target format.
  963. */
  964. public byte[] translateTransferable(Transferable contents,
  965. DataFlavor flavor,
  966. long format) throws IOException
  967. {
  968. // Obtain the transfer data in the source DataFlavor.
  969. //
  970. // Note that we special case DataFlavor.plainTextFlavor because
  971. // StringSelection supports this flavor incorrectly -- instead of
  972. // returning an InputStream as the DataFlavor representation class
  973. // states, it returns a Reader. Instead of using this broken
  974. // functionality, we request the data in stringFlavor (the other
  975. // DataFlavor which StringSelection supports) and use the String
  976. // translator.
  977. Object obj;
  978. boolean stringSelectionHack;
  979. try {
  980. obj = contents.getTransferData(flavor);
  981. if (obj == null) {
  982. return null;
  983. }
  984. if (flavor.equals(DataFlavor.plainTextFlavor) &&
  985. !(obj instanceof InputStream))
  986. {
  987. obj = contents.getTransferData(DataFlavor.stringFlavor);
  988. if (obj == null) {
  989. return null;
  990. }
  991. stringSelectionHack = true;
  992. } else {
  993. stringSelectionHack = false;
  994. }
  995. } catch (UnsupportedFlavorException e) {
  996. throw new IOException(e.getMessage());
  997. }
  998. // Source data is a String. Search-and-replace EOLN. Encode into the
  999. // target format. Append terminating NUL bytes.
  1000. if (stringSelectionHack ||
  1001. (String.class.equals(flavor.getRepresentationClass()) &&
  1002. isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1003. String str = removeSuspectedData(flavor, contents, (String)obj);
  1004. return translateTransferableString(
  1005. str,
  1006. format);
  1007. // Source data is a Reader. Convert to a String and recur. In the
  1008. // future, we may want to rewrite this so that we encode on demand.
  1009. } else if (flavor.isRepresentationClassReader()) {
  1010. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1011. throw new IOException
  1012. ("cannot transfer non-text data as Reader");
  1013. }
  1014. StringBuffer buf = new StringBuffer();
  1015. try (Reader r = (Reader)obj) {
  1016. int c;
  1017. while ((c = r.read()) != -1) {
  1018. buf.append((char)c);
  1019. }
  1020. }
  1021. return translateTransferableString(
  1022. buf.toString(),
  1023. format);
  1024. // Source data is a CharBuffer. Convert to a String and recur.
  1025. } else if (flavor.isRepresentationClassCharBuffer()) {
  1026. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1027. throw new IOException
  1028. ("cannot transfer non-text data as CharBuffer");
  1029. }
  1030. CharBuffer buffer = (CharBuffer)obj;
  1031. int size = buffer.remaining();
  1032. char[] chars = new char[size];
  1033. buffer.get(chars, 0, size);
  1034. return translateTransferableString(
  1035. new String(chars),
  1036. format);
  1037. // Source data is a char array. Convert to a String and recur.
  1038. } else if (char[].class.equals(flavor.getRepresentationClass())) {
  1039. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1040. throw new IOException
  1041. ("cannot transfer non-text data as char array");
  1042. }
  1043. return translateTransferableString(
  1044. new String((char[])obj),
  1045. format);
  1046. // Source data is a ByteBuffer. For arbitrary flavors, simply return
  1047. // the array. For text flavors, decode back to a String and recur to
  1048. // reencode according to the requested format.
  1049. } else if (flavor.isRepresentationClassByteBuffer()) {
  1050. ByteBuffer buffer = (ByteBuffer)obj;
  1051. int size = buffer.remaining();
  1052. byte[] bytes = new byte[size];
  1053. buffer.get(bytes, 0, size);
  1054. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1055. String sourceEncoding = DataTransferer.getTextCharset(flavor);
  1056. return translateTransferableString(
  1057. new String(bytes, sourceEncoding),
  1058. format);
  1059. } else {
  1060. return bytes;
  1061. }
  1062. // Source data is a byte array. For arbitrary flavors, simply return
  1063. // the array. For text flavors, decode back to a String and recur to
  1064. // reencode according to the requested format.
  1065. } else if (byte[].class.equals(flavor.getRepresentationClass())) {
  1066. byte[] bytes = (byte[])obj;
  1067. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1068. String sourceEncoding = DataTransferer.getTextCharset(flavor);
  1069. return translateTransferableString(
  1070. new String(bytes, sourceEncoding),
  1071. format);
  1072. } else {
  1073. return bytes;
  1074. }
  1075. // Source data is Image
  1076. } else if (DataFlavor.imageFlavor.equals(flavor)) {
  1077. if (!isImageFormat(format)) {
  1078. throw new IOException("Data translation failed: " +
  1079. "not an image format");
  1080. }
  1081. Image image = (Image)obj;
  1082. byte[] bytes = imageToPlatformBytes(image, format);
  1083. if (bytes == null) {
  1084. throw new IOException("Data translation failed: " +
  1085. "cannot convert java image to native format");
  1086. }
  1087. return bytes;
  1088. }
  1089. byte[] theByteArray = null;
  1090. // Target data is a file list. Source data must be a
  1091. // java.util.List which contains java.io.File or String instances.
  1092. if (isFileFormat(format)) {
  1093. if (!DataFlavor.javaFileListFlavor.equals(flavor)) {
  1094. throw new IOException("data translation failed");
  1095. }
  1096. final List list = (List)obj;
  1097. final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents);
  1098. final ArrayList<String> fileList = castToFiles(list, userProtectionDomain);
  1099. try (ByteArrayOutputStream bos = convertFileListToBytes(fileList)) {
  1100. theByteArray = bos.toByteArray();
  1101. }
  1102. // Target data is a URI list. Source data must be a
  1103. // java.util.List which contains java.io.File or String instances.
  1104. } else if (isURIListFormat(format)) {
  1105. if (!DataFlavor.javaFileListFlavor.equals(flavor)) {
  1106. throw new IOException("data translation failed");
  1107. }
  1108. String nat = getNativeForFormat(format);
  1109. String targetCharset = null;
  1110. if (nat != null) {
  1111. try {
  1112. targetCharset = new DataFlavor(nat).getParameter("charset");
  1113. } catch (ClassNotFoundException cnfe) {
  1114. throw new IOException(cnfe);
  1115. }
  1116. }
  1117. if (targetCharset == null) {
  1118. targetCharset = "UTF-8";
  1119. }
  1120. final List list = (List)obj;
  1121. final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents);
  1122. final ArrayList<String> fileList = castToFiles(list, userProtectionDomain);
  1123. final ArrayList<String> uriList = new ArrayList<String>(fileList.size());
  1124. for (String fileObject : fileList) {
  1125. final URI uri = new File(fileObject).toURI();
  1126. // Some implementations are fussy about the number of slashes (file:///path/to/file is best)
  1127. try {
  1128. uriList.add(new URI(uri.getScheme(), "", uri.getPath(), uri.getFragment()).toString());
  1129. } catch (URISyntaxException uriSyntaxException) {
  1130. throw new IOException(uriSyntaxException);
  1131. }
  1132. }
  1133. byte[] eoln = "\r\n".getBytes(targetCharset);
  1134. try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
  1135. for (int i = 0; i < uriList.size(); i++) {
  1136. byte[] bytes = uriList.get(i).getBytes(targetCharset);
  1137. bos.write(bytes, 0, bytes.length);
  1138. bos.write(eoln, 0, eoln.length);
  1139. }
  1140. theByteArray = bos.toByteArray();
  1141. }
  1142. // Source data is an InputStream. For arbitrary flavors, just grab the
  1143. // bytes and dump them into a byte array. For text flavors, decode back
  1144. // to a String and recur to reencode according to the requested format.
  1145. } else if (flavor.isRepresentationClassInputStream()) {
  1146. // Workaround to JDK-8024061: Exception thrown when drag and drop
  1147. // between two components is executed quickly.
  1148. // and JDK-8065098: JColorChooser no longer supports drag and drop
  1149. // between two JVM instances
  1150. if (!(obj instanceof InputStream)) {
  1151. return new byte[0];
  1152. }
  1153. try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
  1154. try (InputStream is = (InputStream)obj) {
  1155. boolean eof = false;
  1156. int avail = is.available();
  1157. byte[] tmp = new byte[avail > 8192 ? avail : 8192];
  1158. do {
  1159. int aValue;
  1160. if (!(eof = (aValue = is.read(tmp, 0, tmp.length)) == -1)) {
  1161. bos.write(tmp, 0, aValue);
  1162. }
  1163. } while (!eof);
  1164. }
  1165. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1166. byte[] bytes = bos.toByteArray();
  1167. String sourceEncoding = DataTransferer.getTextCharset(flavor);
  1168. return translateTransferableString(
  1169. new String(bytes, sourceEncoding),
  1170. format);
  1171. }
  1172. theByteArray = bos.toByteArray();
  1173. }
  1174. // Source data is an RMI object
  1175. } else if (flavor.isRepresentationClassRemote()) {
  1176. Object mo = RMI.newMarshalledObject(obj);
  1177. theByteArray = convertObjectToBytes(mo);
  1178. // Source data is Serializable
  1179. } else if (flavor.isRepresentationClassSerializable()) {
  1180. theByteArray = convertObjectToBytes(obj);
  1181. } else {
  1182. throw new IOException("data translation failed");
  1183. }
  1184. return theByteArray;
  1185. }
  1186. private static byte[] convertObjectToBytes(Object object) throws IOException {
  1187. try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
  1188. ObjectOutputStream oos = new ObjectOutputStream(bos))
  1189. {
  1190. oos.writeObject(object);
  1191. return bos.toByteArray();
  1192. }
  1193. }
  1194. protected abstract ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList) throws IOException;
  1195. private String removeSuspectedData(DataFlavor flavor, final Transferable contents, final String str)
  1196. throws IOException
  1197. {
  1198. if (null == System.getSecurityManager()
  1199. || !flavor.isMimeTypeEqual("text/uri-list"))
  1200. {
  1201. return str;
  1202. }
  1203. String ret_val = "";
  1204. final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents);
  1205. try {
  1206. ret_val = (String) AccessController.doPrivileged(new PrivilegedExceptionAction() {
  1207. public Object run() {
  1208. StringBuffer allowedFiles = new StringBuffer(str.length());
  1209. String [] uriArray = str.split("(\\s)+");
  1210. for (String fileName : uriArray)
  1211. {
  1212. File file = new File(fileName);
  1213. if (file.exists() &&
  1214. !(isFileInWebstartedCache(file) ||
  1215. isForbiddenToRead(file, userProtectionDomain)))
  1216. {
  1217. if (0 != allowedFiles.length())
  1218. {
  1219. allowedFiles.append("\\r\\n");
  1220. }
  1221. allowedFiles.append(fileName);
  1222. }
  1223. }
  1224. return allowedFiles.toString();
  1225. }
  1226. });
  1227. } catch (PrivilegedActionException pae) {
  1228. throw new IOException(pae.getMessage(), pae);
  1229. }
  1230. return ret_val;
  1231. }
  1232. private static ProtectionDomain getUserProtectionDomain(Transferable contents) {
  1233. return contents.getClass().getProtectionDomain();
  1234. }
  1235. private boolean isForbiddenToRead (File file, ProtectionDomain protectionDomain)
  1236. {
  1237. if (null == protectionDomain) {
  1238. return false;
  1239. }
  1240. try {
  1241. FilePermission filePermission =
  1242. new FilePermission(file.getCanonicalPath(), "read, delete");
  1243. if (protectionDomain.implies(filePermission)) {
  1244. return false;
  1245. }
  1246. } catch (IOException e) {}
  1247. return true;
  1248. }
  1249. private ArrayList<String> castToFiles(final List files,
  1250. final ProtectionDomain userProtectionDomain) throws IOException
  1251. {
  1252. final ArrayList<String> fileList = new ArrayList<String>();
  1253. try {
  1254. AccessController.doPrivileged(new PrivilegedExceptionAction() {
  1255. public Object run() throws IOException {
  1256. for (Object fileObject : files)
  1257. {
  1258. File file = castToFile(fileObject);
  1259. if (file != null &&
  1260. (null == System.getSecurityManager() ||
  1261. !(isFileInWebstartedCache(file) ||
  1262. isForbiddenToRead(file, userProtectionDomain))))
  1263. {
  1264. fileList.add(file.getCanonicalPath());
  1265. }
  1266. }
  1267. return null;
  1268. }
  1269. });
  1270. } catch (PrivilegedActionException pae) {
  1271. throw new IOException(pae.getMessage());
  1272. }
  1273. return fileList;
  1274. }
  1275. // It is important do not use user's successors
  1276. // of File class.
  1277. private File castToFile(Object fileObject) throws IOException {
  1278. String filePath = null;
  1279. if (fileObject instanceof File) {
  1280. filePath = ((File)fileObject).getCanonicalPath();
  1281. } else if (fileObject instanceof String) {
  1282. filePath = (String) fileObject;
  1283. } else {
  1284. return null;
  1285. }
  1286. return new File(filePath);
  1287. }
  1288. private final static String[] DEPLOYMENT_CACHE_PROPERTIES = {
  1289. "deployment.system.cachedir",
  1290. "deployment.user.cachedir",
  1291. "deployment.javaws.cachedir",
  1292. "deployment.javapi.cachedir"
  1293. };
  1294. private final static ArrayList <File> deploymentCacheDirectoryList =
  1295. new ArrayList<File>();
  1296. private static boolean isFileInWebstartedCache(File f) {
  1297. if (deploymentCacheDirectoryList.isEmpty()) {
  1298. for (String cacheDirectoryProperty : DEPLOYMENT_CACHE_PROPERTIES) {
  1299. String cacheDirectoryPath = System.getProperty(cacheDirectoryProperty);
  1300. if (cacheDirectoryPath != null) {
  1301. try {
  1302. File cacheDirectory = (new File(cacheDirectoryPath)).getCanonicalFile();
  1303. if (cacheDirectory != null) {
  1304. deploymentCacheDirectoryList.add(cacheDirectory);
  1305. }
  1306. } catch (IOException ioe) {}
  1307. }
  1308. }
  1309. }
  1310. for (File deploymentCacheDirectory : deploymentCacheDirectoryList) {
  1311. for (File dir = f; dir != null; dir = dir.getParentFile()) {
  1312. if (dir.equals(deploymentCacheDirectory)) {
  1313. return true;
  1314. }
  1315. }
  1316. }
  1317. return false;
  1318. }
  1319. public Object translateBytes(byte[] bytes, DataFlavor flavor,
  1320. long format, Transferable localeTransferable)
  1321. throws IOException
  1322. {
  1323. Object theObject = null;
  1324. // Source data is a file list. Use the dragQueryFile native function to
  1325. // do most of the decoding. Then wrap File objects around the String
  1326. // filenames and return a List.
  1327. if (isFileFormat(format)) {
  1328. if (!DataFlavor.javaFileListFlavor.equals(flavor)) {
  1329. throw new IOException("data translation failed");
  1330. }
  1331. String[] filenames = dragQueryFile(bytes);
  1332. if (filenames == null) {
  1333. return null;
  1334. }
  1335. // Convert the strings to File objects
  1336. File[] files = new File[filenames.length];
  1337. for (int i = 0; i < filenames.length; i++) {
  1338. files[i] = new File(filenames[i]);
  1339. }
  1340. // Turn the list of Files into a List and return
  1341. theObject = Arrays.asList(files);
  1342. // Source data is a URI list. Convert to DataFlavor.javaFileListFlavor
  1343. // where possible.
  1344. } else if (isURIListFormat(format)
  1345. && DataFlavor.javaFileListFlavor.equals(flavor)) {
  1346. try (ByteArrayInputStream str = new ByteArrayInputStream(bytes)) {
  1347. URI uris[] = dragQueryURIs(str, format, localeTransferable);
  1348. if (uris == null) {
  1349. return null;
  1350. }
  1351. List<File> files = new ArrayList<>();
  1352. for (URI uri : uris) {
  1353. try {
  1354. files.add(new File(uri));
  1355. } catch (IllegalArgumentException illegalArg) {
  1356. // When converting from URIs to less generic files,
  1357. // common practice (Wine, SWT) seems to be to
  1358. // silently drop the URIs that aren't local files.
  1359. }
  1360. }
  1361. theObject = files;
  1362. }
  1363. // Target data is a String. Strip terminating NUL bytes. Decode bytes
  1364. // into characters. Search-and-replace EOLN.
  1365. } else if (String.class.equals(flavor.getRepresentationClass()) &&
  1366. isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1367. theObject = translateBytesToString(bytes, format, localeTransferable);
  1368. // Target data is a Reader. Obtain data in InputStream format, encoded
  1369. // as "Unicode" (utf-16be). Then use an InputStreamReader to decode
  1370. // back to chars on demand.
  1371. } else if (flavor.isRepresentationClassReader()) {
  1372. try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
  1373. theObject = translateStream(bais,
  1374. flavor, format, localeTransferable);
  1375. }
  1376. // Target data is a CharBuffer. Recur to obtain String and wrap.
  1377. } else if (flavor.isRepresentationClassCharBuffer()) {
  1378. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1379. throw new IOException
  1380. ("cannot transfer non-text data as CharBuffer");
  1381. }
  1382. CharBuffer buffer = CharBuffer.wrap(
  1383. translateBytesToString(bytes,format, localeTransferable));
  1384. theObject = constructFlavoredObject(buffer, flavor, CharBuffer.class);
  1385. // Target data is a char array. Recur to obtain String and convert to
  1386. // char array.
  1387. } else if (char[].class.equals(flavor.getRepresentationClass())) {
  1388. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1389. throw new IOException
  1390. ("cannot transfer non-text data as char array");
  1391. }
  1392. theObject = translateBytesToString(
  1393. bytes, format, localeTransferable).toCharArray();
  1394. // Target data is a ByteBuffer. For arbitrary flavors, just return
  1395. // the raw bytes. For text flavors, convert to a String to strip
  1396. // terminators and search-and-replace EOLN, then reencode according to
  1397. // the requested flavor.
  1398. } else if (flavor.isRepresentationClassByteBuffer()) {
  1399. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1400. bytes = translateBytesToString(
  1401. bytes, format, localeTransferable).getBytes(
  1402. DataTransferer.getTextCharset(flavor)
  1403. );
  1404. }
  1405. ByteBuffer buffer = ByteBuffer.wrap(bytes);
  1406. theObject = constructFlavoredObject(buffer, flavor, ByteBuffer.class);
  1407. // Target data is a byte array. For arbitrary flavors, just return
  1408. // the raw bytes. For text flavors, convert to a String to strip
  1409. // terminators and search-and-replace EOLN, then reencode according to
  1410. // the requested flavor.
  1411. } else if (byte[].class.equals(flavor.getRepresentationClass())) {
  1412. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1413. theObject = translateBytesToString(
  1414. bytes, format, localeTransferable
  1415. ).getBytes(DataTransferer.getTextCharset(flavor));
  1416. } else {
  1417. theObject = bytes;
  1418. }
  1419. // Target data is an InputStream. For arbitrary flavors, just return
  1420. // the raw bytes. For text flavors, decode to strip terminators and
  1421. // search-and-replace EOLN, then reencode according to the requested
  1422. // flavor.
  1423. } else if (flavor.isRepresentationClassInputStream()) {
  1424. try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
  1425. theObject = translateStream(bais, flavor, format, localeTransferable);
  1426. }
  1427. } else if (flavor.isRepresentationClassRemote()) {
  1428. try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
  1429. ObjectInputStream ois = new ObjectInputStream(bais))
  1430. {
  1431. theObject = RMI.getMarshalledObject(ois.readObject());
  1432. } catch (Exception e) {
  1433. throw new IOException(e.getMessage());
  1434. }
  1435. // Target data is Serializable
  1436. } else if (flavor.isRepresentationClassSerializable()) {
  1437. try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
  1438. theObject = translateStream(bais, flavor, format, localeTransferable);
  1439. }
  1440. // Target data is Image
  1441. } else if (DataFlavor.imageFlavor.equals(flavor)) {
  1442. if (!isImageFormat(format)) {
  1443. throw new IOException("data translation failed");
  1444. }
  1445. theObject = platformImageBytesToImage(bytes, format);
  1446. }
  1447. if (theObject == null) {
  1448. throw new IOException("data translation failed");
  1449. }
  1450. return theObject;
  1451. }
  1452. /**
  1453. * Primary translation function for translating
  1454. * an InputStream into an Object, given a source format and a target
  1455. * DataFlavor.
  1456. */
  1457. public Object translateStream(InputStream str, DataFlavor flavor,
  1458. long format, Transferable localeTransferable)
  1459. throws IOException
  1460. {
  1461. Object theObject = null;
  1462. // Source data is a URI list. Convert to DataFlavor.javaFileListFlavor
  1463. // where possible.
  1464. if (isURIListFormat(format)
  1465. && DataFlavor.javaFileListFlavor.equals(flavor))
  1466. {
  1467. URI uris[] = dragQueryURIs(str, format, localeTransferable);
  1468. if (uris == null) {
  1469. return null;
  1470. }
  1471. ArrayList files = new ArrayList();
  1472. for (URI uri : uris) {
  1473. try {
  1474. files.add(new File(uri));
  1475. } catch (IllegalArgumentException illegalArg) {
  1476. // When converting from URIs to less generic files,
  1477. // common practice (Wine, SWT) seems to be to
  1478. // silently drop the URIs that aren't local files.
  1479. }
  1480. }
  1481. theObject = files;
  1482. // Target data is a String. Strip terminating NUL bytes. Decode bytes
  1483. // into characters. Search-and-replace EOLN.
  1484. } else if (String.class.equals(flavor.getRepresentationClass()) &&
  1485. isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1486. return translateBytesToString(inputStreamToByteArray(str),
  1487. format, localeTransferable);
  1488. // Special hack to maintain backwards-compatibility with the brokenness
  1489. // of StringSelection. Return a StringReader instead of an InputStream.
  1490. // Recur to obtain String and encapsulate.
  1491. } else if (DataFlavor.plainTextFlavor.equals(flavor)) {
  1492. theObject = new StringReader(translateBytesToString(
  1493. inputStreamToByteArray(str),
  1494. format, localeTransferable));
  1495. // Target data is an InputStream. For arbitrary flavors, just return
  1496. // the raw bytes. For text flavors, decode to strip terminators and
  1497. // search-and-replace EOLN, then reencode according to the requested
  1498. // flavor.
  1499. } else if (flavor.isRepresentationClassInputStream()) {
  1500. theObject = translateStreamToInputStream(str, flavor, format,
  1501. localeTransferable);
  1502. // Target data is a Reader. Obtain data in InputStream format, encoded
  1503. // as "Unicode" (utf-16be). Then use an InputStreamReader to decode
  1504. // back to chars on demand.
  1505. } else if (flavor.isRepresentationClassReader()) {
  1506. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1507. throw new IOException
  1508. ("cannot transfer non-text data as Reader");
  1509. }
  1510. InputStream is = (InputStream)translateStreamToInputStream(
  1511. str, DataFlavor.plainTextFlavor,
  1512. format, localeTransferable);
  1513. String unicode = DataTransferer.getTextCharset(DataFlavor.plainTextFlavor);
  1514. Reader reader = new InputStreamReader(is, unicode);
  1515. theObject = constructFlavoredObject(reader, flavor, Reader.class);
  1516. // Target data is a byte array
  1517. } else if (byte[].class.equals(flavor.getRepresentationClass())) {
  1518. if(isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1519. theObject = translateBytesToString(inputStreamToByteArray(str), format, localeTransferable)
  1520. .getBytes(DataTransferer.getTextCharset(flavor));
  1521. } else {
  1522. theObject = inputStreamToByteArray(str);
  1523. }
  1524. // Target data is an RMI object
  1525. } else if (flavor.isRepresentationClassRemote()) {
  1526. try (ObjectInputStream ois =
  1527. new ObjectInputStream(str))
  1528. {
  1529. theObject = RMI.getMarshalledObject(ois.readObject());
  1530. }catch (Exception e) {
  1531. throw new IOException(e.getMessage());
  1532. }
  1533. // Target data is Serializable
  1534. } else if (flavor.isRepresentationClassSerializable()) {
  1535. try (ObjectInputStream ois =
  1536. new ObjectInputStream(str))
  1537. {
  1538. theObject = ois.readObject();
  1539. } catch (Exception e) {
  1540. throw new IOException(e.getMessage());
  1541. }
  1542. // Target data is Image
  1543. } else if (DataFlavor.imageFlavor.equals(flavor)) {
  1544. if (!isImageFormat(format)) {
  1545. throw new IOException("data translation failed");
  1546. }
  1547. theObject = platformImageBytesToImage(inputStreamToByteArray(str), format);
  1548. }
  1549. if (theObject == null) {
  1550. throw new IOException("data translation failed");
  1551. }
  1552. return theObject;
  1553. }
  1554. /**
  1555. * For arbitrary flavors, just use the raw InputStream. For text flavors,
  1556. * ReencodingInputStream will decode and reencode the InputStream on demand
  1557. * so that we can strip terminators and search-and-replace EOLN.
  1558. */
  1559. private Object translateStreamToInputStream
  1560. (InputStream str, DataFlavor flavor, long format,
  1561. Transferable localeTransferable) throws IOException
  1562. {
  1563. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1564. str = new ReencodingInputStream
  1565. (str, format, DataTransferer.getTextCharset(flavor),
  1566. localeTransferable);
  1567. }
  1568. return constructFlavoredObject(str, flavor, InputStream.class);
  1569. }
  1570. /**
  1571. * We support representations which are exactly of the specified Class,
  1572. * and also arbitrary Objects which have a constructor which takes an
  1573. * instance of the Class as its sole parameter.
  1574. */
  1575. private Object constructFlavoredObject(Object arg, DataFlavor flavor,
  1576. Class clazz)
  1577. throws IOException
  1578. {
  1579. final Class dfrc = flavor.getRepresentationClass();
  1580. if (clazz.equals(dfrc)) {
  1581. return arg; // simple case
  1582. } else {
  1583. Constructor[] constructors = null;
  1584. try {
  1585. constructors = (Constructor[])
  1586. AccessController.doPrivileged(new PrivilegedAction() {
  1587. public Object run() {
  1588. return dfrc.getConstructors();
  1589. }
  1590. });
  1591. } catch (SecurityException se) {
  1592. throw new IOException(se.getMessage());
  1593. }
  1594. Constructor constructor = null;
  1595. for (int j = 0; j < constructors.length; j++) {
  1596. if (!Modifier.isPublic(constructors[j].getModifiers())) {
  1597. continue;
  1598. }
  1599. Class[] ptypes = constructors[j].getParameterTypes();
  1600. if (ptypes != null && ptypes.length == 1 &&
  1601. clazz.equals(ptypes[0])) {
  1602. constructor = constructors[j];
  1603. break;
  1604. }
  1605. }
  1606. if (constructor == null) {
  1607. throw new IOException("can't find <init>(L"+ clazz +
  1608. ";)V for class: " + dfrc.getName());
  1609. }
  1610. try {
  1611. return constructor.newInstance(new Object[] { arg } );
  1612. } catch (Exception e) {
  1613. throw new IOException(e.getMessage());
  1614. }
  1615. }
  1616. }
  1617. /**
  1618. * Used for decoding and reencoding an InputStream on demand so that we
  1619. * can strip NUL terminators and perform EOLN search-and-replace.
  1620. */
  1621. public class ReencodingInputStream extends InputStream {
  1622. protected BufferedReader wrapped;
  1623. protected final char[] in = new char[2];
  1624. protected byte[] out;
  1625. protected CharsetEncoder encoder;
  1626. protected CharBuffer inBuf;
  1627. protected ByteBuffer outBuf;
  1628. protected char[] eoln;
  1629. protected int numTerminators;
  1630. protected boolean eos;
  1631. protected int index, limit;
  1632. public ReencodingInputStream(InputStream bytestream, long format,
  1633. String targetEncoding,
  1634. Transferable localeTransferable)
  1635. throws IOException
  1636. {
  1637. Long lFormat = Long.valueOf(format);
  1638. String sourceEncoding = null;
  1639. if (isLocaleDependentTextFormat(format) &&
  1640. localeTransferable != null &&
  1641. localeTransferable.
  1642. isDataFlavorSupported(javaTextEncodingFlavor))
  1643. {
  1644. try {
  1645. sourceEncoding = new String((byte[])localeTransferable.
  1646. getTransferData(javaTextEncodingFlavor),
  1647. "UTF-8");
  1648. } catch (UnsupportedFlavorException cannotHappen) {
  1649. }
  1650. } else {
  1651. sourceEncoding = getCharsetForTextFormat(lFormat);
  1652. }
  1653. if (sourceEncoding == null) {
  1654. // Only happens when we have a custom text type.
  1655. sourceEncoding = getDefaultTextCharset();
  1656. }
  1657. wrapped = new BufferedReader
  1658. (new InputStreamReader(bytestream, sourceEncoding));
  1659. if (targetEncoding == null) {
  1660. // Throw NullPointerException for compatibility with the former
  1661. // call to sun.io.CharToByteConverter.getConverter(null)
  1662. // (Charset.forName(null) throws unspecified IllegalArgumentException
  1663. // now; see 6228568)
  1664. throw new NullPointerException("null target encoding");
  1665. }
  1666. try {
  1667. encoder = Charset.forName(targetEncoding).newEncoder();
  1668. out = new byte[(int)(encoder.maxBytesPerChar() * 2 + 0.5)];
  1669. inBuf = CharBuffer.wrap(in);
  1670. outBuf = ByteBuffer.wrap(out);
  1671. } catch (IllegalCharsetNameException e) {
  1672. throw new IOException(e.toString());
  1673. } catch (UnsupportedCharsetException e) {
  1674. throw new IOException(e.toString());
  1675. } catch (UnsupportedOperationException e) {
  1676. throw new IOException(e.toString());
  1677. }
  1678. String sEoln = (String)nativeEOLNs.get(lFormat);
  1679. if (sEoln != null) {
  1680. eoln = sEoln.toCharArray();
  1681. }
  1682. // A hope and a prayer that this works generically. This will
  1683. // definitely work on Win32.
  1684. Integer terminators = (Integer)nativeTerminators.get(lFormat);
  1685. if (terminators != null) {
  1686. numTerminators = terminators.intValue();
  1687. }
  1688. }
  1689. private int readChar() throws IOException {
  1690. int c = wrapped.read();
  1691. if (c == -1) { // -1 is EOS
  1692. eos = true;
  1693. return -1;
  1694. }
  1695. // "c == 0" is not quite correct, but good enough on Windows.
  1696. if (numTerminators > 0 && c == 0) {
  1697. eos = true;
  1698. return -1;
  1699. } else if (eoln != null && matchCharArray(eoln, c)) {
  1700. c = '\n' & 0xFFFF;
  1701. }
  1702. return c;
  1703. }
  1704. public int read() throws IOException {
  1705. if (eos) {
  1706. return -1;
  1707. }
  1708. if (index >= limit) {
  1709. // deal with supplementary characters
  1710. int c = readChar();
  1711. if (c == -1) {
  1712. return -1;
  1713. }
  1714. in[0] = (char) c;
  1715. in[1] = 0;
  1716. inBuf.limit(1);
  1717. if (Character.isHighSurrogate((char) c)) {
  1718. c = readChar();
  1719. if (c != -1) {
  1720. in[1] = (char) c;
  1721. inBuf.limit(2);
  1722. }
  1723. }
  1724. inBuf.rewind();
  1725. outBuf.limit(out.length).rewind();
  1726. encoder.encode(inBuf, outBuf, false);
  1727. outBuf.flip();
  1728. limit = outBuf.limit();
  1729. index = 0;
  1730. return read();
  1731. } else {
  1732. return out[index++] & 0xFF;
  1733. }
  1734. }
  1735. public int available() throws IOException {
  1736. return ((eos) ? 0 : (limit - index));
  1737. }
  1738. public void close() throws IOException {
  1739. wrapped.close();
  1740. }
  1741. /**
  1742. * Checks to see if the next array.length characters in wrapped
  1743. * match array. The first character is provided as c. Subsequent
  1744. * characters are read from wrapped itself. When this method returns,
  1745. * the wrapped index may be different from what it was when this
  1746. * method was called.
  1747. */
  1748. private boolean matchCharArray(char[] array, int c)
  1749. throws IOException
  1750. {
  1751. wrapped.mark(array.length); // BufferedReader supports mark
  1752. int count = 0;
  1753. if ((char)c == array[0]) {
  1754. for (count = 1; count < array.length; count++) {
  1755. c = wrapped.read();
  1756. if (c == -1 || ((char)c) != array[count]) {
  1757. break;
  1758. }
  1759. }
  1760. }
  1761. if (count == array.length) {
  1762. return true;
  1763. } else {
  1764. wrapped.reset();
  1765. return false;
  1766. }
  1767. }
  1768. }
  1769. /**
  1770. * Decodes a byte array into a set of String filenames.
  1771. */
  1772. protected abstract String[] dragQueryFile(byte[] bytes);
  1773. /**
  1774. * Decodes URIs from either a byte array or a stream.
  1775. */
  1776. protected URI[] dragQueryURIs(InputStream stream,
  1777. long format,
  1778. Transferable localeTransferable)
  1779. throws IOException
  1780. {
  1781. throw new IOException(
  1782. new UnsupportedOperationException("not implemented on this platform"));
  1783. }
  1784. /**
  1785. * Translates either a byte array or an input stream which contain
  1786. * platform-specific image data in the given format into an Image.
  1787. */
  1788. protected abstract Image platformImageBytesToImage(
  1789. byte[] bytes,long format) throws IOException;
  1790. /**
  1791. * Translates either a byte array or an input stream which contain
  1792. * an image data in the given standard format into an Image.
  1793. *
  1794. * @param mimeType image MIME type, such as: image/png, image/jpeg, image/gif
  1795. */
  1796. protected Image standardImageBytesToImage(
  1797. byte[] bytes, String mimeType) throws IOException
  1798. {
  1799. Iterator readerIterator = ImageIO.getImageReadersByMIMEType(mimeType);
  1800. if (!readerIterator.hasNext()) {
  1801. throw new IOException("No registered service provider can decode " +
  1802. " an image from " + mimeType);
  1803. }
  1804. IOException ioe = null;
  1805. while (readerIterator.hasNext()) {
  1806. ImageReader imageReader = (ImageReader)readerIterator.next();
  1807. try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) {
  1808. ImageInputStream imageInputStream =
  1809. ImageIO.createImageInputStream(bais);
  1810. try {
  1811. ImageReadParam param = imageReader.getDefaultReadParam();
  1812. imageReader.setInput(imageInputStream, true, true);
  1813. BufferedImage bufferedImage =
  1814. imageReader.read(imageReader.getMinIndex(), param);
  1815. if (bufferedImage != null) {
  1816. return bufferedImage;
  1817. }
  1818. } finally {
  1819. imageInputStream.close();
  1820. imageReader.dispose();
  1821. }
  1822. } catch (IOException e) {
  1823. ioe = e;
  1824. continue;
  1825. }
  1826. }
  1827. if (ioe == null) {
  1828. ioe = new IOException("Registered service providers failed to decode"
  1829. + " an image from " + mimeType);
  1830. }
  1831. throw ioe;
  1832. }
  1833. /**
  1834. * Translates a Java Image into a byte array which contains platform-
  1835. * specific image data in the given format.
  1836. */
  1837. protected abstract byte[] imageToPlatformBytes(Image image, long format)
  1838. throws IOException;
  1839. /**
  1840. * Translates a Java Image into a byte array which contains
  1841. * an image data in the given standard format.
  1842. *
  1843. * @param mimeType image MIME type, such as: image/png, image/jpeg
  1844. */
  1845. protected byte[] imageToStandardBytes(Image image, String mimeType)
  1846. throws IOException {
  1847. IOException originalIOE = null;
  1848. Iterator writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
  1849. if (!writerIterator.hasNext()) {
  1850. throw new IOException("No registered service provider can encode " +
  1851. " an image to " + mimeType);
  1852. }
  1853. if (image instanceof RenderedImage) {
  1854. // Try to encode the original image.
  1855. try {
  1856. return imageToStandardBytesImpl((RenderedImage)image, mimeType);
  1857. } catch (IOException ioe) {
  1858. originalIOE = ioe;
  1859. }
  1860. }
  1861. // Retry with a BufferedImage.
  1862. int width = 0;
  1863. int height = 0;
  1864. if (image instanceof ToolkitImage) {
  1865. ImageRepresentation ir = ((ToolkitImage)image).getImageRep();
  1866. ir.reconstruct(ImageObserver.ALLBITS);
  1867. width = ir.getWidth();
  1868. height = ir.getHeight();
  1869. } else {
  1870. width = image.getWidth(null);
  1871. height = image.getHeight(null);
  1872. }
  1873. ColorModel model = ColorModel.getRGBdefault();
  1874. WritableRaster raster =
  1875. model.createCompatibleWritableRaster(width, height);
  1876. BufferedImage bufferedImage =
  1877. new BufferedImage(model, raster, model.isAlphaPremultiplied(),
  1878. null);
  1879. Graphics g = bufferedImage.getGraphics();
  1880. try {
  1881. g.drawImage(image, 0, 0, width, height, null);
  1882. } finally {
  1883. g.dispose();
  1884. }
  1885. try {
  1886. return imageToStandardBytesImpl(bufferedImage, mimeType);
  1887. } catch (IOException ioe) {
  1888. if (originalIOE != null) {
  1889. throw originalIOE;
  1890. } else {
  1891. throw ioe;
  1892. }
  1893. }
  1894. }
  1895. protected byte[] imageToStandardBytesImpl(RenderedImage renderedImage,
  1896. String mimeType)
  1897. throws IOException {
  1898. Iterator writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
  1899. ImageTypeSpecifier typeSpecifier =
  1900. new ImageTypeSpecifier(renderedImage);
  1901. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  1902. IOException ioe = null;
  1903. while (writerIterator.hasNext()) {
  1904. ImageWriter imageWriter = (ImageWriter)writerIterator.next();
  1905. ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider();
  1906. if (!writerSpi.canEncodeImage(typeSpecifier)) {
  1907. continue;
  1908. }
  1909. try {
  1910. ImageOutputStream imageOutputStream =
  1911. ImageIO.createImageOutputStream(baos);
  1912. try {
  1913. imageWriter.setOutput(imageOutputStream);
  1914. imageWriter.write(renderedImage);
  1915. imageOutputStream.flush();
  1916. } finally {
  1917. imageOutputStream.close();
  1918. }
  1919. } catch (IOException e) {
  1920. imageWriter.dispose();
  1921. baos.reset();
  1922. ioe = e;
  1923. continue;
  1924. }
  1925. imageWriter.dispose();
  1926. baos.close();
  1927. return baos.toByteArray();
  1928. }
  1929. baos.close();
  1930. if (ioe == null) {
  1931. ioe = new IOException("Registered service providers failed to encode "
  1932. + renderedImage + " to " + mimeType);
  1933. }
  1934. throw ioe;
  1935. }
  1936. /**
  1937. * Concatenates the data represented by two objects. Objects can be either
  1938. * byte arrays or instances of <code>InputStream</code>. If both arguments
  1939. * are byte arrays byte array will be returned. Otherwise an
  1940. * <code>InputStream</code> will be returned.
  1941. * <p>
  1942. * Currently is only called from native code to prepend palette data to
  1943. * platform-specific image data during image transfer on Win32.
  1944. *
  1945. * @param obj1 the first object to be concatenated.
  1946. * @param obj2 the second object to be concatenated.
  1947. * @return a byte array or an <code>InputStream</code> which represents
  1948. * a logical concatenation of the two arguments.
  1949. * @throws NullPointerException is either of the arguments is
  1950. * <code>null</code>
  1951. * @throws ClassCastException is either of the arguments is
  1952. * neither byte array nor an instance of <code>InputStream</code>.
  1953. */
  1954. private Object concatData(Object obj1, Object obj2) {
  1955. InputStream str1 = null;
  1956. InputStream str2 = null;
  1957. if (obj1 instanceof byte[]) {
  1958. byte[] arr1 = (byte[])obj1;
  1959. if (obj2 instanceof byte[]) {
  1960. byte[] arr2 = (byte[])obj2;
  1961. byte[] ret = new byte[arr1.length + arr2.length];
  1962. System.arraycopy(arr1, 0, ret, 0, arr1.length);
  1963. System.arraycopy(arr2, 0, ret, arr1.length, arr2.length);
  1964. return ret;
  1965. } else {
  1966. str1 = new ByteArrayInputStream(arr1);
  1967. str2 = (InputStream)obj2;
  1968. }
  1969. } else {
  1970. str1 = (InputStream)obj1;
  1971. if (obj2 instanceof byte[]) {
  1972. str2 = new ByteArrayInputStream((byte[])obj2);
  1973. } else {
  1974. str2 = (InputStream)obj2;
  1975. }
  1976. }
  1977. return new SequenceInputStream(str1, str2);
  1978. }
  1979. public byte[] convertData(final Object source,
  1980. final Transferable contents,
  1981. final long format,
  1982. final Map formatMap,
  1983. final boolean isToolkitThread)
  1984. throws IOException
  1985. {
  1986. byte[] ret = null;
  1987. /*
  1988. * If the current thread is the Toolkit thread we should post a
  1989. * Runnable to the event dispatch thread associated with source Object,
  1990. * since translateTransferable() calls Transferable.getTransferData()
  1991. * that may contain client code.
  1992. */
  1993. if (isToolkitThread) try {
  1994. final Stack stack = new Stack();
  1995. final Runnable dataConverter = new Runnable() {
  1996. // Guard against multiple executions.
  1997. private boolean done = false;
  1998. public void run() {
  1999. if (done) {
  2000. return;
  2001. }
  2002. byte[] data = null;
  2003. try {
  2004. DataFlavor flavor = (DataFlavor)formatMap.get(Long.valueOf(format));
  2005. if (flavor != null) {
  2006. data = translateTransferable(contents, flavor, format);
  2007. }
  2008. } catch (Exception e) {
  2009. e.printStackTrace();
  2010. data = null;
  2011. }
  2012. try {
  2013. getToolkitThreadBlockedHandler().lock();
  2014. stack.push(data);
  2015. getToolkitThreadBlockedHandler().exit();
  2016. } finally {
  2017. getToolkitThreadBlockedHandler().unlock();
  2018. done = true;
  2019. }
  2020. }
  2021. };
  2022. final AppContext appContext = SunToolkit.targetToAppContext(source);
  2023. getToolkitThreadBlockedHandler().lock();
  2024. if (appContext != null) {
  2025. appContext.put(DATA_CONVERTER_KEY, dataConverter);
  2026. }
  2027. SunToolkit.executeOnEventHandlerThread(source, dataConverter);
  2028. while (stack.empty()) {
  2029. getToolkitThreadBlockedHandler().enter();
  2030. }
  2031. if (appContext != null) {
  2032. appContext.remove(DATA_CONVERTER_KEY);
  2033. }
  2034. ret = (byte[])stack.pop();
  2035. } finally {
  2036. getToolkitThreadBlockedHandler().unlock();
  2037. } else {
  2038. DataFlavor flavor = (DataFlavor)
  2039. formatMap.get(Long.valueOf(format));
  2040. if (flavor != null) {
  2041. ret = translateTransferable(contents, flavor, format);
  2042. }
  2043. }
  2044. return ret;
  2045. }
  2046. public void processDataConversionRequests() {
  2047. if (EventQueue.isDispatchThread()) {
  2048. AppContext appContext = AppContext.getAppContext();
  2049. getToolkitThreadBlockedHandler().lock();
  2050. try {
  2051. Runnable dataConverter =
  2052. (Runnable)appContext.get(DATA_CONVERTER_KEY);
  2053. if (dataConverter != null) {
  2054. dataConverter.run();
  2055. appContext.remove(DATA_CONVERTER_KEY);
  2056. }
  2057. } finally {
  2058. getToolkitThreadBlockedHandler().unlock();
  2059. }
  2060. }
  2061. }
  2062. public abstract ToolkitThreadBlockedHandler
  2063. getToolkitThreadBlockedHandler();
  2064. /**
  2065. * Helper function to reduce a Map with Long keys to a long array.
  2066. * <p>
  2067. * The map keys are sorted according to the native formats preference
  2068. * order.
  2069. */
  2070. public static long[] keysToLongArray(SortedMap map) {
  2071. Set keySet = map.keySet();
  2072. long[] retval = new long[keySet.size()];
  2073. int i = 0;
  2074. for (Iterator iter = keySet.iterator(); iter.hasNext(); i++) {
  2075. retval[i] = ((Long)iter.next()).longValue();
  2076. }
  2077. return retval;
  2078. }
  2079. /**
  2080. * Helper function to convert a Set of DataFlavors to a sorted array.
  2081. * The array will be sorted according to <code>DataFlavorComparator</code>.
  2082. */
  2083. public static DataFlavor[] setToSortedDataFlavorArray(Set flavorsSet) {
  2084. DataFlavor[] flavors = new DataFlavor[flavorsSet.size()];
  2085. flavorsSet.toArray(flavors);
  2086. final Comparator comparator =
  2087. new DataFlavorComparator(IndexedComparator.SELECT_WORST);
  2088. Arrays.sort(flavors, comparator);
  2089. return flavors;
  2090. }
  2091. /**
  2092. * Helper function to convert an InputStream to a byte[] array.
  2093. */
  2094. protected static byte[] inputStreamToByteArray(InputStream str)
  2095. throws IOException
  2096. {
  2097. try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
  2098. int len = 0;
  2099. byte[] buf = new byte[8192];
  2100. while ((len = str.read(buf)) != -1) {
  2101. baos.write(buf, 0, len);
  2102. }
  2103. return baos.toByteArray();
  2104. }
  2105. }
  2106. /**
  2107. * Returns platform-specific mappings for the specified native.
  2108. * If there are no platform-specific mappings for this native, the method
  2109. * returns an empty <code>List</code>.
  2110. */
  2111. public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) {
  2112. return new LinkedHashSet<>();
  2113. }
  2114. /**
  2115. * Returns platform-specific mappings for the specified flavor.
  2116. * If there are no platform-specific mappings for this flavor, the method
  2117. * returns an empty <code>List</code>.
  2118. */
  2119. public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) {
  2120. return new LinkedHashSet<>();
  2121. }
  2122. /**
  2123. * A Comparator which includes a helper function for comparing two Objects
  2124. * which are likely to be keys in the specified Map.
  2125. */
  2126. public abstract static class IndexedComparator implements Comparator {
  2127. /**
  2128. * The best Object (e.g., DataFlavor) will be the last in sequence.
  2129. */
  2130. public static final boolean SELECT_BEST = true;
  2131. /**
  2132. * The best Object (e.g., DataFlavor) will be the first in sequence.
  2133. */
  2134. public static final boolean SELECT_WORST = false;
  2135. protected final boolean order;
  2136. public IndexedComparator() {
  2137. this(SELECT_BEST);
  2138. }
  2139. public IndexedComparator(boolean order) {
  2140. this.order = order;
  2141. }
  2142. /**
  2143. * Helper method to compare two objects by their Integer indices in the
  2144. * given map. If the map doesn't contain an entry for either of the
  2145. * objects, the fallback index will be used for the object instead.
  2146. *
  2147. * @param indexMap the map which maps objects into Integer indexes.
  2148. * @param obj1 the first object to be compared.
  2149. * @param obj2 the second object to be compared.
  2150. * @param fallbackIndex the Integer to be used as a fallback index.
  2151. * @return a negative integer, zero, or a positive integer as the
  2152. * first object is mapped to a less, equal to, or greater
  2153. * index than the second.
  2154. */
  2155. protected static int compareIndices(Map indexMap,
  2156. Object obj1, Object obj2,
  2157. Integer fallbackIndex) {
  2158. Integer index1 = (Integer)indexMap.get(obj1);
  2159. Integer index2 = (Integer)indexMap.get(obj2);
  2160. if (index1 == null) {
  2161. index1 = fallbackIndex;
  2162. }
  2163. if (index2 == null) {
  2164. index2 = fallbackIndex;
  2165. }
  2166. return index1.compareTo(index2);
  2167. }
  2168. /**
  2169. * Helper method to compare two objects by their Long indices in the
  2170. * given map. If the map doesn't contain an entry for either of the
  2171. * objects, the fallback index will be used for the object instead.
  2172. *
  2173. * @param indexMap the map which maps objects into Long indexes.
  2174. * @param obj1 the first object to be compared.
  2175. * @param obj2 the second object to be compared.
  2176. * @param fallbackIndex the Long to be used as a fallback index.
  2177. * @return a negative integer, zero, or a positive integer as the
  2178. * first object is mapped to a less, equal to, or greater
  2179. * index than the second.
  2180. */
  2181. protected static int compareLongs(Map indexMap,
  2182. Object obj1, Object obj2,
  2183. Long fallbackIndex) {
  2184. Long index1 = (Long)indexMap.get(obj1);
  2185. Long index2 = (Long)indexMap.get(obj2);
  2186. if (index1 == null) {
  2187. index1 = fallbackIndex;
  2188. }
  2189. if (index2 == null) {
  2190. index2 = fallbackIndex;
  2191. }
  2192. return index1.compareTo(index2);
  2193. }
  2194. }
  2195. /**
  2196. * An IndexedComparator which compares two String charsets. The comparison
  2197. * follows the rules outlined in DataFlavor.selectBestTextFlavor. In order
  2198. * to ensure that non-Unicode, non-ASCII, non-default charsets are sorted
  2199. * in alphabetical order, charsets are not automatically converted to their
  2200. * canonical forms.
  2201. */
  2202. public static class CharsetComparator extends IndexedComparator {
  2203. private static final Map charsets;
  2204. private static String defaultEncoding;
  2205. private static final Integer DEFAULT_CHARSET_INDEX = Integer.valueOf(2);
  2206. private static final Integer OTHER_CHARSET_INDEX = Integer.valueOf(1);
  2207. private static final Integer WORST_CHARSET_INDEX = Integer.valueOf(0);
  2208. private static final Integer UNSUPPORTED_CHARSET_INDEX =
  2209. Integer.valueOf(Integer.MIN_VALUE);
  2210. private static final String UNSUPPORTED_CHARSET = "UNSUPPORTED";
  2211. static {
  2212. HashMap charsetsMap = new HashMap(8, 1.0f);
  2213. // we prefer Unicode charsets
  2214. charsetsMap.put(canonicalName("UTF-16LE"), Integer.valueOf(4));
  2215. charsetsMap.put(canonicalName("UTF-16BE"), Integer.valueOf(5));
  2216. charsetsMap.put(canonicalName("UTF-8"), Integer.valueOf(6));
  2217. charsetsMap.put(canonicalName("UTF-16"), Integer.valueOf(7));
  2218. // US-ASCII is the worst charset supported
  2219. charsetsMap.put(canonicalName("US-ASCII"), WORST_CHARSET_INDEX);
  2220. String defEncoding = DataTransferer.canonicalName
  2221. (DataTransferer.getDefaultTextCharset());
  2222. if (charsetsMap.get(defaultEncoding) == null) {
  2223. charsetsMap.put(defaultEncoding, DEFAULT_CHARSET_INDEX);
  2224. }
  2225. charsetsMap.put(UNSUPPORTED_CHARSET, UNSUPPORTED_CHARSET_INDEX);
  2226. charsets = Collections.unmodifiableMap(charsetsMap);
  2227. }
  2228. public CharsetComparator() {
  2229. this(SELECT_BEST);
  2230. }
  2231. public CharsetComparator(boolean order) {
  2232. super(order);
  2233. }
  2234. /**
  2235. * Compares two String objects. Returns a negative integer, zero,
  2236. * or a positive integer as the first charset is worse than, equal to,
  2237. * or better than the second.
  2238. *
  2239. * @param obj1 the first charset to be compared
  2240. * @param obj2 the second charset to be compared
  2241. * @return a negative integer, zero, or a positive integer as the
  2242. * first argument is worse, equal to, or better than the
  2243. * second.
  2244. * @throws ClassCastException if either of the arguments is not
  2245. * instance of String
  2246. * @throws NullPointerException if either of the arguments is
  2247. * <code>null</code>.
  2248. */
  2249. public int compare(Object obj1, Object obj2) {
  2250. String charset1 = null;
  2251. String charset2 = null;
  2252. if (order == SELECT_BEST) {
  2253. charset1 = (String)obj1;
  2254. charset2 = (String)obj2;
  2255. } else {
  2256. charset1 = (String)obj2;
  2257. charset2 = (String)obj1;
  2258. }
  2259. return compareCharsets(charset1, charset2);
  2260. }
  2261. /**
  2262. * Compares charsets. Returns a negative integer, zero, or a positive
  2263. * integer as the first charset is worse than, equal to, or better than
  2264. * the second.
  2265. * <p>
  2266. * Charsets are ordered according to the following rules:
  2267. * <ul>
  2268. * <li>All unsupported charsets are equal.
  2269. * <li>Any unsupported charset is worse than any supported charset.
  2270. * <li>Unicode charsets, such as "UTF-16", "UTF-8", "UTF-16BE" and
  2271. * "UTF-16LE", are considered best.
  2272. * <li>After them, platform default charset is selected.
  2273. * <li>"US-ASCII" is the worst of supported charsets.
  2274. * <li>For all other supported charsets, the lexicographically less
  2275. * one is considered the better.
  2276. * </ul>
  2277. *
  2278. * @param charset1 the first charset to be compared
  2279. * @param charset2 the second charset to be compared.
  2280. * @return a negative integer, zero, or a positive integer as the
  2281. * first argument is worse, equal to, or better than the
  2282. * second.
  2283. */
  2284. protected int compareCharsets(String charset1, String charset2) {
  2285. charset1 = getEncoding(charset1);
  2286. charset2 = getEncoding(charset2);
  2287. int comp = compareIndices(charsets, charset1, charset2,
  2288. OTHER_CHARSET_INDEX);
  2289. if (comp == 0) {
  2290. return charset2.compareTo(charset1);
  2291. }
  2292. return comp;
  2293. }
  2294. /**
  2295. * Returns encoding for the specified charset according to the
  2296. * following rules:
  2297. * <ul>
  2298. * <li>If the charset is <code>null</code>, then <code>null</code> will
  2299. * be returned.
  2300. * <li>Iff the charset specifies an encoding unsupported by this JRE,
  2301. * <code>UNSUPPORTED_CHARSET</code> will be returned.
  2302. * <li>If the charset specifies an alias name, the corresponding
  2303. * canonical name will be returned iff the charset is a known
  2304. * Unicode, ASCII, or default charset.
  2305. * </ul>
  2306. *
  2307. * @param charset the charset.
  2308. * @return an encoding for this charset.
  2309. */
  2310. protected static String getEncoding(String charset) {
  2311. if (charset == null) {
  2312. return null;
  2313. } else if (!DataTransferer.isEncodingSupported(charset)) {
  2314. return UNSUPPORTED_CHARSET;
  2315. } else {
  2316. // Only convert to canonical form if the charset is one
  2317. // of the charsets explicitly listed in the known charsets
  2318. // map. This will happen only for Unicode, ASCII, or default
  2319. // charsets.
  2320. String canonicalName = DataTransferer.canonicalName(charset);
  2321. return (charsets.containsKey(canonicalName))
  2322. ? canonicalName
  2323. : charset;
  2324. }
  2325. }
  2326. }
  2327. /**
  2328. * An IndexedComparator which compares two DataFlavors. For text flavors,
  2329. * the comparison follows the rules outlined in
  2330. * DataFlavor.selectBestTextFlavor. For non-text flavors, unknown
  2331. * application MIME types are preferred, followed by known
  2332. * application/x-java-* MIME types. Unknown application types are preferred
  2333. * because if the user provides his own data flavor, it will likely be the
  2334. * most descriptive one. For flavors which are otherwise equal, the
  2335. * flavors' string representation are compared in the alphabetical order.
  2336. */
  2337. public static class DataFlavorComparator extends IndexedComparator {
  2338. private final CharsetComparator charsetComparator;
  2339. private static final Map exactTypes;
  2340. private static final Map primaryTypes;
  2341. private static final Map nonTextRepresentations;
  2342. private static final Map textTypes;
  2343. private static final Map decodedTextRepresentations;
  2344. private static final Map encodedTextRepresentations;
  2345. private static final Integer UNKNOWN_OBJECT_LOSES =
  2346. Integer.valueOf(Integer.MIN_VALUE);
  2347. private static final Integer UNKNOWN_OBJECT_WINS =
  2348. Integer.valueOf(Integer.MAX_VALUE);
  2349. private static final Long UNKNOWN_OBJECT_LOSES_L =
  2350. Long.valueOf(Long.MIN_VALUE);
  2351. private static final Long UNKNOWN_OBJECT_WINS_L =
  2352. Long.valueOf(Long.MAX_VALUE);
  2353. static {
  2354. {
  2355. HashMap exactTypesMap = new HashMap(4, 1.0f);
  2356. // application/x-java-* MIME types
  2357. exactTypesMap.put("application/x-java-file-list",
  2358. Integer.valueOf(0));
  2359. exactTypesMap.put("application/x-java-serialized-object",
  2360. Integer.valueOf(1));
  2361. exactTypesMap.put("application/x-java-jvm-local-objectref",
  2362. Integer.valueOf(2));
  2363. exactTypesMap.put("application/x-java-remote-object",
  2364. Integer.valueOf(3));
  2365. exactTypes = Collections.unmodifiableMap(exactTypesMap);
  2366. }
  2367. {
  2368. HashMap primaryTypesMap = new HashMap(1, 1.0f);
  2369. primaryTypesMap.put("application", Integer.valueOf(0));
  2370. primaryTypes = Collections.unmodifiableMap(primaryTypesMap);
  2371. }
  2372. {
  2373. HashMap nonTextRepresentationsMap = new HashMap(3, 1.0f);
  2374. nonTextRepresentationsMap.put(java.io.InputStream.class,
  2375. Integer.valueOf(0));
  2376. nonTextRepresentationsMap.put(java.io.Serializable.class,
  2377. Integer.valueOf(1));
  2378. Class<?> remoteClass = RMI.remoteClass();
  2379. if (remoteClass != null) {
  2380. nonTextRepresentationsMap.put(remoteClass,
  2381. Integer.valueOf(2));
  2382. }
  2383. nonTextRepresentations =
  2384. Collections.unmodifiableMap(nonTextRepresentationsMap);
  2385. }
  2386. {
  2387. HashMap textTypesMap = new HashMap(16, 1.0f);
  2388. // plain text
  2389. textTypesMap.put("text/plain", Integer.valueOf(0));
  2390. // stringFlavor
  2391. textTypesMap.put("application/x-java-serialized-object",
  2392. Integer.valueOf(1));
  2393. // misc
  2394. textTypesMap.put("text/calendar", Integer.valueOf(2));
  2395. textTypesMap.put("text/css", Integer.valueOf(3));
  2396. textTypesMap.put("text/directory", Integer.valueOf(4));
  2397. textTypesMap.put("text/parityfec", Integer.valueOf(5));
  2398. textTypesMap.put("text/rfc822-headers", Integer.valueOf(6));
  2399. textTypesMap.put("text/t140", Integer.valueOf(7));
  2400. textTypesMap.put("text/tab-separated-values", Integer.valueOf(8));
  2401. textTypesMap.put("text/uri-list", Integer.valueOf(9));
  2402. // enriched
  2403. textTypesMap.put("text/richtext", Integer.valueOf(10));
  2404. textTypesMap.put("text/enriched", Integer.valueOf(11));
  2405. textTypesMap.put("text/rtf", Integer.valueOf(12));
  2406. // markup
  2407. textTypesMap.put("text/html", Integer.valueOf(13));
  2408. textTypesMap.put("text/xml", Integer.valueOf(14));
  2409. textTypesMap.put("text/sgml", Integer.valueOf(15));
  2410. textTypes = Collections.unmodifiableMap(textTypesMap);
  2411. }
  2412. {
  2413. HashMap decodedTextRepresentationsMap = new HashMap(4, 1.0f);
  2414. decodedTextRepresentationsMap.put
  2415. (char[].class, Integer.valueOf(0));
  2416. decodedTextRepresentationsMap.put
  2417. (java.nio.CharBuffer.class, Integer.valueOf(1));
  2418. decodedTextRepresentationsMap.put
  2419. (java.lang.String.class, Integer.valueOf(2));
  2420. decodedTextRepresentationsMap.put
  2421. (java.io.Reader.class, Integer.valueOf(3));
  2422. decodedTextRepresentations =
  2423. Collections.unmodifiableMap(decodedTextRepresentationsMap);
  2424. }
  2425. {
  2426. HashMap encodedTextRepresentationsMap = new HashMap(3, 1.0f);
  2427. encodedTextRepresentationsMap.put
  2428. (byte[].class, Integer.valueOf(0));
  2429. encodedTextRepresentationsMap.put
  2430. (java.nio.ByteBuffer.class, Integer.valueOf(1));
  2431. encodedTextRepresentationsMap.put
  2432. (java.io.InputStream.class, Integer.valueOf(2));
  2433. encodedTextRepresentations =
  2434. Collections.unmodifiableMap(encodedTextRepresentationsMap);
  2435. }
  2436. }
  2437. public DataFlavorComparator() {
  2438. this(SELECT_BEST);
  2439. }
  2440. public DataFlavorComparator(boolean order) {
  2441. super(order);
  2442. charsetComparator = new CharsetComparator(order);
  2443. }
  2444. public int compare(Object obj1, Object obj2) {
  2445. DataFlavor flavor1 = null;
  2446. DataFlavor flavor2 = null;
  2447. if (order == SELECT_BEST) {
  2448. flavor1 = (DataFlavor)obj1;
  2449. flavor2 = (DataFlavor)obj2;
  2450. } else {
  2451. flavor1 = (DataFlavor)obj2;
  2452. flavor2 = (DataFlavor)obj1;
  2453. }
  2454. if (flavor1.equals(flavor2)) {
  2455. return 0;
  2456. }
  2457. int comp = 0;
  2458. String primaryType1 = flavor1.getPrimaryType();
  2459. String subType1 = flavor1.getSubType();
  2460. String mimeType1 = primaryType1 + "/" + subType1;
  2461. Class class1 = flavor1.getRepresentationClass();
  2462. String primaryType2 = flavor2.getPrimaryType();
  2463. String subType2 = flavor2.getSubType();
  2464. String mimeType2 = primaryType2 + "/" + subType2;
  2465. Class class2 = flavor2.getRepresentationClass();
  2466. if (flavor1.isFlavorTextType() && flavor2.isFlavorTextType()) {
  2467. // First, compare MIME types
  2468. comp = compareIndices(textTypes, mimeType1, mimeType2,
  2469. UNKNOWN_OBJECT_LOSES);
  2470. if (comp != 0) {
  2471. return comp;
  2472. }
  2473. // Only need to test one flavor because they both have the
  2474. // same MIME type. Also don't need to worry about accidentally
  2475. // passing stringFlavor because either
  2476. // 1. Both flavors are stringFlavor, in which case the
  2477. // equality test at the top of the function succeeded.
  2478. // 2. Only one flavor is stringFlavor, in which case the MIME
  2479. // type comparison returned a non-zero value.
  2480. if (doesSubtypeSupportCharset(flavor1)) {
  2481. // Next, prefer the decoded text representations of Reader,
  2482. // String, CharBuffer, and [C, in that order.
  2483. comp = compareIndices(decodedTextRepresentations, class1,
  2484. class2, UNKNOWN_OBJECT_LOSES);
  2485. if (comp != 0) {
  2486. return comp;
  2487. }
  2488. // Next, compare charsets
  2489. comp = charsetComparator.compareCharsets
  2490. (DataTransferer.getTextCharset(flavor1),
  2491. DataTransferer.getTextCharset(flavor2));
  2492. if (comp != 0) {
  2493. return comp;
  2494. }
  2495. }
  2496. // Finally, prefer the encoded text representations of
  2497. // InputStream, ByteBuffer, and [B, in that order.
  2498. comp = compareIndices(encodedTextRepresentations, class1,
  2499. class2, UNKNOWN_OBJECT_LOSES);
  2500. if (comp != 0) {
  2501. return comp;
  2502. }
  2503. } else {
  2504. // First, prefer application types.
  2505. comp = compareIndices(primaryTypes, primaryType1, primaryType2,
  2506. UNKNOWN_OBJECT_LOSES);
  2507. if (comp != 0) {
  2508. return comp;
  2509. }
  2510. if (flavor1.isFlavorTextType()) {
  2511. return 1;
  2512. }
  2513. if (flavor2.isFlavorTextType()) {
  2514. return -1;
  2515. }
  2516. // Next, look for application/x-java-* types. Prefer unknown
  2517. // MIME types because if the user provides his own data flavor,
  2518. // it will likely be the most descriptive one.
  2519. comp = compareIndices(exactTypes, mimeType1, mimeType2,
  2520. UNKNOWN_OBJECT_WINS);
  2521. if (comp != 0) {
  2522. return comp;
  2523. }
  2524. // Finally, prefer the representation classes of Remote,
  2525. // Serializable, and InputStream, in that order.
  2526. comp = compareIndices(nonTextRepresentations, class1, class2,
  2527. UNKNOWN_OBJECT_LOSES);
  2528. if (comp != 0) {
  2529. return comp;
  2530. }
  2531. }
  2532. // The flavours are not equal but still not distinguishable.
  2533. // Compare String representations in alphabetical order
  2534. return flavor1.getMimeType().compareTo(flavor2.getMimeType());
  2535. }
  2536. }
  2537. /*
  2538. * Given the Map that maps objects to Integer indices and a boolean value,
  2539. * this Comparator imposes a direct or reverse order on set of objects.
  2540. * <p>
  2541. * If the specified boolean value is SELECT_BEST, the Comparator imposes the
  2542. * direct index-based order: an object A is greater than an object B if and
  2543. * only if the index of A is greater than the index of B. An object that
  2544. * doesn't have an associated index is less or equal than any other object.
  2545. * <p>
  2546. * If the specified boolean value is SELECT_WORST, the Comparator imposes the
  2547. * reverse index-based order: an object A is greater than an object B if and
  2548. * only if A is less than B with the direct index-based order.
  2549. */
  2550. public static class IndexOrderComparator extends IndexedComparator {
  2551. private final Map indexMap;
  2552. private static final Integer FALLBACK_INDEX =
  2553. Integer.valueOf(Integer.MIN_VALUE);
  2554. public IndexOrderComparator(Map indexMap) {
  2555. super(SELECT_BEST);
  2556. this.indexMap = indexMap;
  2557. }
  2558. public IndexOrderComparator(Map indexMap, boolean order) {
  2559. super(order);
  2560. this.indexMap = indexMap;
  2561. }
  2562. public int compare(Object obj1, Object obj2) {
  2563. if (order == SELECT_WORST) {
  2564. return -compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);
  2565. } else {
  2566. return compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);
  2567. }
  2568. }
  2569. }
  2570. /**
  2571. * A class that provides access to java.rmi.Remote and java.rmi.MarshalledObject
  2572. * without creating a static dependency.
  2573. */
  2574. private static class RMI {
  2575. private static final Class<?> remoteClass = getClass("java.rmi.Remote");
  2576. private static final Class<?> marshallObjectClass =
  2577. getClass("java.rmi.MarshalledObject");
  2578. private static final Constructor<?> marshallCtor =
  2579. getConstructor(marshallObjectClass, Object.class);
  2580. private static final Method marshallGet =
  2581. getMethod(marshallObjectClass, "get");
  2582. private static Class<?> getClass(String name) {
  2583. try {
  2584. return Class.forName(name, true, null);
  2585. } catch (ClassNotFoundException e) {
  2586. return null;
  2587. }
  2588. }
  2589. private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) {
  2590. try {
  2591. return (c == null) ? null : c.getDeclaredConstructor(types);
  2592. } catch (NoSuchMethodException x) {
  2593. throw new AssertionError(x);
  2594. }
  2595. }
  2596. private static Method getMethod(Class<?> c, String name, Class<?>... types) {
  2597. try {
  2598. return (c == null) ? null : c.getMethod(name, types);
  2599. } catch (NoSuchMethodException e) {
  2600. throw new AssertionError(e);
  2601. }
  2602. }
  2603. /**
  2604. * Returns {@code true} if the given class is java.rmi.Remote.
  2605. */
  2606. static boolean isRemote(Class<?> c) {
  2607. return (remoteClass == null) ? null : remoteClass.isAssignableFrom(c);
  2608. }
  2609. /**
  2610. * Returns java.rmi.Remote.class if RMI is present; otherwise {@code null}.
  2611. */
  2612. static Class<?> remoteClass() {
  2613. return remoteClass;
  2614. }
  2615. /**
  2616. * Returns a new MarshalledObject containing the serialized representation
  2617. * of the given object.
  2618. */
  2619. static Object newMarshalledObject(Object obj) throws IOException {
  2620. try {
  2621. return marshallCtor.newInstance(obj);
  2622. } catch (InstantiationException x) {
  2623. throw new AssertionError(x);
  2624. } catch (IllegalAccessException x) {
  2625. throw new AssertionError(x);
  2626. } catch (InvocationTargetException x) {
  2627. Throwable cause = x.getCause();
  2628. if (cause instanceof IOException)
  2629. throw (IOException)cause;
  2630. throw new AssertionError(x);
  2631. }
  2632. }
  2633. /**
  2634. * Returns a new copy of the contained marshalled object.
  2635. */
  2636. static Object getMarshalledObject(Object obj)
  2637. throws IOException, ClassNotFoundException
  2638. {
  2639. try {
  2640. return marshallGet.invoke(obj);
  2641. } catch (IllegalAccessException x) {
  2642. throw new AssertionError(x);
  2643. } catch (InvocationTargetException x) {
  2644. Throwable cause = x.getCause();
  2645. if (cause instanceof IOException)
  2646. throw (IOException)cause;
  2647. if (cause instanceof ClassNotFoundException)
  2648. throw (ClassNotFoundException)cause;
  2649. throw new AssertionError(x);
  2650. }
  2651. }
  2652. }
  2653. }