1 /* 2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #import "CDataTransferer.h" 27 #include "sun_lwawt_macosx_CDataTransferer.h" 28 29 #import "JNIUtilities.h" 30 31 // ***** NOTE ***** This dictionary corresponds to the static array predefinedClipboardNames 32 // in CDataTransferer.java. 33 NSMutableDictionary *sStandardMappings = nil; 34 35 NSMutableDictionary *getMappingTable() { 36 if (sStandardMappings == nil) { 37 sStandardMappings = [[NSMutableDictionary alloc] init]; 38 [sStandardMappings setObject:NSStringPboardType 39 forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_STRING]]; 40 [sStandardMappings setObject:NSFilenamesPboardType 41 forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_FILE]]; 42 [sStandardMappings setObject:NSTIFFPboardType 43 forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_TIFF]]; 44 [sStandardMappings setObject:NSRTFPboardType 45 forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_RICH_TEXT]]; 46 [sStandardMappings setObject:NSHTMLPboardType 47 forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_HTML]]; 48 [sStandardMappings setObject:NSPDFPboardType 49 forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_PDF]]; 50 [sStandardMappings setObject:NSURLPboardType 51 forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_URL]]; 52 [sStandardMappings setObject:NSPasteboardTypePNG 53 forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_PNG]]; 54 [sStandardMappings setObject:(NSString*)kUTTypeJPEG 55 forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_JPEG]]; 56 [sStandardMappings setObject:NSPICTPboardType 57 forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_XPICT]]; 58 } 59 return sStandardMappings; 60 } 61 62 /* 63 * Convert from a standard NSPasteboard data type to an index in our mapping table. 64 */ 65 jlong indexForFormat(NSString *format) { 66 jlong returnValue = -1; 67 68 NSMutableDictionary *mappingTable = getMappingTable(); 69 NSArray *matchingKeys = [mappingTable allKeysForObject:format]; 70 71 // There should only be one matching key here... 72 if ([matchingKeys count] > 0) { 73 NSNumber *formatID = (NSNumber *)[matchingKeys objectAtIndex:0]; 74 returnValue = [formatID longValue]; 75 } 76 77 // If we don't recognize the format, but it's a Java "custom" format register it 78 if (returnValue == -1 && ([format hasPrefix:@"JAVA_DATAFLAVOR:"]) ) { 79 returnValue = registerFormatWithPasteboard(format); 80 } 81 82 return returnValue; 83 } 84 85 /* 86 * Inverse of above -- given a long int index, get the matching data format NSString. 87 */ 88 NSString *formatForIndex(jlong inFormatCode) { 89 return [getMappingTable() objectForKey:[NSNumber numberWithLong:inFormatCode]]; 90 } 91 92 jlong registerFormatWithPasteboard(NSString *format) { 93 NSMutableDictionary *mappingTable = getMappingTable(); 94 NSUInteger nextID = [mappingTable count] + 1; 95 [mappingTable setObject:format forKey:[NSNumber numberWithLong:nextID]]; 96 return nextID; 97 } 98 99 100 /* 101 * Class: sun_lwawt_macosx_CDataTransferer 102 * Method: registerFormatWithPasteboard 103 * Signature: (Ljava/lang/String;)J 104 */ 105 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CDataTransferer_registerFormatWithPasteboard 106 (JNIEnv *env, jobject jthis, jstring newformat) 107 { 108 jlong returnValue = -1; 109 JNI_COCOA_ENTER(env); 110 returnValue = registerFormatWithPasteboard(JavaStringToNSString(env, newformat)); 111 JNI_COCOA_EXIT(env); 112 return returnValue; 113 } 114 115 /* 116 * Class: sun_lwawt_macosx_CDataTransferer 117 * Method: formatForIndex 118 * Signature: (J)Ljava/lang/String; 119 */ 120 JNIEXPORT jstring JNICALL Java_sun_lwawt_macosx_CDataTransferer_formatForIndex 121 (JNIEnv *env, jobject jthis, jlong index) 122 { 123 jstring returnValue = NULL; 124 JNI_COCOA_ENTER(env); 125 returnValue = NSStringToJavaString(env, formatForIndex(index)); 126 JNI_COCOA_EXIT(env); 127 return returnValue; 128 } 129 130 static jobjectArray CreateJavaFilenameArray(JNIEnv *env, NSArray *filenameArray) 131 { 132 NSUInteger filenameCount = [filenameArray count]; 133 if (filenameCount == 0) return nil; 134 135 // Get the java.lang.String class object: 136 jclass stringClazz = (*env)->FindClass(env, "java/lang/String"); 137 CHECK_NULL_RETURN(stringClazz, nil); 138 jobject jfilenameArray = (*env)->NewObjectArray(env, filenameCount, stringClazz, NULL); 139 if ((*env)->ExceptionOccurred(env)) { 140 (*env)->ExceptionDescribe(env); 141 (*env)->ExceptionClear(env); 142 return nil; 143 } 144 if (!jfilenameArray) { 145 NSLog(@"CDataTransferer_CreateJavaFilenameArray: couldn't create jfilenameArray."); 146 return nil; 147 } 148 (*env)->DeleteLocalRef(env, stringClazz); 149 150 // Iterate through all the filenames: 151 NSUInteger i; 152 for (i = 0; i < filenameCount; i++) { 153 NSMutableString *stringVal = [[NSMutableString alloc] initWithString:[filenameArray objectAtIndex:i]]; 154 CFStringNormalize((CFMutableStringRef)stringVal, kCFStringNormalizationFormC); 155 const char* stringBytes = [stringVal UTF8String]; 156 157 // Create a Java String: 158 jstring string = (*env)->NewStringUTF(env, stringBytes); 159 if ((*env)->ExceptionOccurred(env)) { 160 (*env)->ExceptionDescribe(env); 161 (*env)->ExceptionClear(env); 162 continue; 163 } 164 if (!string) { 165 NSLog(@"CDataTransferer_CreateJavaFilenameArray: couldn't create jstring[%lu] for [%@].", (unsigned long) i, stringVal); 166 continue; 167 } 168 169 // Set the Java array element with our String: 170 (*env)->SetObjectArrayElement(env, jfilenameArray, i, string); 171 if ((*env)->ExceptionOccurred(env)) { 172 (*env)->ExceptionDescribe(env); 173 (*env)->ExceptionClear(env); 174 continue; 175 } 176 177 // Release local String reference: 178 (*env)->DeleteLocalRef(env, string); 179 } 180 181 return jfilenameArray; 182 } 183 184 /* 185 * Class: sun_lwawt_macosx_CDataTransferer 186 * Method: draqQueryFile 187 * Signature: ([B)[Ljava/lang/String; 188 */ 189 JNIEXPORT jobjectArray JNICALL 190 Java_sun_lwawt_macosx_CDataTransferer_nativeDragQueryFile 191 (JNIEnv *env, jclass clazz, jbyteArray jbytearray) 192 { 193 // Decodes a byte array into a set of String filenames. 194 // bytes here is an XML property list containing all of the filenames in the drag. 195 // Parse the XML list into strings and return an array of Java strings matching all of the 196 // files in the list. 197 198 jobjectArray jreturnArray = NULL; 199 200 JNI_COCOA_ENTER(env); 201 // Get byte array elements: 202 jboolean isCopy; 203 jbyte* jbytes = (*env)->GetByteArrayElements(env, jbytearray, &isCopy); 204 if (jbytes == NULL) { 205 return NULL; 206 } 207 208 // Wrap jbytes in an NSData object: 209 jsize jbytesLength = (*env)->GetArrayLength(env, jbytearray); 210 NSData *xmlData = [NSData dataWithBytesNoCopy:jbytes length:jbytesLength freeWhenDone:NO]; 211 212 // Create a property list from the Java data: 213 NSString *errString = nil; 214 NSPropertyListFormat plistFormat = 0; 215 id plist = [NSPropertyListSerialization propertyListFromData:xmlData mutabilityOption:NSPropertyListImmutable 216 format:&plistFormat errorDescription:&errString]; 217 218 // The property list must be an array of strings: 219 if (plist == nil || [plist isKindOfClass:[NSArray class]] == FALSE) { 220 NSLog(@"CDataTransferer_dragQueryFile: plist not a valid NSArray (error %@):\n%@", errString, plist); 221 (*env)->ReleaseByteArrayElements(env, jbytearray, jbytes, JNI_ABORT); 222 return NULL; 223 } 224 225 // Transfer all string items from the plistArray to filenameArray. This wouldn't be necessary 226 // if we could trust the array to contain all valid elements but this way we'll be sure. 227 NSArray *plistArray = (NSArray *)plist; 228 NSUInteger plistItemCount = [plistArray count]; 229 NSMutableArray *filenameArray = [[NSMutableArray alloc] initWithCapacity:plistItemCount]; 230 231 NSUInteger i; 232 for (i = 0; i < plistItemCount; i++) { 233 // Filenames must be strings: 234 id idVal = [plistArray objectAtIndex:i]; 235 if ([idVal isKindOfClass:[NSString class]] == FALSE) { 236 NSLog(@"CDataTransferer_dragQueryFile: plist[%lu] not an NSString:\n%@", (unsigned long) i, idVal); 237 continue; 238 } 239 240 [filenameArray addObject:idVal]; 241 } 242 243 // Convert our array of filenames into a Java array of String filenames: 244 jreturnArray = CreateJavaFilenameArray(env, filenameArray); 245 246 [filenameArray release]; 247 248 // We're done with the jbytes (backing the plist/plistArray): 249 (*env)->ReleaseByteArrayElements(env, jbytearray, jbytes, JNI_ABORT); 250 JNI_COCOA_EXIT(env); 251 return jreturnArray; 252 }