forked from andreww/fox
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathm_dom_utils.f90
437 lines (384 loc) · 15 KB
/
m_dom_utils.f90
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
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
module m_dom_utils
use fox_m_fsys_array_str, only: str_vs, vs_str
use fox_m_fsys_format, only: operator(//)
use m_common_attrs, only: getValue
use m_common_element, only: element_t, attribute_t, &
get_attlist_size, get_attribute_declaration, express_attribute_declaration
use m_common_struct, only: xml_doc_state
! Public interfaces
use m_dom_dom, only: DOMConfiguration, NamedNodeMap, Node, &
NodeList, &
ATTRIBUTE_NODE, CDATA_SECTION_NODE, COMMENT_NODE, DOCUMENT_NODE, &
DOCUMENT_TYPE_NODE, ELEMENT_NODE, ENTITY_REFERENCE_NODE, &
PROCESSING_INSTRUCTION_NODE, TEXT_NODE, &
getAttributes, getChildNodes, getData, getDomConfig, getEntities, &
getFirstChild, getFoX_checks, getLength, getLocalName, getName, &
getNamespaceURI, getNextSibling, getNodeName, getNodeType, getNotationName,&
getNotations, getOwnerDocument, getOwnerElement, getParameter, &
getParentNode, getPrefix, getPublicId, getSpecified, getSystemId, &
getTagName, getTarget, getXmlStandalone, getXmlVersion, getValue, &
haschildnodes, item, normalizeDocument
! Private interfaces
use m_dom_dom, only: getNamespaceNodes, getStringValue, getXds, namespaceFixup
use m_dom_error, only: DOMException, inException, throw_exception, &
getExceptionCode, &
NAMESPACE_ERR, SERIALIZE_ERR, FoX_INTERNAL_ERROR, FoX_INVALID_NODE
use FoX_wxml, only: xmlf_t, &
xml_AddAttribute, xml_AddCharacters, xml_AddComment, xml_AddElementToDTD, &
xml_AddEntityReference, xml_AddExternalEntity, xml_AddInternalEntity, &
xml_AddDOCTYPE, xml_AddNotation, xml_AddXMLDeclaration, xml_AddXMLPI, &
xml_EndElement, xml_Close, xml_DeclareNamespace, xml_NewElement, &
xml_OpenFile, xml_UndeclareNamespace, xml_AddAttlistToDTD
implicit none
public :: dumpTree
public :: serialize
private
contains
subroutine dumpTree(startNode)
type(Node), pointer :: startNode
integer :: indent_level
indent_level = 0
call dump2(startNode)
contains
recursive subroutine dump2(input)
type(Node), pointer :: input
type(Node), pointer :: temp, np
type(NamedNodeMap), pointer :: attrs
type(NodeList), pointer :: nsnodes
integer :: i
temp => input
do while(associated(temp))
write(*,"(3a,i0)") repeat(" ", indent_level), &
getNodeName(temp), " of type ", &
getNodeType(temp)
if (getNodeType(temp)==ELEMENT_NODE) then
write(*,"(2a)") repeat(" ", indent_level), &
" ATTRIBUTES:"
attrs => getAttributes(temp)
do i = 0, getLength(attrs) - 1
np => item(attrs, i)
write(*, "(2a)") repeat(" ", indent_level)//" ", &
getName(np)
enddo
write(*,"(2a)") repeat(" ", indent_level), &
" IN-SCOPE NAMESPACES:"
nsnodes => getNamespaceNodes(temp)
do i = 0, getLength(nsnodes) - 1
np => item(nsnodes, i)
write(*,"(4a)") repeat(" ", indent_level)//" ", &
getPrefix(np), ':', &
getNamespaceURI(np)
enddo
endif
if (hasChildNodes(temp)) then
indent_level = indent_level + 3
call dump2(getFirstChild(temp))
indent_level = indent_level - 3
endif
temp => getNextSibling(temp)
enddo
end subroutine dump2
end subroutine dumpTree
subroutine serialize(startNode, name, ex)
type(DOMException), intent(out), optional :: ex
type(Node), pointer :: startNode
character(len=*), intent(in) :: name
type(Node), pointer :: doc
type(xmlf_t) :: xf
integer :: iostat
logical :: xmlDecl
if (getNodeType(startNode)/=DOCUMENT_NODE &
.and.getNodeType(startNode)/=ELEMENT_NODE) then
if (getFoX_checks().or.FoX_INVALID_NODE<200) then
call throw_exception(FoX_INVALID_NODE, "serialize", ex)
if (present(ex)) then
if (inException(ex)) then
return
endif
endif
endif
endif
if (getNodeType(startNode)==DOCUMENT_NODE) then
doc => startNode
if (getParameter(getDomConfig(doc), "canonical-form") &
.and.getXmlVersion(doc)=="1.1") then
if (getFoX_checks().or.SERIALIZE_ERR<200) then
call throw_exception(SERIALIZE_ERR, "serialize", ex)
if (present(ex)) then
if (inException(ex)) then
return
endif
endif
endif
endif
call normalizeDocument(startNode, ex)
if (present(ex)) then
! Only possible error should be namespace error ...
! but we should avoid turning an error of 0 into
! an internal error!
if (inException(ex)) then
if (getExceptionCode(ex)/=NAMESPACE_ERR) then
if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then
call throw_exception(FoX_INTERNAL_ERROR, "serialize", ex)
if (present(ex)) then
if (inException(ex)) then
return
endif
endif
endif
else
if (getFoX_checks().or.SERIALIZE_ERR<200) then
call throw_exception(SERIALIZE_ERR, "serialize", ex)
if (present(ex)) then
if (inException(ex)) then
return
endif
endif
endif
endif
endif
endif
else
doc => getOwnerDocument(startNode)
! We need to do this namespace fixup or serialization will fail.
! it doesn't change the semantics of the docs, but other
! normalization would, so we done here
! But only normalize if this is not a DOM level 1 node.
if (getLocalName(startNode)/="" &
.and.getParameter(getDomConfig(doc), "namespaces")) &
call namespaceFixup(startNode, .true.)
endif
xmlDecl = getParameter(getDomConfig(doc), "xml-declaration")
! FIXME we shouldnt really normalize the Document here
! (except for namespace Normalization) but rather just
! pay attention to the DOMConfig values
! NOTE: We set pretty_print on the basis of the FoX specific
! "invalid-pretty-print" config option. The DOM-L3-LS
! option "format-pretty-print is always false and is
! not settable by the user - this is because WXML
! cannot preserve validity conditions that may be set
! by a DTD. If WXML ever learns to do this we will need
! to pass the value of "format-pretty-print" through.
call xml_OpenFile(name, xf, iostat=iostat, unit=-1, &
pretty_print=getParameter(getDomConfig(doc), "invalid-pretty-print"), &
canonical=getParameter(getDomConfig(doc), "canonical-form"), &
warning=.false., addDecl=.false.)
if (iostat/=0) then
if (getFoX_checks().or.SERIALIZE_ERR<200) then
call throw_exception(SERIALIZE_ERR, "serialize", ex)
if (present(ex)) then
if (inException(ex)) then
return
endif
endif
endif
endif
if (xmlDecl) then
if (getXmlStandalone(doc)) then
call xml_AddXMLDeclaration(xf, version=getXmlVersion(doc), standalone=.true.)
else
call xml_AddXMLDeclaration(xf, version=getXmlVersion(doc))
endif
endif
call iter_dmp_xml(xf, startNode, ex)
call xml_Close(xf)
end subroutine serialize
subroutine iter_dmp_xml(xf, arg, ex)
type(DOMException), intent(out), optional :: ex
type(xmlf_t), intent(inout) :: xf
type(Node), pointer :: this, arg, treeroot
type(Node), pointer :: doc, attrchild, np
type(NamedNodeMap), pointer :: nnm
type(DOMConfiguration), pointer :: dc
type(xml_doc_state), pointer :: xds
type(element_t), pointer :: elem
type(attribute_t), pointer :: att_decl
integer :: i_tree, j, k
logical :: doneChildren, doneAttributes
character, pointer :: attrvalue(:), tmp(:)
if (getNodeType(arg)==DOCUMENT_NODE) then
doc => arg
else
doc => getOwnerDocument(arg)
endif
dc => getDomConfig(doc)
xds => getXds(doc)
treeroot => arg
i_tree = 0
doneChildren = .false.
doneAttributes = .false.
this => treeroot
do
if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then
select case(getNodeType(this))
case (ELEMENT_NODE)
nnm => getAttributes(this)
do j = 0, getLength(nnm) - 1
attrchild => item(nnm, j)
if (getLocalName(attrchild)=="xmlns") then
if (len(getValue(attrchild))==0) then
call xml_UndeclareNamespace(xf)
else
call xml_DeclareNamespace(xf, getValue(attrchild))
endif
elseif (getPrefix(attrchild)=="xmlns") then
if (len(getValue(attrchild))==0) then
call xml_UndeclareNamespace(xf, getLocalName(attrchild))
else
call xml_DeclareNamespace(xf, getValue(attrchild), &
getLocalName(attrchild))
endif
endif
enddo
call xml_NewElement(xf, getTagName(this))
case (ATTRIBUTE_NODE)
if ((.not.getParameter(dc, "discard-default-content") &
.or.getSpecified(this)) &
! only output it if it is not a default, or we are outputting defaults
.and. (getPrefix(this)/="xmlns".and.getLocalName(this)/="xmlns")) then
! and we dont output NS declarations here.
! complex loop below is because we might have to worry about entrefs
! being preserved in the attvalue. If we dont, we only go through the loop once anyway.
allocate(attrvalue(0))
do j = 0, getLength(getChildNodes(this)) - 1
attrchild => item(getChildNodes(this), j)
if (getNodeType(attrchild)==TEXT_NODE) then
tmp => attrvalue
allocate(attrvalue(size(tmp)+getLength(attrchild)))
attrvalue(:size(tmp)) = tmp
attrvalue(size(tmp)+1:) = vs_str(getData(attrChild))
deallocate(tmp)
elseif (getNodeType(attrchild)==ENTITY_REFERENCE_NODE) then
tmp => attrvalue
allocate(attrvalue(size(tmp)+len(getNodeName(attrchild))+2))
attrvalue(:size(tmp)) = tmp
attrvalue(size(tmp)+1:) = vs_str("&"//getData(attrChild)//";")
deallocate(tmp)
else
if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then
call throw_exception(FoX_INTERNAL_ERROR, "iter_dmp_xml", ex)
if (present(ex)) then
if (inException(ex)) then
return
endif
endif
endif
endif
enddo
call xml_AddAttribute(xf, getName(this), str_vs(attrvalue))
deallocate(attrvalue)
endif
doneChildren = .true.
case (TEXT_NODE)
call xml_AddCharacters(xf, getData(this))
case (CDATA_SECTION_NODE)
if (getParameter(getDomConfig(doc), "canonical-form")) then
call xml_AddCharacters(xf, getData(this))
else
call xml_AddCharacters(xf, getData(this), parsed = .false.)
endif
case (ENTITY_REFERENCE_NODE)
if (.not.getParameter(getDomConfig(doc), "canonical-form")) then
call xml_AddEntityReference(xf, getNodeName(this))
doneChildren = .true.
endif
case (PROCESSING_INSTRUCTION_NODE)
call xml_AddXMLPI(xf, getTarget(this), getData(this))
case (COMMENT_NODE)
if (.not.getParameter(getDomConfig(doc), "comments")) then
call xml_AddComment(xf, getData(this))
endif
case (DOCUMENT_TYPE_NODE)
if (.not.getParameter(getDomConfig(doc), "canonical-form")) then
call xml_AddDOCTYPE(xf, getName(this))
nnm => getNotations(this)
do j = 0, getLength(nnm)-1
np => item(nnm, j)
if (getSystemId(np)=="") then
call xml_AddNotation(xf, getNodeName(np), public=getPublicId(np))
elseif (getPublicId(np)=="") then
call xml_AddNotation(xf, getNodeName(np), system=getSystemId(np))
else
call xml_AddNotation(xf, getNodeName(np), system=getSystemId(np), &
public=getPublicId(np))
endif
enddo
nnm => getEntities(this)
do j = 0, getLength(nnm)-1
np => item(nnm, j)
if (getSystemId(np)=="") then
call xml_AddInternalEntity(xf, getNodeName(np), getStringValue(np))
elseif (getPublicId(np)=="".and.getNotationName(np)=="") then
call xml_AddExternalEntity(xf, getNodeName(np), system=getSystemId(np))
elseif (getNotationName(np)=="") then
call xml_AddExternalEntity(xf, getNodeName(np), system=getSystemId(np), &
public=getPublicId(np))
elseif (getPublicId(np)=="") then
call xml_AddExternalEntity(xf, getNodeName(np), system=getSystemId(np), &
notation=getNotationName(np))
else
call xml_AddExternalEntity(xf, getNodeName(np), system=getSystemId(np), &
public=getPublicId(np), notation=getNotationName(np))
endif
enddo
do j = 1, size(xds%element_list%list)
elem => xds%element_list%list(j)
if (associated(elem%model)) &
call xml_AddElementToDTD(xf, str_vs(elem%name), str_vs(elem%model))
! Because we may have some undeclared but referenced elements
do k = 1, get_attlist_size(elem)
att_decl => get_attribute_declaration(elem, k)
call xml_AddAttlistToDTD(xf, str_vs(elem%name), &
express_attribute_declaration(att_decl))
enddo
enddo
endif
end select
else
if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then
doneAttributes = .true.
else
if (getNodeType(this)==ELEMENT_NODE) then
call xml_EndElement(xf, getTagName(this))
endif
endif
endif
if (.not.doneChildren) then
if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then
if (getLength(getAttributes(this))>0) then
this => item(getAttributes(this), 0)
else
doneAttributes = .true.
endif
elseif (hasChildNodes(this)) then
this => getFirstChild(this)
doneChildren = .false.
doneAttributes = .false.
else
doneChildren = .true.
doneAttributes = .false.
endif
else ! if doneChildren
if (associated(this, treeroot)) exit
if (getNodeType(this)==ATTRIBUTE_NODE) then
if (i_tree<getLength(getAttributes(getOwnerElement(this)))-1) then
i_tree= i_tree+ 1
this => item(getAttributes(getOwnerElement(this)), i_tree)
doneChildren = .false.
else
i_tree= 0
this => getOwnerElement(this)
doneAttributes = .true.
doneChildren = .false.
endif
elseif (associated(getNextSibling(this))) then
this => getNextSibling(this)
doneChildren = .false.
doneAttributes = .false.
else
this => getParentNode(this)
endif
endif
enddo
end subroutine iter_dmp_xml
end module m_dom_utils