001: /*
002: * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt.X11;
027:
028: import java.awt.Image;
029:
030: import java.awt.datatransfer.DataFlavor;
031:
032: import java.awt.image.BufferedImage;
033: import java.awt.image.ColorModel;
034: import java.awt.image.WritableRaster;
035:
036: import java.io.InputStream;
037: import java.io.IOException;
038:
039: import java.util.ArrayList;
040: import java.util.Iterator;
041: import java.util.List;
042:
043: import javax.imageio.ImageIO;
044: import javax.imageio.ImageTypeSpecifier;
045: import javax.imageio.ImageWriter;
046: import javax.imageio.spi.ImageWriterSpi;
047:
048: import sun.awt.datatransfer.DataTransferer;
049: import sun.awt.datatransfer.ToolkitThreadBlockedHandler;
050:
051: /**
052: * Platform-specific support for the data transfer subsystem.
053: */
054: public class XDataTransferer extends DataTransferer {
055: static final XAtom FILE_NAME_ATOM = XAtom.get("FILE_NAME");
056: static final XAtom DT_NET_FILE_ATOM = XAtom.get("_DT_NETFILE");
057: static final XAtom PNG_ATOM = XAtom.get("PNG");
058: static final XAtom JFIF_ATOM = XAtom.get("JFIF");
059: static final XAtom TARGETS_ATOM = XAtom.get("TARGETS");
060: static final XAtom INCR_ATOM = XAtom.get("INCR");
061: static final XAtom MULTIPLE_ATOM = XAtom.get("MULTIPLE");
062:
063: /**
064: * Singleton constructor
065: */
066: private XDataTransferer() {
067: }
068:
069: private static XDataTransferer transferer;
070:
071: static XDataTransferer getInstanceImpl() {
072: synchronized (XDataTransferer.class) {
073: if (transferer == null) {
074: transferer = new XDataTransferer();
075: }
076: }
077: return transferer;
078: }
079:
080: public String getDefaultUnicodeEncoding() {
081: return "iso-10646-ucs-2";
082: }
083:
084: public boolean isLocaleDependentTextFormat(long format) {
085: return false;
086: }
087:
088: public boolean isTextFormat(long format) {
089: return super .isTextFormat(format)
090: || isMimeFormat(format, "text");
091: }
092:
093: protected String getCharsetForTextFormat(Long lFormat) {
094: long format = lFormat.longValue();
095: if (isMimeFormat(format, "text")) {
096: String nat = getNativeForFormat(format);
097: DataFlavor df = new DataFlavor(nat, null);
098: // Ignore the charset parameter of the MIME type if the subtype
099: // doesn't support charset.
100: if (!DataTransferer.doesSubtypeSupportCharset(df)) {
101: return null;
102: }
103: String charset = df.getParameter("charset");
104: if (charset != null) {
105: return charset;
106: }
107: }
108: return super .getCharsetForTextFormat(lFormat);
109: }
110:
111: public boolean isFileFormat(long format) {
112: return format == FILE_NAME_ATOM.getAtom()
113: || format == DT_NET_FILE_ATOM.getAtom();
114: }
115:
116: public boolean isImageFormat(long format) {
117: return format == PNG_ATOM.getAtom()
118: || format == JFIF_ATOM.getAtom()
119: || isMimeFormat(format, "image");
120: }
121:
122: protected Long getFormatForNativeAsLong(String str) {
123: // Just get the atom. If it has already been retrived
124: // once, we'll get a copy so this should be very fast.
125: long atom = XAtom.get(str).getAtom();
126: return Long.valueOf(atom);
127: }
128:
129: protected String getNativeForFormat(long format) {
130: return getTargetNameForAtom(format);
131: }
132:
133: public ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler() {
134: return XToolkitThreadBlockedHandler
135: .getToolkitThreadBlockedHandler();
136: }
137:
138: /**
139: * Gets an format name for a given format (atom)
140: */
141: private String getTargetNameForAtom(long atom) {
142: return XAtom.get(atom).getName();
143: }
144:
145: protected byte[] imageToPlatformBytes(Image image, long format)
146: throws IOException {
147: String mimeType = null;
148: if (format == PNG_ATOM.getAtom()) {
149: mimeType = "image/png";
150: } else if (format == JFIF_ATOM.getAtom()) {
151: mimeType = "image/jpeg";
152: } else {
153: // Check if an image MIME format.
154: try {
155: String nat = getNativeForFormat(format);
156: DataFlavor df = new DataFlavor(nat);
157: String primaryType = df.getPrimaryType();
158: if ("image".equals(primaryType)) {
159: mimeType = df.getPrimaryType() + "/"
160: + df.getSubType();
161: }
162: } catch (Exception e) {
163: // Not an image MIME format.
164: }
165: }
166: if (mimeType != null) {
167: return imageToStandardBytes(image, mimeType);
168: } else {
169: String nativeFormat = getNativeForFormat(format);
170: throw new IOException("Translation to " + nativeFormat
171: + " is not supported.");
172: }
173: }
174:
175: /**
176: * Translates either a byte array or an input stream which contain
177: * platform-specific image data in the given format into an Image.
178: */
179: protected Image platformImageBytesOrStreamToImage(
180: InputStream inputStream, byte[] bytes, long format)
181: throws IOException {
182: String mimeType = null;
183: if (format == PNG_ATOM.getAtom()) {
184: mimeType = "image/png";
185: } else if (format == JFIF_ATOM.getAtom()) {
186: mimeType = "image/jpeg";
187: } else {
188: // Check if an image MIME format.
189: try {
190: String nat = getNativeForFormat(format);
191: DataFlavor df = new DataFlavor(nat);
192: String primaryType = df.getPrimaryType();
193: if ("image".equals(primaryType)) {
194: mimeType = df.getPrimaryType() + "/"
195: + df.getSubType();
196: }
197: } catch (Exception e) {
198: // Not an image MIME format.
199: }
200: }
201: if (mimeType != null) {
202: return standardImageBytesOrStreamToImage(inputStream,
203: bytes, mimeType);
204: } else {
205: String nativeFormat = getNativeForFormat(format);
206: throw new IOException("Translation from " + nativeFormat
207: + " is not supported.");
208: }
209: }
210:
211: protected String[] dragQueryFile(byte[] bytes) {
212: XToolkit.awtLock();
213: try {
214: return XlibWrapper.XTextPropertyToStringList(bytes, XAtom
215: .get("STRING").getAtom());
216: } finally {
217: XToolkit.awtUnlock();
218: }
219: }
220:
221: /**
222: * Returns true if and only if the name of the specified format Atom
223: * constitutes a valid MIME type with the specified primary type.
224: */
225: private boolean isMimeFormat(long format, String primaryType) {
226: String nat = getNativeForFormat(format);
227:
228: if (nat == null) {
229: return false;
230: }
231:
232: try {
233: DataFlavor df = new DataFlavor(nat);
234: if (primaryType.equals(df.getPrimaryType())) {
235: return true;
236: }
237: } catch (Exception e) {
238: // Not a MIME format.
239: }
240:
241: return false;
242: }
243:
244: /*
245: * The XDnD protocol prescribes that the Atoms used as targets for data
246: * transfer should have string names that represent the corresponding MIME
247: * types.
248: * To meet this requirement we check if the passed native format constitutes
249: * a valid MIME and return a list of flavors to which the data in this MIME
250: * type can be translated by the Data Transfer subsystem.
251: */
252: public List getPlatformMappingsForNative(String nat) {
253: List flavors = new ArrayList();
254:
255: if (nat == null) {
256: return flavors;
257: }
258:
259: DataFlavor df = null;
260:
261: try {
262: df = new DataFlavor(nat);
263: } catch (Exception e) {
264: // The string doesn't constitute a valid MIME type.
265: return flavors;
266: }
267:
268: Object value = df;
269: final String primaryType = df.getPrimaryType();
270: final String baseType = primaryType + "/" + df.getSubType();
271:
272: // For text formats we map natives to MIME strings instead of data
273: // flavors to enable dynamic text native-to-flavor mapping generation.
274: // See SystemFlavorMap.getFlavorsForNative() for details.
275: if ("text".equals(primaryType)) {
276: value = primaryType + "/" + df.getSubType();
277: } else if ("image".equals(primaryType)) {
278: Iterator readers = ImageIO
279: .getImageReadersByMIMEType(baseType);
280: if (readers.hasNext()) {
281: flavors.add(DataFlavor.imageFlavor);
282: }
283: }
284:
285: flavors.add(value);
286:
287: return flavors;
288: }
289:
290: private static ImageTypeSpecifier defaultSpecifier = null;
291:
292: private ImageTypeSpecifier getDefaultImageTypeSpecifier() {
293: if (defaultSpecifier == null) {
294: ColorModel model = ColorModel.getRGBdefault();
295: WritableRaster raster = model
296: .createCompatibleWritableRaster(10, 10);
297:
298: BufferedImage bufferedImage = new BufferedImage(model,
299: raster, model.isAlphaPremultiplied(), null);
300:
301: defaultSpecifier = new ImageTypeSpecifier(bufferedImage);
302: }
303:
304: return defaultSpecifier;
305: }
306:
307: /*
308: * The XDnD protocol prescribes that the Atoms used as targets for data
309: * transfer should have string names that represent the corresponding MIME
310: * types.
311: * To meet this requirement we return a list of formats that represent
312: * MIME types to which the data in this flavor can be translated by the Data
313: * Transfer subsystem.
314: */
315: public List getPlatformMappingsForFlavor(DataFlavor df) {
316: List natives = new ArrayList(1);
317:
318: if (df == null) {
319: return natives;
320: }
321:
322: String charset = df.getParameter("charset");
323: String baseType = df.getPrimaryType() + "/" + df.getSubType();
324: String mimeType = baseType;
325:
326: if (charset != null
327: && DataTransferer.isFlavorCharsetTextType(df)) {
328: mimeType += ";charset=" + charset;
329: }
330:
331: // Add a mapping to the MIME native whenever the representation class
332: // doesn't require translation.
333: if (df.getRepresentationClass() != null
334: && (df.isRepresentationClassInputStream()
335: || df.isRepresentationClassByteBuffer() || byteArrayClass
336: .equals(df.getRepresentationClass()))) {
337: natives.add(mimeType);
338: }
339:
340: if (DataFlavor.imageFlavor.equals(df)) {
341: String[] mimeTypes = ImageIO.getWriterMIMETypes();
342: if (mimeTypes != null) {
343: for (int i = 0; i < mimeTypes.length; i++) {
344: Iterator writers = ImageIO
345: .getImageWritersByMIMEType(mimeTypes[i]);
346:
347: while (writers.hasNext()) {
348: ImageWriter imageWriter = (ImageWriter) writers
349: .next();
350: ImageWriterSpi writerSpi = imageWriter
351: .getOriginatingProvider();
352:
353: if (writerSpi != null
354: && writerSpi
355: .canEncodeImage(getDefaultImageTypeSpecifier())) {
356: natives.add(mimeTypes[i]);
357: break;
358: }
359: }
360: }
361: }
362: } else if (DataTransferer.isFlavorCharsetTextType(df)) {
363: final Iterator iter = DataTransferer.standardEncodings();
364:
365: // stringFlavor is semantically equivalent to the standard
366: // "text/plain" MIME type.
367: if (DataFlavor.stringFlavor.equals(df)) {
368: baseType = "text/plain";
369: }
370:
371: while (iter.hasNext()) {
372: String encoding = (String) iter.next();
373: if (!encoding.equals(charset)) {
374: natives.add(baseType + ";charset=" + encoding);
375: }
376: }
377:
378: // Add a MIME format without specified charset.
379: if (!natives.contains(baseType)) {
380: natives.add(baseType);
381: }
382: }
383:
384: return natives;
385: }
386: }
|