forked from lee-soft/ViPad
-
Notifications
You must be signed in to change notification settings - Fork 0
/
XMLParser.bas
370 lines (278 loc) · 11.6 KB
/
XMLParser.bas
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
Attribute VB_Name = "XMLParser"
Option Explicit
Private Const XML_IDENTIFIER As String = "XML_"
Private Const ATTRIB_IDENTIFIER As String = "ATTRIB_"
Public XMLElementCount As Long
Private m_attributeCount As Long
Public Function GetNextElementID(Optional elementCollection As Collection)
If XMLElementCount < 2147483647 Then
XMLElementCount = XMLElementCount + 1
Else
XMLElementCount = 0
End If
GetNextElementID = XML_IDENTIFIER & XMLElementCount
End Function
Public Function GetNextAttributeID(Optional elementCollection As Collection)
If m_attributeCount < 2147483647 Then
m_attributeCount = m_attributeCount + 1
Else
m_attributeCount = 0
End If
GetNextAttributeID = ATTRIB_IDENTIFIER & m_attributeCount
End Function
Public Function MakeAttribute(theName As String, theValue As String) As XMLAttribute
Dim attrib As New XMLAttribute
With attrib
.Name = theName
.Value = theValue
End With
Set MakeAttribute = attrib
End Function
Public Function ParseAttributes(ByRef attributeCollection As Collection, sourceXMLHeader As String)
Dim nextWhiteSpace As Long
Dim thisAttribute As String
Dim thisAttributeObj As XMLAttribute
Dim equalPosition As Long
Dim FirstQuote As Long
Dim SecondQuote As Long
sourceXMLHeader = Replace(sourceXMLHeader, vbCrLf, "")
While Len(sourceXMLHeader) > 0
sourceXMLHeader = Trim(sourceXMLHeader)
equalPosition = InStr(sourceXMLHeader, "=")
FirstQuote = InStr(sourceXMLHeader, """") + 1
SecondQuote = InStr(FirstQuote, sourceXMLHeader, """")
If FirstQuote = 1 Or SecondQuote = 0 Then Exit Function
Set thisAttributeObj = New XMLAttribute
thisAttributeObj.Name = Trim(Mid(sourceXMLHeader, 1, equalPosition - 1))
thisAttributeObj.Value = Trim(Mid(sourceXMLHeader, FirstQuote, SecondQuote - FirstQuote))
attributeCollection.Add thisAttributeObj, thisAttributeObj.Key
sourceXMLHeader = Mid(sourceXMLHeader, SecondQuote + 1)
Wend
End Function
Private Function GetLastIncompleteType(ByRef xmlHeaders As Collection, ElementType As String) As XMLFragment
Dim thisElement As XMLFragment
Dim elementIndex As Long
If xmlHeaders.Count = 0 Then
Exit Function
End If
For elementIndex = 0 To xmlHeaders.Count - 1
Set thisElement = xmlHeaders(xmlHeaders.Count - elementIndex)
'Debug.Print thisElement.ElementType
If Not thisElement.Complete And thisElement.ElementType = ElementType Then
Set GetLastIncompleteType = thisElement
Exit For
End If
Next
End Function
Private Function GetLastIncomplete(ByRef xmlHeaders As Collection) As XMLFragment
Dim thisElement As XMLFragment
Dim elementIndex As Long
If xmlHeaders.Count = 0 Then
Exit Function
End If
For elementIndex = 0 To xmlHeaders.Count - 1
Set thisElement = xmlHeaders(xmlHeaders.Count - elementIndex)
If Not thisElement.Complete Then
Set GetLastIncomplete = thisElement
Exit For
End If
Next
End Function
Public Function ParseFragmentedXML(ByRef xmlHeaders As Collection, ByRef m_buffer As String)
Dim charIndex As Long
Dim inXmlElementHeader As Boolean
Dim inXmlElementFooter As Boolean
Dim inTypeDefinition As Boolean
Dim inQuote As Boolean
Dim thisXML As XMLFragment
Dim thisChar As String
Dim nextChar As String
Dim oldXML As XMLFragment
Dim footerType As String
Dim headerIndex As Long
Dim lastIncompleteElement As XMLFragment
Dim cutPoint As Long
Dim tempStr As String
Set thisXML = New XMLFragment
thisXML.Key = GetNextElementID()
charIndex = 1
thisXML.Orphan = True
While charIndex <= Len(m_buffer)
thisChar = Mid(m_buffer, charIndex, 1)
If thisChar = """" Then
If inQuote Then
inQuote = False
Else
inQuote = True
End If
End If
Set lastIncompleteElement = GetLastIncomplete(xmlHeaders)
thisXML.XML = thisXML.XML & thisChar
If Not lastIncompleteElement Is Nothing Then
If Not inXmlElementHeader And Not inXmlElementFooter And _
Not thisChar = "<" And Not thisChar = ">" Then
lastIncompleteElement.Text = lastIncompleteElement.Text & thisChar
lastIncompleteElement.XML = lastIncompleteElement.XML & thisChar
If charIndex > cutPoint Then
cutPoint = charIndex
End If
End If
End If
If Not inQuote Then
If inTypeDefinition Then
If thisChar = " " Or thisChar = ">" Then
inTypeDefinition = False
End If
End If
If thisChar = "/" Then
If inXmlElementHeader Then
If charIndex < Len(m_buffer) Then
nextChar = Mid(m_buffer, charIndex + 1, 1)
inTypeDefinition = False
inXmlElementFooter = True
If Not nextChar = ">" Then
inXmlElementHeader = False
Else
footerType = thisXML.ElementType
End If
End If
End If
End If
If inTypeDefinition Then
thisXML.ElementType = thisXML.ElementType & thisChar
End If
If inXmlElementFooter Then
If thisChar = " " Or thisChar = ">" Then
inXmlElementFooter = False
'Add support for ShortTaggedXML <a/>
If footerType = thisXML.ElementType Then
Set oldXML = thisXML
Else
Set oldXML = GetLastIncompleteType(xmlHeaders, footerType)
oldXML.XML = oldXML.XML & "</" & footerType & ">"
End If
If Not oldXML Is Nothing Then
oldXML.Complete = True
If Not oldXML.Parent Is Nothing Then
tempStr = "<#" & oldXML.Key & "#>"
oldXML.Parent.XML = Replace(oldXML.Parent.XML, tempStr, oldXML.XML, 1, 1)
oldXML.Parent.children.Add oldXML, oldXML.Key
End If
If charIndex > cutPoint Then
cutPoint = charIndex
End If
footerType = ""
End If
ElseIf Not thisChar = "/" Then
footerType = footerType & thisChar
End If
End If
'Start XML Element Definition
If Not inXmlElementHeader Then
If thisChar = "<" Then
If charIndex < Len(m_buffer) Then
nextChar = Mid(m_buffer, charIndex + 1, 1)
'Beginning a new element header definately terminates within the buffer
If Not nextChar = "/" And InStr(charIndex, m_buffer, ">") > 0 Then
Set oldXML = GetLastIncomplete(xmlHeaders)
If Not oldXML Is Nothing Then
thisXML.Orphan = False
Set thisXML.Parent = oldXML
tempStr = "<#" & thisXML.Key & "#>"
If Not Right(oldXML.XML, Len(tempStr)) = tempStr Then
oldXML.XML = oldXML.XML & tempStr
End If
End If
thisXML.XML = "<"
End If
inXmlElementHeader = True
inTypeDefinition = True
End If
End If
Else
If thisChar = ">" Then
inXmlElementHeader = False
xmlHeaders.Add thisXML, thisXML.Key
Set thisXML = New XMLFragment
thisXML.Key = GetNextElementID(xmlHeaders)
thisXML.Orphan = True
If charIndex > cutPoint Then
cutPoint = charIndex
End If
End If
End If
End If
charIndex = charIndex + 1
Wend
If cutPoint > 0 Then
m_buffer = Mid(m_buffer, cutPoint + 1)
End If
End Function
Public Function ParseXML(ByRef targetXML As XMLElement2, sourceXML As String)
Dim endHeader As Long
Dim startHeader As Long
Dim allInclusive As Long
Dim firstWhiteSpace As Long
Dim elementTypeDef As String
If targetXML Is Nothing Then
Exit Function
End If
If sourceXML = "" Then Exit Function
Dim thisXML As XMLElement2
endHeader = InStr(sourceXML, ">")
startHeader = InStr(sourceXML, "<")
allInclusive = InStr(sourceXML, "/>")
firstWhiteSpace = InStr(sourceXML, " ")
If firstWhiteSpace = 0 Then firstWhiteSpace = InStr(sourceXML, vbCrLf)
'No More XML
If startHeader = 0 Then Exit Function
If InStr(sourceXML, "</") = 1 Then
sourceXML = Mid(sourceXML, endHeader + 1)
Set thisXML = New XMLElement2
Set thisXML.Parent = targetXML.Parent.Parent
ParseXML thisXML, sourceXML
Exit Function
End If
If Not startHeader = 1 Then
Set thisXML = New XMLElement2
thisXML.ElementType = ""
Set thisXML.Parent = targetXML.Parent
targetXML.XML = Mid(sourceXML, 1, startHeader - 1)
If Not targetXML.Parent Is Nothing Then
targetXML.Parent.AddChild targetXML
End If
sourceXML = Mid(sourceXML, startHeader)
ParseXML thisXML, sourceXML
Exit Function
End If
If firstWhiteSpace < endHeader And firstWhiteSpace > 0 Then
elementTypeDef = Mid(sourceXML, 2, firstWhiteSpace - 2)
If allInclusive > 0 And allInclusive < endHeader Then
targetXML.Header = Mid(sourceXML, firstWhiteSpace, allInclusive - firstWhiteSpace)
Else
targetXML.Header = Mid(sourceXML, firstWhiteSpace, endHeader - firstWhiteSpace)
End If
Else
elementTypeDef = Replace(Mid(sourceXML, 2, endHeader - 2), "/", "")
End If
targetXML.ElementType = elementTypeDef
'All inclusive XML element, no children!
If allInclusive > 0 And allInclusive < endHeader Then
sourceXML = Mid(sourceXML, allInclusive + 2)
Set thisXML = New XMLElement2
Set thisXML.Parent = targetXML.Parent
'Incase its a standalone inclusive xml element
If Not targetXML.Parent Is Nothing Then
targetXML.Parent.AddChild targetXML
End If
ParseXML thisXML, sourceXML
Else
Set thisXML = New XMLElement2
Set thisXML.Parent = targetXML
If Not targetXML.Parent Is Nothing Then
targetXML.Parent.AddChild targetXML
End If
sourceXML = Mid(sourceXML, endHeader + 1)
ParseXML thisXML, sourceXML
End If
End Function