MONC
saxparser.F90
Go to the documentation of this file.
1 
6  use datadefn_mod, only : string_length
8  implicit none
9 
10 #ifndef TEST_MODE
11  private
12 #endif
13 
14  interface
15 
16  subroutine start_element_callback_interface(element_name, number_of_attributes, attribute_names, attribute_values)
17  character(len=*), intent(in) :: element_name
18  character(len=*), dimension(:), intent(in) :: attribute_names, attribute_values
19  integer, intent(in) :: number_of_attributes
21 
23  subroutine end_element_callback_interface(element_name)
24  character(len=*), intent(in) :: element_name
25  end subroutine end_element_callback_interface
26  end interface
27 
28  public xml_parse
29 contains
30 
35  subroutine xml_parse(raw_contents, start_element_callback, end_element_callback)
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
59  end subroutine xml_parse
60 
67  subroutine process_individual_tag(raw_contents, start_element_callback, end_element_callback, start_index, end_index)
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
110  end subroutine process_individual_tag
111 
119  integer function get_attribute(contents, start_index, attribute_names, attribute_values, attribute_index)
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
167  end function get_attribute
168 
172  integer function occurances_of_substring(string, substring)
173  character(len=*), intent(in) :: string, substring
174 
175  integer :: current_index, found_index, sub_len
176 
177  sub_len=len(substring)
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
186  current_index=current_index+found_index+sub_len
187  end if
188  end do
189  end function occurances_of_substring
190 end module sax_xml_parser_mod
integer function occurances_of_substring(string, substring)
Returns the number of times a specific substring can be found in a string.
Definition: saxparser.F90:173
The end element callback interface (on closing of XML tag, this is not called if an opening tag self ...
Definition: saxparser.F90:23
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 c...
Definition: saxparser.F90:36
integer, parameter, public log_error
Only log ERROR messages.
Definition: logging.F90:11
Logging utility.
Definition: logging.F90:2
Contains common definitions for the data and datatypes used by MONC.
Definition: datadefn.F90:2
subroutine, public log_log(level, message, str)
Logs a message at the specified level. If the level is above the current level then the message is ig...
Definition: logging.F90:75
The start element callback interface (on opening of XML tag)
Definition: saxparser.F90:16
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 sear...
Definition: saxparser.F90:120
integer, parameter, public log_warn
Log WARNING and ERROR messages.
Definition: logging.F90:12
integer, parameter, public string_length
Default length of strings.
Definition: datadefn.F90:10
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 addi...
Definition: saxparser.F90:68
A SAX parser for XML files. This is used to parse the description of the data and rules...
Definition: saxparser.F90:5