CDataTransferer.m revision 4632
4632N/A/*
4632N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
4632N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4632N/A *
4632N/A * This code is free software; you can redistribute it and/or modify it
4632N/A * under the terms of the GNU General Public License version 2 only, as
4632N/A * published by the Free Software Foundation. Oracle designates this
4632N/A * particular file as subject to the "Classpath" exception as provided
4632N/A * by Oracle in the LICENSE file that accompanied this code.
4632N/A *
4632N/A * This code is distributed in the hope that it will be useful, but WITHOUT
4632N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4632N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4632N/A * version 2 for more details (a copy is included in the LICENSE file that
4632N/A * accompanied this code).
4632N/A *
4632N/A * You should have received a copy of the GNU General Public License version
4632N/A * 2 along with this work; if not, write to the Free Software Foundation,
4632N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4632N/A *
4632N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
4632N/A * or visit www.oracle.com if you need additional information or have any
4632N/A * questions.
4632N/A */
4632N/A
4632N/A#import "CDataTransferer.h"
4632N/A#include "sun_lwawt_macosx_CDataTransferer.h"
4632N/A
4632N/A#import <AppKit/AppKit.h>
4632N/A#import <JavaNativeFoundation/JavaNativeFoundation.h>
4632N/A
4632N/A#include "ThreadUtilities.h"
4632N/A
4632N/A
4632N/A// ***** NOTE ***** This dictionary corresponds to the static array predefinedClipboardNames
4632N/A// in CDataTransferer.java.
4632N/ANSMutableDictionary *sStandardMappings = nil;
4632N/A
4632N/ANSMutableDictionary *getMappingTable() {
4632N/A if (sStandardMappings == nil) {
4632N/A sStandardMappings = [[NSMutableDictionary alloc] init];
4632N/A [sStandardMappings setObject:NSStringPboardType
4632N/A forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_STRING]];
4632N/A [sStandardMappings setObject:NSFilenamesPboardType
4632N/A forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_FILE]];
4632N/A [sStandardMappings setObject:NSTIFFPboardType
4632N/A forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_TIFF]];
4632N/A [sStandardMappings setObject:NSRTFPboardType
4632N/A forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_RICH_TEXT]];
4632N/A [sStandardMappings setObject:NSHTMLPboardType
4632N/A forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_HTML]];
4632N/A [sStandardMappings setObject:NSPDFPboardType
4632N/A forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_PDF]];
4632N/A [sStandardMappings setObject:NSURLPboardType
4632N/A forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_URL]];
4632N/A }
4632N/A return sStandardMappings;
4632N/A}
4632N/A
4632N/A/*
4632N/A * Convert from a standard NSPasteboard data type to an index in our mapping table.
4632N/A */
4632N/Ajlong indexForFormat(NSString *format) {
4632N/A jlong returnValue = -1;
4632N/A
4632N/A NSMutableDictionary *mappingTable = getMappingTable();
4632N/A NSArray *matchingKeys = [mappingTable allKeysForObject:format];
4632N/A
4632N/A // There should only be one matching key here...
4632N/A if ([matchingKeys count] > 0) {
4632N/A NSNumber *formatID = (NSNumber *)[matchingKeys objectAtIndex:0];
4632N/A returnValue = [formatID longValue];
4632N/A }
4632N/A
4632N/A // If we don't recognize the format, but it's a Java "custom" format register it
4632N/A if (returnValue == -1 && ([format hasPrefix:@"JAVA_DATAFLAVOR:"]) ) {
4632N/A returnValue = registerFormatWithPasteboard(format);
4632N/A }
4632N/A
4632N/A return returnValue;
4632N/A}
4632N/A
4632N/A/*
4632N/A * Inverse of above -- given a long int index, get the matching data format NSString.
4632N/A */
4632N/ANSString *formatForIndex(jlong inFormatCode) {
4632N/A return [getMappingTable() objectForKey:[NSNumber numberWithLong:inFormatCode]];
4632N/A}
4632N/A
4632N/Ajlong registerFormatWithPasteboard(NSString *format) {
4632N/A NSMutableDictionary *mappingTable = getMappingTable();
4632N/A NSUInteger nextID = [mappingTable count] + 1;
4632N/A [mappingTable setObject:format forKey:[NSNumber numberWithLong:nextID]];
4632N/A return nextID;
4632N/A}
4632N/A
4632N/A
4632N/A/*
4632N/A * Class: sun_lwawt_macosx_CDataTransferer
4632N/A * Method: registerFormatWithPasteboard
4632N/A * Signature: (Ljava/lang/String;)J
4632N/A */
4632N/AJNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CDataTransferer_registerFormatWithPasteboard
4632N/A(JNIEnv *env, jobject jthis, jstring newformat)
4632N/A{
4632N/A jlong returnValue = -1;
4632N/AJNF_COCOA_ENTER(env);
4632N/A returnValue = registerFormatWithPasteboard(JNFJavaToNSString(env, newformat));
4632N/AJNF_COCOA_EXIT(env);
4632N/A return returnValue;
4632N/A}
4632N/A
4632N/A/*
4632N/A * Class: sun_lwawt_macosx_CDataTransferer
4632N/A * Method: formatForIndex
4632N/A * Signature: (J)Ljava/lang/String;
4632N/A */
4632N/AJNIEXPORT jstring JNICALL Java_sun_lwawt_macosx_CDataTransferer_formatForIndex
4632N/A (JNIEnv *env, jobject jthis, jlong index)
4632N/A{
4632N/A jstring returnValue = NULL;
4632N/AJNF_COCOA_ENTER(env);
4632N/A returnValue = JNFNSToJavaString(env, formatForIndex(index));
4632N/AJNF_COCOA_EXIT(env);
4632N/A return returnValue;
4632N/A}
4632N/A
4632N/A/*
4632N/A * Class: sun_lwawt_macosx_CDataTransferer
4632N/A * Method: imageDataToPlatformImageBytes
4632N/A * Signature: ([III)[B
4632N/A */
4632N/AJNIEXPORT jbyteArray JNICALL Java_sun_lwawt_macosx_CDataTransferer_imageDataToPlatformImageBytes
4632N/A(JNIEnv *env, jobject obj, jintArray inPixelData, jint inWidth, jint inHeight)
4632N/A{
4632N/A jbyteArray returnValue = nil;
4632N/AJNF_COCOA_ENTER(env);
4632N/A UInt32 *rawImageData = (UInt32 *)(*env)->GetPrimitiveArrayCritical(env, inPixelData, 0);
4632N/A
4632N/A // The pixel data is in premultiplied ARGB format. That's exactly what
4632N/A // we need for the bitmap image rep.
4632N/A if (rawImageData != NULL) {
4632N/A NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
4632N/A pixelsWide:inWidth
4632N/A pixelsHigh:inHeight
4632N/A bitsPerSample:8
4632N/A samplesPerPixel:4
4632N/A hasAlpha:YES
4632N/A isPlanar:NO
4632N/A colorSpaceName:NSCalibratedRGBColorSpace
4632N/A bytesPerRow:(inWidth*4)
4632N/A bitsPerPixel:32];
4632N/A
4632N/A // Conver the ARGB data into RGBA data that the bitmap can draw.
4632N/A unsigned char *destData = [imageRep bitmapData];
4632N/A unsigned char *currentRowBase;
4632N/A jint x, y;
4632N/A
4632N/A for (y = 0; y < inHeight; y++) {
4632N/A currentRowBase = destData + y * (inWidth * 4);
4632N/A unsigned char *currElement = currentRowBase;
4632N/A for (x = 0; x < inWidth; x++) {
4632N/A UInt32 currPixel = rawImageData[y * inWidth + x];
4632N/A *currElement++ = ((currPixel & 0xFF0000) >> 16);
4632N/A *currElement++ = ((currPixel & 0xFF00) >> 8);
4632N/A *currElement++ = (currPixel & 0xFF);
4632N/A *currElement++ = ((currPixel & 0xFF000000) >> 24);
4632N/A }
4632N/A }
4632N/A
4632N/A (*env)->ReleasePrimitiveArrayCritical(env, inPixelData, rawImageData, JNI_ABORT);
4632N/A NSData *tiffImage = [imageRep TIFFRepresentation];
4632N/A jsize tiffSize = (jsize)[tiffImage length]; // #warning 64-bit: -length returns NSUInteger, but NewByteArray takes jsize
4632N/A returnValue = (*env)->NewByteArray(env, tiffSize);
4632N/A jbyte *tiffData = (jbyte *)(*env)->GetPrimitiveArrayCritical(env, returnValue, 0);
4632N/A [tiffImage getBytes:tiffData];
4632N/A (*env)->ReleasePrimitiveArrayCritical(env, returnValue, tiffData, 0); // Do not use JNI_COMMIT, as that will not free the buffer copy when +ProtectJavaHeap is on.
4632N/A [imageRep release];
4632N/A }
4632N/AJNF_COCOA_EXIT(env);
4632N/A return returnValue;
4632N/A
4632N/A}
4632N/A
4632N/Astatic jobject getImageForByteStream(JNIEnv *env, jbyteArray sourceData)
4632N/A{
4632N/A if (sourceData == NULL) return NULL;
4632N/A
4632N/A jsize sourceSize = (*env)->GetArrayLength(env, sourceData);
4632N/A if (sourceSize == 0) return NULL;
4632N/A
4632N/A jbyte *sourceBytes = (*env)->GetPrimitiveArrayCritical(env, sourceData, NULL);
4632N/A NSData *rawData = [NSData dataWithBytes:sourceBytes length:sourceSize];
4632N/A
4632N/A NSImage *newImage = [[NSImage alloc] initWithData:rawData];
4632N/A if (newImage) CFRetain(newImage); // GC
4632N/A [newImage release];
4632N/A
4632N/A (*env)->ReleasePrimitiveArrayCritical(env, sourceData, sourceBytes, JNI_ABORT);
4632N/A
4632N/A if (newImage == nil) return NULL;
4632N/A
4632N/A // The ownership of the NSImage is passed to the new CImage jobject. No need to release it.
4632N/A static JNF_CLASS_CACHE(jc_CImage, "sun/lwawt/macosx/CImage");
4632N/A static JNF_STATIC_MEMBER_CACHE(jm_CImage_getCreator, jc_CImage, "getCreator", "()Lsun/lwawt/macosx/CImage$Creator;");
4632N/A jobject creator = JNFCallStaticObjectMethod(env, jm_CImage_getCreator);
4632N/A
4632N/A static JNF_CLASS_CACHE(jc_CImage_Generator, "sun/lwawt/macosx/CImage$Creator");
4632N/A static JNF_MEMBER_CACHE(jm_CImage_Generator_createImageUsingNativeSize, jc_CImage_Generator, "createImageUsingNativeSize", "(J)Ljava/awt/image/BufferedImage;");
4632N/A return JNFCallObjectMethod(env, creator, jm_CImage_Generator_createImageUsingNativeSize, ptr_to_jlong(newImage)); // AWT_THREADING Safe (known object)
4632N/A}
4632N/A
4632N/A/*
4632N/A * Class: sun_lwawt_macosx_CDataTransferer
4632N/A * Method: getImageForByteStream
4632N/A * Signature: ([B)Ljava/awt/Image;
4632N/A */
4632N/AJNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CDataTransferer_getImageForByteStream
4632N/A (JNIEnv *env, jobject obj, jbyteArray sourceData)
4632N/A{
4632N/A jobject img = NULL;
4632N/AJNF_COCOA_ENTER(env);
4632N/A img = getImageForByteStream(env, sourceData);
4632N/AJNF_COCOA_EXIT(env);
4632N/A return img;
4632N/A}
4632N/A
4632N/Astatic jobjectArray CreateJavaFilenameArray(JNIEnv *env, NSArray *filenameArray)
4632N/A{
4632N/A NSUInteger filenameCount = [filenameArray count];
4632N/A if (filenameCount == 0) return nil;
4632N/A
4632N/A // Get the java.lang.String class object:
4632N/A jclass stringClazz = (*env)->FindClass(env, "java/lang/String"); // can't be null
4632N/A jobject jfilenameArray = (*env)->NewObjectArray(env, filenameCount, stringClazz, NULL); // AWT_THREADING Safe (known object)
4632N/A if ((*env)->ExceptionOccurred(env)) {
4632N/A (*env)->ExceptionDescribe(env);
4632N/A (*env)->ExceptionClear(env);
4632N/A return nil;
4632N/A }
4632N/A if (!jfilenameArray) {
4632N/A NSLog(@"CDataTransferer_CreateJavaFilenameArray: couldn't create jfilenameArray.");
4632N/A return nil;
4632N/A }
4632N/A (*env)->DeleteLocalRef(env, stringClazz);
4632N/A
4632N/A // Iterate through all the filenames:
4632N/A NSUInteger i;
4632N/A for (i = 0; i < filenameCount; i++) {
4632N/A NSMutableString *stringVal = [[NSMutableString alloc] initWithString:[filenameArray objectAtIndex:i]];
4632N/A CFStringNormalize((CFMutableStringRef)stringVal, kCFStringNormalizationFormC);
4632N/A const char* stringBytes = [stringVal UTF8String];
4632N/A
4632N/A // Create a Java String:
4632N/A jstring string = (*env)->NewStringUTF(env, stringBytes);
4632N/A if ((*env)->ExceptionOccurred(env)) {
4632N/A (*env)->ExceptionDescribe(env);
4632N/A (*env)->ExceptionClear(env);
4632N/A continue;
4632N/A }
4632N/A if (!string) {
4632N/A NSLog(@"CDataTransferer_CreateJavaFilenameArray: couldn't create jstring[%lu] for [%@].", (unsigned long) i, stringVal);
4632N/A continue;
4632N/A }
4632N/A
4632N/A // Set the Java array element with our String:
4632N/A (*env)->SetObjectArrayElement(env, jfilenameArray, i, string);
4632N/A if ((*env)->ExceptionOccurred(env)) {
4632N/A (*env)->ExceptionDescribe(env);
4632N/A (*env)->ExceptionClear(env);
4632N/A continue;
4632N/A }
4632N/A
4632N/A // Release local String reference:
4632N/A (*env)->DeleteLocalRef(env, string);
4632N/A }
4632N/A
4632N/A return jfilenameArray;
4632N/A}
4632N/A
4632N/A/*
4632N/A * Class: sun_lwawt_macosx_CDataTransferer
4632N/A * Method: draqQueryFile
4632N/A * Signature: ([B)[Ljava/lang/String;
4632N/A */
4632N/AJNIEXPORT jobjectArray JNICALL
4632N/AJava_sun_lwawt_macosx_CDataTransferer_nativeDragQueryFile
4632N/A(JNIEnv *env, jclass clazz, jbyteArray jbytearray)
4632N/A{
4632N/A // Decodes a byte array into a set of String filenames.
4632N/A // bytes here is an XML property list containing all of the filenames in the drag.
4632N/A // Parse the XML list into strings and return an array of Java strings matching all of the
4632N/A // files in the list.
4632N/A
4632N/A jobjectArray jreturnArray = NULL;
4632N/A
4632N/AJNF_COCOA_ENTER(env);
4632N/A // Get byte array elements:
4632N/A jboolean isCopy;
4632N/A jbyte* jbytes = (*env)->GetByteArrayElements(env, jbytearray, &isCopy);
4632N/A if (jbytes == NULL) {
4632N/A return NULL;
4632N/A }
4632N/A
4632N/A // Wrap jbytes in an NSData object:
4632N/A jsize jbytesLength = (*env)->GetArrayLength(env, jbytearray);
4632N/A NSData *xmlData = [NSData dataWithBytesNoCopy:jbytes length:jbytesLength freeWhenDone:NO];
4632N/A
4632N/A // Create a property list from the Java data:
4632N/A NSString *errString = nil;
4632N/A NSPropertyListFormat plistFormat = 0;
4632N/A id plist = [NSPropertyListSerialization propertyListFromData:xmlData mutabilityOption:NSPropertyListImmutable
4632N/A format:&plistFormat errorDescription:&errString];
4632N/A
4632N/A // The property list must be an array of strings:
4632N/A if (plist == nil || [plist isKindOfClass:[NSArray class]] == FALSE) {
4632N/A NSLog(@"CDataTransferer_dragQueryFile: plist not a valid NSArray (error %@):\n%@", errString, plist);
4632N/A (*env)->ReleaseByteArrayElements(env, jbytearray, jbytes, JNI_ABORT);
4632N/A return NULL;
4632N/A }
4632N/A
4632N/A // Transfer all string items from the plistArray to filenameArray. This wouldn't be necessary
4632N/A // if we could trust the array to contain all valid elements but this way we'll be sure.
4632N/A NSArray *plistArray = (NSArray *)plist;
4632N/A NSUInteger plistItemCount = [plistArray count];
4632N/A NSMutableArray *filenameArray = [[NSMutableArray alloc] initWithCapacity:plistItemCount];
4632N/A
4632N/A NSUInteger i;
4632N/A for (i = 0; i < plistItemCount; i++) {
4632N/A // Filenames must be strings:
4632N/A id idVal = [plistArray objectAtIndex:i];
4632N/A if ([idVal isKindOfClass:[NSString class]] == FALSE) {
4632N/A NSLog(@"CDataTransferer_dragQueryFile: plist[%lu] not an NSString:\n%@", (unsigned long) i, idVal);
4632N/A continue;
4632N/A }
4632N/A
4632N/A [filenameArray addObject:idVal];
4632N/A }
4632N/A
4632N/A // Convert our array of filenames into a Java array of String filenames:
4632N/A jreturnArray = CreateJavaFilenameArray(env, filenameArray);
4632N/A
4632N/A [filenameArray release];
4632N/A
4632N/A // We're done with the jbytes (backing the plist/plistArray):
4632N/A (*env)->ReleaseByteArrayElements(env, jbytearray, jbytes, JNI_ABORT);
4632N/AJNF_COCOA_EXIT(env);
4632N/A return jreturnArray;
4632N/A}