Remove unnecessary code and resources
[pithos-ios] / Classes / SBJsonWriter.m
1 /*
2  Copyright (C) 2009 Stig Brautaset. All rights reserved.
3  
4  Redistribution and use in source and binary forms, with or without
5  modification, are permitted provided that the following conditions are met:
6  
7  * Redistributions of source code must retain the above copyright notice, this
8    list of conditions and the following disclaimer.
9  
10  * Redistributions in binary form must reproduce the above copyright notice,
11    this list of conditions and the following disclaimer in the documentation
12    and/or other materials provided with the distribution.
13  
14  * Neither the name of the author nor the names of its contributors may be used
15    to endorse or promote products derived from this software without specific
16    prior written permission.
17  
18  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
22  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #import "SBJsonWriter.h"
31
32 @interface SBJsonWriter ()
33
34 - (BOOL)appendValue:(id)fragment into:(NSMutableString*)json;
35 - (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json;
36 - (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json;
37 - (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json;
38
39 - (NSString*)indent;
40
41 @end
42
43 @implementation SBJsonWriter
44
45 static NSMutableCharacterSet *kEscapeChars;
46
47 + (void)initialize {
48         kEscapeChars = [[NSMutableCharacterSet characterSetWithRange: NSMakeRange(0,32)] retain];
49         [kEscapeChars addCharactersInString: @"\"\\"];
50 }
51
52
53 @synthesize sortKeys;
54 @synthesize humanReadable;
55
56 /**
57  @deprecated This exists in order to provide fragment support in older APIs in one more version.
58  It should be removed in the next major version.
59  */
60 - (NSString*)stringWithFragment:(id)value {
61     [self clearErrorTrace];
62     depth = 0;
63     NSMutableString *json = [NSMutableString stringWithCapacity:128];
64     
65     if ([self appendValue:value into:json])
66         return json;
67     
68     return nil;
69 }
70
71
72 - (NSString*)stringWithObject:(id)value {
73     
74     if ([value isKindOfClass:[NSDictionary class]] || [value isKindOfClass:[NSArray class]]) {
75         return [self stringWithFragment:value];
76     }
77     
78     if ([value respondsToSelector:@selector(proxyForJson)]) {
79         NSString *tmp = [self stringWithObject:[value proxyForJson]];
80         if (tmp)
81             return tmp;
82     }
83         
84
85     [self clearErrorTrace];
86     [self addErrorWithCode:EFRAGMENT description:@"Not valid type for JSON"];
87     return nil;
88 }
89
90
91 - (NSString*)indent {
92     return [@"\n" stringByPaddingToLength:1 + 2 * depth withString:@" " startingAtIndex:0];
93 }
94
95 - (BOOL)appendValue:(id)fragment into:(NSMutableString*)json {
96     if ([fragment isKindOfClass:[NSDictionary class]]) {
97         if (![self appendDictionary:fragment into:json])
98             return NO;
99         
100     } else if ([fragment isKindOfClass:[NSArray class]]) {
101         if (![self appendArray:fragment into:json])
102             return NO;
103         
104     } else if ([fragment isKindOfClass:[NSString class]]) {
105         if (![self appendString:fragment into:json])
106             return NO;
107         
108     } else if ([fragment isKindOfClass:[NSNumber class]]) {
109         if ('c' == *[fragment objCType])
110             [json appendString:[fragment boolValue] ? @"true" : @"false"];
111         else
112             [json appendString:[fragment stringValue]];
113         
114     } else if ([fragment isKindOfClass:[NSNull class]]) {
115         [json appendString:@"null"];
116     } else if ([fragment respondsToSelector:@selector(proxyForJson)]) {
117         [self appendValue:[fragment proxyForJson] into:json];
118         
119     } else {
120         [self addErrorWithCode:EUNSUPPORTED description:[NSString stringWithFormat:@"JSON serialisation not supported for %@", [fragment class]]];
121         return NO;
122     }
123     return YES;
124 }
125
126 - (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json {
127     if (maxDepth && ++depth > maxDepth) {
128         [self addErrorWithCode:EDEPTH description: @"Nested too deep"];
129         return NO;
130     }
131     [json appendString:@"["];
132     
133     BOOL addComma = NO;    
134     for (id value in fragment) {
135         if (addComma)
136             [json appendString:@","];
137         else
138             addComma = YES;
139         
140         if ([self humanReadable])
141             [json appendString:[self indent]];
142         
143         if (![self appendValue:value into:json]) {
144             return NO;
145         }
146     }
147     
148     depth--;
149     if ([self humanReadable] && [fragment count])
150         [json appendString:[self indent]];
151     [json appendString:@"]"];
152     return YES;
153 }
154
155 - (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json {
156     if (maxDepth && ++depth > maxDepth) {
157         [self addErrorWithCode:EDEPTH description: @"Nested too deep"];
158         return NO;
159     }
160     [json appendString:@"{"];
161     
162     NSString *colon = [self humanReadable] ? @" : " : @":";
163     BOOL addComma = NO;
164     NSArray *keys = [fragment allKeys];
165     if (self.sortKeys)
166         keys = [keys sortedArrayUsingSelector:@selector(compare:)];
167     
168     for (id value in keys) {
169         if (addComma)
170             [json appendString:@","];
171         else
172             addComma = YES;
173         
174         if ([self humanReadable])
175             [json appendString:[self indent]];
176         
177         if (![value isKindOfClass:[NSString class]]) {
178             [self addErrorWithCode:EUNSUPPORTED description: @"JSON object key must be string"];
179             return NO;
180         }
181         
182         if (![self appendString:value into:json])
183             return NO;
184         
185         [json appendString:colon];
186         if (![self appendValue:[fragment objectForKey:value] into:json]) {
187             [self addErrorWithCode:EUNSUPPORTED description:[NSString stringWithFormat:@"Unsupported value for key %@ in object", value]];
188             return NO;
189         }
190     }
191     
192     depth--;
193     if ([self humanReadable] && [fragment count])
194         [json appendString:[self indent]];
195     [json appendString:@"}"];
196     return YES;    
197 }
198
199 - (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json {
200     
201     [json appendString:@"\""];
202     
203     NSRange esc = [fragment rangeOfCharacterFromSet:kEscapeChars];
204     if ( !esc.length ) {
205         // No special chars -- can just add the raw string:
206         [json appendString:fragment];
207         
208     } else {
209         NSUInteger length = [fragment length];
210         for (NSUInteger i = 0; i < length; i++) {
211             unichar uc = [fragment characterAtIndex:i];
212             switch (uc) {
213                 case '"':   [json appendString:@"\\\""];       break;
214                 case '\\':  [json appendString:@"\\\\"];       break;
215                 case '\t':  [json appendString:@"\\t"];        break;
216                 case '\n':  [json appendString:@"\\n"];        break;
217                 case '\r':  [json appendString:@"\\r"];        break;
218                 case '\b':  [json appendString:@"\\b"];        break;
219                 case '\f':  [json appendString:@"\\f"];        break;
220                 default:    
221                     if (uc < 0x20) {
222                         [json appendFormat:@"\\u%04x", uc];
223                     } else {
224                         CFStringAppendCharacters((CFMutableStringRef)json, &uc, 1);
225                     }
226                     break;
227                     
228             }
229         }
230     }
231     
232     [json appendString:@"\""];
233     return YES;
234 }
235
236
237 @end