Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
OldSchoolPropertyListWriting.m
Go to the documentation of this file.
1/*
2 OldSchoolPropertyListWriting.m
3 Copyright 2006-2013 Jens Ayton
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy of this software
6 and associated documentation files (the "Software"), to deal in the Software without
7 restriction, including without limitation the rights to use, copy, modify, merge, publish,
8 distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
9 Software is furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all copies or
12 substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
15 BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19*/
20
21#include <assert.h>
22
25
26
27static void AppendNewLineAndIndent(NSMutableString *ioString, unsigned indentDepth);
28
29
30@implementation NSString (OldSchoolPropertyListWriting)
31
32- (NSString *)oldSchoolPListFormatWithIndentation:(unsigned)inIndentation errorDescription:(NSString **)outErrorDescription
33{
34 NSCharacterSet *charSet;
35 NSRange foundRange, searchRange;
36 NSString *foundString;
37 NSMutableString *newString;
38 NSUInteger length;
39
40 length = [self length];
41 if (0 != length
42 && [self rangeOfCharacterFromSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]].location == NSNotFound
43 && ![[NSCharacterSet decimalDigitCharacterSet] longCharacterIsMember:[self characterAtIndex:0]])
44 {
45 // This is an alphanumeric string whose first character is not a digit
46 return [[self copy] autorelease];
47 }
48 else
49 {
50 charSet = [NSCharacterSet characterSetWithCharactersInString:@"\"\r\n\\"];
51 foundRange = [self rangeOfCharacterFromSet:charSet options:NSLiteralSearch];
52 if (NSNotFound == foundRange.location)
53 {
54 newString = (NSMutableString *)self;
55 }
56 else
57 {
58 // Escape quotes, backslashes and newlines
59 newString = [[[self substringToIndex:foundRange.location] mutableCopy] autorelease];
60
61 for (;;)
62 {
63 // Append escaped character
64 foundString = [self substringWithRange:foundRange];
65 if ([foundString isEqual:@"\""]) [newString appendString:@"\\\""];
66 else if ([foundString isEqual:@"\n"]) [newString appendString:@"\\\n"];
67 else if ([foundString isEqual:@"\r"]) [newString appendString:@"\\\r"];
68 else if ([foundString isEqual:@"\\"]) [newString appendString:@"\\\\"];
69 else
70 {
71 [NSException raise:NSInternalInconsistencyException format:@"%s: expected \" or newline, found %@", __PRETTY_FUNCTION__, foundString];
72 }
73
74 // Use rest of string…
75 searchRange.location = foundRange.location + foundRange.length;
76 searchRange.length = length - searchRange.location;
77
78 // …to search for next char needing escaping
79 foundRange = [self rangeOfCharacterFromSet:charSet options:NSLiteralSearch range:searchRange];
80 if (NSNotFound == foundRange.location)
81 {
82 [newString appendString:[self substringWithRange:searchRange]];
83 break;
84 }
85 }
86 }
87
88 return [NSString stringWithFormat:@"\"%@\"", newString];
89 }
90}
91
92@end
93
94
95@implementation NSNumber (OldSchoolPropertyListWriting)
96
97- (NSString *)oldSchoolPListFormatWithIndentation:(unsigned)inIndentation errorDescription:(NSString **)outErrorDescription
98{
99 NSString *result;
100 double dVal;
101
102 if ([self oo_isBoolean])
103 {
104 if ([self boolValue]) result = @"true";
105 else result = @"false";
106 }
107 else if ([self oo_isFloatingPointNumber])
108 {
109 dVal = [self doubleValue];
110 result = [NSString stringWithFormat:@"%.8g", dVal];
111 }
112 else result = [NSString stringWithFormat:@"%@", self];
113
114 // Allow infinities, but remember that they’ll be read in as strings
115#if 0
116 if ([result isEqual:@"inf"] || [result isEqual:@"-inf"])
117 {
118 *outErrorDescription = @"infinities cannot be represented in old-school property lists";
119 return nil;
120 }
121#endif
122
123 return result;
124}
125
126@end
127
128
129@implementation NSData (OldSchoolPropertyListWriting)
130
131- (NSString *)oldSchoolPListFormatWithIndentation:(unsigned)inIndentation errorDescription:(NSString **)outErrorDescription
132{
133 const uint8_t *srcBytes;
134 uint8_t *dstBytes, *curr;
135 NSUInteger i, j, srcLength, dstLength;
136 const char hexTable[] = "0123456789ABCDEF";
137 NSString *result;
138
139 srcBytes = [self bytes];
140 srcLength = [self length];
141
142 dstLength = 2 * srcLength + srcLength/8 + 2 + (srcLength/64 * (1 + inIndentation));
143
144 dstBytes = malloc(dstLength);
145 if (dstBytes == NULL)
146 {
147 if (NULL != outErrorDescription)
148 {
149 *outErrorDescription = [NSString stringWithFormat:@"failed to allocate space (%lu bytes) for conversion of NSData to old-school property list representation", dstLength];
150 }
151 return nil;
152 }
153
154 curr = dstBytes;
155 *curr++ = '<';
156 for (i = 0; i != srcLength; ++i)
157 {
158 if (0 != i && 0 == (i & 3))
159 {
160 if (0 == (i & 31))
161 {
162 *curr++ = '\n';
163 j = inIndentation;
164 while (--j) *curr++ = '\t';
165 }
166 *curr++ = ' ';
167 }
168 *curr++ = hexTable[srcBytes[i] >> 4];
169 *curr++ = hexTable[srcBytes[i] & 0xF];
170 }
171 *curr = '>';
172
173 assert((size_t)(curr - dstBytes) <= dstLength);
174
175 result = [[NSString alloc] initWithBytesNoCopy:dstBytes length:dstLength encoding:NSASCIIStringEncoding freeWhenDone:YES];
176 return [result autorelease];
177}
178
179@end
180
181
182@implementation NSArray (OldSchoolPropertyListWriting)
183
184- (NSString *)oldSchoolPListFormatWithIndentation:(unsigned)inIndentation errorDescription:(NSString **)outErrorDescription
185{
186 NSMutableString *result;
187 NSUInteger i, count;
188 id object;
189
190 result = [NSMutableString string];
191
192 [result appendString:@"("];
193
194 count = [self count];
195 AppendNewLineAndIndent(result, inIndentation + 1);
196
197 for (i = 0; i != count; ++i)
198 {
199 if (0 != i)
200 {
201 [result appendString:@","];
202 AppendNewLineAndIndent(result, inIndentation + 1);
203 }
204
205 object = [self objectAtIndex:i];
206 if (![object conformsToProtocol:@protocol (OldSchoolPropertyListWriting)])
207 {
208 if (nil != object && NULL != outErrorDescription)
209 {
210 *outErrorDescription = [NSString stringWithFormat:@"non-plist object in dictionary"];
211 }
212 return nil;
213 }
214
215 object = [object oldSchoolPListFormatWithIndentation:inIndentation + 1 errorDescription:outErrorDescription];
216 if (nil == object) return nil;
217 [result appendString:object];
218 }
219
220 AppendNewLineAndIndent(result, inIndentation);
221 [result appendString:@")"];
222 return result;
223}
224
225@end
226
227
228@implementation NSDictionary (OldSchoolPropertyListWriting)
229
230- (NSString *)oldSchoolPListFormatWithIndentation:(unsigned)inIndentation errorDescription:(NSString **)outErrorDescription
231{
232 NSMutableString *result;
233 NSUInteger i, count;
234 NSArray *allKeys;
235 id key, value;
236 NSString *valueDesc;
237
238 result = [NSMutableString string];
239 allKeys = [[self allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
240 count = [allKeys count];
241
242 [result appendString:@"{"];
243
244 AppendNewLineAndIndent(result, inIndentation + 1);
245
246 for (i = 0; i != count; ++i)
247 {
248 if (0 != i)
249 {
250 AppendNewLineAndIndent(result, inIndentation + 1);
251 }
252
253 key = [allKeys objectAtIndex:i];
254 if (![key isKindOfClass:[NSString class]])
255 {
256 if (NULL != outErrorDescription) *outErrorDescription = [NSString stringWithFormat:@"non-string key in dictionary"];
257 return nil;
258 }
259 value = [self objectForKey:key];
260 if (![value conformsToProtocol:@protocol(OldSchoolPropertyListWriting)])
261 {
262 if (nil != value && NULL != outErrorDescription)
263 {
264 *outErrorDescription = [NSString stringWithFormat:@"non-plist object in dictionary"];
265 }
266 return nil;
267 }
268
269 key = [key oldSchoolPListFormatWithIndentation:inIndentation + 1 errorDescription:outErrorDescription];
270 if (nil == key) return nil;
271 valueDesc = [value oldSchoolPListFormatWithIndentation:inIndentation + 1 errorDescription:outErrorDescription];
272 if (nil == valueDesc) return nil;
273
274 [result appendFormat:@"%@ =", key];
275 if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]])
276 {
277 AppendNewLineAndIndent(result, inIndentation + 1);
278 }
279 else
280 {
281 [result appendString:@" "];
282 }
283 [result appendFormat:@"%@;", valueDesc];
284 }
285
286 AppendNewLineAndIndent(result, inIndentation);
287 [result appendString:@"}"];
288
289 return result;
290}
291
292@end
293
294
295@interface NSObject (OldSchoolPropertyListWriting_Private)
296
297- (NSString *)oldSchoolPListFormatWithIndentation:(unsigned)inIndentation errorDescription:(NSString **)outErrorDescription;
298
299@end
300
301
302@implementation NSObject (OldSchoolPropertyListWriting)
303
304- (NSData *)oldSchoolPListFormatWithErrorDescription:(NSString **)outErrorDescription
305{
306 NSString *string;
307
308 string = [self oldSchoolPListFormatWithIndentation:0 errorDescription:outErrorDescription];
309 return [[string stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding];
310}
311
312
313- (NSString *)oldSchoolPListFormatWithIndentation:(unsigned)inIndentation errorDescription:(NSString **)outErrorDescription
314{
315 if (NULL != outErrorDescription)
316 {
317 *outErrorDescription = [NSString stringWithFormat:@"Class %@ does not support OldSchoolPropertyListWriting", [self className]];
318 }
319 return nil;
320}
321
322@end
323
324
325static void AppendNewLineAndIndent(NSMutableString *ioString, unsigned indentDepth)
326{
327 [ioString appendString:@"\n"];
328 while (indentDepth--) [ioString appendString:@"\t"];
329}
return self
unsigned count
return nil
static void AppendNewLineAndIndent(NSMutableString *ioString, unsigned indentDepth)