PageRenderTime 64ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://bitbucket.org/infinitekind/openjdk7u-jdk
Java | 3090 lines | 1944 code | 392 blank | 754 comment | 389 complexity | 47e806af89d2a7c61fd4c6bc2e7a83a5 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause-No-Nuclear-License-2014, LGPL-3.0
  1. /*
  2. * Copyright (c) 2000, 2008, 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 standardEncodings = load();
  151. private static SortedSet load() {
  152. final Comparator comparator =
  153. new CharsetComparator(IndexedComparator.SELECT_WORST);
  154. final SortedSet tempSet = new TreeSet(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.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 Iterator standardEncodings() {
  463. return StandardEncodingsHolder.standardEncodings.iterator();
  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 getFormatsForTransferable(Transferable contents,
  577. FlavorTable map) {
  578. DataFlavor[] flavors = contents.getTransferDataFlavors();
  579. if (flavors == null) {
  580. return new TreeMap();
  581. }
  582. return getFormatsForFlavors(flavors, map);
  583. }
  584. /**
  585. * Returns a Map whose keys are all of the possible formats into which data
  586. * in the specified DataFlavor can be translated. The value of each key
  587. * is the DataFlavor in which a Transferable's data should be requested
  588. * when converting to the format.
  589. * <p>
  590. * The map keys are sorted according to the native formats preference
  591. * order.
  592. */
  593. public SortedMap getFormatsForFlavor(DataFlavor flavor, FlavorTable map) {
  594. return getFormatsForFlavors(new DataFlavor[] { flavor },
  595. map);
  596. }
  597. /**
  598. * Returns a Map whose keys are all of the possible formats into which data
  599. * in the specified DataFlavors can be translated. The value of each key
  600. * is the DataFlavor in which the Transferable's data should be requested
  601. * when converting to the format.
  602. * <p>
  603. * The map keys are sorted according to the native formats preference
  604. * order.
  605. *
  606. * @param flavors the data flavors
  607. * @param map the FlavorTable which contains mappings between
  608. * DataFlavors and data formats
  609. * @throws NullPointerException if flavors or map is <code>null</code>
  610. */
  611. public SortedMap getFormatsForFlavors(DataFlavor[] flavors, FlavorTable map) {
  612. Map formatMap = new HashMap(flavors.length);
  613. Map textPlainMap = new HashMap(flavors.length);
  614. // Maps formats to indices that will be used to sort the formats
  615. // according to the preference order.
  616. // Larger index value corresponds to the more preferable format.
  617. Map indexMap = new HashMap(flavors.length);
  618. Map textPlainIndexMap = new HashMap(flavors.length);
  619. int currentIndex = 0;
  620. // Iterate backwards so that preferred DataFlavors are used over
  621. // other DataFlavors. (See javadoc for
  622. // Transferable.getTransferDataFlavors.)
  623. for (int i = flavors.length - 1; i >= 0; i--) {
  624. DataFlavor flavor = flavors[i];
  625. if (flavor == null) continue;
  626. // Don't explicitly test for String, since it is just a special
  627. // case of Serializable
  628. if (flavor.isFlavorTextType() ||
  629. flavor.isFlavorJavaFileListType() ||
  630. DataFlavor.imageFlavor.equals(flavor) ||
  631. flavor.isRepresentationClassSerializable() ||
  632. flavor.isRepresentationClassInputStream() ||
  633. flavor.isRepresentationClassRemote())
  634. {
  635. List natives = map.getNativesForFlavor(flavor);
  636. currentIndex += natives.size();
  637. for (Iterator iter = natives.iterator(); iter.hasNext(); ) {
  638. Long lFormat =
  639. getFormatForNativeAsLong((String)iter.next());
  640. Integer index = Integer.valueOf(currentIndex--);
  641. formatMap.put(lFormat, flavor);
  642. indexMap.put(lFormat, index);
  643. // SystemFlavorMap.getNativesForFlavor will return
  644. // text/plain natives for all text/*. While this is good
  645. // for a single text/* flavor, we would prefer that
  646. // text/plain native data come from a text/plain flavor.
  647. if (("text".equals(flavor.getPrimaryType()) &&
  648. "plain".equals(flavor.getSubType())) ||
  649. flavor.equals(DataFlavor.stringFlavor))
  650. {
  651. textPlainMap.put(lFormat, flavor);
  652. textPlainIndexMap.put(lFormat, index);
  653. }
  654. }
  655. currentIndex += natives.size();
  656. }
  657. }
  658. formatMap.putAll(textPlainMap);
  659. indexMap.putAll(textPlainIndexMap);
  660. // Sort the map keys according to the formats preference order.
  661. Comparator comparator =
  662. new IndexOrderComparator(indexMap, IndexedComparator.SELECT_WORST);
  663. SortedMap sortedMap = new TreeMap(comparator);
  664. sortedMap.putAll(formatMap);
  665. return sortedMap;
  666. }
  667. /**
  668. * Reduces the Map output for the root function to an array of the
  669. * Map's keys.
  670. */
  671. public long[] getFormatsForTransferableAsArray(Transferable contents,
  672. FlavorTable map) {
  673. return keysToLongArray(getFormatsForTransferable(contents, map));
  674. }
  675. public long[] getFormatsForFlavorAsArray(DataFlavor flavor,
  676. FlavorTable map) {
  677. return keysToLongArray(getFormatsForFlavor(flavor, map));
  678. }
  679. public long[] getFormatsForFlavorsAsArray(DataFlavor[] flavors,
  680. FlavorTable map) {
  681. return keysToLongArray(getFormatsForFlavors(flavors, map));
  682. }
  683. /**
  684. * Returns a Map whose keys are all of the possible DataFlavors into which
  685. * data in the specified format can be translated. The value of each key
  686. * is the format in which the Clipboard or dropped data should be requested
  687. * when converting to the DataFlavor.
  688. */
  689. public Map getFlavorsForFormat(long format, FlavorTable map) {
  690. return getFlavorsForFormats(new long[] { format }, map);
  691. }
  692. /**
  693. * Returns a Map whose keys are all of the possible DataFlavors into which
  694. * data in the specified formats can be translated. The value of each key
  695. * is the format in which the Clipboard or dropped data should be requested
  696. * when converting to the DataFlavor.
  697. */
  698. public Map getFlavorsForFormats(long[] formats, FlavorTable map) {
  699. Map flavorMap = new HashMap(formats.length);
  700. Set mappingSet = new HashSet(formats.length);
  701. Set flavorSet = new HashSet(formats.length);
  702. // First step: build flavorSet, mappingSet and initial flavorMap
  703. // flavorSet - the set of all the DataFlavors into which
  704. // data in the specified formats can be translated;
  705. // mappingSet - the set of all the mappings from the specified formats
  706. // into any DataFlavor;
  707. // flavorMap - after this step, this map maps each of the DataFlavors
  708. // from flavorSet to any of the specified formats.
  709. for (int i = 0; i < formats.length; i++) {
  710. long format = formats[i];
  711. String nat = getNativeForFormat(format);
  712. List flavors = map.getFlavorsForNative(nat);
  713. for (Iterator iter = flavors.iterator(); iter.hasNext(); ) {
  714. DataFlavor flavor = (DataFlavor)iter.next();
  715. // Don't explicitly test for String, since it is just a special
  716. // case of Serializable
  717. if (flavor.isFlavorTextType() ||
  718. flavor.isFlavorJavaFileListType() ||
  719. DataFlavor.imageFlavor.equals(flavor) ||
  720. flavor.isRepresentationClassSerializable() ||
  721. flavor.isRepresentationClassInputStream() ||
  722. flavor.isRepresentationClassRemote())
  723. {
  724. Long lFormat = Long.valueOf(format);
  725. Object mapping =
  726. DataTransferer.createMapping(lFormat, flavor);
  727. flavorMap.put(flavor, lFormat);
  728. mappingSet.add(mapping);
  729. flavorSet.add(flavor);
  730. }
  731. }
  732. }
  733. // Second step: for each DataFlavor try to figure out which of the
  734. // specified formats is the best to translate to this flavor.
  735. // Then map each flavor to the best format.
  736. // For the given flavor, FlavorTable indicates which native will
  737. // best reflect data in the specified flavor to the underlying native
  738. // platform. We assume that this native is the best to translate
  739. // to this flavor.
  740. // Note: FlavorTable allows one-way mappings, so we can occasionally
  741. // map a flavor to the format for which the corresponding
  742. // format-to-flavor mapping doesn't exist. For this reason we have built
  743. // a mappingSet of all format-to-flavor mappings for the specified formats
  744. // and check if the format-to-flavor mapping exists for the
  745. // (flavor,format) pair being added.
  746. for (Iterator flavorIter = flavorSet.iterator();
  747. flavorIter.hasNext(); ) {
  748. DataFlavor flavor = (DataFlavor)flavorIter.next();
  749. List natives = map.getNativesForFlavor(flavor);
  750. for (Iterator nativeIter = natives.iterator();
  751. nativeIter.hasNext(); ) {
  752. Long lFormat =
  753. getFormatForNativeAsLong((String)nativeIter.next());
  754. Object mapping = DataTransferer.createMapping(lFormat, flavor);
  755. if (mappingSet.contains(mapping)) {
  756. flavorMap.put(flavor, lFormat);
  757. break;
  758. }
  759. }
  760. }
  761. return flavorMap;
  762. }
  763. /**
  764. * Returns a Set of all DataFlavors for which
  765. * 1) a mapping from at least one of the specified formats exists in the
  766. * specified map and
  767. * 2) the data translation for this mapping can be performed by the data
  768. * transfer subsystem.
  769. *
  770. * @param formats the data formats
  771. * @param map the FlavorTable which contains mappings between
  772. * DataFlavors and data formats
  773. * @throws NullPointerException if formats or map is <code>null</code>
  774. */
  775. public Set getFlavorsForFormatsAsSet(long[] formats, FlavorTable map) {
  776. Set flavorSet = new HashSet(formats.length);
  777. for (int i = 0; i < formats.length; i++) {
  778. String nat = getNativeForFormat(formats[i]);
  779. List flavors = map.getFlavorsForNative(nat);
  780. for (Iterator iter = flavors.iterator(); iter.hasNext(); ) {
  781. DataFlavor flavor = (DataFlavor)iter.next();
  782. // Don't explicitly test for String, since it is just a special
  783. // case of Serializable
  784. if (flavor.isFlavorTextType() ||
  785. flavor.isFlavorJavaFileListType() ||
  786. DataFlavor.imageFlavor.equals(flavor) ||
  787. flavor.isRepresentationClassSerializable() ||
  788. flavor.isRepresentationClassInputStream() ||
  789. flavor.isRepresentationClassRemote())
  790. {
  791. flavorSet.add(flavor);
  792. }
  793. }
  794. }
  795. return flavorSet;
  796. }
  797. /**
  798. * Returns an array of all DataFlavors for which
  799. * 1) a mapping from the specified format exists in the specified map and
  800. * 2) the data translation for this mapping can be performed by the data
  801. * transfer subsystem.
  802. * The array will be sorted according to a
  803. * <code>DataFlavorComparator</code> created with the specified
  804. * map as an argument.
  805. *
  806. * @param format the data format
  807. * @param map the FlavorTable which contains mappings between
  808. * DataFlavors and data formats
  809. * @throws NullPointerException if map is <code>null</code>
  810. */
  811. public DataFlavor[] getFlavorsForFormatAsArray(long format,
  812. FlavorTable map) {
  813. return getFlavorsForFormatsAsArray(new long[] { format }, map);
  814. }
  815. /**
  816. * Returns an array of all DataFlavors for which
  817. * 1) a mapping from at least one of the specified formats exists in the
  818. * specified map and
  819. * 2) the data translation for this mapping can be performed by the data
  820. * transfer subsystem.
  821. * The array will be sorted according to a
  822. * <code>DataFlavorComparator</code> created with the specified
  823. * map as an argument.
  824. *
  825. * @param formats the data formats
  826. * @param map the FlavorTable which contains mappings between
  827. * DataFlavors and data formats
  828. * @throws NullPointerException if formats or map is <code>null</code>
  829. */
  830. public DataFlavor[] getFlavorsForFormatsAsArray(long[] formats,
  831. FlavorTable map) {
  832. // getFlavorsForFormatsAsSet() is less expensive than
  833. // getFlavorsForFormats().
  834. return setToSortedDataFlavorArray(getFlavorsForFormatsAsSet(formats, map));
  835. }
  836. /**
  837. * Returns an object that represents a mapping between the specified
  838. * key and value. <tt>null</tt> values and the <tt>null</tt> keys are
  839. * permitted. The internal representation of the mapping object is
  840. * irrelevant. The only requrement is that the two mapping objects are equal
  841. * if and only if their keys are equal and their values are equal.
  842. * More formally, the two mapping objects are equal if and only if
  843. * <tt>(value1 == null ? value2 == null : value1.equals(value2))
  844. * && (key1 == null ? key2 == null : key1.equals(key2))</tt>.
  845. */
  846. private static Object createMapping(Object key, Object value) {
  847. // NOTE: Should be updated to use AbstractMap.SimpleEntry as
  848. // soon as it is made public.
  849. return Arrays.asList(new Object[] { key, value });
  850. }
  851. /**
  852. * Looks-up or registers the String native with the native data transfer
  853. * system and returns a long format corresponding to that native.
  854. */
  855. protected abstract Long getFormatForNativeAsLong(String str);
  856. /**
  857. * Looks-up the String native corresponding to the specified long format in
  858. * the native data transfer system.
  859. */
  860. protected abstract String getNativeForFormat(long format);
  861. /* Contains common code for finding the best charset for
  862. * clipboard string encoding/decoding, basing on clipboard
  863. * format and localeTransferable(on decoding, if available)
  864. */
  865. private String getBestCharsetForTextFormat(Long lFormat,
  866. Transferable localeTransferable) throws IOException
  867. {
  868. String charset = null;
  869. if (localeTransferable != null &&
  870. isLocaleDependentTextFormat(lFormat) &&
  871. localeTransferable.isDataFlavorSupported(javaTextEncodingFlavor))
  872. {
  873. try {
  874. charset = new String(
  875. (byte[])localeTransferable.getTransferData(javaTextEncodingFlavor),
  876. "UTF-8"
  877. );
  878. } catch (UnsupportedFlavorException cannotHappen) {
  879. }
  880. } else {
  881. charset = getCharsetForTextFormat(lFormat);
  882. }
  883. if (charset == null) {
  884. // Only happens when we have a custom text type.
  885. charset = getDefaultTextCharset();
  886. }
  887. return charset;
  888. }
  889. /**
  890. * Translation function for converting string into
  891. * a byte array. Search-and-replace EOLN. Encode into the
  892. * target format. Append terminating NUL bytes.
  893. *
  894. * Java to Native string conversion
  895. */
  896. private byte[] translateTransferableString(String str,
  897. long format) throws IOException
  898. {
  899. Long lFormat = Long.valueOf(format);
  900. String charset = getBestCharsetForTextFormat(lFormat, null);
  901. // Search and replace EOLN. Note that if EOLN is "\n", then we
  902. // never added an entry to nativeEOLNs anyway, so we'll skip this
  903. // code altogether.
  904. // windows: "abc\nde"->"abc\r\nde"
  905. String eoln = (String)nativeEOLNs.get(lFormat);
  906. if (eoln != null) {
  907. int length = str.length();
  908. StringBuffer buffer =
  909. new StringBuffer(length * 2); // 2 is a heuristic
  910. for (int i = 0; i < length; i++) {
  911. // Fix for 4914613 - skip native EOLN
  912. if (str.startsWith(eoln, i)) {
  913. buffer.append(eoln);
  914. i += eoln.length() - 1;
  915. continue;
  916. }
  917. char c = str.charAt(i);
  918. if (c == '\n') {
  919. buffer.append(eoln);
  920. } else {
  921. buffer.append(c);
  922. }
  923. }
  924. str = buffer.toString();
  925. }
  926. // Encode text in target format.
  927. byte[] bytes = str.getBytes(charset);
  928. // Append terminating NUL bytes. Note that if terminators is 0,
  929. // the we never added an entry to nativeTerminators anyway, so
  930. // we'll skip code altogether.
  931. // "abcde" -> "abcde\0"
  932. Integer terminators = (Integer)nativeTerminators.get(lFormat);
  933. if (terminators != null) {
  934. int numTerminators = terminators.intValue();
  935. byte[] terminatedBytes =
  936. new byte[bytes.length + numTerminators];
  937. System.arraycopy(bytes, 0, terminatedBytes, 0, bytes.length);
  938. for (int i = bytes.length; i < terminatedBytes.length; i++) {
  939. terminatedBytes[i] = 0x0;
  940. }
  941. bytes = terminatedBytes;
  942. }
  943. return bytes;
  944. }
  945. /**
  946. * Translating either a byte array or an InputStream into an String.
  947. * Strip terminators and search-and-replace EOLN.
  948. *
  949. * Native to Java string conversion
  950. */
  951. private String translateBytesOrStreamToString(InputStream str, byte[] bytes,
  952. long format,
  953. Transferable localeTransferable)
  954. throws IOException
  955. {
  956. // A String holds all of its data in memory at one time, so
  957. // we can't avoid reading the entire InputStream at this point.
  958. if (bytes == null) {
  959. bytes = inputStreamToByteArray(str);
  960. }
  961. str.close();
  962. Long lFormat = Long.valueOf(format);
  963. String charset = getBestCharsetForTextFormat(lFormat, localeTransferable);
  964. // Locate terminating NUL bytes. Note that if terminators is 0,
  965. // the we never added an entry to nativeTerminators anyway, so
  966. // we'll skip code altogether.
  967. // In other words: we are doing char alignment here basing on suggestion
  968. // that count of zero-'terminators' is a number of bytes in one symbol
  969. // for selected charset (clipboard format). It is not complitly true for
  970. // multibyte coding like UTF-8, but helps understand the procedure.
  971. // "abcde\0" -> "abcde"
  972. String eoln = (String)nativeEOLNs.get(lFormat);
  973. Integer terminators = (Integer)nativeTerminators.get(lFormat);
  974. int count;
  975. if (terminators != null) {
  976. int numTerminators = terminators.intValue();
  977. search:
  978. for (count = 0; count < (bytes.length - numTerminators + 1); count += numTerminators) {
  979. for (int i = count; i < count + numTerminators; i++) {
  980. if (bytes[i] != 0x0) {
  981. continue search;
  982. }
  983. }
  984. // found terminators
  985. break search;
  986. }
  987. } else {
  988. count = bytes.length;
  989. }
  990. // Decode text to chars. Don't include any terminators.
  991. String converted = new String(bytes, 0, count, charset);
  992. // Search and replace EOLN. Note that if EOLN is "\n", then we
  993. // never added an entry to nativeEOLNs anyway, so we'll skip this
  994. // code altogether.
  995. // Count of NUL-terminators and EOLN coding are platform-specific and
  996. // loaded from flavormap.properties file
  997. // windows: "abc\r\nde" -> "abc\nde"
  998. if (eoln != null) {
  999. /* Fix for 4463560: replace EOLNs symbol-by-symbol instead
  1000. * of using buf.replace()
  1001. */
  1002. char[] buf = converted.toCharArray();
  1003. char[] eoln_arr = eoln.toCharArray();
  1004. converted = null;
  1005. int j = 0;
  1006. boolean match;
  1007. for (int i = 0; i < buf.length; ) {
  1008. // Catch last few bytes
  1009. if (i + eoln_arr.length > buf.length) {
  1010. buf[j++] = buf[i++];
  1011. continue;
  1012. }
  1013. match = true;
  1014. for (int k = 0, l = i; k < eoln_arr.length; k++, l++) {
  1015. if (eoln_arr[k] != buf[l]) {
  1016. match = false;
  1017. break;
  1018. }
  1019. }
  1020. if (match) {
  1021. buf[j++] = '\n';
  1022. i += eoln_arr.length;
  1023. } else {
  1024. buf[j++] = buf[i++];
  1025. }
  1026. }
  1027. converted = new String(buf, 0, j);
  1028. }
  1029. return converted;
  1030. }
  1031. /**
  1032. * Primary translation function for translating a Transferable into
  1033. * a byte array, given a source DataFlavor and target format.
  1034. */
  1035. public byte[] translateTransferable(Transferable contents,
  1036. DataFlavor flavor,
  1037. long format) throws IOException
  1038. {
  1039. // Obtain the transfer data in the source DataFlavor.
  1040. //
  1041. // Note that we special case DataFlavor.plainTextFlavor because
  1042. // StringSelection supports this flavor incorrectly -- instead of
  1043. // returning an InputStream as the DataFlavor representation class
  1044. // states, it returns a Reader. Instead of using this broken
  1045. // functionality, we request the data in stringFlavor (the other
  1046. // DataFlavor which StringSelection supports) and use the String
  1047. // translator.
  1048. Object obj;
  1049. boolean stringSelectionHack;
  1050. try {
  1051. obj = contents.getTransferData(flavor);
  1052. if (obj == null) {
  1053. return null;
  1054. }
  1055. if (flavor.equals(DataFlavor.plainTextFlavor) &&
  1056. !(obj instanceof InputStream))
  1057. {
  1058. obj = contents.getTransferData(DataFlavor.stringFlavor);
  1059. if (obj == null) {
  1060. return null;
  1061. }
  1062. stringSelectionHack = true;
  1063. } else {
  1064. stringSelectionHack = false;
  1065. }
  1066. } catch (UnsupportedFlavorException e) {
  1067. throw new IOException(e.getMessage());
  1068. }
  1069. // Source data is a String. Search-and-replace EOLN. Encode into the
  1070. // target format. Append terminating NUL bytes.
  1071. if (stringSelectionHack ||
  1072. (String.class.equals(flavor.getRepresentationClass()) &&
  1073. isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1074. String str = removeSuspectedData(flavor, contents, (String)obj);
  1075. return translateTransferableString(
  1076. str,
  1077. format);
  1078. // Source data is a Reader. Convert to a String and recur. In the
  1079. // future, we may want to rewrite this so that we encode on demand.
  1080. } else if (flavor.isRepresentationClassReader()) {
  1081. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1082. throw new IOException
  1083. ("cannot transfer non-text data as Reader");
  1084. }
  1085. Reader r = (Reader)obj;
  1086. StringBuffer buf = new StringBuffer();
  1087. int c;
  1088. while ((c = r.read()) != -1) {
  1089. buf.append((char)c);
  1090. }
  1091. r.close();
  1092. return translateTransferableString(
  1093. buf.toString(),
  1094. format);
  1095. // Source data is a CharBuffer. Convert to a String and recur.
  1096. } else if (flavor.isRepresentationClassCharBuffer()) {
  1097. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1098. throw new IOException
  1099. ("cannot transfer non-text data as CharBuffer");
  1100. }
  1101. CharBuffer buffer = (CharBuffer)obj;
  1102. int size = buffer.remaining();
  1103. char[] chars = new char[size];
  1104. buffer.get(chars, 0, size);
  1105. return translateTransferableString(
  1106. new String(chars),
  1107. format);
  1108. // Source data is a char array. Convert to a String and recur.
  1109. } else if (charArrayClass.equals(flavor.getRepresentationClass())) {
  1110. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1111. throw new IOException
  1112. ("cannot transfer non-text data as char array");
  1113. }
  1114. return translateTransferableString(
  1115. new String((char[])obj),
  1116. format);
  1117. // Source data is a ByteBuffer. For arbitrary flavors, simply return
  1118. // the array. For text flavors, decode back to a String and recur to
  1119. // reencode according to the requested format.
  1120. } else if (flavor.isRepresentationClassByteBuffer()) {
  1121. ByteBuffer buffer = (ByteBuffer)obj;
  1122. int size = buffer.remaining();
  1123. byte[] bytes = new byte[size];
  1124. buffer.get(bytes, 0, size);
  1125. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1126. String sourceEncoding = DataTransferer.getTextCharset(flavor);
  1127. return translateTransferableString(
  1128. new String(bytes, sourceEncoding),
  1129. format);
  1130. } else {
  1131. return bytes;
  1132. }
  1133. // Source data is a byte array. For arbitrary flavors, simply return
  1134. // the array. For text flavors, decode back to a String and recur to
  1135. // reencode according to the requested format.
  1136. } else if (byteArrayClass.equals(flavor.getRepresentationClass())) {
  1137. byte[] bytes = (byte[])obj;
  1138. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1139. String sourceEncoding = DataTransferer.getTextCharset(flavor);
  1140. return translateTransferableString(
  1141. new String(bytes, sourceEncoding),
  1142. format);
  1143. } else {
  1144. return bytes;
  1145. }
  1146. // Source data is Image
  1147. } else if (DataFlavor.imageFlavor.equals(flavor)) {
  1148. if (!isImageFormat(format)) {
  1149. throw new IOException("Data translation failed: " +
  1150. "not an image format");
  1151. }
  1152. Image image = (Image)obj;
  1153. byte[] bytes = imageToPlatformBytes(image, format);
  1154. if (bytes == null) {
  1155. throw new IOException("Data translation failed: " +
  1156. "cannot convert java image to native format");
  1157. }
  1158. return bytes;
  1159. }
  1160. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  1161. // Target data is a file list. Source data must be a
  1162. // java.util.List which contains java.io.File or String instances.
  1163. if (isFileFormat(format)) {
  1164. if (!DataFlavor.javaFileListFlavor.equals(flavor)) {
  1165. throw new IOException("data translation failed");
  1166. }
  1167. final List list = (List)obj;
  1168. final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents);
  1169. final ArrayList<String> fileList = castToFiles(list, userProtectionDomain);
  1170. bos = convertFileListToBytes(fileList);
  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. for (int i = 0; i < uriList.size(); i++) {
  1204. byte[] bytes = uriList.get(i).getBytes(targetCharset);
  1205. bos.write(bytes, 0, bytes.length);
  1206. bos.write(eoln, 0, eoln.length);
  1207. }
  1208. // Source data is an InputStream. For arbitrary flavors, just grab the
  1209. // bytes and dump them into a byte array. For text flavors, decode back
  1210. // to a String and recur to reencode according to the requested format.
  1211. } else if (flavor.isRepresentationClassInputStream()) {
  1212. InputStream is = (InputStream)obj;
  1213. boolean eof = false;
  1214. int avail = is.available();
  1215. byte[] tmp = new byte[avail > 8192 ? avail : 8192];
  1216. do {
  1217. int ret;
  1218. if (!(eof = (ret = is.read(tmp, 0, tmp.length)) == -1)) {
  1219. bos.write(tmp, 0, ret);
  1220. }
  1221. } while (!eof);
  1222. is.close();
  1223. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1224. byte[] bytes = bos.toByteArray();
  1225. bos.close();
  1226. String sourceEncoding = DataTransferer.getTextCharset(flavor);
  1227. return translateTransferableString(
  1228. new String(bytes, sourceEncoding),
  1229. format);
  1230. }
  1231. // Source data is an RMI object
  1232. } else if (flavor.isRepresentationClassRemote()) {
  1233. Object mo = RMI.newMarshalledObject(obj);
  1234. ObjectOutputStream oos = new ObjectOutputStream(bos);
  1235. oos.writeObject(mo);
  1236. oos.close();
  1237. // Source data is Serializable
  1238. } else if (flavor.isRepresentationClassSerializable()) {
  1239. ObjectOutputStream oos = new ObjectOutputStream(bos);
  1240. oos.writeObject(obj);
  1241. oos.close();
  1242. } else {
  1243. throw new IOException("data translation failed");
  1244. }
  1245. byte[] ret = bos.toByteArray();
  1246. bos.close();
  1247. return ret;
  1248. }
  1249. protected abstract ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList) throws IOException;
  1250. private String removeSuspectedData(DataFlavor flavor, final Transferable contents, final String str)
  1251. throws IOException
  1252. {
  1253. if (null == System.getSecurityManager()
  1254. || !flavor.isMimeTypeEqual("text/uri-list"))
  1255. {
  1256. return str;
  1257. }
  1258. String ret_val = "";
  1259. final ProtectionDomain userProtectionDomain = getUserProtectionDomain(contents);
  1260. try {
  1261. ret_val = (String) AccessController.doPrivileged(new PrivilegedExceptionAction() {
  1262. public Object run() {
  1263. StringBuffer allowedFiles = new StringBuffer(str.length());
  1264. String [] uriArray = str.split("(\\s)+");
  1265. for (String fileName : uriArray)
  1266. {
  1267. File file = new File(fileName);
  1268. if (file.exists() &&
  1269. !(isFileInWebstartedCache(file) ||
  1270. isForbiddenToRead(file, userProtectionDomain)))
  1271. {
  1272. if (0 != allowedFiles.length())
  1273. {
  1274. allowedFiles.append("\\r\\n");
  1275. }
  1276. allowedFiles.append(fileName);
  1277. }
  1278. }
  1279. return allowedFiles.toString();
  1280. }
  1281. });
  1282. } catch (PrivilegedActionException pae) {
  1283. throw new IOException(pae.getMessage(), pae);
  1284. }
  1285. return ret_val;
  1286. }
  1287. private static ProtectionDomain getUserProtectionDomain(Transferable contents) {
  1288. return contents.getClass().getProtectionDomain();
  1289. }
  1290. private boolean isForbiddenToRead (File file, ProtectionDomain protectionDomain)
  1291. {
  1292. if (null == protectionDomain) {
  1293. return false;
  1294. }
  1295. try {
  1296. FilePermission filePermission =
  1297. new FilePermission(file.getCanonicalPath(), "read, delete");
  1298. if (protectionDomain.implies(filePermission)) {
  1299. return false;
  1300. }
  1301. } catch (IOException e) {}
  1302. return true;
  1303. }
  1304. private ArrayList<String> castToFiles(final List files,
  1305. final ProtectionDomain userProtectionDomain) throws IOException
  1306. {
  1307. final ArrayList<String> fileList = new ArrayList<String>();
  1308. try {
  1309. AccessController.doPrivileged(new PrivilegedExceptionAction() {
  1310. public Object run() throws IOException {
  1311. for (Object fileObject : files)
  1312. {
  1313. File file = castToFile(fileObject);
  1314. if (file != null &&
  1315. (null == System.getSecurityManager() ||
  1316. !(isFileInWebstartedCache(file) ||
  1317. isForbiddenToRead(file, userProtectionDomain))))
  1318. {
  1319. fileList.add(file.getCanonicalPath());
  1320. }
  1321. }
  1322. return null;
  1323. }
  1324. });
  1325. } catch (PrivilegedActionException pae) {
  1326. throw new IOException(pae.getMessage());
  1327. }
  1328. return fileList;
  1329. }
  1330. // It is important do not use user's successors
  1331. // of File class.
  1332. private File castToFile(Object fileObject) throws IOException {
  1333. String filePath = null;
  1334. if (fileObject instanceof File) {
  1335. filePath = ((File)fileObject).getCanonicalPath();
  1336. } else if (fileObject instanceof String) {
  1337. filePath = (String) fileObject;
  1338. } else {
  1339. return null;
  1340. }
  1341. return new File(filePath);
  1342. }
  1343. private final static String[] DEPLOYMENT_CACHE_PROPERTIES = {
  1344. "deployment.system.cachedir",
  1345. "deployment.user.cachedir",
  1346. "deployment.javaws.cachedir",
  1347. "deployment.javapi.cachedir"
  1348. };
  1349. private final static ArrayList <File> deploymentCacheDirectoryList =
  1350. new ArrayList<File>();
  1351. private static boolean isFileInWebstartedCache(File f) {
  1352. if (deploymentCacheDirectoryList.isEmpty()) {
  1353. for (String cacheDirectoryProperty : DEPLOYMENT_CACHE_PROPERTIES) {
  1354. String cacheDirectoryPath = System.getProperty(cacheDirectoryProperty);
  1355. if (cacheDirectoryPath != null) {
  1356. try {
  1357. File cacheDirectory = (new File(cacheDirectoryPath)).getCanonicalFile();
  1358. if (cacheDirectory != null) {
  1359. deploymentCacheDirectoryList.add(cacheDirectory);
  1360. }
  1361. } catch (IOException ioe) {}
  1362. }
  1363. }
  1364. }
  1365. for (File deploymentCacheDirectory : deploymentCacheDirectoryList) {
  1366. for (File dir = f; dir != null; dir = dir.getParentFile()) {
  1367. if (dir.equals(deploymentCacheDirectory)) {
  1368. return true;
  1369. }
  1370. }
  1371. }
  1372. return false;
  1373. }
  1374. public Object translateBytes(byte[] bytes, DataFlavor flavor,
  1375. long format, Transferable localeTransferable)
  1376. throws IOException
  1377. {
  1378. return translateBytesOrStream(null, bytes, flavor, format,
  1379. localeTransferable);
  1380. }
  1381. public Object translateStream(InputStream str, DataFlavor flavor,
  1382. long format, Transferable localeTransferable)
  1383. throws IOException
  1384. {
  1385. return translateBytesOrStream(str, null, flavor, format,
  1386. localeTransferable);
  1387. }
  1388. /**
  1389. * Primary translation function for translating either a byte array or
  1390. * an InputStream into an Object, given a source format and a target
  1391. * DataFlavor.
  1392. *
  1393. * One of str/bytes is non-null; the other is null.
  1394. * The conversion from byte[] to InputStream is cheap, so do that
  1395. * immediately if necessary. The opposite conversion is expensive,
  1396. * so avoid it if possible.
  1397. */
  1398. protected Object translateBytesOrStream(InputStream str, byte[] bytes,
  1399. DataFlavor flavor, long format,
  1400. Transferable localeTransferable)
  1401. throws IOException
  1402. {
  1403. if (str == null) {
  1404. str = new ByteArrayInputStream(bytes);
  1405. }
  1406. // Source data is a file list. Use the dragQueryFile native function to
  1407. // do most of the decoding. Then wrap File objects around the String
  1408. // filenames and return a List.
  1409. if (isFileFormat(format)) {
  1410. if (!DataFlavor.javaFileListFlavor.equals(flavor)) {
  1411. throw new IOException("data translation failed");
  1412. }
  1413. if (bytes == null) {
  1414. bytes = inputStreamToByteArray(str);
  1415. }
  1416. String[] filenames = dragQueryFile(bytes);
  1417. if (filenames == null) {
  1418. str.close();
  1419. return null;
  1420. }
  1421. // Convert the strings to File objects
  1422. File[] files = new File[filenames.length];
  1423. for (int i = 0; i < filenames.length; i++) {
  1424. files[i] = new File(filenames[i]);
  1425. }
  1426. str.close();
  1427. // Turn the list of Files into a List and return
  1428. return Arrays.asList(files);
  1429. // Source data is a URI list. Convert to DataFlavor.javaFileListFlavor
  1430. // where possible.
  1431. } else if (isURIListFormat(format) && DataFlavor.javaFileListFlavor.equals(flavor)) {
  1432. try {
  1433. URI uris[] = dragQueryURIs(str, bytes, format, localeTransferable);
  1434. if (uris == null) {
  1435. return null;
  1436. }
  1437. ArrayList files = new ArrayList();
  1438. for (URI uri : uris) {
  1439. try {
  1440. files.add(new File(uri));
  1441. } catch (IllegalArgumentException illegalArg) {
  1442. // When converting from URIs to less generic files,
  1443. // common practice (Wine, SWT) seems to be to
  1444. // silently drop the URIs that aren't local files.
  1445. }
  1446. }
  1447. return files;
  1448. } finally {
  1449. str.close();
  1450. }
  1451. // Target data is a String. Strip terminating NUL bytes. Decode bytes
  1452. // into characters. Search-and-replace EOLN.
  1453. } else if (String.class.equals(flavor.getRepresentationClass()) &&
  1454. isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1455. return translateBytesOrStreamToString(
  1456. str, bytes,
  1457. format, localeTransferable);
  1458. // Special hack to maintain backwards-compatibility with the brokenness
  1459. // of StringSelection. Return a StringReader instead of an InputStream.
  1460. // Recur to obtain String and encapsulate.
  1461. } else if (DataFlavor.plainTextFlavor.equals(flavor)) {
  1462. return new StringReader(translateBytesOrStreamToString(
  1463. str, bytes,
  1464. format, localeTransferable));
  1465. // Target data is an InputStream. For arbitrary flavors, just return
  1466. // the raw bytes. For text flavors, decode to strip terminators and
  1467. // search-and-replace EOLN, then reencode according to the requested
  1468. // flavor.
  1469. } else if (flavor.isRepresentationClassInputStream()) {
  1470. return translateBytesOrStreamToInputStream(str, flavor, format,
  1471. localeTransferable);
  1472. // Target data is a Reader. Obtain data in InputStream format, encoded
  1473. // as "Unicode" (utf-16be). Then use an InputStreamReader to decode
  1474. // back to chars on demand.
  1475. } else if (flavor.isRepresentationClassReader()) {
  1476. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1477. throw new IOException
  1478. ("cannot transfer non-text data as Reader");
  1479. }
  1480. InputStream is = (InputStream)
  1481. translateBytesOrStreamToInputStream
  1482. (str, DataFlavor.plainTextFlavor, format,
  1483. localeTransferable);
  1484. String unicode =
  1485. DataTransferer.getTextCharset(DataFlavor.plainTextFlavor);
  1486. Reader reader = new InputStreamReader(is, unicode);
  1487. return constructFlavoredObject(reader, flavor, Reader.class);
  1488. // Target data is a CharBuffer. Recur to obtain String and wrap.
  1489. } else if (flavor.isRepresentationClassCharBuffer()) {
  1490. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1491. throw new IOException
  1492. ("cannot transfer non-text data as CharBuffer");
  1493. }
  1494. CharBuffer buffer = CharBuffer.wrap(translateBytesOrStreamToString(
  1495. str, bytes,
  1496. format, localeTransferable));
  1497. return constructFlavoredObject(buffer, flavor, CharBuffer.class);
  1498. // Target data is a char array. Recur to obtain String and convert to
  1499. // char array.
  1500. } else if (charArrayClass.equals(flavor.getRepresentationClass())) {
  1501. if (!(isFlavorCharsetTextType(flavor) && isTextFormat(format))) {
  1502. throw new IOException
  1503. ("cannot transfer non-text data as char array");
  1504. }
  1505. return translateBytesOrStreamToString(
  1506. str, bytes,
  1507. format, localeTransferable).toCharArray();
  1508. // Target data is a ByteBuffer. For arbitrary flavors, just return
  1509. // the raw bytes. For text flavors, convert to a String to strip
  1510. // terminators and search-and-replace EOLN, then reencode according to
  1511. // the requested flavor.
  1512. } else if (flavor.isRepresentationClassByteBuffer()) {
  1513. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1514. bytes = translateBytesOrStreamToString(
  1515. str, bytes,
  1516. format, localeTransferable
  1517. ).getBytes(
  1518. DataTransferer.getTextCharset(flavor)
  1519. );
  1520. } else {
  1521. if (bytes == null) {
  1522. bytes = inputStreamToByteArray(str);
  1523. }
  1524. }
  1525. ByteBuffer buffer = ByteBuffer.wrap(bytes);
  1526. return constructFlavoredObject(buffer, flavor, ByteBuffer.class);
  1527. // Target data is a byte array. For arbitrary flavors, just return
  1528. // the raw bytes. For text flavors, convert to a String to strip
  1529. // terminators and search-and-replace EOLN, then reencode according to
  1530. // the requested flavor.
  1531. } else if (byteArrayClass.equals(flavor.getRepresentationClass())) {
  1532. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1533. return translateBytesOrStreamToString(
  1534. str, bytes,
  1535. format, localeTransferable
  1536. ).getBytes(
  1537. DataTransferer.getTextCharset(flavor)
  1538. );
  1539. } else {
  1540. return (bytes != null) ? bytes : inputStreamToByteArray(str);
  1541. }
  1542. // Target data is an RMI object
  1543. } else if (flavor.isRepresentationClassRemote()) {
  1544. try {
  1545. byte[] ba = inputStreamToByteArray(str);
  1546. ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(ba));
  1547. Object ret = RMI.getMarshalledObject(ois.readObject());
  1548. ois.close();
  1549. str.close();
  1550. return ret;
  1551. } catch (Exception e) {
  1552. throw new IOException(e.getMessage());
  1553. }
  1554. // Target data is Serializable
  1555. } else if (flavor.isRepresentationClassSerializable()) {
  1556. try {
  1557. byte[] ba = inputStreamToByteArray(str);
  1558. ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(ba));
  1559. Object ret = ois.readObject();
  1560. ois.close();
  1561. str.close();
  1562. return ret;
  1563. } catch (Exception e) {
  1564. throw new IOException(e.getMessage());
  1565. }
  1566. // Target data is Image
  1567. } else if (DataFlavor.imageFlavor.equals(flavor)) {
  1568. if (!isImageFormat(format)) {
  1569. throw new IOException("data translation failed");
  1570. }
  1571. Image image = platformImageBytesOrStreamToImage(str, bytes, format);
  1572. str.close();
  1573. return image;
  1574. }
  1575. throw new IOException("data translation failed");
  1576. }
  1577. /**
  1578. * For arbitrary flavors, just use the raw InputStream. For text flavors,
  1579. * ReencodingInputStream will decode and reencode the InputStream on demand
  1580. * so that we can strip terminators and search-and-replace EOLN.
  1581. */
  1582. private Object translateBytesOrStreamToInputStream
  1583. (InputStream str, DataFlavor flavor, long format,
  1584. Transferable localeTransferable) throws IOException
  1585. {
  1586. if (isFlavorCharsetTextType(flavor) && isTextFormat(format)) {
  1587. str = new ReencodingInputStream
  1588. (str, format, DataTransferer.getTextCharset(flavor),
  1589. localeTransferable);
  1590. }
  1591. return constructFlavoredObject(str, flavor, InputStream.class);
  1592. }
  1593. /**
  1594. * We support representations which are exactly of the specified Class,
  1595. * and also arbitrary Objects which have a constructor which takes an
  1596. * instance of the Class as its sole parameter.
  1597. */
  1598. private Object constructFlavoredObject(Object arg, DataFlavor flavor,
  1599. Class clazz)
  1600. throws IOException
  1601. {
  1602. final Class dfrc = flavor.getRepresentationClass();
  1603. if (clazz.equals(dfrc)) {
  1604. return arg; // simple case
  1605. } else {
  1606. Constructor[] constructors = null;
  1607. try {
  1608. constructors = (Constructor[])
  1609. AccessController.doPrivileged(new PrivilegedAction() {
  1610. public Object run() {
  1611. return dfrc.getConstructors();
  1612. }
  1613. });
  1614. } catch (SecurityException se) {
  1615. throw new IOException(se.getMessage());
  1616. }
  1617. Constructor constructor = null;
  1618. for (int j = 0; j < constructors.length; j++) {
  1619. if (!Modifier.isPublic(constructors[j].getModifiers())) {
  1620. continue;
  1621. }
  1622. Class[] ptypes = constructors[j].getParameterTypes();
  1623. if (ptypes != null && ptypes.length == 1 &&
  1624. clazz.equals(ptypes[0])) {
  1625. constructor = constructors[j];
  1626. break;
  1627. }
  1628. }
  1629. if (constructor == null) {
  1630. throw new IOException("can't find <init>(L"+ clazz +
  1631. ";)V for class: " + dfrc.getName());
  1632. }
  1633. try {
  1634. return constructor.newInstance(new Object[] { arg } );
  1635. } catch (Exception e) {
  1636. throw new IOException(e.getMessage());
  1637. }
  1638. }
  1639. }
  1640. /**
  1641. * Used for decoding and reencoding an InputStream on demand so that we
  1642. * can strip NUL terminators and perform EOLN search-and-replace.
  1643. */
  1644. public class ReencodingInputStream extends InputStream {
  1645. protected BufferedReader wrapped;
  1646. protected final char[] in = new char[1];
  1647. protected byte[] out;
  1648. protected CharsetEncoder encoder;
  1649. protected CharBuffer inBuf;
  1650. protected ByteBuffer outBuf;
  1651. protected char[] eoln;
  1652. protected int numTerminators;
  1653. protected boolean eos;
  1654. protected int index, limit;
  1655. public ReencodingInputStream(InputStream bytestream, long format,
  1656. String targetEncoding,
  1657. Transferable localeTransferable)
  1658. throws IOException
  1659. {
  1660. Long lFormat = Long.valueOf(format);
  1661. String sourceEncoding = null;
  1662. if (isLocaleDependentTextFormat(format) &&
  1663. localeTransferable != null &&
  1664. localeTransferable.
  1665. isDataFlavorSupported(javaTextEncodingFlavor))
  1666. {
  1667. try {
  1668. sourceEncoding = new String((byte[])localeTransferable.
  1669. getTransferData(javaTextEncodingFlavor),
  1670. "UTF-8");
  1671. } catch (UnsupportedFlavorException cannotHappen) {
  1672. }
  1673. } else {
  1674. sourceEncoding = getCharsetForTextFormat(lFormat);
  1675. }
  1676. if (sourceEncoding == null) {
  1677. // Only happens when we have a custom text type.
  1678. sourceEncoding = getDefaultTextCharset();
  1679. }
  1680. wrapped = new BufferedReader
  1681. (new InputStreamReader(bytestream, sourceEncoding));
  1682. if (targetEncoding == null) {
  1683. // Throw NullPointerException for compatibility with the former
  1684. // call to sun.io.CharToByteConverter.getConverter(null)
  1685. // (Charset.forName(null) throws unspecified IllegalArgumentException
  1686. // now; see 6228568)
  1687. throw new NullPointerException("null target encoding");
  1688. }
  1689. try {
  1690. encoder = Charset.forName(targetEncoding).newEncoder();
  1691. out = new byte[(int)(encoder.maxBytesPerChar() + 0.5)];
  1692. inBuf = CharBuffer.wrap(in);
  1693. outBuf = ByteBuffer.wrap(out);
  1694. } catch (IllegalCharsetNameException e) {
  1695. throw new IOException(e.toString());
  1696. } catch (UnsupportedCharsetException e) {
  1697. throw new IOException(e.toString());
  1698. } catch (UnsupportedOperationException e) {
  1699. throw new IOException(e.toString());
  1700. }
  1701. String sEoln = (String)nativeEOLNs.get(lFormat);
  1702. if (sEoln != null) {
  1703. eoln = sEoln.toCharArray();
  1704. }
  1705. // A hope and a prayer that this works generically. This will
  1706. // definitely work on Win32.
  1707. Integer terminators = (Integer)nativeTerminators.get(lFormat);
  1708. if (terminators != null) {
  1709. numTerminators = terminators.intValue();
  1710. }
  1711. }
  1712. public int read() throws IOException {
  1713. if (eos) {
  1714. return -1;
  1715. }
  1716. if (index >= limit) {
  1717. int c = wrapped.read();
  1718. if (c == -1) { // -1 is EOS
  1719. eos = true;
  1720. return -1;
  1721. }
  1722. // "c == 0" is not quite correct, but good enough on Windows.
  1723. if (numTerminators > 0 && c == 0) {
  1724. eos = true;
  1725. return -1;
  1726. } else if (eoln != null && matchCharArray(eoln, c)) {
  1727. c = '\n' & 0xFFFF;
  1728. }
  1729. in[0] = (char)c;
  1730. inBuf.rewind();
  1731. outBuf.rewind();
  1732. encoder.encode(inBuf, outBuf, false);
  1733. outBuf.flip();
  1734. limit = outBuf.limit();
  1735. index = 0;
  1736. return read();
  1737. } else {
  1738. return out[index++] & 0xFF;
  1739. }
  1740. }
  1741. public int available() throws IOException {
  1742. return ((eos) ? 0 : (limit - index));
  1743. }
  1744. public void close() throws IOException {
  1745. wrapped.close();
  1746. }
  1747. /**
  1748. * Checks to see if the next array.length characters in wrapped
  1749. * match array. The first character is provided as c. Subsequent
  1750. * characters are read from wrapped itself. When this method returns,
  1751. * the wrapped index may be different from what it was when this
  1752. * method was called.
  1753. */
  1754. private boolean matchCharArray(char[] array, int c)
  1755. throws IOException
  1756. {
  1757. wrapped.mark(array.length); // BufferedReader supports mark
  1758. int count = 0;
  1759. if ((char)c == array[0]) {
  1760. for (count = 1; count < array.length; count++) {
  1761. c = wrapped.read();
  1762. if (c == -1 || ((char)c) != array[count]) {
  1763. break;
  1764. }
  1765. }
  1766. }
  1767. if (count == array.length) {
  1768. return true;
  1769. } else {
  1770. wrapped.reset();
  1771. return false;
  1772. }
  1773. }
  1774. }
  1775. /**
  1776. * Decodes a byte array into a set of String filenames.
  1777. */
  1778. protected abstract String[] dragQueryFile(byte[] bytes);
  1779. /**
  1780. * Decodes URIs from either a byte array or a stream.
  1781. */
  1782. protected URI[] dragQueryURIs(InputStream stream,
  1783. byte[] bytes,
  1784. long format,
  1785. Transferable localeTransferable)
  1786. throws IOException
  1787. {
  1788. throw new IOException(
  1789. new UnsupportedOperationException("not implemented on this platform"));
  1790. }
  1791. /**
  1792. * Translates either a byte array or an input stream which contain
  1793. * platform-specific image data in the given format into an Image.
  1794. */
  1795. protected abstract Image platformImageBytesOrStreamToImage(InputStream str,
  1796. byte[] bytes,
  1797. long format)
  1798. throws IOException;
  1799. /**
  1800. * Translates either a byte array or an input stream which contain
  1801. * an image data in the given standard format into an Image.
  1802. *
  1803. * @param mimeType image MIME type, such as: image/png, image/jpeg, image/gif
  1804. */
  1805. protected Image standardImageBytesOrStreamToImage(InputStream inputStream,
  1806. byte[] bytes,
  1807. String mimeType)
  1808. throws IOException {
  1809. if (inputStream == null) {
  1810. inputStream = new ByteArrayInputStream(bytes);
  1811. }
  1812. Iterator readerIterator = ImageIO.getImageReadersByMIMEType(mimeType);
  1813. if (!readerIterator.hasNext()) {
  1814. throw new IOException("No registered service provider can decode " +
  1815. " an image from " + mimeType);
  1816. }
  1817. IOException ioe = null;
  1818. while (readerIterator.hasNext()) {
  1819. ImageReader imageReader = (ImageReader)readerIterator.next();
  1820. try {
  1821. ImageInputStream imageInputStream =
  1822. ImageIO.createImageInputStream(inputStream);
  1823. try {
  1824. ImageReadParam param = imageReader.getDefaultReadParam();
  1825. imageReader.setInput(imageInputStream, true, true);
  1826. BufferedImage bufferedImage =
  1827. imageReader.read(imageReader.getMinIndex(), param);
  1828. if (bufferedImage != null) {
  1829. return bufferedImage;
  1830. }
  1831. } finally {
  1832. imageInputStream.close();
  1833. imageReader.dispose();
  1834. }
  1835. } catch (IOException e) {
  1836. ioe = e;
  1837. continue;
  1838. }
  1839. }
  1840. if (ioe == null) {
  1841. ioe = new IOException("Registered service providers failed to decode"
  1842. + " an image from " + mimeType);
  1843. }
  1844. throw ioe;
  1845. }
  1846. /**
  1847. * Translates a Java Image into a byte array which contains platform-
  1848. * specific image data in the given format.
  1849. */
  1850. protected abstract byte[] imageToPlatformBytes(Image image, long format)
  1851. throws IOException;
  1852. /**
  1853. * Translates a Java Image into a byte array which contains
  1854. * an image data in the given standard format.
  1855. *
  1856. * @param mimeType image MIME type, such as: image/png, image/jpeg
  1857. */
  1858. protected byte[] imageToStandardBytes(Image image, String mimeType)
  1859. throws IOException {
  1860. IOException originalIOE = null;
  1861. Iterator writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
  1862. if (!writerIterator.hasNext()) {
  1863. throw new IOException("No registered service provider can encode " +
  1864. " an image to " + mimeType);
  1865. }
  1866. if (image instanceof RenderedImage) {
  1867. // Try to encode the original image.
  1868. try {
  1869. return imageToStandardBytesImpl((RenderedImage)image, mimeType);
  1870. } catch (IOException ioe) {
  1871. originalIOE = ioe;
  1872. }
  1873. }
  1874. // Retry with a BufferedImage.
  1875. int width = 0;
  1876. int height = 0;
  1877. if (image instanceof ToolkitImage) {
  1878. ImageRepresentation ir = ((ToolkitImage)image).getImageRep();
  1879. ir.reconstruct(ImageObserver.ALLBITS);
  1880. width = ir.getWidth();
  1881. height = ir.getHeight();
  1882. } else {
  1883. width = image.getWidth(null);
  1884. height = image.getHeight(null);
  1885. }
  1886. ColorModel model = ColorModel.getRGBdefault();
  1887. WritableRaster raster =
  1888. model.createCompatibleWritableRaster(width, height);
  1889. BufferedImage bufferedImage =
  1890. new BufferedImage(model, raster, model.isAlphaPremultiplied(),
  1891. null);
  1892. Graphics g = bufferedImage.getGraphics();
  1893. try {
  1894. g.drawImage(image, 0, 0, width, height, null);
  1895. } finally {
  1896. g.dispose();
  1897. }
  1898. try {
  1899. return imageToStandardBytesImpl(bufferedImage, mimeType);
  1900. } catch (IOException ioe) {
  1901. if (originalIOE != null) {
  1902. throw originalIOE;
  1903. } else {
  1904. throw ioe;
  1905. }
  1906. }
  1907. }
  1908. protected byte[] imageToStandardBytesImpl(RenderedImage renderedImage,
  1909. String mimeType)
  1910. throws IOException {
  1911. Iterator writerIterator = ImageIO.getImageWritersByMIMEType(mimeType);
  1912. ImageTypeSpecifier typeSpecifier =
  1913. new ImageTypeSpecifier(renderedImage);
  1914. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  1915. IOException ioe = null;
  1916. while (writerIterator.hasNext()) {
  1917. ImageWriter imageWriter = (ImageWriter)writerIterator.next();
  1918. ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider();
  1919. if (!writerSpi.canEncodeImage(typeSpecifier)) {
  1920. continue;
  1921. }
  1922. try {
  1923. ImageOutputStream imageOutputStream =
  1924. ImageIO.createImageOutputStream(baos);
  1925. try {
  1926. imageWriter.setOutput(imageOutputStream);
  1927. imageWriter.write(renderedImage);
  1928. imageOutputStream.flush();
  1929. } finally {
  1930. imageOutputStream.close();
  1931. }
  1932. } catch (IOException e) {
  1933. imageWriter.dispose();
  1934. baos.reset();
  1935. ioe = e;
  1936. continue;
  1937. }
  1938. imageWriter.dispose();
  1939. baos.close();
  1940. return baos.toByteArray();
  1941. }
  1942. baos.close();
  1943. if (ioe == null) {
  1944. ioe = new IOException("Registered service providers failed to encode "
  1945. + renderedImage + " to " + mimeType);
  1946. }
  1947. throw ioe;
  1948. }
  1949. /**
  1950. * Concatenates the data represented by two objects. Objects can be either
  1951. * byte arrays or instances of <code>InputStream</code>. If both arguments
  1952. * are byte arrays byte array will be returned. Otherwise an
  1953. * <code>InputStream</code> will be returned.
  1954. * <p>
  1955. * Currently is only called from native code to prepend palette data to
  1956. * platform-specific image data during image transfer on Win32.
  1957. *
  1958. * @param obj1 the first object to be concatenated.
  1959. * @param obj2 the second object to be concatenated.
  1960. * @return a byte array or an <code>InputStream</code> which represents
  1961. * a logical concatenation of the two arguments.
  1962. * @throws NullPointerException is either of the arguments is
  1963. * <code>null</code>
  1964. * @throws ClassCastException is either of the arguments is
  1965. * neither byte array nor an instance of <code>InputStream</code>.
  1966. */
  1967. private Object concatData(Object obj1, Object obj2) {
  1968. InputStream str1 = null;
  1969. InputStream str2 = null;
  1970. if (obj1 instanceof byte[]) {
  1971. byte[] arr1 = (byte[])obj1;
  1972. if (obj2 instanceof byte[]) {
  1973. byte[] arr2 = (byte[])obj2;
  1974. byte[] ret = new byte[arr1.length + arr2.length];
  1975. System.arraycopy(arr1, 0, ret, 0, arr1.length);
  1976. System.arraycopy(arr2, 0, ret, arr1.length, arr2.length);
  1977. return ret;
  1978. } else {
  1979. str1 = new ByteArrayInputStream(arr1);
  1980. str2 = (InputStream)obj2;
  1981. }
  1982. } else {
  1983. str1 = (InputStream)obj1;
  1984. if (obj2 instanceof byte[]) {
  1985. str2 = new ByteArrayInputStream((byte[])obj2);
  1986. } else {
  1987. str2 = (InputStream)obj2;
  1988. }
  1989. }
  1990. return new SequenceInputStream(str1, str2);
  1991. }
  1992. public byte[] convertData(final Object source,
  1993. final Transferable contents,
  1994. final long format,
  1995. final Map formatMap,
  1996. final boolean isToolkitThread)
  1997. throws IOException
  1998. {
  1999. byte[] ret = null;
  2000. /*
  2001. * If the current thread is the Toolkit thread we should post a
  2002. * Runnable to the event dispatch thread associated with source Object,
  2003. * since translateTransferable() calls Transferable.getTransferData()
  2004. * that may contain client code.
  2005. */
  2006. if (isToolkitThread) try {
  2007. final Stack stack = new Stack();
  2008. final Runnable dataConverter = new Runnable() {
  2009. // Guard against multiple executions.
  2010. private boolean done = false;
  2011. public void run() {
  2012. if (done) {
  2013. return;
  2014. }
  2015. byte[] data = null;
  2016. try {
  2017. DataFlavor flavor = (DataFlavor)formatMap.get(Long.valueOf(format));
  2018. if (flavor != null) {
  2019. data = translateTransferable(contents, flavor, format);
  2020. }
  2021. } catch (Exception e) {
  2022. e.printStackTrace();
  2023. data = null;
  2024. }
  2025. try {
  2026. getToolkitThreadBlockedHandler().lock();
  2027. stack.push(data);
  2028. getToolkitThreadBlockedHandler().exit();
  2029. } finally {
  2030. getToolkitThreadBlockedHandler().unlock();
  2031. done = true;
  2032. }
  2033. }
  2034. };
  2035. final AppContext appContext = SunToolkit.targetToAppContext(source);
  2036. getToolkitThreadBlockedHandler().lock();
  2037. if (appContext != null) {
  2038. appContext.put(DATA_CONVERTER_KEY, dataConverter);
  2039. }
  2040. SunToolkit.executeOnEventHandlerThread(source, dataConverter);
  2041. while (stack.empty()) {
  2042. getToolkitThreadBlockedHandler().enter();
  2043. }
  2044. if (appContext != null) {
  2045. appContext.remove(DATA_CONVERTER_KEY);
  2046. }
  2047. ret = (byte[])stack.pop();
  2048. } finally {
  2049. getToolkitThreadBlockedHandler().unlock();
  2050. } else {
  2051. DataFlavor flavor = (DataFlavor)
  2052. formatMap.get(Long.valueOf(format));
  2053. if (flavor != null) {
  2054. ret = translateTransferable(contents, flavor, format);
  2055. }
  2056. }
  2057. return ret;
  2058. }
  2059. public void processDataConversionRequests() {
  2060. if (EventQueue.isDispatchThread()) {
  2061. AppContext appContext = AppContext.getAppContext();
  2062. getToolkitThreadBlockedHandler().lock();
  2063. try {
  2064. Runnable dataConverter =
  2065. (Runnable)appContext.get(DATA_CONVERTER_KEY);
  2066. if (dataConverter != null) {
  2067. dataConverter.run();
  2068. appContext.remove(DATA_CONVERTER_KEY);
  2069. }
  2070. } finally {
  2071. getToolkitThreadBlockedHandler().unlock();
  2072. }
  2073. }
  2074. }
  2075. public abstract ToolkitThreadBlockedHandler
  2076. getToolkitThreadBlockedHandler();
  2077. /**
  2078. * Helper function to reduce a Map with Long keys to a long array.
  2079. * <p>
  2080. * The map keys are sorted according to the native formats preference
  2081. * order.
  2082. */
  2083. public static long[] keysToLongArray(SortedMap map) {
  2084. Set keySet = map.keySet();
  2085. long[] retval = new long[keySet.size()];
  2086. int i = 0;
  2087. for (Iterator iter = keySet.iterator(); iter.hasNext(); i++) {
  2088. retval[i] = ((Long)iter.next()).longValue();
  2089. }
  2090. return retval;
  2091. }
  2092. /**
  2093. * Helper function to reduce a Map with DataFlavor keys to a DataFlavor
  2094. * array. The array will be sorted according to
  2095. * <code>DataFlavorComparator</code>.
  2096. */
  2097. public static DataFlavor[] keysToDataFlavorArray(Map map) {
  2098. return setToSortedDataFlavorArray(map.keySet(), map);
  2099. }
  2100. /**
  2101. * Helper function to convert a Set of DataFlavors to a sorted array.
  2102. * The array will be sorted according to <code>DataFlavorComparator</code>.
  2103. */
  2104. public static DataFlavor[] setToSortedDataFlavorArray(Set flavorsSet) {
  2105. DataFlavor[] flavors = new DataFlavor[flavorsSet.size()];
  2106. flavorsSet.toArray(flavors);
  2107. final Comparator comparator =
  2108. new DataFlavorComparator(IndexedComparator.SELECT_WORST);
  2109. Arrays.sort(flavors, comparator);
  2110. return flavors;
  2111. }
  2112. /**
  2113. * Helper function to convert a Set of DataFlavors to a sorted array.
  2114. * The array will be sorted according to a
  2115. * <code>DataFlavorComparator</code> created with the specified
  2116. * flavor-to-native map as an argument.
  2117. */
  2118. public static DataFlavor[] setToSortedDataFlavorArray
  2119. (Set flavorsSet, Map flavorToNativeMap)
  2120. {
  2121. DataFlavor[] flavors = new DataFlavor[flavorsSet.size()];
  2122. flavorsSet.toArray(flavors);
  2123. Comparator comparator =
  2124. new DataFlavorComparator(flavorToNativeMap,
  2125. IndexedComparator.SELECT_WORST);
  2126. Arrays.sort(flavors, comparator);
  2127. return flavors;
  2128. }
  2129. /**
  2130. * Helper function to convert an InputStream to a byte[] array.
  2131. */
  2132. protected static byte[] inputStreamToByteArray(InputStream str)
  2133. throws IOException
  2134. {
  2135. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  2136. int len = 0;
  2137. byte[] buf = new byte[8192];
  2138. while ((len = str.read(buf)) != -1) {
  2139. baos.write(buf, 0, len);
  2140. }
  2141. return baos.toByteArray();
  2142. }
  2143. /**
  2144. * Returns platform-specific mappings for the specified native.
  2145. * If there are no platform-specific mappings for this native, the method
  2146. * returns an empty <code>List</code>.
  2147. */
  2148. public List getPlatformMappingsForNative(String nat) {
  2149. return new ArrayList();
  2150. }
  2151. /**
  2152. * Returns platform-specific mappings for the specified flavor.
  2153. * If there are no platform-specific mappings for this flavor, the method
  2154. * returns an empty <code>List</code>.
  2155. */
  2156. public List getPlatformMappingsForFlavor(DataFlavor df) {
  2157. return new ArrayList();
  2158. }
  2159. /**
  2160. * A Comparator which includes a helper function for comparing two Objects
  2161. * which are likely to be keys in the specified Map.
  2162. */
  2163. public abstract static class IndexedComparator implements Comparator {
  2164. /**
  2165. * The best Object (e.g., DataFlavor) will be the last in sequence.
  2166. */
  2167. public static final boolean SELECT_BEST = true;
  2168. /**
  2169. * The best Object (e.g., DataFlavor) will be the first in sequence.
  2170. */
  2171. public static final boolean SELECT_WORST = false;
  2172. protected final boolean order;
  2173. public IndexedComparator() {
  2174. this(SELECT_BEST);
  2175. }
  2176. public IndexedComparator(boolean order) {
  2177. this.order = order;
  2178. }
  2179. /**
  2180. * Helper method to compare two objects by their Integer indices in the
  2181. * given map. If the map doesn't contain an entry for either of the
  2182. * objects, the fallback index will be used for the object instead.
  2183. *
  2184. * @param indexMap the map which maps objects into Integer indexes.
  2185. * @param obj1 the first object to be compared.
  2186. * @param obj2 the second object to be compared.
  2187. * @param fallbackIndex the Integer to be used as a fallback index.
  2188. * @return a negative integer, zero, or a positive integer as the
  2189. * first object is mapped to a less, equal to, or greater
  2190. * index than the second.
  2191. */
  2192. protected static int compareIndices(Map indexMap,
  2193. Object obj1, Object obj2,
  2194. Integer fallbackIndex) {
  2195. Integer index1 = (Integer)indexMap.get(obj1);
  2196. Integer index2 = (Integer)indexMap.get(obj2);
  2197. if (index1 == null) {
  2198. index1 = fallbackIndex;
  2199. }
  2200. if (index2 == null) {
  2201. index2 = fallbackIndex;
  2202. }
  2203. return index1.compareTo(index2);
  2204. }
  2205. /**
  2206. * Helper method to compare two objects by their Long indices in the
  2207. * given map. If the map doesn't contain an entry for either of the
  2208. * objects, the fallback index will be used for the object instead.
  2209. *
  2210. * @param indexMap the map which maps objects into Long indexes.
  2211. * @param obj1 the first object to be compared.
  2212. * @param obj2 the second object to be compared.
  2213. * @param fallbackIndex the Long to be used as a fallback index.
  2214. * @return a negative integer, zero, or a positive integer as the
  2215. * first object is mapped to a less, equal to, or greater
  2216. * index than the second.
  2217. */
  2218. protected static int compareLongs(Map indexMap,
  2219. Object obj1, Object obj2,
  2220. Long fallbackIndex) {
  2221. Long index1 = (Long)indexMap.get(obj1);
  2222. Long index2 = (Long)indexMap.get(obj2);
  2223. if (index1 == null) {
  2224. index1 = fallbackIndex;
  2225. }
  2226. if (index2 == null) {
  2227. index2 = fallbackIndex;
  2228. }
  2229. return index1.compareTo(index2);
  2230. }
  2231. }
  2232. /**
  2233. * An IndexedComparator which compares two String charsets. The comparison
  2234. * follows the rules outlined in DataFlavor.selectBestTextFlavor. In order
  2235. * to ensure that non-Unicode, non-ASCII, non-default charsets are sorted
  2236. * in alphabetical order, charsets are not automatically converted to their
  2237. * canonical forms.
  2238. */
  2239. public static class CharsetComparator extends IndexedComparator {
  2240. private static final Map charsets;
  2241. private static String defaultEncoding;
  2242. private static final Integer DEFAULT_CHARSET_INDEX = Integer.valueOf(2);
  2243. private static final Integer OTHER_CHARSET_INDEX = Integer.valueOf(1);
  2244. private static final Integer WORST_CHARSET_INDEX = Integer.valueOf(0);
  2245. private static final Integer UNSUPPORTED_CHARSET_INDEX =
  2246. Integer.valueOf(Integer.MIN_VALUE);
  2247. private static final String UNSUPPORTED_CHARSET = "UNSUPPORTED";
  2248. static {
  2249. HashMap charsetsMap = new HashMap(8, 1.0f);
  2250. // we prefer Unicode charsets
  2251. charsetsMap.put(canonicalName("UTF-16LE"), Integer.valueOf(4));
  2252. charsetsMap.put(canonicalName("UTF-16BE"), Integer.valueOf(5));
  2253. charsetsMap.put(canonicalName("UTF-8"), Integer.valueOf(6));
  2254. charsetsMap.put(canonicalName("UTF-16"), Integer.valueOf(7));
  2255. // US-ASCII is the worst charset supported
  2256. charsetsMap.put(canonicalName("US-ASCII"), WORST_CHARSET_INDEX);
  2257. String defEncoding = DataTransferer.canonicalName
  2258. (DataTransferer.getDefaultTextCharset());
  2259. if (charsetsMap.get(defaultEncoding) == null) {
  2260. charsetsMap.put(defaultEncoding, DEFAULT_CHARSET_INDEX);
  2261. }
  2262. charsetsMap.put(UNSUPPORTED_CHARSET, UNSUPPORTED_CHARSET_INDEX);
  2263. charsets = Collections.unmodifiableMap(charsetsMap);
  2264. }
  2265. public CharsetComparator() {
  2266. this(SELECT_BEST);
  2267. }
  2268. public CharsetComparator(boolean order) {
  2269. super(order);
  2270. }
  2271. /**
  2272. * Compares two String objects. Returns a negative integer, zero,
  2273. * or a positive integer as the first charset is worse than, equal to,
  2274. * or better than the second.
  2275. *
  2276. * @param obj1 the first charset to be compared
  2277. * @param obj2 the second charset to be compared
  2278. * @return a negative integer, zero, or a positive integer as the
  2279. * first argument is worse, equal to, or better than the
  2280. * second.
  2281. * @throws ClassCastException if either of the arguments is not
  2282. * instance of String
  2283. * @throws NullPointerException if either of the arguments is
  2284. * <code>null</code>.
  2285. */
  2286. public int compare(Object obj1, Object obj2) {
  2287. String charset1 = null;
  2288. String charset2 = null;
  2289. if (order == SELECT_BEST) {
  2290. charset1 = (String)obj1;
  2291. charset2 = (String)obj2;
  2292. } else {
  2293. charset1 = (String)obj2;
  2294. charset2 = (String)obj1;
  2295. }
  2296. return compareCharsets(charset1, charset2);
  2297. }
  2298. /**
  2299. * Compares charsets. Returns a negative integer, zero, or a positive
  2300. * integer as the first charset is worse than, equal to, or better than
  2301. * the second.
  2302. * <p>
  2303. * Charsets are ordered according to the following rules:
  2304. * <ul>
  2305. * <li>All unsupported charsets are equal.
  2306. * <li>Any unsupported charset is worse than any supported charset.
  2307. * <li>Unicode charsets, such as "UTF-16", "UTF-8", "UTF-16BE" and
  2308. * "UTF-16LE", are considered best.
  2309. * <li>After them, platform default charset is selected.
  2310. * <li>"US-ASCII" is the worst of supported charsets.
  2311. * <li>For all other supported charsets, the lexicographically less
  2312. * one is considered the better.
  2313. * </ul>
  2314. *
  2315. * @param charset1 the first charset to be compared
  2316. * @param charset2 the second charset to be compared.
  2317. * @return a negative integer, zero, or a positive integer as the
  2318. * first argument is worse, equal to, or better than the
  2319. * second.
  2320. */
  2321. protected int compareCharsets(String charset1, String charset2) {
  2322. charset1 = getEncoding(charset1);
  2323. charset2 = getEncoding(charset2);
  2324. int comp = compareIndices(charsets, charset1, charset2,
  2325. OTHER_CHARSET_INDEX);
  2326. if (comp == 0) {
  2327. return charset2.compareTo(charset1);
  2328. }
  2329. return comp;
  2330. }
  2331. /**
  2332. * Returns encoding for the specified charset according to the
  2333. * following rules:
  2334. * <ul>
  2335. * <li>If the charset is <code>null</code>, then <code>null</code> will
  2336. * be returned.
  2337. * <li>Iff the charset specifies an encoding unsupported by this JRE,
  2338. * <code>UNSUPPORTED_CHARSET</code> will be returned.
  2339. * <li>If the charset specifies an alias name, the corresponding
  2340. * canonical name will be returned iff the charset is a known
  2341. * Unicode, ASCII, or default charset.
  2342. * </ul>
  2343. *
  2344. * @param charset the charset.
  2345. * @return an encoding for this charset.
  2346. */
  2347. protected static String getEncoding(String charset) {
  2348. if (charset == null) {
  2349. return null;
  2350. } else if (!DataTransferer.isEncodingSupported(charset)) {
  2351. return UNSUPPORTED_CHARSET;
  2352. } else {
  2353. // Only convert to canonical form if the charset is one
  2354. // of the charsets explicitly listed in the known charsets
  2355. // map. This will happen only for Unicode, ASCII, or default
  2356. // charsets.
  2357. String canonicalName = DataTransferer.canonicalName(charset);
  2358. return (charsets.containsKey(canonicalName))
  2359. ? canonicalName
  2360. : charset;
  2361. }
  2362. }
  2363. }
  2364. /**
  2365. * An IndexedComparator which compares two DataFlavors. For text flavors,
  2366. * the comparison follows the rules outlined in
  2367. * DataFlavor.selectBestTextFlavor. For non-text flavors, unknown
  2368. * application MIME types are preferred, followed by known
  2369. * application/x-java-* MIME types. Unknown application types are preferred
  2370. * because if the user provides his own data flavor, it will likely be the
  2371. * most descriptive one. For flavors which are otherwise equal, the
  2372. * flavors' native formats are compared, with greater long values
  2373. * taking precedence.
  2374. */
  2375. public static class DataFlavorComparator extends IndexedComparator {
  2376. protected final Map flavorToFormatMap;
  2377. private final CharsetComparator charsetComparator;
  2378. private static final Map exactTypes;
  2379. private static final Map primaryTypes;
  2380. private static final Map nonTextRepresentations;
  2381. private static final Map textTypes;
  2382. private static final Map decodedTextRepresentations;
  2383. private static final Map encodedTextRepresentations;
  2384. private static final Integer UNKNOWN_OBJECT_LOSES =
  2385. Integer.valueOf(Integer.MIN_VALUE);
  2386. private static final Integer UNKNOWN_OBJECT_WINS =
  2387. Integer.valueOf(Integer.MAX_VALUE);
  2388. private static final Long UNKNOWN_OBJECT_LOSES_L =
  2389. Long.valueOf(Long.MIN_VALUE);
  2390. private static final Long UNKNOWN_OBJECT_WINS_L =
  2391. Long.valueOf(Long.MAX_VALUE);
  2392. static {
  2393. {
  2394. HashMap exactTypesMap = new HashMap(4, 1.0f);
  2395. // application/x-java-* MIME types
  2396. exactTypesMap.put("application/x-java-file-list",
  2397. Integer.valueOf(0));
  2398. exactTypesMap.put("application/x-java-serialized-object",
  2399. Integer.valueOf(1));
  2400. exactTypesMap.put("application/x-java-jvm-local-objectref",
  2401. Integer.valueOf(2));
  2402. exactTypesMap.put("application/x-java-remote-object",
  2403. Integer.valueOf(3));
  2404. exactTypes = Collections.unmodifiableMap(exactTypesMap);
  2405. }
  2406. {
  2407. HashMap primaryTypesMap = new HashMap(1, 1.0f);
  2408. primaryTypesMap.put("application", Integer.valueOf(0));
  2409. primaryTypes = Collections.unmodifiableMap(primaryTypesMap);
  2410. }
  2411. {
  2412. HashMap nonTextRepresentationsMap = new HashMap(3, 1.0f);
  2413. nonTextRepresentationsMap.put(java.io.InputStream.class,
  2414. Integer.valueOf(0));
  2415. nonTextRepresentationsMap.put(java.io.Serializable.class,
  2416. Integer.valueOf(1));
  2417. Class<?> remoteClass = RMI.remoteClass();
  2418. if (remoteClass != null) {
  2419. nonTextRepresentationsMap.put(remoteClass,
  2420. Integer.valueOf(2));
  2421. }
  2422. nonTextRepresentations =
  2423. Collections.unmodifiableMap(nonTextRepresentationsMap);
  2424. }
  2425. {
  2426. HashMap textTypesMap = new HashMap(16, 1.0f);
  2427. // plain text
  2428. textTypesMap.put("text/plain", Integer.valueOf(0));
  2429. // stringFlavor
  2430. textTypesMap.put("application/x-java-serialized-object",
  2431. Integer.valueOf(1));
  2432. // misc
  2433. textTypesMap.put("text/calendar", Integer.valueOf(2));
  2434. textTypesMap.put("text/css", Integer.valueOf(3));
  2435. textTypesMap.put("text/directory", Integer.valueOf(4));
  2436. textTypesMap.put("text/parityfec", Integer.valueOf(5));
  2437. textTypesMap.put("text/rfc822-headers", Integer.valueOf(6));
  2438. textTypesMap.put("text/t140", Integer.valueOf(7));
  2439. textTypesMap.put("text/tab-separated-values", Integer.valueOf(8));
  2440. textTypesMap.put("text/uri-list", Integer.valueOf(9));
  2441. // enriched
  2442. textTypesMap.put("text/richtext", Integer.valueOf(10));
  2443. textTypesMap.put("text/enriched", Integer.valueOf(11));
  2444. textTypesMap.put("text/rtf", Integer.valueOf(12));
  2445. // markup
  2446. textTypesMap.put("text/html", Integer.valueOf(13));
  2447. textTypesMap.put("text/xml", Integer.valueOf(14));
  2448. textTypesMap.put("text/sgml", Integer.valueOf(15));
  2449. textTypes = Collections.unmodifiableMap(textTypesMap);
  2450. }
  2451. {
  2452. HashMap decodedTextRepresentationsMap = new HashMap(4, 1.0f);
  2453. decodedTextRepresentationsMap.put
  2454. (DataTransferer.charArrayClass, Integer.valueOf(0));
  2455. decodedTextRepresentationsMap.put
  2456. (java.nio.CharBuffer.class, Integer.valueOf(1));
  2457. decodedTextRepresentationsMap.put
  2458. (java.lang.String.class, Integer.valueOf(2));
  2459. decodedTextRepresentationsMap.put
  2460. (java.io.Reader.class, Integer.valueOf(3));
  2461. decodedTextRepresentations =
  2462. Collections.unmodifiableMap(decodedTextRepresentationsMap);
  2463. }
  2464. {
  2465. HashMap encodedTextRepresentationsMap = new HashMap(3, 1.0f);
  2466. encodedTextRepresentationsMap.put
  2467. (DataTransferer.byteArrayClass, Integer.valueOf(0));
  2468. encodedTextRepresentationsMap.put
  2469. (java.nio.ByteBuffer.class, Integer.valueOf(1));
  2470. encodedTextRepresentationsMap.put
  2471. (java.io.InputStream.class, Integer.valueOf(2));
  2472. encodedTextRepresentations =
  2473. Collections.unmodifiableMap(encodedTextRepresentationsMap);
  2474. }
  2475. }
  2476. public DataFlavorComparator() {
  2477. this(SELECT_BEST);
  2478. }
  2479. public DataFlavorComparator(boolean order) {
  2480. super(order);
  2481. charsetComparator = new CharsetComparator(order);
  2482. flavorToFormatMap = Collections.EMPTY_MAP;
  2483. }
  2484. public DataFlavorComparator(Map map) {
  2485. this(map, SELECT_BEST);
  2486. }
  2487. public DataFlavorComparator(Map map, boolean order) {
  2488. super(order);
  2489. charsetComparator = new CharsetComparator(order);
  2490. HashMap hashMap = new HashMap(map.size());
  2491. hashMap.putAll(map);
  2492. flavorToFormatMap = Collections.unmodifiableMap(hashMap);
  2493. }
  2494. public int compare(Object obj1, Object obj2) {
  2495. DataFlavor flavor1 = null;
  2496. DataFlavor flavor2 = null;
  2497. if (order == SELECT_BEST) {
  2498. flavor1 = (DataFlavor)obj1;
  2499. flavor2 = (DataFlavor)obj2;
  2500. } else {
  2501. flavor1 = (DataFlavor)obj2;
  2502. flavor2 = (DataFlavor)obj1;
  2503. }
  2504. if (flavor1.equals(flavor2)) {
  2505. return 0;
  2506. }
  2507. int comp = 0;
  2508. String primaryType1 = flavor1.getPrimaryType();
  2509. String subType1 = flavor1.getSubType();
  2510. String mimeType1 = primaryType1 + "/" + subType1;
  2511. Class class1 = flavor1.getRepresentationClass();
  2512. String primaryType2 = flavor2.getPrimaryType();
  2513. String subType2 = flavor2.getSubType();
  2514. String mimeType2 = primaryType2 + "/" + subType2;
  2515. Class class2 = flavor2.getRepresentationClass();
  2516. if (flavor1.isFlavorTextType() && flavor2.isFlavorTextType()) {
  2517. // First, compare MIME types
  2518. comp = compareIndices(textTypes, mimeType1, mimeType2,
  2519. UNKNOWN_OBJECT_LOSES);
  2520. if (comp != 0) {
  2521. return comp;
  2522. }
  2523. // Only need to test one flavor because they both have the
  2524. // same MIME type. Also don't need to worry about accidentally
  2525. // passing stringFlavor because either
  2526. // 1. Both flavors are stringFlavor, in which case the
  2527. // equality test at the top of the function succeeded.
  2528. // 2. Only one flavor is stringFlavor, in which case the MIME
  2529. // type comparison returned a non-zero value.
  2530. if (doesSubtypeSupportCharset(flavor1)) {
  2531. // Next, prefer the decoded text representations of Reader,
  2532. // String, CharBuffer, and [C, in that order.
  2533. comp = compareIndices(decodedTextRepresentations, class1,
  2534. class2, UNKNOWN_OBJECT_LOSES);
  2535. if (comp != 0) {
  2536. return comp;
  2537. }
  2538. // Next, compare charsets
  2539. comp = charsetComparator.compareCharsets
  2540. (DataTransferer.getTextCharset(flavor1),
  2541. DataTransferer.getTextCharset(flavor2));
  2542. if (comp != 0) {
  2543. return comp;
  2544. }
  2545. }
  2546. // Finally, prefer the encoded text representations of
  2547. // InputStream, ByteBuffer, and [B, in that order.
  2548. comp = compareIndices(encodedTextRepresentations, class1,
  2549. class2, UNKNOWN_OBJECT_LOSES);
  2550. if (comp != 0) {
  2551. return comp;
  2552. }
  2553. } else {
  2554. // First, prefer application types.
  2555. comp = compareIndices(primaryTypes, primaryType1, primaryType2,
  2556. UNKNOWN_OBJECT_LOSES);
  2557. if (comp != 0) {
  2558. return comp;
  2559. }
  2560. // Next, look for application/x-java-* types. Prefer unknown
  2561. // MIME types because if the user provides his own data flavor,
  2562. // it will likely be the most descriptive one.
  2563. comp = compareIndices(exactTypes, mimeType1, mimeType2,
  2564. UNKNOWN_OBJECT_WINS);
  2565. if (comp != 0) {
  2566. return comp;
  2567. }
  2568. // Finally, prefer the representation classes of Remote,
  2569. // Serializable, and InputStream, in that order.
  2570. comp = compareIndices(nonTextRepresentations, class1, class2,
  2571. UNKNOWN_OBJECT_LOSES);
  2572. if (comp != 0) {
  2573. return comp;
  2574. }
  2575. }
  2576. // As a last resort, take the DataFlavor with the greater integer
  2577. // format.
  2578. return compareLongs(flavorToFormatMap, flavor1, flavor2,
  2579. UNKNOWN_OBJECT_LOSES_L);
  2580. }
  2581. }
  2582. /*
  2583. * Given the Map that maps objects to Integer indices and a boolean value,
  2584. * this Comparator imposes a direct or reverse order on set of objects.
  2585. * <p>
  2586. * If the specified boolean value is SELECT_BEST, the Comparator imposes the
  2587. * direct index-based order: an object A is greater than an object B if and
  2588. * only if the index of A is greater than the index of B. An object that
  2589. * doesn't have an associated index is less or equal than any other object.
  2590. * <p>
  2591. * If the specified boolean value is SELECT_WORST, the Comparator imposes the
  2592. * reverse index-based order: an object A is greater than an object B if and
  2593. * only if A is less than B with the direct index-based order.
  2594. */
  2595. public static class IndexOrderComparator extends IndexedComparator {
  2596. private final Map indexMap;
  2597. private static final Integer FALLBACK_INDEX =
  2598. Integer.valueOf(Integer.MIN_VALUE);
  2599. public IndexOrderComparator(Map indexMap) {
  2600. super(SELECT_BEST);
  2601. this.indexMap = indexMap;
  2602. }
  2603. public IndexOrderComparator(Map indexMap, boolean order) {
  2604. super(order);
  2605. this.indexMap = indexMap;
  2606. }
  2607. public int compare(Object obj1, Object obj2) {
  2608. if (order == SELECT_WORST) {
  2609. return -compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);
  2610. } else {
  2611. return compareIndices(indexMap, obj1, obj2, FALLBACK_INDEX);
  2612. }
  2613. }
  2614. }
  2615. /**
  2616. * A class that provides access to java.rmi.Remote and java.rmi.MarshalledObject
  2617. * without creating a static dependency.
  2618. */
  2619. private static class RMI {
  2620. private static final Class<?> remoteClass = getClass("java.rmi.Remote");
  2621. private static final Class<?> marshallObjectClass =
  2622. getClass("java.rmi.MarshalledObject");
  2623. private static final Constructor<?> marshallCtor =
  2624. getConstructor(marshallObjectClass, Object.class);
  2625. private static final Method marshallGet =
  2626. getMethod(marshallObjectClass, "get");
  2627. private static Class<?> getClass(String name) {
  2628. try {
  2629. return Class.forName(name, true, null);
  2630. } catch (ClassNotFoundException e) {
  2631. return null;
  2632. }
  2633. }
  2634. private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) {
  2635. try {
  2636. return (c == null) ? null : c.getDeclaredConstructor(types);
  2637. } catch (NoSuchMethodException x) {
  2638. throw new AssertionError(x);
  2639. }
  2640. }
  2641. private static Method getMethod(Class<?> c, String name, Class<?>... types) {
  2642. try {
  2643. return (c == null) ? null : c.getMethod(name, types);
  2644. } catch (NoSuchMethodException e) {
  2645. throw new AssertionError(e);
  2646. }
  2647. }
  2648. /**
  2649. * Returns {@code true} if the given class is java.rmi.Remote.
  2650. */
  2651. static boolean isRemote(Class<?> c) {
  2652. return (remoteClass == null) ? null : remoteClass.isAssignableFrom(c);
  2653. }
  2654. /**
  2655. * Returns java.rmi.Remote.class if RMI is present; otherwise {@code null}.
  2656. */
  2657. static Class<?> remoteClass() {
  2658. return remoteClass;
  2659. }
  2660. /**
  2661. * Returns a new MarshalledObject containing the serialized representation
  2662. * of the given object.
  2663. */
  2664. static Object newMarshalledObject(Object obj) throws IOException {
  2665. try {
  2666. return marshallCtor.newInstance(obj);
  2667. } catch (InstantiationException x) {
  2668. throw new AssertionError(x);
  2669. } catch (IllegalAccessException x) {
  2670. throw new AssertionError(x);
  2671. } catch (InvocationTargetException x) {
  2672. Throwable cause = x.getCause();
  2673. if (cause instanceof IOException)
  2674. throw (IOException)cause;
  2675. throw new AssertionError(x);
  2676. }
  2677. }
  2678. /**
  2679. * Returns a new copy of the contained marshalled object.
  2680. */
  2681. static Object getMarshalledObject(Object obj)
  2682. throws IOException, ClassNotFoundException
  2683. {
  2684. try {
  2685. return marshallGet.invoke(obj);
  2686. } catch (IllegalAccessException x) {
  2687. throw new AssertionError(x);
  2688. } catch (InvocationTargetException x) {
  2689. Throwable cause = x.getCause();
  2690. if (cause instanceof IOException)
  2691. throw (IOException)cause;
  2692. if (cause instanceof ClassNotFoundException)
  2693. throw (ClassNotFoundException)cause;
  2694. throw new AssertionError(x);
  2695. }
  2696. }
  2697. }
  2698. }