-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSGMLParser.m
More file actions
165 lines (131 loc) · 6.35 KB
/
SGMLParser.m
File metadata and controls
165 lines (131 loc) · 6.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//
// SGMLParser.m
//
//
// Created by Benjamin Humphries on 4/12/16.
// Copyright © 2016 Marz Software. All rights reserved.
//
#import "SGMLParser.h"
#define NAMESPACES false
#define NAMESPACES_PREFIXES false
#define RESOLVE_EXTERNAL_ENTITIES false
@implementation SGMLParser
- (NSDictionary *)parseXMLString:(NSString *)xmlString {
xmlString = [xmlString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
xmlString = [xmlString stringByReplacingOccurrencesOfString:@"\r" withString:@""];
NSMutableDictionary *top = [[NSMutableDictionary alloc] init];
Stack *stack = [[Stack alloc] init];
[stack push:top];
NSMutableString *elementNameSoFar = [NSMutableString string];
NSMutableString *dataSoFar = [NSMutableString string];
BOOL isBuildingElement = false;
BOOL isBuildingData = false;
BOOL isClosingTag = false;
for (NSInteger i = 0; i < xmlString.length; i++) {
if ([xmlString characterAtIndex:i] == '<') {
isClosingTag = [self isNextClosingTag:xmlString currentIndex:i];
// If we were building data then store in current dictionary
if (isBuildingData) {
NSMutableDictionary *top = (NSMutableDictionary *)[stack peek];
[top setValue:dataSoFar forKey:elementNameSoFar];
}
// Start new element
elementNameSoFar = [NSMutableString string];
isBuildingElement = true;
isBuildingData = false;
} else if ([xmlString characterAtIndex:i] == '>') {
// End element
isBuildingElement = false;
if (isClosingTag) {
NSLog(@"closing %@", elementNameSoFar);
[stack pop]; // Just pop?
}
// Not a closing tag
else {
NSInteger next = i + 1;
if (next < xmlString.length) {
// Ignore space
while (next < xmlString.length && ([xmlString characterAtIndex:next] == ' ')) {
next++;
}
i = next - 1;
// LOOK AHEAD if the next value is an element
if ([self isNextElement:xmlString currentIndex:i]) {
// Get the current node
NSMutableDictionary *top = (NSMutableDictionary *)[stack peek];
// What if there are multiple keys with the same name!??!
if ([top objectForKey:elementNameSoFar]) {
// Create an array to store the multi values
NSMutableArray *newArray = [[NSMutableArray alloc] init];
// If the old object at this element is an array itself
if ([[top objectForKey:elementNameSoFar] isKindOfClass:[NSArray class]]) {
// Then copy the values
NSArray *oldArray = (NSMutableArray *)[top objectForKey:elementNameSoFar];
newArray = [[NSMutableArray alloc] initWithArray:oldArray];
} else {
// It is a dictionary
// Add the current one
[newArray addObject:[top objectForKey:elementNameSoFar]];
// create the new one that we will be processesing
}
// Create the new dictionary to process
NSDictionary *newDict = [[NSMutableDictionary alloc] init];
// Add it to the arry
[newArray addObject:newDict];
// Set the new value to point to an array
[top setValue:newArray forKey:elementNameSoFar];
// Push the new Dictionary that we will process
[stack push:newDict];
} else {
// Create a new dictionary node (we know it is a Dict because it has an element in it from look ahead)
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
// Add this dict to the current node dictionary
[top setValue:newDict forKey:elementNameSoFar];
// Make this node the current node dictionary.
[stack push:newDict];
}
}
// Or data
else {
// Start building datavalue
isBuildingData = true;
dataSoFar = [NSMutableString string];
}
}
// NEXT IS OUT OF BOUNDS
else { NSLog(@"done!"); }
}
} else {
if (isBuildingElement) {
[elementNameSoFar appendFormat:@"%c",[xmlString characterAtIndex:i]];
} else if (isBuildingData) {
[dataSoFar appendFormat:@"%c",[xmlString characterAtIndex:i]];
} else {
NSLog(@"hmm where does this go? %c",[xmlString characterAtIndex:i]);
}
}
}
NSLog(@"parse result = %@", top);
return top;
}
# pragma mark - LOOK AHEAD methods
- (BOOL)isNextElement:(NSString *)xmlString currentIndex:(NSInteger)index {
BOOL isElement = false;
NSInteger next = index +1;
if (next < xmlString.length) {
isElement = ([xmlString characterAtIndex:next] == '<');
}
return isElement;
}
- (BOOL)isNextData:(NSString *)xmlString currentIndex:(NSInteger)index {
return ![self isNextElement:xmlString currentIndex:index];
}
- (BOOL)isNextClosingTag:(NSString *)xmlString currentIndex:(NSInteger)index {
BOOL isClosing = false;
NSInteger next = index +1;
if (next < xmlString.length) {
isClosing = ([xmlString characterAtIndex:next] == '/');
}
return isClosing;
}
@end