Ticket #313: patch.txt

File patch.txt, 31.9 KB (added by skyy99_1111@…, 2 years ago)

SVN patch file for adding custom movie virtual directories.

Line 
1### Eclipse Workspace Patch 1.0
2#P sapphire-svn
3Index: branches/CoreData/SapphireFrappliance/Sapphire.xcodeproj/project.pbxproj
4===================================================================
5--- branches/CoreData/SapphireFrappliance/Sapphire.xcodeproj/project.pbxproj    (revision 920)
6+++ branches/CoreData/SapphireFrappliance/Sapphire.xcodeproj/project.pbxproj    (working copy)
7@@ -53,6 +53,8 @@
8                38FAF9B60C0129E400853CFE /* BackRow.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38FAF9B50C0129E400853CFE /* BackRow.framework */; };
9                38FAFC320C012A7800853CFE /* SapphireAppliance.m in Sources */ = {isa = PBXBuildFile; fileRef = 38FAFC2F0C012A7800853CFE /* SapphireAppliance.m */; };
10                38FAFC330C012A7800853CFE /* SapphireBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 38FAFC310C012A7800853CFE /* SapphireBrowser.m */; };
11+               5941C04710786EB2004C887B /* SapphireMovieVirtualDirectory.m in Sources */ = {isa = PBXBuildFile; fileRef = 5941C03A10786DD5004C887B /* SapphireMovieVirtualDirectory.m */; };
12+               5941C04810786ECA004C887B /* SapphireMovieVirtualDirectoryImporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5941C03C10786DD5004C887B /* SapphireMovieVirtualDirectoryImporter.m */; };
13                805420AA0F94FA7E002D2DBF /* NSImage-Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 805420A90F94FA7E002D2DBF /* NSImage-Extensions.m */; };
14                8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; };
15                F500400B0D1998B4003FEA08 /* SapphireLeopardOnly.h in Headers */ = {isa = PBXBuildFile; fileRef = F50040090D1998B4003FEA08 /* SapphireLeopardOnly.h */; };
16@@ -360,6 +362,10 @@
17                38FAFC2F0C012A7800853CFE /* SapphireAppliance.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = SapphireAppliance.m; sourceTree = "<group>"; };
18                38FAFC300C012A7800853CFE /* SapphireAppliance.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SapphireAppliance.h; sourceTree = "<group>"; };
19                38FAFC310C012A7800853CFE /* SapphireBrowser.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = SapphireBrowser.m; sourceTree = "<group>"; };
20+               5941C03910786DD5004C887B /* SapphireMovieVirtualDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SapphireMovieVirtualDirectory.h; sourceTree = "<group>"; };
21+               5941C03A10786DD5004C887B /* SapphireMovieVirtualDirectory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SapphireMovieVirtualDirectory.m; sourceTree = "<group>"; };
22+               5941C03B10786DD5004C887B /* SapphireMovieVirtualDirectoryImporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SapphireMovieVirtualDirectoryImporter.h; sourceTree = "<group>"; };
23+               5941C03C10786DD5004C887B /* SapphireMovieVirtualDirectoryImporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SapphireMovieVirtualDirectoryImporter.m; sourceTree = "<group>"; };
24                805420A80F94FA7E002D2DBF /* NSImage-Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage-Extensions.h"; sourceTree = "<group>"; };
25                805420A90F94FA7E002D2DBF /* NSImage-Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSImage-Extensions.m"; sourceTree = "<group>"; };
26                8D5B49B6048680CD000E48DA /* Sapphire.frappliance */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Sapphire.frappliance; sourceTree = BUILT_PRODUCTS_DIR; };
27@@ -993,6 +999,10 @@
28                F5C55E9E0DE90F0D00661563 /* Support */ = {
29                        isa = PBXGroup;
30                        children = (
31+                               5941C03910786DD5004C887B /* SapphireMovieVirtualDirectory.h */,
32+                               5941C03A10786DD5004C887B /* SapphireMovieVirtualDirectory.m */,
33+                               5941C03B10786DD5004C887B /* SapphireMovieVirtualDirectoryImporter.h */,
34+                               5941C03C10786DD5004C887B /* SapphireMovieVirtualDirectoryImporter.m */,
35                                F5C55EC20DE90F3900661563 /* CoreDataSupportFunctions.h */,
36                                F5C55EC30DE90F3900661563 /* CoreDataSupportFunctions.m */,
37                                F5C55EC50DE90F7000661563 /* SapphireBasicDirectoryFunctions.h */,
38@@ -1381,6 +1391,8 @@
39                                F52E4EAA0F43B53F00AC6C52 /* SapphireConfirmPrompt.m in Sources */,
40                                805420AA0F94FA7E002D2DBF /* NSImage-Extensions.m in Sources */,
41                                F5505DC71040864D0064D392 /* SapphireAudioNowPlayingController.m in Sources */,
42+                               5941C04710786EB2004C887B /* SapphireMovieVirtualDirectory.m in Sources */,
43+                               5941C04810786ECA004C887B /* SapphireMovieVirtualDirectoryImporter.m in Sources */,
44                        );
45                        runOnlyForDeploymentPostprocessing = 0;
46                };
47Index: branches/CoreData/SapphireFrappliance/FRAppliance/SapphireApplianceController.m
48===================================================================
49--- branches/CoreData/SapphireFrappliance/FRAppliance/SapphireApplianceController.m     (revision 920)
50+++ branches/CoreData/SapphireFrappliance/FRAppliance/SapphireApplianceController.m     (working copy)
51@@ -469,7 +469,7 @@
52 - (SapphireBrowser *)movieBrowser
53 {
54        BRTexture *predicateGem = [SapphireApplianceController gemForPredicate:[SapphireApplianceController predicate]];
55-       SapphireMovieDirectory *movieDir = [[SapphireMovieDirectory alloc] initWithContext:moc];
56+       SapphireMovieDirectory *movieDir = [[SapphireMovieDirectory alloc] initWithContext:moc appSupportDir:applicationSupportDir()];
57        SapphireBrowser *movieBrowser = [[SapphireBrowser alloc] initWithScene:[self scene] metaData:movieDir];
58        [movieDir release];
59        [movieBrowser setListTitle:BRLocalizedString(@" Movies", nil)];
60Index: branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectory.m
61===================================================================
62--- branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectory.m      (revision 0)
63+++ branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectory.m      (revision 0)
64@@ -0,0 +1,53 @@
65+/*
66+ * SapphireMovieVirtualDirectory.m
67+ * Sapphire
68+ *
69+ * Created by mjacobsen on Oct. 2, 2009.
70+ * Copyright 2007 Sapphire Development Team and/or www.nanopi.net
71+ * All rights reserved.
72+ *
73+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
74+ * General Public License as published by the Free Software Foundation; either version 3 of the License,
75+ * or (at your option) any later version.
76+ *
77+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
78+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
79+ * Public License for more details.
80+ *
81+ * You should have received a copy of the GNU General Public License along with this program; if not,
82+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
83+ */
84+
85+#import "SapphireMovieVirtualDirectory.h"
86+
87+@implementation SapphireMovieVirtualDirectory
88+- (NSString *)getTitle
89+{
90+       return title;
91+}
92+
93+- (NSString *)getDesc
94+{
95+       return desc;
96+}
97+
98+- (NSPredicate *)getPredicate
99+{
100+       return predicate;
101+}
102+
103+- (void)setTitle:(NSString*)v
104+{
105+       title = v;
106+}
107+
108+- (void)setDesc:(NSString*)v
109+{
110+       desc = v;
111+}
112+
113+- (void)setPredicate:(NSPredicate*)v
114+{
115+       predicate = v;
116+}
117+@end
118Index: branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieDirectory.h
119===================================================================
120--- branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieDirectory.h     (revision 920)
121+++ branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieDirectory.h     (working copy)
122@@ -28,8 +28,8 @@
123  */
124 @interface SapphireMovieDirectory : NSObject <SapphireDirectory> {
125        NSManagedObjectContext                  *moc;                           /*!< @brief The context*/
126-       NSArray                                                 *subDirs;                       /*!< @brief The sub-directory objects*/
127-       NSArray                                                 *names;                         /*!< @brief The sub-directory names*/
128+       NSMutableArray                                  *subDirs;                       /*!< @brief The sub-directory objects*/
129+       NSMutableArray                                  *names;                         /*!< @brief The sub-directory names*/
130        Basic_Directory_Function_Instance_Variables
131 }
132 
133@@ -39,6 +39,6 @@
134  * @param context The context which stores the movies
135  * @return The movie directory
136  */
137-- (id)initWithContext:(NSManagedObjectContext *)context;
138+- (id)initWithContext:(NSManagedObjectContext *)context appSupportDir:(NSString *)appSupportDir;
139 
140 @end
141Index: branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectoryImporter.h
142===================================================================
143--- branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectoryImporter.h      (revision 0)
144+++ branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectoryImporter.h      (revision 0)
145@@ -0,0 +1,37 @@
146+/*
147+ * SapphireMovieVirtualDirectoryImporter.h
148+ * Sapphire
149+ *
150+ * Created by mjacobsen on Oct. 2, 2009.
151+ * Copyright 2007 Sapphire Development Team and/or www.nanopi.net
152+ * All rights reserved.
153+ *
154+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
155+ * General Public License as published by the Free Software Foundation; either version 3 of the License,
156+ * or (at your option) any later version.
157+ *
158+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
159+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
160+ * Public License for more details.
161+ *
162+ * You should have received a copy of the GNU General Public License along with this program; if not,
163+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
164+ */
165+
166+/*!
167+ * @brief The importer of movie virtual directory data
168+ *
169+ * This class is a for importing xml movie virtual directory data.  It will read in data stored in XML files for defining custom movie virtual directories.
170+ */
171+@interface SapphireMovieVirtualDirectoryImporter : NSObject {
172+}
173+- (void)importVirtualDirectories:(NSMutableArray *)virtualDirs path:(NSString *)path;
174+- (NSPredicate *)createPredicate:(NSXMLElement *)elem;
175+- (NSPredicate *)createAllPredicate:(NSXMLElement *)elem;
176+- (NSPredicate *)createAnyPredicate:(NSXMLElement *)elem;
177+- (NSPredicate *)createNotPredicate:(NSXMLElement *)elem;
178+- (NSMutableArray *)createPredicateArray:(NSXMLElement *)elem;
179+- (BOOL)isRegexMatch:(NSXMLElement *)elem;
180+- (BOOL)isCaseInsensitiveMatch:(NSXMLElement *)elem;
181+
182+@end
183Index: branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieDirectory.m
184===================================================================
185--- branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieDirectory.m     (revision 920)
186+++ branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieDirectory.m     (working copy)
187@@ -26,7 +26,8 @@
188 #import "CoreDataSupportFunctions.h"
189 #import "SapphireBasicDirectoryFunctionsImports.h"
190 #import "SapphireFileSorter.h"
191-
192+#import "SapphireMovieVirtualDirectoryImporter.h"
193+#import "SapphireMovieVirtualDirectory.h"
194 #import "SapphireMovie.h"
195 #import "SapphireCast.h"
196 #import "SapphireDirector.h"
197@@ -81,14 +82,15 @@
198 
199 @implementation SapphireMovieDirectory
200 
201-- (id)initWithContext:(NSManagedObjectContext *)context
202+- (id)initWithContext:(NSManagedObjectContext *)context appSupportDir:(NSString *)appSupportDir
203 {
204        self = [super init];
205        if(self == nil)
206                return self;
207       
208        moc = [context retain];
209-
210+       
211+       /*Define the static virtual directories*/
212        NSPredicate *allPred = [NSPredicate predicateWithFormat:@"movie != nil"];
213        SapphireFilteredFileDirectory *all = [[SapphireFilteredFileDirectory alloc] initWithPredicate:allPred Context:moc];
214        SapphireEntityDirectory *cast = [[SapphireEntityDirectory alloc] initWithEntityFetch:castEntityFetch inContext:moc];
215@@ -98,24 +100,23 @@
216        SapphireFilteredFileDirectory *top250 = [[SapphireFilteredFileDirectory alloc] initWithPredicate:top250Pred Context:moc];
217        NSPredicate *oscarPred = [NSPredicate predicateWithFormat:@"movie.oscarsWon != 0"];
218        SapphireFilteredFileDirectory *oscar = [[SapphireFilteredFileDirectory alloc] initWithPredicate:oscarPred Context:moc];
219-       
220-       subDirs = [[NSArray alloc] initWithObjects:
221-                          all,
222-                          cast,
223-                          director,
224-                          genre,
225-                          top250,
226-                          oscar,
227-                          nil];
228-       names = [[NSArray alloc] initWithObjects:
229-                          BRLocalizedString( @"All Movies", @"Select all movies" ),
230-                          BRLocalizedString( @"By Cast", @"Select movies based on cast members" ),
231-                          BRLocalizedString( @"By Director", @"Select movies based on director" ),
232-                          BRLocalizedString( @"By Genre", @"Select movies based on genre" ),
233-                          BRLocalizedString( @"IMDB Top 250", @"Show movies in IMDb Top 250 only" ),
234-                          BRLocalizedString( @"Academy Award Winning", @"Show Oscar winning movies only" ),
235-                          nil];
236+
237+       subDirs = [[NSMutableArray alloc] init];
238+       [subDirs addObject:all];
239+       [subDirs addObject:cast];
240+       [subDirs addObject:director];
241+       [subDirs addObject:genre];
242+       [subDirs addObject:top250];
243+       [subDirs addObject:oscar];
244       
245+       names = [[NSMutableArray alloc] init];
246+       [names addObject:BRLocalizedString( @"All Movies", @"Select all movies" )];
247+       [names addObject:BRLocalizedString( @"By Cast", @"Select movies based on cast members" )];
248+       [names addObject:BRLocalizedString( @"By Director", @"Select movies based on director" )];
249+       [names addObject:BRLocalizedString( @"By Genre", @"Select movies based on genre" )];
250+       [names addObject:BRLocalizedString( @"IMDB Top 250", @"Show movies in IMDb Top 250 only" )];
251+       [names addObject:BRLocalizedString( @"Academy Award Winning", @"Show Oscar winning movies only" )];
252+
253        SapphireFileSorter *titleSort = [SapphireMovieTitleSorter sharedInstance];
254        SapphireFileSorter *imdbRankSort = [SapphireMovieIMDBTop250RankSorter sharedInstance];
255        SapphireFileSorter *awardSort = [SapphireMovieAcademyAwardSorter sharedInstance];
256@@ -124,6 +125,24 @@
257       
258        NSString *moviePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"video_H" ofType:@"png"];
259       
260+       /*Import any defined movie virtual directories*/
261+       SapphireMovieVirtualDirectoryImporter *mvdImp = [[SapphireMovieVirtualDirectoryImporter alloc] init];   
262+    NSMutableArray *virtualDirs = [[NSMutableArray alloc] init];
263+       appSupportDir = [appSupportDir stringByAppendingPathComponent:@"virtualDirs.xml"];
264+       [mvdImp importVirtualDirectories:virtualDirs path:appSupportDir];
265+       NSEnumerator *mvdEnum = [virtualDirs objectEnumerator];
266+       SapphireMovieVirtualDirectory *virtualDir;
267+       while((virtualDir = [mvdEnum nextObject]) != nil)
268+       {
269+               SapphireFilteredFileDirectory *custom = [[SapphireFilteredFileDirectory alloc] initWithPredicate:[virtualDir getPredicate] Context:moc];
270+               [subDirs addObject:custom];
271+               [names addObject:BRLocalizedString([virtualDir getTitle], [virtualDir getDesc])];
272+               [custom setPath:[[VIRTUAL_DIR_ROOT_PATH stringByAppendingString:@"/"] stringByAppendingString:[virtualDir getDesc]]];
273+               [custom setCoverArtPath:moviePath]; // Change this to be part of the XML?
274+               [custom setFileSorters:[NSArray arrayWithObjects:titleSort, dateSort, imdbRatingSort, nil]];
275+       }
276+               
277+       /*Finish the static directory setup*/
278        [all setPath:VIRTUAL_DIR_ALL_PATH];
279        [all setCoverArtPath:moviePath];
280        [all setFileSorters:[NSArray arrayWithObjects:titleSort, dateSort, imdbRatingSort, nil]];
281@@ -151,6 +170,8 @@
282        [genre release];
283        [top250 release];
284        [oscar release];
285+       [mvdImp release];
286+       [virtualDirs release];
287        return self;
288 }
289 
290Index: branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectory.h
291===================================================================
292--- branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectory.h      (revision 0)
293+++ branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectory.h      (revision 0)
294@@ -0,0 +1,39 @@
295+/*
296+ * SapphireMovieVirtualDirectory.h
297+ * Sapphire
298+ *
299+ * Created by mjacobsen on Oct. 2, 2009.
300+ * Copyright 2007 Sapphire Development Team and/or www.nanopi.net
301+ * All rights reserved.
302+ *
303+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
304+ * General Public License as published by the Free Software Foundation; either version 3 of the License,
305+ * or (at your option) any later version.
306+ *
307+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
308+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
309+ * Public License for more details.
310+ *
311+ * You should have received a copy of the GNU General Public License along with this program; if not,
312+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
313+ */
314+
315+
316+/*!
317+ * @brief The simple object to hold movie virtual directory data
318+ *
319+ * This class is a value object for holding imported xml movie virtual directory data.
320+ */
321+@interface SapphireMovieVirtualDirectory : NSObject {
322+       NSString *title;
323+       NSString *desc;
324+       NSPredicate *predicate;
325+}
326+- (NSString *)getTitle;
327+- (NSString *)getDesc;
328+- (NSPredicate *)getPredicate;
329+- (void)setTitle:(NSString*)v;
330+- (void)setDesc:(NSString*)v;
331+- (void)setPredicate:(NSPredicate*)v;
332+@end
333+
334Index: branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectoryImporter.m
335===================================================================
336--- branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectoryImporter.m      (revision 0)
337+++ branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMovieVirtualDirectoryImporter.m      (revision 0)
338@@ -0,0 +1,265 @@
339+/*
340+ * SapphireMovieVirtualDirectoryImporter.m
341+ * Sapphire
342+ *
343+ * Created by mjacobsen on Oct. 2, 2009.
344+ * Copyright 2007 Sapphire Development Team and/or www.nanopi.net
345+ * All rights reserved.
346+ *
347+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
348+ * General Public License as published by the Free Software Foundation; either version 3 of the License,
349+ * or (at your option) any later version.
350+ *
351+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
352+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
353+ * Public License for more details.
354+ *
355+ * You should have received a copy of the GNU General Public License along with this program; if not,
356+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
357+ */
358+#import "SapphireMovieVirtualDirectoryImporter.h"
359+#import "SapphireMovieVirtualDirectory.h"
360+#import "SapphireLogging.h"
361+
362+//Attributes
363+#define MOVIE_MATCH_ELEM_QUERY                 @"/virtualDirs/movieMatch"
364+#define MOVIE_MATCH_NAME_ATTRIB                        @"name"
365+#define MOVIE_MATCH_DESCRIPTION_ATTRIB @"description"
366+#define ELEM_TYPE_ATTRIB                               @"type"
367+#define ELEM_TYPE_ATTRIB_CASE_INSENSITIVE @"i"
368+#define ELEM_TYPE_ATTRIB_REGEX                 @"regex"
369+#define ALL_ELEM                                               @"all"
370+#define ANY_ELEM                                               @"any"
371+// in minutes (db stores in seconds) (at least the value specified)
372+#define DURATION_ELEM                                  @"duration"
373+// t, f, y, n, 1, 0
374+#define WATCHED_ELEM                                   @"watched"
375+// MPEG-2, NTSC, 720x480 (16:9), or DivX 5 (Perian, 448 x352, Millions, etc.
376+#define VIDEO_DESCRIPTION_ELEM                 @"videoDescription"
377+// English, Spanish, or MPEG Layer3, Stereo, 44.100 kHz
378+#define AUDIO_DESCRIPTION_ELEM                 @"audioDescription"
379+// English, Spanish, etc.
380+#define SUBTITLES_ELEM                                 @"subtitles"
381+#define TITLE_ELEM                                             @"title"
382+#define PLOT_ELEM                                              @"plot"
383+// standard data format (db stores in unknown int format -- must convert)
384+#define RELEASE_DATE_ELEM                              @"releaseDate"
385+// float 0 - 10 (at least the value specified)
386+#define IMDB_USER_RATING_ELEM                  @"imdbUserRating"
387+// int 1 - 250 (at most the value specified)
388+#define IMDB_TOP_250_ELEM                              @"imdbTop250"
389+// t, f, y, n, 1, 0
390+#define WON_OSCARS_ELEM                                        @"wonOscars"
391+// PG, PG-13, R, G
392+#define MPAA_RATING_ELEM                               @"mpaaRating"
393+#define CAST_ELEM                                              @"cast"
394+#define GENRE_ELEM                                             @"genre"
395+#define DIRECTOR_ELEM                                  @"director"
396+#define NOT_ELEM                                               @"not"
397+
398+@implementation SapphireMovieVirtualDirectoryImporter
399+
400+- (void)importVirtualDirectories:(NSMutableArray *)virtualDirs path:(NSString *)path
401+{
402+       /*Check for XML file*/
403+       SapphireLog(SAPPHIRE_LOG_ALL, SAPPHIRE_LOG_LEVEL_ERROR, @"Looking for file: %@", path);
404+       NSFileManager *fm = [NSFileManager defaultManager];
405+       BOOL xmlPathIsDir = NO;
406+       if(![fm fileExistsAtPath:path isDirectory:&xmlPathIsDir] || xmlPathIsDir)
407+               return;
408+       
409+       /*Read the XML document*/
410+       NSURL *url = [NSURL fileURLWithPath:path];
411+       NSError *error = nil;
412+       NSXMLDocument *document = [[[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyXML error:&error] autorelease];
413+       NSXMLElement *root = [document rootElement];
414+       if(!root)
415+               return;
416+       
417+       /*For each movie virtual directory*/
418+       NSArray *movieMatchNodes = [root nodesForXPath:MOVIE_MATCH_ELEM_QUERY error:&error];
419+       NSEnumerator *nodeEnum = [movieMatchNodes objectEnumerator];
420+       NSXMLElement *matchElem;
421+       NSXMLNode *tmpNode;
422+       while((matchElem = [nodeEnum nextObject]) != nil)
423+       {
424+               /*Create the virtual directory*/
425+               SapphireMovieVirtualDirectory *virtualDir = [[SapphireMovieVirtualDirectory alloc] init];
426+               tmpNode = [matchElem attributeForName:MOVIE_MATCH_NAME_ATTRIB];
427+               if (tmpNode)
428+                       [virtualDir setTitle:[tmpNode stringValue]];
429+               tmpNode = [matchElem attributeForName:MOVIE_MATCH_DESCRIPTION_ATTRIB];
430+               if (tmpNode)
431+                       [virtualDir setDesc:[tmpNode stringValue]];
432+               
433+               /*Build the query recursively (assume every child in the <movieMatch> is grouped in an implicit <all>). */
434+               NSPredicate *matchPred = [self createAllPredicate:matchElem];
435+               if (matchPred != nil)
436+               {
437+                       NSPredicate *moviePred = [NSPredicate predicateWithFormat:@"movie != nil"];
438+                       matchPred = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:moviePred, matchPred, nil]];
439+                       SapphireLog(SAPPHIRE_LOG_ALL, SAPPHIRE_LOG_LEVEL_INFO, @"Creating virtual directory with filter: %@", matchPred);
440+                       [virtualDir setPredicate:matchPred];
441+                       [virtualDirs addObject:virtualDir];
442+               }
443+       }       
444+}
445+
446+- (NSPredicate *)createPredicate:(NSXMLElement *)elem
447+{
448+       /*NOTE: regex MATCHES operators are not officially supported against core data objects -- though
449+        * some have posted success when using regex MATCHES with simple (non-many-to-one) relationships.
450+        * We'll leave it commented out for now.*/
451+       if ([[elem name] caseInsensitiveCompare:ALL_ELEM] == NSOrderedSame)
452+               return [self createAllPredicate:elem];
453+       else if ([[elem name] caseInsensitiveCompare:ANY_ELEM] == NSOrderedSame)
454+               return [self createAnyPredicate:elem];
455+       else if ([[elem name] caseInsensitiveCompare:NOT_ELEM] == NSOrderedSame)
456+               return [self createNotPredicate:elem];
457+//     else if (([[elem name] caseInsensitiveCompare:TITLE_ELEM] == NSOrderedSame) && ([self isRegexMatch:elem]))
458+//             return [NSPredicate predicateWithFormat:@"(movie.title matches %@)", [elem stringValue]];
459+       else if (([[elem name] caseInsensitiveCompare:TITLE_ELEM] == NSOrderedSame) && ([self isCaseInsensitiveMatch:elem]))
460+               return [NSPredicate predicateWithFormat:@"(movie.title like[cd] %@)", [elem stringValue]];
461+       else if ([[elem name] caseInsensitiveCompare:TITLE_ELEM] == NSOrderedSame)
462+               return [NSPredicate predicateWithFormat:@"(movie.title like %@)", [elem stringValue]];
463+//     else if (([[elem name] caseInsensitiveCompare:PLOT_ELEM] == NSOrderedSame) && ([self isRegexMatch:elem]))
464+//             return [NSPredicate predicateWithFormat:@"(movie.plot matches %@)", [elem stringValue]];
465+       else if (([[elem name] caseInsensitiveCompare:PLOT_ELEM] == NSOrderedSame) && ([self isCaseInsensitiveMatch:elem]))
466+               return [NSPredicate predicateWithFormat:@"(movie.plot like[cd] %@)", [elem stringValue]];
467+       else if ([[elem name] caseInsensitiveCompare:PLOT_ELEM] == NSOrderedSame)
468+               return [NSPredicate predicateWithFormat:@"(movie.plot like %@)", [elem stringValue]];
469+//     else if (([[elem name] caseInsensitiveCompare:VIDEO_DESCRIPTION_ELEM] == NSOrderedSame) && ([self isRegexMatch:elem]))
470+//             return [NSPredicate predicateWithFormat:@"(movie.videoDescription matches %@)", [elem stringValue]];
471+       else if (([[elem name] caseInsensitiveCompare:VIDEO_DESCRIPTION_ELEM] == NSOrderedSame) && ([self isCaseInsensitiveMatch:elem]))
472+               return [NSPredicate predicateWithFormat:@"(movie.videoDescription like[cd] %@)", [elem stringValue]];
473+       else if ([[elem name] caseInsensitiveCompare:VIDEO_DESCRIPTION_ELEM] == NSOrderedSame)
474+               return [NSPredicate predicateWithFormat:@"(movie.videoDescription like %@)", [elem stringValue]];
475+//     else if (([[elem name] caseInsensitiveCompare:AUDIO_DESCRIPTION_ELEM] == NSOrderedSame) && ([self isRegexMatch:elem]))
476+//             return [NSPredicate predicateWithFormat:@"(movie.audioDescription matches %@)", [elem stringValue]];
477+       else if (([[elem name] caseInsensitiveCompare:AUDIO_DESCRIPTION_ELEM] == NSOrderedSame) && ([self isCaseInsensitiveMatch:elem]))
478+               return [NSPredicate predicateWithFormat:@"(movie.audioDescription like[cd] %@)", [elem stringValue]];
479+       else if ([[elem name] caseInsensitiveCompare:AUDIO_DESCRIPTION_ELEM] == NSOrderedSame)
480+               return [NSPredicate predicateWithFormat:@"(movie.audioDescription like %@)", [elem stringValue]];
481+//     else if (([[elem name] caseInsensitiveCompare:SUBTITLES_ELEM] == NSOrderedSame) && ([self isRegexMatch:elem]))
482+//             return [NSPredicate predicateWithFormat:@"(movie.subtitlesDescription matches %@)", [elem stringValue]];
483+       else if (([[elem name] caseInsensitiveCompare:SUBTITLES_ELEM] == NSOrderedSame) && ([self isCaseInsensitiveMatch:elem]))
484+               return [NSPredicate predicateWithFormat:@"(movie.subtitlesDescription like[cd] %@)", [elem stringValue]];
485+       else if ([[elem name] caseInsensitiveCompare:SUBTITLES_ELEM] == NSOrderedSame)
486+               return [NSPredicate predicateWithFormat:@"(movie.subtitlesDescription like %@)", [elem stringValue]];
487+//     else if (([[elem name] caseInsensitiveCompare:MPAA_RATING_ELEM] == NSOrderedSame) && ([self isRegexMatch:elem]))
488+//             return [NSPredicate predicateWithFormat:@"(movie.MPAARating matches %@)", [elem stringValue]];
489+       else if (([[elem name] caseInsensitiveCompare:MPAA_RATING_ELEM] == NSOrderedSame) && ([self isCaseInsensitiveMatch:elem]))
490+               return [NSPredicate predicateWithFormat:@"(movie.MPAARating like[cd] %@)", [elem stringValue]];
491+       else if ([[elem name] caseInsensitiveCompare:MPAA_RATING_ELEM] == NSOrderedSame)
492+               return [NSPredicate predicateWithFormat:@"(movie.MPAARating like %@)", [elem stringValue]];
493+//     else if (([[elem name] caseInsensitiveCompare:CAST_ELEM] == NSOrderedSame) && ([self isRegexMatch:elem]))
494+//             return [NSPredicate predicateWithFormat:@"(ANY movie.#cast.name matches %@)", [elem stringValue]];
495+       else if (([[elem name] caseInsensitiveCompare:CAST_ELEM] == NSOrderedSame) && ([self isCaseInsensitiveMatch:elem]))
496+               return [NSPredicate predicateWithFormat:@"(ANY movie.#cast.name like[cd] %@)", [elem stringValue]];
497+       else if ([[elem name] caseInsensitiveCompare:CAST_ELEM] == NSOrderedSame)
498+               return [NSPredicate predicateWithFormat:@"(ANY movie.#cast.name like %@)", [elem stringValue]];
499+//     else if (([[elem name] caseInsensitiveCompare:GENRE_ELEM] == NSOrderedSame) && ([self isRegexMatch:elem]))
500+//             return [NSPredicate predicateWithFormat:@"(ANY movie.genres.name matches %@)", [elem stringValue]];
501+       else if (([[elem name] caseInsensitiveCompare:GENRE_ELEM] == NSOrderedSame) && ([self isCaseInsensitiveMatch:elem]))
502+               return [NSPredicate predicateWithFormat:@"(ANY movie.genres.name like[cd] %@)", [elem stringValue]];
503+       else if ([[elem name] caseInsensitiveCompare:GENRE_ELEM] == NSOrderedSame)
504+               return [NSPredicate predicateWithFormat:@"(ANY movie.genres.name like %@)", [elem stringValue]];
505+//     else if (([[elem name] caseInsensitiveCompare:DIRECTOR_ELEM] == NSOrderedSame) && ([self isRegexMatch:elem]))
506+//             return [NSPredicate predicateWithFormat:@"(ANY movie.directors.name matches %@)", [elem stringValue]];
507+       else if (([[elem name] caseInsensitiveCompare:DIRECTOR_ELEM] == NSOrderedSame) && ([self isCaseInsensitiveMatch:elem]))
508+               return [NSPredicate predicateWithFormat:@"(ANY movie.directors.name like[cd] %@)", [elem stringValue]];
509+       else if ([[elem name] caseInsensitiveCompare:DIRECTOR_ELEM] == NSOrderedSame)
510+               return [NSPredicate predicateWithFormat:@"(ANY movie.directors.name like %@)", [elem stringValue]];
511+       else if (([[elem name] caseInsensitiveCompare:WON_OSCARS_ELEM] == NSOrderedSame) && ([[elem stringValue] boolValue]))
512+               return [NSPredicate predicateWithFormat:@"(movie.oscarsWon != 0)"];
513+       else if (([[elem name] caseInsensitiveCompare:WON_OSCARS_ELEM] == NSOrderedSame) && !([[elem stringValue] boolValue]))
514+               return [NSPredicate predicateWithFormat:@"(movie.oscarsWon == 0)"];
515+       else if (([[elem name] caseInsensitiveCompare:WATCHED_ELEM] == NSOrderedSame) && ([[elem stringValue] boolValue]))
516+               return [NSPredicate predicateWithFormat:@"(movie.watched != 0)"];
517+       else if (([[elem name] caseInsensitiveCompare:WATCHED_ELEM] == NSOrderedSame) && !([[elem stringValue] boolValue]))
518+               return [NSPredicate predicateWithFormat:@"(movie.watched == 0)"];
519+       else if ([[elem name] caseInsensitiveCompare:IMDB_TOP_250_ELEM] == NSOrderedSame)
520+               return [NSPredicate predicateWithFormat:@"(movie.imdbTop250Ranking <= %d AND movie.imdbTop250Ranking > 0)", [[elem stringValue] intValue]];
521+       else if ([[elem name] caseInsensitiveCompare:IMDB_USER_RATING_ELEM] == NSOrderedSame)
522+               return [NSPredicate predicateWithFormat:@"(movie.imdbRating >= %f)", [[elem stringValue] floatValue]];
523+       else if ([[elem name] caseInsensitiveCompare:DURATION_ELEM] == NSOrderedSame)
524+               return [NSPredicate predicateWithFormat:@"(movie.duration >= %f)", [[elem stringValue] floatValue]*60];
525+       else if ([[elem name] caseInsensitiveCompare:RELEASE_DATE_ELEM] == NSOrderedSame)
526+               return [NSPredicate predicateWithFormat:@"(movie.releaseDate >= %@)", [NSDate dateWithNaturalLanguageString:[elem stringValue]]];
527+       else
528+               return nil;
529+}
530+
531+- (NSPredicate *)createAllPredicate:(NSXMLElement *)elem
532+{
533+       NSMutableArray *preds = [self createPredicateArray:elem];
534+       
535+       /*Now set the returned predicates to an AND predicate*/
536+       if ([preds count])
537+               return [NSCompoundPredicate andPredicateWithSubpredicates:preds];
538+       else
539+               return nil;
540+}
541+
542+- (NSPredicate *)createAnyPredicate:(NSXMLElement *)elem
543+{
544+       NSMutableArray *preds = [self createPredicateArray:elem];
545+       
546+       /*Now set the returned predicates to an ANY predicate*/
547+       if ([preds count])
548+               return [NSCompoundPredicate orPredicateWithSubpredicates:preds];
549+       else
550+               return nil;     
551+}
552+
553+- (NSPredicate *)createNotPredicate:(NSXMLElement *)elem
554+{
555+       NSMutableArray *preds = [self createPredicateArray:elem];
556+       
557+       /*Now set the returned predicate to a NOT predicate*/
558+       if ([preds count])
559+               return [NSCompoundPredicate notPredicateWithSubpredicate:(NSPredicate *)[preds objectAtIndex:0]];
560+       else
561+               return nil;     
562+}
563+
564+- (NSMutableArray *)createPredicateArray:(NSXMLElement *)elem
565+{
566+       NSMutableArray *preds = [[NSMutableArray alloc] init];
567+       NSEnumerator *nodeEnum = [[elem children] objectEnumerator];
568+       NSXMLNode *node;
569+       while((node = [nodeEnum nextObject]) != nil)
570+       {
571+               /*Check for type of node*/
572+               if ([node kind] == NSXMLElementKind)
573+               {
574+                       /*Create a predicate and add it our array*/
575+                       NSPredicate *pred = [self createPredicate:(NSXMLElement *)node];
576+                       if (pred != nil)
577+                               [preds addObject:pred];
578+               }
579+       }
580+       return preds;   
581+}
582+
583+- (BOOL)isRegexMatch:(NSXMLElement *)elem
584+{
585+       /* Need to check if this is a regex match. Default to wildcard (NO).
586+        * Only return YES if attribute type="regex" exists. */
587+       NSString *type = [[elem attributeForName:ELEM_TYPE_ATTRIB] stringValue];
588+       if(type!=nil)
589+               return ([type caseInsensitiveCompare:ELEM_TYPE_ATTRIB_REGEX] == NSOrderedSame);
590+       return NO;
591+}
592+
593+- (BOOL)isCaseInsensitiveMatch:(NSXMLElement *)elem
594+{
595+       /* Need to check if this is a case insensitive match. Default to case sensitive (NO).
596+        * Only return YES if attribute type="i" exists. */
597+       NSString *type = [[elem attributeForName:ELEM_TYPE_ATTRIB] stringValue];
598+       if(type!=nil)
599+               return ([type caseInsensitiveCompare:ELEM_TYPE_ATTRIB_CASE_INSENSITIVE] == NSOrderedSame);
600+       return NO;
601+}
602+
603+@end
604\ No newline at end of file