MONC
Data Types | Functions/Subroutines
sax_xml_parser_mod Module Reference

A SAX parser for XML files. This is used to parse the description of the data and rules. Being a SAX parser it works in a callback fashion, as these often do, and will call the start and end subroutines along with tag details. It still requires some additional stability work to handle ill formatted XML and error checking. More...

Data Types

interface  end_element_callback_interface
 The end element callback interface (on closing of XML tag, this is not called if an opening tag self closes) More...
 
interface  start_element_callback_interface
 The start element callback interface (on opening of XML tag) More...
 

Functions/Subroutines

subroutine, public xml_parse (raw_contents, start_element_callback, end_element_callback)
 Parses some raw XML raw_contents The raw (unparsed) XML string start_element_callback Subroutine to call for each XML start element end_element_callback Subroutine to call for each XML end element. More...
 
subroutine process_individual_tag (raw_contents, start_element_callback, end_element_callback, start_index, end_index)
 Processes an individual XML tag. This deduces whether it is a start or end tag, the name and any additional attributes. More...
 
integer function get_attribute (contents, start_index, attribute_names, attribute_values, attribute_index)
 Retrieves the "next" attribute from the XML tag and returns the position after this attribute to search from next. This contains some additional complexities to deal with a number of different formatted tags. More...
 
integer function occurances_of_substring (string, substring)
 Returns the number of times a specific substring can be found in a string. More...
 

Detailed Description

A SAX parser for XML files. This is used to parse the description of the data and rules. Being a SAX parser it works in a callback fashion, as these often do, and will call the start and end subroutines along with tag details. It still requires some additional stability work to handle ill formatted XML and error checking.

Function/Subroutine Documentation

◆ get_attribute()

integer function sax_xml_parser_mod::get_attribute ( character(len=*), intent(in)  contents,
integer, intent(in)  start_index,
character(len=*), dimension(:), intent(inout), allocatable  attribute_names,
character(len=*), dimension(:), intent(inout), allocatable  attribute_values,
integer, intent(in)  attribute_index 
)
private

Retrieves the "next" attribute from the XML tag and returns the position after this attribute to search from next. This contains some additional complexities to deal with a number of different formatted tags.

Parameters
contentsThe XML string
start_indexThe index to start from when getting the next attribute
attribute_namesCollection of attribute names that the extraced name is appended to
attribute_valuesCollection of attribute values that the extacted value is appended to
attribute_indexThe index to write the name and value of this attribute into

Definition at line 120 of file saxparser.F90.

120  character(len=*), intent(in) :: contents
121  integer, intent(in) :: start_index, attribute_index
122  character(len=*), dimension(:), allocatable, intent(inout) :: attribute_names, attribute_values
123 
124  integer :: equals_posn, end_oftag_absolute, equals_absolute, open_quote, close_quote, begin_index, space_point
125 
126  close_quote=0
127  equals_posn=index(contents(start_index:), "=")
128  if (equals_posn .ne. 0) then
129  ! Currently each attribute requires a specified value
130  equals_absolute=equals_posn+start_index-1
131  if (index(trim(adjustl(contents(start_index:equals_absolute))), " ") .ne. 0) then
132  ! This warns of and eliminates any garbage before the attribute name, such as 't abc' sets 'abc' as the name
133  call log_log(log_warn, "Ignorning leading garbage in attribute name '"//contents(start_index:equals_absolute)//"'")
134  begin_index=start_index+index(trim(adjustl(contents(start_index:equals_absolute))), " ")
135  else
136  begin_index=start_index
137  end if
138  open_quote=index(contents(equals_absolute:), """")
139  if (open_quote .ne. 0) close_quote=index(contents(equals_absolute+open_quote:), """")
140 
141  if (close_quote == 0) then
142  ! No quote therefore must be quoteless and check for whitespace, also check for termination tag and use whichever is closed
143  space_point=index(contents(equals_absolute+open_quote:), " ")
144  close_quote=index(contents(equals_absolute+open_quote:), "/>")-1
145  if (close_quote .lt. 0) close_quote=index(contents(equals_absolute+open_quote:), ">")-1
146  if (space_point .ne. 0 .and. space_point .lt. close_quote) close_quote=space_point
147  else
148  if (open_quote .gt. 2) then
149  ! Deals with matching over to the next tag as no quotes around this value
150  if (len(trim(adjustl(contents(equals_absolute+1:equals_absolute+open_quote-1)))) .gt. 0) then
151  close_quote=index(trim(adjustl(contents(equals_absolute+1:equals_absolute+open_quote-1))), " ")
152  open_quote=0
153  end if
154  end if
155  end if
156  end_oftag_absolute=close_quote+equals_absolute+open_quote
157 
158  attribute_names(attribute_index)=trim(adjustl(contents(begin_index:equals_absolute-1)))
159  attribute_values(attribute_index)=trim(adjustl(contents(equals_absolute+1:end_oftag_absolute-1)))
160  if (len_trim(attribute_names(attribute_index)) == 0 .or. len_trim(attribute_values(attribute_index)) == 0) then
161  call log_log(log_error, "Empty IO server XML configuration name or value")
162  end if
163  get_attribute=end_oftag_absolute
164  else
165  get_attribute=0
166  end if
Here is the call graph for this function:
Here is the caller graph for this function:

◆ occurances_of_substring()

integer function sax_xml_parser_mod::occurances_of_substring ( character(len=*), intent(in)  string,
character(len=*), intent(in)  substring 
)
private

Returns the number of times a specific substring can be found in a string.

Parameters
stringThe whole string to search
substringThe substring to search for and count the number occurances of

Definition at line 173 of file saxparser.F90.

173  character(len=*), intent(in) :: string, substring
174 
175  integer :: current_index, found_index, sub_len
176 
177  sub_len=len(substring)
178  occurances_of_substring=0
179  current_index=1
180  found_index=1
181 
182  do while (found_index .gt. 0)
183  found_index = index(string(current_index:), substring)
184  if (found_index .gt. 0) then
185  occurances_of_substring=occurances_of_substring+1
186  current_index=current_index+found_index+sub_len
187  end if
188  end do
Here is the caller graph for this function:

◆ process_individual_tag()

subroutine sax_xml_parser_mod::process_individual_tag ( character(len=*), intent(in)  raw_contents,
procedure(start_element_callback_interface start_element_callback,
procedure(end_element_callback_interface end_element_callback,
integer, intent(in)  start_index,
integer, intent(in)  end_index 
)
private

Processes an individual XML tag. This deduces whether it is a start or end tag, the name and any additional attributes.

Parameters
raw_contentsThe raw XML string
start_element_callbackSubroutine to call on XML element opening tags
end_element_callbackSubroutine to call on XML element closing tags
start_indexThe start index in the raw contents to go from (the start of this tag)
end_indexThe end index in the raw contents to parse to (the end of this tag)

Definition at line 68 of file saxparser.F90.

68  character(len=*), intent(in) :: raw_contents
69  procedure(start_element_callback_interface) :: start_element_callback
70  procedure(end_element_callback_interface) :: end_element_callback
71  integer, intent(in) :: start_index, end_index
72 
73  character(len=STRING_LENGTH) :: tag_name
74  character(len=STRING_LENGTH), dimension(:), allocatable :: attribute_names, attribute_values
75  logical :: start_tag
76  integer :: name_start_index, name_end_index, number_attributes, attribute_index, i, attribute_start_posn, new_start_posn
77 
78  if (raw_contents(start_index+1:start_index+1) .eq. "!") return
79 
80  start_tag=.not. raw_contents(start_index+1:start_index+1) .eq. "/"
81  name_start_index = start_index+1
82  if (.not. start_tag) name_start_index=name_start_index+1
83  name_end_index=index(raw_contents(name_start_index:end_index), " ")
84  if (name_end_index .eq. 0) then
85  name_end_index=end_index-1
86  else
87  name_end_index=name_end_index+name_start_index-1
88  end if
89 
90  tag_name=raw_contents(name_start_index : name_end_index)
91 
92  if (.not. start_tag) then
93  call end_element_callback(tag_name)
94  else
95  number_attributes=occurances_of_substring(raw_contents(name_end_index+1:end_index), "=")
96  attribute_index=1
97  attribute_start_posn=1
98  allocate(attribute_names(number_attributes), attribute_values(number_attributes))
99  attribute_names(:)=""
100  attribute_values(:)=""
101  do i=1,number_attributes
102  new_start_posn=get_attribute(raw_contents(name_end_index+1:end_index), attribute_start_posn, attribute_names, &
103  attribute_values, i)
104  attribute_start_posn=new_start_posn
105  end do
106  call start_element_callback(tag_name, number_attributes, attribute_names, attribute_values)
107  deallocate(attribute_names, attribute_values)
108  if (raw_contents(end_index-1:end_index) .eq. "/>") call end_element_callback(tag_name)
109  end if
Here is the call graph for this function:
Here is the caller graph for this function:

◆ xml_parse()

subroutine, public sax_xml_parser_mod::xml_parse ( character, dimension(:), intent(in)  raw_contents,
procedure(start_element_callback_interface start_element_callback,
procedure(end_element_callback_interface end_element_callback 
)

Parses some raw XML raw_contents The raw (unparsed) XML string start_element_callback Subroutine to call for each XML start element end_element_callback Subroutine to call for each XML end element.

Definition at line 36 of file saxparser.F90.

36  character, dimension(:), intent(in) :: raw_contents
37  procedure(start_element_callback_interface) :: start_element_callback
38  procedure(end_element_callback_interface) :: end_element_callback
39 
40  character(len=size(raw_contents)) :: string_to_process
41 
42  integer :: current_index, start_index, end_index, i
43  current_index=1
44 
45  ! Here we copy the raw contents array into a string so that string intrinsics can be used on it
46  do i=1, size(raw_contents)
47  string_to_process(i:i)=raw_contents(i)
48  end do
49  do while (current_index .lt. len(string_to_process))
50  start_index=index(string_to_process(current_index:),"<")
51  if (start_index .eq. 0) exit
52  start_index=start_index+current_index-1
53  end_index=index(string_to_process(start_index:),">")
54  if (end_index .eq. 0) exit
55  end_index=end_index+start_index-1
56  call process_individual_tag(string_to_process, start_element_callback, end_element_callback, start_index, end_index)
57  current_index=end_index
58  end do
Here is the call graph for this function:
Here is the caller graph for this function: