| /* |
| * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.awt.X11; |
| |
| import java.awt.Image; |
| |
| import java.awt.datatransfer.DataFlavor; |
| import java.awt.datatransfer.Transferable; |
| import java.awt.datatransfer.UnsupportedFlavorException; |
| |
| 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; |
| import java.util.List; |
| |
| import javax.imageio.ImageIO; |
| import javax.imageio.ImageTypeSpecifier; |
| import javax.imageio.ImageWriter; |
| import javax.imageio.spi.ImageWriterSpi; |
| |
| import sun.awt.datatransfer.DataTransferer; |
| import sun.awt.datatransfer.ToolkitThreadBlockedHandler; |
| |
| import java.io.ByteArrayOutputStream; |
| |
| /** |
| * Platform-specific support for the data transfer subsystem. |
| */ |
| public class XDataTransferer extends DataTransferer { |
| static final XAtom FILE_NAME_ATOM = XAtom.get("FILE_NAME"); |
| static final XAtom DT_NET_FILE_ATOM = XAtom.get("_DT_NETFILE"); |
| static final XAtom PNG_ATOM = XAtom.get("PNG"); |
| static final XAtom JFIF_ATOM = XAtom.get("JFIF"); |
| static final XAtom TARGETS_ATOM = XAtom.get("TARGETS"); |
| static final XAtom INCR_ATOM = XAtom.get("INCR"); |
| static final XAtom MULTIPLE_ATOM = XAtom.get("MULTIPLE"); |
| |
| /** |
| * Singleton constructor |
| */ |
| private XDataTransferer() { |
| } |
| |
| private static XDataTransferer transferer; |
| |
| static XDataTransferer getInstanceImpl() { |
| synchronized (XDataTransferer.class) { |
| if (transferer == null) { |
| transferer = new XDataTransferer(); |
| } |
| } |
| return transferer; |
| } |
| |
| public String getDefaultUnicodeEncoding() { |
| return "iso-10646-ucs-2"; |
| } |
| |
| public boolean isLocaleDependentTextFormat(long format) { |
| return false; |
| } |
| |
| public boolean isTextFormat(long format) { |
| return super.isTextFormat(format) |
| || isMimeFormat(format, "text"); |
| } |
| |
| protected String getCharsetForTextFormat(Long lFormat) { |
| long format = lFormat.longValue(); |
| if (isMimeFormat(format, "text")) { |
| String nat = getNativeForFormat(format); |
| DataFlavor df = new DataFlavor(nat, null); |
| // Ignore the charset parameter of the MIME type if the subtype |
| // doesn't support charset. |
| if (!DataTransferer.doesSubtypeSupportCharset(df)) { |
| return null; |
| } |
| String charset = df.getParameter("charset"); |
| if (charset != null) { |
| return charset; |
| } |
| } |
| return super.getCharsetForTextFormat(lFormat); |
| } |
| |
| protected 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) { |
| return format == FILE_NAME_ATOM.getAtom() || |
| format == DT_NET_FILE_ATOM.getAtom(); |
| } |
| |
| public boolean isImageFormat(long format) { |
| return format == PNG_ATOM.getAtom() || |
| format == JFIF_ATOM.getAtom() || |
| isMimeFormat(format, "image"); |
| } |
| |
| protected Long getFormatForNativeAsLong(String str) { |
| // Just get the atom. If it has already been retrived |
| // once, we'll get a copy so this should be very fast. |
| long atom = XAtom.get(str).getAtom(); |
| return Long.valueOf(atom); |
| } |
| |
| protected String getNativeForFormat(long format) { |
| return getTargetNameForAtom(format); |
| } |
| |
| public ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler() { |
| return XToolkitThreadBlockedHandler.getToolkitThreadBlockedHandler(); |
| } |
| |
| /** |
| * Gets an format name for a given format (atom) |
| */ |
| private String getTargetNameForAtom(long atom) { |
| return XAtom.get(atom).getName(); |
| } |
| |
| protected byte[] imageToPlatformBytes(Image image, long format) |
| throws IOException { |
| String mimeType = null; |
| if (format == PNG_ATOM.getAtom()) { |
| mimeType = "image/png"; |
| } else if (format == JFIF_ATOM.getAtom()) { |
| mimeType = "image/jpeg"; |
| } else { |
| // Check if an image MIME format. |
| try { |
| String nat = getNativeForFormat(format); |
| DataFlavor df = new DataFlavor(nat); |
| String primaryType = df.getPrimaryType(); |
| if ("image".equals(primaryType)) { |
| mimeType = df.getPrimaryType() + "/" + df.getSubType(); |
| } |
| } catch (Exception e) { |
| // Not an image MIME format. |
| } |
| } |
| if (mimeType != null) { |
| return imageToStandardBytes(image, mimeType); |
| } else { |
| String nativeFormat = getNativeForFormat(format); |
| throw new IOException("Translation to " + nativeFormat + |
| " is not supported."); |
| } |
| } |
| |
| protected ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList) |
| throws IOException |
| { |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| for (int i = 0; i < fileList.size(); i++) |
| { |
| byte[] bytes = fileList.get(i).getBytes(); |
| if (i != 0) bos.write(0); |
| bos.write(bytes, 0, bytes.length); |
| } |
| return bos; |
| } |
| |
| /** |
| * Translates either a byte array or an input stream which contain |
| * platform-specific image data in the given format into an Image. |
| */ |
| protected Image platformImageBytesOrStreamToImage(InputStream inputStream, |
| byte[] bytes, |
| long format) |
| throws IOException { |
| String mimeType = null; |
| if (format == PNG_ATOM.getAtom()) { |
| mimeType = "image/png"; |
| } else if (format == JFIF_ATOM.getAtom()) { |
| mimeType = "image/jpeg"; |
| } else { |
| // Check if an image MIME format. |
| try { |
| String nat = getNativeForFormat(format); |
| DataFlavor df = new DataFlavor(nat); |
| String primaryType = df.getPrimaryType(); |
| if ("image".equals(primaryType)) { |
| mimeType = df.getPrimaryType() + "/" + df.getSubType(); |
| } |
| } catch (Exception e) { |
| // Not an image MIME format. |
| } |
| } |
| if (mimeType != null) { |
| return standardImageBytesOrStreamToImage(inputStream, bytes, mimeType); |
| } else { |
| String nativeFormat = getNativeForFormat(format); |
| throw new IOException("Translation from " + nativeFormat + |
| " is not supported."); |
| } |
| } |
| |
| protected String[] dragQueryFile(byte[] bytes) { |
| XToolkit.awtLock(); |
| try { |
| return XlibWrapper.XTextPropertyToStringList(bytes, |
| XAtom.get("STRING").getAtom()); |
| } finally { |
| XToolkit.awtUnlock(); |
| } |
| } |
| |
| protected URI[] dragQueryURIs(InputStream stream, |
| byte[] bytes, |
| long format, |
| Transferable localeTransferable) |
| throws IOException { |
| |
| String charset = null; |
| if (localeTransferable != null && |
| isLocaleDependentTextFormat(format) && |
| localeTransferable.isDataFlavorSupported(javaTextEncodingFlavor)) { |
| try { |
| charset = new String( |
| (byte[])localeTransferable.getTransferData(javaTextEncodingFlavor), |
| "UTF-8" |
| ); |
| } catch (UnsupportedFlavorException cannotHappen) { |
| } |
| } else { |
| charset = getCharsetForTextFormat(format); |
| } |
| if (charset == null) { |
| // Only happens when we have a custom text type. |
| charset = getDefaultTextCharset(); |
| } |
| |
| BufferedReader reader = null; |
| try { |
| reader = new BufferedReader(new InputStreamReader(stream, charset)); |
| String line; |
| ArrayList<URI> uriList = new ArrayList<URI>(); |
| 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(); |
| } |
| } |
| |
| /** |
| * Returns true if and only if the name of the specified format Atom |
| * constitutes a valid MIME type with the specified primary type. |
| */ |
| private boolean isMimeFormat(long format, String primaryType) { |
| String nat = getNativeForFormat(format); |
| |
| if (nat == null) { |
| return false; |
| } |
| |
| try { |
| DataFlavor df = new DataFlavor(nat); |
| if (primaryType.equals(df.getPrimaryType())) { |
| return true; |
| } |
| } catch (Exception e) { |
| // Not a MIME format. |
| } |
| |
| return false; |
| } |
| |
| /* |
| * The XDnD protocol prescribes that the Atoms used as targets for data |
| * transfer should have string names that represent the corresponding MIME |
| * types. |
| * To meet this requirement we check if the passed native format constitutes |
| * a valid MIME and return a list of flavors to which the data in this MIME |
| * type can be translated by the Data Transfer subsystem. |
| */ |
| public List getPlatformMappingsForNative(String nat) { |
| List flavors = new ArrayList(); |
| |
| if (nat == null) { |
| return flavors; |
| } |
| |
| DataFlavor df = null; |
| |
| try { |
| df = new DataFlavor(nat); |
| } catch (Exception e) { |
| // The string doesn't constitute a valid MIME type. |
| return flavors; |
| } |
| |
| Object value = df; |
| final String primaryType = df.getPrimaryType(); |
| final String baseType = primaryType + "/" + df.getSubType(); |
| |
| // For text formats we map natives to MIME strings instead of data |
| // flavors to enable dynamic text native-to-flavor mapping generation. |
| // See SystemFlavorMap.getFlavorsForNative() for details. |
| if ("text".equals(primaryType)) { |
| value = primaryType + "/" + df.getSubType(); |
| } else if ("image".equals(primaryType)) { |
| Iterator readers = ImageIO.getImageReadersByMIMEType(baseType); |
| if (readers.hasNext()) { |
| flavors.add(DataFlavor.imageFlavor); |
| } |
| } |
| |
| flavors.add(value); |
| |
| return flavors; |
| } |
| |
| private static ImageTypeSpecifier defaultSpecifier = null; |
| |
| private ImageTypeSpecifier getDefaultImageTypeSpecifier() { |
| if (defaultSpecifier == null) { |
| ColorModel model = ColorModel.getRGBdefault(); |
| WritableRaster raster = |
| model.createCompatibleWritableRaster(10, 10); |
| |
| BufferedImage bufferedImage = |
| new BufferedImage(model, raster, model.isAlphaPremultiplied(), |
| null); |
| |
| defaultSpecifier = new ImageTypeSpecifier(bufferedImage); |
| } |
| |
| return defaultSpecifier; |
| } |
| |
| /* |
| * The XDnD protocol prescribes that the Atoms used as targets for data |
| * transfer should have string names that represent the corresponding MIME |
| * types. |
| * To meet this requirement we return a list of formats that represent |
| * MIME types to which the data in this flavor can be translated by the Data |
| * Transfer subsystem. |
| */ |
| public List getPlatformMappingsForFlavor(DataFlavor df) { |
| List natives = new ArrayList(1); |
| |
| if (df == null) { |
| return natives; |
| } |
| |
| String charset = df.getParameter("charset"); |
| String baseType = df.getPrimaryType() + "/" + df.getSubType(); |
| String mimeType = baseType; |
| |
| if (charset != null && DataTransferer.isFlavorCharsetTextType(df)) { |
| mimeType += ";charset=" + charset; |
| } |
| |
| // Add a mapping to the MIME native whenever the representation class |
| // doesn't require translation. |
| if (df.getRepresentationClass() != null && |
| (df.isRepresentationClassInputStream() || |
| df.isRepresentationClassByteBuffer() || |
| byteArrayClass.equals(df.getRepresentationClass()))) { |
| natives.add(mimeType); |
| } |
| |
| if (DataFlavor.imageFlavor.equals(df)) { |
| String[] mimeTypes = ImageIO.getWriterMIMETypes(); |
| if (mimeTypes != null) { |
| for (int i = 0; i < mimeTypes.length; i++) { |
| Iterator writers = |
| ImageIO.getImageWritersByMIMEType(mimeTypes[i]); |
| |
| while (writers.hasNext()) { |
| ImageWriter imageWriter = (ImageWriter)writers.next(); |
| ImageWriterSpi writerSpi = |
| imageWriter.getOriginatingProvider(); |
| |
| if (writerSpi != null && |
| writerSpi.canEncodeImage(getDefaultImageTypeSpecifier())) { |
| natives.add(mimeTypes[i]); |
| break; |
| } |
| } |
| } |
| } |
| } else if (DataTransferer.isFlavorCharsetTextType(df)) { |
| final Iterator iter = DataTransferer.standardEncodings(); |
| |
| // stringFlavor is semantically equivalent to the standard |
| // "text/plain" MIME type. |
| if (DataFlavor.stringFlavor.equals(df)) { |
| baseType = "text/plain"; |
| } |
| |
| while (iter.hasNext()) { |
| String encoding = (String)iter.next(); |
| if (!encoding.equals(charset)) { |
| natives.add(baseType + ";charset=" + encoding); |
| } |
| } |
| |
| // Add a MIME format without specified charset. |
| if (!natives.contains(baseType)) { |
| natives.add(baseType); |
| } |
| } |
| |
| return natives; |
| } |
| } |