Show mogenerator.m syntax highlighted
/*******************************************************************************
mogenerator.m
Copyright (c) 2006-2007 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
Some rights reserved: <http://opensource.org/licenses/mit-license.php>
***************************************************************************/
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "MiscMergeTemplate.h"
#import "MiscMergeCommandBlock.h"
#import "MiscMergeEngine.h"
#import "FoundationAdditions.h"
#import "nsenumerate.h"
#import "NSString+MiscAdditions.h"
#import "DDCommandLineInterface.h"
NSString *gCustomBaseClass;
@interface NSEntityDescription (customBaseClass)
- (BOOL)hasCustomSuperentity;
- (NSString*)customSuperentity;
@end
@implementation NSEntityDescription (customBaseClass)
- (BOOL)hasCustomSuperentity {
NSEntityDescription *superentity = [self superentity];
if (superentity) {
return YES;
} else {
return gCustomBaseClass ? YES : NO;
}
}
- (NSString*)customSuperentity {
NSEntityDescription *superentity = [self superentity];
if (superentity) {
return [superentity managedObjectClassName];
} else {
return gCustomBaseClass ? gCustomBaseClass : @"NSManagedObject";
}
}
/** @TypeInfo NSAttributeDescription */
- (NSArray*)noninheritedAttributes {
NSEntityDescription *superentity = [self superentity];
if (superentity) {
NSMutableArray *result = [[[[self attributesByName] allValues] mutableCopy] autorelease];
[result removeObjectsInArray:[[superentity attributesByName] allValues]];
return result;
} else {
return [[self attributesByName] allValues];
}
}
/** @TypeInfo NSAttributeDescription */
- (NSArray*)noninheritedRelationships {
NSEntityDescription *superentity = [self superentity];
if (superentity) {
NSMutableArray *result = [[[[self relationshipsByName] allValues] mutableCopy] autorelease];
[result removeObjectsInArray:[[superentity relationshipsByName] allValues]];
return result;
} else {
return [[self relationshipsByName] allValues];
}
}
@end
@interface NSAttributeDescription (scalarAttributeType)
- (BOOL)hasScalarAttributeType;
- (NSString*)scalarAttributeType;
- (BOOL)hasDefinedAttributeType;
@end
@implementation NSAttributeDescription (scalarAttributeType)
- (BOOL)hasScalarAttributeType {
switch ([self attributeType]) {
case NSInteger16AttributeType:
case NSInteger32AttributeType:
case NSInteger64AttributeType:
case NSDoubleAttributeType:
case NSFloatAttributeType:
case NSBooleanAttributeType:
return YES;
break;
default:
return NO;
}
}
- (NSString*)scalarAttributeType {
switch ([self attributeType]) {
case NSInteger16AttributeType:
return @"short";
break;
case NSInteger32AttributeType:
return @"int";
break;
case NSInteger64AttributeType:
return @"long long";
break;
case NSDoubleAttributeType:
return @"double";
break;
case NSFloatAttributeType:
return @"float";
break;
case NSBooleanAttributeType:
return @"BOOL";
break;
default:
return nil;
}
}
- (BOOL)hasDefinedAttributeType {
return [self attributeType] != NSUndefinedAttributeType;
}
@end
@interface NSString (camelCaseString)
- (NSString*)camelCaseString;
@end
@implementation NSString (camelCaseString)
- (NSString*)camelCaseString {
NSArray *lowerCasedWordArray = [[self wordArray] arrayByMakingObjectsPerformSelector:@selector(lowercaseString)];
unsigned wordIndex = 1, wordCount = [lowerCasedWordArray count];
NSMutableArray *camelCasedWordArray = [NSMutableArray arrayWithCapacity:wordCount];
if (wordCount)
[camelCasedWordArray addObject:[lowerCasedWordArray objectAtIndex:0]];
for (; wordIndex < wordCount; wordIndex++) {
[camelCasedWordArray addObject:[[lowerCasedWordArray objectAtIndex:wordIndex] initialCapitalString]];
}
return [camelCasedWordArray componentsJoinedByString:@""];
}
@end
static MiscMergeEngine* engineWithTemplatePath(NSString *templatePath_) {
MiscMergeTemplate *template = [[[MiscMergeTemplate alloc] init] autorelease];
[template setStartDelimiter:@"<$" endDelimiter:@"$>"];
[template parseContentsOfFile:templatePath_];
return [[[MiscMergeEngine alloc] initWithTemplate:template] autorelease];
}
@interface MOGeneratorApp : NSObject <DDCliApplicationDelegate> {
NSString *tempMOMPath;
NSManagedObjectModel *model;
NSString *baseClass;
NSString *includem;
NSString *templatePath;
NSString *outputDir;
NSString *machineDir;
NSString *humanDir;
NSString *templateGroup;
BOOL _help;
BOOL _version;
}
- (NSString*)appSupportFileNamed:(NSString*)fileName_;
@end
@implementation MOGeneratorApp
NSString *ApplicationSupportSubdirectoryName = @"mogenerator";
- (NSString*)appSupportFileNamed:(NSString*)fileName_ {
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDirectory;
if (templatePath) {
if ([fileManager fileExistsAtPath:templatePath isDirectory:&isDirectory] && isDirectory) {
return [templatePath stringByAppendingPathComponent:fileName_];
}
} else {
NSArray *appSupportDirectories = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask+NSLocalDomainMask, YES);
assert(appSupportDirectories);
nsenumerate (appSupportDirectories, NSString*, appSupportDirectory) {
if ([fileManager fileExistsAtPath:appSupportDirectory isDirectory:&isDirectory]) {
NSString *appSupportSubdirectory = [appSupportDirectory stringByAppendingPathComponent:ApplicationSupportSubdirectoryName];
if (templateGroup) {
appSupportSubdirectory = [appSupportSubdirectory stringByAppendingPathComponent:templateGroup];
}
if ([fileManager fileExistsAtPath:appSupportSubdirectory isDirectory:&isDirectory] && isDirectory) {
NSString *appSupportFile = [appSupportSubdirectory stringByAppendingPathComponent:fileName_];
if ([fileManager fileExistsAtPath:appSupportFile isDirectory:&isDirectory] && !isDirectory) {
return appSupportFile;
}
}
}
}
}
NSLog(@"appSupportFileNamed:@\"%@\": file not found", fileName_);
exit(EXIT_FAILURE);
return nil;
}
- (void) application: (DDCliApplication *) app
willParseOptions: (DDGetoptLongParser *) optionsParser;
{
[optionsParser setGetoptLongOnly: YES];
DDGetoptOption optionTable[] =
{
// Long Short Argument options
{@"model", 'm', DDGetoptRequiredArgument},
{@"base-class", 0, DDGetoptRequiredArgument},
// For compatibility:
{@"baseClass", 0, DDGetoptRequiredArgument},
{@"includem", 0, DDGetoptRequiredArgument},
{@"template-path", 0, DDGetoptRequiredArgument},
// For compatibility:
{@"templatePath", 0, DDGetoptRequiredArgument},
{@"output-dir", 'O', DDGetoptRequiredArgument},
{@"machine-dir", 'M', DDGetoptRequiredArgument},
{@"human-dir", 'H', DDGetoptRequiredArgument},
{@"template-group", 0, DDGetoptRequiredArgument},
{@"help", 'h', DDGetoptNoArgument},
{@"version", 0, DDGetoptNoArgument},
{nil, 0, 0},
};
[optionsParser addOptionsFromTable: optionTable];
}
- (void) printUsage;
{
ddprintf(@"%@: Usage [OPTIONS] <argument> [...]\n", DDCliApp);
printf("\n"
" -m, --model MODEL Path to model\n"
" --base-class CLASS Custom base class\n"
" --includem FILE Generate aggregate include file\n"
" --template-path PATH Path to templates\n"
" --template-group NAME Name of template group\n"
" -O, --output-dir DIR Output directory\n"
" -M, --machine-dir DIR Output directory for machine files\n"
" -H, --human-dir DIR Output director for human files\n"
" --version Display version and exit\n"
" -h, --help Display this help and exit\n"
"\n"
"Implements generation gap codegen pattern for Core Data.\n"
"Inspired by eogenerator.\n");
}
- (void) setModel: (NSString *) path;
{
assert(!model); // Currently we only can load one model.
if( ![[NSFileManager defaultManager] fileExistsAtPath:path]){
NSString * reason = [NSString stringWithFormat: @"error loading file at %@: no such file exists", path];
DDCliParseException * e = [DDCliParseException parseExceptionWithReason: reason
exitCode: EX_NOINPUT];
@throw e;
}
if ([[path pathExtension] isEqualToString:@"xcdatamodel"]) {
// We've been handed a .xcdatamodel data model, transparently compile it into a .mom managed object model.
NSString *momc = [[NSFileManager defaultManager] fileExistsAtPath:@"/Library/Application Support/Apple/Developer Tools/Plug-ins/XDCoreDataModel.xdplugin/Contents/Resources/momc"]
? @"/Library/Application Support/Apple/Developer Tools/Plug-ins/XDCoreDataModel.xdplugin/Contents/Resources/momc"
: @"/Developer/Library/Xcode/Plug-ins/XDCoreDataModel.xdplugin/Contents/Resources/momc";
tempMOMPath = [[NSTemporaryDirectory() stringByAppendingPathComponent:[(id)CFUUIDCreateString(kCFAllocatorDefault, CFUUIDCreate(kCFAllocatorDefault)) autorelease]] stringByAppendingPathExtension:@"mom"];
system([[NSString stringWithFormat:@"\"%@\" %@ %@", momc, path, tempMOMPath] UTF8String]); // Ignored system's result -- momc doesn't return any relevent error codes.
path = tempMOMPath;
}
model = [[[NSManagedObjectModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path]] autorelease];
assert(model);
}
- (int) application: (DDCliApplication *) app
runWithArguments: (NSArray *) arguments;
{
if (_help)
{
[self printUsage];
return EXIT_SUCCESS;
}
if (_version)
{
printf("mogenerator 1.5. By Jonathan 'Wolf' Rentzsch + friends.\n");
return EXIT_SUCCESS;
}
gCustomBaseClass = [baseClass retain];
NSString * mfilePath = includem;
NSMutableString * mfileContent = [NSMutableString stringWithString:@""];
if (outputDir == nil)
outputDir = @"";
if (machineDir == nil)
machineDir = outputDir;
if (humanDir == nil)
humanDir = outputDir;
NSFileManager *fm = [NSFileManager defaultManager];
int machineFilesGenerated = 0;
int humanFilesGenerated = 0;
if (model) {
MiscMergeEngine *machineH = engineWithTemplatePath([self appSupportFileNamed:@"machine.h.motemplate"]);
assert(machineH);
MiscMergeEngine *machineM = engineWithTemplatePath([self appSupportFileNamed:@"machine.m.motemplate"]);
assert(machineM);
MiscMergeEngine *humanH = engineWithTemplatePath([self appSupportFileNamed:@"human.h.motemplate"]);
assert(humanH);
MiscMergeEngine *humanM = engineWithTemplatePath([self appSupportFileNamed:@"human.m.motemplate"]);
assert(humanM);
int entityCount = [[model entities] count];
if(entityCount == 0){
printf("No entities found in model. No files will be generated.\n");
NSLog(@"the model description is %@.", model);
}
nsenumerate ([model entities], NSEntityDescription, entity) {
NSString *entityClassName = [entity managedObjectClassName];
if ([entityClassName isEqualToString:@"NSManagedObject"] ||
[entityClassName isEqualToString:gCustomBaseClass]){
ddprintf(@"skipping entity %@ because it doesn't use a custom subclass.\n",
entityClassName);
continue;
}
NSString *generatedMachineH = [machineH executeWithObject:entity sender:nil];
NSString *generatedMachineM = [machineM executeWithObject:entity sender:nil];
NSString *generatedHumanH = [humanH executeWithObject:entity sender:nil];
NSString *generatedHumanM = [humanM executeWithObject:entity sender:nil];
BOOL machineDirtied = NO;
NSString *machineHFileName = [machineDir stringByAppendingPathComponent:
[NSString stringWithFormat:@"_%@.h", entityClassName]];
if (![fm regularFileExistsAtPath:machineHFileName] || ![generatedMachineH isEqualToString:[NSString stringWithContentsOfFile:machineHFileName]]) {
// If the file doesn't exist or is different than what we just generated, write it out.
[generatedMachineH writeToFile:machineHFileName atomically:NO];
machineDirtied = YES;
machineFilesGenerated++;
}
NSString *machineMFileName = [machineDir stringByAppendingPathComponent:
[NSString stringWithFormat:@"_%@.m", entityClassName]];
if (![fm regularFileExistsAtPath:machineMFileName] || ![generatedMachineM isEqualToString:[NSString stringWithContentsOfFile:machineMFileName]]) {
// If the file doesn't exist or is different than what we just generated, write it out.
[generatedMachineM writeToFile:machineMFileName atomically:NO];
machineDirtied = YES;
machineFilesGenerated++;
}
NSString *humanHFileName = [humanDir stringByAppendingPathComponent:
[NSString stringWithFormat:@"%@.h", entityClassName]];
if ([fm regularFileExistsAtPath:humanHFileName]) {
if (machineDirtied)
[fm touchPath:humanHFileName];
} else {
[generatedHumanH writeToFile:humanHFileName atomically:NO];
humanFilesGenerated++;
}
NSString *humanMFileName = [humanDir stringByAppendingPathComponent:
[NSString stringWithFormat:@"%@.m", entityClassName]];
NSString *humanMMFileName = [humanDir stringByAppendingPathComponent:
[NSString stringWithFormat:@"%@.mm", entityClassName]];
if (![fm regularFileExistsAtPath:humanMFileName] && [fm regularFileExistsAtPath:humanMMFileName]) {
// Allow .mm human files as well as .m files.
humanMFileName = humanMMFileName;
}
if ([fm regularFileExistsAtPath:humanMFileName]) {
if (machineDirtied)
[fm touchPath:humanMFileName];
} else {
[generatedHumanM writeToFile:humanMFileName atomically:NO];
humanFilesGenerated++;
}
[mfileContent appendFormat:@"#include \"%@\"\n#include \"%@\"\n",
[humanMFileName lastPathComponent], [machineMFileName lastPathComponent]];
}
}
if (tempMOMPath) {
[fm removeFileAtPath:tempMOMPath handler:nil];
}
bool mfileGenerated = NO;
if (mfilePath && ![mfileContent isEqualToString:@""]) {
[mfileContent writeToFile:mfilePath atomically:NO];
mfileGenerated = YES;
}
printf("%d machine files%s %d human files%s generated.\n", machineFilesGenerated,
(mfileGenerated ? "," : " and"), humanFilesGenerated, (mfileGenerated ? " and one include.m file" : ""));
return EXIT_SUCCESS;
}
@end
int main (int argc, char * const * argv)
{
return DDCliAppRunWithClass([MOGeneratorApp class]);
}
See more files for this project here