Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -85,11 +85,11 @@ OFStdIOStream.m \ OFStream.m \ OFString.m \ OFString+CryptoHashing.m \ OFString+JSONParsing.m \ - OFString+PropertyListValue.m \ + OFString+PropertyListParsing.m \ OFString+Serialization.m \ OFString+URLEncoding.m \ OFString+XMLEscaping.m \ OFString+XMLUnescaping.m \ OFSystemInfo.m \ ADDED src/OFString+PropertyListParsing.h Index: src/OFString+PropertyListParsing.h ================================================================== --- src/OFString+PropertyListParsing.h +++ src/OFString+PropertyListParsing.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * 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 "OFString.h" + +OF_ASSUME_NONNULL_BEGIN + +#ifdef __cplusplus +extern "C" { +#endif +extern int _OFString_PropertyListParsing_reference; +#ifdef __cplusplus +} +#endif + +@interface OFString (PropertyListParsing) +/*! + * @brief The string interpreted as a property list and parsed as an object. + * + * @note This only supports XML property lists! + */ +@property (readonly, nonatomic) id objectByParsingPropertyList; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFString+PropertyListParsing.m Index: src/OFString+PropertyListParsing.m ================================================================== --- src/OFString+PropertyListParsing.m +++ src/OFString+PropertyListParsing.m @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * 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 "OFString+PropertyListParsing.h" +#import "OFArray.h" +#import "OFData.h" +#import "OFDate.h" +#import "OFDictionary.h" +#import "OFNumber.h" +#import "OFXMLAttribute.h" +#import "OFXMLElement.h" + +#import "OFInvalidFormatException.h" +#import "OFUnsupportedVersionException.h" + +int _OFString_PropertyListParsing_reference; + +static id parseElement(OFXMLElement *element); + +static OFArray * +parseArrayElement(OFXMLElement *element) +{ + OFMutableArray *ret = [OFMutableArray array]; + void *pool = objc_autoreleasePoolPush(); + + for (OFXMLElement *child in element.elements) + [ret addObject: parseElement(child)]; + + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +static OFDictionary * +parseDictElement(OFXMLElement *element) +{ + OFMutableDictionary *ret = [OFMutableDictionary dictionary]; + void *pool = objc_autoreleasePoolPush(); + OFArray OF_GENERIC(OFXMLElement *) *children = element.elements; + OFEnumerator OF_GENERIC(OFXMLElement *) *enumerator; + OFXMLElement *key, *object; + + if (children.count % 2 != 0) + @throw [OFInvalidFormatException exception]; + + enumerator = [children objectEnumerator]; + while ((key = [enumerator nextObject]) && + (object = [enumerator nextObject])) { + if (key.namespace != nil || key.attributes.count != 0 || + ![key.name isEqual: @"key"]) + @throw [OFInvalidFormatException exception]; + + [ret setObject: parseElement(object) + forKey: key.stringValue]; + } + + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +static OFString * +parseStringElement(OFXMLElement *element) +{ + return element.stringValue; +} + +static OFData * +parseDataElement(OFXMLElement *element) +{ + return [OFData dataWithBase64EncodedString: element.stringValue]; +} + +static OFDate * +parseDateElement(OFXMLElement *element) +{ + return [OFDate dateWithDateString: element.stringValue + format: @"%Y-%m-%dT%H:%M:%SZ"]; +} + +static OFNumber * +parseTrueElement(OFXMLElement *element) +{ + if (element.children.count != 0) + @throw [OFInvalidFormatException exception]; + + return [OFNumber numberWithBool: true]; +} + +static OFNumber * +parseFalseElement(OFXMLElement *element) +{ + if (element.children.count != 0) + @throw [OFInvalidFormatException exception]; + + return [OFNumber numberWithBool: false]; +} + +static OFNumber * +parseRealElement(OFXMLElement *element) +{ + return [OFNumber numberWithDouble: element.doubleValue]; +} + +static OFNumber * +parseIntegerElement(OFXMLElement *element) +{ + void *pool = objc_autoreleasePoolPush(); + OFString *stringValue; + OFNumber *ret; + + stringValue = element.stringValue.stringByDeletingEnclosingWhitespaces; + + if ([stringValue hasPrefix: @"-"]) + ret = [OFNumber numberWithLongLong: stringValue.longLongValue]; + else + ret = [OFNumber numberWithUnsignedLongLong: + stringValue.unsignedLongLongValue]; + + [ret retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +static id +parseElement(OFXMLElement *element) +{ + OFString *elementName; + + if (element.namespace != nil || element.attributes.count != 0) + @throw [OFInvalidFormatException exception]; + + elementName = element.name; + + if ([elementName isEqual: @"array"]) + return parseArrayElement(element); + else if ([elementName isEqual: @"dict"]) + return parseDictElement(element); + else if ([elementName isEqual: @"string"]) + return parseStringElement(element); + else if ([elementName isEqual: @"data"]) + return parseDataElement(element); + else if ([elementName isEqual: @"date"]) + return parseDateElement(element); + else if ([elementName isEqual: @"true"]) + return parseTrueElement(element); + else if ([elementName isEqual: @"false"]) + return parseFalseElement(element); + else if ([elementName isEqual: @"real"]) + return parseRealElement(element); + else if ([elementName isEqual: @"integer"]) + return parseIntegerElement(element); + else + @throw [OFInvalidFormatException exception]; +} + +@implementation OFString (PropertyListParsing) +- (id)objectByParsingPropertyList +{ + void *pool = objc_autoreleasePoolPush(); + OFXMLElement *rootElement = [OFXMLElement elementWithXMLString: self]; + OFXMLAttribute *versionAttribute; + OFArray OF_GENERIC(OFXMLElement *) *elements; + id ret; + + if (![rootElement.name isEqual: @"plist"] || + rootElement.namespace != nil) + @throw [OFInvalidFormatException exception]; + + versionAttribute = [rootElement attributeForName: @"version"]; + + if (versionAttribute == nil) + @throw [OFInvalidFormatException exception]; + + if (![versionAttribute.stringValue isEqual: @"1.0"]) + @throw [OFUnsupportedVersionException + exceptionWithVersion: [versionAttribute stringValue]]; + + elements = rootElement.elements; + + if (elements.count != 1) + @throw [OFInvalidFormatException exception]; + + ret = parseElement(elements.firstObject); + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} +@end DELETED src/OFString+PropertyListValue.h Index: src/OFString+PropertyListValue.h ================================================================== --- src/OFString+PropertyListValue.h +++ src/OFString+PropertyListValue.h @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * 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 "OFString.h" - -OF_ASSUME_NONNULL_BEGIN - -#ifdef __cplusplus -extern "C" { -#endif -extern int _OFString_PropertyListValue_reference; -#ifdef __cplusplus -} -#endif - -@interface OFString (PropertyListValue) -/*! - * @brief The string interpreted as a property list and parsed as an object. - * - * @note This only supports XML property lists! - */ -@property (readonly, nonatomic) id propertyListValue; -@end - -OF_ASSUME_NONNULL_END DELETED src/OFString+PropertyListValue.m Index: src/OFString+PropertyListValue.m ================================================================== --- src/OFString+PropertyListValue.m +++ src/OFString+PropertyListValue.m @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * 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 "OFString+PropertyListValue.h" -#import "OFArray.h" -#import "OFData.h" -#import "OFDate.h" -#import "OFDictionary.h" -#import "OFNumber.h" -#import "OFXMLAttribute.h" -#import "OFXMLElement.h" - -#import "OFInvalidFormatException.h" -#import "OFUnsupportedVersionException.h" - -int _OFString_PropertyListValue_reference; - -static id parseElement(OFXMLElement *element); - -static OFArray * -parseArrayElement(OFXMLElement *element) -{ - OFMutableArray *ret = [OFMutableArray array]; - void *pool = objc_autoreleasePoolPush(); - - for (OFXMLElement *child in element.elements) - [ret addObject: parseElement(child)]; - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -static OFDictionary * -parseDictElement(OFXMLElement *element) -{ - OFMutableDictionary *ret = [OFMutableDictionary dictionary]; - void *pool = objc_autoreleasePoolPush(); - OFArray OF_GENERIC(OFXMLElement *) *children = element.elements; - OFEnumerator OF_GENERIC(OFXMLElement *) *enumerator; - OFXMLElement *key, *object; - - if (children.count % 2 != 0) - @throw [OFInvalidFormatException exception]; - - enumerator = [children objectEnumerator]; - while ((key = [enumerator nextObject]) && - (object = [enumerator nextObject])) { - if (key.namespace != nil || key.attributes.count != 0 || - ![key.name isEqual: @"key"]) - @throw [OFInvalidFormatException exception]; - - [ret setObject: parseElement(object) - forKey: key.stringValue]; - } - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -static OFString * -parseStringElement(OFXMLElement *element) -{ - return element.stringValue; -} - -static OFData * -parseDataElement(OFXMLElement *element) -{ - return [OFData dataWithBase64EncodedString: element.stringValue]; -} - -static OFDate * -parseDateElement(OFXMLElement *element) -{ - return [OFDate dateWithDateString: element.stringValue - format: @"%Y-%m-%dT%H:%M:%SZ"]; -} - -static OFNumber * -parseTrueElement(OFXMLElement *element) -{ - if (element.children.count != 0) - @throw [OFInvalidFormatException exception]; - - return [OFNumber numberWithBool: true]; -} - -static OFNumber * -parseFalseElement(OFXMLElement *element) -{ - if (element.children.count != 0) - @throw [OFInvalidFormatException exception]; - - return [OFNumber numberWithBool: false]; -} - -static OFNumber * -parseRealElement(OFXMLElement *element) -{ - return [OFNumber numberWithDouble: element.doubleValue]; -} - -static OFNumber * -parseIntegerElement(OFXMLElement *element) -{ - void *pool = objc_autoreleasePoolPush(); - OFString *stringValue; - OFNumber *ret; - - stringValue = element.stringValue.stringByDeletingEnclosingWhitespaces; - - if ([stringValue hasPrefix: @"-"]) - ret = [OFNumber numberWithLongLong: stringValue.longLongValue]; - else - ret = [OFNumber numberWithUnsignedLongLong: - stringValue.unsignedLongLongValue]; - - [ret retain]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; -} - -static id -parseElement(OFXMLElement *element) -{ - OFString *elementName; - - if (element.namespace != nil || element.attributes.count != 0) - @throw [OFInvalidFormatException exception]; - - elementName = element.name; - - if ([elementName isEqual: @"array"]) - return parseArrayElement(element); - else if ([elementName isEqual: @"dict"]) - return parseDictElement(element); - else if ([elementName isEqual: @"string"]) - return parseStringElement(element); - else if ([elementName isEqual: @"data"]) - return parseDataElement(element); - else if ([elementName isEqual: @"date"]) - return parseDateElement(element); - else if ([elementName isEqual: @"true"]) - return parseTrueElement(element); - else if ([elementName isEqual: @"false"]) - return parseFalseElement(element); - else if ([elementName isEqual: @"real"]) - return parseRealElement(element); - else if ([elementName isEqual: @"integer"]) - return parseIntegerElement(element); - else - @throw [OFInvalidFormatException exception]; -} - -@implementation OFString (PropertyListValue) -- (id)propertyListValue -{ - void *pool = objc_autoreleasePoolPush(); - OFXMLElement *rootElement = [OFXMLElement elementWithXMLString: self]; - OFXMLAttribute *versionAttribute; - OFArray OF_GENERIC(OFXMLElement *) *elements; - id ret; - - if (![rootElement.name isEqual: @"plist"] || - rootElement.namespace != nil) - @throw [OFInvalidFormatException exception]; - - versionAttribute = [rootElement attributeForName: @"version"]; - - if (versionAttribute == nil) - @throw [OFInvalidFormatException exception]; - - if (![versionAttribute.stringValue isEqual: @"1.0"]) - @throw [OFUnsupportedVersionException - exceptionWithVersion: [versionAttribute stringValue]]; - - elements = rootElement.elements; - - if (elements.count != 1) - @throw [OFInvalidFormatException exception]; - - ret = parseElement(elements.firstObject); - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} -@end Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -1282,11 +1282,11 @@ # import "OFString+CryptoHashing.h" # import "OFString+JSONParsing.h" # ifdef OF_HAVE_FILES # import "OFString+PathAdditions.h" # endif -# import "OFString+PropertyListValue.h" +# import "OFString+PropertyListParsing.h" # import "OFString+Serialization.h" # import "OFString+URLEncoding.h" # import "OFString+XMLEscaping.h" # import "OFString+XMLUnescaping.h" #endif Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -132,11 +132,11 @@ _OFString_CryptoHashing_reference = 1; _OFString_JSONParsing_reference = 1; #ifdef OF_HAVE_FILES _OFString_PathAdditions_reference = 1; #endif - _OFString_PropertyListValue_reference = 1; + _OFString_PropertyListParsing_reference = 1; _OFString_Serialization_reference = 1; _OFString_URLEncoding_reference = 1; _OFString_XMLEscaping_reference = 1; _OFString_XMLUnescaping_reference = 1; } Index: src/OFSystemInfo.m ================================================================== --- src/OFSystemInfo.m +++ src/OFSystemInfo.m @@ -150,11 +150,11 @@ void *pool = objc_autoreleasePoolPush(); @try { OFDictionary *propertyList = [OFString stringWithContentsOfFile: @"/System/Library/CoreServices/SystemVersion.plist"] - .propertyListValue; + .objectByParsingPropertyList; operatingSystemVersion = [[propertyList objectForKey: @"ProductVersion"] copy]; } @finally { objc_autoreleasePoolPop(pool); Index: tests/OFPropertyListTests.m ================================================================== --- tests/OFPropertyListTests.m +++ tests/OFPropertyListTests.m @@ -68,52 +68,54 @@ [OFNumber numberWithBool: false], [OFNumber numberWithFloat: 12.25], [OFNumber numberWithInt: -10], nil]; - TEST(@"-[propertyListValue:] #1", - [PLIST1.propertyListValue isEqual: @"Hello"]) - - TEST(@"-[propertyListValue:] #2", - [PLIST2.propertyListValue isEqual: array]) - - TEST(@"-[propertyListValue:] #3", - [PLIST3.propertyListValue isEqual: + TEST(@"-[objectByParsingPropertyList:] #1", + [PLIST1.objectByParsingPropertyList isEqual: @"Hello"]) + + TEST(@"-[objectByParsingPropertyList:] #2", + [PLIST2.objectByParsingPropertyList isEqual: array]) + + TEST(@"-[objectByParsingPropertyList:] #3", + [PLIST3.objectByParsingPropertyList isEqual: [OFDictionary dictionaryWithKeysAndObjects: @"array", array, @"foo", @"bar", nil]]) - EXPECT_EXCEPTION(@"-[propertyListValue] detecting unsupported version", + EXPECT_EXCEPTION(@"Detecting unsupported version", OFUnsupportedVersionException, [[PLIST(@"") stringByReplacingOccurrencesOfString: @"1.0" withString: @"1.1"] - propertyListValue]) - - EXPECT_EXCEPTION( - @"-[propertyListValue] detecting invalid format #1", - OFInvalidFormatException, - [PLIST(@"") propertyListValue]) - - EXPECT_EXCEPTION( - @"-[propertyListValue] detecting invalid format #2", - OFInvalidFormatException, - [PLIST(@"") propertyListValue]) - - EXPECT_EXCEPTION( - @"-[propertyListValue] detecting invalid format #3", - OFInvalidFormatException, - [PLIST(@"") propertyListValue]) - - EXPECT_EXCEPTION( - @"-[propertyListValue] detecting invalid format #4", - OFInvalidFormatException, - [PLIST(@"") propertyListValue]) - - EXPECT_EXCEPTION( - @"-[propertyListValue] detecting invalid format #5", - OFInvalidFormatException, - [PLIST(@"") propertyListValue]) + objectByParsingPropertyList]) + + EXPECT_EXCEPTION( + @"-[objectByParsingPropertyList] detecting invalid format #1", + OFInvalidFormatException, + [PLIST(@"") objectByParsingPropertyList]) + + EXPECT_EXCEPTION( + @"-[objectByParsingPropertyList] detecting invalid format #2", + OFInvalidFormatException, + [PLIST(@"") objectByParsingPropertyList]) + + EXPECT_EXCEPTION( + @"-[objectByParsingPropertyList] detecting invalid format #3", + OFInvalidFormatException, + [PLIST(@"") objectByParsingPropertyList]) + + EXPECT_EXCEPTION( + @"-[objectByParsingPropertyList] detecting invalid format #4", + OFInvalidFormatException, + [PLIST(@"") + objectByParsingPropertyList]) + + EXPECT_EXCEPTION( + @"-[objectByParsingPropertyList] detecting invalid format #5", + OFInvalidFormatException, + [PLIST(@"") + objectByParsingPropertyList]) objc_autoreleasePoolPop(pool); } @end