PageRenderTime 56ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

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

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