PageRenderTime 105ms CodeModel.GetById 63ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 0ms

/core/externals/google-toolbox-for-mac/SpotlightPlugins/InterfaceBuilder/GetMetadataForFile.m

http://macfuse.googlecode.com/
Objective C | 208 lines | 169 code | 15 blank | 24 comment | 31 complexity | f59b7958cd034add99cea5dc6ab1dd2e MD5 | raw file
  1//
  2//  GetMetadataForFile.m
  3//
  4//  Copyright 2008 Google Inc.
  5//
  6//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7//  use this file except in compliance with the License.  You may obtain a copy
  8//  of the License at
  9// 
 10//  http://www.apache.org/licenses/LICENSE-2.0
 11// 
 12//  Unless required by applicable law or agreed to in writing, software
 13//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 14//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 15//  License for the specific language governing permissions and limitations under
 16//  the License.
 17//
 18
 19#import <Foundation/Foundation.h>
 20#import "GTMGarbageCollection.h"
 21
 22static BOOL AddStringsToTextContent(NSSet *stringSet, 
 23                                    NSMutableDictionary *attributes) {
 24  BOOL wasGood = NO;
 25  if ([stringSet count]) {
 26    NSString *allStrings = [[stringSet allObjects] componentsJoinedByString:@"\n"];
 27    NSString *oldContent = [attributes objectForKey:(NSString*)kMDItemTextContent];
 28    if (oldContent) {
 29      allStrings = [NSString stringWithFormat:@"%@\n%@", allStrings, oldContent];
 30    }
 31    [attributes setObject:allStrings forKey:(NSString*)kMDItemTextContent];
 32    wasGood = YES;
 33  }
 34  return wasGood;
 35}
 36
 37static BOOL ExtractClasses(NSDictionary *ibToolData,
 38                           NSMutableDictionary *attributes) {
 39  NSString *classesKey = @"com.apple.ibtool.document.classes";
 40  NSDictionary *classes = [ibToolData objectForKey:classesKey];
 41  NSMutableSet *classSet = [NSMutableSet set];
 42  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; 
 43  NSArray *classPrefixesToIgnore 
 44    = [ud objectForKey:@"classPrefixesToIgnore"];
 45  if (!classPrefixesToIgnore) {
 46    classPrefixesToIgnore = [NSArray arrayWithObjects:
 47                             @"IB", 
 48                             @"FirstResponder", 
 49                             @"NS", 
 50                             @"Web", 
 51                             nil];
 52    [ud setObject:classPrefixesToIgnore forKey:@"classPrefixesToIgnore"];
 53    [ud synchronize];
 54  }
 55  NSDictionary *entry;
 56  NSEnumerator *entryEnum = [classes objectEnumerator];
 57  while ((entry = [entryEnum nextObject])) {
 58    NSString *classStr = [entry objectForKey:@"class"];
 59    if (classStr) {
 60      NSString *prefix;
 61      NSEnumerator *classPrefixesToIgnoreEnum 
 62        = [classPrefixesToIgnore objectEnumerator];
 63      while (classStr && (prefix = [classPrefixesToIgnoreEnum nextObject])) {
 64        if ([classStr hasPrefix:prefix]) {
 65          classStr = nil;
 66        }
 67      }
 68      if (classStr) {
 69        [classSet addObject:classStr];
 70      }
 71    }
 72  }
 73  return AddStringsToTextContent(classSet, attributes);
 74}
 75
 76static BOOL ExtractLocalizableStrings(NSDictionary *ibToolData,
 77                                      NSMutableDictionary *attributes) {
 78  NSString *localStrKey = @"com.apple.ibtool.document.localizable-strings";
 79  NSDictionary *strings = [ibToolData objectForKey:localStrKey];
 80  NSMutableSet *stringSet = [NSMutableSet set];
 81  NSDictionary *entry;
 82  NSEnumerator *entryEnum = [strings objectEnumerator];
 83  while ((entry = [entryEnum nextObject])) {
 84    NSEnumerator *stringEnum = [entry objectEnumerator];
 85    NSString *string;
 86    while ((string = [stringEnum nextObject])) {
 87      [stringSet addObject:string];
 88    }
 89  }
 90  return AddStringsToTextContent(stringSet, attributes);
 91}
 92
 93static BOOL ExtractConnections(NSDictionary *ibToolData,
 94                               NSMutableDictionary *attributes) {
 95  NSString *connectionsKey = @"com.apple.ibtool.document.connections";
 96  NSDictionary *connections = [ibToolData objectForKey:connectionsKey];
 97  NSMutableSet *connectionsSet = [NSMutableSet set];
 98  NSDictionary *entry;
 99  NSEnumerator *entryEnum = [connections objectEnumerator];
100  while ((entry = [entryEnum nextObject])) {
101    NSString *typeStr = [entry objectForKey:@"type"];
102    NSString *value = nil;
103    if (typeStr) {
104      if ([typeStr isEqualToString:@"IBBindingConnection"]) {
105        value = [entry objectForKey:@"keypath"];
106      } else if ([typeStr isEqualToString:@"IBCocoaOutletConnection"] ||
107                 [typeStr isEqualToString:@"IBCocoaActionConnection"]) {
108        value = [entry objectForKey:@"label"];
109      }
110      if (value) {
111        [connectionsSet addObject:value];
112      }
113    }
114  }
115  return AddStringsToTextContent(connectionsSet, attributes);
116}
117
118static NSString *FindIBTool(void) {
119  NSString *result = nil;
120
121  NSString *possiblePaths[] = {
122    @"/usr/bin/ibtool",
123    @"/Developer/usr/bin/ibtool",
124  };
125
126  NSFileManager *fm = [NSFileManager defaultManager];
127  BOOL isDir;
128  for (size_t i = 0; i < (sizeof(possiblePaths) / sizeof(NSString*)); ++i) {
129    if ([fm fileExistsAtPath:possiblePaths[i] isDirectory:&isDir] &&
130        !isDir) {
131      result = possiblePaths[i];
132      break;
133    }
134  }
135
136  return result;
137}
138
139static NSData *CommandOutput(NSString *cmd) {
140  NSMutableData *result = [NSMutableData data];
141
142  // NOTE: we use popen/pclose in here instead of NSTask because NSTask uses
143  // a delayed selector to clean up the process it spawns, so since we have
144  // no runloop it gets ungly trying to clean up the zombie process.
145
146  FILE *fp;
147  char buffer[2048];
148  size_t len;
149  if((fp = popen([cmd UTF8String], "r"))) {
150    // spool it all in
151    while ((len = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
152      [result appendBytes:buffer length:len];
153    }
154    // make sure we get a clean exit status
155    if (pclose(fp) != 0) {
156      result = nil;
157    }
158  }
159  return result;
160}
161
162static BOOL ImportIBFile(NSMutableDictionary *attributes, 
163                         NSString *pathToFile) {
164  BOOL wasGood = NO;
165  NSString *ibtoolPath = FindIBTool();
166  if (ibtoolPath) {
167    NSString *cmdString 
168      = @"%@ --classes --localizable-strings --connections \"%@\"";
169    NSString *cmd = [NSString stringWithFormat:cmdString, ibtoolPath, pathToFile];
170    NSData *data = CommandOutput(cmd);
171    if (data) {
172      NSDictionary *results 
173        = GTMCFAutorelease(CFPropertyListCreateFromXMLData(NULL, 
174                                                           (CFDataRef)data ,
175                                                           kCFPropertyListImmutable,
176                                                           NULL));
177      if (results && [results isKindOfClass:[NSDictionary class]]) {
178        wasGood = ExtractClasses(results, attributes);
179        wasGood |= ExtractLocalizableStrings(results, attributes);
180        wasGood |= ExtractConnections(results, attributes);
181      }
182    }
183  }
184  
185  return wasGood;
186}
187
188// Grabs all of the classes, localizable strings, bindings, outlets 
189// and actions and sticks them into kMDItemTextContent.
190Boolean GetMetadataForFile(void* interface, 
191                           CFMutableDictionaryRef cfAttributes, 
192                           CFStringRef contentTypeUTI,
193                           CFStringRef cfPathToFile) {
194  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
195  NSMutableDictionary *attributes = (NSMutableDictionary*)cfAttributes;
196  NSString *pathToFile = (NSString*)cfPathToFile;
197  BOOL wasGood = NO;
198  if (UTTypeConformsTo(contentTypeUTI, 
199                       CFSTR("com.apple.interfacebuilder.document"))
200      || UTTypeConformsTo(contentTypeUTI, 
201                          CFSTR("com.apple.interfacebuilder.document.cocoa"))
202      || UTTypeConformsTo(contentTypeUTI, 
203                          CFSTR("com.apple.interfacebuilder.document.carbon"))) {
204    wasGood = ImportIBFile(attributes, pathToFile);
205  }
206  [pool release];
207  return  wasGood == NO ? FALSE : TRUE;
208}