diff -r 0f955581dc0b src/share/classes/java/awt/datatransfer/DataFlavor.java --- a/src/share/classes/java/awt/datatransfer/DataFlavor.java Mon Mar 24 06:33:16 2008 -0700 +++ b/src/share/classes/java/awt/datatransfer/DataFlavor.java Sat May 03 12:04:38 2008 +0200 @@ -217,8 +217,29 @@ public class DataFlavor implements Exter * representation class of java.util.List is used. * Each element of the list is required/guaranteed to be of type * java.io.File. + * + * You should generally use javaURIListFlavor instead of this. */ public static final DataFlavor javaFileListFlavor = createConstant("application/x-java-file-list;class=java.util.List", null); + + /** + * To transfer a list of files and/or URIs to/from Java (and + * the underlying platform) a DataFlavor of this + * type/subtype and representation class of + * java.util.List is used. Each element of this + * list is required/guaranteed to be of type + * java.net.URI. + *

+ * You should use this flavor instead of javaFileListFlavor, + * because some platforms provide only URI lists, not file + * lists. On these platforms, if and only if all URIs are + * files for the transfer in question, you can use/get + * javaFileListFlavor in addition to this flavor, for + * backward compatibility. + * + * @since 1.7 + */ + public static final DataFlavor javaURIListFlavor = createConstant("application/x-java-uri-list;class=java.util.List", null); /** * To transfer a reference to an arbitrary Java object reference that @@ -1227,6 +1248,20 @@ public class DataFlavor implements Exter } + /** + * Returns true if the DataFlavor specified represents + * a list of URI objects. + * @return true is the DataFlavor specified represents + * a List of File objects + */ + + public boolean isFlavorJavaURIListType() { + if (mimeType == null || representationClass == null) + return false; + return java.util.List.class.isAssignableFrom(representationClass) && + mimeType.match(javaURIListFlavor.mimeType); + } + /** * Returns whether this DataFlavor is a valid text flavor for * this implementation of the Java platform. Only flavors equivalent to diff -r 0f955581dc0b src/share/classes/sun/awt/datatransfer/DataTransferer.java --- a/src/share/classes/sun/awt/datatransfer/DataTransferer.java Mon Mar 24 06:33:16 2008 -0700 +++ b/src/share/classes/sun/awt/datatransfer/DataTransferer.java Sat May 03 12:04:38 2008 +0200 @@ -50,6 +50,10 @@ import java.io.Reader; import java.io.Reader; import java.io.SequenceInputStream; import java.io.StringReader; + +import java.net.InetAddress; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -616,6 +620,16 @@ public abstract class DataTransferer { public abstract boolean isImageFormat(long format); /** + * Determines whether the DataFlavor corresponding to the specified long + * format is DataFlavor.javaURIListFlavor. + */ + public boolean isURIListFormat(long format) + { + // FIXME: make into an abstract method, with a Windows implementation? + return false; + } + + /** * Returns a Map whose keys are all of the possible formats into which the * Transferable's transfer data flavors can be translated. The value of * each key is the DataFlavor in which the Transferable's data should be @@ -683,6 +697,7 @@ public abstract class DataTransferer { // case of Serializable if (flavor.isFlavorTextType() || flavor.isFlavorJavaFileListType() || + flavor.isFlavorJavaURIListType() || DataFlavor.imageFlavor.equals(flavor) || flavor.isRepresentationClassSerializable() || flavor.isRepresentationClassInputStream() || @@ -786,6 +801,7 @@ public abstract class DataTransferer { // case of Serializable if (flavor.isFlavorTextType() || flavor.isFlavorJavaFileListType() || + flavor.isFlavorJavaURIListType() || DataFlavor.imageFlavor.equals(flavor) || flavor.isRepresentationClassSerializable() || flavor.isRepresentationClassInputStream() || @@ -862,6 +878,7 @@ public abstract class DataTransferer { // case of Serializable if (flavor.isFlavorTextType() || flavor.isFlavorJavaFileListType() || + flavor.isFlavorJavaURIListType() || DataFlavor.imageFlavor.equals(flavor) || flavor.isRepresentationClassSerializable() || flavor.isRepresentationClassInputStream() || @@ -948,7 +965,7 @@ public abstract class DataTransferer { * clipboard string encoding/decoding, basing on clipboard * format and localeTransferable(on decoding, if available) */ - private String getBestCharsetForTextFormat(Long lFormat, + protected String getBestCharsetForTextFormat(Long lFormat, Transferable localeTransferable) throws IOException { String charset = null; @@ -971,6 +988,68 @@ public abstract class DataTransferer { charset = getDefaultTextCharset(); } return charset; + } + + /** + * Translate a transferable to a URI list. The default implementation + * provides a text/uri-list MIME type in the target encoding. + */ + protected void translateTransferableToURIList(Object obj, + long format, + ByteArrayOutputStream outStream) throws IOException + { + String nat = getNativeForFormat(format); + String targetCharset = "UTF-8"; + if (nat != null) { + try { + if ((targetCharset = new DataFlavor(nat).getParameter("charset")) == null) + targetCharset = "UTF-8"; + } catch (ClassNotFoundException cnfe) { + throw new IOException(cnfe); + } + } + final List list = (List)obj; + int nItems = 0; + for (Object o : list) { + if (o instanceof String || o instanceof File || + o instanceof URI) + nItems++; + } + final String[] items = new String[nItems]; + + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws IOException { + try { + int j = 0; + for (Object o : list) { + if (o instanceof URI) { + items[j++] = ((URI)o).toString(); + } else if (o instanceof File) { + // Many implementations are fussy about the number of slashes + items[j++] = new URI("file:///" + ((File)o).getAbsolutePath()).toString(); + } else if (o instanceof String) { + items[j++] = new URI((String)o).toString(); + } + } + } catch (URISyntaxException uriSyntaxException) { + throw new IOException(uriSyntaxException); + } + return null; + } + }); + } catch (PrivilegedActionException pae) { + throw new IOException(pae.getMessage()); + } + + byte[] bytes = items[0].getBytes(targetCharset); + outStream.write(bytes, 0, bytes.length); + byte[] eoln = "\r\n".getBytes(targetCharset); + for (int i = 1; i < items.length; i++) { + outStream.write(eoln, 0, eoln.length); + bytes = items[i].getBytes(targetCharset); + outStream.write(bytes, 0, bytes.length); + } } /** @@ -1279,18 +1358,37 @@ search: ByteArrayOutputStream bos = new ByteArrayOutputStream(); + // Target data is a URI list. Source data must be a + // j.u.List of j.i.File, j.l.String, or j.n.URI objects. + if (isURIListFormat(format)) { + if (!DataFlavor.javaFileListFlavor.equals(flavor) && + !DataFlavor.javaURIListFlavor.equals(flavor)) { + throw new IOException("data translation failed"); + } + translateTransferableToURIList(obj, format, bos); + // Target data is a file list. Source data must be a // java.util.List which contains java.io.File or String instances. - if (isFileFormat(format)) { + } else if (isFileFormat(format)) { if (!DataFlavor.javaFileListFlavor.equals(flavor)) { throw new IOException("data translation failed"); } final List list = (List)obj; int nFiles = 0; + final String myHostname = InetAddress.getLocalHost().getHostName(); for (int i = 0; i < list.size(); i++) { Object o = list.get(i); if (o instanceof File || o instanceof String) { nFiles++; + } else if (o instanceof URI) { + URI uri = (URI) o; + if (uri.getScheme().equals("file")) { + String host = uri.getHost(); + if (host == null || host.equals("localhost") || + host.equals(myHostname)) { + nFiles++; + } + } } } final String[] files = new String[nFiles]; @@ -1300,7 +1398,16 @@ search: public Object run() throws IOException { for (int i = 0, j = 0; i < list.size(); i++) { Object o = list.get(i); - if (o instanceof File) { + if (o instanceof URI) { + URI uri = (URI) o; + if (uri.getScheme().equals("file")) { + String host = uri.getHost(); + if (host == null || host.equals("localhost") || + host.equals(myHostname)) { + files[j++] = new File(uri.getPath()).getCanonicalPath(); + } + } + } else if (o instanceof File) { files[j++] = ((File)o).getCanonicalPath(); } else if (o instanceof String) { files[j++] = (String)o; @@ -1403,32 +1510,61 @@ search: str = new ByteArrayInputStream(bytes); } + // Source data is a URI list. Parse it into List or + // List depending on the desired format. + if (isURIListFormat(format)) { + URI uris[] = dragQueryURIs(str, bytes, format, localeTransferable); + if (DataFlavor.javaURIListFlavor.equals(flavor)) { + return Arrays.asList(uris); + } else if (DataFlavor.javaFileListFlavor.equals(flavor)) { + ArrayList files = new ArrayList(); + String myHostname = InetAddress.getLocalHost().getHostName(); + for (URI uri : uris) { + // All file must be local, or we have to return + // an empty list (can't have missing files) + if (!uri.getScheme().equals("file")) { + return new ArrayList(); + } + String host = uri.getHost(); + if (host != null && !host.equals("localhost") && + !host.equals(myHostname)) { + return new ArrayList(); + } + files.add(new File(uri.getPath())); + } + return files; + } else { + throw new IOException("data translation failed"); + } // Source data is a file list. Use the dragQueryFile native function to // do most of the decoding. Then wrap File objects around the String // filenames and return a List. - if (isFileFormat(format)) { - if (!DataFlavor.javaFileListFlavor.equals(flavor)) { - throw new IOException("data translation failed"); - } + } else if (isFileFormat(format)) { if (bytes == null) { bytes = inputStreamToByteArray(str); } String[] filenames = dragQueryFile(bytes); + str.close(); if (filenames == null) { - str.close(); return null; } - // Convert the strings to File objects - File[] files = new File[filenames.length]; - for (int i = 0; i < filenames.length; i++) { - files[i] = new File(filenames[i]); + if (DataFlavor.javaFileListFlavor.equals(flavor)) { + // Convert the strings to File objects + File[] files = new File[filenames.length]; + for (int i = 0; i < filenames.length; i++) { + files[i] = new File(filenames[i]); + } + return Arrays.asList(files); + } else if (DataFlavor.javaURIListFlavor.equals(flavor)) { + URI[] uris = new URI[filenames.length]; + for (int i = 0; i < filenames.length; i++) { + uris[i] = new File(filenames[i]).toURI(); + } + return Arrays.asList(uris); + } else { + throw new IOException("data translation failed"); } - str.close(); - - // Turn the list of Files into a List and return - return Arrays.asList(files); - // Target data is a String. Strip terminating NUL bytes. Decode bytes // into characters. Search-and-replace EOLN. } else if (String.class.equals(flavor.getRepresentationClass()) && @@ -1812,6 +1948,19 @@ search: * Decodes a byte array into a set of String filenames. */ protected abstract String[] dragQueryFile(byte[] bytes); + + /** + * Decode URIs from either a byte array or a stream. + */ + protected URI[] dragQueryURIs(InputStream stream, + byte[] bytes, + long format, + Transferable localeTransferable) + throws IOException + { + throw new IOException( + new UnsupportedOperationException("not implemented on this platform")); + } /** * Translates either a byte array or an input stream which contain diff -r 0f955581dc0b src/solaris/classes/sun/awt/X11/XDataTransferer.java --- a/src/solaris/classes/sun/awt/X11/XDataTransferer.java Mon Mar 24 06:33:16 2008 -0700 +++ b/src/solaris/classes/sun/awt/X11/XDataTransferer.java Sat May 03 12:04:38 2008 +0200 @@ -28,13 +28,19 @@ import java.awt.Image; import java.awt.Image; import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.WritableRaster; +import java.io.BufferedReader; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.IOException; + +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Iterator; @@ -106,6 +112,22 @@ public class XDataTransferer extends Dat } } return super.getCharsetForTextFormat(lFormat); + } + + public boolean isURIListFormat(long format) { + String nat = getNativeForFormat(format); + if (nat == null) { + return false; + } + try { + DataFlavor df = new DataFlavor(nat); + if (df.getPrimaryType().equals("text") && df.getSubType().equals("uri-list")) { + return true; + } + } catch (Exception e) { + // Not a MIME format. + } + return false; } public boolean isFileFormat(long format) { @@ -212,6 +234,33 @@ public class XDataTransferer extends Dat XAtom.get("STRING").getAtom()); } finally { XToolkit.awtUnlock(); + } + } + + protected URI[] dragQueryURIs(InputStream stream, + byte[] bytes, + long format, + Transferable localeTransferable) + throws IOException { + String charset = getBestCharsetForTextFormat(format, localeTransferable); + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(stream, charset)); + String line; + ArrayList uriList = new ArrayList(); + URI uri; + while ((line = reader.readLine()) != null) { + try { + uri = new URI(line); + } catch (URISyntaxException uriSyntaxException) { + throw new IOException(uriSyntaxException); + } + uriList.add(uri); + } + return uriList.toArray(new URI[uriList.size()]); + } finally { + if (reader != null) + reader.close(); } } diff -r 0f955581dc0b src/solaris/lib/flavormap.properties --- a/src/solaris/lib/flavormap.properties Mon Mar 24 06:33:16 2008 -0700 +++ b/src/solaris/lib/flavormap.properties Sat May 03 12:04:38 2008 +0200 @@ -73,5 +73,8 @@ TEXT=text/plain;eoln="\n";terminators=0 TEXT=text/plain;eoln="\n";terminators=0 STRING=text/plain;charset=iso8859-1;eoln="\n";terminators=0 FILE_NAME=application/x-java-file-list;class=java.util.List +text/uri-list=application/x-java-uri-list;class=java.util.List +FILE_NAME=application/x-java-uri-list;class=java.util.List +text/uri-list=application/x-java-file-list;class=java.util.List PNG=image/x-java-image;class=java.awt.Image JFIF=image/x-java-image;class=java.awt.Image diff -r 0f955581dc0b src/windows/lib/flavormap.properties --- a/src/windows/lib/flavormap.properties Mon Mar 24 06:33:16 2008 -0700 +++ b/src/windows/lib/flavormap.properties Sat May 03 12:04:38 2008 +0200 @@ -64,6 +64,7 @@ HTML\ Format=text/html;charset=utf-8;eol HTML\ Format=text/html;charset=utf-8;eoln="\r\n";terminators=1 Rich\ Text\ Format=text/rtf HDROP=application/x-java-file-list;class=java.util.List +HDROP=application/x-java-uri-list;class=java.util.List PNG=image/x-java-image;class=java.awt.Image JFIF=image/x-java-image;class=java.awt.Image DIB=image/x-java-image;class=java.awt.Image