PageRenderTime 79ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

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

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