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 "jni_util.h"
  30 
  31 #import <JavaNativeFoundation/JavaNativeFoundation.h>
  32 
  33 
  34 // ***** NOTE ***** This dictionary corresponds to the static array predefinedClipboardNames
  35 // in CDataTransferer.java.
  36 NSMutableDictionary *sStandardMappings = nil;
  37 
  38 NSMutableDictionary *getMappingTable() {
  39     if (sStandardMappings == nil) {
  40         sStandardMappings = [[NSMutableDictionary alloc] init];
  41         [sStandardMappings setObject:NSStringPboardType
  42                               forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_STRING]];
  43         [sStandardMappings setObject:NSFilenamesPboardType
  44                               forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_FILE]];
  45         [sStandardMappings setObject:NSTIFFPboardType
  46                               forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_TIFF]];
  47         [sStandardMappings setObject:NSRTFPboardType
  48                               forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_RICH_TEXT]];
  49         [sStandardMappings setObject:NSHTMLPboardType
  50                               forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_HTML]];
  51         [sStandardMappings setObject:NSPDFPboardType
  52                               forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_PDF]];
  53         [sStandardMappings setObject:NSURLPboardType
  54                               forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_URL]];
  55         [sStandardMappings setObject:NSPasteboardTypePNG
  56                               forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_PNG]];
  57         [sStandardMappings setObject:(NSString*)kUTTypeJPEG
  58                               forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_JPEG]];
  59         [sStandardMappings setObject:NSPICTPboardType
  60                                        forKey:[NSNumber numberWithLong:sun_lwawt_macosx_CDataTransferer_CF_XPICT]];
  61     }
  62     return sStandardMappings;
  63 }
  64 
  65 /*
  66  * Convert from a standard NSPasteboard data type to an index in our mapping table.
  67  */
  68 jlong indexForFormat(NSString *format) {
  69     jlong returnValue = -1;
  70 
  71     NSMutableDictionary *mappingTable = getMappingTable();
  72     NSArray *matchingKeys = [mappingTable allKeysForObject:format];
  73 
  74     // There should only be one matching key here...
  75     if ([matchingKeys count] > 0) {
  76         NSNumber *formatID = (NSNumber *)[matchingKeys objectAtIndex:0];
  77         returnValue = [formatID longValue];
  78     }
  79 
  80     // If we don't recognize the format, but it's a Java "custom" format register it
  81     if (returnValue == -1 && ([format hasPrefix:@"JAVA_DATAFLAVOR:"]) ) {
  82         returnValue = registerFormatWithPasteboard(format);
  83     }
  84 
  85     return returnValue;
  86 }
  87 
  88 /*
  89  * Inverse of above -- given a long int index, get the matching data format NSString.
  90  */
  91 NSString *formatForIndex(jlong inFormatCode) {
  92     return [getMappingTable() objectForKey:[NSNumber numberWithLong:inFormatCode]];
  93 }
  94 
  95 jlong registerFormatWithPasteboard(NSString *format) {
  96     NSMutableDictionary *mappingTable = getMappingTable();
  97     NSUInteger nextID = [mappingTable count] + 1;
  98     [mappingTable setObject:format forKey:[NSNumber numberWithLong:nextID]];
  99     return nextID;
 100 }
 101 
 102 
 103 /*
 104  * Class:     sun_lwawt_macosx_CDataTransferer
 105  * Method:    registerFormatWithPasteboard
 106  * Signature: (Ljava/lang/String;)J
 107  */
 108 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CDataTransferer_registerFormatWithPasteboard
 109 (JNIEnv *env, jobject jthis, jstring newformat)
 110 {
 111     jlong returnValue = -1;
 112 JNF_COCOA_ENTER(env);
 113     returnValue = registerFormatWithPasteboard(JNFJavaToNSString(env, newformat));
 114 JNF_COCOA_EXIT(env);
 115     return returnValue;
 116 }
 117 
 118 /*
 119  * Class:     sun_lwawt_macosx_CDataTransferer
 120  * Method:    formatForIndex
 121  * Signature: (J)Ljava/lang/String;
 122  */
 123 JNIEXPORT jstring JNICALL Java_sun_lwawt_macosx_CDataTransferer_formatForIndex
 124   (JNIEnv *env, jobject jthis, jlong index)
 125 {
 126     jstring returnValue = NULL;
 127 JNF_COCOA_ENTER(env);
 128     returnValue = JNFNSToJavaString(env, formatForIndex(index));
 129 JNF_COCOA_EXIT(env);
 130     return returnValue;
 131 }
 132 
 133 static jobjectArray CreateJavaFilenameArray(JNIEnv *env, NSArray *filenameArray)
 134 {
 135     NSUInteger filenameCount = [filenameArray count];
 136     if (filenameCount == 0) return nil;
 137 
 138     // Get the java.lang.String class object:
 139     jclass stringClazz = (*env)->FindClass(env, "java/lang/String");
 140     CHECK_NULL_RETURN(stringClazz, nil);
 141     jobject jfilenameArray = (*env)->NewObjectArray(env, filenameCount, stringClazz, NULL); // AWT_THREADING Safe (known object)
 142     if ((*env)->ExceptionOccurred(env)) {
 143         (*env)->ExceptionDescribe(env);
 144         (*env)->ExceptionClear(env);
 145         return nil;
 146     }
 147     if (!jfilenameArray) {
 148         NSLog(@"CDataTransferer_CreateJavaFilenameArray: couldn't create jfilenameArray.");
 149         return nil;
 150     }
 151     (*env)->DeleteLocalRef(env, stringClazz);
 152 
 153     // Iterate through all the filenames:
 154     NSUInteger i;
 155     for (i = 0; i < filenameCount; i++) {
 156         NSMutableString *stringVal = [[NSMutableString alloc] initWithString:[filenameArray objectAtIndex:i]];
 157         CFStringNormalize((CFMutableStringRef)stringVal, kCFStringNormalizationFormC);
 158         const char* stringBytes = [stringVal UTF8String];
 159 
 160         // Create a Java String:
 161         jstring string = (*env)->NewStringUTF(env, stringBytes);
 162         if ((*env)->ExceptionOccurred(env)) {
 163             (*env)->ExceptionDescribe(env);
 164             (*env)->ExceptionClear(env);
 165             continue;
 166         }
 167         if (!string) {
 168             NSLog(@"CDataTransferer_CreateJavaFilenameArray: couldn't create jstring[%lu] for [%@].", (unsigned long) i, stringVal);
 169             continue;
 170         }
 171 
 172         // Set the Java array element with our String:
 173         (*env)->SetObjectArrayElement(env, jfilenameArray, i, string);
 174         if ((*env)->ExceptionOccurred(env)) {
 175             (*env)->ExceptionDescribe(env);
 176             (*env)->ExceptionClear(env);
 177             continue;
 178         }
 179 
 180         // Release local String reference:
 181         (*env)->DeleteLocalRef(env, string);
 182     }
 183 
 184     return jfilenameArray;
 185 }
 186 
 187 /*
 188  * Class:     sun_lwawt_macosx_CDataTransferer
 189  * Method:    draqQueryFile
 190  * Signature: ([B)[Ljava/lang/String;
 191  */
 192 JNIEXPORT jobjectArray JNICALL
 193 Java_sun_lwawt_macosx_CDataTransferer_nativeDragQueryFile
 194 (JNIEnv *env, jclass clazz, jbyteArray jbytearray)
 195 {
 196     // Decodes a byte array into a set of String filenames.
 197     // bytes here is an XML property list containing all of the filenames in the drag.
 198     // Parse the XML list into strings and return an array of Java strings matching all of the
 199     // files in the list.
 200 
 201     jobjectArray jreturnArray = NULL;
 202 
 203 JNF_COCOA_ENTER(env);
 204     // Get byte array elements:
 205     jboolean isCopy;
 206     jbyte* jbytes = (*env)->GetByteArrayElements(env, jbytearray, &isCopy);
 207     if (jbytes == NULL) {
 208         return NULL;
 209     }
 210 
 211     // Wrap jbytes in an NSData object:
 212     jsize jbytesLength = (*env)->GetArrayLength(env, jbytearray);
 213     NSData *xmlData = [NSData dataWithBytesNoCopy:jbytes length:jbytesLength freeWhenDone:NO];
 214 
 215     // Create a property list from the Java data:
 216     NSString *errString = nil;
 217     NSPropertyListFormat plistFormat = 0;
 218     id plist = [NSPropertyListSerialization propertyListFromData:xmlData mutabilityOption:NSPropertyListImmutable
 219         format:&plistFormat errorDescription:&errString];
 220 
 221     // The property list must be an array of strings:
 222     if (plist == nil || [plist isKindOfClass:[NSArray class]] == FALSE) {
 223         NSLog(@"CDataTransferer_dragQueryFile: plist not a valid NSArray (error %@):\n%@", errString, plist);
 224         (*env)->ReleaseByteArrayElements(env, jbytearray, jbytes, JNI_ABORT);
 225         return NULL;
 226     }
 227 
 228     // Transfer all string items from the plistArray to filenameArray. This wouldn't be necessary
 229     // if we could trust the array to contain all valid elements but this way we'll be sure.
 230     NSArray *plistArray = (NSArray *)plist;
 231     NSUInteger plistItemCount = [plistArray count];
 232     NSMutableArray *filenameArray = [[NSMutableArray alloc] initWithCapacity:plistItemCount];
 233 
 234     NSUInteger i;
 235     for (i = 0; i < plistItemCount; i++) {
 236         // Filenames must be strings:
 237         id idVal = [plistArray objectAtIndex:i];
 238         if ([idVal isKindOfClass:[NSString class]] == FALSE) {
 239             NSLog(@"CDataTransferer_dragQueryFile: plist[%lu] not an NSString:\n%@", (unsigned long) i, idVal);
 240             continue;
 241         }
 242 
 243         [filenameArray addObject:idVal];
 244     }
 245 
 246     // Convert our array of filenames into a Java array of String filenames:
 247     jreturnArray = CreateJavaFilenameArray(env, filenameArray);
 248 
 249     [filenameArray release];
 250 
 251     // We're done with the jbytes (backing the plist/plistArray):
 252     (*env)->ReleaseByteArrayElements(env, jbytearray, jbytes, JNI_ABORT);
 253 JNF_COCOA_EXIT(env);
 254     return jreturnArray;
 255 }