3. Walking the tree
Before running, solver prints the whole case: every tag, its attributes and its text, indented by depth, and then its boundaries. Nothing here knows the names in advance.
f90
program solver
!< Tutorial, chapter 3: walking the tree.
use foxy
implicit none
type(xml_file) :: case_file
integer(I4P), allocatable :: ids(:)
integer(I4P) :: b
integer(I4P) :: iostat
type(regex) :: walls
call case_file%parse(filename='case.xml')
print '(A,I0,A)', 'case.xml has ', case_file%tags_number(), ' tags'
call outline(id=1, depth=0)
ids = case_file%find_all(path='case/boundary') ! every match, in document order
do b=1, size(ids)
print '(A)', 'boundary '//case_file%attribute(ids(b), 'side')//': '//case_file%attribute(ids(b), 'type')
enddo
call walls%compile('^(wall|outflow)$', iostat=iostat) ! a regular expression (StringiFor engine)
ids = case_file%matching(walls, ids=case_file%find_all(path='case/boundary'), attribute='type')
print '(A,I0,A)', 'closed boundaries: ', size(ids), ' ('//case_file%attribute(ids(1), 'side')//', ...)'
ids = case_file%select('case/boundary[@type="wall"]') ! a path expression: the same in one call
print '(A,*(1X,A))', 'walls:', (case_file%attribute(ids(b), 'side'), b=1, size(ids))
contains
recursive subroutine outline(id, depth)
!< Print a tag, its attributes and, indented, its children.
integer(I4P), intent(in) :: id, depth
character(len=:), allocatable :: line
integer(I4P), allocatable :: kids(:)
integer(I4P) :: a, k
line = repeat(' ', depth)//case_file%name(id)
do a=1, case_file%attributes_number(id) ! the attributes, without knowing their names
line = line//' '//case_file%attribute_name(id, a)//'='//case_file%attribute(id, case_file%attribute_name(id, a))
enddo
if (len(case_file%content(id=id))>0) line = line//': '//case_file%content(id=id)
print '(A)', line
kids = case_file%children(id)
do k=1, size(kids)
call outline(id=kids(k), depth=depth+1)
enddo
endsubroutine outline
endprogram solvertags_number()is the number of tags: the IDs go from1totags_number().children(id)returns the IDs of the direct children of a tag (children(0): the root tags);name(id)its name.attributes_number(id)andattribute_name(id, a)list the attributes of a tag, in source order.find_allreturns every match, in document order: here the fourboundarytags undercase.matchingkeeps the tags whose name, content or attribute matches a regular expression: here the boundaries whosetypeis a wall or an outflow. The regex (type(regex), StringiFor's engine) is compiled once.selecttakes a path expression, a small XPath:/for children,//for descendants,*for any name, and tests in brackets ([@type="wall"],[@side~="^(east|west)$"],[2]). It does in one call whatfind_allandmatchingdo above; a malformed expression returnsXML_MALFORMEDwith its column. See the path selector.
Running it
$ solver
case.xml has 18 tags
case name=cavity version=2
mesh
grid ni=64 nj=32 nk=1
domain: 0.0 1.0 0.0 0.5 0.0 0.0
time
dt unit=s: 1.0e-3
steps: 500
adaptive: true
fluid name=air
density unit=kg/m3: 1.225
viscosity unit=Pa s: 1.8e-5
boundary side=west type=inflow
velocity: 1.0 0.0 0.0
boundary side=east type=outflow
boundary side=north type=wall
boundary side=south type=wall
output every=100: results.xml
notes: Lid-driven cavity & inflow, after <paper ref="1">.
boundary west: inflow
boundary east: outflow
boundary north: wall
boundary south: wall
closed boundaries: 3 (east, ...)
walls: north southThe text of notes shows two more things that content does for you: & is decoded, and the <![CDATA[...]]> section is taken literally (its < is text, not a tag).
What you learned
children and name walk the tree, attributes_number and attribute_name list attributes, find_all collects repeated tags, matching filters them by a regular expression, select does both with a path expression. Reference: Querying.
Next: 4. Editing and saving.