/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, * 2018 * Jonathan Schleifer <js@heap.zone> * * 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 stringValue] doubleValue]]; } static OFNumber * parseIntegerElement(OFXMLElement *element) { return [OFNumber numberWithIntMax: [[element stringValue] decimalValue]]; } 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