pure subroutine parse_from_string(self, source_string)
!< Parse xml data from a chunk of source string (file stringified for IO on device).
class(xml_file), intent(inout) :: self !< XML file handler.
character(*), intent(in) :: source_string !< String containing xml data.
integer(I4P) :: pos, start_pos, end_pos, end_content_pos !< Position indexes.
character(:), allocatable :: tag_name !< Tag name buffer.
character(:), allocatable :: attributes_str !< Tag attributes string buffer.
character(:), allocatable :: tag_content !< Tag content string buffer.
integer(I4P) :: current_level !< Nesting level counter.
logical :: is_closing_tag !< Sentinel for closing tag.
logical :: is_self_closing !< Sentinel for self closing tag.
type(xml_tag) :: tag !< XML tag handler.
integer(I4P) :: parent_id !< Uniq parent tag ID.
integer(I4P), allocatable :: parent_stack(:) !< Stack of parents ID.
call self%free
pos = 1_I4P
current_level = 0_I4P
allocate(parent_stack(1))
parent_stack = 0_I4P
do while (pos <= len_trim(source_string))
! next tag start
start_pos = index(source_string(pos:), '<')
if (start_pos == 0) exit
start_pos = pos + start_pos - 1
! skip comment, XML header
if (start_pos + 3 <= len_trim(source_string)) then
if (source_string(start_pos:start_pos+3) == '<!--'.or.source_string(start_pos:start_pos+1) == '<?') then
end_pos = index(source_string(start_pos+1:), '>')
if (end_pos == 0) exit
pos = start_pos + end_pos + 1
cycle
endif
endif
! close current tag
end_pos = index(source_string(start_pos:), '>')
if (end_pos == 0) exit
end_pos = start_pos + end_pos - 1
! parse tag
call parse_tag_name(tag_str = source_string(start_pos:end_pos), &
tag_name = tag_name, &
attributes_str = attributes_str, &
is_closing = is_closing_tag, &
is_self_closing = is_self_closing)
if (allocated(tag_name)) then
if (is_closing_tag) then
current_level = current_level - 1
else
! add new tag to XML tags list
call tag%free
call self%add_tag(tag=tag)
current_level = current_level + 1
! get parent/child id
if (current_level>1) then
if (parent_stack(current_level-1)>0) then
parent_id = parent_stack(current_level-1)
call self%add_child(parent_id=parent_stack(current_level - 1), child_id=self%nt)
endif
elseif (current_level==1) then
parent_id = 0_I4P
endif
! parent_stack(current_level) = self%nt
if (current_level==1) then
parent_stack(1) = self%nt
else
if (current_level>1) parent_stack = [parent_stack(1:current_level-1),self%nt]
endif
end_content_pos = -1 ! initialize position for self closing tag
if (.not.is_self_closing) then
! get tag content
call get_tag_content(source=source_string, tag_name=tag_name, start_pos=end_pos + 1, content=tag_content, &
end_pos=end_content_pos)
endif
call self%tag(self%nt)%set(name = tag_name, &
sanitize_attributes_value = .true., &
pos = [start_pos, end_pos, end_content_pos], &
indent = (current_level-1)*2, &
is_self_closing = is_self_closing, &
id = self%nt, &
level = current_level, &
parent_id = parent_id, &
attributes_stream_alloc = attributes_str, &
content_alloc = tag_content)
if (is_self_closing) current_level = current_level - 1
endif
endif
pos = end_pos + 1
enddo
endsubroutine parse_from_string