Path: blob/master/src/java.desktop/unix/classes/sun/awt/X11/XDataTransferer.java
41159 views
/*1* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.awt.X11;2627import java.awt.Image;2829import java.awt.datatransfer.DataFlavor;30import java.awt.datatransfer.Transferable;3132import java.awt.image.BufferedImage;33import java.awt.image.ColorModel;34import java.awt.image.WritableRaster;3536import java.io.BufferedReader;37import java.io.InputStream;38import java.io.InputStreamReader;39import java.io.IOException;4041import java.net.URI;42import java.net.URISyntaxException;4344import java.util.ArrayList;45import java.util.Iterator;46import java.util.LinkedHashSet;4748import javax.imageio.ImageIO;49import javax.imageio.ImageReader;50import javax.imageio.ImageTypeSpecifier;51import javax.imageio.ImageWriter;52import javax.imageio.spi.ImageWriterSpi;5354import sun.datatransfer.DataFlavorUtil;55import sun.awt.datatransfer.DataTransferer;56import sun.awt.datatransfer.ToolkitThreadBlockedHandler;5758import java.io.ByteArrayOutputStream;5960/**61* Platform-specific support for the data transfer subsystem.62*/63public class XDataTransferer extends DataTransferer {64static final XAtom FILE_NAME_ATOM = XAtom.get("FILE_NAME");65static final XAtom DT_NET_FILE_ATOM = XAtom.get("_DT_NETFILE");66static final XAtom PNG_ATOM = XAtom.get("PNG");67static final XAtom JFIF_ATOM = XAtom.get("JFIF");68static final XAtom TARGETS_ATOM = XAtom.get("TARGETS");69static final XAtom INCR_ATOM = XAtom.get("INCR");70static final XAtom MULTIPLE_ATOM = XAtom.get("MULTIPLE");7172/**73* Singleton constructor74*/75private XDataTransferer() {76}7778private static XDataTransferer transferer;7980static synchronized XDataTransferer getInstanceImpl() {81if (transferer == null) {82transferer = new XDataTransferer();83}84return transferer;85}8687@Override88public String getDefaultUnicodeEncoding() {89return "iso-10646-ucs-2";90}9192@Override93public boolean isLocaleDependentTextFormat(long format) {94return false;95}9697@Override98public boolean isTextFormat(long format) {99return super.isTextFormat(format)100|| isMimeFormat(format, "text");101}102103@Override104protected String getCharsetForTextFormat(Long lFormat) {105if (isMimeFormat(lFormat, "text")) {106String nat = getNativeForFormat(lFormat);107DataFlavor df = new DataFlavor(nat, null);108// Ignore the charset parameter of the MIME type if the subtype109// doesn't support charset.110if (!DataFlavorUtil.doesSubtypeSupportCharset(df)) {111return null;112}113String charset = df.getParameter("charset");114if (charset != null) {115return charset;116}117}118return super.getCharsetForTextFormat(lFormat);119}120121@Override122protected boolean isURIListFormat(long format) {123String nat = getNativeForFormat(format);124if (nat == null) {125return false;126}127try {128DataFlavor df = new DataFlavor(nat);129if (df.getPrimaryType().equals("text") && df.getSubType().equals("uri-list")) {130return true;131}132} catch (Exception e) {133// Not a MIME format.134}135return false;136}137138@Override139public boolean isFileFormat(long format) {140return format == FILE_NAME_ATOM.getAtom() ||141format == DT_NET_FILE_ATOM.getAtom();142}143144@Override145public boolean isImageFormat(long format) {146return format == PNG_ATOM.getAtom() ||147format == JFIF_ATOM.getAtom() ||148isMimeFormat(format, "image");149}150151@Override152protected Long getFormatForNativeAsLong(String str) {153// Just get the atom. If it has already been retrived154// once, we'll get a copy so this should be very fast.155return XAtom.get(str).getAtom();156}157158@Override159protected String getNativeForFormat(long format) {160return getTargetNameForAtom(format);161}162163public ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler() {164return XToolkitThreadBlockedHandler.getToolkitThreadBlockedHandler();165}166167/**168* Gets an format name for a given format (atom)169*/170private String getTargetNameForAtom(long atom) {171return XAtom.get(atom).getName();172}173174@Override175protected byte[] imageToPlatformBytes(Image image, long format)176throws IOException {177String mimeType = null;178if (format == PNG_ATOM.getAtom()) {179mimeType = "image/png";180} else if (format == JFIF_ATOM.getAtom()) {181mimeType = "image/jpeg";182} else {183// Check if an image MIME format.184try {185String nat = getNativeForFormat(format);186DataFlavor df = new DataFlavor(nat);187String primaryType = df.getPrimaryType();188if ("image".equals(primaryType)) {189mimeType = df.getPrimaryType() + "/" + df.getSubType();190}191} catch (Exception e) {192// Not an image MIME format.193}194}195if (mimeType != null) {196return imageToStandardBytes(image, mimeType);197} else {198String nativeFormat = getNativeForFormat(format);199throw new IOException("Translation to " + nativeFormat +200" is not supported.");201}202}203204@Override205protected ByteArrayOutputStream convertFileListToBytes(ArrayList<String> fileList)206throws IOException207{208ByteArrayOutputStream bos = new ByteArrayOutputStream();209for (int i = 0; i < fileList.size(); i++)210{211byte[] bytes = fileList.get(i).getBytes();212if (i != 0) bos.write(0);213bos.write(bytes, 0, bytes.length);214}215return bos;216}217218/**219* Translates either a byte array or an input stream which contain220* platform-specific image data in the given format into an Image.221*/222@Override223protected Image platformImageBytesToImage(224byte[] bytes, long format) throws IOException225{226String mimeType = null;227if (format == PNG_ATOM.getAtom()) {228mimeType = "image/png";229} else if (format == JFIF_ATOM.getAtom()) {230mimeType = "image/jpeg";231} else {232// Check if an image MIME format.233try {234String nat = getNativeForFormat(format);235DataFlavor df = new DataFlavor(nat);236String primaryType = df.getPrimaryType();237if ("image".equals(primaryType)) {238mimeType = df.getPrimaryType() + "/" + df.getSubType();239}240} catch (Exception e) {241// Not an image MIME format.242}243}244if (mimeType != null) {245return standardImageBytesToImage(bytes, mimeType);246} else {247String nativeFormat = getNativeForFormat(format);248throw new IOException("Translation from " + nativeFormat +249" is not supported.");250}251}252253@Override254protected String[] dragQueryFile(byte[] bytes) {255XToolkit.awtLock();256try {257return XlibWrapper.XTextPropertyToStringList(bytes,258XAtom.get("STRING").getAtom());259} finally {260XToolkit.awtUnlock();261}262}263264@Override265protected URI[] dragQueryURIs(InputStream stream,266long format,267Transferable localeTransferable)268throws IOException {269270String charset = getBestCharsetForTextFormat(format, localeTransferable);271try (InputStreamReader isr = new InputStreamReader(stream, charset);272BufferedReader reader = new BufferedReader(isr)) {273String line;274ArrayList<URI> uriList = new ArrayList<>();275URI uri;276while ((line = reader.readLine()) != null) {277try {278uri = new URI(line);279} catch (URISyntaxException uriSyntaxException) {280throw new IOException(uriSyntaxException);281}282uriList.add(uri);283}284return uriList.toArray(new URI[uriList.size()]);285}286}287288/**289* Returns true if and only if the name of the specified format Atom290* constitutes a valid MIME type with the specified primary type.291*/292private boolean isMimeFormat(long format, String primaryType) {293String nat = getNativeForFormat(format);294295if (nat == null) {296return false;297}298299try {300DataFlavor df = new DataFlavor(nat);301if (primaryType.equals(df.getPrimaryType())) {302return true;303}304} catch (Exception e) {305// Not a MIME format.306}307308return false;309}310311/*312* The XDnD protocol prescribes that the Atoms used as targets for data313* transfer should have string names that represent the corresponding MIME314* types.315* To meet this requirement we check if the passed native format constitutes316* a valid MIME and return a list of flavors to which the data in this MIME317* type can be translated by the Data Transfer subsystem.318*/319@Override320public LinkedHashSet<DataFlavor> getPlatformMappingsForNative(String nat) {321LinkedHashSet<DataFlavor> flavors = new LinkedHashSet<>();322323if (nat == null) {324return flavors;325}326327DataFlavor df;328try {329df = new DataFlavor(nat);330} catch (Exception e) {331// The string doesn't constitute a valid MIME type.332return flavors;333}334335DataFlavor value = df;336final String primaryType = df.getPrimaryType();337final String baseType = primaryType + "/" + df.getSubType();338339// For text formats we map natives to MIME strings instead of data340// flavors to enable dynamic text native-to-flavor mapping generation.341// See SystemFlavorMap.getFlavorsForNative() for details.342if ("image".equals(primaryType)) {343Iterator<ImageReader> readers = ImageIO.getImageReadersByMIMEType(baseType);344if (readers.hasNext()) {345flavors.add(DataFlavor.imageFlavor);346}347}348349flavors.add(value);350351return flavors;352}353354private static ImageTypeSpecifier defaultSpecifier = null;355356private ImageTypeSpecifier getDefaultImageTypeSpecifier() {357if (defaultSpecifier == null) {358ColorModel model = ColorModel.getRGBdefault();359WritableRaster raster =360model.createCompatibleWritableRaster(10, 10);361362BufferedImage bufferedImage =363new BufferedImage(model, raster, model.isAlphaPremultiplied(),364null);365366defaultSpecifier = new ImageTypeSpecifier(bufferedImage);367}368369return defaultSpecifier;370}371372/*373* The XDnD protocol prescribes that the Atoms used as targets for data374* transfer should have string names that represent the corresponding MIME375* types.376* To meet this requirement we return a list of formats that represent377* MIME types to which the data in this flavor can be translated by the Data378* Transfer subsystem.379*/380@Override381public LinkedHashSet<String> getPlatformMappingsForFlavor(DataFlavor df) {382LinkedHashSet<String> natives = new LinkedHashSet<>(1);383384if (df == null) {385return natives;386}387388String charset = df.getParameter("charset");389String baseType = df.getPrimaryType() + "/" + df.getSubType();390String mimeType = baseType;391392if (charset != null && DataFlavorUtil.isFlavorCharsetTextType(df)) {393mimeType += ";charset=" + charset;394}395396// Add a mapping to the MIME native whenever the representation class397// doesn't require translation.398if (df.getRepresentationClass() != null &&399(df.isRepresentationClassInputStream() ||400df.isRepresentationClassByteBuffer() ||401byte[].class.equals(df.getRepresentationClass()))) {402natives.add(mimeType);403}404405if (DataFlavor.imageFlavor.equals(df)) {406String[] mimeTypes = ImageIO.getWriterMIMETypes();407if (mimeTypes != null) {408for (String mime : mimeTypes) {409Iterator<ImageWriter> writers = ImageIO.getImageWritersByMIMEType(mime);410while (writers.hasNext()) {411ImageWriter imageWriter = writers.next();412ImageWriterSpi writerSpi = imageWriter.getOriginatingProvider();413414if (writerSpi != null &&415writerSpi.canEncodeImage(getDefaultImageTypeSpecifier())) {416natives.add(mime);417break;418}419}420}421}422} else if (DataFlavorUtil.isFlavorCharsetTextType(df)) {423// stringFlavor is semantically equivalent to the standard424// "text/plain" MIME type.425if (DataFlavor.stringFlavor.equals(df)) {426baseType = "text/plain";427}428429for (String encoding : DataFlavorUtil.standardEncodings()) {430if (!encoding.equals(charset)) {431natives.add(baseType + ";charset=" + encoding);432}433}434435// Add a MIME format without specified charset.436if (!natives.contains(baseType)) {437natives.add(baseType);438}439}440441return natives;442}443}444445446