1 /*
   2  * Copyright (c) 2012, 2013, 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 "iPodAccess.h"
  27 
  28 @implementation IPodAccess
  29 
  30 
  31 @synthesize query;
  32 
  33 
  34 static NSDictionary *dictPredicateKeys;
  35 static NSDictionary *dictGroupingKeys;
  36 
  37 // initialize static immutable look-up tables that associates Java predicate keys to
  38 // MPMediaProperty strings and Java grouping types to MPMediaGrouping
  39 
  40 // dictionary values must correspond to their Java counterparts!
  41 
  42 + (void) initialize {
  43 
  44     // we need to initialize the dictionary exactly once
  45     if (self == [IPodAccess class]) {
  46 
  47         dictPredicateKeys = [[NSDictionary alloc] initWithObjectsAndKeys:
  48 
  49                              MPMediaItemPropertyMediaType, [NSNumber numberWithInt: 0],
  50                              MPMediaItemPropertyTitle, [NSNumber numberWithInt: 1],
  51                              MPMediaItemPropertyAlbumTitle, [NSNumber numberWithInt: 2],
  52                              MPMediaItemPropertyArtist, [NSNumber numberWithInt: 3],
  53                              MPMediaItemPropertyAlbumArtist, [NSNumber numberWithInt: 4],
  54                              MPMediaItemPropertyGenre, [NSNumber numberWithInt: 5],
  55                              MPMediaItemPropertyComposer, [NSNumber numberWithInt: 6],
  56                              MPMediaItemPropertyIsCompilation, [NSNumber numberWithInt: 7],
  57                              nil];
  58 
  59         // probably replace this table with a trivial switch/case/case... statement
  60         dictGroupingKeys = [[NSDictionary alloc] initWithObjectsAndKeys:
  61 
  62                             [NSNumber numberWithInt: MPMediaGroupingTitle], [NSNumber numberWithInt: 0],
  63                             [NSNumber numberWithInt: MPMediaGroupingAlbum], [NSNumber numberWithInt: 1],
  64                             [NSNumber numberWithInt: MPMediaGroupingArtist], [NSNumber numberWithInt: 2],
  65                             [NSNumber numberWithInt: MPMediaGroupingAlbumArtist], [NSNumber numberWithInt: 3],
  66                             [NSNumber numberWithInt: MPMediaGroupingComposer], [NSNumber numberWithInt: 4],
  67                             [NSNumber numberWithInt: MPMediaGroupingGenre], [NSNumber numberWithInt: 5],
  68                             [NSNumber numberWithInt: MPMediaGroupingPlaylist], [NSNumber numberWithInt: 6],
  69                             [NSNumber numberWithInt: MPMediaGroupingPodcastTitle], [NSNumber numberWithInt: 7],
  70                             nil];
  71     }
  72 }
  73 
  74 // instance methods
  75 
  76 - (id) init {
  77     self = [super init];
  78     return self;
  79 }
  80 
  81 - (void) createQuery {
  82     MPMediaQuery* newQuery = [[MPMediaQuery alloc] init];
  83     [self setQuery: newQuery];
  84 }
  85 
  86 - (NSString *) predicateKeyToMediaItemProperty: (int) predicateKey {
  87     NSNumber *key = [NSNumber numberWithInt: predicateKey];
  88     return (NSString *) [dictPredicateKeys objectForKey: key];
  89 }
  90 
  91 - (MPMediaGrouping) groupingKeyToMediaGrouping: (int) groupingKey {
  92     NSNumber *key = [NSNumber numberWithInt: groupingKey];
  93     NSNumber *value = (NSNumber *) [dictGroupingKeys objectForKey: key];
  94 
  95     return [value integerValue];
  96 }
  97 
  98 - (void) addNumberPredicateForKey: (int) predicateKey
  99                             value: (int) predicateValue {
 100     NSString *propertyName = [self predicateKeyToMediaItemProperty: predicateKey];
 101     NSNumber *propertyValue = [NSNumber numberWithInt: predicateValue];
 102 
 103     MPMediaPropertyPredicate *predicate =
 104     [MPMediaPropertyPredicate predicateWithValue: propertyValue
 105                                      forProperty: propertyName];
 106 
 107     [[self query] addFilterPredicate: predicate];
 108 }
 109 
 110 - (void) addStringPredicateForKey: (int) predicateKey
 111                             value: (NSString *) predicateValue {
 112     NSString *propertyName = [self predicateKeyToMediaItemProperty: predicateKey];
 113 
 114     MPMediaPropertyPredicate *predicate =
 115     [MPMediaPropertyPredicate predicateWithValue: predicateValue
 116                                      forProperty: propertyName];
 117 
 118     [[self query] addFilterPredicate: predicate];
 119 }
 120 
 121 - (jstring) createJavaString: (NSString *) nsString
 122                       JNIEnv: (JNIEnv *) env {
 123     const char *cString = [nsString UTF8String];
 124     return (jstring) (*env)->NewStringUTF(env, cString);
 125 }
 126 
 127 - (jstring) javaStringForProperty: (NSString *) propertyName
 128                         MediaItem: (MPMediaItem *) item
 129                            JNIEnv: (JNIEnv *) env {
 130     NSString* nsString = (NSString *) [item valueForProperty: propertyName];
 131     return [self createJavaString: nsString
 132                            JNIEnv: env];
 133 }
 134 
 135 // note: method IDs should be created outside of this method so that it's not repeated over and over
 136 - (jobject) createDate: (NSDate *) date
 137                 JNIEnv: (JNIEnv *) env {
 138 
 139     jclass calendarClass = (*env)->FindClass(env, "java/util/GregorianCalendar");
 140     jmethodID constructorID = (*env)->GetMethodID(env, calendarClass, "<init>", "(III)V");
 141 
 142     NSDateComponents *components = [[NSCalendar currentCalendar]
 143                                     components: NSDayCalendarUnit |
 144                                                 NSMonthCalendarUnit |
 145                                                 NSYearCalendarUnit
 146                                     fromDate: date];
 147 
 148     jint day = (jint) [components day];
 149     // Java Calendar counts months from zero!
 150     jint month = (jint) ([components month] - 1);
 151     jint year = (jint) [components year];
 152 
 153     jobject jDate = (*env)->NewObject(env, calendarClass, constructorID, year, month, day);
 154 
 155     (*env)->DeleteLocalRef(env, calendarClass);
 156 
 157     return jDate;
 158 }
 159 
 160 // note: method IDs should be created outside of this method so that it's not repeated over and over
 161 - (jobject) createDuration: (NSNumber *) seconds
 162                     JNIEnv: (JNIEnv *) env {
 163     jclass durationClass = (*env)->FindClass(env, "javafx/util/Duration");
 164     jmethodID constructorID = (*env)->GetMethodID(env, durationClass, "<init>", "(D)V");
 165 
 166     double millis = 1000.0 * [seconds doubleValue];
 167     jobject jDuration = (*env)->NewObject(env, durationClass, constructorID, (jdouble) millis);
 168 
 169     (*env)->DeleteLocalRef(env, durationClass);
 170 
 171     return jDuration;
 172 }
 173 
 174 // note: get all the method IDs outside of this method, so that it's not repeated for every media item
 175 - (jobject) createMediaItem: (MPMediaItem *) item
 176                      JNIEnv: (JNIEnv *) env {
 177     jclass classMediaItem = (*env)->FindClass(env, CLASS_MEDIA_ITEM);
 178     jobject refMediaItem = (*env)->NewGlobalRef(env, classMediaItem);
 179     jmethodID midConstructor = (*env)->GetMethodID(env, refMediaItem, "<init>", "()V");
 180     jobject objMediaItem = (*env)->NewObject(env, refMediaItem, midConstructor);
 181 
 182     // set playback duration (Duration)
 183     jmethodID midSetPlaybackDuration = (*env)->GetMethodID(env, refMediaItem, "setPlaybackDuration", "(Ljavafx/util/Duration;)V");
 184     NSNumber *nsDuration = (NSNumber *) [item valueForProperty: MPMediaItemPropertyPlaybackDuration];
 185     jobject jDuration = [self createDuration: nsDuration JNIEnv: env];
 186     (*env)->CallVoidMethod(env, objMediaItem, midSetPlaybackDuration, jDuration);
 187 
 188     // set release date (Date)
 189     jmethodID midSetReleaseDate = (*env)->GetMethodID(env, refMediaItem, "setReleaseDate", "(Ljava/util/Calendar;)V");
 190     NSDate *nsDate = (NSDate *) [item valueForProperty: MPMediaItemPropertyReleaseDate];
 191     if (nsDate != nil) {
 192         jobject jDate = [self createDate: nsDate JNIEnv: env];
 193         (*env)->CallVoidMethod(env, objMediaItem, midSetReleaseDate, jDate);
 194     }
 195     // set album track number (int)
 196     jmethodID midSetAlbumTrackNumber = (*env)->GetMethodID(env, refMediaItem, "setAlbumTrackNumber", "(I)V");
 197     NSNumber *nsAlbumTrackNumber = (NSNumber *) [item valueForProperty: MPMediaItemPropertyAlbumTrackNumber];
 198     (*env)->CallVoidMethod(env, objMediaItem, midSetAlbumTrackNumber, (jint) [nsAlbumTrackNumber intValue]);
 199     // set album track count (int)
 200     jmethodID midSetAlbumTrackCount = (*env)->GetMethodID(env, refMediaItem, "setAlbumTrackCount", "(I)V");
 201     NSNumber *nsAlbumTrackCount = (NSNumber *) [item valueForProperty: MPMediaItemPropertyAlbumTrackCount];
 202     (*env)->CallVoidMethod(env, objMediaItem, midSetAlbumTrackCount, (jint) [nsAlbumTrackCount intValue]);
 203     // set disc number (int)
 204     jmethodID midSetDiscNumber = (*env)->GetMethodID(env, refMediaItem, "setDiscNumber", "(I)V");
 205     NSNumber *nsDiscNumber = (NSNumber *) [item valueForProperty: MPMediaItemPropertyDiscNumber];
 206     (*env)->CallVoidMethod(env, objMediaItem, midSetDiscNumber, (jint) [nsDiscNumber intValue]);
 207     // set disc count (int)
 208     jmethodID midSetDiscCount = (*env)->GetMethodID(env, refMediaItem, "setDiscCount", "(I)V");
 209     NSNumber *nsDiscCount = (NSNumber *) [item valueForProperty: MPMediaItemPropertyDiscCount];
 210     (*env)->CallVoidMethod(env, objMediaItem, midSetDiscCount, (jint) [nsDiscCount intValue]);
 211     // set BPM (int)
 212     jmethodID midSetBPM = (*env)->GetMethodID(env, refMediaItem, "setBeatsPerMinute", "(I)V");
 213     NSNumber *nsBPM = (NSNumber *) [item valueForProperty: MPMediaItemPropertyBeatsPerMinute];
 214     (*env)->CallVoidMethod(env, objMediaItem, midSetBPM, (jint) [nsBPM intValue]);
 215     // set Is Compilation (boolean)
 216     jmethodID midSetIsCompilation = (*env)->GetMethodID(env, refMediaItem, "setIsCompilation", "(Z)V");
 217     NSNumber *nsIsCompilation = (NSNumber *) [item valueForProperty: MPMediaItemPropertyIsCompilation];
 218     (*env)->CallVoidMethod(env, objMediaItem, midSetIsCompilation, (jboolean) [nsIsCompilation boolValue]);
 219     // set title (String)
 220     jmethodID midSetTitle = (*env)->GetMethodID(env, refMediaItem, "setTitle", "(Ljava/lang/String;)V");
 221     jstring jTitle = [self javaStringForProperty: MPMediaItemPropertyTitle MediaItem: item JNIEnv: env];
 222     (*env)->CallVoidMethod(env, objMediaItem, midSetTitle, jTitle);
 223     // set album title (String)
 224     jmethodID midSetAlbumTitle = (*env)->GetMethodID(env, refMediaItem, "setAlbumTitle", "(Ljava/lang/String;)V");
 225     jstring jAlbumTitle = [self javaStringForProperty: MPMediaItemPropertyAlbumTitle MediaItem: item JNIEnv: env];
 226     (*env)->CallVoidMethod(env, objMediaItem, midSetAlbumTitle, jAlbumTitle);
 227     // set artist (String)
 228     jmethodID midSetArtist = (*env)->GetMethodID(env, refMediaItem, "setArtist", "(Ljava/lang/String;)V");
 229     jstring jArtist = [self javaStringForProperty: MPMediaItemPropertyArtist MediaItem: item JNIEnv: env];
 230     (*env)->CallVoidMethod(env, objMediaItem, midSetArtist, jArtist);
 231     // set album artist (String)
 232     jmethodID midSetAlbumArtist = (*env)->GetMethodID(env, refMediaItem, "setAlbumArtist", "(Ljava/lang/String;)V");
 233     jstring jAlbumArtist = [self javaStringForProperty: MPMediaItemPropertyAlbumArtist MediaItem: item JNIEnv: env];
 234     (*env)->CallVoidMethod(env, objMediaItem, midSetAlbumArtist, jAlbumArtist);
 235     // set genre (String)
 236     jmethodID midSetGenre = (*env)->GetMethodID(env, refMediaItem, "setGenre", "(Ljava/lang/String;)V");
 237     jstring jGenre = [self javaStringForProperty: MPMediaItemPropertyGenre MediaItem: item JNIEnv: env];
 238     (*env)->CallVoidMethod(env, objMediaItem, midSetGenre, jGenre);
 239     // set composer (String)
 240     jmethodID midSetComposer = (*env)->GetMethodID(env, refMediaItem, "setComposer", "(Ljava/lang/String;)V");
 241     jstring jComposer = [self javaStringForProperty: MPMediaItemPropertyComposer MediaItem: item JNIEnv: env];
 242     (*env)->CallVoidMethod(env, objMediaItem, midSetComposer, jComposer);
 243     // set lyrics (String)
 244     jmethodID midSetLyrics = (*env)->GetMethodID(env, refMediaItem, "setLyrics", "(Ljava/lang/String;)V");
 245     jstring jLyrics = [self javaStringForProperty: MPMediaItemPropertyLyrics MediaItem: item JNIEnv: env];
 246     (*env)->CallVoidMethod(env, objMediaItem, midSetLyrics, jLyrics);
 247     // set comments (String)
 248     jmethodID midSetComments = (*env)->GetMethodID(env, refMediaItem, "setComments", "(Ljava/lang/String;)V");
 249     jstring jComments = [self javaStringForProperty: MPMediaItemPropertyComments MediaItem: item JNIEnv: env];
 250     (*env)->CallVoidMethod(env, objMediaItem, midSetComments, jComments);
 251     // set URL (String)
 252     jmethodID midSetURL = (*env)->GetMethodID(env, refMediaItem, "setURL", "(Ljava/lang/String;)V");
 253     NSURL *nsURL = (NSURL *) [item valueForProperty: MPMediaItemPropertyAssetURL];
 254     jstring jURL = [self createJavaString: [nsURL absoluteString] JNIEnv: env];
 255     (*env)->CallVoidMethod(env, objMediaItem, midSetURL, jURL);
 256     // set media type (int)
 257     jmethodID midSetMediaType = (*env)->GetMethodID(env, refMediaItem, "setMediaType", "(I)V");
 258     NSNumber *nsMediaType = (NSNumber *) [item valueForProperty: MPMediaItemPropertyMediaType];
 259     (*env)->CallVoidMethod(env, objMediaItem, midSetMediaType, (jint) [nsMediaType intValue]);
 260     // cleanup
 261     (*env)->DeleteGlobalRef(env, refMediaItem);
 262     (*env)->DeleteLocalRef(env, classMediaItem);
 263     return objMediaItem;
 264 }
 265 
 266 - (void) fillItemListOfMediaQuery: (jobject) obj
 267                            jniEnv: (JNIEnv *) env {
 268     NSArray *items = [[self query] items];
 269 
 270     if ([items count] != 0) {
 271 
 272         // note: this can be done in the constructor, let's not waste time here
 273         jobject jMediaQuery = (*env)->NewGlobalRef(env, obj);
 274         jclass klass = (*env)->GetObjectClass(env, obj);
 275 
 276         jmethodID midAddMediaItem = (*env)->GetMethodID(env,
 277                                                         klass,
 278                                                         "addMediaItem",
 279                                                         "(Lcom/sun/javafx/ext/device/ios/ipod/MediaItem;)V");
 280 
 281         NSEnumerator *enumerator = [items objectEnumerator];
 282 
 283         id item;
 284         while (item = [enumerator nextObject]) {
 285             jobject newMediaItem = [self createMediaItem: (MPMediaItem *) item
 286                                                   JNIEnv: env];
 287             (*env)->CallVoidMethod(env,
 288                                    jMediaQuery,
 289                                    midAddMediaItem,
 290                                    newMediaItem);
 291         }
 292         (*env)->DeleteLocalRef(env, klass);
 293         (*env)->DeleteGlobalRef(env, jMediaQuery);
 294     }
 295 }
 296 
 297 - (jobject) createJavaListWithEnv: (JNIEnv *) env {
 298     jclass listClass = (*env)->FindClass(env, "java/util/LinkedList");
 299         jmethodID constructorID = (*env)->GetMethodID(env, listClass, "<init>", "()V");
 300 
 301         jobject jList = (*env)->NewObject(env, listClass, constructorID);
 302 
 303         (*env)->DeleteLocalRef(env, listClass);
 304 
 305     return jList;
 306 }
 307 
 308 - (void) addItemToList: (jobject) item
 309                   list: (jobject) list
 310                 JNIEnv: (JNIEnv *) env {
 311 
 312     jclass klass = (*env)->GetObjectClass(env, list);
 313 
 314     // note: initialize all method IDs at startup
 315     jmethodID midAddToList = (*env)->GetMethodID(env,
 316                                                  klass,
 317                                                  "add",
 318                                                  "(Ljava/lang/Object;)Z");
 319 
 320     (*env)->CallVoidMethod(env,
 321                            list,
 322                            midAddToList,
 323                            item);
 324 
 325 }
 326 
 327 - (void) fillCollectionsOfMediaQuery: (jobject) obj
 328                               jniEnv: (JNIEnv *) env {
 329     NSArray *collections = [[self query] collections];
 330 
 331     if ([collections count] != 0) {
 332 
 333         // note: this can be done in the constructor, ..
 334         jobject jMediaQuery = (*env)->NewGlobalRef(env, obj);
 335         jclass klass = (*env)->GetObjectClass(env, obj);
 336 
 337         jmethodID midAddCollection = (*env)->GetMethodID(env,
 338                                                          klass,
 339                                                          "addCollection",
 340                                                          "(Ljava/util/List;)V");
 341 
 342         for (MPMediaItemCollection *collection in collections) {
 343 
 344             jobject list = [self createJavaListWithEnv: env];
 345             NSArray *items = [collection items];
 346 
 347             for (MPMediaItem *item in items) {
 348                 jobject newMediaItem = [self createMediaItem: item
 349                                                       JNIEnv: env];
 350                 [self addItemToList: newMediaItem
 351                                list: list
 352                              JNIEnv: env];
 353             }
 354 
 355             (*env)->CallVoidMethod(env,
 356                                    jMediaQuery,
 357                                    midAddCollection,
 358                                    list);
 359         }
 360 
 361         (*env)->DeleteLocalRef(env, klass);
 362         (*env)->DeleteGlobalRef(env, jMediaQuery);
 363 
 364     }
 365 }
 366 
 367 - (void) setGroupingType: (int) type {
 368     MPMediaGrouping groupingType = [self groupingKeyToMediaGrouping: type];
 369     [[self query] setGroupingType: groupingType];
 370 }
 371 
 372 - (void) disposeQuery {
 373     [[self query] release];
 374 }
 375 
 376 
 377 @end