File: PCBundleManager.m

package info (click to toggle)
projectcenter.app 0.6.2%2Bgit20190606-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 2,968 kB
  • sloc: objc: 20,132; ansic: 1,463; makefile: 38
file content (421 lines) | stat: -rw-r--r-- 10,918 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
/*
   GNUstep ProjectCenter - http://www.gnustep.org/experience/ProjectCenter.html

   Copyright (C) 2000-2004 Free Software Foundation

   Authors: Philippe C.D. Robert
            Serg Stoyan

   This file is part of GNUstep.

   This application is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This application is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/

// TODO: Finish support for third party bundles.
//       It involves support for user defined bundle directories
//       through preferences. Now supported are:
//       - PC application resource dir
//         (GNUSTEP_SYSTEM_APPS/ProjectCenter.app/Resources)
//       - GNUSTEP_SYSTEM_LIBRARY/Bundles/ProjectCenter
//         (NSApplicationSupportDirectory)

#import <ProjectCenter/PCBundleManager.h>
#import <ProjectCenter/PCDefines.h>

#import <ProjectCenter/PCLogController.h>

@implementation PCBundleManager

//----------------------------------------------------------------------------
// Init and free methods
//----------------------------------------------------------------------------

- (id)init
{
  if ((self = [super init]))
    {
      loadedBundles = [[NSMutableDictionary alloc] init];
      bundlesInfo = [[NSMutableDictionary alloc] init];
    }

  return self;
}

- (void)dealloc
{
  RELEASE(loadedBundles);
  RELEASE(bundlesInfo);

  [super dealloc];
}

//
- (NSString *)resourcePath
{
  NSString *path = [[NSBundle mainBundle] resourcePath];

  if (![[NSFileManager defaultManager] fileExistsAtPath:path]) 
    {
      [NSException raise:@"PCBundleManagerPathException" 
	          format:@"ProjectCenter installed incorrectly"];
      return nil;
    }

  return path;
}

// --- Handling of bundles' Info.table dictionaries without actual
// --- bundles loading

// bundlesInfo is a dictionary. key/value pair is the following:
//  (NSString *)              (NSDictionary *)
// "full path of a bundle" = "Info.table contents"
- (NSDictionary *)infoForBundlesType:(NSString *)extension
{
  NSArray             *bundles;
  NSEnumerator        *enumerator;
  NSString            *bundlePath;
  NSString            *infoTablePath;
  NSDictionary        *infoTable;
  NSMutableDictionary *reqBundlesInfo;

  reqBundlesInfo = [NSMutableDictionary dictionary];
  bundles = [NSBundle pathsForResourcesOfType:extension 
                                  inDirectory:[self resourcePath]];
  enumerator = [bundles objectEnumerator];

  while ((bundlePath = [enumerator nextObject]))
    {
      infoTablePath = [NSString 
	stringWithFormat:@"%@/Resources/Info.table", bundlePath];
      // TODO: fill 'reqBundlesInfo' with element from 'bundlesInfo' if
      // exists
      infoTable = [NSDictionary dictionaryWithContentsOfFile:infoTablePath];
      [reqBundlesInfo setObject:infoTable forKey:bundlePath];
      [bundlesInfo setObject:infoTable forKey:bundlePath];
    }

  return reqBundlesInfo;
}

// Key value can be checked against NSString and NSArray values only.
- (NSDictionary *)infoForBundleType:(NSString *)extension
			    keyName:(NSString *)key
			keyContains:(NSString *)value
{
  NSDictionary *reqBundlesInfo;
  NSEnumerator *enumerator;
  NSString     *bundlePath;
  id           keyValue;
  NSDictionary *infoTable;

  if (extension == nil)
    {
      return nil;
    }

  reqBundlesInfo = [self infoForBundlesType:extension];
  enumerator = [[reqBundlesInfo allKeys] objectEnumerator];

  infoTable = nil;
  while ((bundlePath = [enumerator nextObject]))
    {
      infoTable = [reqBundlesInfo objectForKey:bundlePath];

      if (key == nil || value == nil)
	{
	  break;
	}

      keyValue = [infoTable objectForKey:key];

      if ([keyValue isKindOfClass:[NSString class]] &&
	  [keyValue isEqualToString:value])
	{
	  break;
	}
      else if ([keyValue isKindOfClass:[NSArray class]] &&
	       [keyValue containsObject:value])
	{
	  break;
	}
      else
	{
	  infoTable = nil;
	}
    }

  return infoTable;
}

- (NSDictionary *)infoForBundleName:(NSString *)name
			       type:(NSString *)type
{
  NSDictionary *reqBundlesInfo = [self infoForBundlesType:type];
  NSEnumerator *enumerator = [[reqBundlesInfo allKeys] objectEnumerator];
  NSString     *bundlePath;
  NSDictionary *infoTable;

  infoTable = nil;
  while ((bundlePath = [enumerator nextObject]))
    {
      infoTable = [reqBundlesInfo objectForKey:bundlePath];
      if ([[infoTable objectForKey:@"Name"] isEqualToString:name])
	{
	  break;
	}
      else
	{
	  infoTable = nil;
	}
    }

  return infoTable;
}

- (NSString *)classNameForBundleType:(NSString*)type 
			    fileName:(NSString *)fileName
{
  NSString     *fileExtension = [fileName pathExtension];
  NSDictionary *infoTable = nil;
  NSString     *className = nil;

  infoTable = [self infoForBundleType:type
			      keyName:@"FileTypes"
			  keyContains:fileExtension];

  className = [infoTable objectForKey:@"PrincipalClassName"];

  return className;
}

- (NSString *)bundlePathWithName:(NSString *)bundleName
{
  NSArray      *bundlePaths = nil;
  NSString     *bundleFullPath = nil;
  NSEnumerator *enumerator = nil;

  // Search for bundle full path in bundlesInfo dictionary
  bundlePaths = [bundlesInfo allKeys];
  enumerator = [bundlePaths objectEnumerator];

//  NSLog(@"Bundle fullpath method #1: %@", 
//	[[self resourcePath] stringByAppendingPathComponent:bundleName]);

  while ((bundleFullPath = [enumerator nextObject]))
    {
      if ([[bundleFullPath lastPathComponent] isEqualToString:bundleName])
	{
	  break;
	}
    }

//  NSLog(@"Bundle fullpath method #2: %@", bundleFullPath);

  return bundleFullPath;
}

// --- Invokes loading of bundle

- (id)objectForClassName:(NSString *)className
	      bundleType:(NSString *)bundleExtension
		protocol:(Protocol *)proto
{
  Class objectClass;

  if (!className)
    {
      NSLog(@"Bundle for class called with empty className");
      return nil;
    }

  if ([self bundleOfType:bundleExtension withClassName:className] == nil)
    {
      NSLog(@"Bundle for class %@ NOT FOUND!", className);
      return nil;
    }

  objectClass = NSClassFromString(className);

  if (proto != nil && ![objectClass conformsToProtocol:proto])
    {
      [NSException raise:NOT_A_PROJECT_TYPE_EXCEPTION 
	          format:@"%@ does not conform to protocol!", className];
      return nil;
    }

  return [[objectClass alloc] init];
}

- (id)objectForBundleWithName:(NSString *)name
			 type:(NSString *)extension
		     protocol:(Protocol *)proto
{
  NSDictionary *infoTable;
  NSString     *className;

  infoTable = [self infoForBundleName:name type:extension];
  className = [infoTable objectForKey:@"PrincipalClassName"];

  return [self objectForClassName:className 
		       bundleType:extension
			 protocol:proto];
}

- (id)objectForBundleType:(NSString *)extension
		 protocol:(Protocol *)proto
		 fileName:(NSString *)fileName
{
  NSString     *className;

  className = [self classNameForBundleType:extension fileName:fileName];

  return [self objectForClassName:className 
		       bundleType:extension
			 protocol:proto];
}

// --- Bundle loading

- (NSBundle *)bundleOfType:(NSString *)type
	     withClassName:(NSString *)className
{
  NSArray      *bundlePaths = nil;
  NSString     *bundleFullPath = nil;
  NSDictionary *infoTable = nil;
  NSEnumerator *enumerator = nil;
  NSString     *bundleName = nil;
  NSString     *principalClass;

  // Search for bundle full path in bundlesInfo dictionary
  bundlePaths = [bundlesInfo allKeys];
  enumerator = [bundlePaths objectEnumerator];
  
  while ((bundleFullPath = [enumerator nextObject]))
    {

      if ([[bundleFullPath pathExtension] isEqualToString:type])
	{
	  infoTable = [bundlesInfo objectForKey:bundleFullPath];
	  principalClass = [infoTable objectForKey:@"PrincipalClassName"];
	  if ([className isEqualToString:principalClass])
	    {
	      break;
	    }
	}
    }

//  NSLog(@"bundleForClassName: %@ path %@", className, bundleFullPath);

  // Extract from full bundle path it's name and extension
  bundleName = [bundleFullPath lastPathComponent];
  if (![self loadBundleIfNeededWithName:bundleName])
    {
      return nil;
    }

  return [loadedBundles objectForKey:bundleFullPath];
}

- (BOOL)loadBundleIfNeededWithName:(NSString *)bundleName
{
  NSString *bundleFullPath = [self bundlePathWithName:bundleName];

  // Check if bundle allready loaded
  if ([[loadedBundles allKeys] containsObject:bundleFullPath] == NO)
    {
      return [self loadBundleWithFullPath:bundleFullPath];
    }
  
  return YES;
}

- (void)loadBundlesWithExtension:(NSString *)extension
{
  NSEnumerator	*enumerator;
  NSFileManager	*fileManager = [NSFileManager defaultManager];
  BOOL 		isDir;
  NSString      *path = [self resourcePath];

  if (path)
    {
      [self loadBundlesAtPath:path withExtension:extension];
    }
 
  // Load third party bundles
  enumerator = [NSSearchPathForDirectoriesInDomains
		 (NSApplicationSupportDirectory, NSAllDomainsMask, YES) 
		 objectEnumerator];
  while ((path = [enumerator nextObject]) != nil)
    {
      path = [path stringByAppendingPathComponent:@"ProjectCenter"];

      if ([fileManager fileExistsAtPath:path isDirectory:&isDir] && isDir)
	{
	  PCLogInfo(self, @"Loading bundles at %@", path);
	  [self loadBundlesAtPath:path withExtension:extension];
	}
    }
}

- (void)loadBundlesAtPath:(NSString *)path withExtension:(NSString *)extension
{
  NSEnumerator *enumerator;
  NSString     *bundleName;
  NSArray      *dir;

  dir = [[NSFileManager defaultManager] directoryContentsAtPath:path];
  enumerator = [dir objectEnumerator];

  while ((bundleName = [enumerator nextObject]))
    {
      if ([[bundleName pathExtension] isEqualToString:extension]) 
	{
	  NSString *fullPath = nil;

	  fullPath = [NSString stringWithFormat:@"%@/%@", path, bundleName];

	  [self loadBundleWithFullPath:fullPath];
	}
    }
}

- (BOOL)loadBundleWithFullPath:(NSString *)path
{
  NSBundle *bundle = nil;

  if ((bundle = [NSBundle bundleWithPath:path]) && [bundle load])
    {
      [loadedBundles setObject:bundle forKey:path];

      PCLogInfo(self, @"Bundle %@ successfully loaded!", path);
    }
  else 
    {
      NSRunAlertPanel(@"Load Bundle",
		      @"Could not load bundle %@!",
		      @"OK", nil, nil, path);
      return NO;
    }

  return YES;
}

- (NSDictionary *)loadedBundles
{
  return loadedBundles;
}

@end