Index: utils/objfw-new/Makefile ================================================================== --- utils/objfw-new/Makefile +++ utils/objfw-new/Makefile @@ -1,11 +1,12 @@ include ../../extra.mk PROG = objfw-new${PROG_SUFFIX} SRCS = NewApp.m \ NewClass.m \ - ObjFWNew.m + ObjFWNew.m \ + Property.m include ../../buildsys.mk ${PROG}: ${LIBOBJFW_DEP_LVL2} ${LIBOBJFWRT_DEP_LVL2} Index: utils/objfw-new/NewClass.m ================================================================== --- utils/objfw-new/NewClass.m +++ utils/objfw-new/NewClass.m @@ -14,20 +14,23 @@ */ #include "config.h" #include + +#import "Property.h" #import "OFApplication.h" +#import "OFArray.h" #import "OFFile.h" #import "OFStdIOStream.h" #import "OFString.h" #import "OFOpenItemFailedException.h" void -newClass(OFString *name, OFString *superclass) +newClass(OFString *name, OFString *superclass, OFMutableArray *properties) { OFString *headerPath = [name stringByAppendingPathExtension: @"h"]; OFString *implPath = [name stringByAppendingPathExtension: @"m"]; OFFile *headerFile = nil, *implFile = nil; @try { @@ -42,21 +45,52 @@ [OFApplication terminateWithStatus: 1]; } if (superclass == nil) superclass = @"OFObject"; + + for (size_t i = 0; i < properties.count; i++) { + Property *property = [Property propertyWithString: + [properties objectAtIndex: i]]; + [properties replaceObjectAtIndex: i + withObject: property]; + } [headerFile writeFormat: @"#import \n" @"\n" - @"@interface %@: %@\n" - @"@end\n", + @"OF_ASSUME_NONNULL_BEGIN\n" + @"\n" + @"@interface %@: %@\n", name, superclass]; + if (properties.count > 0) + [headerFile writeString: @"{\n"]; + + for (Property *property in properties) + [headerFile writeFormat: @"\t%@_%@;\n", + property.type, property.name]; + + if (properties.count > 0) + [headerFile writeString: @"}\n\n"]; + + for (Property *property in properties) + [headerFile writeFormat: @"@property %@%@;\n", + property.type, property.name]; + + [headerFile writeString: @"@end\n" + @"\n" + @"OF_ASSUME_NONNULL_END\n"]; + [implFile writeFormat: @"#import \"%@\"\n" @"\n" - @"@implementation %@\n" - @"@end\n", + @"@implementation %@\n", headerPath, name]; + + for (Property *property in properties) + [implFile writeFormat: @"@synthesize %@ = _%@;\n", + property.name, property.name]; + + [implFile writeString: @"@end\n"]; [headerFile close]; [implFile close]; } Index: utils/objfw-new/ObjFWNew.m ================================================================== --- utils/objfw-new/ObjFWNew.m +++ utils/objfw-new/ObjFWNew.m @@ -24,11 +24,11 @@ @interface ObjFWNew: OFObject @end extern void newApp(OFString *); -extern void newClass(OFString *, OFString *); +extern void newClass(OFString *, OFString *, OFMutableArray *); OF_APPLICATION_DELEGATE(ObjFWNew) static void showUsage(void) @@ -42,36 +42,53 @@ @implementation ObjFWNew - (void)applicationDidFinishLaunching { bool app, class; OFString *superclass = nil; + OFMutableArray OF_GENERIC(OFString *) *properties = nil; const OFOptionsParserOption options[] = { { '\0', @"app", 0, &app, NULL }, { '\0', @"class", 0, &class, NULL }, { '\0', @"superclass", 1, NULL, &superclass }, + { '\0', @"property", 1, NULL, NULL }, { '\0', nil, 0, NULL, NULL } }; OFOptionsParser *optionsParser; OFUnichar option; optionsParser = [OFOptionsParser parserWithOptions: options]; - while ((option = [optionsParser nextOption]) != '\0') - if (option == '?' || option == ':' || option == '=') + while ((option = [optionsParser nextOption]) != '\0') { + switch (option) { + case '-':; + if ([optionsParser.lastLongOption + isEqual: @"property"]) { + if (properties == nil) + properties = [OFMutableArray array]; + + [properties addObject: optionsParser.argument]; + } + break; + case '?': + case ':': + case '=': showUsage(); + break; + } + } if ((app ^ class) != 1 || optionsParser.remainingArguments.count != 1) showUsage(); - if (superclass && !class) + if ((superclass && !class) || (properties != nil && !class)) showUsage(); if (app) newApp(optionsParser.remainingArguments.firstObject); else if (class) newClass(optionsParser.remainingArguments.firstObject, - superclass); + superclass, properties); else showUsage(); [OFApplication terminate]; } @end ADDED utils/objfw-new/Property.h Index: utils/objfw-new/Property.h ================================================================== --- utils/objfw-new/Property.h +++ utils/objfw-new/Property.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2008-2022 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFObject.h" +#import "OFString.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface Property: OFObject +{ + OFString *_name, *_type; +} + ++ (instancetype)propertyWithString: (OFString *)string; +- (instancetype)initWithString: (OFString *)string; + +@property (readonly, nonatomic) OFString *name; +@property (readonly, nonatomic) OFString *type; +@end + +OF_ASSUME_NONNULL_END ADDED utils/objfw-new/Property.m Index: utils/objfw-new/Property.m ================================================================== --- utils/objfw-new/Property.m +++ utils/objfw-new/Property.m @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2008-2022 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "Property.h" + +#import "OFInvalidArgumentException.h" +#import "OFOutOfRangeException.h" + +@interface Property () +- (void)parseString: (OFString *)string; +@end + +@implementation Property +@synthesize name = _name, type = _type; + ++ (instancetype)propertyWithString: (OFString *)string +{ + return [[[self alloc] initWithString: string] autorelease]; +} + +- (instancetype)initWithString: (OFString *)string +{ + self = [super init]; + + @try { + [self parseString: string]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)parseString: (OFString *)string +{ + const char *UTF8String = string.UTF8String; + size_t length = string.UTF8StringLength, nameIdx = -1; + + if (length > SSIZE_MAX) + @throw [OFOutOfRangeException exception]; + + for (ssize_t i = (ssize_t)length - 1; i > 0; i--) { + if (UTF8String[i] == '*' || UTF8String[i] == ' ' || + UTF8String[i] == '\t') { + nameIdx = i + 1; + break; + } + } + + if (nameIdx < 0) + @throw [OFInvalidArgumentException exception]; + + _name = [[OFString alloc] initWithUTF8String: UTF8String + nameIdx]; + _type = [[OFString alloc] initWithUTF8String: UTF8String + length: (size_t)nameIdx]; +} + +- (void)dealloc +{ + [_name release]; + [_type release]; + + [super dealloc]; +} +@end