File: NSObject%2BKeyExtraction.m

package info (click to toggle)
gnustep-xcode 0.5.0-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 1,168 kB
  • sloc: objc: 9,258; sh: 61; makefile: 20
file content (405 lines) | stat: -rw-r--r-- 11,181 bytes parent folder | download
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
/*
   Copyright (C) 2024 Free Software Foundation, Inc.

   Written by: Gregory John Casamento <greg.casamento@gmail.com>
   Date: 2024

   This file is part of the GNUstep XCode Library

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

   This library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110 USA.
*/

#import <Foundation/NSObject.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>

#import "NSObject+KeyExtraction.h"
#import "NSString+PBXAdditions.h"

//
// Check dictionary to see if it's equvalent...
//
@interface NSDictionary (Private)
- (BOOL) isEqualDictionary: (NSDictionary *)dict;
@end

@implementation NSDictionary (Private)
- (BOOL) isEqualDictionary: (NSDictionary *)dict
{
  NSEnumerator *en = [self keyEnumerator];
  id k = nil;
  BOOL result = YES;

  while ((k = [en nextObject]) != nil)
    {
      id v1 = [self objectForKey: k];
      id v2 = [dict objectForKey: k];

      if ([v1 isKindOfClass: [NSDictionary class]]
	  && [v2 isKindOfClass: [NSDictionary class]])
	{
	  if ([v1 isEqualToDictionary: v2] == NO)
	    {
	      result = NO;
	      break;
	    }
	}
      else if ([v1 isEqual: v2] == NO)
	{
	  result = NO;
	  break;
	}
    }

  return result;
}
@end

// Function to generate a 24-character GUID (uppercase, alphanumeric, no dashes)
NSString *generateGUID()
{
  return [[NSUUID UUID] UUIDString];
}

// Move PBXContainer information to the top level dictionary...
id moveContainerProperties(NSDictionary *input)
{
  NSMutableDictionary *result =
    [NSMutableDictionary dictionaryWithDictionary: input];
  NSMutableDictionary *objects =
    [NSMutableDictionary dictionaryWithDictionary:
			     [result objectForKey: @"objects"]];
  NSEnumerator *en = [objects keyEnumerator];
  id key = nil;
  id keyToChange = nil;

  // NSLog(@"result = %@", result);
  while ((key = [en nextObject]) != nil)
    {
      id d = [objects objectForKey: key];
      
      if ([d isKindOfClass: [NSDictionary class]])
	{
	  NSString *cn = [d objectForKey: @"isa"];

	  if ([cn isEqualToString: @"PBXContainer"])
	    {
	      keyToChange = key;
	      break;
	    }
	}
    }

  // Update objects...
  if (keyToChange != nil)
    {
      NSMutableDictionary *containerDict = [objects objectForKey: keyToChange];

      [containerDict removeObjectForKey: @"rootObject"];
      [containerDict removeObjectForKey: @"isa"];
      [containerDict removeObjectForKey: @"objects"];
      
      [objects removeObjectForKey: keyToChange];
      [result addEntriesFromDictionary: containerDict];
      [result setObject: objects forKey: @"objects"];
    }

  // NSLog(@"result = %@", result); 
  
  return result;
}

NSString *guidInCachedObjects(NSDictionary *objects, NSDictionary *dict)
{
  NSString *guid = nil;
  NSEnumerator *en = [objects keyEnumerator];
  NSString *g = nil;
  
  while ((g = [en nextObject]) != nil)
    {
      NSDictionary *d = [objects objectForKey: g];

      if ([dict isEqualToDictionary: d])
	{
	  guid = g;
	  break;
	}
    }

  return guid;
}

// Recursive function to flatten the property list
id flattenPropertyList(id propertyList, NSMutableDictionary *objects, NSString **rootObjectGUID)
{
  if ([propertyList isKindOfClass:[NSDictionary class]])
    {
      NSDictionary *dict = (NSDictionary *)propertyList;
      
      // Check if the dictionary has an "isa" element
      if ([dict objectForKey:@"isa"])
	{
	  // Generate a GUID for this dictionary
	  NSString *guid = generateGUID();
	  
	  // If the "isa" is "PBXProject", set the rootObjectGUID
	  if ([[dict objectForKey:@"isa"] isEqualToString:@"PBXProject"])
	    {
	      *rootObjectGUID = guid;
	    }
	  
	  // Add the dictionary to the objects array with its GUID
	  NSMutableDictionary *flattenedDict = [NSMutableDictionary dictionary];
	  NSEnumerator *en = [dict keyEnumerator];
	  id key = nil;
	  
	  while ((key = [en nextObject]) != nil)
	    {
	      [flattenedDict setObject: flattenPropertyList([dict objectForKey:key], objects, rootObjectGUID)
				forKey: key];
	    }

	  NSString *existingGuid = guidInCachedObjects(objects, flattenedDict);
	  if (existingGuid != nil)
	    {
	      guid = existingGuid;
	    }
	  else
	    {
	      [objects setObject:flattenedDict forKey:guid];
	    }
	  
	  // Return the GUID to replace the dictionary
	  return guid;
	}
      else
	{
	  // Recursively process each value in the dictionary
	  NSMutableDictionary *processedDict = [NSMutableDictionary dictionary];
	  NSEnumerator *en = [dict keyEnumerator];
	  id key = nil;

	  while ((key = [en nextObject]) != nil)
	    {
	      [processedDict setObject: flattenPropertyList([dict objectForKey:key], objects, rootObjectGUID)
				forKey: key];
	    }
	  return processedDict;
	}
    }
  else if ([propertyList isKindOfClass:[NSArray class]])
    {
      // Recursively process each item in the array
      NSMutableArray *processedArray = [NSMutableArray array];
      NSEnumerator *en = [propertyList objectEnumerator];
      id item = nil;
      
      while((item = [en nextObject]) != nil)
	{
	  [processedArray addObject:flattenPropertyList(item, objects, rootObjectGUID)];
	}
      return processedArray;
    }
  else
    {
      // For non-collection types, return the item as-is
      return propertyList;
    }
}

// Main function to initiate the flattening process
NSDictionary *flattenPlist(id propertyList)
{
  NSMutableDictionary *objects = [NSMutableDictionary dictionary];
  NSString *rootObjectGUID = nil;
  NSMutableDictionary *results = [NSMutableDictionary dictionary];
  
  // Flatten the property list and find the rootObjectGUID
  flattenPropertyList(propertyList, objects, &rootObjectGUID);

  // Put the results together...
  [results setObject: rootObjectGUID
	      forKey: @"rootObject"];
  [results setObject: objects
	      forKey: @"objects"];
  
  // Return the final structure
  return results;
}

@implementation NSObject (KeyExtraction)

+ (void) getAllMethodsForClass: (Class)cls
		     intoArray: (NSMutableArray *)methodsArray
{
  if (cls == nil || cls == [NSObject class])
    {
      return;
    }
  
  unsigned int methodCount = 0;
  Method *methods = class_copyMethodList(cls, &methodCount);
  unsigned int i = 0;
  
  for (i = 0; i < methodCount; i++)
    {
      Method method = methods[i];
      [methodsArray addObject:NSStringFromSelector(method_getName(method))];
    }
  
  free(methods);  // Don't forget to free the list
  
  // Recursively call this method for the superclass
  [self getAllMethodsForClass:class_getSuperclass(cls) intoArray:methodsArray];
}

+ (NSArray *) recursiveGetAllMethodsForClass: (Class)cls
{
  NSMutableArray *methodsArray = [NSMutableArray array];
  [self getAllMethodsForClass: cls
		    intoArray: methodsArray];
  return [methodsArray copy];
}

+ (NSArray *) skippedKeys
{
  return [NSArray arrayWithObjects: @"context", // @"buildConfigurationList", @"buildConfigurations",
		  @"array", @"valueforKey", @"objectatIndexedSubscript", @"totalFiles",
		  @"filename", @"currentFile", @"parameter", @"showEnvVarsInLog", nil];
}

- (NSArray *) keysForObject: (id)object
{
  NSArray *methods = [NSObject recursiveGetAllMethodsForClass: [object class]];
  NSEnumerator *en = [methods objectEnumerator];
  NSString *selectorName = nil;
  NSMutableArray *result = [NSMutableArray arrayWithCapacity: [methods count]];
  
  while ((selectorName = [en nextObject]) != nil)
    {
      if ([selectorName hasPrefix: @"set"] && [selectorName isEqualToString: @"settings"] == NO)
	{
	  NSString *keyName = [selectorName substringFromIndex: 3];

	  keyName = [keyName stringByReplacingOccurrencesOfString: @":" withString: @""];
	  keyName = [keyName lowercaseFirstCharacter];
	  [result addObject: keyName];
	}
    }

  return result;
}

- (NSDictionary *) recursiveKeysAndValuesForObject: (id)object
{
  NSMutableDictionary *keysAndValues = nil;

  if (object && [object isKindOfClass: [NSNull class]] == NO)
    {
      NSArray *properties = [self keysForObject: object];
      NSEnumerator *pen = [properties objectEnumerator];
      id key = nil;

      keysAndValues = [NSMutableDictionary dictionary];
      while ((key = [pen nextObject]) != nil)
	{
	  if ([[NSObject skippedKeys] containsObject: key])
	    {
	      continue;
	    }

	  NS_DURING
	    {
	      id value = [object valueForKey: key];

	      if ([value isKindOfClass: [NSArray class]])
		{
		  NSMutableArray *arrayValues = [NSMutableArray array];
		  NSEnumerator *en = [value objectEnumerator];
		  id item = nil;

		  while ((item = [en nextObject]) != nil)
		    {
		      [arrayValues addObject: [self recursiveKeysAndValuesForObject: item]];
		    }

		  [keysAndValues setObject: arrayValues
				    forKey: key];
		}
	      else if ([value isKindOfClass: [NSDictionary class]] // add a dictionary representing a class...
		       && [value objectForKey: @"isa"] != nil)
		{
		  NSMutableDictionary *dictValues = [NSMutableDictionary dictionary];
		  NSEnumerator *en = [value keyEnumerator];
		  id dictKey = nil;

		  while ((dictKey = [en nextObject]) != nil)
		    {
		      id dictValue = [value objectForKey: dictKey];
		      [dictValues setObject: [self recursiveKeysAndValuesForObject: dictValue]
				     forKey: dictKey];
		    }

		  [keysAndValues setObject: dictValues
				    forKey: key];
		}
	      else if ([value isKindOfClass: [NSDictionary class]] // add a simple dictionary...
		       && [value objectForKey: @"isa"] == nil)
		{
		  [keysAndValues setObject: value
				    forKey: key];
		}
	      else if ([value isKindOfClass: [NSObject class]]
		       && ![value isKindOfClass: [NSString class]]
		       && ![value isKindOfClass: [NSNumber class]])
		{
		  [keysAndValues setObject: NSStringFromClass([object class])
				    forKey: @"isa"];
		  [keysAndValues setObject: [self recursiveKeysAndValuesForObject: value]
				    forKey: key];
		}
	      else
		{
		  [keysAndValues setObject: NSStringFromClass([object class])
				    forKey: @"isa"];
		  if (value)
		    {
		      [keysAndValues setObject: value
					forKey: key];
		    }
		}
	    }
	  NS_HANDLER
	    {
	      NSLog(@"Exception %@ while retrieving value for key '%@' on class %@", localException, key,
		    NSStringFromClass([object class]));
	    }
	  NS_ENDHANDLER;
	}
    }

  // NSLog(@"missingKeys are %@", missingKeys);
  return keysAndValues;
}

- (NSDictionary *) allKeysAndValues
{
  id r = flattenPlist([self recursiveKeysAndValuesForObject: self]);
  NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary: r];
  return moveContainerProperties(d);
}

@end