0c532ca4 |
// CMetadataImporter.m
//
// Lisp Metadata Importer
//
// Created by John Wiseman on 9/1/05.
// Copyright 2005 John Wiseman.
//
// Licensed under the MIT license--see the accompanying LICENSE.txt
// file.
#import "CMetadataImporter.h"
#import "NSString_HMext.h"
#import "NSData_HMext.h"
#import "DebugLog.h"
@implementation CMetadataImporter
|
6c6aefdd |
int MaxSourceSize = 500000; // Default maximum number of bytes that will be read for indexing purposes.
|
0c532ca4 |
long NO_MAXIMUM = -1;
// All sorts of static data that we initialize once, then use many many times.
static BOOL StaticDataIsInitialized = NO;
// Lots of regexes in string form, waiting to be compiled.
static NSString *LispDef1_pat = @"(?i)^\\(def[^\\s]*[\\s\\']+(\\(setf\\s+[^\\s]+\\))";
|
6c6aefdd |
static NSRegularExpression *LispDef1_RE = nil;
|
0c532ca4 |
static NSString *LispDef2_pat = @"(?i)^\\(def[^\\s]*[\\s\\']+([^\\s\\)]+)";
|
6c6aefdd |
static NSRegularExpression *LispDef2_RE = nil;
|
0c532ca4 |
static NSString *LispDefun_pat = @"(?i)^\\(defun\\s+([^\\s\\)\\(]+)";
|
6c6aefdd |
static NSRegularExpression *LispDefun_RE = nil;
|
0c532ca4 |
static NSString *LispDefunsetf_pat = @"(?i)^\\(defun\\s+(\\(setf\\s+[^\\s]+\\))";
|
6c6aefdd |
static NSRegularExpression *LispDefunsetf_RE = nil;
|
0c532ca4 |
static NSString *LispDefmethod_pat = @"(?i)^\\(defmethod\\s+([^\\s\\)\\(]+)";
|
6c6aefdd |
static NSRegularExpression *LispDefmethod_RE = nil;
|
0c532ca4 |
static NSString *LispDefmethodsetf_pat = @"(?i)^\\(defmethod\\s+(\\(setf\\s+[^\\s]+\\))";
|
6c6aefdd |
static NSRegularExpression *LispDefmethodsetf_RE = nil;
|
0c532ca4 |
static NSString *LispDefgeneric_pat = @"(?i)^\\(defgeneric\\s+((?:[^\\s\\)\\(]+|\\(setf\\s+[^\\s]+\\)))";
|
6c6aefdd |
static NSRegularExpression *LispDefgeneric_RE = nil;
|
0c532ca4 |
static NSString *LispDefgenericsetf_pat = @"(?i)^\\(defgeneric\\s+(\\(setf\\s+[^\\s]+\\))";
|
6c6aefdd |
static NSRegularExpression *LispDefgenericsetf_RE = nil;
|
0c532ca4 |
static NSString *LispDefmacro_pat = @"(?i)^\\(defmacro\\s+([^\\s\\)]+)";
|
6c6aefdd |
static NSRegularExpression *LispDefmacro_RE = nil;
|
0c532ca4 |
static NSString *LispDefclass_pat = @"(?i)^\\(defclass\\s+([^\\s\\)]+)";
|
6c6aefdd |
static NSRegularExpression *LispDefclass_RE = nil;
|
0c532ca4 |
static NSString *LispDefstruct_pat = @"(?i)^\\(defstruct\\s+\\(?([^\\s\\)]+)";
|
6c6aefdd |
static NSRegularExpression *LispDefstruct_RE = nil;
|
0c532ca4 |
static NSString *LispDefvar_pat = @"(?i)^\\((?:defvar|defparameter|defconstant)\\s+([^\\s\\)]+)";
|
6c6aefdd |
static NSRegularExpression *LispDefvar_RE = nil;
|
0c532ca4 |
|
6c6aefdd |
static NSError *err = nil;
|
0c532ca4 |
- (void)initStaticData
{
NSLog(@"Import Lisp");
|
0223e6a6 |
if (StaticDataIsInitialized)
{
return;
}
StaticDataIsInitialized = YES;
// Find the bundle, and Info.plist. Set the debug level specified
// there, as well as the maximum file length to index.
NSBundle *theBundle = [NSBundle bundleForClass:[self class]];
NSObject *debugLevelObj = [theBundle objectForInfoDictionaryKey:@"DebugLevel"];
if (debugLevelObj != nil)
{
SetDebugLogLevel(DebugLevelNameToValue((NSString*)debugLevelObj));
}
NSObject *maxSourceSizeObj = [theBundle objectForInfoDictionaryKey:@"MaxSourceSizeToIndex"];
int max = [(NSNumber*)maxSourceSizeObj intValue];
if (max != 0)
{
DebugLog(DEBUG_LEVEL_DEBUG, @"Using MaxSourceSize=%d", max);
MaxSourceSize = max;
}
else
{
NSLog(@"Error parsing MaxSourceSizeToIndex, using %d", MaxSourceSize);
}
// Precompile our regexes.
|
6c6aefdd |
LispDef1_RE = [NSRegularExpression regularExpressionWithPattern:LispDef1_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
LispDef2_RE = [NSRegularExpression regularExpressionWithPattern:LispDef2_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
LispDefun_RE = [NSRegularExpression regularExpressionWithPattern:LispDefun_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
LispDefunsetf_RE = [NSRegularExpression regularExpressionWithPattern:LispDefunsetf_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
LispDefmethod_RE = [NSRegularExpression regularExpressionWithPattern:LispDefmethod_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
LispDefmethodsetf_RE = [NSRegularExpression regularExpressionWithPattern:LispDefmethodsetf_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
LispDefgeneric_RE = [NSRegularExpression regularExpressionWithPattern:LispDefgeneric_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
LispDefgenericsetf_RE = [NSRegularExpression regularExpressionWithPattern:LispDefgenericsetf_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
LispDefclass_RE = [NSRegularExpression regularExpressionWithPattern:LispDefclass_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
LispDefstruct_RE = [NSRegularExpression regularExpressionWithPattern:LispDefstruct_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
LispDefvar_RE = [NSRegularExpression regularExpressionWithPattern:LispDefvar_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
LispDefmacro_RE = [NSRegularExpression regularExpressionWithPattern:LispDefmacro_pat
options:NSRegularExpressionCaseInsensitive
error:&err];
|
0223e6a6 |
DebugLog(DEBUG_LEVEL_DEBUG, @"Static data has been initialized.");
|
0c532ca4 |
}
static NSStringEncoding PossibleSourceTextEncodings[] = { NSUTF8StringEncoding,
|
0223e6a6 |
NSMacOSRomanStringEncoding,
NSISOLatin1StringEncoding,
NSWindowsCP1252StringEncoding };
|
0c532ca4 |
// Tries to read the file using the encodings specified in
// PossibleSourceTextEncodings, in order, until one succeeds.
//
// There's probably a better way to do this (TEC Sniffers?). The
// seemingly obvious way, stringWithContentsOfFile:usedEncoding:error,
// doesn't work--apparently it just does something minimal, like
// decide between UTF-8 and UCS-16 or something.
- (NSString*)readContentsOfFile:(NSString*)pathToFile error:(NSError**)theError
{
|
0223e6a6 |
int i;
NSStringEncoding theEncoding;
NSString *theSource = nil;
NSData *data;
DebugLog(DEBUG_LEVEL_DEBUG, @"Indexing %@", pathToFile);
// Read the file.
if (MaxSourceSize == NO_MAXIMUM)
{
data = [NSData dataWithContentsOfFile:pathToFile options:0 error:theError];
}
else
{
data = [NSData dataWithContentsOfFile:pathToFile maxSize:MaxSourceSize error:theError];
if ([data length] == MaxSourceSize)
{
// This is not absolutely certain to be correct, since the file might just have been
// MaxSourceSize bytes long.
DebugLog(DEBUG_LEVEL_DEBUG, @"Truncated indexing of '%@' to %d bytes", pathToFile, MaxSourceSize);
}
}
if (data == nil)
{
return nil;
}
// Try to convert the file contents to a string by trying the candidate
// encodings, in order.
for (i = 0; i < sizeof(PossibleSourceTextEncodings); i++)
{
theEncoding = PossibleSourceTextEncodings[i];
DebugLog(DEBUG_LEVEL_VERBOSE, @"Trying encoding %d", theEncoding);
theSource = [[[NSString alloc] initWithData:data encoding:theEncoding] autorelease];
if (theSource != nil)
{
break;
}
else
{
DebugLog(DEBUG_LEVEL_DEBUG, @"Reading with encoding %d failed.", theEncoding);
}
}
return theSource;
|
0c532ca4 |
}
// Adds metadata values to the specified dictionary under the
// specified key, using the specified regular expression.
|
6c6aefdd |
- (BOOL)addMatchesTo:(NSMutableDictionary *)attributes fromLine:(NSString *)line usingRE:(NSRegularExpression *)regex forKey:(NSString *)key
|
0c532ca4 |
{
|
6c6aefdd |
NSTextCheckingResult *match = [regex firstMatchInString:line options:NSMatchingAnchored range:NSMakeRange(0, [line length])];
|
0223e6a6 |
if (match)
{
|
2fbaf64c |
NSLog(@"%s", [line UTF8String]);
|
6c6aefdd |
NSString *name = [line substringWithRange: [match rangeAtIndex:1]];
|
0223e6a6 |
[[attributes objectForKey:key] addObject:name];
return YES;
}
else
{
return NO;
}
|
0c532ca4 |
}
// This is the method that does all the importing and indexing work.
// It stuffs attributes into the specified dictionary.
- (BOOL)importFile:(NSString *)inPathToFile contentType:(NSString *)inContentType attributes:(NSMutableDictionary *)inAttributes
{
|
0223e6a6 |
BOOL theResult = NO;
@try
{
NSAutoreleasePool *theAutoreleasePool = [[NSAutoreleasePool alloc] init];
NSError *error = nil;
NSString *source;
[self initStaticData];
source = [self readContentsOfFile:inPathToFile error:&error];
if (source == nil)
{
if (error)
{
NSLog(@"Lisp Metadata Importer: Could not process file '%@': %@", inPathToFile, error);
}
else
{
NSLog(@"Lisp Metadata Importer: Could not process file '%@': unknown error", inPathToFile);
}
return NO;
}
// Only process the first MaxSourceSize of the file. To try to do more
// invites the swapping death.
if ([source length] > MaxSourceSize)
{
source = [source substringToIndex:MaxSourceSize];
}
NSMutableDictionary *moreAttributes = [[[NSMutableDictionary alloc] initWithCapacity:10] autorelease];
[moreAttributes setObject:[NSMutableArray arrayWithCapacity:100] forKey:@"org_lisp_definitions"];
[moreAttributes setObject:[NSMutableArray arrayWithCapacity:100] forKey:@"org_lisp_defuns"];
[moreAttributes setObject:[NSMutableArray arrayWithCapacity:100] forKey:@"org_lisp_defmethods"];
[moreAttributes setObject:[NSMutableArray arrayWithCapacity:100] forKey:@"org_lisp_defgenerics"];
[moreAttributes setObject:[NSMutableArray arrayWithCapacity:100] forKey:@"org_lisp_defmacros"];
[moreAttributes setObject:[NSMutableArray arrayWithCapacity:100] forKey:@"org_lisp_defvars"];
[moreAttributes setObject:[NSMutableArray arrayWithCapacity:100] forKey:@"org_lisp_defclasses"];
[moreAttributes setObject:[NSMutableArray arrayWithCapacity:100] forKey:@"org_lisp_defstructs"];
// Divide the file contents into lines, using either CR or LF to end a line.
NSCharacterSet *eol = [NSCharacterSet characterSetWithCharactersInString:@"\n\r"];
NSArray *lines = [source componentsSeparatedByCharacterFromSet:eol];
NSEnumerator *theEnum = [lines objectEnumerator];
NSString *theLine;
while (nil != (theLine = [theEnum nextObject]))
{
// The following check speeds the indexer up by roughly 6x.
if (([theLine length] > 0) && ([theLine characterAtIndex:0] == '('))
{
if (![self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDef1_RE forKey:@"org_lisp_definitions"])
{
// The first expression didn't fire, try the second one.
[self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDef2_RE forKey:@"org_lisp_definitions"];
}
[self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDefun_RE forKey:@"org_lisp_defuns"];
[self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDefunsetf_RE forKey:@"org_lisp_defuns"];
[self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDefmethod_RE forKey:@"org_lisp_defmethods"];
[self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDefmethodsetf_RE forKey:@"org_lisp_defmethods"];
[self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDefgeneric_RE forKey:@"org_lisp_defgenerics"];
[self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDefgenericsetf_RE forKey:@"org_lisp_defgenerics"];
[self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDefmacro_RE forKey:@"org_lisp_defmacros"];
[self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDefvar_RE forKey:@"org_lisp_defvars"];
[self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDefclass_RE forKey:@"org_lisp_defclasses"];
[self addMatchesTo:moreAttributes fromLine:theLine usingRE:LispDefstruct_RE forKey:@"org_lisp_defstructs"];
}
}
// Add the complete source code as metadata.
[moreAttributes setObject:source forKey:@"kMDItemTextContent"];
[inAttributes addEntriesFromDictionary:moreAttributes];
theResult = YES;
[theAutoreleasePool release];
}
@catch (NSException *localException)
{
NSLog(@"Lisp Metadata Importer: Could not process file '%@' (Exception: %@)", inPathToFile, localException);
}
@finally
{
}
return(theResult);
|
0c532ca4 |
}
@end
|