From 38f52a5db9b5c750cab77f5ac568aa471f90ea05 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 10:42:42 -0400 Subject: [PATCH 01/45] added FoX XML API in folder xml --- src/Makefile | 17 +- src/xml/LICENSE | 71 + src/xml/README | 3 + src/xml/common/FoX_common.F90 | 55 + src/xml/common/m_common_attrs.F90 | 1081 ++ src/xml/common/m_common_buffer.F90 | 261 + src/xml/common/m_common_charset.F90 | 447 + src/xml/common/m_common_content_model.F90 | 490 + src/xml/common/m_common_element.F90 | 1645 +++ src/xml/common/m_common_elstack.F90 | 236 + src/xml/common/m_common_entities.F90 | 475 + src/xml/common/m_common_entity_expand.F90 | 86 + src/xml/common/m_common_error.F90 | 211 + src/xml/common/m_common_io.F90 | 102 + src/xml/common/m_common_namecheck.F90 | 501 + src/xml/common/m_common_namespaces.F90 | 830 ++ src/xml/common/m_common_notations.F90 | 120 + src/xml/common/m_common_struct.F90 | 121 + src/xml/common/makefile | 58 + src/xml/dom/FoX_dom.F90 | 261 + src/xml/dom/m_dom_dom.F90 | 12321 ++++++++++++++++ src/xml/dom/m_dom_error.F90 | 211 + src/xml/dom/m_dom_extras.F90 | 2376 +++ src/xml/dom/m_dom_parse.F90 | 577 + src/xml/dom/m_dom_utils.F90 | 433 + src/xml/dom/makefile | 48 + src/xml/fsys/fox_m_fsys_abort_flush.F90 | 119 + src/xml/fsys/fox_m_fsys_array_str.F90 | 90 + src/xml/fsys/fox_m_fsys_count_parse_input.F90 | 561 + src/xml/fsys/fox_m_fsys_format.F90 | 2250 +++ src/xml/fsys/fox_m_fsys_parse_input.F90 | 2204 +++ src/xml/fsys/fox_m_fsys_realtypes.F90 | 12 + src/xml/fsys/fox_m_fsys_string.F90 | 35 + src/xml/fsys/fox_m_fsys_string_list.F90 | 191 + src/xml/fsys/m_ieee.F90 | 17 + src/xml/fsys/makefile | 47 + src/xml/sax/FoX_sax.F90 | 34 + src/xml/sax/m_sax_operate.F90 | 313 + src/xml/sax/m_sax_parser.F90 | 3027 ++++ src/xml/sax/m_sax_reader.F90 | 402 + src/xml/sax/m_sax_tokenizer.F90 | 1185 ++ src/xml/sax/m_sax_types.F90 | 161 + src/xml/sax/m_sax_xml_source.F90 | 438 + src/xml/sax/makefile | 49 + src/xml/utils/FoX_utils.F90 | 22 + src/xml/utils/fox_m_utils_mtprng.F90 | 360 + src/xml/utils/fox_m_utils_uri.F90 | 1013 ++ src/xml/utils/fox_m_utils_uuid.F90 | 247 + src/xml/utils/makefile | 45 + src/xml/wxml/FoX_wxml.F90 | 43 + src/xml/wxml/m_wxml_core.F90 | 1848 +++ src/xml/wxml/m_wxml_escape.F90 | 140 + src/xml/wxml/m_wxml_overloads.F90 | 1028 ++ src/xml/wxml/makefile | 46 + 54 files changed, 38962 insertions(+), 2 deletions(-) create mode 100644 src/xml/LICENSE create mode 100644 src/xml/README create mode 100644 src/xml/common/FoX_common.F90 create mode 100644 src/xml/common/m_common_attrs.F90 create mode 100644 src/xml/common/m_common_buffer.F90 create mode 100644 src/xml/common/m_common_charset.F90 create mode 100644 src/xml/common/m_common_content_model.F90 create mode 100644 src/xml/common/m_common_element.F90 create mode 100644 src/xml/common/m_common_elstack.F90 create mode 100644 src/xml/common/m_common_entities.F90 create mode 100644 src/xml/common/m_common_entity_expand.F90 create mode 100644 src/xml/common/m_common_error.F90 create mode 100644 src/xml/common/m_common_io.F90 create mode 100644 src/xml/common/m_common_namecheck.F90 create mode 100644 src/xml/common/m_common_namespaces.F90 create mode 100644 src/xml/common/m_common_notations.F90 create mode 100644 src/xml/common/m_common_struct.F90 create mode 100644 src/xml/common/makefile create mode 100644 src/xml/dom/FoX_dom.F90 create mode 100644 src/xml/dom/m_dom_dom.F90 create mode 100644 src/xml/dom/m_dom_error.F90 create mode 100644 src/xml/dom/m_dom_extras.F90 create mode 100644 src/xml/dom/m_dom_parse.F90 create mode 100644 src/xml/dom/m_dom_utils.F90 create mode 100644 src/xml/dom/makefile create mode 100644 src/xml/fsys/fox_m_fsys_abort_flush.F90 create mode 100644 src/xml/fsys/fox_m_fsys_array_str.F90 create mode 100644 src/xml/fsys/fox_m_fsys_count_parse_input.F90 create mode 100644 src/xml/fsys/fox_m_fsys_format.F90 create mode 100644 src/xml/fsys/fox_m_fsys_parse_input.F90 create mode 100644 src/xml/fsys/fox_m_fsys_realtypes.F90 create mode 100644 src/xml/fsys/fox_m_fsys_string.F90 create mode 100644 src/xml/fsys/fox_m_fsys_string_list.F90 create mode 100644 src/xml/fsys/m_ieee.F90 create mode 100644 src/xml/fsys/makefile create mode 100644 src/xml/sax/FoX_sax.F90 create mode 100644 src/xml/sax/m_sax_operate.F90 create mode 100644 src/xml/sax/m_sax_parser.F90 create mode 100644 src/xml/sax/m_sax_reader.F90 create mode 100644 src/xml/sax/m_sax_tokenizer.F90 create mode 100644 src/xml/sax/m_sax_types.F90 create mode 100644 src/xml/sax/m_sax_xml_source.F90 create mode 100644 src/xml/sax/makefile create mode 100644 src/xml/utils/FoX_utils.F90 create mode 100644 src/xml/utils/fox_m_utils_mtprng.F90 create mode 100644 src/xml/utils/fox_m_utils_uri.F90 create mode 100644 src/xml/utils/fox_m_utils_uuid.F90 create mode 100644 src/xml/utils/makefile create mode 100644 src/xml/wxml/FoX_wxml.F90 create mode 100644 src/xml/wxml/m_wxml_core.F90 create mode 100644 src/xml/wxml/m_wxml_escape.F90 create mode 100644 src/xml/wxml/m_wxml_overloads.F90 create mode 100644 src/xml/wxml/makefile diff --git a/src/Makefile b/src/Makefile index 05cb7df6e..9f0cf555d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -227,7 +227,14 @@ endif # Targets #=============================================================================== -all: xml-fortran $(program) +all: xml xml-fortran $(program) +xml: + cd xml; cd fsys; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; cd utils; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; cd common; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; cd wxml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; cd sax; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; cd dom; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" xml-fortran: cd xml-fortran; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" cd templates; make F90=$(F90) F90FLAGS="$(F90FLAGS)" @@ -248,6 +255,12 @@ uninstall: @rm $(DESTDIR)$(prefix)/share/man/man1/openmc.1 @rm $(DESTDIR)$(prefix)/share/doc/$(program)/copyright distclean: clean + cd xml; cd fsys; make clean + cd xml; cd utils; make clean + cd xml; cd common; make clean + cd xml; cd wxml; make clean + cd xml; cd sax; make clean + cd xml; cd dom; make clean cd xml-fortran; make clean cd templates; make clean clean: @@ -260,7 +273,7 @@ neat: #=============================================================================== .SUFFIXES: .F90 .o -.PHONY: all xml-fortran install uninstall clean neat distclean +.PHONY: all xml xml-fortran install uninstall clean neat distclean %.o: %.F90 $(F90) $(F90FLAGS) -DGIT_SHA1="\"$(GIT_SHA1)\"" -Ixml-fortran -Itemplates -c $< diff --git a/src/xml/LICENSE b/src/xml/LICENSE new file mode 100644 index 000000000..53df0326a --- /dev/null +++ b/src/xml/LICENSE @@ -0,0 +1,71 @@ +FoX - Fortran XML library + +FoX was originally derived from the xmlf90 codebase, +(c) Alberto Garcia & Jon Wakelin, 2003-2004. + +FoX also includes externally-written code from +Scott Ladd , which is licensed +as shown in the file utils/fox_m_utils_mtprng.f90 + +This version of FoX is: +(c) 2005-2009 Toby White +(c) 2007-2009 Gen-Tao Chiang +(c) 2008-2012 Andrew Walker + +All rights reserved. + +* Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This distribution also includes code wrtten by Scott Ladd +utils/, + +! This computer program source file is supplied "AS IS". Scott Robert
+! Ladd (hereinafter referred to as "Author") disclaims all warranties,
+! expressed or implied, including, without limitation, the warranties
+! of merchantability and of fitness for any purpose. The Author
+! assumes no liability for direct, indirect, incidental, special,
+! exemplary, or consequential damages, which may result from the use
+! of this software, even if advised of the possibility of such damage.
+!
+! The Author hereby grants anyone permission to use, copy, modify, and
+! distribute this source code, or portions hereof, for any purpose,
+! without fee, subject to the following restrictions:
+!
+! 1. The origin of this source code must not be misrepresented.
+!
+! 2. Altered versions must be plainly marked as such and must not
+! be misrepresented as being the original source.
+!
+! 3. This Copyright notice may not be removed or altered from any
+! source or altered source distribution.
+!
+! The Author specifically permits (without fee) and encourages the use
+! of this source code for entertainment, education, or decoration. If
+! you use this source code in a product, acknowledgment is not required
+! but would be appreciated. diff --git a/src/xml/README b/src/xml/README new file mode 100644 index 000000000..5707d4524 --- /dev/null +++ b/src/xml/README @@ -0,0 +1,3 @@ +This directory contains source code from FoX - A Fortran Library for XML +Current version is 4.1.2 +www1.gly.bris.ac.uk/~walker/FoX/ diff --git a/src/xml/common/FoX_common.F90 b/src/xml/common/FoX_common.F90 new file mode 100644 index 000000000..eaa7e684f --- /dev/null +++ b/src/xml/common/FoX_common.F90 @@ -0,0 +1,55 @@ +module FoX_common + + use fox_m_fsys_array_str + use fox_m_fsys_format + use fox_m_fsys_parse_input + use fox_m_fsys_count_parse_input + use m_common_attrs + use m_common_error + + implicit none + private + +#ifdef DUMMYLIB + character(len=*), parameter :: FoX_version = '4.1.2-dummy' +#else + character(len=*), parameter :: FoX_version = '4.1.2' +#endif + + public :: FoX_version + + public :: rts + public :: countrts + public :: str + public :: operator(//) + + public :: FoX_set_fatal_errors + public :: FoX_get_fatal_errors + public :: FoX_set_fatal_warnings + public :: FoX_get_fatal_warnings + +#ifndef DUMMYLIB + public :: str_vs + public :: vs_str + public :: alloc + public :: concat +#endif + +!These are all exported through SAX now + public :: dictionary_t +!SAX functions + public :: getIndex + public :: getLength + public :: getLocalName + public :: getQName + public :: getURI + public :: getValue + public :: getType + public :: isSpecified + public :: isDeclared + public :: setSpecified + public :: setDeclared +!For convenience + public :: hasKey + +end module FoX_common diff --git a/src/xml/common/m_common_attrs.F90 b/src/xml/common/m_common_attrs.F90 new file mode 100644 index 000000000..33a2cdb48 --- /dev/null +++ b/src/xml/common/m_common_attrs.F90 @@ -0,0 +1,1081 @@ +module m_common_attrs + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only : str_vs, vs_str_alloc + use m_common_element, only: get_att_type_enum, ATT_CDATA, ATT_CDAMB, ATT_TYPES + use m_common_error, only : FoX_error, FoX_fatal + + implicit none + private + + !Initial length of dictionary + integer, parameter :: DICT_INIT_LEN = 10 + !Multiplier if we need to extend it. + real, parameter :: DICT_LEN_MULT = 1.5 + + type dict_item + character(len=1), pointer, dimension(:) :: nsURI => null() + character(len=1), pointer, dimension(:) :: localName => null() + character(len=1), pointer, dimension(:) :: prefix => null() + character(len=1), pointer, dimension(:) :: key => null() + character(len=1), pointer, dimension(:) :: value => null() + logical :: specified = .true. + logical :: declared = .false. + logical :: isId = .false. + integer :: type = 11 + end type dict_item + + type dict_item_ptr + type(dict_item), pointer :: d => null() + end type dict_item_ptr +#endif + + type dictionary_t + private +#ifndef DUMMYLIB + type(dict_item_ptr), dimension(:), pointer :: list => null() + character, dimension(:), pointer :: base => null() +#else + integer :: i +#endif + end type dictionary_t + + public :: dictionary_t + + ! Building procedures +#ifndef DUMMYLIB + public :: init_dict + public :: reset_dict + public :: add_item_to_dict + public :: destroy_dict +#endif + ! Query and extraction procedures + + ! SAX names: + public :: getIndex + public :: getLength + public :: getLocalName + public :: getQName + public :: getURI + public :: getValue + public :: getType + public :: isSpecified + public :: setSpecified + public :: isDeclared + public :: setDeclared + public :: hasKey + +#ifndef DUMMYLIB + public :: len + public :: get_key + public :: get_value + public :: remove_key + public :: has_key + public :: print_dict + + ! Namespaces + public :: get_prefix + public :: get_localName + public :: set_nsURI + public :: set_prefix + public :: set_localName +#endif + + ! For internal FoX use only: + public :: get_att_index_pointer + public :: getWhitespaceHandling + public :: setIsId + public :: getIsId + + public :: setBase + public :: getBase + + public :: sortAttrs + + interface len + module procedure getLength + end interface + + interface hasKey + module procedure has_key, has_key_ns + end interface + + interface getIndex + module procedure get_key_index, get_key_index_ns + end interface + + interface getQName + module procedure get_key + end interface + + interface getValue + module procedure get_value_by_key, get_value_by_index, get_value_by_key_ns + end interface +#ifndef DUMMYLIB + interface get_value + module procedure get_value_by_key, get_value_by_index + end interface + interface remove_key + module procedure remove_key_by_index + end interface +#endif + + interface getURI + module procedure get_nsURI_by_index + end interface +#ifndef DUMMYLIB + interface get_prefix + module procedure get_prefix_by_index + end interface +#endif + interface getLocalName + module procedure get_localName_by_index + end interface +#ifndef DUMMYLIB + interface get_localName + module procedure get_localName_by_index + end interface + interface set_nsURI + module procedure set_nsURI_by_index + end interface + interface set_prefix + module procedure set_prefix_by_index + end interface + interface set_localName + module procedure set_localName_by_index_s + module procedure set_localName_by_index_vs + end interface +#endif + + interface getType + module procedure getType_by_index + module procedure getType_by_keyname + end interface + + interface isSpecified + module procedure isSpecified_by_index + module procedure isSpecified_by_key + module procedure isSpecified_by_keyNS + end interface + + interface isDeclared + module procedure isDeclared_by_index + module procedure isDeclared_by_key + module procedure isDeclared_by_keyNS + end interface + +#ifndef DUMMYLIB + interface getIsId + module procedure getIsId_by_index + end interface + + interface setIsId + module procedure setIsId_by_index + end interface + + interface destroy + module procedure destroy_dict_item + module procedure destroy_dict + end interface + +#endif + +contains + + pure function getLength(dict) result(n) + type(dictionary_t), intent(in) :: dict + integer :: n + +#ifndef DUMMYLIB + n = ubound(dict%list, 1) +#else + n = 0 +#endif + end function getLength + + + function has_key(dict, key) result(found) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: key + logical :: found + +#ifndef DUMMYLIB + integer :: i + + do i = 1, ubound(dict%list, 1) + if (key==str_vs(dict%list(i)%d%key)) then + found = .true. + return + endif + enddo + found = .false. +#else + found = .false. +#endif + end function has_key + + function has_key_ns(dict, uri, localname) result(found) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: uri, localname + logical :: found + +#ifndef DUMMYLIB + integer :: i + found = .false. + do i = 1, ubound(dict%list, 1) + ! FIXME xlf 10.01 segfaults if the below is done as + ! an AND rather than two separate ifs. This is + ! probably due to the Heisenbug + if (uri==str_vs(dict%list(i)%d%nsURI)) then + if (localname==str_vs(dict%list(i)%d%localname)) then + found = .true. + exit + endif + endif + enddo +#else + found = .false. +#endif + end function has_key_ns + + pure function get_key_index(dict, key) result(ind) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: key + integer :: ind + +#ifndef DUMMYLIB + integer :: i + do i = 1, ubound(dict%list, 1) + if (key == str_vs(dict%list(i)%d%key)) then + ind = i + return + endif + enddo + ind = 0 +#else + ind = 0 +#endif + end function get_key_index + + pure function get_key_index_ns(dict, uri, localname) result(ind) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: uri, localname + integer :: ind + +#ifndef DUMMYLIB + integer :: i + ind = -1 + do i = 1, ubound(dict%list, 1) + if (uri==str_vs(dict%list(i)%d%nsURI) & + .and. localname==str_vs(dict%list(i)%d%localname)) then + ind = i + exit + endif + enddo +#else + ind = 0 +#endif + end function get_key_index_ns + +#ifndef DUMMYLIB + pure function get_value_by_key_len(dict, key) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: key + integer :: n + + integer :: i + + do i = 1, ubound(dict%list, 1) + if (key == str_vs(dict%list(i)%d%key)) then + n = size(dict%list(i)%d%value) + return + endif + enddo + n = 0 + end function get_value_by_key_len +#endif + + function get_value_by_key(dict, key) result(value) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: key +#ifndef DUMMYLIB + character(len=get_value_by_key_len(dict,key)) :: value + + integer :: i + + do i = 1, ubound(dict%list, 1) + if (key == str_vs(dict%list(i)%d%key)) then + value = str_vs(dict%list(i)%d%value) + return + endif + enddo + value = "" +#else + character(len=1) :: value + value = "" +#endif + end function get_value_by_key + +#ifndef DUMMYLIB + pure function get_value_by_key_ns_len(dict, nsUri, localname) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: nsUri + character(len=*), intent(in) :: localname + integer :: n + + integer :: i + + do i = 1, ubound(dict%list, 1) + if (nsUri==str_vs(dict%list(i)%d%nsURI) & + .and.localname==str_vs(dict%list(i)%d%localname)) then + n = size(dict%list(i)%d%value) + return + endif + enddo + n = 0 + end function get_value_by_key_ns_len +#endif + + function get_value_by_key_ns(dict, nsUri, localname) result(value) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: nsUri + character(len=*), intent(in) :: localname +#ifndef DUMMYLIB + character(len=get_value_by_key_ns_len(dict, nsURI, localname)) :: value + + integer :: i + + do i = 1, ubound(dict%list, 1) + if (nsUri==str_vs(dict%list(i)%d%nsURI) & + .and.localname==str_vs(dict%list(i)%d%localname)) then + value = str_vs(dict%list(i)%d%value) + return + endif + enddo + value = "" +#else + character(len=1) :: value + value = "" +#endif + end function get_value_by_key_ns + +#ifndef DUMMYLIB + subroutine get_att_index_pointer(dict, key, i, value) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: key + integer, intent(out) :: i + character, pointer :: value(:) + + value => null() + do i = 1, ubound(dict%list, 1) + if (key == str_vs(dict%list(i)%d%key)) then + value => dict%list(i)%d%value + return + endif + enddo + i = 0 + end subroutine get_att_index_pointer + + subroutine remove_key_by_index(dict, ind) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: ind + + integer :: i, n + type(dict_item_ptr), pointer :: tempList(:) + + n = ubound(dict%list, 1) + + if (ind<=0.or.ind>n) return + + allocate(tempList(0:n-1)) + do i = 0, ind-1 + tempList(i)%d => dict%list(i)%d + enddo + call destroy(dict%list(ind)%d) + do i = ind+1, n + tempList(i-1)%d => dict%list(i)%d + enddo + deallocate(dict%list) + dict%list => tempList + end subroutine remove_key_by_index +#endif + + pure function get_value_by_index_len(dict, i) result(n) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + integer :: n + +#ifdef DUMMYLIB + n = 1 +#else + if (i>0.and.i<=ubound(dict%list, 1)) then + n = size(dict%list(i)%d%value) + else + n = 0 + endif +#endif + end function get_value_by_index_len + + function get_value_by_index(dict, i) result(value) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i +#ifndef DUMMYLIB + character(len=get_value_by_index_len(dict, i)) :: value + + if (i>0.and.i<=ubound(dict%list, 1)) then + value = str_vs(dict%list(i)%d%value) + else + value = "" + endif +#else + character(len=1) :: value + value = "" +#endif + end function get_value_by_index + +#ifndef DUMMYLIB + pure function get_key_len(dict, i) result(n) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + integer :: n + + if (i>0.and.i<=ubound(dict%list, 1)) then + n = size(dict%list(i)%d%key) + else + n = 0 + endif + end function get_key_len +#endif + + function get_key(dict, i) result(key) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i +#ifndef DUMMYLIB + character(len=get_key_len(dict,i)) :: key + + if (i>0.and.i<=ubound(dict%list, 1)) then + key = str_vs(dict%list(i)%d%key) + else + key = "" + endif +#else + character(len=1) :: key + key = "" +#endif + end function get_key + +#ifndef DUMMYLIB + subroutine add_item_to_dict(dict, key, value, prefix, nsURI, type, itype, specified, declared) + type(dictionary_t), intent(inout) :: dict + character(len=*), intent(in) :: key + character(len=*), intent(in) :: value + character(len=*), intent(in), optional :: prefix + character(len=*), intent(in), optional :: nsURI + character(len=*), intent(in), optional :: type + integer, intent(in), optional :: itype + logical, intent(in), optional :: specified + logical, intent(in), optional :: declared + + type(dict_item_ptr), pointer :: tempList(:) + integer :: i, n + + if (present(prefix) .eqv. .not.present(nsURI)) & + call FoX_Error('Namespace improperly specified') + + n = ubound(dict%list, 1) + allocate(tempList(0:n+1)) + do i = 0, n + tempList(i)%d => dict%list(i)%d + enddo + n = n + 1 + + allocate(tempList(n)%d) + tempList(n)%d%value => vs_str_alloc(value) + if (present(prefix)) then + tempList(n)%d%key => vs_str_alloc(prefix//":"//key) + tempList(n)%d%localname => vs_str_alloc(key) + tempList(n)%d%prefix => vs_str_alloc(prefix) + tempList(n)%d%nsURI => vs_str_alloc(nsURI) + else + tempList(n)%d%key => vs_str_alloc(key) + tempList(n)%d%localname => vs_str_alloc(key) + allocate(tempList(n)%d%prefix(0)) + allocate(tempList(n)%d%nsURI(0)) + endif + if (present(type)) then + if (present(itype)) & + call FoX_fatal("internal library error in add_item_to_dict") + tempList(n)%d%type = get_att_type_enum(type) + elseif (present(itype)) then + tempList(n)%d%type = itype + else + tempList(n)%d%type = ATT_CDAMB + endif + if (present(specified)) then + tempList(n)%d%specified = specified + else + tempList(n)%d%specified = .true. + endif + if (present(declared)) then + tempList(n)%d%declared = declared + else + tempList(n)%d%declared = .false. + endif + + deallocate(dict%list) + dict%list => tempList + + end subroutine add_item_to_dict + + subroutine set_nsURI_by_index(dict, i, nsURI) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + character(len=*), intent(in) :: nsURI + + if (associated(dict%list(i)%d%nsURI)) & + deallocate(dict%list(i)%d%nsURI) + dict%list(i)%d%nsURI => vs_str_alloc(nsURI) + end subroutine set_nsURI_by_index + + subroutine set_prefix_by_index(dict, i, prefix) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + character(len=*), intent(in) :: prefix + + if (associated(dict%list(i)%d%prefix)) & + deallocate(dict%list(i)%d%prefix) + dict%list(i)%d%prefix => vs_str_alloc(prefix) + end subroutine set_prefix_by_index + + subroutine set_localName_by_index_s(dict, i, localName) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + character(len=*), intent(in) :: localName + + if (associated(dict%list(i)%d%localName)) & + deallocate(dict%list(i)%d%localName) + dict%list(i)%d%localName => vs_str_alloc(localName) + end subroutine set_localName_by_index_s + + subroutine set_localName_by_index_vs(dict, i, localName) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + character(len=1), dimension(:), intent(in) :: localName + + if (associated(dict%list(i)%d%localName)) & + deallocate(dict%list(i)%d%localName) + allocate(dict%list(i)%d%localName(size(localName))) + dict%list(i)%d%localName = localName + end subroutine set_localName_by_index_vs +#endif + + pure function get_nsURI_by_index(dict, i) result(nsURI) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i +#ifndef DUMMYLIB + character(len=size(dict%list(i)%d%nsURI)) :: nsURI + nsURI = str_vs(dict%list(i)%d%nsURI) +#else + character(len=1) :: nsURI + nsURI = "" +#endif + end function get_nsURI_by_index + +#ifndef DUMMYLIB + pure function get_prefix_by_index(dict, i) result(prefix) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + character(len=size(dict%list(i)%d%prefix)) :: prefix + + prefix = str_vs(dict%list(i)%d%prefix) + end function get_prefix_by_index +#endif + + pure function get_localName_by_index(dict, i) result(localName) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i +#ifndef DUMMYLIB + character(len=size(dict%list(i)%d%localName)) :: localName + localName = str_vs(dict%list(i)%d%localName) +#else + character(len=1) :: localName + localName = "" +#endif + end function get_localName_by_index + +#ifndef DUMMYLIB + pure function get_nsURI_by_keyname_len(dict, keyname) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + integer :: n + + integer :: i + + i = get_key_index(dict, keyname) + n = size(dict%list(i)%d%nsURI) + end function get_nsURI_by_keyname_len +#endif + +#ifndef DUMMYLIB + pure function get_nsURI_by_keyname(dict, keyname) result(nsURI) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + character(len=get_nsURI_by_keyname_len(dict, keyname)) :: nsURI + integer :: i + + i = get_key_index(dict, keyname) + nsURI = str_vs(dict%list(i)%d%nsURI) + end function get_nsURI_by_keyname + + pure function get_prefix_by_keyname_len(dict, keyname) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + integer :: n + + integer :: i + + i = get_key_index(dict, keyname) + n = size(dict%list(i)%d%prefix) + + end function get_prefix_by_keyname_len + + pure function get_prefix_by_keyname(dict, keyname) result(prefix) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + character(len=get_prefix_by_keyname_len(dict,keyname)) :: prefix + integer :: i + + i = get_key_index(dict, keyname) + prefix = str_vs(dict%list(i)%d%prefix) + + end function get_prefix_by_keyname + + pure function get_localname_by_keyname_len(dict, keyname) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + integer :: n + + integer :: i + + i = get_key_index(dict, keyname) + n = size(dict%list(i)%d%localName) + + end function get_localname_by_keyname_len + + pure function get_localName_by_keyname(dict, keyname) result(localName) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + character(get_localname_by_keyname_len(dict, keyname)) :: localname + integer :: i + + i=get_key_index(dict, keyname) + localName = str_vs(dict%list(i)%d%localName) + + end function get_localName_by_keyname +#endif + +#ifndef DUMMYLIB + pure function getType_by_index_len(dict, i) result(n) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + integer :: n + + if (i>0.and.i<=ubound(dict%list, 1)) then + n = len_trim(ATT_TYPES(dict%list(i)%d%type)) + else + n = 0 + endif + end function getType_by_index_len +#endif + + function getType_by_index(dict, i) result(type) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i +#ifndef DUMMYLIB + character(len=getType_by_index_len(dict, i)) :: type + + if (i>0.and.i<=ubound(dict%list, 1)) then + type = ATT_TYPES(dict%list(i)%d%type) + else + type = "" + endif +#else + character(len=1) :: type + type = "" +#endif + end function getType_by_index + +#ifndef DUMMYLIB + pure function getType_by_keyname_len(dict, keyname) result(n) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname + integer :: n + + integer :: i + i = get_key_index(dict, keyname) + + if (i>0) then + n = len_trim(ATT_TYPES(i)) + else + n = 0 + endif + end function getType_by_keyname_len +#endif + + function getType_by_keyname(dict, keyname) result(type) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: keyname +#ifndef DUMMYLIB + character(len=getType_by_keyname_len(dict, keyname)) :: type + + integer :: i + i = get_key_index(dict, keyname) + if (i>0) then + type = ATT_TYPES(dict%list(i)%d%type) + else + type = "" + endif +#else + character(len=1) :: type + type = "" +#endif + end function getType_by_keyname + + function isSpecified_by_index(dict, i) result(p) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + logical :: p + +#ifndef DUMMYLIB + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%specified + else + p = .false. + endif +#else + p = .false. +#endif + end function isSpecified_by_index + + function isSpecified_by_key(dict, qName) result(p) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: qName + logical :: p + +#ifndef DUMMYLIB + integer :: i + i = getIndex(dict, qName) + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%specified + else + p = .false. + endif +#else + p = .false. +#endif + end function isSpecified_by_key + + subroutine setSpecified(dict, i, p) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + logical, intent(in) :: p + +#ifndef DUMMYLIB + if (i>0.and.i<=ubound(dict%list, 1)) then + dict%list(i)%d%specified = p + endif +#endif + end subroutine setSpecified + + function isSpecified_by_keyNS(dict, uri, localName) result(p) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: uri + character(len=*), intent(in) :: localName + logical :: p + +#ifndef DUMMYLIB + integer :: i + i = getIndex(dict, uri, localName) + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%specified + else + p = .false. + endif +#else + p = .false. +#endif + end function isSpecified_by_keyNS + + function isDeclared_by_index(dict, i) result(p) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + logical :: p + +#ifndef DUMMYLIB + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%declared + else + p = .false. + endif +#else + p = .false. +#endif + end function isDeclared_by_index + + function isDeclared_by_key(dict, qName) result(p) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: qName + logical :: p + +#ifndef DUMMYLIB + integer :: i + i = getIndex(dict, qName) + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%declared + else + p = .false. + endif +#else + p = .false. +#endif + end function isDeclared_by_key + + function isDeclared_by_keyNS(dict, uri, localName) result(p) + type(dictionary_t), intent(in) :: dict + character(len=*), intent(in) :: uri + character(len=*), intent(in) :: localName + logical :: p + +#ifndef DUMMYLIB + integer :: i + i = getIndex(dict, uri, localName) + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%declared + else + p = .false. + endif +#else + p = .false. +#endif + end function isDeclared_by_keyNS + + subroutine setDeclared(dict, i, p) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + logical, intent(in) :: p + +#ifndef DUMMYLIB + if (i>0.and.i<=ubound(dict%list, 1)) then + dict%list(i)%d%declared = p + endif +#endif + end subroutine setDeclared + +#ifndef DUMMYLIB + function getIsId_by_index(dict, i) result(p) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + logical :: p + + if (i>0.and.i<=ubound(dict%list, 1)) then + p = dict%list(i)%d%isId + else + p = .false. + endif + + end function getIsId_by_index + + subroutine setIsId_by_index(dict, i, p) + type(dictionary_t), intent(inout) :: dict + integer, intent(in) :: i + logical, intent(in) :: p + + if (i>0.and.i<=ubound(dict%list, 1)) then + dict%list(i)%d%isId = p + endif + end subroutine setIsId_by_index + + function getWhitespaceHandling(dict, i) result(j) + type(dictionary_t), intent(in) :: dict + integer, intent(in) :: i + integer :: j + + if (i<=ubound(dict%list, 1)) then + select case(dict%list(i)%d%type) + case (ATT_CDATA) + j = 0 ! + case (ATT_CDAMB) + j = 1 + case default + j = 2 + end select + else + j = 2 + endif + + end function getWhitespaceHandling + + subroutine setBase(dict, base) + type(dictionary_t), intent(inout) :: dict + character(len=*), intent(in) :: base + + if (associated(dict%base)) deallocate(dict%base) + dict%base => vs_str_alloc(base) + end subroutine setBase + + pure function getBase_len(dict) result(n) + type(dictionary_t), intent(in) :: dict + integer :: n + + if (associated(dict%base)) then + n = size(dict%base) + else + n = 0 + endif + end function getBase_len + + function getBase(dict) result(base) + type(dictionary_t), intent(in) :: dict + character(len=getBase_len(dict)) :: base + + if (associated(dict%base)) then + base = str_vs(dict%base) + else + base = "" + endif + end function getBase + + subroutine destroy_dict_item(d) + type(dict_item), pointer :: d + + if (associated(d)) then + deallocate(d%key) + deallocate(d%value) + deallocate(d%nsURI) + deallocate(d%prefix) + deallocate(d%localName) + deallocate(d) + endif + end subroutine destroy_dict_item + + subroutine init_dict(dict) + type(dictionary_t), intent(out) :: dict + + + allocate(dict%list(0:0)) + allocate(dict%list(0)%d) + allocate(dict%list(0)%d%key(0)) + + end subroutine init_dict + + subroutine destroy_dict(dict) + type(dictionary_t), intent(inout) :: dict + integer :: i + + if (associated(dict%list)) then + deallocate(dict%list(0)%d%key) + deallocate(dict%list(0)%d) + do i = 1, ubound(dict%list, 1) + call destroy(dict%list(i)%d) + enddo + deallocate(dict%list) + endif + if (associated(dict%base)) deallocate(dict%base) + + end subroutine destroy_dict + + + subroutine reset_dict(dict) + type(dictionary_t), intent(inout) :: dict + + call destroy_dict(dict) + call init_dict(dict) + + end subroutine reset_dict + + subroutine sortAttrs(dict) + type(dictionary_t), intent(inout) :: dict + + logical :: done(ubound(dict%list, 1)) + type(dict_item_ptr), dimension(:), pointer :: list => null() + integer :: i, j, n, firstIndex + character, pointer :: firstKey(:) + + !Ridiculously naive sort algorithm. We are unlikely + !to ever be sorting more then ten or so attributes though + + n = ubound(dict%list, 1) + + allocate(list(0:n)) + list(0)%d => dict%list(0)%d + + j = 1 + done = .false. + + firstIndex = 1 + do while (firstIndex/=0) + firstIndex = 0 + firstKey => null() + do i = 1, n + if (.not.done(i).and.str_vs(dict%list(i)%d%key)=="xmlns" & + .or. str_vs(dict%list(i)%d%prefix)=="xmlns") then + firstIndex = i + if (associated(firstKey)) then + if (llt(str_vs(dict%list(i)%d%key),str_vs(firstKey))) then + firstIndex = i + firstKey => dict%list(i)%d%key + endif + else + firstIndex = i + firstKey => dict%list(i)%d%key + endif + endif + enddo + if (firstIndex/=0) then + done(firstIndex) = .true. + list(j)%d => dict%list(firstIndex)%d + j = j + 1 + endif + enddo + + do while (any(.not.done)) + firstIndex = 0 + firstKey => null() + do i = 1, n + if (.not.done(i)) then + if (associated(firstKey)) then + if (llt(str_vs(dict%list(i)%d%key),str_vs(firstKey))) then + firstIndex = i + firstKey => dict%list(i)%d%key + endif + else + firstIndex = i + firstKey => dict%list(i)%d%key + endif + endif + enddo + done(firstIndex) = .true. + list(j)%d => dict%list(firstIndex)%d + j = j + 1 + enddo + + deallocate(dict%list) + dict%list => list + + end subroutine sortAttrs + + + subroutine print_dict(dict) + type(dictionary_t), intent(in) :: dict + + integer :: i + + do i = 1, ubound(dict%list, 1) + write(*,'(7a)') str_vs(dict%list(i)%d%key), " [ {", str_vs(dict%list(i)%d%nsURI), & + "}", str_vs(dict%list(i)%d%localName), " ] = ", str_vs(dict%list(i)%d%value) + enddo + + end subroutine print_dict + +#endif +end module m_common_attrs diff --git a/src/xml/common/m_common_buffer.F90 b/src/xml/common/m_common_buffer.F90 new file mode 100644 index 000000000..0bf518362 --- /dev/null +++ b/src/xml/common/m_common_buffer.F90 @@ -0,0 +1,261 @@ +module m_common_buffer + +#ifndef DUMMYLIB + use fox_m_fsys_format, only: str + use m_common_charset, only: XML1_0 + use m_common_error, only: FoX_error, FoX_warning + + implicit none + private + + ! At this point we use a fixed-size buffer. + ! Note however that buffer overflows will only be + ! triggered by overly long *unbroken* pcdata values, or + ! by overly long attribute values. Hopefully + ! element or attribute names are "short enough". + ! + ! In a forthcoming implementation it could be made dynamical... + + ! MAX_BUFF_SIZE cannot be bigger than the maximum available + ! record length for a compiler. In practice, this means + ! 1024 seems to be the biggest available size. + + integer, parameter :: MAX_BUFF_SIZE = 1024 + + type buffer_t + private + integer :: size + character(len=MAX_BUFF_SIZE) :: str + integer :: unit + integer :: xml_version + end type buffer_t + + public :: buffer_t + + public :: add_to_buffer + public :: print_buffer, str, char, len + public :: buffer_to_chararray + public :: reset_buffer + public :: dump_buffer + + interface str + module procedure buffer_to_str + end interface + + interface char + module procedure buffer_to_str + end interface + + interface len + module procedure buffer_length + end interface + +contains + + subroutine reset_buffer(buffer, unit, xml_version) + type(buffer_t), intent(inout) :: buffer + integer, intent(in), optional :: unit + integer, intent(in) :: xml_version + + buffer%size = 0 + if (present(unit)) then + buffer%unit = unit + else + buffer%unit = 6 + endif + buffer%xml_version = xml_version + + end subroutine reset_buffer + + + subroutine print_buffer(buffer) + type(buffer_t), intent(in) :: buffer + + write(unit=6,fmt="(a)") buffer%str(:buffer%size) + + end subroutine print_buffer + + + function buffer_to_str(buffer) result(str) + type(buffer_t), intent(in) :: buffer + character(len=buffer%size) :: str + + str = buffer%str(:buffer%size) + end function buffer_to_str + + + function buffer_to_chararray(buffer) result(str) + type(buffer_t), intent(in) :: buffer + character(len=1), dimension(buffer%size) :: str + integer :: i + + do i = 1, buffer%size + str(i) = buffer%str(i:i) + enddo + end function buffer_to_chararray + + + function buffer_length(buffer) result(length) + type(buffer_t), intent(in) :: buffer + integer :: length + + length = buffer%size + + end function buffer_length + + + subroutine dump_buffer(buffer, lf) + type(buffer_t), intent(inout) :: buffer + logical, intent(in), optional :: lf + + integer :: i, n + logical :: lf_ + + if (present(lf)) then + lf_ = lf + else + lf_ = .true. + endif + + i = scan(buffer%str(:buffer%size), achar(10)//achar(13)) + n = 1 + do while (i>0) + write(buffer%unit, '(a)', advance="yes") buffer%str(n:n+i-2) + n = n + i + if (n>buffer%size) exit + i = scan(buffer%str(n:), achar(10)//achar(13)) + enddo + + if (n<=buffer%size) then + if (lf_) then + write(buffer%unit, '(a)', advance="yes") buffer%str(n:buffer%size) + else + write(buffer%unit, '(a)', advance="no") buffer%str(n:buffer%size) + endif + endif + + buffer%size = 0 + end subroutine dump_buffer + + + subroutine check_buffer(s, version) + character(len=*), intent(in) :: s + integer, intent(in) :: version + + integer :: i + +!FIXME this is almost a duplicate of logic in wxml/m_wxml_escape.f90 + + ! We have to do it this way (with achar etc) in case the native + ! platform encoding is not ASCII + + do i = 1, len(s) + select case (iachar(s(i:i))) + case (0) + call FoX_error("Tried to output a NUL character") + case (1:8,11:12,14:31) + if (version==XML1_0) then + call FoX_error("Tried to output a character invalid under XML 1.0: &#"//str(iachar(s(i:i)))//";") + endif + case (128:) + !TOHW we should maybe just disallow this ... + call FoX_warning("emitting non-ASCII character. Platform-dependent result!") + end select + enddo + + end subroutine check_buffer + + + subroutine add_to_buffer(s, buffer, ws_significant) + character(len=*), intent(in) :: s + type(buffer_t), intent(inout) :: buffer + logical, intent(in), optional :: ws_significant + + character(len=(buffer%size+len(s))) :: s2 + integer :: i, n, len_b + logical :: warning, ws_ + + ! Is whitespace significant in this context? + ! We have to assume so unless told otherwise. + if (present(ws_significant)) then + ws_ = ws_significant + else + ws_ = .true. + endif + + ! FIXME The algorithm below unilaterally forces all + ! line feeds and carriage returns to native EOL, regardless + ! of input document. Thus it is impossible to output a + ! document containing a literal non-native newline character + ! Ideally we would put this under the control of the user. + + ! We check if whitespace is significant. If not, we can + ! adjust the buffer without worrying about it. + ! But if we are not told, we warn about it. + ! And if we are told it definitely is - then we error out. + + ! If we overreach our buffer size, we will be unable to + ! output any more characters without a newline. + ! Go through new string, insert newlines + ! at spaces just before MAX_BUFF_SIZE chars + ! until we have less than MAX_BUFF_SIZE left to go, + ! then put that in the buffer. + + ! If no whitespace is found in the newly-added string, then + ! insert a new line immediately before it (at the end of the + ! current buffer) + + call check_buffer(s, buffer%xml_version) + + s2 = buffer%str(:buffer%size)//s + + + ! output as much of this using existing newlines as possible. + warning = .false. + n = 1 + do while (n<=len(s2)) + ! Note this is an XML-1.0 only definition of newline + i = scan(s2(n:), achar(10)//achar(13)) + if (i>0) then + ! turn that newline into an output newline ... + write(buffer%unit, '(a)') s2(n:n+i-2) + n = n + i + elseif (n<=len(s2)-MAX_BUFF_SIZE) then + ! We need to insert a newline, or we'll overrun the buffer + ! No suitable newline, so convert a space or tab into a newline. + i = scan(s2(n:n+MAX_BUFF_SIZE-1), achar(9)//achar(32), back=.true.) + ! If no space or tab is present, we fail. + if (i>0.and..not.present(ws_significant)) then + ! We can insert a newline, but we don't know whether it is significant. Warn: + if (.not.warning) then + ! We only output this warning once. + call FoX_warning( & + "Fortran made FoX insert a newline. "// & + "If whitespace might be significant, check your output.") + warning = .true. + endif + elseif (i==0) then + call FoX_error( & + "Fortran made FoX insert a newline but it can't. Stopping now.") + elseif (ws_) then + call FoX_error( & + "Fortran made FoX insert a newline but whitespace is significant. Stopping now.") + else + continue ! without error or warning, because whitespace is not significant + endif + write(buffer%unit, '(a)') s2(n:n+i-1) + n = n + i + else + ! We don't need to do anything, just add the remainder to the buffer. + exit + endif + enddo + + len_b = len(s2) - n + 1 + buffer%str(:len_b) = s2(n:) + buffer%size = len_b + + end subroutine add_to_buffer + +#endif +end module m_common_buffer diff --git a/src/xml/common/m_common_charset.F90 b/src/xml/common/m_common_charset.F90 new file mode 100644 index 000000000..d46a95ec2 --- /dev/null +++ b/src/xml/common/m_common_charset.F90 @@ -0,0 +1,447 @@ +module m_common_charset + +#ifndef DUMMYLIB + ! Written to use ASCII charset only. Full UNICODE would + ! take much more work and need a proper unicode library. + + use fox_m_fsys_string, only: toLower + + implicit none + private + +!!$ character(len=1), parameter :: ASCII = & +!!$achar(0)//achar(1)//achar(2)//achar(3)//achar(4)//achar(5)//achar(6)//achar(7)//achar(8)//achar(9)//& +!!$achar(10)//achar(11)//achar(12)//achar(13)//achar(14)//achar(15)//achar(16)//achar(17)//achar(18)//achar(19)//& +!!$achar(20)//achar(21)//achar(22)//achar(23)//achar(24)//achar(25)//achar(26)//achar(27)//achar(28)//achar(29)//& +!!$achar(30)//achar(31)//achar(32)//achar(33)//achar(34)//achar(35)//achar(36)//achar(37)//achar(38)//achar(39)//& +!!$achar(40)//achar(41)//achar(42)//achar(43)//achar(44)//achar(45)//achar(46)//achar(47)//achar(48)//achar(49)//& +!!$achar(50)//achar(51)//achar(52)//achar(53)//achar(54)//achar(55)//achar(56)//achar(57)//achar(58)//achar(59)//& +!!$achar(60)//achar(61)//achar(62)//achar(63)//achar(64)//achar(65)//achar(66)//achar(67)//achar(68)//achar(69)//& +!!$achar(70)//achar(71)//achar(72)//achar(73)//achar(74)//achar(75)//achar(76)//achar(77)//achar(78)//achar(79)//& +!!$achar(80)//achar(81)//achar(82)//achar(83)//achar(84)//achar(85)//achar(86)//achar(87)//achar(88)//achar(89)//& +!!$achar(90)//achar(91)//achar(92)//achar(93)//achar(94)//achar(95)//achar(96)//achar(97)//achar(98)//achar(99)//& +!!$achar(100)//achar(101)//achar(102)//achar(103)//achar(104)//achar(105)//achar(106)//achar(107)//achar(108)//achar(109)//& +!!$achar(110)//achar(111)//achar(112)//achar(113)//achar(114)//achar(115)//achar(116)//achar(117)//achar(118)//achar(119)//& +!!$achar(120)//achar(121)//achar(122)//achar(123)//achar(124)//achar(125)//achar(126)//achar(127) + + character(len=1), parameter :: SPACE = achar(32) + character(len=1), parameter :: NEWLINE = achar(10) + character(len=1), parameter :: CARRIAGE_RETURN = achar(13) + character(len=1), parameter :: TAB = achar(9) + + character(len=*), parameter :: whitespace = SPACE//NEWLINE//CARRIAGE_RETURN//TAB + + character(len=*), parameter :: lowerCase = "abcdefghijklmnopqrstuvwxyz" + character(len=*), parameter :: upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + character(len=*), parameter :: digits = "0123456789" + character(len=*), parameter :: hexdigits = "0123456789abcdefABCDEF" + character(len=*), parameter :: InitialNCNameChars = lowerCase//upperCase//"_" + character(len=*), parameter :: NCNameChars = InitialNCNameChars//digits//".-" + character(len=*), parameter :: InitialNameChars = InitialNCNameChars//":" + character(len=*), parameter :: NameChars = NCNameChars//":" + + character(len=*), parameter :: PubIdChars = NameChars//whitespace//"'()+,/=?;!*#@$%" + character(len=*), parameter :: validchars = & + whitespace//"!""#$%&'()*+,-./"//digits// & + ":;<=>?@"//upperCase//"[\]^_`"//lowerCase//"{|}~" + ! these are all the standard ASCII printable characters: whitespace + (33-126) + ! which are the only characters we can guarantee to know how to handle properly. + + integer, parameter :: XML1_0 = 10 ! NB 0x7F was legal in XML-1.0, but illegal in XML-1.1 + + integer, parameter :: XML1_1 = 11 + + character(len=*), parameter :: XML1_0_ILLEGALCHARS = achar(0) + character(len=*), parameter :: XML1_1_ILLEGALCHARS = NameChars + + character(len=*), parameter :: XML1_0_INITIALNAMECHARS = InitialNameChars + character(len=*), parameter :: XML1_1_INITIALNAMECHARS = InitialNameChars + character(len=*), parameter :: XML1_0_NAMECHARS = NameChars + character(len=*), parameter :: XML1_1_NAMECHARS = NameChars + + character(len=*), parameter :: XML1_0_INITIALNCNAMECHARS = InitialNCNameChars + character(len=*), parameter :: XML1_1_INITIALNCNAMECHARS = InitialNCNameChars + character(len=*), parameter :: XML1_0_NCNAMECHARS = NCNameChars + character(len=*), parameter :: XML1_1_NCNAMECHARS = NCNameChars + + + character(len=*), parameter :: XML_WHITESPACE = whitespace + character(len=*), parameter :: XML_INITIALENCODINGCHARS = lowerCase//upperCase + character(len=*), parameter :: XML_ENCODINGCHARS = lowerCase//upperCase//digits//'._-' + + public :: validchars + public :: whitespace + public :: uppercase + + public :: digits + public :: hexdigits + + public :: XML1_0 + public :: XML1_1 + public :: XML1_0_NAMECHARS + public :: XML1_1_NAMECHARS + public :: XML1_0_INITIALNAMECHARS + public :: XML1_1_INITIALNAMECHARS + public :: XML_WHITESPACE + public :: XML_INITIALENCODINGCHARS + public :: XML_ENCODINGCHARS + + public :: isLegalChar + public :: isLegalCharRef + public :: isRepCharRef + public :: isInitialNameChar + public :: isNameChar + public :: isInitialNCNameChar + public :: isNCNameChar + public :: isXML1_0_NameChar + public :: isXML1_1_NameChar + public :: checkChars + public :: isUSASCII + public :: allowed_encoding + +contains + + pure function isLegalChar(c, ascii_p, xml_version) result(p) + character, intent(in) :: c + ! really we should check the encoding here & be more intelligent + ! for now we worry only about is it ascii or not. + logical, intent(in) :: ascii_p + integer, intent(in) :: xml_version + logical :: p + ! Is this character legal as a source character in the document? + integer :: i + i = iachar(c) + if (i<0) then + p = .false. + return + elseif (i>127) then + p = .not.ascii_p + return + ! ie if we are ASCII, then >127 is definitely illegal. + ! otherwise maybe it's ok + endif + select case(xml_version) + case (XML1_0) + p = (i==9.or.i==10.or.i==13.or.(i>31.and.i<128)) + case (XML1_1) + p = (i==9.or.i==10.or.i==13.or.(i>31.and.i<127)) + ! NB 0x7F was legal in XML-1.0, but illegal in XML-1.1 + end select + end function isLegalChar + + pure function isLegalCharRef(i, xml_version) result(p) + integer, intent(in) :: i + integer, intent(in) :: xml_version + logical :: p + + ! Is Unicode character #i legal as a character reference? + + if (xml_version==XML1_0) then + p = (i==9).or.(i==10).or.(i==13).or.(i>31.and.i<55296).or.(i>57343.and.i<65534).or.(i>65535.and.i<1114112) + elseif (xml_version==XML1_1) then + p = (i>0.and.i<55296).or.(i>57343.and.i<65534).or.(i>65535.and.i<1114112) + ! XML 1.1 made all control characters legal as character references. + end if + end function isLegalCharRef + + pure function isRepCharRef(i, xml_version) result(p) + integer, intent(in) :: i + integer, intent(in) :: xml_version + logical :: p + + ! Is Unicode character #i legal and representable here? + + if (xml_version==XML1_0) then + p = (i==9).or.(i==10).or.(i==13).or.(i>31.and.i<128) + elseif (xml_version==XML1_1) then + p = (i>0.and.i<128) + ! XML 1.1 made all control characters legal as character references. + end if + end function isRepCharRef + + pure function isInitialNameChar(c, xml_version) result(p) + character, intent(in) :: c + integer, intent(in) :: xml_version + logical :: p + + select case(xml_version) + case (XML1_0) + p = (verify(c, XML1_0_INITIALNAMECHARS)==0) + case (XML1_1) + p = (verify(c, XML1_1_INITIALNAMECHARS)==0) + end select + + end function isInitialNameChar + + pure function isNameChar(c, xml_version) result(p) + character(len=*), intent(in) :: c + integer, intent(in) :: xml_version + logical :: p + + select case(xml_version) + case (XML1_0) + p = (verify(c, XML1_0_NAMECHARS)==0) + case (XML1_1) + p = (verify(c, XML1_1_NAMECHARS)==0) + end select + + end function isNameChar + + pure function isInitialNCNameChar(c, xml_version) result(p) + character, intent(in) :: c + integer, intent(in) :: xml_version + logical :: p + + select case(xml_version) + case (XML1_0) + p = (verify(c, XML1_0_INITIALNCNAMECHARS)==0) + case (XML1_1) + p = (verify(c, XML1_1_INITIALNCNAMECHARS)==0) + end select + end function isInitialNCNameChar + + pure function isNCNameChar(c, xml_version) result(p) + character(len=*), intent(in) :: c + integer, intent(in) :: xml_version + logical :: p + + select case(xml_version) + case (XML1_0) + p = (verify(c, XML1_0_NCNAMECHARS)==0) + case (XML1_1) + p = (verify(c, XML1_1_NCNAMECHARS)==0) + end select + end function isNCNameChar + + function isXML1_0_NameChar(c) result(p) + character, intent(in) :: c + logical :: p + + p = (verify(c, XML1_0_NAMECHARS)==0) + + end function isXML1_0_NameChar + + function isXML1_1_NameChar(c) result(p) + character, intent(in) :: c + logical :: p + + p = (verify(c, XML1_1_NAMECHARS)==0) + + end function isXML1_1_NameChar + + pure function checkChars(value, xv) result(p) + character(len=*), intent(in) :: value + integer, intent(in) :: xv + logical :: p + + ! This checks if value only contains values + ! legal to appear (escaped or unescaped) + ! according to whichever XML version is in force. + integer :: i + + p = .true. + do i = 1, len(value) + if (xv == XML1_0) then + select case(iachar(value(i:i))) + case (0,8) + p = .false. + case (11,12) + p = .false. + end select + else + if (iachar(value(i:i))==0) p =.false. + endif + enddo + end function checkChars + + function isUSASCII(encoding) result(p) + character(len=*), intent(in) :: encoding + logical :: p + + character(len=len(encoding)) :: enc + enc = toLower(encoding) + p = (enc=="ansi_x3.4-1968" & + .or. enc=="ansi_x3.4-1986" & + .or. enc=="iso_646.irv:1991" & + .or. enc=="ascii" & + .or. enc=="iso646-us" & + .or. enc=="us-ascii" & + .or. enc=="us" & + .or. enc=="ibm367" & + .or. enc=="cp367" & + .or. enc=="csascii") + + end function isUSASCII + + function allowed_encoding(encoding) result(p) + character(len=*), intent(in) :: encoding + logical :: p + + character(len=len(encoding)) :: enc + logical :: utf8, usascii, iso88591, iso88592, iso88593, iso88594, & + iso88595, iso88596, iso88597, iso88598, iso88599, iso885910, & + iso885913, iso885914, iso885915, iso885916 + + enc = toLower(encoding) + + ! From http://www.iana.org/assignments/character-sets + ! We can only reliably do US-ASCII (the below is mostly + ! a list of synonyms for US-ASCII) but we also accept + ! UTF-8 as a practicality. We bail out if any non-ASCII + ! characters are used later on. + utf8 = (enc=="utf-8") + + usascii = (enc=="ansi_x3.4-1968" & + .or. enc=="ansi_x3.4-1986" & + .or. enc=="iso_646.irv:1991" & + .or. enc=="ascii" & + .or. enc=="iso646-us" & + .or. enc=="us-ascii" & + .or. enc=="us" & + .or. enc=="ibm367" & + .or. enc=="cp367" & + .or. enc=="csascii") +! As of FoX 4.0, we accept ISO-8859-??, also as practicality +! since we know it is identical to ASCII as far as 0x7F + + iso88591 = (enc =="iso_8859-1:1987" & + .or. enc=="iso-ir-100" & + .or. enc=="iso_8859-1" & + .or. enc=="iso-8859-1" & + .or. enc=="latin1" & + .or. enc=="l1" & + .or. enc=="ibm819" & + .or. enc=="cp819" & + .or. enc=="csisolatin1") + + iso88592 = (enc=="iso_8859-2:1987" & + .or. enc=="iso-ir-101" & + .or. enc=="iso_8859-2" & + .or. enc=="iso-8859-2" & + .or. enc=="latin2" & + .or. enc=="l2" & + .or. enc=="csisolatin2") + + iso88593 = (enc=="iso_8859-3:1988" & + .or. enc=="iso-ir-109" & + .or. enc=="iso_8859-3" & + .or. enc=="iso-8859-3" & + .or. enc=="latin3" & + .or. enc=="l3" & + .or. enc=="csisolatin3") + + iso88594 = (enc=="iso_8859-4:1988" & + .or. enc=="iso-ir-110" & + .or. enc=="iso_8859-4" & + .or. enc=="iso-8859-4" & + .or. enc=="latin4" & + .or. enc=="l4" & + .or. enc=="csisolatin4") + + iso88595 = (enc=="iso_8859-5:1988" & + .or. enc=="iso-ir-144" & + .or. enc=="iso_8859-5" & + .or. enc=="iso-8859-5" & + .or. enc=="cyrillic" & + .or. enc=="csisolatincyrillic") + + iso88596 = (enc=="iso_8859-6:1987" & + .or. enc=="iso-ir-127" & + .or. enc=="iso_8859-6" & + .or. enc=="iso-8859-6" & + .or. enc=="ecma-114" & + .or. enc=="asmo-708" & + .or. enc=="arabic" & + .or. enc=="csisolatinarabic") + + iso88597 = (enc=="iso_8859-7:1987" & + .or. enc=="iso-ir-126" & + .or. enc=="iso_8859-7" & + .or. enc=="iso-8859-7" & + .or. enc=="elot_928" & + .or. enc=="ecma-118" & + .or. enc=="greek" & + .or. enc=="greek8" & + .or. enc=="csisolatingreek") + + iso88598 = (enc=="iso_8859-8:1988" & + .or. enc=="iso-ir-138" & + .or. enc=="iso_8859-8" & + .or. enc=="iso-8859-8" & + .or. enc=="hebrew" & + .or. enc=="csisolatinhebrew") + + iso88599 = (enc=="iso_8859-9:1989" & + .or. enc=="iso-ir-148" & + .or. enc=="iso_8859-9" & + .or. enc=="iso-8859-9" & + .or. enc=="latin5" & + .or. enc=="l5" & + .or. enc=="csisolatin5") + + iso885910 = (enc=="iso-8859-10" & + .or. enc=="iso-ir-157" & + .or. enc=="l6" & + .or. enc=="iso_8859-10:1992" & + .or. enc=="csisolatin6" & + .or. enc=="latin6") + +! ISO 6937 replaces $ sign with currency sign. +! JIS-X0201 has Yen instead of backslash, macron instead of tilde +! 16, 17, 18, 19 - Japanese encoding we can't use. +! BS 4730 replaces hash with UK pound sign, and tilde to macron +! 21, 22, 23, 24, 25, 26 - other variants of iso646, similar but not identical + +! iso10646utf1 = (enc=="iso-10646-utf-1") ! FIXME check +! iso656basic1983 = (enc=="iso_646.basic:1983" & +! .or. enc=="csiso646basic1983") ! FIXME check +! INVARIANT - almost but not quite a subset of ASCII +! iso646irv = (enc=="iso_646.irv:1983" & +! .or. enc=="iso-ir-2" & +! .or. enc=="irv") +! 31, 32, 33, 34 - NATS scandinavian, different from ASCII +! 35 - another iso646 variant +! 36, 37, 38 Korean shifted/multibyte +! 39, 40 Japanese shifted/multibyte +! 41, 42, JIS (iso646inv 7 bits) +! 43 another iso646 variantt +! 44, 45 greek variants +! 46 another iso646 variant +! 47 greek +! 48 cyrillic ascii relationship unknown +! 49 JIS again +! 50 similar not identical +! 51, 52, 53 not identical +! 54 see 48 +! 55 see 47 +! 56 another iso646 variant +! 57 chinese +! ... to be continued + + iso885913 = (enc=="iso-8859-13") + iso885914 = (enc=="iso-8859-14" & + .or. enc=="iso-ir-199" & + .or. enc=="iso_8859-14:1998" & + .or. enc=="iso_8849-14" & + .or. enc=="iso_latin8" & + .or. enc=="iso-celtic" & + .or. enc=="l8") + iso885915 = (enc=="iso-8859-15" & + .or. enc=="iso-8859-15" & + .or. enc=="latin-9") + iso885916 = (enc=="iso-8859-16" & + .or. enc=="iso-ir226" & + .or. enc=="iso_8859-16:2001" & + .or. enc=="iso_8859-16" & + .or. enc=="latin10" & + .or. enc=="l10") + + p = utf8.or.usascii.or.iso88591.or.iso88592.or.iso88593 & + .or.iso88594.or.iso88595.or.iso88596.or.iso88597 & + .or.iso88598.or.iso88599.or.iso885910.or.iso885913 & + .or.iso885914.or.iso885915.or.iso885916 + + end function allowed_encoding + +#endif +end module m_common_charset diff --git a/src/xml/common/m_common_content_model.F90 b/src/xml/common/m_common_content_model.F90 new file mode 100644 index 000000000..1b5df709e --- /dev/null +++ b/src/xml/common/m_common_content_model.F90 @@ -0,0 +1,490 @@ +module m_common_content_model + +#ifndef DUMMYLIB + ! Allow validating the content model of an XML document + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc, vs_vs_alloc + implicit none + private + + integer, parameter :: OP_NULL = 0 + integer, parameter :: OP_EMPTY = 1 + integer, parameter :: OP_ANY = 2 + integer, parameter :: OP_MIXED = 3 + integer, parameter :: OP_NAME = 4 + integer, parameter :: OP_CHOICE = 5 + integer, parameter :: OP_SEQ = 6 + + integer, parameter :: REP_NULL = 0 + integer, parameter :: REP_ONCE = 1 + integer, parameter :: REP_QUESTION_MARK = 2 + integer, parameter :: REP_ASTERISK = 3 + + type content_particle_t + character, pointer :: name(:) => null() + integer :: operator = OP_NULL + integer :: repeater = REP_NULL + type(content_particle_t), pointer :: nextSibling => null() + type(content_particle_t), pointer :: parent => null() + type(content_particle_t), pointer :: firstChild => null() + end type content_particle_t + + public :: content_particle_t + + public :: newCP + public :: transformCPPlus + public :: checkCP + public :: checkCPToEnd + public :: elementContentCP + public :: emptyContentCP + public :: destroyCPtree + public :: dumpCPtree + + public :: OP_NULL, OP_NAME, OP_MIXED, OP_CHOICE, OP_SEQ + public :: REP_QUESTION_MARK, REP_ASTERISK + +contains + + function newCP(empty, any, name, repeat) result(cp) + logical, intent(in), optional :: empty + logical, intent(in), optional :: any + character(len=*), intent(in), optional :: name + character, intent(in), optional :: repeat + type(content_particle_t), pointer :: cp + + allocate(cp) + if (present(empty)) then + cp%operator = OP_EMPTY + elseif (present(any)) then + cp%operator = OP_ANY + elseif (present(name)) then + cp%operator = OP_NAME + cp%name => vs_str_alloc(name) + else + cp%operator = OP_SEQ + endif + if (present(repeat)) then + select case (repeat) + case("?") + cp%repeater = REP_QUESTION_MARK + case("*") + cp%repeater = REP_ASTERISK + end select + endif + + end function newCP + + function copyCP(cp) result(cp_out) + type(content_particle_t), pointer :: cp + type(content_particle_t), pointer :: cp_out + + allocate(cp_out) + if (associated(cp%name)) cp_out%name => vs_vs_alloc(cp%name) + cp_out%operator = cp%operator + cp_out%repeater = cp%repeater + end function copyCP + + function copyCPtree(cp) result(cp_out) + type(content_particle_t), pointer :: cp + type(content_particle_t), pointer :: cp_out + + type(content_particle_t), pointer :: tcp, tcp_out, tcpn_out, tcpp_out + logical :: done + + tcp => cp + cp_out => copyCP(cp) + tcp_out => cp_out + done = .false. + do while (associated(tcp_out)) + if (.not.done) then + do while (associated(tcp%firstChild)) + tcp => tcp%firstChild + tcpn_out => copyCP(tcp) + tcp_out%firstChild => tcpn_out + tcpn_out%parent => tcp_out + tcp_out => tcpn_out + enddo + endif + tcpp_out => tcp_out%parent + if (associated(tcp%nextSibling)) then + done = .false. + tcp => tcp%nextSibling + tcpn_out => copyCP(tcp) + tcp_out%nextSibling => tcpn_out + tcpn_out%parent => tcpp_out + tcp_out => tcpn_out + else + done = .true. + tcp => tcp%parent + tcp_out => tcp_out%parent + endif + enddo + end function copyCPtree + + subroutine transformCPPlus(cp) + type(content_particle_t), pointer :: cp + + type(content_particle_t), pointer :: tcp, cp_new + + ! Make copy of cp, and graft children on + cp_new => copyCP(cp) + cp_new%firstChild => cp%firstChild + + ! Reset children's parents ... + tcp => cp%firstChild + do while (associated(tcp)) + tcp%parent => cp_new + tcp => tcp%nextSibling + enddo + + ! Clear cp & make it an SEQ + if (associated(cp%name)) deallocate(cp%name) + cp%operator = OP_SEQ + + ! Append our copied cp to the now-an-SEQ + cp%firstChild => cp_new + cp_new%parent => cp + + ! Copy it for a sibling, and make the sibling a * + cp_new%nextSibling => copyCPtree(cp_new) + cp_new%nextSibling%parent => cp + cp_new%nextSibling%repeater = REP_ASTERISK + + end subroutine transformCPPlus + + function checkCP(cp, name) result(p) + type(content_particle_t), pointer :: cp + character(len=*), intent(in) :: name + logical :: p + + type(content_particle_t), pointer :: tcp + + ! for EMPTY, ANY or MIXED, cp never moves. + ! for element content, we move the pointer as we + ! move through the regex. + + ! If the regex includes ambiguous content, we are + ! a bit screwed. But the document is in error if so. + ! (and we are not required to diagnose errors.) + + p = .false. + if (.not.associated(cp)) return + + select case(cp%operator) + case (OP_EMPTY) + continue ! anything fails + case (OP_ANY) + p = .true. + case (OP_MIXED) + tcp => cp%firstChild + do while (associated(tcp)) + if (name==str_vs(tcp%name)) then + p = .true. + exit + endif + tcp => tcp%nextSibling + enddo + case default + do + if (.not.associated(cp)) exit + select case (cp%operator) + case (OP_NAME) + p = (name==str_vs(cp%name)) + if (p) then + tcp => nextCPAfterMatch(cp) + cp => tcp + exit + else + tcp => nextCPAfterFail(cp) + cp => tcp + endif + case (OP_CHOICE, OP_SEQ) + cp => cp%firstChild + end select + end do + end select + + end function checkCP + + function nextCPaftermatch(cp) result(cp_next) + type (content_particle_t), pointer :: cp + type (content_particle_t), pointer :: cp_next + + type (content_particle_t), pointer :: tcp + + cp_next => cp + + do + if (cp_next%repeater==REP_ASTERISK) exit + tcp => cp_next%parent + if (associated(tcp)) then + if (tcp%operator==OP_CHOICE) then + ! siblings are uninteresting, we've matched this CHOICE + cp_next => tcp + ! Move up & try the whole thing again on the parent CHOICE + elseif (tcp%operator==OP_SEQ) then + ! we do care about siblings, move onto next one + if (associated(cp_next%nextSibling)) then + cp_next => cp_next%nextSibling + ! thatll do, itll be the next thing to try + exit + else + ! No sibling, move up a level + cp_next => tcp + ! and try again + endif + endif + else + ! We've got to the top already. + cp_next => tcp + exit + endif + enddo + + end function nextCPaftermatch + + function nextCPafterfail(cp) result(cp_next) + type(content_particle_t), pointer :: cp + type(content_particle_t), pointer :: cp_next + + type(content_particle_t), pointer :: tcp + logical :: match + + match = .false. + cp_next => cp + do + tcp => cp_next%parent + if (associated(tcp)) then + if (tcp%operator==OP_CHOICE) then + ! we care about siblings, lets try the next one + if (associated(cp_next%nextSibling)) then + cp_next => cp_next%nextSibling + ! super, lets go back and try that + exit + else ! weve failed to match any cp in this CHOICE ... + cp_next => tcp + ! go up a level and see if theres another legitimate choice + endif + elseif (tcp%operator==OP_SEQ) then + if ((match.or.cp_next%repeater/=REP_NULL) & + .and.associated(cp_next%nextSibling)) then + ! we were allowed to fail to match, try sibling + cp_next => cp_next%nextSibling + exit + elseif (cp_next%repeater/=REP_NULL) then + match = .true. + ! The last item was optional, so weve matched at this level + cp_next => tcp + elseif (associated(tcp%firstChild, cp_next)) then + ! we havent matched - but we hadnt started, Maybe it was ok + ! not to match because we are nested inside an optional thingy + cp_next => tcp + else + ! We were not allowed to fail there, + ! there is no legitimate next choice. + cp_next => null() + exit + endif + endif + else + ! weve got all the way to the top without + ! finding a new cp to try. But if this top-level + ! cp is ASTERISK'ed we can try it agin + cp_next => null() + exit + endif + enddo + + end function nextCPafterfail + + function checkCPToEnd(cp) result(p) + type(content_particle_t), pointer :: cp + logical :: p + + type(content_particle_t), pointer :: tcp + + if (associated(cp)) then + select case(cp%operator) + case (OP_EMPTY, OP_ANY, OP_MIXED) + p = .true. + case default + tcp => nextCPMustMatch(cp) + p = .not.associated(tcp) + end select + else + p = .true. + endif + end function checkCPToEnd + + function nextCPMustMatch(cp) result(cp_next) + type(content_particle_t), pointer :: cp + type(content_particle_t), pointer :: cp_next + + type(content_particle_t), pointer :: tcp + + if (.not.associated(cp)) return + if (.not.associated(cp%parent)) then + ! we havent started exploring this one. + ! get the first starting position + cp_next => cp + do while (cp_next%repeater==REP_NULL) + if (associated(cp_next%firstChild)) then + cp_next => cp_next%firstChild + else + exit + endif + enddo + else + cp_next => cp + endif + if (cp_next%repeater==REP_NULL) return + do + tcp => cp_next%parent + if (associated(tcp)) then + if (tcp%operator==OP_CHOICE) then + ! its matched by the optional one we are on, go up a level + cp_next => tcp + elseif (tcp%operator==OP_SEQ) then + ! check all siblings for any compulsory ones + do while (associated(cp_next%nextSibling)) + cp_next => cp_next%nextSibling + if (cp_next%repeater==REP_NULL) return + enddo + ! all were optional, go up a level + cp_next => tcp + endif + else + ! weve got all the way to the top without + ! finding a new cp to try + cp_next => tcp + exit + endif + enddo + + end function nextCPMustMatch + + function elementContentCP(cp) result(p) + type(content_particle_t), pointer :: cp + logical :: p + + if (associated(cp)) then + select case (cp%operator) + case (OP_EMPTY, OP_ANY, OP_MIXED) + p = .false. + case default + p = .true. + end select + else + p = .true. + endif + + end function elementContentCP + + function emptyContentCP(cp) result(p) + type(content_particle_t), pointer :: cp + logical :: p + + if (associated(cp)) then + p = cp%operator==OP_EMPTY + else + p = .false. + endif + + end function emptyContentCP + + subroutine destroyCP(cp) + type(content_particle_t), pointer :: cp + + if (associated(cp%name)) deallocate(cp%name) + deallocate(cp) + end subroutine destroyCP + + subroutine destroyCPtree(cp) + type(content_particle_t), pointer :: cp + + type(content_particle_t), pointer :: current, tcp + + current => cp + do + do while (associated(current%firstChild)) + current => current%firstChild + enddo + if (associated(current, cp)) exit + tcp => current + if (associated(current%nextSibling)) then + current => current%nextSibling + call destroyCP(tcp) + else + current => current%parent + call destroyCP(tcp) + current%firstChild => null() + endif + enddo + call destroyCP(cp) + + end subroutine destroyCPtree + + subroutine dumpCP(cp) + type(content_particle_t), pointer :: cp + + select case(cp%operator) + case (OP_EMPTY) + write(*,'(a)', advance="no") "EMPTY" + case (OP_ANY) + write(*,'(a)', advance="no") "ANY" + case (OP_MIXED) + write(*,'(a)', advance="no") "MIXED" + case (OP_NAME) + write(*,'(a)', advance="no") str_vs(cp%name) + case (OP_CHOICE) + write(*,'(a)', advance="no") "CHOICE" + case (OP_SEQ) + write(*,'(a)', advance="no") "SEQ" + end select + select case(cp%repeater) + case (REP_QUESTION_MARK) + write(*,'(a)', advance="no") "?" + case (REP_ASTERISK) + write(*,'(a)', advance="no") "*" + end select + write(*,*) + end subroutine dumpCP + + subroutine dumpCPtree(cp) + type(content_particle_t), pointer :: cp + + type(content_particle_t), pointer :: current + + integer :: i + logical :: done + i = 0 + current => cp + done = .false. + call dumpCP(current) + do + if (.not.done) then + do while (associated(current%firstChild)) + i = i + 2 + current => current%firstChild + write(*,'(a)', advance="no") repeat(" ",i) + call dumpCP(current) + enddo + endif + if (associated(current, cp)) exit + if (associated(current%nextSibling)) then + done = .false. + current => current%nextSibling + write(*,'(a)', advance="no") repeat(" ",i) + call dumpCP(current) + else + done = .true. + i = i - 2 + current => current%parent + endif + enddo + + end subroutine dumpCPtree + +#endif + +end module m_common_content_model diff --git a/src/xml/common/m_common_element.F90 b/src/xml/common/m_common_element.F90 new file mode 100644 index 000000000..5eecbbfdc --- /dev/null +++ b/src/xml/common/m_common_element.F90 @@ -0,0 +1,1645 @@ +module m_common_element + +#ifndef DUMMYLIB + ! Structure and manipulation of element specification + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc, vs_vs_alloc + use fox_m_fsys_string_list, only: string_list, init_string_list, & + destroy_string_list, add_string, tokenize_to_string_list, & + registered_string + use m_common_charset, only: isInitialNameChar, isNameChar, & + upperCase, XML_WHITESPACE + use m_common_content_model, only: content_particle_t, newCP, destroyCPtree, & + OP_MIXED, OP_CHOICE, OP_SEQ, OP_NAME, & + REP_QUESTION_MARK, REP_ASTERISK, & + transformCPPlus ! , dumpCPtree ! For debugging - see below. + use m_common_error, only: error_stack, add_error, in_error + use m_common_namecheck, only: checkName, checkNames, checkNCName, & + checkNCNames, checkQName, checkNmtoken, checkNmtokens + + implicit none + private + + integer, parameter :: ST_START = 0 + integer, parameter :: ST_EMPTYANY = 1 + integer, parameter :: ST_FIRSTCHILD = 2 + integer, parameter :: ST_END = 3 + integer, parameter :: ST_PCDATA = 4 + integer, parameter :: ST_NAME = 5 + integer, parameter :: ST_CHILD = 6 + integer, parameter :: ST_AFTERBRACKET = 7 + integer, parameter :: ST_AFTERLASTBRACKET = 8 + integer, parameter :: ST_SEPARATOR = 9 + integer, parameter :: ST_AFTERNAME = 10 + integer, parameter :: ST_ATTTYPE = 11 + integer, parameter :: ST_AFTER_NOTATION = 12 + integer, parameter :: ST_NOTATION_LIST = 13 + integer, parameter :: ST_ENUMERATION = 14 + integer, parameter :: ST_ENUM_NAME = 15 + integer, parameter :: ST_AFTER_ATTTYPE_SPACE = 16 + integer, parameter :: ST_AFTER_ATTTYPE = 17 + integer, parameter :: ST_DEFAULT_DECL = 18 + integer, parameter :: ST_AFTERDEFAULTDECL = 19 + integer, parameter :: ST_DEFAULTVALUE = 20 + + integer, parameter :: ATT_NULL = 0 + + integer, parameter :: ATT_CDATA = 1 + integer, parameter :: ATT_ID = 2 + integer, parameter :: ATT_IDREF = 3 + integer, parameter :: ATT_IDREFS = 4 + integer, parameter :: ATT_ENTITY = 5 + integer, parameter :: ATT_ENTITIES = 6 + integer, parameter :: ATT_NMTOKEN = 7 + integer, parameter :: ATT_NMTOKENS = 8 + integer, parameter :: ATT_NOTATION = 9 + integer, parameter :: ATT_ENUM = 10 + integer, parameter :: ATT_CDANO = 11 + integer, parameter :: ATT_CDAMB = 12 + + character(len=8), parameter :: ATT_TYPES(12) = (/ & + "CDATA ", & + "ID ", & + "IDREF ", & + "IDREFS ", & + "ENTITY ", & + "ENTITIES", & + "NMTOKEN ", & + "NMTOKENS", & + "NOTATION", & + "ENUM ", & + "CDANO ", & + "CDAMB "/) + + integer, parameter :: ATT_REQUIRED = 1 + integer, parameter :: ATT_IMPLIED = 2 + integer, parameter :: ATT_DEFAULT = 4 + integer, parameter :: ATT_FIXED = 3 + + + type attribute_t + character, pointer :: name(:) => null() + integer :: attType = ATT_NULL + integer :: attDefault = ATT_NULL + type(string_list) :: enumerations + character, pointer :: default(:) => null() + logical :: internal = .true. + end type attribute_t + + type attribute_list + type(attribute_t), pointer :: list(:) => null() + end type attribute_list + + type element_t + character, pointer :: name(:) => null() + logical :: empty = .false. + logical :: any = .false. + logical :: mixed = .false. + logical :: id_declared = .false. + logical :: internal = .true. + type (content_particle_t), pointer :: cp => null() + character, pointer :: model(:) => null() + type(attribute_list) :: attlist + end type element_t + + type element_list + type(element_t), pointer :: list(:) => null() + end type element_list + + + public :: element_t + public :: element_list + + public :: attribute_t + public :: attribute_list + + public :: init_element_list + public :: destroy_element_list + public :: existing_element + public :: declared_element + public :: get_element + public :: add_element + + public :: parse_dtd_element + + public :: init_attribute_list + public :: destroy_attribute_list + + + public :: parse_dtd_attlist + + public :: report_declarations + + public :: attribute_has_default + public :: get_attlist_size + public :: get_attribute_declaration + public :: express_attribute_declaration + + public :: att_value_normalize + + public :: get_att_type_enum + + public :: ATT_NULL + public :: ATT_CDATA + public :: ATT_ID + public :: ATT_IDREF + public :: ATT_IDREFS + public :: ATT_ENTITY + public :: ATT_ENTITIES + public :: ATT_NMTOKEN + public :: ATT_NMTOKENS + public :: ATT_NOTATION + public :: ATT_ENUM + + public :: ATT_CDANO + public :: ATT_CDAMB + + public :: ATT_REQUIRED + public :: ATT_IMPLIED + public :: ATT_DEFAULT + public :: ATT_FIXED + + public :: ATT_TYPES + + interface get_attribute_declaration + module procedure get_attdecl_by_index + module procedure get_attdecl_by_name + end interface + +contains + + subroutine init_element_list(e_list) + type(element_list), intent(inout) :: e_list + + allocate(e_list%list(0)) + end subroutine init_element_list + + subroutine destroy_element_list(e_list) + type(element_list), intent(inout) :: e_list + + integer :: i + + do i = 1, size(e_list%list) + deallocate(e_list%list(i)%name) + if (associated(e_list%list(i)%cp)) call destroyCPtree(e_list%list(i)%cp) + if (associated(e_list%list(i)%model)) deallocate(e_list%list(i)%model) + call destroy_attribute_list(e_list%list(i)%attlist) + enddo + deallocate(e_list%list) + end subroutine destroy_element_list + + function existing_element(e_list, name) result(p) + type(element_list), intent(in) :: e_list + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + do i = 1, size(e_list%list) + if (str_vs(e_list%list(i)%name)==name) then + p = .true. + exit + endif + enddo + end function existing_element + + function declared_element(e_list, name) result(p) + type(element_list), intent(in) :: e_list + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + do i = 1, size(e_list%list) + if (str_vs(e_list%list(i)%name)==name) then + p = associated(e_list%list(i)%model) + exit + endif + enddo + end function declared_element + + function get_element(e_list, name) result(e) + type(element_list), intent(in) :: e_list + character(len=*), intent(in) :: name + type(element_t), pointer :: e + + integer :: i + + do i = 1, size(e_list%list) + if (str_vs(e_list%list(i)%name)==name) then + e => e_list%list(i) + return + endif + enddo + e => null() + end function get_element + + function add_element(e_list, name) result(e) + type(element_list), intent(inout) :: e_list + character(len=*), intent(in) :: name + type(element_t), pointer :: e + + type(element_t), pointer :: temp(:) + integer :: i + + temp => e_list%list + + allocate(e_list%list(size(temp)+1)) + do i = 1, size(temp) + e_list%list(i)%name => temp(i)%name + e_list%list(i)%model => temp(i)%model + e_list%list(i)%empty = temp(i)%empty + e_list%list(i)%any = temp(i)%any + e_list%list(i)%mixed = temp(i)%mixed + e_list%list(i)%cp => temp(i)%cp + e_list%list(i)%id_declared = temp(i)%id_declared + e_list%list(i)%internal = temp(i)%internal + e_list%list(i)%attlist%list => temp(i)%attlist%list + enddo + deallocate(temp) + e => e_list%list(i) + e%name => vs_str_alloc(name) + call init_attribute_list(e%attlist) + + end function add_element + + subroutine parse_dtd_element(contents, xv, stack, element, internal) + character(len=*), intent(in) :: contents + integer, intent(in) :: xv + type(error_stack), intent(inout) :: stack + type(element_t), pointer :: element + logical, intent(in) :: internal + + integer :: state + integer :: i, nbrackets + logical :: mixed, empty, any + character :: c + character, pointer :: order(:), name(:), temp(:) + type(content_particle_t), pointer :: top, current, tcp + logical :: mixed_additional, firstChild + + ! FIXME should we check namespaces here (for element names) + ! checking duplicates - valid or wf? - and only for MIXED? + + order => null() + name => null() + temp => null() + + any = .false. + empty = .false. + mixed = .false. + nbrackets = 0 + mixed_additional = .false. + firstChild = .true. + state = ST_START + + top => null() + + do i = 1, len(contents) + 1 + if (i<=len(contents)) then + c = contents(i:i) + else + c = ' ' + endif + + if (state==ST_START) then + !write(*,*)'ST_START' + if (verify(c, XML_WHITESPACE)==0) then + continue + elseif (verify(c, 'EMPTYANY')==0) then + name => vs_str_alloc(c) + state = ST_EMPTYANY + elseif (c=='(') then + order => vs_str_alloc(" ") + nbrackets = 1 + top => newCP() + current => top + state = ST_FIRSTCHILD + else + call add_error(stack, & + 'Unexpected character "'//c//'" at start of ELEMENT specification') + goto 100 + endif + + elseif (state==ST_EMPTYANY) then + !write(*,*)'ST_EMPTYANY' + if (verify(c, upperCase)==0) then + temp => name + name => vs_str_alloc(str_vs(temp)//c) + deallocate(temp) + elseif (verify(c, XML_WHITESPACE)==0) then + if (str_vs(name)=='EMPTY') then + empty = .true. + top => newCP(empty=.true.) + current => top + elseif (str_vs(name)=='ANY') then + any = .true. + top => newCP(any=.true.) + current => top + else + call add_error(stack, & + 'Unexpected ELEMENT specification; expecting EMPTY or ANY') + goto 100 + endif + deallocate(name) + state = ST_END + else + call add_error(stack, & + 'Unexpected ELEMENT specification; expecting EMPTY or ANY') + goto 100 + endif + + elseif (state==ST_FIRSTCHILD) then + !write(*,*)'ST_FIRSTCHILD' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='#') then + mixed = .true. + state = ST_PCDATA + name => vs_str_alloc("") + elseif (isInitialNameChar(c, xv)) then + allocate(name(1)) + name(1) = c + state = ST_NAME + elseif (c=='(') then + nbrackets = nbrackets + 1 + deallocate(order) + tcp => newCP() + current%firstChild => tcp + tcp%parent => current + current => tcp + order => vs_str_alloc(" ") + state = ST_CHILD + else + call add_error(stack, & + 'Unexpected character in ELEMENT specification') + goto 100 + endif + + elseif (state==ST_PCDATA) then + !write(*,*)'ST_PCDATA' + if (verify(c, 'PCDATA')==0) then + temp => name + name => vs_str_alloc(str_vs(temp)//c) + deallocate(temp) + elseif (verify(c, XML_WHITESPACE)==0) then + if (str_vs(name)=='PCDATA') then + deallocate(name) + else + call add_error(stack, & + 'Unexpected token after #') + goto 100 + endif + ! Must be first child + current%operator = OP_MIXED + tcp => newCP(name="#PCDATA") + current%firstChild => tcp + tcp%parent => current + current => tcp + firstChild = .false. + state = ST_SEPARATOR + elseif (c==')') then + if (str_vs(name)=='PCDATA') then + deallocate(name) + nbrackets = 0 + state = ST_AFTERLASTBRACKET + deallocate(order) + else + call add_error(stack, & + 'Unexpected token after #') + goto 100 + endif + ! Must be first child + current%operator = OP_MIXED + tcp => newCP(name="#PCDATA") + current%firstChild => tcp + tcp%parent => current + firstChild = .false. + elseif (c=='|') then + if (str_vs(name)=='PCDATA') then + firstChild = .false. + deallocate(name) + else + call add_error(stack, & + 'Unexpected token after #') + goto 100 + endif + ! Must be first child + current%operator = OP_MIXED + tcp => newCP(name="#PCDATA") + current%firstChild => tcp + tcp%parent => current + current => tcp + firstChild = .false. + order(1) = '|' + state = ST_CHILD + elseif (c==',') then + call add_error(stack, & + 'Ordered specification not allowed for Mixed elements') + goto 100 + else + call add_error(stack, & + 'Unexpected character in ELEMENT specification') + goto 100 + endif + + elseif (state==ST_NAME) then + !write(*,*)'ST_NAME' + if (isNameChar(c, xv)) then + temp => name + name => vs_str_alloc(str_vs(temp)//c) + deallocate(temp) + elseif (scan(c, "?+*")>0) then + if (mixed) then + call add_error(stack, & + 'Repeat operators forbidden for Mixed elements') + goto 100 + endif + tcp => newCP(name=str_vs(name), repeat=c) + deallocate(name) + if (firstChild) then + current%firstChild => tcp + tcp%parent => current + firstChild = .false. + else + current%nextSibling => tcp + tcp%parent => current%parent + endif + current => tcp + if (c=="+") call transformCPPlus(current) + state = ST_SEPARATOR + elseif (verify(c, XML_WHITESPACE)==0) then + if (mixed) mixed_additional = .true. + tcp => newCP(name=str_vs(name)) + deallocate(name) + if (firstChild) then + current%firstChild => tcp + tcp%parent => current + firstChild = .false. + else + current%nextSibling => tcp + tcp%parent => current%parent + endif + current => tcp + state = ST_SEPARATOR + elseif (scan(c,',|')>0) then + if (order(nbrackets)=='') then + order(nbrackets)=c + elseif (order(nbrackets)/=c) then + call add_error(stack, & + 'Cannot mix ordered and unordered elements') + goto 100 + endif + if (mixed) mixed_additional = .true. + tcp => newCP(name=str_vs(name)) + deallocate(name) + if (firstChild) then + current%firstChild => tcp + tcp%parent => current + firstChild = .false. + else + current%nextSibling => tcp + tcp%parent => current%parent + endif + current => tcp + if (c=="|".and.current%parent%operator/=OP_MIXED) & + current%parent%operator = OP_CHOICE + state = ST_CHILD + elseif (c==')') then + if (mixed) mixed_additional = .true. + nbrackets = nbrackets - 1 + if (nbrackets==0) then + state = ST_AFTERLASTBRACKET + deallocate(order) + else + temp => order + allocate(order(nbrackets)) + order = temp(:size(order)) + deallocate(temp) + state = ST_AFTERBRACKET + endif + tcp => newCP(name=str_vs(name)) + deallocate(name) + if (firstChild) then + current%firstChild => tcp + tcp%parent => current + firstChild = .false. + else + current%nextSibling => tcp + tcp%parent => current%parent + current => current%parent + if (.not.check_duplicates(current)) & + goto 100 + endif + else + call add_error(stack, & + 'Unexpected character found after element name') + goto 100 + endif + + elseif (state==ST_CHILD) then + !write(*,*)'ST_CHILD' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='#') then + call add_error(stack, & + '# forbidden except as first child element') + goto 100 + elseif (isInitialNameChar(c, xv)) then + name => vs_str_alloc(c) + state = ST_NAME + elseif (c=='(') then + if (mixed) then + call add_error(stack, & + 'Nested brackets forbidden for Mixed content') + goto 100 + endif + tcp => newCP() + if (firstChild) then + current%firstChild => tcp + tcp%parent => current + else + current%nextSibling => tcp + tcp%parent => current%parent + firstChild = .true. + endif + current => tcp + nbrackets = nbrackets + 1 + temp => order + order => vs_str_alloc(str_vs(temp)//" ") + deallocate(temp) + else + call add_error(stack, & + 'Unexpected character "'//c//'" found after (') + goto 100 + endif + + elseif (state==ST_SEPARATOR) then + !write(*,*)'ST_SEPARATOR' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='#') then + call add_error(stack, & + '#PCDATA must be first in list') + goto 100 + elseif (scan(c,'|,')>0) then + if (order(nbrackets)=='') then + order(nbrackets) = c + elseif (order(nbrackets)/=c) then + call add_error(stack, & + 'Cannot mix ordered and unordered elements') + goto 100 + endif + if (c=="|".and.current%parent%operator/=OP_MIXED) & + current%parent%operator = OP_CHOICE + state = ST_CHILD + elseif (c==')') then + nbrackets = nbrackets - 1 + if (nbrackets==0) then + state = ST_AFTERLASTBRACKET + deallocate(order) + else + temp => order + allocate(order(nbrackets)) + order = temp(:size(order)) + deallocate(temp) + state = ST_AFTERBRACKET + endif + current => current%parent + if (.not.check_duplicates(current)) & + goto 100 + else + call add_error(stack, & + 'Unexpected character found in element declaration.') + goto 100 + endif + + elseif (state==ST_AFTERBRACKET) then + !write(*,*)'ST_AFTERBRACKET' + if (c=='*') then + current%repeater = REP_ASTERISK + state = ST_SEPARATOR + elseif (c=='+') then + call transformCPPlus(current) + state = ST_SEPARATOR + elseif (c=='?') then + current%repeater = REP_QUESTION_MARK + state = ST_SEPARATOR + elseif (verify(c, XML_WHITESPACE)==0) then + state = ST_SEPARATOR + elseif (scan(c,'|,')>0) then + if (order(nbrackets)=='') then + order(nbrackets) = c + elseif (order(nbrackets)/=c) then + call add_error(stack, & + 'Cannot mix ordered and unordered elements') + goto 100 + endif + if (c=="|".and.current%parent%operator/=OP_MIXED) & + current%parent%operator = OP_CHOICE + state = ST_CHILD + elseif (c==')') then + nbrackets = nbrackets - 1 + if (nbrackets==0) then + deallocate(order) + state = ST_AFTERLASTBRACKET + else + temp => order + allocate(order(nbrackets)) + order = temp(:size(order)) + deallocate(temp) + state = ST_AFTERBRACKET + endif + current => current%parent + if (.not.check_duplicates(current)) & + goto 100 + else + call add_error(stack, & + 'Unexpected character "'//c//'"found after ")"') + goto 100 + endif + + elseif (state==ST_AFTERLASTBRACKET) then + !write(*,*)'ST_AFTERLASTBRACKET' + if (c=='*') then + state = ST_END + current%repeater = REP_ASTERISK + elseif (c=='+') then + if (mixed) then + call add_error(stack, & + '+ operator disallowed for Mixed elements') + goto 100 + endif + call transformCPPlus(current) + state = ST_END + elseif (c=='?') then + if (mixed) then + call add_error(stack, & + '? operator disallowed for Mixed elements') + goto 100 + endif + current%repeater = REP_QUESTION_MARK + state = ST_END + elseif (verify(c, XML_WHITESPACE)==0) then + if (mixed) then + if (mixed_additional) then + call add_error(stack, & + 'Missing "*" at end of Mixed element specification') + goto 100 + endif + endif + state = ST_END + else + call add_error(stack, & + 'Unexpected character "'//c//'" found after final ")"') + goto 100 + endif + + elseif (state==ST_END) then + !write(*,*)'ST_END' + if (verify(c, XML_WHITESPACE)==0) then + continue + else + call add_error(stack, & + 'Unexpected token found after end of element specification') + goto 100 + endif + + endif + + enddo + + if (state/=ST_END) then + call add_error(stack, "Error in parsing contents of element declaration") + goto 100 + endif + + if (associated(element)) then + element%any = any + element%empty = empty + element%mixed = mixed + element%model => vs_str_alloc(trim(strip_spaces(contents))) + element%cp => top + element%internal = internal +! For debugging it may be useful to dump the result here... +! Also need to use the subroutine. +! call dumpCPtree(top) + else + if (associated(top)) call destroyCPtree(top) + endif + return + +100 if (associated(order)) deallocate(order) + if (associated(name)) deallocate(name) + if (associated(top)) call destroyCPtree(top) + + contains + function strip_spaces(s1) result(s2) + character(len=*) :: s1 + character(len=len(s1)) :: s2 + integer :: i, i2 + i2 = 1 + do i = 1, len(s1) + if (verify(s1(i:i), XML_WHITESPACE)==0) cycle + s2(i2:i2) = s1(i:i) + i2 = i2 + 1 + end do + s2(i2:) = '' + end function strip_spaces + + function check_duplicates(cp) result(p) + type(content_particle_t), pointer :: cp + logical :: p + + type(string_list) :: sl + type(content_particle_t), pointer :: tcp + + if (cp%operator==OP_SEQ) then + p = .true. + return + endif + + call init_string_list(sl) + tcp => cp%firstChild + p = .false. + do while (associated(tcp)) + if (tcp%operator==OP_NAME) then + if (registered_string(sl, str_vs(tcp%name))) then + call destroy_string_list(sl) + if (cp%operator==OP_MIXED) then + call add_error(stack, & + "Duplicate element names found in MIXED") + elseif (cp%operator==OP_CHOICE) then + call add_error(stack, & + "Duplicate element names found in CHOICE") + endif + return + else + call add_string(sl, str_vs(tcp%name)) + endif + endif + tcp => tcp%nextSibling + enddo + p = .true. + call destroy_string_list(sl) + end function check_duplicates + end subroutine parse_dtd_element + + + subroutine init_attribute_list(a_list) + type(attribute_list), intent(inout) :: a_list + + allocate(a_list%list(0)) + end subroutine init_attribute_list + + subroutine destroy_attribute_t(a) + type(attribute_t), pointer :: a + + if (associated(a%name)) deallocate(a%name) + if (associated(a%default)) deallocate(a%default) + call destroy_string_list(a%enumerations) + + deallocate(a) + end subroutine destroy_attribute_t + + subroutine destroy_attribute_list(a_list) + type(attribute_list), intent(inout) :: a_list + + integer :: i + + do i = 1, size(a_list%list) + deallocate(a_list%list(i)%name) + if (associated(a_list%list(i)%default)) deallocate(a_list%list(i)%default) + call destroy_string_list(a_list%list(i)%enumerations) + enddo + deallocate(a_list%list) + + end subroutine destroy_attribute_list + + function existing_attribute(a_list, name) result(p) + type(attribute_list), intent(inout) :: a_list + character(len=*), intent(in) :: name + logical :: p + + integer :: i + p = .false. + do i = 1, size(a_list%list) + p = (str_vs(a_list%list(i)%name)==name) + if (p) exit + enddo + end function existing_attribute + + function add_attribute(a_list, name, internal) result(a) + type(attribute_list), intent(inout) :: a_list + character(len=*), intent(in) :: name + logical, intent(in) :: internal + type(attribute_t), pointer :: a + + integer :: i + type(attribute_t), pointer :: temp(:) + + temp => a_list%list + allocate(a_list%list(size(temp)+1)) + do i = 1, size(temp) + a_list%list(i)%name => temp(i)%name + a_list%list(i)%atttype = temp(i)%atttype + a_list%list(i)%attdefault = temp(i)%attdefault + a_list%list(i)%default => temp(i)%default + a_list%list(i)%enumerations%list => temp(i)%enumerations%list + a_list%list(i)%internal = temp(i)%internal + enddo + deallocate(temp) + a => a_list%list(i) + + a%name => vs_str_alloc(name) + call init_string_list(a%enumerations) + a%internal = internal + + end function add_attribute + + function get_attribute(a_list, name) result(a) + type(attribute_list), intent(inout) :: a_list + character(len=*), intent(in) :: name + type(attribute_t), pointer :: a + + integer :: i + do i = 1, size(a_list%list) + if (str_vs(a_list%list(i)%name)==name) then + a => a_list%list(i) + exit + endif + enddo + end function get_attribute + + subroutine parse_dtd_attlist(contents, xv, namespaces, validCheck, stack, elem, internal) + character(len=*), intent(in) :: contents + integer, intent(in) :: xv + logical, intent(in) :: validCheck + logical, intent(in) :: namespaces + type(error_stack), intent(inout) :: stack + type(element_t), pointer :: elem + logical, intent(in) :: internal + + integer :: i + integer :: state + character :: c, q + character, pointer :: name(:), attType(:), default(:), value(:), temp(:) + + type(attribute_t), pointer :: ca + type(attribute_t), pointer :: ignore_att + + ignore_att => null() + ! We need ignore_att to process but not take account of duplicate attributes + ! elem is optional so we can not record declarations if necessary. + ca => null() + name => null() + attType => null() + default => null() + value => null() + temp => null() + + state = ST_START + + do i = 1, len(contents) + 1 + if (in_error(stack)) exit + if (i<=len(contents)) then + c = contents(i:i) + else + c = " " + endif + + if (state==ST_START) then + !write(*,*)'ST_START' + if (verify(c, XML_WHITESPACE)==0) cycle + if (isInitialNameChar(c, xv)) then + name => vs_str_alloc(c) + state = ST_NAME + else + call add_error(stack, & + 'Unexpected character in Attlist') + endif + + elseif (state==ST_NAME) then + !write(*,*)'ST_NAME' + if (isNameChar(c, xv)) then + temp => vs_str_alloc(str_vs(name)//c) + deallocate(name) + name => temp + elseif (verify(c, XML_WHITESPACE)==0) then + if (namespaces.and..not.checkQName(str_vs(name), xv)) then + call add_error(stack, & + "Attribute name in ATTLIST must be QName") + elseif (associated(elem)) then + if (existing_attribute(elem%attlist, str_vs(name))) then + if (associated(ignore_att)) call destroy_attribute_t(ignore_att) + allocate(ignore_att) + call init_string_list(ignore_att%enumerations) + ignore_att%name => vs_vs_alloc(name) + ca => ignore_att + else + ca => add_attribute(elem%attlist, str_vs(name), internal) + endif + else + if (associated(ignore_att)) call destroy_attribute_t(ignore_att) + allocate(ignore_att) + call init_string_list(ignore_att%enumerations) + ignore_att%name => vs_vs_alloc(name) + ca => ignore_att + endif + deallocate(name) + state = ST_AFTERNAME + else + call add_error(stack, & + 'Unexpected character in Attlist Name') + endif + + elseif (state==ST_AFTERNAME) then + !write(*,*)'ST_AFTERNAME' + if (verify(c, XML_WHITESPACE)==0) cycle + if (verify(c, upperCase)==0) then + attType => vs_str_alloc(c) + state = ST_ATTTYPE + elseif (c=='(') then + allocate(value(0)) + ca%attType = ATT_ENUM + state = ST_ENUMERATION + else + call add_error(stack, & + 'Unexpected error after Attlist Name') + endif + + elseif (state==ST_ATTTYPE) then + !write(*,*)'ST_ATTTYPE' + if (verify(c, upperCase)==0) then + temp => attType + attType => vs_str_alloc(str_vs(temp)//c) + deallocate(temp) + elseif (verify(c, XML_WHITESPACE)==0) then + ! xml:id constraint + if (str_vs(ca%name)=="xml:id" & + .and..not.str_vs(attType)=="ID") then + call add_error(stack, & + "xml:id attribute must be declared as type ID") + elseif (str_vs(attType)=='CDATA') then + ca%attType = ATT_CDATA + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='ID') then + if (validCheck) then + ! Validity Constraint: One ID per Element Type + if (associated(elem)) then + if (elem%id_declared) then + call add_error(stack, & + "Cannot have two declared attributes of type ID on one element type.") + else + elem%id_declared = .true. + endif + endif + endif + ca%attType = ATT_ID + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='IDREF') then + ca%attType = ATT_IDREF + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='IDREFS') then + ca%attType = ATT_IDREFS + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='ENTITY') then + ca%attType = ATT_ENTITY + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='ENTITIES') then + ca%attType = ATT_ENTITIES + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='NMTOKEN') then + ca%attType = ATT_NMTOKEN + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='NMTOKENS') then + ca%attType = ATT_NMTOKENS + state = ST_AFTER_ATTTYPE + elseif (str_vs(attType)=='NOTATION') then + ca%attType = ATT_NOTATION + state = ST_AFTER_NOTATION + else + call add_error(stack, & + 'Unknown AttType') + endif + deallocate(attType) + else + call add_error(stack, & + 'Unexpected character in AttType') + endif + + elseif (state==ST_AFTER_NOTATION) then + !write(*,*)'ST_AFTER_NOTATION' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='(') then + state = ST_NOTATION_LIST + else + call add_error(stack, & + 'Unexpected character after Notation') + endif + + elseif (state==ST_NOTATION_LIST) then + !write(*,*)'ST_NOTATION_LIST' + if (verify(c, XML_WHITESPACE)==0) cycle + if (isInitialNameChar(c, xv)) then + value => vs_str_alloc(c) + state = ST_ENUM_NAME + else + call add_error(stack, & + 'Unexpected character in Notation list') + endif + + elseif (state==ST_ENUMERATION) then + !write(*,*)'ST_ENUMERATION' + if (verify(c, XML_WHITESPACE)==0) cycle + if (isNameChar(c, xv)) then + temp => vs_str_alloc(str_vs(value)//c) + deallocate(value) + value => temp + state = ST_ENUM_NAME + elseif (c=='|') then + call add_error(stack, & + "Missing token in Enumeration") + elseif (c==')') then + call add_error(stack, & + "Missing tokens in Enumeration") + else + call add_error(stack, & + 'Unexpected character in attlist enumeration') + endif + + elseif (state==ST_ENUM_NAME) then + !write(*,*)'ST_ENUM_NAME' + if (isNameChar(c, xv)) then + temp => vs_str_alloc(str_vs(value)//c) + deallocate(value) + value => temp + elseif (verify(c, XML_WHITESPACE)==0) then + if (validCheck.and.registered_string(ca%enumerations, str_vs(value))) then + call add_error(stack, & + "Duplicate enumeration value in ATTLIST") + elseif (namespaces.and.ca%attType==ATT_NOTATION & + .and..not.checkNCName(str_vs(value), xv)) then + call add_error(stack, & + "Notation name must be NCName") + else + call add_string(ca%enumerations, str_vs(value)) + endif + deallocate(value) + state = ST_SEPARATOR + elseif (c=='|') then + if (validCheck.and.registered_string(ca%enumerations, str_vs(value))) then + call add_error(stack, & + "Duplicate enumeration value in ATTLIST") + elseif (namespaces.and.ca%attType==ATT_NOTATION & + .and..not.checkNCName(str_vs(value), xv)) then + call add_error(stack, & + "Notation name must be NCName") + else + call add_string(ca%enumerations, str_vs(value)) + endif + deallocate(value) + if (ca%attType==ATT_NOTATION) then + state = ST_NOTATION_LIST + else + allocate(value(0)) + state = ST_ENUMERATION + endif + elseif (c==')') then + if (size(value)==0) then + call add_error(stack, & + 'Missing token in Enumeration list') + endif + if (validCheck.and.registered_string(ca%enumerations, str_vs(value))) then + call add_error(stack, & + "Duplicate enumeration value in ATTLIST") + elseif (namespaces.and.ca%attType==ATT_NOTATION & + .and..not.checkNCName(str_vs(value), xv)) then + call add_error(stack, & + "Notation name must be NCName") + else + call add_string(ca%enumerations, str_vs(value)) + endif + deallocate(value) + state = ST_AFTER_ATTTYPE_SPACE + else + call add_error(stack, & + 'Unexpected character in attlist enumeration') + endif + + elseif (state==ST_SEPARATOR) then + !write(*,*)'ST_SEPARATOR' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='|') then + if (ca%attType==ATT_NOTATION) then + state = ST_NOTATION_LIST + else + allocate(value(0)) + state = ST_ENUMERATION + endif + elseif (c==')') then + state = ST_AFTER_ATTTYPE_SPACE + else + call add_error(stack, & + 'Unexpected character in attlist enumeration') + endif + + elseif (state==ST_AFTER_ATTTYPE_SPACE) then + if (verify(c, XML_WHITESPACE)/=0) then + call add_error(stack, & + 'Missing whitespace in attlist enumeration') + endif + state = ST_AFTER_ATTTYPE + + elseif (state==ST_AFTER_ATTTYPE) then + !write(*,*)'ST_AFTER_ATTTYPE' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='#') then + allocate(default(0)) + state = ST_DEFAULT_DECL + elseif (c=='"'.or.c=="'") then + if (validCheck) then + ! Validity Constraint: ID Attribute Default + if (ca%attType==ATT_ID) & + call add_error(stack, & + "Attribute of type ID may not have default value") + endif + ca%attDefault = ATT_DEFAULT + q = c + allocate(value(0)) + state = ST_DEFAULTVALUE + else + call add_error(stack, & + 'Unexpected character after AttType') + endif + + elseif (state==ST_DEFAULT_DECL) then + !write(*,*)'ST_DEFAULT_DECL' + if (verify(c, upperCase)==0) then + temp => vs_str_alloc(str_vs(default)//c) + deallocate(default) + default => temp + elseif (verify(c, XML_WHITESPACE)==0) then + if (str_vs(default)=='REQUIRED') then + ca%attdefault = ATT_REQUIRED + deallocate(default) + state = ST_START + elseif (str_vs(default)=='IMPLIED') then + ca%attdefault = ATT_IMPLIED + deallocate(default) + state = ST_START + elseif (str_vs(default)=='FIXED') then + if (validCheck) then + ! Validity Constraint: ID Attribute Default + if (ca%attType==ATT_ID) & + call add_error(stack, & + "Attribute of type ID may not have FIXED value") + endif + ca%attdefault = ATT_FIXED + deallocate(default) + state = ST_AFTERDEFAULTDECL + else + call add_error(stack, & + 'Unknown Default declaration') + endif + else + call add_error(stack, & + 'Unexpected character in Default declaration') + endif + + elseif (state==ST_AFTERDEFAULTDECL) then + !write(*,*)'ST_AFTERDEFAULTDECL' + if (verify(c, XML_WHITESPACE)==0) cycle + if (c=='"') then + q = c + allocate(value(0)) + state = ST_DEFAULTVALUE + elseif (c=="'") then + q = c + allocate(value(0)) + state = ST_DEFAULTVALUE + else + call add_error(stack, & + 'Unexpected character after Default declaration') + endif + + elseif (state==ST_DEFAULTVALUE) then + !write(*,*)'ST_DEFAULTVALUE' + if (c==q) then + if (ca%attType/=ATT_CDATA) then + temp => vs_str_alloc(att_value_normalize(str_vs(value))) + deallocate(value) + value => temp + endif + if (validCheck) then + select case(ca%attType) + ! Can't have ID with defaults + case (ATT_IDREF) + ! VC: IDREF + if (namespaces) then + if (.not.checkNCName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type IDREF must have a value which is an XML NCName") + else + if (.not.checkName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type IDREF must have a value which is an XML Name") + endif + case (ATT_IDREFS) + ! VC: IDREF + if (namespaces) then + if (.not.checkNCNames(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type IDREFS must have a value which contains only XML NCNames") + else + if (.not.checkNames(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type IDREFS must have a value which contains only XML Names") + endif + case (ATT_ENTITY) + ! VC: Entity Name + if (namespaces) then + if (.not.checkNCName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type ENTITY must have a value which is an XML NCName") + else + if (.not.checkName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type ENTITY must have a value which is an XML Name") + endif + case (ATT_ENTITIES) + ! VC: Entity Name + if (namespaces) then + if (.not.checkNames(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type ENTITIES must have a value which contains only XML NCNames") + else + if (.not.checkNames(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type ENTITIES must have a value which contains only XML Names") + endif + case (ATT_NMTOKEN) + ! VC Name Token + if (.not.checkNmtoken(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type NMTOKEN must have a value which is a NMTOKEN") + case (ATT_NMTOKENS) + ! VC: Name Token + if (.not.checkNmtokens(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type NMTOKENS must have a value which contain only NMTOKENs") + case (ATT_NOTATION) + ! VC: Notation Attributes + if (namespaces) then + if (.not.checkNCName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type NOTATION must have a value which is an XMLNCName") + else + if (.not.checkName(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type NOTATION must have a value which is an XML Name") + endif + case (ATT_ENUM) + ! VC: Enumeration + if (.not.checkNmtoken(str_vs(value), xv)) & + call add_error(stack, & + "Attributes of type ENUM must have a value which is an NMTOKENs") + if (.not.registered_string(ca%enumerations, str_vs(value))) & + call add_error(stack, & + "Default value of ENUM does not match permitted values") + end select + endif + if (.not.in_error(stack)) then + if (ca%attType==ATT_ENTITIES) then + call destroy_string_list(ca%enumerations) + ca%enumerations = tokenize_to_string_list(str_vs(value)) + endif + ca%default => value + value => null() + state = ST_START + endif + else + temp => vs_str_alloc(str_vs(value)//c) + deallocate(value) + value => temp + endif + + endif + + enddo + + if (associated(ignore_att)) call destroy_attribute_t(ignore_att) + + if (.not.in_error(stack)) then + if (state==ST_START) then + return + else + call add_error(stack, & + 'Incomplete Attlist declaration') + endif + endif + + if (associated(name)) deallocate(name) + if (associated(attType)) deallocate(attType) + if (associated(default)) deallocate(default) + if (associated(value)) deallocate(value) + + end subroutine parse_dtd_attlist + + subroutine report_declarations(elem, attributeDecl_handler) + type(element_t), intent(in) :: elem + interface + subroutine attributeDecl_handler(eName, aName, type, mode, value) + character(len=*), intent(in) :: eName + character(len=*), intent(in) :: aName + character(len=*), intent(in) :: type + character(len=*), intent(in), optional :: mode + character(len=*), intent(in), optional :: value + end subroutine attributeDecl_handler + end interface + + integer :: i + character(len=8) :: type + character(len=8) :: mode + type(attribute_t), pointer :: a + + do i = 1, size(elem%attlist%list) + a => elem%attlist%list(i) + type = ATT_TYPES(a%attType) + select case (a%attDefault) + case (ATT_REQUIRED) + mode = "REQUIRED" + case (ATT_IMPLIED) + mode = "IMPLIED" + case (ATT_FIXED) + mode = "FIXED" + end select + + if (a%attType==ATT_NOTATION) then + if (a%attDefault==ATT_DEFAULT) then + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + 'NOTATION '//make_token_group(a%enumerations), value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + 'NOTATION '//make_token_group(a%enumerations)) + endif + else + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + 'NOTATION '//make_token_group(a%enumerations), mode=trim(mode), & + value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + 'NOTATION '//make_token_group(a%enumerations), mode=trim(mode)) + endif + endif + elseif (a%attType==ATT_ENUM) then + if (a%attDefault==ATT_DEFAULT) then + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + make_token_group(a%enumerations), value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + make_token_group(a%enumerations)) + endif + else + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + make_token_group(a%enumerations), mode=trim(mode), & + value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + make_token_group(a%enumerations), mode=trim(mode)) + endif + endif + else + if (a%attDefault==ATT_DEFAULT) then + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + trim(type), value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + trim(type)) + endif + else + if (associated(a%default)) then + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + trim(type), mode=trim(mode), value=str_vs(a%default)) + else + call attributeDecl_handler(str_vs(elem%name), str_vs(a%name), & + trim(type), mode=trim(mode)) + endif + endif + endif + enddo + + + end subroutine report_declarations + + pure function make_token_group_len(s_list) result(n) + type(string_list), intent(in) :: s_list + integer :: n + + integer :: i + n = size(s_list%list) + 1 + do i = 1, size(s_list%list) + n = n + size(s_list%list(i)%s) + enddo + end function make_token_group_len + + function make_token_group(s_list) result(s) + type(string_list), intent(in) :: s_list + character(len=make_token_group_len(s_list)) :: s + + integer :: i, m, n + s(1:1) = '(' + n = 2 + do i = 1, size(s_list%list)-1 + m = size(s_list%list(i)%s) + s(n:n+m) = str_vs(s_list%list(i)%s)//'|' + n = n + m + 1 + enddo + s(n:) = str_vs(s_list%list(i)%s)//')' + end function make_token_group + + function attribute_has_default(att) result(p) + type(attribute_t), pointer :: att + logical :: p + + if (associated(att)) then + p = att%attDefault==ATT_DEFAULT.or.att%attDefault==ATT_FIXED + else + p = .false. + endif + end function attribute_has_default + + function get_attlist_size(elem) result(n) + type(element_t), pointer :: elem + integer :: n + + if (associated(elem)) then + n = size(elem%attlist%list) + else + n = 0 + endif + end function get_attlist_size + + function get_attdecl_by_index(elem, n) result(att) + type(element_t), pointer :: elem + integer, intent(in) :: n + type(attribute_t), pointer :: att + + att => null() + if (associated(elem)) then + if (n>0.and.n<=size(elem%attlist%list)) then + att => elem%attlist%list(n) + endif + endif + end function get_attdecl_by_index + + function get_attdecl_by_name(elem, name) result(att) + type(element_t), pointer :: elem + character(len=*), intent(in) :: name + type(attribute_t), pointer :: att + + integer :: i + att => null() + if (associated(elem)) then + do i = 1, size(elem%attlist%list) + if (str_vs(elem%attlist%list(i)%name)==name) then + att => elem%attlist%list(i) + return + endif + enddo + endif + end function get_attdecl_by_name + + pure function express_att_decl_len(a) result(n) + type(attribute_t), intent(in) :: a + integer :: n + + if (a%attType==ATT_ENUM) then + n = size(a%name) + else + n = size(a%name)+1+len_trim(ATT_TYPES(a%attType)) + endif + + if (a%attType==ATT_NOTATION & + .or.a%attType==ATT_ENUM) & + n = n + 1 + make_token_group_len(a%enumerations) + + select case(a%attDefault) + case (ATT_REQUIRED) + n = n + len(" #REQUIRED") + case (ATT_IMPLIED) + n = n + len(" #IMPLIED") + case (ATT_DEFAULT) + n = n + len(" ") + case (ATT_FIXED) + n = n + len(" #FIXED") + end select + + if (associated(a%default)) & + n = n + 3 + size(a%default) + end function express_att_decl_len + + function express_attribute_declaration(a) result(s) + type(attribute_t), intent(in) :: a + character(len=express_att_decl_len(a)) :: s + + if (a%attType==ATT_ENUM) then + s = str_vs(a%name) + else + s = str_vs(a%name)//" "//ATT_TYPES(a%attType) + endif + if (a%attType==ATT_NOTATION & + .or.a%attType==ATT_ENUM) & + s = trim(s)//" "//make_token_group(a%enumerations) + + select case(a%attDefault) + case (ATT_REQUIRED) + s = trim(s)//" #REQUIRED" + case (ATT_IMPLIED) + s = trim(s)//" #IMPLIED" + case (ATT_DEFAULT) + s = trim(s)//" " + case (ATT_FIXED) + s = trim(s)//" #FIXED" + end select + + if (associated(a%default)) & + s = trim(s)//" """//str_vs(a%default)//"""" + end function express_attribute_declaration + + function get_att_type_enum(s) result(n) + character(len=*), intent(in) :: s + integer :: n + + select case(s) + case ('CDATA') + n = ATT_CDATA + case ('ID') + n = ATT_ID + case ('IDREF') + n = ATT_IDREF + case ('IDREFS') + n = ATT_IDREFS + case ('NMTOKEN') + n = ATT_NMTOKEN + case ('NMTOKENS') + n = ATT_NMTOKENS + case ('ENTITY') + n = ATT_ENTITY + case ('ENTITIES') + n = ATT_ENTITIES + case ('NOTATION') + n = ATT_NOTATION + case ('CDANO') + n= ATT_CDANO + case ('CDAMB') + n = ATT_CDAMB + end select + end function get_att_type_enum + + pure function att_value_normalize_len(s1) result(n) + character(len=*), intent(in) :: s1 + integer :: n + + integer :: i + logical :: w + + n = 0 + w = .true. + do i = 1, len(s1) + if (w.and.(verify(s1(i:i),XML_WHITESPACE)==0)) cycle + w = .false. + n = n + 1 + if (verify(s1(i:i),XML_WHITESPACE)==0) w = .true. + enddo + if (w) n = n - 1 ! Discard final space + + end function att_value_normalize_len + + function att_value_normalize(s1) result(s2) + character(len=*), intent(in) :: s1 + character(len=att_value_normalize_len(s1)) :: s2 + + integer :: i, i2 + logical :: w + + i = 0 + i2 = 1 + w = .true. + do while (i2<=len(s2)) + i = i + 1 + if (w.and.(verify(s1(i:i),XML_WHITESPACE)==0)) cycle + w = .false. + s2(i2:i2) = s1(i:i) + i2 = i2 + 1 + if (verify(s1(i:i),XML_WHITESPACE)==0) w = .true. + enddo + + end function att_value_normalize + +#endif +end module m_common_element diff --git a/src/xml/common/m_common_elstack.F90 b/src/xml/common/m_common_elstack.F90 new file mode 100644 index 000000000..ccfca15dc --- /dev/null +++ b/src/xml/common/m_common_elstack.F90 @@ -0,0 +1,236 @@ +module m_common_elstack + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: str_vs, vs_str + use m_common_error, only: FoX_fatal + use m_common_content_model, only: content_particle_t, checkCP, & + elementContentCP, emptyContentCP, checkCPToEnd + + implicit none + private + + ! Element stack during parsing. Keeps track of element names + ! and optionally tracks validity of content model + + ! Initial stack size: + integer, parameter :: STACK_SIZE_INIT = 10 + ! Multiplier when stack is exceeded: + real, parameter :: STACK_SIZE_MULT = 1.5 + + type :: elstack_item + character, dimension(:), pointer :: name => null() + type(content_particle_t), pointer :: cp => null() + end type elstack_item + + type :: elstack_t + private + integer :: n_items + type(elstack_item), pointer, dimension(:) :: stack => null() + end type elstack_t + + public :: elstack_t + + public :: push_elstack, pop_elstack, init_elstack, destroy_elstack, reset_elstack, print_elstack + public :: get_top_elstack, is_empty + public :: checkContentModel + public :: checkContentModelToEnd + public :: elementContent + public :: emptyContent + public :: len + + interface len + module procedure number_of_items + end interface + + interface is_empty + module procedure is_empty_elstack + end interface + +contains + + subroutine init_elstack(elstack) + type(elstack_t), intent(inout) :: elstack + + ! We go from 0 (and initialize the 0th string to "") + ! in order that we can safely check the top of an + ! empty stack + allocate(elstack%stack(0:STACK_SIZE_INIT)) + elstack%n_items = 0 + allocate(elstack%stack(0)%name(0)) + + end subroutine init_elstack + + subroutine destroy_elstack(elstack) + type(elstack_t), intent(inout) :: elstack + integer :: i + do i = 0, elstack % n_items + deallocate(elstack%stack(i)%name) + enddo + deallocate(elstack%stack) + end subroutine destroy_elstack + + subroutine reset_elstack(elstack) + type(elstack_t), intent(inout) :: elstack + + call destroy_elstack(elstack) + call init_elstack(elstack) + + end subroutine reset_elstack + + subroutine resize_elstack(elstack) + type(elstack_t), intent(inout) :: elstack + type(elstack_item), dimension(0:ubound(elstack%stack,1)) :: temp + integer :: i, s + + s = ubound(elstack%stack, 1) + + do i = 0, s + temp(i)%name => elstack%stack(i)%name + temp(i)%cp => elstack%stack(i)%cp + enddo + deallocate(elstack%stack) + allocate(elstack%stack(0:nint(s*STACK_SIZE_MULT))) + do i = 0, s + elstack%stack(i)%name => temp(i)%name + elstack%stack(i)%cp => temp(i)%cp + enddo + + end subroutine resize_elstack + + pure function is_empty_elstack(elstack) result(answer) + type(elstack_t), intent(in) :: elstack + logical :: answer + + answer = (elstack%n_items == 0) + end function is_empty_elstack + + function number_of_items(elstack) result(n) + type(elstack_t), intent(in) :: elstack + integer :: n + + n = elstack%n_items + end function number_of_items + + subroutine push_elstack(elstack, name, cp) + type(elstack_t), intent(inout) :: elstack + character(len=*), intent(in) :: name + type(content_particle_t), pointer, optional :: cp + + integer :: n + + n = elstack%n_items + n = n + 1 + if (n == size(elstack%stack)) then + call resize_elstack(elstack) + endif + allocate(elstack%stack(n)%name(len(name))) + elstack%stack(n)%name = vs_str(name) + if (present(cp)) elstack%stack(n)%cp => cp + elstack%n_items = n + + end subroutine push_elstack + + function pop_elstack(elstack) result(item) + type(elstack_t), intent(inout) :: elstack + character(len=merge(size(elstack%stack(elstack%n_items)%name), 0, elstack%n_items > 0)) :: item + + integer :: n + + n = elstack%n_items + if (n == 0) then + call FoX_fatal("Element stack empty") + endif + item = str_vs(elstack%stack(n)%name) + deallocate(elstack%stack(n)%name) + elstack%n_items = n - 1 + + end function pop_elstack + + pure function get_top_elstack(elstack) result(item) + ! Get the top element of the stack, *without popping it*. + type(elstack_t), intent(in) :: elstack + character(len=merge(size(elstack%stack(elstack%n_items)%name), 0, elstack%n_items > 0)) :: item + + integer :: n + + n = elstack%n_items + + if (n==0) then + item = "" + else + item = str_vs(elstack%stack(n)%name) + endif + + end function get_top_elstack + + function checkContentModel(elstack, name) result(p) + type(elstack_t), intent(inout) :: elstack + character(len=*), intent(in) :: name + logical :: p + + type(content_particle_t), pointer :: cp + + integer :: n + n = elstack%n_items + if (n==0) then + p = .true. + else + cp => elstack%stack(n)%cp + p = checkCP(cp, name) + elstack%stack(n)%cp => cp + endif + end function checkContentModel + + function checkContentModelToEnd(elstack) result(p) + type(elstack_t), intent(inout) :: elstack + logical :: p + + type(content_particle_t), pointer :: cp + + integer :: n + n = elstack%n_items + + cp => elstack%stack(n)%cp + p = checkCPToEnd(cp) + + end function checkContentModelToEnd + + function elementContent(elstack) result(p) + type(elstack_t), intent(in) :: elstack + logical :: p + + integer :: n + n = elstack%n_items + if (n==0) then + p = .false. + else + p = elementContentCP(elstack%stack(n)%cp) + endif + end function elementContent + + function emptyContent(elstack) result(p) + type(elstack_t), intent(in) :: elstack + logical :: p + + integer :: n + n = elstack%n_items + if (n==0) then + p = .false. + else + p = emptyContentCP(elstack%stack(n)%cp) + endif + end function emptyContent + + subroutine print_elstack(elstack,unit) + type(elstack_t), intent(in) :: elstack + integer, intent(in) :: unit + integer :: i + + do i = elstack%n_items, 1, -1 + write(unit=unit,fmt=*) elstack%stack(i)%name + enddo + + end subroutine print_elstack + +#endif +end module m_common_elstack diff --git a/src/xml/common/m_common_entities.F90 b/src/xml/common/m_common_entities.F90 new file mode 100644 index 000000000..071455b29 --- /dev/null +++ b/src/xml/common/m_common_entities.F90 @@ -0,0 +1,475 @@ +module m_common_entities + +#ifndef DUMMYLIB + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc + use fox_m_fsys_format, only: str_to_int_10, str_to_int_16 + use fox_m_utils_uri, only: URI, destroyURI + use m_common_charset, only: digits, hexdigits + use m_common_error, only: FoX_error + + implicit none + private + + type entity_t + logical :: external + logical :: wfc ! Was this entity declared externally or in a PE, where + ! a non-validating processor might not see it? + character(len=1), dimension(:), pointer :: name => null() + character(len=1), dimension(:), pointer :: text => null() + character(len=1), dimension(:), pointer :: publicId => null() + character(len=1), dimension(:), pointer :: systemId => null() + character(len=1), dimension(:), pointer :: notation => null() + type(URI), pointer :: baseURI => null() + end type entity_t + + type entity_list + private + type(entity_t), dimension(:), pointer :: list => null() + end type entity_list + + public :: is_unparsed_entity + public :: is_external_entity + + public :: expand_entity_text + public :: expand_entity_text_len + public :: existing_entity + + public :: expand_char_entity + + public :: expand_entity + public :: expand_entity_len + + public :: entity_t + public :: entity_list + public :: init_entity_list + public :: reset_entity_list + public :: destroy_entity_list + public :: print_entity_list + public :: add_internal_entity + public :: add_external_entity + public :: pop_entity_list + + interface size + module procedure size_el + end interface + + interface is_unparsed_entity + module procedure is_unparsed_entity_ + module procedure is_unparsed_entity_from_list + end interface + + public :: getEntityByIndex + public :: getEntityByName + + public :: size + +contains + + function size_el(el) result(n) + type(entity_list), intent(in) :: el + integer :: n + + n = ubound(el%list, 1) + end function size_el + + function shallow_copy_entity(ent1) result(ent2) + type(entity_t), intent(in) :: ent1 + type(entity_t) :: ent2 + + ent2%external = ent1%external + ent2%wfc = ent1%wfc + ent2%name => ent1%name + ent2%text => ent1%text + ent2%publicId => ent1%publicId + ent2%systemId => ent1%systemId + ent2%notation => ent1%notation + ent2%baseURI => ent1%baseURI + + end function shallow_copy_entity + + function getEntityByIndex(el, i) result(e) + type(entity_list), intent(in) :: el + integer, intent(in) :: i + type(entity_t), pointer :: e + + e => el%list(i) + end function getEntityByIndex + + function getEntityNameByIndex(el, i) result(c) + type(entity_list), intent(in) :: el + integer, intent(in) :: i + character(len=size(el%list(i)%name)) :: c + + c = str_vs(el%list(i)%name) + end function getEntityNameByIndex + + function getEntityByName(el, name) result(e) + type(entity_list), intent(in) :: el + character(len=*), intent(in) :: name + type(entity_t), pointer :: e + + integer :: i + + e => null() + do i = 1, size(el%list) + if (str_vs(el%list(i)%name)==name) then + e => el%list(i) + exit + endif + enddo + end function getEntityByName + + + subroutine destroy_entity(ent) + type(entity_t), intent(inout) :: ent + + deallocate(ent%name) + deallocate(ent%text) + deallocate(ent%publicId) + deallocate(ent%systemId) + deallocate(ent%notation) + + if (associated(ent%baseURI)) call destroyURI(ent%baseURI) + + end subroutine destroy_entity + + + subroutine init_entity_list(ents) + type(entity_list), intent(inout) :: ents + + if (associated(ents%list)) deallocate(ents%list) + allocate(ents%list(0)) + + end subroutine init_entity_list + + + subroutine reset_entity_list(ents) + type(entity_list), intent(inout) :: ents + + call destroy_entity_list(ents) + call init_entity_list(ents) + + end subroutine reset_entity_list + + + subroutine destroy_entity_list(ents) + type(entity_list), intent(inout) :: ents + + integer :: i, n + + n = size(ents%list) + do i = 1, n + call destroy_entity(ents%list(i)) + enddo + deallocate(ents%list) + end subroutine destroy_entity_list + + function pop_entity_list(ents) result(name) + type(entity_list), intent(inout) :: ents + character(len=size(ents%list(size(ents%list))%name)) :: name + + type(entity_t), pointer :: ents_tmp(:) + integer :: i, n + n = size(ents%list) + + ents_tmp => ents%list + allocate(ents%list(n-1)) + do i = 1, n - 1 + ents%list(i) = shallow_copy_entity(ents_tmp(i)) + enddo + name = str_vs(ents_tmp(i)%name) + + call destroy_entity(ents_tmp(i)) + deallocate(ents_tmp) + end function pop_entity_list + + subroutine print_entity_list(ents) + type(entity_list), intent(in) :: ents + + integer :: i, n + + n = size(ents%list) + write(*,'(a)') '>ENTITYLIST' + do i = 1, n + write(*,'(a)') str_vs(ents%list(i)%name) + write(*,'(a)') str_vs(ents%list(i)%text) + write(*,'(a)') str_vs(ents%list(i)%publicId) + write(*,'(a)') str_vs(ents%list(i)%systemId) + write(*,'(a)') str_vs(ents%list(i)%notation) + enddo + write(*,'(a)') ' ents%list + allocate(ents%list(n+1)) + do i = 1, n + ents%list(i) = shallow_copy_entity(ents_tmp(i)) + enddo + deallocate(ents_tmp) + ents%list(i)%external = len(systemId)>0 + ents%list(i)%wfc = wfc + ents%list(i)%name => vs_str_alloc(name) + ents%list(i)%text => vs_str_alloc(text) + ents%list(i)%publicId => vs_str_alloc(publicId) + ents%list(i)%systemId => vs_str_alloc(systemId) + ents%list(i)%notation => vs_str_alloc(notation) + ents%list(i)%baseURI => baseURI + end subroutine add_entity + + + subroutine add_internal_entity(ents, name, text, baseURI, wfc) + type(entity_list), intent(inout) :: ents + character(len=*), intent(in) :: name + character(len=*), intent(in) :: text + type(URI), pointer :: baseURI + logical, intent(in) :: wfc + + call add_entity(ents, name=name, text=text, & + publicId="", systemId="", notation="", baseURI=baseURI, wfc=wfc) + end subroutine add_internal_entity + + + subroutine add_external_entity(ents, name, systemId, baseURI, wfc, publicId, notation) + type(entity_list), intent(inout) :: ents + character(len=*), intent(in) :: name + character(len=*), intent(in) :: systemId + character(len=*), intent(in), optional :: publicId + character(len=*), intent(in), optional :: notation + type(URI), pointer :: baseURI + logical, intent(in) :: wfc + + if (present(publicId) .and. present(notation)) then + call add_entity(ents, name=name, text="", & + publicId=publicId, systemId=systemId, notation=notation, & + wfc=wfc, baseURI=baseURI) + elseif (present(publicId)) then + call add_entity(ents, name=name, text="", & + publicId=publicId, systemId=systemId, notation="", & + wfc=wfc, baseURI=baseURI) + elseif (present(notation)) then + call add_entity(ents, name=name, text="", & + publicId="", systemId=systemId, notation=notation, & + wfc=wfc, baseURI=baseURI) + else + call add_entity(ents, name=name, text="", & + publicId="", systemId=systemId, notation="", & + wfc=wfc, baseURI=baseURI) + endif + end subroutine add_external_entity + + + function is_unparsed_entity_from_list(ents, name) result(p) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + p = (size(ents%list(i)%notation)>0) + exit + endif + enddo + end function is_unparsed_entity_from_list + + function is_unparsed_entity_(ent) result(p) + type(entity_t), intent(in) :: ent + logical :: p + + p = (size(ent%notation)>0) + + end function is_unparsed_entity_ + + + function is_external_entity(ents, name) result(p) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + p = ents%list(i)%external + exit + endif + enddo + end function is_external_entity + + pure function expand_char_entity_len(name) result(n) + character(len=*), intent(in) :: name + integer :: n + + integer :: number + + if (name(1:1) == "#") then + if (name(2:2) == "x") then ! hex character reference + if (verify(name(3:), hexdigits) == 0) then + number = str_to_int_16(name(3:)) + if (0 <= number .and. number <= 128) then + n = 1 + else + n = len(name) + 2 + endif + else + n = 0 + endif + else ! decimal character reference + if (verify(name(3:), digits) == 0) then + number = str_to_int_10(name(2:)) + if (0 <= number .and. number <= 128) then + n = 1 + else + n = len(name) + 2 + endif + else + n = 0 + endif + endif + else + n = 0 + endif + end function expand_char_entity_len + + + function expand_char_entity(name) result(text) + character(len=*), intent(in) :: name + character(len=expand_char_entity_len(name)) :: text + + integer :: number + + select case (len(text)) + case (0) + call FoX_error("Invalid character entity reference") + case (1) + if (name(2:2) == "x") then ! hex character reference + number = str_to_int_16(name(3:)) + else ! decimal character reference + number = str_to_int_10(name(2:)) + endif + text = achar(number) + ! FIXME what about > 127 ... + case default + text = "&"//name//";" + end select + + end function expand_char_entity + + + pure function existing_entity(ents, name) result(p) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + +!FIXME the following test is not entirely in accordance with the valid chars check we do elsewhere... + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + p = .true. + return + endif + enddo + + end function existing_entity + + + pure function expand_entity_text_len(ents, name) result(n) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + integer :: n + + integer :: i + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + n = size(ents%list(i)%text) + endif + enddo + + end function expand_entity_text_len + + + function expand_entity_text(ents, name) result(text) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + character(len=expand_entity_text_len(ents, name)) :: text + + integer :: i + + ! No error checking - make sure entity exists before calling it. + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + text = str_vs(ents%list(i)%text) + exit + endif + enddo + + end function expand_entity_text + + + pure function expand_entity_len(ents, name) result(n) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + integer :: n + + integer :: i + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + n = size(ents%list(i)%text) + endif + enddo + + end function expand_entity_len + + + function expand_entity(ents, name) result(text) + type(entity_list), intent(in) :: ents + character(len=*), intent(in) :: name + character(len=expand_entity_len(ents, name)) :: text + + integer :: i + + do i = 1, size(ents%list) + if (name == str_vs(ents%list(i)%name)) then + text = str_vs(ents%list(i)%text) + endif + enddo + + end function expand_entity + +#endif +end module m_common_entities diff --git a/src/xml/common/m_common_entity_expand.F90 b/src/xml/common/m_common_entity_expand.F90 new file mode 100644 index 000000000..565700710 --- /dev/null +++ b/src/xml/common/m_common_entity_expand.F90 @@ -0,0 +1,86 @@ +module m_common_entity_expand + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: str_vs, vs_str + use m_common_entities, only: expand_char_entity + use m_common_error, only: error_stack, add_error + use m_common_namecheck, only: checkName, checkCharacterEntityReference, & + checkRepCharEntityReference + use m_common_struct, only: xml_doc_state + + implicit none + private + + public :: expand_entity_value_alloc + + ! This does the first level of expansion of the contents of an entity + ! reference, for storage during processing. Only character references + ! are expanded. + +contains + + function expand_entity_value_alloc(repl, xds, stack) result(repl_new) + !perform expansion of character entity references + ! check that no parameter entities are present + ! and check that all general entity references are well-formed. + !before storing it. + ! + ! This is only ever called from the SAX parser + ! (might it be called from WXML?) + ! so input & output is with character arrays, not strings. + character, dimension(:), intent(in) :: repl + type(xml_doc_state), intent(in) :: xds + type(error_stack), intent(inout) :: stack + character, dimension(:), pointer :: repl_new + + character, dimension(size(repl)) :: repl_temp + integer :: i, i2, j + + allocate(repl_new(0)) + if (index(str_vs(repl),'%')/=0) then + call add_error(stack, "Not allowed % in internal subset general entity value") + return + endif + + i = 1 + i2 = 1 + do + if (i>size(repl)) exit + if (repl(i)=='&') then + j = index(str_vs(repl(i+1:)),';') + if (j==0) then + call add_error(stack, "Not allowed bare & in entity value") + return + elseif (checkName(str_vs(repl(i+1:i+j-1)), xds%xml_version)) then + repl_temp(i2:i2+j) = repl(i:i+j) + i = i + j + 1 + i2 = i2 + j + 1 + ! For SAX, we need to be able to represent the character: + elseif (checkRepCharEntityReference(str_vs(repl(i+1:i+j-1)), xds%xml_version)) then + !if it is ascii then + repl_temp(i2:i2) = vs_str(expand_char_entity(str_vs(repl(i+1:i+j-1)))) + i = i + j + 1 + i2 = i2 + 1 + elseif (checkCharacterEntityReference(str_vs(repl(i+1:i+j-1)), xds%xml_version)) then + ! We can't represent it. Issue an error and stop. + call add_error(stack, "Unable to digest character entity reference in entity value, sorry.") + return + else + call add_error(stack, "Invalid entity reference in entity value") + return + endif + else + repl_temp(i2) = repl(i) + i = i + 1 + i2 = i2 + 1 + endif + enddo + + deallocate(repl_new) + allocate(repl_new(i2-1)) + repl_new = repl_temp(:i2-1) + + end function expand_entity_value_alloc + +#endif +end module m_common_entity_expand diff --git a/src/xml/common/m_common_error.F90 b/src/xml/common/m_common_error.F90 new file mode 100644 index 000000000..0a1f1c1a8 --- /dev/null +++ b/src/xml/common/m_common_error.F90 @@ -0,0 +1,211 @@ +module m_common_error + +#ifndef DUMMYLIB + use fox_m_fsys_abort_flush, only: pxfabort, pxfflush + use fox_m_fsys_array_str, only: vs_str_alloc + + implicit none + private + + integer, parameter :: ERR_NULL = 0 + integer, parameter :: ERR_WARNING = 1 + integer, parameter :: ERR_ERROR = 2 + integer, parameter :: ERR_FATAL = 3 +#endif + logical, save :: errors_are_fatal = .false. + logical, save :: warnings_are_fatal = .false. + +#ifndef DUMMYLIB + type error_t + integer :: severity = ERR_NULL + integer :: error_code = 0 + character, dimension(:), pointer :: msg => null() + end type error_t + + type error_stack + type(error_t), dimension(:), pointer :: stack => null() + end type error_stack + + interface FoX_warning + module procedure FoX_warning_base + end interface + + interface FoX_error + module procedure FoX_error_base + end interface + + interface FoX_fatal + module procedure FoX_fatal_base + end interface + + public :: ERR_NULL + public :: ERR_WARNING + public :: ERR_ERROR + public :: ERR_FATAL + + public :: error_t + public :: error_stack + + public :: init_error_stack + public :: destroy_error_stack + + public :: FoX_warning + public :: FoX_error + public :: FoX_fatal + + public :: FoX_warning_base + public :: FoX_error_base + public :: FoX_fatal_base + + public :: add_error + public :: in_error + +#endif + public :: FoX_set_fatal_errors + public :: FoX_get_fatal_errors + public :: FoX_set_fatal_warnings + public :: FoX_get_fatal_warnings + +contains +#ifndef DUMMYLIB + + subroutine FoX_warning_base(msg) + ! Emit warning, but carry on. + character(len=*), intent(in) :: msg + + if (warnings_are_fatal) then + write(0,'(a)') 'FoX warning made fatal' + call FoX_fatal_base(msg) + endif + + write(0,'(a)') 'WARNING(FoX)' + write(0,'(a)') msg + call pxfflush(0) + + end subroutine FoX_warning_base + + + subroutine FoX_error_base(msg) + ! Emit error message and stop. + ! No clean up is done here, but this can + ! be overridden to include clean-up routines + character(len=*), intent(in) :: msg + + if (errors_are_fatal) then + write(0,'(a)') 'FoX error made fatal' + call FoX_fatal_base(msg) + endif + + write(0,'(a)') 'ERROR(FoX)' + write(0,'(a)') msg + call pxfflush(0) + + stop + + end subroutine FoX_error_base + + subroutine FoX_fatal_base(msg) + !Emit error message and abort with coredump. + !No clean-up occurs + + character(len=*), intent(in) :: msg + + write(0,'(a)') 'ABORT(FOX)' + write(0,'(a)') msg + call pxfflush(0) + + call pxfabort() + + end subroutine FoX_fatal_base + + + subroutine init_error_stack(stack) + type(error_stack), intent(inout) :: stack + + allocate(stack%stack(0)) + end subroutine init_error_stack + + + subroutine destroy_error_stack(stack) + type(error_stack), intent(inout) :: stack + + integer :: i + + do i = 1, size(stack%stack) + deallocate(stack%stack(i)%msg) + enddo + deallocate(stack%stack) + end subroutine destroy_error_stack + + + subroutine add_error(stack, msg, severity, error_code) + type(error_stack), intent(inout) :: stack + character(len=*), intent(in) :: msg + integer, intent(in), optional :: severity + integer, intent(in), optional :: error_code + + integer :: i, n + type(error_t), dimension(:), pointer :: temp_stack + + if (.not.associated(stack%stack)) & + call init_error_stack(stack) + + n = size(stack%stack) + + temp_stack => stack%stack + allocate(stack%stack(n+1)) + do i = 1, size(temp_stack) + stack%stack(i)%msg => temp_stack(i)%msg + stack%stack(i)%severity = temp_stack(i)%severity + stack%stack(i)%error_code = temp_stack(i)%error_code + enddo + deallocate(temp_stack) + + stack%stack(n+1)%msg => vs_str_alloc(msg) + if (present(severity)) then + stack%stack(n+1)%severity = severity + else + stack%stack(n+1)%severity = ERR_ERROR + endif + if (present(error_code)) then + stack%stack(n+1)%error_code = error_code + else + stack%stack(n+1)%error_code = -1 + endif + + end subroutine add_error + + + function in_error(stack) result(p) + type(error_stack), intent(in) :: stack + logical :: p + + if (associated(stack%stack)) then + p = (size(stack%stack) > 0) + else + p = .false. + endif + end function in_error + +#endif + subroutine FoX_set_fatal_errors(newvalue) + logical, intent(in) :: newvalue + errors_are_fatal = newvalue + end subroutine FoX_set_fatal_errors + + function FoX_get_fatal_errors() + logical :: FoX_get_fatal_errors + FoX_get_fatal_errors = errors_are_fatal + end function FoX_get_fatal_errors + + subroutine FoX_set_fatal_warnings(newvalue) + logical, intent(in) :: newvalue + warnings_are_fatal = newvalue + end subroutine FoX_set_fatal_warnings + + function FoX_get_fatal_warnings() + logical :: FoX_get_fatal_warnings + FoX_get_fatal_warnings = warnings_are_fatal + end function FoX_get_fatal_warnings + +end module m_common_error diff --git a/src/xml/common/m_common_io.F90 b/src/xml/common/m_common_io.F90 new file mode 100644 index 000000000..a15a25dbc --- /dev/null +++ b/src/xml/common/m_common_io.F90 @@ -0,0 +1,102 @@ +module m_common_io + +#ifndef DUMMYLIB + use m_common_error, only : FoX_error + + implicit none + private + + ! Basic I/O tools + + integer, save :: io_eor + integer, save :: io_eof + integer, save :: io_err + + public :: io_eor + public :: io_eof + public :: io_err + public :: get_unit + public :: setup_io + +contains + + subroutine setup_io() + call find_eor_eof(io_eor, io_eof) + end subroutine setup_io + + + subroutine get_unit(lun,iostat) + ! Get an available Fortran unit number + integer, intent(out) :: lun + integer, intent(out) :: iostat + + integer :: i + logical :: unit_used + + do i = 10, 99 + lun = i + inquire(unit=lun,opened=unit_used) + if (.not. unit_used) then + iostat = 0 + return + endif + enddo + iostat = -1 + lun = -1 + end subroutine get_unit + + + subroutine find_eor_eof(io_eor,io_eof) + ! Determines the values of the iostat values for End of File and + ! End of Record (in non-advancing I/O) +#ifdef __NAG__ + use f90_iostat +#endif + integer, intent(out) :: io_eor + integer, intent(out) :: io_eof + +#ifdef __NAG__ + io_eor = ioerr_eor + io_eof = ioerr_eof +#else + integer :: lun, iostat + character(len=1) :: c + + call get_unit(lun,iostat) + + if (iostat /= 0) call FoX_error("Out of unit numbers") + + open(unit=lun,status="scratch",form="formatted", & + action="readwrite",position="rewind",iostat=iostat) + if (iostat /= 0) call FoX_error("Cannot open test file") + + write(unit=lun,fmt=*) "a" + write(unit=lun,fmt=*) "b" + + rewind(unit=lun) + + io_eor = 0 + do + read(unit=lun,fmt="(a1)",advance="no",iostat=io_eor) c + if (io_eor /= 0) exit + enddo + + io_eof = 0 + do + read(unit=lun,fmt=*,iostat=io_eof) + if (io_eof /= 0) exit + enddo + + close(unit=lun,status="delete") +#endif + + ! Invent an io_err ... + io_err = 1 + do + if (io_err/=0.and.io_err/=io_eor.and.io_err/=io_eof) exit + io_err = io_err + 1 + end do + end subroutine find_eor_eof + +#endif +end module m_common_io diff --git a/src/xml/common/m_common_namecheck.F90 b/src/xml/common/m_common_namecheck.F90 new file mode 100644 index 000000000..a796732e7 --- /dev/null +++ b/src/xml/common/m_common_namecheck.F90 @@ -0,0 +1,501 @@ +module m_common_namecheck + +#ifndef DUMMYLIB + ! These are basically a collection of what would be regular + ! expressions in a more sensible language. + ! The only external dependency should be knowing how these + ! regular expressions may differ between XML-1.0 and 1.1 (which + ! is only in the areas of + ! 1: allowing character entity references to control characters + ! 2: More characters allowed in Names (but this only affects + ! unicode-aware programs, so is only skeleton here) + + use fox_m_fsys_format, only: str_to_int_10, str_to_int_16, operator(//) + use fox_m_fsys_string, only: toLower + use m_common_charset, only: isLegalCharRef, isNCNameChar, & + isInitialNCNameChar, isInitialNameChar, isNameChar, isRepCharRef + + implicit none + private + + character(len=*), parameter :: lowerCase = "abcdefghijklmnopqrstuvwxyz" + character(len=*), parameter :: upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + character(len=*), parameter :: letters = lowerCase//upperCase + character(len=*), parameter :: digits = "0123456789" + character(len=*), parameter :: hexdigits = "0123456789abcdefABCDEF" + character(len=*), parameter :: NameChars = lowerCase//upperCase//digits//".-_:" + + public :: checkName + public :: checkNames + public :: checkQName + public :: checkQNames + public :: checkNmtoken + public :: checkNmtokens + public :: checkNCName + public :: checkNCNames + public :: checkEncName + public :: checkPITarget + public :: checkPublicId + public :: checkPEDef + public :: checkPseudoAttValue + public :: checkAttValue + public :: checkCharacterEntityReference + public :: checkRepCharEntityReference + public :: likeCharacterEntityReference + + public :: prefixOfQName + public :: localpartOfQName + +contains + + pure function checkEncName(name) result(good) + ![81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* + character(len=*), intent(in) :: name + logical :: good + + integer :: n + + n = len(name) + good = (n > 0) + if (good) good = (scan(name(1:1), letters) /= 0) + if (good .and. n > 1) & + good = (verify(name(2:), letters//digits//'.-_') == 0) + end function checkEncName + + + function checkPITarget(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for a NAME + ! Is not fully compliant; ignores UTF issues. + + good = checkName(name, xv) & + .and.toLower(name)/="xml" + + end function checkPITarget + + + pure function checkName(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for a NAME + ! Is not fully compliant; ignores UTF issues. + + good = (len(name) > 0) + if (.not.good) return + if (good) good = isInitialNameChar(name(1:1), xv) + if (.not.good.or.len(name)==1) return + good = isNameChar(name(2:), xv) + + end function checkName + + pure function checkNames(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the production for NAMES + + integer :: i, j + + good = (len(name) > 0) + if (.not.good) return + i = verify(name, " ") + if (i==0) then + good = .false. + return + endif + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + do + good = checkName(name(i:j), xv) + if (.not.good) return + i = j + 1 + j = verify(name(i:), " ") + if (j==0) exit + i = i + j - 1 + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + enddo + + end function checkNames + + + pure function checkQName(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for a NAME + ! Is not fully compliant; ignores UTF issues. + + integer :: n + + n = index(name, ':') + if (n == 0) then + good = checkNCName(name, xv) + else + good = (checkNCName(name(:n-1), xv) .and. checkNCName(name(n+1:), xv)) + endif + end function checkQName + + + pure function checkQNames(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the production for NAMES + + integer :: i, j + + good = (len(name) > 0) + if (.not.good) return + i = verify(name, " ") + if (i==0) then + good = .false. + return + endif + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + do + good = checkQName(name(i:j), xv) + if (.not.good) return + i = j + 1 + j = verify(name(i:), " ") + if (j==0) exit + i = i + j - 1 + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + enddo + + end function checkQNames + + + pure function checkNCName(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for an NCNAME + ! Is not fully compliant; ignores UTF issues. + + good = (len(name)/=0) + if (.not.good) return + good = isInitialNCNameChar(name(1:1), xv) + if (.not.good.or.len(name)==1) return + good = isNCNameChar(name(2:), xv) + + end function checkNCName + + + pure function checkNCNames(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the production for NAMES + + integer :: i, j + + good = (len(name) > 0) + if (.not.good) return + i = verify(name, " ") + if (i==0) then + good = .false. + return + endif + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + do + good = checkNCName(name(i:j), xv) + if (.not.good) return + i = j + 1 + j = verify(name(i:), " ") + if (j==0) exit + i = i + j - 1 + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + enddo + + end function checkNCNames + + + pure function checkNmtoken(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for an NCNAME + ! Is not fully compliant; ignores UTF issues. + + good = isNameChar(name, xv) + + end function checkNmtoken + + + pure function checkNmtokens(name, xv) result(good) + character(len=*), intent(in) :: name + integer, intent(in) :: xv + logical :: good + ! Validates a string against the XML requirements for an NCNAME + ! Is not fully compliant; ignores UTF issues. + + integer :: i, j + + good = (len(name) > 0) + if (.not.good) return + i = verify(name, " ") + if (i==0) then + good = .false. + return + endif + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + do + good = isNameChar(name(i:j), xv) + if (.not.good) return + i = j + 1 + j = verify(name(i:), " ") + if (j==0) exit + i = i + j - 1 + j = scan(name(i:), " ") + if (j==0) then + j = len(name) + else + j = i + j - 2 + endif + enddo + + end function checkNmtokens + + + function checkPublicId(value) result(good) + character(len=*), intent(in) :: value + logical :: good + character(len=*), parameter :: PubIdChars = & + " "//achar(10)//achar(13)//lowerCase//upperCase//digits//"-'()+,./:=?;!*#@$_%" + + good = (verify(value, PubIdChars)==0) + end function checkPublicId + + + function checkPEDef(value, xv) result(p) + character(len=*), intent(in) :: value + integer, intent(in) :: xv + logical :: p + + integer :: i1, i2 + + p = .false. + if (scan(value, '%&')==0) then + p = .true. + elseif (scan(value, '"')==0) then + i1 = scan(value, '%&') + i2 = 0 + do while (i1>0) + i1 = i2 + i1 + i2 = index(value(i1+1:),';') + if (i2==0) return + i2 = i1 + i2 + if (value(i1:i1)=='&') then + if (.not.checkName(value(i1+1:i2-1), xv) .and. & + .not.checkCharacterEntityReference(value(i1+1:i2-1), xv)) return + else + if (.not.checkName(value(i1+1:i2-1), xv)) & + return + endif + i1 = scan(value(i2+1:), '%&') + enddo + p = .true. + endif + end function checkPEDef + + function checkPseudoAttValue(value, xv) result(p) + character(len=*), intent(in) :: value + integer, intent(in) :: xv + logical :: p + + integer :: i1, i2 +!fixme can we have entrefs in PIs? + p = .false. + if (scan(value, '"<&')==0) then + p = .true. + elseif (index(value, '&') > 0) then + i1 = index(value, '&') + i2 = 0 + do while (i1 > 0) + i1 = i2 + i1 + i2 = index(value(i1+1:),';') + if (i2==0) return + i2 = i1 + i2 + if (value(i1+1:i2-1) /= 'amp' .and. & + value(i1+1:i2-1) /= 'lt' .and. & + value(i1+1:i2-1) /= 'gt' .and. & + value(i1+1:i2-1) /= 'quot' .and. & + value(i1+1:i2-1) /= 'apos' .and. & + .not.checkCharacterEntityReference(value(i1+1:i2-1), xv)) & + return + i1 = index(value(i2+1:), '&') + enddo + p = .true. + endif + end function checkPseudoAttValue + + function checkAttValue(value, xv) result(p) + character(len=*), intent(in) :: value + integer, intent(in) :: xv + logical :: p + ! This function is called basically to make sure + ! that any attribute value looks like one. It will + ! not flag up out-of-range character entities, and + ! is a very weak check. Only used from xml_AddAttribute + ! when escaping is off. + integer :: i1, i2 + + p = .false. + if (scan(value, '"<&'//"'")==0) then + p = .true. + elseif (index(value, '&') > 0) then + i1 = index(value, '&') + i2 = 0 + do while (i1 > 0) + i1 = i1 + i2 + 1 + i2 = scan(value(i1+1:),';') + if (i2 == 0) return + i2 = i1 + i2 + if (.not.checkName(value(i1+1:i2-1), xv) .and. & + .not.likeCharacterEntityReference(value(i1+1:i2-1))) then + print*, value(i1+1:i2-1), " ", & + likeCharacterEntityReference(value(i1+1:i2-1)) + return + endif + i1 = index(value(i2+1:), '&') + enddo + p = .true. + endif + end function checkAttValue + + + function likeCharacterEntityReference(code) result(good) + character(len=*), intent(in) :: code + logical :: good + + good = .false. + if (len(code) > 0) then + if (code(1:1) == "#") then + if (code(2:2) == "x") then + if (len(code) > 2) then + good = (verify(code(3:), hexdigits) == 0) + endif + else + good = (verify(code(2:), digits) == 0) + endif + endif + endif + + end function likeCharacterEntityReference + + function checkCharacterEntityReference(code, xv) result(good) + character(len=*), intent(in) :: code + integer, intent(in) :: xv + logical :: good + + integer :: i + + good = .false. + if (len(code) > 0) then + if (code(1:1) == "#") then + if (code(2:2) == "x") then + if (len(code) > 2) then + good = (verify(code(3:), hexdigits) == 0) + if (good) then + i = str_to_int_16(code(3:)) + endif + endif + else + good = (verify(code(2:), digits) == 0) + if (good) then + i = str_to_int_10(code(2:)) + endif + endif + endif + endif + if (good) good = isLegalCharRef(i, xv) + + end function checkCharacterEntityReference + + function checkRepCharEntityReference(code, xv) result(good) + character(len=*), intent(in) :: code + integer, intent(in) :: xv + logical :: good + + ! Is this a reference to a character we can actually represent + ! in memory? ie without unicode, US-ASCII only. + + integer :: i + + good = .false. + if (len(code) > 0) then + if (code(1:1) == "#") then + if (code(2:2) == "x") then + if (len(code) > 2) then + good = (verify(code(3:), hexdigits) == 0) + if (good) then + i = str_to_int_16(code(3:)) + endif + endif + else + good = (verify(code(2:), digits) == 0) + if (good) then + i = str_to_int_10(code(2:)) + endif + endif + endif + endif + if (good) good = isRepCharRef(i, xv) + + end function checkRepCharEntityReference + + + pure function prefixOfQName(qname) result(prefix) + character(len=*), intent(in) :: qname + character(len=max(index(qname, ':')-1,0)) :: prefix + + prefix = qname ! automatic truncation + end function prefixOfQName + + + pure function localpartOfQname(qname) result(localpart) + character(len=*), intent(in) :: qname + character(len=max(len(qname)-index(qname,':'),0)) ::localpart + + localpart = qname(index(qname,':')+1:) + end function localpartOfQname + +#endif +end module m_common_namecheck diff --git a/src/xml/common/m_common_namespaces.F90 b/src/xml/common/m_common_namespaces.F90 new file mode 100644 index 000000000..e3bdff60c --- /dev/null +++ b/src/xml/common/m_common_namespaces.F90 @@ -0,0 +1,830 @@ +module m_common_namespaces + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: str_vs, vs_str, vs_str_alloc + + use fox_m_utils_uri, only: URI, parseURI, destroyURI, hasScheme + use m_common_attrs, only: dictionary_t, get_key, get_value, remove_key, getLength, hasKey + use m_common_attrs, only: set_nsURI, set_localName, get_prefix, add_item_to_dict + use m_common_charset, only: XML1_0, XML1_1 + use m_common_error, only: FoX_error, FoX_warning, error_stack, add_error, in_error + use m_common_namecheck, only: checkNCName + use m_common_struct, only: xml_doc_state + + implicit none + private + + character(len=*), parameter :: invalidNS = '::INVALID::' + ! an invalid URI name to indicate a namespace error. + + type URIMapping + character, dimension(:), pointer :: URI + integer :: ix ! link back to node depth + end type URIMapping + !This is a single URI, and the node depth under which + !its namespace applies. + + type prefixMapping + character, dimension(:), pointer :: prefix + type(URIMapping), dimension(:), pointer :: urilist + end type prefixMapping + !This is the mapping for a single prefix; with the + !list of namespaces which are in force at various + !depths + + type namespaceDictionary + private + type(URIMapping), dimension(:), pointer :: defaults + type(prefixMapping), dimension(:), pointer :: prefixes + end type namespaceDictionary + !This is the full namespace dictionary; defaults is + !the list of default namespaces in force; prefix a + !list of all prefixes in force. + + public :: invalidNS + + public :: initNamespaceDictionary + public :: destroyNamespaceDictionary + public :: namespaceDictionary + public :: checkNamespaces + public :: checkNamespacesWriting + public :: checkEndNamespaces + public :: getnamespaceURI + interface getnamespaceURI + module procedure getURIofDefaultNS, getURIofPrefixedNS + end interface + public :: isPrefixInForce + public :: isDefaultNSInForce + public :: getNumberOfPrefixes + public :: getPrefixByIndex + + public :: dumpnsdict !FIXME + + public :: addDefaultNS + public :: removeDefaultNS + public :: addPrefixedNS + public :: removePrefixedNS + +contains + + + subroutine initNamespaceDictionary(nsDict) + type(namespaceDictionary), intent(inout) :: nsDict + + !We need to properly initialize 0th elements + !(which are never used) in order to provide + !sensible behaviour when trying to manipulate + !an empty dictionary. + + allocate(nsDict%defaults(0:0)) + allocate(nsDict%defaults(0)%URI(0)) + !The 0th element of the defaults NS is the empty namespace + nsDict%defaults(0)%ix = -1 + + allocate(nsDict%prefixes(0:0)) + allocate(nsDict%prefixes(0)%prefix(0)) + allocate(nsDict%prefixes(0)%urilist(0:0)) + allocate(nsDict%prefixes(0)%urilist(0)%URI(len(invalidNS))) + nsDict%prefixes(0)%urilist(0)%URI = vs_str(invalidNS) + nsDict%prefixes(0)%urilist(0)%ix = -1 + + end subroutine initNamespaceDictionary + + + subroutine destroyNamespaceDictionary(nsDict) + type(namespaceDictionary), intent(inout) :: nsDict + + integer :: i, j + + do i = 0, ubound(nsDict%defaults,1) + deallocate(nsDict%defaults(i)%URI) + enddo + deallocate(nsDict%defaults) + do i = 0, ubound(nsDict%prefixes,1) + do j = 0, ubound(nsDict%prefixes(i)%urilist,1) + deallocate(nsDict%prefixes(i)%urilist(j)%URI) + enddo + deallocate(nsDict%prefixes(i)%prefix) + deallocate(nsDict%prefixes(i)%urilist) + enddo + deallocate(nsDict%prefixes) + end subroutine destroyNamespaceDictionary + + + subroutine copyURIMapping(urilist1, urilist2, l_m) + type(URIMapping), dimension(0:), intent(inout) :: urilist1 + type(URIMapping), dimension(0:), intent(inout) :: urilist2 + integer, intent(in):: l_m + integer :: i + + if (ubound(urilist1,1) < l_m .or. ubound(urilist2,1) < l_m) then + call FoX_error('Internal error in m_sax_namespaces:copyURIMapping') + endif + ! Now copy all defaults across (or rather - add pointers to them) + do i = 0, l_m + urilist2(i)%ix = urilist1(i)%ix + urilist2(i)%URI => urilist1(i)%URI + enddo + + end subroutine copyURIMapping + + + subroutine addDefaultNS(nsDict, uri, ix, es) + type(namespaceDictionary), intent(inout) :: nsDict + character(len=*), intent(in) :: uri + integer, intent(in) :: ix + type(error_stack), intent(inout), optional :: es + + type(URIMapping), dimension(:), allocatable :: tempMap + integer :: l_m, l_s + + if (uri=="http://www.w3.org/XML/1998/namespace") then + if (present(es)) then + call add_error(es, "Attempt to assign incorrect URI to prefix 'xml'") + else + call FoX_error("Attempt to assign incorrect URI to prefix 'xml'") + endif + elseif (uri=="http://www.w3.org/2000/xmlns/") then + if (present(es)) then + call add_error(es, "Attempt to assign prefix to xmlns namespace") + else + call FoX_error("Attempt to assign prefix to xmlns namespace") + endif + endif + + ! FIXME check URI is valid ... + + l_m = ubound(nsDict%defaults,1) + allocate(tempMap(0:l_m)) + ! Now copy all defaults across ... + call copyURIMapping(nsDict%defaults, tempMap, l_m) + deallocate(nsDict%defaults) + l_m = l_m + 1 + allocate(nsDict%defaults(0:l_m)) + !Now copy everything back ... + call copyURIMapping(tempMap, nsDict%defaults, l_m-1) + deallocate(tempMap) + ! And finally, add the new default NS + nsDict%defaults(l_m)%ix = ix + l_s = len(uri) + allocate(nsDict%defaults(l_m)%URI(l_s)) + nsDict%defaults(l_m)%URI = vs_str(uri) + + end subroutine addDefaultNS + + + subroutine addPrefixedURI(nsPrefix, uri, ix) + type(PrefixMapping), intent(inout) :: nsPrefix + character, dimension(:), intent(in) :: uri + integer, intent(in) :: ix + + type(URIMapping), dimension(:), allocatable :: tempMap + integer :: l_m, l_s + + l_m = ubound(nsPrefix%urilist,1) + allocate(tempMap(0:l_m)) + ! Now copy all across ... + call copyURIMapping(nsPrefix%urilist, tempMap, l_m) + deallocate(nsPrefix%urilist) + l_m = l_m + 1 + allocate(nsPrefix%urilist(0:l_m)) + !Now copy everything back ... + call copyURIMapping(tempMap, nsPrefix%urilist, l_m-1) + deallocate(tempMap) + ! And finally, add the new default NS + nsPrefix%urilist(l_m)%ix = ix + l_s = size(uri) + allocate(nsPrefix%urilist(l_m)%URI(l_s)) + nsPrefix%urilist(l_m)%URI = uri + + end subroutine addPrefixedURI + + subroutine removeDefaultNS(nsDict) + type(namespaceDictionary), intent(inout) :: nsDict + + type(URIMapping), dimension(:), allocatable :: tempMap + integer :: l_m + + l_m = ubound(nsDict%defaults,1) + allocate(tempMap(0:l_m-1)) + ! Now copy all defaults across ... + call copyURIMapping(nsDict%defaults, tempMap, l_m-1) + !And remove tail-end charlie + deallocate(nsDict%defaults(l_m)%URI) + deallocate(nsDict%defaults) + l_m = l_m - 1 + allocate(nsDict%defaults(0:l_m)) + !Now copy everything back ... + call copyURIMapping(tempMap, nsDict%defaults, l_m) + deallocate(tempMap) + + end subroutine removeDefaultNS + + subroutine removePrefixedURI(nsPrefix) + type(PrefixMapping), intent(inout) :: nsPrefix + + type(URIMapping), dimension(:), allocatable :: tempMap + integer :: l_m + + l_m = ubound(nsPrefix%urilist,1) + allocate(tempMap(0:l_m-1)) + ! Now copy all defaults across ... + call copyURIMapping(nsPrefix%urilist, tempMap, l_m-1) + !And remove tail-end charlie + deallocate(nsPrefix%urilist(l_m)%URI) + deallocate(nsPrefix%urilist) + l_m = l_m - 1 + allocate(nsPrefix%urilist(0:l_m)) + !Now copy everything back ... + call copyURIMapping(tempMap, nsPrefix%urilist, l_m) + deallocate(tempMap) + + end subroutine removePrefixedURI + + subroutine addPrefixedNS(nsDict, prefix, URI, ix, xds, xml, es) + type(namespaceDictionary), intent(inout) :: nsDict + character(len=*), intent(in) :: prefix + character(len=*), intent(in) :: uri + integer, intent(in) :: ix + type(xml_doc_state), intent(in) :: xds + logical, intent(in), optional :: xml + type(error_stack), intent(inout), optional :: es + + integer :: l_p, p_i, i + logical :: xml_ + + if (present(xml)) then + xml_ = xml + else + xml_ = .false. + endif + + if (prefix=='xml' .and. & + URI/='http://www.w3.org/XML/1998/namespace') then + if (present(es)) then + call add_error(es, "Attempt to assign incorrect URI to prefix 'xml'") + else + call FoX_error("Attempt to assign incorrect URI to prefix 'xml'") + endif + elseif (prefix/='xml' .and. & + URI=='http://www.w3.org/XML/1998/namespace') then + if (present(es)) then + call add_error(es, "Attempt to assign incorrect prefix to XML namespace") + else + call FoX_error("Attempt to assign incorrect prefix to XML namespace") + endif + elseif (prefix == 'xmlns') then + if (present(es)) then + call add_error(es, "Attempt to declare 'xmlns' prefix") + else + call FoX_error("Attempt to declare 'xmlns' prefix") + endif + elseif (URI=="http://www.w3.org/2000/xmlns/") then + if (present(es)) then + call add_error(es, "Attempt to assign prefix to xmlns namespace") + else + call FoX_error("Attempt to assign prefix to xmlns namespace") + endif + elseif (len(prefix) > 2) then + if ((verify(prefix(1:1), 'xX') == 0) & + .and. (verify(prefix(2:2), 'mM') == 0) & + .and. (verify(prefix(3:3), 'lL') == 0)) then + if (.not.xml_) then + ! FIXME need working warning infrastructure + !if (present(es)) then + ! call add_error(es, "Attempt to declare reserved prefix: "//prefix) + !else + call FoX_warning("Attempt to declare reserved prefix: "//prefix) + !endif + endif + endif + endif + + if (.not.checkNCName(prefix, xds%xml_version)) & + call FoX_error("Attempt to declare invalid prefix: "//prefix) + + ! FIXME check URI is valid + + l_p = ubound(nsDict%prefixes, 1) + + p_i = 0 + do i = 1, l_p + if (str_vs(nsDict%prefixes(i)%prefix) == prefix) then + p_i = i + exit + endif + enddo + + if (p_i == 0) then + call addPrefix(nsDict, vs_str(prefix)) + p_i = l_p + 1 + endif + + call addPrefixedURI(nsDict%prefixes(p_i), vs_str(URI), ix) + + end subroutine addPrefixedNS + + subroutine removePrefixedNS(nsDict, prefix) + type(namespaceDictionary), intent(inout) :: nsDict + character, dimension(:), intent(in) :: prefix + integer :: l_p, p_i, i + l_p = ubound(nsDict%prefixes, 1) + + p_i = 0 + do i = 1, l_p + if (str_vs(nsDict%prefixes(i)%prefix) == str_vs(prefix)) then + p_i = i + exit + endif + enddo + + if (p_i /= 0) then + call removePrefixedURI(nsDict%prefixes(p_i)) + if (ubound(nsDict%prefixes(p_i)%urilist,1) == 0) then + !that was the last mapping for that prefix + call removePrefix(nsDict, p_i) + endif + else + call FoX_error('Internal error in m_sax_namespaces:removePrefixedNS') + endif + + end subroutine removePrefixedNS + + subroutine addPrefix(nsDict, prefix) + type(namespaceDictionary), intent(inout) :: nsDict + character, dimension(:), intent(in) :: prefix + integer :: l_p + + type(prefixMapping), dimension(:), pointer :: tempPrefixMap + + integer :: i + + !Add a new prefix to the namespace dictionary. + !Unfortunately this involves copying the entire + !prefixes dictionary to a temporary structure, then + !reallocating the prefixes dictionary to be one + !longer, then copying everything back: + + l_p = ubound(nsDict%prefixes, 1) + allocate(tempPrefixMap(0:l_p)) + + !for each current prefix, append everything to temporary structure + do i = 0, l_p + tempPrefixMap(i)%prefix => nsDict%prefixes(i)%prefix + tempPrefixMap(i)%urilist => nsDict%prefixes(i)%urilist + enddo + deallocate(nsDict%prefixes) + !extend prefix dictionary by one ... + l_p = l_p + 1 + allocate(nsDict%prefixes(0:l_p)) + !and copy back ... + do i = 0, l_p-1 + nsDict%prefixes(i)%prefix => tempPrefixMap(i)%prefix + nsDict%prefixes(i)%urilist => tempPrefixMap(i)%urilist + enddo + deallocate(tempPrefixMap) + + allocate(nsDict%prefixes(l_p)%prefix(size(prefix))) + nsDict%prefixes(l_p)%prefix = prefix + allocate(nsDict%prefixes(l_p)%urilist(0:0)) + allocate(nsDict%prefixes(l_p)%urilist(0)%URI(len(invalidNS))) + nsDict%prefixes(l_p)%urilist(0)%URI = vs_str(invalidNS) + nsDict%prefixes(l_p)%urilist(0)%ix = -1 + + end subroutine addPrefix + + subroutine removePrefix(nsDict, i_p) + type(namespaceDictionary), intent(inout) :: nsDict + integer, intent(in) :: i_p + integer :: l_p + + type(prefixMapping), dimension(:), pointer :: tempPrefixMap + + integer :: i + + !Remove a prefix from the namespace dictionary. + !Unfortunately this involves copying the entire + !prefixes dictionary to a temporary structure, then + !reallocating the prefixes dictionary to be one + !shorter, then copying everything back: + + l_p = ubound(nsDict%prefixes, 1) + allocate(tempPrefixMap(0:l_p-1)) + + !for each current prefix, append everything to temporary structure + do i = 0, i_p-1 + tempPrefixMap(i)%prefix => nsDict%prefixes(i)%prefix + tempPrefixMap(i)%urilist => nsDict%prefixes(i)%urilist + enddo + deallocate(nsDict%prefixes(i_p)%urilist(0)%URI) + deallocate(nsDict%prefixes(i_p)%urilist) + deallocate(nsDict%prefixes(i_p)%prefix) + !this subroutine will only get called if the urilist is already + !empty, so no need to deallocate it. + do i = i_p+1, l_p + tempPrefixMap(i-1)%prefix => nsDict%prefixes(i)%prefix + tempPrefixMap(i-1)%urilist => nsDict%prefixes(i)%urilist + enddo + deallocate(nsDict%prefixes) + !shorten prefix dictionary by one ... + l_p = l_p - 1 + allocate(nsDict%prefixes(0:l_p)) + !and copy back ... + do i = 0, l_p + nsDict%prefixes(i)%prefix => tempPrefixMap(i)%prefix + nsDict%prefixes(i)%urilist => tempPrefixMap(i)%urilist + enddo + deallocate(tempPrefixMap) + + end subroutine removePrefix + + + subroutine checkNamespaces(atts, nsDict, ix, xds, namespace_prefixes, xmlns_uris, es, & + partial, start_prefix_handler, end_prefix_handler) + type(dictionary_t), intent(inout) :: atts + type(namespaceDictionary), intent(inout) :: nsDict + integer, intent(in) :: ix ! depth of nesting of current element. + type(xml_doc_state), intent(in) :: xds + logical, intent(in) :: namespace_prefixes, xmlns_uris + type(error_stack), intent(inout) :: es + logical, intent(in) :: partial ! if so, don't try and resolve anything except xml & xmlns + optional :: start_prefix_handler, end_prefix_handler + + interface + subroutine start_prefix_handler(namespaceURI, prefix) + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: prefix + end subroutine start_prefix_handler + subroutine end_prefix_handler(prefix) + character(len=*), intent(in) :: prefix + end subroutine end_prefix_handler + end interface + + character(len=6) :: xmlns + character, dimension(:), pointer :: QName, URIstring + integer :: i, n + type(URI), pointer :: URIref + !Check for namespaces; *and* remove xmlns references from + !the attributes dictionary. + + ! we can't do a simple loop across the attributes, + ! because we need to remove some as we go along ... + i = 1 + do while (i <= getLength(atts)) + xmlns = get_key(atts, i) + if (xmlns == 'xmlns ') then + !Default namespace is being set + URIstring => vs_str_alloc(get_value(atts, i)) + if (str_vs(URIstring)=="") then + ! Empty nsURI on default namespace has same effect in 1.0 and 1.1 + if (present(end_prefix_handler)) & + call end_prefix_handler("") + call addDefaultNS(nsDict, invalidNS, ix) + deallocate(URIstring) + else + URIref => parseURI(str_vs(URIstring)) + if (.not.associated(URIref)) then + call add_error(es, "Invalid URI: "//str_vs(URIstring)) + deallocate(URIstring) + return + elseif (.not.hasScheme(URIref)) then + call add_error(es, "Relative namespace in URI deprecated: "//str_vs(URIstring)) + deallocate(URIstring) + call destroyURI(URIref) + return + endif + call destroyURI(URIref) + if (present(start_prefix_handler)) & + call start_prefix_handler(str_vs(URIstring), "") + call addDefaultNS(nsDict, str_vs(URIstring), ix) + deallocate(URIstring) + endif + if (namespace_prefixes) then + i = i + 1 + else + call remove_key(atts, i) + endif + elseif (xmlns == 'xmlns:') then + !Prefixed namespace is being set + QName => vs_str_alloc(get_key(atts, i)) + URIstring => vs_str_alloc(get_value(atts, i)) + if (str_vs(URIstring)=="") then + if (xds%xml_version==XML1_0) then + call add_error(es, "Empty nsURI is invalid in XML 1.0") + deallocate(URIstring) + deallocate(QName) + return + elseif (xds%xml_version==XML1_1) then + call addPrefixedNS(nsDict, str_vs(QName(7:)), invalidNS, ix, xds, es=es) + if (in_error(es)) then + deallocate(URIstring) + deallocate(QName) + return + elseif (present(end_prefix_handler)) then + call end_prefix_handler(str_vs(QName(7:))) + endif + deallocate(URIstring) + deallocate(QName) + endif + else + URIref => parseURI(str_vs(URIstring)) + if (.not.associated(URIref)) then + call add_error(es, "Invalid URI: "//str_vs(URIstring)) + deallocate(URIstring) + deallocate(QName) + return + elseif (.not.hasScheme(URIref)) then + call add_error(es, "Relative namespace in URI deprecated: "//str_vs(URIstring)) + deallocate(URIstring) + deallocate(QName) + call destroyURI(URIref) + return + endif + call destroyURI(URIref) + call addPrefixedNS(nsDict, str_vs(QName(7:)), str_vs(URIstring), ix, xds, es=es) + if (in_error(es)) then + deallocate(URIstring) + deallocate(QName) + return + elseif (present(start_prefix_handler)) then + call start_prefix_handler(str_vs(URIstring), str_vs(QName(7:))) + endif + deallocate(URIstring) + deallocate(QName) + endif + if (namespace_prefixes) then + i = i + 1 + else + call remove_key(atts, i) + endif + else + ! we only increment if we haven't removed a key + i = i + 1 + endif + enddo + + ! having done that, now resolve all attribute namespaces: + do i = 1, getLength(atts) + QName => vs_str_alloc(get_key(atts,i)) + n = index(str_vs(QName), ":") + if (n > 0) then + if (str_vs(QName(1:n-1))=="xmlns") then + ! FIXME but this can be controlled by SAX configuration xmlns-uris + if (xmlns_uris) then + call set_nsURI(atts, i, "http://www.w3.org/2000/xmlns/") + else + call set_nsURI(atts, i, "") + endif + else + if (str_vs(QName(1:n-1))=="xml") then + call set_nsURI(atts, i, "http://www.w3.org/XML/1998/namespace") + elseif (getnamespaceURI(nsDict, str_vs(QName(1:n-1)))==invalidNS) then + ! Sometimes we don't want to worry about unbound prefixes, + ! eg if we are in the middle of parsing an entity. + if (.not.partial) then + call add_error(es, "Unbound namespace prefix") + deallocate(QName) + return + else + call set_nsURI(atts, i, "") + endif + else + call set_nsURI(atts, i, getnamespaceURI(nsDict, str_vs(QName(1:n-1)))) + endif + endif + else + if (xmlns_uris.and.str_vs(QName)=="xmlns") then + call set_nsURI(atts, i, "http://www.w3.org/2000/xmlns/") + else + call set_nsURI(atts, i, "") ! no such thing as a default namespace on attributes + endif + endif + ! Check for duplicates + if (hasKey(atts, getnamespaceURI(nsDict, str_vs(QName(1:n-1))), str_vs(QName(n+1:)))) then + call add_error(es, "Duplicate attribute names after namespace processing") + deallocate(QName) + return + endif + call set_localName(atts, i, QName(n+1:)) + deallocate(QName) + enddo + + end subroutine checkNamespaces + + + subroutine checkNamespacesWriting(atts, nsdict, ix) + type(dictionary_t), intent(inout) :: atts + type(namespaceDictionary), intent(inout) :: nsDict + integer, intent(in) :: ix + ! Read through a list of attributes, check with currently + ! active namespaces & add any necessary declarations + + integer :: i, i_p, l_d, l_ps, n + + n = getLength(atts) ! we need the length before we fiddle with it + + !Does the default NS need added? + l_d = ubound(nsDict%defaults,1) + if (nsDict%defaults(l_d)%ix == ix) then + !It's not been registered yet: + call add_item_to_dict(atts, "xmlns", & + str_vs(nsDict%defaults(l_d)%URI), type="CDATA") + endif + + !next, add any overdue prefixed NS's in the same way: + ! there should only ever be one. More would be an error, + ! but the check should have been done earlier. + do i_p = 0, ubound(nsDict%prefixes, 1) + l_ps = ubound(nsDict%prefixes(i_p)%urilist,1) + if (nsDict%prefixes(i_p)%urilist(l_ps)%ix == ix) then + call add_item_to_dict(atts, & + "xmlns:"//str_vs(nsDict%prefixes(i_p)%prefix), & + str_vs(nsDict%prefixes(i_p)%urilist(l_ps)%URI), & + type="CDATA") + endif + enddo + + + !Finally, we may have some we've added for attribute QNames + ! have to get those too: + do i = 1, getLength(atts) + ! get prefix, and identify the relevant NS mapping + i_p = getPrefixIndex(nsDict, get_prefix(atts, i)) + l_ps = ubound(nsDict%prefixes(i_p)%urilist,1) + !If the index is greater than what it should be: + if (nsDict%prefixes(i_p)%urilist(l_ps)%ix > ix) then + !we only just added this, so we need to declare it + call add_item_to_dict(atts, "xmlns:"//get_prefix(atts, i), & + str_vs(nsDict%prefixes(i_p)%urilist(l_ps)%URI), & + type="CDATA") + !Reset the index to the right value: + nsDict%prefixes(i_p)%urilist(l_ps)%ix = ix + endif + enddo + + end subroutine checkNamespacesWriting + + + subroutine checkEndNamespaces(nsDict, ix, end_prefix_handler) + type(namespaceDictionary), intent(inout) :: nsDict + integer, intent(in) :: ix + + optional :: end_prefix_handler + + interface + subroutine end_prefix_handler(prefix) + character(len=*), intent(in) :: prefix + end subroutine end_prefix_handler + end interface + + integer :: l_d, l_p, l_ps, i + character, pointer :: prefix(:) + + !It will only ever be the final elements in the list which + ! might have expired. + + l_d = ubound(nsDict%defaults,1) + do while (nsDict%defaults(l_d)%ix == ix) + if (present(end_prefix_handler)) & + call end_prefix_handler("") + call removeDefaultNS(nsDict) + l_d = ubound(nsDict%defaults,1) + enddo + + l_p = ubound(nsDict%prefixes, 1) + i = 1 + do while (i <= l_p) + l_ps = ubound(nsDict%prefixes(l_p)%urilist,1) + if (nsDict%prefixes(i)%urilist(l_ps)%ix == ix) then + if (present(end_prefix_handler)) & + call end_prefix_handler(str_vs(nsDict%prefixes(i)%prefix)) + ! We have to assign this pointer explicitly, otherwise the next call + ! aliases its arguments illegally. + prefix => nsDict%prefixes(i)%prefix + call removePrefixedNS(nsDict, prefix) + if (l_p > ubound(nsDict%prefixes, 1)) then + ! we just removed the last reference to that prefix, + ! so our list of prefixes has shrunk - update the running total. + ! and go to the next prefix, which is at the same index + l_p = l_p - 1 + cycle + endif + endif + i = i + 1 + enddo + + end subroutine checkEndNamespaces + + + subroutine dumpnsdict(nsdict) + type(namespaceDictionary), intent(in) :: nsdict + integer :: i, j + write(*,'(a)')'* default namespaces *' + + do i = 1, ubound(nsdict%defaults, 1) + write(*,'(i0,a)') nsdict%defaults(i)%ix, str_vs(nsdict%defaults(i)%URI) + enddo + write(*,'(a)') '* Prefixed namespaces *' + do i = 1, ubound(nsdict%prefixes, 1) + write(*,'(2a)') '* prefix: ', str_vs(nsdict%prefixes(i)%prefix) + do j = 1, ubound(nsdict%prefixes(i)%urilist, 1) + write(*,'(i0,a)') nsdict%prefixes(i)%urilist(j)%ix, str_vs(nsdict%prefixes(i)%urilist(j)%URI) + enddo + enddo + + end subroutine dumpnsdict + + + pure function getURIofDefaultNS(nsDict) result(uri) + type(namespaceDictionary), intent(in) :: nsDict + character(len=size(nsDict%defaults(ubound(nsDict%defaults,1))%URI)) :: URI + + integer :: l_d + l_d = ubound(nsDict%defaults,1) + uri = str_vs(nsDict%defaults(l_d)%URI) + end function getURIofDefaultNS + + + pure function isPrefixInForce(nsDict, prefix) result(force) + type(namespaceDictionary), intent(in) :: nsDict + character(len=*), intent(in) :: prefix + logical :: force + integer :: i, l_s + + force = .false. + do i = 1, ubound(nsDict%prefixes, 1) + if (str_vs(nsDict%prefixes(i)%prefix) == prefix) then + l_s = ubound(nsDict%prefixes(i)%urilist, 1) + force = (size(nsdict%prefixes(i)%urilist(l_s)%URI) > 0) + exit + endif + enddo + + end function isPrefixInForce + + + pure function isDefaultNSInForce(nsDict) result(force) + type(namespaceDictionary), intent(in) :: nsDict + logical :: force + integer :: l_s + + force = .false. + l_s = ubound(nsDict%defaults, 1) + if (l_s > 0) & + force = (size(nsdict%defaults(l_s)%URI) > 0) + + end function isDefaultNSInForce + + + pure function getPrefixIndex(nsDict, prefix) result(p) + type(namespaceDictionary), intent(in) :: nsDict + character(len=*), intent(in) :: prefix + integer :: p + + integer :: i + p = 0 + do i = 1, ubound(nsDict%prefixes, 1) + if (str_vs(nsDict%prefixes(i)%prefix) == prefix) then + p = i + exit + endif + enddo + end function getPrefixIndex + + + function getNumberOfPrefixes(nsDict) result(n) + type(namespaceDictionary), intent(in) :: nsDict + integer :: n + n = ubound(nsDict%prefixes, 1) + end function getNumberOfPrefixes + + + function getPrefixByIndex(nsDict, i) result(c) + type(namespaceDictionary), intent(in) :: nsDict + integer, intent(in) :: i + character(len=size(nsDict%prefixes(i)%prefix)) :: c + + c = str_vs(nsDict%prefixes(i)%prefix) + end function getPrefixByIndex + + + pure function getURIofPrefixedNS(nsDict, prefix) result(uri) + type(namespaceDictionary), intent(in) :: nsDict + character(len=*), intent(in) :: prefix + character(len=size( & + nsDict%prefixes( & + getPrefixIndex(nsDict,prefix) & + ) & + %urilist( & + ubound(nsDict%prefixes(getPrefixIndex(nsDict,prefix))%urilist, 1) & + ) & + %uri)) :: URI + integer :: p_i, l_m + p_i = getPrefixIndex(nsDict, prefix) + l_m = ubound(nsDict%prefixes(p_i)%urilist, 1) + uri = str_vs(nsDict%prefixes(p_i)%urilist(l_m)%URI) + + end function getURIofPrefixedNS + +#endif +end module m_common_namespaces diff --git a/src/xml/common/m_common_notations.F90 b/src/xml/common/m_common_notations.F90 new file mode 100644 index 000000000..ee404da35 --- /dev/null +++ b/src/xml/common/m_common_notations.F90 @@ -0,0 +1,120 @@ +module m_common_notations + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: vs_str, str_vs + use m_common_error, only: FoX_error + + implicit none + private + + type notation + character(len=1), dimension(:), pointer :: name + character(len=1), dimension(:), pointer :: systemID + character(len=1), dimension(:), pointer :: publicId + end type notation + + type notation_list + type(notation), dimension(:), pointer :: list + end type notation_list + + public :: notation + public :: notation_list + public :: init_notation_list + public :: destroy_notation_list + public :: add_notation + public :: notation_exists + +contains + + subroutine init_notation_list(nlist) +! It is not clear how we should specify the +! intent of this argument - different +! compilers seem to have different semantics + type(notation_list), intent(inout) :: nlist + + allocate(nlist%list(0:0)) + allocate(nlist%list(0)%name(0)) + allocate(nlist%list(0)%systemId(0)) + allocate(nlist%list(0)%publicId(0)) + + end subroutine init_notation_list + + + subroutine destroy_notation_list(nlist) + type(notation_list), intent(inout) :: nlist + + integer :: i + + do i = 0, ubound(nlist%list, 1) + deallocate(nlist%list(i)%name) + deallocate(nlist%list(i)%systemId) + deallocate(nlist%list(i)%publicId) + enddo + deallocate(nlist%list) + end subroutine destroy_notation_list + + + subroutine add_notation(nlist, name, systemId, publicId) + type(notation_list), intent(inout) :: nlist + character(len=*), intent(in) :: name + character(len=*), intent(in), optional :: systemId + character(len=*), intent(in), optional :: publicId + + integer :: i + type(notation), dimension(:), pointer :: temp + ! pointer not allocatable to avoid bug on Lahey + + if (.not.present(systemId) .and. .not.present(publicId)) & + call FoX_error("Neither System nor Public Id specified for notation: "//name) + + allocate(temp(0:ubound(nlist%list,1))) + do i = 0, ubound(nlist%list, 1) + temp(i)%name => nlist%list(i)%name + temp(i)%systemId => nlist%list(i)%systemId + temp(i)%publicId => nlist%list(i)%publicId + enddo + + deallocate(nlist%list) + allocate(nlist%list(0:ubound(temp, 1)+1)) + do i = 0, ubound(temp, 1) + nlist%list(i)%name => temp(i)%name + nlist%list(i)%systemId => temp(i)%systemId + nlist%list(i)%publicId => temp(i)%publicId + enddo + deallocate(temp) + + allocate(nlist%list(i)%name(len(name))) + nlist%list(i)%name = vs_str(name) + if (present(systemId)) then + allocate(nlist%list(i)%systemId(len(systemId))) + nlist%list(i)%systemId = vs_str(systemId) + else + allocate(nlist%list(i)%systemId(0)) + endif + if (present(publicId)) then + allocate(nlist%list(i)%publicId(len(publicId))) + nlist%list(i)%publicId = vs_str(publicId) + else + allocate(nlist%list(i)%publicId(0)) + endif + end subroutine add_notation + + + function notation_exists(nlist, name) result(p) + type(notation_list), intent(in) :: nlist + character(len=*), intent(in) :: name + logical :: p + + integer :: i + + p = .false. + do i = 1, ubound(nlist%list, 1) + if (str_vs(nlist%list(i)%name) == name) then + p = .true. + exit + endif + enddo + end function notation_exists + +#endif +end module m_common_notations diff --git a/src/xml/common/m_common_struct.F90 b/src/xml/common/m_common_struct.F90 new file mode 100644 index 000000000..ec92b5291 --- /dev/null +++ b/src/xml/common/m_common_struct.F90 @@ -0,0 +1,121 @@ +module m_common_struct + +#ifndef DUMMYLIB + ! Common parts of an XML document. Shared by both SAX & WXML. + + use FoX_utils, only: URI + + use m_common_charset, only: XML1_0 + use m_common_entities, only: entity_list, init_entity_list, destroy_entity_list, & + add_internal_entity, add_external_entity + use m_common_element, only: element_list, init_element_list, destroy_element_list + use m_common_notations, only: notation_list, init_notation_list, destroy_notation_list + + implicit none + private + + type xml_doc_state + logical :: building = .false. ! Are we in the middle of building this doc? + integer :: xml_version = XML1_0 + logical :: standalone_declared = .false. + logical :: standalone = .false. + type(entity_list) :: entityList + type(entity_list) :: PEList + type(notation_list) :: nList + type(element_list) :: element_list + logical :: warning = .false. ! Do we care about warnings? + logical :: valid = .true. ! Do we care about validity? + logical :: liveNodeLists = .true. ! Do we want live nodelists? + character, pointer :: encoding(:) => null() + character, pointer :: inputEncoding(:) => null() + character, pointer :: documentURI(:) => null() + character, pointer :: intSubset(:) => null() + end type xml_doc_state + + public :: xml_doc_state + + public :: init_xml_doc_state + public :: destroy_xml_doc_state + + public :: register_internal_PE + public :: register_external_PE + public :: register_internal_GE + public :: register_external_GE + +contains + + subroutine init_xml_doc_state(xds) + type(xml_doc_state), intent(inout) :: xds + call init_entity_list(xds%entityList) + call init_entity_list(xds%PEList) + call init_notation_list(xds%nList) + call init_element_list(xds%element_list) + allocate(xds%inputEncoding(0)) + allocate(xds%intSubset(0)) + end subroutine init_xml_doc_state + + subroutine destroy_xml_doc_state(xds) + type(xml_doc_state), intent(inout) :: xds + call destroy_entity_list(xds%entityList) + call destroy_entity_list(xds%PEList) + call destroy_notation_list(xds%nList) + call destroy_element_list(xds%element_list) + if (associated(xds%encoding)) deallocate(xds%encoding) + if (associated(xds%inputEncoding)) deallocate(xds%inputEncoding) + if (associated(xds%documentURI)) deallocate(xds%documentURI) + deallocate(xds%intSubset) + end subroutine destroy_xml_doc_state + + subroutine register_internal_PE(xds, name, text, wfc, baseURI) + type(xml_doc_state), intent(inout) :: xds + character(len=*), intent(in) :: name + character(len=*), intent(in) :: text + logical, intent(in) :: wfc + type(URI), pointer :: baseURI + + call add_internal_entity(xds%PEList, name=name, text=text, & + wfc=wfc, baseURI=baseURI) + + end subroutine register_internal_PE + + subroutine register_external_PE(xds, name, systemId, wfc, baseURI, publicId) + type(xml_doc_state), intent(inout) :: xds + character(len=*), intent(in) :: name + character(len=*), intent(in) :: systemId + logical, intent(in) :: wfc + character(len=*), intent(in), optional :: publicId + type(URI), pointer :: baseURI + + call add_external_entity(xds%PEList, name=name, & + publicId=publicId, systemId=systemId, & + wfc=wfc, baseURI=baseURI) + end subroutine register_external_PE + + subroutine register_internal_GE(xds, name, text, wfc, baseURI) + type(xml_doc_state), intent(inout) :: xds + character(len=*), intent(in) :: name + character(len=*), intent(in) :: text + logical, intent(in) :: wfc + type(URI), pointer :: baseURI + + call add_internal_entity(xds%entityList, name=name, text=text, & + wfc=wfc, baseURI=baseURI) + + end subroutine register_internal_GE + + subroutine register_external_GE(xds, name, systemId, wfc, baseURI, publicId, notation) + type(xml_doc_state), intent(inout) :: xds + character(len=*), intent(in) :: name + character(len=*), intent(in) :: systemId + logical, intent(in) :: wfc + character(len=*), intent(in), optional :: publicId + character(len=*), intent(in), optional :: notation + type(URI), pointer :: baseURI + + call add_external_entity(xds%entityList, name=name, & + systemId=systemId, publicId=publicId, notation=notation, & + wfc=wfc, baseURI=baseURI) + end subroutine register_external_GE + +#endif +end module m_common_struct diff --git a/src/xml/common/makefile b/src/xml/common/makefile new file mode 100644 index 000000000..063c288a2 --- /dev/null +++ b/src/xml/common/makefile @@ -0,0 +1,58 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../fsys -I../utils $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_common.o: m_common_attrs.o +m_common_attrs.o: m_common_element.o m_common_error.o +m_common_buffer.o: m_common_charset.o m_common_error.o +m_common_element.o: m_common_charset.o m_common_content_model.o m_common_error.o m_common_namecheck.o +m_common_elstack.o: m_common_content_model.o m_common_error.o +m_common_entities.o: m_common_charset.o m_common_error.o +m_common_entity_expand.o: m_common_entities.o m_common_error.o +m_common_entity_expand.o: m_common_namecheck.o m_common_struct.o +m_common_io.o: m_common_error.o +m_common_namecheck.o: m_common_charset.o +m_common_namespaces.o: m_common_attrs.o m_common_charset.o m_common_error.o +m_common_namespaces.o: m_common_namecheck.o m_common_struct.o +m_common_notations.o: m_common_error.o +m_common_struct.o: m_common_charset.o m_common_element.o m_common_entities.o +m_common_struct.o: m_common_notations.o diff --git a/src/xml/dom/FoX_dom.F90 b/src/xml/dom/FoX_dom.F90 new file mode 100644 index 000000000..e6d8cafc6 --- /dev/null +++ b/src/xml/dom/FoX_dom.F90 @@ -0,0 +1,261 @@ +module FoX_dom + + use fox_m_fsys_array_str + use fox_m_fsys_format + + use m_dom_dom + use m_dom_error + use m_dom_extras + use m_dom_parse + use m_dom_utils + + implicit none + private + + public :: str_vs, vs_vs_alloc, vs_str_alloc + public :: str, operator(//) + + public :: DOMImplementation + public :: Node + public :: NodeList + public :: NamedNodeMap + + ! DOM DOMString + ! no + + ! DOM DOMTimestamp + ! public :: DOMTimestamp + + ! DOM Exceptions + public :: DOMException + public :: inException + public :: getExceptionCode + + public :: INDEX_SIZE_ERR + public :: DOMSTRING_SIZE_ERR + public :: HIERARCHY_REQUEST_ERR + public :: WRONG_DOCUMENT_ERR + public :: INVALID_CHARACTER_ERR + public :: NO_DATA_ALLOWED_ERR + public :: NO_MODIFICATION_ALLOWED_ERR + public :: NOT_FOUND_ERR + public :: NOT_SUPPORTED_ERR + public :: INUSE_ATTRIBUTE_ERR + public :: INVALID_STATE_ERR + public :: SYNTAX_ERR + public :: INVALID_MODIFICATION_ERR + public :: NAMESPACE_ERR + public :: INVALID_ACCESS_ERR + public :: VALIDATION_ERR + public :: TYPE_MISMATCH_ERR + ! XPath + public :: INVALID_EXPRESSION_ERR + public :: TYPE_ERR + ! LS + public :: PARSE_ERR + public :: SERIALIZE_ERR + + ! DOM Implementation + public :: hasFeature + public :: createDocumentType + public :: createDocument + + ! DOM Document + public :: getDocumentElement + public :: getDocType + public :: getImplementation + public :: createDocumentFragment + public :: createElement + public :: createTextNode + public :: createComment + public :: createCDATASection + public :: createProcessingInstruction + public :: createAttribute + public :: createEntityReference + public :: getElementsByTagName + public :: getElementById + + public :: importNode + public :: createElementNS + public :: createAttributeNS + public :: getElementsByTagNameNS + + public :: getXmlStandalone + public :: setXmlStandalone + public :: getXmlVersion + public :: setXmlVersion + public :: getXmlEncoding + public :: getInputEncoding + public :: getDocumentURI + public :: setDocumentURI + public :: getStrictErrorChecking + public :: setStrictErrorChecking + public :: getDomConfig + public :: normalizeDocument + public :: renameNode + public :: adoptNode + + ! DOM Node + public :: ELEMENT_NODE + public :: ATTRIBUTE_NODE + public :: TEXT_NODE + public :: CDATA_SECTION_NODE + public :: ENTITY_REFERENCE_NODE + public :: ENTITY_NODE + public :: PROCESSING_INSTRUCTION_NODE + public :: COMMENT_NODE + public :: DOCUMENT_NODE + public :: DOCUMENT_TYPE_NODE + public :: DOCUMENT_FRAGMENT_NODE + public :: NOTATION_NODE + + public :: getNodeName + public :: getNodeValue + public :: setNodeValue + public :: getNodeType + public :: getFirstChild + public :: getLastChild + public :: getAttributes + public :: getNextSibling + public :: getPreviousSibling + public :: getParentNode + public :: getChildNodes + public :: getOwnerDocument + public :: insertBefore + public :: replaceChild + public :: removeChild + public :: appendChild + public :: hasChildNodes + public :: cloneNode + public :: normalize + + public :: isSupported + public :: getNamespaceURI + public :: getPrefix + public :: setPrefix + public :: getLocalName + public :: hasAttributes + + public :: getTextContent + public :: setTextContent + public :: isEqualNode + public :: isSameNode + public :: isDefaultNamespace + public :: lookupNamespaceURI + public :: lookupPrefix + + ! DOM NodeList + public :: item + public :: append + + ! DOM NamedNodeMap + public :: getLength + public :: getNamedItem + public :: setNamedItem + public :: removeNamedItem +! public :: item + public :: getNamedItemNS + public :: setNamedItemNS + public :: removeNamedItemNS + + ! DOM CharacterData + ! NB We use the native Fortran string type here + ! rather than inventing a DOM String, thus no + ! string type to make public +! public :: getData +! public :: setData + public :: substringData + public :: appendData + public :: insertData + public :: deleteData + public :: replaceData + + ! DOM Attr +! public :: getName + public :: getSpecified + public :: getValue + public :: setValue + public :: getOwnerElement + public :: getIsId + + ! DOM Element + public :: getTagName + public :: getAttribute + public :: setAttribute + public :: removeAttribute + public :: getAttributeNode + public :: setAttributeNode + public :: removeAttributeNode +! public :: getElementsByTagName + public :: getAttributeNS + public :: setAttributeNS + public :: removeAttributeNS + public :: getAttributeNodeNS + public :: setAttributeNodeNS +! public :: getElementsByTagNameNS + public :: hasAttribute + public :: hasAttributeNS + public :: setIdAttribute + public :: setIdAttributeNS + public :: setIdAttributeNode + + !DOM Text + public :: splitText + public :: getIsElementContentWhitespace + + !DOM CData +! public :: getData +! public :: setData + + !DOM DocumentType + public :: getEntities + public :: getNotations + public :: getInternalSubset + + !DOM Notation + + !DOM Entity + public :: getNotationName + + !DOM EntityReference + + !DOM ProcessingInstruction +! public :: getData +! public :: setData + public :: getTarget + + !DOM common + public :: getData + public :: setData + public :: getName + public :: getPublicId + public :: getSystemId + + !DOM Configuration + public :: DOMConfiguration + public :: getParameter + public :: setParameter + public :: canSetParameter + public :: getParameterNames + + ! FoX-only interfaces + public :: newDOMConfig + + public :: getNodePath + + public :: extractDataContent + public :: extractDataAttribute + public :: extractDataAttributeNS + + public :: parseFile + public :: parseString + public :: serialize + + public :: destroy + public :: getFoX_checks + public :: setFoX_checks + public :: getLiveNodeLists + public :: setLiveNodeLists + public :: getNamespaceNodes + +end module FoX_dom diff --git a/src/xml/dom/m_dom_dom.F90 b/src/xml/dom/m_dom_dom.F90 new file mode 100644 index 000000000..6725f5243 --- /dev/null +++ b/src/xml/dom/m_dom_dom.F90 @@ -0,0 +1,12321 @@ +! ATTENTION +! THIS FILE IS AUTOGENERATED +! DO NOT EDIT DIRECTLY +! EDIT FILES dom/m_dom_***.m4 +! +module m_dom_dom + + use fox_m_fsys_array_str, only: str_vs, vs_str, vs_str_alloc + use fox_m_fsys_format, only: operator(//) + use fox_m_fsys_string, only: toLower + use fox_m_utils_uri, only: URI, parseURI, destroyURI, isAbsoluteURI, & + rebaseURI, expressURI + use m_common_charset, only: checkChars, XML1_0, XML1_1 + use m_common_element, only: element_t, get_element, attribute_t, & + attribute_has_default, get_attribute_declaration, get_attlist_size + use m_common_namecheck, only: checkQName, prefixOfQName, localPartOfQName, & + checkName, checkPublicId, checkNCName + use m_common_struct, only: xml_doc_state, init_xml_doc_state, destroy_xml_doc_state + + use m_dom_error, only: DOMException, throw_exception, inException, getExceptionCode, & + NO_MODIFICATION_ALLOWED_ERR, NOT_FOUND_ERR, HIERARCHY_REQUEST_ERR, & + WRONG_DOCUMENT_ERR, FoX_INTERNAL_ERROR, FoX_NODE_IS_NULL, FoX_LIST_IS_NULL, & + INUSE_ATTRIBUTE_ERR, FoX_MAP_IS_NULL, INVALID_CHARACTER_ERR, NAMESPACE_ERR, & + FoX_INVALID_PUBLIC_ID, FoX_INVALID_SYSTEM_ID, FoX_IMPL_IS_NULL, FoX_INVALID_NODE, & + FoX_INVALID_CHARACTER, FoX_INVALID_COMMENT, FoX_INVALID_CDATA_SECTION, & + FoX_INVALID_PI_DATA, NOT_SUPPORTED_ERR, FoX_INVALID_ENTITY, & + INDEX_SIZE_ERR, FoX_NO_SUCH_ENTITY, FoX_HIERARCHY_REQUEST_ERR, & + FoX_INVALID_URI + + implicit none + private + + + integer, parameter :: configParamLen = 42 + + character(len=configParamLen), parameter :: configParams(24) = (/ & + ! DOM 3 Core: + "canonical-form ", & + "cdata-sections ", & + "check-character-normalization ", & + "comments ", & + "datatype-normalization ", & + "element-content-whitespace ", & + "entities ", & + "error-handler ", & +! "infoset ", & is not a real config option + "namespaces ", & + "namespace-declarations ", & + "normalize-characters ", & +! "schema-location ", & we dont implement +! "schema-type ", & we dont implement + "split-cdata-sections ", & + "validate ", & + "validate-if-schema ", & + "well-formed ", & + ! DOM 3 LS (Parser): + "charset-overrides-xml-encoding ", & + "disallow-doctype ", & + "ignore-unknown-character-denormalizations", & + "resource-resolver ", & + "supported-media-types-only ", & + ! DOM 3 LS (Serializer) + "discard-default-content ", & + "format-pretty-print ", & + "xml-declaration ", & + ! Extra (FoX) configuration options + "invalid-pretty-print " /) + + integer, parameter :: paramSettable = 27293398 + integer, parameter :: paramDefaults = 10786516 + + type DOMConfiguration + private + integer :: parameters = paramDefaults + ! FIXME make sure this is 32 bit at least. + end type DOMConfiguration + + interface canSetParameter + module procedure canSetParameter_log + module procedure canSetParameter_ch + end interface canSetParameter + + public :: setParameter + public :: getParameter + public :: canSetParameter + public :: getParameterNames + + public :: newDOMConfig + public :: copyDOMConfig + + + integer, parameter :: ELEMENT_NODE = 1 + integer, parameter :: ATTRIBUTE_NODE = 2 + integer, parameter :: TEXT_NODE = 3 + integer, parameter :: CDATA_SECTION_NODE = 4 + integer, parameter :: ENTITY_REFERENCE_NODE = 5 + integer, parameter :: ENTITY_NODE = 6 + integer, parameter :: PROCESSING_INSTRUCTION_NODE = 7 + integer, parameter :: COMMENT_NODE = 8 + integer, parameter :: DOCUMENT_NODE = 9 + integer, parameter :: DOCUMENT_TYPE_NODE = 10 + integer, parameter :: DOCUMENT_FRAGMENT_NODE = 11 + integer, parameter :: NOTATION_NODE = 12 + integer, parameter :: XPATH_NAMESPACE_NODE = 13 + + type DOMImplementation + private + character(len=7) :: id = "FoX_DOM" + logical :: FoX_checks = .true. ! Do extra checks not mandated by DOM + end type DOMImplementation + + type ListNode + private + type(Node), pointer :: this => null() + end type ListNode + + type NodeList + private + character, pointer :: nodeName(:) => null() ! What was getByTagName run on? + character, pointer :: localName(:) => null() ! What was getByTagNameNS run on? + character, pointer :: namespaceURI(:) => null() ! What was getByTagNameNS run on? + type(Node), pointer :: element => null() ! which element or document was the getByTagName run from? + type(ListNode), pointer :: nodes(:) => null() + integer :: length = 0 + end type NodeList + + type NodeListptr + private + type(NodeList), pointer :: this + end type NodeListptr + + type NamedNodeMap + private + logical :: readonly = .false. + type(Node), pointer :: ownerElement => null() + type(ListNode), pointer :: nodes(:) => null() + integer :: length = 0 + end type NamedNodeMap + + type documentExtras + type(DOMImplementation), pointer :: implementation => null() ! only for doctype + type(Node), pointer :: docType => null() + type(Node), pointer :: documentElement => null() + character, pointer :: inputEncoding(:) => null() + character, pointer :: xmlEncoding(:) => null() + type(NodeListPtr), pointer :: nodelists(:) => null() ! document + ! In order to keep track of all nodes not connected to the document + logical :: liveNodeLists ! For the document, are nodelists live? + type(NodeList) :: hangingNodes ! For the document, list of nodes not associated with doc + type(xml_doc_state), pointer :: xds => null() + logical :: strictErrorChecking = .true. + logical :: brokenNS = .false. ! FIXME consolidate these logical variables into bitmask + type(DOMConfiguration), pointer :: domConfig => null() + end type documentExtras + + type elementOrAttributeExtras + ! Needed for all: + character, pointer, dimension(:) :: namespaceURI => null() + character, pointer, dimension(:) :: prefix => null() + character, pointer, dimension(:) :: localName => null() + ! Needed for elements: + type(NamedNodeMap) :: attributes + type(NodeList) :: namespaceNodes + ! Needed for attributes: + type(Node), pointer :: ownerElement => null() + logical :: specified = .true. + logical :: isId = .false. + logical :: dom1 = .false. + end type elementOrAttributeExtras + + type docTypeExtras + character, pointer :: publicId(:) => null() ! doctype, entity, notation + character, pointer :: systemId(:) => null() ! doctype, entity, notation + character, pointer :: notationName(:) => null() ! entity + logical :: illFormed = .false. ! entity + type(namedNodeMap) :: entities ! doctype + type(namedNodeMap) :: notations ! doctype + end type docTypeExtras + + type Node + private + logical :: readonly = .false. + character, pointer, dimension(:) :: nodeName => null() + character, pointer, dimension(:) :: nodeValue => null() + integer :: nodeType = 0 + type(Node), pointer :: parentNode => null() + type(Node), pointer :: firstChild => null() + type(Node), pointer :: lastChild => null() + type(Node), pointer :: previousSibling => null() + type(Node), pointer :: nextSibling => null() + type(Node), pointer :: ownerDocument => null() + type(NodeList) :: childNodes ! not for text, cdata, PI, comment, notation, docType, XPath + logical :: inDocument = .false.! For a node, is this node associated to the doc? + logical :: ignorableWhitespace = .false. ! Text nodes only + type(documentExtras), pointer :: docExtras => null() + type(elementOrAttributeExtras), pointer :: elExtras => null() + type(docTypeExtras), pointer :: dtdExtras => null() + integer :: textContentLength = 0 + end type Node + + type(DOMImplementation), save, target :: FoX_DOM + + interface destroy + module procedure destroyNode + module procedure destroyNodeList + module procedure destroyNamedNodeMap + module procedure destroyDOMConfig + end interface destroy + + public :: ELEMENT_NODE + public :: ATTRIBUTE_NODE + public :: TEXT_NODE + public :: CDATA_SECTION_NODE + public :: ENTITY_REFERENCE_NODE + public :: ENTITY_NODE + public :: PROCESSING_INSTRUCTION_NODE + public :: COMMENT_NODE + public :: DOCUMENT_NODE + public :: DOCUMENT_TYPE_NODE + public :: DOCUMENT_FRAGMENT_NODE + public :: NOTATION_NODE + + public :: DOMImplementation + public :: DOMConfiguration + public :: Node + + public :: ListNode + public :: NodeList + public :: NamedNodeMap + + public :: destroy + public :: destroyAllNodesRecursively + + + + public :: getNodeName + public :: getNodeValue + public :: setNodeValue + public :: getNodeType + public :: getParentNode + public :: getChildNodes + public :: getFirstChild + public :: getLastChild + public :: getNextSibling + public :: getPreviousSibling + public :: getAttributes + public :: getOwnerDocument + public :: insertBefore + public :: replaceChild + public :: removeChild + public :: appendChild + public :: hasChildNodes + public :: cloneNode + public :: normalize + public :: isSupported + public :: getNamespaceURI + public :: getPrefix + public :: setPrefix + public :: getLocalName + public :: hasAttributes + public :: isEqualNode + public :: isSameNode + public :: isDefaultNamespace + public :: lookupNamespaceURI + public :: lookupPrefix + public :: getTextContent + public :: setTextContent + + public :: getNodePath + + public :: setStringValue + public :: getStringValue + public :: setReadonlyNode + public :: getReadOnly + + public :: getBaseURI + + + + public :: item + public :: append + public :: pop_nl + public :: remove_nl + public :: destroyNodeList + + interface append + module procedure append_nl + end interface + + interface item + module procedure item_nl + end interface + + interface getLength + module procedure getLength_nl + end interface getLength + + + public :: getNamedItem + public :: setNamedItem + public :: removeNamedItem +! public :: item +! public :: getLength + public :: getNamedItemNS + public :: setNamedItemNS + public :: removeNamedItemNS + +! public :: append + public :: setReadOnlyMap + public :: destroyNamedNodeMap + + + interface item + module procedure item_nnm + end interface + + interface getLength + module procedure getLength_nnm + end interface + + + + public :: hasFeature + public :: createDocument + public :: createDocumentType + + public :: destroyDocument + public :: createEmptyDocument + + public :: getFoX_checks + public :: setFoX_checks + + + +!FIXME lots of these should have a check if(namespaces) checkNCName + + public :: getDocType + public :: getImplementation + public :: getDocumentElement + public :: setDocumentElement + + public :: createElement + public :: createDocumentFragment + public :: createTextNode + public :: createComment + public :: createCdataSection + public :: createProcessingInstruction + public :: createAttribute + public :: createEntityReference + public :: createEmptyEntityReference + public :: getElementsByTagName + public :: importNode + public :: createElementNS + public :: createAttributeNS + public :: getElementsByTagNameNS + public :: getElementById + public :: getXmlStandalone + public :: setXmlStandalone + public :: getXmlVersion + public :: setXmlVersion + public :: getXmlEncoding + public :: getInputEncoding + public :: getDocumentURI + public :: setDocumentURI + public :: getStrictErrorChecking + public :: setStrictErrorChecking + public :: getDomConfig + public :: renameNode + public :: adoptNode + + public :: setDocType + public :: setDomConfig + public :: setXds + public :: createNamespaceNode + public :: createEntity + public :: createNotation + public :: setGCstate + public :: getXds + public :: getLiveNodeLists + public :: setLiveNodeLists + + + !public :: getName + public :: getEntities + public :: getNotations +! public :: getPublicId +! public :: getSystemId + public :: getInternalSubset + + + + public :: getTagName + public :: getAttribute + public :: setAttribute + public :: removeAttribute + public :: getAttributeNode + public :: setAttributeNode + public :: removeAttributeNode + public :: getAttributeNS + public :: setAttributeNS + public :: removeAttributeNS + public :: getAttributeNodeNS + public :: setAttributeNodeNS + public :: removeAttributeNodeNS + public :: hasAttribute + public :: hasAttributeNS + public :: setIdAttribute + public :: setIdAttributeNS + public :: setIdAttributeNode + + + + !public :: getName + public :: getSpecified + public :: setSpecified + interface getValue + module procedure getValue_DOM + end interface + public :: getValue + public :: setValue + public :: getOwnerElement + + public :: getIsId + public :: setIsId + interface getIsId + module procedure getIsId_DOM + end interface + interface setIsId + module procedure setIsId_DOM + end interface + + + + public :: getLength +! public :: getData +! public :: setData + public :: substringData + public :: appendData + public :: insertData + public :: deleteData + public :: replaceData + + interface getLength + module procedure getLength_characterdata + end interface + + + + public :: getNotationName + + public :: getIllFormed + public :: setIllFormed + + + + public :: getTarget + + + public :: splitText + public :: getIsElementContentWhitespace + public :: setIsElementContentWhitespace + + +! Assorted functions with identical signatures despite belonging to different types. + + public :: getData + public :: setData + public :: getName + public :: getPublicId + public :: getSystemId + + + + public :: normalizeDocument + + public :: getNamespaceNodes + public :: namespaceFixup + + +contains + + + subroutine resetParameter(domConfig, name) + type(DOMConfiguration), pointer :: domConfig + character(len=*), intent(in) :: name + + integer :: i, n + do i = 1, size(configParams) + if (toLower(name)==trim(configParams(i))) then + n = i + exit + endif + enddo + if (i>size(configParams)) return + if (.not.btest(paramSettable, n)) return + if (btest(paramDefaults, n)) then + domConfig%parameters = ibset(domConfig%parameters, n) + else + domConfig%parameters = ibclr(domConfig%parameters, n) + endif + end subroutine resetParameter + + recursive subroutine setParameter(domConfig, name, value, ex) + type(DOMException), intent(out), optional :: ex + type(DOMConfiguration), pointer :: domConfig + character(len=*), intent(in) :: name + logical, intent(in) :: value + integer :: i, n + + if (toLower(name)=="infoset") then + if (value) then + call setParameter(domConfig, "validate-if-schema", .false.) + call setParameter(domConfig, "entities", .false.) + ! cant do datatype-normalization + call setParameter(domConfig, "cdata-sections", .false.) + call setParameter(domConfig, "namespace-declarations", .true.) + ! well-formed cannot be changed + call setParameter(domConfig, "element-content-whitespace", .true.) + call setParameter(domConfig, "comments", .true.) + call setParameter(domConfig, "namespaces", .true.) + endif + return + endif + + do i = 1, size(configParams) + if (toLower(name)==trim(configParams(i))) then + n = i + exit + endif + enddo + if (i > size(configParams)) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "setParameter", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (.not.canSetParameter(domConfig, name, value)) then + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "setParameter", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (value) then + domConfig%parameters = ibset(domConfig%parameters, n) + else + domConfig%parameters = ibclr(domConfig%parameters, n) + endif + + select case (toLower(name)) + case ("canonical-form") + if (value) then + domConfig%parameters = ibclr(domConfig%parameters, 7) + ! cant do normalize-characters + domConfig%parameters = ibclr(domConfig%parameters, 2) + domConfig%parameters = ibset(domConfig%parameters, 9) + domConfig%parameters = ibset(domConfig%parameters, 10) + ! well-formed cannot be changed + domConfig%parameters = ibset(domConfig%parameters, 6) + ! FIXME when we work out pretty-print/preserve-whitespace semantics + ! call setParameter(domConfig, "format-pretty-print", .false.) + domConfig%parameters = ibclr(domConfig%parameters, 21) + domConfig%parameters = ibclr(domConfig%parameters, 23) + domConfig%parameters = ibclr(domConfig%parameters, 24) + else + call resetParameter(domConfig, "entities") + ! cant do normalize-characters + call resetParameter(domConfig, "cdata-sections") + call resetParameter(domConfig, "namespaces") + call resetParameter(domConfig, "namespace-declarations") + ! well-formed cannot be changed + call resetParameter(domConfig, "element-content-whitespace") + call resetParameter(domConfig, "format-pretty-print") + call resetParameter(domConfig, "discard-default-content") + call resetParameter(domConfig, "xml-declaration") + call resetParameter(domConfig, "invalid-pretty-print") + endif + case ("cdata-sections") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("element-content-whitespace") + if (.not.value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("entities") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("namespaces") + if (.not.value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("namespaces-declarations") + if (.not.value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case("validate") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 14) + case ("validate-if-schema") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 13) + case ("format-pretty-print") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("discard-default-content") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("xml-declaration") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + case ("invalid-pretty-print") + if (value) domConfig%parameters = ibclr(domConfig%parameters, 1) + end select + + end subroutine setParameter + + recursive function getParameter(domConfig, name, ex)result(value) + type(DOMException), intent(out), optional :: ex + type(DOMConfiguration), pointer :: domConfig + character(len=*), intent(in) :: name + logical :: value + + integer :: i, n + + if (toLower(name)=="infoset") then + value = & + .not.getParameter(domConfig, "validate-if-schema") & + .and..not.getParameter(domConfig, "entities") & + .and..not.getParameter(domConfig, "datatype-normalization") & + .and..not.getParameter(domConfig, "cdata-sections") & + .and.getParameter(domConfig, "namespace-declarations") & + .and.getParameter(domConfig, "well-formed") & + .and.getParameter(domConfig, "element-content-whitespace") & + .and.getParameter(domConfig, "comments") & + .and.getParameter(domConfig, "namespaces") + return + endif + + do i = 1, size(configParams) + if (toLower(name)==trim(configParams(i))) then + n = i + exit + endif + enddo + if (i > size(configParams)) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "getParameter", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + value = btest(domConfig%parameters, n) + + end function getParameter + + function canSetParameter_log(domConfig, name, value, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(DOMConfiguration), pointer :: domConfig + character(len=*), intent(in) :: name + logical, intent(in) :: value + logical :: p + + integer :: i, n + + if (toLower(name)=="infoset") then + p = .true. + return + endif + do i = 1, size(configParams) + if (toLower(name)==trim(configParams(i))) then + n = i + exit + endif + enddo + if (i > size(configParams)) then + p = .false. + return + endif + + p = btest(paramSettable, n) + + end function canSetParameter_log + + function canSetParameter_ch(domConfig, name, value, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(DOMConfiguration), pointer :: domConfig + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + logical :: p + + ! DOM 3 allows some config options to be set to strings + ! (eg schemaLocation) but we dont support any of these, + ! so no parameter can be set to a string. + p = .false. + + end function canSetParameter_ch + + function getParameterNames(domConfig, ex)result(s) + type(DOMException), intent(out), optional :: ex + type(DOMConfiguration), pointer :: domConfig + character(len=configParamLen) :: s(size(configParams)) + + s = configParams + end function getParameterNames + + function newDOMConfig() result(dc) + type(DOMConfiguration), pointer :: dc + allocate(dc) + end function newDOMConfig + + subroutine copyDOMConfig(dc1, dc2) + type(DOMConfiguration), pointer :: dc1, dc2 + + dc1%parameters = dc2%parameters + end subroutine copyDOMConfig + + subroutine destroyDOMConfig(dc) + type(DOMConfiguration), pointer :: dc + + deallocate(dc) + end subroutine destroyDOMConfig + + + + function createNode(arg, nodeType, nodeName, nodeValue, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: nodeType + character(len=*), intent(in) :: nodeName + character(len=*), intent(in) :: nodeValue + type(Node), pointer :: np + + allocate(np) + np%ownerDocument => arg + np%nodeType = nodeType + np%nodeName => vs_str_alloc(nodeName) + np%nodeValue => vs_str_alloc(nodeValue) + + allocate(np%childNodes%nodes(0)) + + end function createNode + + recursive subroutine destroyNode(np, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + + if (.not.associated(np)) return + + select case(np%nodeType) + case (ELEMENT_NODE, ATTRIBUTE_NODE, XPATH_NAMESPACE_NODE) + call destroyElementOrAttribute(np) + case (DOCUMENT_TYPE_NODE) + call destroyDocumentType(np) + case (ENTITY_NODE, NOTATION_NODE) + call destroyEntityOrNotation(np) + case (DOCUMENT_NODE) + call destroyDocument(np) + end select + call destroyNodeContents(np) + deallocate(np) + + end subroutine destroyNode + + recursive subroutine destroyElementOrAttribute(np, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + + integer :: i + + if (np%nodeType /= ELEMENT_NODE & + .and. np%nodeType /= ATTRIBUTE_NODE & + .and. np%nodeType /= XPATH_NAMESPACE_NODE) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "destroyElementOrAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%elExtras%attributes%nodes)) deallocate(np%elExtras%attributes%nodes) + do i = 1, np%elExtras%namespaceNodes%length + call destroyNode(np%elExtras%namespaceNodes%nodes(i)%this) + enddo + if (associated(np%elExtras%namespaceNodes%nodes)) deallocate(np%elExtras%namespaceNodes%nodes) + if (associated(np%elExtras%namespaceURI)) deallocate(np%elExtras%namespaceURI) + if (associated(np%elExtras%prefix)) deallocate(np%elExtras%prefix) + if (associated(np%elExtras%localName)) deallocate(np%elExtras%localName) + deallocate(np%elExtras) + + end subroutine destroyElementOrAttribute + + subroutine destroyEntityOrNotation(np, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + + if (np%nodeType /= ENTITY_NODE & + .and. np%nodeType /= NOTATION_NODE) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "destroyEntityOrNotation", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%dtdExtras%publicId)) deallocate(np%dtdExtras%publicId) + if (associated(np%dtdExtras%systemId)) deallocate(np%dtdExtras%systemId) + if (associated(np%dtdExtras%notationName)) deallocate(np%dtdExtras%notationName) + + deallocate(np%dtdExtras) + + end subroutine destroyEntityOrNotation + + subroutine destroyDocumentType(np, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + + integer :: i + + if (np%nodeType /= DOCUMENT_TYPE_NODE) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "destroyDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%dtdExtras%publicId)) deallocate(np%dtdExtras%publicId) + if (associated(np%dtdExtras%systemId)) deallocate(np%dtdExtras%systemId) + + ! Destroy all entities & notations (docType only) + if (associated(np%dtdExtras%entities%nodes)) then + do i = 1, size(np%dtdExtras%entities%nodes) + call destroyAllNodesRecursively(np%dtdExtras%entities%nodes(i)%this) + enddo + deallocate(np%dtdExtras%entities%nodes) + endif + if (associated(np%dtdExtras%notations%nodes)) then + do i = 1, size(np%dtdExtras%notations%nodes) + call destroy(np%dtdExtras%notations%nodes(i)%this) + enddo + deallocate(np%dtdExtras%notations%nodes) + endif + + deallocate(np%dtdExtras) + + end subroutine destroyDocumentType + + recursive subroutine destroyAllNodesRecursively(arg, except) + ! Only recurses once into destroyDocumentType + type(Node), pointer :: arg + logical, intent(in), optional :: except + + type(Node), pointer :: this, deadNode, treeroot + logical :: doneChildren, doneAttributes + integer :: i_tree + + if (.not.associated(arg)) return + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + deadNode => null() + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + deadNode => null() + + 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 + + deadNode => this + if (associated(this, treeroot)) exit + if (getNodeType(this)==ATTRIBUTE_NODE) then + if (i_tree 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 + call destroy(deadNode) + endif + + enddo + + + + deallocate(arg%childNodes%nodes) + allocate(arg%childNodes%nodes(0)) + arg%firstChild => null() + arg%lastChild => null() + + if (.not.present(except)) call destroyNode(arg) + + end subroutine destroyAllNodesRecursively + + subroutine destroyNodeContents(np) + type(Node), intent(inout) :: np + + if (associated(np%nodeName)) deallocate(np%nodeName) + if (associated(np%nodeValue)) deallocate(np%nodeValue) + + deallocate(np%childNodes%nodes) + + end subroutine destroyNodeContents + + + + + pure function getnodeName_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p) then + n = size(np%nodeName) + else + n = 0 + endif + end function getnodeName_len +function getnodeName(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getnodeName_len(np, .true.)) :: c +#else + character(len=getnodeName_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getnodeName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c = str_vs(np%nodeName) + + end function getnodeName + + + pure function getNodeValue_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + n = 0 + if (.not.p) return + + select case(np%nodeType) + case (ATTRIBUTE_NODE) + n = getTextContent_len(np, .true.) + case (CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE, TEXT_NODE) + n = size(np%nodeValue) + end select + + end function getNodeValue_len + + function getNodeValue(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getNodeValue_len(np, .true.)) :: c +#else + character(len=getNodeValue_len(np, associated(np))) :: c +#endif + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getNodeValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + select case(np%nodeType) + case (ATTRIBUTE_NODE) + c = getTextContent(np) + case (CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE, TEXT_NODE) + c = str_vs(np%nodeValue) + case default + c = "" + end select + + end function getNodeValue + + subroutine setNodeValue(arg, nodeValue, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*) :: nodeValue + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setNodeValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(getOwnerDocument(arg))) then + if (.not.checkChars(nodeValue, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "setNodeValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + endif ! Otherwise its a document node, and nothing will happen anyway + + select case(arg%nodeType) + case (ATTRIBUTE_NODE) + call setValue(arg, nodeValue, ex) + case (CDATA_SECTION_NODE, COMMENT_NODE, PROCESSING_INSTRUCTION_NODE, TEXT_NODE) + call setData(arg, nodeValue, ex) + end select + + end subroutine setNodeValue + +function getnodeType(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + integer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getnodeType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c = np%nodeType + + end function getnodeType + + +function getparentNode(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getparentNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%parentNode + + end function getparentNode + + +function getchildNodes(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(NodeList), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getchildNodes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%childNodes + + end function getchildNodes + + +function getfirstChild(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getfirstChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%firstChild + + end function getfirstChild + + +function getlastChild(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getlastChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%lastChild + + end function getlastChild + + +function getpreviousSibling(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getpreviousSibling", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%previousSibling + + end function getpreviousSibling + + +function getnextSibling(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getnextSibling", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c => np%nextSibling + + end function getnextSibling + + + function getAttributes(arg, ex)result(nnm) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(NamedNodeMap), pointer :: nnm + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getAttributes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(arg)==ELEMENT_NODE) then + nnm => arg%elExtras%attributes + else + nnm => null() + endif + end function getAttributes + + function getOwnerDocument(arg, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getOwnerDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType==DOCUMENT_NODE) then + np => null() + else + np => arg%ownerDocument + endif + end function getOwnerDocument + +subroutine setownerDocument(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setownerDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setownerDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%ownerDocument => c + + end subroutine setownerDocument + + + function insertBefore(arg, newChild, refChild, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: newChild + type(Node), pointer :: refChild + type(Node), pointer :: np + + type(Node), pointer :: testChild, testParent, treeroot, this + type(ListNode), pointer :: temp_nl(:) + integer :: i, i2, i_t, i_tree + logical :: doneChildren, doneAttributes + + if (.not.associated(arg).or..not.associated(newChild)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.associated(refChild)) then + np => appendChild(arg, newChild, ex) + return + endif + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + testParent => arg + ! Check if you are allowed to put a newChild nodetype under a arg nodetype + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i = 1, newChild%childNodes%length + testChild => newChild%childNodes%nodes(i)%this + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement))) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType)))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + enddo + else + testChild => newChild + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement))) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType)))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + ! And then check that newChild is not arg or one of args ancestors + ! (this would never be true if newChild is a documentFragment) + testParent => arg + do while (associated(testParent)) + if (associated(testParent, newChild)) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + testParent => testParent%parentNode + enddo + endif + + if (getNodeType(newChild)/=DOCUMENT_TYPE_NODE.and. & + .not.(associated(arg%ownerDocument, newChild%ownerDocument) & + .or.associated(arg, newChild%ownerDocument))) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE & + .and. newChild%childNodes%length==0) then + np => newChild + return + ! Nothing to do + endif + if (associated(getParentNode(newChild))) then + np => removeChild(getParentNode(newChild), newChild, ex) + newChild => np + endif + + if (arg%childNodes%length==0) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + allocate(temp_nl(arg%childNodes%length+newChild%childNodes%length)) + else + allocate(temp_nl(arg%childNodes%length+1)) + endif + + i_t = 0 + np => null() + do i = 1, arg%childNodes%length + if (associated(arg%childNodes%nodes(i)%this, refChild)) then + np => refChild + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i2 = 1, newChild%childNodes%length + i_t = i_t + 1 + temp_nl(i_t)%this => newChild%childNodes%nodes(i2)%this + temp_nl(i_t)%this%parentNode => arg +! call namespaceFixup(temp_nl(i_t)%this) + enddo + else + i_t = i_t + 1 + temp_nl(i_t)%this => newChild + temp_nl(i_t)%this%parentNode => arg +! call namespaceFixup(temp_nl(i_t)%this) + endif + if (i==1) then + arg%firstChild => temp_nl(1)%this + !temp_nl(1)%this%previousSibling => null() ! This is a no-op + else + temp_nl(i-1)%this%nextSibling => temp_nl(i)%this + temp_nl(i)%this%previousSibling => temp_nl(i-1)%this + endif + arg%childNodes%nodes(i)%this%previousSibling => temp_nl(i_t)%this + temp_nl(i_t)%this%nextSibling => arg%childNodes%nodes(i)%this + endif + i_t = i_t + 1 + temp_nl(i_t)%this => arg%childNodes%nodes(i)%this + enddo + + if (.not.associated(np)) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "insertBefore", ex) + if (present(ex)) then + if (inException(ex)) then + + if (associated(temp_nl)) deallocate(temp_nl) + return + endif + endif +endif + + endif + + np => newChild + if (getGCstate(arg%ownerDocument)) then + if (arg%inDocument) then + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i = 1, newChild%childNodes%length + call putNodesInDocument(arg%ownerDocument, newChild%childNodes%nodes(i)%this) + enddo + else + call putNodesInDocument(arg%ownerDocument, newChild) + endif + ! If newChild was originally in document, it was removed above so must be re-added + ! Ideally we would avoid the cost of removal & readding to hanging nodelist + endif + ! If arg was not in the document, then newChildren were either + ! a) removed above in call to removeChild or + ! b) in a document fragment and therefore not part of doc either + endif + + + if (getNodeType(newChild)==DOCUMENT_FRAGMENT_NODE) then + deallocate(newChild%childNodes%nodes) + allocate(newChild%childNodes%nodes(0)) + newChild%childNodes%length = 0 + endif + deallocate(arg%childNodes%nodes) + arg%childNodes%nodes => temp_nl + arg%childNodes%length = size(arg%childNodes%nodes) + + call updateNodeLists(arg%ownerDocument) + + call updateTextContentLength(arg, newChild%textContentLength) + + end function insertBefore + + + function replaceChild(arg, newChild, oldChild, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: newChild + type(Node), pointer :: oldChild + type(Node), pointer :: np + + type(Node), pointer :: testChild, testParent, treeroot, this + type(ListNode), pointer :: temp_nl(:) + integer :: i, i2, i_t, i_tree + logical :: doneChildren, doneAttributes + + if (.not.associated(arg).or..not.associated(newChild).or..not.associated(oldChild)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + testParent => arg + ! Check if you are allowed to put a newChild nodetype under a arg nodetype + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i = 1, newChild%childNodes%length + testChild => newChild%childNodes%nodes(i)%this + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement) & + .and.oldChild%nodeType/=ELEMENT_NODE)) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType) & + .and.oldChild%nodeType/=DOCUMENT_TYPE_NODE))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + enddo + else + testChild => newChild + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement) & + .and.oldChild%nodeType/=ELEMENT_NODE)) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType) & + .and.oldChild%nodeType/=DOCUMENT_TYPE_NODE))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + ! And then check that newChild is not arg or one of args ancestors + ! (this would never be true if newChild is a documentFragment) + testParent => arg + do while (associated(testParent)) + if (associated(testParent, newChild)) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + testParent => testParent%parentNode + enddo + endif + + if (getNodeType(newChild)/=DOCUMENT_TYPE_NODE.and. & + .not.(associated(arg%ownerDocument, newChild%ownerDocument) & + .or.associated(arg, newChild%ownerDocument))) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(getParentNode(newChild))) & + newChild => removeChild(getParentNode(newChild), newChild, ex) + + if (arg%childNodes%length==0) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + allocate(temp_nl(arg%childNodes%length+newChild%childNodes%length-1)) + else + temp_nl => arg%childNodes%nodes + endif + + i_t = 0 + np => null() + do i = 1, arg%childNodes%length + if (associated(arg%childNodes%nodes(i)%this, oldChild)) then + np => oldChild + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i2 = 1, newChild%childNodes%length + i_t = i_t + 1 + temp_nl(i_t)%this => newChild%childNodes%nodes(i2)%this + temp_nl(i_t)%this%parentNode => arg +! call namespaceFixup(temp_nl(i_t)%this) + enddo + else + i_t = i_t + 1 + temp_nl(i_t)%this => newChild + temp_nl(i_t)%this%parentNode => arg +! call namespaceFixup(temp_nl(i_t)%this) + endif + if (i==1) then + arg%firstChild => temp_nl(1)%this + !temp_nl(1)%this%previousSibling => null() ! This is a no-op + else + temp_nl(i-1)%this%nextSibling => temp_nl(i)%this + temp_nl(i)%this%previousSibling => temp_nl(i-1)%this + endif + if (i==arg%childNodes%length) then + arg%lastChild => temp_nl(i_t)%this + !temp_nl(i_t)%this%nextSibling => null() ! This is a no-op + else + arg%childNodes%nodes(i+1)%this%previousSibling => temp_nl(i_t)%this + temp_nl(i_t)%this%nextSibling => arg%childNodes%nodes(i+1)%this + endif + else + i_t = i_t + 1 + temp_nl(i_t)%this => arg%childNodes%nodes(i)%this + endif + enddo + + if (.not.associated(np)) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "replaceChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + np%parentNode => null() + np%previousSibling => null() + np%nextSibling => null() + +! call namespaceFixup(np) + + if (getGCstate(arg%ownerDocument)) then + if (arg%inDocument) then + call removeNodesFromDocument(arg%ownerDocument, oldChild) + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i = 1, newChild%childNodes%length + call putNodesInDocument(arg%ownerDocument, newChild%childNodes%nodes(i)%this) + enddo + else + call putNodesInDocument(arg%ownerDocument, newChild) + endif + ! If newChild was originally in document, it was removed above so must be re-added + ! Ideally we would avoid the cost of removing & re-adding to hangingnodelist + endif + ! If arg was not in the document, then newChildren were either + ! a) removed above in call to removeChild or + ! b) in a document fragment and therefore not part of doc either + endif + + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + deallocate(newChild%childNodes%nodes) + allocate(newChild%childNodes%nodes(0)) + newChild%childNodes%length = 0 + deallocate(arg%childNodes%nodes) + arg%childNodes%nodes => temp_nl + arg%childNodes%length = size(arg%childNodes%nodes) + endif + + call updateNodeLists(arg%ownerDocument) + + call updateTextContentLength(arg, newChild%textContentLength-oldChild%textContentLength) + + end function replaceChild + + + function removeChild(arg, oldChild, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: oldChild + type(Node), pointer :: np + + type(ListNode), pointer :: temp_nl(:) + integer :: i, i_t + + if (.not.associated(arg).or..not.associated(oldChild)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "removeChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "removeChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + allocate(temp_nl(size(arg%childNodes%nodes)-1)) + i_t = 1 + do i = 1, size(arg%childNodes%nodes) + if (associated(arg%childNodes%nodes(i)%this, oldChild)) then + if (associated(arg%firstChild, arg%lastChild)) then + ! There is only one child, we are removing it. + arg%firstChild => null() + arg%lastChild => null() + elseif (i==1) then + ! We are removing the first child, but there is a second + arg%firstChild => arg%childNodes%nodes(2)%this + arg%childNodes%nodes(2)%this%previousSibling => null() + elseif (i==size(arg%childNodes%nodes)) then + ! We are removing the last child, but there is a second-to-last + arg%lastChild => arg%childNodes%nodes(i-1)%this + arg%childNodes%nodes(i-1)%this%nextSibling => null() + else + ! We are removing a child in the middle + arg%childNodes%nodes(i-1)%this%nextSibling => arg%childNodes%nodes(i+1)%this + arg%childNodes%nodes(i+1)%this%previousSibling => arg%childNodes%nodes(i-1)%this + endif + else + if (i_t==size(arg%childNodes%nodes)) exit ! We have failed to find the child + temp_nl(i_t)%this => arg%childNodes%nodes(i)%this + i_t = i_t + 1 + endif + enddo + + deallocate(arg%childNodes%nodes) + arg%childNodes%nodes => temp_nl + arg%childNodes%length = size(temp_nl) + if (i==i_t) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "removeChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + oldChild%parentNode => null() + oldChild%previousSibling => null() + oldChild%nextSibling => null() + +! call namespaceFixup(oldChild) + + if (getGCstate(arg%ownerDocument)) then + if (arg%inDocument) then + call removeNodesFromDocument(arg%ownerDocument, oldChild) + endif + endif + + np => oldChild + + call updateNodeLists(arg%ownerDocument) + + call updateTextContentLength(arg, -oldChild%textContentLength) + + end function removeChild + + + function appendChild(arg, newChild, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: newChild + type(Node), pointer :: np + + type(Node), pointer :: testChild, testParent, treeroot, this + type(ListNode), pointer :: temp_nl(:) + integer :: i, i_t, i_tree + logical :: doneChildren, doneAttributes + + if (.not.associated(arg).or..not.associated(newChild)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + testParent => arg + ! Check if you are allowed to put a newChild nodetype under a arg nodetype + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + do i = 1, newChild%childNodes%length + testChild => newChild%childNodes%nodes(i)%this + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement))) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType)))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + enddo + else + testChild => newChild + select case(testParent%nodeType) + case (ELEMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ATTRIBUTE_NODE) + if (testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (testChild%nodeType==ENTITY_REFERENCE_NODE) then + treeroot => testChild + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)/=TEXT_NODE.and.getNodeType(this)/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.FoX_HIERARCHY_REQUEST_ERR<200) then + call throw_exception(FoX_HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 + + + endif + case (DOCUMENT_NODE) + if ((testChild%nodeType/=ELEMENT_NODE .or. & + (testChild%nodeType==ELEMENT_NODE & + .and.associated(testParent%docExtras%documentElement))) & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. (testChild%nodeType/=DOCUMENT_TYPE_NODE .or. & + (testChild%nodeType==DOCUMENT_TYPE_NODE & + .and.associated(testParent%docExtras%docType)))) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (DOCUMENT_FRAGMENT_NODE) + if (testChild%nodeType/=ELEMENT_NODE & + .and. testChild%nodeType/=TEXT_NODE & + .and. testChild%nodeType/=COMMENT_NODE & + .and. testChild%nodeType/=PROCESSING_INSTRUCTION_NODE & + .and. testChild%nodeType/=CDATA_SECTION_NODE & + .and. testChild%nodeType/=ENTITY_REFERENCE_NODE) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (ENTITY_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case (ENTITY_REFERENCE_NODE) + continue ! only allowed by DOM parser, not by user. + ! but entity nodes are always readonly anyway, so no problem + case default + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + ! And then check that newChild is not arg or one of args ancestors + ! (this would never be true if newChild is a documentFragment) + testParent => arg + do while (associated(testParent)) + if (associated(testParent, newChild)) then + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + testParent => testParent%parentNode + enddo + endif + + if (getNodeType(newChild)/=DOCUMENT_TYPE_NODE.and. & + .not.(associated(arg%ownerDocument, newChild%ownerDocument) & + .or.associated(arg, newChild%ownerDocument))) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "appendChild", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE & + .and. newChild%childNodes%length==0) then + np => newChild + return + ! Nothing to do + endif + + if (associated(getParentNode(newChild))) & + newChild => removeChild(getParentNode(newChild), newChild, ex) + + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + allocate(temp_nl(arg%childNodes%length+newChild%childNodes%length)) + else + allocate(temp_nl(arg%childNodes%length+1)) + endif + + do i = 1, arg%childNodes%length + temp_nl(i)%this => arg%childNodes%nodes(i)%this + enddo + + if (newChild%nodeType==DOCUMENT_FRAGMENT_NODE) then + i_t = arg%childNodes%length + do i = 1, newChild%childNodes%length + i_t = i_t + 1 + temp_nl(i_t)%this => newChild%childNodes%nodes(i)%this + if (arg%inDocument) & + call putNodesInDocument(arg%ownerDocument, temp_nl(i_t)%this) + temp_nl(i_t)%this%parentNode => arg +! call namespaceFixup(temp_nl(i_t)%this) + enddo + if (arg%childNodes%length==0) then + arg%firstChild => newChild%firstChild + else + newChild%firstChild%previousSibling => arg%lastChild + arg%lastChild%nextSibling => newChild%firstChild + endif + arg%lastChild => newChild%lastChild + newChild%firstChild => null() + newChild%lastChild => null() + deallocate(newChild%childNodes%nodes) + allocate(newChild%childNodes%nodes(0)) + newChild%childNodes%length = 0 + else + temp_nl(i)%this => newChild + if (i==1) then + arg%firstChild => newChild + newChild%previousSibling => null() + else + temp_nl(i-1)%this%nextSibling => newChild + newChild%previousSibling => temp_nl(i-1)%this + endif + if (getGCstate(arg%ownerDocument)) then + if (arg%inDocument.and..not.newChild%inDocument) then + call putNodesInDocument(arg%ownerDocument, newChild) + endif + endif + newChild%nextSibling => null() + arg%lastChild => newChild + newChild%parentNode => arg +! call namespaceFixup(newChild) + endif + + deallocate(arg%childNodes%nodes) + arg%childNodes%nodes => temp_nl + arg%childNodes%length = size(temp_nl) + + np => newChild + + call updateNodeLists(arg%ownerDocument) + + call updateTextContentLength(arg, newChild%textContentLength) + + end function appendChild + + + function hasChildNodes(arg, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + logical :: hasChildNodes + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "hasChildNodes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + hasChildNodes = associated(arg%firstChild) + + end function hasChildNodes + + recursive function cloneNode(arg, deep, ex)result(np) + type(DOMException), intent(out), optional :: ex + ! Needs to be recursive in case of entity-references within each other. + type(Node), pointer :: arg + logical, intent(in) :: deep + type(Node), pointer :: np + + type(Node), pointer :: doc, treeroot, thatParent, this, new, ERchild + + logical :: doneAttributes, doneChildren, readonly, brokenNS + integer :: i_tree + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "cloneNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + thatParent => null() + ERchild => null() + doc => getOwnerDocument(arg) + if (.not.associated(doc)) return + np => null() + brokenNS = doc%docExtras%brokenNS + doc%docExtras%brokenNS = .true. ! May need to do stupid NS things + readonly = .false. + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + + new => null() + select case(getNodeType(this)) + case (ELEMENT_NODE) + if (getParameter(getDomConfig(doc), "namespaces")) then + new => createEmptyElementNS(doc, getNamespaceURI(this), getTagName(this)) + else + new => createEmptyElement(doc, getTagName(this)) + endif + case (ATTRIBUTE_NODE) + if (getParameter(getDomConfig(doc), "namespaces")) then + new => createAttributeNS(doc, getNamespaceURI(this), getName(this)) + else + new => createAttribute(doc, getName(this)) + endif + if (associated(this, arg)) then + call setSpecified(new, .true.) + else + call setSpecified(new, getSpecified(this)) + endif + case (TEXT_NODE) + new => createTextNode(doc, getData(this)) + case (CDATA_SECTION_NODE) + new => createCDataSection(doc, getData(this)) + case (ENTITY_REFERENCE_NODE) + ERchild => this + readonly = .true. + new => createEntityReference(doc, getNodeName(this)) + doneChildren = .true. + case (ENTITY_NODE) + return + case (PROCESSING_INSTRUCTION_NODE) + new => createProcessingInstruction(doc, getTarget(this), getData(this)) + case (COMMENT_NODE) + new => createComment(doc, getData(this)) + case (DOCUMENT_NODE) + return + case (DOCUMENT_TYPE_NODE) + return + case (DOCUMENT_FRAGMENT_NODE) + new => createDocumentFragment(doc) + case (NOTATION_NODE) + return + end select + + if (.not.associated(thatParent)) then + thatParent => new + elseif (associated(new)) then + if (this%nodeType==ATTRIBUTE_NODE) then + new => setAttributeNode(thatParent, new) + else + new => appendChild(thatParent, new) + endif + endif + + if (.not.deep) then + if (getNodeType(arg)==ATTRIBUTE_NODE.or.getNodeType(arg)==ELEMENT_NODE) then + continue + else + exit + endif + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + + if (getNodeType(this)==ENTITY_REFERENCE_NODE & + .and.associated(ERchild, this)) then + ERchild => null() + readonly = .false. + endif + this%readonly = readonly + + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + if (.not.associated(this, treeroot)) thatParent => getLastChild(thatParent) + this => item(getAttributes(this), 0) + else + if (.not.deep) exit + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + if (getNodeType(this)==ELEMENT_NODE.and..not.deep) exit + if (.not.associated(this, treeroot)) then + if (getNodeType(this)==ATTRIBUTE_NODE) then + thatParent => item(getAttributes(thatParent), i_tree) + else + thatParent => getLastChild(thatParent) + endif + endif + 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 item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + if (associated(getParentNode(thatParent))) thatParent => getParentNode(thatParent) + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + if (.not.associated(this, treeroot)) then + if (getNodeType(this)==ATTRIBUTE_NODE) then + thatParent => getOwnerElement(thatParent) + else + thatParent => getParentNode(thatParent) + endif + endif + endif + endif + + enddo + + + + np => thatParent + doc%docExtras%brokenNS = brokenNS + + end function cloneNode + + + function hasAttributes(arg, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + logical :: hasAttributes + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "hasAttributes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType == ELEMENT_NODE) then + hasAttributes = (getLength(getAttributes(arg)) > 0) + else + hasAttributes = .false. + endif + + end function hasAttributes + +! function getBaseURI FIXME + +! function compareDocumentPosition FIXME + + subroutine normalize(arg, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: this, tempNode, oldNode, treeroot + integer :: i_tree, i_t + logical :: doneChildren, doneAttributes + character, pointer :: temp(:) + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "normalize", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +! DOM standard requires we ignore readonly status + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + + if (getNodeType(this)==TEXT_NODE) then + if (associated(this, arg)) exit ! If we are called on a text node itself, then do nothing. + i_t = getLength(this) + tempNode => getNextSibling(this) + do while (associated(tempNode)) + if (getNodeType(tempNode)/=TEXT_NODE) exit + i_t = i_t + getLength(tempNode) + tempNode => getNextSibling(tempNode) + enddo + if (.not.associated(tempNode, getNextSibling(this))) then + allocate(temp(i_t)) + temp(:getLength(this)) = vs_str(getData(this)) + i_t = getLength(this) + tempNode => getNextSibling(this) + do while (associated(tempNode)) + if (getNodeType(tempNode)/=TEXT_NODE) exit + temp(i_t+1:i_t+getLength(tempNode)) = vs_str(getData(tempNode)) + i_t = i_t + getLength(tempNode) + oldNode => tempNode + tempNode => getNextSibling(tempNode) + oldNode => removeChild(getParentNode(oldNode), oldNode) + call remove_node_nl(arg%ownerDocument%docExtras%hangingNodes, oldNode) + call destroy(oldNode) + enddo + deallocate(this%nodeValue) + this%nodeValue => temp + endif + end if + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 normalize + + function isSupported(arg, feature, version, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: feature + character(len=*), intent(in) :: version + logical :: p + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "isSupported", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + p = hasFeature(getImplementation(arg%ownerDocument), feature, version) + end function isSupported + + pure function getNamespaceURI_len(arg, p) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (p) then + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + n = size(arg%elExtras%namespaceURI) + endif + endif + + end function getNamespaceURI_len + + function getNamespaceURI(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getNamespaceURI_len(arg, .true.)) :: c +#else + character(len=getNamespaceURI_len(arg, associated(arg))) :: c +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getNamespaceURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = "" + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + c = str_vs(arg%elExtras%namespaceURI) + endif + end function getNamespaceURI + +subroutine setnamespaceURI(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*) :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setnamespaceURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=XPATH_NAMESPACE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setnamespaceURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%elExtras%namespaceURI)) deallocate(np%elExtras%namespaceURI) + np%elExtras%namespaceURI => vs_str_alloc(c) + + end subroutine setnamespaceURI + + + pure function getPrefix_len(arg, p) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (p) then + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + n = size(arg%elExtras%prefix) + endif + endif + + end function getPrefix_len + + function getPrefix(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getPrefix_len(arg, .true.)) :: c +#else + character(len=getPrefix_len(arg, associated(arg))) :: c +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = "" + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + c = str_vs(arg%elExtras%prefix) + endif + + end function getPrefix + + subroutine setPrefix(arg, prefix, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*) :: prefix + + character, pointer :: tmp(:) + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(prefix, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkNCName(prefix, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (size(arg%elExtras%namespaceURI)==0) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefix=="xml" .and. & + str_vs(arg%elExtras%namespaceURI)/="http://www.w3.org/XML/1998/namespace") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefix=="xmlns" .and. (getNodeType(arg)/=ATTRIBUTE_NODE & + .or. str_vs(arg%elExtras%namespaceURI)/="http://www.w3.org/2000/xmlns/")) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)==ATTRIBUTE_NODE.and.getName(arg)=="xmlns") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif +! FIXME check if prefix is declared and already points to same namespace +! but only if we ever get full error-checking up and running. + deallocate(arg%elExtras%prefix) + arg%elExtras%prefix => vs_str_alloc(prefix) + tmp => arg%nodeName + i = index(str_vs(arg%nodeName), ":") + if (i==0) then + arg%nodeName => vs_str_alloc(prefix//":"//str_vs(tmp)) + else + arg%nodeName => vs_str_alloc(prefix//str_vs(tmp(i:))) + endif + deallocate(tmp) + endif + + call updateNodeLists(arg%ownerDocument) + + end subroutine setPrefix + + pure function getLocalName_len(arg, p) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (p) then + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + n = size(arg%elExtras%localName) + endif + endif + + end function getLocalName_len + + function getLocalName(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getLocalName_len(arg, .true.)) :: c +#else + character(len=getLocalName_len(arg, associated(arg))) :: c +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getLocalName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = "" + if (arg%nodeType==ELEMENT_NODE & + .or. arg%nodeType==ATTRIBUTE_NODE & + .or. arg%nodeType==XPATH_NAMESPACE_NODE) then + c = str_vs(arg%elExtras%localName) + endif + + end function getLocalName + + recursive function isEqualNode(arg, other, ex)result(p) + type(DOMException), intent(out), optional :: ex + ! We only have one level of recursion, in case of element attributes + type(Node), pointer :: arg + type(Node), pointer :: other + logical :: p + + type(Node), pointer :: this, that, treeroot, treeroot2, att1, att2 + type(NodeList), pointer :: children1, children2 + type(NamedNodeMap), pointer :: atts1, atts2 + + integer :: i_tree, i + logical :: doneChildren, doneAttributes, equal + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "isEqualNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (isSameNode(arg, other)) then + ! Shortcut the treewalking + p = .true. + return + else + p = .false. + endif + + treeroot => arg + treeroot2 => other + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + that => treeroot2 + equal = .false. + do + if (getNodeType(this)/=getNodeType(that)) exit + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + + if (getNodeType(this)/=getNodeType(that)) return + ! Check necessary equal attributes ... + if (getNodeName(this)/=getNodeName(that) & + .or. getLocalName(this)/=getLocalName(that) & + .or. getNamespaceURI(this)/=getNamespaceURI(that) & + .or. getPrefix(this)/=getPrefix(that) & + .or. getNodeValue(this)/=getNodeValue(that)) & + return + children1 => getChildNodes(this) + children2 => getChildNodes(that) + if (getLength(children1)/=getLength(children2)) return + ! Well get to the contents of the children later on anyway. + if (getNodeType(this)==ELEMENT_NODE) then + ! We must treat attributes specially here (rather than relying on + ! treewalk) since the order can legitimately change. + atts1 => getAttributes(this) + atts2 => getAttributes(that) + if (getLength(atts1)/=getLength(atts2)) return + do i = 0, getLength(atts1)-1 + att1 => item(atts1, i) + if (getNamespaceURI(att1)=="") then + att2 => getNamedItem(atts2, getNodeName(att1)) + else + att2 => getNamedItemNS(atts2, getLocalName(att1), getNamespaceURI(att1)) + endif + if (.not.associated(att2)) return + if (.not.isEqualNode(att1, att2)) return + enddo + doneAttributes = .true. + elseif (getNodeType(this)==DOCUMENT_TYPE_NODE) then + if (getPublicId(this)/=getPublicId(that) & + .or. getSystemId(this)/=getSystemId(that) & + .or. getInternalSubset(this)/=getInternalSubset(that)) return + atts1 => getEntities(this) + atts2 => getEntities(that) + if (getLength(atts1)/=getLength(atts2)) return + do i = 0, getLength(atts1)-1 + att1 => item(atts1, i) + att2 => getNamedItem(atts2, getNodeName(att1)) + if (.not.associated(att2)) return + if (.not.isEqualNode(att1, att2)) return + enddo + atts1 => getNotations(this) + atts2 => getNotations(that) + if (getLength(atts1)/=getLength(atts2)) return + do i = 0, getLength(atts1)-1 + att1 => item(atts1, i) + att2 => getNamedItem(atts2, getNodeName(att1)) + if (.not.associated(att2)) return + if (.not.isEqualNode(att1, att2)) return + enddo + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))/=getLength(getAttributes(that))) exit + if (getLength(getAttributes(this))>0) then + this => item(getAttributes(this), 0) + that => item(getAttributes(that), 0) + else + doneAttributes = .true. + endif + elseif (hasChildNodes(this).or.hasChildNodes(that)) then + if (getLength(getChildNodes(this))/=getLength(getChildNodes(that))) exit + this => getFirstChild(this) + that => getFirstChild(that) + 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 item(getAttributes(getOwnerElement(this)), i_tree) + that => item(getAttributes(getOwnerElement(that)), i_tree) + doneChildren = .false. + else + i_tree= 0 + this => getOwnerElement(this) + that => getOwnerElement(that) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + that => getNextSibling(that) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + that => getParentNode(that) + endif + endif + + enddo + + + + p = .true. + + end function isEqualNode + + + function isSameNode(arg, other, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: other + logical :: isSameNode + + if (.not.associated(arg).or..not.associated(other)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "isSameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + isSameNode = associated(arg, other) + + end function isSameNode + + !FIXME all the lookup* functions below are out of spec, + ! since they rely on a statically-calculated set of NSnodes + ! which is only generated at parse time, and updated after + ! normalize. + ! the spec reckons it should be dynamic, but because we need + ! to know string lengths, which must be calculated inside + ! a pure function, we cant do the recursive walk we need to. + ! (although isDefaultNamespace could be fixed easily enough) + + function isDefaultNamespace(np, namespaceURI, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*), intent(in) :: namespaceURI + logical :: p + + type(Node), pointer :: el + integer :: i + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "isDefaultNamespace", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + el => null() + select case(getNodeType(np)) + case (ELEMENT_NODE) + el => np + case (ATTRIBUTE_NODE) + el => getOwnerElement(np) + case (DOCUMENT_NODE) + el => getDocumentElement(np) + end select + + p = .false. + if (associated(el)) then + do i = 1, el%elExtras%namespaceNodes%length + if (size(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix)==0) then + p = (str_vs(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) + return + endif + enddo + endif + end function isDefaultNamespace + + pure function lookupNamespaceURI_len(np, prefix, p) result(n) + type(Node), intent(in) :: np + character(len=*), intent(in) :: prefix + logical, intent(in) :: p + integer :: n + + integer :: i + + n = 0 + if (.not.p) return + if (np%nodeType/=ELEMENT_NODE & + .and. np%nodeType/=ATTRIBUTE_NODE & + .and. np%nodeType/=DOCUMENT_NODE) return + + if (prefix=="xml".or.prefix=="xmlns") then + n = 0 + return + endif + + select case(np%nodeType) + case (ELEMENT_NODE) + do i = 1, np%elExtras%namespaceNodes%length + if (str_vs(np%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix)==prefix) then + n = size(np%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI) + return + endif + enddo + case (ATTRIBUTE_NODE) + if (associated(np%elExtras%ownerElement)) then + do i = 1, np%elExtras%ownerElement%elExtras%namespaceNodes%length + if (str_vs(np%elExtras%ownerElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix)==prefix) then + n = size(np%elExtras%ownerElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI) + return + endif + enddo + endif + case (DOCUMENT_NODE) + if (associated(np%docExtras%documentElement)) then + do i = 1, np%docExtras%documentElement%elExtras%namespaceNodes%length + if (str_vs(np%docExtras%documentElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix)==prefix) then + n = size(np%docExtras%documentElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI) + return + endif + enddo + endif + end select + + end function lookupNamespaceURI_len + + function lookupNamespaceURI(np, prefix, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*), intent(in) :: prefix +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=lookupNamespaceURI_len(np, prefix, .true.)) :: c +#else + character(len=lookupNamespaceURI_len(np, prefix, associated(np))) :: c +#endif + + type(Node), pointer :: el + integer :: i + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "lookupNamespaceURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(c)==0) then + c = "" + return + endif + + el => null() + select case(getNodeType(np)) + case (ELEMENT_NODE) + el => np + case (ATTRIBUTE_NODE) + el => getOwnerElement(np) + case (DOCUMENT_NODE) + el => getDocumentElement(np) + end select + + if (associated(el)) then + do i = 1, el%elExtras%namespaceNodes%length + if (str_vs(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix)==prefix) then + c = str_vs(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI) + return + endif + enddo + endif + + end function lookupNamespaceURI + + pure function lookupPrefix_len(np, namespaceURI, p) result(n) + type(Node), intent(in) :: np + character(len=*), intent(in) :: namespaceURI + logical, intent(in) :: p + integer :: n + + integer :: i + + n = 0 + if (.not.p) return + if (np%nodeType/=ELEMENT_NODE & + .and. np%nodeType/=ATTRIBUTE_NODE & + .and. np%nodeType/=DOCUMENT_NODE) return + + if (namespaceURI=="" & + .or. namespaceURI=="http://www.w3.org/XML/1998/namespace" & + .or. namespaceURI=="http://www.w3.org/2000/xmlns/") then + return + endif + + select case(np%nodeType) + case (ELEMENT_NODE) + do i = 1, np%elExtras%namespaceNodes%length + if (str_vs(np%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) then + n = size(np%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix) + return + endif + enddo + case (ATTRIBUTE_NODE) + if (associated(np%elExtras%ownerElement)) then + do i = 1, np%elExtras%ownerElement%elExtras%namespaceNodes%length + if (str_vs(np%elExtras%ownerElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) then + n = size(np%elExtras%ownerElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix) + return + endif + enddo + endif + case (DOCUMENT_NODE) + if (associated(np%docExtras%documentElement)) then + do i = 1, np%docExtras%documentElement%elExtras%namespaceNodes%length + if (str_vs(np%docExtras%documentElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) then + n = size(np%docExtras%documentElement%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix) + return + endif + enddo + endif + end select + + end function lookupPrefix_len + + function lookupPrefix(np, namespaceURI, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*), intent(in) :: namespaceURI +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=lookupPrefix_len(np, namespaceURI, .true.)) :: c +#else + character(len=lookupPrefix_len(np, namespaceURI, associated(np))) :: c +#endif + + type(Node), pointer :: el + integer :: i + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "lookupPrefix", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(c)==0) then + c = "" + return + endif + + el => null() + select case(getNodeType(np)) + case (ELEMENT_NODE) + el => np + case (ATTRIBUTE_NODE) + el => getOwnerElement(np) + case (DOCUMENT_NODE) + el => getDocumentElement(np) + end select + + if (associated(el)) then + do i = 1, el%elExtras%namespaceNodes%length + if (str_vs(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) then + c = str_vs(el%elExtras%namespaceNodes%nodes(i)%this%elExtras%prefix) + return + endif + enddo + endif + end function lookupPrefix + + ! function getUserData + ! function setUserData + ! will not implement ... + + subroutine updateTextContentLength(np, n) + type(Node), pointer :: np + integer, intent(in) :: n + + type(Node), pointer :: this + + if (n/=0) then + this => np + do while (associated(this)) + this%textContentLength = this%textContentLength + n + this => getParentNode(this) + if (associated(this)) then + if (getNodeType(this)==DOCUMENT_NODE) exit + endif + enddo + endif + end subroutine updateTextContentLength + + pure function getTextContent_len(arg, p) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + integer :: n + + if (p) then + n = arg%textContentLength + else + n = 0 + endif + end function getTextContent_len + + function getTextContent(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getTextContent_len(arg, .true.)) :: c +#else + character(len=getTextContent_len(arg, associated(arg))) :: c +#endif + + type(Node), pointer :: this, treeroot + integer :: i, i_tree + logical :: doneChildren, doneAttributes + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getTextContent", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(c) == 0) then + c = "" + return + endif + + i = 1 + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (associated(this, treeroot).and.isCharData(getNodeType(this))) then + c = getData(this) + return + endif + select case(getNodeType(this)) + case (ELEMENT_NODE) + doneAttributes = .true. + ! Ignore attributes for text content (unless this is an attribute!) + case(TEXT_NODE, CDATA_SECTION_NODE) + if (.not.getIsElementContentWhitespace(this)) then + c(i:i+size(this%nodeValue)-1) = str_vs(this%nodeValue) + i = i + size(this%nodeValue) + endif + end select + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 function getTextContent + + subroutine setTextContent(arg, textContent, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: textContent + + type(Node), pointer :: np + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setTextContent", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.checkChars(textContent, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "setTextContent", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + select case(getNodeType(arg)) + case (ELEMENT_NODE, ATTRIBUTE_NODE, DOCUMENT_FRAGMENT_NODE) + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setTextContent", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + do i = 1, getLength(getChildNodes(arg)) + call destroyNode(arg%childNodes%nodes(i)%this) + enddo + deallocate(arg%childNodes%nodes) + allocate(arg%childNodes%nodes(0)) + arg%childNodes%length = 0 + arg%firstChild => null() + arg%lastChild => null() + arg%textContentLength = 0 + np => createTextNode(getOwnerDocument(arg), textContent) + np => appendChild(arg, np) + case (TEXT_NODE, CDATA_SECTION_NODE, PROCESSING_INSTRUCTION_NODE, COMMENT_NODE) + call setData(arg, textContent) + case (ENTITY_NODE, ENTITY_REFERENCE_NODE) + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setTextContent", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + end subroutine setTextContent + + function getBaseURI(arg, ex)result(baseURI) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=200) :: baseURI + + type(Node), pointer :: el + type(URI), pointer :: URIref, URIbase, newURI + + select case(getNodeType(arg)) + case (ELEMENT_NODE) + el => arg + case (ATTRIBUTE_NODE) + if (getName(arg)=="xml:base") then + if (associated(getOwnerElement(arg))) then + el => getParentNode(getOwnerElement(arg)) + else + el => null() + endif + else + el => getOwnerElement(arg) + endif + case (TEXT_NODE) + ! then are we in an attribute or textContent? + el => getParentNode(arg) + do while (associated(el)) + if (getNodeType(el)==ELEMENT_NODE) then + exit + elseif (getNodeType(el)==ATTRIBUTE_NODE) then + el => getOwnerElement(el) + exit + else + el => getParentNode(el) + endif + enddo + case (PROCESSING_INSTRUCTION_NODE) + ! then are we in or out of element content? + el => getParentNode(arg) + do while (associated(el)) + if (getNodeType(el)==ELEMENT_NODE) then + exit + elseif (getNodeType(el)==DOCUMENT_NODE) then + el => getOwnerElement(el) + exit + else + el => getParentNode(el) + endif + enddo + case default + el => null() + end select + + URIref => parseURI("") + + do while (associated(el)) + select case (getNodeType(el)) + case (ELEMENT_NODE) + if (hasAttribute(el, "xml:base")) then + URIbase => parseURI(getAttribute(el, "xml:base")) + newURI => rebaseURI(URIbase, URIref) + call destroyURI(URIbase) + call destroyURI(URIref) + URIref => newURI + if (isAbsoluteURI(URIref)) exit + endif + case (ENTITY_REFERENCE_NODE) + if (getSystemId(el)/="") then + URIbase => parseURI(getSystemId(el)) + newURI => rebaseURI(URIbase, URIref) + call destroyURI(URIbase) + call destroyURI(URIref) + URIref => newURI + if (isAbsoluteURI(URIref)) exit + endif + case default + exit + end select + el => getParentNode(el) + end do + + if (isAbsoluteURI(URIref)) then + baseURI = expressURI(URIref) + else + baseURI = "" + endif + call destroyURI(URIref) + + end function getBaseURI + + recursive function getNodePath(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + ! recursive only for atts and text + type(Node), pointer :: arg + character(len=100) :: c + + type(Node), pointer :: this, this2 + character(len=len(c)) :: c2 + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getNodePath", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = "" + if (.not.arg%inDocument) return + select case(getNodeType(arg)) + + case (ELEMENT_NODE) + this => arg + do while (getNodeType(this)/=DOCUMENT_NODE) + c2 = "" + this2 => getPreviousSibling(this) + n = 0 + do while (associated(this2)) + if (getNodeType(this2)==ELEMENT_NODE & + .and.getNodeName(this2)==getNodeName(this)) n = n + 1 + this2 => getPreviousSibling(this2) + enddo + if (n==0) then + this2 => getNextSibling(this) + do while (associated(this2)) + if (getNodeType(this2)==ELEMENT_NODE & + .and.getNodeName(this2)==getNodeName(this)) then + n = 1 + exit + endif + this2 => getNextSibling(this2) + enddo + else + n = n + 1 + endif + if (n>0) c2 = "["//n//"]" + ! What name to use: + if (getNamespaceURI(this)/="".and.getPrefix(this)=="") then + ! default namespace; need to do the * trick + ! how many previous siblings? + c2 = "/*"//c2 + else + c2 = "/"//getNodeName(this)//c2 + endif + c = trim(c2)//c + this => getParentNode(this) + enddo + + case (ATTRIBUTE_NODE) + c = trim(getNodePath(getOwnerElement(arg)))//"/@"//getNodeName(arg) + + case (TEXT_NODE, CDATA_SECTION_NODE) + ! FIXME this will give wrong answers sometimes if + ! the tree contains entity references + this => getParentNode(arg) + do while (associated(this)) + if (getNodeType(this)==ELEMENT_NODE) exit + this => getParentNode(this) + enddo + if (getNodeType(this)/=ELEMENT_NODE) & + this => getOwnerElement(this) + c = trim(getNodePath(this))//"/text()" + this => getPreviousSibling(arg) + n = 0 + do while (associated(this)) + if (getNodeType(this)==TEXT_NODE & + .or.getNodeType(this)==CDATA_SECTION_NODE) n = n + 1 + this => getPreviousSibling(this) + enddo + if (n==0) then + this => getNextSibling(arg) + do while (associated(this)) + if (getNodeType(this)==COMMENT_NODE & + .or.getNodeType(this)==CDATA_SECTION_NODE) then + n = 1 + exit + endif + this => getNextSibling(this) + enddo + else + n = n + 1 + endif + if (n>0) c = trim(c)//"["//n//"]" + + case (PROCESSING_INSTRUCTION_NODE) + this => getParentNode(arg) + c = trim(getNodePath(this))//"/processing-instruction("//getNodeName(arg)//")" + this => getPreviousSibling(arg) + n = 0 + do while (associated(this)) + if (getNodeType(this)==PROCESSING_INSTRUCTION_NODE & + .and.getNodeName(this)==getNodeName(arg)) n = n + 1 + this => getPreviousSibling(this) + enddo + if (n==0) then + this => getNextSibling(arg) + do while (associated(this)) + if (getNodeType(this)==PROCESSING_INSTRUCTION_NODE & + .and.getNodeName(this)==getNodeName(arg)) then + n = 1 + exit + endif + this => getNextSibling(this) + enddo + else + n = n + 1 + endif + if (n>0) c = trim(c)//"["//n//"]" + + case (COMMENT_NODE) + this => getParentNode(arg) + c = trim(getNodePath(this))//"/comment()" + this => getPreviousSibling(arg) + n = 0 + do while (associated(this)) + if (getNodeType(this)==COMMENT_NODE) n = n + 1 + this => getPreviousSibling(this) + enddo + if (n==0) then + this => getNextSibling(arg) + do while (associated(this)) + if (getNodeType(this)==COMMENT_NODE) then + n = 1 + exit + endif + this => getNextSibling(this) + enddo + else + n = n + 1 + endif + if (n>0) c = trim(c)//"["//n//"]" + + case (DOCUMENT_NODE) + c = "/" + + case (XPATH_NAMESPACE_NODE) + this => getOwnerElement(arg) + if (getPrefix(arg)=="") then + c = trim(getNodePath(this))//"/namespace::xmlns" + else + c = trim(getNodePath(this))//"/namespace::"//getPrefix(arg) + endif + ! FIXME namespace nodes are not marked as inDocument correctly + + end select + + end function getNodePath + + subroutine putNodesInDocument(doc, arg) + type(Node), pointer :: doc, arg + type(Node), pointer :: this, treeroot + logical :: doneChildren, doneAttributes + integer :: i_tree + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + this%inDocument = .true. + call remove_node_nl(doc%docExtras%hangingNodes, this) + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 putNodesInDocument + + subroutine removeNodesFromDocument(doc, arg) + type(Node), pointer :: doc, arg + type(Node), pointer :: this, treeroot + logical :: doneChildren, doneAttributes + integer :: i_tree + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + this%inDocument = .false. + call append_nl(doc%docExtras%hangingNodes, this) + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 removeNodesFromDocument + + subroutine setReadOnlyNode(arg, p, deep) + type(Node), pointer :: arg + logical, intent(in) :: p + logical, intent(in) :: deep + + type(Node), pointer :: this, treeroot + integer :: i_tree + logical :: doneAttributes, doneChildren + + if (deep) then + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + this%readonly = p + if (this%nodeType==ELEMENT_NODE) & + this%elExtras%attributes%readonly = p + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 + + + else + arg%readonly = p + if (arg%nodeType==ELEMENT_NODE) & + arg%elExtras%attributes%readonly = p + endif + + end subroutine setReadOnlyNode + +function getreadonly(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getreadonly", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + c = np%readonly + + end function getreadonly + + + + + function item_nl(list, index, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NodeList), pointer :: list + integer, intent(in) :: index + type(Node), pointer :: np + + if (.not.associated(list)) then + if (getFoX_checks().or.FoX_LIST_IS_NULL<200) then + call throw_exception(FoX_LIST_IS_NULL, "item_nl", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (index>=0.and.index list%nodes(index+1)%this + else + np => null() + endif + + end function item_nl + + subroutine append_nl(list, arg) + type(NodeList), intent(inout) :: list + type(Node), pointer :: arg + + type(ListNode), pointer :: temp_nl(:) + integer :: i + + if (.not.associated(list%nodes)) then + allocate(list%nodes(1)) + list%nodes(1)%this => arg + list%length = 1 + else + temp_nl => list%nodes + allocate(list%nodes(size(temp_nl)+1)) + do i = 1, size(temp_nl) + list%nodes(i)%this => temp_nl(i)%this + enddo + deallocate(temp_nl) + list%nodes(size(list%nodes))%this => arg + list%length = size(list%nodes) + endif + + end subroutine append_nl + + function pop_nl(list, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NodeList), pointer :: list + type(Node), pointer :: np + + type(ListNode), pointer :: temp_nl(:) + integer :: i + + if (list%length==0) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "pop_nl", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => list%nodes(size(list%nodes))%this + + if (list%length==1) then + deallocate(list%nodes) + list%length = 0 + else + temp_nl => list%nodes + allocate(list%nodes(size(temp_nl)-1)) + do i = 1, size(temp_nl)-1 + list%nodes(i)%this => temp_nl(i)%this + enddo + deallocate(temp_nl) + list%length = size(list%nodes) + endif + + end function pop_nl + + + function remove_nl(nl, index, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NodeList), intent(inout) :: nl + integer, intent(in) :: index + type(Node), pointer :: np + + type(ListNode), pointer :: temp_nl(:) + + integer :: i + + if (index>nl%length) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "remove_nl", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => nl%nodes(index)%this + temp_nl => nl%nodes + allocate(nl%nodes(size(temp_nl)-1)) + nl%length = nl%length - 1 + do i = 1, index - 1 + nl%nodes(i)%this => temp_nl(i)%this + enddo + do i = index, nl%length + nl%nodes(i)%this => temp_nl(i+1)%this + enddo + deallocate(temp_nl) + + end function remove_nl + + + subroutine remove_node_nl(nl, np) + type(NodeList), intent(inout) :: nl + type(Node), pointer :: np + + integer :: i + + do i = 1, nl%length + if (associated(nl%nodes(i)%this, np)) exit + enddo + np => remove_nl(nl, i) + + end subroutine remove_node_nl + + + function getLength_nl(nl, ex)result(n) + type(DOMException), intent(out), optional :: ex + type(NodeList), pointer :: nl + integer :: n + + if (.not.associated(nl)) then + if (getFoX_checks().or.FoX_LIST_IS_NULL<200) then + call throw_exception(FoX_LIST_IS_NULL, "getLength_nl", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + n = size(nl%nodes) + end function getLength_nl + + subroutine destroyNodeList(nl) + type(NodeList), pointer :: nl + + if (associated(nl%nodes)) deallocate(nl%nodes) + if (associated(nl%nodeName)) deallocate(nl%nodeName) + if (associated(nl%localName)) deallocate(nl%localName) + if (associated(nl%namespaceURI)) deallocate(nl%namespaceURI) + deallocate(nl) + end subroutine destroyNodeList + + subroutine updateNodeLists(doc) + ! When triggered, update all nodelists + type(Node), pointer :: doc + + type(NodeList), pointer :: nl, nl_orig + type(NodeListPtr), pointer :: temp_nll(:) + integer :: i, i_t + + if (.not.getGCstate(doc)) return + if (.not.doc%docExtras%liveNodeLists) return + if (.not.associated(doc%docExtras%nodelists)) return + + ! We point the old list of nodelists to temp_nll, then recalculate + ! them all (which repopulates nodelists) + temp_nll => doc%docExtras%nodelists + i_t = size(temp_nll) + allocate(doc%docExtras%nodelists(0)) + do i = 1, i_t + nl_orig => temp_nll(i)%this + ! + ! Although all nodes should be searched whatever the result, + ! we should only do the appropriate sort of search for this + ! list - according to namespaces or not. + ! + if (associated(nl_orig%nodeName)) then + ! this was made by getElementsByTagName + nl => getElementsByTagName(nl_orig%element, str_vs(nl_orig%nodeName)) + elseif (associated(nl_orig%namespaceURI)) then + ! this was made by getElementsByTagNameNS + nl => getElementsByTagNameNS(nl_orig%element, & + str_vs(nl_orig%localName), str_vs(nl_orig%namespaceURI)) + endif + enddo + ! We dont care about the nodelists weve calculated now + nullify(nl) + + deallocate(temp_nll) + + end subroutine updateNodeLists + + + + function getNamedItem(map, name, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + character(len=*), intent(in) :: name + type(Node), pointer :: np + + integer :: i + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "getNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + do i = 1, map%length + if (str_vs(map%nodes(i)%this%nodeName)==name) then + np => map%nodes(i)%this + return + endif + enddo + + np => null() + + end function getNamedItem + + + function setNamedItem(map, arg, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + type(Node), pointer :: arg + type(Node), pointer :: np + + integer :: i + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (map%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (map%ownerElement%nodeType==ELEMENT_NODE) then + if (.not.associated(map%ownerElement%ownerDocument, arg%ownerDocument)) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ATTRIBUTE_NODE) then + !Additional check from DOM 3 + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + endif + + if (getNodeType(arg)==ATTRIBUTE_NODE) then + if (associated(map%ownerElement, getOwnerElement(arg))) then + ! we are looking at literally the same node + np => arg + return + elseif (associated(getOwnerElement(arg))) then + if (getFoX_checks().or.INUSE_ATTRIBUTE_ERR<200) then + call throw_exception(INUSE_ATTRIBUTE_ERR, "setNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + arg%elExtras%ownerElement => map%ownerElement + endif + + do i = 0, getLength(map)-1 + np => item(map, i) + if (getNodeName(np)==getNodeName(arg)) then + map%nodes(i+1)%this => arg + exit + endif + enddo + + if (i null() + call append_nnm(map, arg) + endif + + if (map%ownerElement%nodeType==ELEMENT_NODE) then + if (getGCstate(getOwnerDocument(map%ownerElement))) then + ! We need to worry about importing this node + if (map%ownerElement%inDocument) then + if (.not.arg%inDocument) & + call putNodesInDocument(getOwnerDocument(map%ownerElement), arg) + else + if (arg%inDocument) & + call removeNodesFromDocument(getOwnerDocument(map%ownerElement), arg) + endif + endif + endif + ! Otherwise we only ever setNNM when building the doc, so we know this + ! does not matter + + end function setNamedItem + + + function removeNamedItem(map, name, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + character(len=*), intent(in) :: name + type(Node), pointer :: np + + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + type(ListNode), pointer :: temp_nl(:) + integer :: i, i2 + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "removeNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (map%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "removeNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + do i = 0, map%length-1 + np => item(map, i) + if (getNodeName(np)==name) then + xds => getXds(getOwnerDocument(map%ownerElement)) + elem => get_element(xds%element_list, getNodeName(map%ownerElement)) + att => get_attribute_declaration(elem, name) + if (associated(att)) then + if (attribute_has_default(att)) then ! there is a default value + ! Well swap the old one out & put a new one in. + ! Do *nothing* about namespace handling at this stage, + ! wait until we are asked for namespace normalization + if (getParameter( & + getDomConfig(getOwnerDocument(map%ownerElement)), & + "namespaces")) then + np => createAttributeNS(getOwnerDocument(map%ownerElement), "", name) + else + np => createAttribute(getOwnerDocument(map%ownerElement), name) + endif + call setValue(np, str_vs(att%default)) + call setSpecified(np, .false.) + np => setNamedItem(map, np) + call setSpecified(np, .true.) + return + endif + endif + ! Otherwise there was no default value, so we just remove the node. + ! Grab this node + if (getNodeType(np)==ATTRIBUTE_NODE) np%elExtras%ownerElement => null() + ! and shrink the node list + temp_nl => map%nodes + allocate(map%nodes(size(temp_nl)-1)) + do i2 = 1, i + map%nodes(i2)%this => temp_nl(i2)%this + enddo + do i2 = i + 2, map%length + map%nodes(i2-1)%this => temp_nl(i2)%this + enddo + map%length = size(map%nodes) + deallocate(temp_nl) + if (np%inDocument.and.getGCstate(getOwnerDocument(map%ownerElement))) & + call removeNodesFromDocument(getOwnerDocument(map%ownerElement), np) + !otherwise we are only going to destroy these nodes anyway, + ! and finish + return + endif + enddo + + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "removeNamedItem", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + + end function removeNamedItem + + + function item_nnm(map, index, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + integer, intent(in) :: index + type(Node), pointer :: np + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "item_nnm", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (index<0 .or. index>map%length-1) then + np => null() + else + np => map%nodes(index+1)%this + endif + + end function item_nnm + + function getLength_nnm(map, ex)result(n) + type(DOMException), intent(out), optional :: ex + type(namedNodeMap), pointer :: map + integer :: n + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "getLength_nnm", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + n = map%length + + end function getLength_nnm + + + function getNamedItemNS(map, namespaceURI, localName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + type(Node), pointer :: np + + integer :: i + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "getNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (map%ownerElement%nodeType/=ELEMENT_NODE) then + np => null() + return + endif + + do i = 0, getLength(map) - 1 + np => item(map, i) + if (getNamespaceURI(np)==namespaceURI & + .and. getLocalName(np)==localName) then + return + endif + enddo + np => null() + + end function getNamedItemNS + + + function setNamedItemNS(map, arg, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + type(Node), pointer :: arg + type(Node), pointer :: np + + integer :: i + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (map%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (map%ownerElement%nodeType==ELEMENT_NODE) then + if (.not.associated(map%ownerElement%ownerDocument, arg%ownerDocument)) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ATTRIBUTE_NODE) then + !Additional check from DOM 3 + if (getFoX_checks().or.HIERARCHY_REQUEST_ERR<200) then + call throw_exception(HIERARCHY_REQUEST_ERR, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + endif + + if (getNodeType(arg)==ATTRIBUTE_NODE) then + if (associated(map%ownerElement, getOwnerElement(arg))) then + ! we are looking at literally the same node, so do nothing else + np => arg + return + elseif (associated(getOwnerElement(arg))) then + if (getFoX_checks().or.INUSE_ATTRIBUTE_ERR<200) then + call throw_exception(INUSE_ATTRIBUTE_ERR, "setNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + arg%elExtras%ownerElement => map%ownerElement + endif + + do i = 0, getLength(map) - 1 + np => item(map, i) + if ((getLocalName(arg)==getLocalName(np) & + .and.getNamespaceURI(arg)==getNamespaceURI(np)) & + ! Additional case to catch adding of specified attributeNS over + ! default (NS but unspecified URI) attribute + .or.(getNamespaceURI(arg)=="".and.getName(arg)==getName(np))) then + map%nodes(i+1)%this => arg + exit + endif + enddo + + if (i null() + call append_nnm(map, arg) + endif + + if (map%ownerElement%nodeType==ELEMENT_NODE) then + if (getGCstate(getOwnerDocument(map%ownerElement))) then + ! We need to worry about importing this node + if (map%ownerElement%inDocument) then + if (.not.arg%inDocument) & + call putNodesInDocument(getOwnerDocument(map%ownerElement), arg) + else + if (arg%inDocument) & + call removeNodesFromDocument(getOwnerDocument(map%ownerElement), arg) + endif + endif + endif + + end function setNamedItemNS + + + function removeNamedItemNS(map, namespaceURI, localName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(NamedNodeMap), pointer :: map + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + type(Node), pointer :: np + + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + type(ListNode), pointer :: temp_nl(:) + integer :: i, i2 + + if (.not.associated(map)) then + if (getFoX_checks().or.FoX_MAP_IS_NULL<200) then + call throw_exception(FoX_MAP_IS_NULL, "removeNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (map%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "removeNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + do i = 0, getLength(map) - 1 + np => item(map, i) + if (getNamespaceURI(np)==namespaceURI & + .and. getLocalName(np)==localName) then + ! Grab this node + xds => getXds(getOwnerDocument(map%ownerElement)) + elem => get_element(xds%element_list, getNodeName(map%ownerElement)) + att => get_attribute_declaration(elem, getName(np)) + if (associated(att)) then + if (attribute_has_default(att)) then ! there is a default value + ! Well swap the old one out & put a new one in. + ! Do *nothing* about namespace handling at this stage, + ! wait until we are asked for namespace normalization + np => createAttributeNS(getOwnerDocument(map%ownerElement), getNamespaceURI(np), getName(np)) + call setValue(np, str_vs(att%default)) + call setSpecified(np, .false.) + np => setNamedItemNS(map, np) + call setSpecified(np, .true.) + return + endif + endif + ! Otherwise there was no default value, so we just remove the node. + ! and shrink the node list + if (getNodeType(np)==ATTRIBUTE_NODE) np%elExtras%ownerElement => null() + temp_nl => map%nodes + allocate(map%nodes(size(temp_nl)-1)) + do i2 = 1, i + map%nodes(i2)%this => temp_nl(i2)%this + enddo + do i2 = i + 2, map%length + map%nodes(i2-1)%this => temp_nl(i2)%this + enddo + map%length = size(map%nodes) + deallocate(temp_nl) + if (np%inDocument.and.getGCstate(getOwnerDocument(map%ownerElement))) & + call removeNodesFromDocument(getOwnerDocument(map%ownerElement), np) + !otherwise we are only going to destroy these nodes anyway, + ! and finish + return + endif + enddo + + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "removeNamedItemNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + + end function removeNamedItemNS + + + subroutine append_nnm(map, arg) + type(namedNodeMap), pointer :: map + type(node), pointer :: arg + + type(ListNode), pointer :: temp_nl(:) + integer :: i + + if (.not.associated(map%nodes)) then + allocate(map%nodes(1)) + map%nodes(1)%this => arg + map%length = 1 + else + temp_nl => map%nodes + allocate(map%nodes(size(temp_nl)+1)) + do i = 1, size(temp_nl) + map%nodes(i)%this => temp_nl(i)%this + enddo + deallocate(temp_nl) + map%nodes(size(map%nodes))%this => arg + map%length = size(map%nodes) + endif + if (getNodeType(arg)==ATTRIBUTE_NODE) arg%elExtras%ownerElement => map%ownerElement + + end subroutine append_nnm + + + subroutine setReadOnlyMap(map, r) + type(namedNodeMap), pointer :: map + logical, intent(in) :: r + + map%readonly = r + end subroutine setReadOnlyMap + + subroutine destroyNamedNodeMap(map) + type(namedNodeMap), pointer :: map + + if (associated(map%nodes)) deallocate(map%nodes) + deallocate(map) + end subroutine destroyNamedNodeMap + + + + function hasFeature(impl, feature, version, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(DOMImplementation), pointer :: impl + character(len=*), intent(in) :: feature + character(len=*), intent(in) :: version + logical :: p + + if (.not.associated(impl)) then + if (getFoX_checks().or.FoX_IMPL_IS_NULL<200) then + call throw_exception(FoX_IMPL_IS_NULL, "hasFeature", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (version=="1.0".or.version=="2.0".or.version=="") then + p = (toLower(feature)=="core".or.toLower(feature)=="xml") + else + p = .false. + endif + + end function hasFeature + + function createDocumentType(impl, qualifiedName, publicId, systemId, ex)result(dt) + type(DOMException), intent(out), optional :: ex + type(DOMImplementation), pointer :: impl + character(len=*), intent(in) :: qualifiedName + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + type(Node), pointer :: dt + + type(URI), pointer :: URIref + + dt => null() + + if (.not.associated(impl)) then + if (getFoX_checks().or.FoX_IMPL_IS_NULL<200) then + call throw_exception(FoX_IMPL_IS_NULL, "createDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.checkName(qualifiedName, XML1_0)) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkQName(qualifiedName, XML1_0)) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkPublicId(publicId)) then + if (getFoX_checks().or.FoX_INVALID_PUBLIC_ID<200) then + call throw_exception(FoX_INVALID_PUBLIC_ID, "createDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + URIref => parseURI(systemId) + if (.not.associated(URIref)) then + if (getFoX_checks().or.FoX_INVALID_SYSTEM_ID<200) then + call throw_exception(FoX_INVALID_SYSTEM_ID, "createDocumentType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + call destroyURI(URIref) + +! Dont use raw null() below or PGI will complain + dt => createNode(dt, DOCUMENT_TYPE_NODE, qualifiedName, "") + allocate(dt%dtdExtras) + dt%readonly = .true. + dt%dtdExtras%publicId => vs_str_alloc(publicId) + dt%dtdExtras%systemId => vs_str_alloc(systemId) + dt%dtdExtras%entities%ownerElement => dt + dt%dtdExtras%notations%ownerElement => dt + + dt%ownerDocument => null() + + end function createDocumentType + + + function createDocument(impl, namespaceURI, qualifiedName, docType, ex)result(doc) + type(DOMException), intent(out), optional :: ex + type(DOMImplementation), pointer :: impl + character(len=*), intent(in), optional :: namespaceURI + character(len=*), intent(in), optional :: qualifiedName + type(Node), pointer :: docType + type(Node), pointer :: doc, dt, de + + doc => null() + + if (.not.associated(impl)) then + if (getFoX_checks().or.FoX_IMPL_IS_NULL<200) then + call throw_exception(FoX_IMPL_IS_NULL, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (associated(docType)) then + if (associated(getOwnerDocument(docType))) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + endif + + if (.not.checkName(qualifiedName, XML1_0)) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif(.not.checkQName(qualifiedName, XML1_0)) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)/="".and.namespaceURI=="") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)=="xml".neqv.namespaceURI=="http://www.w3.org/XML/1998/namespace") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/2000/xmlns/") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (qualifiedName=="xmlns" .or. prefixOfQName(qualifiedName)=="xmlns") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +! Dont use raw null() below or PGI will complain + doc => createNode(doc, DOCUMENT_NODE, "#document", "") + doc%ownerDocument => doc ! Makes life easier. DOM compliance in getter + doc%inDocument = .true. + + allocate(doc%docExtras) + doc%docExtras%implementation => FoX_DOM + allocate(doc%docExtras%nodelists(0)) + allocate(doc%docExtras%xds) + call init_xml_doc_state(doc%docExtras%xds) + allocate(doc%docExtras%xds%documentURI(0)) + allocate(doc%docExtras%domConfig) + + if (associated(docType)) then + dt => docType + dt%ownerDocument => doc + doc%docExtras%docType => appendChild(doc, dt, ex) + endif + + if (qualifiedName/="") then + ! NB It is impossible to create a non-namespaced document. + ! since createDocument doesnt exist in DOM Core 1 + de => createElementNS(doc, namespaceURI, qualifiedName) + de => appendChild(doc, de) + call setDocumentElement(doc, de) + endif + + call setGCstate(doc, .true.) + + end function createDocument + + + function createEmptyDocument() result(doc) + type(Node), pointer :: doc + +! PGI again + doc => null() + doc => createNode(doc, DOCUMENT_NODE, "#document", "") + doc%ownerDocument => doc ! Makes life easier. DOM compliance maintained in getter + doc%inDocument = .true. + + allocate(doc%docExtras) + doc%docExtras%implementation => FoX_DOM + allocate(doc%docExtras%nodelists(0)) + allocate(doc%docExtras%xds) + call init_xml_doc_state(doc%docExtras%xds) + + end function createEmptyDocument + + + subroutine destroyDocument(arg, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "destroyDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "destroyDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +! Switch off all GC - since this is GC! + call setGCstate(arg, .false.) + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "destroyDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +! Destroy all remaining nodelists + + do i = 1, size(arg%docExtras%nodelists) + call destroy(arg%docExtras%nodelists(i)%this) + enddo + deallocate(arg%docExtras%nodelists) + + ! Destroy all remaining hanging nodes + do i = 1, arg%docExtras%hangingNodes%length + call destroy(arg%docExtras%hangingNodes%nodes(i)%this) + enddo + if (associated(arg%docExtras%hangingNodes%nodes)) deallocate(arg%docExtras%hangingNodes%nodes) + + call destroy_xml_doc_state(arg%docExtras%xds) + deallocate(arg%docExtras%xds) + deallocate(arg%docExtras%domConfig) + deallocate(arg%docExtras) + + call destroyAllNodesRecursively(arg, except=.true.) + + end subroutine destroyDocument + + function getFoX_checks() result(FoX_checks) + logical :: FoX_checks + + FoX_checks = FoX_DOM%FoX_checks + end function getFoX_checks + + subroutine setFoX_checks(FoX_checks) + logical, intent(in) :: FoX_checks + + FoX_DOM%FoX_checks = FoX_checks + end subroutine setFoX_checks + + + + +function getdocType(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getdocType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getdocType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c => np%docExtras%docType + + end function getdocType + + + subroutine setDocType(arg, np, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setDocType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setDocType", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + arg%docExtras%docType => np +!NB special case in order to set ownerDocument + np%ownerDocument => arg + end subroutine setDocType + +function getdocumentElement(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getdocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getdocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c => np%docExtras%documentElement + + end function getdocumentElement + + + subroutine setXds(arg, xds, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(xml_doc_state), pointer :: xds + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setXds", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setXds", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif +!NB special case in order to destroy_xml_doc_state etc + call destroy_xml_doc_state(arg%docExtras%xds) + deallocate(arg%docExtras%xds) + arg%docExtras%xds => xds + + end subroutine setXds + + function getImplementation(arg, ex)result(imp) + type(DOMException), intent(out), optional :: ex + type(Node), pointer, optional :: arg + type(DOMImplementation), pointer :: imp + + ! According to the testsuite, you get to call + ! getImplementation with no args. Dont know + ! where they get that from ... + if (present(arg)) then + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getImplementation", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getImplementation", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + imp => arg%docExtras%implementation + else + imp => FoX_DOM + endif + end function getImplementation + + + subroutine setDocumentElement(arg, np, ex) + type(DOMException), intent(out), optional :: ex + ! Only for use by FoX, not exported through FoX_DOM interface + type(Node), pointer :: arg + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setDocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +!NB special case due to additional error conditions: + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setDocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (np%nodeType/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setDocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.associated(np%ownerDocument, arg)) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "setDocumentElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + arg%docExtras%documentElement => np + + end subroutine setDocumentElement + + ! Methods + + function createElement(arg, tagName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: tagName + type(Node), pointer :: np + + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + logical :: defaults_ + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(tagName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + np => createNode(arg, ELEMENT_NODE, tagName, "") + allocate(np%elExtras) + np%elExtras%dom1 = .true. + np%elExtras%attributes%ownerElement => np + allocate(np%elExtras%namespaceURI(0)) + allocate(np%elExtras%prefix(0)) + allocate(np%elExtras%localname(0)) + allocate(np%elExtras%namespaceNodes%nodes(0)) + + np%elExtras%attributes%ownerElement => np + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + ! We only add default attributes if we are *not* building the doc + xds => getXds(arg) + elem => get_element(xds%element_list, tagName) + if (associated(elem)) then + do i = 1, get_attlist_size(elem) + att => get_attribute_declaration(elem, i) + if (attribute_has_default(att)) then + ! Since this is a non-namespaced function, we create + ! a non-namespaced attribute ... + call setAttribute(np, str_vs(att%name), str_vs(att%default)) + endif + enddo + endif + else + np%inDocument = .true. + endif + + end function createElement + + function createEmptyElement(arg, tagName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: tagName + type(Node), pointer :: np + +! NO CHECKS ! + + np => createNode(arg, ELEMENT_NODE, tagName, "") + allocate(np%elExtras) + np%elExtras%dom1 = .true. + np%elExtras%attributes%ownerElement => np + allocate(np%elExtras%namespaceURI(0)) + allocate(np%elExtras%prefix(0)) + allocate(np%elExtras%localname(0)) + allocate(np%elExtras%namespaceNodes%nodes(0)) + + np%elExtras%attributes%ownerElement => np + + if (getGCstate(arg)) then + call append(arg%docExtras%hangingnodes, np) + np%inDocument = .false. + else + np%inDocument = .true. + endif + end function createEmptyElement + + function createDocumentFragment(arg, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createDocumentFragment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createDocumentFragment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, DOCUMENT_FRAGMENT_NODE, "#document-fragment", "") + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createDocumentFragment + + function createTextNode(arg, data, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: data + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createTextNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createTextNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkChars(data, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "createTextNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, TEXT_NODE, "#text", data) + np%textContentLength = len(data) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createTextNode + + function createComment(arg, data, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: data + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createComment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createComment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkChars(data, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "createComment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (index(data,"--")>0) then + if (getFoX_checks().or.FoX_INVALID_COMMENT<200) then + call throw_exception(FoX_INVALID_COMMENT, "createComment", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, COMMENT_NODE, "#comment", data) + np%textContentLength = len(data) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createComment + + function createCdataSection(arg, data, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: data + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createCdataSection", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createCdataSection", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkChars(data, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "createCdataSection", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (index(data,"]]>")>0) then + if (getFoX_checks().or.FoX_INVALID_CDATA_SECTION<200) then + call throw_exception(FoX_INVALID_CDATA_SECTION, "createCdataSection", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, CDATA_SECTION_NODE, "#cdata-section", data) + np%textContentLength = len(data) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createCdataSection + + function createProcessingInstruction(arg, target, data, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: target + character(len=*), intent(in) :: data + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createProcessingInstruction", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createProcessingInstruction", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(target, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createProcessingInstruction", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkChars(data, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "createProcessingInstruction", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (index(data,"?>")>0) then + if (getFoX_checks().or.FoX_INVALID_PI_DATA<200) then + call throw_exception(FoX_INVALID_PI_DATA, "createProcessingInstruction", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, PROCESSING_INSTRUCTION_NODE, target, data) + np%textContentLength = len(data) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createProcessingInstruction + + function createAttribute(arg, name, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(name, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, ATTRIBUTE_NODE, name, "") + allocate(np%elExtras) + np%elExtras%dom1 = .true. + allocate(np%elExtras%namespaceURI(0)) + allocate(np%elExtras%prefix(0)) + allocate(np%elExtras%localname(0)) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createAttribute + + + recursive function createEntityReference(arg, name, ex)result(np) + type(DOMException), intent(out), optional :: ex + ! Needs to be recursive in case of entity-references within each other. + type(Node), pointer :: arg + character(len=*), intent(in) :: name + type(Node), pointer :: np + + type(Node), pointer :: ent, newNode + integer :: i + logical :: brokenNS + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(name, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getXmlStandalone(arg).and..not.associated(getDocType(arg))) then + if (getFoX_checks().or.FoX_NO_SUCH_ENTITY<200) then + call throw_exception(FoX_NO_SUCH_ENTITY, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, ENTITY_REFERENCE_NODE, name, "") + if (getGCstate(arg)) then ! otherwise the parser will fill these nodes in itself + if (associated(getDocType(arg))) then + ent => getNamedItem(getEntities(getDocType(arg)), name) + if (associated(ent)) then + if (getIllFormed(ent)) then + if (getFoX_checks().or.FoX_INVALID_ENTITY<200) then + call throw_exception(FoX_INVALID_ENTITY, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + brokenNS = arg%docExtras%brokenNS + arg%docExtras%brokenNS = .true. ! We need to not worry about NS errors for a bit + do i = 0, getLength(getChildNodes(ent)) - 1 + newNode => appendChild(np, cloneNode(item(getChildNodes(ent), i), .true., ex)) + ! No namespace calcs here - wait for a namespace normalization + call setReadOnlyNode(newNode, .true., .true.) + enddo + arg%docExtras%brokenNS = brokenNS ! FIXME also for all new default attributes + elseif (getXmlStandalone(arg)) then + if (getFoX_checks().or.FoX_NO_SUCH_ENTITY<200) then + call throw_exception(FoX_NO_SUCH_ENTITY, "createEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + + if (associated(np)) deallocate(np) + return + endif + endif +endif + + endif + endif + endif + + call setReadOnlyNode(np, .true., .false.) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append_nl(arg%docExtras%hangingNodes, np) + ! All child nodes were created outside the document by cloneNode above + else + np%inDocument = .true. + endif + + end function createEntityReference + + function createEmptyEntityReference(arg, name, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createEmptyEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createEmptyEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(name, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createEmptyEntityReference", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, ENTITY_REFERENCE_NODE, name, "") + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createEmptyEntityReference + + function getElementsByTagName(doc, tagName, name, ex)result(list) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: doc + character(len=*), intent(in), optional :: tagName, name + type(NodeList), pointer :: list + + type(NodeListPtr), pointer :: nll(:), temp_nll(:) + type(Node), pointer :: arg, this, treeroot + logical :: doneChildren, doneAttributes, allElements + integer :: i, i_tree + + if (.not.associated(doc)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getElementsByTagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (doc%nodeType==DOCUMENT_NODE) then + if (present(name).or..not.present(tagName)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getElementsByTagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + elseif (doc%nodeType==ELEMENT_NODE) then + if (present(name).or..not.present(tagName)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getElementsByTagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + else + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getElementsByTagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (doc%nodeType==DOCUMENT_NODE) then + arg => getDocumentElement(doc) + else + arg => doc + endif + + allocate(list) + allocate(list%nodes(0)) + list%element => doc + if (present(name)) list%nodeName => vs_str_alloc(name) + if (present(tagName)) list%nodeName => vs_str_alloc(tagName) + + allElements = (str_vs(list%nodeName)=="*") + + if (doc%nodeType==DOCUMENT_NODE) then + nll => doc%docExtras%nodelists + elseif (doc%nodeType==ELEMENT_NODE) then + nll => doc%ownerDocument%docExtras%nodelists + endif + allocate(temp_nll(size(nll)+1)) + do i = 1, size(nll) + temp_nll(i)%this => nll(i)%this + enddo + temp_nll(i)%this => list + deallocate(nll) + if (doc%nodeType==DOCUMENT_NODE) then + doc%docExtras%nodelists => temp_nll + elseif (doc%nodeType==ELEMENT_NODE) then + doc%ownerDocument%docExtras%nodelists => temp_nll + endif + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + if (this%nodeType==ELEMENT_NODE) then + if ((allElements .or. str_vs(this%nodeName)==tagName) & + .and..not.(getNodeType(doc)==ELEMENT_NODE.and.associated(this, arg))) & + call append(list, this) + doneAttributes = .true. + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 function getElementsByTagName + + function importNode(doc , arg, deep , ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: doc + type(Node), pointer :: arg + logical, intent(in) :: deep + type(Node), pointer :: np + + type(Node), pointer :: this, thatParent, new, treeroot + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + logical :: doneAttributes, doneChildren, brokenNS + integer :: i_tree + + if (.not.associated(doc).or..not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "importNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(doc)/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "importNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)==DOCUMENT_NODE .or. & + getNodeType(arg)==DOCUMENT_TYPE_NODE) then + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "importNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + brokenNS = doc%docExtras%brokenNS + doc%docExtras%brokenNS = .true. ! We need to do stupid NS things + xds => getXds(doc) + thatParent => null() + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + + new => null() + select case (getNodeType(this, ex)) + case (ELEMENT_NODE) + if (.not.doneAttributes) then + ! We dont create an empty node - we insist on having all default + ! properties created. + if (getParameter(getDomConfig(doc, ex), "namespaces", ex)) then + new => createElementNS(doc, getNamespaceURI(this, ex), getTagName(this, ex), ex) + else + new => createElement(doc, getTagName(this, ex), ex) + endif + endif + case (ATTRIBUTE_NODE) + if (associated(this, arg).or.getSpecified(this, ex)) then + ! We are importing just this attribute node + ! or this was an explicitly specified attribute; either + ! way, we import it as is, and it remains specified. + if (getParameter(getDomConfig(doc), "namespaces")) then + new => createAttributeNS(doc, getNamespaceURI(this, ex), getName(this, ex), ex) + else + new => createAttribute(doc, getName(this), ex) + endif + call setSpecified(new, .true.) + else + ! This is an attribute being imported as part of a hierarchy, + ! but its only here by default. Is there a default attribute + ! of this name in the new document? + elem => get_element(xds%element_list, & + getTagName(getOwnerElement(this))) + att => get_attribute_declaration(elem, getName(this)) + if (attribute_has_default(att)) then + ! Create the new default: + if (getParameter(getDomConfig(doc, ex), "namespaces", ex)) then + ! We create a namespaced attribute. Of course, its + ! namespaceURI remains empty for the moment unless we know it ... + if (prefixOfQName(getName(this, ex))=="xml") then + new => createAttributeNS(doc, & + "http://www.w3.org/XML/1998/namespace", & + getName(this, ex), ex) + elseif (getName(this, ex)=="xmlns" & + .or. prefixOfQName(getName(this, ex))=="xmlns") then + new => createAttributeNS(doc, & + "http://www.w3.org/2000/xmlns/", & + getName(this, ex), ex) + else + ! Wait for namespace fixup ... + new => createAttributeNS(doc, "", & + getName(this, ex), ex) + endif + else + new => createAttribute(doc, getName(this, ex), ex) + endif + call setValue(new, str_vs(att%default), ex) + call setSpecified(new, .false.) + endif + ! In any case, we dont want to copy the children of this node. + doneChildren=.true. + endif + case (TEXT_NODE) + new => createTextNode(doc, getData(this, ex), ex) + case (CDATA_SECTION_NODE) + new => createCDataSection(doc, getData(this, ex), ex) + case (ENTITY_REFERENCE_NODE) + new => createEntityReference(doc, getNodeName(this, ex), ex) + ! This will automatically populate the entity reference if doc defines it, so no children needed + doneChildren = .true. + case (ENTITY_NODE) + new => createEntity(doc, getNodeName(this, ex), & + getPublicId(this, ex), getSystemId(this, ex), & + getNotationName(this, ex), ex) + case (PROCESSING_INSTRUCTION_NODE) + new => createProcessingInstruction(doc, & + getTarget(this, ex), getData(this, ex), ex) + case (COMMENT_NODE) + new => createComment(doc, getData(this, ex), ex) + case (DOCUMENT_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "importNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case (DOCUMENT_TYPE_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "importNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case (DOCUMENT_FRAGMENT_NODE) + new => createDocumentFragment(doc, ex) + case (NOTATION_NODE) + new => createNotation(doc, getNodeName(this, ex), & + getPublicId(this, ex), getSystemId(this, ex), ex) + end select + + if (.not.associated(thatParent)) then + thatParent => new + elseif (associated(new)) then + if (getNodeType(this, ex)==ATTRIBUTE_NODE) then + new => setAttributeNode(thatParent, new, ex) + else + new => appendChild(thatParent, new, ex) + endif + endif + + if (.not.deep) then + if (getNodeType(arg, ex)==ATTRIBUTE_NODE & + .or.getNodeType(arg, ex)==ELEMENT_NODE) then + continue + else + exit + endif + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + endif + endif + + + if (.not.doneChildren) then + if (getNodeType(this)==ELEMENT_NODE.and..not.doneAttributes) then + if (getLength(getAttributes(this))>0) then + if (.not.associated(this, treeroot)) thatParent => getLastChild(thatParent) + this => item(getAttributes(this), 0) + else + if (.not.deep) exit + doneAttributes = .true. + endif + elseif (hasChildNodes(this)) then + if (getNodeType(this)==ELEMENT_NODE.and..not.deep) exit + if (.not.associated(this, treeroot)) then + if (getNodeType(this)==ATTRIBUTE_NODE) then + thatParent => item(getAttributes(thatParent), i_tree) + else + thatParent => getLastChild(thatParent) + endif + endif + 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 item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .false. + else + i_tree= 0 + if (associated(getParentNode(thatParent))) thatParent => getParentNode(thatParent) + this => getOwnerElement(this) + doneAttributes = .true. + doneChildren = .false. + endif + elseif (associated(getNextSibling(this))) then + + this => getNextSibling(this) + doneChildren = .false. + doneAttributes = .false. + else + this => getParentNode(this) + if (.not.associated(this, treeroot)) then + if (getNodeType(this)==ATTRIBUTE_NODE) then + thatParent => getOwnerElement(thatParent) + else + thatParent => getParentNode(thatParent) + endif + endif + endif + endif + + enddo + + + + np => thatParent + doc%docExtras%brokenNS = brokenNS +! call namespaceFixup(np) + + end function importNode + + function createElementNS(arg, namespaceURI, qualifiedName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, qualifiedName + type(Node), pointer :: np + + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + integer :: i + logical :: brokenNS + type(URI), pointer :: URIref + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkQName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)/="" & + .and. namespaceURI=="".and..not.arg%docExtras%brokenNS) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/XML/1998/namespace" .neqv. & + prefixOfQName(qualifiedName)=="xml") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/2000/xmlns/") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + URIref => parseURI(namespaceURI) + if (.not.associated(URIref)) then + if (getFoX_checks().or.FoX_INVALID_URI<200) then + call throw_exception(FoX_INVALID_URI, "createElementNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + call destroyURI(URIref) + + np => createNode(arg, ELEMENT_NODE, qualifiedName, "") + allocate(np%elExtras) + np%elExtras%namespaceURI => vs_str_alloc(namespaceURI) + np%elExtras%prefix => vs_str_alloc(prefixOfQName(qualifiedname)) + np%elExtras%localName => vs_str_alloc(localpartOfQName(qualifiedname)) + allocate(np%elExtras%namespaceNodes%nodes(0)) + + np%elExtras%attributes%ownerElement => np + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + ! We only add default attributes if we are *not* building the doc + xds => getXds(arg) + elem => get_element(xds%element_list, qualifiedName) + if (associated(elem)) then + do i = 1, get_attlist_size(elem) + att => get_attribute_declaration(elem, i) + if (attribute_has_default(att)) then + ! Since this is a namespaced function, we create a namespaced + ! attribute. Of course, its namespaceURI remains empty + ! for the moment unless we know it ... + if (prefixOfQName(str_vs(att%name))=="xml") then + call setAttributeNS(np, & + "http://www.w3.org/XML/1998/namespace", & + str_vs(att%name), str_vs(att%default), ex) + elseif (str_vs(att%name)=="xmlns" & + .or. prefixOfQName(str_vs(att%name))=="xmlns") then + call setAttributeNS(np, & + "http://www.w3.org/2000/xmlns/", & + str_vs(att%name), str_vs(att%default), ex) + else + ! Wait for namespace fixup ... + brokenNS = arg%docExtras%brokenNS + arg%docExtras%brokenNS = .true. + call setAttributeNS(np, "", str_vs(att%name), & + str_vs(att%default), ex) + arg%docExtras%brokenNS = brokenNS + endif + endif + enddo + endif + else + np%inDocument = .true. + endif + + end function createElementNS + + function createEmptyElementNS(arg, namespaceURI, qualifiedName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, qualifiedName + type(Node), pointer :: np + +! NO CHECKS ! + + np => createNode(arg, ELEMENT_NODE, qualifiedName, "") + allocate(np%elExtras) + np%elExtras%namespaceURI => vs_str_alloc(namespaceURI) + np%elExtras%prefix => vs_str_alloc(prefixOfQName(qualifiedname)) + np%elExtras%localName => vs_str_alloc(localpartOfQName(qualifiedname)) + allocate(np%elExtras%namespaceNodes%nodes(0)) + + np%elExtras%attributes%ownerElement => np + + if (getGCstate(arg)) then + call append(arg%docExtras%hangingnodes, np) + np%inDocument = .false. + else + np%inDocument = .true. + endif + end function createEmptyElementNS + + function createAttributeNS(arg, namespaceURI, qualifiedname, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, qualifiedName + type(Node), pointer :: np + + type(URI), pointer :: URIref + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkQName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)/="" & + .and. namespaceURI=="".and..not.arg%docExtras%brokenNS) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/XML/1998/namespace" .neqv. & + prefixOfQName(qualifiedName)=="xml") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/2000/xmlns/" .neqv. & + (qualifiedName=="xmlns" .or. prefixOfQName(qualifiedName)=="xmlns")) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + URIref => parseURI(namespaceURI) + if (.not.associated(URIref)) then + if (getFoX_checks().or.FoX_INVALID_URI<200) then + call throw_exception(FoX_INVALID_URI, "createAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + call destroyURI(URIref) + + + np => createNode(arg, ATTRIBUTE_NODE, qualifiedName, "") + allocate(np%elExtras) + np%elExtras%namespaceURI => vs_str_alloc(namespaceURI) + np%elExtras%localname => vs_str_alloc(localPartofQName(qualifiedname)) + np%elExtras%prefix => vs_str_alloc(PrefixofQName(qualifiedname)) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createAttributeNS + + function getElementsByTagNameNS(doc, namespaceURI, localName, ex)result(list) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: doc + character(len=*), intent(in) :: namespaceURI, localName + type(NodeList), pointer :: list + + type(NodeListPtr), pointer :: nll(:), temp_nll(:) + type(Node), pointer :: this, arg, treeroot + logical :: doneChildren, doneAttributes, allLocalNames, allNameSpaces + integer :: i, i_tree + + if (.not.associated(doc)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getElementsByTagNameNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (doc%nodeType/=DOCUMENT_NODE.and.doc%nodeType/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getElementsByTagNameNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + allNamespaces = (namespaceURI=="*") + allLocalNames = (localName=="*") + + if (doc%nodeType==DOCUMENT_NODE) then + arg => getDocumentElement(doc) + else + arg => doc + endif + + allocate(list) + allocate(list%nodes(0)) + list%element => doc + list%localName => vs_str_alloc(localName) + list%namespaceURI => vs_str_alloc(namespaceURI) + + if (doc%nodeType==DOCUMENT_NODE) then + nll => doc%docExtras%nodelists + elseif (doc%nodeType==ELEMENT_NODE) then + nll => doc%ownerDocument%docExtras%nodelists + endif + allocate(temp_nll(size(nll)+1)) + do i = 1, size(nll) + temp_nll(i)%this => nll(i)%this + enddo + temp_nll(i)%this => list + deallocate(nll) + if (doc%nodeType==DOCUMENT_NODE) then + doc%docExtras%nodelists => temp_nll + elseif (doc%nodeType==ELEMENT_NODE) then + doc%ownerDocument%docExtras%nodelists => temp_nll + endif + + treeroot => arg + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (getNodeType(this)==ELEMENT_NODE) then + if (getNamespaceURI(this)/="") then + if ((allNameSpaces .or. getNameSpaceURI(this)==namespaceURI) & + .and. (allLocalNames .or. getLocalName(this)==localName) & + .and..not.(getNodeType(doc)==ELEMENT_NODE.and.associated(this, arg))) & + call append(list, this) + else + if ((allNameSpaces .or. namespaceURI=="") & + .and. (allLocalNames .or. getNodeName(this)==localName) & + .and..not.(getNodeType(doc)==ELEMENT_NODE.and.associated(this, arg))) & + call append(list, this) + endif + doneAttributes = .true. ! Never search attributes + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 function getElementsByTagNameNS + + + function getElementById(arg, elementId, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: elementId + type(Node), pointer :: np + + type(Node), pointer :: this, treeroot + integer :: i_tree + logical :: doneChildren, doneAttributes + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getElementById", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getElementById", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => null() + treeroot => getDocumentElement(arg) + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + if (this%nodeType==ATTRIBUTE_NODE) then + if (getIsId(this).and.getValue(this)==elementId) then + np => getOwnerElement(this) + return + endif + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 function getElementById + +function getxmlStandalone(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getxmlStandalone", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getxmlStandalone", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%docExtras%xds%standalone + + end function getxmlStandalone + +subroutine setxmlStandalone(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setxmlStandalone", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setxmlStandalone", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%docExtras%xds%standalone = c + + end subroutine setxmlStandalone + +! FIXME additional check on setting - do we have any undefined entrefs present? + + function getXmlVersion(arg, ex)result(s) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=3) :: s + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getXmlVersion", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE & + .and.arg%nodeType/=ENTITY_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getXmlVersion", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getXmlVersionEnum(arg)==XML1_0) then + s = "1.0" + elseif (getXmlVersionEnum(arg)==XML1_1) then + s = "1.1" + else + s = "XXX" + endif + + end function getXmlVersion + + subroutine setXmlVersion(arg, s, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*) :: s + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setXmlVersion", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setXmlVersion", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (s=="1.0") then + arg%docExtras%xds%xml_version = XML1_0 + elseif (s=="1.1") then + arg%docExtras%xds%xml_version = XML1_1 + else + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "setXmlVersion", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end subroutine setXmlVersion + + pure function getXmlEncoding_len(arg, p) result(n) + type(Node), pointer :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (.not.p) return + if (arg%nodeType==DOCUMENT_NODE) & + n = size(arg%docExtras%xds%encoding) + end function getXmlEncoding_len + + function getXmlEncoding(arg, ex)result(s) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getXmlEncoding_len(arg, .true.)) :: s +#else + character(len=getXmlEncoding_len(arg, associated(arg))) :: s +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getXmlEncoding", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType==DOCUMENT_NODE) then + s = str_vs(arg%docExtras%xds%encoding) + elseif (arg%nodeType==ENTITY_NODE) then + s = "" !FIXME revisit when we have working external entities + else + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getXmlEncoding", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end function getXmlEncoding + + pure function getInputEncoding_len(arg, p) result(n) + type(Node), pointer :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (.not.p) return + if (arg%nodeType==DOCUMENT_NODE) & + n = size(arg%docExtras%xds%inputEncoding) + end function getInputEncoding_len + + function getInputEncoding(arg, ex)result(s) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getInputEncoding_len(arg, .true.)) :: s +#else + character(len=getInputEncoding_len(arg, associated(arg))) :: s +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getInputEncoding", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType==DOCUMENT_NODE) then + s = str_vs(arg%docExtras%xds%inputEncoding) + elseif (arg%nodeType==ENTITY_NODE) then + s = "" !FIXME revisit when we have working external entities + else + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getInputEncoding", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end function getInputEncoding + + + pure function getdocumentURI_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==DOCUMENT_NODE .or. & + .false.)) then + n = size(np%docExtras%xds%documentURI) + else + n = 0 + endif + end function getdocumentURI_len +function getdocumentURI(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getdocumentURI_len(np, .true.)) :: c +#else + character(len=getdocumentURI_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getdocumentURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getdocumentURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%docExtras%xds%documentURI) + + end function getdocumentURI + +subroutine setdocumentURI(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*) :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setdocumentURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setdocumentURI", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%docExtras%xds%documentURI)) deallocate(np%docExtras%xds%documentURI) + np%docExtras%xds%documentURI => vs_str_alloc(c) + + end subroutine setdocumentURI + + +function getstrictErrorChecking(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getstrictErrorChecking", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getstrictErrorChecking", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%docExtras%strictErrorChecking + + end function getstrictErrorChecking + +subroutine setstrictErrorChecking(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setstrictErrorChecking", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setstrictErrorChecking", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%docExtras%strictErrorChecking = c + + end subroutine setstrictErrorChecking + + + function adoptNode(doc , arg , ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: doc + type(Node), pointer :: arg + type(Node), pointer :: np + + type(Node), pointer :: this, thatParent, new, treeroot, parent, dead + type(xml_doc_state), pointer :: xds + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + logical :: doneAttributes, doneChildren, brokenNS + integer :: i_tree + + if (.not.associated(doc).or..not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(doc)/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)==DOCUMENT_NODE .or. & + getNodeType(arg)==DOCUMENT_TYPE_NODE .or. & + getNodeType(arg)==NOTATION_NODE .or. & + getNodeType(arg)==ENTITY_NODE) then + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getReadonly(arg)) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + brokenNS = doc%docExtras%brokenNS + doc%docExtras%brokenNS = .true. ! We need to do stupid NS things + xds => getXds(doc) + + if (associated(getParentNode(arg))) then + np => removeChild(getParentNode(arg), arg) + else + np => arg + endif + + if (associated(arg, getOwnerDocument(arg))) return + + thatParent => null() + treeroot => np + + 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) + if (.not.doneAttributes) call setOwnerDocument(this, doc) + case (ATTRIBUTE_NODE) + if (associated(this, arg).or.getSpecified(this)) then + ! We are importing just this attribute node + ! or this was an explicitly specified attribute; either + ! way, we import it as is, and it becomes/remains specified. + call setOwnerDocument(this, doc) + call setSpecified(this, .true.) + else + ! This is an attribute being imported as part of a hierarchy, + ! but its only here by default. Is there a default attribute + ! of this name in the new document? + elem => get_element(xds%element_list, & + getTagName(getOwnerElement(this))) + att => get_attribute_declaration(elem, getName(this)) + if (attribute_has_default(att)) then + ! Create the new default: + if (getParameter(getDomConfig(doc), "namespaces")) then + ! We create a namespaced attribute. Of course, its + ! namespaceURI remains empty for the moment unless we know it ... + if (prefixOfQName(getName(this))=="xml") then + new => createAttributeNS(np, & + "http://www.w3.org/XML/1998/namespace", & + getName(this)) + elseif (getName(this)=="xmlns" & + .or. prefixOfQName(getName(this))=="xmlns") then + new => createAttributeNS(np, & + "http://www.w3.org/2000/xmlns/", & + getName(this)) + else + ! Wait for namespace fixup ... + new => createAttributeNS(np, "", & + getName(this)) + endif + else + new => createAttribute(doc, getName(this)) + endif + call setValue(new, str_vs(att%default)) + call setSpecified(new, .false.) + ! In any case, we dont want to copy the children of this node. + doneChildren = .true. + dead => setAttributeNode(getOwnerElement(this), new) + this => new + call destroyAllNodesRecursively(dead) + endif + ! Otherwise no attribute here, so go back to previous node + dead => this + if (i_tree==0) then + this => getOwnerElement(this) + else + i_tree = i_tree - 1 + this => item(getAttributes(getOwnerElement(this)), i_tree) + doneChildren = .true. + endif + call removeAttribute(getOwnerElement(dead), getNodeName(dead)) + endif + case (ENTITY_REFERENCE_NODE) + new => createEntityReference(doc, getNodeName(this)) + ! This will automatically populate the entity reference if doc defines it, so no children needed + parent => getParentNode(this) + if (associated(parent)) then + dead => replaceChild(parent, new, this) + this => new + call destroyAllNodesRecursively(dead) + endif + doneChildren = .true. + case (ENTITY_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case (DOCUMENT_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case (DOCUMENT_TYPE_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case (NOTATION_NODE) + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "adoptNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + case default + call setOwnerDocument(this, doc) + end select + + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 + + + + doc%docExtras%brokenNS = brokenNS +! call namespaceFixup(np) + + end function adoptNode + +function getdomConfig(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(DOMConfiguration), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getdomConfig", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getdomConfig", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c => np%docExtras%domConfig + + end function getdomConfig + +subroutine setdomConfig(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(DOMConfiguration), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setdomConfig", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setdomConfig", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%docExtras%domConfig => c + + end subroutine setdomConfig + + + + function renameNode(arg, n, namespaceURI, qualifiedName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: n + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: qualifiedName + type(Node), pointer :: np + + type(Node), pointer :: attNode + integer :: i + logical :: brokenNS + type(element_t), pointer :: elem + type(attribute_t), pointer :: att + type(xml_doc_state), pointer :: xds + type(URI), pointer :: URIref + + if (.not.associated(arg).or..not.associated(n)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(arg)/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.associated(arg, getOwnerDocument(n))) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkQName(qualifiedName, getXmlVersionEnum(arg))) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)/="" & + .and. namespaceURI=="".and..not.arg%docExtras%brokenNS) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/XML/1998/namespace" .neqv. & + prefixOfQName(qualifiedName)=="xml") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/2000/xmlns/") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + URIref => parseURI(namespaceURI) + if (.not.associated(URIref)) then + if (getFoX_checks().or.FoX_INVALID_URI<200) then + call throw_exception(FoX_INVALID_URI, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + call destroyURI(URIref) + +! FIXME what if this is called on a Level 1 node +! FIXME what if this is called on a read-only node +! FIXME what if this is called on an attribute whose specified=fals + select case(getNodeType(n)) + case (ELEMENT_NODE, ATTRIBUTE_NODE) + deallocate(n%nodeName) + n%nodeName => vs_str_alloc(qualifiedName) + deallocate(n%elExtras%namespaceURI) + n%elExtras%namespaceURI => vs_str_alloc(namespaceURI) + deallocate(n%elExtras%localName) + n%elExtras%localName => vs_str_alloc(localpartOfQName(qualifiedname)) + case default + if (getFoX_checks().or.NOT_SUPPORTED_ERR<200) then + call throw_exception(NOT_SUPPORTED_ERR, "renameNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + end select + + if (getNodeType(n)==ELEMENT_NODE) then + i = 0 + do while (i item(getAttributes(n), i) + if (.not.getSpecified(attNode)) then + attNode => removeAttributeNode(n, attNode) + call destroyNode(attNode) + else + i = i + 1 + endif + enddo + xds => getXds(arg) + elem => get_element(xds%element_list, qualifiedName) + if (associated(elem)) then + do i = 1, get_attlist_size(elem) + att => get_attribute_declaration(elem, i) + if (attribute_has_default(att)) then + ! Since this is a namespaced function, we create a namespaced + ! attribute. Of course, its namespaceURI remains empty + ! for the moment unless we know it ... + if (prefixOfQName(str_vs(att%name))=="xml") then + call setAttributeNS(np, & + "http://www.w3.org/XML/1998/namespace", & + str_vs(att%name), str_vs(att%default)) + elseif (str_vs(att%name)=="xmlns" & + .or. prefixOfQName(str_vs(att%name))=="xmlns") then + call setAttributeNS(np, & + "http://www.w3.org/2000/xmlns/", & + str_vs(att%name), str_vs(att%default)) + else + ! Wait for namespace fixup ... + brokenNS = arg%docExtras%brokenNS + arg%docExtras%brokenNS = .true. + call setAttributeNS(np, "", str_vs(att%name), & + str_vs(att%default)) + arg%docExtras%brokenNS = brokenNS + endif + endif + enddo + endif + endif + + np => n + + end function renameNode + + ! Internal function, not part of API + + function createNamespaceNode(arg, prefix, URI, specified, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: prefix + character(len=*), intent(in) :: URI + logical, intent(in) :: specified + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createNamespaceNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createNamespaceNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, XPATH_NAMESPACE_NODE, "#namespace", URI) + allocate(np%elExtras) + np%elExtras%prefix => vs_str_alloc(prefix) + np%elExtras%namespaceURI => vs_str_alloc(URI) + np%elExtras%specified = specified + + end function createNamespaceNode + + function createEntity(arg, name, publicId, systemId, notationName, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + character(len=*), intent(in) :: notationName + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createEntity", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createEntity", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, ENTITY_NODE, name, "") + allocate(np%dtdExtras) + np%dtdExtras%publicId => vs_str_alloc(publicId) + np%dtdExtras%systemId => vs_str_alloc(systemId) + np%dtdExtras%notationName => vs_str_alloc(notationName) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createEntity + + function createNotation(arg, name, publicId, systemId, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + type(Node), pointer :: np + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "createNotation", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "createNotation", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => createNode(arg, NOTATION_NODE, name, "") + allocate(np%dtdExtras) + np%dtdExtras%publicId => vs_str_alloc(publicId) + np%dtdExtras%systemId => vs_str_alloc(systemId) + + if (getGCstate(arg)) then + np%inDocument = .false. + call append(arg%docExtras%hangingnodes, np) + else + np%inDocument = .true. + endif + + end function createNotation + + function getXmlVersionEnum(arg, ex)result(n) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "getXmlVersionEnum", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + n = arg%docExtras%xds%xml_version + + end function getXmlVersionEnum + + function getXds(arg, ex)result(xds) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(xml_doc_state), pointer :: xds + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + call throw_exception(FoX_INTERNAL_ERROR, "getXds", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + xds => arg%docExtras%xds + + end function getXds + + +function getGCstate(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getGCstate", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getGCstate", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%docExtras%xds%building + + end function getGCstate + +subroutine setGCstate(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setGCstate", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setGCstate", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%docExtras%xds%building = c + + end subroutine setGCstate + + +function getliveNodeLists(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getliveNodeLists", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getliveNodeLists", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%docExtras%liveNodeLists + + end function getliveNodeLists + +subroutine setliveNodeLists(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setliveNodeLists", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setliveNodeLists", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%docExtras%liveNodeLists = c + + end subroutine setliveNodeLists + + + + +! function getName(docType) result(c) See m_dom_common + + function getEntities(arg, ex)result(nnp) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(NamedNodeMap), pointer :: nnp + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getEntities", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_TYPE_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getEntities", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + nnp => arg%dtdExtras%entities + end function getEntities + + function getNotations(arg, ex)result(nnp) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(NamedNodeMap), pointer :: nnp + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getNotations", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_TYPE_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getNotations", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + nnp => arg%dtdExtras%notations + end function getNotations + + +! function getPublicId(docType) result(c) See m_dom_common + + +! function getSystemId(docType) result(c) See m_dom_common + + pure function getInternalSubset_len(arg, p) result(n) + type(Node), pointer :: arg + logical, intent(in) :: p + integer :: n + + n = 0 + if (p) then + if (associated(arg%ownerDocument)) then + if (associated(arg%ownerDocument%docExtras%xds%intSubset)) then + n = size(arg%ownerDocument%docExtras%xds%intSubset) + endif + endif + endif + end function getInternalSubset_len + + function getInternalSubset(arg, ex)result(s) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getInternalSubset_len(arg, .true.)) :: s +#else + character(len=getInternalSubset_len(arg, associated(arg))) :: s +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getInternalSubset", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType/=DOCUMENT_TYPE_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getInternalSubset", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(s)>0) then + s = str_vs(arg%ownerDocument%docExtras%xds%intSubset) + else + s = "" + endif + end function getInternalSubset + + + + + pure function gettagName_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==ELEMENT_NODE .or. & + .false.)) then + n = size(np%nodeName) + else + n = 0 + endif + end function gettagName_len +function gettagName(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=gettagName_len(np, .true.)) :: c +#else + character(len=gettagName_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "gettagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ELEMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "gettagName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%nodeName) + + end function gettagName + + + pure function getAttribute_len(arg, p, name) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + character(len=*), intent(in) :: name + integer :: n + + integer :: i + + n = 0 + if (.not.p) return + if (arg%nodeType/=ELEMENT_NODE) return + + do i = 1, arg%elExtras%attributes%length + if (str_vs(arg%elExtras%attributes%nodes(i)%this%nodeName)==name) then + n = getTextContent_len(arg%elExtras%attributes%nodes(i)%this, .true.) + exit + endif + enddo + + end function getAttribute_len + + function getAttribute(arg, name, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getAttribute_len(arg, .true., name)) :: c +#else + character(len=getAttribute_len(arg, associated(arg), name)) :: c +#endif + + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(arg) /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(c)>0) then + do i = 1, arg%elExtras%attributes%length + if (str_vs(arg%elExtras%attributes%nodes(i)%this%nodeName)==name) then + c = getTextContent(arg%elExtras%attributes%nodes(i)%this) + exit + endif + enddo + else + c = "" + endif + + end function getAttribute + + + subroutine setAttribute(arg, name, value, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + + type(Node), pointer :: nn, dummy + logical :: quickFix + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodetype(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(name, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "setAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkChars(value, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "setAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + quickFix = getGCstate(getOwnerDocument(arg)) & + .and. arg%inDocument + + if (quickFix) call setGCstate(getOwnerDocument(arg), .false.) + ! then the created attribute is going straight into the document, + ! so dont faff with hanging-node lists. + + nn => createAttribute(arg%ownerDocument, name) + call setValue(nn, value) + dummy => setNamedItem(getAttributes(arg), nn) + if (associated(dummy)) then + if (getGCstate(getOwnerDocument(arg)).and..not.dummy%inDocument) & + call putNodesInDocument(getOwnerDocument(arg), dummy) + ! ... so that dummy & children are removed from hangingNodes list. + call destroyAllNodesRecursively(dummy) + endif + + if (quickFix) call setGCstate(getOwnerDocument(arg), .true.) + + end subroutine setAttribute + + + subroutine removeAttribute(arg, name, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + type(DOMException) :: ex2 + type(Node), pointer :: dummy + integer :: e + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "removeAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodetype(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "removeAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "removeAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%inDocument) & + call setGCstate(getOwnerDocument(arg), .false.) + + dummy => removeNamedItem(getAttributes(arg), name, ex2) + ! removeNamedItem took care of any default attributes + if (inException(ex2)) then + e = getExceptionCode(ex2) + if (e/=NOT_FOUND_ERR) then + if (getFoX_checks().or.e<200) then + call throw_exception(e, "removeAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + else + if (.not.arg%inDocument) then + ! dummy was not in the doc, so was on hangingNode list. + ! To remove it from the list: + call putNodesInDocument(arg%ownerDocument, dummy) + endif + call destroyAllNodesRecursively(dummy) + endif + + if (arg%inDocument) & + call setGCstate(arg%ownerDocument, .true.) + + end subroutine removeAttribute + + + function getAttributeNode(arg, name, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + attr => getNamedItem(getAttributes(arg), name) + + end function getAttributeNode + + + function setAttributeNode(arg, newattr, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: newattr + type(Node), pointer :: attr + type(Node), pointer :: dummy + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.associated(arg%ownerDocument, newattr%ownerDocument)) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "setAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(getOwnerElement(newattr), arg)) then + attr => newattr + return + ! Nothing to do, this attribute is already in this element + elseif (associated(getOwnerElement(newattr))) then + if (getFoX_checks().or.INUSE_ATTRIBUTE_ERR<200) then + call throw_exception(INUSE_ATTRIBUTE_ERR, "setAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! this checks if attribute exists already + ! It also does any adding/removing of hangingnodes + ! and sets ownerElement appropriately + dummy => setNamedItem(getAttributes(arg), newattr, ex) + attr => dummy + + end function setAttributeNode + + + function removeAttributeNode(arg, oldattr, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: oldattr + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "removeAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "removeAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.associated(arg, getOwnerElement(oldattr))) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "removeAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + attr => removeNamedItem(getAttributes(arg), & + getNodeName(oldattr), ex) + + end function removeAttributeNode + + +! function getElementsByTagName - see m_dom_document + + + pure function getAttributesNS_len(arg, p, localname, namespaceURI) result(n) + type(Node), intent(in) :: arg + logical, intent(in) :: p + character(len=*), intent(in) :: localname + character(len=*), intent(in) :: namespaceURI + integer :: n + + integer :: i + + n = 0 + if (.not.p) return + if (arg%nodeType/=ELEMENT_NODE) return + + do i = 1, arg%elExtras%attributes%length + if ((str_vs(arg%elExtras%attributes%nodes(i)%this%elExtras%localName)==localname & + .and. str_vs(arg%elExtras%attributes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) & + .or. (namespaceURI=="".and.str_vs(arg%elExtras%attributes%nodes(i)%this%nodeName)==localname)) then + n = getTextContent_len(arg%elExtras%attributes%nodes(i)%this, .true.) + exit + endif + enddo + + end function getAttributesNS_len + + function getAttributeNS(arg, namespaceURI, localName, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getAttributesNS_len(arg, .true., localname, namespaceURI)) :: c +#else + character(len=getAttributesNS_len(arg, associated(arg), localname, namespaceURI)) :: c +#endif + + integer :: i + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (len(c)>0) then + do i = 1, arg%elExtras%attributes%length + if ((str_vs(arg%elExtras%attributes%nodes(i)%this%elExtras%localName)==localname & + .and. str_vs(arg%elExtras%attributes%nodes(i)%this%elExtras%namespaceURI)==namespaceURI) & + .or. (namespaceURI=="".and.str_vs(arg%elExtras%attributes%nodes(i)%this%nodeName)==localname)) then + c = getTextContent(arg%elExtras%attributes%nodes(i)%this) + exit + endif + enddo + else + c = "" + endif + + end function getAttributeNS + + + subroutine setAttributeNS(arg, namespaceURI, qualifiedname, value, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: qualifiedName + character(len=*), intent(in) :: value + + type(Node), pointer :: nn, dummy + logical :: quickfix + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.checkName(qualifiedname, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.INVALID_CHARACTER_ERR<200) then + call throw_exception(INVALID_CHARACTER_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (.not.arg%ownerDocument%docExtras%brokenNS) then + if (.not.checkQName(qualifiedname, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)/="" & + .and. namespaceURI=="") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (prefixOfQName(qualifiedName)=="xml" .neqv. & + namespaceURI=="http://www.w3.org/XML/1998/namespace") then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (namespaceURI=="http://www.w3.org/2000/xmlns/" .neqv. & + (qualifiedName=="xmlns" .or. prefixOfQName(qualifiedName)=="xmlns")) then + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "setAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + endif + +! FIXME what if namespace is undeclared? Throw an error *only* if FoX_errors is on, otherwise its taken care of by namespace fixup on serialization + + quickFix = getGCstate(getOwnerDocument(arg)) & + .and. arg%inDocument + + if (quickFix) call setGCstate(getOwnerDocument(arg), .false.) + ! then the created attribute is going straight into the document, + ! so dont faff with hanging-node lists. + + nn => createAttributeNS(arg%ownerDocument, namespaceURI, qualifiedname) + call setValue(nn, value) + dummy => setNamedItemNS(getAttributes(arg), nn) + + if (associated(dummy)) then + if (getGCstate(getOwnerDocument(arg)).and..not.dummy%inDocument) & + call putNodesInDocument(getOwnerDocument(arg), dummy) + ! ... so that dummy & children are removed from hangingNodes list. + call destroyAllNodesRecursively(dummy) + endif + + if (quickFix) call setGCstate(getOwnerDocument(arg), .true.) + + end subroutine setAttributeNS + + + subroutine removeAttributeNS(arg, namespaceURI, localName, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + + type(DOMException) :: ex2 + type(Node), pointer :: dummy + integer :: e + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "removeAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "removeAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "removeAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%inDocument) & + call setGCstate(getOwnerDocument(arg), .false.) + ! So we dont add the removed nodes to the hanging node list + + dummy => removeNamedItemNS(getAttributes(arg), namespaceURI, localName, ex2) + ! removeNamedItemNS took care of any default attributes + if (inException(ex2)) then + e = getExceptionCode(ex2) + if (e/=NOT_FOUND_ERR) then + if (getFoX_checks().or.e<200) then + call throw_exception(e, "removeAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + else + if (.not.arg%inDocument) then + ! dummy was not in the doc, so was already on hangingNode list. + ! To remove it from the list: + call putNodesInDocument(arg%ownerDocument, dummy) + endif + call destroyAllNodesRecursively(dummy) + endif + + if (arg%inDocument) & + call setGCstate(arg%ownerDocument, .true.) + + end subroutine removeAttributeNS + + + function getAttributeNodeNS(arg, namespaceURI, localName, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + attr => null() ! as per specs, if not found + attr => getNamedItemNS(getAttributes(arg), namespaceURI, localname) + end function getAttributeNodeNS + + + function setAttributeNodeNS(arg, newattr, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: newattr + type(Node), pointer :: attr + type(Node), pointer :: dummy + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.associated(arg%ownerDocument, newattr%ownerDocument)) then + if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then + call throw_exception(WRONG_DOCUMENT_ERR, "setAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(getOwnerElement(newattr), arg)) then + attr => newattr + return + ! Nothing to do, this attribute is already in this element + elseif (associated(getOwnerElement(newattr))) then + if (getFoX_checks().or.INUSE_ATTRIBUTE_ERR<200) then + call throw_exception(INUSE_ATTRIBUTE_ERR, "setAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! this checks if attribute exists already + ! It also does any adding/removing of hangingnodes + ! and sets ownerElement appropriately + dummy => setNamedItemNS(getAttributes(arg), newattr, ex) + attr => dummy + + end function setAttributeNodeNS + + + function removeAttributeNodeNS(arg, oldattr, ex)result(attr) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: oldattr + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "removeAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "removeAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.associated(arg, getOwnerElement(oldattr))) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "removeAttributeNodeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + attr => removeNamedItemNS(getAttributes(arg), & + getNamespaceURI(oldattr), getLocalName(oldattr), ex) + + end function removeAttributeNodeNS + + +! function getElementsByTagNameNS - see m_dom_document + + + function hasAttribute(arg, name, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + logical :: p + + integer :: i + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "hasAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "hasAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + p = .false. + do i = 0, getLength(getAttributes(arg)) - 1 + attr => item(getAttributes(arg), i) + if (getNodeName(attr)==name) then + p = .true. + exit + endif + enddo + + end function hasAttribute + + + function hasAttributeNS(arg, namespaceURI, localName, ex)result(p) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + logical :: p + + integer :: i + type(Node), pointer :: attr + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "hasAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (arg%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "hasAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + p = .false. + do i = 0, getLength(getAttributes(arg))-1 + attr => item(getAttributes(arg), i) + if (getNamespaceURI(attr)==namespaceURI & + .and. getLocalName(attr)==localName) then + p = .true. + exit + endif + enddo + + end function hasAttributeNS + + subroutine setIdAttribute(arg, name, isId, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + logical, intent(in) :: isId + + type(Node), pointer :: np + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setIdAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => getAttributeNode(arg, name) + if (associated(np)) then + call setIsId(np, isId) + else + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "setIdAttribute", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end subroutine setIdAttribute + + subroutine setIdAttributeNS(arg, namespaceURI, localname, isId, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + logical, intent(in) :: isId + + type(Node), pointer :: np + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setIdAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np => getAttributeNodeNS(arg, namespaceURI, localname) + if (associated(np)) then + call setIsId(np, isId) + else + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "setIdAttributeNS", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end subroutine setIdAttributeNS + + subroutine setIdAttributeNode(arg, idAttr, isId, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + type(Node), pointer :: idAttr + logical, intent(in) :: isId + + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setIdAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (.not.associated(arg, getOwnerElement(idAttr))) then + if (getFoX_checks().or.NOT_FOUND_ERR<200) then + call throw_exception(NOT_FOUND_ERR, "setIdAttributeNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + call setIsId(idAttr, isId) + + end subroutine setIdAttributeNode + + + + ! function getName(attribute) result(c) See m_dom_common + +! NB All functions manipulating attributes play with the nodelist +! directly rather than through helper functions. +! This is so that getValue_length can be pure, and the nodeList +! can be explicitly kept up to dat. + +function getspecified(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getspecified", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getspecified", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%elExtras%specified + + end function getspecified + + +subroutine setspecified(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setspecified", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setspecified", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%elExtras%specified = c + + end subroutine setspecified + + +function getisId_DOM(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getisId_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getisId_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%elExtras%isId + + end function getisId_DOM + + +subroutine setisId_DOM(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setisId_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setisId_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%elExtras%isId = c + + end subroutine setisId_DOM + + +function getownerElement(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(Node), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getownerElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getownerElement", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c => np%elExtras%ownerElement + + end function getownerElement + + + function getValue_DOM(arg, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getTextContent_len(arg, .true.)) :: c +#else + character(len=getTextContent_len(arg, associated(arg))) :: c +#endif + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getValue_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(arg)/=ATTRIBUTE_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getValue_DOM", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = getTextContent(arg, ex) + + end function getValue_DOM + + subroutine setValue(arg, value, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: value + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(arg)/=ATTRIBUTE_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + call setTextContent(arg, value, ex) + + end subroutine setValue + + + + pure function isCharData(nodeType) result(p) + integer, intent(in) :: nodeType + logical :: p + + p = (nodeType == TEXT_NODE .or. & + nodeType == COMMENT_NODE .or. & + nodeType == CDATA_SECTION_NODE) + end function isCharData + + + function getLength_characterdata(arg, ex)result(n) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getLength_characterdata", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getLength_characterdata", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + n = size(arg%nodeValue) + + end function getLength_characterdata + + + function subStringData(arg, offset, count, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: offset + integer, intent(in) :: count + character(len=count) :: c + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "subStringData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "subStringData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (offset<0.or.offset>size(arg%nodeValue).or.count<0) then + if (getFoX_checks().or.INDEX_SIZE_ERR<200) then + call throw_exception(INDEX_SIZE_ERR, "subStringData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (offset+count>size(arg%nodeValue)) then + c = str_vs(arg%nodeValue(offset+1:)) + else + c = str_vs(arg%nodeValue(offset+1:offset+count)) + endif + + end function subStringData + + + subroutine appendData(arg, data, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: data + + character, pointer :: tmp(:) + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.checkChars(data, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + tmp => arg%nodeValue + arg%nodeValue => vs_str_alloc(str_vs(tmp)//data) + deallocate(tmp) + + ! We have to do these checks *after* appending data in case offending string + ! spans old & new data + if (arg%nodeType==COMMENT_NODE .and. index(str_vs(arg%nodeValue),"--")>0) then + if (getFoX_checks().or.FoX_INVALID_COMMENT<200) then + call throw_exception(FoX_INVALID_COMMENT, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%nodeType==CDATA_SECTION_NODE .and. index(str_vs(arg%nodeValue), "]]>")>0) then + if (getFoX_checks().or.FoX_INVALID_CDATA_SECTION<200) then + call throw_exception(FoX_INVALID_CDATA_SECTION, "appendData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! And propagate length upwards ... + if (getNodeType(arg)/=COMMENT_NODE) & + call updateTextContentLength(arg, len(data)) + + end subroutine appendData + + + subroutine insertData(arg, offset, data, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: offset + character(len=*), intent(in) :: data + + character, pointer :: tmp(:) + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (offset<0.or.offset>size(arg%nodeValue)) then + if (getFoX_checks().or.INDEX_SIZE_ERR<200) then + call throw_exception(INDEX_SIZE_ERR, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.checkChars(data, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + tmp => arg%nodeValue + arg%nodeValue => vs_str_alloc(str_vs(tmp(:offset))//data//str_vs(tmp(offset+1:))) + deallocate(tmp) + + ! We have to do these checks *after* appending data in case offending string + ! spans old & new data + if (arg%nodeType==COMMENT_NODE .and. index(str_vs(arg%nodeValue),"--")>0) then + if (getFoX_checks().or.FoX_INVALID_COMMENT<200) then + call throw_exception(FoX_INVALID_COMMENT, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%nodeType==CDATA_SECTION_NODE .and. index(str_vs(arg%nodeValue), "]]>")>0) then + if (getFoX_checks().or.FoX_INVALID_CDATA_SECTION<200) then + call throw_exception(FoX_INVALID_CDATA_SECTION, "insertData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! And propagate length upwards ... + if (getNodeType(arg)/=COMMENT_NODE) & + call updateTextContentLength(arg, len(data)) + + end subroutine insertData + + + subroutine deleteData(arg, offset, count, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: offset + integer, intent(in) :: count + + character, pointer :: tmp(:) + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "deleteData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "deleteData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "deleteData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (offset<0.or.offset>size(arg%nodeValue).or.count<0) then + if (getFoX_checks().or.INDEX_SIZE_ERR<200) then + call throw_exception(INDEX_SIZE_ERR, "deleteData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (offset+count>size(arg%nodeValue)) then + n = size(arg%nodeValue)-offset + else + n = count + endif + + tmp => arg%nodeValue + arg%nodeValue => vs_str_alloc(str_vs(tmp(:offset))//str_vs(tmp(offset+count+1:))) + deallocate(tmp) + + ! And propagate length upwards ... + if (getNodeType(arg)/=COMMENT_NODE) & + call updateTextContentLength(arg, -n) + + end subroutine deleteData + + + subroutine replaceData(arg, offset, count, data, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: offset + integer, intent(in) :: count + character(len=*), intent(in) :: data + + character, pointer :: tmp(:) + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.isCharData(arg%nodeType)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (offset<0.or.offset>size(arg%nodeValue).or.count<0) then + if (getFoX_checks().or.INDEX_SIZE_ERR<200) then + call throw_exception(INDEX_SIZE_ERR, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.checkChars(data, getXmlVersionEnum(getOwnerDocument(arg)))) then + if (getFoX_checks().or.FoX_INVALID_CHARACTER<200) then + call throw_exception(FoX_INVALID_CHARACTER, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (offset+count>size(arg%nodeValue)) then + n = len(data)-(size(arg%nodeValue)-offset) + else + n = len(data)-count + endif + + tmp => arg%nodeValue + if (offset+count <= size(arg%nodeValue)) then + arg%nodeValue => vs_str_alloc(str_vs(tmp(:offset))//data//str_vs(tmp(offset+count+1:))) + else + arg%nodeValue => vs_str_alloc(str_vs(tmp(:offset))//data) + endif + deallocate(tmp) + + ! We have to do these checks *after* appending data in case offending string + ! spans old & new data + if (arg%nodeType==COMMENT_NODE .and. index(str_vs(arg%nodeValue),"--")>0) then + if (getFoX_checks().or.FoX_INVALID_COMMENT<200) then + call throw_exception(FoX_INVALID_COMMENT, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%nodeType==CDATA_SECTION_NODE .and. index(str_vs(arg%nodeValue), "]]>")>0) then + if (getFoX_checks().or.FoX_INVALID_CDATA_SECTION<200) then + call throw_exception(FoX_INVALID_CDATA_SECTION, "replaceData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! And propagate length upwards ... + if (getNodeType(arg)/=COMMENT_NODE) & + call updateTextContentLength(arg, n) + + end subroutine replaceData + + + + + pure function getnotationName_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==ENTITY_NODE .or. & + .false.)) then + n = size(np%dtdExtras%notationName) + else + n = 0 + endif + end function getnotationName_len +function getnotationName(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getnotationName_len(np, .true.)) :: c +#else + character(len=getnotationName_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getnotationName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getnotationName", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%dtdExtras%notationName) + + end function getnotationName + + +!Internally-used getters/setters: + + function getillFormed(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getillFormed", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getillFormed", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%dtdExtras%illFormed + + end function getillFormed + + subroutine setillFormed(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setillFormed", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setillFormed", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + np%dtdExtras%illFormed = c + + end subroutine setillFormed + + + + pure function getstringValue_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==ENTITY_NODE .or. & + .false.)) then + n = size(np%nodeValue) + else + n = 0 + endif + end function getstringValue_len +function getstringValue(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getstringValue_len(np, .true.)) :: c +#else + character(len=getstringValue_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getstringValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getstringValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%nodeValue) + + end function getstringValue + + subroutine setstringValue(np, c, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*) :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setstringValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setstringValue", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (associated(np%nodeValue)) deallocate(np%nodeValue) + np%nodeValue => vs_str_alloc(c) + + end subroutine setstringValue + + + + + + pure function getTarget_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==PROCESSING_INSTRUCTION_NODE .or. & + .false.)) then + n = size(np%nodename) + else + n = 0 + endif + end function getTarget_len +function getTarget(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getTarget_len(np, .true.)) :: c +#else + character(len=getTarget_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getTarget", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=PROCESSING_INSTRUCTION_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getTarget", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%nodename) + + end function getTarget + + + + + function splitText(arg, offset, ex)result(np) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(in) :: offset + + type(Node), pointer :: np + + character, pointer :: tmp(:) + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "splitText", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (.not.(arg%nodeType==TEXT_NODE.or.arg%nodeType==CDATA_SECTION_NODE)) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "splitText", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "splitText", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (offset<0 .or. offset>size(arg%nodeValue)) then + if (getFoX_checks().or.INDEX_SIZE_ERR<200) then + call throw_exception(INDEX_SIZE_ERR, "splitText", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + tmp => arg%nodeValue + if (arg%nodeType==TEXT_NODE) then + np => createTextNode(arg%ownerDocument, str_vs(tmp(offset+1:))) + elseif (arg%nodeType==CDATA_SECTION_NODE) then + np => createCdataSection(arg%ownerDocument, str_vs(tmp(offset+1:))) + endif + arg%nodeValue => vs_str_alloc(str_vs(tmp(:offset))) + deallocate(tmp) + if (associated(arg%parentNode)) then + if (associated(arg%nextSibling)) then + np => insertBefore(arg%parentNode, np, arg%nextSibling) + else + np => appendChild(arg%parentNode, np) + endif + endif + + end function splitText + +function getisElementContentWhitespace(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getisElementContentWhitespace", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=TEXT_NODE .and. & +getNodeType(np)/=CDATA_SECTION_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getisElementContentWhitespace", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = np%ignorableWhitespace + + end function getisElementContentWhitespace + + + subroutine setIsElementContentWhitespace(np, isElementContentWhitespace, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + logical :: isElementContentWhitespace + + integer :: n + + np%ignorableWhitespace = isElementContentWhitespace + + if (isElementContentWhitespace) then + n = -np%textContentLength + else + n = size(np%nodeValue) + endif + + call updateTextContentLength(np, n) + end subroutine setIsElementContentWhitespace + +! function getWholeText +! function replaceWholeText + + + + + pure function getdata_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==TEXT_NODE .or. & + np%nodeType==COMMENT_NODE .or. & + np%nodeType==CDATA_SECTION_NODE .or. & + np%nodeType==PROCESSING_INSTRUCTION_NODE .or. & + .false.)) then + n = size(np%nodeValue) + else + n = 0 + endif + end function getdata_len +function getdata(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getdata_len(np, .true.)) :: c +#else + character(len=getdata_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getdata", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=TEXT_NODE .and. & +getNodeType(np)/=COMMENT_NODE .and. & +getNodeType(np)/=CDATA_SECTION_NODE .and. & +getNodeType(np)/=PROCESSING_INSTRUCTION_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getdata", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%nodeValue) + + end function getdata + + + subroutine setData(arg, data, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*) :: data + + integer :: n + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + +!NB special case in order to check readonly correctly + if (arg%nodeType==TEXT_NODE .or. & + arg%nodeType==COMMENT_NODE .or. & + arg%nodeType==CDATA_SECTION_NODE .or. & + arg%nodeType==PROCESSING_INSTRUCTION_NODE) then + if (arg%readonly) then + if (getFoX_checks().or.NO_MODIFICATION_ALLOWED_ERR<200) then + call throw_exception(NO_MODIFICATION_ALLOWED_ERR, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + else + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + select case (arg%nodeType) + case (CDATA_SECTION_NODE) + if (index(data,"]]>")>0) then + if (getFoX_checks().or.FoX_INVALID_CDATA_SECTION<200) then + call throw_exception(FoX_INVALID_CDATA_SECTION, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (COMMENT_NODE) + if (index(data,"--")>0) then + if (getFoX_checks().or.FoX_INVALID_COMMENT<200) then + call throw_exception(FoX_INVALID_COMMENT, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + case (PROCESSING_INSTRUCTION_NODE) + if (index(data,"?>")>0) then + if (getFoX_checks().or.FoX_INVALID_PI_DATA<200) then + call throw_exception(FoX_INVALID_PI_DATA, "setData", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + end select + + deallocate(arg%nodeValue) + arg%nodeValue => vs_str_alloc(data) + + if (arg%nodeType==TEXT_NODE .or. & + arg%nodeType==CDATA_SECTION_NODE) then + n = len(data) - arg%textContentLength + call updateTextContentLength(arg, n) + endif + + end subroutine setData + + + pure function getname_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==DOCUMENT_TYPE_NODE .or. & + np%nodeType==ATTRIBUTE_NODE .or. & + .false.)) then + n = size(np%nodeName) + else + n = 0 + endif + end function getname_len +function getname(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getname_len(np, .true.)) :: c +#else + character(len=getname_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getname", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_TYPE_NODE .and. & +getNodeType(np)/=ATTRIBUTE_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getname", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%nodeName) + + end function getname + + + + pure function getpublicId_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==DOCUMENT_TYPE_NODE .or. & + np%nodeType==NOTATION_NODE .or. & + np%nodeType==ENTITY_NODE .or. & + .false.)) then + n = size(np%dtdExtras%publicId) + else + n = 0 + endif + end function getpublicId_len +function getpublicId(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getpublicId_len(np, .true.)) :: c +#else + character(len=getpublicId_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getpublicId", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_TYPE_NODE .and. & +getNodeType(np)/=NOTATION_NODE .and. & +getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getpublicId", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%dtdExtras%publicId) + + end function getpublicId + + + + pure function getsystemId_len(np, p) result(n) + type(Node), intent(in) :: np + logical, intent(in) :: p + integer :: n + + if (p .and. ( & + np%nodeType==DOCUMENT_TYPE_NODE .or. & + np%nodeType==NOTATION_NODE .or. & + np%nodeType==ENTITY_NODE .or. & + .false.)) then + n = size(np%dtdExtras%systemId) + else + n = 0 + endif + end function getsystemId_len +function getsystemId(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np +#ifdef RESTRICTED_ASSOCIATED_BUG + character(len=getsystemId_len(np, .true.)) :: c +#else + character(len=getsystemId_len(np, associated(np))) :: c +#endif + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getsystemId", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=DOCUMENT_TYPE_NODE .and. & +getNodeType(np)/=NOTATION_NODE .and. & +getNodeType(np)/=ENTITY_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getsystemId", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c = str_vs(np%dtdExtras%systemId) + + end function getsystemId + + + + + function getnamespaceNodes(np, ex)result(c) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + type(NodeList), pointer :: c + + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "getnamespaceNodes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(np)/=ELEMENT_NODE .and. & + .true.) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "getnamespaceNodes", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + c => np%elExtras%namespaceNodes + + end function getnamespaceNodes + + + subroutine appendNSNode(np, prefix, namespaceURI, specified, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: np + character(len=*), intent(in) :: prefix + character(len=*), intent(in) :: namespaceURI + logical, intent(in) :: specified + + type(Node), pointer :: ns + type(NodeList), pointer :: nsnodes + integer :: i + logical :: quickFix + + if (.not.associated(np)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "appendNSNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (np%nodeType /= ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "appendNSNode", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + ! We never put namespace nodes in the hanging nodes + ! list since they can never be separated from their + ! parent element node, so will always be destroyed alongside it. + quickFix = getGCState(getOwnerDocument(np)) + call setGCState(getOwnerDocument(np), .false.) + nsnodes => getNamespaceNodes(np) + ! If we already have this prefix registered in the list, then remove it + do i = 0, getLength(nsNodes)-1 + ns => item(nsNodes, i) +! Intel 8.1 & 9.1 insist on separate variable here and just below + if (getPrefix(ns)==prefix) then + call setNamespaceURI(ns, namespaceURI) + exit + endif + enddo + if (i==getLength(nsNodes)) then + ns => createNamespaceNode(getOwnerDocument(np), & + prefix, namespaceURI, specified) + call append_nl(nsNodes, ns) + endif + call setGCState(getOwnerDocument(np), quickFix) + + end subroutine appendNSNode + + subroutine normalizeDocument(doc, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: doc + + type(Node), pointer :: this, treeroot, dummy, new, old, nsp + type(DOMConfiguration), pointer :: dc + logical :: doneAttributes, doneChildren + integer :: i_tree, i_children + + type(Node), pointer :: parent, attr + type(NamedNodeMap), pointer :: attrs + type(NodeList), pointer :: nsNodes, nsNodesParent + integer :: i, nsIndex + logical :: merged, ns + + if (.not.associated(doc)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "normalizeDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (getNodeType(doc)/=DOCUMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "normalizeDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + dc => getDomConfig(doc) + ns = getParameter(dc, "namespaces") + treeroot => doc + + call setGCstate(doc, .false.) + ! switch off the memory management, we are going + ! to destroy all nodes we remove from the tree + ! immediately. + + ! exception object is *not* passed through in any + ! of the DOM calls below. This is because all of + ! these should succeed - if they dont then there + ! is a problem so we need to terminate immediately + + i_tree = 0 + doneChildren = .false. + doneAttributes = .false. + this => treeroot + do + if (.not.doneChildren.and..not.(getNodeType(this)==ELEMENT_NODE.and.doneAttributes)) then + + if (.not.getReadonly(this)) then + select case (getNodeType(this)) + case (ELEMENT_NODE) + if (ns) then + + ! Clear all current namespace nodes: + nsnodes => getNamespaceNodes(this) + do i = 1, getLength(nsNodes) + call destroyNode(nsNodes%nodes(i)%this) + enddo + deallocate(nsNodes%nodes) + + parent => getParentNode(this) + do while (associated(parent)) + ! Go up (through perhaps multiple entref nodes) + if (getNodeType(parent)==ELEMENT_NODE) exit + parent => getParentNode(parent) + enddo + ! Inherit from parent (or not ...) + if (associated(parent)) then + nsNodesParent => getNamespaceNodes(parent) + allocate(nsNodes%nodes(getLength(nsNodesParent))) + nsNodes%length = getLength(nsNodesParent) + do i = 0, getLength(nsNodes) - 1 + ! separate variable for intel + nsp => item(nsNodesParent, i) + nsNodes%nodes(i+1)%this => & + createNamespaceNode(getOwnerDocument(this), & + getPrefix(nsp), getNamespaceURI(nsp), & + specified=.false.) + enddo + else + allocate(nsNodes%nodes(0)) + nsNodes%length = 0 + endif + + ! Now check for broken NS declarations, and add namespace + ! nodes for all non-broken declarations + attrs => getAttributes(this) + do i = 0, getLength(attrs)-1 + attr => item(attrs, i) + if ((getLocalName(attr)=="xmlns" & + .or.getPrefix(attr)=="xmlns") & + .and.getNamespaceURI(attr)/="http://www.w3.org/2000/xmlns/") then + ! This can only I think happen if we bugger about with setPrefix ... + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "normalizeDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (getNamespaceURI(attr)=="http://www.w3.org/2000/xmlns/") then + if (getLocalName(attr)=="xmlns") then + call appendNSNode(this, "", getValue(attr), specified=.true.) + else + call appendNSNode(this, getLocalName(attr), & + getValue(attr), specified=.true.) + endif + endif + enddo + + + if (getNamespaceURI(this)/="") then + ! Is the nsURI of this node bound to its prefix? + ! This will automatically do any necessary replacements ... + if (getPrefix(this)=="") then + if (.not.isDefaultNamespace(this, getNamespaceURI(this))) then + ! We are dealing with the default prefix + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns", getNamespaceURI(this)) + call appendNSNode(this, "", getNamespaceURI(this), specified=.true.) + endif + elseif (lookupNamespaceURI(this, getPrefix(this))/=getNamespaceURI(this)) then + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(this), getNamespaceURI(this)) + call appendNSNode(this, getPrefix(this), getNamespaceURI(this), specified=.true.) + endif + else + ! No (or empty) namespace URI ... + if (getLocalName(this)=="") then + ! DOM level 1 node ... report error + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "normalizeDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + else + ! We must declare the elements prefix to have an empty nsURI + if (lookupNamespaceURI(this, getPrefix(this))/="") then + if (getPrefix(this)=="") then + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns", "") + else + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(this), "") + endif + ! and add a namespace node for the empty nsURI + call appendNSNode(this, getPrefix(this), "", specified=.true.) + endif + endif + endif + + do i = 0, getLength(attrs)-1 + ! This loops over the number of attrs present initially, so any we + ! add within this loop will not get checked - but they will only + ! be namespace declarations about which we dont care anyway. + attr => item(attrs, i) + if (getNamespaceURI(attr)=="http://www.w3.org/2000/xmlns/") then + cycle ! We already worried about it above. + elseif (getNamespaceURI(attr)=="http://www.w3.org/XML/1998/namespace") then + cycle ! We dont have to declare these + elseif (getNamespaceURI(attr)/="") then + ! This is a namespaced attribute + if (getPrefix(attr)=="" & + .or. lookupNamespaceURI(this, getPrefix(attr))/=getNamespaceURI(attr)) then + ! It has an inappropriate prefix + if (lookupPrefix(this, getNamespaceURI(attr))/="") then + ! then an appropriate prefix exists, use it. + call setPrefix(attr, lookupPrefix(this, getNamespaceURI(attr))) + ! FIXME should be "most local" prefix. Make sure lookupPrefix does that. + else + ! No suitable prefix exists, declare one. + if (getPrefix(attr)/="") then + ! Then the current prefix is not in use, its just undeclared. + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(attr), getNamespaceURI(attr)) + call appendNSNode(this, getPrefix(attr), getNamespaceURI(attr), specified=.true.) + else + ! This node has no prefix, but needs one. Make it up. + nsIndex = 1 + do while (lookupNamespaceURI(this, "NS"//nsIndex)/="") + ! FIXME this will exit if the namespace is undeclared *or* if it is declared to be empty. + nsIndex = nsIndex+1 + enddo + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:NS"//nsIndex, getNamespaceURI(attr)) + ! and create namespace node + call appendNSNode(this, "NS"//nsIndex, getNamespaceURI(attr), specified=.true.) + call setPrefix(attr, "NS"//nsIndex) + endif + endif + endif + else + ! attribute has no namespace URI + if (getLocalName(this)=="") then + ! DOM level 1 node ... report error + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "normalizeDocument", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + ! otherwise no problem + endif + enddo + + endif + + case (ATTRIBUTE_NODE) + if (getParameter(dc, "entities")) then + ! we dont care about any attribute children, + ! we arent going to do anything + doneChildren = .true. + endif + + case (TEXT_NODE) + ! we may need to reset "this" later on ... + old => getPreviousSibling(this) + if (.not.associated(old)) old => getParentNode(this) + merged = .false. + if (getIsElementContentWhitespace(this) & + .and..not.getParameter(dc, "element-content-whitespace")) then + dummy => removeChild(getParentNode(this), this) + call destroy(dummy) + this => old + merged = .true. + endif + if (.not.merged) then + ! We didnt just remove this node. + ! Do we need to normalize? + dummy => getPreviousSibling(this) + if (associated(dummy)) then + if (getNodeType(dummy)==TEXT_NODE) then + call appendData(dummy, getData(this)) + parent => getParentNode(this) + dummy => removeChild(parent, this) + call destroy(dummy) + this => old + endif + endif + endif + + case (CDATA_SECTION_NODE) + if (.not.getParameter(dc, "cdata-sections")) then + ! we may need to reset "this" later on ... + old => getPreviousSibling(this) + if (.not.associated(old)) old => getParentNode(this) + merged = .false. + dummy => getPreviousSibling(this) + if (associated(dummy)) then + if (getNodeType(dummy)==TEXT_NODE) then + ! append the data to the previous node & chuck away this node + call appendData(dummy, getData(this)) + dummy => removeChild(getParentNode(this), this) + call destroy(dummy) + this => old + merged =.true. + endif + endif + if (.not.merged) then + ! we didnt merge it so just convert this to a text node + new => createTextNode(doc, getData(this)) + dummy => replaceChild(getParentNode(this), new, this) + call destroy(dummy) + this => new + endif + elseif (.not.getParameter(dc, "split-cdata-sections")) then + ! Actually, on re-reading DOM 3, this is a ridiculous + ! option. Ignoring for now. + endif + + case (ENTITY_REFERENCE_NODE) + if (.not.getParameter(dc, "entities")) then + if (associated(getFirstChild(this))) then + !If this node is not representing an unexpanded entity + ! we will need to reset "this" later on ... + old => getPreviousSibling(this) + if (.not.associated(old)) old => getParentNode(this) + ! take each child, and insert it immediately before the current node + do i_children = 0, getLength(getChildNodes(this))-1 + dummy => insertBefore(getParentNode(this), getFirstChild(this), this) + enddo + ! and finally remove the current node + dummy => removeChild(getParentNode(this), this) + call destroy(dummy) + ! and set the "this" pointer back so we go over these again + this => old + endif + endif + + case (COMMENT_NODE) + if (.not.getParameter(dc, "comments")) then + old => getPreviousSibling(this) + if (.not.associated(old)) old => getParentNode(this) + dummy => removeChild(getParentNode(this), this) + call destroy(dummy) + this => old + endif + + case (DOCUMENT_TYPE_NODE) + if (getParameter(dc, "canonical-form")) then + old => getPreviousSibling(this) + if (.not.associated(old)) old => getParentNode(this) + dummy => removeChild(getParentNode(this), this) + call destroy(this) + this => old + endif + + end select + endif + + else + if (getNodeType(this)==ELEMENT_NODE.and..not.doneChildren) then + doneAttributes = .true. + else + + 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 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 normalizeDocument + + recursive subroutine namespaceFixup(this, deep, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: this + logical, intent(in) :: deep + + type(Node), pointer :: parent, child, attr, nsp + type(NamedNodeMap), pointer :: attrs + type(NodeList), pointer :: nsNodes, nsNodesParent + integer :: i, nsIndex + + if (getNodeType(this) /= ELEMENT_NODE & + .and. getNodeType(this) /= ENTITY_REFERENCE_NODE & + .and. getNodeType(this)/=DOCUMENT_FRAGMENT_NODE) then + return + endif + + if (this%nodeType==ELEMENT_NODE) then + + ! Clear all current namespace nodes: + nsnodes => getNamespaceNodes(this) + do i = 1, getLength(nsNodes) + call destroyNode(nsNodes%nodes(i)%this) + enddo + deallocate(nsNodes%nodes) + + parent => getParentNode(this) + do while (associated(parent)) + ! Go up (through perhaps multiple entref nodes) + if (getNodeType(parent)==ELEMENT_NODE) exit + parent => getParentNode(parent) + enddo + ! Inherit from parent (or not ...) + if (associated(parent)) then + nsNodesParent => getNamespaceNodes(parent) + allocate(nsNodes%nodes(getLength(nsNodesParent))) + nsNodes%length = getLength(nsNodesParent) + do i = 0, getLength(nsNodes) - 1 + ! separate variable for intel + nsp => item(nsNodesParent, i) + nsNodes%nodes(i+1)%this => & + createNamespaceNode(getOwnerDocument(this), & + getPrefix(nsp), getNamespaceURI(nsp), & + specified=.false.) + enddo + else + allocate(nsNodes%nodes(0)) + nsNodes%length = 0 + endif + + ! Now check for broken NS declarations, and add namespace + ! nodes for all non-broken declarations + attrs => getAttributes(this) + do i = 0, getLength(attrs)-1 + attr => item(attrs, i) + if ((getLocalName(attr)=="xmlns" & + .or.getPrefix(attr)=="xmlns") & + .and.getNamespaceURI(attr)/="http://www.w3.org/2000/xmlns/") then + ! This can only I think happen if we bugger about with setPrefix ... + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "namespaceFixup", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + if (getNamespaceURI(attr)=="http://www.w3.org/2000/xmlns/") then + if (getLocalName(attr)=="xmlns") then + call appendNSNode(this, "", getValue(attr), specified=.true.) + else + call appendNSNode(this, getLocalName(attr), & + getValue(attr), specified=.true.) + endif + endif + enddo + + + if (getNamespaceURI(this)/="") then + ! Is the nsURI of this node bound to its prefix? + ! This will automatically do any necessary replacements ... + if (getPrefix(this)=="") then + if (.not.isDefaultNamespace(this, getNamespaceURI(this))) then + ! We are dealing with the default prefix + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns", getNamespaceURI(this)) + call appendNSNode(this, "", getNamespaceURI(this), specified=.true.) + endif + elseif (lookupNamespaceURI(this, getPrefix(this))/=getNamespaceURI(this)) then + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(this), getNamespaceURI(this)) + call appendNSNode(this, getPrefix(this), getNamespaceURI(this), specified=.true.) + endif + else + ! No (or empty) namespace URI ... + if (getLocalName(this)=="") then + ! DOM level 1 node ... report error + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "namespaceFixup", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + else + ! We must declare the elements prefix to have an empty nsURI + if (lookupNamespaceURI(this, getPrefix(this))/="") then + if (getPrefix(this)=="") then + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns", "") + else + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(this), "") + endif + ! and add a namespace node for the empty nsURI + call appendNSNode(this, getPrefix(this), "", specified=.true.) + endif + endif + endif + + do i = 0, getLength(attrs)-1 + ! This loops over the number of attrs present initially, so any we + ! add within this loop will not get checked - but they will only + ! be namespace declarations about which we dont care anyway. + attr => item(attrs, i) + if (getNamespaceURI(attr)=="http://www.w3.org/2000/xmlns/") then + cycle ! We already worried about it above. + elseif (getNamespaceURI(attr)=="http://www.w3.org/XML/1998/namespace") then + cycle ! We dont have to declare these + elseif (getNamespaceURI(attr)/="") then + ! This is a namespaced attribute + if (getPrefix(attr)=="" & + .or. lookupNamespaceURI(this, getPrefix(attr))/=getNamespaceURI(attr)) then + ! It has an inappropriate prefix + if (lookupPrefix(this, getNamespaceURI(attr))/="") then + ! then an appropriate prefix exists, use it. + call setPrefix(attr, lookupPrefix(this, getNamespaceURI(attr))) + ! FIXME should be "most local" prefix. Make sure lookupPrefix does that. + else + ! No suitable prefix exists, declare one. + if (getPrefix(attr)/="") then + ! Then the current prefix is not in use, its just undeclared. + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:"//getPrefix(attr), getNamespaceURI(attr)) + call appendNSNode(this, getPrefix(attr), getNamespaceURI(attr), specified=.true.) + else + ! This node has no prefix, but needs one. Make it up. + nsIndex = 1 + do while (lookupNamespaceURI(this, "NS"//nsIndex)/="") + ! FIXME this will exit if the namespace is undeclared *or* if it is declared to be empty. + nsIndex = nsIndex+1 + enddo + call setAttributeNS(this, "http://www.w3.org/2000/xmlns/", & + "xmlns:NS"//nsIndex, getNamespaceURI(attr)) + ! and create namespace node + call appendNSNode(this, "NS"//nsIndex, getNamespaceURI(attr), specified=.true.) + call setPrefix(attr, "NS"//nsIndex) + endif + endif + endif + else + ! attribute has no namespace URI + if (getLocalName(this)=="") then + ! DOM level 1 node ... report error + if (getFoX_checks().or.NAMESPACE_ERR<200) then + call throw_exception(NAMESPACE_ERR, "namespaceFixup", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + ! otherwise no problem + endif + enddo + + endif + + if (deep) then + ! And now call this on all appropriate children ... + child => getFirstChild(this) + do while (associated(child)) + call namespaceFixup(child, .true.) + child => getNextSibling(child) + enddo + endif + + end subroutine namespaceFixup + + +end module m_dom_dom diff --git a/src/xml/dom/m_dom_error.F90 b/src/xml/dom/m_dom_error.F90 new file mode 100644 index 000000000..c6fdae418 --- /dev/null +++ b/src/xml/dom/m_dom_error.F90 @@ -0,0 +1,211 @@ +module m_dom_error + + use fox_m_fsys_abort_flush, only: pxfabort + + use m_common_error, only: error_stack, add_error, in_error, destroy_error_stack + + implicit none + private + + type DOMException + private + type(error_stack) :: stack + end type DOMException + + integer, parameter, public :: INDEX_SIZE_ERR = 1 + integer, parameter, public :: DOMSTRING_SIZE_ERR = 2 + integer, parameter, public :: HIERARCHY_REQUEST_ERR = 3 + integer, parameter, public :: WRONG_DOCUMENT_ERR = 4 + integer, parameter, public :: INVALID_CHARACTER_ERR = 5 + integer, parameter, public :: NO_DATA_ALLOWED_ERR = 6 + integer, parameter, public :: NO_MODIFICATION_ALLOWED_ERR = 7 + integer, parameter, public :: NOT_FOUND_ERR = 8 + integer, parameter, public :: NOT_SUPPORTED_ERR = 9 + integer, parameter, public :: INUSE_ATTRIBUTE_ERR = 10 + integer, parameter, public :: INVALID_STATE_ERR = 11 + integer, parameter, public :: SYNTAX_ERR = 12 + integer, parameter, public :: INVALID_MODIFICATION_ERR = 13 + integer, parameter, public :: NAMESPACE_ERR = 14 + integer, parameter, public :: INVALID_ACCESS_ERR = 15 + integer, parameter, public :: VALIDATION_ERR = 16 + integer, parameter, public :: TYPE_MISMATCH_ERR = 17 + + integer, parameter, public :: INVALID_EXPRESSION_ERR = 51 + integer, parameter, public :: TYPE_ERR = 52 + + integer, parameter, public :: PARSE_ERR = 81 + integer, parameter, public :: SERIALIZE_ERR = 82 + + integer, parameter, public :: FoX_INVALID_NODE = 201 + integer, parameter, public :: FoX_INVALID_CHARACTER = 202 + integer, parameter, public :: FoX_NO_SUCH_ENTITY = 203 + integer, parameter, public :: FoX_INVALID_PI_DATA = 204 + integer, parameter, public :: FoX_INVALID_CDATA_SECTION = 205 + integer, parameter, public :: FoX_HIERARCHY_REQUEST_ERR = 206 + integer, parameter, public :: FoX_INVALID_PUBLIC_ID = 207 + integer, parameter, public :: FoX_INVALID_SYSTEM_ID = 208 + integer, parameter, public :: FoX_INVALID_COMMENT = 209 + integer, parameter, public :: FoX_NODE_IS_NULL = 210 + integer, parameter, public :: FoX_INVALID_ENTITY = 211 + integer, parameter, public :: FoX_INVALID_URI = 212 + integer, parameter, public :: FoX_IMPL_IS_NULL = 213 + integer, parameter, public :: FoX_MAP_IS_NULL = 214 + integer, parameter, public :: FoX_LIST_IS_NULL = 215 + + integer, parameter, public :: FoX_INTERNAL_ERROR = 999 + + + public :: DOMException + public :: getExceptionCode + public :: throw_exception + public :: inException + public :: dom_error + public :: internal_error + +contains + + pure function errorString(code) result(s) + integer, intent(in) :: code + character(len=27) :: s + + select case(code) + case(1) + s = "INDEX_SIZE_ERR" + case(2) + s = "DOMSTRING_SIZE_ERR" + case(3) + s = "HIERARCHY_REQUEST_ERR" + case(4) + s = "WRONG_DOCUMENT_ERR" + case(5) + s = "INVALID_CHARACTER_ERR" + case(6) + s = "NO_DATA_ALLOWED_ERR" + case(7) + s = "NO_MODIFICATION_ALLOWED_ERR" + case(8) + s = "NOT_FOUND_ERR" + case(9) + s = "NOT_SUPPORTED_ERR" + case(10) + s = "INUSE_ATTRIBUTE_ERR" + case(11) + s = "INVALID_STATE_ERR" + case(12) + s = "SYNTAX_ERR" + case(13) + s = "INVALID_MODIFICATION_ERR" + case(14) + s = "NAMESPACE_ERR" + case(15) + s = "INVALID_ACCESS_ERR" + case(16) + s = "VALIDATION_ERR" + case(18) + s = "TYPE_MISMATCH_ERR" + case(51) + s = "INVALID_EXPRESSION_ERR" + case(52) + s = "TYPE_ERR" + case(81) + s = "PARSE_ERR" + case(82) + s = "SERIALIZE_ERR" + case(201) + s = "FoX_INVALID_NODE" + case(202) + s = "FoX_INVALID_CHARACTER" + case(203) + s = "FoX_NO_SUCH_ENTITY" + case(204) + s = "FoX_INVALID_PI_DATA" + case(205) + s = "FoX_INVALID_CDATA_SECTION" + case(206) + s = "FoX_HIERARCHY_REQUEST_ERR" + case(207) + s = "FoX_INVALID_PUBLIC_ID" + case(208) + s = "FoX_INVALID_SYSTEM_ID" + case(209) + s = "FoX_INVALID_COMMENT" + case(210) + s = "FoX_NODE_IS_NULL" + case(211) + s = "FoX_INVALID_ENTITY" + case(212) + s = "FoX_NO_DOCTYPE" + case(213) + s = "FoX_IMPL_IS_NULL" + case(214) + s = "FoX_MAP_IS_NULL" + case(215) + s = "FoX_LIST_IS_NULL" + case default + s = "INTERNAL ERROR!!!!" + end select + + end function errorString + + function getExceptionCode(ex) result(n) + type(DOMException), intent(inout) :: ex + integer :: n + + if (in_error(ex%stack)) then + n = ex%stack%stack(size(ex%stack%stack))%error_code + call destroy_error_stack(ex%stack) + else + n = 0 + endif + end function getExceptionCode + + subroutine throw_exception(code, msg, ex) + integer, intent(in) :: code + character(len=*), intent(in) :: msg + type(DOMException), intent(inout), optional :: ex + + if (present(ex)) then + call add_error(ex%stack, msg, error_code=code) ! FIXME + else + write(0,'(a)') errorString(code) + write(0,'(i0,a)') code, " "//msg + call pxfabort() + endif + + end subroutine throw_exception + + + subroutine dom_error(name,code,msg) + character(len=*), intent(in) :: name, msg + integer, intent(in) :: code + + write(0,'(4a)') "Routine ", name, ":", msg + write(0,'(a)') errorString(code) + call pxfabort() + + end subroutine dom_error + + + subroutine destroyDOMException(ex) + type(DOMException), intent(inout) :: ex + + call destroy_error_stack(ex%stack) + end subroutine destroyDOMException + + + subroutine internal_error(name,msg) + character(len=*), intent(in) :: name, msg + + write(0,'(4a)') "Internal error in ", name, ":", msg + call pxfabort() + + end subroutine internal_error + + function inException(ex) result(p) + type(DOMException), intent(in) :: ex + logical :: p + + p = in_error(ex%stack) + end function inException + +end module m_dom_error diff --git a/src/xml/dom/m_dom_extras.F90 b/src/xml/dom/m_dom_extras.F90 new file mode 100644 index 000000000..f301622d5 --- /dev/null +++ b/src/xml/dom/m_dom_extras.F90 @@ -0,0 +1,2376 @@ +module m_dom_extras + + use fox_m_fsys_realtypes, only: sp, dp + use fox_m_fsys_parse_input, only: rts + + use m_dom_error, only: DOMException, inException, throw_exception, & + FoX_NODE_IS_NULL, FoX_INVALID_NODE + use m_dom_dom, only: Node, ELEMENT_NODE, & + getAttribute, getAttributeNS, getTextContent, getNodeType, getFoX_checks + + implicit none + private + + public :: extractDataContent + public :: extractDataAttribute + public :: extractDataAttributeNS + + interface extractDataContent + module procedure extractDataContentCmplxDpSca + module procedure extractDataContentCmplxDpArr + module procedure extractDataContentCmplxDpMat + module procedure extractDataContentCmplxSpSca + module procedure extractDataContentCmplxSpArr + module procedure extractDataContentCmplxSpMat + module procedure extractDataContentRealDpSca + module procedure extractDataContentRealDpArr + module procedure extractDataContentRealDpMat + module procedure extractDataContentRealSpSca + module procedure extractDataContentRealSpArr + module procedure extractDataContentRealSpMat + module procedure extractDataContentIntSca + module procedure extractDataContentIntArr + module procedure extractDataContentIntMat + module procedure extractDataContentLgSca + module procedure extractDataContentLgArr + module procedure extractDataContentLgMat + module procedure extractDataContentChSca + module procedure extractDataContentChArr + module procedure extractDataContentChMat + + end interface extractDataContent + + interface extractDataAttribute + module procedure extractDataAttributeCmplxDpSca + module procedure extractDataAttributeCmplxDpArr + module procedure extractDataAttributeCmplxDpMat + module procedure extractDataAttributeCmplxSpSca + module procedure extractDataAttributeCmplxSpArr + module procedure extractDataAttributeCmplxSpMat + module procedure extractDataAttributeRealDpSca + module procedure extractDataAttributeRealDpArr + module procedure extractDataAttributeRealDpMat + module procedure extractDataAttributeRealSpSca + module procedure extractDataAttributeRealSpArr + module procedure extractDataAttributeRealSpMat + module procedure extractDataAttributeIntSca + module procedure extractDataAttributeIntArr + module procedure extractDataAttributeIntMat + module procedure extractDataAttributeLgSca + module procedure extractDataAttributeLgArr + module procedure extractDataAttributeLgMat + module procedure extractDataAttributeChSca + module procedure extractDataAttributeChArr + module procedure extractDataAttributeChMat + + end interface extractDataAttribute + + interface extractDataAttributeNS + module procedure extractDataAttNSCmplxDpSca + module procedure extractDataAttNSCmplxDpArr + module procedure extractDataAttNSCmplxDpMat + module procedure extractDataAttNSCmplxSpSca + module procedure extractDataAttNSCmplxSpArr + module procedure extractDataAttNSCmplxSpMat + module procedure extractDataAttNSRealDpSca + module procedure extractDataAttNSRealDpArr + module procedure extractDataAttNSRealDpMat + module procedure extractDataAttNSRealSpSca + module procedure extractDataAttNSRealSpArr + module procedure extractDataAttNSRealSpMat + module procedure extractDataAttNSIntSca + module procedure extractDataAttNSIntArr + module procedure extractDataAttNSIntMat + module procedure extractDataAttNSLgSca + module procedure extractDataAttNSLgArr + module procedure extractDataAttNSLgMat + module procedure extractDataAttNSChSca + module procedure extractDataAttNSChArr + module procedure extractDataAttNSChMat + + end interface extractDataAttributeNS + +contains + +subroutine extractDataContentCmplxDpSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(dp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxDpSca + +subroutine extractDataContentCmplxSpSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(sp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxSpSca + +subroutine extractDataContentRealDpSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(dp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealDpSca + +subroutine extractDataContentRealSpSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(sp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealSpSca + +subroutine extractDataContentIntSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentIntSca + +subroutine extractDataContentLgSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + logical, intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentLgSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentLgSca + +subroutine extractDataContentChSca(arg, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentChSca", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + if (present(ex)) then + call rts(getTextContent(arg, ex), data, separator, csv, num, iostat) + else + call rts(getTextContent(arg), data, separator, csv, num, iostat) + endif + + end subroutine extractDataContentChSca + + +subroutine extractDataContentCmplxDpArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(dp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxDpArr + +subroutine extractDataContentCmplxSpArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(sp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxSpArr + +subroutine extractDataContentRealDpArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(dp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealDpArr + +subroutine extractDataContentRealSpArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(sp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealSpArr + +subroutine extractDataContentIntArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentIntArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentIntArr + +subroutine extractDataContentLgArr(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + logical, dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentLgArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentLgArr + +subroutine extractDataContentChArr(arg, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), dimension(:), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentChArr", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + if (present(ex)) then + call rts(getTextContent(arg, ex), data, separator, csv, num, iostat) + else + call rts(getTextContent(arg), data, separator, csv, num, iostat) + endif + + end subroutine extractDataContentChArr + + +subroutine extractDataContentCmplxDpMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(dp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxDpMat + +subroutine extractDataContentCmplxSpMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + complex(sp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentCmplxSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentCmplxSpMat + +subroutine extractDataContentRealDpMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(dp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealDpMat + +subroutine extractDataContentRealSpMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + real(sp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentRealSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentRealSpMat + +subroutine extractDataContentIntMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer, dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentIntMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentIntMat + +subroutine extractDataContentLgMat(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + logical, dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentLgMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentLgMat + +subroutine extractDataContentChMat(arg, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), dimension(:,:), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentChMat", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + if (present(ex)) then + call rts(getTextContent(arg, ex), data, separator, csv, num, iostat) + else + call rts(getTextContent(arg), data, separator, csv, num, iostat) + endif + + end subroutine extractDataContentChMat + + +subroutine extractDataAttributeCmplxDpSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(dp), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxDpSca + +subroutine extractDataAttributeCmplxSpSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(sp), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxSpSca + +subroutine extractDataAttributeRealDpSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(dp), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealDpSca + +subroutine extractDataAttributeRealSpSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(sp), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealSpSca + +subroutine extractDataAttributeIntSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + integer, intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeIntSca + +subroutine extractDataAttributeLgSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeLgSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeLgSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeLgSca + +subroutine extractDataAttributeChSca(arg, name, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + character(len=*), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeChSca", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeChSca", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, separator, csv, num, iostat) + else + call rts(getAttribute(arg, name), data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttributeChSca + + +subroutine extractDataAttributeCmplxDpArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(dp), dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxDpArr + +subroutine extractDataAttributeCmplxSpArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(sp), dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxSpArr + +subroutine extractDataAttributeRealDpArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(dp), dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealDpArr + +subroutine extractDataAttributeRealSpArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(sp), dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealSpArr + +subroutine extractDataAttributeIntArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + integer, dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeIntArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeIntArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeIntArr + +subroutine extractDataAttributeLgArr(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeLgArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeLgArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeLgArr + +subroutine extractDataAttributeChArr(arg, name, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + character(len=*), dimension(:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeChArr", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeChArr", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, separator, csv, num, iostat) + else + call rts(getAttribute(arg, name), data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttributeChArr + + +subroutine extractDataAttributeCmplxDpMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(dp), dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxDpMat + +subroutine extractDataAttributeCmplxSpMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + complex(sp), dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeCmplxSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeCmplxSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeCmplxSpMat + +subroutine extractDataAttributeRealDpMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(dp), dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealDpMat + +subroutine extractDataAttributeRealSpMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + real(sp), dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeRealSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeRealSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeRealSpMat + +subroutine extractDataAttributeIntMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + integer, dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeIntMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeIntMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeIntMat + +subroutine extractDataAttributeLgMat(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeLgMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeLgMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeLgMat + +subroutine extractDataAttributeChMat(arg, name, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + character(len=*), dimension(:,:), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeChMat", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeChMat", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, separator, csv, num, iostat) + else + call rts(getAttribute(arg, name), data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttributeChMat + + +subroutine extractDataAttNSCmplxDpSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(dp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxDpSca + +subroutine extractDataAttNSCmplxSpSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(sp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxSpSca + +subroutine extractDataAttNSRealDpSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(dp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealDpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealDpSca + +subroutine extractDataAttNSRealSpSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(sp), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealSpSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealSpSca + +subroutine extractDataAttNSIntSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + integer, intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSIntSca + +subroutine extractDataAttNSLgSca(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + logical, intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSLgSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSLgSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSLgSca + +subroutine extractDataAttNSChSca(arg, namespaceURI, localName, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + character(len=*), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSChSca", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSChSca", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, separator, csv, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttNSChSca + + +subroutine extractDataAttNSCmplxDpArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(dp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxDpArr + +subroutine extractDataAttNSCmplxSpArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(sp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxSpArr + +subroutine extractDataAttNSRealDpArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(dp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealDpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealDpArr + +subroutine extractDataAttNSRealSpArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(sp), dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealSpArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealSpArr + +subroutine extractDataAttNSIntArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + integer, dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSIntArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSIntArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSIntArr + +subroutine extractDataAttNSLgArr(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + logical, dimension(:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSLgArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSLgArr", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSLgArr + +subroutine extractDataAttNSChArr(arg, namespaceURI, localName, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + character(len=*), dimension(:), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSChArr", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSChArr", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, separator, csv, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttNSChArr + + +subroutine extractDataAttNSCmplxDpMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(dp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxDpMat + +subroutine extractDataAttNSCmplxSpMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + complex(sp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSCmplxSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSCmplxSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSCmplxSpMat + +subroutine extractDataAttNSRealDpMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(dp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealDpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealDpMat + +subroutine extractDataAttNSRealSpMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + real(sp), dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSRealSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSRealSpMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSRealSpMat + +subroutine extractDataAttNSIntMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + integer, dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSIntMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSIntMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSIntMat + +subroutine extractDataAttNSLgMat(arg, namespaceURI, localName, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + logical, dimension(:,:), intent(out) :: data + + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSLgMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSLgMat", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, num, iostat) + endif + + + end subroutine extractDataAttNSLgMat + +subroutine extractDataAttNSChMat(arg, namespaceURI, localName, data, separator, csv, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: namespaceURI, localName + character(len=*), dimension(:,:), intent(out) :: data + + logical, intent(in), optional :: csv + character, intent(in), optional :: separator + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttNSChMat", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttNSChMat", ex) + if (present(ex)) then + if (inException(ex)) then + data = "" + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getAttributeNS(arg, namespaceURI, localName, ex), & + data, separator, csv, num, iostat) + else + call rts(getAttributeNS(arg, namespaceURI, localName), & + data, separator, csv, num, iostat) + endif + + + end subroutine extractDataAttNSChMat + + + +end module m_dom_extras diff --git a/src/xml/dom/m_dom_parse.F90 b/src/xml/dom/m_dom_parse.F90 new file mode 100644 index 000000000..72e683732 --- /dev/null +++ b/src/xml/dom/m_dom_parse.F90 @@ -0,0 +1,577 @@ +module m_dom_parse + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc + use fox_m_utils_uri, only: URI, parseURI, rebaseURI, expressURI, destroyURI + use m_common_attrs, only: hasKey, getValue, getIndex, getIsId, getBase, & + add_item_to_dict + use m_common_entities, only: entity_t, size, getEntityByIndex + use m_common_error, only: FoX_error, in_error + use m_common_struct, only: xml_doc_state + use FoX_common, only: dictionary_t, getLength + use FoX_common, only: getQName, getValue, getURI, isSpecified + use m_sax_parser, only: sax_parse + use FoX_sax, only: xml_t + use FoX_sax, only: open_xml_file, open_xml_string, close_xml_t + + ! Public interfaces + use m_dom_dom, only: DOMConfiguration, Node, NamedNodeMap, & + TEXT_NODE, & + getAttributes, getData, getDocType, getEntities, getImplementation, & + getLastChild, getNodeType, & + getNotations, getParameter, getParentNode, getXmlVersion, & + setAttribute, setAttributeNS, setData, setValue, & + appendChild, createAttribute, createAttributeNS, createCdataSection, & + createComment, createDocumentType, createElement, createElementNS, & + createEntityReference, createProcessingInstruction, createTextNode, & + getNamedItem, setAttributeNode, setAttributeNodeNS, setNamedItem, & + getFoX_checks + + ! Private interfaces + use m_dom_dom, only: copyDOMConfig, createEmptyDocument, setDocumentElement, & + createEmptyEntityReference, createEntity, createNotation, & + getReadOnly, getStringValue, getXds, destroy, destroyAllNodesRecursively, & + namespaceFixup, setDocType, setDomConfig, setGCstate, setIllFormed, & + setIsElementContentWhitespace, setIsId, setReadOnlyMap, setReadonlyNode, & + setSpecified, setXds, setStringValue + + use m_dom_error, only: DOMException, inException, throw_exception, & + getExceptionCode, PARSE_ERR + + implicit none + private + + public :: parsefile + public :: parsestring + + type(xml_t), target, save :: fxml + + type(Node), pointer, save :: mainDoc => null() + type(Node), pointer, save :: current => null() + + type(DOMConfiguration), pointer :: domConfig + + logical :: cdata + character, pointer :: error(:) => null() + character, pointer :: inEntity(:) => null() + +contains + + subroutine startElement_handler(nsURI, localname, name, attrs) + character(len=*), intent(in) :: nsURI + character(len=*), intent(in) :: localname + character(len=*), intent(in) :: name + type(dictionary_t), intent(in) :: attrs + + type(URI), pointer :: URIref, URIbase, newURI + type(Node), pointer :: el, attr, dummy + character, pointer :: baseURI(:) + integer :: i + + if (getParameter(domConfig, "namespaces")) then + el => createElementNS(mainDoc, nsURI, name) + else + el => createElement(mainDoc, name) + endif + + if (getBase(attrs)/="") then + i = getIndex(attrs, "xml:base") + if (i>0) then + URIbase => parseURI(getBase(attrs)) + URIref => parseURI(getValue(attrs, i)) + newURI => rebaseURI(URIbase, URIref) + call destroyURI(URIbase) + call destroyURI(URIref) + baseURI => vs_str_alloc(expressURI(newURI)) + call destroyURI(newURI) + else + baseURI => vs_str_alloc(getBase(attrs)) + endif + if (getParameter(domConfig, "namespaces")) then + attr => createAttributeNS(mainDoc, & + "http://www.w3.org/XML/1998/namespace", "xml:base") + else + attr => createAttribute(mainDoc, "xml:base") + endif + call setValue(attr, str_vs(baseURI)) + deallocate(baseURI) + if (i>0) then + call setSpecified(attr, isSpecified(attrs, i)) + call setIsId(attr, getIsId(attrs, i)) + endif + if (getParameter(domConfig, "namespaces")) then + dummy => setAttributeNodeNS(el, attr) + else + dummy => setAttributeNode(el, attr) + endif + endif + + do i = 1, getLength(attrs) + if (getQName(attrs, i)=="xml:base") cycle + if (getParameter(domConfig, "namespaces")) then + attr => createAttributeNS(mainDoc, getURI(attrs, i), getQName(attrs, i)) + else + attr => createAttribute(mainDoc, getQName(attrs, i)) + endif + call setValue(attr, getValue(attrs, i)) + call setSpecified(attr, isSpecified(attrs, i)) + call setIsId(attr, getIsId(attrs, i)) + if (getParameter(domConfig, "namespaces")) then + dummy => setAttributeNodeNS(el, attr) + else + dummy => setAttributeNode(el, attr) + endif + if (associated(inEntity)) call setReadOnlyNode(attr, .true., .true.) + enddo + + if (associated(current, mainDoc)) then + current => appendChild(current,el) + call setDocumentElement(mainDoc, current) + else + current => appendChild(current,el) + endif + if (getParameter(domConfig, "namespaces")) & + call namespaceFixup(current, .false.) + + if (associated(inEntity)) & + call setReadOnlyMap(getAttributes(current), .true.) + + cdata = .false. + + end subroutine startElement_handler + + subroutine endElement_handler(URI, localName, name) + character(len=*), intent(in) :: URI + character(len=*), intent(in) :: localname + character(len=*), intent(in) :: name + + if (associated(inEntity)) call setReadOnlyNode(current, .true., .false.) + + current => getParentNode(current) + end subroutine endElement_handler + + ! FIXME to pick up entity references within attribute values, we need + ! separate just_the_element, start_attribute, attribute_text etc. calls. + + subroutine characters_handler(chunk) + character(len=*), intent(in) :: chunk + + type(Node), pointer :: temp + logical :: readonly + + temp => getLastChild(current) + if (associated(temp)) then + if (.not.cdata.and.getNodeType(temp)==TEXT_NODE) then + readonly = getReadOnly(temp) ! Reset readonly status quickly + call setReadOnlyNode(temp, .false., .false.) + call setData(temp, getData(temp)//chunk) + call setReadOnlyNode(temp, readonly, .false.) + return + endif + endif + if (cdata) then + temp => createCdataSection(mainDoc, chunk) + temp => appendChild(current, temp) + else + temp => createTextNode(mainDoc, chunk) + temp => appendChild(current, temp) + endif + + if (associated(inEntity)) call setReadOnlyNode(temp, .true., .false.) + + end subroutine characters_handler + + subroutine ignorableWhitespace_handler(chunk) + character(len=*), intent(in) :: chunk + + type(Node), pointer :: temp + logical :: readonly + + if (getParameter(domConfig, "element-content-whitespace")) then + temp => getLastChild(current) + if (associated(temp)) then + if (getNodeType(temp)==TEXT_NODE) then + readonly = getReadOnly(temp) ! Reset readonly status quickly + call setReadOnlyNode(temp, .false., .false.) + call setData(temp, getData(temp)//chunk) + call setReadOnlyNode(temp, readonly, .false.) + call setIsElementContentWhitespace(temp, .true.) + return + endif + endif + temp => createTextNode(mainDoc, chunk) + temp => appendChild(current, temp) + call setIsElementContentWhitespace(temp, .true.) + if (associated(inEntity)) call setReadOnlyNode(temp, .true., .false.) + endif + + end subroutine ignorableWhitespace_handler + + subroutine comment_handler(comment) + character(len=*), intent(in) :: comment + + type(Node), pointer :: temp + + if (getParameter(domConfig, "comments")) then + temp => appendChild(current, createComment(mainDoc, comment)) + if (associated(inEntity)) call setReadOnlyNode(temp, .true., .false.) + endif + + end subroutine comment_handler + + subroutine processingInstruction_handler(target, data) + character(len=*), intent(in) :: target + character(len=*), intent(in) :: data + + type(Node), pointer :: temp + + temp => appendChild(current, & + createProcessingInstruction(mainDoc, target, data)) + + if (associated(inEntity)) call setReadOnlyNode(temp, .true., .false.) + end subroutine processingInstruction_handler + + subroutine startDocument_handler + mainDoc => createEmptyDocument() + current => mainDoc + call setGCstate(mainDoc, .false.) + call setDomConfig(mainDoc, domConfig) + end subroutine startDocument_handler + + subroutine endDocument_Handler + call setGCstate(mainDoc, .true.) + end subroutine endDocument_Handler + + subroutine startDTD_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + + type(Node), pointer :: np + + np => createDocumentType(getImplementation(mainDoc), name, publicId=publicId, systemId=systemId) + np => appendChild(mainDoc, np) + call setDocType(mainDoc, np) + + end subroutine startDTD_handler + + subroutine endDTD_handler + + type(Node), pointer :: np, oldcurrent + type(NamedNodeMap), pointer :: entities + type(xml_t) :: subsax + type(xml_doc_state), pointer :: xds + type(entity_t), pointer :: ent + integer :: i, ios + logical :: ok + + entities => getEntities(getDocType(mainDoc)) + xds => getXds(mainDoc) + + do i = 1, size(xds%entityList) + ent => getEntityByIndex(xds%entityList, i) + np => getNamedItem(entities, str_vs(ent%name)) + + ok = .false. + if (ent%external) then + if (size(ent%notation)==0) then + call open_xml_file(subsax, expressURI(ent%baseURI), iostat=ios) + if (ios/=0) then + call setIllFormed(np, .true.) + else + ok = .true. + endif + endif + else + call open_xml_string(subsax, getStringValue(np)) + ok = .true. + endif + if (ok) then + oldcurrent => current + current => np + ! Run the parser over value + ! We do this with all entities already declared. + call sax_parse(subsax%fx, subsax%fb, & + startElement_handler=startElement_handler, & + endElement_handler=endElement_handler, & + characters_handler=characters_handler, & + startCdata_handler=startCdata_handler, & + endCdata_handler=endCdata_handler, & + comment_handler=comment_handler, & + processingInstruction_handler=processingInstruction_handler, & + fatalError_handler=entityErrorHandler, & + startInCharData = .true., & + externalEntity = ent%external, & + xmlVersion = getXmlVersion(mainDoc), & + namespaces=getParameter(domConfig, "namespaces"), & + initial_entities = xds%entityList) + call close_xml_t(subsax) + + current => oldcurrent + endif + enddo + + if (associated(getDocType(mainDoc))) then + call setReadonlyMap(getEntities(getDocType(mainDoc)), .true.) + call setReadonlyMap(getNotations(getDocType(mainDoc)), .true.) + endif + + end subroutine endDTD_handler + + subroutine FoX_endDTD_handler(state) + type(xml_doc_state), pointer :: state + + call setXds(mainDoc, state) + + end subroutine FoX_endDTD_handler + + subroutine notationDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + + type(Node), pointer :: np + + np => createNotation(mainDoc, name, publicId=publicId, systemId=systemId) + np => setNamedItem(getNotations(getDocType(mainDoc)), np) + ! The SAX parser will never give us duplicate entities, + ! so there is no need to check + + end subroutine notationDecl_handler + + subroutine startCdata_handler() + if (getParameter(domConfig, "cdata-sections")) cdata = .true. + end subroutine startCdata_handler + subroutine endCdata_handler() + cdata = .false. + end subroutine endCdata_handler + + subroutine internalEntityDecl_handler(name, value) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + + type(Node), pointer :: np + + if (name(1:1)=="%") return + ! Do nothing with parameter entities + + ! We only note that these exist here. + ! A second parsing stage is triggered at the end + ! of the DTD, in order to resolve entity references (which + ! need not be declared in order) + + np => createEntity(mainDoc, name, "", "", "") + call setStringValue(np, value) + np => setNamedItem(getEntities(getDocType(mainDoc)), np) + + end subroutine internalEntityDecl_handler + + subroutine normalErrorHandler(msg) + character(len=*), intent(in) :: msg + ! This is called if the main parsing routine fails + error => vs_str_alloc(msg) + end subroutine normalErrorHandler + + subroutine entityErrorHandler(msg) + character(len=*), intent(in) :: msg + + !This gets called if parsing of an entity failed. If so, + !then we need to destroy all nodes which were being generated as + !children of this entity, then mark the entity as ill-formed - but + !otherwise carry on parsing the document, and only throw an error + !if a reference is made to it. + + call destroyAllNodesRecursively(current, except=.true.) + call setIllFormed(current, .true.) + end subroutine entityErrorHandler + + subroutine externalEntityDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + type(Node), pointer :: np + + if (name(1:1)=="%") return + ! Do nothing with parameter entities + + np => createEntity(mainDoc, name, & + publicId=publicId, systemId=systemId, notationName="") + np => setNamedItem(getEntities(getDocType(mainDoc)), np) + + end subroutine externalEntityDecl_handler + + subroutine unparsedEntityDecl_handler(name, publicId, systemId, notationName) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + character(len=*), intent(in) :: notationName + type(Node), pointer :: np + + np => getNamedItem(getEntities(getDocType(mainDoc)), name) + if (.not.associated(np)) then + np => createEntity(mainDoc, name, publicId=publicId, systemId=systemId, notationName=notationName) + np => setNamedItem(getEntities(getDocType(mainDoc)), np) + endif + + end subroutine unparsedEntityDecl_handler + + subroutine startEntity_handler(name) + character(len=*), intent(in) :: name + + if (name(1:1)=="%") return + ! Do nothing with parameter entities + + if (getParameter(domConfig, "entities")) then + if (.not.associated(inEntity)) then + inEntity => vs_str_alloc(name) + endif + current => appendChild(current, createEmptyEntityReference(mainDoc, name)) + endif + end subroutine startEntity_handler + + subroutine endEntity_handler(name) + character(len=*), intent(in) :: name + + if (name(1:1)=="%") return + ! Do nothing with parameter entities + + if (getParameter(domConfig, "entities")) then + call setReadOnlyNode(current, .true., .false.) + if (str_vs(inEntity)==name) deallocate(inEntity) + current => getParentNode(current) + endif + + end subroutine endEntity_handler + + subroutine skippedEntity_handler(name) + character(len=*), intent(in) :: name + + type(Node), pointer :: temp + + if (name(1:1)=="%") return + ! Do nothing with parameter entities + + temp => appendChild(current, createEntityReference(mainDoc, name)) + if (associated(inEntity)) call setReadonlyNode(temp, .true., .false.) + end subroutine skippedEntity_handler + + + subroutine runParser(fxml, configuration, ex) + type(DOMException), intent(out), optional :: ex + type(xml_t), intent(inout) :: fxml + type(DOMConfiguration), pointer, optional :: configuration + + allocate(DOMConfig) + if (present(configuration)) call copyDOMConfig(DOMConfig, configuration) + +! We use internal sax_parse rather than public interface in order +! to use internal callbacks to get extra info. + call sax_parse(fx=fxml%fx, fb=fxml%fb,& + characters_handler=characters_handler, & + endDocument_handler=endDocument_handler, & + endElement_handler=endElement_handler, & + !endPrefixMapping_handler, & + ignorableWhitespace_handler=ignorableWhitespace_handler, & + processingInstruction_handler=processingInstruction_handler, & + ! setDocumentLocator + skippedEntity_handler=skippedEntity_handler, & + startDocument_handler=startDocument_handler, & + startElement_handler=startElement_handler, & + !startPrefixMapping_handler, & + notationDecl_handler=notationDecl_handler, & + unparsedEntityDecl_handler=unparsedEntityDecl_handler, & + !error_handler, & + fatalError_handler=normalErrorHandler, & + !warning_handler, & + !attributeDecl_handler, & + !elementDecl_handler, & + externalEntityDecl_handler=externalEntityDecl_handler, & + internalEntityDecl_handler=internalEntityDecl_handler, & + comment_handler=comment_handler, & + endCdata_handler=endCdata_handler, & + endDTD_handler=endDTD_handler, & + endEntity_handler=endEntity_handler, & + startCdata_handler=startCdata_handler, & + startDTD_handler=startDTD_handler, & + startEntity_handler=startEntity_handler, & + FoX_endDTD_handler=FoX_endDTD_handler, & + namespaces = getParameter(domConfig, "namespaces"), & + namespace_prefixes = .true., & + validate = getParameter(domConfig, "validate"), & ! FIXME what about validate-if-present ... + xmlns_uris = .true.) + + call close_xml_t(fxml) + + if (associated(error)) then + if (associated(inEntity)) deallocate(inEntity) + ! FIXME pass the value of the error through + ! when we let exceptions do that + deallocate(error) + call destroy(mainDoc) + if (getFoX_checks().or.PARSE_ERR<200) then + call throw_exception(PARSE_ERR, "runParser", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + end subroutine runParser + + + function parsefile(filename, configuration, iostat, ex) + type(DOMException), intent(out), optional :: ex + character(len=*), intent(in) :: filename + type(DOMConfiguration), pointer, optional :: configuration + integer, intent(out), optional :: iostat + type(Node), pointer :: parsefile + + type(DOMException) :: ex_ + integer :: iostat_ + + call open_xml_file(fxml, filename, iostat_) + if (present(iostat)) then + iostat = iostat_ + if (iostat/=0) return + elseif (in_error(fxml%fx%error_stack)) then + call FoX_error(str_vs(fxml%fx%error_stack%stack(1)%msg)) + elseif (iostat_/=0) then + call FoX_error("Cannot open file") + endif + + if (present(ex)) then + call runParser(fxml, configuration, ex) + elseif (present(iostat)) then + call runParser(fxml, configuration, ex_) + else + call runParser(fxml, configuration) + endif + + if (present(iostat).and.inException(ex_)) then + iostat = getExceptionCode(ex_) + endif + + parsefile => mainDoc + mainDoc => null() + + end function parsefile + + + function parsestring(string, configuration, ex) + type(DOMException), intent(out), optional :: ex + character(len=*), intent(in) :: string + type(DOMConfiguration), pointer, optional :: configuration + type(Node), pointer :: parsestring + + call open_xml_string(fxml, string) + + call runParser(fxml, configuration, ex) + + parsestring => mainDoc + mainDoc => null() + + end function parsestring + +end module m_dom_parse diff --git a/src/xml/dom/m_dom_utils.F90 b/src/xml/dom/m_dom_utils.F90 new file mode 100644 index 000000000..f0e443f21 --- /dev/null +++ b/src/xml/dom/m_dom_utils.F90 @@ -0,0 +1,433 @@ +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 ... + 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 + 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 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 diff --git a/src/xml/dom/makefile b/src/xml/dom/makefile new file mode 100644 index 000000000..98286bc78 --- /dev/null +++ b/src/xml/dom/makefile @@ -0,0 +1,48 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils -I../sax -I../wxml $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_dom.o: m_dom_dom.o m_dom_error.o m_dom_extras.o m_dom_parse.o m_dom_utils.o +m_dom_dom.o: m_dom_error.o +m_dom_extras.o: m_dom_dom.o m_dom_error.o +m_dom_parse.o: m_dom_dom.o m_dom_error.o +m_dom_utils.o: m_dom_dom.o m_dom_error.o diff --git a/src/xml/fsys/fox_m_fsys_abort_flush.F90 b/src/xml/fsys/fox_m_fsys_abort_flush.F90 new file mode 100644 index 000000000..612523bb9 --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_abort_flush.F90 @@ -0,0 +1,119 @@ +module fox_m_fsys_abort_flush + + implicit none + + public :: pxfflush + public :: pxfabort + public :: pure_pxfabort + + ! pxf.F90 - assortment of Fortran wrappers to various + ! unix-y system calls. + ! + ! Copyright Toby White, 2005 + + ! The name pxf is intended to be reminiscent of the POSIX + ! fortran interfaces defined by IEEE 1003.9-1992, although + ! in fact I don't think that either flush or abort were + ! covered by said standard. + + ! Of the preprocessor defines used here, only xlC is + ! automatically defined by the appropriate compiler. All + ! others must be defined by some other mechanism - I + ! recommend autoconf. + + + ! FLUSH: flushes buffered output on a given unit. Not guaranteed + ! to do anything at all (particularly under MPI when even FLUSHed + ! buffers may not make it to the host cpu after an abort. + ! + ! IMPLEMENTATION: in F2003, this is a native operation called by the + ! FLUSH statement. + ! In almost every compiler, there is a FLUSH intrinsic subroutine + ! available which takes one argument, the unit to be flushed. + ! (some will flush all open units when no argument is given - this + ! facility is not used here. + ! NAG complicates matters by having to USE a module to get at flush. + ! + ! If no FLUSH is available, the subroutine is a no-op. + + +CONTAINS + + subroutine pxfflush(unit) +#ifdef __NAG__ + use f90_unix_io, only : flush +#endif + integer, intent(in) :: unit + +#if defined(F2003) + flush(unit) +#elif defined(xlC) + call flush_(unit) +#elif defined (FC_HAVE_FLUSH) + call flush(unit) +#else + continue +#endif + + end subroutine pxfflush + + ! ABORT: terminates program execution in such a way that a backtrace + ! is produced. (see abort() in the C Standard Library). No arguments. + ! + ! IMPLEMENTATION: In F2003, the C interoperability features mean that + ! the abort in stdlib.h is available to be linked against. + ! In several other compilers an ABORT intrinsic subroutine is available. + ! Again, NAG complicates matters by needing to USE a module. + ! + ! In the case where no native ABORT can be found, we emulate one + ! by dereferencing a null pointer. This has reliably produced coredumps + ! on every platform I've tried it with. Just in case it doesn't (it need + ! not even stop execution) a stop is given to force termination. + + subroutine pxfabort() +#ifdef __NAG__ + use f90_unix_proc, only : abort +#endif +#ifdef F2003 + interface + subroutine abort() bind(c) + end subroutine abort + end interface +#define FC_HAVE_ABORT +#endif +#ifndef FC_HAVE_ABORT + integer, pointer :: i +#endif + + call pxfflush(6) + +#ifdef FC_HAVE_ABORT +#ifdef FC_ABORT_UNDERSCORE + call abort_() +#elif defined(FC_ABORT_ARG) + call abort("") +#else + call abort() +#endif +#else + i=>null() + Print*,i +#endif + stop + + end subroutine pxfabort + + ! For where we need a pxfabort that is pure, we have + ! this below. I am less sure of its working everywhe + ! also it must be used as a function not a subroutine + ! (otherwise it would be optimized away as side-effect + ! free + + pure function pure_pxfabort() result (crash) + integer :: crash + integer, pointer :: i + nullify(i) + crash = i + end function pure_pxfabort + +end module fox_m_fsys_abort_flush diff --git a/src/xml/fsys/fox_m_fsys_array_str.F90 b/src/xml/fsys/fox_m_fsys_array_str.F90 new file mode 100644 index 000000000..b49554832 --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_array_str.F90 @@ -0,0 +1,90 @@ +module fox_m_fsys_array_str +#ifndef DUMMYLIB + + implicit none + private + + interface destroy + module procedure destroy_vs + end interface + + interface concat + module procedure vs_s_concat + end interface + + interface alloc + module procedure vs_str_alloc + end interface + + public :: str_vs + public :: vs_str + public :: vs_str_alloc + public :: vs_vs_alloc + + public :: concat + public :: alloc + + public :: destroy + +contains + + pure function str_vs(vs) result(s) + character, dimension(:), intent(in) :: vs + character(len=size(vs)) :: s +#ifdef PGF90 +!PGI crashes on this use of transfer. Knob-ends. + integer :: i + do i = 1, size(vs) + s(i:i) = vs(i) + enddo +#else + s = transfer(vs, s) +#endif + end function str_vs + + + pure function vs_str(s) result(vs) + character(len=*), intent(in) :: s + character, dimension(len(s)) :: vs + + vs = transfer(s, vs) + end function vs_str + + pure function vs_str_alloc(s) result(vs) + character(len=*), intent(in) :: s + character, dimension(:), pointer :: vs + + allocate(vs(len(s))) + vs = vs_str(s) + end function vs_str_alloc + + pure function vs_vs_alloc(s) result(vs) + character, dimension(:), pointer :: s + character, dimension(:), pointer :: vs + + if (associated(s)) then + allocate(vs(size(s))) + vs = s + else + vs => null() + endif + end function vs_vs_alloc + + pure function vs_s_concat(vs, s) result(vs2) + character, dimension(:), intent(in) :: vs + character(len=*), intent(in) :: s + character, dimension(:), pointer :: vs2 + + allocate(vs2(size(vs)+len(s))) + vs2(:size(vs)) = vs + vs2(size(vs)+1:) = vs_str(s) + end function vs_s_concat + + subroutine destroy_vs(vs) + character, dimension(:), pointer :: vs + + deallocate(vs) + end subroutine destroy_vs + +#endif +end module fox_m_fsys_array_str diff --git a/src/xml/fsys/fox_m_fsys_count_parse_input.F90 b/src/xml/fsys/fox_m_fsys_count_parse_input.F90 new file mode 100644 index 000000000..b158d26fa --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_count_parse_input.F90 @@ -0,0 +1,561 @@ +module fox_m_fsys_count_parse_input + + use fox_m_fsys_realtypes, only: sp, dp + + implicit none + private + + character(len=1), parameter :: SPACE = achar(32) + character(len=1), parameter :: NEWLINE = achar(10) + character(len=1), parameter :: CARRIAGE_RETURN = achar(13) + character(len=1), parameter :: TAB = achar(9) + character(len=*), parameter :: whitespace = & + SPACE//NEWLINE//CARRIAGE_RETURN//TAB + + interface countrts + module procedure countstring + module procedure countlogical + module procedure countinteger + module procedure countrealsp + module procedure countrealdp + module procedure countcomplexsp + module procedure countcomplexdp + end interface + + public :: countrts + +contains + + pure function countstring(s, datatype, separator, csv) result(num) + character(len=*), intent(in) :: s + character(len=*), intent(in) :: datatype + character, intent(in), optional :: separator + logical, intent(in), optional :: csv + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + character(len=len(s)) :: s2 + logical :: csv_, eof + integer :: m + + csv_ = .false. + if (present(csv)) then + if (csv) csv_ = csv + endif + + s_i = 1 + err = 0 + eof = .false. + ij = 0 + loop: do + if (csv_) then + if (s_i>len(s)) then + ij = ij + 1 + exit loop + endif + k = verify(s(s_i:), achar(10)//achar(13)) + if (k==0) then + ij = ij + 1 + exit loop + elseif (s(s_i+k-1:s_i+k-1)=="""") then + ! we have a quote-delimited string; + s_i = s_i + k + s2 = "" + quote: do + k = index(s(s_i:), """") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 1 + s2(m:) = s(s_i:k) + m = m + (k-s_i+1) + k = k + 2 + if (k>len(s)) then + err = 2 + exit loop + endif + if (s(k:k)/="""") exit + s_i = k + 1 + if (s_i > len(s)) then + err = 2 + exit loop + endif + m = m + 1 + s2(m:m) = """" + enddo quote + k = scan(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + else + s_i = s_i + k - 1 + k = scan(s(s_i:), achar(10)//achar(13)//",") + if (k==0) then + eof = .true. + k = len(s) + else + k = s_i + k - 2 + endif + if (index(s(s_i:k), """")/=0) then + err = 2 + exit loop + endif + endif + ij = ij + 1 + s_i = k + 2 + if (eof) exit loop + + else + if (present(separator)) then + k = index(s(s_i:), separator) + else + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + k = scan(s(s_i:), whitespace) + endif + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + ij = ij + 1 + s_i = k + 2 + if (s_i>len(s)) exit loop + + endif + end do loop + + num = ij + if (err/=0) num=0 + +#else + num = 0 +#endif + end function countstring + + pure function countlogical(s, datatype) result(num) + character(len=*), intent(in) :: s + logical, intent(in) :: datatype + logical :: dummy_data + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + if (.not.((s(s_i:k)=="true".or.s(s_i:k)=="1").or. & + (s(s_i:k)=="false".or.s(s_i:k)=="0"))) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif + end function countlogical + + pure function countinteger(s, datatype) result(num) + character(len=*), intent(in) :: s + integer, intent(in) :: datatype + integer :: dummy_data + integer num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) dummy_data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif +end function countinteger + + pure function countrealsp(s, datatype) result(num) + character(len=*), intent(in) :: s + real(sp), intent(in) :: datatype + real(sp) :: dummy_data + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) dummy_data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif + end function countrealsp + + pure function countrealdp(s, datatype) result(num) + character(len=*), intent(in) :: s + real(dp), intent(in) :: datatype + real(dp) :: dummy_data + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) dummy_data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif + end function countrealdp + + pure function countcomplexsp(s, datatype) result(num) + character(len=*), intent(in) :: s + complex(sp), intent(in) :: datatype + complex(sp) :: dummy_data + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + bracketed = .false. + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + select case (s(s_i:s_i)) + case ("(") + bracketed = .true. + k = verify(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + s_i = s_i + k + case (",") + k = verify(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + s_i = s_i + k - 1 + case ("+", "-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9") + continue + case default + err = 2 + exit loop + end select + if (bracketed) then + k = index(s(s_i:), ")+i(") + else + k = scan(s(s_i:), whitespace//",") + endif + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + read(s(s_i:k), *, iostat=ios) r + if (ios/=0) then + err = 2 + exit loop + endif + if (bracketed) then + s_i = k + 5 + if (s_i>len(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif + end function countcomplexsp + + pure function countcomplexdp(s, datatype) result(num) + character(len=*), intent(in) :: s + complex(dp), intent(in) :: datatype + complex(dp) :: dummy_data + integer :: num +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + ij = 0 + length = 1 + loop: do + bracketed = .false. + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + select case (s(s_i:s_i)) + case ("(") + bracketed = .true. + k = verify(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + s_i = s_i + k + case (",") + k = verify(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + s_i = s_i + k - 1 + case ("+", "-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9") + continue + case default + err = 2 + exit loop + end select + if (bracketed) then + k = index(s(s_i:), ")+i(") + else + k = scan(s(s_i:), whitespace//",") + endif + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + read(s(s_i:k), *, iostat=ios) r + if (ios/=0) then + err = 2 + exit loop + endif + if (bracketed) then + s_i = k + 5 + if (s_i>len(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + num = ij + if (verify(s(s_i:), whitespace)/=0) num = 0 + if (err/=0) num=0 + + +#else + num = 0 +#endif + end function countcomplexdp + +end module fox_m_fsys_count_parse_input diff --git a/src/xml/fsys/fox_m_fsys_format.F90 b/src/xml/fsys/fox_m_fsys_format.F90 new file mode 100644 index 000000000..448f44ebd --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_format.F90 @@ -0,0 +1,2250 @@ +module fox_m_fsys_format + +!Note that there are several oddities to this package, +!to get round assorted compiler bugs. + +!All the _matrix_ subroutines should be straight +!call-throughs to the relevant _array_ subroutine, +!but with flattened arrays. (this would allow easy +!generation of all functions up to 7 dimensions) +!but unfortunately that breaks PGI-6.1, and causes +!errors on Pathscale-2.4. + +!The Logical array/matrix functions should be able +!to COUNT their length inline in the specification +!expression, but Pathscale-2.4 gives an error on that. + + use fox_m_fsys_abort_flush, only: pxfflush + use fox_m_fsys_realtypes, only: sp, dp + + implicit none + private + +#ifndef DUMMYLIB + integer, parameter :: sig_sp = digits(1.0_sp)/4 + integer, parameter :: sig_dp = digits(1.0_dp)/4 ! Approximate precision worth outputting of each type. + + character(len=*), parameter :: digit = "0123456789:" + character(len=*), parameter :: hexdigit = "0123456789abcdefABCDEF" +#endif + + interface str +! This is for external use only: str should not be called within this +! file. +! All *_chk subroutines check that the fmt they are passed is valid. + module procedure str_string, str_string_array, str_string_matrix, & + str_integer, str_integer_array, str_integer_matrix, & + str_integer_fmt, str_integer_array_fmt, str_integer_matrix_fmt, & + str_logical, str_logical_array, str_logical_matrix, & + str_real_sp, str_real_sp_fmt_chk, & + str_real_sp_array, str_real_sp_array_fmt_chk, & + str_real_sp_matrix, str_real_sp_matrix_fmt_chk, & + str_real_dp, str_real_dp_fmt_chk, & + str_real_dp_array, str_real_dp_array_fmt_chk, & + str_real_dp_matrix, str_real_dp_matrix_fmt_chk, & + str_complex_sp, str_complex_sp_fmt_chk, & + str_complex_sp_array, str_complex_sp_array_fmt_chk, & + str_complex_sp_matrix, str_complex_sp_matrix_fmt_chk, & + str_complex_dp, str_complex_dp_fmt_chk, & + str_complex_dp_array, str_complex_dp_array_fmt_chk, & + str_complex_dp_matrix, str_complex_dp_matrix_fmt_chk + end interface str + +#ifndef DUMMYLIB + interface safestr +! This is for internal use only - no check is made on the validity of +! any fmt input. + module procedure str_string, str_string_array, str_string_matrix, & + str_integer, str_integer_array, str_integer_matrix, & + str_logical, str_logical_array, str_logical_matrix, & + str_real_sp, str_real_sp_fmt, & + str_real_sp_array, str_real_sp_array_fmt, & + str_real_sp_matrix, str_real_sp_matrix_fmt, & + str_real_dp, str_real_dp_fmt, & + str_real_dp_array, str_real_dp_array_fmt, & + str_real_dp_matrix, str_real_dp_matrix_fmt, & + str_complex_sp, str_complex_sp_fmt, & + str_complex_sp_array, str_complex_sp_array_fmt, & + str_complex_sp_matrix, str_complex_sp_matrix_fmt, & + str_complex_dp, str_complex_dp_fmt, & + str_complex_dp_array, str_complex_dp_array_fmt, & + str_complex_dp_matrix, str_complex_dp_matrix_fmt + end interface safestr + + interface len + module procedure str_integer_len, str_integer_array_len, str_integer_matrix_len, & + str_integer_fmt_len, str_integer_array_fmt_len, str_integer_matrix_fmt_len, & + str_logical_len, str_logical_array_len, str_logical_matrix_len, & + str_real_sp_len, str_real_sp_fmt_len, & + str_real_sp_array_len, str_real_sp_array_fmt_len, & + str_real_sp_matrix_len, str_real_sp_matrix_fmt_len, & + str_real_dp_len, str_real_dp_fmt_len, & + str_real_dp_array_len, str_real_dp_array_fmt_len, & + str_real_dp_matrix_len, str_real_dp_matrix_fmt_len, & + str_complex_sp_len, str_complex_sp_fmt_len, & + str_complex_sp_array_len, str_complex_sp_array_fmt_len, & + str_complex_sp_matrix_len, str_complex_sp_matrix_fmt_len, & + str_complex_dp_len, str_complex_dp_fmt_len, & + str_complex_dp_array_len, str_complex_dp_array_fmt_len, & + str_complex_dp_matrix_len, str_complex_dp_matrix_fmt_len + end interface +#endif + + interface operator(//) + module procedure concat_str_int, concat_int_str, & + concat_str_logical, concat_logical_str, & + concat_real_sp_str, concat_str_real_sp, & + concat_real_dp_str, concat_str_real_dp, & + concat_complex_sp_str, concat_str_complex_sp, & + concat_complex_dp_str, concat_str_complex_dp + end interface + + public :: str + public :: operator(//) + +#ifndef DUMMYLIB + public :: str_to_int_10 + public :: str_to_int_16 +#endif + +contains + +#ifndef DUMMYLIB + ! NB: The len generic module procedure is used in + ! many initialisation statments (to set the + ! length of the output string needed for the + ! converted number). As of the Fortran 2008 + ! spec every specific function belonging to + ! a generic used in this way must be defined + ! in the module before use. This is enforced + ! by at least version 7.4.4 of the Cray + ! Fortran compiler. Hence we put all the *_len + ! functions here at the top of the file. + pure function str_string_array_len(st) result(n) + character(len=*), dimension(:), intent(in) :: st + integer :: n + + integer :: k + + n = size(st) - 1 + do k = 1, size(st) + n = n + len(st(k)) + enddo + + end function str_string_array_len + + pure function str_string_matrix_len(st) result(n) + character(len=*), dimension(:, :), intent(in) :: st + integer :: n + + n = len(st) * size(st) + size(st) - 1 + end function str_string_matrix_len + + pure function str_integer_len(i) result(n) + integer, intent(in) :: i + integer :: n + + n = int(log10(real(max(abs(i),1)))) + 1 + dim(-i,0)/max(abs(i),1) + + end function str_integer_len + + pure function str_integer_base_len(i, b) result(n) + integer, intent(in) :: i, b + integer :: n + + n = int(log10(real(max(abs(i),1)))/log10(real(b))) & + + 1 + dim(-i,0)/max(abs(i),1) + + end function str_integer_base_len + + pure function str_integer_fmt_len(i, fmt) result(n) + integer, intent(in) :: i + character(len=*), intent(in) :: fmt + integer :: n + + select case (len(fmt)) + case(0) + n = 0 + case(1) + if (fmt=="x") then + n = int(log10(real(max(abs(i),1)))/log10(16.0)) + 1 + dim(-i,0)/max(abs(i),1) + elseif (fmt=="d") then + n = int(log10(real(max(abs(i),1)))) + 1 + dim(-i,0)/max(abs(i),1) + else + return + endif + case default + if (fmt(1:1)/='x'.and.fmt(1:1)/='d') then + n = 0 + elseif (verify(fmt(2:), digit)==0) then + n = str_to_int_10(fmt(2:)) + else + n = 0 + endif + end select + + end function str_integer_fmt_len + + pure function str_integer_array_len(ia) result(n) + integer, dimension(:), intent(in) :: ia + integer :: n + + integer :: j + + n = size(ia) - 1 + + do j = 1, size(ia) + n = n + len(ia(j)) + enddo + + end function str_integer_array_len + + pure function str_integer_array_fmt_len(ia, fmt) result(n) + integer, dimension(:), intent(in) :: ia + character(len=*), intent(in) :: fmt + integer :: n + + integer :: j + + n = size(ia) - 1 + + do j = 1, size(ia) + n = n + len(ia(j), fmt) + enddo + + end function str_integer_array_fmt_len + + pure function str_integer_matrix_len(ia) result(n) + integer, dimension(:,:), intent(in) :: ia + integer :: n + + integer :: j, k + + n = size(ia) - 1 + + do k = 1, size(ia, 2) + do j = 1, size(ia, 1) + n = n + len(ia(j, k)) + enddo + enddo + + end function str_integer_matrix_len + + pure function str_integer_matrix_fmt_len(ia, fmt) result(n) + integer, dimension(:,:), intent(in) :: ia + character(len=*), intent(in) :: fmt + integer :: n + + integer :: j, k + + n = size(ia) - 1 + + do k = 1, size(ia, 2) + do j = 1, size(ia, 1) + n = n + len(ia(j, k), fmt) + enddo + enddo + + end function str_integer_matrix_fmt_len + + pure function str_logical_len(l) result (n) + logical, intent(in) :: l + integer :: n + + if (l) then + n = 4 + else + n = 5 + endif + end function str_logical_len + + pure function str_logical_array_len(la) result(n) +! This function should be inlined in the declarations of +! str_logical_array below but PGI and pathscale don't like it. + logical, dimension(:), intent(in) :: la + integer :: n + n = 5*size(la) - 1 + count(.not.la) + end function str_logical_array_len + + pure function str_logical_matrix_len(la) result(n) +! This function should be inlined in the declarations of +! str_logical_matrix below but PGI and pathscale don't like it. + logical, dimension(:,:), intent(in) :: la + integer :: n + n = 5*size(la) - 1 + count(.not.la) + end function str_logical_matrix_len + + pure function str_real_sp_fmt_len(x, fmt) result(n) + real(sp), intent(in) :: x + character(len=*), intent(in) :: fmt + integer :: n + + integer :: dec, sig + integer :: e + + if (.not.checkFmt(fmt)) then + n = 0 + return + endif + + if (x == 0.0_sp) then + e = 1 + else + e = floor(log10(abs(x))) + endif + + if (x < 0.0_sp) then + n = 1 + else + n = 0 + endif + + if (len(fmt) == 0) then + sig = sig_sp + + n = n + sig + 2 + len(e) + ! for the decimal point and the e + + elseif (fmt(1:1) == "s") then + if (len(fmt) > 1) then + sig = str_to_int_10(fmt(2:)) + else + sig = sig_sp + endif + sig = max(sig, 1) + sig = min(sig, digits(1.0_sp)) + + if (sig > 1) n = n + 1 + ! for the decimal point + + n = n + sig + 1 + len(e) + + elseif (fmt(1:1) == "r") then + + if (len(fmt) > 1) then + dec = str_to_int_10(fmt(2:)) + else + dec = sig_sp - e - 1 + endif + dec = min(dec, digits(1.0_sp)-e) + dec = max(dec, 0) + + if (dec > 0) n = n + 1 + if (abs(x) >= 1.0_sp) n = n + 1 + + ! Need to know if there's an overflow .... + if (e+dec+1 > 0) then + if (index(real_sp_str(abs(x), e+dec+1), "!") == 1) & + e = e + 1 + endif + + n = n + abs(e) + dec + + endif + + end function str_real_sp_fmt_len + + pure function str_real_sp_len(x) result(n) + real(sp), intent(in) :: x + integer :: n + + n = len(x, "") + + end function str_real_sp_len + + pure function str_real_sp_array_len(xa) result(n) + real(sp), dimension(:), intent(in) :: xa + integer :: n + + integer :: k + + n = size(xa) - 1 + do k = 1, size(xa) + n = n + len(xa(k), "") + enddo + + end function str_real_sp_array_len + + pure function str_real_sp_array_fmt_len(xa, fmt) result(n) + real(sp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt + integer :: n + + integer :: k + + n = size(xa) - 1 + do k = 1, size(xa) + n = n + len(xa(k), fmt) + enddo + + end function str_real_sp_array_fmt_len + + pure function str_real_sp_matrix_fmt_len(xa, fmt) result(n) + real(sp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt + integer :: n + + integer :: j, k + + n = size(xa) - 1 + do k = 1, size(xa, 2) + do j = 1, size(xa, 1) + n = n + len(xa(j,k), fmt) + enddo + enddo + + end function str_real_sp_matrix_fmt_len + + pure function str_real_sp_matrix_len(xa) result(n) + real(sp), dimension(:,:), intent(in) :: xa + integer :: n + + n = len(xa, "") + end function str_real_sp_matrix_len + + pure function str_real_dp_fmt_len(x, fmt) result(n) + real(dp), intent(in) :: x + character(len=*), intent(in) :: fmt + integer :: n + + integer :: dec, sig + integer :: e + + if (.not.checkFmt(fmt)) then + n = 0 + return + endif + + if (x == 0.0_dp) then + e = 1 + else + e = floor(log10(abs(x))) + endif + + if (x < 0.0_dp) then + n = 1 + else + n = 0 + endif + + if (len(fmt) == 0) then + sig = sig_dp + + n = n + sig + 2 + len(e) + ! for the decimal point and the e + + elseif (fmt(1:1) == "s") then + if (len(fmt) > 1) then + sig = str_to_int_10(fmt(2:)) + else + sig = sig_dp + endif + sig = max(sig, 1) + sig = min(sig, digits(1.0_dp)) + + if (sig > 1) n = n + 1 + ! for the decimal point + + n = n + sig + 1 + len(e) + + elseif (fmt(1:1) == "r") then + + if (len(fmt) > 1) then + dec = str_to_int_10(fmt(2:)) + else + dec = sig_dp - e - 1 + endif + dec = min(dec, digits(1.0_dp)-e) + dec = max(dec, 0) + + if (dec > 0) n = n + 1 + if (abs(x) >= 1.0_dp) n = n + 1 + + ! Need to know if there's an overflow .... + if (e+dec+1 > 0) then + if (index(real_dp_str(abs(x), e+dec+1), "!") == 1) & + e = e + 1 + endif + + n = n + abs(e) + dec + + endif + + end function str_real_dp_fmt_len + + pure function str_real_dp_len(x) result(n) + real(dp), intent(in) :: x + integer :: n + + n = len(x, "") + + end function str_real_dp_len + + pure function str_real_dp_array_len(xa) result(n) + real(dp), dimension(:), intent(in) :: xa + integer :: n + + integer :: k + + n = size(xa) - 1 + do k = 1, size(xa) + n = n + len(xa(k), "") + enddo + + end function str_real_dp_array_len + + pure function str_real_dp_array_fmt_len(xa, fmt) result(n) + real(dp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt + integer :: n + + integer :: k + + n = size(xa) - 1 + do k = 1, size(xa) + n = n + len(xa(k), fmt) + enddo + + end function str_real_dp_array_fmt_len + + pure function str_real_dp_matrix_fmt_len(xa, fmt) result(n) + real(dp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt + integer :: n + + integer :: j, k + + n = size(xa) - 1 + do k = 1, size(xa, 2) + do j = 1, size(xa, 1) + n = n + len(xa(j,k), fmt) + enddo + enddo + + end function str_real_dp_matrix_fmt_len + + pure function str_real_dp_matrix_len(xa) result(n) + real(dp), dimension(:,:), intent(in) :: xa + integer :: n + + n = len(xa, "") + end function str_real_dp_matrix_len + + pure function str_complex_sp_fmt_len(c, fmt) result(n) + complex(sp), intent(in) :: c + character(len=*), intent(in) :: fmt + integer :: n + + real(sp) :: re, im + re = real(c) + im = aimag(c) + + n = len(re, fmt) + len(im, fmt) + 6 + end function str_complex_sp_fmt_len + + pure function str_complex_sp_len(c) result(n) + complex(sp), intent(in) :: c + integer :: n + + n = len(c, "") + end function str_complex_sp_len + + pure function str_complex_sp_array_fmt_len(ca, fmt) result(n) + complex(sp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt + integer :: n + + integer :: i + + n = size(ca) - 1 + do i = 1, size(ca) + n = n + len(ca(i), fmt) + enddo + end function str_complex_sp_array_fmt_len + + pure function str_complex_sp_array_len(ca) result(n) + complex(sp), dimension(:), intent(in) :: ca + integer :: n + + n = len(ca, "") + end function str_complex_sp_array_len + + pure function str_complex_sp_matrix_fmt_len(ca, fmt) result(n) + complex(sp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt + integer :: n + + integer :: i, j + + n = size(ca) - 1 + do i = 1, size(ca, 1) + do j = 1, size(ca, 2) + n = n + len(ca(i, j), fmt) + enddo + enddo + end function str_complex_sp_matrix_fmt_len + + pure function str_complex_sp_matrix_len(ca) result(n) + complex(sp), dimension(:, :), intent(in) :: ca + integer :: n + + n = len(ca, "") + end function str_complex_sp_matrix_len + + pure function str_complex_dp_fmt_len(c, fmt) result(n) + complex(dp), intent(in) :: c + character(len=*), intent(in) :: fmt + integer :: n + + real(dp) :: re, im + re = real(c) + im = aimag(c) + + n = len(re, fmt) + len(im, fmt) + 6 + end function str_complex_dp_fmt_len + + pure function str_complex_dp_len(c) result(n) + complex(dp), intent(in) :: c + integer :: n + + n = len(c, "") + end function str_complex_dp_len + + pure function str_complex_dp_array_fmt_len(ca, fmt) result(n) + complex(dp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt + integer :: n + + integer :: i + + n = size(ca) - 1 + do i = 1, size(ca) + n = n + len(ca(i), fmt) + enddo + end function str_complex_dp_array_fmt_len + + pure function str_complex_dp_array_len(ca) result(n) + complex(dp), dimension(:), intent(in) :: ca + integer :: n + + n = len(ca, "") + end function str_complex_dp_array_len + + pure function str_complex_dp_matrix_fmt_len(ca, fmt) result(n) + complex(dp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt + integer :: n + + integer :: i, j + + n = size(ca) - 1 + do i = 1, size(ca, 1) + do j = 1, size(ca, 2) + n = n + len(ca(i, j), fmt) + enddo + enddo + end function str_complex_dp_matrix_fmt_len + + pure function str_complex_dp_matrix_len(ca) result(n) + complex(dp), dimension(:, :), intent(in) :: ca + integer :: n + + n = len(ca, "") + end function str_complex_dp_matrix_len +#endif + +#ifndef DUMMYLIB + subroutine FoX_error(msg) + ! Emit error message and stop. + ! No clean up is done here, but this can + ! be overridden to include clean-up routines + character(len=*), intent(in) :: msg + + write(0,'(a)') 'ERROR(FoX)' + write(0,'(a)') msg + call pxfflush(0) + + stop + + end subroutine FoX_error + + + pure function str_to_int_10(str) result(n) + ! Takes a string containing digits, and returns + ! the integer representable by those digits. + ! Does not deal with negative numbers, and + ! presumes that the number is representable + ! in a default integer + ! Error is flagged by returning -1 + character(len=*), intent(in) :: str + integer :: n + + integer :: max_power, i, j + + if (verify(str, digit) > 0) then + n = -1 + return + endif + + max_power = len(str) - 1 + + n = 0 + do i = 0, max_power + j = max_power - i + 1 + n = n + (index(digit, str(j:j)) - 1) * 10**i + enddo + + end function str_to_int_10 + + pure function str_to_int_16(str) result(n) + ! Takes a string containing hexadecimal digits, and returns + ! the integer representable by those digits. + ! Does not deal with negative numbers, and + ! presumes that the number is representable + ! in a default integer + ! Error is flagged by returning -1 + character(len=*), intent(in) :: str + integer :: n + + character(len=len(str)) :: str_l + integer :: max_power, i, j + + if (verify(str, hexdigit) == 0) then + str_l = to_lower(str) + else + n = -1 + return + endif + + max_power = len(str) - 1 + + n = 0 + do i = 0, max_power + j = max_power - i + 1 + n = n + (index(hexdigit, str_l(j:j)) - 1) * 16**i + enddo + + contains + pure function to_lower(s) result(s2) + character(len=*), intent(in) :: s + character(len=len(s)) :: s2 + character(len=*), parameter :: hex = "abcdef" + integer :: j, k + do j = 1, len(s) + k = index('ABCDEF', s(j:j)) + if (k > 0) then + s2(j:j) = hex(k:k) + else + s2(j:j) = s(j:j) + endif + enddo + end function to_lower + + end function str_to_int_16 +#endif + + pure function str_string(st) result(s) + character(len=*), intent(in) :: st +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(st)) :: s + s = st +#endif + end function str_string + + pure function str_string_array(st, delimiter) result(s) + character(len=*), dimension(:), intent(in) :: st + character(len=1), intent(in), optional :: delimiter +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=str_string_array_len(st)) :: s + + integer :: k, n + character(len=1) :: d + + if (present(delimiter)) then + d = delimiter + else + d = " " + endif + + n = 1 + do k = 1, size(st) - 1 + s(n:n+len(st(k))) = st(k)//d + n = n + len(st(k)) + 1 + enddo + s(n:) = st(k) +#endif + end function str_string_array + + pure function str_string_matrix(st, delimiter) result(s) + character(len=*), dimension(:, :), intent(in) :: st + character(len=1), intent(in), optional :: delimiter +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=str_string_matrix_len(st)) :: s + + integer :: j, k, n + character(len=1) :: d + + if (present(delimiter)) then + d = delimiter + else + d = " " + endif + + s(1:len(st)) = st(1,1) + n = len(st) + 1 + do j = 2, size(st, 1) + s(n:n+len(st)) = d//st(j,1) + n = n + len(st) + 1 + enddo + do k = 2, size(st, 2) + do j = 1, size(st, 1) + s(n:n+len(st(j,k))) = d//st(j,k) + n = n + len(st) + 1 + enddo + enddo +#endif + end function str_string_matrix + + pure function str_integer(i) result(s) + integer, intent(in) :: i +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=str_integer_len(i)) :: s + + integer :: b, ii, j, k, n + + b = 10 + + if (i < 0) then + s(1:1) = "-" + n = 2 + else + n = 1 + endif + ii = abs(i) + do k = len(s) - n, 0, -1 + j = ii/(b**k) + ii = ii - j*(b**k) + s(n:n) = digit(j+1:j+1) + n = n + 1 + enddo +#endif + end function str_integer + + pure function str_integer_fmt(i, fmt) result(s) + integer, intent(in) :: i + character(len=*), intent(in):: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=str_integer_fmt_len(i, fmt)) :: s + + character :: f + integer :: b, ii, j, k, n, ls + + if (len(fmt)>0) then + if (fmt(1:1)=="d") then + f = 'd' + b = 10 + elseif (fmt(1:1)=="x") then + f = 'x' + b = 16 + else + ! Undefined outcome + s = "" + return + endif + else + ! Undefined outcome + s = "" + return + endif + + ls = str_integer_base_len(i, b) + n = len(s) - ls + 1 + + if (i < 0) then + if (n>0) s(:n) = "-"//repeat("0", n-1) + n = n + 1 + else + if (n>1) s(:n) = repeat("0", n) + endif + + ii = abs(i) + do k = 1, -n + 1 + j = ii/(b**k) + ii = ii - j*(b**k) + n = n + 1 + enddo + do k = len(s) - n, 0, -1 + j = ii/(b**k) + ii = ii - j*(b**k) + s(n:n) = hexdigit(j+1:j+1) + n = n + 1 + enddo +#endif + end function str_integer_fmt + + pure function str_integer_array(ia) result(s) + integer, dimension(:), intent(in) :: ia +#ifdef DUMMYLIB + character(len=1) :: s +#else + character(len=len(ia, "d")) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(ia) - 1 + j = len(ia(k)) + s(n:n+j) = str(ia(k))//" " + n = n + j + 1 + enddo + s(n:) = str(ia(k)) +#endif + end function str_integer_array + + + function str_integer_array_fmt(ia, fmt) result(s) + integer, dimension(:), intent(in) :: ia + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ia, fmt)) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(ia) - 1 + j = len(ia(k), fmt) + s(n:n+j) = str(ia(k), fmt)//" " + n = n + j + 1 + enddo + s(n:) = str(ia(k), fmt) +#endif + end function str_integer_array_fmt + + pure function str_integer_matrix(ia) result(s) + integer, dimension(:,:), intent(in) :: ia +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ia, "d")) :: s + + integer :: j, k, n + + s(:len(ia(1,1))) = str(ia(1,1)) + n = len(ia(1,1)) + 1 + do j = 2, size(ia, 1) + s(n:n+len(ia(j,1))) = " "//str(ia(j,1)) + n = n + len(ia(j,1)) + 1 + enddo + do k = 2, size(ia, 2) + do j = 1, size(ia, 1) + s(n:n+len(ia(j,k))) = " "//str(ia(j,k)) + n = n + len(ia(j,k)) + 1 + enddo + enddo +#endif + end function str_integer_matrix + + + pure function str_integer_matrix_fmt(ia, fmt) result(s) + integer, dimension(:,:), intent(in) :: ia + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ia, fmt)) :: s + + integer :: j, k, n + + s(:len(ia(1,1), fmt)) = str(ia(1,1), fmt) + n = len(ia(1,1), fmt) + 1 + do j = 2, size(ia, 1) + s(n:n+len(ia(j,1), fmt)) = " "//str(ia(j,1), fmt) + n = n + len(ia(j,1), fmt) + 1 + enddo + do k = 2, size(ia, 2) + do j = 1, size(ia, 1) + s(n:n+len(ia(j,k), fmt)) = " "//str(ia(j,k), fmt) + n = n + len(ia(j,k), fmt) + 1 + enddo + enddo +#endif + end function str_integer_matrix_fmt + + pure function str_logical(l) result(s) + logical, intent(in) :: l +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else +! Pathscale 2.5 gets it wrong if we use merge here +! character(len=merge(4,5,l)) :: s +! And g95 (sep2007) cant resolve the generic here + character(len=str_logical_len(l)) :: s + + if (l) then + s="true" + else + s="false" + endif +#endif + end function str_logical + + pure function str_logical_array(la) result(s) + logical, dimension(:), intent(in) :: la +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(la)) :: s + + integer :: k, n + + n = 1 + do k = 1, size(la) - 1 + if (la(k)) then + s(n:n+3) = "true" + n = n + 5 + else + s(n:n+4) = "false" + n = n + 6 + endif + s(n-1:n-1) = " " + enddo + if (la(k)) then + s(n:) = "true" + else + s(n:) = "false" + endif +#endif + end function str_logical_array + + pure function str_logical_matrix(la) result(s) + logical, dimension(:,:), intent(in) :: la +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(la)) :: s + + integer :: j, k, n + + if (la(1,1)) then + s(:4) = "true" + n = 5 + else + s(:5) = "false" + n = 6 + endif + do j = 2, size(la, 1) + s(n:n) = " " + if (la(j,1)) then + s(n+1:n+4) = "true" + n = n + 5 + else + s(n+1:n+5) = "false" + n = n + 6 + endif + enddo + do k = 2, size(la, 2) + do j = 1, size(la, 1) + s(n:n) = " " + if (la(j,k)) then + s(n+1:n+4) = "true" + n = n + 5 + else + s(n+1:n+5) = "false" + n = n + 6 + endif + enddo + enddo +#endif + end function str_logical_matrix + +#ifndef DUMMYLIB + ! In order to convert real numbers to strings, we need to + ! perform an internal write - but how long will the + ! resultant string be? We don't know & there is no way + ! to discover for an arbitrary format. Therefore, + ! (if we have the capability; f95 or better) + ! we assume it will be less than 100 characters, write + ! it to a string of that length, then remove leading & + ! trailing whitespace. (this means that if the specified + ! format includes whitespace, this will be lost.) + ! + ! If we are working with an F90-only compiler, then + ! we cannot do this trick - the output string will + ! always be 100 chars in length, though we will remove + ! leading whitespace. + + + ! The standard Fortran format functions do not give us + ! enough control, so we write our own real number formatting + ! routines here. For each real type, we optionally take a + ! format like so: + ! "r" which will produce output without an exponent, + ! and digits after the decimal point. + ! or + ! "s": which implies scientific notation, with an + ! exponent, with significant figures. + ! If the integer is absent, then the precision will be + ! half of the number of significant figures available + ! for that real type. + ! The absence of a format implies scientific notation, with + ! the default precision. + + ! These routines are fairly imperfect - they are inaccurate for + ! the lower-end bits of the number, since they work by simple + ! multiplications by 10. + ! Also they will probably be orders of magnitude slower than library IO. + ! Ideally they'd be rewritten to convert from teh native format by + ! bit-twidding. Not sure how to do that portably though. + + ! The format specification could be done more nicely - but unfortunately + ! not in F95 due to *stupid* restrictions on specification expressions. + + ! And I wouldn't have to invent my own format specification if Fortran + ! had a proper IO library anyway. + +!FIXME Signed zero is not handled correctly; don't quite understand why. +!FIXME too much duplication between sp & dp, we should m4. + + pure function real_sp_str(x, sig) result(s) + real(sp), intent(in) :: x + integer, intent(in) :: sig + character(len=sig) :: s + ! make a string of numbers sig long of x. + integer :: e, i, j, k, n + real(sp) :: x_ + + if (sig < 1) then + s ="" + return + endif + + if (x == 0.0_sp) then + e = 1 + else + e = floor(log10(abs(x))) + endif + x_ = abs(x) + ! Have to do this next in a loop rather than just exponentiating in + ! order to avoid under/over-flow. + do i = 1, abs(e) + ! Have to multiply by 10^-e rather than divide by 10^e + ! to avoid rounding errors. + x_ = x_ * (10.0_sp**(-abs(e)/e)) + enddo + n = 1 + do k = sig - 2, 0, -1 + ! This baroque way of taking int() ensures the optimizer + ! stores it in j without keeping a different value in cache. + j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 + if (j==10) then + ! This can happen if, on the previous cycle, int(x_) in + ! the line above gave a result approx. 1.0 less than + ! expected. + ! In this case we want to quit the cycle & just get 999... to the end + s(n:) = repeat("9", sig - n + 1) + return + endif + s(n:n) = digit(j+1:j+1) + n = n + 1 + x_ = (x_ - j) * 10.0_sp + enddo + j = nint(x_) + if (j == 10) then + ! Now round ... + s(n:n) = "9" + ! Are they all 9's? + i = verify(s, "9", .true.) + if (i == 0) then + s(1:1) = "!" + ! overflow + return + endif + j = index(digit, s(i:i)) + s(i:i) = digit(j+1:j+1) + s(i+1:) = repeat("0", sig - i + 1) + else + s(n:n) = digit(j+1:j+1) + endif + + end function real_sp_str + +#endif + + function str_real_sp_fmt_chk(x, fmt) result(s) + real(sp), intent(in) :: x + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(x, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(x, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_real_sp_fmt_chk + +#ifndef DUMMYLIB + pure function str_real_sp_fmt(x, fmt) result(s) + real(sp), intent(in) :: x + character(len=*), intent(in) :: fmt + character(len=len(x, fmt)) :: s + + integer :: sig, dec + integer :: e, n + character(len=len(x, fmt)) :: num !this will always be enough memory. + + if (x == 0.0_sp) then + e = 0 + else + e = floor(log10(abs(x))) + endif + + if (x < 0.0_sp) then + s(1:1) = "-" + n = 2 + else + n = 1 + endif + + if (len(fmt) == 0) then + + sig = sig_sp + + num = real_sp_str(abs(x), sig) + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (sig == 1) then + s(n:n) = num + n = n + 1 + else + s(n:n+1) = num(1:1)//"." + s(n+2:n+sig) = num(2:) + n = n + sig + 1 + endif + + s(n:n) = "e" + s(n+1:) = str(e) + + elseif (fmt(1:1) == "s") then + + if (len(fmt) > 1) then + sig = str_to_int_10(fmt(2:)) + else + sig = sig_sp + endif + sig = max(sig, 1) + sig = min(sig, digits(1.0_sp)) + + num = real_sp_str(abs(x), sig) + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (sig == 1) then + s(n:n) = num + n = n + 1 + else + s(n:n+1) = num(1:1)//"." + s(n+2:n+sig) = num(2:) + n = n + sig + 1 + endif + + s(n:n) = "e" + s(n+1:) = str(e) + + elseif (fmt(1:1) == "r") then + + if (len(fmt) > 1) then + dec = str_to_int_10(fmt(2:)) + else + dec = sig_sp - e - 1 + endif + dec = min(dec, digits(1.0_sp)-e-1) + dec = max(dec, 0) + + if (e+dec+1 > 0) then + num = real_sp_str(abs(x), e+dec+1) + else + num = "" + endif + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (abs(x) >= 1.0_sp) then + s(n:n+e) = num(:e+1) + n = n + e + 1 + if (dec > 0) then + s(n:n) = "." + n = n + 1 + s(n:) = num(e+2:) + endif + else + s(n:n) = "0" + if (dec > 0) then + s(n+1:n+1) = "." + n = n + 2 + if (dec < -e-1) then + s(n:) = repeat("0", dec) + else + s(n:n-e-2) = repeat("0", max(-e-1,0)) + n = n - min(e,-1) - 1 + if (n <= len(s)) then + s(n:) = num + endif + endif + endif + endif + + endif + + end function str_real_sp_fmt +#endif + + pure function str_real_sp(x) result(s) + real(sp), intent(in) :: x +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(x)) :: s + + s = safestr(x, "") +#endif + end function str_real_sp + + pure function str_real_sp_array(xa) result(s) + real(sp), dimension(:), intent(in) :: xa +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa)) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(xa) - 1 + j = len(xa(k), "") + s(n:n+j) = safestr(xa(k), "")//" " + n = n + j + 1 + enddo + s(n:) = safestr(xa(k), "") +#endif + end function str_real_sp_array + +#ifndef DUMMYLIB + pure function str_real_sp_array_fmt(xa, fmt) result(s) + real(sp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt + character(len=len(xa, fmt)) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(xa) - 1 + j = len(xa(k), fmt) + s(n:n+j) = safestr(xa(k), fmt)//" " + n = n + j + 1 + enddo + s(n:) = safestr(xa(k), fmt) + + end function str_real_sp_array_fmt +#endif + + function str_real_sp_array_fmt_chk(xa, fmt) result(s) + real(sp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(xa, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_real_sp_array_fmt_chk + +#ifndef DUMMYLIB + pure function str_real_sp_matrix_fmt(xa, fmt) result(s) + real(sp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt + character(len=len(xa,fmt)) :: s + + integer :: i, j, k, n + + i = len(xa(1,1), fmt) + s(:i) = safestr(xa(1,1), fmt) + n = i + 1 + do j = 2, size(xa, 1) + i = len(xa(j,1), fmt) + s(n:n+i) = " "//safestr(xa(j,1), fmt) + n = n + i + 1 + enddo + do k = 2, size(xa, 2) + do j = 1, size(xa, 1) + i = len(xa(j,k), fmt) + s(n:n+i) = " "//safestr(xa(j,k), fmt) + n = n + i + 1 + enddo + enddo + + end function str_real_sp_matrix_fmt +#endif + + function str_real_sp_matrix_fmt_chk(xa, fmt) result(s) + real(sp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa,fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(xa, fmt) + else + call FoX_error("Invalid format: "//fmt) + end if +#endif + end function str_real_sp_matrix_fmt_chk + + pure function str_real_sp_matrix(xa) result(s) + real(sp), dimension(:,:), intent(in) :: xa +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa)) :: s + + s = safestr(xa, "") +#endif + end function str_real_sp_matrix + +#ifndef DUMMYLIB + pure function real_dp_str(x, sig) result(s) + real(dp), intent(in) :: x + integer, intent(in) :: sig + character(len=sig) :: s + ! make a string of numbers sig long of x. + integer :: e, i, j, k, n + real(dp) :: x_ + + if (sig < 1) then + s ="" + return + endif + + if (x == 0.0_dp) then + e = 1 + else + e = floor(log10(abs(x))) + endif + x_ = abs(x) + ! Have to do this next in a loop rather than just exponentiating in + ! order to avoid under/over-flow. + do i = 1, abs(e) + ! Have to multiply by 10^-e rather than divide by 10^e + ! to avoid rounding errors. + x_ = x_ * (10.0_dp**(-abs(e)/e)) + enddo + n = 1 + do k = sig - 2, 0, -1 + ! This baroque way of taking int() ensures the optimizer definitely + ! stores it in j without keeping a different value in cache. + j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 + if (j==10) then + ! This can happen if, on the previous cycle, int(x_) in + ! the line above gave a result almost exactly 1.0 less than + ! expected - but FP arithmetic is not consistent. + ! In this case we want to quit the cycle & just get 999... to the end + s(n:) = repeat("9", sig - n + 1) + return + endif + s(n:n) = digit(j+1:j+1) + n = n + 1 + x_ = (x_ - j) * 10.0_dp + enddo + j = nint(x_) + if (j == 10) then + ! Now round ... + s(n:n) = "9" + i = verify(s, "9", .true.) + if (i == 0) then + s(1:1) = "!" + !overflow + return + endif + j = index(digit, s(i:i)) + s(i:i) = digit(j+1:j+1) + s(i+1:) = repeat("0", sig - i + 1) + else + s(n:n) = digit(j+1:j+1) + endif + + end function real_dp_str + + +#endif + + function str_real_dp_fmt_chk(x, fmt) result(s) + real(dp), intent(in) :: x + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(x, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(x, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_real_dp_fmt_chk + +#ifndef DUMMYLIB + pure function str_real_dp_fmt(x, fmt) result(s) + real(dp), intent(in) :: x + character(len=*), intent(in) :: fmt + character(len=len(x, fmt)) :: s + + integer :: sig, dec + integer :: e, n + character(len=len(x, fmt)) :: num !this will always be enough memory. + + if (x == 0.0_dp) then + e = 0 + else + e = floor(log10(abs(x))) + endif + + if (x < 0.0_dp) then + s(1:1) = "-" + n = 2 + else + n = 1 + endif + + if (len(fmt) == 0) then + + sig = sig_dp + + num = real_dp_str(abs(x), sig) + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (sig == 1) then + s(n:n) = num + n = n + 1 + else + s(n:n+1) = num(1:1)//"." + s(n+2:n+sig) = num(2:) + n = n + sig + 1 + endif + + s(n:n) = "e" + s(n+1:) = safestr(e) + + elseif (fmt(1:1) == "s") then + + if (len(fmt) > 1) then + sig = str_to_int_10(fmt(2:)) + else + sig = sig_dp + endif + sig = max(sig, 1) + sig = min(sig, digits(1.0_dp)) + + num = real_dp_str(abs(x), sig) + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (sig == 1) then + s(n:n) = num + n = n + 1 + else + s(n:n+1) = num(1:1)//"." + s(n+2:n+sig) = num(2:) + n = n + sig + 1 + endif + + s(n:n) = "e" + s(n+1:) = safestr(e) + + elseif (fmt(1:1) == "r") then + + if (len(fmt) > 1) then + dec = str_to_int_10(fmt(2:)) + else + dec = sig_dp - e - 1 + endif + dec = min(dec, digits(1.0_dp)-e-1) + dec = max(dec, 0) + + if (e+dec+1 > 0) then + num = real_dp_str(abs(x), e+dec+1) + else + num = "" + endif + if (num(1:1) == "!") then + e = e + 1 + num = "1"//repeat("0",len(num)-1) + endif + + if (abs(x) >= 1.0_dp) then + s(n:n+e) = num(:e+1) + n = n + e + 1 + if (dec > 0) then + s(n:n) = "." + n = n + 1 + s(n:) = num(e+2:) + endif + else + s(n:n) = "0" + if (dec > 0) then + s(n+1:n+1) = "." + n = n + 2 + if (dec < -e-1) then + s(n:) = repeat("0", dec) + else + s(n:n-e-2) = repeat("0", max(-e-1,0)) + n = n - min(e,-1) - 1 + if (n <= len(s)) then + s(n:) = num + endif + endif + endif + endif + + endif + + end function str_real_dp_fmt + +#endif + + pure function str_real_dp(x) result(s) + real(dp), intent(in) :: x +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(x)) :: s + + s = safestr(x, "") +#endif + end function str_real_dp + + pure function str_real_dp_array(xa) result(s) + real(dp), dimension(:), intent(in) :: xa +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa)) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(xa) - 1 + j = len(xa(k), "") + s(n:n+j) = safestr(xa(k), "")//" " + n = n + j + 1 + enddo + s(n:) = safestr(xa(k)) +#endif + end function str_real_dp_array + +#ifndef DUMMYLIB + pure function str_real_dp_array_fmt(xa, fmt) result(s) + real(dp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt + character(len=len(xa, fmt)) :: s + + integer :: j, k, n + + n = 1 + do k = 1, size(xa) - 1 + j = len(xa(k), fmt) + s(n:n+j) = safestr(xa(k), fmt)//" " + n = n + j + 1 + enddo + s(n:) = safestr(xa(k), fmt) + + end function str_real_dp_array_fmt +#endif + + function str_real_dp_array_fmt_chk(xa, fmt) result(s) + real(dp), dimension(:), intent(in) :: xa + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(xa, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_real_dp_array_fmt_chk + +#ifndef DUMMYLIB + function str_real_dp_matrix_fmt(xa, fmt) result(s) + real(dp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt + character(len=len(xa,fmt)) :: s + + integer :: i, j, k, n + + i = len(xa(1,1), fmt) + s(:i) = safestr(xa(1,1), fmt) + n = i + 1 + do j = 2, size(xa, 1) + i = len(xa(j,1), fmt) + s(n:n+i) = " "//safestr(xa(j,1), fmt) + n = n + i + 1 + enddo + do k = 2, size(xa, 2) + do j = 1, size(xa, 1) + i = len(xa(j,k), fmt) + s(n:n+i) = " "//safestr(xa(j,k), fmt) + n = n + i + 1 + enddo + enddo + + end function str_real_dp_matrix_fmt +#endif + + function str_real_dp_matrix_fmt_chk(xa, fmt) result(s) + real(dp), dimension(:,:), intent(in) :: xa + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa,fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(xa, fmt) + else + call FoX_error("Invalid format: "//fmt) + end if +#endif + end function str_real_dp_matrix_fmt_chk + + function str_real_dp_matrix(xa) result(s) + real(dp), dimension(:,:), intent(in) :: xa +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(xa)) :: s + + s = safestr(xa, "") +#endif + end function str_real_dp_matrix + +! For complex numbers, there's not really much prior art, so +! we use the easy solution: a+bi, where a & b are real numbers +! as output above. + + function str_complex_sp_fmt_chk(c, fmt) result(s) + complex(sp), intent(in) :: c + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(c, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(c, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_sp_fmt_chk + +#ifndef DUMMYLIB + pure function str_complex_sp_fmt(c, fmt) result(s) + complex(sp), intent(in) :: c + character(len=*), intent(in) :: fmt + character(len=len(c, fmt)) :: s + + real(sp) :: re, im + integer :: i + re = real(c) + im = aimag(c) + i = len(re, fmt) + s(:i+4) = "("//safestr(re, fmt)//")+i" + s(i+5:)="("//safestr(im,fmt)//")" + end function str_complex_sp_fmt +#endif + + pure function str_complex_sp(c) result(s) + complex(sp), intent(in) :: c +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(c, "")) :: s + + s = safestr(c, "") +#endif + end function str_complex_sp + +#ifndef DUMMYLIB + pure function str_complex_sp_array_fmt(ca, fmt) result(s) + complex(sp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt + character(len=len(ca, fmt)) :: s + + integer :: i, n + + s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) + n = len(ca(1), fmt)+1 + do i = 2, size(ca) + s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) + n = n + len(ca(i), fmt)+1 + enddo + end function str_complex_sp_array_fmt +#endif + + function str_complex_sp_array_fmt_chk(ca, fmt) result(s) + complex(sp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(ca, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_sp_array_fmt_chk + + pure function str_complex_sp_array(ca) result(s) + complex(sp), dimension(:), intent(in) :: ca +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca)) :: s + + s = safestr(ca, "") +#endif + end function str_complex_sp_array + +#ifndef DUMMYLIB + pure function str_complex_sp_matrix_fmt(ca, fmt) result(s) + complex(sp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt + character(len=len(ca, fmt)) :: s + + integer :: i, j, k, n + + i = len(ca(1,1), fmt) + s(:i) = safestr(ca(1,1), fmt) + n = i + 1 + do j = 2, size(ca, 1) + i = len(ca(j,1), fmt) + s(n:n+i) = " "//safestr(ca(j,1), fmt) + n = n + i + 1 + enddo + do k = 2, size(ca, 2) + do j = 1, size(ca, 1) + i = len(ca(j,k), fmt) + s(n:n+i) = " "//safestr(ca(j,k), fmt) + n = n + i + 1 + enddo + enddo + + end function str_complex_sp_matrix_fmt +#endif + + function str_complex_sp_matrix_fmt_chk(ca, fmt) result(s) + complex(sp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(ca, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_sp_matrix_fmt_chk + + pure function str_complex_sp_matrix(ca) result(s) + complex(sp), dimension(:, :), intent(in) :: ca +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca)) :: s + + s = safestr(ca, "") +#endif + end function str_complex_sp_matrix + + function str_complex_dp_fmt_chk(c, fmt) result(s) + complex(dp), intent(in) :: c + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(c, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(c, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_dp_fmt_chk + +#ifndef DUMMYLIB + pure function str_complex_dp_fmt(c, fmt) result(s) + complex(dp), intent(in) :: c + character(len=*), intent(in) :: fmt + character(len=len(c, fmt)) :: s + + real(dp) :: re, im + integer :: i + re = real(c) + im = aimag(c) + i = len(re, fmt) + s(:i+4) = "("//safestr(re, fmt)//")+i" + s(i+5:)="("//safestr(im,fmt)//")" + end function str_complex_dp_fmt +#endif + + pure function str_complex_dp(c) result(s) + complex(dp), intent(in) :: c +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(c, "")) :: s + + s = safestr(c, "") +#endif + end function str_complex_dp + +#ifndef DUMMYLIB + pure function str_complex_dp_array_fmt(ca, fmt) result(s) + complex(dp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt + character(len=len(ca, fmt)) :: s + + integer :: i, n + + s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) + n = len(ca(1), fmt)+1 + do i = 2, size(ca) + s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) + n = n + len(ca(i), fmt)+1 + enddo + end function str_complex_dp_array_fmt +#endif + + function str_complex_dp_array_fmt_chk(ca, fmt) result(s) + complex(dp), dimension(:), intent(in) :: ca + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(ca, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_dp_array_fmt_chk + + pure function str_complex_dp_array(ca) result(s) + complex(dp), dimension(:), intent(in) :: ca +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca)) :: s + + s = safestr(ca, "") +#endif + end function str_complex_dp_array + +#ifndef DUMMYLIB + pure function str_complex_dp_matrix_fmt(ca, fmt) result(s) + complex(dp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt + character(len=len(ca, fmt)) :: s + + integer :: i, j, k, n + + i = len(ca(1,1), fmt) + s(:i) = safestr(ca(1,1), fmt) + n = i + 1 + do j = 2, size(ca, 1) + i = len(ca(j,1), fmt) + s(n:n+i) = " "//safestr(ca(j,1), fmt) + n = n + i + 1 + enddo + do k = 2, size(ca, 2) + do j = 1, size(ca, 1) + i = len(ca(j,k), fmt) + s(n:n+i) = " "//safestr(ca(j,k), fmt) + n = n + i + 1 + enddo + enddo + + end function str_complex_dp_matrix_fmt +#endif + + function str_complex_dp_matrix_fmt_chk(ca, fmt) result(s) + complex(dp), dimension(:, :), intent(in) :: ca + character(len=*), intent(in) :: fmt +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca, fmt)) :: s + + if (checkFmt(fmt)) then + s = safestr(ca, fmt) + else + call FoX_error("Invalid format: "//fmt) + endif +#endif + end function str_complex_dp_matrix_fmt_chk + + pure function str_complex_dp_matrix(ca) result(s) + complex(dp), dimension(:, :), intent(in) :: ca +#ifdef DUMMYLIB + character(len=1) :: s + s = " " +#else + character(len=len(ca)) :: s + + s = safestr(ca, "") +#endif + end function str_complex_dp_matrix + +#ifndef DUMMYLIB + pure function checkFmt(fmt) result(good) + character(len=*), intent(in) :: fmt + logical :: good + + ! should be ([rs]\d*)? + + if (len(fmt) > 0) then + if (fmt(1:1) == "r" .or. fmt(1:1) == "s") then + if (len(fmt) > 1) then + good = (verify(fmt(2:), digit) == 0) + else + good = .true. + endif + else + good = .false. + endif + else + good = .true. + endif + end function checkFmt +#endif + + pure function concat_str_int(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + integer, intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_int + pure function concat_int_str(s1, s2) result(s3) + integer, intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_int_str + + pure function concat_str_logical(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + logical, intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_logical + pure function concat_logical_str(s1, s2) result(s3) + logical, intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_logical_str + + pure function concat_str_real_sp(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + real(sp), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_real_sp + pure function concat_real_sp_str(s1, s2) result(s3) + real(sp), intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_real_sp_str + + pure function concat_str_real_dp(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + real(dp), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_real_dp + pure function concat_real_dp_str(s1, s2) result(s3) + real(dp), intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_real_dp_str + + pure function concat_str_complex_sp(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + complex(sp), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_complex_sp + pure function concat_complex_sp_str(s1, s2) result(s3) + complex(sp), intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_complex_sp_str + + pure function concat_str_complex_dp(s1, s2) result(s3) + character(len=*), intent(in) :: s1 + complex(dp), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = s1//str(s2) +#endif + end function concat_str_complex_dp + pure function concat_complex_dp_str(s1, s2) result(s3) + complex(dp), intent(in) :: s1 + character(len=*), intent(in) :: s2 +#ifdef DUMMYLIB + character(len=1) :: s3 + s3 = " " +#else + character(len=len(s1)+len(s2)) :: s3 + s3 = str(s1)//s2 +#endif + end function concat_complex_dp_str + +end module fox_m_fsys_format diff --git a/src/xml/fsys/fox_m_fsys_parse_input.F90 b/src/xml/fsys/fox_m_fsys_parse_input.F90 new file mode 100644 index 000000000..e0307e99c --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_parse_input.F90 @@ -0,0 +1,2204 @@ +module fox_m_fsys_parse_input + + use fox_m_fsys_realtypes, only: sp, dp + + implicit none + private + + character(len=1), parameter :: SPACE = achar(32) + character(len=1), parameter :: NEWLINE = achar(10) + character(len=1), parameter :: CARRIAGE_RETURN = achar(13) + character(len=1), parameter :: TAB = achar(9) + character(len=*), parameter :: whitespace = & + SPACE//NEWLINE//CARRIAGE_RETURN//TAB + + interface rts + module procedure scalartostring + module procedure scalartological + module procedure scalartointeger + module procedure scalartorealsp + module procedure scalartorealdp + module procedure scalartocomplexsp + module procedure scalartocomplexdp + module procedure arraytostring + module procedure arraytological + module procedure arraytointeger + module procedure arraytorealsp + module procedure arraytorealdp + module procedure arraytocomplexsp + module procedure arraytocomplexdp + module procedure matrixtostring + module procedure matrixtological + module procedure matrixtointeger + module procedure matrixtorealsp + module procedure matrixtorealdp + module procedure matrixtocomplexsp + module procedure matrixtocomplexdp + end interface + + public :: rts + +contains + + subroutine scalartostring(s, data, separator, csv, num, iostat) + character(len=*), intent(in) :: s + character(len=*), intent(out) :: data + character, intent(in), optional :: separator + logical, intent(in), optional :: csv + integer, intent(out), optional :: num, iostat +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + character(len=len(s)) :: s2 + logical :: csv_, eof, sp + integer :: m + + csv_ = .false. + if (present(csv)) then + csv_ = csv + endif + + s_i = 1 + err = 0 + eof = .false. + data = "" + ij = 0 + length = 1 + loop: do + if (csv_) then + if (s_i>len(s)) then + data = "" + ij = ij + 1 + exit loop + endif + k = verify(s(s_i:), achar(10)//achar(13)) + if (k==0) then + data = "" + ij = ij + 1 + exit loop + elseif (s(s_i+k-1:s_i+k-1)=="""") then + ! we have a quote-delimited string; + s_i = s_i + k + s2 = "" + quote: do + k = index(s(s_i:), """") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 1 + s2(m:) = s(s_i:k) + m = m + (k-s_i+1) + k = k + 2 + if (k>len(s)) then + err = 2 + exit loop + endif + if (s(k:k)/="""") exit + s_i = k + 1 + if (s_i > len(s)) then + err = 2 + exit loop + endif + m = m + 1 + s2(m:m) = """" + enddo quote + data = s2 + k = scan(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + else + s_i = s_i + k - 1 + k = scan(s(s_i:), achar(10)//achar(13)//",") + if (k==0) then + eof = .true. + k = len(s) + else + if (ij+1==length.and.s(s_i+k-1:s_i+k-1)==",") err = 1 + k = s_i + k - 2 + endif + data = s(s_i:k) + if (index(data, """")/=0) then + err = 2 + exit loop + endif + endif + ij = ij + 1 + s_i = k + 2 + if (eof) exit loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + if (s(s_i:k)=="true".or.s(s_i:k)=="1") then + data = .true. + elseif (s(s_i:k)=="false".or.s(s_i:k)=="0") then + data = .false. + else + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + data(i) = "" + ij = ij + 1 + exit loop + endif + k = verify(s(s_i:), achar(10)//achar(13)) + if (k==0) then + data(i) = "" + ij = ij + 1 + exit loop + elseif (s(s_i+k-1:s_i+k-1)=="""") then + ! we have a quote-delimited string; + s_i = s_i + k + s2 = "" + quote: do + k = index(s(s_i:), """") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 1 + s2(m:) = s(s_i:k) + m = m + (k-s_i+1) + k = k + 2 + if (k>len(s)) then + err = 2 + exit loop + endif + if (s(k:k)/="""") exit + s_i = k + 1 + if (s_i > len(s)) then + err = 2 + exit loop + endif + m = m + 1 + s2(m:m) = """" + enddo quote + data(i) = s2 + k = scan(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + else + s_i = s_i + k - 1 + k = scan(s(s_i:), achar(10)//achar(13)//",") + if (k==0) then + eof = .true. + k = len(s) + else + if (ij+1==length.and.s(s_i+k-1:s_i+k-1)==",") err = 1 + k = s_i + k - 2 + endif + data(i) = s(s_i:k) + if (index(data(i), """")/=0) then + err = 2 + exit loop + endif + endif + ij = ij + 1 + s_i = k + 2 + if (eof) exit loop + + else + if (present(separator)) then + k = index(s(s_i:), separator) + else + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + k = scan(s(s_i:), whitespace) + endif + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + data(i) = s(s_i:k) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + endif + end do loop + + if (present(num)) num = ij + if (ij=0) & + err = 1 + else + if (verify(s(s_i:), whitespace)/=0) & + err = 1 + endif + endif + + if (present(iostat)) then + iostat = err + else + select case (err) + case(-1) + write(0, *) "Error in arraytostring" + write(0, *) "Too few elements found" + stop + case(1) + write(0, *) "Error in arraytostring" + write(0, *) "Too many elements found" + stop + case(2) + write(0, *) "Error in arraytostring" + write(0, *) "Malformed input" + stop + end select + end if + +#else + data = "" +#endif + end subroutine arraytostring + + subroutine matrixtostring(s, data, separator, csv, num, iostat) + character(len=*) :: data(:,:) + character, intent(in), optional :: separator + logical, intent(in), optional :: csv + character(len=*), intent(in) :: s + integer, intent(out), optional :: num + integer, intent(out), optional :: iostat + +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + character(len=len(s)) :: s2 + logical :: csv_, eof + integer :: m + + csv_ = .false. + if (present(csv)) then + if (csv) csv_ = csv + endif + + s_i = 1 + err = 0 + eof = .false. + data = "" + ij = 0 + length = size(data) + loop: do j = 1, size(data, 2) + do i = 1, size(data, 1) + if (csv_) then + if (s_i>len(s)) then + data(i, j) = "" + ij = ij + 1 + exit loop + endif + k = verify(s(s_i:), achar(10)//achar(13)) + if (k==0) then + data(i, j) = "" + ij = ij + 1 + exit loop + elseif (s(s_i+k-1:s_i+k-1)=="""") then + ! we have a quote-delimited string; + s_i = s_i + k + s2 = "" + quote: do + k = index(s(s_i:), """") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 1 + s2(m:) = s(s_i:k) + m = m + (k-s_i+1) + k = k + 2 + if (k>len(s)) then + err = 2 + exit loop + endif + if (s(k:k)/="""") exit + s_i = k + 1 + if (s_i > len(s)) then + err = 2 + exit loop + endif + m = m + 1 + s2(m:m) = """" + enddo quote + data(i, j) = s2 + k = scan(s(s_i:), whitespace) + if (k==0) then + err = 2 + exit loop + endif + else + s_i = s_i + k - 1 + k = scan(s(s_i:), achar(10)//achar(13)//",") + if (k==0) then + eof = .true. + k = len(s) + else + if (ij+1==length.and.s(s_i+k-1:s_i+k-1)==",") err = 1 + k = s_i + k - 2 + endif + data(i, j) = s(s_i:k) + if (index(data(i, j), """")/=0) then + err = 2 + exit loop + endif + endif + ij = ij + 1 + s_i = k + 2 + if (eof) exit loop + else + if (present(separator)) then + k = index(s(s_i:), separator) + else + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + k = scan(s(s_i:), whitespace) + endif + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + data(i, j) = s(s_i:k) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + endif + end do + end do loop + + if (present(num)) num = ij + if (ij=0) & + err = 1 + else + if (verify(s(s_i:), whitespace)/=0) & + err = 1 + endif + endif + + if (present(iostat)) then + iostat = err + else + select case (err) + case(-1) + write(0, *) "Error in matrixtostring" + write(0, *) "Too few elements found" + stop + case(1) + write(0, *) "Error in matrixtostring" + write(0, *) "Too many elements found" + stop + case(2) + write(0, *) "Error in matrixtostring" + write(0, *) "Malformed input" + stop + end select + end if + +#else + data = "" +#endif + end subroutine matrixtostring + + subroutine arraytological(s, data, num, iostat) + logical, intent(out) :: data(:) + character(len=*), intent(in) :: s + integer, intent(out), optional :: num + integer, intent(out), optional :: iostat + +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + data = .false. + ij = 0 + length = size(data) + loop: do i = 1, size(data) + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + if (s(s_i:k)=="true".or.s(s_i:k)=="1") then + data(i) = .true. + elseif (s(s_i:k)=="false".or.s(s_i:k)=="0") then + data(i) = .false. + else + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + if (s(s_i:k)=="true".or.s(s_i:k)=="1") then + data(i, j) = .true. + elseif (s(s_i:k)=="false".or.s(s_i:k)=="0") then + data(i, j) = .false. + else + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i, j) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i, j) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data(i, j) + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data(i) = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data(i, j) = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data(i) = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ijlen(s)) then + err = 2 + exit loop + endif + else + s_i = k + 2 + endif + if (bracketed) then + k = index(s(s_i:), ")") + if (k==0) then + err = 2 + exit loop + endif + k = s_i + k - 2 + else + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + endif + read(s(s_i:k), *, iostat=ios) c + if (ios/=0) then + err = 2 + exit loop + endif + data(i, j) = cmplx(r, c) + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do + end do loop + + if (present(num)) num = ij + if (ij0) then + s(i:i) = lowerAlphabet(n:n) + else + s(i:i) = s_in(i:i) + endif + enddo + + end function toLower + +#endif +end module fox_m_fsys_string diff --git a/src/xml/fsys/fox_m_fsys_string_list.F90 b/src/xml/fsys/fox_m_fsys_string_list.F90 new file mode 100644 index 000000000..ecf407483 --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_string_list.F90 @@ -0,0 +1,191 @@ +module fox_m_fsys_string_list +#ifndef DUMMYLIB + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc + implicit none + private + + type string_t + character, pointer :: s(:) => null() + end type string_t + + type string_list + type(string_t), pointer :: list(:) => null() + end type string_list + + public :: string_t + public :: string_list + + public :: init_string_list + public :: destroy_string_list + public :: add_string + public :: remove_last_string + public :: get_last_string + public :: tokenize_to_string_list + public :: tokenize_and_add_strings + public :: registered_string + + interface destroy + module procedure destroy_string_list + end interface + + public :: destroy + +contains + + subroutine init_string_list(s_list) + type(string_list), intent(inout) :: s_list + + allocate(s_list%list(0)) + end subroutine init_string_list + + subroutine destroy_string_list(s_list) + type(string_list), intent(inout) :: s_list + + integer :: i + + if (associated(s_list%list)) then + do i = 1, ubound(s_list%list, 1) + deallocate(s_list%list(i)%s) + enddo + deallocate(s_list%list) + endif + end subroutine destroy_string_list + + subroutine add_string(s_list, s) + type(string_list), intent(inout) :: s_list + character(len=*), intent(in) :: s + + integer :: i + type(string_t), pointer :: temp(:) + + temp => s_list%list + allocate(s_list%list(size(temp)+1)) + do i = 1, size(temp) + s_list%list(i)%s => temp(i)%s + enddo + deallocate(temp) + s_list%list(i)%s => vs_str_alloc(s) + end subroutine add_string + + subroutine remove_last_string(s_list) + type(string_list), intent(inout) :: s_list + + integer :: i + type(string_t), pointer :: temp(:) + + temp => s_list%list + allocate(s_list%list(size(temp)-1)) + do i = 1, size(temp)-1 + s_list%list(i)%s => temp(i)%s + enddo + deallocate(temp) + + end subroutine remove_last_string + + function get_last_string(s_list) result(s) + type(string_list), intent(in) :: s_list + character(len=size(s_list%list(size(s_list%list))%s)) :: s + + s = str_vs(s_list%list(size(s_list%list))%s) + end function get_last_string + + function tokenize_to_string_list(s) result(s_list) + character(len=*), intent(in) :: s + type(string_list) :: s_list + + ! tokenize a whitespace-separated list of strings + ! and place results in a string list + + character(len=*), parameter :: & + WHITESPACE = achar(9)//achar(10)//achar(13)//achar(32) + integer :: i, j + + call init_string_list(s_list) + + i = verify(s, WHITESPACE) + if (i==0) return + j = scan(s(i:), WHITESPACE) + if (j==0) then + j = len(s) + else + j = i + j - 2 + endif + do + call add_string(s_list, s(i:j)) + i = j + 1 + j = verify(s(i:), WHITESPACE) + if (j==0) exit + i = i + j - 1 + j = scan(s(i:), WHITESPACE) + if (j==0) then + j = len(s) + else + j = i + j - 2 + endif + enddo + + end function tokenize_to_string_list + + function registered_string(s_list, s) result(p) + type(string_list), intent(in) :: s_list + character(len=*), intent(in) :: s + logical :: p + + integer :: i + + p = .false. + do i = 1, size(s_list%list) + if (str_vs(s_list%list(i)%s)//"x"==s//"x") then + p = .true. + exit + endif + enddo + end function registered_string + + subroutine tokenize_and_add_strings(s_list, s, uniquify) + type(string_list), intent(inout) :: s_list + character(len=*), intent(in) :: s + logical, intent(in), optional :: uniquify + + ! tokenize a whitespace-separated list of strings + ! and place results in the given string list + + character(len=*), parameter :: & + WHITESPACE = achar(9)//achar(10)//achar(13)//achar(32) + integer :: i, j + logical :: uniquify_ + + if (present(uniquify)) then + uniquify_ = uniquify + else + uniquify_ = .false. + endif + + i = verify(s, WHITESPACE) + if (i==0) return + j = scan(s(i:), WHITESPACE) + if (j==0) then + j = len(s) + else + j = i + j - 2 + endif + do + if (uniquify_.and..not.registered_string(s_list, s(i:j))) & + call add_string(s_list, s(i:j)) + i = j + 1 + j = verify(s(i:), WHITESPACE) + if (j==0) exit + i = i + j - 1 + j = scan(s(i:), WHITESPACE) + if (j==0) then + j = len(s) + else + j = i + j - 2 + endif + enddo + + end subroutine tokenize_and_add_strings + +#endif +end module fox_m_fsys_string_list diff --git a/src/xml/fsys/m_ieee.F90 b/src/xml/fsys/m_ieee.F90 new file mode 100644 index 000000000..afec6f080 --- /dev/null +++ b/src/xml/fsys/m_ieee.F90 @@ -0,0 +1,17 @@ +module m_ieee + + implicit none + private + + public :: generate_nan + +contains + + function generate_nan() result(nan) + real :: nan + real :: zero + zero = 0.0 + nan = 0.0/zero + end function generate_nan + +end module m_ieee diff --git a/src/xml/fsys/makefile b/src/xml/fsys/makefile new file mode 100644 index 000000000..d6b81cc87 --- /dev/null +++ b/src/xml/fsys/makefile @@ -0,0 +1,47 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +fox_m_fsys_format.o: fox_m_fsys_realtypes.o fox_m_fsys_abort_flush.o +fox_m_fsys_parse_input.o: fox_m_fsys_realtypes.o +fox_m_fsys_count_parse_input.o: fox_m_fsys_realtypes.o +fox_m_fsys_string_list.o: fox_m_fsys_array_str.o diff --git a/src/xml/sax/FoX_sax.F90 b/src/xml/sax/FoX_sax.F90 new file mode 100644 index 000000000..17dc2280e --- /dev/null +++ b/src/xml/sax/FoX_sax.F90 @@ -0,0 +1,34 @@ +module FoX_sax + + use FoX_common + use m_sax_operate + + implicit none + private + + public :: open_xml_file + public :: open_xml_string + public :: close_xml_t + public :: parse + public :: stop_parser + public :: SAX_OPEN_ERROR + + public :: xml_t + + public :: dictionary_t +!SAX functions + public :: getIndex + public :: getLength + public :: getLocalName + public :: getQName + public :: getURI + public :: getValue + public :: getType + public :: isSpecified + public :: isDeclared + public :: setSpecified + public :: setDeclared +!For convenience + public :: hasKey + +end module FoX_sax diff --git a/src/xml/sax/m_sax_operate.F90 b/src/xml/sax/m_sax_operate.F90 new file mode 100644 index 000000000..531618ad2 --- /dev/null +++ b/src/xml/sax/m_sax_operate.F90 @@ -0,0 +1,313 @@ +module m_sax_operate + +#ifndef DUMMYLIB + use m_common_error, only: FoX_error, in_error + use FoX_common, only : str_vs + + use m_sax_reader, only: open_file, close_file + use m_sax_parser, only: sax_parser_init, sax_parser_destroy, sax_parse + use m_sax_types, only: ST_STOP +#endif + use m_sax_types, only: xml_t + + implicit none + private + + integer, parameter :: SAX_OPEN_ERROR = 1001 + + public :: xml_t + public :: open_xml_file + public :: open_xml_string + public :: close_xml_t + public :: parse + public :: stop_parser + public :: SAX_OPEN_ERROR + +contains + + subroutine open_xml_file(xt, file, iostat, lun) + type(xml_t), intent(out) :: xt + character(len=*), intent(in) :: file + integer, intent(out), optional :: iostat + integer, intent(in), optional :: lun +#ifdef DUMMYLIB + if (present(iostat)) iostat = 0 +#else + integer :: i + + call open_file(xt%fb, file=trim(file), iostat=i, lun=lun, es=xt%fx%error_stack) + if (present(iostat)) then + if (in_error(xt%fx%error_stack)) i = SAX_OPEN_ERROR + iostat = i + if (i/=0) return + else + if (i/=0) & + call FoX_error("Error opening file in open_xml_file") + if (in_error(xt%fx%error_stack)) & + call FoX_error(str_vs(xt%fx%error_stack%stack(1)%msg)) + endif + + if (i==0) call sax_parser_init(xt%fx, xt%fb) +#endif + end subroutine open_xml_file + + subroutine open_xml_string(xt, string) + type(xml_t), intent(out) :: xt + character(len=*), intent(in) :: string +#ifndef DUMMYLIB + integer :: iostat + + call open_file(xt%fb, string=string, iostat=iostat, es=xt%fx%error_stack) + call sax_parser_init(xt%fx, xt%fb) +#endif + end subroutine open_xml_string + + subroutine close_xml_t(xt) + type(xml_t), intent(inout) :: xt +#ifndef DUMMYLIB + call close_file(xt%fb) + call sax_parser_destroy(xt%fx) +#endif + end subroutine close_xml_t + + + subroutine parse(xt, & + characters_handler, & + endDocument_handler, & + endElement_handler, & + endPrefixMapping_handler, & + ignorableWhitespace_handler, & + processingInstruction_handler, & + ! setDocumentLocator + skippedEntity_handler, & + startDocument_handler, & + startElement_handler, & + startPrefixMapping_handler, & + notationDecl_handler, & + unparsedEntityDecl_handler, & + error_handler, & + fatalError_handler, & + warning_handler, & + attributeDecl_handler, & + elementDecl_handler, & + externalEntityDecl_handler, & + internalEntityDecl_handler, & + comment_handler, & + endCdata_handler, & + endDTD_handler, & + endEntity_handler, & + startCdata_handler, & + startDTD_handler, & + startEntity_handler, & +! Features / properties + namespaces, & + namespace_prefixes, & + validate, & + xmlns_uris) + + type(xml_t), intent(inout) :: xt + optional :: characters_handler + optional :: endDocument_handler + optional :: endElement_handler + optional :: endPrefixMapping_handler + optional :: ignorableWhitespace_handler + optional :: processingInstruction_handler + optional :: skippedEntity_handler + optional :: startElement_handler + optional :: startDocument_handler + optional :: startPrefixMapping_handler + optional :: notationDecl_handler + optional :: unparsedEntityDecl_handler + optional :: error_handler + optional :: fatalError_handler + optional :: warning_handler + optional :: attributeDecl_handler + optional :: elementDecl_handler + optional :: externalEntityDecl_handler + optional :: internalEntityDecl_handler + optional :: comment_handler + optional :: endCdata_handler + optional :: endEntity_handler + optional :: endDTD_handler + optional :: startCdata_handler + optional :: startDTD_handler + optional :: startEntity_handler + + logical, intent(in), optional :: namespaces + logical, intent(in), optional :: namespace_prefixes + logical, intent(in), optional :: validate + logical, intent(in), optional :: xmlns_uris + + interface + + subroutine characters_handler(chunk) + character(len=*), intent(in) :: chunk + end subroutine characters_handler + + subroutine endDocument_handler() + end subroutine endDocument_handler + + subroutine endElement_handler(namespaceURI, localName, name) + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + character(len=*), intent(in) :: name + end subroutine endElement_handler + + subroutine endPrefixMapping_handler(prefix) + character(len=*), intent(in) :: prefix + end subroutine endPrefixMapping_handler + + subroutine ignorableWhitespace_handler(chars) + character(len=*), intent(in) :: chars + end subroutine ignorableWhitespace_handler + + subroutine processingInstruction_handler(name, content) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: content + end subroutine processingInstruction_handler + + subroutine skippedEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine skippedEntity_handler + + subroutine startDocument_handler() + end subroutine startDocument_handler + + subroutine startElement_handler(namespaceURI, localName, name, attributes) + use FoX_common + character(len=*), intent(in) :: namespaceUri + character(len=*), intent(in) :: localName + character(len=*), intent(in) :: name + type(dictionary_t), intent(in) :: attributes + end subroutine startElement_handler + + subroutine startPrefixMapping_handler(namespaceURI, prefix) + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: prefix + end subroutine startPrefixMapping_handler + + subroutine notationDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine notationDecl_handler + + subroutine unparsedEntityDecl_handler(name, publicId, systemId, notation) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + character(len=*), intent(in) :: notation + end subroutine unparsedEntityDecl_handler + + subroutine error_handler(msg) + character(len=*), intent(in) :: msg + end subroutine error_handler + + subroutine fatalError_handler(msg) + character(len=*), intent(in) :: msg + end subroutine fatalError_handler + + subroutine warning_handler(msg) + character(len=*), intent(in) :: msg + end subroutine warning_handler + + subroutine attributeDecl_handler(eName, aName, type, mode, value) + character(len=*), intent(in) :: eName + character(len=*), intent(in) :: aName + character(len=*), intent(in) :: type + character(len=*), intent(in), optional :: mode + character(len=*), intent(in), optional :: value + end subroutine attributeDecl_handler + + subroutine elementDecl_handler(name, model) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: model + end subroutine elementDecl_handler + + subroutine externalEntityDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine externalEntityDecl_handler + + subroutine internalEntityDecl_handler(name, value) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + end subroutine internalEntityDecl_handler + + subroutine comment_handler(comment) + character(len=*), intent(in) :: comment + end subroutine comment_handler + + subroutine endCdata_handler() + end subroutine endCdata_handler + + subroutine endDTD_handler() + end subroutine endDTD_handler + + subroutine endEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine endEntity_handler + + subroutine startCdata_handler() + end subroutine startCdata_handler + + subroutine startDTD_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine startDTD_handler + + subroutine startEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine startEntity_handler + + end interface +#ifndef DUMMYLIB + ! FIXME check xt is initialized + + call sax_parse(xt%fx, xt%fb, & + characters_handler, & + endDocument_handler, & + endElement_handler, & + endPrefixMapping_handler, & + ignorableWhitespace_handler, & + processingInstruction_handler, & + ! setDocumentLocator + skippedEntity_handler, & + startDocument_handler, & + startElement_handler, & + startPrefixMapping_handler, & + notationDecl_handler, & + unparsedEntityDecl_handler, & + error_handler, & + fatalError_handler, & + warning_handler, & + attributeDecl_handler, & + elementDecl_handler, & + externalEntityDecl_handler, & + internalEntityDecl_handler, & + comment_handler, & + endCdata_handler, & + endDTD_handler, & + endEntity_handler, & + startCdata_handler, & + startDTD_handler, & + startEntity_handler, & + namespaces=namespaces, & + namespace_prefixes=namespace_prefixes, & + validate=validate, & + xmlns_uris=xmlns_uris) +#endif + end subroutine parse + + subroutine stop_parser(xt) + ! To be called from within a callback function; + ! this will stop the parser. + type(xml_t), intent(inout) :: xt +#ifndef DUMMYLIB + xt%fx%state = ST_STOP +#endif + end subroutine stop_parser + +end module m_sax_operate diff --git a/src/xml/sax/m_sax_parser.F90 b/src/xml/sax/m_sax_parser.F90 new file mode 100644 index 000000000..6d63fd046 --- /dev/null +++ b/src/xml/sax/m_sax_parser.F90 @@ -0,0 +1,3027 @@ +module m_sax_parser + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc, vs_vs_alloc + use fox_m_fsys_string_list, only: string_list, destroy_string_list, & + tokenize_to_string_list, registered_string, init_string_list, & + add_string, tokenize_and_add_strings, destroy + use m_common_attrs, only: init_dict, destroy_dict, reset_dict, & + add_item_to_dict, has_key, get_value, get_att_index_pointer, & + getLength, setIsId, setBase + use m_common_charset, only: XML1_0, XML1_1, XML_WHITESPACE + use m_common_element, only: element_t, existing_element, add_element, & + get_element, parse_dtd_element, parse_dtd_attlist, report_declarations, & + declared_element, attribute_t, att_value_normalize, & + get_attribute_declaration, & + ATT_CDATA, ATT_ID, ATT_IDREF, ATT_IDREFS, ATT_ENTITY, ATT_ENTITIES, & + ATT_NMTOKEN, ATT_NMTOKENS, ATT_NOTATION, ATT_ENUM, & + ATT_REQUIRED, ATT_IMPLIED, ATT_DEFAULT, ATT_FIXED + use m_common_elstack, only: push_elstack, pop_elstack, init_elstack, & + destroy_elstack, is_empty, len, get_top_elstack, checkContentModel, & + elementContent, emptyContent, checkContentModelToEnd + use m_common_entities, only: existing_entity, init_entity_list, & + destroy_entity_list, add_internal_entity, is_unparsed_entity, & + expand_entity, expand_char_entity, pop_entity_list, size, & + entity_t, getEntityByIndex, getEntityByName + use m_common_entity_expand, only: expand_entity_value_alloc + use m_common_error, only: FoX_error, add_error, & + init_error_stack, destroy_error_stack, in_error + use m_common_namecheck, only: checkName, checkPublicId, & + checkCharacterEntityReference, likeCharacterEntityReference, & + checkQName, checkNCName, checkPITarget, checkNmtoken, checkNmtokens, & + checkRepCharEntityReference, checkNames, checkNCNames + use m_common_namespaces, only: getnamespaceURI, invalidNS, & + checkNamespaces, checkEndNamespaces, namespaceDictionary, & + initNamespaceDictionary, destroyNamespaceDictionary + use m_common_notations, only: init_notation_list, destroy_notation_list, & + add_notation, notation_exists + use m_common_struct, only: init_xml_doc_state, & + destroy_xml_doc_state, register_internal_PE, register_external_PE, & + register_internal_GE, register_external_GE + + use FoX_utils, only: URI, parseURI, rebaseURI, copyURI, destroyURI, & + hasFragment, expressURI + + use m_sax_reader, only: file_buffer_t, pop_buffer_stack, open_new_string, & + open_new_file, parse_xml_declaration, parse_text_declaration, & + reading_main_file, reading_first_entity + use m_sax_tokenizer, only: sax_tokenize, normalize_attribute_text, & + expand_pe_text + use m_sax_types ! everything, really + + implicit none + private + + public :: getNSDict + + public :: sax_parser_init + public :: sax_parser_destroy + public :: sax_parse + +contains + + function getNSDict(fx) result(ns) + type(sax_parser_t), target :: fx + type(namespaceDictionary), pointer :: ns + + ns => fx%nsDict + end function getNSDict + + subroutine sax_parser_init(fx, fb) + type(sax_parser_t), intent(out) :: fx + type(file_buffer_t), intent(in) :: fb +#ifdef PGF90 + type(URI), pointer :: nullURI + + nullURI => null() +#endif + + allocate(fx%token(0)) + + call init_error_stack(fx%error_stack) + call init_elstack(fx%elstack) + call init_dict(fx%attributes) + + call initNamespaceDictionary(fx%nsdict) + call init_notation_list(fx%nlist) + ! FIXME do we copy correctly from fx%nlist to fx%xds%nlist? + allocate(fx%xds) + call init_xml_doc_state(fx%xds) + deallocate(fx%xds%inputEncoding) + fx%xds%inputEncoding => vs_str_alloc("us-ascii") + ! because it always is ... + if (fb%f(1)%lun>0) then + fx%xds%documentURI => vs_vs_alloc(fb%f(1)%filename) + else + fx%xds%documentURI => vs_str_alloc("") + endif + + fx%xds%standalone = fb%standalone + + call init_entity_list(fx%forbidden_ge_list) + call init_entity_list(fx%forbidden_pe_list) + call init_entity_list(fx%predefined_e_list) + +#ifdef PGF90 + call add_internal_entity(fx%predefined_e_list, 'amp', '&', nullURI, .false.) + call add_internal_entity(fx%predefined_e_list, 'lt', '<', nullURI, .false.) + call add_internal_entity(fx%predefined_e_list, 'gt', '>', nullURI, .false.) + call add_internal_entity(fx%predefined_e_list, 'apos', "'", nullURI, .false.) + call add_internal_entity(fx%predefined_e_list, 'quot', '"', nullURI, .false.) +#else + call add_internal_entity(fx%predefined_e_list, 'amp', '&', null(), .false.) + call add_internal_entity(fx%predefined_e_list, 'lt', '<', null(), .false.) + call add_internal_entity(fx%predefined_e_list, 'gt', '>', null(), .false.) + call add_internal_entity(fx%predefined_e_list, 'apos', "'", null(), .false.) + call add_internal_entity(fx%predefined_e_list, 'quot', '"', null(), .false.) +#endif + end subroutine sax_parser_init + + subroutine sax_parser_destroy(fx) + type(sax_parser_t), intent(inout) :: fx + + fx%context = CTXT_NULL + fx%state = ST_NULL + + if (associated(fx%token)) deallocate(fx%token) + if (associated(fx%root_element)) deallocate(fx%root_element) + + call destroy_error_stack(fx%error_stack) + call destroy_elstack(fx%elstack) + call destroy_dict(fx%attributes) + call destroyNamespaceDictionary(fx%nsdict) + call destroy_notation_list(fx%nlist) + if (.not.fx%xds_used) then + call destroy_xml_doc_state(fx%xds) + deallocate(fx%xds) + endif + + call destroy_entity_list(fx%forbidden_ge_list) + call destroy_entity_list(fx%forbidden_pe_list) + call destroy_entity_list(fx%predefined_e_list) + + if (associated(fx%token)) deallocate(fx%token) + if (associated(fx%content)) deallocate(fx%content) + if (associated(fx%name)) deallocate(fx%name) + if (associated(fx%attname)) deallocate(fx%attname) + if (associated(fx%publicId)) deallocate(fx%publicId) + if (associated(fx%systemId)) deallocate(fx%systemId) + if (associated(fx%Ndata)) deallocate(fx%Ndata) + + end subroutine sax_parser_destroy + + recursive subroutine sax_parse(fx, fb, & + ! org.xml.sax + ! SAX ContentHandler + characters_handler, & + endDocument_handler, & + endElement_handler, & + endPrefixMapping_handler, & + ignorableWhitespace_handler, & + processingInstruction_handler, & + ! setDocumentLocator + skippedEntity_handler, & + startDocument_handler, & + startElement_handler, & + startPrefixMapping_handler, & + ! SAX DTDHandler + notationDecl_handler, & + unparsedEntityDecl_handler, & + ! SAX ErrorHandler + error_handler, & + fatalError_handler, & + warning_handler, & + ! org.xml.sax.ext + ! SAX DeclHandler + attributeDecl_handler, & + elementDecl_handler, & + externalEntityDecl_handler, & + internalEntityDecl_handler, & + ! SAX LexicalHandler + comment_handler, & + endCdata_handler, & + endDTD_handler, & + endEntity_handler, & + startCdata_handler, & + startDTD_handler, & + startEntity_handler, & + namespaces, & + namespace_prefixes, & + xmlns_uris, & + validate, & + FoX_endDTD_handler, & + startInCharData, & + externalEntity, & + xmlVersion, & + initial_entities) + + type(sax_parser_t), intent(inout) :: fx + type(file_buffer_t), intent(inout) :: fb + optional :: characters_handler + optional :: endDocument_handler + optional :: endElement_handler + optional :: endPrefixMapping_handler + optional :: ignorableWhitespace_handler + optional :: processingInstruction_handler + optional :: skippedEntity_handler + optional :: startElement_handler + optional :: startDocument_handler + optional :: startPrefixMapping_handler + optional :: notationDecl_handler + optional :: unparsedEntityDecl_handler + optional :: error_handler + optional :: fatalError_handler + optional :: warning_handler + optional :: attributeDecl_handler + optional :: elementDecl_handler + optional :: externalEntityDecl_handler + optional :: internalEntityDecl_handler + optional :: comment_handler + optional :: endCdata_handler + optional :: endEntity_handler + optional :: endDTD_handler + optional :: FoX_endDTD_handler + optional :: startCdata_handler + optional :: startDTD_handler + optional :: startEntity_handler + + logical, intent(in), optional :: namespaces + logical, intent(in), optional :: namespace_prefixes + logical, intent(in), optional :: xmlns_uris + + logical, intent(in), optional :: validate + logical, intent(in), optional :: startInCharData + logical, intent(in), optional :: externalEntity + character(len=*), intent(in), optional :: xmlVersion + + type(entity_list), optional :: initial_entities +#ifdef PGF90 + type(URI), pointer :: nullURI +#endif + + interface + + subroutine characters_handler(chunk) + character(len=*), intent(in) :: chunk + end subroutine characters_handler + + subroutine endDocument_handler() + end subroutine endDocument_handler + + subroutine endElement_handler(namespaceURI, localName, name) + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: localName + character(len=*), intent(in) :: name + end subroutine endElement_handler + + subroutine endPrefixMapping_handler(prefix) + character(len=*), intent(in) :: prefix + end subroutine endPrefixMapping_handler + + subroutine ignorableWhitespace_handler(chars) + character(len=*), intent(in) :: chars + end subroutine ignorableWhitespace_handler + + subroutine processingInstruction_handler(name, content) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: content + end subroutine processingInstruction_handler + + subroutine skippedEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine skippedEntity_handler + + subroutine startDocument_handler() + end subroutine startDocument_handler + + subroutine startElement_handler(namespaceURI, localName, name, attributes) + use FoX_common + character(len=*), intent(in) :: namespaceUri + character(len=*), intent(in) :: localName + character(len=*), intent(in) :: name + type(dictionary_t), intent(in) :: attributes + end subroutine startElement_handler + + subroutine startPrefixMapping_handler(namespaceURI, prefix) + character(len=*), intent(in) :: namespaceURI + character(len=*), intent(in) :: prefix + end subroutine startPrefixMapping_handler + + subroutine notationDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine notationDecl_handler + + subroutine unparsedEntityDecl_handler(name, publicId, systemId, notation) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + character(len=*), intent(in) :: notation + end subroutine unparsedEntityDecl_handler + + subroutine error_handler(msg) + character(len=*), intent(in) :: msg + end subroutine error_handler + + subroutine fatalError_handler(msg) + character(len=*), intent(in) :: msg + end subroutine fatalError_handler + + subroutine warning_handler(msg) + character(len=*), intent(in) :: msg + end subroutine warning_handler + + subroutine attributeDecl_handler(eName, aName, type, mode, value) + character(len=*), intent(in) :: eName + character(len=*), intent(in) :: aName + character(len=*), intent(in) :: type + character(len=*), intent(in), optional :: mode + character(len=*), intent(in), optional :: value + end subroutine attributeDecl_handler + + subroutine elementDecl_handler(name, model) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: model + end subroutine elementDecl_handler + + subroutine externalEntityDecl_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine externalEntityDecl_handler + + subroutine internalEntityDecl_handler(name, value) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + end subroutine internalEntityDecl_handler + + subroutine comment_handler(comment) + character(len=*), intent(in) :: comment + end subroutine comment_handler + + subroutine endCdata_handler() + end subroutine endCdata_handler + + subroutine endDTD_handler() + end subroutine endDTD_handler + + subroutine FoX_endDTD_handler(state) + use m_common_struct, only: xml_doc_state + type(xml_doc_state), pointer :: state + end subroutine FoX_endDTD_handler + + subroutine endEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine endEntity_handler + + subroutine startCdata_handler() + end subroutine startCdata_handler + + subroutine startDTD_handler(name, publicId, systemId) + character(len=*), intent(in) :: name + character(len=*), intent(in) :: publicId + character(len=*), intent(in) :: systemId + end subroutine startDTD_handler + + subroutine startEntity_handler(name) + character(len=*), intent(in) :: name + end subroutine startEntity_handler + + end interface + + logical :: validCheck, startInCharData_, processDTD, pe, nameOK, eof + logical :: namespaces_, namespace_prefixes_, xmlns_uris_, externalEntity_ + integer :: i, iostat, temp_i, nextState, ignoreDepth, declSepValue + character, pointer :: tempString(:) + character :: dummy + type(element_t), pointer :: elem + type(attribute_t), pointer :: attDecl + type(entity_t), pointer :: ent + type(URI), pointer :: extSubsetURI, URIref, newURI + integer, pointer :: wf_stack(:), temp_wf_stack(:), extEntStack(:) + logical :: inExtSubset + type(string_list) :: id_list, idref_list + +#ifdef PGF90 + nullURI => null() +#endif + tempString => null() + elem => null() + attDecl => null() + + if (present(namespaces)) then + namespaces_ = namespaces + else + namespaces_ = .true. + endif + if (present(namespace_prefixes)) then + namespace_prefixes_ = namespace_prefixes + else + namespace_prefixes_ = .false. + endif + if (present(xmlns_uris)) then + xmlns_uris_ = xmlns_uris + else + xmlns_uris_ = .false. + endif + if (present(validate)) then + validCheck = validate + else + validCheck = .false. + endif + if (present(startInCharData)) then + startInCharData_ = startInCharData + else + startInCharData_ = .false. + endif + if (present(externalEntity)) then + externalEntity_ = externalEntity + else + externalEntity_ = .false. + endif + if (present(initial_entities)) then + do i = 1, size(initial_entities) + ent => getEntityByIndex(initial_entities, i) + if (ent%external) then + call register_external_GE(fx%xds, & + name=str_vs(ent%name), systemId=str_vs(ent%systemId), & + publicId=str_vs(ent%publicId), & + wfc=ent%wfc, baseURI=copyURI(ent%baseURI)) + else + call register_internal_GE(fx%xds, & + name=str_vs(ent%name), text=str_vs(ent%text), & + wfc=ent%wfc, baseURI=copyURI(ent%baseURI)) + endif + enddo + endif + + allocate(wf_stack(1)) + wf_stack(1) = 0 + allocate(extEntStack(0)) + fx%inIntSubset = .false. + extSubsetURI => null() + inExtSubset = .false. + declSepValue = 0 + processDTD = .true. + iostat = 0 + + if (startInCharData_) then + fx%context = CTXT_IN_CONTENT + fx%state = ST_CHAR_IN_CONTENT + fx%well_formed = .true. + if (externalEntity_) call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + if (present(xmlVersion)) then + if (xmlVersion=="1.0") then + fx%xds%xml_version = XML1_0 + elseif (xmlVersion=="1.1") then + fx%xds%xml_version = XML1_1 + endif + endif + elseif (reading_main_file(fb)) then + fx%context = CTXT_BEFORE_DTD + fx%state = ST_MISC + if (present(startDocument_handler)) then + call startDocument_handler() + if (fx%state==ST_STOP) goto 100 + endif + call parse_xml_declaration(fb, fx%xds%xml_version, fx%xds%encoding, fx%xds%standalone, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + call init_string_list(id_list) + call init_string_list(idref_list) + endif + + do + call sax_tokenize(fx, fb, eof) + if (in_error(fx%error_stack)) then + ! Any error, we want to quit sax_tokenizer + call add_error(fx%error_stack, 'Error getting token') + goto 100 + elseif (eof.and..not.reading_main_file(fb)) then + if (inExtSubset.and.reading_first_entity(fb)) then + if (wf_stack(1)>0) then + call add_error(fx%error_stack, & + "Unclosed conditional section or markup in external subset") + goto 100 + elseif (fx%state_dtd/=ST_DTD_SUBSET) then + call add_error(fx%error_stack, & + "Markup not terminated in external subset") + goto 100 + endif + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + inExtSubset = .false. + fx%state = ST_MISC + fx%context = CTXT_BEFORE_CONTENT + elseif (fx%context==CTXT_IN_DTD) then + if (validCheck) then + if (wf_stack(1)/=0) then + call add_error(fx%error_stack, & + "Markup not terminated in parameter entity") + goto 100 + endif + endif + if (declSepValue==size(wf_stack)) then + if (wf_stack(1)/=0) then + call add_error(fx%error_stack, & + "Markup not terminated in parameter entity") + goto 100 + else + declSepValue = 0 + endif + endif + if (present(endEntity_handler)) then + call endEntity_handler('%'//pop_entity_list(fx%forbidden_pe_list)) + if (fx%state==ST_STOP) goto 100 + else + dummy = pop_entity_list(fx%forbidden_pe_list) + endif + if (fx%state_dtd==ST_DTD_ATTLIST_CONTENTS & + .or.fx%state_dtd==ST_DTD_ELEMENT_CONTENTS) then + ! stick the token back in contents ... + fx%content => fx%token + fx%token => vs_str_alloc("") + endif + if (reading_main_file(fb)) & + fx%inIntSubset = .true. + elseif (fx%context==CTXT_IN_CONTENT) then + if (fx%state==ST_TAG_IN_CONTENT) fx%state = ST_CHAR_IN_CONTENT + ! because CHAR_IN_CONTENT *always* leads to TAG_IN_CONTENT + ! *except* when it is the end of an entity expansion + if (present(endEntity_handler)) then + call endEntity_handler(pop_entity_list(fx%forbidden_ge_list)) + if (fx%state==ST_STOP) goto 100 + else + dummy = pop_entity_list(fx%forbidden_ge_list) + endif + if (fx%state/=ST_CHAR_IN_CONTENT.or.wf_stack(1)/=0) then + call add_error(fx%error_stack, 'Ill-formed entity') + goto 100 + endif + endif + temp_wf_stack => wf_stack + allocate(wf_stack(size(temp_wf_stack)-1)) + wf_stack = temp_wf_stack(2:) + ! If we are not doing validity checking, we might have + ! finished PE expansion with wf_stack(1) non-zero + wf_stack(1) = wf_stack(1) + temp_wf_stack(1) + deallocate(temp_wf_stack) + temp_wf_stack => extEntStack + allocate(extEntStack(size(temp_wf_stack)-1)) + extEntStack = temp_wf_stack(2:) + deallocate(temp_wf_stack) + call pop_buffer_stack(fb) + cycle + endif + if (fx%tokenType==TOK_NULL) then + call add_error(fx%error_stack, 'Internal error! No token found!') + goto 100 + endif + + nextState = ST_NULL + + select case (fx%state) + + case (ST_MISC) + !write(*,*) 'ST_MISC', str_vs(fx%token) + select case (fx%tokenType) + case (TOK_PI_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_START_PI + case (TOK_BANG_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_BANG_TAG + case (TOK_OPEN_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_START_TAG + end select + + + case (ST_BANG_TAG) + !write(*,*) 'ST_BANG_TAG' + select case (fx%tokenType) + case (TOK_OPEN_SB) + nextState = ST_START_CDATA_DECLARATION + case (TOK_OPEN_COMMENT) + nextState = ST_START_COMMENT + case (TOK_NAME) + if (str_vs(fx%token)=='DOCTYPE') then + fx%context = CTXT_IN_DTD + nextState = ST_IN_DOCTYPE + endif + end select + + + case (ST_START_PI) + !write(*,*)'ST_START_PI' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (nameOk) then + if (str_vs(fx%token)=='xml') then + call add_error(fx%error_stack, "XML declaration must be at start of document") + goto 100 + elseif (checkPITarget(str_vs(fx%token), fx%xds%xml_version)) then + nextState = ST_PI_CONTENTS + fx%name => fx%token + fx%token => null() + else + call add_error(fx%error_stack, "Invalid PI target name") + goto 100 + endif + endif + end select + + case (ST_PI_CONTENTS) + !write(*,*)'ST_PI_CONTENTS' + if (validCheck) then + if (emptyContent(fx%elstack)) then + call add_error(fx%error_stack, "Content inside empty element") + goto 100 + endif + endif + wf_stack(1) = wf_stack(1) - 1 + + select case(fx%tokenType) + case (TOK_CHAR) + if (present(processingInstruction_handler)) then + call processingInstruction_handler(str_vs(fx%name), str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + deallocate(fx%name) + nextState = ST_PI_END + case (TOK_PI_END) + if (present(processingInstruction_handler)) then + call processingInstruction_handler(str_vs(fx%name), "") + if (fx%state==ST_STOP) goto 100 + endif + deallocate(fx%name) + if (fx%context==CTXT_IN_CONTENT) then + nextState = ST_CHAR_IN_CONTENT + else + nextState = ST_MISC + endif + end select + + case (ST_PI_END) + !write(*,*)'ST_PI_END' + select case(fx%tokenType) + case (TOK_PI_END) + if (fx%context==CTXT_IN_CONTENT) then + nextState = ST_CHAR_IN_CONTENT + else + nextState = ST_MISC + endif + end select + + case (ST_START_COMMENT) + !write(*,*)'ST_START_COMMENT' + select case (fx%tokenType) + case (TOK_CHAR) + fx%name => fx%token + nullify(fx%token) + nextState = ST_COMMENT_END + end select + + case (ST_COMMENT_END) + !write(*,*)'ST_COMMENT_END' + if (validCheck) then + if (emptyContent(fx%elstack)) then + call add_error(fx%error_stack, "Content inside empty element") + goto 100 + endif + endif + wf_stack(1) = wf_stack(1) - 1 + + select case (fx%tokenType) + case (TOK_COMMENT_END) + if (present(comment_handler)) then + call comment_handler(str_vs(fx%name)) + if (fx%state==ST_STOP) goto 100 + endif + deallocate(fx%name) + if (fx%context==CTXT_IN_CONTENT) then + nextState = ST_CHAR_IN_CONTENT + else + nextState = ST_MISC + endif + end select + + case (ST_START_TAG) + !write(*,*)'ST_START_TAG', fx%context + select case (fx%tokenType) + case (TOK_NAME) + if (fx%context==CTXT_BEFORE_DTD & + .or. fx%context==CTXT_BEFORE_CONTENT & + .or. fx%context==CTXT_IN_CONTENT) then + if (namespaces_) then + nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Illegal element name") + goto 100 + endif + fx%name => fx%token + nullify(fx%token) + nextState = ST_IN_TAG + elseif (fx%context == CTXT_AFTER_CONTENT) then + call add_error(fx%error_stack, "Cannot open second root element") + goto 100 + elseif (fx%context == CTXT_IN_DTD) then + call add_error(fx%error_stack, "Cannot open root element before DTD is finished") + goto 100 + endif + end select + + case (ST_START_CDATA_DECLARATION) + !write(*,*) "ST_START_CDATA_DECLARATION" + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token)=="CDATA") then + if (fx%context/=CTXT_IN_CONTENT) then + call add_error(fx%error_stack, "CDATA section only allowed in text content.") + goto 100 + else + nextState = ST_FINISH_CDATA_DECLARATION + endif + else + call add_error(fx%error_stack, "Unknown keyword found in marked section declaration.") + endif + end select + + case (ST_FINISH_CDATA_DECLARATION) + !write(*,*) "ST_FINISH_CDATA_DECLARATION" + select case (fx%tokenType) + case (TOK_OPEN_SB) + nextState = ST_CDATA_CONTENTS + end select + + + case (ST_CDATA_CONTENTS) + !write(*,*)'ST_CDATA_CONTENTS' + select case (fx%tokenType) + case (TOK_CHAR) + fx%name => fx%token + nullify(fx%token) + nextState = ST_CDATA_END + end select + + case (ST_CDATA_END) + !write(*,*)'ST_CDATA_END' + if (validCheck) then + if (emptyContent(fx%elstack).or.elementContent(fx%elstack)) then + call add_error(fx%error_stack, "Content inside empty element") + goto 100 + endif + endif + wf_stack(1) = wf_stack(1) - 1 + + select case(fx%tokenType) + case (TOK_SECTION_END) + if (present(startCdata_handler)) then + call startCdata_handler + if (fx%state==ST_STOP) goto 100 + endif + if (size(fx%name)>0) then + if (present(characters_handler)) then + call characters_handler(str_vs(fx%name)) + if (fx%state==ST_STOP) goto 100 + endif + endif + if (present(endCdata_handler)) then + call endCdata_handler + if (fx%state==ST_STOP) goto 100 + endif + deallocate(fx%name) + nextState = ST_CHAR_IN_CONTENT + end select + + case (ST_IN_TAG) + !write(*,*)'ST_IN_TAG' + select case (fx%tokenType) + case (TOK_END_TAG) + if (fx%context /= CTXT_IN_CONTENT) then + if (associated(fx%root_element)) then + if (validCheck) then + if (str_vs(fx%name)/=str_vs(fx%root_element)) then + call add_error(fx%error_stack, "Root element name does not match document name") + goto 100 + endif + endif + deallocate(fx%root_element) + elseif (validCheck) then + call add_error(fx%error_stack, "No DTD defined") + goto 100 + else + ! We havent had a DTD, so we havent handed xds + ! over to the DOM. + if (present(FoX_endDTD_handler)) then + fx%xds_used = .true. + call FoX_endDTD_handler(fx%xds) + endif + endif + fx%context = CTXT_IN_CONTENT + endif + call open_tag + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + deallocate(fx%name) + nextState = ST_CHAR_IN_CONTENT + + case (TOK_END_TAG_CLOSE) + if (fx%context==CTXT_IN_CONTENT) then + nextState = ST_CHAR_IN_CONTENT + else + ! only a single element in this doc + if (associated(fx%root_element)) then + if (validCheck) then + if (str_vs(fx%name)/=str_vs(fx%root_element)) then + call add_error(fx%error_stack, "Root element name does not match document name") + goto 100 + endif + endif + deallocate(fx%root_element) + elseif (validCheck) then + call add_error(fx%error_stack, "No DTD defined") + goto 100 + else + ! No DTD, so we havent handed over xds + if (present(FoX_endDTD_handler)) then + fx%xds_used = .true. + call FoX_endDTD_handler(fx%xds) + endif + endif + endif + call open_tag + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + call close_tag + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + deallocate(fx%name) + if (fx%context/=CTXT_IN_CONTENT) then + fx%well_formed = .true. + fx%context = CTXT_AFTER_CONTENT + nextState = ST_MISC + endif + + case (TOK_NAME) + if (namespaces_) then + nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Illegal attribute name") + goto 100 + endif + !Have we already had this dictionary item? + if (has_key(fx%attributes, str_vs(fx%token))) then + call add_error(fx%error_stack, "Duplicate attribute name") + goto 100 + endif + fx%attname => fx%token + nullify(fx%token) + if (associated(elem)) then + attDecl => get_attribute_declaration(elem, str_vs(fx%attname)) + else + attDecl => null() + endif + nextState = ST_ATT_NAME + end select + + case (ST_ATT_NAME) + !write(*,*)'ST_ATT_NAME' + select case (fx%tokenType) + case (TOK_EQUALS) + nextState = ST_ATT_EQUALS + end select + + case (ST_ATT_EQUALS) + !write(*,*)'ST_ATT_EQUALS' + ! token is pre-processed attribute value. + ! fx%name still contains attribute name + select case (fx%tokenType) + case (TOK_CHAR) + !First, expand all entities: + tempString => normalize_attribute_text(fx, fx%token) + deallocate(fx%token) + fx%token => tempString + tempString => null() + !If this attribute is not CDATA, we must process further; + if (associated(attDecl)) then + temp_i = attDecl%attType + else + temp_i = ATT_CDATA + endif + if (temp_i==ATT_CDATA) then + call add_item_to_dict(fx%attributes, str_vs(fx%attname), & + str_vs(fx%token), itype=ATT_CDATA, declared=associated(attDecl)) + else + if (validCheck) then + if (fx%xds%standalone.and..not.attDecl%internal & + .and.(str_vs(fx%token)//"x"/=att_value_normalize(str_vs(fx%token))//"x")) then + call add_error(fx%error_stack, & + "Externally-declared attribute value normalization results in changed value "// & + "in standalone document") + goto 100 + endif + endif + call add_item_to_dict(fx%attributes, str_vs(fx%attname), & + att_value_normalize(str_vs(fx%token)), itype=temp_i, & + declared=.true.) + endif + deallocate(fx%attname) + nextState = ST_IN_TAG + end select + + case (ST_CHAR_IN_CONTENT) + !write(*,*)'ST_CHAR_IN_CONTENT' + select case (fx%tokenType) + case (TOK_CHAR) + if (size(fx%token)>0) then + if (validCheck) then + if (elementContent(fx%elstack)) then + if (verify(str_vs(fx%token), XML_WHITESPACE)==0) then + if (fx%xds%standalone.and..not.elem%internal) then + call add_error(fx%error_stack, & + "Externally-specified ignorable whitespace used in standalone document") + goto 100 + endif + if (present(ignorableWhitespace_handler)) then + call ignorableWhitespace_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + else + call add_error(fx%error_stack, "Forbidden content inside elementc: "//get_top_elstack(fx%elstack)) + goto 100 + endif + elseif (emptyContent(fx%elstack)) then + call add_error(fx%error_stack, "Forbidden content inside element: "//get_top_elstack(fx%elstack)) + goto 100 + else + if (present(characters_handler)) then + call characters_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + endif + else + if (present(characters_handler)) then + call characters_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + endif + endif + nextState = ST_TAG_IN_CONTENT + end select + + case (ST_TAG_IN_CONTENT) + !write(*,*) 'ST_TAG_IN_CONTENT' + select case (fx%tokenType) + case (TOK_ENTITY) + nextState = ST_START_ENTITY + case (TOK_PI_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_START_PI + case (TOK_BANG_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_BANG_TAG + case (TOK_CLOSE_TAG) + nextState = ST_CLOSING_TAG + case (TOK_OPEN_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_START_TAG + end select + + case (ST_START_ENTITY) + !write(*,*) 'ST_START_ENTITY' + select case (fx%tokenType) + case (TOK_NAME) + if (validCheck) then + elem => get_element(fx%xds%element_list, get_top_elstack(fx%elstack)) + if (associated(elem)) then + if (elem%empty) then + call add_error(fx%error_stack, & + "Forbidden content inside element") + goto 100 + endif + else + call add_error(fx%error_stack, & + 'Encountered reference to undeclared entity') + endif + endif + ent => getEntityByName(fx%forbidden_ge_list, str_vs(fx%token)) + if (associated(ent)) then + call add_error(fx%error_stack, 'Recursive entity reference') + goto 100 + endif + ent => getEntityByName(fx%predefined_e_list, str_vs(fx%token)) + if (associated(ent)) then + if (present(startEntity_handler)) then + call startEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + if (validCheck) then + if (associated(elem)) then + if (.not.elem%mixed.and..not.elem%any) then + call add_error(fx%error_stack, & + "Forbidden content inside element") + goto 100 + endif + endif + endif + if (present(characters_handler)) then + call characters_handler(expand_entity(fx%predefined_e_list, str_vs(fx%token))) + if (fx%state==ST_STOP) goto 100 + endif + if (present(endEntity_handler)) then + call endEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + elseif (likeCharacterEntityReference(str_vs(fx%token))) then + if (checkRepCharEntityReference(str_vs(fx%token), fx%xds%xml_version)) then + if (validCheck) then + if (associated(elem)) then + if (.not.elem%mixed.and..not.elem%any) then + call add_error(fx%error_stack, & + "Forbidden content inside element") + goto 100 + endif + endif + endif + if (present(characters_handler)) then + call characters_handler(expand_char_entity(str_vs(fx%token))) + if (fx%state==ST_STOP) goto 100 + endif + elseif (checkCharacterEntityReference(str_vs(fx%token), fx%xds%xml_version)) then + call add_error(fx%error_stack, "Unable to digest character entity reference in content, sorry.") + goto 100 + else + call add_error(fx%error_stack, "Illegal character reference") + goto 100 + endif + elseif (existing_entity(fx%xds%entityList, str_vs(fx%token))) then + ent => getEntityByName(fx%xds%entityList, str_vs(fx%token)) + if (ent%wfc.and.fx%xds%standalone) then + call add_error(fx%error_stack, & + 'Externally declared entity referenced in standalone document') + goto 100 + elseif (str_vs(ent%notation)/="") then + call add_error(fx%error_stack, & + 'Cannot reference unparsed entity in content') + goto 100 + elseif (ent%external) then + call open_new_file(fb, ent%baseURI, iostat) + if (iostat/=0) then + if (validCheck) then + call add_error(fx%error_stack, & + "Unable to retrieve external entity "//str_vs(fx%token)) + goto 100 + endif + if (present(skippedEntity_handler)) then + call skippedEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + else + if (present(startEntity_handler)) then + call startEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", null(), .false.) +#endif + temp_wf_stack => wf_stack + allocate(wf_stack(size(temp_wf_stack)+1)) + wf_stack = (/0, temp_wf_stack/) + deallocate(temp_wf_stack) + temp_wf_stack => extEntStack + allocate(extEntStack(size(temp_wf_stack)+1)) + extEntStack = (/len(fx%elstack), temp_wf_stack/) + deallocate(temp_wf_stack) + call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + endif + else + if (validCheck.and.associated(elem)) then + if (elem%empty) then + call add_error(fx%error_stack, & + "Forbidden content inside element") + goto 100 + endif + endif + if (present(startEntity_handler)) then + call startEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", null(), .false.) +#endif + call open_new_string(fb, expand_entity(fx%xds%entityList, str_vs(fx%token)), str_vs(fx%token), baseURI=ent%baseURI) + temp_wf_stack => wf_stack + allocate(wf_stack(size(temp_wf_stack)+1)) + wf_stack = (/0, temp_wf_stack/) + deallocate(temp_wf_stack) + endif + else + ! Unknown entity check standalone etc + if (fx%skippedExternal.and..not.fx%xds%standalone) then + if (present(skippedEntity_handler)) then + call skippedEntity_handler(str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + else + call add_error(fx%error_stack, & + 'Encountered reference to undeclared entity') + endif + endif + nextState = ST_CHAR_IN_CONTENT + end select + + case (ST_CLOSING_TAG) + !write(*,*)'ST_CLOSING_TAG' + select case (fx%tokenType) + case (TOK_NAME) + if (checkName(str_vs(fx%token), fx%xds%xml_version)) then + fx%name => fx%token + nullify(fx%token) + nextState = ST_IN_CLOSING_TAG + else + call add_error(fx%error_stack, "Closing tag: expecting a Name") + goto 100 + end if + end select + + case (ST_IN_CLOSING_TAG) + !write(*,*)'ST_IN_CLOSING_TAG' + select case (fx%tokenType) + case (TOK_END_TAG) + call close_tag + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + deallocate(fx%name) + if (is_empty(fx%elstack)) then + if (startInCharData_) then + fx%well_formed = .true. + nextState = ST_CHAR_IN_CONTENT + else + !we're done + if (validCheck) then + call checkIdRefs + if (in_error(fx%error_stack)) goto 100 + endif + fx%well_formed = .true. + nextState = ST_MISC + fx%context = CTXT_AFTER_CONTENT + endif + else + nextState = ST_CHAR_IN_CONTENT + endif + end select + + case (ST_IN_DOCTYPE) + !write(*,*)'ST_IN_DOCTYPE' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Invalid document name") + goto 100 + endif + fx%root_element => fx%token + nullify(fx%token) + nextState = ST_DOC_NAME + end select + + case (ST_DOC_NAME) + !write(*,*) 'ST_DOC_NAME ', str_vs(fx%token) + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token)=='SYSTEM') then + nextState = ST_DOC_SYSTEM + elseif (str_vs(fx%token)=='PUBLIC') then + nextState = ST_DOC_PUBLIC + endif + case (TOK_OPEN_SB) + if (present(startDTD_handler)) then + call startDTD_handler(str_vs(fx%root_element), "", "") + if (fx%state==ST_STOP) goto 100 + endif + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_IN_SUBSET + fx%inIntSubset = .true. + case (TOK_END_TAG) + if (present(startDTD_handler)) then + call startDTD_handler(str_vs(fx%root_element), "", "") + if (fx%state==ST_STOP) goto 100 + endif + wf_stack(1) = wf_stack(1) - 1 + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + fx%context = CTXT_BEFORE_CONTENT + nextState = ST_MISC + case default + call add_error(fx%error_stack, "Unexpected token") + goto 100 + end select + + case (ST_DOC_PUBLIC) + !write(*,*) 'ST_DOC_PUBLIC' + select case (fx%tokenType) + case (TOK_CHAR) + if (checkPublicId(str_vs(fx%token))) then + fx%publicId => fx%token + fx%token => null() + nextState = ST_DOC_SYSTEM + else + call add_error(fx%error_stack, "Invalid document public id") + goto 100 + endif + end select + + case (ST_DOC_SYSTEM) + !write(*,*) 'ST_DOC_SYSTEM' + select case (fx%tokenType) + case (TOK_CHAR) + fx%systemId => fx%token + fx%token => null() + nextState = ST_DOC_DECL + end select + + case (ST_DOC_DECL) + !write(*,*) 'ST_DOC_DECL' + select case (fx%tokenType) + case (TOK_OPEN_SB) + if (present(startDTD_handler)) then + if (associated(fx%publicId)) then + call startDTD_handler(str_vs(fx%root_element), & + publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + elseif (associated(fx%systemId)) then + call startDTD_handler(str_vs(fx%root_element), & + publicId="", systemId=str_vs(fx%systemId)) + else + call startDTD_handler(str_vs(fx%root_element), "", "") + endif + if (fx%state==ST_STOP) goto 100 + endif + if (associated(fx%systemId)) then + extSubsetURI => parseURI(str_vs(fx%systemId)) + deallocate(fx%systemId) + endif + if (associated(fx%publicId)) deallocate(fx%publicId) + fx%inIntSubset = .true. + wf_stack(1) = wf_stack(1) + 1 + nextState = ST_IN_SUBSET + case (TOK_END_TAG) + if (present(startDTD_handler)) then + if (associated(fx%publicId)) then + call startDTD_handler(str_vs(fx%root_element), publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + deallocate(fx%publicId) + elseif (associated(fx%systemId)) then + call startDTD_handler(str_vs(fx%root_element), publicId="", systemId=str_vs(fx%systemId)) + else + call startDTD_handler(str_vs(fx%root_element), "", "") + endif + if (fx%state==ST_STOP) goto 100 + endif + if (associated(fx%systemId)) then + extSubsetURI => parseURI(str_vs(fx%systemId)) + if (.not.associated(extSubsetURI)) then + call add_error(fx%error_stack, "Invalid URI specified for DTD SYSTEM") + goto 100 + endif + call open_new_file(fb, extSubsetURI, iostat) + if (iostat==0) then + fx%inIntSubset=.false. + call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + temp_wf_stack => wf_stack + allocate(wf_stack(size(temp_wf_stack)+1)) + wf_stack = (/0, temp_wf_stack/) + deallocate(temp_wf_stack) + inExtSubset = .true. + nextState = ST_IN_SUBSET + else + if (validCheck) then + call add_error(fx%error_stack, & + "Unable to retrieve external subset "//str_vs(fx%systemId)) + goto 100 + endif + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + fx%context = CTXT_BEFORE_CONTENT + nextState = ST_MISC + endif + call destroyURI(extSubsetURI) + else + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + fx%context = CTXT_BEFORE_CONTENT + nextState = ST_MISC + endif + if (associated(fx%systemId)) deallocate(fx%systemId) + if (associated(fx%publicId)) deallocate(fx%publicId) + case default + call add_error(fx%error_stack, "Unexpected token in DTD") + goto 100 + end select + + + case (ST_CLOSE_DOCTYPE) + !write(*,*) "ST_CLOSE_DOCTYPE" + select case (fx%tokenType) + case (TOK_END_TAG) + if (wf_stack(1)>1) then + call add_error(fx%error_stack, "Cannot end DTD while conditional section is still open") + goto 100 + endif + if (associated(extSubsetURI)) then + call open_new_file(fb, extSubsetURI, iostat) + call destroyURI(extSubsetURI) + if (iostat==0) then + fx%inIntSubset = .false. + call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + temp_wf_stack => wf_stack + allocate(wf_stack(size(temp_wf_stack)+1)) + wf_stack = (/0, temp_wf_stack/) + deallocate(temp_wf_stack) + inExtSubset = .true. + nextState = ST_IN_SUBSET + else + if (validCheck) then + call add_error(fx%error_stack, & + "Unable to retrieve external subset") + goto 100 + endif + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + wf_stack(1) = wf_stack(1) - 1 + nextState = ST_MISC + fx%context = CTXT_BEFORE_CONTENT + endif + else + call endDTDchecks + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + wf_stack(1) = wf_stack(1) - 1 + nextState = ST_MISC + fx%context = CTXT_BEFORE_CONTENT + endif + end select + + case (ST_IN_SUBSET) + select case(fx%tokenType) + case (TOK_ENTITY) + nextState = ST_START_PE + case default + call parseDTD + if (in_error(fx%error_stack)) goto 100 + if (fx%state==ST_STOP) goto 100 + if (fx%state_dtd==ST_DTD_DONE) & + fx%state_dtd = ST_DTD_SUBSET + end select + + case (ST_START_PE) + !write(*,*) 'ST_START_PE' + select case (fx%tokenType) + case (TOK_NAME) + if (existing_entity(fx%forbidden_pe_list, str_vs(fx%token))) then + call add_error(fx%error_stack, & + 'Recursive entity reference') + goto 100 + endif + ent => getEntityByName(fx%xds%PEList, str_vs(fx%token)) + if (associated(ent)) then + if (ent%wfc.and.fx%xds%standalone) then + call add_error(fx%error_stack, & + "Externally declared entity used in standalone document") + goto 100 + elseif (ent%external) then + if (present(startEntity_handler)) then + call startEntity_handler('%'//str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", null(), .false.) +#endif + call open_new_file(fb, ent%baseURI, iostat, pe=.true.) + if (iostat/=0) then + if (validCheck) then + call add_error(fx%error_stack, & + "Unable to retrieve external parameter entity "//str_vs(fx%token)) + goto 100 + endif + if (present(skippedEntity_handler)) then + call skippedEntity_handler('%'//str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + ! having skipped a PE, we must now not process + ! declarations any further (unless we are declared standalone) + ! (XML section 5.1) + fx%skippedExternal = .true. + processDTD = fx%xds%standalone + else + fx%inIntSubset = .false. + if (present(startEntity_handler)) then + call startEntity_handler('%'//str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", null(), .false.) +#endif + call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + allocate(temp_wf_stack(size(wf_stack)+1)) + temp_wf_stack = (/0, wf_stack/) + deallocate(wf_stack) + wf_stack => temp_wf_stack + if (fx%state_dtd==ST_DTD_SUBSET) & + declSepValue = size(wf_stack) + endif + else + ! Expand the entity, + if (present(startEntity_handler)) then + call startEntity_handler('%'//str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", nullURI, .false.) + call open_new_string(fb, & + expand_entity(fx%xds%PEList, str_vs(fx%token)), str_vs(fx%token), baseURI=nullURI, pe=.true.) +#else + call add_internal_entity(fx%forbidden_pe_list, & + str_vs(fx%token), "", null(), .false.) + call open_new_string(fb, & + expand_entity(fx%xds%PEList, str_vs(fx%token)), str_vs(fx%token), baseURI=null(), pe=.true.) +#endif + ! NB because we are just expanding a string here, anything + ! evaluated as a result of this string is evaluated in the + ! context of the currently open file, so has a baseURI of + ! this file + allocate(temp_wf_stack(size(wf_stack)+1)) + temp_wf_stack = (/0, wf_stack/) + deallocate(wf_stack) + wf_stack => temp_wf_stack + if (fx%state_dtd==ST_DTD_SUBSET) & + declSepValue = size(wf_stack) + endif + ! and do nothing else, carry on ... + else + ! Have we previously skipped an external entity? + if (fx%skippedExternal.and..not.fx%xds%standalone) then + if (processDTD) then + if (present(skippedEntity_handler)) then + call skippedEntity_handler('%'//str_vs(fx%token)) + if (fx%state==ST_STOP) goto 100 + endif + endif + else + ! If not, + call add_error(fx%error_stack, & + "Reference to undeclared parameter entity.") + goto 100 + endif + endif + nextState = ST_IN_SUBSET + + end select + + end select + + if (nextState/=ST_NULL) then + fx%state = nextState + else + call add_error(fx%error_stack, "Internal error in parser - no suitable token found") + goto 100 + endif + + end do + +100 if (associated(tempString)) deallocate(tempString) + if (associated(extSubsetURI)) call destroyURI(extSubsetURI) + call destroy_string_list(id_list) + call destroy_string_list(idref_list) + deallocate(wf_stack) + if (associated(extEntStack)) deallocate(extEntStack) + + if (fx%state==ST_STOP) return + if (.not.eof) then + ! We have encountered an error before the end of a file + if (.not.reading_main_file(fb)) then !we are parsing an entity + if (inExtSubset) then + call add_error(fx%error_stack, "Error encountered processing external subset.") + else + call add_error(fx%error_stack, "Error encountered processing entity.") + endif + call sax_error(fx, fatalError_handler) + else + call sax_error(fx, fatalError_handler) + endif + else + ! EOF of main file + if (startInChardata_) then + if (fx%well_formed) then + if (fx%state==ST_CHAR_IN_CONTENT.and.associated(fx%token)) then + if (size(fx%token)>0.and.present(characters_handler)) & + call characters_handler(str_vs(fx%token)) + endif + else + if (present(fatalError_handler)) & + call fatalError_handler("Ill-formed XML fragment") + endif + elseif (fx%well_formed.and.fx%state==ST_MISC) then + if (present(endDocument_handler)) & + call endDocument_handler() + else + call add_error(fx%error_stack, "File is not well-formed") + call sax_error(fx, fatalError_handler) + endif + endif + + contains + + subroutine parseDTD + + integer :: nextDTDState +#ifdef PGF90 + type(element_t), pointer :: nullElement + + nullElement => null() +#endif + + nextDTDState = ST_DTD_NULL + + select case (fx%state_dtd) + + case (ST_DTD_SUBSET) + !write(*,*) "ST_DTD_SUBSET" + select case (fx%tokenType) + case (TOK_CLOSE_SB) + if (.not.reading_main_file(fb)) then + call add_error(fx%error_stack, "Cannot close DOCTYPE in external entity") + return + endif + wf_stack(1) = wf_stack(1) - 1 + fx%inIntSubset = .false. + nextState = ST_CLOSE_DOCTYPE + nextDTDState = ST_DTD_DONE + case (TOK_SECTION_END) + if (wf_stack(1)==0) then + call add_error(fx%error_stack, "Unbalanced conditional section in parameter entity") + return + endif + wf_stack(1) = wf_stack(1) - 2 + nextDTDState = ST_DTD_SUBSET + case (TOK_PI_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextDTDState = ST_DTD_START_PI + case (TOK_BANG_TAG) + wf_stack(1) = wf_stack(1) + 1 + nextDTDState = ST_DTD_BANG_TAG + case default + call add_error(fx%error_stack, "Unexpected token in document subset") + return + end select + + + case (ST_DTD_BANG_TAG) + !write(*,*) 'ST_DTD_BANG_TAG' + select case (fx%tokenType) + case (TOK_OPEN_SB) + nextDTDState = ST_DTD_START_SECTION_DECL + case (TOK_OPEN_COMMENT) + nextDTDState = ST_DTD_START_COMMENT + case (TOK_NAME) + if (str_vs(fx%token)=='ATTLIST') then + nextDTDState = ST_DTD_ATTLIST + elseif (str_vs(fx%token)=='ELEMENT') then + nextDTDState = ST_DTD_ELEMENT + elseif (str_vs(fx%token)=='ENTITY') then + nextDTDState = ST_DTD_ENTITY + elseif (str_vs(fx%token)=='NOTATION') then + nextDTDState = ST_DTD_NOTATION + endif + end select + + case (ST_DTD_START_SECTION_DECL) + !write(*,*) "ST_DTD_START_SECTION_DECL" + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token)=="IGNORE") then + if (fx%context/=CTXT_IN_DTD.or.reading_main_file(fb)) then + call add_error(fx%error_stack, "IGNORE section only allowed in external subset.") + return + else + ignoreDepth = 0 + fx%context = CTXT_IGNORE + nextDTDState = ST_DTD_FINISH_SECTION_DECL + endif + elseif (str_vs(fx%token)=="INCLUDE") then + if (fx%context/=CTXT_IN_DTD.or.reading_main_file(fb)) then + call add_error(fx%error_stack, "INCLUDE section only allowed in external subset.") + return + else + nextDTDState = ST_DTD_FINISH_SECTION_DECL + endif + else + call add_error(fx%error_stack, "Unknown keyword found in marked section declaration.") + endif + end select + + + case (ST_DTD_FINISH_SECTION_DECL) + !write(*,*) "ST_FINISH_SECTION_DECL" + select case (fx%tokenType) + case (TOK_OPEN_SB) + wf_stack(1) = wf_stack(1) + 1 + if (fx%context==CTXT_IGNORE) then + nextDTDState = ST_DTD_IN_IGNORE_SECTION + ignoreDepth = ignoreDepth + 1 + else + nextDTDState = ST_DTD_SUBSET + endif + end select + + + case (ST_DTD_IN_IGNORE_SECTION) + !write(*,*) "ST_IN_IGNORE_SECTION" + select case (fx%tokenType) + case (TOK_SECTION_START) + wf_stack(1) = wf_stack(1) + 2 + ignoreDepth = ignoreDepth + 1 + nextDTDState = ST_DTD_IN_IGNORE_SECTION + case (TOK_SECTION_END) + wf_stack(1) = wf_stack(1) - 2 + ignoreDepth = ignoreDepth - 1 + if (ignoreDepth==0) then + fx%context = CTXT_IN_DTD + nextDTDState = ST_DTD_SUBSET + else + nextDTDState = ST_DTD_IN_IGNORE_SECTION + endif + end select + + case (ST_DTD_START_PI) + !write(*,*)'ST_DTD_START_PI' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (nameOk) then + if (str_vs(fx%token)=='xml') then + call add_error(fx%error_stack, "XML declaration must be at start of document") + return + elseif (checkPITarget(str_vs(fx%token), fx%xds%xml_version)) then + nextDTDState = ST_DTD_PI_CONTENTS + fx%name => fx%token + fx%token => null() + else + call add_error(fx%error_stack, "Invalid PI target name") + return + endif + endif + end select + + case (ST_DTD_PI_CONTENTS) + !write(*,*)'ST_DTD_PI_CONTENTS' + if (validCheck) then + if (fx%context==CTXT_IN_DTD.and.wf_stack(1)==0) then + call add_error(fx%error_stack, & + "PI not balanced in parameter entity") + return + endif + if (len(fx%elstack)>0) then + elem => & + get_element(fx%xds%element_list, get_top_elstack(fx%elstack)) + if (associated(elem)) then + if (elem%empty) then + call add_error(fx%error_stack, "Content inside empty element") + endif + endif + endif + endif + wf_stack(1) = wf_stack(1) - 1 + + select case(fx%tokenType) + case (TOK_CHAR) + if (present(processingInstruction_handler)) then + call processingInstruction_handler(str_vs(fx%name), str_vs(fx%token)) + if (fx%state==ST_STOP) return + endif + deallocate(fx%name) + nextDTDState = ST_DTD_PI_END + case (TOK_PI_END) + if (present(processingInstruction_handler)) then + call processingInstruction_handler(str_vs(fx%name), '') + if (fx%state==ST_STOP) return + endif + deallocate(fx%name) + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_PI_END) + !write(*,*)'ST_DTD_PI_END' + select case(fx%tokenType) + case (TOK_PI_END) + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_START_COMMENT) + !write(*,*)'ST_DTD_START_COMMENT' + select case (fx%tokenType) + case (TOK_CHAR) + fx%name => fx%token + nullify(fx%token) + nextDTDState = ST_DTD_COMMENT_END + end select + + case (ST_DTD_COMMENT_END) + !write(*,*)'ST_DTD_COMMENT_END' + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "Comment not balanced in entity") + return + endif + if (len(fx%elstack)>0) then + elem => & + get_element(fx%xds%element_list, get_top_elstack(fx%elstack)) + if (associated(elem)) then + if (elem%empty) then + call add_error(fx%error_stack, "Content inside empty element") + endif + endif + endif + endif + wf_stack(1) = wf_stack(1) - 1 + + select case (fx%tokenType) + case (TOK_COMMENT_END) + if (present(comment_handler)) then + call comment_handler(str_vs(fx%name)) + if (fx%state==ST_STOP) return + endif + deallocate(fx%name) + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_ATTLIST) + !write(*,*) 'ST_DTD_ATTLIST' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Invalid element name for ATTLIST") + return + endif + if (existing_element(fx%xds%element_list, str_vs(fx%token))) then + elem => get_element(fx%xds%element_list, str_vs(fx%token)) + else + elem => add_element(fx%xds%element_list, str_vs(fx%token)) + endif + nextDTDState = ST_DTD_ATTLIST_CONTENTS + end select + + case (ST_DTD_ATTLIST_CONTENTS) + !write(*,*) 'ST_DTD_ATTLIST_CONTENTS' + select case (fx%tokenType) + case (TOK_ENTITY) + !Weve found a PEref in the middle of the element contents + ! Leave DTD state as it is & expand the entity ... + nextState = ST_START_PE + case (TOK_DTD_CONTENTS) + if (processDTD) then + call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, elem, & + internal=reading_main_file(fb)) + else +#ifdef PGF90 + call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, nullElement, & + internal=reading_main_file(fb)) +#else + call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, null(), & + internal=reading_main_file(fb)) +#endif + endif + if (in_error(fx%error_stack)) return + ! Normalize attribute values in attlist + if (processDTD) then + do i = 1, size(elem%attlist%list) + if (associated(elem%attlist%list(i)%default)) then + tempString => elem%attlist%list(i)%default + elem%attlist%list(i)%default => & + normalize_attribute_text(fx, tempString) + deallocate(tempString) + if (in_error(fx%error_stack)) return + endif + enddo + endif + nextDTDState = ST_DTD_ATTLIST_END + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "ATTLIST not balanced in parameter entity") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + call parse_dtd_attlist("", fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, elem, & + internal=reading_main_file(fb)) + else +#ifdef PGF90 + call parse_dtd_attlist("", fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, nullElement, & + internal=reading_main_file(fb)) +#else + call parse_dtd_attlist("", fx%xds%xml_version, & + namespaces_, validCheck, fx%error_stack, null(), & + internal=reading_main_file(fb)) +#endif + endif + if (in_error(fx%error_stack)) return + if (processDTD) then + if (present(attributeDecl_handler)) then + call report_declarations(elem, attributeDecl_handler) + if (fx%state==ST_STOP) return + endif + endif + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_ATTLIST_END) + !write(*,*) 'ST_DTD_ATTLIST_END' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "ATTLIST not balanced in parameter entity") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + if (present(attributeDecl_handler)) then + call report_declarations(elem, attributeDecl_handler) + if (fx%state==ST_STOP) return + endif + endif + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_ELEMENT) + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Invalid name for ELEMENT") + return + endif + fx%name => fx%token + fx%token => null() + nextDTDState = ST_DTD_ELEMENT_CONTENTS + end select + + case (ST_DTD_ELEMENT_CONTENTS) + !write(*,*)'ST_DTD_ELEMENT_CONTENTS' + select case (fx%tokenType) + case (TOK_OPEN_PAR) + ! increment well_formedness + wf_stack(1) = wf_stack(1) + 1 + nextDTDState = ST_DTD_ELEMENT_CONTENTS + case (TOK_CLOSE_PAR) + ! increment well_formedness + wf_stack(1) = wf_stack(1) - 1 + nextDTDState = ST_DTD_ELEMENT_CONTENTS + case (TOK_ENTITY) + !Weve found a PEref in the middle of the element contents + ! Leave DTD state as it is & expand the entity ... + nextState = ST_START_PE + case (TOK_DTD_CONTENTS) + if (declared_element(fx%xds%element_list, str_vs(fx%name))) then + if (validCheck) then + call add_error(fx%error_stack, "Duplicate Element declaration") + return + else + ! Ignore contents ... + elem => null() + endif + elseif (processDTD) then + if (existing_element(fx%xds%element_list, str_vs(fx%name))) then + elem => get_element(fx%xds%element_list, str_vs(fx%name)) + else + elem => add_element(fx%xds%element_list, str_vs(fx%name)) + endif + else + elem => null() + endif + call parse_dtd_element(str_vs(fx%token), fx%xds%xml_version, fx%error_stack, & + elem, reading_main_file(fb)) + if (in_error(fx%error_stack)) return + nextDTDState = ST_DTD_ELEMENT_END + end select + + case (ST_DTD_ELEMENT_END) + !write(*,*)'ST_DTD_ELEMENT_END' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "ELEMENT not balanced in parameter entity") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD.and.associated(elem)) then + if (present(elementDecl_handler)) then + call elementDecl_handler(str_vs(fx%name), str_vs(elem%model)) + if (fx%state==ST_STOP) return + endif + endif + deallocate(fx%name) + nextDTDState = ST_DTD_SUBSET + end select + + case (ST_DTD_ENTITY) + !write(*,*) 'ST_DTD_ENTITY' + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token)=="%") then + pe = .true. + ! this will be a PE + nextDTDState = ST_DTD_ENTITY_PE + else + pe = .false. + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Illegal name for general entity") + return + endif + fx%name => fx%token + fx%token => null() + nextDTDState = ST_DTD_ENTITY_ID + endif + end select + + case (ST_DTD_ENTITY_PE) + !write(*,*) 'ST_DTD_ENTITY_PE' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Illegal name for parameter entity") + return + endif + fx%name => fx%token + fx%token => null() + nextDTDState = ST_DTD_ENTITY_ID + end select + + case (ST_DTD_ENTITY_ID) + !write(*,*) 'ST_DTD_ENTITY_ID' + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token) == "PUBLIC") then + nextDTDState = ST_DTD_ENTITY_PUBLIC + elseif (str_vs(fx%token) == "SYSTEM") then + nextDTDState = ST_DTD_ENTITY_SYSTEM + else + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + endif + case (TOK_CHAR) + if (reading_main_file(fb)) then + tempString => fx%token + else + tempString => expand_pe_text(fx, fx%token, fb) + endif + fx%attname => expand_entity_value_alloc(tempString, fx%xds, fx%error_stack) + if (reading_main_file(fb)) then + tempString => null() + else + deallocate(tempString) + endif + if (in_error(fx%error_stack)) return + nextDTDState = ST_DTD_ENTITY_END + case default + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + end select + + case (ST_DTD_ENTITY_PUBLIC) + !write(*,*) 'ST_DTD_ENTITY_PUBLIC' + select case (fx%tokenType) + case (TOK_CHAR) + if (checkPublicId(str_vs(fx%token))) then + fx%publicId => fx%token + fx%token => null() + nextDTDState = ST_DTD_ENTITY_SYSTEM + else + call add_error(fx%error_stack, "Invalid PUBLIC id in ENTITY") + return + endif + case default + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + end select + + case (ST_DTD_ENTITY_SYSTEM) + !write(*,*) 'ST_DTD_ENTITY_SYSTEM' + select case (fx%tokenType) + case (TOK_CHAR) + fx%systemId => fx%token + fx%token => null() + nextDTDState = ST_DTD_ENTITY_NDATA + case default + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + end select + + case (ST_DTD_ENTITY_NDATA) + !write(*,*) 'ST_DTD_ENTITY_NDATA' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "ENTITY not balanced in parameter entity") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + call add_entity + if (in_error(fx%error_stack)) return + if (fx%state==ST_STOP) return + endif + deallocate(fx%name) + if (associated(fx%attname)) deallocate(fx%attname) + if (associated(fx%systemId)) deallocate(fx%systemId) + if (associated(fx%publicId)) deallocate(fx%publicId) + if (associated(fx%Ndata)) deallocate(fx%Ndata) + nextDTDState = ST_DTD_SUBSET + case (TOK_NAME) + if (str_vs(fx%token)=='NDATA') then + if (pe) then + call add_error(fx%error_stack, "Parameter entity cannot have NDATA declaration") + return + endif + nextDTDState = ST_DTD_ENTITY_NDATA_VALUE + else + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + endif + case default + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + end select + + case (ST_DTD_ENTITY_NDATA_VALUE) + !write(*,*) 'ST_DTD_ENTITY_NDATA_VALUE' + !check is a name and exists in notationlist + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Invalid name for Notation") + return + endif + fx%Ndata => fx%token + fx%token => null() + nextDTDState = ST_DTD_ENTITY_END + case default + call add_error(fx%error_stack, "Unexpected token in ENTITY") + return + end select + + case (ST_DTD_ENTITY_END) + !write(*,*) 'ST_DTD_ENTITY_END' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "ENTITY not balanced in parameter entity") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + call add_entity + if (in_error(fx%error_stack)) return + if (fx%state==ST_STOP) return + endif + deallocate(fx%name) + if (associated(fx%attname)) deallocate(fx%attname) + if (associated(fx%systemId)) deallocate(fx%systemId) + if (associated(fx%publicId)) deallocate(fx%publicId) + if (associated(fx%Ndata)) deallocate(fx%Ndata) + nextDTDState = ST_DTD_SUBSET + case default + call add_error(fx%error_stack, "Unexpected token at end of ENTITY") + return + end select + + case (ST_DTD_NOTATION) + !write(*,*) 'ST_DTD_NOTATION' + select case (fx%tokenType) + case (TOK_NAME) + if (namespaces_) then + nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + else + nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, "Invalid name for Notation") + return + endif + fx%name => fx%token + fx%token => null() + nextDTDState = ST_DTD_NOTATION_ID + case default + call add_error(fx%error_stack, "Unexpected token in NOTATION") + return + end select + + case (ST_DTD_NOTATION_ID) + !write(*,*)'ST_DTD_NOTATION_ID' + select case (fx%tokenType) + case (TOK_NAME) + if (str_vs(fx%token)=='SYSTEM') then + nextDTDState = ST_DTD_NOTATION_SYSTEM + elseif (str_vs(fx%token)=='PUBLIC') then + nextDTDState = ST_DTD_NOTATION_PUBLIC + else + call add_error(fx%error_stack, "Unexpected token after NOTATION") + return + endif + case default + call add_error(fx%error_stack, "Unexpected token after NOTATION") + return + end select + + case (ST_DTD_NOTATION_SYSTEM) + !write(*,*)'ST_DTD_NOTATION_SYSTEM' + select case (fx%tokenType) + case (TOK_CHAR) + fx%systemId => fx%token + fx%token => null() + nextDTDState = ST_DTD_NOTATION_END + case default + call add_error(fx%error_stack, "Unexpected token in NOTATION") + return + end select + + case (ST_DTD_NOTATION_PUBLIC) + !write(*,*)'ST_DTD_NOTATION_PUBLIC' + select case (fx%tokenType) + case (TOK_CHAR) + if (checkPublicId(str_vs(fx%token))) then + fx%publicId => fx%token + fx%token => null() + nextDTDState = ST_DTD_NOTATION_PUBLIC_2 + else + call add_error(fx%error_stack, "Invalid PUBLIC id in NOTATION") + return + endif + case default + call add_error(fx%error_stack, "Unexpected token in NOTATION") + return + end select + + case (ST_DTD_NOTATION_PUBLIC_2) + !write(*,*)'ST_DTD_NOTATION_PUBLIC_2' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "NOTATION not balanced in parameter entity") + return + endif + if (notation_exists(fx%nlist, str_vs(fx%name))) then + call add_error(fx%error_stack, "Duplicate notation declaration") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + call add_notation(fx%nlist, str_vs(fx%name), publicId=str_vs(fx%publicId)) + if (present(notationDecl_handler)) then + call notationDecl_handler(str_vs(fx%name), publicId=str_vs(fx%publicId), systemId="") + if (fx%state==ST_STOP) return + endif + endif + deallocate(fx%name) + deallocate(fx%publicId) + nextDTDState = ST_DTD_SUBSET + case (TOK_CHAR) + fx%systemId => fx%token + fx%token => null() + nextDTDState = ST_DTD_NOTATION_END + end select + + case (ST_DTD_NOTATION_END) + !write(*,*)'ST_DTD_NOTATION_END' + select case (fx%tokenType) + case (TOK_END_TAG) + if (validCheck) then + if (wf_stack(1)==0) then + call add_error(fx%error_stack, & + "NOTATION not balanced in parameter entity") + return + endif + if (notation_exists(fx%nlist, str_vs(fx%name))) then + call add_error(fx%error_stack, "Duplicate notation declaration") + return + endif + endif + wf_stack(1) = wf_stack(1) - 1 + if (processDTD) then + URIref => parseURI(str_vs(fx%systemId)) + if (.not.associated(URIref)) then + call add_error(fx%error_stack, "Invalid SYSTEM literal") + return + endif + if (hasFragment(URIref)) then + call destroyURI(URIref) + call add_error(fx%error_stack, "SYSTEM literal may not contain fragment") + return + endif + ! We aren't ever going to do anything with this URI, + ! since we don't do NOTATIONs. + ! Throw it away again + call destroyURI(URIref) + if (associated(fx%publicId)) then + call add_notation(fx%nlist, str_vs(fx%name), & + publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + if (present(notationDecl_handler)) then + call notationDecl_handler(str_vs(fx%name), & + publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + if (fx%state==ST_STOP) return + endif + else + call add_notation(fx%nlist, str_vs(fx%name), & + systemId=str_vs(fx%systemId)) + if (present(notationDecl_handler)) then + call notationDecl_handler(str_vs(fx%name), & + publicId="", systemId=str_vs(fx%systemId)) + if (fx%state==ST_STOP) return + endif + endif + endif + if (associated(fx%publicId)) deallocate(fx%publicId) + deallocate(fx%systemId) + deallocate(fx%name) + nextDTDState = ST_DTD_SUBSET + case default + call add_error(fx%error_stack, "Unexpected token in NOTATION") + return + end select + + end select + + if (nextDTDState==ST_DTD_NULL) then + call add_error(fx%error_stack, & + "Bad token found in DTD parsing") + else + fx%state_dtd = nextDTDState + endif + if (nextState==ST_NULL) & + nextState = ST_IN_SUBSET + + end subroutine parseDTD + + subroutine open_tag + elem => get_element(fx%xds%element_list, str_vs(fx%name)) + if (associated(elem)) then + if (validCheck) then + call checkAttributes(elem, fx%attributes) + if (.not.checkContentModel(fx%elstack, str_vs(fx%name))) then + call add_error(fx%error_stack, & + "Element "//str_vs(fx%name)//" not permitted in this context") + return + endif + else + call getDefaultAttributes(elem, fx%attributes) + endif + else + if (validCheck) then + call add_error(fx%error_stack, & + "Trying to use an undeclared element") + return + endif + endif + ! Check for namespace changes + if (namespaces_) then + call checkNamespaces(fx%attributes, fx%nsDict, & + len(fx%elstack), fx%xds, namespace_prefixes_, xmlns_uris_, & + fx%error_stack, startInCharData_, & + startPrefixMapping_handler, endPrefixMapping_handler) + if (fx%state==ST_STOP) return + endif + if (in_error(fx%error_stack)) return + call checkXmlAttributes + if (in_error(fx%error_stack)) return + if (size(extEntStack)>0) then + if (len(fx%elstack)==extEntStack(1)) & + ! This is a top-level element in the current entity + call setBase(fx%attributes, expressURI(fb%f(1)%baseURI)) + endif + if (namespaces_.and.getURIofQName(fx,str_vs(fx%name))==invalidNS) then + ! no namespace was found for the current element + if (.not.startInCharData_) then + ! but we ignore this if we are parsing an entity through DOM + call add_error(fx%error_stack, "No namespace found for current element") + return + elseif (present(startElement_handler)) then + ! Record it as having an empty URI + call startElement_handler("", & + getlocalNameofQName(str_vs(fx%name)), & + str_vs(fx%name), fx%attributes) + if (fx%state==ST_STOP) return + endif + elseif (namespaces_) then + ! Normal state of affairs + if (present(startElement_handler)) then + call startElement_handler(getURIofQName(fx, str_vs(fx%name)), & + getlocalNameofQName(str_vs(fx%name)), & + str_vs(fx%name), fx%attributes) + if (fx%state==ST_STOP) return + endif + else + ! Non-namespace aware processing + if (present(startElement_handler)) then + call startElement_handler("", "", & + str_vs(fx%name), fx%attributes) + if (fx%state==ST_STOP) return + endif + endif + if (validCheck) then + call push_elstack(fx%elstack, str_vs(fx%name), elem%cp) + else + call push_elstack(fx%elstack, str_vs(fx%name)) + endif + call reset_dict(fx%attributes) + end subroutine open_tag + + subroutine close_tag + character :: dummy + wf_stack(1) = wf_stack(1) - 1 + if (wf_stack(1)<0) then + call add_error(fx%error_stack, & + 'Ill-formed entity') + return + endif + if (str_vs(fx%name)/=get_top_elstack(fx%elstack)) then + call add_error(fx%error_stack, & + "Mismatching close tag: trying to close "//get_top_elstack(fx%elstack) & + //" with "//str_vs(fx%name)) + return + endif + if (validCheck) then + if (.not.checkContentModelToEnd(fx%elstack)) then + call add_error(fx%error_stack, & + "Failed to fulfil content model for "//str_vs(fx%name)) + return + endif + endif + dummy = pop_elstack(fx%elstack) + if (present(endElement_handler)) then + if (namespaces_.and.getURIofQName(fx,str_vs(fx%name))==invalidNS) then + ! no namespace was found for the current element, we must be + ! closing inside a DOM entity. + ! Record it as having an empty URI + call endElement_handler("", & + getlocalNameofQName(str_vs(fx%name)), & + str_vs(fx%name)) + elseif (namespaces_) then + ! Normal state of affairs + call endElement_handler(getURIofQName(fx, str_vs(fx%name)), & + getlocalnameofQName(str_vs(fx%name)), & + str_vs(fx%name)) + else + ! Non-namespace-aware processing: + call endElement_handler("", "", & + str_vs(fx%name)) + endif + if (fx%state==ST_STOP) return + endif + if (namespaces_) then + call checkEndNamespaces(fx%nsDict, len(fx%elstack), & + endPrefixMapping_handler) + if (fx%state==ST_STOP) return + endif + end subroutine close_tag + + subroutine add_entity + !Parameter or General Entity? + logical :: wfc + wfc = fb%f(1)%pe.or.inExtSubset + if (pe) then + !Does entity with this name exist? + if (.not.existing_entity(fx%xds%PEList, str_vs(fx%name))) then + ! Internal or external? + if (associated(fx%attname)) then ! it's internal + call register_internal_PE(fx%xds, & + name=str_vs(fx%name), text=str_vs(fx%attname), & + wfc=wfc, baseURI=copyURI(fb%f(1)%baseURI)) + ! FIXME need to expand value here before reporting ... + if (present(internalEntityDecl_handler)) then + call internalEntityDecl_handler('%'//str_vs(fx%name), str_vs(fx%attname)) + if (fx%state==ST_STOP) return + endif + else ! PE can't have Ndata declaration + URIref => parseURI(str_vs(fx%systemId)) + if (.not.associated(URIref)) then + call add_error(fx%error_stack, "Invalid URI specified for SYSTEM") + elseif (hasFragment(URIref)) then + call add_error(fx%error_stack, "Fragment not permitted on SYSTEM URI") + call destroyURI(URIref) + else + newURI => rebaseURI(fb%f(1)%baseURI, URIref) + call destroyURI(URIref) + if (associated(fx%publicId)) then + call register_external_PE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + wfc=wfc, baseURI=newURI) + if (present(externalEntityDecl_handler)) & + call externalEntityDecl_handler('%'//str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId)) + else + call register_external_PE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), & + wfc=wfc, baseURI=newURI) + if (present(externalEntityDecl_handler)) & + call externalEntityDecl_handler('%'//str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId="") + endif + endif + endif + ! else we ignore it + endif + else !It's a general entity + if (.not.existing_entity(fx%xds%entityList, str_vs(fx%name))) then + ! Internal or external? + if (associated(fx%attname)) then ! it's internal + call register_internal_GE(fx%xds, name=str_vs(fx%name), & + text=str_vs(fx%attname), & + wfc=wfc, baseURI=copyURI(fb%f(1)%baseURI)) + if (present(internalEntityDecl_handler)) then + call internalEntityDecl_handler(str_vs(fx%name),& + str_vs(fx%attname)) + if (fx%state==ST_STOP) return + endif + else + URIref => parseURI(str_vs(fx%systemId)) + if (.not.associated(URIref)) then + call add_error(fx%error_stack, "Invalid URI specified for SYSTEM") + elseif (hasFragment(URIref)) then + call add_error(fx%error_stack, "Fragment not permitted on SYSTEM URI") + call destroyURI(URIref) + else + newURI => rebaseURI(fb%f(1)%baseURI, URIref) + call destroyURI(URIref) + if (associated(fx%publicId).and.associated(fx%Ndata)) then + call register_external_GE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + notation=str_vs(fx%Ndata), & + wfc=wfc, baseURI=newURI) + if (present(unparsedEntityDecl_handler)) & + call unparsedEntityDecl_handler(str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + notation=str_vs(fx%Ndata)) + elseif (associated(fx%Ndata)) then + call register_external_GE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), notation=str_vs(fx%Ndata), & + wfc=wfc, baseURI=newURI) + if (present(unparsedEntityDecl_handler)) & + call unparsedEntityDecl_handler(str_vs(fx%name), publicId="", & + systemId=str_vs(fx%systemId), notation=str_vs(fx%Ndata)) + elseif (associated(fx%publicId)) then + call register_external_GE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + wfc=wfc, baseURI=newURI) + if (present(externalEntityDecl_handler)) & + call externalEntityDecl_handler(str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId)) + else + call register_external_GE(fx%xds, name=str_vs(fx%name), & + systemId=str_vs(fx%systemId), wfc=wfc, baseURI=newURI) + if (present(externalEntityDecl_handler)) & + call externalEntityDecl_handler(str_vs(fx%name), & + systemId=str_vs(fx%systemId), publicId="") + endif + endif + endif + endif + endif + end subroutine add_entity + + subroutine getDefaultAttributes(el, dict) + type(element_t), pointer :: el + type(dictionary_t), intent(inout) :: dict + + type(attribute_t), pointer :: att + integer :: i, ind + character, pointer :: attValue(:) + + do i = 1, size(el%attlist%list) + att => el%attlist%list(i) + call get_att_index_pointer(dict, str_vs(att%name), ind, attValue) + if (.not.associated(attValue)) then + if (att%attDefault==ATT_DEFAULT & + .or.att%attDefault==ATT_FIXED) then + call add_item_to_dict(dict, & + str_vs(att%name), str_vs(att%default), & + specified=.false., declared=.true.) + endif + endif + end do + end subroutine getDefaultAttributes + + subroutine checkAttributes(el, dict) + type(element_t), pointer :: el + type(dictionary_t), intent(inout) :: dict + + integer :: i, j + + type(attribute_t), pointer :: att + type(string_list) :: s_list + character, pointer :: attValue(:), s(:) + + integer :: ind + logical, allocatable :: attributesLeft(:) + allocate(attributesLeft(getLength(dict))) + attributesLeft = .true. + + do i = 1, size(el%attlist%list) + att => el%attlist%list(i) + call get_att_index_pointer(dict, str_vs(att%name), ind, attValue) + if (associated(attValue)) attributesLeft(ind) = .false. + select case(att%attDefault) + case (ATT_REQUIRED, ATT_IMPLIED, ATT_DEFAULT) + if (.not.associated(attValue)) then + if (att%attDefault==ATT_REQUIRED) then + ! Validity Constraint: Required Attribute + call add_error(fx%error_stack, & + "REQUIRED attribute "//str_vs(att%name)//" not present") + return + elseif (att%attDefault==ATT_DEFAULT) then + if (fx%xds%standalone.and..not.att%internal) then + ! VC: Standalone document declaration" + call add_error(fx%error_stack, & + "Externally-specifid default attribute used in non-standalone document") + return + else + call add_item_to_dict(dict, & + str_vs(att%name), str_vs(att%default), & + specified=.false., declared=.true.) + endif + endif + else + select case(att%attType) + case (ATT_ID) + ! VC: ID + if (namespaces_) then + nameOk = checkNCName(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkName(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type ID must have a value which is an XML Name") + return + endif + if (registered_string(id_list, str_vs(attValue))) then + call add_error(fx%error_stack, & + "Cannot declare the same ID twice") + return + endif + call add_string(id_list, str_vs(attValue)) + call setIsId(dict, ind, .true.) + ! We don't need to check for duplicate ID & xml:ids on the same + ! element - if we are validating we'd already have noticed, + ! if we're not, it's not possible + case (ATT_IDREF) + ! VC: IDREF + if (namespaces_) then + nameOk = checkNCName(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkName(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type IDREF must have a value which is an XML Name") + return + endif + ! FIXME in a namespaced document they must match QName + if (.not.registered_string(idref_list, str_vs(attValue))) & + call add_string(idref_list, str_vs(attValue)) + case (ATT_IDREFS) + ! VC: IDREF + if (namespaces_) then + nameOk = checkNCNames(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkNames(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type IDREFS must have a value which contains only XML Names") + return + endif + call tokenize_and_add_strings(idref_list, str_vs(attValue), .true.) + case (ATT_ENTITY) + ! VC: Entity Name + if (namespaces_) then + nameOk = checkNCName(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkName(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type ENTITY must have a value which is an XML Name") + return + endif + ent => getEntityByName(fx%xds%entityList, str_vs(attValue)) + if (associated(ent)) then + if (.not.is_unparsed_entity(ent)) then + ! Validity Constraint: Entity Name + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as ENTITY refers to parsed entity") + return + endif + else + ! Validity Constraint: Entity Name + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as ENTITY refers to non-existent entity") + return + endif + case (ATT_ENTITIES) + ! VC: Entity Name + if (namespaces_) then + nameOk = checkNCNames(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkNames(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type ENTITIES must have a value which contains only XML Names") + return + endif + s_list = tokenize_to_string_list(str_vs(attValue)) + do j = 1, size(s_list%list) + s => s_list%list(j)%s + ent => getEntityByName(fx%xds%entityList, str_vs(s)) + if (associated(ent)) then + if (.not.is_unparsed_entity(ent)) then + ! Validity Constraint: Entity Name + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as ENTITIES refers to parsed entity "& + //str_vs(s)) + call destroy_string_list(s_list) + return + endif + else + ! Validity Constraint: Entity Name + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as ENTITIES refers to non-existent entity "& + //str_vs(s)) + call destroy_string_list(s_list) + return + endif + enddo + call destroy_string_list(s_list) + case (ATT_NMTOKEN) + ! VC Name Token + if (.not.checkNmtoken(str_vs(attValue), fx%xds%xml_version)) then + call add_error(fx%error_stack, & + "Attributes of type NMTOKEN must have a value which is a NMTOKEN") + return + endif + case (ATT_NMTOKENS) + ! VC: Name Token + if (.not.checkNmtokens(str_vs(attValue), fx%xds%xml_version)) then + call add_error(fx%error_stack, & + "Attributes of type NMTOKENS must have a value which contain only NMTOKENs") + return + endif + case (ATT_NOTATION) + ! VC: Notation Attributes + if (namespaces_) then + nameOk = checkNCName(str_vs(attValue), fx%xds%xml_version) + else + nameOk = checkName(str_vs(attValue), fx%xds%xml_version) + endif + if (.not.nameOk) then + call add_error(fx%error_stack, & + "Attributes of type NOTATION must have a value which is an XML Name") + return + endif + if (.not.notation_exists(fx%nlist, str_vs(attValue))) then + ! Validity Constraint: Notation Attributes + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" declared as NOTATION refers to non-existent notation "& + //str_vs(attValue)) + return + endif + if (att%attDefault==ATT_REQUIRED) then + if (.not.registered_string(att%enumerations, str_vs(attValue))) & + call add_error(fx%error_stack, & + "NOTATION attribute is not among declared values.") + endif + case (ATT_ENUM) + ! VC: Notation Attributes + if (.not.checkNmtoken(str_vs(attValue), fx%xds%xml_version)) then + call add_error(fx%error_stack, & + "Attributes of type ENUM must have a value which is an NMTOKEN") + return + endif + if (.not.registered_string(att%enumerations, str_vs(attValue))) then + ! Validity Constraint: Enumeration + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as ENUM refers to undeclared enumeration "& + //str_vs(attValue)) + return + endif + end select + endif + case (ATT_FIXED) + if (associated(attValue)) then + if (str_vs(att%default)//"x"/=str_vs(attValue)//"x") then + ! Validity Constraint: Fixed Attribute Default + call add_error(fx%error_stack, & + "FIXED attribute has wrong value") + return + endif + else + if (fx%xds%standalone.and..not.att%internal) then + ! VC: Standalone document declaration" + call add_error(fx%error_stack, & + "Externally-specified default attribute used in non-standalone document") + return + else + call add_item_to_dict(dict, & + str_vs(att%name), str_vs(att%default), & + specified=.false., declared=.true.) + endif + endif + end select + enddo + + if (any(attributesLeft)) & + call add_error(fx%error_stack, & + "Undeclared attributes forbidden") + + end subroutine checkAttributes + + subroutine checkXMLAttributes + integer :: ind + character, pointer :: attValue(:) + ! These must all be done with the name of the attribute, + ! not the nsURI/localname pair, in case we are + ! processing for a non-namespace aware application + if (has_key(fx%attributes, 'xml:space')) then + if (get_value(fx%attributes, 'xml:space')/='default' & + .and. get_value(fx%attributes, 'xml:space')/='preserve') then + call add_error(fx%error_stack, 'Illegal value of xml:space attribute') + return + endif + endif + call get_att_index_pointer(fx%attributes, "xml:id", ind, attValue) + if (associated(attValue)) then + ! Per xml:id spec, NCName even in non-namespace aware document + if (.not.checkNCName(str_vs(attValue), fx%xds%xml_version)) then + call add_error(fx%error_stack, & + "xml:id attributes must have values which are NCNames") + return + elseif (registered_string(id_list, str_vs(attValue))) then + call add_error(fx%error_stack, & + "xml:id attributes must be unique within a document") + return + endif + call add_string(id_list, str_vs(attValue)) + call setIsId(fx%attributes, ind, .true.) + endif + if (has_key(fx%attributes, "xml:base")) then + URIref => parseURI(get_value(fx%attributes,"xml:base")) + if (.not.associated(URIref)) then + call add_error(fx%error_stack, & + "Invalid URI reference specified for xml:base attribute") + else + call destroyURI(URIref) + endif + endif + !if (has_key(fx%attributes, 'xml:lang')) then + ! We never care about this at the SAX level. + !endif + end subroutine checkXMLAttributes + + subroutine endDTDchecks + type(element_t), pointer :: el + type(attribute_t), pointer :: att + type(entity_t), pointer :: ent + type(string_list) :: s_list + character, pointer :: s(:) + integer :: i, j, k + + if (present(FoX_endDTD_handler)) then + fx%xds_used = .true. + call FoX_endDTD_handler(fx%xds) + endif + if (present(endDTD_handler)) then + call endDTD_handler() + if (fx%state==ST_STOP) return + endif + ! Check that all notations used have been declared: + if (validCheck) then + do i = 1, size(fx%xds%entityList) + ent => getEntityByIndex(fx%xds%entityList, i) + if (str_vs(ent%notation)/="" & + .and..not.notation_exists(fx%nlist, str_vs(ent%notation))) then + ! Validity Constraint: Notation Declared + call add_error(fx%error_stack, "Attempt to use undeclared notation") + exit + endif + enddo + validLoop: do i = 1, size(fx%xds%element_list%list) + el => fx%xds%element_list%list(i) + do j = 1, size(el%attlist%list) + att => el%attlist%list(j) + ! For NOTATION, need to check enumerated as well as default ... + if (att%attType==ATT_NOTATION) then + do k = 1, size(att%enumerations%list) + s => att%enumerations%list(k)%s + if (.not.notation_exists(fx%nlist, str_vs(s))) then + ! Validity Constraint: Notation Attributes + call add_error(fx%error_stack, & + "Enumerated NOTATION in "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" refers to non-existent notation") + call destroy(s_list) + exit validLoop + endif + enddo + if (associated(att%default)) then + s_list = tokenize_to_string_list(str_vs(att%default)) + do k = 1, size(s_list%list) + s => s_list%list(k)%s + if (.not.notation_exists(fx%nlist, str_vs(s))) then + ! Validity Constraint: Notation Attributes + call add_error(fx%error_stack, & + "Attribute "//str_vs(att%name) & + //" of element "//str_vs(el%name) & + //" declared as NOTATION refers to non-existent notation") + call destroy(s_list) + exit validLoop + endif + enddo + call destroy(s_list) + endif + endif + enddo + enddo validLoop + endif + end subroutine endDTDchecks + + subroutine checkIdRefs + integer :: i + character, pointer :: s(:) + do i = 1, size(idRef_list%list) + s => idRef_list%list(i)%s + if (.not.registered_string(id_list, str_vs(s))) then + call add_error(fx%error_stack, & + "Reference to undeclared ID "//str_vs(s)) + return + endif + enddo + end subroutine checkIdRefs + end subroutine sax_parse + + + subroutine sax_error(fx, error_handler) + type(sax_parser_t), intent(inout) :: fx + optional :: error_handler + interface + subroutine error_handler(msg) + character(len=*), intent(in) :: msg + end subroutine error_handler + end interface + + character, dimension(:), pointer :: errmsg + + integer :: i, m, n, n_err + n = size(fx%error_stack%stack) + n_err = n + + do i = 1, n + n_err = n_err + size(fx%error_stack%stack(i)%msg) ! + spaces + size of entityref + enddo + allocate(errmsg(n_err)) + errmsg = '' + n = 1 + do i = 1, size(fx%error_stack%stack) + m = size(fx%error_stack%stack(i)%msg) + errmsg(n:n+m-1) = fx%error_stack%stack(i)%msg + errmsg(n+m:n+m) = " " + n = n + m + 1 + enddo + ! FIXME put location information in here + if (present(error_handler)) then + call error_handler(str_vs(errmsg)) + deallocate(errmsg) + if (fx%state==ST_STOP) return + else + call FoX_error(str_vs(errmsg)) + endif + + end subroutine sax_error + + pure function URIlength(fx, qname) result(l_u) + type(sax_parser_t), intent(in) :: fx + character(len=*), intent(in) :: qName + integer :: l_u + integer :: n + n = index(QName, ':') + if (n > 0) then + l_u = len(getnamespaceURI(fx%nsDict, QName(1:n-1))) + else + l_u = len(getnamespaceURI(fx%nsDict)) + endif + end function URIlength + + pure function getURIofQName(fx, qname) result(URI) + type(sax_parser_t), intent(in) :: fx + character(len=*), intent(in) :: qName + character(len=URIlength(fx, qname)) :: URI + + integer :: n + n = index(QName, ':') + if (n > 0) then + URI = getnamespaceURI(fx%nsDict, QName(1:n-1)) + else + URI = getnamespaceURI(fx%nsDict) + endif + + end function getURIofQName + + pure function getLocalNameofQName(qname) result(localName) + character(len=*), intent(in) :: qName + character(len=len(QName)-index(QName,':')) :: localName + + localName = QName(index(QName,':')+1:) + end function getLocalNameofQName +#endif + +end module m_sax_parser diff --git a/src/xml/sax/m_sax_reader.F90 b/src/xml/sax/m_sax_reader.F90 new file mode 100644 index 000000000..4693ff7f4 --- /dev/null +++ b/src/xml/sax/m_sax_reader.F90 @@ -0,0 +1,402 @@ +module m_sax_reader +#ifndef DUMMYLIB + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc, vs_vs_alloc + use fox_m_fsys_format, only: operator(//) + use m_common_charset, only: XML1_0 + use m_common_error, only: error_stack, FoX_error, in_error, add_error + use m_common_io, only: setup_io, get_unit, io_err + + use FoX_utils, only: URI, parseURI, copyURI, destroyURI, & + hasScheme, getScheme, getPath + + use m_sax_xml_source, only: xml_source_t, & + get_char_from_file, push_file_chars, parse_declaration + + implicit none + private + + type file_buffer_t + !FIXME private + type(xml_source_t), pointer :: f(:) => null() + logical :: standalone = .false. + integer :: xml_version = XML1_0 + end type file_buffer_t + + public :: file_buffer_t + public :: line + public :: column + + public :: open_file + public :: close_file + + public :: open_new_file + + public :: push_chars + + public :: get_character + public :: get_all_characters + + public :: open_new_string + public :: pop_buffer_stack + + public :: parse_xml_declaration + public :: parse_text_declaration + + public :: reading_main_file + public :: reading_first_entity + +contains + + subroutine open_file(fb, iostat, file, lun, string, es) + type(file_buffer_t), intent(out) :: fb + character(len=*), intent(in), optional :: file + integer, intent(out) :: iostat + integer, intent(in), optional :: lun + character(len=*), intent(in), optional :: string + type(error_stack), intent(inout) :: es + + type(URI), pointer :: fileURI + + iostat = 0 + + call setup_io() + if (present(string)) then + if (present(file)) then + call FoX_error("Cannot specify both file and string input to open_xml") + elseif (present(lun)) then + call FoX_error("Cannot specify lun for string input to open_xml") + endif + fileURI => parseURI("") + call open_new_string(fb, string, "", baseURI=fileURI) + else + fileURI => parseURI(file) + if (.not.associated(fileURI)) then + call add_error(es, "Could not open file "//file//" - not a valid URI") + return + endif + call open_new_file(fb, fileURI, iostat, lun) + endif + call destroyURI(fileURI) + + end subroutine open_file + + + subroutine open_new_file(fb, baseURI, iostat, lun, pe) + type(file_buffer_t), intent(inout) :: fb + integer, intent(out) :: iostat + type(URI), pointer :: baseURI + integer, intent(in), optional :: lun + logical, intent(in), optional :: pe + + integer :: i + type(xml_source_t) :: f + type(xml_source_t), pointer :: temp(:) + logical :: pe_ + + if (present(pe)) then + pe_ = pe + else + pe_ = .false. + endif + + if (hasScheme(baseURI)) then + if (getScheme(baseURI)/="file") then + iostat = io_err + return + endif + endif + + call open_actual_file(f, getPath(baseURI), iostat, lun) + if (iostat==0) then + if (.not.associated(fb%f)) allocate(fb%f(0)) + ! First file + + temp => fb%f + allocate(fb%f(size(temp)+1)) + do i = 1, size(temp) + fb%f(i+1)%lun = temp(i)%lun + fb%f(i+1)%xml_version = temp(i)%xml_version + fb%f(i+1)%encoding => temp(i)%encoding + fb%f(i+1)%filename => temp(i)%filename + fb%f(i+1)%line = temp(i)%line + fb%f(i+1)%col = temp(i)%col + fb%f(i+1)%startChar = temp(i)%startChar + fb%f(i+1)%next_chars => temp(i)%next_chars + fb%f(i+1)%input_string => temp(i)%input_string + fb%f(i+1)%baseURI => temp(i)%baseURI + fb%f(i+1)%pe = temp(i)%pe + enddo + deallocate(temp) + fb%f(1)%lun = f%lun + fb%f(1)%filename => f%filename + if (pe_) then + fb%f(1)%next_chars => vs_str_alloc(" ") + else + fb%f(1)%next_chars => vs_str_alloc("") + endif + fb%f(1)%pe = pe_ + fb%f(1)%baseURI => copyURI(baseURI) + endif + + end subroutine open_new_file + + subroutine open_actual_file(f, file, iostat, lun) + type(xml_source_t), intent(out) :: f + character(len=*), intent(in) :: file + integer, intent(out) :: iostat + integer, intent(in), optional :: lun + + if (present(lun)) then + f%lun = lun + else + call get_unit(f%lun, iostat) + if (iostat/=0) return + endif + open(unit=f%lun, file=file, form="formatted", status="old", & + action="read", position="rewind", iostat=iostat) + if (iostat/=0) return + f%filename => vs_str_alloc(file) + + end subroutine open_actual_file + + subroutine close_file(fb) + type(file_buffer_t), intent(inout) :: fb + + integer :: i + + do i = 1, size(fb%f) + call close_actual_file(fb%f(i)) + enddo + + deallocate(fb%f) + + end subroutine close_file + + + subroutine close_actual_file(f) + type(xml_source_t), intent(inout) :: f + + deallocate(f%filename) + + if (f%lun>0) then + close(f%lun) + else + deallocate(f%input_string%s) + deallocate(f%input_string) + endif + + if (associated(f%encoding)) deallocate(f%encoding) + f%line = 0 + f%col = 0 + deallocate(f%next_chars) + call destroyURI(f%baseURI) + end subroutine close_actual_file + + + subroutine open_new_string(fb, string, name, baseURI, pe) + type(file_buffer_t), intent(inout) :: fb + character(len=*), intent(in) :: string + character(len=*), intent(in) :: name + type(URI), pointer :: baseURI + logical, intent(in), optional :: pe + + integer :: i + type(xml_source_t), pointer :: temp(:) + logical :: pe_ + + if (present(pe)) then + pe_ = pe + else + pe_ = .false. + endif + + if (.not.associated(fb%f)) allocate(fb%f(0)) + + temp => fb%f + allocate(fb%f(size(temp)+1)) + do i = 1, size(temp) + fb%f(i+1)%lun = temp(i)%lun + fb%f(i+1)%xml_version = temp(i)%xml_version + fb%f(i+1)%encoding => temp(i)%encoding + fb%f(i+1)%filename => temp(i)%filename + fb%f(i+1)%line = temp(i)%line + fb%f(i+1)%col = temp(i)%col + fb%f(i+1)%startChar = temp(i)%startChar + fb%f(i+1)%next_chars => temp(i)%next_chars + fb%f(i+1)%input_string => temp(i)%input_string + fb%f(i+1)%baseURI => temp(i)%baseURI + fb%f(i+1)%pe = temp(i)%pe + enddo + deallocate(temp) + + allocate(fb%f(1)%input_string) + fb%f(1)%filename => vs_str_alloc(name) + fb%f(1)%input_string%s => vs_str_alloc(string) + if (pe_) then + fb%f(1)%next_chars => vs_str_alloc(" ") + else + fb%f(1)%next_chars => vs_str_alloc("") + endif + fb%f(1)%pe = pe_ + if (associated(baseURI)) then + fb%f(1)%baseURI => copyURI(baseURI) + else + fb%f(1)%baseURI => copyURI(fb%f(2)%baseURI) + endif + + end subroutine open_new_string + + subroutine pop_buffer_stack(fb) + type(file_buffer_t), intent(inout) :: fb + + integer :: i + type(xml_source_t), pointer :: temp(:) + + call close_actual_file(fb%f(1)) + + temp => fb%f + allocate(fb%f(size(temp)-1)) + do i = 1, size(temp)-1 + fb%f(i)%lun = temp(i+1)%lun + fb%f(i)%xml_version = temp(i+1)%xml_version + fb%f(i)%encoding => temp(i+1)%encoding + fb%f(i)%filename => temp(i+1)%filename + fb%f(i)%line = temp(i+1)%line + fb%f(i)%col = temp(i+1)%col + fb%f(i)%startChar = temp(i+1)%startChar + fb%f(i)%next_chars => temp(i+1)%next_chars + fb%f(i)%input_string => temp(i+1)%input_string + fb%f(i)%baseURI => temp(i+1)%baseURI + fb%f(i)%pe = temp(i+1)%pe + enddo + deallocate(temp) + + end subroutine pop_buffer_stack + + + subroutine push_chars(fb, s) + type(file_buffer_t), intent(inout) :: fb + character(len=*), intent(in) :: s + + call push_file_chars(fb%f(1), s) + + end subroutine push_chars + + function get_character(fb, eof, es) result(string) + type(file_buffer_t), intent(inout) :: fb + logical, intent(out) :: eof + type(error_stack), intent(inout) :: es + character(len=1) :: string + + type(xml_source_t), pointer :: f + character, pointer :: temp(:) + + f => fb%f(1) + + if (size(f%next_chars)>0) then + eof = .false. + string = f%next_chars(1) + if (size(f%next_chars)>1) then + temp => vs_str_alloc(str_vs(f%next_chars(2:))) + else + temp => vs_str_alloc("") + endif + deallocate(f%next_chars) + f%next_chars => temp + else + string = get_char_from_file(f, fb%xml_version, eof, es) + endif + + end function get_character + + function get_all_characters(fb, es) result(s) + type(file_buffer_t), intent(inout) :: fb + type(error_stack), intent(inout) :: es + character, pointer :: s(:) + + logical :: eof + character :: c + character, pointer :: temp(:) + + eof = .false. + s => vs_str_alloc("") + do while (.not.eof) + c = get_character(fb, eof, es) + if (eof.or.in_error(es)) return + temp => vs_str_alloc(str_vs(s)//c) + deallocate(s) + s => temp + enddo + end function get_all_characters + + function line(fb) result(n) + type(file_buffer_t), intent(in) :: fb + integer :: n + + n = fb%f(1)%line + end function line + + function column(fb) result(n) + type(file_buffer_t), intent(in) :: fb + integer :: n + + n = fb%f(1)%col + end function column + + subroutine parse_xml_declaration(fb, xv, enc, sa, es) + type(file_buffer_t), intent(inout) :: fb + integer, intent(out) :: xv + character, pointer :: enc(:) + logical, intent(out) :: sa + type(error_stack), intent(inout) :: es + + logical :: eof + + call parse_declaration(fb%f(1), eof, es, sa) + if (eof.or.in_error(es)) then + call add_error(es, "Error parsing XML declaration") + else + fb%xml_version = fb%f(1)%xml_version + xv = fb%xml_version + enc => vs_vs_alloc(fb%f(1)%encoding) + endif + end subroutine parse_xml_declaration + + subroutine parse_text_declaration(fb, es) + type(file_buffer_t), intent(inout) :: fb + type(error_stack), intent(inout) :: es + + logical :: eof + integer :: xv + + xv = fb%f(size(fb%f))%xml_version + + call parse_declaration(fb%f(1), eof, es) + if (in_error(es)) then + call add_error(es, "Error parsing text declaration") + return + elseif (xv==XML1_0.and.fb%f(1)%xml_version/=XML1_0) then + call add_error(es, "XML 1.0 document cannot reference entities with higher version numbers") + return + endif + + end subroutine parse_text_declaration + + + function reading_main_file(fb) result(p) + type(file_buffer_t), intent(in) :: fb + logical :: p + + p = (size(fb%f)==1) + end function reading_main_file + + function reading_first_entity(fb) result(p) + type(file_buffer_t), intent(in) :: fb + logical :: p + + p = (size(fb%f)==2) + end function reading_first_entity +#endif + +end module m_sax_reader diff --git a/src/xml/sax/m_sax_tokenizer.F90 b/src/xml/sax/m_sax_tokenizer.F90 new file mode 100644 index 000000000..97824fd7a --- /dev/null +++ b/src/xml/sax/m_sax_tokenizer.F90 @@ -0,0 +1,1185 @@ +module m_sax_tokenizer + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: vs_str, str_vs, vs_str_alloc + use m_common_charset, only: XML_WHITESPACE, & + upperCase, isInitialNameChar + use m_common_error, only: add_error, in_error + use m_common_entities, only: entity_t, existing_entity, & + expand_entity_text, expand_char_entity, & + add_internal_entity, pop_entity_list, getEntityByName + use m_common_namecheck, only: checkName, checkCharacterEntityReference + + use m_sax_reader, only: file_buffer_t, open_new_file, pop_buffer_stack, & + push_chars, get_character, get_all_characters, & + parse_text_declaration + use m_sax_types ! everything, really +#ifdef PGF90 + use fox_m_utils_uri, only: URI +#endif + + implicit none + private + + public :: sax_tokenize + public :: normalize_attribute_text + public :: expand_pe_text + +contains + + subroutine sax_tokenize(fx, fb, eof) + type(sax_parser_t), intent(inout) :: fx + type(file_buffer_t), intent(inout) :: fb + logical, intent(out) :: eof + + character :: c, q + integer :: xv, phrase + logical :: firstChar, ws_discard + character, pointer :: tempString(:) + + xv = fx%xds%xml_version + + if (associated(fx%token)) deallocate(fx%token) + fx%token => vs_str_alloc("") + if (fx%nextTokenType/=TOK_NULL) then + eof = .false. + fx%tokenType = fx%nextTokenType + fx%nextTokenType = TOK_NULL + return + endif + fx%tokentype = TOK_NULL + + q = " " + phrase = 0 + firstChar = .true. + ws_discard = .false. + do + c = get_character(fb, eof, fx%error_stack) + if (eof) then + if (fx%state==ST_CHAR_IN_CONTENT) then + if (phrase==1) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(fx%token)//"]") + deallocate(tempString) + elseif (phrase==2) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(fx%token)//"]]") + deallocate(tempString) + endif + fx%tokenType = TOK_CHAR + endif + if (fx%tokenType/=TOK_NULL) then + ! make sure we pass back this token before eof'ing + eof = .false. + return + endif + endif + if (eof.or.in_error(fx%error_stack)) return + if (fx%inIntSubset) then + tempString => fx%xds%intSubset + fx%xds%intSubset => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + + select case (fx%state) + case (ST_MISC) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c, XML_WHITESPACE)>0) then + if (c=="<") then + ws_discard = .false. + else + call add_error(fx%error_stack, "Unexpected character found outside content") + endif + endif + else + if (c=="?") then + fx%tokenType = TOK_PI_TAG + elseif (c=="!") then + fx%tokenType = TOK_BANG_TAG + elseif (isInitialNameChar(c, xv)) then + call push_chars(fb, c) + fx%tokenType = TOK_OPEN_TAG + else + call add_error(fx%error_stack, "Unexpected character after <") + endif + endif + + case (ST_BANG_TAG) + if (firstChar) then + if (c=="-") then + phrase = 1 + elseif (c=="[") then + fx%tokenType = TOK_OPEN_SB + elseif (verify(c,upperCase)==0) then + deallocate(fx%token) + fx%token => vs_str_alloc(c) + else + call add_error(fx%error_stack, "Unexpected character after 0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + call push_chars(fb, c) + fx%tokenType = TOK_NAME + endif + + case (ST_START_PI) + ! grab until whitespace or ? + if (verify(c, XML_WHITESPACE//"?")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + fx%tokenType = TOK_NAME + if (c=="?") call push_chars(fb, c) + endif + + case (ST_PI_CONTENTS) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c, XML_WHITESPACE)/=0) then + ws_discard = .false. + else + cycle + endif + endif + if (phrase==1) then + if (c==">") then + fx%tokenType = TOK_CHAR + fx%nextTokenType = TOK_PI_END + elseif (c=="?") then + ! The last ? didn't mean anything, but this one might. + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"?") + deallocate(tempString) + else + phrase = 0 + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"?"//c) + deallocate(tempString) + endif + elseif (c=="?") then + phrase = 1 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + + case (ST_START_COMMENT) + select case(phrase) + case (0) + if (c=="-") then + phrase = 1 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + case (1) + if (c=="-") then + phrase = 2 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"-"//c) + deallocate(tempString) + phrase = 0 + endif + case (2) + if (c==">") then + fx%tokenType = TOK_CHAR + fx%nextTokenType = TOK_COMMENT_END + exit + else + call add_error(fx%error_stack, & + "Expecting > after -- inside a comment.") + endif + end select + + case (ST_START_TAG) + ! grab until whitespace or /, > + if (verify(c, XML_WHITESPACE//"/>")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + fx%tokenType = TOK_NAME + if (c==">") then + fx%nextTokenType = TOK_END_TAG + else + call push_chars(fb, c) + endif + endif + + case (ST_START_CDATA_DECLARATION) + if (firstChar) then + if (verify(c, XML_WHITESPACE)==0) then + call add_error(fx%error_stack, & + "Whitespace not allowed around CDATA in section declaration") + else + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + else + if (verify(c, XML_WHITESPACE)==0) then + call add_error(fx%error_stack, & + "Whitespace not allowed around CDATA in section declaration") + elseif (c=="[") then + fx%tokenType = TOK_NAME + if (c=="[") fx%nextTokenType = TOK_OPEN_SB + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + endif + + + case (ST_CDATA_CONTENTS) + select case(phrase) + case (0) + if (c=="]") then + phrase = 1 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + case (1) + if (c=="]") then + phrase = 2 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]"//c) + deallocate(tempString) + phrase = 0 + endif + case (2) + if (c==">") then + fx%tokenType = TOK_CHAR + fx%nextTokenType = TOK_SECTION_END + elseif (c=="]") then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]") + deallocate(tempString) + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]]"//c) + deallocate(tempString) + phrase = 0 + endif + end select + + case (ST_IN_TAG) + if (firstChar) then + if (verify(c,XML_WHITESPACE//"/>")>0) then + call add_error(fx%error_stack, & + "Whitespace required inside tag") + exit + endif + ws_discard = .true. + endif + if (ws_discard) then + if (verify(c,XML_WHITESPACE)>0) then + if (c==">") then + fx%tokenType = TOK_END_TAG + elseif (c=="/") then + phrase = 1 + ws_discard = .false. + else + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + endif + else + if (phrase==1) then + if (c==">") then + fx%tokenType = TOK_END_TAG_CLOSE + else + call add_error(fx%error_stack, & + "Unexpected character after / in element tag") + exit + endif + else + if (verify(c,XML_WHITESPACE//"=/>")==0) then + fx%tokenType = TOK_NAME + if (c=="=") then + fx%nextTokenType = TOK_EQUALS + elseif (c==">") then + fx%nextTokenType = TOK_END_TAG + else + call push_chars(fb, c) + endif + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + endif + endif + + case (ST_ATT_NAME) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c,XML_WHITESPACE)>0) then + if (c=="=") then + fx%tokenType = TOK_EQUALS + else + call add_error(fx%error_stack, & + "Unexpected character in element tag, expected =") + endif + endif + endif + + case (ST_ATT_EQUALS) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c,XML_WHITESPACE)>0) then + if (verify(c,"'""")==0) then + q = c + ws_discard = .false. + else + call add_error(fx%error_stack, "Expecting "" or '") + endif + endif + else + if (c==q) then + fx%tokenType = TOK_CHAR + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + endif + + case (ST_CHAR_IN_CONTENT) + if (c=="<".or.c=="&") then + if (phrase==1) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]") + deallocate(tempString) + elseif (phrase==2) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]]") + deallocate(tempString) + endif + fx%tokenType = TOK_CHAR + if (c=="<") then + call push_chars(fb, c) + elseif (c=="&") then + fx%nextTokenType = TOK_ENTITY + endif + elseif (c=="]") then + if (phrase==0) then + phrase = 1 + elseif (phrase==1) then + phrase = 2 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]") + deallocate(tempString) + endif + elseif (c==">") then + if (phrase==1) then + phrase = 0 + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]>") + deallocate(tempString) + elseif (phrase==2) then + call add_error(fx%error_stack, "]]> forbidden in character context") + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//">") + deallocate(tempString) + endif + elseif (phrase==1) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]"//c) + deallocate(tempString) + elseif (phrase==2) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"]]"//c) + deallocate(tempString) + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + + case (ST_TAG_IN_CONTENT) + if (phrase==0) then + if (c=="<") then + phrase = 1 + ws_discard = .false. + elseif (c=="&") then + fx%tokenType = TOK_ENTITY + else + call add_error(fx%error_stack, "Unexpected character found in content") + endif + elseif (phrase==1) then + if (c=="?") then + fx%tokenType = TOK_PI_TAG + elseif (c=="!") then + fx%tokenType = TOK_BANG_TAG + elseif (c=="/") then + fx%tokenType = TOK_CLOSE_TAG + elseif (isInitialNameChar(c, xv)) then + call push_chars(fb, c) + fx%tokenType = TOK_OPEN_TAG + else + call add_error(fx%error_stack, "Unexpected character after <") + endif + endif + + case (ST_START_ENTITY) + if (verify(c,XML_WHITESPACE//";")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + elseif (c==";") then + fx%tokenType = TOK_NAME + else + call add_error(fx%error_stack, "Entity reference must be terminated with a ;") + endif + + case (ST_CLOSING_TAG) + if (verify(c,XML_WHITESPACE//">")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + fx%tokenType = TOK_NAME + if (c==">") fx%nextTokenType = TOK_END_TAG + endif + + case (ST_IN_CLOSING_TAG) + if (verify(c, XML_WHITESPACE)>0) then + if (c==">") then + fx%tokenType = TOK_END_TAG + else + call add_error(fx%error_stack, "Unexpected character - expecting >") + endif + endif + + case (ST_IN_DOCTYPE, ST_DOC_NAME, ST_DOC_SYSTEM, ST_DOC_PUBLIC, & + ST_DOC_DECL, ST_CLOSE_DOCTYPE) + if (firstChar) then + if (verify(c, XML_WHITESPACE)>0) then + if (c==">") then + fx%tokenType = TOK_END_TAG + elseif (c=="[") then + fx%tokenType = TOK_OPEN_SB + else + call add_error(fx%error_stack, & + "Missing whitespace in doctype delcaration.") + endif + else + ws_discard = .true. + endif + endif + if (ws_discard) then + if (verify(c, XML_WHITESPACE)>0) then + if (verify(c, "'""")==0) then + q = c + deallocate(fx%token) + fx%token => vs_str_alloc("") + ws_discard = .false. + elseif (c=="[") then + fx%tokenType = TOK_OPEN_SB + elseif (c==">") then + fx%tokenType = TOK_END_TAG + else + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + endif + else + if (q/=" ".and.c==q) then + fx%tokenType = TOK_CHAR + elseif (q==" ".and.verify(c, ">")==0.and.(fx%state==ST_IN_DOCTYPE)) then + fx%nextTokenType = TOK_END_TAG + fx%tokenType = TOK_NAME + elseif (q==" ".and.verify(c, XML_WHITESPACE)==0) then + call push_chars(fb, c) + fx%tokenType = TOK_NAME + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + endif + + case (ST_IN_SUBSET) + call tokenizeDTD + + case (ST_START_PE) + if (verify(c,XML_WHITESPACE//";")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + elseif (c==";") then + fx%tokenType = TOK_NAME + else + call add_error(fx%error_stack, "Entity reference must be terminated with a ;") + endif + + end select + + firstChar = .false. + if (fx%tokenType/=TOK_NULL) exit + enddo + + contains + + subroutine tokenizeDTD + + if (c=="%") then + if (fx%state_dtd==ST_DTD_START_COMMENT & + .or.fx%state_dtd==ST_DTD_START_PI & + .or.fx%state_dtd==ST_DTD_PI_CONTENTS & + .or.fx%state_dtd==ST_DTD_ENTITY & + .or.fx%state_dtd==ST_DTD_NOTATION_SYSTEM & + .or.fx%state_dtd==ST_DTD_NOTATION_PUBLIC & + .or.fx%state_dtd==ST_DTD_NOTATION_PUBLIC_2 & + .or.fx%state_dtd==ST_DTD_ENTITY_PUBLIC & + .or.fx%state_dtd==ST_DTD_ENTITY_SYSTEM) then + ! % is perfectly legitimate + continue + elseif (fx%state_dtd==ST_DTD_SUBSET) then + if (.not.fx%spaceBeforeEntity) then + fx%spaceBeforeEntity = .true. + call push_chars(fb, c) + c = " " + else + fx%spaceBeforeEntity = .false. + fx%tokenType = TOK_ENTITY + return + endif + elseif (fx%state_dtd==ST_DTD_ATTLIST_CONTENTS & + .and. q/="") then + ! We are inside a ATTLIST attvalue, so apparent PErefs arent. + continue + elseif (fx%inIntSubset) then + call add_error(fx%error_stack, & + "Parameter entity reference not permitted inside markup for internal subset") + return + elseif (fx%state_dtd==ST_DTD_ATTLIST_CONTENTS & + .or.fx%state_dtd==ST_DTD_ELEMENT_CONTENTS) then + if (.not.associated(fx%content)) then + ! content will not always be empty here; + ! if we have two PErefs bang next to each other. + fx%content => fx%token + fx%token => vs_str_alloc("") + endif + fx%tokenType = TOK_ENTITY + return + elseif (fx%state_dtd==ST_DTD_ENTITY_ID) then + ! % is ok if we are in the external subset + continue + else + if (.not.fx%spaceBeforeEntity) then + fx%spaceBeforeEntity = .true. + call push_chars(fb, c) + c = " " + else + fx%spaceBeforeEntity = .false. + fx%tokenType = TOK_ENTITY + return + endif + endif + endif + + select case(fx%state_dtd) + + case (ST_DTD_SUBSET) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c, XML_WHITESPACE)>0) then + if (c=="]") then + phrase = 1 + q = c + ws_discard = .false. + elseif (c=="<") then + phrase = 1 + q = c + ws_discard = .false. + else + call add_error(fx%error_stack, "Unexpected character found in document subset") + endif + endif + elseif (phrase==1) then + if (q=="<") then + if (c=="?") then + fx%tokenType = TOK_PI_TAG + elseif (c=="!") then + fx%tokenType = TOK_BANG_TAG + else + call add_error(fx%error_stack, "Unexpected character, expecting ! or ?") + endif + elseif (q=="]") then + if (c=="]") then + phrase = 2 + else + fx%tokenType = TOK_CLOSE_SB + call push_chars(fb, c) + if (fx%inIntSubset) then + tempString => fx%xds%intSubset + allocate(fx%xds%intSubset(size(tempString)-2)) + fx%xds%intSubset = tempString(:size(tempString)-2) + deallocate(tempString) + endif + endif + endif + elseif (phrase==2) then + if (c==">") then + fx%tokenType = TOK_SECTION_END + else + call add_error(fx%error_stack, "Unexpected character, expecting >") + endif + endif + + + case (ST_DTD_BANG_TAG) + if (firstChar) then + if (c=="-") then + phrase = 1 + elseif (c=="[") then + fx%tokenType = TOK_OPEN_SB + elseif (verify(c,upperCase)==0) then + deallocate(fx%token) + fx%token => vs_str_alloc(c) + else + call add_error(fx%error_stack, "Unexpected character after 0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + call push_chars(fb, c) + fx%tokenType = TOK_NAME + endif + + + case (ST_DTD_START_SECTION_DECL) + if (firstChar) then + ws_discard = .true. + endif + if (ws_discard) then + if (verify(c, XML_WHITESPACE)/=0) then + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + else + if (verify(c, XML_WHITESPACE//"[")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + fx%tokenType = TOK_NAME + if (c=="[") fx%nextTokenType = TOK_OPEN_SB + endif + endif + + case (ST_DTD_FINISH_SECTION_DECL) + if (verify(c, XML_WHITESPACE)>0) then + if (c/="[") then + call add_error(fx%error_stack, & + "Unexpected token found, expecting [") + else + fx%tokenType = TOK_OPEN_SB + endif + endif + + case (ST_DTD_IN_IGNORE_SECTION) + select case(phrase) + case (0) + if (c=="<".or.c=="]") then + phrase = 1 + q = c + endif + case (1) + if ((q=="<".and.c=="!").or.(q=="]".and.c=="]")) then + phrase = 2 + else + phrase = 0 + endif + case (2) + if (q=="<".and.c=="[") then + fx%tokenType = TOK_SECTION_START + elseif (q=="]".and.c==">") then + fx%tokenType = TOK_SECTION_END + else + phrase = 0 + endif + end select + + + case (ST_DTD_START_PI) + ! grab until whitespace or ? + if (verify(c, XML_WHITESPACE//"?")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + fx%tokenType = TOK_NAME + if (c=="?") call push_chars(fb, c) + endif + + case (ST_DTD_PI_CONTENTS) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c, XML_WHITESPACE)/=0) then + ws_discard = .false. + else + return + endif + endif + if (phrase==1) then + if (c==">") then + fx%tokenType = TOK_CHAR + fx%nextTokenType = TOK_PI_END + elseif (c=="?") then + ! The last ? didn't mean anything, but this one might. + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"?") + deallocate(tempString) + else + phrase = 0 + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"?"//c) + deallocate(tempString) + endif + elseif (c=="?") then + phrase = 1 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + + case (ST_DTD_START_COMMENT) + select case(phrase) + case (0) + if (c=="-") then + phrase = 1 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + case (1) + if (c=="-") then + phrase = 2 + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//"-"//c) + deallocate(tempString) + phrase = 0 + endif + case (2) + if (c==">") then + fx%tokenType = TOK_CHAR + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + fx%nextTokenType = TOK_COMMENT_END + else + call add_error(fx%error_stack, & + "Expecting > after -- inside a comment.") + endif + end select + + case (ST_DTD_ATTLIST, ST_DTD_ELEMENT, ST_DTD_ENTITY, & + ST_DTD_ENTITY_PE, ST_DTD_NOTATION) + if (firstChar) ws_discard = .true. + if (ws_discard) then + if (verify(c,XML_WHITESPACE)>0) then + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + elseif (verify(c,XML_WHITESPACE//">")>0) then + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + else + if (c==">") then + fx%nextTokenType = TOK_END_TAG + else + call push_chars(fb, c) + endif + fx%tokenType = TOK_NAME + endif + + case (ST_DTD_ELEMENT_CONTENTS) + if (c==">") then + if (associated(fx%content)) then + deallocate(fx%token) + fx%token => fx%content + fx%content => null() + endif + fx%tokenType = TOK_DTD_CONTENTS + fx%nextTokenType = TOK_END_TAG + else + if (associated(fx%content)) then + deallocate(fx%token) + fx%token => vs_str_alloc(str_vs(fx%content)//c) + deallocate(fx%content) + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + if (c=="(") then + fx%tokenType = TOK_OPEN_PAR + fx%content => fx%token + fx%token => vs_str_alloc("") + elseif (c==")") then + fx%tokenType = TOK_CLOSE_PAR + fx%content => fx%token + fx%token => vs_str_alloc("") + endif + endif + + case (ST_DTD_ATTLIST_CONTENTS) + if (c==">") then + fx%tokenType = TOK_DTD_CONTENTS + fx%nextTokenType = TOK_END_TAG + elseif (associated(fx%content)) then + deallocate(fx%token) + fx%token => vs_str_alloc(str_vs(fx%content)//c) + deallocate(fx%content) + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + if (c=="'".or.c=="""") then + if (q==c) then + q = "" + else + q = c + endif + endif + + case (ST_DTD_ENTITY_ID, ST_DTD_ENTITY_PUBLIC, ST_DTD_ENTITY_SYSTEM, & + ST_DTD_ENTITY_NDATA, ST_DTD_ENTITY_END, ST_DTD_ENTITY_NDATA_VALUE, & + ST_DTD_NOTATION_ID, ST_DTD_NOTATION_SYSTEM, ST_DTD_NOTATION_PUBLIC, & + ST_DTD_NOTATION_PUBLIC_2, ST_DTD_NOTATION_END) + if (firstChar) then + if (verify(c, XML_WHITESPACE)>0) then + if (c==">") then + fx%tokenType = TOK_END_TAG + else + call add_error(fx%error_stack, "Missing whitespace in DTD.") + endif + else + ws_discard = .true. + endif + elseif (ws_discard) then + if (verify(c, XML_WHITESPACE)>0) then + if (verify(c, "'""")==0) then + q = c + deallocate(fx%token) + fx%token => vs_str_alloc("") + ws_discard = .false. + elseif (c==">") then + fx%tokenType = TOK_END_TAG + else + deallocate(fx%token) + fx%token => vs_str_alloc(c) + ws_discard = .false. + endif + endif + else + if (q/=" ".and.c==q) then + fx%tokenType = TOK_CHAR + elseif (q==" ".and.verify(c, XML_WHITESPACE//">")==0) then + fx%tokenType = TOK_NAME + if (c==">") then + fx%nextTokenType = TOK_END_TAG + else + call push_chars(fb, c) + endif + else + tempString => fx%token + fx%token => vs_str_alloc(str_vs(tempString)//c) + deallocate(tempString) + endif + endif + + end select + end subroutine tokenizeDTD + + end subroutine sax_tokenize + + + recursive function normalize_attribute_text(fx, s_in) result(s_out) + type(sax_parser_t), intent(inout) :: fx + character, dimension(:), intent(in) :: s_in + character, dimension(:), pointer :: s_out + + character, dimension(:), pointer :: s_temp, s_temp2, s_ent, tempString + character :: dummy + integer :: i, i2, j + type(entity_t), pointer :: ent +#ifdef PGF90 + type(URI), pointer :: nullURI + nullURI => null() +#endif + + ! Condense all whitespace, only if we are validating, + ! Expand all & + ! Complain about < and & + + allocate(s_temp(size(s_in))) ! in the first instance + allocate(s_out(0)) ! in case we return early ... + s_ent => null() + tempString => null() + + i2 = 1 + i = 1 + do + if (i > size(s_in)) exit + ! Firstly, all whitespace must become 0x20 + if (verify(s_in(i),XML_WHITESPACE)==0) then + s_temp(i2) = " " + ! Then, < is always illegal + i = i + 1 + i2 = i2 + 1 + elseif (s_in(i)=='<') then + call add_error(fx%error_stack, "Illegal < found in attribute.") + goto 100 + ! Then, expand < + elseif (s_in(i)=='&') then + j = index(str_vs(s_in(i+1:)), ';') + if (j==0) then + call add_error(fx%error_stack, "Illegal & found in attribute") + goto 100 + elseif (j==1) then + call add_error(fx%error_stack, "No entity reference found") + goto 100 + endif + allocate(tempString(j-1)) + tempString = s_in(i+1:i+j-1) + if (existing_entity(fx%predefined_e_list, str_vs(tempString))) then + ! Expand immediately + s_temp(i2) = expand_entity_text(fx%predefined_e_list, str_vs(tempString)) + i = i + j + 1 + i2 = i2 + 1 + elseif (checkCharacterEntityReference(str_vs(tempString), fx%xds%xml_version)) then + ! Expand all character entities + s_temp(i2) = expand_char_entity(str_vs(tempString)) + i = i + j + 1 + i2 = i2 + 1 ! fixme + elseif (checkName(str_vs(tempString), fx%xds%xml_version)) then + ent => getEntityByName(fx%forbidden_ge_list, str_vs(tempString)) + if (associated(ent)) then + call add_error(fx%error_stack, 'Recursive entity expansion') + goto 100 + else + ent => getEntityByName(fx%xds%entityList, str_vs(tempString)) + endif + if (associated(ent)) then + if (ent%wfc.and.fx%xds%standalone) then + call add_error(fx%error_stack, 'Externally declared entity referenced in standalone document') + goto 100 + endif + !is it the right sort of entity? + if (ent%external) then + call add_error(fx%error_stack, "External entity forbidden in attribute") + goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_ge_list, str_vs(tempString), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_ge_list, str_vs(tempString), "", null(), .false.) +#endif + ! Recursively expand entity, checking for errors. + s_ent => normalize_attribute_text(fx, & + vs_str(expand_entity_text(fx%xds%entityList, str_vs(tempString)))) + dummy = pop_entity_list(fx%forbidden_ge_list) + if (in_error(fx%error_stack)) then + goto 100 + endif + allocate(s_temp2(size(s_temp)+size(s_ent)-j)) + s_temp2(:i2-1) = s_temp(:i2-1) + s_temp2(i2:i2+size(s_ent)-1) = s_ent + deallocate(s_temp) + s_temp => s_temp2 + nullify(s_temp2) + i = i + j + 1 + i2 = i2 + size(s_ent) + deallocate(s_ent) + else + s_temp(i2:i2+j) = s_in(i:i+j) + i = i + j + 1 + i2 = i2 + j + 1 + if (.not.fx%skippedExternal.or.fx%xds%standalone) then + call add_error(fx%error_stack, "Undeclared entity encountered in standalone document.") + goto 100 + endif + endif + else + call add_error(fx%error_stack, "Illegal entity reference") + goto 100 + endif + deallocate(tempString) + else + s_temp(i2) = s_in(i) + i = i + 1 + i2 = i2 + 1 + endif + enddo + + deallocate(s_out) + allocate(s_out(i2-1)) + s_out = s_temp(:i2-1) +100 deallocate(s_temp) + if (associated(s_ent)) deallocate(s_ent) + if (associated(tempString)) deallocate(tempString) + + end function normalize_attribute_text + + recursive function expand_pe_text(fx, s_in, fb) result(s_out) + type(sax_parser_t), intent(inout) :: fx + character, dimension(:), intent(in) :: s_in + type(file_buffer_t), intent(inout) :: fb + character, dimension(:), pointer :: s_out + + character, dimension(:), pointer :: s_temp, s_temp2, s_ent, tempString + character :: dummy + integer :: i, i2, j, iostat + type(entity_t), pointer :: ent +#ifdef PGF90 + type(URI), pointer :: nullURI + nullURI => null() +#endif + + ! Expand all %PE; + + allocate(s_temp(size(s_in))) ! in the first instance + allocate(s_out(0)) ! in case we return early ... + s_ent => null() + tempString => null() + s_temp2 => null() + + i2 = 1 + i = 1 + do while (i <= size(s_in)) + if (s_in(i)=='%') then + j = index(str_vs(s_in(i+1:)), ';') + if (j==0) then + call add_error(fx%error_stack, "Illegal % found in attribute") + goto 100 + elseif (j==1) then + call add_error(fx%error_stack, "No entity reference found") + goto 100 + endif + allocate(tempString(j-1)) + tempString = s_in(i+1:i+j-1) + if (checkName(str_vs(tempString), fx%xds%xml_version)) then + ent => getEntityByName(fx%forbidden_pe_list, str_vs(tempString)) + if (associated(ent)) then + call add_error(fx%error_stack, 'Recursive entity expansion') + goto 100 + endif + ent => getEntityByName(fx%xds%peList, str_vs(tempString)) + if (associated(ent)) then + if (ent%wfc.and.fx%xds%standalone) then + call add_error(fx%error_stack, & + "Externally declared entity used in standalone document") + goto 100 + elseif (str_vs(ent%notation)/="") then + call add_error(fx%error_stack, "Unparsed entity reference forbidden in entity value") + goto 100 + endif +#ifdef PGF90 + call add_internal_entity(fx%forbidden_pe_list, str_vs(tempString), "", nullURI, .false.) +#else + call add_internal_entity(fx%forbidden_pe_list, str_vs(tempString), "", null(), .false.) +#endif + ! Recursively expand entity, checking for errors. + if (ent%external) then + call open_new_file(fb, ent%baseURI, iostat) + if (iostat/=0) then + call add_error(fx%error_stack, "Unable to access external parameter entity") + goto 100 + endif + call parse_text_declaration(fb, fx%error_stack) + if (in_error(fx%error_stack)) goto 100 + s_temp2 => get_all_characters(fb, fx%error_stack) + call pop_buffer_stack(fb) + if (in_error(fx%error_stack)) goto 100 + s_ent => expand_pe_text(fx, s_temp2, fb) + deallocate(s_temp2) + else + s_ent => expand_pe_text(fx, & + vs_str(expand_entity_text(fx%xds%peList, str_vs(tempString))), fb) + endif + dummy = pop_entity_list(fx%forbidden_pe_list) + if (in_error(fx%error_stack)) then + goto 100 + endif + allocate(s_temp2(size(s_temp)+size(s_ent)-j)) + s_temp2(:i2-1) = s_temp(:i2-1) + s_temp2(i2:i2+size(s_ent)-1) = s_ent + deallocate(s_temp) + s_temp => s_temp2 + s_temp2 => null() + i = i + j + 1 + i2 = i2 + size(s_ent) + deallocate(s_ent) + else + s_temp(i2:i2+j) = s_in(i:i+j) + i = i + j + 1 + i2 = i2 + j + 1 + if (.not.fx%skippedExternal.or.fx%xds%standalone) then + call add_error(fx%error_stack, "Reference to undeclared parameter entity encountered in standalone document.") + goto 100 + endif + endif + else + call add_error(fx%error_stack, "Illegal parameter entity reference") + goto 100 + endif + deallocate(tempString) + else + s_temp(i2) = s_in(i) + i = i + 1 + i2 = i2 + 1 + endif + enddo + + deallocate(s_out) + allocate(s_out(i2-1)) + s_out = s_temp(:i2-1) +100 deallocate(s_temp) + if (associated(s_temp2)) deallocate(s_temp2) + if (associated(s_ent)) deallocate(s_ent) + if (associated(tempString)) deallocate(tempString) + + end function expand_pe_text +#endif + +end module m_sax_tokenizer diff --git a/src/xml/sax/m_sax_types.F90 b/src/xml/sax/m_sax_types.F90 new file mode 100644 index 000000000..b0148a693 --- /dev/null +++ b/src/xml/sax/m_sax_types.F90 @@ -0,0 +1,161 @@ +module m_sax_types + +#ifndef DUMMYLIB + use m_common_attrs, only: dictionary_t + use m_common_elstack, only: elstack_t + use m_common_entities, only: entity_list + use m_common_error, only: error_stack + use m_common_namespaces, only: namespacedictionary + use m_common_notations, only: notation_list + use m_common_struct, only: xml_doc_state + + use m_sax_reader, only: file_buffer_t + + implicit none + + ! Context + + integer, parameter :: CTXT_NULL = -1 + integer, parameter :: CTXT_INIT = 0 + integer, parameter :: CTXT_BEFORE_DTD = 1 + integer, parameter :: CTXT_IN_DTD = 2 + integer, parameter :: CTXT_IGNORE = 3 + integer, parameter :: CTXT_BEFORE_CONTENT = 4 + integer, parameter :: CTXT_IN_CONTENT = 5 + integer, parameter :: CTXT_AFTER_CONTENT = 6 + + ! State + + integer, parameter :: ST_STOP = -1 + integer, parameter :: ST_NULL = 0 + integer, parameter :: ST_MISC = 1 + integer, parameter :: ST_BANG_TAG = 2 + integer, parameter :: ST_START_PI = 3 + integer, parameter :: ST_PI_CONTENTS = 4 + integer, parameter :: ST_PI_END = 5 + integer, parameter :: ST_START_COMMENT = 6 + integer, parameter :: ST_COMMENT_END = 7 + integer, parameter :: ST_START_TAG = 8 + integer, parameter :: ST_START_CDATA_DECLARATION = 9 + integer, parameter :: ST_FINISH_CDATA_DECLARATION = 10 + integer, parameter :: ST_IN_TAG = 11 + integer, parameter :: ST_ATT_NAME = 12 + integer, parameter :: ST_ATT_EQUALS = 13 + integer, parameter :: ST_CHAR_IN_CONTENT = 14 + integer, parameter :: ST_CLOSING_TAG = 15 + integer, parameter :: ST_CDATA_CONTENTS = 16 + integer, parameter :: ST_IN_CLOSING_TAG = 17 + integer, parameter :: ST_TAG_IN_CONTENT = 18 + integer, parameter :: ST_CDATA_END = 19 + integer, parameter :: ST_IN_DOCTYPE = 20 + integer, parameter :: ST_DOC_NAME = 21 + integer, parameter :: ST_DOC_SYSTEM = 22 + integer, parameter :: ST_DOC_PUBLIC = 23 + integer, parameter :: ST_DOC_DECL = 24 + integer, parameter :: ST_CLOSE_DOCTYPE = 25 + integer, parameter :: ST_START_ENTITY = 26 + integer, parameter :: ST_START_PE = 27 + integer, parameter :: ST_IN_SUBSET = 28 + +! DTD states + integer, parameter :: ST_DTD_NULL = 50 + integer, parameter :: ST_DTD_SUBSET = 51 + integer, parameter :: ST_DTD_START_SECTION_DECL = 52 + integer, parameter :: ST_DTD_FINISH_SECTION_DECL = 53 + integer, parameter :: ST_DTD_IN_IGNORE_SECTION = 54 + integer, parameter :: ST_DTD_BANG_TAG = 55 + integer, parameter :: ST_DTD_START_PI = 56 + integer, parameter :: ST_DTD_PI_CONTENTS = 57 + integer, parameter :: ST_DTD_PI_END = 58 + integer, parameter :: ST_DTD_COMMENT_END = 59 + integer, parameter :: ST_DTD_START_COMMENT = 60 + integer, parameter :: ST_DTD_ATTLIST = 61 + integer, parameter :: ST_DTD_ELEMENT = 62 + integer, parameter :: ST_DTD_ENTITY = 63 + integer, parameter :: ST_DTD_NOTATION = 64 + integer, parameter :: ST_DTD_NOTATION_ID = 65 + integer, parameter :: ST_DTD_NOTATION_SYSTEM = 66 + integer, parameter :: ST_DTD_NOTATION_PUBLIC = 67 + integer, parameter :: ST_DTD_NOTATION_PUBLIC_2 = 68 + integer, parameter :: ST_DTD_NOTATION_END = 69 + integer, parameter :: ST_DTD_ENTITY_PE = 70 + integer, parameter :: ST_DTD_ENTITY_ID = 71 + integer, parameter :: ST_DTD_ENTITY_PUBLIC = 72 + integer, parameter :: ST_DTD_ENTITY_SYSTEM = 73 + integer, parameter :: ST_DTD_ENTITY_NDATA = 74 + integer, parameter :: ST_DTD_ENTITY_NDATA_VALUE = 75 + integer, parameter :: ST_DTD_ENTITY_END = 76 + integer, parameter :: ST_DTD_ATTLIST_CONTENTS = 77 + integer, parameter :: ST_DTD_ATTLIST_END = 78 + integer, parameter :: ST_DTD_ELEMENT_CONTENTS = 79 + integer, parameter :: ST_DTD_ELEMENT_END = 80 + integer, parameter :: ST_DTD_DONE = 81 + +! token types + + integer, parameter :: TOK_NULL = 0 + integer, parameter :: TOK_PI_TAG = 1 ! + integer, parameter :: TOK_COMMENT_END = 10 ! --> + integer, parameter :: TOK_SECTION_START = 11 ! + integer, parameter :: TOK_END_TAG = 13 ! > + integer, parameter :: TOK_END_TAG_CLOSE = 14 ! /> + integer, parameter :: TOK_CLOSE_TAG = 15 ! null() + character, dimension(:), pointer :: content => null() + integer :: tokenType = TOK_NULL + integer :: nextTokenType = TOK_NULL + character, dimension(:), pointer :: name => null() + character, dimension(:), pointer :: attname => null() + logical :: error = .false. + type(error_stack) :: error_stack + ! Aspects of document structure + character, dimension(:), pointer :: root_element => null() + type(elstack_t) :: elstack + type(dictionary_t) :: attributes + type(namespacedictionary) :: nsdict + type(notation_list) :: nlist + type(entity_list) :: predefined_e_list + type(entity_list) :: forbidden_pe_list + type(entity_list) :: forbidden_ge_list + character(len=1), dimension(:), pointer :: PublicId => null() + character(len=1), dimension(:), pointer :: SystemId => null() + character(len=1), dimension(:), pointer :: Ndata => null() + logical :: inIntSubset = .false. + logical :: spaceBeforeEntity = .false. + end type sax_parser_t +#endif + + type xml_t +#ifndef DUMMYLIB + type(file_buffer_t) :: fb + type(sax_parser_t) :: fx +#else + integer :: i = 0 +#endif + end type xml_t + +end module m_sax_types diff --git a/src/xml/sax/m_sax_xml_source.F90 b/src/xml/sax/m_sax_xml_source.F90 new file mode 100644 index 000000000..21c238e6a --- /dev/null +++ b/src/xml/sax/m_sax_xml_source.F90 @@ -0,0 +1,438 @@ +module m_sax_xml_source + +#ifndef DUMMYLIB + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc + use fox_m_fsys_format, only: operator(//) + use m_common_error, only: error_stack, add_error, in_error + use m_common_charset, only: XML_WHITESPACE, XML_INITIALENCODINGCHARS, & + XML_ENCODINGCHARS, XML1_0, XML1_1, isXML1_0_NameChar, & + isLegalChar, isUSASCII, allowed_encoding + use m_common_io, only: io_eor, io_eof + + use FoX_utils, only: URI + + implicit none + private + + type buffer_t + character, dimension(:), pointer :: s + integer :: pos = 1 + end type buffer_t + + type xml_source_t + !FIXME private + integer :: lun = -1 + integer :: xml_version = XML1_0 + character, pointer :: encoding(:) => null() + logical :: isUSASCII + character, pointer :: filename(:) => null() + type(URI), pointer :: baseURI => null() + integer :: line = 0 + integer :: col = 0 + integer :: startChar = 1 ! First character after XML decl + character, pointer :: next_chars(:) => null() ! pushback buffer + type(buffer_t), pointer :: input_string => null() + logical :: pe = .false. ! is this a parameter entity? + logical :: eof = .false.! need to keep track of this at the end of pes + end type xml_source_t + + public :: buffer_t + public :: xml_source_t + + public :: get_char_from_file + public :: push_file_chars + public :: parse_declaration + +contains + + + function get_char_from_file(f, xv, eof, es) result(string) + type(xml_source_t), intent(inout) :: f + integer, intent(in) :: xv + logical, intent(out) :: eof + type(error_stack), intent(inout) :: es + character(len=1) :: string + + integer :: iostat + logical :: pending + character :: c, c2 + + pending = .false. + eof = .false. + c = read_single_char(f, iostat) + if (iostat==io_eof) then + eof = .true. + return + elseif (iostat/=0) then + call add_error(es, "Error reading "//str_vs(f%filename)) + return + endif + if (.not.isLegalChar(c, f%isUSASCII, xv)) then + call add_error(es, "Illegal character found at " & + //str_vs(f%filename)//":"//f%line//":"//f%col) + return + endif + if (c==achar(13)) then + c = achar(10) + c2 = read_single_char(f, iostat) + if (iostat==io_eof) then + ! the file has just ended on a single CR. Report is as a LF. + ! Ignore the eof just now, it'll be picked up if we need to + ! perform another read. + eof = .false. + elseif (iostat/=0) then + call add_error(es, "Error reading "//str_vs(f%filename)) + return + elseif (c2/=achar(10)) then + ! then we keep c2, otherwise we'd just ignore it. + pending = .true. + endif + endif + string = c + + if (pending) then + ! we have one character left over, put in the pushback buffer + deallocate(f%next_chars) + allocate(f%next_chars(1)) + f%next_chars = c2 + endif + + if (c==achar(10)) then + f%line = f%line + 1 + f%col = 0 + else + f%col = f%col + 1 + endif + + end function get_char_from_file + + function read_single_char(f, iostat) result(c) + type(xml_source_t), intent(inout) :: f + integer, intent(out) :: iostat + character :: c + + if (f%eof) then + c = "" + iostat = io_eof + return + endif + if (f%lun==-1) then + if (f%input_string%pos>size(f%input_string%s)) then + c = "" + if (f%pe) then + iostat = 0 + else + iostat = io_eof + endif + f%eof = .true. + else + iostat = 0 + c = f%input_string%s(f%input_string%pos) + f%input_string%pos = f%input_string%pos + 1 + endif + else + read (unit=f%lun, iostat=iostat, advance="no", fmt="(a1)") c + if (iostat==io_eor) then + iostat = 0 +#ifdef FC_EOR_LF + c = achar(10) +#else + c = achar(13) +#endif + elseif (iostat==io_eof) then + if (f%pe) iostat = 0 + c = "" + f%eof = .true. + endif + endif + end function read_single_char + + subroutine rewind_source(f) + type(xml_source_t), intent(inout) :: f + + if (f%lun==-1) then + f%input_string%pos = 1 + else + rewind(f%lun) + endif + end subroutine rewind_source + + subroutine push_file_chars(f, s) + type(xml_source_t), intent(inout) :: f + character(len=*), intent(in) :: s + character, dimension(:), pointer :: nc + + nc => vs_str_alloc(s//str_vs(f%next_chars)) + deallocate(f%next_chars) + f%next_chars => nc + + end subroutine push_file_chars + + + subroutine parse_declaration(f, eof, es, standalone) + type(xml_source_t), intent(inout) :: f + logical, intent(out) :: eof + type(error_stack), intent(inout) :: es + logical, intent(out), optional :: standalone + + integer :: parse_state, xd_par + character :: c, q + character, pointer :: ch(:), ch2(:) + + integer, parameter :: XD_0 = 0 + integer, parameter :: XD_START = 1 + integer, parameter :: XD_TARGET = 2 + integer, parameter :: XD_MISC = 3 + integer, parameter :: XD_PA = 4 + integer, parameter :: XD_EQ = 5 + integer, parameter :: XD_QUOTE = 6 + integer, parameter :: XD_PV = 7 + integer, parameter :: XD_END = 8 + integer, parameter :: XD_SPACE = 9 + + integer, parameter :: xd_nothing = 0 + integer, parameter :: xd_version = 1 + integer, parameter :: xd_encoding = 2 + integer, parameter :: xd_standalone = 3 + + f%xml_version = XML1_0 + if (present(standalone)) standalone = .false. + + f%startChar = 1 + + parse_state = XD_0 + xd_par = xd_nothing + ch => null() + do + c = get_char_from_file(f, XML1_0, eof, es) + if (eof) then + call rewind_source(f) + exit + elseif (in_error(es)) then + goto 100 + endif + f%startChar = f%startChar + 1 + + select case (parse_state) + + case (XD_0) + if (c=="<") then + parse_state = XD_START + else + call rewind_source(f) + exit + endif + + case (XD_START) + if (c=="?") then + parse_state = XD_TARGET + ch => vs_str_alloc("") + else + call rewind_source(f) + exit + endif + + case (XD_TARGET) + if (isXML1_0_NameChar(c)) then + ch2 => vs_str_alloc(str_vs(ch)//c) + deallocate(ch) + ch => ch2 + elseif (verify(c, XML_WHITESPACE)==0 & + .and.str_vs(ch)=="xml") then + deallocate(ch) + parse_state = XD_MISC + else + call rewind_source(f) + deallocate(ch) + exit + endif + + case (XD_SPACE) + if (verify(c, XML_WHITESPACE)==0) then + parse_state = XD_MISC + elseif (c=="?") then + parse_state = XD_END + else + call add_error(es, & + "Missing space in XML declaration") + endif + + case (XD_MISC) + if (c=="?") then + parse_state = XD_END + elseif (isXML1_0_NameChar(c)) then + ch => vs_str_alloc(c) + parse_state = XD_PA + elseif (verify(c, XML_WHITESPACE)>0) then + call add_error(es, & + "Unexpected character in XML declaration") + endif + + case (XD_PA) + if (isXML1_0_NameChar(c)) then + ch2 => vs_str_alloc(str_vs(ch)//c) + deallocate(ch) + ch => ch2 + elseif (verify(c, XML_WHITESPACE//"=")==0) then + select case (str_vs(ch)) + + case ("version") + select case (xd_par) + case (xd_nothing) + xd_par = xd_version + case default + call add_error(es, & + "Cannot specify version twice in XML declaration") + end select + + case ("encoding") + select case (xd_par) + case (xd_nothing) + if (present(standalone)) then + call add_error(es, & + "Must specify version before encoding in XML declaration") + else + xd_par = xd_encoding + endif + case (xd_version) + xd_par = xd_encoding + case (xd_encoding) + call add_error(es, & + "Cannot specify encoding twice in XML declaration") + case (xd_standalone) + call add_error(es, & + "Cannot specify encoding after standalone in XML declaration") + end select + + case ("standalone") + if (.not.present(standalone)) & + call add_error(es, & + "Cannot specify standalone in text declaration") + select case (xd_par) + case (xd_nothing) + call add_error(es, & + "Must specify version before standalone in XML declaration") + case (xd_version, xd_encoding) + xd_par = xd_standalone + case (xd_standalone) + call add_error(es, & + "Cannot specify standalone twice in XML declaration") + end select + + case default + call add_error(es, & + "Unknown parameter "//str_vs(ch)//" in XML declaration, "//& + "expecting version, encoding or standalone") + + end select + + deallocate(ch) + if (c=="=") then + parse_state = XD_QUOTE + else + parse_state = XD_EQ + endif + else + call add_error(es, & + "Unexpected character found in XML declaration") + endif + + case (XD_EQ) + if (c=="=") then + parse_state = XD_QUOTE + elseif (verify(c, XML_WHITESPACE)>0) then + call add_error(es, & + "Unexpected character found in XML declaration; expecting ""=""") + endif + + case (XD_QUOTE) + if (verify(c, "'""")==0) then + q = c + parse_state = XD_PV + ch => vs_str_alloc("") + elseif (verify(c, XML_WHITESPACE)>0) then + call add_error(es, & + "Unexpected character found in XML declaration; expecting "" or '") + endif + + case (XD_PV) + if (c==q) then + select case (xd_par) + case (xd_version) + if (str_vs(ch)//"x"=="1.0x") then + f%xml_version = XML1_0 + deallocate(ch) + elseif (str_vs(ch)//"x"=="1.1x") then + f%xml_version = XML1_1 + deallocate(ch) + else + call add_error(es, & + "Unknown version number "//str_vs(ch)//" found in XML declaration; expecting 1.0 or 1.1") + endif + case (xd_encoding) + if (size(ch)==0) then + call add_error(es, & + "Empty value for encoding not allowed in XML declaration") + elseif (size(ch)==1.and.verify(ch(1), XML_INITIALENCODINGCHARS)>0) then + call add_error(es, & + "Invalid encoding found in XML declaration; illegal characters in encoding name") + elseif (size(ch)>1.and. & + (verify(ch(1), XML_INITIALENCODINGCHARS)>0 & + .or.verify(str_vs(ch(2:)), XML_ENCODINGCHARS)>0)) then + call add_error(es, & + "Invalid encoding found in XML declaration; illegal characters in encoding name") + elseif (.not.allowed_encoding(str_vs(ch))) then + call add_error(es, "Unknown character encoding in XML declaration") + else + f%encoding => ch + f%isUSASCII = isUSASCII(str_vs(ch)) + ch => null() + endif + case (xd_standalone) + if (str_vs(ch)//"x"=="yesx") then + standalone = .true. + deallocate(ch) + elseif (str_vs(ch)//"x"=="nox") then + standalone = .false. + deallocate(ch) + else + call add_error(es, & + "Invalid value for standalone found in XML declaration; expecting yes or no") + + endif + end select + parse_state = XD_SPACE + else + ch2 => vs_str_alloc(str_vs(ch)//c) + deallocate(ch) + ch => ch2 + endif + + case (XD_END) + if (c==">") then + exit + else + call add_error(es, & + "Unexpected character found in XML declaration; expecting >") + endif + + end select + + end do + + if (.not.associated(f%encoding)) then + if (present(standalone).or.parse_state/=XD_END) then + f%encoding => vs_str_alloc("utf-8") + else + call add_error(es, "Missing encoding in text declaration") + endif + endif + +100 if (associated(ch)) deallocate(ch) + ! if there is no XML declaraion, or if parsing caused an error, then + if (parse_state/=XD_END.or.in_error(es)) f%startChar = 1 + + end subroutine parse_declaration +#endif + +end module m_sax_xml_source diff --git a/src/xml/sax/makefile b/src/xml/sax/makefile new file mode 100644 index 000000000..a440a53c8 --- /dev/null +++ b/src/xml/sax/makefile @@ -0,0 +1,49 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_sax.o: m_sax_operate.o +m_sax_operate.o: m_sax_parser.o m_sax_reader.o m_sax_types.o +m_sax_parser.o: m_sax_reader.o m_sax_tokenizer.o m_sax_types.o +m_sax_reader.o: m_sax_xml_source.o +m_sax_tokenizer.o: m_sax_reader.o m_sax_types.o +m_sax_types.o: m_sax_reader.o diff --git a/src/xml/utils/FoX_utils.F90 b/src/xml/utils/FoX_utils.F90 new file mode 100644 index 000000000..560f91e59 --- /dev/null +++ b/src/xml/utils/FoX_utils.F90 @@ -0,0 +1,22 @@ +module FoX_utils + + use fox_m_utils_uuid + use fox_m_utils_uri + + implicit none + private + + public :: generate_uuid + + public :: URI + public :: parseURI + public :: rebaseURI + public :: copyURI + public :: destroyURI + public :: expressURI + public :: hasFragment + public :: hasScheme + public :: getScheme + public :: getPath + +end module FoX_utils diff --git a/src/xml/utils/fox_m_utils_mtprng.F90 b/src/xml/utils/fox_m_utils_mtprng.F90 new file mode 100644 index 000000000..90a766d2b --- /dev/null +++ b/src/xml/utils/fox_m_utils_mtprng.F90 @@ -0,0 +1,360 @@ +module fox_m_utils_mtprng +#ifndef DUMMYLIB +!--------------------------------------------------------------------- +! From the Algorithmic Conjurings of Scott Robert Ladd comes... +!--------------------------------------------------------------------- +! +! mtprng.f90 (a Fortran 95 module) +! +! An implementation of the Mersenne Twister algorithm for generating +! psuedo-random sequences. +! +! History +! ------- +! 1.0.0 Initial release +! +! 1.1.0 6 February 2002 +! Updated to support algorithm revisions posted +! by Matsumoto and Nishimura on 26 January 2002 +! +! 1.5.0 12 December 2003 +! Added to hypatia project +! Minor style changes +! Tightened code +! Now state based; no static variables +! Removed mtprng_rand_real53 +! +! 2.0.0 4 January 2004 +! Corrected erroneous unsigned bit manipulations +! Doubled resolution by using 64-bit math +! Added mtprng_rand64 + +! Version for distribution with FoX +! Very small cosmetic changes to fit FoX naming scheme and +! avoid additional dependencies. +! Toby White , 2007 + +! +! ORIGINAL ALGORITHM COPYRIGHT +! ============================ +! Copyright (C) 1997,2002 Makoto Matsumoto and Takuji Nishimura. +! Any feedback is very welcome. For any question, comments, see +! http://www.math.keio.ac.jp/matumoto/emt.html or email +! matumoto@math.keio.ac.jp +!--------------------------------------------------------------------- +! +! COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: +! +! This notice applies *only* to this specific expression of this +! algorithm, and does not imply ownership or invention of the +! implemented algorithm. +! +! If you modify this file, you may insert additional notices +! immediately following this sentence. +! +! Copyright 2001, 2002, 2004 Scott Robert Ladd. +! All rights reserved, except as noted herein. +! +! This computer program source file is supplied "AS IS". Scott Robert +! Ladd (hereinafter referred to as "Author") disclaims all warranties, +! expressed or implied, including, without limitation, the warranties +! of merchantability and of fitness for any purpose. The Author +! assumes no liability for direct, indirect, incidental, special, +! exemplary, or consequential damages, which may result from the use +! of this software, even if advised of the possibility of such damage. +! +! The Author hereby grants anyone permission to use, copy, modify, and +! distribute this source code, or portions hereof, for any purpose, +! without fee, subject to the following restrictions: +! +! 1. The origin of this source code must not be misrepresented. +! +! 2. Altered versions must be plainly marked as such and must not +! be misrepresented as being the original source. +! +! 3. This Copyright notice may not be removed or altered from any +! source or altered source distribution. +! +! The Author specifically permits (without fee) and encourages the use +! of this source code for entertainment, education, or decoration. If +! you use this source code in a product, acknowledgment is not required +! but would be appreciated. +! +! Acknowledgement: +! This license is based on the wonderful simple license that +! accompanies libpng. +! +!----------------------------------------------------------------------- +! +! For more information on this software package, please visit +! Scott's web site, Coyote Gulch Productions, at: +! +! http://www.coyotegulch.com +! +!----------------------------------------------------------------------- + + implicit none + + ! Kind types for 64-, 32-, 16-, and 8-bit signed integers + integer, parameter :: INT64 = selected_int_kind(18) + integer, parameter :: INT32 = selected_int_kind(9) + integer, parameter :: INT16 = selected_int_kind(4) + integer, parameter :: INT08 = selected_int_kind(2) + + ! Kind types for IEEE 754/IEC 60559 single- and double-precision reals + integer, parameter :: IEEE32 = selected_real_kind( 6, 37 ) + integer, parameter :: IEEE64 = selected_real_kind( 15, 307 ) + + !------------------------------------------------------------------------------ + ! Everything is private unless explicitly made public + private + + public :: mtprng_state, & + mtprng_init, mtprng_init_by_array, & + mtprng_rand64, mtprng_rand, mtprng_rand_range, & + mtprng_rand_real1, mtprng_rand_real2, mtprng_rand_real3 + + !------------------------------------------------------------------------------ + ! Constants + integer(INT32), parameter :: N = 624_INT32 + integer(INT32), parameter :: M = 397_INT32 + + !------------------------------------------------------------------------------ + ! types + type mtprng_state + integer(INT32) :: mti = -1 + integer(INT64), dimension(0:N-1) :: mt + end type + +contains + !-------------------------------------------------------------------------- + ! Initializes the generator with "seed" + subroutine mtprng_init(seed, state) + + ! arguments + integer(INT32), intent(in) :: seed + type(mtprng_state), intent(out) :: state + + ! working storage + integer :: i + + ! save seed + state%mt(0) = seed + + ! Set the seed using values suggested by Matsumoto & Nishimura, using + ! a generator by Knuth. See original source for details. + do i = 1, N - 1 + state%mt(i) = iand(4294967295_INT64,1812433253_INT64 * ieor(state%mt(i-1),ishft(state%mt(i-1),-30_INT64)) + i) + end do + + state%mti = N + + end subroutine mtprng_init + + !-------------------------------------------------------------------------- + ! Initialize with an array of seeds + subroutine mtprng_init_by_array(init_key, state) + + ! arguments + integer(INT32), dimension(:), intent(in) :: init_key + type(mtprng_state), intent(out) :: state + + ! working storage + integer :: key_length + integer :: i + integer :: j + integer :: k + + call mtprng_init(19650218_INT32,state) + + i = 1 + j = 0 + key_length = size(init_key) + + do k = max(N,key_length), 0, -1 + state%mt(i) = ieor(state%mt(i),(ieor(state%mt(i-1),ishft(state%mt(i-1),-30_INT64) * 1664525_INT64))) + init_key(j) + j + + i = i + 1 + j = j + 1 + + if (i >= N) then + state%mt(0) = state%mt(N-1) + i = 1 + end if + + if (j >= key_length) j = 0 + end do + + do k = N-1, 0, -1 + state%mt(i) = ieor(state%mt(i),(ieor(state%mt(i-1),ishft(state%mt(i-1),-30_INT64) * 1566083941_INT64))) - i + + i = i + 1 + + if (i>=N) then + state%mt(0) = state%mt(N-1) + i = 1 + end if + end do + + state%mt(0) = 1073741824_INT64 ! 0x40000000, assuring non-zero initial array + + end subroutine mtprng_init_by_array + + !-------------------------------------------------------------------------- + ! Obtain the next 32-bit integer in the psuedo-random sequence + function mtprng_rand64(state) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + + !return type + integer(INT64) :: r + + ! internal constants + integer(INT64), dimension(0:1), parameter :: mag01 = (/ 0_INT64, -1727483681_INT64 /) + + ! Period parameters + integer(INT64), parameter :: UPPER_MASK = 2147483648_INT64 + integer(INT64), parameter :: LOWER_MASK = 2147483647_INT64 + + ! Tempering parameters + integer(INT64), parameter :: TEMPERING_B = -1658038656_INT64 + integer(INT64), parameter :: TEMPERING_C = -272236544_INT64 + + ! Note: variable names match those in original example + integer(INT32) :: kk + + ! Generate N words at a time + if (state%mti >= N) then + ! The value -1 acts as a flag saying that the seed has not been set. + if (state%mti == -1) call mtprng_init(4357_INT32,state) + + ! Fill the mt array + do kk = 0, N - M - 1 + r = ior(iand(state%mt(kk),UPPER_MASK),iand(state%mt(kk+1),LOWER_MASK)) + state%mt(kk) = ieor(ieor(state%mt(kk + M),ishft(r,-1_INT64)),mag01(iand(r,1_INT64))) + end do + + do kk = N - M, N - 2 + r = ior(iand(state%mt(kk),UPPER_MASK),iand(state%mt(kk+1),LOWER_MASK)) + state%mt(kk) = ieor(ieor(state%mt(kk + (M - N)),ishft(r,-1_INT64)),mag01(iand(r,1_INT64))) + end do + + r = ior(iand(state%mt(N-1),UPPER_MASK),iand(state%mt(0),LOWER_MASK)) + state%mt(N-1) = ieor(ieor(state%mt(M-1),ishft(r,-1)),mag01(iand(r,1_INT64))) + + ! Start using the array from first element + state%mti = 0 + end if + + ! Here is where we actually calculate the number with a series of + ! transformations + r = state%mt(state%mti) + state%mti = state%mti + 1 + + r = ieor(r,ishft(r,-11)) + r = iand(4294967295_INT64,ieor(r,iand(ishft(r, 7),TEMPERING_B))) + r = iand(4294967295_INT64,ieor(r,iand(ishft(r,15),TEMPERING_C))) + r = ieor(r,ishft(r,-18)) + + end function mtprng_rand64 + + !-------------------------------------------------------------------------- + ! Obtain the next 32-bit integer in the psuedo-random sequence + function mtprng_rand(state) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + + !return type + integer(INT32) :: r + + ! working storage + integer(INT64) :: x + + ! done + x = mtprng_rand64(state) + + if (x > 2147483647_INT64) then + r = x - 4294967296_INT64 + else + r = x + end if + + end function mtprng_rand + + !--------------------------------------------------------------------------- + ! Obtain a psuedorandom integer in the range [lo,hi] + function mtprng_rand_range(state, lo, hi) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + integer, intent(in) :: lo + integer, intent(in) :: hi + + ! return type + integer(INT32) :: r + + ! Use real value to caluclate range + r = lo + floor((hi - lo + 1.0_IEEE64) * mtprng_rand_real2(state)) + + end function mtprng_rand_range + + !-------------------------------------------------------------------------- + ! Obtain a psuedorandom real number in the range [0,1], i.e., a number + ! greater than or equal to 0 and less than or equal to 1. + function mtprng_rand_real1(state) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + + ! return type + real(IEEE64) :: r + + ! Local constant; precalculated to avoid division below + real(IEEE64), parameter :: factor = 1.0_IEEE64 / 4294967295.0_IEEE64 + + ! compute + r = real(mtprng_rand64(state),IEEE64) * factor + + end function mtprng_rand_real1 + + !-------------------------------------------------------------------------- + ! Obtain a psuedorandom real number in the range [0,1), i.e., a number + ! greater than or equal to 0 and less than 1. + function mtprng_rand_real2(state) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + + ! return type + real(IEEE64) :: r + + ! Local constant; precalculated to avoid division below + real(IEEE64), parameter :: factor = 1.0_IEEE64 / 4294967296.0_IEEE64 + + ! compute + r = real(mtprng_rand64(state),IEEE64) * factor + + end function mtprng_rand_real2 + + !-------------------------------------------------------------------------- + ! Obtain a psuedorandom real number in the range (0,1), i.e., a number + ! greater than 0 and less than 1. + function mtprng_rand_real3(state) result(r) + + ! arguments + type(mtprng_state), intent(inout) :: state + + ! return type + real(IEEE64) :: r + + ! Local constant; precalculated to avoid division below + real(IEEE64), parameter :: factor = 1.0_IEEE64 / 4294967296.0_IEEE64 + + r = (real(mtprng_rand64(state),IEEE64) + 0.5_IEEE64) * factor + + end function mtprng_rand_real3 + +#endif +end module fox_m_utils_mtprng diff --git a/src/xml/utils/fox_m_utils_uri.F90 b/src/xml/utils/fox_m_utils_uri.F90 new file mode 100644 index 000000000..be6fefcdd --- /dev/null +++ b/src/xml/utils/fox_m_utils_uri.F90 @@ -0,0 +1,1013 @@ +module fox_m_utils_uri +#ifndef DUMMYLIB + + ! Manipulate URIs and URI references a la RFC 2396 + ! NB: ... + ! Forbidden (ASCII control) characters are not handled correctly + ! checking of reg names (not hosts) is done wrongly + ! checking of ipv6/X is untested + + use fox_m_fsys_array_str, only: str_vs, vs_str_alloc, vs_vs_alloc + use fox_m_fsys_format, only: str_to_int_10, str_to_int_16, str + use fox_m_fsys_string, only: toLower + + implicit none + private + + type path_segment + character, pointer :: s(:) => null() + end type path_segment +#endif + + type URI + private +#ifndef DUMMYLIB + character, pointer :: scheme(:) => null() + character, pointer :: authority(:) => null() + character, pointer :: userinfo(:) => null() + character, pointer :: host(:) => null() + integer :: port = -1 + character, pointer :: path(:) => null() + type(path_segment), pointer :: segments(:) => null() + character, pointer :: query(:) => null() + character, pointer :: fragment(:) => null() +#else + integer :: i +#endif + end type URI + +#ifndef DUMMYLIB + character(len=*), parameter :: lowalpha = "abcdefghijklmnopqrstuvwxyz" + character(len=*), parameter :: upalpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + character(len=*), parameter :: alpha = lowalpha//upalpha + character(len=*), parameter :: digit = "0123456789" + character(len=*), parameter :: hexdigit = "0123456789abcdefABCDEF" + character(len=*), parameter :: alphanum = alpha//digit + character(len=*), parameter :: unreserved = alphanum//"-._~" + character(len=*), parameter :: gen_delims = ":/?#[]@" + character(len=*), parameter :: sub_delims = "!$&'()*+,;=" + character(len=*), parameter :: reserved = gen_delims//sub_delims + character(len=*), parameter :: pchar = unreserved//":@&=+$," + character(len=*), parameter :: uric_no_slash = unreserved//";?:@&=+$," + character(len=*), parameter :: uric = unreserved//reserved + character(len=*), parameter :: unwise = "{}|\^[]`" +#endif + + public :: URI + public :: parseURI + public :: expressURI + public :: isAbsoluteURI + public :: rebaseURI + public :: copyURI + public :: destroyURI + + public :: hasScheme + public :: getScheme + public :: hasAuthority + public :: getAuthority + public :: hasUserinfo + public :: getUserinfo + public :: hasHost + public :: getHost + public :: hasPort + public :: getPort + public :: getPath + public :: hasQuery + public :: getQuery + public :: hasFragment + public :: getFragment + +#ifndef DUMMYLIB + public :: dumpURI +#endif + +contains + +#ifndef DUMMYLIB + function unEscape_alloc(s) result(c) + character(len=*), intent(in) :: s + character, pointer :: c(:) + + integer :: i, j, n + character(len(s)) :: t + + c => null() + + i = 1 + j = 0 + do while (i<=len(s)) + j = j + 1 + if (s(i:i)=="%") then + if (i+2>len(s)) return + if (verify(s(i+1:i+2), hexdigit)/=0) return + n = str_to_int_16(s(i+1:i+2)) + t(j:j) = achar(n) + i = i + 3 + else + t(j:j) = s(i:i) + i = i + 1 + endif + enddo + + c => vs_str_alloc(t(:j)) + end function unEscape_alloc + + function verifyWithPctEncoding(s, chars) result(p) + character(len=*), intent(in) :: s + character(len=*), intent(in) :: chars + logical :: p + + integer :: i + + p = .false. + i = 1 + do while (i<=len(s)) + if (s(i:i)=="%") then + if (i+2>len(s)) return + if (verify(s(i+1:i+2), hexdigit)>0) return + i = i + 3 + else + if (verify(s(i:i),chars)>0) return + i = i + 1 + endif + enddo + p = .true. + end function verifyWithPctEncoding + + pure function pctEncode_len(s, chars) result(n) + character(len=*), intent(in) :: s + character(len=*), intent(in) :: chars + integer :: n + + integer :: i + n = 0 + do i = 1, len(s) + n = n + 1 + if (verify(s(i:i), unwise)==0.or.verify(s(i:i), chars)>0) n = n + 2 + enddo + + end function pctEncode_len + + function pctEncode(s, chars) result(ps) + character(len=*), intent(in) :: s + character(len=*), intent(in) :: chars + character(len=pctEncode_len(s, chars)) :: ps + + integer :: i, n + + n = 1 + do i = 1, len(s) + if (verify(s(i:i), unwise)==0.or.verify(s(i:i), chars)>0) then + ps(n:n+2) = "%"//str(iachar(s(i:i)), "x2") + n = n + 3 + else + ps(n:n) = s(i:i) + n = n + 1 + endif + enddo + + end function pctEncode + + function checkOpaquePart(part) result(p) + character(len=*), intent(in) :: part + logical :: p + + if (len(part)>0) then + p = verify(part(1:1), uric_no_slash)==0 + if (p.and.len(part)>1) & + p = verify(part(1:1), uric)==0 + endif + end function checkOpaquePart + + + function checkScheme(scheme) result(p) + character(len=*), intent(in) :: scheme + logical :: p + + p = len(scheme)>0 + if (p) then + p = verify(scheme(1:1), lowalpha//upalpha)==0 + if (p.and.len(scheme)>1) then + p = verify(scheme(2:), alphanum//"+-.")==0 + endif + endif + end function checkScheme + + function checkIpvX(host) result(p) + character(len=*), intent(in) :: host + logical :: p + + integer :: i, n1, n2 + + p = (len(host)>5).and.(host(1:1)=="[".and.host(len(host):len(host))=="]") + + if (p) then + + ! Try IPvFuture: + p = (verify(host(2:2),"Vv")==0 & + .and.verify(host(3:3),hexdigit)==0 & + .and.host(4:4)=="." & + .and.verify(host(3:3),unreserved//sub_delims//":")==0) + + if (.not.p) then ! is it IPv6? + n1 = 0 + do i = 1, 4 + n2 = index(host(n1+1:), ":") + if (n2==0.or.n2>6) return + n2 = n2 + n1 + if (verify(host(n1+1:n2-1),hexdigit)>0) return + n1 = n2 + enddo + n2 = index(host(n1+1:), ":") + if (n2==0) then + ! this must be ipv4 format + do i = 1, 3 + n2 = index(host(n1+1:), ".") + if (n2==0) return + n2 = n2 + n1 + if (verify(host(n1+1:n2-1),digit)>0) return + if (str_to_int_10(host(n1+1:n2-1))>255) return + n1 = n2 + enddo + ! Now there must be 3 or less digits followed by ] + n2 = len(host)-1 + if (verify(host(n1+1:n2-1),digit)>0) return + if (str_to_int_10(host(n1+1:n2-1))>255) return + elseif (n2<6) then + n2 = n2 + n1 + if (verify(host(n1+1:n2-1),hexdigit)>0) return + ! Now there must be 4 or less digits followed by ] + n1 = n2 + n2 = len(host) + if (n2-n1>4) return + if (verify(host(n1+1:n2-1),hexdigit)>0) return + endif + p = .true. + endif + endif + end function checkIpvX + + function checkHost(host) result(p) + character(len=*), intent(in) :: host + logical :: p + + p = checkIpvX(host) + if (.not.p) & + p = verifyWithPctEncoding(host, unreserved//sub_delims) + + end function checkHost + + function checkAuthority(authority, userinfo, host, port) result(p) + character(len=*), intent(in) :: authority + character, pointer :: userinfo(:), host(:) + integer :: port + logical :: p + + integer :: i1, i2 + + p = .true. + if (len(authority)==0) return + + i1 = index(authority, "@") + if (i1>0) then + i2 = index(authority(i1+1:), ":") + else + i2 = index(authority, ":") + endif + if (i1==0) then + userinfo => null() + else + p = verifyWithPctEncoding(authority(:i1-1), unreserved//sub_delims//":") + if (p) userinfo => unEscape_alloc(authority(:i1-1)) + endif + if (i2==0) then + i2 = len(authority)+1 + else + i2 = i1 + i2 + p = p.and.verify(authority(i2+1:), digit)==0 + if (p) port = str_to_int_10(authority(i2+1:)) + endif + p = p.and.checkHost(authority(i1+1:i2-1)) + if (p) then + host => vs_str_alloc(authority(i1+1:i2-1)) + else + if (associated(userinfo)) deallocate(userinfo) + end if + + end function checkAuthority + + function checkPathSegment(segment) result(p) + character(len=*), intent(in) :: segment + logical :: p + + integer :: i1 + + i1 = index(segment, ";") + if (i1>0) then + p = verifyWithPctEncoding(segment(:i1-1), pchar) & + .and.verifyWithPctEncoding(segment(i1+1:), pchar) + else + p = verifyWithPctEncoding(segment, unreserved//pchar) + endif + end function checkPathSegment + + function checkNonOpaquePath(path, segments) result(p) + character(len=*), intent(in) :: path + type(path_segment), pointer :: segments(:) + logical :: p + + integer :: i, i1, i2 + type(path_segment), pointer :: temp(:) + + p = .true. + i1 = index(path, "/") + if (i1==1) then + allocate(segments(1)) + segments(1)%s => vs_str_alloc("/") + else + allocate(segments(0)) + i1 = 0 + endif + + do + i2 = index(path(i1+1:), "/") + if (i2==0) then + i2 = len(path) + else + i2 = i1 + i2 + endif + if (checkPathSegment(path(i1+1:i2-1))) then + allocate(temp(size(segments)+1)) + do i = 1, size(segments) + temp(i)%s => segments(i)%s + enddo + temp(i)%s => unEscape_alloc(path(i1+1:i2)) + deallocate(segments) + segments => temp + else + do i = 1, size(segments) + deallocate(segments(i)%s) + enddo + deallocate(segments) + p = .false. + return + endif + if (i2==len(path)) exit + i1 = i2 + end do + end function checkNonOpaquePath + + function checkPath(path, segments) result(p) + character(len=*), intent(in) :: path + type(path_segment), pointer :: segments(:) + logical :: p + + p = checkNonOpaquePath(path, segments) + if (.not.p) then + p = checkOpaquePart(path) + if (p) allocate(segments(0)) + endif + + end function checkPath + + function checkQuery(query) result(p) + character(len=*), intent(in) :: query + logical :: p + + p = verifyWithPctEncoding(query, uric) + end function checkQuery + + function checkFragment(fragment) result(p) + character(len=*), intent(in) :: fragment + logical :: p + + p = verifyWithPctEncoding(fragment, uric) + end function checkFragment +#endif + + function parseURI(URIstring) result(u) + character(len=*), intent(in) :: URIstring + type(URI), pointer :: u +#ifndef DUMMYLIB + character, pointer, dimension(:) :: scheme, authority, & + userinfo, host, path, query, fragment + integer :: port + type(path_segment), pointer :: segments(:) + integer :: i1, i2, i3, i4 + logical :: p + +#endif + u => null() +#ifndef DUMMYLIB + + scheme => null() + authority => null() + userinfo => null() + host => null() + port = -1 + path => null() + segments => null() + query => null() + fragment => null() + + i1 = index(URIstring, ":") + if (i1>0) then + p = checkScheme(URIstring(:i1-1)) + if (p) then + scheme => vs_str_alloc(toLower(URIstring(:i1-1))) + else + i1 = 0 + endif + endif + ! if either i1==0 or the scheme doesn't validate, there is no scheme.. + if (len(URIstring)>=i1+3) then + if (URIstring(i1+1:i1+2)=="//") then + i2 = scan(URIstring(i1+3:), "/#?") + if (i2==0) then + i2 = len(URIstring) + 1 + else + i2 = i1 + i2 + 2 + endif + p = checkAuthority(URIstring(i1+3:i2-1), userinfo, host, port) + if (.not.p) then + call cleanUp + return + endif + authority => vs_str_alloc(URIstring(i1+3:i2-1)) + else + i2 = i1 + 1 + endif + else + i2 = i1 + 1 + endif + + if (i2>len(URIstring)) then + path => vs_str_alloc("") + allocate(segments(1)) + segments(1)%s => vs_str_alloc("") + call produceResult + return + endif + + i3 = scan(URIstring(i2:),"#?") + if (i3==0) then + i3 = len(URIstring) + 1 + else + i3 = i2 + i3 - 1 + endif + p = checkPath(URIstring(i2:i3-1), segments) + if (.not.p) then + call cleanUp + return + endif + path => unEscape_alloc(URIstring(i2:i3-1)) + + if (i3>len(URIstring)) then + call produceResult + return + endif + + if (URIstring(i3:i3)=="?") then + i4 = index(URIstring(i3+1:), "#") + if (i4==0) then + i4 = len(URIstring) + 1 + else + i4 = i3 + i4 + endif + p = checkQuery(URIstring(i3+1:i4-1)) + if (.not.p) then + call cleanUp + return + endif + query => vs_str_alloc(URIstring(i3+1:i4-1)) + else + i4 = i3 + endif + + if (i4>len(URIstring)) then + call produceResult + return + endif + + p = checkFragment(URIstring(i4+1:)) + if (.not.p) then + call cleanUp + return + endif + fragment => vs_str_alloc(URIstring(i4+1:)) + call produceResult + + contains + subroutine cleanUp + integer :: i + if (associated(scheme)) deallocate(scheme) + if (associated(authority)) deallocate(authority) + if (associated(userinfo)) deallocate(userinfo) + if (associated(host)) deallocate(host) + if (associated(path)) deallocate(path) + if (associated(query)) deallocate(query) + if (associated(fragment)) deallocate(fragment) + if (associated(segments)) then + do i = 1, size(segments) + deallocate(segments(i)%s) + enddo + deallocate(segments) + endif + end subroutine cleanUp + subroutine produceResult + allocate(u) + u%scheme => scheme + u%authority => authority + u%userinfo => userinfo + u%host => host + u%port = port + u%path => path + u%segments => segments + u%query => query + u%fragment => fragment + u%segments => segments + end subroutine produceResult +#endif + end function parseURI + + function isAbsoluteURI(u) result(p) + type(URI), intent(in) :: u + logical :: p + +#ifdef DUMMYLIB + p = .false. +#else + p = associated(u%scheme).or.associated(u%authority) + if (.not.p.and.size(u%segments(1)%s)>0) then + p = u%segments(1)%s(1)=="/" + endif +#endif + end function isAbsoluteURI + + function rebaseURI(u1, u2) result(u3) + type(URI), pointer :: u1, u2 + type(URI), pointer :: u3 + + u3 => null() +#ifndef DUMMYLIB + + if (associated(u2%scheme).or.associated(u2%authority)) then + u3 => copyURI(u2) + return + endif + + allocate(u3) + if (associated(u1%scheme)) u3%scheme => vs_vs_alloc(u1%scheme) + if (associated(u1%authority)) u3%authority => vs_vs_alloc(u1%authority) + + u3%segments => appendPaths(u1%segments, u2%segments) + u3%path => expressSegments(u3%segments) + + if (associated(u2%query)) u3%query => vs_vs_alloc(u2%query) + if (associated(u2%fragment)) u3%fragment => vs_vs_alloc(u2%fragment) +#endif + end function rebaseURI + +#ifndef DUMMYLIB + function appendPaths(seg1, seg2) result(seg3) + type(path_segment), pointer :: seg1(:), seg2(:) + type(path_segment), pointer :: seg3(:) + + type(path_segment), pointer :: temp(:) + + integer :: i, n, n2 + + if (size(seg2(1)%s)==0) then + seg3 => normalizePath(seg1) + return + elseif (seg2(1)%s(1)=="/") then + seg3 => normalizePath(seg2) + return + endif + + n = size(seg1) + size(seg2) + i = size(seg1) + if (seg1(i)%s(size(seg1(i)%s))/="/") & + n = n - 1 + + allocate(temp(n)) + n2 = 1 + do i = 1, size(seg1) + if (i==size(seg1).and.seg1(i)%s(size(seg1(i)%s))/="/") exit ! it's a file + temp(n2)%s => vs_vs_alloc(seg1(i)%s) + n2 = n2 + 1 + enddo + + do i = 1, size(seg2) + temp(n2)%s => vs_vs_alloc(seg2(i)%s) + n2 = n2 + 1 + enddo + + seg3 => normalizePath(temp) + do i = 1, size(temp) + deallocate(temp(i)%s) + enddo + deallocate(temp) + + end function appendPaths + + function normalizepath(seg1) result(seg2) + type(path_segment), pointer :: seg1(:) + type(path_segment), pointer :: seg2(:) + + integer :: i, n, n2, parents + character, pointer :: tmp(:) + + + ! If the last of the input segments are + ! equal to '.' or '..', append a slash + ! so the rest of the subroutine works. + + if ((str_vs(seg1(size(seg1))%s) == '.').or. & + (str_vs(seg1(size(seg1))%s) == '..')) then + tmp => vs_vs_alloc(seg1(size(seg1))%s) + deallocate(seg1(size(seg1))%s) + seg1(size(seg1))%s => vs_str_alloc(str_vs(tmp)//"/") + deallocate(tmp) + endif + + n = 0 + parents = 0 + do i = 1, size(seg1) + if (str_vs(seg1(i)%s)//"x"=="./x") then + continue + elseif (str_vs(seg1(i)%s)//"x"=="../x") then + if (n>0) then + n = n - 1 + else + parents = parents + 1 + endif + else + n = n + 1 + endif + enddo + + n = n + parents + allocate(seg2(n)) + + n2 = parents + do i = 1, parents + seg2(i)%s => vs_str_alloc("../") + enddo + do i = 1, size(seg1) + if (str_vs(seg1(i)%s)//"x"=="./x") then + continue + elseif (str_vs(seg1(i)%s)//"x"=="../x") then + if (n2>parents) then + if (n2<=n) deallocate(seg2(n2)%s) + n2 = n2 - 1 + endif + else + n2 = n2 + 1 + if (n2>0.and.n2<=n) & + seg2(n2)%s => vs_vs_alloc(seg1(i)%s) + endif + enddo + + end function normalizepath + + function expressSegments(seg1) result(s) + type(path_segment), pointer :: seg1(:) + character, pointer :: s(:) + + integer :: i, n + + n = 0 + + do i = 1, size(seg1) + n = n + size(seg1(i)%s) + enddo + allocate(s(n)) + n = 1 + do i = 1, size(seg1) + s(n:n+size(seg1(i)%s)-1) = seg1(i)%s + n = n + size(seg1(i)%s) + enddo + end function expressSegments + + pure function expressURI_len(u) result(n) + type(URI), intent(in) :: u + integer :: n + + n = 0 + if (associated(u%scheme)) & + n = size(u%scheme) + 1 + if (associated(u%authority)) & + n = n + pctEncode_len(str_vs(u%authority), unreserved//sub_delims//"@:") + 2 + !FIXME - I suspect that ';' as the first character of a segment should be escaped + n = n + pctEncode_len(str_vs(u%path), pchar//";"//"/") + if (associated(u%query)) & + n = n + pctEncode_len(str_vs(u%query), uric) + 1 + if (associated(u%fragment)) & + n = n + pctEncode_len(str_vs(u%fragment), uric) + 1 + + end function expressURI_len + + function expressURI(u) result(URIstring) + type(URI), intent(in) :: u + character(len=expressURI_len(u)) :: URIstring + + integer :: i, j + URIstring="" + i = 1 + if (associated(u%scheme)) then + URIstring(:size(u%scheme)+1) = str_vs(u%scheme)//":" + i = i + size(u%scheme) + 1 + endif + if (associated(u%authority)) then + j = pctEncode_len(str_vs(u%authority), unreserved//sub_delims//"@:") + URIstring(i:i+j+1) = & + "//"//pctEncode(str_vs(u%authority), unreserved//sub_delims//"@:") + i = i + j + 2 + endif + if (size(u%path)>0) then + !FIXME - I suspect that ';' as the first character of a segment should be escaped + j = pctEncode_len(str_vs(u%path), pchar//";"//"/") + URIstring(i:i+j-1) = pctEncode(str_vs(u%path), pchar//";"//"/") + i = i + j + endif + if (associated(u%query)) then + j = pctEncode_len(str_vs(u%query), uric) + URIstring(i:i+j) = "?"//pctEncode(str_vs(u%query), uric) + i = i + j + 1 + endif + if (associated(u%fragment)) then + j = pctEncode_len(str_vs(u%fragment), uric) + URIstring(i:i+j) = "#"//pctEncode(str_vs(u%fragment), uric) + endif + + end function expressURI + + subroutine dumpURI(u) + type(URI), intent(in) :: u + integer :: i + if (associated(u%scheme)) then + write(*,*) "scheme: ", str_vs(u%scheme) + else + write(*,*) "scheme UNDEFINED" + endif + if (associated(u%authority)) then + write(*,*) "authority: ", str_vs(u%authority) + else + write(*,*) "authority UNDEFINED" + endif + if (associated(u%userinfo)) then + write(*,*) "userinfo: ", str_vs(u%userinfo) + else + write(*,*) "userinfo UNDEFINED" + endif + if (associated(u%host)) then + write(*,*) "host: ", str_vs(u%host) + else + write(*,*) "host UNDEFINED" + endif + if (u%port>0) then + write(*,*) "port: ", str(u%port) + else + write(*,*) "port UNDEFINED" + endif + if (associated(u%path)) then + write(*,*) "path: ", str_vs(u%path) + else + write(*,*) "path UNDEFINED" + endif + if (associated(u%segments)) then + do i = 1, size(u%segments) + write(*,*) " segment: ", str_vs(u%segments(i)%s) + enddo + endif + if (associated(u%query)) then + write(*,*) "query: ", str_vs(u%query) + else + write(*,*) "query UNDEFINED" + endif + if (associated(u%fragment)) then + write(*,*) "fragment: ", str_vs(u%fragment) + else + write(*,*) "fragment UNDEFINED" + endif + end subroutine dumpURI +#endif + + function copyURI(u1) result(u2) + type(URI), pointer :: u1 + type(URI), pointer :: u2 +#ifndef DUMMYLIB + integer :: i + + if (.not.associated(u1)) then +#endif + u2 => null() +#ifndef DUMMYLIB + return + endif + allocate(u2) + u2%scheme => vs_vs_alloc(u1%scheme) + u2%authority => vs_vs_alloc(u1%authority) + u2%userinfo => vs_vs_alloc(u1%userinfo) + u2%host => vs_vs_alloc(u1%host) + u2%port = u1%port + u2%path => vs_vs_alloc(u1%path) + allocate(u2%segments(size(u1%segments))) + do i = 1, size(u1%segments) + u2%segments(i)%s => vs_vs_alloc(u1%segments(i)%s) + enddo + u2%query => vs_vs_alloc(u1%query) + u2%fragment => vs_vs_alloc(u1%fragment) +#endif + end function copyURI + + + subroutine destroyURI(u) + type(URI), pointer :: u +#ifndef DUMMYLIB + integer :: i + if (associated(u%scheme)) deallocate(u%scheme) + if (associated(u%authority)) deallocate(u%authority) + if (associated(u%userinfo)) deallocate(u%userinfo) + if (associated(u%host)) deallocate(u%host) + if (associated(u%path)) deallocate(u%path) + if (associated(u%segments)) then + do i = 1, size(u%segments) + deallocate(u%segments(i)%s) + enddo + deallocate(u%segments) + endif + if (associated(u%query)) deallocate(u%query) + if (associated(u%fragment)) deallocate(u%fragment) + + deallocate(u) +#endif + end subroutine destroyURI + + function hasScheme(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%scheme) +#endif + end function hasScheme + + function getScheme(u) result(s) + type(URI), pointer :: u + +#ifndef DUMMYLIB + character(len=size(u%scheme)) :: s + s = str_vs(u%scheme) +#else + character(len=1) :: s + s = "" +#endif + end function getScheme + + function hasAuthority(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%authority) +#endif + end function hasAuthority + + function getAuthority(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%authority)) :: s + s = str_vs(u%authority) +#else + character(len=1) :: s + s = "" +#endif + end function getAuthority + + function hasUserinfo(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%userinfo) +#endif + end function hasUserinfo + + function getUserinfo(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%userinfo)) :: s + s = str_vs(u%userinfo) +#else + character(len=1) :: s + s = "" +#endif + end function getUserinfo + + function hasHost(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%host) +#endif + end function hasHost + + function getHost(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%host)) :: s + s = str_vs(u%host) +#else + character(len=1) :: s + s = "" +#endif + end function getHost + + function hasPort(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = u%port > 0 +#endif + end function hasPort + + function getPort(u) result(n) + type(URI), pointer :: u + integer :: n +#ifndef DUMMYLIB + n = u%port +#else + n = 0 +#endif + end function getPort + + function getPath(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%path)) :: s + s = str_vs(u%path) +#else + character(len=1) :: s + s = "" +#endif + end function getPath + + function hasQuery(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%query) +#endif + end function hasQuery + + function getQuery(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%query)) :: s + s = str_vs(u%query) +#else + character(len=1) :: s + s = "" +#endif + end function getQuery + + function hasFragment(u) result(p) + type(URI), pointer :: u + logical :: p + + p = .false. +#ifndef DUMMYLIB + if (.not.associated(u)) return + p = associated(u%fragment) +#endif + end function hasFragment + + function getFragment(u) result(s) + type(URI), pointer :: u +#ifndef DUMMYLIB + character(len=size(u%fragment)) :: s + s = str_vs(u%fragment) +#else + character(len=1) :: s + s = "" +#endif + end function getFragment + +end module fox_m_utils_uri diff --git a/src/xml/utils/fox_m_utils_uuid.F90 b/src/xml/utils/fox_m_utils_uuid.F90 new file mode 100644 index 000000000..fd9c272d9 --- /dev/null +++ b/src/xml/utils/fox_m_utils_uuid.F90 @@ -0,0 +1,247 @@ +module fox_m_utils_uuid + + !This generates UUIDs according to RFC 4122 + + ! Only types 1 (time-based) and 4 (pseudo-RNG-based) are implemented. + +#ifndef DUMMYLIB + use fox_m_utils_mtprng, only : mtprng_state, mtprng_init, mtprng_rand64 +#endif + + implicit none + private + +#ifndef DUMMYLIB + integer, parameter :: i4b = selected_int_kind(9) + integer, parameter :: i8b = selected_int_kind(18) + + character, parameter :: hexdigits(0:15) = & + (/'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'/) + + type(mtprng_state), save :: rng_state + logical, save :: initialized = .false. + integer, save :: values_save ! must be default for date_and_time + integer(kind=i4b), save :: hires_count = 0 + +! clock-seq holds a random number constant for the lifetime of the program +! using this module. That's the best we can do per S 4.1.5 + integer, save :: clock_seq = 0 + +#endif + + public :: generate_uuid + +contains + + function generate_uuid(version) result(uuid) + integer, intent(in), optional :: version + character(len=36) :: uuid + +#ifdef DUMMYLIB + uuid = "" +#else + integer(kind=i8b) :: timestamp, node + integer(kind=i4b) :: clock_sequence + + integer(kind=i4b) :: time_low, time_mid, time_hi_and_version + integer(kind=i4b) :: clk_seq_hi_res, clk_seq_low + + integer :: values(8) ! must be default for date_and_time + integer(kind=i4b) :: variant, v + + + if (.not.initialized) then + ! Use the current date and time to init mtprng + ! but this gives limited varaibility, so mix + ! the result up. Can we do better? In any + ! case, this gets passed through a quick + ! generator inside mtprng_init. + call date_and_time(values=values) + values(7) = values(7)*1000+values(5)*100+values(3)*10+values(1) + values(8) = values(2)*1000+values(4)*100+values(6)*10+values(8) + call mtprng_init(int(values(7)*10000+values(8), i4b), rng_state) + clock_seq = int(mtprng_rand64(rng_state), i4b) + initialized = .true. + endif + + variant = 1 + + if (present(version)) then + v = version + else + v = 4 + endif + + select case (v) + case (0) + ! Nil UUID - S 4.1.7 + uuid = repeat('0',8)//'-'//repeat('0',4)//'-'//repeat('0',4)// & + '-'//repeat('0',4)//'-'//repeat('0',12) + return + case(1) + call date_and_time(values=values) + ! In case of too-frequent requests, we will replace time_low + ! with the count below ... + if (all(values==values_save)) then + hires_count = hires_count + 1 + else + hires_count = 0 + endif + case(2-3) + !Unimplemented + uuid = '' + return + case(4) + continue + case(5) + !Unimplemented + uuid = '' + return + case default + !Unspecified + uuid = '' + return + end select + +!4.1.4 Timestamp + + select case(v) + case(1) + timestamp = get_utc_since_1582(values) + case(4) + timestamp = ior(mtprng_rand64(rng_state), ishft(mtprng_rand64(rng_state), 28)) + end select + +!4.1.5 Clock Sequence + ! 14 bits + select case(v) + case(1) + clock_sequence = clock_seq + case(4) + clock_sequence = int(mtprng_rand64(rng_state), i4b) + end select + +!4.1.6 Node + ! 48 bits + select case(v) + case(1) + node = ior(mtprng_rand64(rng_state), ishft(mtprng_rand64(rng_state), 16)) + ! No MAC address accessible - see section 4.5 !FIXME + case(4) + node = ior(mtprng_rand64(rng_state), ishft(mtprng_rand64(rng_state), 16)) + end select + + time_low = ibits(timestamp, 0, 32) + time_mid = ibits(timestamp, 32, 16) + if (hires_count==0) then + time_hi_and_version = ior(int(ibits(timestamp, 48, 12), i4b), ishft(v, 12)) + else + time_hi_and_version = ior(hires_count, ishft(v, 12)) + endif + + clk_seq_low = ibits(clock_sequence, 0, 8) + clk_seq_hi_res = ior(ibits(clock_sequence, 8, 6), ishft(variant, 6)) + + uuid = int32ToHexOctets(time_low, 4)//"-"// & + int32ToHexOctets(time_mid, 2)//"-"// & + int32ToHexOctets(time_hi_and_version, 2)//"-"// & + int32ToHexOctets(clk_seq_hi_res, 1)// & + int32ToHexOctets(clk_seq_low, 1)//"-"// & + int64ToHexOctets(node, 6) + + contains + + function int32ToHexOctets(b, n) result(s) + integer(i4b), intent(in) :: b + integer, intent(in) :: n ! number of octets to print + character(len=2*n) :: s + + integer :: i + + do i = 0, 2*n-1 + s(2*n-i:2*n-i) = hexdigits(ibits(b, i*4, 4)) + enddo + + end function int32ToHexOctets + function int64ToHexOctets(b, n) result(s) + integer(i8b), intent(in) :: b + integer, intent(in) :: n ! number of octets to print + character(len=2*n) :: s + + integer :: i + + do i = 0, 2*n-1 + s(2*n-i:2*n-i) = hexdigits(ibits(b, i*4, 4)) + enddo + + end function int64ToHexOctets + +#endif + end function generate_uuid + +#ifndef DUMMYLIB + function get_utc_since_1582(values) result(ns) + ! This subroutine is a little broken. It only works + ! for times after 1/1/2006 and takes no account + ! of any future leapseconds. It ought to serve regardless. + + ! It returns the number of 100-ns intervals since 1582-10-15-00-00-00 + + integer, dimension(8), intent(in) :: values + integer(kind=i8b) :: ns + + integer :: days + integer :: years + + integer, parameter :: days_in_normal_year(12) = & + (/31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31/) + + ns = 23_i8b * 1000_i8b * 1000_i8b * 10_i8b ! 23 leap seconds until 24:00:00 31/12/2005 + + ! A count of the 100-nanosecond intervals since the + ! beginning of the day. + ns = ns & + ! milliseconds + + int(values(8), i8b) * 10_i8b * 1000_i8b & + ! seconds + + int(values(7), i8b) * 10_i8b * 1000_i8b * 1000_i8b & + ! minutes (with timezone adjustment) + + int(values(6) + values(4), i8b) * 10_i8b * 1000_i8b * 1000_i8b * 60_i8b & + ! hours + + int(values(5), i8b) * 10_i8b * 1000_i8b * 1000_i8b * 60_i8b * 60_i8b + + ! Number of days this year: + days = sum(days_in_normal_year(:values(2)-1)) + days = days + values(3) - 1 !add days in current month + if (values(2)>2 .and. isLeapYear(values(1))) then + days = days + 1 + endif + !That's all the time since the turn of this year + + days = days + 78 ! From the start of 15th Oct to the end of 31st Dec in 1582 + !That's the additional time before the turn of the year 1583 + + days = days + 102 ! 102 leap years from 1584 to 2000 inclusive + ! That's all the intercalataed days until 2000 + + years = values(1) - 2000 - 1 ! years since 2000 - not including this year + + days = days + years/4 - years/100 + years/400 !Add extra leap days to this total: + ! That's all out intercalated days - remaining years are all 365 days long. + + years = years + 418 ! Add the years from 1583-2000 inclusive back on. + + ! Multiply by number of time units in one day & add to today's total. + ns = ns + 864000000000_i8b * (int(days,i8b) + 365_i8b * int(years,i8b)) + + contains + function isLeapYear(y) result(p) + integer, intent(in) :: y + logical :: p + p = (mod(y,4)==0 .and. .not.mod(y,100)==0 .or. mod(y,400)==0) + end function isLeapYear + + end function get_utc_since_1582 + +#endif +end module fox_m_utils_uuid diff --git a/src/xml/utils/makefile b/src/xml/utils/makefile new file mode 100644 index 000000000..dca350436 --- /dev/null +++ b/src/xml/utils/makefile @@ -0,0 +1,45 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../fsys $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_utils.o: fox_m_utils_uuid.o fox_m_utils_uri.o +fox_m_utils_uuid.o: fox_m_utils_mtprng.o diff --git a/src/xml/wxml/FoX_wxml.F90 b/src/xml/wxml/FoX_wxml.F90 new file mode 100644 index 000000000..41c1e5652 --- /dev/null +++ b/src/xml/wxml/FoX_wxml.F90 @@ -0,0 +1,43 @@ +module FoX_wxml + + use m_wxml_core + use m_wxml_overloads + + implicit none + private + + public :: xmlf_t + public :: xml_OpenFile + public :: xml_Close + public :: xml_NewElement + public :: xml_EndElement + public :: xml_AddXMLDeclaration + public :: xml_AddXMLStylesheet + public :: xml_AddXMLPI + public :: xml_AddComment + public :: xml_AddEntityReference + + public :: xml_AddCharacters + public :: xml_AddNewline + public :: xml_AddAttribute + public :: xml_AddPseudoAttribute + + public :: xml_DeclareNamespace + public :: xml_UndeclareNamespace + + public :: xml_AddDOCTYPE + public :: xml_AddParameterEntity + public :: xml_AddInternalEntity + public :: xml_AddExternalEntity + public :: xml_AddNotation + public :: xml_AddElementToDTD + public :: xml_AddAttlistToDTD + public :: xml_AddPEreferenceToDTD + + public :: xmlf_GetPretty_print + public :: xmlf_SetPretty_print + + public :: xmlf_Name + public :: xmlf_OpenTag + +end module FoX_wxml diff --git a/src/xml/wxml/m_wxml_core.F90 b/src/xml/wxml/m_wxml_core.F90 new file mode 100644 index 000000000..2afd30b06 --- /dev/null +++ b/src/xml/wxml/m_wxml_core.F90 @@ -0,0 +1,1848 @@ +module m_wxml_core + +#ifndef DUMMYLIB + use fox_m_fsys_abort_flush, only: pxfabort + use fox_m_fsys_array_str, only: vs_str, str_vs, vs_str_alloc + use fox_m_fsys_string, only: toLower + use fox_m_utils_uri, only: URI, parseURI, destroyURI + use m_common_attrs, only: dictionary_t, getLength, get_key, get_value, & + hasKey, add_item_to_dict, init_dict, reset_dict, destroy_dict, & + getWhitespaceHandling, sortAttrs + use m_common_buffer, only: buffer_t, len, add_to_buffer, reset_buffer, & + dump_buffer + use m_common_charset, only: XML1_0, XML1_1, checkChars + use m_common_element, only: parse_dtd_element, parse_dtd_attlist + use m_common_elstack, only: elstack_t, len, get_top_elstack, pop_elstack, & + is_empty, init_elstack, push_elstack, destroy_elstack + use m_common_entities, only: existing_entity, is_unparsed_entity + use m_common_error, only: FoX_warning_base, FoX_error_base, FoX_fatal_base, & + error_stack, in_error, FoX_get_fatal_errors, FoX_get_fatal_warnings + use m_common_io, only: get_unit + use m_common_namecheck, only: checkEncName, checkName, checkQName, & + checkCharacterEntityReference, checkPublicId, prefixOfQName, & + localpartofQName, checkPEDef, checkPseudoAttValue, checkAttValue, checkNCName, & + likeCharacterEntityReference, checkCharacterEntityReference + use m_common_namespaces, only: namespaceDictionary, getnamespaceURI, & + initnamespaceDictionary, addDefaultNS, destroyNamespaceDictionary, & + addPrefixedNS, isPrefixInForce, checkNamespacesWriting, checkEndNamespaces + use m_common_notations, only: add_notation, notation_exists + use m_common_struct, only: xml_doc_state, init_xml_doc_state, destroy_xml_doc_state, & + register_internal_PE, register_external_PE, register_internal_GE, register_external_GE + use m_wxml_escape, only: escape_string +#ifdef PGF90 + use m_common_element, only : element_t +#endif +#endif + + implicit none + private + +#ifndef DUMMYLIB + integer, parameter :: indent_inc = 2 + ! TOHW should we let this be set? + + !Output State Machines + ! status wrt root element: + integer, parameter :: WXML_STATE_1_JUST_OPENED = 0 + !File is just opened, nothing written to it yet. + integer, parameter :: WXML_STATE_1_BEFORE_ROOT = 1 + !File has been opened, something has been written, but no root element yet. + integer, parameter :: WXML_STATE_1_DURING_ROOT = 2 + !The root element has been opened but not closed + integer, parameter :: WXML_STATE_1_AFTER_ROOT = 3 + !The root element has been opened but not closed + + ! status wrt tags: + integer, parameter :: WXML_STATE_2_OUTSIDE_TAG = 0 + !We are not within a tag. + integer, parameter :: WXML_STATE_2_INSIDE_PI = 1 + !We are inside a Processing Instruction tag + integer, parameter :: WXML_STATE_2_INSIDE_ELEMENT = 2 + !We are inside an element tag. + integer, parameter :: WXML_STATE_2_IN_CHARDATA = 3 + !We are inside deliberately-constructed text. (this is only necessary for preserve_whitespace) + + ! status wrt DTD + integer, parameter :: WXML_STATE_3_BEFORE_DTD = 0 + ! No DTD has been encountered yet. + integer, parameter :: WXML_STATE_3_DURING_DTD = 1 + ! Halfway throught outputting a DTD + integer, parameter :: WXML_STATE_3_INSIDE_INTSUBSET = 2 + !We are inside the internal subset definition + integer, parameter :: WXML_STATE_3_AFTER_DTD = 3 + ! Finished outputting a DTD +#endif + + + type xmlf_t + private +#ifdef DUMMYLIB + integer :: i = 0 +#else + type(xml_doc_state) :: xds + integer :: lun = -1 + type(buffer_t) :: buffer + type(elstack_t) :: stack + type(dictionary_t) :: dict + integer :: state_1 = -1 + integer :: state_2 = -1 + integer :: state_3 = -1 + logical :: minimize_overrun = .true. + logical :: pretty_print = .false. + logical :: canonical = .false. + integer :: indent = 0 + character, pointer :: name(:) + logical :: namespace = .true. + type(namespaceDictionary) :: nsDict +#endif + end type xmlf_t + + public :: xmlf_t + + public :: xml_OpenFile + public :: xml_NewElement + public :: xml_EndElement + public :: xml_Close + public :: xml_AddXMLDeclaration + public :: xml_AddXMLStylesheet + public :: xml_AddXMLPI + public :: xml_AddComment + public :: xml_AddCharacters + public :: xml_AddNewline + public :: xml_AddEntityReference + public :: xml_AddAttribute + public :: xml_AddPseudoAttribute + public :: xml_DeclareNamespace + public :: xml_UnDeclareNamespace + public :: xml_AddDOCTYPE + public :: xml_AddParameterEntity + public :: xml_AddInternalEntity + public :: xml_AddExternalEntity + public :: xml_AddNotation + public :: xml_AddElementToDTD + public :: xml_AddAttlistToDTD + public :: xml_AddPEreferenceToDTD + + public :: xmlf_Name + public :: xmlf_OpenTag + + public :: xmlf_SetPretty_print + public :: xmlf_GetPretty_print + + interface xml_AddCharacters + module procedure xml_AddCharacters_Ch + end interface + interface xml_AddAttribute + module procedure xml_AddAttribute_Ch + end interface + interface xml_AddPseudoAttribute + module procedure xml_AddPseudoAttribute_Ch + end interface + +#ifndef DUMMYLIB + !overload error handlers to allow file info + interface wxml_warning + module procedure wxml_warning_xf, FoX_warning_base + end interface + interface wxml_error + module procedure wxml_error_xf, FoX_error_base + end interface + interface wxml_fatal + module procedure wxml_fatal_xf, FoX_fatal_base + end interface + + ! Heuristic (approximate) target for justification of output + ! only gets used for outputting attributes + integer, parameter :: COLUMNS = 80 + + ! TOHW - This is the longest string that may be output without + ! a newline. The buffer must not be larger than this, but its size + ! can be tuned for performance. + !lowest value found so far is 4096, for NAG. We use 1024 just in case. + integer, parameter :: xml_recl = 1024 +#endif + +contains + + subroutine xml_OpenFile(filename, xf, unit, iostat, preserve_whitespace, & + pretty_print, minimize_overrun, canonical, replace, addDecl, warning, & + validate, namespace) + character(len=*), intent(in) :: filename + type(xmlf_t), intent(inout) :: xf + integer, intent(in), optional :: unit + integer, intent(out), optional :: iostat + logical, intent(in), optional :: preserve_whitespace + logical, intent(in), optional :: pretty_print + logical, intent(in), optional :: minimize_overrun + logical, intent(in), optional :: canonical + logical, intent(in), optional :: replace + logical, intent(in), optional :: addDecl + logical, intent(in), optional :: warning + logical, intent(in), optional :: validate + logical, intent(in), optional :: namespace + +#ifdef DUMMYLIB + if (present(iostat)) iostat = 0 +#else + logical :: repl, decl + integer :: iostat_ + + if (xf%lun /= -1) & + call wxml_fatal("Trying to reopen an already-open XML file") + + if (present(replace)) then + repl = replace + else + repl = .true. + endif + if (present(addDecl)) then + decl = addDecl + else + decl = .true. + endif + if (present(iostat)) iostat = 0 + + allocate(xf%name(0)) + + if (present(unit)) then + if (unit==-1) then + call get_unit(xf%lun,iostat_) + if (iostat_ /= 0) then + if (present(iostat)) iostat = iostat_ + return + endif + else + xf%lun = unit + endif + else + call get_unit(xf%lun,iostat_) + if (iostat_ /= 0) then + if (present(iostat)) iostat = iostat_ + return + endif + endif + + ! Use large I/O buffer in case the O.S./Compiler combination + ! has hard-limits by default (i.e., NAGWare f95's 1024 byte limit) + ! This is related to the maximum size of the buffer. + ! TOHW - This is the longest string that may be output without + ! a newline. The buffer must not be larger than this, but its size + ! can be tuned for performance. + + if (repl) then + ! NAG insists on unnecessary duplication of iostat etc here + if (present(iostat)) then + open(unit=xf%lun, file=filename, form="formatted", status="replace", & + action="write", recl=xml_recl, iostat=iostat) + else + open(unit=xf%lun, file=filename, form="formatted", status="replace", & + action="write", recl=xml_recl) + endif + else + if (present(iostat)) then + open(unit=xf%lun, file=filename, form="formatted", status="new", & + action="write", recl=xml_recl, iostat=iostat) + else + open(unit=xf%lun, file=filename, form="formatted", status="new", & + action="write", recl=xml_recl) + endif + endif + + call init_elstack(xf%stack) + + call init_dict(xf%dict) + !NB it can make no difference which XML version we are using + !until after we output the XML declaration. So we set it to + !1.0 for the moment & reset below. + ! Actually, this is done automatically in initializing xf%xds + call init_xml_doc_state(xf%xds) + xf%xds%documentURI => vs_str_alloc(filename) + + if (present(warning)) then + xf%xds%warning = warning + else + xf%xds%warning = .false. + endif + if (present(validate)) then + xf%xds%valid = validate + else + xf%xds%valid = .false. + endif + xf%state_1 = WXML_STATE_1_JUST_OPENED + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + xf%state_3 = WXML_STATE_3_BEFORE_DTD + + if (present(pretty_print)) then + xf%pretty_print = pretty_print + else + xf%pretty_print = .true. + endif + if (present(minimize_overrun)) then + xf%minimize_overrun = minimize_overrun + else + xf%minimize_overrun = .false. + endif + if (present(preserve_whitespace)) then + xf%pretty_print = .not.preserve_whitespace + xf%minimize_overrun = preserve_whitespace + endif + if (present(canonical)) then + xf%canonical = canonical + else + xf%canonical = .false. + endif +! FIXME interplay of above options + + xf%indent = 0 + + if (decl) then + call xml_AddXMLDeclaration(xf,encoding='UTF-8') + ! which will reset the buffer itself + else + call reset_buffer(xf%buffer, xf%lun, xf%xds%xml_version) + endif + + if (present(namespace)) then + xf%namespace = namespace + else + xf%namespace = .true. + endif + if (xf%namespace) & + call initNamespaceDictionary(xf%nsDict) +#endif + end subroutine xml_OpenFile + + + subroutine xml_AddXMLDeclaration(xf, version, encoding, standalone) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in), optional :: version + character(len=*), intent(in), optional :: encoding + logical, intent(in), optional :: standalone + +#ifndef DUMMYLIB + call check_xf(xf) + ! Don't need to call checkChars on args, everything is checked + ! fully below anyway. + + if (xf%state_1 /= WXML_STATE_1_JUST_OPENED) & + call wxml_error("Tried to put XML declaration in wrong place") + + call reset_buffer(xf%buffer, xf%lun, xf%xds%xml_version) + + call xml_AddXMLPI(xf, "xml", xml=.true.) + if (present(version)) then + if (version =="1.0") then + xf%xds%xml_version = XML1_0 + call xml_AddPseudoAttribute(xf, "version", version) + elseif (version=="1.1") then + xf%xds%xml_version = XML1_1 + call xml_AddPseudoAttribute(xf, "version", version) + else + call wxml_error("Invalid XML version.") + endif + else + call xml_AddPseudoAttribute(xf, "version", "1.0") + xf%xds%xml_version = XML1_0 + endif + if (present(encoding)) then + if (.not.checkEncName(encoding)) & + call wxml_error("Invalid encoding name: "//encoding) + if (encoding /= 'UTF-8' .and. encoding /= 'utf-8') & + call wxml_warning(xf, "Non-default encoding specified: "//encoding) + call xml_AddPseudoAttribute(xf, "encoding", encoding) + endif + if (present(standalone)) then + xf%xds%standalone_declared = .true. + xf%xds%standalone = standalone + if (standalone) then + call xml_AddPseudoAttribute(xf, "standalone", "yes") + else + call xml_AddPseudoAttribute(xf, "standalone", "no") + endif + endif + call close_start_tag(xf) + ! We have to close explicitly here to ensure nothing gets tied + ! up in the XML declaration + xf%state_1 = WXML_STATE_1_BEFORE_ROOT +#endif + end subroutine xml_AddXMLDeclaration + + + subroutine xml_AddDOCTYPE(xf, name, system, public) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in), optional :: system, public + +#ifndef DUMMYLIB + type(URI), pointer :: URIref + + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkQName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + + if (present(system)) then + URIref => parseURI(system) + if (.not.associated(URIref)) call wxml_error("xml_AddDOCTYPE: Invalid SYSTEM URI") + call destroyURI(URIref) + endif + if (present(public)) then + if (.not.checkPublicId(public)) call wxml_error("xml_AddDOCTYPE: Invalid PUBLIC ID") + endif + + if (present(public).and..not.present(system)) & + call wxml_error('xml_AddDOCTYPE: PUBLIC supplied without SYSTEM for: '//name) + + ! By having an external ID we probably render this non-standalone (unless we've said that it is in the declaration) + if (present(system).and..not.xf%xds%standalone_declared) & + xf%xds%standalone=.false. + + call close_start_tag(xf) + + if (xf%state_1 /= WXML_STATE_1_BEFORE_ROOT) & + call wxml_error("Tried to put XML DOCTYPE in wrong place: "//name) + + if (xf%state_3 /= WXML_STATE_3_BEFORE_DTD) then + call wxml_error("Tried to output more than one DOCTYPE declaration: "//name) + else + xf%state_3 = WXML_STATE_3_DURING_DTD + endif + + call add_eol(xf) + call add_to_buffer(" null() +#endif + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + + if (present(PEDef)) then + if (.not.checkChars(PEDef,xf%xds%xml_version)) call wxml_error("xml_AddParameterEntity: Invalid character in PEDef") + endif + + if (present(system)) then + URIref => parseURI(system) + if (.not.associated(URIref)) call wxml_error("xml_AddParameterEntity: Invalid SYSTEM URI") + call destroyURI(URIref) + endif + if (present(public)) then + if (.not.checkPublicId(public)) call wxml_error("xml_AddParameterEntity: Invalid PUBLIC ID") + endif + + ! By adding a parameter entity (internal or external) we make this + ! a non-standalone document. + if (.not.xf%xds%standalone_declared) & + xf%xds%standalone = .false. + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot define Parameter Entity here: "//name) + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + if (present(PEdef)) then + if (present(system) .or. present(public)) & + call wxml_fatal("Parameter entity "//name//" cannot have both a PEdef and an External ID") + else + if (.not.present(system)) & + call wxml_fatal("Parameter entity "//name//" must have either a PEdef or an External ID") + endif + if (present(PEdef)) then + if (.not.checkPEDef(PEDef, xf%xds%xml_version)) & + call wxml_fatal("Parameter entity definition is invalid: "//PEDef) + if (xf%xds%standalone) then + if (.not.checkExistingRefs()) & + call wxml_error("Tried to reference unregistered parameter entity") + else + if (.not.checkExistingRefs()) & + call wxml_warning(xf, "Reference to unknown parameter entity") + endif +#ifdef PGF90 + call register_internal_PE(xf%xds, name=name, text=PEdef, baseURI=nullURIref, wfc=.false.) +#else + call register_internal_PE(xf%xds, name=name, text=PEdef, baseURI=null(), wfc=.false.) +#endif + + else +#ifdef PGF90 + call register_external_PE(xf%xds, name=name, systemId=system, & + publicId=public, baseURI=nullURIref, wfc=.false.) +#else + call register_external_PE(xf%xds, name=name, systemId=system, & + publicId=public, baseURI=null(), wfc=.false.) +#endif + endif + + call add_eol(xf) + + call add_to_buffer(" 0) then ! FIXME what if PEdef has both " and ' in it + call add_to_buffer(" '"//PEdef//"'", xf%buffer, .true.) + else + call add_to_buffer(" """//PEdef//"""", xf%buffer, .true.) + endif + call add_to_buffer(">", xf%buffer, .false.) + else + if (present(public)) then + call add_to_buffer(" PUBLIC", xf%buffer, .false.) + call add_to_buffer(" """//public//"""", xf%buffer, .true.) + else + call add_to_buffer(" SYSTEM", xf%buffer, .false.) + endif + if (scan(system, """")/=0) then + call add_to_buffer(" '"//system//"'", xf%buffer, .true.) + else + call add_to_buffer(" """//system//"""", xf%buffer, .true.) + endif + call add_to_buffer(">", xf%buffer) + endif + + contains + function checkExistingRefs() result(p) + logical :: p + + integer :: i1, i2 + + ! Here we assume we have syntactic well-formedness as + ! checked by checkPEDef. + + p = .false. + i1 = index(PEDef, '%') + i2 = 0 + do while (i1 > 0) + i1 = i2 + i1 + i2 = index(PEDef(i1+1:),';') + if (i2 == 0) return + i2 = i1 + i2 + if (.not.existing_entity(xf%xds%PEList, PEDef(i1+1:i2-1))) & + return + i1 = index(PEDef(i2+1:), '%') + enddo + p = .true. + + end function checkExistingRefs +#endif + end subroutine xml_AddParameterEntity + + + subroutine xml_AddInternalEntity(xf, name, value) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + +#ifndef DUMMYLIB +#ifdef PGF90 + type(URI), pointer :: nullURI + nullURI => null() +#endif + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + + if (.not.checkChars(value, xf%xds%xml_version)) call wxml_error("xml_AddInternalEntity: Invalid character in value") + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot define Entity here: "//name) + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("xml_AddInternalEntity: Invalid Name: "//name) +#ifdef PGF90 + call register_internal_GE(xf%xds, name=name, text=value, baseURI=nullURI, wfc=.false.) +#else + call register_internal_GE(xf%xds, name=name, text=value, baseURI=null(), wfc=.false.) +#endif + + call add_eol(xf) + + !FIXME - valid entity values? + call add_to_buffer(" 0) then + call add_to_buffer("'"//value//"'>", xf%buffer, .true.) + else + call add_to_buffer(""""//value//""">", xf%buffer, .true.) + endif +#endif + end subroutine xml_AddInternalEntity + + + subroutine xml_AddExternalEntity(xf, name, system, public, notation) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: system + character(len=*), intent(in), optional :: public + character(len=*), intent(in), optional :: notation + +#ifndef DUMMYLIB + type(URI), pointer :: URIref +#ifdef PGF90 + type(URI), pointer :: nullURI + nullURI => null() +#endif + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + URIref => parseURI(system) + if (.not.associated(URIref)) call wxml_error("xml_AddExternalEntity: Invalid SYSTEM URI") + call destroyURI(URIref) + if (present(public)) then + if (.not.checkPublicId(public)) call wxml_error("xml_AddExternalEntity: Invalid PUBLIC ID") + endif + if (present(notation)) then + if (xf%namespace) then + if (.not.checkNCName(notation, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(notation, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + endif + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot define Entity here: "//name) + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + ! Notation only needs checked if not already registered - done above. +#ifdef PGF90 + call register_external_GE(xf%xds, name=name, & + systemID=system, publicId=public, notation=notation, & + baseURI=nullURI, wfc=.false.) +#else + call register_external_GE(xf%xds, name=name, & + systemID=system, publicId=public, notation=notation, & + baseURI=null(), wfc=.false.) +#endif + + call add_eol(xf) + + call add_to_buffer("", xf%buffer, .false.) +#endif + end subroutine xml_AddExternalEntity + + + subroutine xml_AddNotation(xf, name, system, public) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in), optional :: system + character(len=*), intent(in), optional :: public + +#ifndef DUMMYLIB + type(URI), pointer :: URIref + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Name in DTD "//name) + endif + if (present(system)) then + URIref => parseURI(system) + if (.not.associated(URIref)) call wxml_error("xml_AddNotation: Invalid SYSTEM URI") + call destroyURI(URIref) + endif + if (present(public)) then + if (.not.checkPublicId(public)) call wxml_error("xml_AddNotation: Invalid PUBLIC ID") + endif + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot define Notation here: "//name) + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + if (notation_exists(xf%xds%nList, name)) & + call wxml_error("Tried to create duplicate notation: "//name) + + call add_eol(xf) + + call add_notation(xf%xds%nList, name, system, public) + call add_to_buffer(" 0) then + call add_to_buffer(" '"//system//"'", xf%buffer, .true.) + else + call add_to_buffer(" """//system//"""", xf%buffer, .true.) + endif + endif + call add_to_buffer(">", xf%buffer, .false.) +#endif + end subroutine xml_AddNotation + + + subroutine xml_AddElementToDTD(xf, name, declaration) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: declaration + +#ifndef DUMMYLIB + type(error_stack) :: stack +#ifdef PGF90 + type (element_t), pointer :: nullElement + + nullElement => null() +#endif + call check_xf(xf) + + if (.not.checkChars(declaration,xf%xds%xml_version)) call wxml_error("xml_AddElementToDTD: Invalid character in declaration") + + if (xf%namespace) then + if (.not.checkQName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Element Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Element Name in DTD "//name) + endif +#ifdef PGF90 + call parse_dtd_element(declaration, xf%xds%xml_version, stack, nullElement, .true.) +#else + call parse_dtd_element(declaration, xf%xds%xml_version, stack, null(), .true.) +#endif + if (in_error(stack)) call wxml_error(xf, "Invalid ELEMENT declaration") + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot write to DTD here: xml_AddElementToDTD") + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + call add_eol(xf) + call add_to_buffer("", xf%buffer, .false.) +#endif + end subroutine xml_AddElementToDTD + + + subroutine xml_AddAttlistToDTD(xf, name, declaration) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: declaration + +#ifndef DUMMYLIB + type(error_stack) :: stack +#ifdef PGF90 + type (element_t), pointer :: nullElement + + nullElement => null() +#endif + call check_xf(xf) + + if (.not.checkChars(declaration,xf%xds%xml_version)) call wxml_error("xml_AddAttListToDTD: Invalid character in declaration") + + if (xf%namespace) then + if (.not.checkQName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Attribute Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Attribute Name in DTD "//name) + endif + +#ifdef PGF90 + call parse_dtd_attlist(declaration, xf%xds%xml_version, & + validCheck=.false., namespaces=xf%namespace, stack=stack, & + elem=nullElement, internal=.true.) +#else + call parse_dtd_attlist(declaration, xf%xds%xml_version, & + validCheck=.false., namespaces=xf%namespace, stack=stack, & + elem=null(), internal=.true.) +#endif + + if (in_error(stack)) call wxml_error(xf, "Invalid ATTLIST declaration") + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot write to DTD here: xml_AddAttlistToDTD") + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + call add_eol(xf) + call add_to_buffer("", xf%buffer, .false.) +#endif + end subroutine xml_AddAttlistToDTD + + + subroutine xml_AddPEReferenceToDTD(xf, name) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + +#ifndef DUMMYLIB + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid PE Name in DTD "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid PE Name in DTD "//name) + endif + + call wxml_warning(xf, "Adding PEReference to DTD. Cannot guarantee well-formedness") + if (.not.existing_entity(xf%xds%PEList, name)) then + if (.not.xf%xds%standalone) then + call wxml_warning(xf, "Tried to reference possibly unregistered parameter entity in DTD: "//name) + else + call wxml_error("Tried to reference unregistered parameter entity in DTD "//name) + endif + else + if (is_unparsed_entity(xf%xds%PEList, name)) & + call wxml_error("Tried to reference unparsed parameter entity in DTD "//name) + endif + + if (xf%state_3 == WXML_STATE_3_DURING_DTD) then + call add_to_buffer(" [", xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_INSIDE_INTSUBSET + endif + + if (xf%state_3 /= WXML_STATE_3_INSIDE_INTSUBSET) & + call wxml_fatal("Cannot write to DTD here: xml_AddPEReferenceToDTD") + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) then + call close_start_tag(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + endif + + call add_eol(xf) + call add_to_buffer("%"//name//";", xf%buffer, .false.) + +#endif + end subroutine xml_AddPEReferenceToDTD + + + subroutine xml_AddXMLStylesheet(xf, href, type, title, media, charset, alternate) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: href + character(len=*), intent(in) :: type + character(len=*), intent(in), optional :: title + character(len=*), intent(in), optional :: media + character(len=*), intent(in), optional :: charset + logical, intent(in), optional :: alternate + +#ifndef DUMMYLIB + call check_xf(xf) + ! Don't bother checking name - all pseudoatts get checked anyway. + + if (xf%state_1 /= WXML_STATE_1_JUST_OPENED & + .and. xf%state_1 /= WXML_STATE_1_BEFORE_ROOT) & + call wxml_error("Cannot add stylesheet here: "//href) + + call close_start_tag(xf) + + call xml_AddXMLPI(xf, 'xml-stylesheet', xml=.true.) + call xml_AddPseudoAttribute(xf, 'href', href) + call xml_AddPseudoAttribute(xf, 'type', type) + + if (present(title)) call xml_AddPseudoAttribute(xf, 'title', title) + if (present(media)) call xml_AddPseudoAttribute(xf, 'media', media) + if (present(charset)) call xml_AddPseudoAttribute(xf, 'charset', charset) + if (present(alternate)) then + if (alternate) then + call xml_AddPseudoAttribute(xf, 'alternate', 'yes') + else + call xml_AddPseudoAttribute(xf, 'alternate', 'no') + endif + endif + if (xf%state_1 == WXML_STATE_1_JUST_OPENED) & + xf%state_1 = WXML_STATE_1_BEFORE_ROOT + xf%state_2 = WXML_STATE_2_INSIDE_PI +#endif + end subroutine xml_AddXMLStylesheet + + + subroutine xml_AddXMLPI(xf, name, data, xml, ws_significant) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in), optional :: data + logical, intent(in), optional :: xml + logical, intent(in), optional :: ws_significant + + logical :: xml_ +#ifndef DUMMYLIB + call check_xf(xf) + + if (present(xml)) then + xml_ = xml + else + xml_ = .false. + endif + + if (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid PI target "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid PI target "//name) + endif + if (.not.xml_) then + if (len(name)==3.and.(toLower(name)=="xml")) & + call wxml_error("Invalid PI target "//name) + endif + + if (present(data)) then + if (.not.checkChars(data,xf%xds%xml_version)) & + call wxml_error("xml_AddXMLPI: Invalid character in data") + endif + + select case (xf%state_1) + case (WXML_STATE_1_JUST_OPENED) + xf%state_1 = WXML_STATE_1_BEFORE_ROOT + case (WXML_STATE_1_DURING_ROOT) + call close_start_tag(xf) + if (xf%pretty_print) call add_eol(xf) + case default + call close_start_tag(xf) + call add_eol(xf) + end select + call add_to_buffer("0) then + if (index(data, '?>') > 0) & + call wxml_error(xf, "Tried to output invalid PI data "//data) + call add_to_buffer(' ', xf%buffer, .false.) + call add_to_buffer(data//'?>', xf%buffer, ws_significant) + ! state_2 is now OUTSIDE_TAG from close_start_tag + else + xf%state_2 = WXML_STATE_2_INSIDE_PI + call reset_dict(xf%dict) + endif + else + xf%state_2 = WXML_STATE_2_INSIDE_PI + call reset_dict(xf%dict) + endif +#endif + end subroutine xml_AddXMLPI + + + subroutine xml_AddComment(xf, comment, ws_significant) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: comment + logical, intent(in), optional :: ws_significant + +#ifndef DUMMYLIB + call check_xf(xf) + if (.not.checkChars(comment,xf%xds%xml_version)) call wxml_error("xml_AddComment: Invalid character in comment") + + select case (xf%state_1) + case (WXML_STATE_1_JUST_OPENED) + xf%state_1 = WXML_STATE_1_BEFORE_ROOT + case (WXML_STATE_1_DURING_ROOT) + call close_start_tag(xf) + if (xf%pretty_print.and.xf%state_2 == WXML_STATE_2_OUTSIDE_TAG) call add_eol(xf) + case default + call close_start_tag(xf) + call add_eol(xf) + end select + + if (index(comment,'--') > 0 .or. comment(len(comment):) == '-') & + call wxml_error("Tried to output invalid comment "//comment) + + call add_to_buffer("", xf%buffer, .false.) +#endif + end subroutine xml_AddComment + + + subroutine xml_NewElement(xf, name) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + +#ifndef DUMMYLIB + call check_xf(xf) + + if (xf%namespace) then + if (.not.checkQName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Element Name "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Element Name "//name) + endif + + select case (xf%state_1) + case (WXML_STATE_1_JUST_OPENED, WXML_STATE_1_BEFORE_ROOT) + if (xf%xds%valid) then + if (size(xf%name)==0) then + call wxml_error(xf, "No DTD specified for document") + elseif (str_vs(xf%name) /= name) then + call wxml_error(xf, "Root element name does not match DTD") + endif + endif + call close_start_tag(xf) + if (xf%state_3 /= WXML_STATE_3_BEFORE_DTD) then + select case (xf%state_3) + case (WXML_STATE_3_DURING_DTD) + call add_to_buffer('>', xf%buffer, .false.) + xf%state_3 = WXML_STATE_3_AFTER_DTD + case (WXML_STATE_3_INSIDE_INTSUBSET) + xf%state_3 = WXML_STATE_3_AFTER_DTD + call add_eol(xf) + call add_to_buffer(']>', xf%buffer, .false.) + end select + endif + call add_eol(xf) + case (WXML_STATE_1_DURING_ROOT) + call close_start_tag(xf) + if (xf%pretty_print) call add_eol(xf) + case (WXML_STATE_1_AFTER_ROOT) + call wxml_error(xf, "Two root elements: "//name) + end select + + if (xf%namespace) then + if (len(prefixOfQName(name)) > 0) then + if (.not.isPrefixInForce(xf%nsDict, prefixOfQName(name))) & + call wxml_error(xf, "Namespace prefix not registered: "//prefixOfQName(name)) + endif + endif + + call push_elstack(xf%stack, name) + call add_to_buffer("<"//name, xf%buffer, .false.) + xf%state_2 = WXML_STATE_2_INSIDE_ELEMENT + call reset_dict(xf%dict) + xf%indent = xf%indent + indent_inc + xf%state_1 = WXML_STATE_1_DURING_ROOT +#endif + end subroutine xml_NewElement + + + subroutine xml_AddCharacters_ch(xf, chars, parsed, ws_significant) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: chars + logical, intent(in), optional :: parsed + logical, intent(in), optional :: ws_significant + +#ifndef DUMMYLIB + logical :: pc + + call check_xf(xf) + if (.not.checkChars(chars, xf%xds%xml_version)) call wxml_error("xml_AddCharacters: Invalid character in chars") + + if (xf%state_1 /= WXML_STATE_1_DURING_ROOT) & + call wxml_fatal("Tried to add text section in wrong place: "//chars) + + if (present(parsed)) then + pc = parsed + else + pc = .true. + endif + + call close_start_tag(xf) + + if (pc) then + call add_to_buffer(escape_string(chars, xf%xds%xml_version), xf%buffer, ws_significant) + else + ! FIXME what if we try and output two separate character events? + ! need to keep track of this ... + if (index(chars,']]>') > 0) & + call wxml_fatal("Tried to output invalid CDATA: "//chars) + call add_to_buffer("", xf%buffer, ws_significant) + endif + + xf%state_2 = WXML_STATE_2_IN_CHARDATA +#endif + end subroutine xml_AddCharacters_Ch + + + subroutine xml_AddNewline(xf) + type(xmlf_t), intent(inout) :: xf + +#ifndef DUMMYLIB + call xml_AddCharacters(xf, "") ! To ensure we are in a text section + call add_eol(xf) +#endif + end subroutine xml_AddNewline + + + subroutine xml_AddEntityReference(xf, name) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + +#ifndef DUMMYLIB + call check_xf(xf) + + if (likeCharacterEntityReference(name)) then + if (.not.checkCharacterEntityReference(name, xf%xds%xml_version)) & + call wxml_error("Invalid Character Entity Reference "//name) + elseif (xf%namespace) then + if (.not.checkNCName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Entity Name "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Entity Name "//name) + endif + + call close_start_tag(xf) + + if (xf%state_2 /= WXML_STATE_2_OUTSIDE_TAG .and. & + xf%state_2 /= WXML_STATE_2_IN_CHARDATA) & + call wxml_fatal("Tried to add entity reference in wrong place: "//name) + + if (.not.checkCharacterEntityReference(name, xf%xds%xml_version)) then + !it's not just a unicode entity + call wxml_warning(xf, "Entity reference added - document may not be well-formed") + if (.not.existing_entity(xf%xds%entityList, name)) then + if (xf%xds%standalone) then + call wxml_error("Tried to reference unregistered entity") + else + call wxml_warning(xf, "Tried to reference unregistered entity") + endif + else + if (is_unparsed_entity(xf%xds%entityList, name)) & + call wxml_error("Tried to reference unparsed entity") + endif + endif + + call add_to_buffer('&'//name//';', xf%buffer, .false.) + xf%state_2 = WXML_STATE_2_IN_CHARDATA +#endif + end subroutine xml_AddEntityReference + + + subroutine xml_AddAttribute_Ch(xf, name, value, escape, type, ws_significant) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + logical, intent(in), optional :: escape + character(len=*), intent(in), optional :: type + logical, intent(in), optional :: ws_significant + +#ifndef DUMMYLIB + logical :: esc + character, pointer :: type_(:) + + if (present(type)) then + if (type/='CDATA'.and.type/='ID'.and.type/='IDREF'.and.type/='IDREFS'.and.type/='NMTOKEN'.and.type/='NMTOKENS' & + .and.type/='ENTITY'.and.type/='ENTITIES'.and.type/='NOTATION') then + call wxml_fatal("Invalid type in xml_AddAttribute: "//type) + endif + type_ => vs_str_alloc(type) + else + ! We assume CDATA, but need to worry about whether the caller cares about whitespace ... + if (present(ws_significant)) then + if (ws_significant) then + type_ => vs_str_alloc('CDATA') + else + type_ => vs_str_alloc('CDANO') ! CDAta, whitespace Not significant + endif + else + type_ => vs_str_alloc('CDAMB') ! CDAta, whitespace MayBe significant + endif + endif + + call check_xf(xf) + + if (.not.checkChars(value, xf%xds%xml_version)) call wxml_error("xml_AddAttribute: Invalid character in value") + + if (xf%namespace) then + if (.not.checkQName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Attribute Name "//name) + else + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid Attribute Name "//name) + endif + + if (present(escape)) then + esc = escape + else + esc = .true. + endif + + if (name=="xml:space") then + ! The value can only be "default" or "preserve", by 2.10 + if (.not.esc) then + if (value/="default".and.value/="preserve") & + call wxml_fatal("Invalid value for xml:space attrbute") + endif + endif + + ! FIXME when escape is false we should still do full verification + ! where possible. + ! Currently - minimal check: only extra allowed is character entity references. + ! We check they exist, and are not unparsed. + ! Ideally we would fully expand all entity references (at least for + ! a standalone document where we can) and then + ! match the resultant production against [XML]-3.3.1. This is + ! initially too much work though, so we just check simple + ! syntactic constraint. + + if (.not.esc) then + if (.not.checkAttValue(value, xf%xds%xml_version)) & + call wxml_error(xf, "Invalid attribute value: "//value) + if (index(value, '&') > 0) then + ! There are entity references + ! They should exist (unless we are not standalone) and they must not be unparsed. + if (.not.checkExistingRefsInAttValue()) then + if (xf%xds%standalone) then + call wxml_error(xf, "outputting unknown entity. Cannot guarantee validity.") + else + call wxml_warning(xf, "Warning: outputting unknown entity. Cannot guarantee validity.") + endif + endif + if (.not.checkParsedRefsInAttValue()) & + call wxml_error(xf, "Warning: outputting unknown entity. Cannot guarantee validity.") + endif + endif + + if (xf%state_2 /= WXML_STATE_2_INSIDE_ELEMENT) & + call wxml_error(xf, "attributes outside element content: "//name) + + if (hasKey(xf%dict,name)) then + call wxml_error(xf, "duplicate att name: "//name) + elseif (xf%namespace) then + if (hasKey(xf%dict, & + getnamespaceURI(xf%nsDict,prefixOfQname(name)), localpartofQname(name))) then + call wxml_error(xf, "duplicate att after namespace processing: "//name) + endif + endif + + if (xf%namespace) then + if (len(prefixOfQName(name))>0) then + if (prefixOfQName(name)/="xml".and.prefixOfQName(name)/="xmlns") then + if (.not.isPrefixInForce(xf%nsDict, prefixOfQName(name))) & + call wxml_error(xf, "namespace prefix not registered: "//prefixOfQName(name)) + endif + if (esc) then + call add_item_to_dict(xf%dict, localpartofQname(name), escape_string(value, xf%xds%xml_version), prefixOfQName(name), & + getnamespaceURI(xf%nsDict,prefixOfQname(name)), type=str_vs(type_)) + else + call add_item_to_dict(xf%dict, localpartofQname(name), value, prefixOfQName(name), & + getnamespaceURI(xf%nsDict,prefixOfQName(name)), type=str_vs(type_)) + endif + else + if (esc) then + call add_item_to_dict(xf%dict, name, escape_string(value, xf%xds%xml_version), type=str_vs(type_)) + else + call add_item_to_dict(xf%dict, name, value, type=str_vs(type_)) + endif + endif + else + if (esc) then + call add_item_to_dict(xf%dict, name, escape_string(value, xf%xds%xml_version), type=str_vs(type_)) + else + call add_item_to_dict(xf%dict, name, value, type=str_vs(type_)) + endif + endif + + !FIXME need to deallocate this when we move to better error handling + deallocate(type_) + + contains + function checkExistingRefsInAttValue() result(p) + logical :: p + + integer :: i1, i2 + + ! Here we assume we have syntactic well-formedness as + ! checked by checkAttValue. + ! We also assume we do not have simply one entity as + ! the contents - that is checked by checkAttValueEntity + + p = .false. + i1 = index(value, '&') + i2 = 0 + do while (i1 > 0) + i1 = i2 + i1 + i2 = index(value(i1+1:),';') + if (i2 == 0) return + i2 = i1 + i2 + if (.not.existing_entity(xf%xds%entityList, value(i1+1:i2-1)) .and. & + .not.checkCharacterEntityReference(value(i1+1:i2-1), xf%xds%xml_version)) & + return + i1 = index(value(i2+1:), '&') + enddo + p = .true. + + end function checkExistingRefsInAttValue + + function checkParsedRefsInAttValue() result(p) + logical :: p + + integer :: i1, i2 + + ! Here we assume we have syntactic well-formedness as + ! checked by checkAttValue. + + p = .false. + i1 = index(value, '&') + i2 = 0 + do while (i1 > 0) + i1 = i1 + i2 + i2 = index(value(i1+1:),';') + if (i2 == 0) return + i2 = i1 + i2 + if (is_unparsed_entity(xf%xds%entityList, value(i1+1:i2-1))) & + return + i1 = index(value(i2+1:), '&') + enddo + p = .true. + + end function checkParsedRefsInAttValue +#endif + end subroutine xml_AddAttribute_Ch + + + subroutine xml_AddPseudoAttribute_Ch(xf, name, value, escape, ws_significant) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) :: value + logical, intent(in), optional :: escape + logical, intent(in), optional :: ws_significant + +#ifndef DUMMYLIB + logical :: esc + character(len=5) :: type + + call check_xf(xf) + if (.not.checkChars(name, xf%xds%xml_version)) call wxml_error("xml_AddPseudoAttribute: Invalid character in name") + if (.not.checkChars(value, xf%xds%xml_version)) call wxml_error("xml_AddPseudoAttribute: Invalid character in value") + + if (present(escape)) then + esc = escape + else + esc = .true. + endif + if (present(ws_significant)) then + if (ws_significant) then + type='CDATA' + else + type='CDANO' ! CDAta, whitespace Not significant + endif + else + type='CDAMB' ! CDAta, whitespace MayBe significant + endif + + if (index(value, '?>') > 0) & + call wxml_error(xf, "Invalid pseudo-attribute value: "//value) + if (.not.esc) then + if (.not.checkPseudoAttValue(value, xf%xds%xml_version)) & + call wxml_error(xf, "Invalid pseudo-attribute value: "//value) + endif + + if (xf%state_2 /= WXML_STATE_2_INSIDE_PI) & + call wxml_error("PI pseudo-attribute outside PI: "//name) + + ! This is mostly ad-hoc, pseudo-attribute names are not defined anywhere. + if (.not.checkName(name, xf%xds%xml_version)) & + call wxml_error("Invalid pseudo-attribute name: "//name) + + if (hasKey(xf%dict,name)) & + call wxml_error(xf, "duplicate pseudo-attribute name: "//name) + + if (index(value, '?>') > 0) & + call wxml_error(xf, "Invalid pseudo-attribute data: "//value) + + if (esc) then + call add_item_to_dict(xf%dict, name, escape_string(value, xf%xds%xml_version), type=type) + else + call add_item_to_dict(xf%dict, name, value, type=type) + endif +#endif + end subroutine xml_AddPseudoAttribute_Ch + + + subroutine xml_EndElement(xf, name) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + + character :: dummy +#ifndef DUMMYLIB + call check_xf(xf) + ! No point in doing checkChars, name is compared to stack anyway. + + if (len(xf%stack) == 0) & + call wxml_fatal(xf,'Trying to close '//name//' but no tags are open.') + + if (get_top_elstack(xf%stack) /= name) & + call wxml_fatal(xf, 'Trying to close '//name//' but '//get_top_elstack(xf%stack)// & + ' is open. Either you have failed to open '//name//& + ' or you have failed to close '//get_top_elstack(xf%stack)//'.') + xf%indent = xf%indent - indent_inc + + if (xf%state_2==WXML_STATE_2_INSIDE_ELEMENT) then + if (xf%namespace) call checkNamespacesWriting(xf%dict, xf%nsDict, len(xf%stack)) + if (getLength(xf%dict) > 0) call write_attributes(xf) + if (xf%minimize_overrun) call add_eol(xf) + endif + if (xf%state_2==WXML_STATE_2_INSIDE_ELEMENT.and..not.xf%canonical) then + call add_to_buffer("/>", xf%buffer, .false.) + else + if (xf%state_2==WXML_STATE_2_INSIDE_ELEMENT) & + call add_to_buffer('>', xf%buffer, .false.) + if (xf%state_2==WXML_STATE_2_INSIDE_PI) & + call close_start_tag(xf) + if (xf%state_2==WXML_STATE_2_OUTSIDE_TAG.and.xf%pretty_print) & + call add_eol(xf) +! XLF does a weird thing here, and if pop_elstack is called as an +! argument to the add_to_buffer, it gets called twice. So we have to separate +! out get_top_... from pop_... + call add_to_buffer("", xf%buffer, .false.) + endif + dummy = pop_elstack(xf%stack) + + if (xf%namespace) call checkEndNamespaces(xf%nsDict, len(xf%stack)+1) + if (is_empty(xf%stack)) then + xf%state_1 = WXML_STATE_1_AFTER_ROOT + endif + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG +#endif + end subroutine xml_EndElement + + + subroutine xml_DeclareNamespace(xf, nsURI, prefix, xml) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: nsURI + character(len=*), intent(in), optional :: prefix + logical, intent(in), optional :: xml + +#ifndef DUMMYLIB + call check_xf(xf) + if (.not.xf%namespace) call wxml_error("Cannot declare a namespace in a non-namespaced document") + + !if (.not.checkNCName(nsURI, xf%xds%xml_version)) call wxml_error("xml_DeclareNamespace: Invalid nsURI") + if (present(prefix)) then + if (.not.checkNCName(prefix, xf%xds%xml_version)) call wxml_error("xml_DeclareNamespace: Invalid prefix") + endif + + if (xf%state_1 == WXML_STATE_1_AFTER_ROOT) & + call wxml_error(xf, "adding namespace outside element content") + + if (len(nsURI) == 0) then + if (present(prefix).and.xf%xds%xml_version==XML1_0) & + call wxml_error(xf, "prefixed namespace with empty URI forbidden in XML 1.0") + endif + + if (present(prefix)) then + call addPrefixedNS(xf%nsDict, prefix, nsURI, len(xf%stack)+1, xf%xds, xml) + else + call addDefaultNS(xf%nsDict, nsURI, len(xf%stack)+1) + endif +#endif + end subroutine xml_DeclareNamespace + + + subroutine xml_UndeclareNamespace(xf, prefix) + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in), optional :: prefix + +#ifndef DUMMYLIB + call check_xf(xf) + !No need to checkChars, prefix is checked against stack + if (.not.xf%namespace) call wxml_error("Cannot declare a namespace in a non-namespaced document") + + if (present(prefix).and.xf%xds%xml_version==XML1_0) & + call wxml_error("cannot undeclare prefixed namespaces in XML 1.0") + + if (xf%state_1 == WXML_STATE_1_AFTER_ROOT) & + call wxml_error(xf, "Undeclaring namespace outside element content") + + if (present(prefix)) then + call addPrefixedNS(xf%nsDict, prefix, "", len(xf%stack)+1, xf%xds) + else + call addDefaultNS(xf%nsDict, "", len(xf%stack)+1) + endif +#endif + end subroutine xml_UndeclareNamespace + + + subroutine xml_Close(xf, empty) + type(xmlf_t), intent(inout) :: xf + logical, optional :: empty + +#ifndef DUMMYLIB + logical :: empty_ + + if (present(empty)) then + empty_ = empty + else + empty_ = .false. + endif + + if (xf%lun == -1) & + call wxml_fatal('Tried to close XML file which is not open') + + if (xf%state_2 == WXML_STATE_2_INSIDE_PI) & + call close_start_tag(xf) + + if (xf%state_3 /= WXML_STATE_3_BEFORE_DTD & + .and. xf%state_3 /= WXML_STATE_3_AFTER_DTD) then + select case (xf%state_3) + case (WXML_STATE_3_DURING_DTD) + call add_to_buffer('>', xf%buffer, .false.) + case (WXML_STATE_3_INSIDE_INTSUBSET) + call add_eol(xf) + call add_to_buffer(']>', xf%buffer, .false.) + end select + xf%state_3 = WXML_STATE_3_AFTER_DTD + endif + + do while (xf%state_1 == WXML_STATE_1_DURING_ROOT) + call xml_EndElement(xf, get_top_elstack(xf%stack)) + enddo + + if (xf%state_1 /= WXML_STATE_1_AFTER_ROOT) then + if (empty_) then + call wxml_warning(xf, 'Invalid XML document produced: No root element') + else + call wxml_error(xf, 'Invalid XML document produced: No root element') + endif + endif + + call dump_buffer(xf%buffer) + close(unit=xf%lun) + xf%lun = -1 + + call destroy_dict(xf%dict) + call destroy_elstack(xf%stack) + + if (xf%namespace) & + call destroyNamespaceDictionary(xf%nsDict) + call destroy_xml_doc_state(xf%xds) + + deallocate(xf%name) +#endif + end subroutine xml_Close + + subroutine xmlf_SetPretty_print(xf, new_value) + type(xmlf_t), intent(inout) :: xf + logical, intent(in) :: new_value +#ifndef DUMMYLIB + xf%pretty_print = new_value +#endif + end subroutine xmlf_SetPretty_print + + pure function xmlf_GetPretty_print(xf) result(value) + logical :: value + type(xmlf_t), intent(in) :: xf +#ifdef DUMMYLIB + value = .false. +#else + value = xf%pretty_print +#endif + end function xmlf_GetPretty_print + + pure function xmlf_name(xf) result(fn) + type (xmlf_t), intent(in) :: xf +#ifdef DUMMYLIB + character(len=1) :: fn + fn = " " +#else + character(len=size(xf%xds%documentURI)) :: fn + fn = str_vs(xf%xds%documentURI) +#endif + end function xmlf_name + +#ifndef DUMMYLIB + pure function xmlf_opentag_len(xf) result(n) + type (xmlf_t), intent(in) :: xf + integer :: n + + if (xf%lun == -1) then + n = 0 + elseif (is_empty(xf%stack)) then + n = 0 + else + n = len(get_top_elstack(xf%stack)) + endif + end function xmlf_opentag_len +#endif + + function xmlf_opentag(xf) result(fn) + type (xmlf_t), intent(in) :: xf +#ifdef DUMMYLIB + character(len=1) :: fn + fn = " " +#else + character(len=xmlf_opentag_len(xf)) :: fn + + if (xf%lun == -1) then + fn = '' + elseif (is_empty(xf%stack)) then + fn = '' + else + fn = get_top_elstack(xf%stack) + endif +#endif + end function xmlf_opentag + +#ifndef DUMMYLIB + + subroutine check_xf(xf) + type(xmlf_t), intent(inout) :: xf + if (xf%lun == -1) & + call wxml_fatal("Tried to manipulate an XML File which is not open") + + end subroutine check_xf + + + subroutine add_eol(xf) + type(xmlf_t), intent(inout) :: xf + + integer :: indent_level + + ! In case we still have a zero-length stack, we must make + ! sure indent_level is not less than zero. + if (xf%state_3 == WXML_STATE_3_INSIDE_INTSUBSET) then + indent_level = indent_inc + else + indent_level = xf%indent + endif + + !We must flush here (rather than just adding an eol character) + !since we don't know what the eol character is on this system. + !Flushing with a linefeed will get it automatically, though. + call dump_buffer(xf%buffer, lf=.true.) + call reset_buffer(xf%buffer, xf%lun, xf%xds%xml_version) + + if (xf%pretty_print) & + call add_to_buffer(repeat(' ',indent_level),xf%buffer, .false.) + + end subroutine add_eol + + + subroutine close_start_tag(xf) + type(xmlf_t), intent(inout) :: xf + + select case (xf%state_2) + case (WXML_STATE_2_INSIDE_ELEMENT) + if (xf%namespace) call checkNamespacesWriting(xf%dict, xf%nsDict, len(xf%stack)) + if (getLength(xf%dict) > 0) call write_attributes(xf) + if (xf%minimize_overrun) call add_eol(xf) + call add_to_buffer('>', xf%buffer, .false.) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + case (WXML_STATE_2_INSIDE_PI) + if (getLength(xf%dict) > 0) call write_attributes(xf) + call add_to_buffer('?>', xf%buffer, .false.) + if (xf%pretty_print.and.xf%state_3/=WXML_STATE_3_INSIDE_INTSUBSET) call add_eol(xf) + xf%state_2 = WXML_STATE_2_OUTSIDE_TAG + case (WXML_STATE_2_IN_CHARDATA) + continue + case (WXML_STATE_2_OUTSIDE_TAG) + continue + end select + + end subroutine close_start_tag + + + subroutine write_attributes(xf) + type(xmlf_t), intent(inout) :: xf + + integer :: i, j, size + + if (xf%state_2 /= WXML_STATE_2_INSIDE_PI .and. & + xf%state_2 /= WXML_STATE_2_INSIDE_ELEMENT) & + call wxml_fatal("Internal library error") + + if (xf%canonical) call sortAttrs(xf%dict) + + do i = 1, getLength(xf%dict) + size = len(get_key(xf%dict, i)) + len(get_value(xf%dict, i)) + 4 + if (xf%minimize_overrun.and.(len(xf%buffer) + size) > COLUMNS) then + call add_eol(xf) + else + call add_to_buffer(" ", xf%buffer, .false.) + endif + call add_to_buffer(get_key(xf%dict, i), xf%buffer, .false.) + call add_to_buffer("=", xf%buffer, .false.) + call add_to_buffer('"',xf%buffer, .false.) + j = getWhiteSpaceHandling(xf%dict, i) + if (j==0) then + call add_to_buffer(get_value(xf%dict, i), xf%buffer, .true.) + elseif (j==1) then + call add_to_buffer(get_value(xf%dict, i), xf%buffer) + else + call add_to_buffer(get_value(xf%dict, i), xf%buffer, .false.) + endif + call add_to_buffer('"', xf%buffer, .false.) + enddo + + end subroutine write_attributes + + subroutine wxml_warning_xf(xf, msg) + ! Emit warning, but carry on. + type(xmlf_t), intent(in) :: xf + character(len=*), intent(in) :: msg + + if (FoX_get_fatal_warnings()) then + write(6,'(a)') 'FoX warning made fatal' + call wxml_fatal_xf(xf, msg) + endif + + if (xf%xds%warning) then + write(6,'(a)') 'WARNING(wxml) in writing to file ', xmlf_name(xf) + write(6,'(a)') msg + endif + + end subroutine wxml_warning_xf + + + subroutine wxml_error_xf(xf, msg) + ! Emit error message, clean up file and stop. + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: msg + + if (FoX_get_fatal_errors()) then + write(6,'(a)') 'FoX error made fatal' + call wxml_fatal_xf(xf, msg) + endif + + write(6,'(a)') 'ERROR(wxml) in writing to file ', xmlf_name(xf) + write(6,'(a)') msg + + !call xml_Close(xf) + stop + + end subroutine wxml_error_xf + + + subroutine wxml_fatal_xf(xf, msg) + !Emit error message and abort with coredump. Does not try to + !close file, so should be used from anything xml_Close might + !itself call (to avoid infinite recursion!) + + type(xmlf_t), intent(in) :: xf + character(len=*), intent(in) :: msg + + write(6,'(a)') 'ERROR(wxml) in writing to file ', xmlf_name(xf) + write(6,'(a)') msg + + call pxfabort() + stop + + end subroutine wxml_fatal_xf + +#endif + +end module m_wxml_core diff --git a/src/xml/wxml/m_wxml_escape.F90 b/src/xml/wxml/m_wxml_escape.F90 new file mode 100644 index 000000000..99bfcdab0 --- /dev/null +++ b/src/xml/wxml/m_wxml_escape.F90 @@ -0,0 +1,140 @@ +module m_wxml_escape + +#ifndef DUMMYLIB + !Ensure all characters are safe to go out into XML file. + + use fox_m_fsys_format, only: str + use m_common_charset, only: XML1_0 + use m_common_error, only: FoX_error, FoX_warning + + implicit none + private + + integer, parameter :: AMP = iachar('&') + integer, parameter :: LT = iachar('<') + integer, parameter :: GT = iachar('>') + integer, parameter :: QUOT = iachar('"') + integer, parameter :: APOS = iachar("'") + + public :: escape_string + public :: escape_string_len + +contains + + pure function escape_string_len(s) result(c) + character(len=*), intent(in) :: s + integer :: c + + integer :: i + c = len(s) + do i = 1, len(s) + select case(iachar(s(i:i))) + case (AMP) + c = c + 4 + case (LT) + c = c + 3 + case (GT) + c = c + 3 + case (QUOT) + c = c + 5 + case (APOS) + c = c + 5 + case (1:8) + c = c + 3 + case (11:12) + c = c + 4 + case (14:31) + c = c + 4 + case (127:) + c = c + 5 + ! a char can never contain more than 8 bits = 256 characters, so + ! we never need more than 3 chars to represent the int. + end select + enddo + + end function escape_string_len + + + function escape_string(s, version) result (s2) + character(len=*), intent(in) :: s + integer, intent(in) :: version + character(len=escape_string_len(s)) :: s2 + + integer :: c, i, ic + + ! We have to do it this way (with achar etc) in case the native + ! platform encoding is not ASCII + + c = 1 + do i = 1, len(s) + ic = iachar(s(i:i)) + select case (ic) + case (0) + call FoX_error("Tried to output a NUL character") + case (1:8) + if (version==XML1_0) then + call FoX_error("Tried to output a character invalid under XML 1.0") + else + s2(c:c+3) = "&#"//str(ic)//";" + c = c + 4 + endif + case(9:10) + s2(c:c) = achar(ic) + c = c + 1 + case(11:12) + if (version==XML1_0) then + call FoX_error("Tried to output a character invalid under XML 1.0") + else + s2(c:c+5) = "&#"//str(ic)//";" + c = c + 5 + endif + case(13) + s2(c:c) = achar(13) + c = c + 1 + case (14:31) + if (version==XML1_0) then + call FoX_error("Tried to output a character invalid under XML 1.0") + else + s2(c:c+5) = "&#"//str(ic)//";" + c = c + 5 + endif + case (32:126) + select case (iachar(s(i:i))) + case (AMP) + s2(c:c+4) = "&" + c = c + 5 + case (LT) + s2(c:c+3) = "<" + c = c + 4 + case (GT) + s2(c:c+3) = ">" + c = c + 4 + case (QUOT) + s2(c:c+5) = """ + c = c + 6 + case (APOS) + s2(c:c+5) = "'" + c = c + 6 + case default + s2(c:c) = achar(ic) + c = c + 1 + end select + case (127) + s2(c:c+5) = "" + c = c + 6 + case default + !TOHW we should maybe just disallow this ... + call FoX_warning("emitting non-ASCII character. Platform-dependent result!") + s2(c:c+6) = "&#"//str(ic)//";" + c = c + 6 + ! a char can never contain more than 8 bits = 256 characters, so + ! we never need more than 3 chars to represent the int. + ! We have to encode it though, because UTF-8 x7F-x9F must be + ! encoded, and if they are in the native charset they'll be > 128 + end select + enddo + + end function escape_string + +#endif +end module m_wxml_escape diff --git a/src/xml/wxml/m_wxml_overloads.F90 b/src/xml/wxml/m_wxml_overloads.F90 new file mode 100644 index 000000000..5f6281dcb --- /dev/null +++ b/src/xml/wxml/m_wxml_overloads.F90 @@ -0,0 +1,1028 @@ +! This file is AUTOGENERATED!!!! +! Do not edit this file; edit m_wxml_overloads.m4 and regenerate. +! +! +module m_wxml_overloads + +#ifndef DUMMYLIB + use fox_m_fsys_format, only: str +#endif + use fox_m_fsys_realtypes, only: sp, dp + use m_wxml_core, only: xmlf_t + use m_wxml_core, only: xml_AddCharacters + use m_wxml_core, only: xml_AddAttribute + use m_wxml_core, only: xml_AddPseudoAttribute + + implicit none + private + + interface xml_AddCharacters + module procedure CharactersScalarCmplxDp + module procedure CharactersScalarCmplxSp + module procedure CharactersScalarRealDp + module procedure CharactersScalarRealSp + module procedure CharactersScalarInt + module procedure CharactersScalarLg + + module procedure CharactersArrayCmplxDp + module procedure CharactersArrayCmplxSp + module procedure CharactersArrayRealDp + module procedure CharactersArrayRealSp + module procedure CharactersArrayInt + module procedure CharactersArrayLg + module procedure CharactersArrayCh + module procedure CharactersMatrixCmplxDp + module procedure CharactersMatrixCmplxSp + module procedure CharactersMatrixRealDp + module procedure CharactersMatrixRealSp + module procedure CharactersMatrixInt + module procedure CharactersMatrixLg + module procedure CharactersMatrixCh + end interface + + interface xml_AddAttribute + module procedure AttributeScalarCmplxDp + module procedure AttributeScalarCmplxSp + module procedure AttributeScalarRealDp + module procedure AttributeScalarRealSp + module procedure AttributeScalarInt + module procedure AttributeScalarLg + + module procedure AttributeArrayCmplxDp + module procedure AttributeArrayCmplxSp + module procedure AttributeArrayRealDp + module procedure AttributeArrayRealSp + module procedure AttributeArrayInt + module procedure AttributeArrayLg + module procedure AttributeArrayCh + module procedure AttributeMatrixCmplxDp + module procedure AttributeMatrixCmplxSp + module procedure AttributeMatrixRealDp + module procedure AttributeMatrixRealSp + module procedure AttributeMatrixInt + module procedure AttributeMatrixLg + module procedure AttributeMatrixCh + end interface + + interface xml_AddPseudoAttribute + module procedure PseudoAttributeScalarCmplxDp + module procedure PseudoAttributeScalarCmplxSp + module procedure PseudoAttributeScalarRealDp + module procedure PseudoAttributeScalarRealSp + module procedure PseudoAttributeScalarInt + module procedure PseudoAttributeScalarLg + + module procedure PseudoAttributeArrayCmplxDp + module procedure PseudoAttributeArrayCmplxSp + module procedure PseudoAttributeArrayRealDp + module procedure PseudoAttributeArrayRealSp + module procedure PseudoAttributeArrayInt + module procedure PseudoAttributeArrayLg + module procedure PseudoAttributeArrayCh + module procedure PseudoAttributeMatrixCmplxDp + module procedure PseudoAttributeMatrixCmplxSp + module procedure PseudoAttributeMatrixRealDp + module procedure PseudoAttributeMatrixRealSp + module procedure PseudoAttributeMatrixInt + module procedure PseudoAttributeMatrixLg + module procedure PseudoAttributeMatrixCh + end interface + + public :: xml_AddCharacters + public :: xml_AddAttribute + public :: xml_AddPseudoAttribute + +contains + + subroutine CharactersScalarCmplxDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(dp), intent(in) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, "")) + endif +#endif + end subroutine CharactersScalarCmplxDp + + + subroutine CharactersScalarCmplxSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(sp), intent(in) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, "")) + endif +#endif + end subroutine CharactersScalarCmplxSp + + + subroutine CharactersScalarRealDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(dp), intent(in) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt)) + else + call xml_AddCharacters(xf=xf, chars=str(chars)) + endif +#endif + end subroutine CharactersScalarRealDp + + + subroutine CharactersScalarRealSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(sp), intent(in) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt)) + else + call xml_AddCharacters(xf=xf, chars=str(chars)) + endif +#endif + end subroutine CharactersScalarRealSp + + + subroutine CharactersScalarInt & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + integer, intent(in) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars)) +#endif + end subroutine CharactersScalarInt + + + subroutine CharactersScalarLg & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + logical, intent(in) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars)) +#endif + end subroutine CharactersScalarLg + + + + + subroutine CharactersArrayCmplxDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(dp), intent(in) , dimension(:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, ""), ws_significant=.false.) + endif +#endif + end subroutine CharactersArrayCmplxDp + + + subroutine CharactersArrayCmplxSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(sp), intent(in) , dimension(:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, ""), ws_significant=.false.) + endif +#endif + end subroutine CharactersArrayCmplxSp + + + subroutine CharactersArrayRealDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(dp), intent(in) , dimension(:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) + endif +#endif + end subroutine CharactersArrayRealDp + + + subroutine CharactersArrayRealSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(sp), intent(in) , dimension(:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) + endif +#endif + end subroutine CharactersArrayRealSp + + + subroutine CharactersArrayInt & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + integer, intent(in) , dimension(:) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) +#endif + end subroutine CharactersArrayInt + + + subroutine CharactersArrayLg & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + logical, intent(in) , dimension(:) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) +#endif + end subroutine CharactersArrayLg + + + subroutine CharactersArrayCh & + (xf, chars, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) , dimension(:) :: chars + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars, delimiter), ws_significant=.false.) +#endif + end subroutine CharactersArrayCh + + + subroutine CharactersMatrixCmplxDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(dp), intent(in) , dimension(:,:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, ""), ws_significant=.false.) + endif +#endif + end subroutine CharactersMatrixCmplxDp + + + subroutine CharactersMatrixCmplxSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + complex(sp), intent(in) , dimension(:,:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddCharacters(xf=xf, chars=str(chars, ""), ws_significant=.false.) + endif +#endif + end subroutine CharactersMatrixCmplxSp + + + subroutine CharactersMatrixRealDp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(dp), intent(in) , dimension(:,:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) + endif +#endif + end subroutine CharactersMatrixRealDp + + + subroutine CharactersMatrixRealSp & + (xf, chars, fmt) + + type(xmlf_t), intent(inout) :: xf + real(sp), intent(in) , dimension(:,:) :: chars + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddCharacters(xf=xf, chars=str(chars, fmt), ws_significant=.false.) + else + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) + endif +#endif + end subroutine CharactersMatrixRealSp + + + subroutine CharactersMatrixInt & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + integer, intent(in) , dimension(:,:) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) +#endif + end subroutine CharactersMatrixInt + + + subroutine CharactersMatrixLg & + (xf, chars) + + type(xmlf_t), intent(inout) :: xf + logical, intent(in) , dimension(:,:) :: chars + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars), ws_significant=.false.) +#endif + end subroutine CharactersMatrixLg + + + subroutine CharactersMatrixCh & + (xf, chars, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) , dimension(:,:) :: chars + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddCharacters(xf=xf, chars=str(chars, delimiter), ws_significant=.false.) +#endif + end subroutine CharactersMatrixCh + + + + subroutine AttributeScalarCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, "")) + endif +#endif + end subroutine AttributeScalarCmplxDp + + subroutine AttributeScalarCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, "")) + endif +#endif + end subroutine AttributeScalarCmplxSp + + subroutine AttributeScalarRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt)) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value)) + endif +#endif + end subroutine AttributeScalarRealDp + + subroutine AttributeScalarRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt)) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value)) + endif +#endif + end subroutine AttributeScalarRealSp + + subroutine AttributeScalarInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value)) +#endif + end subroutine AttributeScalarInt + + subroutine AttributeScalarLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value)) +#endif + end subroutine AttributeScalarLg + + + subroutine AttributeArrayCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine AttributeArrayCmplxDp + + subroutine AttributeArrayCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine AttributeArrayCmplxSp + + subroutine AttributeArrayRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine AttributeArrayRealDp + + subroutine AttributeArrayRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine AttributeArrayRealSp + + subroutine AttributeArrayInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) , dimension(:) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine AttributeArrayInt + + subroutine AttributeArrayLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) , dimension(:) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine AttributeArrayLg + + subroutine AttributeArrayCh & + (xf, name, value, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) , dimension(:) :: value + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value, delimiter), ws_significant=.false.) +#endif + end subroutine AttributeArrayCh + + subroutine AttributeMatrixCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine AttributeMatrixCmplxDp + + subroutine AttributeMatrixCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine AttributeMatrixCmplxSp + + subroutine AttributeMatrixRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine AttributeMatrixRealDp + + subroutine AttributeMatrixRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine AttributeMatrixRealSp + + subroutine AttributeMatrixInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) , dimension(:,:) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine AttributeMatrixInt + + subroutine AttributeMatrixLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) , dimension(:,:) :: value + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine AttributeMatrixLg + + subroutine AttributeMatrixCh & + (xf, name, value, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) , dimension(:,:) :: value + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddAttribute(xf=xf, name=name, value=str(value, delimiter), ws_significant=.false.) +#endif + end subroutine AttributeMatrixCh + + + subroutine PseudoAttributeScalarCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, "")) + endif +#endif + end subroutine PseudoAttributeScalarCmplxDp + + subroutine PseudoAttributeScalarCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt)) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, "")) + endif +#endif + end subroutine PseudoAttributeScalarCmplxSp + + subroutine PseudoAttributeScalarRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt)) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value)) + endif +#endif + end subroutine PseudoAttributeScalarRealDp + + subroutine PseudoAttributeScalarRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt)) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value)) + endif +#endif + end subroutine PseudoAttributeScalarRealSp + + subroutine PseudoAttributeScalarInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value)) +#endif + end subroutine PseudoAttributeScalarInt + + subroutine PseudoAttributeScalarLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value)) +#endif + end subroutine PseudoAttributeScalarLg + + + subroutine PseudoAttributeArrayCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeArrayCmplxDp + + subroutine PseudoAttributeArrayCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeArrayCmplxSp + + subroutine PseudoAttributeArrayRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeArrayRealDp + + subroutine PseudoAttributeArrayRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) , dimension(:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeArrayRealSp + + subroutine PseudoAttributeArrayInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) , dimension(:) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine PseudoAttributeArrayInt + + subroutine PseudoAttributeArrayLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) , dimension(:) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine PseudoAttributeArrayLg + + subroutine PseudoAttributeArrayCh & + (xf, name, value, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) , dimension(:) :: value + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, delimiter), ws_significant=.false.) +#endif + end subroutine PseudoAttributeArrayCh + + subroutine PseudoAttributeMatrixCmplxDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(dp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeMatrixCmplxDp + + subroutine PseudoAttributeMatrixCmplxSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + complex(sp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + ! Add empty optional fmt arg to str to avoid PGI bug: + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, ""), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeMatrixCmplxSp + + subroutine PseudoAttributeMatrixRealDp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(dp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeMatrixRealDp + + subroutine PseudoAttributeMatrixRealSp & + (xf, name, value, fmt) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + real(sp), intent(in) , dimension(:,:) :: value + character(len=*), intent(in), optional :: fmt + +#ifndef DUMMYLIB + if (present(fmt)) then + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, fmt), ws_significant=.false.) + else + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) + endif +#endif + end subroutine PseudoAttributeMatrixRealSp + + subroutine PseudoAttributeMatrixInt & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + integer, intent(in) , dimension(:,:) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine PseudoAttributeMatrixInt + + subroutine PseudoAttributeMatrixLg & + (xf, name, value) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + logical, intent(in) , dimension(:,:) :: value + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value), ws_significant=.false.) +#endif + end subroutine PseudoAttributeMatrixLg + + subroutine PseudoAttributeMatrixCh & + (xf, name, value, delimiter) + + type(xmlf_t), intent(inout) :: xf + character(len=*), intent(in) :: name + character(len=*), intent(in) , dimension(:,:) :: value + character(len=1), intent(in), optional :: delimiter + +#ifndef DUMMYLIB + call xml_AddPseudoAttribute(xf=xf, name=name, value=str(value, delimiter), ws_significant=.false.) +#endif + end subroutine PseudoAttributeMatrixCh + + +end module m_wxml_overloads diff --git a/src/xml/wxml/makefile b/src/xml/wxml/makefile new file mode 100644 index 000000000..7cc8ee288 --- /dev/null +++ b/src/xml/wxml/makefile @@ -0,0 +1,46 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_wxml.o: m_wxml_core.o m_wxml_overloads.o +m_wxml_core.o: m_wxml_escape.o +m_wxml_overloads.o: m_wxml_core.o From 346e0a9caf55c8e4247a39254fb40c4cfade0949 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 12:01:00 -0400 Subject: [PATCH 02/45] new xml parser is changed to be a static library --- src/Makefile | 19 ++++--------- src/xml/Makefile | 24 ++++++++++++++++ src/xml/common/Makefile | 59 ++++++++++++++++++++++++++++++++++++++++ src/xml/dom/FoX_dom.F90 | 4 +++ src/xml/dom/Makefile | 49 +++++++++++++++++++++++++++++++++ src/xml/fsys/Makefile | 48 ++++++++++++++++++++++++++++++++ src/xml/lib/libxml.a | Bin 0 -> 1940196 bytes src/xml/sax/Makefile | 50 ++++++++++++++++++++++++++++++++++ src/xml/utils/Makefile | 46 +++++++++++++++++++++++++++++++ src/xml/wxml/Makefile | 47 ++++++++++++++++++++++++++++++++ 10 files changed, 332 insertions(+), 14 deletions(-) create mode 100644 src/xml/Makefile create mode 100644 src/xml/common/Makefile create mode 100644 src/xml/dom/Makefile create mode 100644 src/xml/fsys/Makefile create mode 100644 src/xml/lib/libxml.a create mode 100644 src/xml/sax/Makefile create mode 100644 src/xml/utils/Makefile create mode 100644 src/xml/wxml/Makefile diff --git a/src/Makefile b/src/Makefile index 9f0cf555d..8d3646973 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,6 +5,7 @@ templates = $(wildcard templates/*.o) xml_fort = xml-fortran/xmlparse.o \ xml-fortran/read_xml_primitives.o \ xml-fortran/write_xml_primitives.o +xml_lib = -Lxml/lib -lxml #=============================================================================== # Object Files @@ -229,17 +230,12 @@ endif all: xml xml-fortran $(program) xml: - cd xml; cd fsys; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd xml; cd utils; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd xml; cd common; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd xml; cd wxml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd xml; cd sax; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd xml; cd dom; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd xml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" xml-fortran: cd xml-fortran; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" cd templates; make F90=$(F90) F90FLAGS="$(F90FLAGS)" $(program): $(objects) - $(F90) $(objects) $(templates) $(xml_fort) $(LDFLAGS) -o $@ + $(F90) $(objects) $(templates) $(xml_fort) $(xml_lib) $(LDFLAGS) -o $@ install: @install -D $(program) $(DESTDIR)$(prefix)/bin/$(program) @install -D utils/statepoint_cmp.py $(DESTDIR)$(prefix)/bin/statepoint_cmp @@ -255,12 +251,7 @@ uninstall: @rm $(DESTDIR)$(prefix)/share/man/man1/openmc.1 @rm $(DESTDIR)$(prefix)/share/doc/$(program)/copyright distclean: clean - cd xml; cd fsys; make clean - cd xml; cd utils; make clean - cd xml; cd common; make clean - cd xml; cd wxml; make clean - cd xml; cd sax; make clean - cd xml; cd dom; make clean + cd xml; make clean cd xml-fortran; make clean cd templates; make clean clean: @@ -276,7 +267,7 @@ neat: .PHONY: all xml xml-fortran install uninstall clean neat distclean %.o: %.F90 - $(F90) $(F90FLAGS) -DGIT_SHA1="\"$(GIT_SHA1)\"" -Ixml-fortran -Itemplates -c $< + $(F90) $(F90FLAGS) -DGIT_SHA1="\"$(GIT_SHA1)\"" -Ixml-fortran -Itemplates -Ixml/include -c $< #=============================================================================== # Dependencies diff --git a/src/xml/Makefile b/src/xml/Makefile new file mode 100644 index 000000000..744da3147 --- /dev/null +++ b/src/xml/Makefile @@ -0,0 +1,24 @@ +#=============================================================================== +# Targets +#=============================================================================== + +all: + mkdir -p include + mkdir -p lib + cd fsys; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd utils; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd common; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd wxml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd sax; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd dom; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" + cd lib; ar rcs libxml.a *.o; rm -f *.o + +clean: + cd fsys; make clean + cd utils; make clean + cd common; make clean + cd wxml; make clean + cd sax; make clean + cd dom; make clean + @rm -r -f include + @rm -r -f lib diff --git a/src/xml/common/Makefile b/src/xml/common/Makefile new file mode 100644 index 000000000..a4874b3b5 --- /dev/null +++ b/src/xml/common/Makefile @@ -0,0 +1,59 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../include $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_common.o: m_common_attrs.o +m_common_attrs.o: m_common_element.o m_common_error.o +m_common_buffer.o: m_common_charset.o m_common_error.o +m_common_element.o: m_common_charset.o m_common_content_model.o m_common_error.o m_common_namecheck.o +m_common_elstack.o: m_common_content_model.o m_common_error.o +m_common_entities.o: m_common_charset.o m_common_error.o +m_common_entity_expand.o: m_common_entities.o m_common_error.o +m_common_entity_expand.o: m_common_namecheck.o m_common_struct.o +m_common_io.o: m_common_error.o +m_common_namecheck.o: m_common_charset.o +m_common_namespaces.o: m_common_attrs.o m_common_charset.o m_common_error.o +m_common_namespaces.o: m_common_namecheck.o m_common_struct.o +m_common_notations.o: m_common_error.o +m_common_struct.o: m_common_charset.o m_common_element.o m_common_entities.o +m_common_struct.o: m_common_notations.o diff --git a/src/xml/dom/FoX_dom.F90 b/src/xml/dom/FoX_dom.F90 index e6d8cafc6..6e1ac36a7 100644 --- a/src/xml/dom/FoX_dom.F90 +++ b/src/xml/dom/FoX_dom.F90 @@ -1,5 +1,6 @@ module FoX_dom + use fox_common, only: countrts use fox_m_fsys_array_str use fox_m_fsys_format @@ -258,4 +259,7 @@ module FoX_dom public :: setLiveNodeLists public :: getNamespaceNodes + ! Length of array + public :: countrts + end module FoX_dom diff --git a/src/xml/dom/Makefile b/src/xml/dom/Makefile new file mode 100644 index 000000000..1f597ddbc --- /dev/null +++ b/src/xml/dom/Makefile @@ -0,0 +1,49 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../include $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_dom.o: m_dom_dom.o m_dom_error.o m_dom_extras.o m_dom_parse.o m_dom_utils.o +m_dom_dom.o: m_dom_error.o +m_dom_extras.o: m_dom_dom.o m_dom_error.o +m_dom_parse.o: m_dom_dom.o m_dom_error.o +m_dom_utils.o: m_dom_dom.o m_dom_error.o diff --git a/src/xml/fsys/Makefile b/src/xml/fsys/Makefile new file mode 100644 index 000000000..6e80265b0 --- /dev/null +++ b/src/xml/fsys/Makefile @@ -0,0 +1,48 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -I../include -c $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +fox_m_fsys_format.o: fox_m_fsys_realtypes.o fox_m_fsys_abort_flush.o +fox_m_fsys_parse_input.o: fox_m_fsys_realtypes.o +fox_m_fsys_count_parse_input.o: fox_m_fsys_realtypes.o +fox_m_fsys_string_list.o: fox_m_fsys_array_str.o diff --git a/src/xml/lib/libxml.a b/src/xml/lib/libxml.a new file mode 100644 index 0000000000000000000000000000000000000000..558e0c9a1657c4724c1243289a726ee54b81d62d GIT binary patch literal 1940196 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

>>2n1L=YEJ0_AAgXt( z`?1u!rIOdZTiuVP-fgvlCU8sj4+$_()(coTs5EyPnJ7nhxfEaIK%zU-MWTi1Pbmiy z=Pj&vD<=HORr*B|g~C#Z+D$ps(jOmO8X(8L+F;jlr1_ZaBMxW+x{jSGY|s+`I?4Op zb}qVX)%{>o6=}h)<4Ae?r1E7M8LO^iH{8Ev6mX1d3fCT7X$rg#SDFIv*HhrTvCCO^ z-r3}}C{i`jSDO{lHdD~onpP}BYNX;8``q=iiD$*k@_Z(!LASTxd?nD=jkuB@f?xu01F>ujZz3t7~pGZgtIFgxf_b zZ5;;g-38}_x=o$*jEPs@?Pgv0v((Js+rRu##k4pNQiEPZP4yadN^7L=%?9ZPU2|Qq z2*`|!D##ru-Lq0^pf(v*feT#wdU=$fE8-kt;b2rmt02aAHmHa~;08Td0NkLH+yfR{ zH|SJ)UI!nAoQ3Et6FmG^Z-)L;t0N=NUY z$WN}?SRLA-ci7uT1)j4Snj3R7{X-6`Mn=!eMVeGAPewU(H8iUU8emKw=Qi$ycq?K6 z!Fb4OXvGUP?#zIQ8yr%z6b5&p*`Z$$1%;&mH7}~qqNt+!8f~&|D6QGM#6V1U_z z9URq11rid~H&UtW=Dl~k?OIjj9CJi-H%zR!$hr8PJ5|4*Klc0u9%eUH2XoHrBq3H) z3T|07puAu*FpE;v!D}a7>>Ezj0880#g53P4g^N-rKdn|R??=nNmQwkvD-m`^kbE75 z5l1hJxdk40LrKSXOT&-&TN)nCTN-k&1=G{UP7lrFT@7t;aAQM$`>h058@1c2iWiEv zyg&f8Lp=HQLT7qIz%7=yGwAfaOFsCp%5w*+|%iAV;&dBP2QrH^9i`kmytfmg%gmGAysdrN8u4=7x8=8WrASR#Z=WNaM zVo&5E9g$|diMZ2B2M65#k|y?lF#uXIVa}ewcDqB~cDvut*>2Z6Ad9x!{XR+_Tkv`? zZk3+k?RK9C*>0Dk5p65hyt7VEy9TNg*5V=he7rZ%j$B0*xqy+kV0&p;e*S;@42W3xMw`Mm?_BgTuOVA&3$W5jc2qc3cSDdJK|ywm zLXe_|2(~)5mPQE@G-=Zf48PSV5hBTa28ms8#IdZEYSJ;>;pRE9q8&^Q#q1{sP z8aBA(lvA?ON6JNssjCPvbw=-P0pcVkp?#j1J_M�*D2| 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≈E=Q+vnMWR0)I?TBooH>shof)!EP8)tZ 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 S(ytf9uq4 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 Dq*C7N literal 0 HcmV?d00001 diff --git a/src/xml/sax/Makefile b/src/xml/sax/Makefile new file mode 100644 index 000000000..c0a134055 --- /dev/null +++ b/src/xml/sax/Makefile @@ -0,0 +1,50 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../include $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_sax.o: m_sax_operate.o +m_sax_operate.o: m_sax_parser.o m_sax_reader.o m_sax_types.o +m_sax_parser.o: m_sax_reader.o m_sax_tokenizer.o m_sax_types.o +m_sax_reader.o: m_sax_xml_source.o +m_sax_tokenizer.o: m_sax_reader.o m_sax_types.o +m_sax_types.o: m_sax_reader.o diff --git a/src/xml/utils/Makefile b/src/xml/utils/Makefile new file mode 100644 index 000000000..449bd4d91 --- /dev/null +++ b/src/xml/utils/Makefile @@ -0,0 +1,46 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../include $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_utils.o: fox_m_utils_uuid.o fox_m_utils_uri.o +fox_m_utils_uuid.o: fox_m_utils_mtprng.o diff --git a/src/xml/wxml/Makefile b/src/xml/wxml/Makefile new file mode 100644 index 000000000..55dcc3d9c --- /dev/null +++ b/src/xml/wxml/Makefile @@ -0,0 +1,47 @@ +source = $(wildcard *.F90) +objects = $(source:.F90=.o) + +#=============================================================================== +# Compiler Options +#=============================================================================== + +# Ignore unusd variables + +ifeq ($(MACHINE),bluegene) + override F90 = xlf2003 +endif + +ifeq ($(F90),ifort) + override F90FLAGS += -warn nounused +endif + +#=============================================================================== +# Targets +#=============================================================================== + +all: $(objects) + mv *.mod ../include + mv *.o ../lib +clean: + @rm -f *.o *.mod +neat: + @rm -f *.o *.mod + +#=============================================================================== +# Rules +#=============================================================================== + +.SUFFIXES: .F90 .o + +.PHONY: clean neat + +%.o: %.F90 + $(F90) $(F90FLAGS) -c -I../include $< + +#=============================================================================== +# Dependencies +#=============================================================================== + +FoX_wxml.o: m_wxml_core.o m_wxml_overloads.o +m_wxml_core.o: m_wxml_escape.o +m_wxml_overloads.o: m_wxml_core.o From b893a3f61c2fb510c50aedc56c5971eec52d0085 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 12:06:42 -0400 Subject: [PATCH 03/45] removed old makefiles --- src/xml/common/makefile | 58 ---------------------------------------- src/xml/dom/makefile | 48 --------------------------------- src/xml/fsys/makefile | 47 -------------------------------- src/xml/lib/libxml.a | Bin 1940196 -> 0 bytes src/xml/sax/makefile | 49 --------------------------------- src/xml/utils/makefile | 45 ------------------------------- src/xml/wxml/makefile | 46 ------------------------------- 7 files changed, 293 deletions(-) delete mode 100644 src/xml/common/makefile delete mode 100644 src/xml/dom/makefile delete mode 100644 src/xml/fsys/makefile delete mode 100644 src/xml/lib/libxml.a delete mode 100644 src/xml/sax/makefile delete mode 100644 src/xml/utils/makefile delete mode 100644 src/xml/wxml/makefile diff --git a/src/xml/common/makefile b/src/xml/common/makefile deleted file mode 100644 index 063c288a2..000000000 --- a/src/xml/common/makefile +++ /dev/null @@ -1,58 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c -I../fsys -I../utils $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -FoX_common.o: m_common_attrs.o -m_common_attrs.o: m_common_element.o m_common_error.o -m_common_buffer.o: m_common_charset.o m_common_error.o -m_common_element.o: m_common_charset.o m_common_content_model.o m_common_error.o m_common_namecheck.o -m_common_elstack.o: m_common_content_model.o m_common_error.o -m_common_entities.o: m_common_charset.o m_common_error.o -m_common_entity_expand.o: m_common_entities.o m_common_error.o -m_common_entity_expand.o: m_common_namecheck.o m_common_struct.o -m_common_io.o: m_common_error.o -m_common_namecheck.o: m_common_charset.o -m_common_namespaces.o: m_common_attrs.o m_common_charset.o m_common_error.o -m_common_namespaces.o: m_common_namecheck.o m_common_struct.o -m_common_notations.o: m_common_error.o -m_common_struct.o: m_common_charset.o m_common_element.o m_common_entities.o -m_common_struct.o: m_common_notations.o diff --git a/src/xml/dom/makefile b/src/xml/dom/makefile deleted file mode 100644 index 98286bc78..000000000 --- a/src/xml/dom/makefile +++ /dev/null @@ -1,48 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils -I../sax -I../wxml $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -FoX_dom.o: m_dom_dom.o m_dom_error.o m_dom_extras.o m_dom_parse.o m_dom_utils.o -m_dom_dom.o: m_dom_error.o -m_dom_extras.o: m_dom_dom.o m_dom_error.o -m_dom_parse.o: m_dom_dom.o m_dom_error.o -m_dom_utils.o: m_dom_dom.o m_dom_error.o diff --git a/src/xml/fsys/makefile b/src/xml/fsys/makefile deleted file mode 100644 index d6b81cc87..000000000 --- a/src/xml/fsys/makefile +++ /dev/null @@ -1,47 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -fox_m_fsys_format.o: fox_m_fsys_realtypes.o fox_m_fsys_abort_flush.o -fox_m_fsys_parse_input.o: fox_m_fsys_realtypes.o -fox_m_fsys_count_parse_input.o: fox_m_fsys_realtypes.o -fox_m_fsys_string_list.o: fox_m_fsys_array_str.o diff --git a/src/xml/lib/libxml.a b/src/xml/lib/libxml.a deleted file mode 100644 index 558e0c9a1657c4724c1243289a726ee54b81d62d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1940196 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
>>2n1L=YEJ0_AAgXt( z`?1u!rIOdZTiuVP-fgvlCU8sj4+$_()(coTs5EyPnJ7nhxfEaIK%zU-MWTi1Pbmiy z=Pj&vD<=HORr*B|g~C#Z+D$ps(jOmO8X(8L+F;jlr1_ZaBMxW+x{jSGY|s+`I?4Op zb}qVX)%{>o6=}h)<4Ae?r1E7M8LO^iH{8Ev6mX1d3fCT7X$rg#SDFIv*HhrTvCCO^ z-r3}}C{i`jSDO{lHdD~onpP}BYNX;8``q=iiD$*k@_Z(!LASTxd?nD=jkuB@f?xu01F>ujZz3t7~pGZgtIFgxf_b zZ5;;g-38}_x=o$*jEPs@?Pgv0v((Js+rRu##k4pNQiEPZP4yadN^7L=%?9ZPU2|Qq z2*`|!D##ru-Lq0^pf(v*feT#wdU=$fE8-kt;b2rmt02aAHmHa~;08Td0NkLH+yfR{ zH|SJ)UI!nAoQ3Et6FmG^Z-)L;t0N=NUY z$WN}?SRLA-ci7uT1)j4Snj3R7{X-6`Mn=!eMVeGAPewU(H8iUU8emKw=Qi$ycq?K6 z!Fb4OXvGUP?#zIQ8yr%z6b5&p*`Z$$1%;&mH7}~qqNt+!8f~&|D6QGM#6V1U_z z9URq11rid~H&UtW=Dl~k?OIjj9CJi-H%zR!$hr8PJ5|4*Klc0u9%eUH2XoHrBq3H) z3T|07puAu*FpE;v!D}a7>>Ezj0880#g53P4g^N-rKdn|R??=nNmQwkvD-m`^kbE75 z5l1hJxdk40LrKSXOT&-&TN)nCTN-k&1=G{UP7lrFT@7t;aAQM$`>h058@1c2iWiEv zyg&f8Lp=HQLT7qIz%7=yGwAfaOFsCp%5w*+|%iAV;&dBP2QrH^9i`kmytfmg%gmGAysdrN8u4=7x8=8WrASR#Z=WNaM zVo&5E9g$|diMZ2B2M65#k|y?lF#uXIVa}ewcDqB~cDvut*>2Z6Ad9x!{XR+_Tkv`? zZk3+k?RK9C*>0Dk5p65hyt7VEy9TNg*5V=he7rZ%j$B0*xqy+kV0&p;e*S;@42W3xMw`Mm?_BgTuOVA&3$W5jc2qc3cSDdJK|ywm zLXe_|2(~)5mPQE@G-=Zf48PSV5hBTa28ms8#IdZEYSJ;>;pRE9q8&^Q#q1{sP z8aBA(lvA?ON6JNssjCPvbw=-P0pcVkp?#j1J_M�*D2| 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≈E=Q+vnMWR0)I?TBooH>shof)!EP8)tZ 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 S(ytf9uq4 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 Dq*C7N diff --git a/src/xml/sax/makefile b/src/xml/sax/makefile deleted file mode 100644 index a440a53c8..000000000 --- a/src/xml/sax/makefile +++ /dev/null @@ -1,49 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -FoX_sax.o: m_sax_operate.o -m_sax_operate.o: m_sax_parser.o m_sax_reader.o m_sax_types.o -m_sax_parser.o: m_sax_reader.o m_sax_tokenizer.o m_sax_types.o -m_sax_reader.o: m_sax_xml_source.o -m_sax_tokenizer.o: m_sax_reader.o m_sax_types.o -m_sax_types.o: m_sax_reader.o diff --git a/src/xml/utils/makefile b/src/xml/utils/makefile deleted file mode 100644 index dca350436..000000000 --- a/src/xml/utils/makefile +++ /dev/null @@ -1,45 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c -I../fsys $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -FoX_utils.o: fox_m_utils_uuid.o fox_m_utils_uri.o -fox_m_utils_uuid.o: fox_m_utils_mtprng.o diff --git a/src/xml/wxml/makefile b/src/xml/wxml/makefile deleted file mode 100644 index 7cc8ee288..000000000 --- a/src/xml/wxml/makefile +++ /dev/null @@ -1,46 +0,0 @@ -source = $(wildcard *.F90) -objects = $(source:.F90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unusd variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) - -clean: - @rm -f *.o *.mod -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .F90 .o - -.PHONY: clean neat - -%.o: %.F90 - $(F90) $(F90FLAGS) -c -I../fsys -I../common -I../utils $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -FoX_wxml.o: m_wxml_core.o m_wxml_overloads.o -m_wxml_core.o: m_wxml_escape.o -m_wxml_overloads.o: m_wxml_core.o From ca5a30672ac464044b0ca20b458e610a0450aef5 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 12:07:10 -0400 Subject: [PATCH 04/45] added static library extension --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8aa7c1f97..4ecff16b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Compiled objects and modules +*.a *.o *.mod *.log From 6524e00a77c64fd1a8612de431ebd5b1a114c059 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 12:08:35 -0400 Subject: [PATCH 05/45] added xml interface --- src/OBJECTS | 3 ++- src/xml_interface.F90 | 53 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/xml_interface.F90 diff --git a/src/OBJECTS b/src/OBJECTS index b07990675..5526dac4b 100644 --- a/src/OBJECTS +++ b/src/OBJECTS @@ -59,4 +59,5 @@ string.o \ tally.o \ tally_header.o \ tally_initialize.o \ -timer_header.o +timer_header.o \ +xml_interface.o diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 new file mode 100644 index 000000000..6eb5e0a7e --- /dev/null +++ b/src/xml_interface.F90 @@ -0,0 +1,53 @@ +module xml_interface + + use fox_dom + + implicit none + private + public :: open_xmldoc + public :: close_xmldoc + +contains + +!=============================================================================== +! OPEN_XMLDOC +!=============================================================================== + + subroutine open_xmldoc(ptr, filename) + + character(len=*) :: filename + type(Node), pointer :: ptr + + ptr => parseFile(trim(filename)) + + end subroutine open_xmldoc + +!=============================================================================== +! CLOSE_XMLDOC +!=============================================================================== + + subroutine close_xmldoc(ptr) + + type(Node), pointer :: ptr + + call destroy(ptr) + + end subroutine close_xmldoc + +!=============================================================================== +! GET_ELEMENT +!=============================================================================== + + subroutine get_element(inptr, elename, outptr) + + character(len=*) :: elename + type(Node), pointer :: inptr + type(Node), pointer :: outptr + type(NodeList), pointer :: tempptr + + tempptr => getElementsByTagname(inptr, elename) + outptr => item(tempptr,0) + + end subroutine get_element + +end module xml_interface From 8d8511020a1df9fafdbd345b44318ee7884fcaf3 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 14:36:12 -0400 Subject: [PATCH 06/45] made target the actual library so it doesn't recompile all the time --- src/xml/Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/xml/Makefile b/src/xml/Makefile index 744da3147..f9151178c 100644 --- a/src/xml/Makefile +++ b/src/xml/Makefile @@ -1,8 +1,10 @@ +xml_lib = lib/libxml.a + #=============================================================================== # Targets #=============================================================================== -all: +$(xml_lib): mkdir -p include mkdir -p lib cd fsys; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" @@ -22,3 +24,9 @@ clean: cd dom; make clean @rm -r -f include @rm -r -f lib + +#=============================================================================== +# Rules +#=============================================================================== + +.PHONY: clean From e17d78384eacd937796f9db301fe035768f4b7d2 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 17:31:30 -0400 Subject: [PATCH 07/45] added long integer scalar processing to XML parser --- src/xml/dom/m_dom_extras.F90 | 68 ++++++++++++++++++++++ src/xml/fsys/fox_m_fsys_parse_input.F90 | 77 +++++++++++++++++++++++++ 2 files changed, 145 insertions(+) diff --git a/src/xml/dom/m_dom_extras.F90 b/src/xml/dom/m_dom_extras.F90 index f301622d5..790561746 100644 --- a/src/xml/dom/m_dom_extras.F90 +++ b/src/xml/dom/m_dom_extras.F90 @@ -31,6 +31,7 @@ module m_dom_extras module procedure extractDataContentIntSca module procedure extractDataContentIntArr module procedure extractDataContentIntMat + module procedure extractDataContentLongSca module procedure extractDataContentLgSca module procedure extractDataContentLgArr module procedure extractDataContentLgMat @@ -56,6 +57,7 @@ module m_dom_extras module procedure extractDataAttributeIntSca module procedure extractDataAttributeIntArr module procedure extractDataAttributeIntMat + module procedure extractDataAttributeLongSca module procedure extractDataAttributeLgSca module procedure extractDataAttributeLgArr module procedure extractDataAttributeLgMat @@ -227,6 +229,33 @@ endif end subroutine extractDataContentIntSca +subroutine extractDataContentLongSca(arg, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + integer(8), intent(out) :: data + + integer, intent(out), optional :: num, iostat + + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataContentIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + if (present(ex)) then + call rts(getTextContent(arg, ex), data, num, iostat) + else + call rts(getTextContent(arg), data, num, iostat) + endif + + end subroutine extractDataContentLongSca + subroutine extractDataContentLgSca(arg, data, num, iostat, ex) type(DOMException), intent(out), optional :: ex type(Node), pointer :: arg @@ -863,6 +892,45 @@ endif end subroutine extractDataAttributeIntSca +subroutine extractDataAttributeLongSca(arg, name, data, num, iostat, ex) + type(DOMException), intent(out), optional :: ex + type(Node), pointer :: arg + character(len=*), intent(in) :: name + + integer(8), intent(out) :: data + integer, intent(out), optional :: num, iostat + if (.not.associated(arg)) then + if (getFoX_checks().or.FoX_NODE_IS_NULL<200) then + call throw_exception(FoX_NODE_IS_NULL, "extractDataAttributeIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + elseif (getNodeType(arg)/=ELEMENT_NODE) then + if (getFoX_checks().or.FoX_INVALID_NODE<200) then + call throw_exception(FoX_INVALID_NODE, "extractDataAttributeIntSca", ex) + if (present(ex)) then + if (inException(ex)) then + return + endif + endif +endif + + endif + + + if (present(ex)) then + call rts(getAttribute(arg, name, ex), data, num, iostat) + else + call rts(getAttribute(arg, name), data, num, iostat) + endif + + + end subroutine extractDataAttributeLongSca + subroutine extractDataAttributeLgSca(arg, name, data, num, iostat, ex) type(DOMException), intent(out), optional :: ex type(Node), pointer :: arg diff --git a/src/xml/fsys/fox_m_fsys_parse_input.F90 b/src/xml/fsys/fox_m_fsys_parse_input.F90 index e0307e99c..45551c1d6 100644 --- a/src/xml/fsys/fox_m_fsys_parse_input.F90 +++ b/src/xml/fsys/fox_m_fsys_parse_input.F90 @@ -16,6 +16,7 @@ module fox_m_fsys_parse_input module procedure scalartostring module procedure scalartological module procedure scalartointeger + module procedure scalartolong module procedure scalartorealsp module procedure scalartorealdp module procedure scalartocomplexsp @@ -346,6 +347,82 @@ contains #endif end subroutine scalartointeger + subroutine scalartolong(s, data, num, iostat) + character(len=*), intent(in) :: s + integer(8), intent(out) :: data + integer, intent(out), optional :: num, iostat +#ifndef DUMMYLIB + logical :: bracketed + integer :: i, j, ij, k, s_i, err, ios, length + real :: r, c + + + s_i = 1 + err = 0 + data = 0 + ij = 0 + length = 1 + loop: do i = 1, 1 + k = verify(s(s_i:), whitespace) + if (k==0) exit loop + s_i = s_i + k - 1 + if (s(s_i:s_i)==",") then + if (s_i+1>len(s)) then + err = 2 + exit loop + endif + k = verify(s(s_i+1:), whitespace) + s_i = s_i + k - 1 + endif + k = scan(s(s_i:), whitespace//",") + if (k==0) then + k = len(s) + else + k = s_i + k - 2 + endif + read(s(s_i:k), *, iostat=ios) data + if (ios/=0) then + err = 2 + exit loop + endif + ij = ij + 1 + s_i = k + 2 + if (ijlen(s)) exit loop + + end do loop + + if (present(num)) num = ij + if (ij Date: Mon, 22 Jul 2013 17:32:07 -0400 Subject: [PATCH 08/45] added logic to skip using fox_dom module --- src/utils/build_dependencies.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/build_dependencies.py b/src/utils/build_dependencies.py index 44a5c5cca..3aa22c607 100755 --- a/src/utils/build_dependencies.py +++ b/src/utils/build_dependencies.py @@ -11,7 +11,7 @@ for src in glob.iglob('*.F90'): d = re.findall(r'\n\s*use\s+(\w+)', open(src,'r').read()) for name in d: - if name in ['mpi','hdf5','h5lt']: + if name in ['mpi','hdf5','h5lt','fox_dom']: continue if name.startswith('xml_data_'): name = name.replace('xml_data_', 'templates/') @@ -20,7 +20,7 @@ for src in glob.iglob('*.F90'): dependencies[module] = sorted(list(deps)) -for module in dependencies.keys(): +for module in sorted(dependencies.keys()): for dep in dependencies[module]: print("{0}.o: {1}.o".format(module, dep)) print('') From 31fb97e03082c6b6bf365d5e287e4d68f127887b Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 17:32:39 -0400 Subject: [PATCH 09/45] started building up xml interface and processing settings XML file --- src/DEPENDENCIES | 10 +- src/input_xml.F90 | 54 +++++----- src/xml_interface.F90 | 222 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 252 insertions(+), 34 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index e5d20644b..39ddf39e9 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -177,8 +177,8 @@ hdf5_summary.o: geometry_header.o hdf5_summary.o: global.o hdf5_summary.o: material_header.o hdf5_summary.o: mesh_header.o -hdf5_summary.o: output_interface.o hdf5_summary.o: output.o +hdf5_summary.o: output_interface.o hdf5_summary.o: string.o hdf5_summary.o: tally_header.o @@ -194,8 +194,8 @@ initialize.o: global.o initialize.o: hdf5_interface.o initialize.o: hdf5_summary.o initialize.o: input_xml.o -initialize.o: output_interface.o initialize.o: output.o +initialize.o: output_interface.o initialize.o: random_lcg.o initialize.o: source.o initialize.o: state_point.o @@ -223,6 +223,7 @@ input_xml.o: templates/materials_t.o input_xml.o: templates/plots_t.o input_xml.o: templates/settings_t.o input_xml.o: templates/tallies_t.o +input_xml.o: xml_interface.o interpolation.o: constants.o interpolation.o: endf_header.o @@ -374,3 +375,8 @@ tally_initialize.o: global.o tally_initialize.o: tally_header.o timer_header.o: constants.o + +xml_interface.o: constants.o +xml_interface.o: error.o +xml_interface.o: global.o + diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 617788e47..de0918daa 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -48,6 +48,7 @@ contains subroutine read_settings_xml() use xml_data_settings_t + use xml_interface integer :: i ! loop index integer :: n @@ -56,6 +57,11 @@ contains character(MAX_FILE_LEN) :: env_variable character(MAX_WORD_LEN) :: type character(MAX_LINE_LEN) :: filename + type(Node), pointer :: doc + type(Node), pointer :: node_eigenvalue + logical :: found + integer :: temp_int + integer(8) :: temp_long ! Display output message message = "Reading settings XML file..." @@ -82,15 +88,16 @@ contains ! Parse settings.xml file call read_xml_file_settings_t(filename) + call open_xmldoc(doc, filename) ! Find cross_sections.xml file -- the first place to look is the ! settings.xml file. If no file is found there, then we check the ! CROSS_SECTIONS environment variable - - if (len_trim(cross_sections_) == 0 .and. run_mode /= MODE_PLOTTING) then + found = check_for_node(doc, "cross_sections") + if (.not. found .and. run_mode /= MODE_PLOTTING) then ! No cross_sections.xml file specified in settings.xml, check environment ! variable - call get_environment_variable("CROSS_SECTIONS", env_variable) + call get_environment_variable("CROSS_SECTIONS", env_variable) if (len_trim(env_variable) == 0) then message = "No cross_sections.xml file was specified in settings.xml & &or in the CROSS_SECTIONS environment variable." @@ -99,47 +106,45 @@ contains path_cross_sections = trim(env_variable) end if else - path_cross_sections = trim(cross_sections_) + call get_node_value(doc, "cross_sections", path_cross_sections) end if ! Set output directory if a path has been specified on the ! element - if (len_trim(output_path_) > 0) then - path_output = output_path_ + found = check_for_node(doc, "output_path") + if (found) then + call get_node_value(doc, "output_path", path_output) if (.not. ends_with(path_output, "/")) & path_output = trim(path_output) // "/" end if - ! Make sure that either criticality or fixed source was specified - if (eigenvalue_ % batches == 0 .and. fixed_source_ % batches == 0 & - .and. criticality_ % batches == 0) then - message = "Number of batches on or & - &tag was zero." + ! Make sure that either eigenvalue or fixed source was specified + if (.not.check_for_node(doc, "eigenvalue") .and. & + .not.check_for_node(doc, "fixed_source")) then + message = " or not specified." call fatal_error() end if - ! Check for old tag - if (criticality_ % batches > 0) then - eigenvalue_ = criticality_ - message = "The element has been deprecated and & - &replaced by ." - call warning() - end if - ! Eigenvalue information - if (eigenvalue_ % batches > 0) then + if (check_for_node(doc, "eigenvalue")) then ! Set run mode if (run_mode == NONE) run_mode = MODE_EIGENVALUE + ! Get pointer to eigenvalue XML block + call get_node_ptr(doc, "eigenvalue", node_eigenvalue) + ! Check number of particles - if (len_trim(eigenvalue_ % particles) == 0) then - message = "Need to specify number of particles per cycles." + if (.not.check_for_node(node_eigenvalue, "particles")) then + message = "Need to specify number of particles per generation." call fatal_error() end if + ! Get number of particles + call get_node_value(node_eigenvalue, "particles", temp_long) + ! If the number of particles was specified as a command-line argument, we ! don't set it here - if (n_particles == 0) n_particles = str_to_int(eigenvalue_ % particles) + if (n_particles == 0) n_particles = temp_long ! Copy batch and generation information n_batches = eigenvalue_ % batches @@ -569,6 +574,9 @@ contains #endif end if + ! Close settings XML file + call close_xmldoc(doc) + end subroutine read_settings_xml !=============================================================================== diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 6eb5e0a7e..3d6924a8b 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -1,11 +1,27 @@ module xml_interface + use constants, only: MAX_LINE_LEN + use error, only: fatal_error + use global, only: message use fox_dom implicit none private + public :: Node public :: open_xmldoc public :: close_xmldoc + public :: check_for_node + public :: get_node_ptr + public :: get_node_value + + integer, parameter :: ATTR_NODE = 0 + integer, parameter :: ELEM_NODE = 1 + + interface get_node_value + module procedure get_node_value_integer + module procedure get_node_value_long + module procedure get_node_value_string + end interface get_node_value contains @@ -19,6 +35,7 @@ contains type(Node), pointer :: ptr ptr => parseFile(trim(filename)) + ptr => getDocumentElement(ptr) end subroutine open_xmldoc @@ -35,19 +52,206 @@ contains end subroutine close_xmldoc !=============================================================================== -! GET_ELEMENT +! CHECK_FOR_NODE !=============================================================================== - subroutine get_element(inptr, elename, outptr) + function check_for_node(ptr, node_name) result(found) - character(len=*) :: elename - type(Node), pointer :: inptr - type(Node), pointer :: outptr - type(NodeList), pointer :: tempptr + character(len=*), intent(in) :: node_name + logical :: found + type(Node), pointer, intent(in) :: ptr - tempptr => getElementsByTagname(inptr, elename) - outptr => item(tempptr,0) + type(Node), pointer :: temp_ptr + type(NodeList), pointer :: elem_list - end subroutine get_element + ! Default that we found it and attribute + found = .true. + + ! Get the attribute node + temp_ptr => getAttributeNode(ptr, trim(node_name)) + + ! Check if node exists + if (associated(temp_ptr)) return + + ! Check for a sub-element + elem_list => getElementsByTagName(ptr, trim(node_name)) + + ! Get the length of the list + if (getLength(elem_list) == 0) then + found = .false. + return + end if + + end function check_for_node + +!=============================================================================== +! GET_NODE_PTR +!=============================================================================== + + subroutine get_node_ptr(in_ptr, node_name, out_ptr, found) + + character(len=*), intent(in) :: node_name + logical, intent(out), optional :: found + type(Node), pointer, intent(in) :: in_ptr + type(Node), pointer, intent(out) :: out_ptr + + logical :: found_ + type(NodeList), pointer :: elem_list => null() + + ! Set found to false + found_ = .false. + + ! Check for a sub-element + elem_list => getElementsByTagName(in_ptr, trim(node_name)) + + ! Get the length of the list + if (getLength(elem_list) == 0) return + + ! Point to the new element + out_ptr => item(elem_list, 0) + found_ = .true. + + ! Check to output found + if (present(found)) found = found_ + + end subroutine get_node_ptr + +!=============================================================================== +! GET_NODE_VALUE_INTEGER +!=============================================================================== + + subroutine get_node_value_integer(ptr, node_name, result_int) + + character(len=*), intent(in) :: node_name + integer :: result_int + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_int) + else + call extractDataContent(temp_ptr, result_int) + end if + + end subroutine get_node_value_integer + +!=============================================================================== +! GET_NODE_VALUE_INTEGER +!=============================================================================== + + subroutine get_node_value_long(ptr, node_name, result_long) + + character(len=*), intent(in) :: node_name + integer(8) :: result_long + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_long) + else + call extractDataContent(temp_ptr, result_long) + end if + + end subroutine get_node_value_long + +!=============================================================================== +! GET_NODE_VALUE_STRING +!=============================================================================== + + subroutine get_node_value_string(ptr, node_name, result_str) + + character(len=*), intent(in) :: node_name + character(len=MAX_LINE_LEN) :: result_str + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_str) + else + call extractDataContent(temp_ptr, result_str) + end if + + end subroutine get_node_value_string + +!=============================================================================== +! GET_NODE +!=============================================================================== + + subroutine get_node(in_ptr, node_name, out_ptr, node_type, found) + + character(len=*), intent(in) :: node_name + integer, intent(out) :: node_type + logical, intent(out) :: found + type(Node), pointer, intent(in) :: in_ptr + type(Node), pointer, intent(out) :: out_ptr + + type(NodeList), pointer :: elem_list + + ! Default that we found it and attribute + found = .true. + node_type = ATTR_NODE + + ! Get the attribute node + out_ptr => getAttributeNode(in_ptr, trim(node_name)) + + ! Check if node exists + if (associated(out_ptr)) return + + ! Check for a sub-element + elem_list => getElementsByTagName(in_ptr, trim(node_name)) + + ! Get the length of the list + if (getLength(elem_list) == 0) then + found = .false. + return + end if + + ! Point to the new element + node_type = ELEM_NODE + out_ptr => item(elem_list, 0) + + end subroutine get_node end module xml_interface From 49d9bf6c221543dd12afee22799ffe405f8aad2e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 18:43:16 -0400 Subject: [PATCH 10/45] fixed Makefile after merge --- src/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index e76aa1788..8d3646973 100644 --- a/src/Makefile +++ b/src/Makefile @@ -228,12 +228,14 @@ endif # Targets #=============================================================================== -all: xml-fortran $(program) +all: xml xml-fortran $(program) +xml: + cd xml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" xml-fortran: cd xml-fortran; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" cd templates; make F90=$(F90) F90FLAGS="$(F90FLAGS)" $(program): $(objects) - $(F90) $(objects) $(templates) $(xml_fort) $(LDFLAGS) -o $@ + $(F90) $(objects) $(templates) $(xml_fort) $(xml_lib) $(LDFLAGS) -o $@ install: @install -D $(program) $(DESTDIR)$(prefix)/bin/$(program) @install -D utils/statepoint_cmp.py $(DESTDIR)$(prefix)/bin/statepoint_cmp From a4118b73f38bb4f8ee70163e7fbad709e177f2f5 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 22 Jul 2013 23:42:39 -0400 Subject: [PATCH 11/45] added array size and read array methods to interface and now read in settings through trace information --- src/input_xml.F90 | 171 ++++++++++++++++++++++++++++-------------- src/xml_interface.F90 | 170 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 281 insertions(+), 60 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index de0918daa..d2ccf8a7d 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -50,18 +50,22 @@ contains use xml_data_settings_t use xml_interface + character(MAX_LINE_LEN) :: temp_str integer :: i ! loop index integer :: n integer :: coeffs_reqd + integer :: temp_int + integer :: temp_int_array(3) + integer(8) :: temp_long logical :: file_exists character(MAX_FILE_LEN) :: env_variable character(MAX_WORD_LEN) :: type character(MAX_LINE_LEN) :: filename type(Node), pointer :: doc - type(Node), pointer :: node_eigenvalue - logical :: found - integer :: temp_int - integer(8) :: temp_long + type(Node), pointer :: node_mode + type(Node), pointer :: node_source + type(Node), pointer :: node_dist + type(Node), pointer :: node_cutoff ! Display output message message = "Reading settings XML file..." @@ -93,8 +97,8 @@ contains ! Find cross_sections.xml file -- the first place to look is the ! settings.xml file. If no file is found there, then we check the ! CROSS_SECTIONS environment variable - found = check_for_node(doc, "cross_sections") - if (.not. found .and. run_mode /= MODE_PLOTTING) then + if (.not. check_for_node(doc, "cross_sections") .and. & + run_mode /= MODE_PLOTTING) then ! No cross_sections.xml file specified in settings.xml, check environment ! variable call get_environment_variable("CROSS_SECTIONS", env_variable) @@ -111,8 +115,7 @@ contains ! Set output directory if a path has been specified on the ! element - found = check_for_node(doc, "output_path") - if (found) then + if (check_for_node(doc, "output_path")) then call get_node_value(doc, "output_path", path_output) if (.not. ends_with(path_output, "/")) & path_output = trim(path_output) // "/" @@ -131,26 +134,28 @@ contains if (run_mode == NONE) run_mode = MODE_EIGENVALUE ! Get pointer to eigenvalue XML block - call get_node_ptr(doc, "eigenvalue", node_eigenvalue) + call get_node_ptr(doc, "eigenvalue", node_mode) ! Check number of particles - if (.not.check_for_node(node_eigenvalue, "particles")) then + if (.not.check_for_node(node_mode, "particles")) then message = "Need to specify number of particles per generation." call fatal_error() end if ! Get number of particles - call get_node_value(node_eigenvalue, "particles", temp_long) + call get_node_value(node_mode, "particles", temp_long) ! If the number of particles was specified as a command-line argument, we ! don't set it here if (n_particles == 0) n_particles = temp_long ! Copy batch and generation information - n_batches = eigenvalue_ % batches - n_inactive = eigenvalue_ % inactive + call get_node_value(node_mode, "batches", n_batches) + call get_node_value(node_mode, "inactive", n_inactive) n_active = n_batches - n_inactive - gen_per_batch = eigenvalue_ % generations_per_batch + if (check_for_node(node_mode, "generations_per_batch")) & + call get_node_value(node_mode, "generations_per_batch", & + gen_per_batch) ! Allocate array for batch keff and entropy allocate(k_generation(n_batches*gen_per_batch)) @@ -159,23 +164,29 @@ contains end if ! Fixed source calculation information - if (fixed_source_ % batches > 0) then + if (check_for_node(doc, "fixed_source")) then ! Set run mode if (run_mode == NONE) run_mode = MODE_FIXEDSOURCE + ! Get pointer to fixed_source XML block + call get_node_ptr(doc, "fixed_source", node_mode) + ! Check number of particles - if (len_trim(fixed_source_ % particles) == 0) then - message = "Need to specify number of particles per cycles." + if (.not.check_for_node(node_mode, "fixed_source")) then + message = "Need to specify number of particles per batch." call fatal_error() end if + ! Get number of particles + call get_node_value(node_mode, "particles", temp_long) + ! If the number of particles was specified as a command-line argument, we ! don't set it here - if (n_particles == 0) n_particles = str_to_int(fixed_source_ % particles) + if (n_particles == 0) n_particles = temp_long ! Copy batch information - n_batches = fixed_source_ % batches - n_active = fixed_source_ % batches + call get_node_value(node_mode, "batches", n_batches) + n_active = n_batches n_inactive = 0 gen_per_batch = 1 end if @@ -193,10 +204,15 @@ contains end if ! Copy random number seed if specified - if (seed_ > 0) seed = seed_ + if (check_for_node(doc, "seed")) call get_node_value(doc, "seed", seed) ! Energy grid methods - select case (energy_grid_) + if (check_for_node(doc, "energy_grid")) then + call get_node_value(doc, "energy_grid", temp_str) + else + temp_str = 'union' + end if + select case (trim(temp_str)) case ('nuclide') grid_method = GRID_NUCLIDE case ('union') @@ -211,13 +227,24 @@ contains ! Verbosity if (verbosity_ > 0) verbosity = verbosity_ + if (check_for_node(doc, "verbosity")) & + call get_node_value(doc, "verbosity", verbosity) ! ========================================================================== ! EXTERNAL SOURCE - if (source_ % file /= '') then + ! Get pointer to source + if (check_for_node(doc, "source")) then + call get_node_ptr(doc, "source", node_source) + else + message = "No source specified in settings XML file." + call fatal_error() + end if + + ! Check for external source file + if (check_for_node(node_source, "file")) then ! Copy path of source file - path_source = source_ % file + call get_node_value(node_source, "file", path_source) ! Check if source file exists inquire(FILE=path_source, EXIST=file_exists) @@ -228,12 +255,18 @@ contains end if else + ! Spatial distribution for external source - if (source_ % space % type /= '') then - ! Read type of spatial distribution - type = source_ % space % type + if (check_for_node(node_source, "space")) then + + ! Get pointer to spatial distribution + call get_node_ptr(node_source, "space", node_dist) + + ! Check for type of spatial distribution + if (check_for_node(node_dist, "type")) & + call get_node_value(node_dist, "type", type) call lower_case(type) - select case (type) + select case (trim(type)) case ('box') external_source % type_space = SRC_SPACE_BOX coeffs_reqd = 6 @@ -242,13 +275,13 @@ contains coeffs_reqd = 3 case default message = "Invalid spatial distribution for external source: " & - // trim(source_ % space % type) + // trim(source_ % space % type) call fatal_error() end select ! Determine number of parameters specified - if (associated(source_ % space % parameters)) then - n = size(source_ % space % parameters) + if (check_for_node(node_dist, "parameters")) then + n = get_arraysize_double(node_dist, "parameters") else n = 0 end if @@ -264,19 +297,25 @@ contains call fatal_error() elseif (n > 0) then allocate(external_source % params_space(n)) - external_source % params_space = source_ % space % parameters + call get_node_array(node_dist, "parameters", & + external_source % params_space) end if else - message = "No spatial distribution specified for external source!" + message = "No spatial distribution specified for external source." call fatal_error() end if ! Determine external source angular distribution - if (source_ % angle % type /= '') then - ! Read type of angular distribution - type = source_ % angle % type + if (check_for_node(node_source, "angle")) then + + ! Get pointer to angular distribution + call get_node_ptr(node_source, "angle", node_dist) + + ! Check for type of angular distribution + if (check_for_node(node_dist, "type")) & + call get_node_value(node_dist, "type", type) call lower_case(type) - select case (type) + select case (trim(type)) case ('isotropic') external_source % type_angle = SRC_ANGLE_ISOTROPIC coeffs_reqd = 0 @@ -292,8 +331,8 @@ contains end select ! Determine number of parameters specified - if (associated(source_ % angle % parameters)) then - n = size(source_ % angle % parameters) + if (check_for_node(node_dist, "parameters")) then + n = get_arraysize_double(node_dist, "parameters") else n = 0 end if @@ -309,7 +348,8 @@ contains call fatal_error() elseif (n > 0) then allocate(external_source % params_angle(n)) - external_source % params_angle = source_ % angle % parameters + call get_node_array(node_dist, "parameters", & + external_source % params_angle) end if else ! Set default angular distribution isotropic @@ -317,11 +357,16 @@ contains end if ! Determine external source energy distribution - if (source_ % energy % type /= '') then - ! Read type of energy distribution - type = source_ % energy % type + if (check_for_node(node_source, "energy")) then + + ! Get pointer to energy distribution + call get_node_ptr(node_source, "energy", node_dist) + + ! Check for type of energy distribution + if (check_for_node(node_dist, "type")) & + call get_node_value(node_dist, "type", type) call lower_case(type) - select case (type) + select case (trim(type)) case ('monoenergetic') external_source % type_energy = SRC_ENERGY_MONO coeffs_reqd = 1 @@ -340,8 +385,8 @@ contains end select ! Determine number of parameters specified - if (associated(source_ % energy % parameters)) then - n = size(source_ % energy % parameters) + if (check_for_node(node_dist, "parameters")) then + n = get_arraysize_double(node_dist, "parameters") else n = 0 end if @@ -357,7 +402,8 @@ contains call fatal_error() elseif (n > 0) then allocate(external_source % params_energy(n)) - external_source % params_energy = source_ % energy % parameters + call get_node_array(node_dist, "parameters", & + external_source % params_energy) end if else ! Set default energy distribution to Watt fission spectrum @@ -368,24 +414,33 @@ contains end if ! Survival biasing - call lower_case(survival_) - if (survival_ == 'true' .or. survival_ == '1') survival_biasing = .true. + if (check_for_node(doc, "survival_biasing")) then + call get_node_value(doc, "survival_biasing", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + survival_biasing = .true. + end if ! Probability tables - call lower_case(ptables_) - if (ptables_ == 'false' .or. ptables_ == '0') urr_ptables_on = .false. + if (check_for_node(doc, "ptables")) then + call get_node_value(doc, "ptables", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'false' .or. trim(temp_str) == '0') & + urr_ptables_on = .false. + end if ! Cutoffs - if (size(cutoff_) > 0) then - weight_cutoff = cutoff_(1) % weight - weight_survive = cutoff_(1) % weight_avg + if (check_for_node(doc, "cutoff")) then + call get_node_value(doc, "weight", weight_cutoff) + call get_node_value(doc, "weight_avg", weight_survive) end if ! Particle trace - if (associated(trace_)) then - trace_batch = trace_(1) - trace_gen = trace_(2) - trace_particle = trace_(3) + if (check_for_node(doc, "trace")) then + call get_node_array(doc, "trace", temp_int_array) + trace_batch = temp_int_array(1) + trace_gen = temp_int_array(2) + trace_particle = int(temp_int_array(3), 8) end if ! Shannon Entropy mesh diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 3d6924a8b..44df324ce 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -13,6 +13,9 @@ module xml_interface public :: check_for_node public :: get_node_ptr public :: get_node_value + public :: get_node_array + public :: get_arraysize_integer + public :: get_arraysize_double integer, parameter :: ATTR_NODE = 0 integer, parameter :: ELEM_NODE = 1 @@ -20,9 +23,15 @@ module xml_interface interface get_node_value module procedure get_node_value_integer module procedure get_node_value_long + module procedure get_node_value_double module procedure get_node_value_string end interface get_node_value + interface get_node_array + module procedure get_node_array_integer + module procedure get_node_array_double + end interface get_node_array + contains !=============================================================================== @@ -150,7 +159,7 @@ contains end subroutine get_node_value_integer !=============================================================================== -! GET_NODE_VALUE_INTEGER +! GET_NODE_VALUE_LONG !=============================================================================== subroutine get_node_value_long(ptr, node_name, result_long) @@ -182,6 +191,105 @@ contains end subroutine get_node_value_long +!=============================================================================== +! GET_NODE_VALUE_DOUBLE +!=============================================================================== + + subroutine get_node_value_double(ptr, node_name, result_double) + + character(len=*), intent(in) :: node_name + real(8) :: result_double + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_double) + else + call extractDataContent(temp_ptr, result_double) + end if + + end subroutine get_node_value_double + +!=============================================================================== +! GET_NODE_ARRAY_INTEGER +!=============================================================================== + + subroutine get_node_array_integer(ptr, node_name, result_int) + + character(len=*), intent(in) :: node_name + integer :: result_int(:) + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_int) + else + call extractDataContent(temp_ptr, result_int) + end if + + end subroutine get_node_array_integer + +!=============================================================================== +! GET_NODE_ARRAY_DOUBLE +!=============================================================================== + + subroutine get_node_array_double(ptr, node_name, result_double) + + character(len=*), intent(in) :: node_name + real(8) :: result_double(:) + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_double) + else + call extractDataContent(temp_ptr, result_double) + end if + + end subroutine get_node_array_double + !=============================================================================== ! GET_NODE_VALUE_STRING !=============================================================================== @@ -189,7 +297,7 @@ contains subroutine get_node_value_string(ptr, node_name, result_str) character(len=*), intent(in) :: node_name - character(len=MAX_LINE_LEN) :: result_str + character(len=*) :: result_str type(Node), pointer, intent(in) :: ptr integer :: node_type @@ -215,6 +323,64 @@ contains end subroutine get_node_value_string +!=============================================================================== +! GET_NODE_ARRAYSIZE_INTEGER +!=============================================================================== + + function get_arraysize_integer(ptr, node_name) result(n) + + character(len=*), intent(in) :: node_name + integer :: n + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Get the size + n = countrts(getTextContent(temp_ptr), 0) + + end function get_arraysize_integer + +!=============================================================================== +! GET_NODE_ARRAYSIZE_DOUBLE +!=============================================================================== + + function get_arraysize_double(ptr, node_name) result(n) + + character(len=*), intent(in) :: node_name + integer :: n + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Get the size + n = countrts(getTextContent(temp_ptr), 0.0_8) + + end function get_arraysize_double + !=============================================================================== ! GET_NODE !=============================================================================== From 308968ba40e857a3059261e59f4bd4a90e1cbd99 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 10:06:21 -0400 Subject: [PATCH 12/45] converted over settings input to new XML reader --- src/input_xml.F90 | 217 +++++++++++++++++++++++++++------------------- 1 file changed, 128 insertions(+), 89 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index d2ccf8a7d..530447162 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -47,25 +47,29 @@ contains subroutine read_settings_xml() - use xml_data_settings_t use xml_interface character(MAX_LINE_LEN) :: temp_str - integer :: i ! loop index + integer :: i integer :: n integer :: coeffs_reqd integer :: temp_int - integer :: temp_int_array(3) + integer :: temp_int_array3(3) + integer, allocatable :: temp_int_array(:) integer(8) :: temp_long logical :: file_exists character(MAX_FILE_LEN) :: env_variable character(MAX_WORD_LEN) :: type character(MAX_LINE_LEN) :: filename - type(Node), pointer :: doc - type(Node), pointer :: node_mode - type(Node), pointer :: node_source - type(Node), pointer :: node_dist - type(Node), pointer :: node_cutoff + type(Node), pointer :: doc => null() + type(Node), pointer :: node_mode => null() + type(Node), pointer :: node_source => null() + type(Node), pointer :: node_dist => null() + type(Node), pointer :: node_cutoff => null() + type(Node), pointer :: node_entropy => null() + type(Node), pointer :: node_ufs => null() + type(Node), pointer :: node_sp => null() + type(Node), pointer :: node_output => null() ! Display output message message = "Reading settings XML file..." @@ -79,19 +83,7 @@ contains call fatal_error() end if - ! Initialize XML scalar variables - cross_sections_ = '' - output_path_ = '' - verbosity_ = 0 - energy_grid_ = 'union' - seed_ = 0_8 - source_ % file = '' - source_ % space % type = '' - source_ % angle % type = '' - source_ % energy % type = '' - ! Parse settings.xml file - call read_xml_file_settings_t(filename) call open_xmldoc(doc, filename) ! Find cross_sections.xml file -- the first place to look is the @@ -221,12 +213,11 @@ contains message = "Lethargy mapped energy grid not yet supported." call fatal_error() case default - message = "Unknown energy grid method: " // energy_grid_ + message = "Unknown energy grid method: " // temp_str call fatal_error() end select ! Verbosity - if (verbosity_ > 0) verbosity = verbosity_ if (check_for_node(doc, "verbosity")) & call get_node_value(doc, "verbosity", verbosity) @@ -263,6 +254,7 @@ contains call get_node_ptr(node_source, "space", node_dist) ! Check for type of spatial distribution + type = '' if (check_for_node(node_dist, "type")) & call get_node_value(node_dist, "type", type) call lower_case(type) @@ -275,7 +267,7 @@ contains coeffs_reqd = 3 case default message = "Invalid spatial distribution for external source: " & - // trim(source_ % space % type) + // trim(type) call fatal_error() end select @@ -312,6 +304,7 @@ contains call get_node_ptr(node_source, "angle", node_dist) ! Check for type of angular distribution + type = '' if (check_for_node(node_dist, "type")) & call get_node_value(node_dist, "type", type) call lower_case(type) @@ -326,7 +319,7 @@ contains external_source % type_angle = SRC_ANGLE_TABULAR case default message = "Invalid angular distribution for external source: " & - // trim(source_ % angle % type) + // trim(type) call fatal_error() end select @@ -363,6 +356,7 @@ contains call get_node_ptr(node_source, "energy", node_dist) ! Check for type of energy distribution + type = '' if (check_for_node(node_dist, "type")) & call get_node_value(node_dist, "type", type) call lower_case(type) @@ -380,7 +374,7 @@ contains external_source % type_energy = SRC_ENERGY_TABULAR case default message = "Invalid energy distribution for external source: " & - // trim(source_ % energy % type) + // trim(type) call fatal_error() end select @@ -437,19 +431,23 @@ contains ! Particle trace if (check_for_node(doc, "trace")) then - call get_node_array(doc, "trace", temp_int_array) - trace_batch = temp_int_array(1) - trace_gen = temp_int_array(2) - trace_particle = int(temp_int_array(3), 8) + call get_node_array(doc, "trace", temp_int_array3) + trace_batch = temp_int_array3(1) + trace_gen = temp_int_array3(2) + trace_particle = int(temp_int_array3(3), 8) end if ! Shannon Entropy mesh - if (size(entropy_) > 0) then + if (check_for_node(doc, "entropy")) then + + ! Get pointer to entropy node + call get_node_ptr(doc, "entropy", node_entropy) + ! Check to make sure enough values were supplied - if (size(entropy_(1) % lower_left) /= 3) then + if (get_arraysize_double(node_entropy, "lower_left") /= 3) then message = "Need to specify (x,y,z) coordinates of lower-left corner & &of Shannon entropy mesh." - elseif (size(entropy_(1) % upper_right) /= 3) then + elseif (get_arraysize_double(node_entropy, "upper_right") /= 3) then message = "Need to specify (x,y,z) coordinates of upper-right corner & &of Shannon entropy mesh." end if @@ -460,8 +458,10 @@ contains allocate(entropy_mesh % upper_right(3)) ! Copy values - entropy_mesh % lower_left = entropy_(1) % lower_left - entropy_mesh % upper_right = entropy_(1) % upper_right + call get_node_array(node_entropy, "lower_left", & + entropy_mesh % lower_left) + call get_node_array(node_entropy, "upper_right", & + entropy_mesh % upper_right) ! Check on values provided if (.not. all(entropy_mesh % upper_right > entropy_mesh % lower_left)) then @@ -472,10 +472,10 @@ contains ! Check if dimensions were specified -- if not, they will be calculated ! automatically upon first entry into shannon_entropy + if (check_for_node(node_entropy, "dimension")) then - if (associated(entropy_(1) % dimension)) then ! If so, make sure proper number of values were given - if (size(entropy_(1) % dimension) /= 3) then + if (get_arraysize_integer(node_entropy, "dimension") /= 3) then message = "Dimension of entropy mesh must be given as three & &integers." call fatal_error() @@ -486,7 +486,7 @@ contains allocate(entropy_mesh % dimension(3)) ! Copy dimensions - entropy_mesh % dimension = entropy_(1) % dimension + call get_node_array(node_entropy, "dimension", entropy_mesh % dimension) end if ! Turn on Shannon entropy calculation @@ -494,15 +494,19 @@ contains end if ! Uniform fission source weighting mesh - if (size(uniform_fs_) > 0) then + if (check_for_node(doc, "uniform_fs")) then + + ! Get pointer to ufs node + call get_node_ptr(doc, "uniform_fs", node_ufs) + ! Check to make sure enough values were supplied - if (size(uniform_fs_(1) % lower_left) /= 3) then + if (get_arraysize_double(node_ufs, "lower_left") /= 3) then message = "Need to specify (x,y,z) coordinates of lower-left corner & &of UFS mesh." - elseif (size(uniform_fs_(1) % upper_right) /= 3) then + elseif (get_arraysize_double(node_ufs, "upper_right") /= 3) then message = "Need to specify (x,y,z) coordinates of upper-right corner & &of UFS mesh." - elseif (size(uniform_fs_(1) % dimension) /= 3) then + elseif (get_arraysize_integer(node_ufs, "dimension") /= 3) then message = "Dimension of UFS mesh must be given as three integers." call fatal_error() end if @@ -518,11 +522,11 @@ contains allocate(ufs_mesh % dimension(3)) ! Copy dimensions - ufs_mesh % dimension = uniform_fs_(1) % dimension + call get_node_array(node_ufs, "dimension", ufs_mesh % dimension) ! Copy values - ufs_mesh % lower_left = uniform_fs_(1) % lower_left - ufs_mesh % upper_right = uniform_fs_(1) % upper_right + call get_node_array(node_ufs, "lower_left", ufs_mesh % lower_left) + call get_node_array(node_ufs, "upper_right", ufs_mesh % upper_right) ! Check on values provided if (.not. all(ufs_mesh % upper_right > ufs_mesh % lower_left)) then @@ -547,25 +551,32 @@ contains end if ! Check if the user has specified to write state points - if (size(state_point_) > 0) then + if (check_for_node(doc, "state_point")) then + + ! Get pointer to state_point node + call get_node_ptr(doc, "state_point", node_sp) + ! Determine number of batches at which to store state points - if (associated(state_point_(1) % batches)) then - n_state_points = size(state_point_(1) % batches) + if (check_for_node(node_sp, "batches")) then + n_state_points = get_arraysize_integer(node_sp, "batches") else n_state_points = 0 end if if (n_state_points > 0) then ! User gave specific batches to write state points + allocate(temp_int_array(n_state_points)) + call get_node_array(node_sp, "batches", temp_int_array) do i = 1, n_state_points - call statepoint_batch % add(state_point_(1) % batches(i)) + call statepoint_batch % add(temp_int_array(i)) end do - - elseif (state_point_(1) % interval /= 0) then + deallocate(temp_int_array) + elseif (check_for_node(node_sp, "interval")) then ! User gave an interval for writing state points - n_state_points = n_batches / state_point_(1) % interval + call get_node_value(node_sp, "interval", temp_int) + n_state_points = n_batches / temp_int do i = 1, n_state_points - call statepoint_batch % add(state_point_(1) % interval * i) + call statepoint_batch % add(temp_int * i) end do else ! If neither were specified, write state point at last batch @@ -574,12 +585,18 @@ contains end if ! Check if the user has specified to write binary source file - call lower_case(state_point_(1) % source_separate) - call lower_case(state_point_(1) % source_write) - if (state_point_(1) % source_separate == 'true' .or. & - state_point_(1) % source_separate == '1') source_separate = .true. - if (state_point_(1) % source_write == 'false' .or. & - state_point_(1) % source_write == '0') source_write = .false. + if (check_for_node(node_sp, "source_separate")) then + call get_node_value(node_sp, "source_separate", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. & + trim(temp_str) == '1') source_separate = .true. + end if + if (check_for_node(node_sp, "source_write")) then + call get_node_value(node_sp, "source_write", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. & + trim(temp_str) == '1') source_separate = .true. + end if else ! If no tag was present, by default write state point at ! last batch only @@ -589,44 +606,66 @@ contains ! Check if the user has specified to not reduce tallies at the end of every ! batch - call lower_case(no_reduce_) - if (no_reduce_ == 'true' .or. no_reduce_ == '1') reduce_tallies = .false. + if (check_for_node(doc, "no_reduce")) then + call get_node_value(doc, "no_reduce", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + reduce_tallies = .false. + end if ! Check if the user has specified to use confidence intervals for ! uncertainties rather than standard deviations - call lower_case(confidence_intervals_) - if (confidence_intervals_ == 'true' .or. & - confidence_intervals_ == '1') confidence_intervals = .true. - - ! Check for output options - if (associated(output_)) then - do i = 1, size(output_) - call lower_case(output_(i)) - select case (output_(i)) - case ('summary') - output_summary = .true. - case ('cross_sections') - output_xs = .true. - case ('tallies') - output_tallies = .true. - case ('none') - output_summary = .false. - output_xs = .false. - output_tallies = .false. - end select - end do + if (check_for_node(doc, "confidence_intervals")) then + call get_node_value(doc, "confidence_intervals", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. & + trim(temp_str) == '1') confidence_intervals = .true. end if - ! check for cmfd run - call lower_case(run_cmfd_) - if (run_cmfd_ == 'true' .or. run_cmfd_ == '1') then - cmfd_run = .true. -#ifndef PETSC - if (master) then - message = 'CMFD is not available, compile OpenMC with PETSc' - call fatal_error() + ! Check for output options + if (check_for_node(doc, "output")) then + + ! Get pointer to output node + call get_node_ptr(doc, "output", node_output) + + ! Check for summary option + if (check_for_node(node_output, "summary")) then + call get_node_value(node_output, "summary", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. & + trim(temp_str) == '1') output_summary = .true. end if + + ! Check for cross sections option + if (check_for_node(node_output, "cross_sections")) then + call get_node_value(node_output, "cross_sections", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. & + trim(temp_str) == '1') output_xs = .true. + end if + + ! Check for ASCII tallies output option + if (check_for_node(node_output, "tallies")) then + call get_node_value(node_output, "tallies", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'false' .or. & + trim(temp_str) == '0') output_tallies = .false. + end if + end if + + ! Check for cmfd run + if (check_for_node(doc, "run_cmfd")) then + call get_node_value(doc, "run_cmfd", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') then + cmfd_run = .true. +#ifndef PETSC + if (master) then + message = 'CMFD is not available, compile OpenMC with PETSc' + call fatal_error() + end if #endif + end if end if ! Close settings XML file From e4d26e84e6c47c25b93b4ae1b3977aba2d4b0c55 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 11:31:10 -0400 Subject: [PATCH 13/45] added capability in xml interface to get number of nodes and select a certain node index --- src/xml_interface.F90 | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 44df324ce..cd948b64e 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -11,6 +11,7 @@ module xml_interface public :: open_xmldoc public :: close_xmldoc public :: check_for_node + public :: get_number_nodes public :: get_node_ptr public :: get_node_value public :: get_node_array @@ -93,17 +94,39 @@ contains end function check_for_node +!=============================================================================== +! GET_NUMBER_NODES +!=============================================================================== + + function get_number_nodes(ptr, node_name) result(n_nodes) + + character(len=*), intent(in) :: node_name + integer :: n_nodes + type(Node), pointer, intent(in) :: ptr + + type(NodeList), pointer :: elem_list + + ! Check for a sub-element + elem_list => getElementsByTagName(ptr, trim(node_name)) + + ! Get the length of the list + n_nodes = getLength(elem_list) + + end function get_number_nodes + !=============================================================================== ! GET_NODE_PTR !=============================================================================== - subroutine get_node_ptr(in_ptr, node_name, out_ptr, found) + subroutine get_node_ptr(in_ptr, node_name, out_ptr, idx, found) character(len=*), intent(in) :: node_name + integer, intent(in), optional :: idx logical, intent(out), optional :: found type(Node), pointer, intent(in) :: in_ptr type(Node), pointer, intent(out) :: out_ptr + integer :: idx_ logical :: found_ type(NodeList), pointer :: elem_list => null() @@ -117,7 +140,9 @@ contains if (getLength(elem_list) == 0) return ! Point to the new element - out_ptr => item(elem_list, 0) + idx_ = 0 + if (present(idx)) idx_ = idx - 1 + out_ptr => item(elem_list, idx_) found_ = .true. ! Check to output found @@ -148,7 +173,7 @@ contains getNodeName(ptr) // "." call fatal_error() end if - + ! Extract value if (node_type == ATTR_NODE) then call extractDataAttribute(ptr, node_name, result_int) From 8d7d24d63ee45b58260830b9bc22a7bb35bfd211 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 11:31:41 -0400 Subject: [PATCH 14/45] converted over geometry input XML reading --- src/input_xml.F90 | 150 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 44 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 530447162..760383d57 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -15,6 +15,7 @@ module input_xml starts_with, ends_with use tally_header, only: TallyObject, TallyFilter use tally_initialize, only: add_tallies + use xml_interface implicit none save @@ -47,8 +48,6 @@ contains subroutine read_settings_xml() - use xml_interface - character(MAX_LINE_LEN) :: temp_str integer :: i integer :: n @@ -689,6 +688,8 @@ contains integer :: n_cells_in_univ integer :: coeffs_reqd integer :: mid + integer :: temp_int_array3(3) + integer, allocatable :: temp_int_array(:) real(8) :: phi, theta, psi logical :: file_exists logical :: boundary_exists @@ -697,6 +698,10 @@ contains type(Cell), pointer :: c => null() type(Surface), pointer :: s => null() type(Lattice), pointer :: lat => null() + type(Node), pointer :: doc => null() + type(Node), pointer :: node_cell => null() + type(Node), pointer :: node_surf => null() + type(Node), pointer :: node_lat => null() ! Display output message message = "Reading geometry XML file..." @@ -715,9 +720,11 @@ contains ! Parse geometry.xml file call read_xml_file_geometry_t(filename) + call open_xmldoc(doc, filename) ! Get number of tags - n_cells = size(cell_) + n_cells = 0 + n_cells = get_number_nodes(doc, "cell") ! Check for no cells if (n_cells == 0) then @@ -737,10 +744,26 @@ contains do i = 1, n_cells c => cells(i) + ! Get pointer to i-th cell node + call get_node_ptr(doc, "cell", node_cell, i) + ! Copy data into cells - c % id = cell_(i) % id - c % universe = cell_(i) % universe - c % fill = cell_(i) % fill + if (check_for_node(node_cell, "id")) then + call get_node_value(node_cell, "id", c % id) + else + message = "Must specify id of cell in geometry XML file." + call fatal_error() + end if + if (check_for_node(node_cell, "universe")) then + call get_node_value(node_cell, "universe", c % universe) + else + c % universe = NONE + end if + if (check_for_node(node_cell, "fill")) then + call get_node_value(node_cell, "fill", c % fill) + else + c % fill = NONE + end if ! Check to make sure 'id' hasn't been used if (cell_dict % has_key(c % id)) then @@ -749,7 +772,9 @@ contains end if ! Read material - word = cell_(i) % material + word = '' + if (check_for_node(node_cell, "material")) & + call get_node_value(node_cell, "material", word) call lower_case(word) select case(word) case ('void') @@ -757,7 +782,7 @@ contains case ('') ! This case is called if no material was specified - c % material = 0 + c % material = NONE case default c % material = int(str_to_int(word), 4) @@ -784,20 +809,20 @@ contains end if ! Check to make sure that surfaces were specified - if (.not. associated(cell_(i) % surfaces)) then + if (.not. check_for_node(node_cell, "surfaces")) then message = "No surfaces specified for cell " // & trim(to_str(c % id)) call fatal_error() end if ! Allocate array for surfaces and copy - n = size(cell_(i) % surfaces) + n = get_arraysize_integer(node_cell, "surfaces") c % n_surfaces = n allocate(c % surfaces(n)) - c % surfaces = cell_(i) % surfaces + call get_node_array(node_cell, "surfaces", c % surfaces) ! Rotation matrix - if (associated(cell_(i) % rotation)) then + if (check_for_node(node_cell, "rotation")) then ! Rotations can only be applied to cells that are being filled with ! another universe if (c % fill == NONE) then @@ -807,7 +832,7 @@ contains end if ! Read number of rotation parameters - n = size(cell_(i) % rotation) + n = get_arraysize_double(node_cell, "rotation") if (n /= 3) then message = "Incorrect number of rotation parameters on cell " // & to_str(c % id) @@ -815,9 +840,10 @@ contains end if ! Copy rotation angles in x,y,z directions - phi = -cell_(i) % rotation(1) * PI/180.0 - theta = -cell_(i) % rotation(2) * PI/180.0 - psi = -cell_(i) % rotation(3) * PI/180.0 + call get_node_array(node_cell, "rotation", temp_int_array3) + phi = -temp_int_array3(1) * PI/180.0_8 + theta = -temp_int_array3(2) * PI/180.0_8 + psi = -temp_int_array3(3) * PI/180.0_8 ! Calculate rotation matrix based on angles given allocate(c % rotation(3,3)) @@ -832,7 +858,7 @@ contains end if ! Translation vector - if (associated(cell_(i) % translation)) then + if (check_for_node(node_cell, "translation")) then ! Translations can only be applied to cells that are being filled with ! another universe if (c % fill == NONE) then @@ -842,7 +868,7 @@ contains end if ! Read number of translation parameters - n = size(cell_(i) % translation) + n = get_arraysize_double(node_cell, "translation") if (n /= 3) then message = "Incorrect number of translation parameters on cell " & // to_str(c % id) @@ -851,7 +877,7 @@ contains ! Copy translation vector allocate(c % translation(3)) - c % translation = cell_(i) % translation + call get_node_array(node_cell, "translation", c % translation) end if ! Add cell to dictionary @@ -860,7 +886,7 @@ contains ! For cells, we also need to check if there's a new universe -- ! also for every cell add 1 to the count of cells for the ! specified universe - universe_num = cell_(i) % universe + universe_num = c % universe if (.not. cells_in_univ_dict % has_key(universe_num)) then n_universes = n_universes + 1 n_cells_in_univ = 1 @@ -880,7 +906,8 @@ contains boundary_exists = .false. ! Get number of tags - n_surfaces = size(surface_) + n_surfaces = 0 + n_surfaces = get_number_nodes(doc, "surface") ! Check for no surfaces if (n_surfaces == 0) then @@ -894,8 +921,16 @@ contains do i = 1, n_surfaces s => surfaces(i) + ! Get pointer to i-th surface node + call get_node_ptr(doc, "surface", node_surf, i) + ! Copy data into cells - s % id = surface_(i) % id + if (check_for_node(node_surf, "id")) then + call get_node_value(node_surf, "id", s % id) + else + message = "Must specify id of surface in geometry XML file." + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (surface_dict % has_key(s % id)) then @@ -905,7 +940,9 @@ contains end if ! Copy and interpret surface type - word = surface_(i) % type + word = '' + if (check_for_node(node_surf, "type")) & + call get_node_value(node_surf, "type", word) call lower_case(word) select case(trim(word)) case ('x-plane') @@ -950,7 +987,7 @@ contains ! have been specified for the given type of surface. Then copy ! surface coordinates. - n = size(surface_(i) % coeffs) + n = get_arraysize_double(node_surf, "coeffs") if (n < coeffs_reqd) then message = "Not enough coefficients specified for surface: " // & trim(to_str(s % id)) @@ -961,11 +998,13 @@ contains call fatal_error() else allocate(s % coeffs(n)) - s % coeffs = surface_(i) % coeffs + call get_node_array(node_surf, "coeffs", s % coeffs) end if ! Boundary conditions - word = surface_(i) % boundary + word = '' + if (check_for_node(node_surf, "boundary")) & + call get_node_value(node_surf, "boundary", word) call lower_case(word) select case (trim(word)) case ('transmission', 'transmit', '') @@ -998,14 +1037,22 @@ contains ! READ LATTICES FROM GEOMETRY.XML ! Allocate lattices array - n_lattices = size(lattice_) + n_lattices = get_number_nodes(doc, "lattice") allocate(lattices(n_lattices)) do i = 1, n_lattices lat => lattices(i) + ! Get pointer to i-th lattice + call get_node_ptr(doc, "lattice", node_lat, i) + ! ID of lattice - lat % id = lattice_(i) % id + if (check_for_node(node_lat, "id")) then + call get_node_value(node_lat, "id", lat % id) + else + message = "Must specify id of lattice in geometry XML file." + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (lattice_dict % has_key(lat % id)) then @@ -1015,7 +1062,9 @@ contains end if ! Read lattice type - word = lattice_(i) % type + word = '' + if (check_for_node(node_lat, "type")) & + call get_node_value(node_lat, "type", word) call lower_case(word) select case (trim(word)) case ('rect', 'rectangle', 'rectangular') @@ -1023,12 +1072,12 @@ contains case ('hex', 'hexagon', 'hexagonal') lat % type = LATTICE_HEX case default - message = "Invalid lattice type: " // trim(lattice_(i) % type) + message = "Invalid lattice type: " // trim(word) call fatal_error() end select ! Read number of lattice cells in each dimension - n = size(lattice_(i) % dimension) + n = get_arraysize_integer(node_lat, "dimension") if (n /= 2 .and. n /= 3) then message = "Lattice must be two or three dimensions." call fatal_error() @@ -1036,27 +1085,29 @@ contains lat % n_dimension = n allocate(lat % dimension(n)) - lat % dimension = lattice_(i) % dimension + call get_node_array(node_lat, "dimension", lat % dimension) ! Read lattice lower-left location - if (size(lattice_(i) % dimension) /= size(lattice_(i) % lower_left)) then + if (size(lat % dimension) /= & + get_arraysize_double(node_lat, "lower_left")) then message = "Number of entries on must be the same as & &the number of entries on ." call fatal_error() end if allocate(lat % lower_left(n)) - lat % lower_left = lattice_(i) % lower_left + call get_node_array(node_lat, "lower_left", lat % lower_left) ! Read lattice widths - if (size(lattice_(i) % width) /= size(lattice_(i) % lower_left)) then + if (size(lat % dimension) /= & + get_arraysize_double(node_lat, "width")) then message = "Number of entries on must be the same as & &the number of entries on ." call fatal_error() end if allocate(lat % width(n)) - lat % width = lattice_(i) % width + call get_node_array(node_lat, "width", lat % width) ! Copy number of dimensions n_x = lat % dimension(1) @@ -1069,28 +1120,36 @@ contains allocate(lat % universes(n_x, n_y, n_z)) ! Check that number of universes matches size - if (size(lattice_(i) % universes) /= n_x*n_y*n_z) then + n = get_arraysize_integer(node_lat, "universes") + if (n /= n_x*n_y*n_z) then message = "Number of universes on does not match size of & &lattice " // trim(to_str(lat % id)) // "." call fatal_error() end if + allocate(temp_int_array(n)) + call get_node_array(node_lat, "universes", temp_int_array) + ! Read universes do m = 1, n_z do k = 0, n_y - 1 do j = 1, n_x - lat % universes(j, n_y - k, m) = lattice_(i) % & - universes(j + n_x*k + n_x*n_y*(m-1)) + lat % universes(j, n_y - k, m) = & + temp_int_array(j + n_x*k + n_x*n_y*(m-1)) end do end do end do + deallocate(temp_int_array) ! Read material for area outside lattice - mid = lattice_(i) % outside - if (mid == 0 .or. mid == MATERIAL_VOID) then - lat % outside = MATERIAL_VOID - else - lat % outside = mid + lat % outside = MATERIAL_VOID + if (check_for_node(node_lat, "outside")) then + call get_node_value(node_lat, "outside", mid) + if (mid == 0 .or. mid == MATERIAL_VOID) then + lat % outside = MATERIAL_VOID + else + lat % outside = mid + end if end if ! Add lattice to dictionary @@ -1098,6 +1157,9 @@ contains end do + ! Close geometry XML file + call close_xmldoc(doc) + end subroutine read_geometry_xml !=============================================================================== From 35532ab144ab25496687e080625e171a4b7e7b2e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 11:38:05 -0400 Subject: [PATCH 15/45] removed old XML reader from geometry portion on file --- src/input_xml.F90 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 760383d57..338b78460 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -679,8 +679,6 @@ contains subroutine read_geometry_xml() - use xml_data_geometry_t - integer :: i, j, k, m integer :: n integer :: n_x, n_y, n_z @@ -719,7 +717,6 @@ contains end if ! Parse geometry.xml file - call read_xml_file_geometry_t(filename) call open_xmldoc(doc, filename) ! Get number of tags @@ -979,7 +976,7 @@ contains s % type = SURF_CONE_Z coeffs_reqd = 4 case default - message = "Invalid surface type: " // trim(surface_(i) % type) + message = "Invalid surface type: " // trim(word) call fatal_error() end select From 404cd634949f2f313f38640d8e46df9cb67c8f5f Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 14:09:36 -0400 Subject: [PATCH 16/45] converted materials XML input section over to new xml interface --- src/input_xml.F90 | 122 +++++++++++++++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 39 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 338b78460..f486a0679 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1166,8 +1166,6 @@ contains subroutine read_materials_xml - use xml_data_materials_t - integer :: i ! loop index for materials integer :: j ! loop index for nuclides integer :: n ! number of nuclides @@ -1176,17 +1174,23 @@ contains integer :: index_nuclide ! index in nuclides integer :: index_sab ! index in sab_tables real(8) :: val ! value entered for density + real(8) :: temp_dble ! temporary double prec. real logical :: file_exists ! does materials.xml exist? logical :: sum_density ! density is taken to be sum of nuclide densities character(12) :: name ! name of isotope, e.g. 92235.03c character(12) :: alias ! alias of nuclide, e.g. U-235.03c character(MAX_WORD_LEN) :: units ! units on density character(MAX_LINE_LEN) :: filename ! absolute path to materials.xml + character(MAX_LINE_LEN) :: temp_str ! temporary string when reading type(ListChar) :: list_names ! temporary list of nuclide names type(ListReal) :: list_density ! temporary list of nuclide densities type(Material), pointer :: mat => null() - type(nuclide_xml), pointer :: nuc => null() - type(sab_xml), pointer :: sab => null() + type(Node), pointer :: doc => null() + type(Node), pointer :: node_mat => null() + type(Node), pointer :: node_dens => null() + type(Node), pointer :: node_nuc => null() + type(Node), pointer :: node_ele => null() + type(Node), pointer :: node_sab => null() ! Display output message message = "Reading materials XML file..." @@ -1201,16 +1205,17 @@ contains end if ! Initialize default cross section variable - default_xs_ = "" + default_xs = "" ! Parse materials.xml file - call read_xml_file_materials_t(filename) + call open_xmldoc(doc, filename) ! Copy default cross section if present - default_xs = default_xs_ + if (check_for_node(doc, "default_xs")) & + call get_node_value(doc, "default_xs", default_xs) ! Allocate cells array - n_materials = size(material_) + n_materials = get_number_nodes(doc, "material") allocate(materials(n_materials)) ! Initialize count for number of nuclides/S(a,b) tables @@ -1220,8 +1225,16 @@ contains do i = 1, n_materials mat => materials(i) + ! Get pointer to i-th material node + call get_node_ptr(doc, "material", node_mat, i) + ! Copy material id - mat % id = material_(i) % id + if (check_for_node(node_mat, "id")) then + call get_node_value(node_mat, "id", mat % id) + else + message = "Must specify id of material in materials XML file" + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (material_dict % has_key(mat % id)) then @@ -1239,9 +1252,18 @@ contains ! ======================================================================= ! READ AND PARSE TAG + ! Get pointer to density element + if (check_for_node(node_mat, "density")) then + call get_node_ptr(node_mat, "density", node_dens) + else + message = "Must specify density element in material " // & + trim(to_str(mat % id)) + call fatal_error() + end if + ! Copy value and units - val = material_(i) % density % value - units = material_(i) % density % units + call get_node_value(node_dens, "value", val) + call get_node_value(node_dens, "units", units) if (units == 'sum') then ! If the user gave the units as 'sum', then the total density of the @@ -1271,7 +1293,7 @@ contains case ('atom/cm3', 'atom/cc') mat % density = 1.0e-24 * val case default - message = "Unkwown units '" // trim(material_(i) % density % units) & + message = "Unkwown units '" // trim(units) & // "' specified on material " // trim(to_str(mat % id)) call fatal_error() end select @@ -1281,101 +1303,112 @@ contains ! READ AND PARSE TAGS ! Check to ensure material has at least one nuclide - if (.not. associated(material_(i) % nuclides) .and. & - .not. associated(material_(i) % elements)) then + if (.not. check_for_node(node_mat, "nuclide") .and. & + .not. check_for_node(node_mat, "element")) then message = "No nuclides or natural elements specified on material " // & trim(to_str(mat % id)) call fatal_error() end if ! Create list of nuclides based on those specified plus natural elements - INDIVIDUAL_NUCLIDES: do j = 1, size(material_(i) % nuclides) + INDIVIDUAL_NUCLIDES: do j = 1, get_number_nodes(node_mat, "nuclide") ! Combine nuclide identifier and cross section and copy into names - nuc => material_(i) % nuclides(j) + call get_node_ptr(node_mat, "nuclide", node_nuc, j) ! Check for empty name on nuclide - if (len_trim(nuc % name) == 0) then + if (.not.check_for_node(node_nuc, "name")) then message = "No name specified on nuclide in material " // & trim(to_str(mat % id)) call fatal_error() end if ! Check for cross section - if (len_trim(nuc % xs) == 0) then + if (.not.check_for_node(node_nuc, "xs")) then if (default_xs == '') then message = "No cross section specified for nuclide in material " & // trim(to_str(mat % id)) call fatal_error() else - nuc % xs = default_xs + name = trim(default_xs) end if end if ! store full name - name = trim(nuc % name) // "." // trim(nuc % xs) + call get_node_value(node_nuc, "name", temp_str) + if (check_for_node(node_nuc, "xs")) & + call get_node_value(node_nuc, "xs", name) + name = trim(temp_str) // "." // trim(name) ! save name and density to list call list_names % append(name) ! Check if no atom/weight percents were specified or if both atom and ! weight percents were specified - if (nuc % ao == ZERO .and. nuc % wo == ZERO) then + if (.not.check_for_node(node_nuc, "ao") .and. & + .not.check_for_node(node_nuc, "wo")) then message = "No atom or weight percent specified for nuclide " // & trim(name) call fatal_error() - elseif (nuc % ao /= ZERO .and. nuc % wo /= ZERO) then + elseif (check_for_node(node_nuc, "ao") .and. & + check_for_node(node_nuc, "wo")) then message = "Cannot specify both atom and weight percents for a & &nuclide: " // trim(name) call fatal_error() end if ! Copy atom/weight percents - if (nuc % ao /= ZERO) then - call list_density % append(nuc % ao) + if (check_for_node(node_nuc, "ao")) then + call get_node_value(node_nuc, "ao", temp_dble) + call list_density % append(temp_dble) else - call list_density % append(-nuc % wo) + call get_node_value(node_nuc, "wo", temp_dble) + call list_density % append(-temp_dble) end if end do INDIVIDUAL_NUCLIDES ! ======================================================================= ! READ AND PARSE TAGS - NATURAL_ELEMENTS: do j = 1, size(material_(i) % elements) - nuc => material_(i) % elements(j) + NATURAL_ELEMENTS: do j = 1, get_number_nodes(node_mat, "element") + call get_node_ptr(node_mat, "element", node_ele, j) ! Check for empty name on natural element - if (len_trim(nuc % name) == 0) then + if (.not.check_for_node(node_ele, "name")) then message = "No name specified on nuclide in material " // & trim(to_str(mat % id)) call fatal_error() end if + call get_node_value(node_ele, "name", name) ! Check for cross section - if (len_trim(nuc % xs) == 0) then + if (.not.check_for_node(node_nuc, "xs")) then if (default_xs == '') then message = "No cross section specified for nuclide in material " & // trim(to_str(mat % id)) call fatal_error() else - nuc % xs = default_xs + temp_str = trim(default_xs) end if end if ! Check if no atom/weight percents were specified or if both atom and ! weight percents were specified - if (nuc % ao == ZERO .and. nuc % wo == ZERO) then - message = "No atom or weight percent specified for nuclide " // & + if (.not.check_for_node(node_nuc, "ao") .and. & + .not.check_for_node(node_nuc, "wo")) then + message = "No atom or weight percent specified for element " // & trim(name) call fatal_error() - elseif (nuc % ao /= ZERO .and. nuc % wo /= ZERO) then + elseif (check_for_node(node_nuc, "ao") .and. & + check_for_node(node_nuc, "wo")) then message = "Cannot specify both atom and weight percents for a & - &nuclide: " // trim(name) + &element: " // trim(name) call fatal_error() end if ! Expand element into naturally-occurring isotopes - if (nuc % ao /= ZERO) then - call expand_natural_element(nuc % name, nuc % xs, nuc % ao, & + if (check_for_node(node_nuc, "ao")) then + call get_node_value(node_nuc, "ao", temp_dble) + call expand_natural_element(name, temp_str, temp_dble, & list_names, list_density) else message = "The ability to expand a natural element based on weight & @@ -1452,7 +1485,7 @@ contains ! ======================================================================= ! READ AND PARSE TAG FOR S(a,b) DATA - n_sab = size(material_(i) % sab) + n_sab = get_number_nodes(node_mat, "sab") if (n_sab > 0) then ! Set number of S(a,b) tables mat % n_sab = n_sab @@ -1467,10 +1500,18 @@ contains do j = 1, n_sab ! Get pointer to S(a,b) table - sab => material_(i) % sab(j) + call get_node_ptr(node_mat, "sab", node_sab, j) ! Determine name of S(a,b) table - name = trim(sab % name) // "." // trim(sab % xs) + if (.not.check_for_node(node_sab, "name") .or. & + .not.check_for_node(node_sab, "xs")) then + message = "Need to specify and for S(a,b) table." + call fatal_error() + end if + call get_node_value(node_sab, "name", name) + call get_node_value(node_sab, "xs", temp_str) + + name = trim(name) // "." // trim(temp_str) mat % sab_names(j) = name ! Check that this nuclide is listed in the cross_sections.xml file @@ -1508,6 +1549,9 @@ contains n_nuclides_total = index_nuclide n_sab_tables = index_sab + ! Close materials XML file + call close_xmldoc(doc) + end subroutine read_materials_xml !=============================================================================== From 63e12ad2917427e5e40ab6a31ce474597cbfed09 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 14:10:44 -0400 Subject: [PATCH 17/45] added () from empty arg list to read_materials --- src/input_xml.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index f486a0679..c673f3fa7 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1164,7 +1164,7 @@ contains ! for errors and placing properly-formatted data in the right data structures !=============================================================================== - subroutine read_materials_xml + subroutine read_materials_xml() integer :: i ! loop index for materials integer :: j ! loop index for nuclides From b7747d727cb6e4770ac271e5645fa598ed518cfb Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 15:10:56 -0400 Subject: [PATCH 18/45] converted tallies over up through reading in mesh --- src/input_xml.F90 | 87 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index c673f3fa7..39765c5fb 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1577,14 +1577,21 @@ contains integer :: n_order ! Scattering order requested integer :: n_order_pos ! Position of Scattering order in score name string integer :: MT ! user-specified MT for score + integer :: iarray3(3) ! temporary integer array logical :: file_exists ! does tallies.xml file exist? + real(8) :: rarray3(3) ! temporary double prec. array character(MAX_LINE_LEN) :: filename character(MAX_WORD_LEN) :: word character(MAX_WORD_LEN) :: score_name + character(MAX_WORD_LEN) :: temp_str type(ElemKeyValueCI), pointer :: pair_list => null() type(TallyObject), pointer :: t => null() type(StructuredMesh), pointer :: m => null() type(TallyFilter), allocatable :: filters(:) ! temporary filters + type(Node), pointer :: doc => null() + type(Node), pointer :: node_mesh => null() + type(Node), pointer :: node_tal => null() + type(Node), pointer :: node_filt => null() ! Check if tallies.xml exists filename = trim(path_input) // "tallies.xml" @@ -1600,15 +1607,16 @@ contains ! Parse tallies.xml file call read_xml_file_tallies_t(filename) + call open_xmldoc(doc, filename) ! ========================================================================== ! DETERMINE SIZE OF ARRAYS AND ALLOCATE ! Check for user meshes - if (.not. associated(mesh_)) then + if (.not.check_for_node(doc, "mesh")) then n_user_meshes = 0 else - n_user_meshes = size(mesh_) + n_user_meshes = get_number_nodes(doc, "mesh") if (cmfd_run) then n_meshes = n_user_meshes + n_cmfd_meshes else @@ -1620,12 +1628,12 @@ contains if (n_meshes > 0) allocate(meshes(n_meshes)) ! Check for user tallies - if (.not. associated(tally_)) then + if (.not.check_for_node(doc, "tally")) then n_user_tallies = 0 message = "No tallies present in tallies.xml file!" call warning() else - n_user_tallies = size(tally_) + n_user_tallies = get_number_nodes(doc, "tally") end if ! Allocate tally array @@ -1634,8 +1642,12 @@ contains end if ! Check for setting - call lower_case(separate_) - if (separate_ == 'true' .or. separate_ == '1') assume_separate = .true. + if (check_for_node(doc, "assume_separate")) then + call get_node_value(doc, "assume_separate", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + assume_separate = .true. + end if ! ========================================================================== ! READ MESH DATA @@ -1643,8 +1655,16 @@ contains do i = 1, n_user_meshes m => meshes(i) - ! copy mesh id - m % id = mesh_(i) % id + ! Get pointer to mesh node + call get_node_ptr(doc, "mesh", node_mesh, i) + + ! Copy mesh id + if (check_for_node(node_mesh, "id")) then + call get_node_value(node_mesh, "id", m % id) + else + message = "Must specify id for mesh in tally XML file." + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (mesh_dict % has_key(m % id)) then @@ -1654,8 +1674,11 @@ contains end if ! Read mesh type - call lower_case(mesh_(i) % type) - select case (mesh_(i) % type) + temp_str = '' + if (check_for_node(node_mesh, "type")) & + call get_node_value(node_mesh, "type", temp_str) + call lower_case(temp_str) + select case (trim(temp_str)) case ('rect', 'rectangle', 'rectangular') m % type = LATTICE_RECT case ('hex', 'hexagon', 'hexagonal') @@ -1666,7 +1689,7 @@ contains end select ! Determine number of dimensions for mesh - n = size(mesh_(i) % dimension) + n = get_arraysize_integer(node_mesh, "dimension") if (n /= 2 .and. n /= 3) then message = "Mesh must be two or three dimensions." call fatal_error() @@ -1680,74 +1703,79 @@ contains allocate(m % upper_right(n)) ! Check that dimensions are all greater than zero - if (any(mesh_(i) % dimension <= 0)) then + call get_node_array(node_mesh, "dimension", iarray3(1:n)) + if (any(iarray3(1:n) <= 0)) then message = "All entries on the element for a tally mesh & &must be positive." call fatal_error() end if ! Read dimensions in each direction - m % dimension = mesh_(i) % dimension + m % dimension = iarray3(1:n) ! Read mesh lower-left corner location - if (m % n_dimension /= size(mesh_(i) % lower_left)) then + if (m % n_dimension /= get_arraysize_double(node_mesh, "lower_left")) then message = "Number of entries on must be the same as & &the number of entries on ." call fatal_error() end if - m % lower_left = mesh_(i) % lower_left + call get_node_array(node_mesh, "lower_left", m % lower_left) - ! Make sure either upper-right or width was specified - if (associated(mesh_(i) % upper_right) .and. & - associated(mesh_(i) % width)) then + ! Make sure both upper-right or width were specified + if (check_for_node(node_mesh, "upper_right") .and. & + check_for_node(node_mesh, "width")) then message = "Cannot specify both and on a & &tally mesh." call fatal_error() end if ! Make sure either upper-right or width was specified - if (.not. associated(mesh_(i) % upper_right) .and. & - .not. associated(mesh_(i) % width)) then + if (.not.check_for_node(node_mesh, "upper_right") .and. & + .not.check_for_node(node_mesh, "width")) then message = "Must specify either and on a & &tally mesh." call fatal_error() end if - if (associated(mesh_(i) % width)) then + if (check_for_node(node_mesh, "width")) then ! Check to ensure width has same dimensions - if (size(mesh_(i) % width) /= size(mesh_(i) % lower_left)) then + if (get_arraysize_double(node_mesh, "width") /= & + get_arraysize_double(node_mesh, "lower_left")) then message = "Number of entries on must be the same as the & &number of entries on ." call fatal_error() end if ! Check for negative widths - if (any(mesh_(i) % width < ZERO)) then + call get_node_array(node_mesh, "width", rarray3(1:n)) + if (any(rarray3(1:n) < ZERO)) then message = "Cannot have a negative on a tally mesh." call fatal_error() end if ! Set width and upper right coordinate - m % width = mesh_(i) % width + m % width = rarray3(1:n) m % upper_right = m % lower_left + m % dimension * m % width - elseif (associated(mesh_(i) % upper_right)) then + elseif (check_for_node(node_mesh, "upper_right")) then ! Check to ensure width has same dimensions - if (size(mesh_(i) % upper_right) /= size(mesh_(i) % lower_left)) then + if (get_arraysize_double(node_mesh, "upper_right") /= & + get_arraysize_double(node_mesh, "lower_left")) then message = "Number of entries on must be the same as & &the number of entries on ." call fatal_error() end if ! Check that upper-right is above lower-left - if (any(mesh_(i) % upper_right < mesh_(i) % lower_left)) then + call get_node_array(node_mesh, "upper_right", rarray3(1:n)) + if (any(rarray3(1:n) < mesh_(i) % lower_left)) then message = "The coordinates must be greater than the & & coordinates on a tally mesh." call fatal_error() end if ! Set width and upper right coordinate - m % upper_right = mesh_(i) % upper_right + m % upper_right = rarray3(1:n) m % width = (m % upper_right - m % lower_left) / m % dimension end if @@ -2350,6 +2378,9 @@ contains ! Add tally to dictionary call tally_dict % add_key(t % id, i) + ! Close XML document + call close_xmldoc(doc) + end do READ_TALLIES end subroutine read_tallies_xml From 63b03b0eb46c18864226eda4059bed50ca470089 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 16:13:38 -0400 Subject: [PATCH 19/45] added string array capability to xml interface and converted tallies XML input --- src/input_xml.F90 | 157 +++++++++++++++++++++++------------------- src/xml_interface.F90 | 64 +++++++++++++++++ 2 files changed, 152 insertions(+), 69 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 39765c5fb..52ef5391e 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -212,7 +212,7 @@ contains message = "Lethargy mapped energy grid not yet supported." call fatal_error() case default - message = "Unknown energy grid method: " // temp_str + message = "Unknown energy grid method: " // trim(temp_str) call fatal_error() end select @@ -424,8 +424,9 @@ contains ! Cutoffs if (check_for_node(doc, "cutoff")) then - call get_node_value(doc, "weight", weight_cutoff) - call get_node_value(doc, "weight_avg", weight_survive) + call get_node_ptr(doc, "cutoff", node_cutoff) + call get_node_value(node_cutoff, "weight", weight_cutoff) + call get_node_value(node_cutoff, "weight_avg", weight_survive) end if ! Particle trace @@ -1429,7 +1430,7 @@ contains ALL_NUCLIDES: do j = 1, mat % n_nuclides ! Check that this nuclide is listed in the cross_sections.xml file - name = list_names % get_item(j) + name = trim(list_names % get_item(j)) if (.not. xs_listing_dict % has_key(name)) then message = "Could not find nuclide " // trim(name) // & " in cross_sections.xml file!" @@ -1561,8 +1562,6 @@ contains subroutine read_tallies_xml - use xml_data_tallies_t - integer :: i ! loop over user-specified tallies integer :: j ! loop over words integer :: k ! another loop index @@ -1584,6 +1583,7 @@ contains character(MAX_WORD_LEN) :: word character(MAX_WORD_LEN) :: score_name character(MAX_WORD_LEN) :: temp_str + character(MAX_WORD_LEN), allocatable :: sarray(:) type(ElemKeyValueCI), pointer :: pair_list => null() type(TallyObject), pointer :: t => null() type(StructuredMesh), pointer :: m => null() @@ -1606,7 +1606,6 @@ contains call write_message(5) ! Parse tallies.xml file - call read_xml_file_tallies_t(filename) call open_xmldoc(doc, filename) ! ========================================================================== @@ -1684,7 +1683,7 @@ contains case ('hex', 'hexagon', 'hexagonal') m % type = LATTICE_HEX case default - message = "Invalid mesh type: " // trim(mesh_(i) % type) + message = "Invalid mesh type: " // trim(temp_str) call fatal_error() end select @@ -1768,7 +1767,7 @@ contains ! Check that upper-right is above lower-left call get_node_array(node_mesh, "upper_right", rarray3(1:n)) - if (any(rarray3(1:n) < mesh_(i) % lower_left)) then + if (any(rarray3(1:n) < m % lower_left)) then message = "The coordinates must be greater than the & & coordinates on a tally mesh." call fatal_error() @@ -1793,6 +1792,9 @@ contains ! Get pointer to tally t => tallies(i) + ! Get pointer to tally xml node + call get_node_ptr(doc, "tally", node_tal, i) + ! Set tally type to volume by default t % type = TALLY_VOLUME @@ -1805,7 +1807,12 @@ contains t % estimator = ESTIMATOR_TRACKLENGTH ! Copy material id - t % id = tally_(i) % id + if (check_for_node(node_tal, "id")) then + call get_node_value(node_tal, "id", t % id) + else + message = "Must specify id for tally in tally XML file." + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (tally_dict % has_key(t % id)) then @@ -1815,7 +1822,9 @@ contains end if ! Copy tally label - t % label = tally_(i) % label + t % label = '' + if (check_for_node(node_tal, "label")) & + call get_node_value(node_tal, "label", t % label) ! ======================================================================= ! READ DATA FOR FILTERS @@ -1824,29 +1833,45 @@ contains ! element followed by sub-elements , , etc. This checks for ! the old format and if it is present, raises an error - if (size(tally_(i) % filters) > 0) then + if (get_number_nodes(node_tal, "filters") > 0) then message = "Tally filters should be specified with multiple & &elements. Did you forget to change your element?" call fatal_error() end if - if (associated(tally_(i) % filter)) then + if (check_for_node(node_tal, "filter")) then ! Determine number of filters - n_filters = size(tally_(i) % filter) + n_filters = get_number_nodes(node_tal, "filter") ! Allocate filters array t % n_filters = n_filters allocate(t % filters(n_filters)) READ_FILTERS: do j = 1, n_filters + ! Get pointer to filter xml node + call get_node_ptr(node_tal, "filter", node_filt, j) + ! Convert filter type to lower case - call lower_case(tally_(i) % filter(j) % type) + temp_str = '' + if (check_for_node(node_filt, "type")) & + call get_node_value(node_filt, "type", temp_str) + call lower_case(temp_str) ! Determine number of bins - n_words = size(tally_(i) % filter(j) % bins) + if (check_for_node(node_filt, "bins")) then + if (trim(temp_str) == 'energy' .or. & + trim(temp_str) == 'energyout') then + n_words = get_arraysize_double(node_filt, "bins") + else + n_words = get_arraysize_integer(node_filt, "bins") + end if + else + message = "Bins not set in filter on tally " // trim(to_str(t % id)) + call fatal_error() + end if ! Determine type of filter - select case (tally_(i) % filter(j) % type) + select case (temp_str) case ('cell') ! Set type of filter t % filters(j) % type = FILTER_CELL @@ -1856,10 +1881,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % int_bins(n_words)) - do k = 1, n_words - t % filters(j) % int_bins(k) = int(str_to_int(& - tally_(i) % filter(j) % bins(k)),4) - end do + call get_node_array(node_filt, "bins", t % filters(j) % int_bins) case ('cellborn') ! Set type of filter @@ -1870,10 +1892,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % int_bins(n_words)) - do k = 1, n_words - t % filters(j) % int_bins(k) = int(str_to_int(& - tally_(i) % filter(j) % bins(k)),4) - end do + call get_node_array(node_filt, "bins", t % filters(j) % int_bins) case ('material') ! Set type of filter @@ -1884,10 +1903,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % int_bins(n_words)) - do k = 1, n_words - t % filters(j) % int_bins(k) = int(str_to_int(& - tally_(i) % filter(j) % bins(k)),4) - end do + call get_node_array(node_filt, "bins", t % filters(j) % int_bins) case ('universe') ! Set type of filter @@ -1898,10 +1914,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % int_bins(n_words)) - do k = 1, n_words - t % filters(j) % int_bins(k) = int(str_to_int(& - tally_(i) % filter(j) % bins(k)),4) - end do + call get_node_array(node_filt, "bins", t % filters(j) % int_bins) case ('surface') message = "Surface filter is not yet supported!" @@ -1915,10 +1928,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % int_bins(n_words)) - do k = 1, n_words - t % filters(j) % int_bins(k) = int(str_to_int(& - tally_(i) % filter(j) % bins(k)),4) - end do + call get_node_array(node_filt, "bins", t % filters(j) % int_bins) case ('mesh') ! Set type of filter @@ -1931,7 +1941,7 @@ contains end if ! Determine id of mesh - id = int(str_to_int(tally_(i) % filter(j) % bins(1)),4) + call get_node_value(node_filt, "bins", id) ! Get pointer to mesh if (mesh_dict % has_key(id)) then @@ -1961,10 +1971,7 @@ contains ! Allocate and store bins allocate(t % filters(j) % real_bins(n_words)) - do k = 1, n_words - t % filters(j) % real_bins(k) = str_to_real(& - tally_(i) % filter(j) % bins(k)) - end do + call get_node_array(node_filt, "bins", t % filters(j) % real_bins) case ('energyout') ! Set type of filter @@ -1975,18 +1982,15 @@ contains ! Allocate and store bins allocate(t % filters(j) % real_bins(n_words)) - do k = 1, n_words - t % filters(j) % real_bins(k) = str_to_real(& - tally_(i) % filter(j) % bins(k)) - end do + call get_node_array(node_filt, "bins", t % filters(j) % real_bins) ! Set to analog estimator t % estimator = ESTIMATOR_ANALOG case default ! Specified tally filter is invalid, raise error - message = "Unknown filter type '" // trim(tally_(i) % & - filter(j) % type) // "' on tally " // & + message = "Unknown filter type '" // & + trim(temp_str) // "' on tally " // & trim(to_str(t % id)) // "." call fatal_error() @@ -2015,8 +2019,13 @@ contains ! ======================================================================= ! READ DATA FOR NUCLIDES - if (associated(tally_(i) % nuclides)) then - if (tally_(i) % nuclides(1) == 'all') then + if (check_for_node(node_tal, "nuclides")) then + + ! Allocate a temporary string array for nuclides and copy values over + allocate(sarray(get_arraysize_string(node_tal, "nuclides"))) + call get_node_array(node_tal, "nuclides", sarray) + + if (trim(sarray(1)) == 'all') then ! Handle special case all allocate(t % nuclide_bins(n_nuclides_total + 1)) @@ -2032,25 +2041,25 @@ contains t % all_nuclides = .true. else ! Any other case, e.g. U-235 Pu-239 - n_words = size(tally_(i) % nuclides) + n_words = get_arraysize_string(node_tal, "nuclides") allocate(t % nuclide_bins(n_words)) do j = 1, n_words ! Check if total material was specified - if (tally_(i) % nuclides(j) == 'total') then + if (trim(sarray(j)) == 'total') then t % nuclide_bins(j) = -1 cycle end if ! Check if xs specifier was given - if (ends_with(tally_(i) % nuclides(j), 'c')) then - word = tally_(i) % nuclides(j) + if (ends_with(sarray(j), 'c')) then + word = sarray(j) else if (default_xs == '') then ! No default cross section specified, search through nuclides pair_list => nuclide_dict % keys() do while (associated(pair_list)) if (starts_with(pair_list % key, & - tally_(i) % nuclides(j))) then + sarray(j))) then word = pair_list % key(1:150) exit end if @@ -2062,14 +2071,14 @@ contains ! Check if no nuclide was found if (.not. associated(pair_list)) then message = "Could not find the nuclide " // trim(& - tally_(i) % nuclides(j)) // " specified in tally " & + sarray(j)) // " specified in tally " & // trim(to_str(t % id)) // " in any material." call fatal_error() end if deallocate(pair_list) else ! Set nuclide to default xs - word = trim(tally_(i) % nuclides(j)) // "." // default_xs + word = trim(sarray(j)) // "." // default_xs end if end if @@ -2088,6 +2097,9 @@ contains t % n_nuclide_bins = n_words end if + ! Deallocate temporary string array + deallocate(sarray) + else ! No were specified -- create only one bin will be added ! for the total material. @@ -2099,18 +2111,20 @@ contains ! ======================================================================= ! READ DATA FOR SCORES - if (associated(tally_(i) % scores)) then + if (check_for_node(node_tal, "scores")) then ! Loop through scores and determine if a scatter-p# input was used ! to allow for proper pre-allocating of t % score_bins ! This scheme allows multiple scatter-p# to be requested by the user ! if so desired - n_words = size(tally_(i) % scores) + n_words = get_arraysize_string(node_tal, "scores") + allocate(sarray(n_words)) + call get_node_array(node_tal, "scores", sarray) n_new = 0 do j = 1, n_words - call lower_case(tally_(i) % scores(j)) + call lower_case(sarray(j)) ! Find if scores(j) is of the form 'scatter-p' ! If so, get the number and do a select case on that. - score_name = tally_(i) % scores(j) + score_name = trim(sarray(j)) if (starts_with(score_name,'scatter-p')) then n_order_pos = scan(score_name,'0123456789') n_order = int(str_to_int( & @@ -2123,7 +2137,7 @@ contains trim(to_str(SCATT_ORDER_MAX)) call warning() n_order = SCATT_ORDER_MAX - tally_(i) % scores(j) = SCATT_ORDER_MAX_PNSTR + sarray(j) = SCATT_ORDER_MAX_PNSTR end if n_new = n_new + n_order end if @@ -2140,7 +2154,7 @@ contains ! Get the input string in scores(l) but if scatter-n or scatter-pn ! then strip off the n, and store it as an integer to be used later ! Peform the select case on this modified (number removed) string - score_name = tally_(i) % scores(l) + score_name = sarray(l) if (starts_with(score_name,'scatter-p')) then n_order_pos = scan(score_name,'0123456789') n_order = int(str_to_int( & @@ -2171,7 +2185,7 @@ contains score_name = "scatter-n" end if - select case (score_name) + select case (trim(score_name)) case ('flux') ! Prohibit user from tallying flux for an individual nuclide if (.not. (t % n_nuclide_bins == 1 .and. & @@ -2326,14 +2340,14 @@ contains t % score_bins(j) = MT else message = "Invalid MT on : " // & - trim(tally_(i) % scores(j)) + trim(sarray(j)) call fatal_error() end if else ! Specified score was not an integer message = "Unknown scoring function: " // & - trim(tally_(i) % scores(j)) + trim(sarray(j)) call fatal_error() end if @@ -2341,6 +2355,9 @@ contains end do t % n_score_bins = n_scores t % n_user_score_bins = n_words + + ! Deallocate temporary string array of scores + deallocate(sarray) else message = "No specified on tally " // trim(to_str(t % id)) & // "." @@ -2351,8 +2368,10 @@ contains ! SET TALLY ESTIMATOR ! Check if user specified estimator - if (len_trim(tally_(i) % estimator) > 0) then - select case(tally_(i) % estimator) + if (check_for_node(node_tal, "estimator")) then + temp_str = '' + call get_node_value(node_tal, "estimator", temp_str) + select case(trim(temp_str)) case ('analog') t % estimator = ESTIMATOR_ANALOG @@ -2369,7 +2388,7 @@ contains t % estimator = ESTIMATOR_TRACKLENGTH case default - message = "Invalid estimator '" // trim(tally_(i) % estimator) & + message = "Invalid estimator '" // trim(temp_str) & // "' on tally " // to_str(t % id) call fatal_error() end select diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index cd948b64e..d1fc88a1c 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -17,6 +17,7 @@ module xml_interface public :: get_node_array public :: get_arraysize_integer public :: get_arraysize_double + public :: get_arraysize_string integer, parameter :: ATTR_NODE = 0 integer, parameter :: ELEM_NODE = 1 @@ -31,6 +32,7 @@ module xml_interface interface get_node_array module procedure get_node_array_integer module procedure get_node_array_double + module procedure get_node_array_string end interface get_node_array contains @@ -315,6 +317,39 @@ contains end subroutine get_node_array_double +!=============================================================================== +! GET_NODE_ARRAY_STRING +!=============================================================================== + + subroutine get_node_array_string(ptr, node_name, result_string) + + character(len=*), intent(in) :: node_name + character(len=*) :: result_string(:) + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Extract value + if (node_type == ATTR_NODE) then + call extractDataAttribute(ptr, node_name, result_string) + else + call extractDataContent(temp_ptr, result_string) + end if + + end subroutine get_node_array_string + !=============================================================================== ! GET_NODE_VALUE_STRING !=============================================================================== @@ -406,6 +441,35 @@ contains end function get_arraysize_double +!=============================================================================== +! GET_NODE_ARRAYSIZE_STRING +!=============================================================================== + + function get_arraysize_string(ptr, node_name) result(n) + + character(len=*), intent(in) :: node_name + integer :: n + type(Node), pointer, intent(in) :: ptr + + integer :: node_type + logical :: found + type(Node), pointer :: temp_ptr + + ! Get pointer to the node + call get_node(ptr, node_name, temp_ptr, node_type, found) + + ! Leave if it was not found + if (.not. found) then + message = "Node " // node_name // " not part of Node " // & + getNodeName(ptr) // "." + call fatal_error() + end if + + ! Get the size + n = countrts(getTextContent(temp_ptr), 'a') + + end function get_arraysize_string + !=============================================================================== ! GET_NODE !=============================================================================== From 6d96884998bcd2ec34eada18630b2ac23bec2799 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 17:06:17 -0400 Subject: [PATCH 20/45] converted plots XML input over to new XML reader --- src/input_xml.F90 | 183 +++++++++++++++++++++++++++++++--------------- 1 file changed, 124 insertions(+), 59 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 52ef5391e..7b2bce19e 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -88,20 +88,22 @@ contains ! Find cross_sections.xml file -- the first place to look is the ! settings.xml file. If no file is found there, then we check the ! CROSS_SECTIONS environment variable - if (.not. check_for_node(doc, "cross_sections") .and. & - run_mode /= MODE_PLOTTING) then - ! No cross_sections.xml file specified in settings.xml, check environment - ! variable - call get_environment_variable("CROSS_SECTIONS", env_variable) - if (len_trim(env_variable) == 0) then - message = "No cross_sections.xml file was specified in settings.xml & - &or in the CROSS_SECTIONS environment variable." - call fatal_error() + if (run_mode /= MODE_PLOTTING) then + if (.not. check_for_node(doc, "cross_sections") .and. & + run_mode /= MODE_PLOTTING) then + ! No cross_sections.xml file specified in settings.xml, check environment + ! variable + call get_environment_variable("CROSS_SECTIONS", env_variable) + if (len_trim(env_variable) == 0) then + message = "No cross_sections.xml file was specified in settings.xml & + &or in the CROSS_SECTIONS environment variable." + call fatal_error() + else + path_cross_sections = trim(env_variable) + end if else - path_cross_sections = trim(env_variable) + call get_node_value(doc, "cross_sections", path_cross_sections) end if - else - call get_node_value(doc, "cross_sections", path_cross_sections) end if ! Set output directory if a path has been specified on the @@ -2397,11 +2399,11 @@ contains ! Add tally to dictionary call tally_dict % add_key(t % id, i) - ! Close XML document - call close_xmldoc(doc) - end do READ_TALLIES + ! Close XML document + call close_xmldoc(doc) + end subroutine read_tallies_xml !=============================================================================== @@ -2410,13 +2412,17 @@ contains subroutine read_plots_xml - use xml_data_plots_t - integer i, j - integer n_cols, col_id + integer n_cols, col_id, n_comp + integer, allocatable :: iarray(:) logical :: file_exists ! does plots.xml file exist? character(MAX_LINE_LEN) :: filename ! absolute path to plots.xml + character(MAX_LINE_LEN) :: temp_str type(ObjectPlot), pointer :: pl => null() + type(Node), pointer :: doc => null() + type(Node), pointer :: node_plot => null() + type(Node), pointer :: node_col => null() + type(Node), pointer :: node_mask => null() ! Check if plots.xml exists filename = trim(path_input) // "plots.xml" @@ -2431,17 +2437,25 @@ contains call write_message(5) ! Parse plots.xml file - call read_xml_file_plots_t(filename) + call open_xmldoc(doc, filename) ! Allocate plots array - n_plots = size(plot_) + n_plots = get_number_nodes(doc, "plot") allocate(plots(n_plots)) READ_PLOTS: do i = 1, n_plots pl => plots(i) + ! Get pointer to plot XML node + call get_node_ptr(doc, "plot", node_plot, i) + ! Copy data into plots - pl % id = plot_(i) % id + if (check_for_node(node_plot, "id")) then + call get_node_value(node_plot, "id", pl % id) + else + message = "Must specify plot id in plots XML file." + call fatal_error() + end if ! Check to make sure 'id' hasn't been used if (plot_dict % has_key(pl % id)) then @@ -2451,40 +2465,46 @@ contains end if ! Copy plot type - select case (plot_(i) % type) + temp_str = 'slice' + if (check_for_node(node_plot, "type")) & + call get_node_value(node_plot, "type", temp_str) + call lower_case(temp_str) + select case (trim(temp_str)) case ("slice") pl % type = PLOT_TYPE_SLICE case ("voxel") pl % type = PLOT_TYPE_VOXEL case default - message = "Unsupported plot type '" // trim(plot_(i) % type) & + message = "Unsupported plot type '" // trim(temp_str) & // "' in plot " // trim(to_str(pl % id)) call fatal_error() end select ! Set output file path + filename = "plot" + if (check_for_node(node_plot, "filename")) & + call get_node_value(node_plot, "filename", filename) select case (pl % type) case (PLOT_TYPE_SLICE) pl % path_plot = trim(path_input) // trim(to_str(pl % id)) // & - "_" // trim(plot_(i) % filename) // ".ppm" + "_" // trim(filename) // ".ppm" case (PLOT_TYPE_VOXEL) pl % path_plot = trim(path_input) // trim(to_str(pl % id)) // & - "_" // trim(plot_(i) % filename) // ".voxel" + "_" // trim(filename) // ".voxel" end select ! Copy plot pixel size if (pl % type == PLOT_TYPE_SLICE) then - if (size(plot_(i) % pixels) == 2) then - pl % pixels(1) = plot_(i) % pixels(1) - pl % pixels(2) = plot_(i) % pixels(2) + if (get_arraysize_integer(node_plot, "pixels") == 2) then + call get_node_array(node_plot, "pixels", pl % pixels(1:2)) else message = " must be length 2 in slice plot " // & trim(to_str(pl % id)) call fatal_error() end if else if (pl % type == PLOT_TYPE_VOXEL) then - if (size(plot_(i) % pixels) == 3) then - pl % pixels = plot_(i) % pixels + if (get_arraysize_integer(node_plot, "pixels") == 3) then + call get_node_array(node_plot, "pixels", pl % pixels(1:3)) else message = " must be length 3 in voxel plot " // & trim(to_str(pl % id)) @@ -2493,14 +2513,14 @@ contains end if ! Copy plot background color - if (associated(plot_(i) % background)) then + if (check_for_node(node_plot, "background")) then if (pl % type == PLOT_TYPE_VOXEL) then message = "Background color ignored in voxel plot " // & trim(to_str(pl % id)) call warning() end if - if (size(plot_(i) % background) == 3) then - pl % not_found % rgb = plot_(i) % background + if (get_arraysize_integer(node_plot, "background") == 3) then + call get_node_array(node_plot, "background", pl % not_found % rgb) else message = "Bad background RGB " & // "in plot " // trim(to_str(pl % id)) @@ -2512,7 +2532,11 @@ contains ! Copy plot basis if (pl % type == PLOT_TYPE_SLICE) then - select case (plot_(i) % basis) + temp_str = 'xy' + if (check_for_node(node_plot, "basis")) & + call get_node_value(node_plot, "basis", temp_str) + call lower_case(temp_str) + select case (trim(temp_str)) case ("xy") pl % basis = PLOT_BASIS_XY case ("xz") @@ -2520,15 +2544,15 @@ contains case ("yz") pl % basis = PLOT_BASIS_YZ case default - message = "Unsupported plot basis '" // plot_(i) % basis & + message = "Unsupported plot basis '" // trim(temp_str) & // "' in plot " // trim(to_str(pl % id)) call fatal_error() end select end if ! Copy plotting origin - if (size(plot_(i) % origin) == 3) then - pl % origin = plot_(i) % origin + if (get_arraysize_double(node_plot, "origin") == 3) then + call get_node_array(node_plot, "origin", pl % origin) else message = "Origin must be length 3 " & // "in plot " // trim(to_str(pl % id)) @@ -2537,17 +2561,16 @@ contains ! Copy plotting width if (pl % type == PLOT_TYPE_SLICE) then - if (size(plot_(i) % width) == 2) then - pl % width(1) = plot_(i) % width(1) - pl % width(2) = plot_(i) % width(2) + if (get_arraysize_double(node_plot, "width") == 2) then + call get_node_array(node_plot, "width", pl % width(1:2)) else message = " must be length 2 in slice plot " // & trim(to_str(pl % id)) call fatal_error() end if else if (pl % type == PLOT_TYPE_VOXEL) then - if (size(plot_(i) % width) == 3) then - pl % width = plot_(i) % width + if (get_arraysize_double(node_plot, "width") == 3) then + call get_node_array(node_plot, "width", pl % width(1:3)) else message = " must be length 3 in voxel plot " // & trim(to_str(pl % id)) @@ -2556,7 +2579,11 @@ contains end if ! Copy plot color type and initialize all colors randomly - select case (plot_(i) % color) + temp_str = '' + if (check_for_node(node_plot, "color")) & + call get_node_value(node_plot, "color", temp_str) + call lower_case(temp_str) + select case (trim(temp_str)) case ("cell") pl % color_by = PLOT_COLOR_CELLS @@ -2578,13 +2605,13 @@ contains end do case default - message = "Unsupported plot color type '" // plot_(i) % color & + message = "Unsupported plot color type '" // trim(temp_str) & // "' in plot " // trim(to_str(pl % id)) call fatal_error() end select ! Copy user specified colors - if (associated(plot_(i) % col_spec_)) then + if (check_for_node(node_plot, "col_spec")) then if (pl % type == PLOT_TYPE_VOXEL) then message = "Color specifications ignored in voxel plot " // & @@ -2592,21 +2619,34 @@ contains call warning() end if - n_cols = size(plot_(i) % col_spec_) + n_cols = get_number_nodes(node_plot, "col_spec") do j = 1, n_cols - if (size(plot_(i) % col_spec_(j) % rgb) /= 3) then + + ! Get pointer to color spec XML node + call get_node_ptr(node_plot, "col_spec", node_col, j) + + ! Check and make sure 3 values are specified for RGB + if (get_arraysize_double(node_col, "rgb") /= 3) then message = "Bad RGB " & // "in plot " // trim(to_str(pl % id)) call fatal_error() end if - col_id = plot_(i) % col_spec_(j) % id + ! Ensure that there is an id for this color specification + if (check_for_node(node_col, "id")) then + call get_node_value(node_col, "id", col_id) + else + message = "Must specify id for color specification in plot " // & + trim(to_str(pl % id)) + call fatal_error() + end if + ! Add RGB if (pl % color_by == PLOT_COLOR_CELLS) then if (cell_dict % has_key(col_id)) then col_id = cell_dict % get_key(col_id) - pl % colors(col_id) % rgb = plot_(i) % col_spec_(j) % rgb + call get_node_array(node_plot, "rgb", pl % colors(col_id) % rgb) else message = "Could not find cell " // trim(to_str(col_id)) // & " specified in plot " // trim(to_str(pl % id)) @@ -2617,7 +2657,7 @@ contains if (material_dict % has_key(col_id)) then col_id = material_dict % get_key(col_id) - pl % colors(col_id) % rgb = plot_(i) % col_spec_(j) % rgb + call get_node_array(node_plot, "rgb", pl % colors(col_id) % rgb) else message = "Could not find material " // trim(to_str(col_id)) // & " specified in plot " // trim(to_str(pl % id)) @@ -2629,7 +2669,7 @@ contains end if ! Deal with masks - if (associated(plot_(i) % mask_)) then + if (check_for_node(node_plot, "mask")) then if (pl % type == PLOT_TYPE_VOXEL) then message = "Mask ignored in voxel plot " // & @@ -2637,23 +2677,37 @@ contains call warning() end if - select case(size(plot_(i) % mask_)) + select case(get_number_nodes(node_plot, "mask")) case default message = "Mutliple masks" // & " specified in plot " // trim(to_str(pl % id)) call fatal_error() case (0) case (1) - + + ! Get pointer to mask + call get_node_ptr(node_plot, "mask", node_mask) + + ! Determine how many components there are and allocate + n_comp = 0 + n_comp = get_arraysize_integer(node_mask, "components") + if (n_comp == 0) then + message = "Missing in mask of plot " // & + trim(to_str(pl % id)) + call fatal_error() + end if + allocate(iarray(n_comp)) + call get_node_array(node_mask, "components", iarray) + ! First we need to change the user-specified identifiers to indices ! in the cell and material arrays - do j=1,size(plot_(i) % mask_(1) % components) - col_id = plot_(i) % mask_(1) % components(j) + do j=1, n_comp + col_id = iarray(j) if (pl % color_by == PLOT_COLOR_CELLS) then if (cell_dict % has_key(col_id)) then - plot_(i) % mask_(1) % components(j) = cell_dict % get_key(col_id) + iarray(j) = cell_dict % get_key(col_id) else message = "Could not find cell " // trim(to_str(col_id)) // & " specified in the mask in plot " // trim(to_str(pl % id)) @@ -2663,7 +2717,7 @@ contains else if (pl % color_by == PLOT_COLOR_MATS) then if (material_dict % has_key(col_id)) then - plot_(i) % mask_(1) % components(j) = material_dict % get_key(col_id) + iarray(j) = material_dict % get_key(col_id) else message = "Could not find material " // trim(to_str(col_id)) // & " specified in the mask in plot " // trim(to_str(pl % id)) @@ -2675,10 +2729,18 @@ contains ! Alter colors based on mask information do j=1,size(pl % colors) - if (.not. any(j .eq. plot_(i) % mask_(1) % components)) then - pl % colors(j) % rgb = plot_(i) % mask_(1) % background + if (.not. any(j .eq. iarray)) then + if (check_for_node(node_mask, "background")) then + call get_node_array(node_mask, "background", pl % colors(j) % rgb) + else + message = "Missing in mask of plot " // & + trim(to_str(pl % id)) + call fatal_error() + end if end if end do + + deallocate(iarray) end select @@ -2689,6 +2751,9 @@ contains end do READ_PLOTS + ! Close plots XML file + call close_xmldoc(doc) + end subroutine read_plots_xml !=============================================================================== From 9143cba7886806d5c1d86ac73d121554be16b4ad Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 17:28:23 -0400 Subject: [PATCH 21/45] converted cross sections XML over to new XML reader --- src/input_xml.F90 | 81 ++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 7b2bce19e..24ec6c2c6 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2763,15 +2763,16 @@ contains subroutine read_cross_sections_xml() - use xml_data_cross_sections_t - integer :: i ! loop index integer :: filetype ! default file type integer :: recl ! default record length integer :: entries ! default number of entries logical :: file_exists ! does cross_sections.xml exist? character(MAX_WORD_LEN) :: directory ! directory with cross sections + character(MAX_LINE_LEN) :: temp_str type(XsListing), pointer :: listing => null() + type(Node), pointer :: doc => null() + type(Node), pointer :: node_ace => null() ! Check if cross_sections.xml exists inquire(FILE=path_cross_sections, EXIST=file_exists) @@ -2785,18 +2786,12 @@ contains message = "Reading cross sections XML file..." call write_message(5) - ! Initialize variables that may go unused - directory_ = "" - filetype_ = "" - record_length_ = 0 - entries_ = 0 - ! Parse cross_sections.xml file - call read_xml_file_cross_sections_t(path_cross_sections) + call open_xmldoc(doc, path_cross_sections) - if (len_trim(directory_) > 0) then + if (check_for_node(doc, "directory")) then ! Copy directory information if present - directory = trim(directory_) + call get_node_value(doc, "directory", directory) else ! If no directory is listed in cross_sections.xml, by default select the ! directory in which the cross_sections.xml file resides @@ -2805,40 +2800,50 @@ contains end if ! determine whether binary/ascii - if (filetype_ == 'ascii') then + temp_str = '' + if (check_for_node(doc, "filetype")) & + call get_node_value(doc, "filetype", temp_str) + if (trim(temp_str) == 'ascii') then filetype = ASCII - elseif (filetype_ == 'binary') then + elseif (trim(temp_str) == 'binary') then filetype = BINARY - elseif (len_trim(filetype_) == 0) then + elseif (len_trim(temp_str) == 0) then filetype = ASCII else - message = "Unknown filetype in cross_sections.xml: " // trim(filetype_) + message = "Unknown filetype in cross_sections.xml: " // trim(temp_str) call fatal_error() end if ! copy default record length and entries for binary files - recl = record_length_ - entries = entries_ + if (filetype == BINARY) then + call get_node_value(doc, "record_length", recl) + call get_node_value(doc, "entries", entries) + end if ! Allocate xs_listings array - if (.not. associated(ace_tables_)) then + if (.not.check_for_node(doc, "ace_table")) then message = "No ACE table listings present in cross_sections.xml file!" call fatal_error() else - n_listings = size(ace_tables_) + n_listings = get_number_nodes(doc, "ace_table") allocate(xs_listings(n_listings)) end if do i = 1, n_listings listing => xs_listings(i) + ! Get pointer to ace table XML node + call get_node_ptr(doc, "ace_table", node_ace, i) + ! copy a number of attributes - listing % name = trim(ace_tables_(i) % name) - listing % alias = trim(ace_tables_(i) % alias) - listing % zaid = ace_tables_(i) % zaid - listing % awr = ace_tables_(i) % awr - listing % kT = ace_tables_(i) % temperature - listing % location = ace_tables_(i) % location + call get_node_value(node_ace, "name", listing % name) + if (check_for_node(node_ace, "alias")) & + call get_node_value(node_ace, "alias", listing % alias) + call get_node_value(node_ace, "zaid", listing % zaid) + call get_node_value(node_ace, "awr", listing % awr) + if (check_for_node(node_ace, "temperature")) & + call get_node_value(node_ace, "temperature", listing % kT) + call get_node_value(node_ace, "location", listing % location) ! determine type of cross section if (ends_with(listing % name, 'c')) then @@ -2849,24 +2854,33 @@ contains ! set filetype, record length, and number of entries listing % filetype = filetype - listing % recl = recl - listing % entries = entries + if (filetype == BINARY) then + listing % recl = recl + listing % entries = entries + end if ! determine metastable state - if (ace_tables_(i) % metastable == 0) then + if (.not.check_for_node(node_ace, "metastable")) then listing % metastable = .false. else listing % metastable = .true. end if ! determine path of cross section table - if (starts_with(ace_tables_(i) % path, '/')) then - listing % path = ace_tables_(i) % path + if (check_for_node(node_ace, "path")) then + call get_node_value(node_ace, "path", temp_str) + else + message = "Path missing for isotope " // listing % name + call fatal_error() + end if + + if (starts_with(temp_str, '/')) then + listing % path = trim(temp_str) else if (ends_with(directory,'/')) then - listing % path = trim(directory) // trim(ace_tables_(i) % path) + listing % path = trim(directory) // trim(temp_str) else - listing % path = trim(directory) // '/' // trim(ace_tables_(i) % path) + listing % path = trim(directory) // '/' // trim(temp_str) end if end if @@ -2877,6 +2891,9 @@ contains end if end do + ! Close cross sections XML file + call close_xmldoc(doc) + end subroutine read_cross_sections_xml !=============================================================================== From d74d4107814b90949bd79c6cbaafd3fa62602c36 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 18:09:38 -0400 Subject: [PATCH 22/45] converted over CMFD XML input to new XML interface --- src/cmfd_input.F90 | 301 +++++++++++++++++++++++++++------------------ 1 file changed, 183 insertions(+), 118 deletions(-) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index c228890d1..df692c46e 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -31,19 +31,24 @@ contains !=============================================================================== subroutine read_cmfd_xml() - + use error, only: fatal_error use global use output, only: write_message use string, only: lower_case - use xml_data_cmfd_t + use xml_interface use, intrinsic :: ISO_FORTRAN_ENV integer :: ng + integer, allocatable :: iarray(:) logical :: file_exists ! does cmfd.xml exist? + logical :: found character(MAX_LINE_LEN) :: filename + character(MAX_LINE_LEN) :: temp_str + type(Node), pointer :: doc => null() + type(Node), pointer :: node_mesh => null() - ! read cmfd infput file + ! read cmfd input file filename = trim(path_input) // "cmfd.xml" inquire(FILE=filename, EXIST=file_exists) if (.not. file_exists) then @@ -62,16 +67,25 @@ contains end if ! parse cmfd.xml file - call read_xml_file_cmfd_t(filename) + call open_xmldoc(doc, filename) + + ! get pointer to mesh XML node + call get_node_ptr(doc, "mesh", node_mesh, found = found) + + ! check if mesh is there + if (.not.found) then + message = "No CMFD mesh specified in CMFD XML file." + call fatal_error() + end if ! set spatial dimensions in cmfd object - cmfd % indices(1:3) = mesh_ % dimension(1:3) ! sets spatial dimensions + call get_node_array(node_mesh, "dimension", cmfd % indices(1:3)) ! get number of energy groups - if (associated(mesh_ % energy)) then - ng = size(mesh_ % energy) + if (check_for_node(node_mesh, "energy")) then + ng = get_arraysize_double(node_mesh, "energy") if(.not.allocated(cmfd%egrid)) allocate(cmfd%egrid(ng)) - cmfd%egrid = mesh_ % energy + call get_node_array(node_mesh, "energy", cmfd%egrid) cmfd % indices(4) = ng - 1 ! sets energy group dimension else if(.not.allocated(cmfd%egrid)) allocate(cmfd%egrid(2)) @@ -80,22 +94,26 @@ contains end if ! set global albedo - if (associated(mesh_ % albedo)) then - cmfd % albedo = mesh_ % albedo + if (check_for_node(node_mesh, "albedo")) then + call get_node_array(node_mesh, "albedo", cmfd % albedo) else cmfd % albedo = (/1.0, 1.0, 1.0, 1.0, 1.0, 1.0/) end if ! get acceleration map - if (associated(mesh_ % map)) then + if (check_for_node(node_mesh, "map")) then allocate(cmfd % coremap(cmfd % indices(1), cmfd % indices(2), & cmfd % indices(3))) - if (size(mesh_ % map) /= product(cmfd % indices(1:3))) then + if (get_arraysize_integer(node_mesh, "map") /= & + product(cmfd % indices(1:3))) then message = 'FATAL==>CMFD coremap not to correct dimensions' call fatal_error() end if - cmfd % coremap = reshape(mesh_ % map,(cmfd % indices(1:3))) + allocate(iarray(get_arraysize_integer(node_mesh, "map"))) + call get_node_array(node_mesh, "map", iarray) + cmfd % coremap = reshape(iarray,(cmfd % indices(1:3))) cmfd_coremap = .true. + deallocate(iarray) end if ! check for core map activation by printing note @@ -105,11 +123,17 @@ contains end if ! check for normalization constant - cmfd % norm = norm_ + if (check_for_node(doc, "norm")) then + call get_node_value(doc, "norm", cmfd % norm) + end if ! set feedback logical - call lower_case(feedback_) - if (feedback_ == 'true' .or. feedback_ == '1') cmfd_feedback = .true. + if (check_for_node(doc, "feedback")) then + call get_node_value(doc, "feedback", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_feedback = .true. + end if ! set balance logical ! call lower_case(balance_) @@ -121,64 +145,97 @@ contains ! cmfd_downscatter = downscatter_ ! set 2 group fix - call lower_case(run_2grp_) - if (run_2grp_ == 'true' .or. run_2grp_ == '1') cmfd_run_2grp = .true. + if (check_for_node(doc, "run_2grp")) then + call get_node_value(doc, "run_2grp", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_run_2grp = .true. + end if ! set the solver type - cmfd_solver_type = solver_(1:10) + if (check_for_node(doc, "solver")) & + call get_node_value(doc, "solver", cmfd_solver_type) - ! set monitoring - call lower_case(snes_monitor_) - call lower_case(ksp_monitor_) - call lower_case(power_monitor_) - if (snes_monitor_ == 'true' .or. snes_monitor_ == '1') & - cmfd_snes_monitor = .true. - if (ksp_monitor_ == 'true' .or. ksp_monitor_ == '1') & - cmfd_ksp_monitor = .true. - if (power_monitor_ == 'true' .or. power_monitor_ == '1') & - cmfd_power_monitor = .true. + ! set monitoring + if (check_for_node(doc, "snes_monitor")) then + call get_node_value(doc, "snes_monitor", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_snes_monitor = .true. + end if + if (check_for_node(doc, "ksp_monitor")) then + call get_node_value(doc, "ksp_monitor", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_ksp_monitor = .true. + end if + if (check_for_node(doc, "power_monitor")) then + call get_node_value(doc, "power_monitor", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_power_monitor = .true. + end if ! output logicals - call lower_case(write_balance_) - call lower_case(write_matrices_) - ! call lower_case(write_hdf5_) - if (write_balance_ == 'true' .or. write_balance_ == '1') & - cmfd_write_balance = .true. - if (write_matrices_ == 'true' .or. write_matrices_ == '1') & - cmfd_write_matrices = .true. - ! if (write_hdf5_ == 'true' .or. write_hdf5_ == '1') & - ! cmfd_write_hdf5 = .true. + if (check_for_node(doc, "write_balance")) then + call get_node_value(doc, "write_balance", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_write_balance = .true. + end if + if (check_for_node(doc, "write_matrices")) then + call get_node_value(doc, "write_matices", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_write_matrices = .true. + end if ! run an adjoint calc - call lower_case(run_adjoint_) - if (run_adjoint_ == 'true' .or. run_adjoint_ == '1') & - cmfd_run_adjoint = .true. + if (check_for_node(doc, "run_adjoint")) then + call get_node_value(doc, "run_adjoint", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + cmfd_run_adjoint = .true. + end if ! batch to begin cmfd - cmfd_begin = begin_ + if (check_for_node(doc, "begin")) & + call get_node_value(doc, "begin", cmfd_begin) ! tally during inactive batches - call lower_case(inactive_) - if (inactive_ == 'false' .or. inactive_ == '0') cmfd_tally_on = .false. + if (check_for_node(doc, "inactive")) then + call get_node_value(doc, "inactive", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'false' .or. trim(temp_str) == '0') & + cmfd_tally_on = .false. + end if ! inactive batch flush window - cmfd_inact_flush(1) = inactive_flush_ - cmfd_inact_flush(2) = num_flushes_ + if (check_for_node(doc, "inactive_flush")) & + call get_node_value(doc, "inactive_flush", cmfd_inact_flush(1)) + if (check_for_node(doc, "num_flushes")) & + call get_node_value(doc, "num_flushes", cmfd_inact_flush(2)) ! last flush before active batches - cmfd_act_flush = active_flush_ + if (check_for_node(doc, "active_flush")) & + call get_node_value(doc, "active_flush", cmfd_act_flush) ! tolerance on keff - cmfd_keff_tol = keff_tol_ + if (check_for_node(doc, "keff_tol")) & + call get_node_value(doc, "keff_tol", cmfd_keff_tol) ! create tally objects - call create_cmfd_tally() + call create_cmfd_tally(doc) ! set number of CMFD processors and report to user - n_procs_cmfd = n_cmfd_procs_ + if (check_for_node(doc, "n_cmfd_procs")) & + call get_node_value(doc, "n_cmfd_procs", n_procs_cmfd) if (master) write(OUTPUT_UNIT,'(A,1X,I0,1X,A)') "CMFD Running on", & n_procs_cmfd," processors." + ! close CMFD XML file + call close_xmldoc(doc) + end subroutine read_cmfd_xml !=============================================================================== @@ -190,7 +247,7 @@ contains ! 3: Surface current !=============================================================================== - subroutine create_cmfd_tally() + subroutine create_cmfd_tally(doc) use error, only: fatal_error, warning use global @@ -200,20 +257,21 @@ contains use tally_header, only: TallyObject, TallyFilter use tally_initialize, only: add_tallies use xml_data_cmfd_t + use xml_interface + + type(Node), pointer :: doc integer :: i ! loop counter integer :: n ! size of arrays in mesh specification integer :: ng ! number of energy groups (default 1) integer :: n_filters ! number of filters integer :: i_filter_mesh ! index for mesh filter - character(MAX_LINE_LEN) :: filename + integer :: iarray3(3) + real(8) :: rarray3(3) type(TallyObject), pointer :: t => null() type(StructuredMesh), pointer :: m => null() type(TallyFilter) :: filters(N_FILTER_TYPES) ! temporary filters - - ! parse cmfd.xml file - filename = trim(path_input) // "cmfd.xml" - call read_xml_file_cmfd_t(filename) + type(Node), pointer :: node_mesh => null() ! set global variables if they are 0 (this can happen if there is no tally ! file) @@ -229,8 +287,11 @@ contains ! set mesh type to rectangular m % type = LATTICE_RECT + ! Get pointer to mesh XML node + call get_node_ptr(doc, "mesh", node_mesh) + ! Determine number of dimensions for mesh - n = size(mesh_ % dimension) + n = get_arraysize_integer(node_mesh, "dimension") if (n /= 2 .and. n /= 3) then message = "Mesh must be two or three dimensions." call fatal_error() @@ -244,75 +305,76 @@ contains allocate(m % upper_right(n)) ! Check that dimensions are all greater than zero - if (any(mesh_ % dimension <= 0)) then - message = "All entries on the element for a tally mesh & - &must be positive." - call fatal_error() + call get_node_array(node_mesh, "dimension", iarray3(1:n)) + if (any(iarray3(1:n) <= 0)) then + message = "All entries on the element for a tally mesh & + &must be positive." + call fatal_error() end if ! Read dimensions in each direction - m % dimension = mesh_ % dimension + m % dimension = iarray3(1:n) ! Read mesh lower-left corner location - if (m % n_dimension /= size(mesh_ % lower_left)) then - message = "Number of entries on must be the same as & - &the number of entries on ." - call fatal_error() + if (m % n_dimension /= get_arraysize_double(node_mesh, "lower_left")) then + message = "Number of entries on must be the same as & + &the number of entries on ." + call fatal_error() end if - m % lower_left = mesh_ % lower_left + call get_node_array(node_mesh, "lower_left", m % lower_left) - ! Make sure either upper-right or width was specified - if (associated(mesh_ % upper_right) .and. & - associated(mesh_ % width)) then - message = "Cannot specify both and on a & - &tally mesh." - call fatal_error() + ! Make sure both upper-right or width were specified + if (check_for_node(node_mesh, "upper_right") .and. & + check_for_node(node_mesh, "width")) then + message = "Cannot specify both and on a & + &tally mesh." + call fatal_error() end if ! Make sure either upper-right or width was specified - if (.not. associated(mesh_ % upper_right) .and. & - .not. associated(mesh_ % width)) then - message = "Must specify either and on a & - &tally mesh." - call fatal_error() + if (.not.check_for_node(node_mesh, "upper_right") .and. & + .not.check_for_node(node_mesh, "width")) then + message = "Must specify either and on a & + &tally mesh." + call fatal_error() end if - if (associated(mesh_ % width)) then - ! Check to ensure width has same dimensions - if (size(mesh_ % width) /= size(mesh_ % lower_left)) then - message = "Number of entries on must be the same as the & - &number of entries on ." - call fatal_error() - end if + if (check_for_node(node_mesh, "width")) then + ! Check to ensure width has same dimensions + if (get_arraysize_double(node_mesh, "width") /= & + get_arraysize_double(node_mesh, "lower_left")) then + message = "Number of entries on must be the same as the & + &number of entries on ." + call fatal_error() + end if - ! Check for negative widths - if (any(mesh_ % width < ZERO)) then - message = "Cannot have a negative on a tally mesh." - call fatal_error() - end if + ! Check for negative widths + call get_node_array(node_mesh, "width", rarray3(1:n)) + if (any(rarray3(1:n) < ZERO)) then + message = "Cannot have a negative on a tally mesh." + call fatal_error() + end if - ! Set width and upper right coordinate - m % width = mesh_ % width - m % upper_right = m % lower_left + m % dimension * m % width + ! Set width and upper right coordinate + m % width = rarray3(1:n) + m % upper_right = m % lower_left + m % dimension * m % width - elseif (associated(mesh_ % upper_right)) then - ! Check to ensure width has same dimensions - if (size(mesh_ % upper_right) /= size(mesh_ % lower_left)) then - message = "Number of entries on must be the same as & - &the number of entries on ." - call fatal_error() - end if + elseif (check_for_node(node_mesh, "upper_right")) then + ! Check to ensure width has same dimensions + if (get_arraysize_double(node_mesh, "upper_right") /= & + get_arraysize_double(node_mesh, "lower_left")) then + message = "Number of entries on must be the same as & + &the number of entries on ." + call fatal_error() + end if - ! Check that upper-right is above lower-left - if (any(mesh_ % upper_right < mesh_ % lower_left)) then - message = "The coordinates must be greater than the & - & coordinates on a tally mesh." - call fatal_error() - end if - - ! Set width and upper right coordinate - m % upper_right = mesh_ % upper_right - m % width = (m % upper_right - m % lower_left) / m % dimension + ! Check that upper-right is above lower-left + call get_node_array(node_mesh, "upper_right", rarray3(1:n)) + if (any(rarray3(1:n) < m % lower_left)) then + message = "The coordinates must be greater than the & + & coordinates on a tally mesh." + call fatal_error() + end if end if ! Set volume fraction @@ -343,13 +405,14 @@ contains t % find_filter(FILTER_MESH) = n_filters ! read and set incoming energy mesh filter - if (associated(mesh_ % energy)) then + if (check_for_node(node_mesh, "energy")) then n_filters = n_filters + 1 filters(n_filters) % type = FILTER_ENERGYIN - ng = size(mesh_ % energy) + ng = get_arraysize_double(node_mesh, "energy") filters(n_filters) % n_bins = ng - 1 allocate(filters(n_filters) % real_bins(ng)) - filters(n_filters) % real_bins = mesh_ % energy + call get_node_array(node_mesh, "energy", & + filters(n_filters) % real_bins) t % find_filter(FILTER_ENERGYIN) = n_filters end if @@ -404,13 +467,14 @@ contains t % type = TALLY_VOLUME ! read and set outgoing energy mesh filter - if (associated(mesh_ % energy)) then + if (check_for_node(node_mesh, "energy")) then n_filters = n_filters + 1 filters(n_filters) % type = FILTER_ENERGYOUT - ng = size(mesh_ % energy) + ng = get_arraysize_double(node_mesh, "energy") filters(n_filters) % n_bins = ng - 1 allocate(filters(n_filters) % real_bins(ng)) - filters(n_filters) % real_bins = mesh_ % energy + call get_node_array(node_mesh, "energy", & + filters(n_filters) % real_bins) t % find_filter(FILTER_ENERGYOUT) = n_filters end if @@ -420,7 +484,7 @@ contains t % filters = filters(1:n_filters) ! deallocate filters bins array - if (associated(mesh_ % energy)) & + if (check_for_node(node_mesh, "energy")) & deallocate(filters(n_filters) % real_bins) ! allocate macro reactions @@ -488,7 +552,8 @@ contains ! deallocate filter bins deallocate(filters(1) % int_bins) - if (associated(mesh_ % energy)) deallocate(filters(2) % real_bins) + if (check_for_node(node_mesh, "energy")) & + deallocate(filters(2) % real_bins) end do From dd9e1c97eb8580f6d2992d853f71163360d006f8 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 23 Jul 2013 18:19:45 -0400 Subject: [PATCH 23/45] removed xml-fortran from build process and deleted files. Also, fixed minor bug in cmfd input --- src/DEPENDENCIES | 9 +- src/Makefile | 17 +- src/cmfd_input.F90 | 11 +- src/templates/Makefile | 38 - src/templates/cmfd.rnc | 58 - src/templates/cmfd_t.xml | 39 - src/templates/cross_sections.rnc | 23 - src/templates/cross_sections_t.xml | 26 - src/templates/geometry.rnc | 35 - src/templates/geometry_t.xml | 39 - src/templates/materials.rnc | 42 - src/templates/materials_t.xml | 44 - src/templates/plots.rnc | 31 - src/templates/plots_t.xml | 32 - src/templates/settings.rnc | 112 -- src/templates/settings_t.xml | 68 -- src/templates/tallies.rnc | 43 - src/templates/tallies_t.xml | 37 - src/utils/build_dependencies.py | 2 - src/xml-fortran/Makefile | 47 - src/xml-fortran/read_from_buffer.inc | 79 -- src/xml-fortran/read_xml_array.inc | 54 - src/xml-fortran/read_xml_primitives.f90 | 527 --------- src/xml-fortran/read_xml_scalar.inc | 40 - src/xml-fortran/read_xml_word.inc | 38 - src/xml-fortran/write_xml_primitives.f90 | 489 --------- src/xml-fortran/xml-fortran.LICENSE | 37 - src/xml-fortran/xmlparse.f90 | 1076 ------------------- src/xml-fortran/xmlreader.f90 | 1241 ---------------------- 29 files changed, 14 insertions(+), 4320 deletions(-) delete mode 100644 src/templates/Makefile delete mode 100644 src/templates/cmfd.rnc delete mode 100644 src/templates/cmfd_t.xml delete mode 100644 src/templates/cross_sections.rnc delete mode 100644 src/templates/cross_sections_t.xml delete mode 100644 src/templates/geometry.rnc delete mode 100644 src/templates/geometry_t.xml delete mode 100644 src/templates/materials.rnc delete mode 100644 src/templates/materials_t.xml delete mode 100644 src/templates/plots.rnc delete mode 100644 src/templates/plots_t.xml delete mode 100644 src/templates/settings.rnc delete mode 100644 src/templates/settings_t.xml delete mode 100644 src/templates/tallies.rnc delete mode 100644 src/templates/tallies_t.xml delete mode 100644 src/xml-fortran/Makefile delete mode 100644 src/xml-fortran/read_from_buffer.inc delete mode 100644 src/xml-fortran/read_xml_array.inc delete mode 100644 src/xml-fortran/read_xml_primitives.f90 delete mode 100644 src/xml-fortran/read_xml_scalar.inc delete mode 100644 src/xml-fortran/read_xml_word.inc delete mode 100644 src/xml-fortran/write_xml_primitives.f90 delete mode 100644 src/xml-fortran/xml-fortran.LICENSE delete mode 100644 src/xml-fortran/xmlparse.f90 delete mode 100644 src/xml-fortran/xmlreader.f90 diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index 39ddf39e9..c5d01441d 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -36,6 +36,7 @@ cmfd_execute.o: tally.o cmfd_header.o: constants.o cmfd_input.o: cmfd_message_passing.o +cmfd_input.o: constants.o cmfd_input.o: error.o cmfd_input.o: global.o cmfd_input.o: mesh_header.o @@ -44,7 +45,7 @@ cmfd_input.o: string.o cmfd_input.o: tally.o cmfd_input.o: tally_header.o cmfd_input.o: tally_initialize.o -cmfd_input.o: templates/cmfd_t.o +cmfd_input.o: xml_interface.o cmfd_jacobian_operator.o: cmfd_loss_operator.o cmfd_jacobian_operator.o: cmfd_prod_operator.o @@ -217,12 +218,6 @@ input_xml.o: random_lcg.o input_xml.o: string.o input_xml.o: tally_header.o input_xml.o: tally_initialize.o -input_xml.o: templates/cross_sections_t.o -input_xml.o: templates/geometry_t.o -input_xml.o: templates/materials_t.o -input_xml.o: templates/plots_t.o -input_xml.o: templates/settings_t.o -input_xml.o: templates/tallies_t.o input_xml.o: xml_interface.o interpolation.o: constants.o diff --git a/src/Makefile b/src/Makefile index 8d3646973..282922678 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,10 +1,6 @@ program = openmc prefix = /usr/local -templates = $(wildcard templates/*.o) -xml_fort = xml-fortran/xmlparse.o \ - xml-fortran/read_xml_primitives.o \ - xml-fortran/write_xml_primitives.o xml_lib = -Lxml/lib -lxml #=============================================================================== @@ -228,14 +224,11 @@ endif # Targets #=============================================================================== -all: xml xml-fortran $(program) +all: xml $(program) xml: cd xml; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" -xml-fortran: - cd xml-fortran; make MACHINE=$(MACHINE) F90=$(F90) F90FLAGS="$(F90FLAGS)" - cd templates; make F90=$(F90) F90FLAGS="$(F90FLAGS)" $(program): $(objects) - $(F90) $(objects) $(templates) $(xml_fort) $(xml_lib) $(LDFLAGS) -o $@ + $(F90) $(objects) $(xml_lib) $(LDFLAGS) -o $@ install: @install -D $(program) $(DESTDIR)$(prefix)/bin/$(program) @install -D utils/statepoint_cmp.py $(DESTDIR)$(prefix)/bin/statepoint_cmp @@ -252,8 +245,6 @@ uninstall: @rm $(DESTDIR)$(prefix)/share/doc/$(program)/copyright distclean: clean cd xml; make clean - cd xml-fortran; make clean - cd templates; make clean clean: @rm -f *.o *.mod $(program) neat: @@ -264,10 +255,10 @@ neat: #=============================================================================== .SUFFIXES: .F90 .o -.PHONY: all xml xml-fortran install uninstall clean neat distclean +.PHONY: all xml install uninstall clean neat distclean %.o: %.F90 - $(F90) $(F90FLAGS) -DGIT_SHA1="\"$(GIT_SHA1)\"" -Ixml-fortran -Itemplates -Ixml/include -c $< + $(F90) $(F90FLAGS) -DGIT_SHA1="\"$(GIT_SHA1)\"" -Ixml/include -c $< #=============================================================================== # Dependencies diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index df692c46e..26a7a3ae0 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -249,6 +249,7 @@ contains subroutine create_cmfd_tally(doc) + use constants, only: MAX_LINE_LEN use error, only: fatal_error, warning use global use mesh_header, only: StructuredMesh @@ -256,11 +257,11 @@ contains use tally, only: setup_active_cmfdtallies use tally_header, only: TallyObject, TallyFilter use tally_initialize, only: add_tallies - use xml_data_cmfd_t use xml_interface type(Node), pointer :: doc + character(MAX_LINE_LEN) :: temp_str integer :: i ! loop counter integer :: n ! size of arrays in mesh specification integer :: ng ! number of energy groups (default 1) @@ -393,8 +394,12 @@ contains t => cmfd_tallies(i) ! set reset property - call lower_case(reset_) - if (reset_ == 'true' .or. reset_ == '1') t % reset = .true. + if (check_for_node(doc, "reset")) then + call get_node_value(doc, "reset", temp_str) + call lower_case(temp_str) + if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & + t % reset = .true. + end if ! set up mesh filter n_filters = 1 diff --git a/src/templates/Makefile b/src/templates/Makefile deleted file mode 100644 index 232ae9609..000000000 --- a/src/templates/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -templates = $(wildcard *.xml) -objects = $(templates:.xml=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Add include for subdirectory - -override F90FLAGS += -I../xml-fortran - -# Ignore unused variables - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(objects) -clean: - @rm -f *.o *.mod *.out *.f90 - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .f90 .o .xml -.PHONY: all clean -.PRECIOUS: %.f90 - -%.f90: %.xml - ../xml-fortran/xmlreader $(basename $@) - -%.o: %.f90 - $(F90) $(F90FLAGS) -c $(basename $@).f90 diff --git a/src/templates/cmfd.rnc b/src/templates/cmfd.rnc deleted file mode 100644 index cbcb7709a..000000000 --- a/src/templates/cmfd.rnc +++ /dev/null @@ -1,58 +0,0 @@ -element cmfd { - element mesh { - (element dimension { list { xsd:int+ } } | - attribute dimension { list { xsd:int+ } }) & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - ( - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) | - (element width { list { xsd:double+ } } | - attribute width { list { xsd:double+ } }) - ) & - (element albedo { list { xsd:double+ } } | - attribute albedo { list { xsd:double+ } }) & - (element map { list { xsd:int+ } } | - attribute map { list { xsd:int+ } })? & - (element energy { list { xsd:double+ } } | - attribute energy { list { xsd:double+ } })? - } & - - element norm { xsd:double }? & - - element feedback { xsd:boolean }? & - - element n_cmfd_procs { xsd:int }? & - - element reset { xsd:boolean }? & - - element balance { xsd:boolean }? & - - element downscatter { xsd:boolean }? & - - element run_2grp { xsd:boolean }? & - - element solver { xsd:string }? & - - element snes_monitor { xsd:boolean }? & - - element ksp_monitor { xsd:boolean }? & - - element power_monitor { xsd:boolean }? & - - element write_balance { xsd:boolean }? & - - element write_matrices { xsd:boolean }? & - - element run_adjoint { xsd:boolean }? & - - element write_hdf5 { xsd:boolean }? & - - element begin { xsd:int }? & - - element inactive { xsd:boolean }? & - - element active_flush { xsd:int }? & - - element keff_tol { xsd:double }? -} diff --git a/src/templates/cmfd_t.xml b/src/templates/cmfd_t.xml deleted file mode 100644 index c9ae3c1fd..000000000 --- a/src/templates/cmfd_t.xml +++ /dev/null @@ -1,39 +0,0 @@ - - diff --git a/src/templates/cross_sections.rnc b/src/templates/cross_sections.rnc deleted file mode 100644 index e82bb986f..000000000 --- a/src/templates/cross_sections.rnc +++ /dev/null @@ -1,23 +0,0 @@ -element cross_sections { - element ace_table { - (element name { xsd:string { maxLength = "15" } } | - attribute name { xsd:string { maxLength = "15" } }) & - (element alias { xsd:string { maxLength = "15" } } | - attribute alias { xsd:string { maxLength = "15" } })? & - (element zaid { xsd:int } | attribute zaid { xsd:int }) & - (element metastable { xsd:int } | attribute metastable { xsd:int })? & - (element awr { xsd:double } | attribute awr { xsd:double }) & - (element temperature { xsd:double } | attribute temperature { xsd:double }) & - (element path { xsd:string { maxLength = "255" } } | - attribute path { xsd:string { maxLength = "255" } }) & - (element location { xsd:int } | attribute location { xsd:int })? - }* & - - element directory { xsd:string { maxLength = "255" } }? & - - element filetype { ( "ascii" | "binary" ) } & - - element record_length { xsd:int }? & - - element entries { xsd:int }? -} \ No newline at end of file diff --git a/src/templates/cross_sections_t.xml b/src/templates/cross_sections_t.xml deleted file mode 100644 index 0852b8504..000000000 --- a/src/templates/cross_sections_t.xml +++ /dev/null @@ -1,26 +0,0 @@ - - diff --git a/src/templates/geometry.rnc b/src/templates/geometry.rnc deleted file mode 100644 index b7975d05f..000000000 --- a/src/templates/geometry.rnc +++ /dev/null @@ -1,35 +0,0 @@ -element geometry { - element cell { - (element id { xsd:int } | attribute id { xsd:int }) & - (element universe { xsd:int } | attribute universe { xsd:int })? & - ( - (element fill { xsd:int } | attribute fill { xsd:int }) | - (element material { ( xsd:int | "void" ) } | - attribute material { ( xsd:int | "void" ) }) - ) & - (element surfaces { list { xsd:int+ } } | attribute surfaces { list { xsd:int+ } }) & - (element rotation { list { xsd:double+ } } | attribute rotation { list { xsd:double+ } })? & - (element translation { list { xsd:double+ } } | attribute translation { list { xsd:double+ } })? - }* - - & element surface { - (element id { xsd:int } | attribute id { xsd:int }) & - (element type { xsd:string { maxLength = "15" } } | - attribute type { xsd:string { maxLength = "15" } }) & - (element coeffs { list { xsd:double+ } } | attribute coeffs { list { xsd:double+ } }) & - (element boundary { ( "transmit" | "reflective" | "vacuum" ) } | - attribute boundary { ( "transmit" | "reflective" | "vacuum" ) })? - }* - - & element lattice { - (element id { xsd:int } | attribute id { xsd:int }) & - (element type { ( "rectangular" | "hexagonal" ) } | - attribute type { ( "rectangular" | "hexagonal" ) })? & - (element dimension { list { xsd:positiveInteger+ } } | - attribute dimension { list { xsd:positiveInteger+ } }) & - (element lower_left { list { xsd:double+ } } | attribute lower_left { list { xsd:double+ } }) & - (element width { list { xsd:double+ } } | attribute width { list { xsd:double+ } }) & - (element universes { list { xsd:int+ } } | attribute universes { list { xsd:int+ } }) & - (element outside { xsd:int } | attribute outside { xsd:int })? - }* -} diff --git a/src/templates/geometry_t.xml b/src/templates/geometry_t.xml deleted file mode 100644 index 539a23f6b..000000000 --- a/src/templates/geometry_t.xml +++ /dev/null @@ -1,39 +0,0 @@ - - diff --git a/src/templates/materials.rnc b/src/templates/materials.rnc deleted file mode 100644 index d497fa745..000000000 --- a/src/templates/materials.rnc +++ /dev/null @@ -1,42 +0,0 @@ -element materials { - element material { - (element id { xsd:int } | attribute id { xsd:int }) & - - element density { - (element value { xsd:double } | attribute value { xsd:double })? & - (element units { xsd:string { maxLength = "10" } } | - attribute units { xsd:string { maxLength = "10" } }) - } & - - element nuclide { - (element name { xsd:string { maxLength = "7" } } | - attribute name { xsd:string { maxLength = "7" } }) & - (element xs { xsd:string { maxLength = "3" } } | - attribute xs { xsd:string { maxLength = "3" } })? & - ( - (element ao { xsd:double } | attribute ao { xsd:double }) | - (element wo { xsd:double } | attribute wo { xsd:double }) - ) - }* & - - element element { - (element name { xsd:string { maxLength = "2" } } | - attribute name { xsd:string { maxLength = "2" } }) & - (element xs { xsd:string { maxLength = "3" } } | - attribute xs { xsd:string { maxLength = "3" } })? & - ( - (element ao { xsd:double } | attribute ao { xsd:double }) | - (element wo { xsd:double } | attribute wo { xsd:double }) - ) - }* & - - element sab { - (element name { xsd:string { maxLength = "7" } } | - attribute name { xsd:string { maxLength = "7" } }) & - (element xs { xsd:string { maxLength = "3" } } | - attribute xs { xsd:string { maxLength = "3" } })? - }* - }+ & - - element default_xs { xsd:string { maxLength = "3" } }? -} diff --git a/src/templates/materials_t.xml b/src/templates/materials_t.xml deleted file mode 100644 index 99950e035..000000000 --- a/src/templates/materials_t.xml +++ /dev/null @@ -1,44 +0,0 @@ - - diff --git a/src/templates/plots.rnc b/src/templates/plots.rnc deleted file mode 100644 index 3c29ba4e0..000000000 --- a/src/templates/plots.rnc +++ /dev/null @@ -1,31 +0,0 @@ -element plots { - element plot { - (element id { xsd:int } | attribute id { xsd:int })? & - (element filename { xsd:string { maxLength = "50" } } | - attribute filename { xsd:string { maxLength = "50" } })? & - (element type { "slice" } | attribute type { "slice" })? & - (element color { ( "cell" | "mat" | "material" ) } | - attribute color { ( "cell" | "mat" | "material" ) })? & - (element origin { list { xsd:double+ } } | - attribute origin { list { xsd:double+ } })? & - (element width { list { xsd:double+ } } | - attribute width { list { xsd:double+ } })? & - (element basis { ( "xy" | "yz" | "xz" ) } | - attribute basis { ( "xy" | "yz" | "xz" ) })? & - (element pixels { list { xsd:int+ } } | - attribute pixels { list { xsd:int+ } })? & - (element background { list { xsd:int+ } } | - attribute background { list { xsd:int+ } })? & - element col_spec { - (element id { xsd:int } | attribute id { xsd:int }) & - (element rgb { list { xsd:int+ } } | - attribute rgb { list { xsd:int+ } }) - }* & - element mask { - (element components { list { xsd:int+ } } | - attribute components { list { xsd:int+ } }) & - (element background { list { xsd:int+ } } | - attribute background { list { xsd:int+ } }) - }* - }* -} diff --git a/src/templates/plots_t.xml b/src/templates/plots_t.xml deleted file mode 100644 index b3b86fc44..000000000 --- a/src/templates/plots_t.xml +++ /dev/null @@ -1,32 +0,0 @@ - - diff --git a/src/templates/settings.rnc b/src/templates/settings.rnc deleted file mode 100644 index 47dd690bc..000000000 --- a/src/templates/settings.rnc +++ /dev/null @@ -1,112 +0,0 @@ -element settings { - element confidence_intervals { xsd:boolean }? & - - ( - element eigenvalue { - (element batches { xsd:positiveInteger } | - attribute batches { xsd:positiveInteger }) & - (element inactive { xsd:nonNegativeInteger } | - attribute inactive { xsd:nonNegativeInteger }) & - (element particles { xsd:positiveInteger } | - attribute particles { xsd:positiveInteger }) & - (element generations_per_batch { xsd:positiveInteger } | - attribute generations_per_batch { xsd:positiveInteger })? - } | - element fixed_source { - (element batches { xsd:positiveInteger } | - attribute batches { xsd:positiveInteger }) & - (element particles { xsd:positiveInteger } | - attribute particles { xsd:positiveInteger }) - } - ) & - - element cross_sections { xsd:string { maxLength = "255" } }? & - - element cutoff { - (element weight { xsd:double } | attribute weight { xsd:double })? & - (element weight_avg { xsd:double } | attribute weight_avg { xsd:double })? - }? & - - element energy_grid { ( "nuclide" | "union" | "lethargy" ) }? & - - element entropy { - (element dimension { list { xsd:int+ } } | - attribute dimension { list { xsd:int+ } })? & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) - }? & - - element no_reduce { xsd:boolean }? & - - element output { list { - ( "summary" | "cross_sections" | "tallies" )+ } }? & - - element output_path { xsd:string { maxLength = "255" } }? & - - element ptables { xsd:boolean }? & - - element run_cmfd { xsd:boolean }? & - - element seed { xsd:positiveInteger }? & - - element source { - element file { xsd:string { maxLength = "255" } }? & - element space { - (element type { xsd:string { maxLength = "16" } } | - attribute type { xsd:string { maxLength = "16" } }) & - (element length { xsd:int } | attribute length { xsd:int })? & - (element interpolation { xsd:string { maxLength = "10" } } | - attribute interplation { xsd:string { maxLength = "10" } })? & - (element parameters { list { xsd:double+ } } | - attribute parameters { list { xsd:double+ } })? - }? & - element angle { - (element type { xsd:string { maxLength = "16" } } | - attribute type { xsd:string { maxLength = "16" } }) & - (element length { xsd:int } | attribute length { xsd:int })? & - (element interpolation { xsd:string { maxLength = "10" } } | - attribute interplation { xsd:string { maxLength = "10" } })? & - (element parameters { list { xsd:double+ } } | - attribute parameters { list { xsd:double+ } })? - }? & - element energy { - (element type { xsd:string { maxLength = "16" } } | - attribute type { xsd:string { maxLength = "16" } }) & - (element length { xsd:int } | attribute length { xsd:int })? & - (element interpolation { xsd:string { maxLength = "10" } } | - attribute interplation { xsd:string { maxLength = "10" } })? & - (element parameters { list { xsd:double+ } } | - attribute parameters { list { xsd:double+ } })? - }? - }? & - - element state_point { - ( - (element batches { list { xsd:positiveInteger+ } } | - attribute batches { list { xsd:positiveInteger+ } }) | - (element interval { xsd:positiveInteger } | - attribute interval { xsd:positiveInteger }) - ) & - (element source_separate { xsd:boolean } | - attribute source_separate { xsd:boolean })? & - (element source_write { xsd:boolean } | - attribute source_write { xsd:boolean })? - }? & - - element survival_biasing { xsd:boolean }? & - - element trace { list { xsd:positiveInteger+ } }? & - - element verbosity { xsd:positiveInteger }? & - - element uniform_fs{ - (element dimension { list { xsd:positiveInteger+ } } | - attribute dimension { list { xsd:positiveInteger+ } }) & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) - }? -} diff --git a/src/templates/settings_t.xml b/src/templates/settings_t.xml deleted file mode 100644 index 3afa61745..000000000 --- a/src/templates/settings_t.xml +++ /dev/null @@ -1,68 +0,0 @@ - - diff --git a/src/templates/tallies.rnc b/src/templates/tallies.rnc deleted file mode 100644 index 06959276b..000000000 --- a/src/templates/tallies.rnc +++ /dev/null @@ -1,43 +0,0 @@ -element tallies { - element mesh { - (element id { xsd:int } | attribute id { xsd:int }) & - (element type { ( "rectangular" | "hexagonal" ) } | - attribute type { ( "rectangular" | "hexagonal" ) }) & - (element dimension { list { xsd:positiveInteger+ } } | - attribute dimension { list { xsd:positiveInteger+ } }) & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & - ( - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) | - (element width { list { xsd:double+ } } | - attribute width { list { xsd:double+ } }) - ) - }* & - - element tally { - (element id { xsd:int } | attribute id { xsd:int }) & - (element label { xsd:string { maxLength="52" } } | - attribute label { xsd:string { maxLength="52" } })? & - (element estimator { ( "analog" | "tracklength" ) } | - attribute estimator { ( "analog" | "tracklength" ) })? & - element filter { - (element type { ( "cell" | "cellborn" | "material" | "universe" | - "surface" | "mesh" | "energy" | "energyout" ) } | - attribute type { ( "cell" | "cellborn" | "material" | "universe" | - "surface" | "mesh" | "energy" | "energyout" ) }) & - (element bins { list { xsd:string { maxLength = "20" }+ } } | - attribute bins { list { xsd:string { maxLength = "20" }+ } }) & - (element groups { xsd:string { maxLength = "20" }+ } | - attribute groups { xsd:string { maxLength = "20" }+ })? - }* & - element nuclides { - list { xsd:string { maxLength = "12" }+ } - }? & - element scores { - list { xsd:string { maxLength = "20" }+ } - } - }* & - - element assume_separate { xsd:boolean }? -} diff --git a/src/templates/tallies_t.xml b/src/templates/tallies_t.xml deleted file mode 100644 index cf653968d..000000000 --- a/src/templates/tallies_t.xml +++ /dev/null @@ -1,37 +0,0 @@ - - diff --git a/src/utils/build_dependencies.py b/src/utils/build_dependencies.py index 3aa22c607..fb966d028 100755 --- a/src/utils/build_dependencies.py +++ b/src/utils/build_dependencies.py @@ -13,8 +13,6 @@ for src in glob.iglob('*.F90'): for name in d: if name in ['mpi','hdf5','h5lt','fox_dom']: continue - if name.startswith('xml_data_'): - name = name.replace('xml_data_', 'templates/') deps.add(name) if deps: dependencies[module] = sorted(list(deps)) diff --git a/src/xml-fortran/Makefile b/src/xml-fortran/Makefile deleted file mode 100644 index c0514533a..000000000 --- a/src/xml-fortran/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -reader = xmlreader -source = $(wildcard *.f90) -objects = $(source:.f90=.o) - -#=============================================================================== -# Compiler Options -#=============================================================================== - -# Ignore unused variables - -ifeq ($(MACHINE),bluegene) - override F90 = xlf2003 -endif - -ifeq ($(F90),ifort) - override F90FLAGS += -warn nounused -endif - -#=============================================================================== -# Targets -#=============================================================================== - -all: $(reader) -$(reader): $(objects) - $(F90) $(objects) -o $@ -clean: - @rm -f *.o *.mod $(reader) -neat: - @rm -f *.o *.mod - -#=============================================================================== -# Rules -#=============================================================================== - -.SUFFIXES: .f90 .o -.PHONY: all clean neat - -%.o: %.f90 - $(F90) $(F90FLAGS) -c $< - -#=============================================================================== -# Dependencies -#=============================================================================== - -read_xml_primitives.o: xmlparse.o -write_xml_primitives.o: xmlparse.o -xmlreader.o: xmlparse.o diff --git a/src/xml-fortran/read_from_buffer.inc b/src/xml-fortran/read_from_buffer.inc deleted file mode 100644 index 0cb6ee959..000000000 --- a/src/xml-fortran/read_from_buffer.inc +++ /dev/null @@ -1,79 +0,0 @@ -! Part of XML-Fortran library: -! -! $Id: read_from_buffer.inc,v 1.2 2006/03/26 19:05:48 arjenmarkus Exp $ -! - character(len=*), intent(in) :: buffer - integer, intent(inout) :: ierror - - integer :: n - integer :: i - integer :: step - integer :: ierr - ! - ! First allocate an array that is surely large enough - ! Note: - ! This is not completely failsafe: with list-directed - ! input you can also use repeat counts (10000*1.0 for - ! instance). - ! - allocate( work(len(buffer)/2+1) ) - - ! - ! NOTE: - ! This is not portable!! - ! - ! read( buffer, *, iostat = ierror ) (work(n), n=1,size(work)) - ! - ! So, use a different strategy: a binary search - ! First: establish that we have at least one item to read - ! Second: do the binary search - ! -! read( buffer, *, iostat = ierr ) work(1) -! if ( ierr /= 0 ) then -! n = 0 -! else - n = 1 - do while ( n <= size(work) ) - n = 2 * n - enddo - n = n / 2 - step = n / 2 -! step = n / 2 - - do while ( step > 0 ) - read( buffer, *, iostat = ierr ) (work(i), i = 1,n) - if ( ierr /= 0 ) then - ierror = ierr ! Store the error code for later use - n = n - step - else - n = n + step - endif - step = step / 2 - enddo -! endif - - ! - ! Then allocate an array of the actual size needed - ! and copy the data - ! - ! - if ( associated( var ) ) then - deallocate( var ) - endif - ! - ! One complication: we may have one too many - ! (consequence of the binary search) - ! - read( buffer, *, iostat = ierr ) (work(i), i = 1,n) - if ( ierr < 0 ) then - n = n - 1 - endif - - allocate( var(n) ) - var(1:n) = work(1:n) - deallocate( work ) - - if ( ierror .lt. 0 ) then - ierror = 0 - endif - diff --git a/src/xml-fortran/read_xml_array.inc b/src/xml-fortran/read_xml_array.inc deleted file mode 100644 index a88086e74..000000000 --- a/src/xml-fortran/read_xml_array.inc +++ /dev/null @@ -1,54 +0,0 @@ -! Part of XML-Fortran library: -! -! $Id: read_xml_array.inc,v 1.3 2007/02/26 20:33:38 arjenmarkus Exp $ -! - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - logical, intent(inout) :: has_var - - character(len=len(attribs(1,1))) :: buffer - integer :: idx - integer :: ierr - - ! - ! The big trick: - ! A string long enough to hold all data strings - ! - character(len=nodata*(len(data(1))+1)) :: bufferd - integer :: start - - ! - ! The value can be stored in an attribute values="..." or in - ! the data - ! - has_var = .false. - idx = xml_find_attrib( attribs, noattribs, 'values', buffer ) - if ( idx .gt. 0 ) then - call read_from_buffer( buffer, var, ierr ) - if ( buffer .ne. ' ' ) then - has_var = .true. - endif - else - bufferd = ' ' - start = 1 - do idx = 1,nodata - if ( data(idx) .ne. ' ' ) then - bufferd(start:) = data(idx) - start = start + len(data(idx)) + 1 - endif - enddo - call read_from_buffer( bufferd, var, ierr ) - if ( bufferd .ne. ' ' ) then - has_var = .true. - endif - endif - - if ( ierr .ne. 0 ) then - write(*,*) 'Error reading variable - tag = ', trim(tag) - has_var = .false. - endif diff --git a/src/xml-fortran/read_xml_primitives.f90 b/src/xml-fortran/read_xml_primitives.f90 deleted file mode 100644 index 1fb63d57b..000000000 --- a/src/xml-fortran/read_xml_primitives.f90 +++ /dev/null @@ -1,527 +0,0 @@ -! read_xml_prims.f90 - Read routines for primitive data -! -! $Id: read_xml_prims.f90,v 1.7 2007/12/07 10:38:41 arjenmarkus Exp $ -! -! Arjen Markus -! -! General information: -! This module is part of the XML-Fortran library. Its -! purpose is to help read individual items from an XML -! file into the variables that have been connected to -! the various tags. It is used by the code generated -! by the make_xml_reader program. -! -! Because the routines differ mostly by the type of the -! output variable, the body is included, to prevent -! too much repeated blocks of code with all the maintenance -! issues that causes. -! -module read_xml_primitives - use xmlparse - implicit none - - private :: read_from_buffer - private :: read_from_buffer_integers - private :: read_from_buffer_reals - private :: read_from_buffer_doubles - private :: read_from_buffer_logicals - private :: read_from_buffer_words - - interface read_from_buffer - module procedure read_from_buffer_integers - module procedure read_from_buffer_reals - module procedure read_from_buffer_doubles - module procedure read_from_buffer_logicals - module procedure read_from_buffer_words - end interface - -contains - -! skip_until_endtag -- -! Routine to read the XML file until the end tag is encountered -! -! Arguments: -! info The XML file data structure -! tag The tag in question -! attribs Array of attributes and their values -! data Array of strings, representing the data -! error Has an error occurred? -! -subroutine skip_until_endtag( info, tag, attribs, data, error ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - character(len=*), dimension(:,:), intent(inout) :: attribs - character(len=*), dimension(:), intent(inout) :: data - logical, intent(out) :: error - - integer :: noattribs - integer :: nodata - integer :: ierr - logical :: endtag - character(len=len(tag)) :: newtag - - error = .true. - do - call xml_get( info, newtag, endtag, attribs, noattribs, & - data, nodata ) - if ( xml_error(info) ) then - error = .true. - exit - endif - if ( endtag .and. newtag == tag ) then - exit - endif - enddo -end subroutine skip_until_endtag - -! read_xml_integer -- -! Routine to read a single integer from the parsed data -! -! Arguments: -! info XML parser structure -! tag The tag in question (error message only) -! endtag End tag found? (Dummy argument, actually) -! attribs Array of attributes and their values -! noattribs Number of attributes found -! data Array of strings, representing the data -! nodata Number of data strings -! var Variable to be filled -! has_var Has the variable been set? -! -subroutine read_xml_integer( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - integer, intent(inout) :: var - - include 'read_xml_scalar.inc' - -end subroutine read_xml_integer - -! read_xml_line -- -! Routine to read a single line of text from the parsed data -! -! Arguments: -! info XML parser structure -! tag The tag in question (error message only) -! endtag End tag found? (Dummy argument, actually) -! attribs Array of attributes and their values -! noattribs Number of attributes found -! data Array of strings, representing the data -! nodata Number of data strings -! var Variable to be filled -! has_var Has the variable been set? -! -subroutine read_xml_line( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - character(len=*), intent(inout) :: var - logical, intent(inout) :: has_var - - character(len=len(attribs(1,1))) :: buffer - integer :: idx - integer :: ierr - - ! - ! The value can be stored in an attribute value="..." or in - ! the data - ! - has_var = .false. - idx = xml_find_attrib( attribs, noattribs, 'value', buffer ) - if ( idx > 0 ) then - var = buffer - has_var = .true. - else - do idx = 1,nodata - if ( data(idx) /= ' ' ) then - var = data(idx) - has_var = .true. - exit - endif - enddo - endif -end subroutine read_xml_line - -! read_xml_real, ... -- -! See read_xml_integer for an explanation -! -subroutine read_xml_real( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - real, intent(inout) :: var - - include 'read_xml_scalar.inc' - -end subroutine read_xml_real - -subroutine read_xml_double( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - real(kind=kind(1.0d00)), intent(inout) :: var - - include 'read_xml_scalar.inc' - -end subroutine read_xml_double - -subroutine read_xml_logical( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - logical, intent(inout) :: var - - include 'read_xml_scalar.inc' - -end subroutine read_xml_logical - -subroutine read_xml_word( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - character(len=*), intent(inout) :: var - - include 'read_xml_word.inc' - -end subroutine read_xml_word - -! read_xml_integer_array -- -! Routine to read a one-dimensional integer array from the parsed -! ata -! -! Arguments: -! info XML parser structure -! tag The tag in question (error message only) -! endtag End tag found? (Dummy argument, actually) -! attribs Array of attributes and their values -! noattribs Number of attributes found -! data Array of strings, representing the data -! nodata Number of data strings -! var Variable to be filled -! has_var Has the variable been set? -! -subroutine read_xml_integer_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - integer, dimension(:), pointer :: var - - include 'read_xml_array.inc' - -end subroutine read_xml_integer_array - -! read_xml_line_array -- -! Routine to read an array of lines of text from the parsed data -! -! Arguments: -! info XML parser structure -! tag The tag in question (error message only) -! attribs Array of attributes and their values -! noattribs Number of attributes found -! data Array of strings, representing the data -! nodata Number of data strings -! var Variable to be filled -! has_var Has the variable been set? -! -subroutine read_xml_line_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - character(len=*), dimension(:), pointer :: var - logical, intent(inout) :: has_var - - character(len=len(attribs(1,1))) :: buffer - integer :: idx - integer :: idxv - integer :: ierr - logical :: started - - ! - ! The value can be stored in an attribute values="..." or in - ! the data - ! - has_var = .false. - idx = xml_find_attrib( attribs, noattribs, 'values', buffer ) - if ( idx > 0 ) then - allocate( var(1:1) ) - var(1) = buffer - if ( buffer /= ' ' ) then - has_var = .true. - endif - else - idxv = 0 - started = .false. - do idx = 1,nodata - if ( data(idx) /= ' ' .or. started ) then - if ( .not. started ) then - allocate( var(1:nodata-idx+1) ) - started = .true. - endif - idxv = idxv + 1 - var(idxv) = data(idx) - endif - enddo - if ( started ) then - has_var = .true. - endif - endif -end subroutine read_xml_line_array - -! read_xml_real_array, ... -- -! See read_xml_integer_array for an explanation -! -subroutine read_xml_real_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - real, dimension(:), pointer :: var - - include 'read_xml_array.inc' - -end subroutine read_xml_real_array - -subroutine read_xml_double_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - real(kind=kind(1.0d00)), dimension(:), pointer :: var - - include 'read_xml_array.inc' - -end subroutine read_xml_double_array - -subroutine read_xml_logical_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - logical, dimension(:), pointer :: var - - include 'read_xml_array.inc' - -end subroutine read_xml_logical_array - -subroutine read_xml_word_array( info, tag, endtag, attribs, noattribs, data, & - nodata, var, has_var ) - character(len=*), dimension(:), pointer :: var - - include 'read_xml_array.inc' - -end subroutine read_xml_word_array - -! read_from_buffer_integers -- -! Routine to read all integers from a long string -! -! Arguments: -! buffer String containing the data -! var Variable to be filled -! ierror Error flag -! -subroutine read_from_buffer_integers( buffer, var, ierror ) - integer, dimension(:), pointer :: var - integer, dimension(:), pointer :: work - - include 'read_from_buffer.inc' - -end subroutine read_from_buffer_integers - -! read_xml_from_buffer_reals, ... - -! See read_xml_from_buffer_integers for an explanation -! -subroutine read_from_buffer_reals( buffer, var, ierror ) - real, dimension(:), pointer :: var - real, dimension(:), pointer :: work - - include 'read_from_buffer.inc' - -end subroutine read_from_buffer_reals - -subroutine read_from_buffer_doubles( buffer, var, ierror ) - real(kind=kind(1.0d00)), dimension(:), pointer :: var - real(kind=kind(1.0d00)), dimension(:), pointer :: work - - include 'read_from_buffer.inc' - -end subroutine read_from_buffer_doubles - -subroutine read_from_buffer_logicals( buffer, var, ierror ) - logical, dimension(:), pointer :: var - logical, dimension(:), pointer :: work - - include 'read_from_buffer.inc' - -end subroutine read_from_buffer_logicals - -subroutine read_from_buffer_words( buffer, var, ierror ) - character(len=*), dimension(:), pointer :: var - character(len=len(var)), dimension(:), pointer :: work - - include 'read_from_buffer.inc' - -end subroutine read_from_buffer_words - -! read_xml_word_1dim, ... - -! Read an array of "words" (or ...) but from different elements -! -subroutine read_xml_integer_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - integer, dimension(:), pointer :: var - logical, intent(inout) :: has_var - - integer,dimension(:), pointer :: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_integer( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_integer_1dim - -subroutine read_xml_real_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - real, dimension(:), pointer :: var - logical, intent(inout) :: has_var - - real, dimension(:), pointer :: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_real( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_real_1dim - -subroutine read_xml_double_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - real(kind=kind(1.0d00)), dimension(:), pointer:: var - logical, intent(inout) :: has_var - - real(kind=kind(1.0d00)), dimension(:), pointer:: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_double( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_double_1dim - -subroutine read_xml_logical_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - logical, dimension(:), pointer :: var - logical, intent(inout) :: has_var - - logical, dimension(:), pointer :: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_logical( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_logical_1dim - -subroutine read_xml_word_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - character(len=*), dimension(:), pointer :: var - logical, intent(inout) :: has_var - - character(len=len(var)),dimension(:), pointer :: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_word( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_word_1dim - -subroutine read_xml_line_1dim( info, tag, endtag, attribs, noattribs, data, nodata, & - var, has_var ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - character(len=*), dimension(:), pointer :: var - logical, intent(inout) :: has_var - - character(len=len(var)),dimension(:), pointer :: newvar - character(len=len(attribs(1,1))) :: buffer - integer :: newsize - integer :: ierr - - newsize = size(var) + 1 - allocate( newvar(1:newsize) ) - newvar(1:newsize-1) = var - deallocate( var ) - var => newvar - - call read_xml_line( info, tag, endtag, attribs, noattribs, data, nodata, & - var(newsize), has_var ) - -end subroutine read_xml_line_1dim - - -end module read_xml_primitives diff --git a/src/xml-fortran/read_xml_scalar.inc b/src/xml-fortran/read_xml_scalar.inc deleted file mode 100644 index b28ba3c7a..000000000 --- a/src/xml-fortran/read_xml_scalar.inc +++ /dev/null @@ -1,40 +0,0 @@ -! Part of XML-Fortran library: -! -! $Id: read_xml_scalar.inc,v 1.3 2007/02/26 20:33:38 arjenmarkus Exp $ -! - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - logical, intent(inout) :: has_var - - character(len=len(attribs(1,1))) :: buffer - integer :: idx - integer :: ierr - - ! - ! The value can be stored in an attribute value="..." or in - ! the data - ! - has_var = .false. - idx = xml_find_attrib( attribs, noattribs, 'value', buffer ) - if ( idx .gt. 0 ) then - read( buffer, *, iostat=ierr ) var - has_var = .true. - else - do idx = 1,nodata - if ( data(idx) .ne. ' ' ) then - read( data(idx), *, iostat=ierr ) var - has_var = .true. - exit - endif - enddo - endif - - if ( ierr .ne. 0 ) then - write(*,*) 'Error reading variable - tag = ', trim(tag) - has_var = .false. - endif diff --git a/src/xml-fortran/read_xml_word.inc b/src/xml-fortran/read_xml_word.inc deleted file mode 100644 index 4c1848dcf..000000000 --- a/src/xml-fortran/read_xml_word.inc +++ /dev/null @@ -1,38 +0,0 @@ -! Part of XML-Fortran library: -! - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - logical, intent(inout) :: endtag - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - character(len=*), dimension(:), intent(in) :: data - integer, intent(in) :: nodata - logical, intent(inout) :: has_var - - character(len=len(attribs(1,1))) :: buffer - integer :: idx - integer :: ierr - - ! - ! The value can be stored in an attribute value="..." or in - ! the data - ! - has_var = .false. - idx = xml_find_attrib( attribs, noattribs, 'value', buffer ) - if ( idx .gt. 0 ) then - read( buffer, *, iostat=ierr ) var - has_var = .true. - else - do idx = 1,nodata - if ( data(idx) .ne. ' ' ) then - read( data(idx), '(A)', iostat=ierr ) var - has_var = .true. - exit - endif - enddo - endif - - if ( ierr .ne. 0 ) then - write(*,*) 'Error reading variable - tag = ', trim(tag) - has_var = .false. - endif diff --git a/src/xml-fortran/write_xml_primitives.f90 b/src/xml-fortran/write_xml_primitives.f90 deleted file mode 100644 index 7d2cf90ba..000000000 --- a/src/xml-fortran/write_xml_primitives.f90 +++ /dev/null @@ -1,489 +0,0 @@ -! write_xml_prims.f90 - Write routines for primitive data -! -! $Id: write_xml_prims.f90,v 1.2 2007/12/27 05:13:59 arjenmarkus Exp $ -! -! Arjen Markus -! -! General information: -! This module is part of the XML-Fortran library. Its -! purpose is to write individual items to an XML -! file using the right tag. It is used by the code generated -! by the make_xml_reader program. -! -module write_xml_primitives - use xmlparse - implicit none - -! interface write_to_xml -! module procedure write_to_xml_integers -! module procedure write_to_xml_reals -! module procedure write_to_xml_doubles -! module procedure write_to_xml_logicals -! module procedure write_to_xml_words -! end interface - interface write_to_xml_word - module procedure write_to_xml_string - end interface - interface write_to_xml_line - module procedure write_to_xml_string - end interface - -contains - -! write_to_xml_integer -- -! Routine to write a single integer to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_integer( info, tag, indent, value ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - integer, intent(in) :: value - - character(len=100) :: indentation - - indentation = ' ' - write( info%lun, '(4a,i0,3a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>', value, '' - -end subroutine write_to_xml_integer - -! write_to_xml_integer_1dim -- -! Routine to write an array of integers to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! values Values to be written -! -subroutine write_to_xml_integer_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - integer, dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_integer( info, tag, indent, values(i) ) - enddo - -end subroutine write_to_xml_integer_1dim - -! write_to_xml_real -- -! Routine to write a single real value (single precision) to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_real( info, tag, indent, value ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real, intent(in) :: value - - character(len=100) :: indentation - character(len=12) :: buffer - - indentation = ' ' - write( buffer, '(1pg12.4)' ) value - write( info%lun, '(8a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>', trim(adjustl(buffer)), '' - -end subroutine write_to_xml_real - -! write_to_xml_real_1dim -- -! Routine to write an array of reals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! values Values to be written -! -subroutine write_to_xml_real_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real, dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_real( info, tag, indent, values(i) ) - enddo - -end subroutine write_to_xml_real_1dim - -! write_to_xml_double -- -! Routine to write one real value (double precision) to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_double( info, tag, indent, value ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real(kind=kind(1.0d0)), intent(in) :: value - - character(len=100) :: indentation - character(len=16) :: buffer - - indentation = ' ' - write( buffer, '(1pg16.7)' ) value - write( info%lun, '(8a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>', trim(adjustl(buffer)), '' - -end subroutine write_to_xml_double - -! write_to_xml_double_1dim -- -! Routine to write an array of double precision reals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! values Values to be written -! -subroutine write_to_xml_double_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real(kind=kind(1.0d00)), dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_double( info, tag, indent, values(i) ) - enddo - -end subroutine write_to_xml_double_1dim - -! write_to_xml_string -- -! Routine to write one string to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_string( info, tag, indent, value ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - character(len=*), intent(in) :: value - - character(len=100) :: indentation - - ! - ! NOTE: No guards against <, >, & and " yet! - ! NOTE: difference needed between words and lines? - ! - indentation = ' ' - write( info%lun, '(8a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>', trim(value), '' - -end subroutine write_to_xml_string - -! write_to_xml_word_1dim -- -! Routine to write an array of single words to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_word_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - character(len=*), dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_string( info, tag, indent, values(i) ) - enddo -end subroutine write_to_xml_word_1dim - -! write_to_xml_string_1dim -- -! Routine to write an array of strings to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! values Values to be written -! -subroutine write_to_xml_string_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - character(len=*), dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_string( info, tag, indent, values(i) ) - enddo - -end subroutine write_to_xml_string_1dim - -! write_to_xml_logical -- -! Routine to write one logical to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! value Value to be written -! -subroutine write_to_xml_logical( info, tag, indent, value ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - logical, intent(in) :: value - - character(len=100) :: indentation - - indentation = ' ' - if ( value ) then - write( info%lun, '(8a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>true' - else - write( info%lun, '(8a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>false' - endif - -end subroutine write_to_xml_logical - -! write_to_xml_logical_1dim -- -! Routine to write an array of logicals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! values Values to be written -! -subroutine write_to_xml_logical_1dim( info, tag, indent, values ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - logical, dimension(:), intent(in) :: values - - integer :: i - - do i = 1,size(values) - call write_to_xml_logical( info, tag, indent, values(i) ) - enddo - -end subroutine write_to_xml_logical_1dim - -! write_to_xml_integer_array -- -! Routine to write an array of integers to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_integer_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - integer, dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i, i2, j - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array),10 - i2 = min( i + 9, size(array) ) - write( info%lun, '(a,10i12)' ) indentation(1:min(indent+4,100)), & - ( array(j) ,j = i,i2 ) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_integer_array - -! write_to_xml_real_array -- -! Routine to write an array of single precision reals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_real_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real, dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i, i2, j - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array),10 - i2 = min( i + 9, size(array) ) - write( info%lun, '(a,10g12.4)' ) indentation(1:min(indent+4,100)), & - ( array(j) ,j = i,i2 ) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_real_array - -! write_to_xml_double_array -- -! Routine to write an array of double precision reals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_double_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - real(kind=kind(1.0d0)), dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i, i2, j - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array),5 - i2 = min( i + 4, size(array) ) - write( info%lun, '(a,5g20.7)' ) indentation(1:min(indent+4,100)), & - ( array(j) ,j = i,i2 ) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_double_array - -! write_to_xml_logical_array -- -! Routine to write an array of logicals to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_logical_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - logical, dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i, i2, j - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array),10 - i2 = min( i + 9, size(array) ) - write( info%lun, '(a,10a)' ) indentation(1:min(indent+4,100)), & - ( merge('true ', 'false ', array(j)) ,j = i,i2 ) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_logical_array - -! write_to_xml_word_array -- -! Routine to write an array of words to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_word_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - character(len=*), dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i, i2, j - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array),10 - i2 = min( i + 9, size(array) ) - write( info%lun, '(a,20a)' ) indentation(1:min(indent+4,100)), & - ( trim(array(j)) , ' ' ,j = i,i2 ) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_word_array - -! write_to_xml_line_array -- -! Routine to write an array of lines to the XML file -! -! Arguments: -! info XML parser structure -! tag The tag in question -! indent Number of spaces for indentation -! array Values to be written -! -subroutine write_to_xml_line_array( info, tag, indent, array ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: tag - integer, intent(in) :: indent - logical, dimension(:), intent(in) :: array - - character(len=100) :: indentation - integer :: i - - indentation = ' ' - - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '<', trim(tag), '>' - do i = 1,size(array) - write( info%lun, '(a)' ) indentation(1:min(indent+4,100)), & - array(i) - enddo - write( info%lun, '(4a)' ) indentation(1:min(indent,100)), & - '' - -end subroutine write_to_xml_line_array - -end module write_xml_primitives diff --git a/src/xml-fortran/xml-fortran.LICENSE b/src/xml-fortran/xml-fortran.LICENSE deleted file mode 100644 index cd0ba2912..000000000 --- a/src/xml-fortran/xml-fortran.LICENSE +++ /dev/null @@ -1,37 +0,0 @@ -Note: The official xml-fortran repository on sourceforge - only lists the license as "BSD License". It -is assumed that this refers to the 3-clause BSD license ("modified" or "new") -that is shown below. - -The license for xml-fortran covers all source code in the xml-fortran/ -directory. - -================================================================================ - -Copyright (c) 2008 Arjen Markus -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of Arjen Markus nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL ARJEN MARKUS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/xml-fortran/xmlparse.f90 b/src/xml-fortran/xmlparse.f90 deleted file mode 100644 index eb522078a..000000000 --- a/src/xml-fortran/xmlparse.f90 +++ /dev/null @@ -1,1076 +0,0 @@ -!=============================================================================== -! XMLPARSE - Simple, limited XML parser in Fortran -! -! General information: -! The module reads XML files by: -! - Identifying the tag and all attributes and data belonging -! to the tag. -! - Returning to the calling subprogram to let it take care of -! the tag, attributes and data. -! - If the tag is actually an ending tag, then this is flagged -! too. -! - Handling all the data is left to the calling subprogram, -! the module merely facilitates in the parsing. -! -! Note: -! The module in its current version has a number of limitations: -! - It does not handle escape sequences (like >. to signify -! a ">" sign) -! - It does not handle tags with attributes that are spread -! over more than one line -! - The maximum length of a line is 1000 characters -! - It may report too many lines of data (empty lines) -! - No DOM support nor support for an object tree -! - It is probably not very robust in detecting malformed XML files -! -! Some questions: -! - What to do with leading blanks? -! -! Update - several ideas: -! - Introduce at least two options (via xml_options): -! - ignore_whitespace - remove leading blanks and leading and trailing -! empty lines from the PCDATA -! - no_data_truncation - consider truncation of data (more -! attributes or lines of character data than -! can be stored) a read error -! - Introduce convenience functions and subroutines: -! - xml_ok() - all is well, reading can continue -! - xml_data_trunc() - was there truncation of the data? -! - xml_find_attrib() - find an attribute by name -! -! Further ideas: -! - simple checking via a table: parent, tag, id, min, max -!=============================================================================== - -module xmlparse - - implicit none - - integer, parameter :: XML_BUFFER_LENGTH = 10000 - -!=============================================================================== -! XML_PARSE defines the data type that holds the parser information -!=============================================================================== - - type XML_PARSE - integer :: lun ! LU-number of the XML-file - integer :: level ! Indentation level (output) - integer :: lineno ! Line in file - logical :: ignore_whitespace ! Ignore leading blanks etc. - logical :: no_data_truncation ! Do not allow data truncation - logical :: too_many_attribs ! More attributes than could be stored? - logical :: too_many_data ! More lines of data than could be stored? - logical :: eof ! End of file? - logical :: error ! Invalid XML file or other error? - character(len=XML_BUFFER_LENGTH) :: line ! Buffer - end type XML_PARSE - -!=============================================================================== -! Global options -!=============================================================================== - - integer, parameter :: XML_STDOUT = -1 - integer, private :: report_lun_ = XML_STDOUT - logical, private :: report_errors_ = .false. - logical, private :: report_details_ = .false. - -!=============================================================================== -! Global data (the ampersand must come first) -!=============================================================================== - - character(len=10), dimension(2,3), save, private :: entities = & - reshape( (/ '& ', '&', & - '> ', '> ', & - '< ', '< ' /), (/2,3/) ) - -!=============================================================================== -! Auxiliary routines - private -!=============================================================================== - - private :: xml_compress_ - private :: xml_put_open_tag_ - private :: xml_put_element_ - private :: xml_put_close_tag_ - private :: xml_replace_entities_ - private :: xml_remove_tabs_ - -!=============================================================================== -! Interfaces to reporting routines -!=============================================================================== - - private :: xml_report_details_int_ - private :: xml_report_details_string_ - private :: xml_report_errors_int_ - private :: xml_report_errors_string_ - - interface xml_report_details - module procedure xml_report_details_int_ - module procedure xml_report_details_string_ - end interface - interface xml_report_errors - module procedure xml_report_errors_int_ - module procedure xml_report_errors_string_ - module procedure xml_report_errors_extern_ - end interface - -contains - -!=============================================================================== -! XML_REPORT_DETAILS_INT_ -- -! Routine to write a text with an integer value -! Arguments: -! text Text to be written -! int Integer value to be added -!=============================================================================== - -subroutine xml_report_details_int_( text, int ) - character(len=*), intent(in) :: text - integer, intent(in) :: int - - if ( report_details_ ) then - if ( report_lun_ == XML_STDOUT ) then - write(*,*) trim(text), int - else - write(report_lun_,*) trim(text), int - endif - endif -end subroutine xml_report_details_int_ - -!=============================================================================== -! XML_REPORT_DETAILS_STRING_ -- -! Routine to write a text with a string value -! Arguments: -! text Text to be written -! string String to be added -!=============================================================================== - -subroutine xml_report_details_string_( text, string ) - character(len=*), intent(in) :: text - character(len=*), intent(in) :: string - - if ( report_details_ ) then - if ( report_lun_ == XML_STDOUT ) then - write(*,*) trim(text), ' ', trim(string) - else - write(report_lun_,*) trim(text), ' ', trim(string) - endif - endif -end subroutine xml_report_details_string_ - -!=============================================================================== -! XML_REPORT_ERRORS_INT_ -- -! Routine to write an error message text with an integer value -! Arguments: -! text Text to be written -! int Integer value to be added -! lineno Line number in the file -!=============================================================================== - -subroutine xml_report_errors_int_( text, int, lineno ) - character(len=*), intent(in) :: text - integer, intent(in) :: int - integer, optional, intent(in) :: lineno - - if ( report_errors_ .or. report_details_ ) then - if ( report_lun_ == XML_STDOUT ) then - write(*,*) trim(text), int - if ( present(lineno) ) then - write(*,*) ' At or near line', lineno - endif - else - write(report_lun_,*) trim(text), int - if ( present(lineno) ) then - write(report_lun_,*) ' At or near line', lineno - endif - endif - endif -end subroutine xml_report_errors_int_ - -!=============================================================================== -! XML_REPORT_ERRORS_STRING_ -- -! Routine to write an error message text with a string value -! Arguments: -! text Text to be written -! string String to be added -! lineno Line number in the file -!=============================================================================== - -subroutine xml_report_errors_string_( text, string, lineno ) - character(len=*), intent(in) :: text - character(len=*), intent(in) :: string - integer, optional, intent(in) :: lineno - - if ( report_errors_ .or. report_details_ ) then - if ( report_lun_ == XML_STDOUT ) then - write(*,*) trim(text), ' ', trim(string) - if ( present(lineno) ) then - write(*,*) ' At or near line', lineno - endif - else - write(report_lun_,*) trim(text), ' ', trim(string) - if ( present(lineno) ) then - write(report_lun_,*) ' At or near line', lineno - endif - endif - endif -end subroutine xml_report_errors_string_ - -!=============================================================================== -! XML_REPORT_ERRORS_EXTERN_ -- -! Routine to write an error message text with a string value -! Arguments: -! info Structure holding information on the XML-file -! text Text to be written -! Note: -! This routine is meant for use by routines outside -! this module -!=============================================================================== - -subroutine xml_report_errors_extern_( info, text ) - type(XML_PARSE), intent(in) :: info - character(len=*), intent(in) :: text - - if ( report_errors_ .or. report_details_ ) then - if ( report_lun_ == XML_STDOUT ) then - write(*,*) trim(text), ' - at or near line', info%lineno - else - write(report_lun_,*) trim(text), ' - at or near line', info%lineno - endif - end if - -end subroutine xml_report_errors_extern_ - -!=============================================================================== -! XML_OPEN -- -! Routine to open an XML file for reading or writing -! Arguments: -! info Structure holding information on the XML-file -! fname Name of the file -! mustread The file will be read (.true.) or written (.false.) -!=============================================================================== - -subroutine xml_open( info, fname, mustread ) - character(len=*), intent(in) :: fname - logical, intent(in) :: mustread - type(XML_PARSE), intent(out) :: info - - integer :: i - integer :: k - integer :: kend - integer :: ierr - logical :: opend - logical :: exists - - info%lun = 10 - info%ignore_whitespace = .false. - info%no_data_truncation = .false. - info%too_many_attribs = .false. - info%too_many_data = .false. - info%eof = .false. - info%error = .false. - info%level = -1 - info%lineno = 0 - - do i = 10,99 - inquire( unit = i, opened = opend ) - if ( .not. opend ) then - info%lun = i - inquire( file = fname, exist = exists ) - if ( .not. exists .and. mustread ) then - call xml_report_errors( 'XML_OPEN: file does not exist:', trim(fname)) - info%lun = -1 - info%error = .true. - else - open( unit = info%lun, file = fname ) - call xml_report_details( 'XML_OPEN: opened file ', trim(fname) ) - call xml_report_details( 'at LU-number: ', info%lun ) - endif - exit - endif - enddo - if ( .not. info%error .and. mustread ) then - k = 1 - do while ( k >= 1 ) - read( info%lun, '(a)', iostat = ierr ) info%line - - ! If we encounter a blank line, skip it and read the next line - if (len_trim(info%line) == 0) cycle - - call xml_remove_tabs_(info%line) - if ( ierr == 0 ) then - info%line = adjustl( info%line ) - k = index( info%line, ' appears on a single line! - ! - if ( k >= 1 ) then - kend = index( info%line, '?>' ) - if ( kend <= 0 ) then - call xml_report_errors( 'XML_OPEN: error reading file with LU-number: ', info%lun ) - call xml_report_errors( 'Line starting with ""', ' ' ) - info%error = .true. - exit - endif - endif - else - call xml_report_errors( 'XML_OPEN: error reading file with LU-number: ', info%lun ) - call xml_report_errors( 'Possibly no line starting with "' - endif -end subroutine xml_open - -!=============================================================================== -! XML_CLOSE -- -! Routine to close an XML file -! Arguments: -! info Structure holding information on the XML-file -!=============================================================================== - -subroutine xml_close( info ) - type(XML_PARSE), intent(inout) :: info - - close( info%lun ) - - ! - ! Only clean up the LU-number, so that the calling program - ! can examine the last condition - ! - call xml_report_details( 'XML_CLOSE: Closing file with LU-number ', info%lun ) - info%lun = -1 -end subroutine xml_close - -!=============================================================================== -! XML_GET -- -! Routine to get the next bit of information from an XML file -! Arguments: -! info Structure holding information on the XML-file -! tag Tag that was encountered -! endtag Whether the end of the element was encountered -! attribs List of attribute-value pairs -! no_attribs Number of pairs in the list -! data Lines of character data found -! no_data Number of lines of character data -!=============================================================================== - -subroutine xml_get( info, tag, endtag, attribs, no_attribs, & - data, no_data ) - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(out) :: tag - logical, intent(out) :: endtag - character(len=*), intent(out), dimension(:,:) :: attribs - integer, intent(out) :: no_attribs - character(len=*), intent(out), dimension(:) :: data - integer, intent(out) :: no_data - - integer :: kspace - integer :: kend - integer :: kcend - integer :: keq - integer :: kfirst - integer :: ksecond - integer :: idxat - integer :: idxdat - integer :: ierr - logical :: close_bracket - logical :: comment_tag - character(len=XML_BUFFER_LENGTH) :: nextline - - ! - ! Initialise the output - ! - endtag = .false. - no_attribs = 0 - no_data = 0 - - info%too_many_attribs = .false. - info%too_many_data = .false. - - if ( info%lun < 0 ) then - call xml_report_details( 'XML_GET on closed file ', ' ' ) - return - endif - - ! - ! From the previous call or the call to xmlopen we have - ! the line that we need to parse already in memory: - ! - ! - comment_tag = .false. - close_bracket = .false. - kspace = index( info%line, ' ' ) - kend = index( info%line, '>' ) - kcend = index( info%line, '-->' ) - do while ( kend <= 0 ) - read( info%lun, '(a)', iostat = ierr ) nextline - call xml_remove_tabs_(nextline) - info%lineno = info%lineno + 1 - - if ( ierr == 0 ) then - info%line = trim(info%line) // ' ' // adjustl(nextline) - else - info%error = .true. - call xml_report_errors( 'XML_GET - end of tag not found ', & - '(buffer too small?)', info%lineno ) - call xml_close( info ) - return - endif - kend = index( info%line, '>' ) - enddo - if ( kend > kspace ) then - kend = kspace - else if (info%line(1:4) == '' ) then - endtag = .true. - tag = info%line(4:kend-1) - else if ( info%line(1:2) == '' ) - if ( keq > kend ) keq = 0 ! Guard against multiple tags - ! with attributes on one line - - ! - ! No attributes any more? - ! - if ( keq < 1 ) then - kend = index( info%line, '/>' ) - if ( kend >= 1 ) then - kend = kend + 1 ! To go beyond the ">" character - endtag = .true. - else - kend = index( info%line, '>' ) - if ( kend < 1 ) then - call xml_report_errors( 'XML_GET - wrong ending of tag ', & - trim(info%line), info%lineno ) - info%error = .true. ! Wrong ending of line! - call xml_close( info ) - return - else - close_bracket = .true. - endif - endif - if ( kend >= 1 ) then - info%line = adjustl( info%line(kend+1:) ) - endif - exit - endif - - idxat = idxat + 1 - if ( idxat <= size(attribs,2) ) then - no_attribs = idxat - attribs(1,idxat) = adjustl(info%line(1:keq-1)) ! Use adjustl() to avoid - ! multiple spaces, etc - info%line = adjustl( info%line(keq+1:) ) - - ! - ! We have almost found the start of the attribute's value - ! - kfirst = index( info%line, '"' ) - if ( kfirst < 1 ) then - call xml_report_errors( 'XML_GET - malformed attribute-value pair: ', & - trim(info%line), info%lineno ) - info%error = .true. ! Wrong form of attribute-value pair - call xml_close( info ) - return - endif - - ksecond = index( info%line(kfirst+1:), '"' ) + kfirst - if ( ksecond < 1 ) then - call xml_report_errors( 'XML_GET - malformed attribute-value pair: ', & - trim(info%line), info%lineno ) - info%error = .true. ! Wrong form of attribute-value pair - call xml_close( info ) - return - endif - - attribs(2,idxat) = info%line(kfirst+1:ksecond-1) - info%line = adjustl( info%line(ksecond+1:) ) - endif - - if ( idxat > size(attribs,2) ) then - call xml_report_errors( 'XML_GET - more attributes than could be stored: ', & - trim(info%line), info%lineno ) - info%too_many_attribs = .true. - info%line = ' ' - exit - endif - enddo - - ! - ! Now read the data associated with the current tag - ! - all the way to the next "<" character - ! - ! To do: reduce the number of data lines - empty ones - ! at the end should not count. - ! - do - if ( comment_tag ) then - kend = index( info%line, '-->' ) - else - kend = index( info%line, '<' ) - endif - idxdat = idxdat + 1 - if ( idxdat <= size(data) ) then - no_data = idxdat - if ( kend >= 1 ) then - data(idxdat) = info%line(1:kend-1) - info%line = info%line(kend:) - else - data(idxdat) = info%line - endif - else - call xml_report_errors( 'XML_GET - more data lines than could be stored: ', & - trim(info%line), info%lineno ) - info%too_many_data = .true. - exit - endif - - ! - ! No more data? Otherwise, read on - ! - if ( kend >= 1 ) then - exit - else - read( info%lun, '(a)', iostat = ierr ) info%line - call xml_remove_tabs_(info%line) - info%lineno = info%lineno + 1 - - if ( ierr < 0 ) then - call xml_report_details( 'XML_GET - end of file found - LU-number: ', & - info%lun ) - info%eof = .true. - elseif ( ierr > 0 ) then - call xml_report_errors( 'XML_GET - error reading file with LU-number ', & - info%lun, info%lineno ) - info%error = .true. - endif - if ( ierr /= 0 ) then - exit - endif - endif - enddo - - ! - ! Compress the data? - ! - if ( info%ignore_whitespace ) then - call xml_compress_( data, no_data ) - endif - - ! - ! Replace the entities, if any - ! - call xml_replace_entities_( data, no_data ) - - call xml_report_details( 'XML_GET - number of attributes: ', no_attribs ) - call xml_report_details( 'XML_GET - number of data lines: ', no_data ) - -end subroutine xml_get - -!=============================================================================== -! XML_PUT -- -! Routine to write a tag with the associated data to an XML file -! Arguments: -! info Structure holding information on the XML-file -! tag Tag that was encountered -! endtag Whether the end of the element was encountered -! attribs List of attribute-value pairs -! no_attribs Number of pairs in the list -! data Lines of character data found -! no_data Number of lines of character data -! type Type of action: -! open - just the opening tag with attributes -! elem - complete element -! close - just the closing tag -!=============================================================================== - -subroutine xml_put(info, tag, attribs, no_attribs, & - data, no_data, type) - - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - character(len=*), intent(in), dimension(:,:) :: attribs - integer, intent(in) :: no_attribs - character(len=*), intent(in), dimension(:) :: data - integer, intent(in) :: no_data - character(len=*) :: type - - select case(type) - case('open') - call xml_put_open_tag_(info, tag, attribs, no_attribs) - case('elem') - call xml_put_element_(info, tag, attribs, no_attribs, & - data, no_data) - case('close') - call xml_put_close_tag_(info, tag) - end select - -end subroutine xml_put - -!=============================================================================== -! XML_PUT_OPEN_TAG_ -- -! Routine to write the opening tag with the attributes -! Arguments: -! info Structure holding information on the XML-file -! tag Tag that was encountered -! endtag Whether the end of the element was encountered -! attribs List of attribute-value pairs -! no_attribs Number of pairs in the list -! data Lines of character data found -! no_data Number of lines of character data -!=============================================================================== - -subroutine xml_put_open_tag_(info, tag, attribs, no_attribs) - - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - character(len=*), intent(in), dimension(:,:) :: attribs - integer, intent(in) :: no_attribs - - integer :: i - character(len=300), parameter :: indent = ' ' - - write( info%lun, '(3a)', advance = 'no' ) & - indent(1:3*info%level), '<', adjustl(tag) - do i=1,no_attribs - if (attribs(2,i)/='') then - write( info%lun, '(5a)', advance = 'no' ) & - ' ',trim(attribs(1,i)),'="', trim(attribs(2,i)),'"' - endif - enddo - write( info%lun, '(a)' ) '>' - info%level = info%level + 1 - -end subroutine xml_put_open_tag_ - -!=============================================================================== -! XML_PUT_ELEMENT_ -- -! Routine to write the complete element -! Arguments: -! info Structure holding information on the XML-file -! tag Tag that was encountered -! endtag Whether the end of the element was encountered -! attribs List of attribute-value pairs -! no_attribs Number of pairs in the list -! data Lines of character data found -! no_data Number of lines of character data -!=============================================================================== - -subroutine xml_put_element_(info, tag, attribs, no_attribs, & - data, no_data) - - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - character(len=*), intent(in), dimension(:,:) :: attribs - integer, intent(in) :: no_attribs - character(len=*), intent(in), dimension(:) :: data - integer, intent(in) :: no_data - - logical :: logic - character(len=1) :: aa - integer :: i, ii - - character(len=300), parameter :: indent = ' ' - - if ( (no_attribs==0 .and. no_data==0) ) then - return - else - logic = .true. - do ii = 1,no_attribs - logic = logic .and. (attribs(2,ii)=='') - enddo - do ii = 1,no_data - logic = logic .and. (data(ii)=='') - enddo - if ( logic ) then - return - else - write( info%lun, '(3a)', advance = 'no' ) & - indent(1:3*info%level), '<', adjustl(tag) - do i = 1,no_attribs - if (attribs(2,i)/='') then - write( info%lun, '(5a)', advance = 'no' ) & - ' ',trim(attribs(1,i)),'="', trim(attribs(2,i)),'"' - endif - enddo - if ( no_attribs>0 .and. no_data==0 ) then - aa='a' - elseif ( (no_attribs>0 .and. no_data>0) .or. & - (no_attribs==0 .and. no_data>0) ) then - aa='b' - else - write(*,*) no_attribs, no_data - endif - endif - endif - - select case(aa) - case('a') - write( info%lun, '(a)' ) '/>' - case('b') - write( info%lun, '(a)',advance='no' ) '>' - write( info%lun, '(2a)', advance='no') ( ' ', trim(data(i)), i=1,no_data ) - write( info%lun, '(4a)' ) ' ','' - end select - -end subroutine xml_put_element_ - -!=============================================================================== -! XML_PUT_CLOSE_TAG_ -- -! Routine to write the closing tag -! Arguments: -! info Structure holding information on the XML-file -! tag Tag that was encountered -! endtag Whether the end of the element was encountered -! attribs List of attribute-value pairs -! no_attribs Number of pairs in the list -! data Lines of character data found -! no_data Number of lines of character data -!=============================================================================== - -subroutine xml_put_close_tag_(info, tag) - - type(XML_PARSE), intent(inout) :: info - character(len=*), intent(in) :: tag - - character(len=300), parameter :: indent = ' ' - - info%level = info%level - 1 - write(info%lun, '(4a)') indent(1:3*info%level), '' - -end subroutine xml_put_close_tag_ - -!=============================================================================== -! XML_COMPRESS_ -- -! Routine to remove empty lines from the character data -! Arguments: -! data Lines of character data found -! no_data (Nett) number of lines of character data -!=============================================================================== - -subroutine xml_compress_( data, no_data ) - character(len=*), intent(inout), dimension(:) :: data - integer, intent(inout) :: no_data - - integer :: i - integer :: j - logical :: empty - - j = 0 - empty = .true. - do i = 1,no_data - if ( len_trim(data(i)) /= 0 .or. .not. empty ) then - j = j + 1 - data(j) = adjustl(data(i)) - empty = .false. - endif - enddo - - no_data = j - - do i = no_data,1,-1 - if ( len_trim(data(i)) /= 0 ) then - exit - else - no_data = no_data - 1 - endif - enddo - -end subroutine xml_compress_ - -!=============================================================================== -! XML_REPLACE_ENTITIES_ -- -! Routine to replace entities such as > by their -! proper character representation -! Arguments: -! data Lines of character data found -! no_data (Nett) number of lines of character data -!=============================================================================== - -subroutine xml_replace_entities_( data, no_data ) - character(len=*), intent(inout), dimension(:) :: data - integer, intent(inout) :: no_data - - integer :: i - integer :: j - integer :: j2 - integer :: k - integer :: pos - logical :: found - - do i = 1,no_data - j = 1 - do - do k = 1,size(entities,2) - found = .false. - pos = index( data(i)(j:), trim(entities(2,k)) ) - if ( pos > 0 ) then - found = .true. - j = j + pos - 1 - j2 = j + len_trim(entities(2,k)) - data(i)(j:) = trim(entities(1,k)) // data(i)(j2:) - j = j2 - endif - enddo - if ( .not. found ) exit - enddo - enddo - -end subroutine xml_replace_entities_ - -!=============================================================================== -! XML_OPTIONS -- -! Routine to handle the parser options -! Arguments: -! info Structure holding information on the XML-file -! ignore_whitespace Ignore whitespace (leading blanks, empty lines) or not -! no_data_truncation Consider truncation of strings an error or not -! report_lun LU-number for reporting information -! report_errors Write messages about errors or not -! report_details Write messages about all kinds of actions or not -!=============================================================================== - -subroutine xml_options( info, ignore_whitespace, no_data_truncation, & - report_lun, report_errors, & - report_details ) - type(XML_PARSE), intent(inout) :: info - logical, intent(in), optional :: ignore_whitespace - logical, intent(in), optional :: no_data_truncation - - integer, intent(in), optional :: report_lun - logical, intent(in), optional :: report_errors - logical, intent(in), optional :: report_details - - if ( present(ignore_whitespace) ) then - info%ignore_whitespace = ignore_whitespace - endif - if ( present(no_data_truncation) ) then - info%no_data_truncation = no_data_truncation - endif - if ( present(report_lun) ) then - report_lun_ = report_lun - endif - if ( present(report_errors) ) then - report_errors_ = report_errors - endif - if ( present(report_details) ) then - report_details_ = report_details - endif -end subroutine xml_options - -!=============================================================================== -! XML_OK -- -! Function that returns whether all was okay or not -! Arguments: -! info Structure holding information on the XML-file -! Returns: -! .true. if there was no error, .false. otherwise -!=============================================================================== - -logical function xml_ok( info ) - type(XML_PARSE), intent(in) :: info - - xml_ok = info%eof .or. info%error .or. & - ( info%no_data_truncation .and. & - ( info%too_many_attribs .or. info%too_many_data ) ) - xml_ok = .not. xml_ok -end function xml_ok - -!=============================================================================== -! XML_ERROR -- -! Function that returns whether there was an error -! Arguments: -! info Structure holding information on the XML-file -! Returns: -! .true. if there was an error, .false. if there was none -!=============================================================================== - -logical function xml_error( info ) - type(XML_PARSE), intent(in) :: info - - xml_error = info%error .or. & - ( info%no_data_truncation .and. & - ( info%too_many_attribs .or. info%too_many_data ) ) -end function xml_error - -!=============================================================================== -! XML_DATA_TRUNC -- -! Function that returns whether data were truncated or not -! Arguments: -! info Structure holding information on the XML-file -! Returns: -! .true. if data were truncated, .false. otherwise -!=============================================================================== - -logical function xml_data_trunc( info ) - type(XML_PARSE), intent(in) :: info - - xml_data_trunc = info%too_many_attribs .or. info%too_many_data -end function xml_data_trunc - -!=============================================================================== -! XML_FIND_ATTRIB -!=============================================================================== - -integer function xml_find_attrib( attribs, no_attribs, name, value ) - character(len=*), dimension(:,:) :: attribs - integer :: no_attribs - character(len=*) :: name - character(len=*) :: value - - integer :: i - - xml_find_attrib = -1 - do i = 1,no_attribs - if ( name == attribs(1,i) ) then - value = attribs(2,i) - xml_find_attrib = i - exit - endif - enddo - -end function xml_find_attrib - -!=============================================================================== -! XML_PROCESS -- -! Routine to read the XML file as a whole and distribute processing -! the contents over three user-defined subroutines -! Arguments: -! filename Name of the file to process -! attribs Array for holding the attributes -! data Array for holding the character data -! startfunc Subroutine to handle the start of elements -! datafunc Subroutine to handle the character data -! endfunc Subroutine to handle the end of elements -! error Indicates if there was an error or not -! Note: -! The routine is declared recursive to allow inclusion of XML files -! (common with XSD schemas). This extends to the auxiliary routines. -!=============================================================================== - -recursive & -subroutine xml_process( filename, attribs, data, startfunc, datafunc, endfunc, lunrep, error ) - character(len=*) :: filename - character(len=*), dimension(:,:) :: attribs - character(len=*), dimension(:) :: data - integer :: lunrep - logical :: error - - interface - recursive subroutine startfunc( tag, attribs, error ) - character(len=*) :: tag - character(len=*), dimension(:,:) :: attribs - logical :: error - end subroutine - end interface - - interface - recursive subroutine datafunc( tag, data, error ) - character(len=*) :: tag - character(len=*), dimension(:) :: data - logical :: error - end subroutine - end interface - - interface - recursive subroutine endfunc( tag, error ) - character(len=*) :: tag - logical :: error - end subroutine - end interface - - type(XML_PARSE) :: info - character(len=80) :: tag - logical :: endtag - integer :: noattribs - integer :: nodata - - call xml_options( info, report_lun = lunrep, report_details = .false. ) - call xml_open( info, filename, .true. ) - - error = .false. - do - call xml_get( info, tag, endtag, attribs, noattribs, data, nodata ) - if ( .not. xml_ok(info) ) then - exit - endif - - if ( xml_error(info) ) then - write(lunrep,*) 'Error reading XML file!' - error = .true. - exit - endif - - if ( .not. endtag .or. noattribs /= 0 ) then - call startfunc( tag, attribs(:,1:noattribs), error ) - if ( error ) exit - - call datafunc( tag, data(1:nodata), error ) - if ( error ) exit - endif - - if ( endtag ) then - call endfunc( tag, error ) - if ( error ) exit - endif - enddo - call xml_close( info ) -end subroutine xml_process - -!=============================================================================== -! XML_REMOVE_TABS_ -- -! Routine to change any horizontal tab characters to spaces when reading a -! new line of data -! Arguments: -! line Line of character data to modify -!=============================================================================== - -subroutine xml_remove_tabs_(line) - character(len=*), intent(inout) :: line - - integer :: i - - do i = 1, len_trim(line) - if (line(i:i) == achar(9)) then - line(i:i) = ' ' - end if - end do - -end subroutine xml_remove_tabs_ - -end module xmlparse diff --git a/src/xml-fortran/xmlreader.f90 b/src/xml-fortran/xmlreader.f90 deleted file mode 100644 index 390324b63..000000000 --- a/src/xml-fortran/xmlreader.f90 +++ /dev/null @@ -1,1241 +0,0 @@ -!=============================================================================== -! XMLREADER: -! Read an XML-file that contains a template for the data and the XML files -! that will be used in a program and generate a module with reading routines. -! -! TODO: -! - Private routines! -! - Error for unknown data types -! - Length for character items -!=============================================================================== - -program xmlreader - use XMLPARSE - implicit none - - character(len=60) :: fname - type(XML_PARSE) :: info - - character(len=80) :: tag - character(len=80) :: starttag - logical :: endtag - character(len=80), dimension(1:2,1:20) :: attribs - integer :: noattribs - character(len=1000), dimension(1:1000) :: data - integer :: nodata - integer :: i - integer :: j - integer, parameter :: notmps = 6 ! Number of temporary files needed! - integer :: ludef - integer :: ludeflt - integer :: luinit - integer :: lusubs - integer :: luprolog - integer :: lustart - integer :: luloop - integer :: luend - integer :: lumain - integer :: luwrite - integer :: luwritp - integer :: luwrv - integer :: argc - logical :: prolog_written - logical :: comp - logical :: error - logical :: begin_loop = .true. - logical :: begin_main_loop = .true. - logical :: begin_component = .false. - logical :: strict - logical :: global_type - logical :: dyn_strings - - character(len=32) :: root_name - character(len=32) :: global_name - character(len=32) :: typename - character(len=32), dimension(1:20) :: placeholder - integer :: no_placeholders - - character(len=50) :: declare - character(len=50), dimension(:,:), pointer :: types - character(len=50), dimension(:,:), pointer :: new_types - - ! TODO: arrays of integers etc, in addition to integer-arrays - see: word - integer, parameter :: notypes_predefined = 27 - character(len=50), dimension(1:4,1:notypes_predefined) :: predefined_types - integer :: notypes = notypes_predefined - data ((predefined_types(i,j) , i=1,4), j=1,notypes_predefined ) / & -'logical' ,' logical' , 'read_xml_logical', 'write_to_xml_logical', & -'logical-1dim' ,' logical, dimension(:), pointer' , 'read_xml_logical_1dim', 'write_to_xml_logical_1dim', & -'logical-array' ,' logical, dimension(:), pointer' , 'read_xml_logical_array', 'write_to_xml_logical_array', & -'logical-shape' ,' logical, dimension(SHAPE)' , 'read_xml_logical_array', 'write_to_xml_logical_array', & -'integer' ,' integer' , 'read_xml_integer', 'write_to_xml_integer', & -'integer-1dim' ,' integer, dimension(:), pointer' , 'read_xml_integer_1dim', 'write_to_xml_integer_1dim', & -'integer-array' ,' integer, dimension(:), pointer' , 'read_xml_integer_array', 'write_to_xml_integer_array', & -'integer-shape' ,' integer, dimension(SHAPE)' , 'read_xml_integer_array', 'write_to_xml_integer_array', & -'real' ,' real' , 'read_xml_real' , 'write_to_xml_real' , & -'real-1dim' ,' real, dimension(:), pointer' , 'read_xml_real_1dim', 'write_to_xml_real_1dim', & -'real-array' ,' real, dimension(:), pointer' , 'read_xml_real_array', 'write_to_xml_real_array', & -'real-shape' ,' real, dimension(SHAPE)' , 'read_xml_real_array', 'write_to_xml_real_array', & -'double' ,' real(kind=kind(1.0d0))' , 'read_xml_double', 'write_to_xml_double', & -'double-1dim' ,' real(kind=kind(1.0d0)), dimension(:), pointer' , & - 'read_xml_double_1dim', 'write_to_xml_double_1dim', & -'double-array' ,' real(kind=kind(1.0d0)), dimension(:), pointer' , & - 'read_xml_double_array', 'write_to_xml_double_array', & -'double-shape' ,' real(kind=kind(1.0d0)), dimension(SHAPE)' , & - 'read_xml_double_array', 'write_to_xml_double_array', & -'word' ,' character(len=?)' , 'read_xml_word', 'write_to_xml_word', & -'word-1dim' ,' character(len=?), dimension(:), pointer' , 'read_xml_word_1dim', 'write_to_xml_word_1dim', & -'word-array' ,' character(len=?), dimension(:), pointer' , 'read_xml_word_array', 'write_to_xml_word_array', & -'word-shape' ,' character(len=?), dimension(SHAPE)' , 'read_xml_word_array', 'write_to_xml_word_array', & -'line' ,' character(len=?)' , 'read_xml_line', 'write_to_xml_line', & -'line-1dim' ,' character(len=?), dimension(:), pointer' , 'read_xml_line_1dim', 'write_to_xml_line_1dim', & -'line-array' ,' character(len=?), dimension(:), pointer' , 'read_xml_line_array', 'write_to_xml_line_array', & -'line-shape' ,' character(len=?), dimension(SHAPE)' , 'read_xml_line_array', 'write_to_xml_line_array', & -'character' ,' character(len=?)' , 'read_xml_line', 'write_to_xml_line', & -'character-array',' character(len=?), dimension(:), pointer' , 'read_xml_line_array', 'write_to_xml_line_array', & -'character-shape',' character(len=?), dimension(SHAPE)' , 'read_xml_line_array', 'write_to_xml_line_array' / - - allocate( types(1:4,1:notypes) ) - types = predefined_types(:,1:notypes) - - ! - ! Read the global options file, if present - ! - strict = .false. - global_type = .false. - dyn_strings = .true. - call get_global_options( attribs, noattribs, strict, global_type, global_name, & - root_name, dyn_strings ) - - ! - ! Open the input file and read the name of the template. - ! Load the template into a tree and then generate it all - ! in stages - ! - argc = COMMAND_ARGUMENT_COUNT() - if (argc > 0) then - call GET_COMMAND_ARGUMENT(1, fname) - else - open(UNIT=10, FILE='xmlreader.inp') - read(UNIT=10, FMT='(a)') fname - close(UNIT=10) - end if - - open( 20, file = 'xmlreader.out' ) - call xml_options( info, report_lun = 20, report_details = .true. ) - - prolog_written = .false. - ! - ! Set the defaults - ! - global_type = .false. - global_name = fname - root_name = fname - - ludef = 21 - lusubs = 22 - luinit = 23 - luwritp = 24 - luwrv = luwritp - open( ludef, file = trim(fname)//'.f90' ) - open( lusubs, status = 'scratch' ) - open( luinit, status = 'scratch' ) - open( luwritp, status = 'scratch' ) - call open_tmp_files( 31 ) - - lumain = luloop - - ! Read the template file and act as we go along: - ! - write the declarations - ! - write the main reading routine - ! - write the individual reading routines - ! - the root element is needed because of the definition of XML files, - ! but we ignore it. - ! - call xml_open( info, trim(fname)//'.xml', .true. ) - - error = .false. - comp = .false. - no_placeholders = 0 - - call xml_get( info, starttag, endtag, attribs, noattribs, data, nodata ) - - do - call xml_get( info, tag, endtag, attribs, noattribs, data, nodata ) - write(20,*) 'tag: ',tag - write(20,*) 'attribs: ',noattribs - if ( noattribs > 0 ) then - write(20,'(4a)') ( ' ', trim(attribs(1,i)), ' = ', trim(attribs(2,i)), i=1,noattribs ) - endif - write(20,*) 'data: ',nodata - if ( nodata > 0 ) then - write(20,'(3a)') ( ' >', trim(data(i)), '<', i=1,nodata ) - endif - if ( xml_error(info) ) then - write(*,*) 'Error reading template file!' - !stop - exit - endif - - ! - ! When encountering the endtag, then close the - ! current definition - ! - if ( endtag .and. noattribs == 0 ) then - select case ( tag ) - case ( 'typedef' ) - call close_typedef( begin_component ) - case ( 'placeholder' ) - !if ( comp ) then - ! call close_placeholder - !endif - !comp = .false. - call close_placeholder - luwrv = luwritp - case default - ! - ! Have we found the end of the definition? - ! - if ( tag == starttag ) then - exit - endif - end select - - if ( xml_ok(info) ) then - cycle - else - exit - endif - - endif - - ! - ! Opening tags: dispatch on the actual tag - ! - select case( tag ) - case( 'options' ) - call set_options( attribs, noattribs, strict, global_type, & - global_name, root_name, dyn_strings ) - if ( .not. prolog_written ) then - prolog_written = .true. - call write_prolog - else - write(20,*) 'Options element should be the first child of the root element',& - 'Otherwise it has no effect!' - endif - - if ( strict ) then - write( lumain, '(a)' ) ' strict_ = .true.' - else - write( lumain, '(a)' ) ' strict_ = .false.' - endif - - case( 'comment', '!--' ) - ! Do nothing - - case( 'placeholder' ) - if ( .not. prolog_written ) then - prolog_written = .true. - call write_prolog - endif - if ( begin_loop .or. begin_main_loop ) then - begin_main_loop = .false. - call add_begin_loop( .true., .false. ) - endif - call add_placeholder(dyn_strings) - begin_component = .false. - luwrv = luwrite - - case( 'typedef' ) - if ( .not. prolog_written ) then - prolog_written = .true. - call write_prolog - endif - call add_typedef(dyn_strings) - - case( 'variable' ) - if ( .not. prolog_written ) then - prolog_written = .true. - call write_prolog - endif - if ( begin_loop .or. begin_main_loop ) then - begin_main_loop = .false. - call add_begin_loop( .true., begin_component ) - endif - call add_variable( component=comp ) - - case( 'component' ) - ! - ! Components of derived types are treated in much the - ! same way as ordinary variables - with one syntactic - ! difference - ! - if ( .not. prolog_written ) then - prolog_written = .true. - call write_prolog - endif - if ( begin_loop ) then - call add_begin_loop( .true., .true. ) - endif - call add_variable( component=.true. ) - begin_component = .true. - - case default - write(20,*) 'Unknown tag: ',trim(tag) - write(20,*) '-- terminating the program!' - stop - end select - end do - - ! - ! Now finish it all - ! - write( luend, '(a)' ) & - & ' if ( present(errout) ) errout = error', & - & ' call xml_close(info)', & - & 'end subroutine', & - & ' ' - write( luwritp, '(a)' ) & - & ' write(info%lun,''(a)'') ''''', & - & ' call xml_close(info)', & - & 'end subroutine', & - & ' ' - - call append_files( luprolog ) - call merge_files - - write( ludef, '(/,a)' ) & - & 'end subroutine', & - & 'end module' - - if ( error ) then - write(*,*) 'Errors found in the definition - please check!' - endif - stop -contains - -!=============================================================================== -! GET_GLOBAL_OPTIONS -- -! Routine to get the global options from the configuration file -! Arguments: -! strict Option to make the parser check for unknown tags -! global_type Option to generate an overall derived type -! global_name Name of that overall derived type (if requested) -! root_name Name of the root element of the XML file -! dyn_strings Whether to use dynamic strings or not -!=============================================================================== - -subroutine get_global_options( attribs, noattribs, strict, global_type, global_name, & - root_name, dyn_strings ) - character(len=*), dimension(:,:), intent(inout) :: attribs - integer, intent(inout) :: noattribs - logical, intent(inout) :: strict - logical, intent(inout) :: global_type - character(len=*), intent(inout) :: global_name - character(len=*), intent(inout) :: root_name - logical, intent(inout) :: dyn_strings - - character(len=20) :: tag - character(len=20), dimension(1) :: data - integer :: nodata - logical :: exists - logical :: endtag - type(XML_PARSE) :: info - - inquire( file = 'xmlreader.conf', exist = exists ) - if ( exists ) then - call xml_open( info, 'xmlreader.conf', .true. ) - do while ( xml_ok(info) ) - call xml_get( info, tag, endtag, attribs, noattribs, data, nodata ) - if ( tag == 'xmlreader' ) then - call set_options( attribs, noattribs, strict, global_type, & - global_name, root_name, dyn_strings ) - endif - enddo - call xml_close( info ) - endif -end subroutine get_global_options - -!=============================================================================== -! SET_OPTIONS -- -! Routine to set the options that influence the parser -! Arguments: -! attribs List of attributes -! noattribs Number of attributes -! strict Option to make the parser check for unknown tags -! global_type Option to generate an overall derived type -! global_name Name of that overall derived type (if requested) -! root_name Name of the root element of the XML file -! dyn_strings Whether to use dynamic strings or not -!=============================================================================== - -subroutine set_options( attribs, noattribs, strict, global_type, global_name, root_name, dyn_strings ) - character(len=*), dimension(:,:), intent(in) :: attribs - integer, intent(in) :: noattribs - logical, intent(inout) :: strict - logical, intent(inout) :: global_type - character(len=*), intent(inout) :: global_name - character(len=*), intent(inout) :: root_name - logical, intent(inout) :: dyn_strings - - integer :: i - - do i = 1,noattribs - select case (attribs(1,i)) - case ('strict') - if ( attribs(2,i) == 'yes' ) then - strict = .true. - else - strict = .false. - endif - case ('globaltype') - if ( attribs(2,i) == 'yes' ) then - global_type = .true. - else - global_type = .false. - endif - case ('globalname') - global_name = attribs(2,i) - case ('rootname') - root_name = attribs(2,i) - case ('dynamicstrings') - if ( attribs(2,i) == 'yes' ) then - dyn_strings = .true. - else - dyn_strings = .false. - endif - case default - write(20,*) 'Unknown option: ',trim(attribs(1,i)), ' - ignored' - end select - enddo -end subroutine set_options - -!=============================================================================== -! OPEN_TMP_FILES -- -! Routine to open the temporary files -! Arguments: -! lufirst First LU-number to use -!=============================================================================== - -subroutine open_tmp_files( lufirst ) - integer, intent(in) :: lufirst - - luprolog = lufirst - lustart = lufirst + 1 - luloop = lufirst + 2 - luend = lufirst + 3 - ludeflt = lufirst + 4 - luwrite = lufirst + 5 - open( luprolog, status = 'scratch' ) - open( lustart, status = 'scratch' ) - open( luloop, status = 'scratch' ) - open( luend, status = 'scratch' ) - open( ludeflt, status = 'scratch' ) - open( luwrite, status = 'scratch' ) -end subroutine open_tmp_files - -!=============================================================================== -! CLOSE_TMP_FILES -- -! Routine to close the temporary files -! Arguments: -! None -!=============================================================================== - -subroutine close_tmp_files - close( luprolog ) - close( lustart ) - close( luloop ) - close( luend ) - close( ludeflt ) - close( luwrite ) - - luprolog = luprolog - notmps - lustart = lustart - notmps - luloop = luloop - notmps - luend = luend - notmps - ludeflt = ludeflt - notmps - luwrite = luwrite - notmps -end subroutine close_tmp_files - -!=============================================================================== -! APPEND_TMP_FILES -- -! Routine to append the contents of the temporary files -! Arguments: -! lufirst First LU-number to use -!=============================================================================== - -subroutine append_files( lufirst ) - integer, intent(in) :: lufirst - - integer :: lu - integer :: io - character(len=120) :: line - - ! - ! If we have not written a subroutine yet, then - ! now is the time to close the overall initialisation - ! routine - ! -- no longer needed - ! - !if ( .not. contains ) then - ! write( lusubs, '(a,/)' ) 'end subroutine' - ! contains = .true. - !endif - - ! - ! Copy the contents of the scratch files - ! - do lu = lufirst,lufirst+notmps-1 - rewind( lu ) - do - read( lu, '(a)', iostat=io ) line - if ( io /= 0 ) exit - write( lusubs, '(a)' ) trim(line) - enddo - rewind( lu ) - enddo -end subroutine append_files - -!=============================================================================== -! MERGE_FILES -- -! Routine to merge all temporary files into the definite file -! Arguments: -! None -!=============================================================================== - -subroutine merge_files - - integer :: io - character(len=120) :: line - - ! - ! Copy the contents of the "subroutines" file - ! - rewind( lusubs ) - do - read( lusubs, '(a)', iostat=io ) line - if ( io /= 0 ) exit - write( ludef, '(a)' ) trim(line) - enddo - - ! - ! Copy the contents of the "write xml file subroutine" file - ! - rewind( luwritp ) - do - read( luwritp, '(a)', iostat=io ) line - if ( io /= 0 ) exit - write( ludef, '(a)' ) trim(line) - enddo - - ! - ! Copy the contents of the "initialisation subroutine" file - ! - rewind( luinit ) - do - read( luinit, '(a)', iostat=io ) line - if ( io /= 0 ) exit - write( ludef, '(a)' ) trim(line) - enddo -end subroutine merge_files - -!=============================================================================== -! WRITE_PROLOG -- -! Routine to write the beginning of the module -! Arguments: -! None -!=============================================================================== - -subroutine write_prolog - write( ludef, '(a)' ) & - & 'module xml_data_' // trim(fname), & - & ' use READ_XML_PRIMITIVES', & - & ' use WRITE_XML_PRIMITIVES', & - & ' use XMLPARSE', & - & ' implicit none', & - & ' save', & - & ' integer, private :: lurep_', & - & ' logical, private :: strict_' - - write( luprolog, '(a)' ) & - & 'subroutine read_xml_file_'//trim(fname)//'(fname, lurep, errout)' , & - & ' character(len=*), intent(in) :: fname' , & - & ' integer, intent(in), optional :: lurep' , & - & ' logical, intent(out), optional :: errout' , & - & ' ' , & - & ' type(XML_PARSE) :: info' , & - & ' logical :: error' , & - & ' character(len=80) :: tag' , & - & ' character(len=80) :: starttag' , & - & ' logical :: endtag' , & - & ' character(len=250), dimension(1:2,1:20) :: attribs' , & - & ' integer :: noattribs' , & - & ' character(len=1000), dimension(1:1000) :: data' , & - & ' integer :: nodata' - - write( lusubs, '(a)' ) & - & 'contains' - write( luinit, '(a)' ) & - & 'subroutine init_xml_file_'//trim(fname) - - write( luwritp, '(a)' ) & - & 'subroutine write_xml_file_'//trim(fname)//'(fname, lurep)' , & - & ' character(len=*), intent(in) :: fname' , & - & ' integer, intent(in), optional :: lurep' , & - & ' ' , & - & ' type(XML_PARSE) :: info' , & - & ' integer :: indent = 0', & - & ' ' , & - & ' call xml_open( info, fname, .false. )' , & - & ' call xml_options( info, report_errors=.true.)' , & - & ' if ( present(lurep) ) then' , & - & ' call xml_options( info, report_errors=.true.)' , & - & ' endif' , & - & ' write(info%lun,''(a)'') &' , & - & ' ''<' // trim(root_name) // '>''' - - write( lumain, '(a)' ) & - & ' ', & - & ' call init_xml_file_'//trim(fname), & - & ' call xml_open( info, fname, .true. )', & - & ' call xml_options( info, report_errors=.false., ignore_whitespace=.true.)', & - & ' lurep_ = 0', & - & ' if ( present(lurep) ) then', & - & ' lurep_ = lurep', & - & ' call xml_options( info, report_lun=lurep )', & - & ' endif', & - & ' do', & - & ' call xml_get( info, starttag, endtag, attribs, noattribs, &', & - & ' data, nodata)', & - & ' if ( starttag /= ''!--'' ) exit', & - & ' enddo', & - & ' if ( starttag /= "' // trim(root_name) // '" ) then', & - & ' call xml_report_errors( info, &', & - & ' ''XML-file should have root element "' // trim(root_name) // '"'')', & - & ' error = .true.', & - & ' call xml_close(info)', & - & ' return', & - & ' endif' - - call add_end_loop -end subroutine write_prolog - -!=============================================================================== -! ADD_BEGIN_LOOP -- -! Routine to write the start of the reading loop -! Arguments: -! checktag Whether code for checking the tag is required -! component Whether this is an ordinary variable or a component -! in a derived type -!=============================================================================== - -subroutine add_begin_loop( checktag, component ) - logical :: checktag - logical :: component - - if ( component ) then - write( luloop, '(a)' ) & - & ' call init_xml_type_'//trim(typename)//'(dvar)', & - & ' has_dvar = .true.' - endif - - begin_loop = .false. - - if ( component ) then - write( luloop, '(a)' ) & - & ' error = .false.' ,& - & ' att_ = 0' ,& - & ' noatt_ = noattribs+1' ,& - & ' endtag_org = endtag' ,& - & ' do', & - & ' if ( nodata /= 0 ) then' ,& - & ' noattribs = 0' ,& - & ' tag = starttag' ,& - & ' elseif ( att_ < noatt_ .and. noatt_ > 1 ) then' ,& - & ' att_ = att_ + 1' ,& - & ' if ( att_ <= noatt_-1 ) then' ,& - & ' tag = attribs(1,att_)' ,& - & ' data(1) = attribs(2,att_)' ,& - & ' noattribs = 0' ,& - & ' nodata = 1' ,& - & ' endtag = .false.' ,& - & ' else' ,& - & ' tag = starttag' ,& - & ' noattribs = 0' ,& - & ' nodata = 0' ,& - & ' endtag = .true.' ,& - & ' cycle' ,& - & ' endif' ,& - & ' else', & - & ' if ( endtag_org ) then', & - & ' return', & - & ' else', & - & ' call xml_get( info, tag, endtag, attribs, noattribs, data, nodata )' ,& - & ' if ( xml_error(info) ) then' ,& - & ' write(lurep_,*) ''Error reading input file!''',& - & ' error = .true.' ,& - & ' return' ,& - & ' endif' ,& - & ' endif' ,& - & ' endif' - else - write( luloop, '(a)' ) & - & ' error = .false.' ,& - & ' do', & - & ' call xml_get( info, tag, endtag, attribs, noattribs, data, nodata )' ,& - & ' if ( xml_error(info) ) then' ,& - & ' write(lurep_,*) ''Error reading input file!''' ,& - & ' error = .true.' ,& - & ' return' ,& - & ' endif' - endif - if ( checktag ) then - write( luloop, '(a)' ) & - & ' if ( endtag .and. tag == starttag ) then' ,& - & ' exit' ,& - & ' endif' - endif - write( luloop, '(a)' ) & - & ' if ( endtag .and. noattribs == 0 ) then' ,& - & ' if ( xml_ok(info) ) then' ,& - & ' cycle' ,& - & ' else' ,& - & ' exit' ,& - & ' endif' ,& - & ' endif' ,& - & ' select case( tag )' -end subroutine add_begin_loop - -!=============================================================================== -! ADD_END_LOOP -- -! Routine to write the end of the reading loop -! Arguments: -! None -!=============================================================================== - -subroutine add_end_loop - - write( luend, '(a)' ) & - & ' case (''comment'', ''!--'')' ,& - & ' ! Simply ignore', & - & ' case default' ,& - & ' if ( strict_ ) then', & - & ' error = .true.', & - & ' call xml_report_errors( info, &', & - & ' ''Unknown or wrongly placed tag: '' // trim(tag))',& - & ' endif' - - write( luend, '(a)' ) & - & ' end select' ,& - & ' nodata = 0' ,& - & ' if ( .not. xml_ok(info) ) exit' , & - & ' end do' -end subroutine add_end_loop - -!=============================================================================== -! ADD_VARIABLE -- -! Routine to write the definition of variables or components of -! derived types -! Arguments: -! component Whether this is an ordinary variable or a component -! in a derived type -!=============================================================================== - -subroutine add_variable( component ) - logical :: component - - integer :: idx1 - integer :: idx2 - integer :: idx3 - integer :: idx4 - integer :: idx5 - integer :: idx6 - integer :: idx7 - integer :: k - integer :: vdim - - character(len=32) :: varname - character(len=40) :: varcomp - character(len=40) :: varshape - character(len=100) :: vardefault - character(len=32) :: vartype - character(len=32) :: vartag - character(len=10) :: dim - character(len=10) :: strlength - character(len=32) :: initptr - - idx1 = xml_find_attrib( attribs, noattribs, 'name', varname ) - idx2 = xml_find_attrib( attribs, noattribs, 'type', vartype ) - strlength = "--" - idx5 = xml_find_attrib( attribs, noattribs, 'length', strlength ) - if ( idx1 <= 0 ) then - write( 20, * ) 'Variable/component found which has no name' - error = .true. - endif - if ( idx2 <= 0 ) then - write( 20, * ) 'Variable/component found which has no type - ',trim(varname) - error = .true. - else - dim = '--' - idx7 = xml_find_attrib( attribs, noattribs, 'dimension', dim ) - idx6 = xml_find_attrib( attribs, noattribs, 'shape', varshape ) - if ( idx7 >= 1 ) then - if ( dim == '1' ) then - idx3 = xml_find_attrib( types, notypes, vartype, declare ) - if ( idx3 > notypes_predefined ) then - vartype = trim(vartype) // '-array' - else - vartype = trim(vartype) // '-1dim' - endif - else - error = .true. - write(20,*) 'Dimension not supported: ',dim - endif - endif - if ( idx6 >= 1 ) then - vartype = trim(vartype) // '-shape' - vdim = 1 - if ( index(varshape, ',') > 0 ) then - vdim = 2 - endif - endif - - idx3 = xml_find_attrib( types, notypes, vartype, declare ) - if ( idx3 <= 0 ) then - write( 20, * ) & - 'Variable/component with unknown type - ',trim(varname) - error = .true. - endif - endif - - idx4 = xml_find_attrib( attribs, noattribs, 'default', vardefault ) - - if ( component ) then - varcomp = 'dvar%'//varname - else - varcomp = varname - endif - - idx1 = xml_find_attrib( attribs, noattribs, 'tag', vartag ) - if ( idx1 < 1 ) then - vartag = varname - endif - - if ( .not. error ) then - if ( index( declare, "pointer" ) > 0 ) then - initptr = " => null()" - else - initptr = "" - endif - - k = index( declare, 'SHAPE' ) - if ( k > 0 ) then - declare = declare(1:k-1) // trim(varshape) // declare(k+5:) - endif - - if ( index( declare, "?" ) <= 0 ) then - write( ludef, '(4a)' ) declare, ' :: ', trim(varname), trim(initptr) - else - if ( strlength == "--" ) then - strlength = "1" ! Hm, error is better? - endif - idx5 = index( declare, "?" ) - write( ludef, '(6a)' ) declare(1:idx5-1), trim(strlength), declare(idx5+1:), & - ' :: ', trim(varname), trim(initptr) - endif - - if ( idx6 > 0 ) then - k = index( types(2,idx3-1), '?' ) - if ( k <= 0 ) then - write( luprolog, '(3a)' ) types(2,idx3-1), ' :: ', 'p_'//trim(varname) - else - write( luprolog, '(6a)' ) types(2,idx3-1)(1:k-1), trim(strlength), & - types(2,idx3-1)(k+1:), ' :: ', 'p_'//trim(varname) - endif - endif - write( luprolog, '(3a)' ) types(2,1), ' :: ', 'has_'//trim(varname) - write( lustart, '(3a)' ) ' has_', varname, ' = .false.' - if ( dim /= '--' ) then - write( lustart, '(3a)' ) ' allocate(' // trim(varcomp), '(0))' - endif - write( luloop, '(a)' ) ' case('''//trim(vartag)//''')' - - if ( idx6 <= 0 ) then - write( luloop, '(a)' ) & - &' call '//trim(types(3,idx3))//'( &', & - &' info, tag, endtag, attribs, noattribs, data, nodata, &',& - &' ' // trim(varcomp) // ', has_'//trim(varname) // ' )' - else - write( luloop, '(a)' ) & - &' call '//trim(types(3,idx3))//'( &', & - &' info, tag, endtag, attribs, noattribs, data, nodata, &',& - &' p_' // trim(varname) // ', has_'//trim(varname) // ' )',& - &' if ( has_'//trim(varname) // ') then' - if ( vdim == 1 ) then - write( luloop, '(a)' ) & - &' if ( size('//trim(varcomp)//') <= size(p_'//trim(varname)//') ) then', & - &' '//trim(varcomp) // ' = p_'//trim(varname)//'(1:size('//trim(varcomp)//'))', & - &' else', & - &' '//trim(varcomp) // '(1:size(p_'//trim(varname)//')) = p_'//trim(varname), & - &' endif' - else - write( luloop, '(a)' ) & - &' if ( size(p_'//trim(varname)//') >= size('//trim(varcomp)//') ) then',& - &' '//trim(varcomp)//' = reshape(p_'//trim(varname)//', shape('//trim(varcomp)//'))',& - &' else',& - &' has_'//trim(varname)//' = .false.',& - &' call xml_report_errors(info, ''Incorrect number of values for '//trim(varname)//''')', & - &' endif' - endif - write( luloop, '(a)' ) & - &' deallocate( p_'//trim(varname)//' )', & - &' endif' - endif - if ( idx4 <= 0 ) then - write( luend, '(a)' ) & - &' if ( .not. has_'//trim(varname)//' ) then' - - if ( component ) then - write( luend, '(a)' ) & - &' has_dvar = .false.' - else - write( luend, '(a)' ) & - &' error = .true.' - endif - - write( luend, '(a)' ) & - &' call xml_report_errors(info, ''Missing data on '//trim(varname)//''')', & - &' endif' - else - ! - ! Note: the attribute value is supposed to have the quotes, if that - ! is relevant for the variable's type - ! - if ( component ) then - write( ludeflt, '(4a)' ) & - &' dvar%', trim(varname), ' = ', attribs(2,idx4) - else - write( luinit, '(4a)' ) & - &' ', trim(varname), ' = ', attribs(2,idx4) - endif - endif - ! - ! Write the component/variable - ! - if ( component ) then - write( luwrite, '(4a)' ) & - &' call '//trim(types(4,idx3))//'(', & - &' info, '''//trim(vartag)//''', indent+3, dvar%', trim(varname), ')' - else - write( luwrv, '(4a)' ) & - &' call '//trim(types(4,idx3))//'(', & - &' info, '''//trim(vartag)//''', indent+3, ', trim(varname), ')' - endif - endif - -end subroutine add_variable - -!=============================================================================== -! ADD_TYPEDEF -- -! Routine to write the definition and other code for a derived type -! Arguments: -! dyn_strings Whether dynamic string lengths are allowed -!=============================================================================== - -subroutine add_typedef( dyn_strings ) - logical, intent(in) :: dyn_strings - - integer :: idx1 - integer :: idx2 - - character(len=32) :: typetag - - idx1 = xml_find_attrib( attribs, noattribs, 'name', typename ) - if ( idx1 <= 0 ) then - write( 20, * ) 'Type definition found which has no name' - error = .true. - endif - - ! - ! We need a new set of temporary files - ! - call open_tmp_files( luprolog+notmps ) - - idx2 = xml_find_attrib( attribs, noattribs, 'tag', typetag ) - if ( idx1 < 1 ) then - typetag = typename - endif - - if ( .not. error ) then - write( ludef, '(/,2a)' ) 'type ',trim(typename) - write( luprolog, '(a)' ) & - & 'subroutine read_xml_type_'//trim(typename)//'_array( &' ,& - & ' info, tag, endtag, attribs, noattribs, data, nodata, &',& - & ' dvar, has_dvar )' ,& - & ' type(XML_PARSE) :: info' ,& - & ' character(len=*), intent(inout) :: tag ',& - & ' logical, intent(inout) :: endtag ',& - & ' character(len=*), dimension(:,:), intent(inout) :: attribs',& - & ' integer, intent(inout) :: noattribs',& - & ' character(len=*), dimension(:), intent(inout) :: data ',& - & ' integer, intent(inout) :: nodata ',& - & ' type('//trim(typename)//'), dimension(:), pointer :: dvar ',& - & ' logical, intent(inout) :: has_dvar ',& - & ' ' ,& - & ' integer :: newsize ',& - & ' type('//trim(typename)//'), dimension(:), pointer :: newvar',& - & ' ' ,& - & ' newsize = size(dvar) + 1' ,& - & ' allocate( newvar(1:newsize) )' ,& - & ' newvar(1:newsize-1) = dvar' ,& - & ' deallocate( dvar )' ,& - & ' dvar => newvar' ,& - & ' ' ,& - & ' call read_xml_type_'//trim(typename)// & - & '( info, tag, endtag, attribs, noattribs, data, nodata, &',& - & ' dvar(newsize), has_dvar )' ,& - & 'end subroutine read_xml_type_'//trim(typename)//'_array' ,& - & ' ' - - write( luwrite, '(a)' ) & - & 'subroutine write_xml_type_'//trim(typename)//'_array( &' ,& - & ' info, tag, indent, dvar )' ,& - & ' type(XML_PARSE) :: info' ,& - & ' character(len=*), intent(in) :: tag' ,& - & ' integer :: indent',& - & ' type('//trim(typename)//'), dimension(:) :: dvar' ,& - & ' integer :: i' ,& - & ' do i = 1,size(dvar)' ,& - & ' call write_xml_type_'//trim(typename)// & - & '( info, tag, indent, dvar(i) )' ,& - & ' enddo' ,& - & 'end subroutine write_xml_type_'//trim(typename)//'_array' ,& - & ' ', & - & 'subroutine write_xml_type_'//trim(typename)//'( &' ,& - & ' info, tag, indent, dvar )' ,& - & ' type(XML_PARSE) :: info' ,& - & ' character(len=*), intent(in) :: tag' ,& - & ' integer :: indent',& - & ' type('//trim(typename)//') :: dvar' ,& - & ' character(len=100) :: indentation' ,& - & ' indentation = '' ''' ,& - & ' write(info%lun, ''(4a)'' ) indentation(1:min(indent,100)),&',& - & ' ''<'',trim(tag), ''>''' - - write( luprolog, '(a)' ) & - & 'subroutine read_xml_type_'//trim(typename)//& - & '( info, starttag, endtag, attribs, noattribs, data, nodata, &' ,& - & ' dvar, has_dvar )' ,& - & ' type(XML_PARSE) :: info' ,& - & ' character(len=*), intent(in) :: starttag',& - & ' logical, intent(inout) :: endtag ',& - & ' character(len=*), dimension(:,:), intent(inout) :: attribs',& - & ' integer, intent(inout) :: noattribs',& - & ' character(len=*), dimension(:), intent(inout) :: data ',& - & ' integer, intent(inout) :: nodata ',& - & ' type('//trim(typename)//'), intent(inout) :: dvar' ,& - & ' logical, intent(inout) :: has_dvar ',& - & ' ' ,& - & ' integer :: att_ ',& - & ' integer :: noatt_ ',& - & ' logical :: error ',& - & ' logical :: endtag_org' - if ( dyn_strings ) then - write( luprolog, '(a)' ) & - & ' character(len=len(starttag)) :: tag ' - else - write( luprolog, '(a)' ) & - & ' character(len=80) :: tag ' - endif - - ! - ! Note: this may require a more sophisticated approach - ! when the components of the type are also pointers ... - ! - write( ludeflt, '(a)' ) & - & 'subroutine init_xml_type_'//trim(typename)//'_array( dvar ) ',& - & ' type('//trim(typename)//'), dimension(:), pointer :: dvar ',& - & ' if ( associated( dvar ) ) then' ,& - & ' deallocate( dvar )' ,& - & ' endif' ,& - & ' allocate( dvar(0) )' ,& - & 'end subroutine init_xml_type_'//trim(typename)//'_array' ,& - & 'subroutine init_xml_type_'//trim(typename)//'(dvar)' ,& - & ' type('//trim(typename)//') :: dvar ' - - begin_loop = .true. - - call add_end_loop - - ! - ! Add the names of the two new types to the list - ! - allocate( new_types(1:4,1:notypes+3) ) - new_types(:,1:notypes) = types - deallocate( types ) - types => new_types - - types(1,notypes+1) = typename - types(2,notypes+1) = ' type('//trim(typename)//')' - types(3,notypes+1) = 'read_xml_type_'//trim(typename) - types(4,notypes+1) = 'write_xml_type_'//trim(typename) - - types(1,notypes+2) = trim(typename) // '-array' - types(2,notypes+2) = ' type('//trim(typename)//'), dimension(:), pointer' - types(3,notypes+2) = 'read_xml_type_'//trim(typename)//'_array' - types(4,notypes+2) = 'write_xml_type_'//trim(typename)//'_array' - - types(1,notypes+3) = trim(typename) // '-shape' - types(2,notypes+3) = ' type('//trim(typename)//'), dimension(SHAPE)' - types(3,notypes+3) = 'read_xml_type_'//trim(typename)//'_array' - types(4,notypes+3) = 'write_xml_type_'//trim(typename)//'_array' - - notypes = notypes + 3 - - endif -end subroutine add_typedef - -!=============================================================================== -! CLOSE_TYPEDEF -- -! Routine to write the last code fragments for a derived type -! Arguments: -! component Turn off the "component" parameter -!=============================================================================== - -subroutine close_typedef( component ) - logical, intent(out) :: component - - component = .false. - write( ludef, '(a)' ) 'end type '//trim(typename) - write( luend, '(a)' ) & - & 'end subroutine read_xml_type_'//trim(typename) - write( ludeflt, '(a)' ) & - & 'end subroutine init_xml_type_'//trim(typename) - write( luwrite, '(a)' ) & - & ' write(info%lun,''(4a)'') indentation(1:min(indent,100)), &' ,& - & ' ''''', & - & 'end subroutine write_xml_type_'//trim(typename) ,& - & ' ' - call append_files( luprolog ) - call close_tmp_files - -end subroutine close_typedef - -!=============================================================================== -! ADD_PLACEHOLDER -- -! Routine to write the starting code fragments for a placeholder tag -! Arguments: -! dyn_strings Whether dynamic string lengths are allowed -!=============================================================================== - -subroutine add_placeholder( dyn_strings ) - logical, intent(in) :: dyn_strings - - integer :: idx1 - integer :: idx2 - - character(len=32) :: tag - character(len=20) :: optional - - idx1 = xml_find_attrib( attribs, noattribs, 'tag', tag ) - if ( idx1 <= 0 ) then - write( 20, * ) 'Placeholder definition found which has no tag name' - error = .true. - endif - - optional = 'no' - idx2 = xml_find_attrib( attribs, noattribs, 'optional', optional ) - - if ( optional == 'yes' ) then - if ( begin_loop ) then - call add_begin_loop( .false., .false. ) - endif - write( luloop, '(a)' ) & - ' case('''//trim(tag)//''')',& - ' ! Simply ignore the tag' - else - no_placeholders = no_placeholders + 1 - placeholder(no_placeholders) = tag - - write( luloop, '(a)' ) & - ' case('''//trim(tag)//''')',& - &' call read_xml_place_'//trim(tag)//'( info, &', & - &' tag, attribs, noattribs, data, nodata )' - comp = .false. - - ! - ! We need a new set of temporary files - ! - call open_tmp_files( luprolog+notmps ) - - ! - ! Write the first part of the routine - ! NOTE: - ! Will require an extra argument when collecting all variables - ! in one derived type - ! - write( luprolog, '(a)' ) & - & 'subroutine read_xml_place_'//trim(tag)//& - & '( info, starttag, attribs, noattribs, data, nodata )' ,& - & ' type(XML_PARSE) :: info' ,& - & ' character(len=*), intent(in) :: starttag',& - & ' character(len=*), dimension(:,:), intent(inout) :: attribs',& - & ' integer, intent(inout) :: noattribs',& - & ' character(len=*), dimension(:), intent(inout) :: data ',& - & ' integer, intent(inout) :: nodata ',& - & ' ' ,& - & ' logical :: error ',& - & ' logical :: endtag ' - if ( dyn_strings ) then - write( luprolog, '(a)' ) & - & ' character(len=len(starttag)) :: tag ' - else - write( luprolog, '(a)' ) & - & ' character(len=80) :: tag ' - endif - - begin_loop = .true. - - call add_end_loop - - write(luwrite,'(a)') & - &'subroutine write_xml_place_'//trim(tag)//'( &' ,& - &' info, indent )' ,& - &' type(XML_PARSE) :: info' ,& - &' integer :: indent',& - &' character(len=100) :: indentation' ,& - &' indentation = '' ''' ,& - &' write(info%lun, ''(4a)'' ) indentation(1:min(indent,100)),&',& - &' ''<'// trim(tag) // '>''' - write(luwritp,'(a)') & - &' call write_xml_place_'//trim(tag)//'( info, indent+3 )' - - endif -end subroutine add_placeholder - -!=============================================================================== -! CLOSE_PLACEHOLDER -- -! Routine to write the last code fragments for a placeholder -! Arguments: -! None -!=============================================================================== - -subroutine close_placeholder - - write( luend, '(a)' ) & - & 'end subroutine read_xml_place_'//trim(placeholder(no_placeholders)) - - write( luwrite, '(a)' ) & - & ' write(info%lun,''(4a)'') indentation(1:min(indent,100)), &' ,& - & ' ''''' ,& - & 'end subroutine write_xml_place_'//trim(placeholder(no_placeholders)) ,& - & ' ' - - call append_files( luprolog ) - call close_tmp_files - - no_placeholders = no_placeholders - 1 - -end subroutine close_placeholder - -end program From ab6ef0835ca96418dc38d2cf6f8af76839d1fe70 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 24 Jul 2013 12:21:59 -0400 Subject: [PATCH 24/45] added two new methods when working with repeated nodes in and XML and changed the input reading routines to use these --- src/input_xml.F90 | 77 +++++++++++++++++++++++++++++++++++-------- src/xml_interface.F90 | 41 +++++++++++++++++++---- 2 files changed, 99 insertions(+), 19 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 24ec6c2c6..ddde113bc 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -703,6 +703,9 @@ contains type(Node), pointer :: node_cell => null() type(Node), pointer :: node_surf => null() type(Node), pointer :: node_lat => null() + type(NodeList), pointer :: node_cell_list => null() + type(NodeList), pointer :: node_surf_list => null() + type(NodeList), pointer :: node_lat_list => null() ! Display output message message = "Reading geometry XML file..." @@ -740,12 +743,15 @@ contains overlap_check_cnt = 0 end if + ! Get pointer to list of XML + call get_node_list(doc, "cell", node_cell_list) + n_universes = 0 do i = 1, n_cells c => cells(i) ! Get pointer to i-th cell node - call get_node_ptr(doc, "cell", node_cell, i) + call get_node_item(node_cell_list, i, node_cell) ! Copy data into cells if (check_for_node(node_cell, "id")) then @@ -918,11 +924,14 @@ contains ! Allocate cells array allocate(surfaces(n_surfaces)) + ! Get pointer to list of XML + call get_node_list(doc, "surface", node_surf_list) + do i = 1, n_surfaces s => surfaces(i) ! Get pointer to i-th surface node - call get_node_ptr(doc, "surface", node_surf, i) + call get_node_item(node_surf_list, i, node_surf) ! Copy data into cells if (check_for_node(node_surf, "id")) then @@ -1040,11 +1049,14 @@ contains n_lattices = get_number_nodes(doc, "lattice") allocate(lattices(n_lattices)) + ! Get pointer to list of XML + call get_node_list(doc, "lattice", node_lat_list) + do i = 1, n_lattices lat => lattices(i) ! Get pointer to i-th lattice - call get_node_ptr(doc, "lattice", node_lat, i) + call get_node_item(node_lat_list, i, node_lat) ! ID of lattice if (check_for_node(node_lat, "id")) then @@ -1194,6 +1206,10 @@ contains type(Node), pointer :: node_nuc => null() type(Node), pointer :: node_ele => null() type(Node), pointer :: node_sab => null() + type(NodeList), pointer :: node_mat_list => null() + type(NodeList), pointer :: node_nuc_list => null() + type(NodeList), pointer :: node_ele_list => null() + type(NodeList), pointer :: node_sab_list => null() ! Display output message message = "Reading materials XML file..." @@ -1225,11 +1241,14 @@ contains index_nuclide = 0 index_sab = 0 + ! Get pointer to list of XML + call get_node_list(doc, "material", node_mat_list) + do i = 1, n_materials mat => materials(i) ! Get pointer to i-th material node - call get_node_ptr(doc, "material", node_mat, i) + call get_node_item(node_mat_list, i, node_mat) ! Copy material id if (check_for_node(node_mat, "id")) then @@ -1313,10 +1332,13 @@ contains call fatal_error() end if + ! Get pointer list of XML + call get_node_list(node_mat, "nuclide", node_nuc_list) + ! Create list of nuclides based on those specified plus natural elements INDIVIDUAL_NUCLIDES: do j = 1, get_number_nodes(node_mat, "nuclide") ! Combine nuclide identifier and cross section and copy into names - call get_node_ptr(node_mat, "nuclide", node_nuc, j) + call get_node_item(node_nuc_list, j, node_nuc) ! Check for empty name on nuclide if (.not.check_for_node(node_nuc, "name")) then @@ -1372,8 +1394,11 @@ contains ! ======================================================================= ! READ AND PARSE TAGS + ! Get pointer list of XML + call get_node_list(node_mat, "element", node_ele_list) + NATURAL_ELEMENTS: do j = 1, get_number_nodes(node_mat, "element") - call get_node_ptr(node_mat, "element", node_ele, j) + call get_node_item(node_ele_list, j, node_ele) ! Check for empty name on natural element if (.not.check_for_node(node_ele, "name")) then @@ -1501,9 +1526,12 @@ contains ! Initialize i_sab_nuclides mat % i_sab_nuclides = NONE + ! Get pointer list to XML + call get_node_list(node_mat, "sab", node_sab_list) + do j = 1, n_sab ! Get pointer to S(a,b) table - call get_node_ptr(node_mat, "sab", node_sab, j) + call get_node_item(node_sab_list, j, node_sab) ! Determine name of S(a,b) table if (.not.check_for_node(node_sab, "name") .or. & @@ -1594,6 +1622,9 @@ contains type(Node), pointer :: node_mesh => null() type(Node), pointer :: node_tal => null() type(Node), pointer :: node_filt => null() + type(NodeList), pointer :: node_mesh_list => null() + type(NodeList), pointer :: node_tal_list => null() + type(NodeList), pointer :: node_filt_list => null() ! Check if tallies.xml exists filename = trim(path_input) // "tallies.xml" @@ -1653,11 +1684,14 @@ contains ! ========================================================================== ! READ MESH DATA + ! Get pointer list to XML + call get_node_list(doc, "mesh", node_mesh_list) + do i = 1, n_user_meshes m => meshes(i) ! Get pointer to mesh node - call get_node_ptr(doc, "mesh", node_mesh, i) + call get_node_item(node_mesh_list, i, node_mesh) ! Copy mesh id if (check_for_node(node_mesh, "id")) then @@ -1790,12 +1824,15 @@ contains ! ========================================================================== ! READ TALLY DATA + ! Get pointer list to XML + call get_node_list(doc, "tally", node_tal_list) + READ_TALLIES: do i = 1, n_user_tallies ! Get pointer to tally t => tallies(i) ! Get pointer to tally xml node - call get_node_ptr(doc, "tally", node_tal, i) + call get_node_item(node_tal_list, i, node_tal) ! Set tally type to volume by default t % type = TALLY_VOLUME @@ -1849,9 +1886,12 @@ contains t % n_filters = n_filters allocate(t % filters(n_filters)) + ! Get pointer list to XML + call get_node_list(node_tal, "filter", node_filt_list) + READ_FILTERS: do j = 1, n_filters ! Get pointer to filter xml node - call get_node_ptr(node_tal, "filter", node_filt, j) + call get_node_item(node_filt_list, j, node_filt) ! Convert filter type to lower case temp_str = '' @@ -2423,6 +2463,8 @@ contains type(Node), pointer :: node_plot => null() type(Node), pointer :: node_col => null() type(Node), pointer :: node_mask => null() + type(NodeList), pointer :: node_plot_list => null() + type(NodeList), pointer :: node_col_list => null() ! Check if plots.xml exists filename = trim(path_input) // "plots.xml" @@ -2443,11 +2485,14 @@ contains n_plots = get_number_nodes(doc, "plot") allocate(plots(n_plots)) + ! Get list pointer to XML + call get_node_list(doc, "plot", node_plot_list) + READ_PLOTS: do i = 1, n_plots pl => plots(i) ! Get pointer to plot XML node - call get_node_ptr(doc, "plot", node_plot, i) + call get_node_item(node_plot_list, i, node_plot) ! Copy data into plots if (check_for_node(node_plot, "id")) then @@ -2619,11 +2664,13 @@ contains call warning() end if + ! Get the number of nodes and get a list of them n_cols = get_number_nodes(node_plot, "col_spec") + call get_node_list(node_plot, "col_spec", node_col_list) do j = 1, n_cols ! Get pointer to color spec XML node - call get_node_ptr(node_plot, "col_spec", node_col, j) + call get_node_item(node_col_list, j, node_col) ! Check and make sure 3 values are specified for RGB if (get_arraysize_double(node_col, "rgb") /= 3) then @@ -2773,6 +2820,7 @@ contains type(XsListing), pointer :: listing => null() type(Node), pointer :: doc => null() type(Node), pointer :: node_ace => null() + type(NodeList), pointer :: node_ace_list => null() ! Check if cross_sections.xml exists inquire(FILE=path_cross_sections, EXIST=file_exists) @@ -2829,11 +2877,14 @@ contains allocate(xs_listings(n_listings)) end if + ! Get node list of all + call get_node_list(doc, "ace_table", node_ace_list) + do i = 1, n_listings listing => xs_listings(i) ! Get pointer to ace table XML node - call get_node_ptr(doc, "ace_table", node_ace, i) + call get_node_item(node_ace_list, i, node_ace) ! copy a number of attributes call get_node_value(node_ace, "name", listing % name) diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index d1fc88a1c..b0c47aa08 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -8,11 +8,14 @@ module xml_interface implicit none private public :: Node + public :: NodeList public :: open_xmldoc public :: close_xmldoc public :: check_for_node public :: get_number_nodes public :: get_node_ptr + public :: get_node_list + public :: get_node_item public :: get_node_value public :: get_node_array public :: get_arraysize_integer @@ -120,15 +123,13 @@ contains ! GET_NODE_PTR !=============================================================================== - subroutine get_node_ptr(in_ptr, node_name, out_ptr, idx, found) + subroutine get_node_ptr(in_ptr, node_name, out_ptr, found) character(len=*), intent(in) :: node_name - integer, intent(in), optional :: idx logical, intent(out), optional :: found type(Node), pointer, intent(in) :: in_ptr type(Node), pointer, intent(out) :: out_ptr - integer :: idx_ logical :: found_ type(NodeList), pointer :: elem_list => null() @@ -142,9 +143,7 @@ contains if (getLength(elem_list) == 0) return ! Point to the new element - idx_ = 0 - if (present(idx)) idx_ = idx - 1 - out_ptr => item(elem_list, idx_) + out_ptr => item(elem_list, 0) found_ = .true. ! Check to output found @@ -152,6 +151,36 @@ contains end subroutine get_node_ptr +!=============================================================================== +! GET_NODE_LIST +!=============================================================================== + + subroutine get_node_list(in_ptr, node_name, out_ptr) + + character(len=*), intent(in) :: node_name + type(Node), pointer, intent(in) :: in_ptr + type(NodeList), pointer, intent(out) :: out_ptr + + ! Check for a sub-element + out_ptr => getElementsByTagName(in_ptr, trim(node_name)) + + end subroutine get_node_list + +!=============================================================================== +! GET_NODE_ITEM +!=============================================================================== + + subroutine get_node_item(in_ptr, idx, out_ptr) + + integer, intent(in) :: idx + type(NodeList), pointer, intent(in) :: in_ptr + type(Node), pointer, intent(out) :: out_ptr + + ! Check for a sub-element + out_ptr => item(in_ptr, idx - 1) + + end subroutine get_node_item + !=============================================================================== ! GET_NODE_VALUE_INTEGER !=============================================================================== From e5bdb8bef9ac0d2ad8c7cd1eb5b412693b0de5f4 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 29 Jul 2013 15:56:24 -0400 Subject: [PATCH 25/45] Changed how to get the number of recurring node types in file, and added comments to XML interface --- src/input_xml.F90 | 162 ++++++++++++++++++++---------------------- src/xml_interface.F90 | 82 ++++++++++----------- 2 files changed, 119 insertions(+), 125 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index ddde113bc..2bf1abe1b 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -725,9 +725,11 @@ contains ! Parse geometry.xml file call open_xmldoc(doc, filename) + ! Get pointer to list of XML + call get_node_list(doc, "cell", node_cell_list) + ! Get number of tags - n_cells = 0 - n_cells = get_number_nodes(doc, "cell") + n_cells = get_list_size(node_cell_list) ! Check for no cells if (n_cells == 0) then @@ -743,15 +745,12 @@ contains overlap_check_cnt = 0 end if - ! Get pointer to list of XML - call get_node_list(doc, "cell", node_cell_list) - n_universes = 0 do i = 1, n_cells c => cells(i) ! Get pointer to i-th cell node - call get_node_item(node_cell_list, i, node_cell) + call get_list_item(node_cell_list, i, node_cell) ! Copy data into cells if (check_for_node(node_cell, "id")) then @@ -911,9 +910,11 @@ contains ! applied to a surface boundary_exists = .false. + ! get pointer to list of xml + call get_node_list(doc, "surface", node_surf_list) + ! Get number of tags - n_surfaces = 0 - n_surfaces = get_number_nodes(doc, "surface") + n_surfaces = get_list_size(node_surf_list) ! Check for no surfaces if (n_surfaces == 0) then @@ -924,14 +925,11 @@ contains ! Allocate cells array allocate(surfaces(n_surfaces)) - ! Get pointer to list of XML - call get_node_list(doc, "surface", node_surf_list) - do i = 1, n_surfaces s => surfaces(i) ! Get pointer to i-th surface node - call get_node_item(node_surf_list, i, node_surf) + call get_list_item(node_surf_list, i, node_surf) ! Copy data into cells if (check_for_node(node_surf, "id")) then @@ -1045,18 +1043,18 @@ contains ! ========================================================================== ! READ LATTICES FROM GEOMETRY.XML - ! Allocate lattices array - n_lattices = get_number_nodes(doc, "lattice") - allocate(lattices(n_lattices)) - ! Get pointer to list of XML call get_node_list(doc, "lattice", node_lat_list) + ! Allocate lattices array + n_lattices = get_list_size(node_lat_list) + allocate(lattices(n_lattices)) + do i = 1, n_lattices lat => lattices(i) ! Get pointer to i-th lattice - call get_node_item(node_lat_list, i, node_lat) + call get_list_item(node_lat_list, i, node_lat) ! ID of lattice if (check_for_node(node_lat, "id")) then @@ -1233,22 +1231,22 @@ contains if (check_for_node(doc, "default_xs")) & call get_node_value(doc, "default_xs", default_xs) + ! Get pointer to list of XML + call get_node_list(doc, "material", node_mat_list) + ! Allocate cells array - n_materials = get_number_nodes(doc, "material") + n_materials = get_list_size(node_mat_list) allocate(materials(n_materials)) ! Initialize count for number of nuclides/S(a,b) tables index_nuclide = 0 index_sab = 0 - ! Get pointer to list of XML - call get_node_list(doc, "material", node_mat_list) - do i = 1, n_materials mat => materials(i) ! Get pointer to i-th material node - call get_node_item(node_mat_list, i, node_mat) + call get_list_item(node_mat_list, i, node_mat) ! Copy material id if (check_for_node(node_mat, "id")) then @@ -1336,9 +1334,9 @@ contains call get_node_list(node_mat, "nuclide", node_nuc_list) ! Create list of nuclides based on those specified plus natural elements - INDIVIDUAL_NUCLIDES: do j = 1, get_number_nodes(node_mat, "nuclide") + INDIVIDUAL_NUCLIDES: do j = 1, get_list_size(node_nuc_list) ! Combine nuclide identifier and cross section and copy into names - call get_node_item(node_nuc_list, j, node_nuc) + call get_list_item(node_nuc_list, j, node_nuc) ! Check for empty name on nuclide if (.not.check_for_node(node_nuc, "name")) then @@ -1397,8 +1395,8 @@ contains ! Get pointer list of XML call get_node_list(node_mat, "element", node_ele_list) - NATURAL_ELEMENTS: do j = 1, get_number_nodes(node_mat, "element") - call get_node_item(node_ele_list, j, node_ele) + NATURAL_ELEMENTS: do j = 1, get_list_size(node_ele_list) + call get_list_item(node_ele_list, j, node_ele) ! Check for empty name on natural element if (.not.check_for_node(node_ele, "name")) then @@ -1513,7 +1511,10 @@ contains ! ======================================================================= ! READ AND PARSE TAG FOR S(a,b) DATA - n_sab = get_number_nodes(node_mat, "sab") + ! Get pointer list to XML + call get_node_list(node_mat, "sab", node_sab_list) + + n_sab = get_list_size(node_sab_list) if (n_sab > 0) then ! Set number of S(a,b) tables mat % n_sab = n_sab @@ -1526,12 +1527,9 @@ contains ! Initialize i_sab_nuclides mat % i_sab_nuclides = NONE - ! Get pointer list to XML - call get_node_list(node_mat, "sab", node_sab_list) - do j = 1, n_sab ! Get pointer to S(a,b) table - call get_node_item(node_sab_list, j, node_sab) + call get_list_item(node_sab_list, j, node_sab) ! Determine name of S(a,b) table if (.not.check_for_node(node_sab, "name") .or. & @@ -1644,28 +1642,28 @@ contains ! ========================================================================== ! DETERMINE SIZE OF ARRAYS AND ALLOCATE + ! Get pointer list to XML + call get_node_list(doc, "mesh", node_mesh_list) + + ! Get pointer list to XML + call get_node_list(doc, "tally", node_tal_list) + ! Check for user meshes - if (.not.check_for_node(doc, "mesh")) then - n_user_meshes = 0 + n_user_meshes = get_list_size(node_mesh_list) + if (cmfd_run) then + n_meshes = n_user_meshes + n_cmfd_meshes else - n_user_meshes = get_number_nodes(doc, "mesh") - if (cmfd_run) then - n_meshes = n_user_meshes + n_cmfd_meshes - else - n_meshes = n_user_meshes - end if + n_meshes = n_user_meshes end if ! Allocate mesh array if (n_meshes > 0) allocate(meshes(n_meshes)) ! Check for user tallies - if (.not.check_for_node(doc, "tally")) then - n_user_tallies = 0 + n_user_tallies = get_list_size(node_tal_list) + if (n_user_tallies == 0) then message = "No tallies present in tallies.xml file!" call warning() - else - n_user_tallies = get_number_nodes(doc, "tally") end if ! Allocate tally array @@ -1684,14 +1682,11 @@ contains ! ========================================================================== ! READ MESH DATA - ! Get pointer list to XML - call get_node_list(doc, "mesh", node_mesh_list) - do i = 1, n_user_meshes m => meshes(i) ! Get pointer to mesh node - call get_node_item(node_mesh_list, i, node_mesh) + call get_list_item(node_mesh_list, i, node_mesh) ! Copy mesh id if (check_for_node(node_mesh, "id")) then @@ -1824,15 +1819,12 @@ contains ! ========================================================================== ! READ TALLY DATA - ! Get pointer list to XML - call get_node_list(doc, "tally", node_tal_list) - READ_TALLIES: do i = 1, n_user_tallies ! Get pointer to tally t => tallies(i) ! Get pointer to tally xml node - call get_node_item(node_tal_list, i, node_tal) + call get_list_item(node_tal_list, i, node_tal) ! Set tally type to volume by default t % type = TALLY_VOLUME @@ -1872,26 +1864,25 @@ contains ! element followed by sub-elements , , etc. This checks for ! the old format and if it is present, raises an error - if (get_number_nodes(node_tal, "filters") > 0) then - message = "Tally filters should be specified with multiple & - &elements. Did you forget to change your element?" - call fatal_error() - end if +! if (get_number_nodes(node_tal, "filters") > 0) then +! message = "Tally filters should be specified with multiple & +! &elements. Did you forget to change your element?" +! call fatal_error() +! end if - if (check_for_node(node_tal, "filter")) then - ! Determine number of filters - n_filters = get_number_nodes(node_tal, "filter") + ! Get pointer list to XML and get number of filters + call get_node_list(node_tal, "filter", node_filt_list) + n_filters = get_list_size(node_filt_list) + + if (n_filters /= 0) then ! Allocate filters array t % n_filters = n_filters allocate(t % filters(n_filters)) - ! Get pointer list to XML - call get_node_list(node_tal, "filter", node_filt_list) - READ_FILTERS: do j = 1, n_filters ! Get pointer to filter xml node - call get_node_item(node_filt_list, j, node_filt) + call get_list_item(node_filt_list, j, node_filt) ! Convert filter type to lower case temp_str = '' @@ -2453,7 +2444,7 @@ contains subroutine read_plots_xml integer i, j - integer n_cols, col_id, n_comp + integer n_cols, col_id, n_comp, n_masks integer, allocatable :: iarray(:) logical :: file_exists ! does plots.xml file exist? character(MAX_LINE_LEN) :: filename ! absolute path to plots.xml @@ -2465,6 +2456,7 @@ contains type(Node), pointer :: node_mask => null() type(NodeList), pointer :: node_plot_list => null() type(NodeList), pointer :: node_col_list => null() + type(NodeList), pointer :: node_mask_list => null() ! Check if plots.xml exists filename = trim(path_input) // "plots.xml" @@ -2481,18 +2473,18 @@ contains ! Parse plots.xml file call open_xmldoc(doc, filename) - ! Allocate plots array - n_plots = get_number_nodes(doc, "plot") - allocate(plots(n_plots)) - ! Get list pointer to XML call get_node_list(doc, "plot", node_plot_list) + ! Allocate plots array + n_plots = get_list_size(node_plot_list) + allocate(plots(n_plots)) + READ_PLOTS: do i = 1, n_plots pl => plots(i) ! Get pointer to plot XML node - call get_node_item(node_plot_list, i, node_plot) + call get_list_item(node_plot_list, i, node_plot) ! Copy data into plots if (check_for_node(node_plot, "id")) then @@ -2655,8 +2647,12 @@ contains call fatal_error() end select + ! Get the number of nodes and get a list of them + call get_node_list(node_plot, "col_spec", node_col_list) + n_cols = get_list_size(node_col_list) + ! Copy user specified colors - if (check_for_node(node_plot, "col_spec")) then + if (n_cols /= 0) then if (pl % type == PLOT_TYPE_VOXEL) then message = "Color specifications ignored in voxel plot " // & @@ -2664,13 +2660,10 @@ contains call warning() end if - ! Get the number of nodes and get a list of them - n_cols = get_number_nodes(node_plot, "col_spec") - call get_node_list(node_plot, "col_spec", node_col_list) do j = 1, n_cols ! Get pointer to color spec XML node - call get_node_item(node_col_list, j, node_col) + call get_list_item(node_col_list, j, node_col) ! Check and make sure 3 values are specified for RGB if (get_arraysize_double(node_col, "rgb") /= 3) then @@ -2716,7 +2709,9 @@ contains end if ! Deal with masks - if (check_for_node(node_plot, "mask")) then + call get_node_list(node_plot, "mask", node_mask_list) + n_masks = get_list_size(node_mask_list) + if (n_masks /= 0) then if (pl % type == PLOT_TYPE_VOXEL) then message = "Mask ignored in voxel plot " // & @@ -2724,16 +2719,15 @@ contains call warning() end if - select case(get_number_nodes(node_plot, "mask")) + select case(n_masks) case default message = "Mutliple masks" // & " specified in plot " // trim(to_str(pl % id)) call fatal_error() - case (0) case (1) ! Get pointer to mask - call get_node_ptr(node_plot, "mask", node_mask) + call get_list_item(node_mask_list, 1, node_mask) ! Determine how many components there are and allocate n_comp = 0 @@ -2868,23 +2862,23 @@ contains call get_node_value(doc, "entries", entries) end if + ! Get node list of all + call get_node_list(doc, "ace_table", node_ace_list) + n_listings = get_list_size(node_ace_list) + ! Allocate xs_listings array - if (.not.check_for_node(doc, "ace_table")) then + if (n_listings == 0) then message = "No ACE table listings present in cross_sections.xml file!" call fatal_error() else - n_listings = get_number_nodes(doc, "ace_table") allocate(xs_listings(n_listings)) end if - ! Get node list of all - call get_node_list(doc, "ace_table", node_ace_list) - do i = 1, n_listings listing => xs_listings(i) ! Get pointer to ace table XML node - call get_node_item(node_ace_list, i, node_ace) + call get_list_item(node_ace_list, i, node_ace) ! copy a number of attributes call get_node_value(node_ace, "name", listing % name) diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index b0c47aa08..76c174f01 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -12,10 +12,10 @@ module xml_interface public :: open_xmldoc public :: close_xmldoc public :: check_for_node - public :: get_number_nodes public :: get_node_ptr public :: get_node_list - public :: get_node_item + public :: get_list_size + public :: get_list_item public :: get_node_value public :: get_node_array public :: get_arraysize_integer @@ -41,7 +41,7 @@ module xml_interface contains !=============================================================================== -! OPEN_XMLDOC +! OPEN_XMLDOC opens and parses an XML and returns a pointer to the document !=============================================================================== subroutine open_xmldoc(ptr, filename) @@ -55,7 +55,7 @@ contains end subroutine open_xmldoc !=============================================================================== -! CLOSE_XMLDOC +! CLOSE_XMLDOC closes and destroys all memory associated with document !=============================================================================== subroutine close_xmldoc(ptr) @@ -67,7 +67,11 @@ contains end subroutine close_xmldoc !=============================================================================== -! CHECK_FOR_NODE +! CHECK_FOR_NODE checks for an attribute or sub-element node with the given +! node name. This should only be used for checking a single occurance of a +! sub-element node. To check for sub-element nodes that repeat, use +! get_node_list and get_list_size. This is to minimize number of calls +! to getElementsByTagName. !=============================================================================== function check_for_node(ptr, node_name) result(found) @@ -100,27 +104,9 @@ contains end function check_for_node !=============================================================================== -! GET_NUMBER_NODES -!=============================================================================== - - function get_number_nodes(ptr, node_name) result(n_nodes) - - character(len=*), intent(in) :: node_name - integer :: n_nodes - type(Node), pointer, intent(in) :: ptr - - type(NodeList), pointer :: elem_list - - ! Check for a sub-element - elem_list => getElementsByTagName(ptr, trim(node_name)) - - ! Get the length of the list - n_nodes = getLength(elem_list) - - end function get_number_nodes - -!=============================================================================== -! GET_NODE_PTR +! GET_NODE_PTR returns a Node pointer to an attribute or sub-element node. +! Note, this should only be used for sub-element nodes that occur only once. +! For repeated nodes, use get_node_list and then get_item_item. !=============================================================================== subroutine get_node_ptr(in_ptr, node_name, out_ptr, found) @@ -152,7 +138,7 @@ contains end subroutine get_node_ptr !=============================================================================== -! GET_NODE_LIST +! GET_NODE_LIST is used to get a pointer to a list of sub-element nodes !=============================================================================== subroutine get_node_list(in_ptr, node_name, out_ptr) @@ -167,10 +153,24 @@ contains end subroutine get_node_list !=============================================================================== -! GET_NODE_ITEM +! GET_LIST_SIZE is used to get the number of elements from a node list !=============================================================================== - subroutine get_node_item(in_ptr, idx, out_ptr) + function get_list_size(in_ptr) result(n_size) + + integer :: n_size + type(NodeList), pointer, intent(in) :: in_ptr + + ! Get the size of the list + n_size = getLength(in_ptr) + + end function get_list_size + +!=============================================================================== +! GET_LIST_ITEM is used to select a specific item from a node list +!=============================================================================== + + subroutine get_list_item(in_ptr, idx, out_ptr) integer, intent(in) :: idx type(NodeList), pointer, intent(in) :: in_ptr @@ -179,10 +179,10 @@ contains ! Check for a sub-element out_ptr => item(in_ptr, idx - 1) - end subroutine get_node_item + end subroutine get_list_item !=============================================================================== -! GET_NODE_VALUE_INTEGER +! GET_NODE_VALUE_INTEGER returns a integer value from an attribute or node !=============================================================================== subroutine get_node_value_integer(ptr, node_name, result_int) @@ -215,7 +215,7 @@ contains end subroutine get_node_value_integer !=============================================================================== -! GET_NODE_VALUE_LONG +! GET_NODE_VALUE_LONG returns an 8-byte integer from attribute or node !=============================================================================== subroutine get_node_value_long(ptr, node_name, result_long) @@ -248,7 +248,7 @@ contains end subroutine get_node_value_long !=============================================================================== -! GET_NODE_VALUE_DOUBLE +! GET_NODE_VALUE_DOUBLE returns a double precision real from attr. or node !=============================================================================== subroutine get_node_value_double(ptr, node_name, result_double) @@ -281,7 +281,7 @@ contains end subroutine get_node_value_double !=============================================================================== -! GET_NODE_ARRAY_INTEGER +! GET_NODE_ARRAY_INTEGER returns a 1-D array of integers !=============================================================================== subroutine get_node_array_integer(ptr, node_name, result_int) @@ -314,7 +314,7 @@ contains end subroutine get_node_array_integer !=============================================================================== -! GET_NODE_ARRAY_DOUBLE +! GET_NODE_ARRAY_DOUBLE returns a 1-D array of double precision reals !=============================================================================== subroutine get_node_array_double(ptr, node_name, result_double) @@ -347,7 +347,7 @@ contains end subroutine get_node_array_double !=============================================================================== -! GET_NODE_ARRAY_STRING +! GET_NODE_ARRAY_STRING returns a 1-D array of strings !=============================================================================== subroutine get_node_array_string(ptr, node_name, result_string) @@ -380,7 +380,7 @@ contains end subroutine get_node_array_string !=============================================================================== -! GET_NODE_VALUE_STRING +! GET_NODE_VALUE_STRING returns a single string from attr. or node !=============================================================================== subroutine get_node_value_string(ptr, node_name, result_str) @@ -413,7 +413,7 @@ contains end subroutine get_node_value_string !=============================================================================== -! GET_NODE_ARRAYSIZE_INTEGER +! GET_NODE_ARRAYSIZE_INTEGER returns the size of the integer array !=============================================================================== function get_arraysize_integer(ptr, node_name) result(n) @@ -442,7 +442,7 @@ contains end function get_arraysize_integer !=============================================================================== -! GET_NODE_ARRAYSIZE_DOUBLE +! GET_NODE_ARRAYSIZE_DOUBLE returns the size of double prec. real array !=============================================================================== function get_arraysize_double(ptr, node_name) result(n) @@ -471,7 +471,7 @@ contains end function get_arraysize_double !=============================================================================== -! GET_NODE_ARRAYSIZE_STRING +! GET_NODE_ARRAYSIZE_STRING returns the size of string array !=============================================================================== function get_arraysize_string(ptr, node_name) result(n) @@ -500,7 +500,7 @@ contains end function get_arraysize_string !=============================================================================== -! GET_NODE +! GET_NODE private routine that gets a pointer to a specific node !=============================================================================== subroutine get_node(in_ptr, node_name, out_ptr, node_type, found) From be410a604344ba506e61222c090a6294ed56b57f Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 29 Jul 2013 16:38:01 -0400 Subject: [PATCH 26/45] XML document is now correctly deallocated --- src/xml_interface.F90 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/xml_interface.F90 b/src/xml_interface.F90 index 76c174f01..4752c0873 100644 --- a/src/xml_interface.F90 +++ b/src/xml_interface.F90 @@ -49,8 +49,8 @@ contains character(len=*) :: filename type(Node), pointer :: ptr - ptr => parseFile(trim(filename)) - ptr => getDocumentElement(ptr) + ptr => parseFile(trim(filename)) ! Pointer to the whole XML document + ptr => getDocumentElement(ptr) ! Grabs root element of XML document end subroutine open_xmldoc @@ -62,7 +62,8 @@ contains type(Node), pointer :: ptr - call destroy(ptr) + ptr => getParentNode(ptr) ! Go from root element ptr to document ptr + call destroy(ptr) ! Deallocates all nodes recursively end subroutine close_xmldoc From 49d915924c81a869361e02c1c6aa927c078ca156 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 29 Jul 2013 16:53:22 -0400 Subject: [PATCH 27/45] added documentation on new element structure --- docs/source/usersguide/input.rst | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index d9b43c821..947534990 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -198,19 +198,26 @@ tally data, this option can significantly improve the parallel efficiency. -------------------- The ```` element determines what output files should be written to disk -during the run. This element has no attributes or sub-elements and should be set -to a list of strings separated by spaces. Valid options are "summary", -"cross-sections", and "tallies". For example, if you want the summary and cross -sections summary file to be written, this element should be given as: +during the run. The sub-elements are described below, where "true" will write +out the file and "false" will not. - .. code-block:: xml + :cross_sections: + Writes out an ASCII summary file of the cross sections that were read in. - summary cross_sections + *Default*: false - .. note:: The tally results will be written to a binary/HDF5 state point file by - default. + :summary: + Writes out an ASCII summary file describing all of the user input files that + were read in. - *Default*: "tallies" + *Default*: false + + :tallies: + Write out an ASCII file of tally results. + + *Default*: true + + .. note:: The tally results will always be written to a binary/HDF5 state point file. ```` Element ------------------------- From dedba81ead49f1c80d60cb566deec2793f9aa1b2 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 15:06:52 -0400 Subject: [PATCH 28/45] if material units are sum, not value will exist -> value is moved to be read after units --- src/input_xml.F90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 2bf1abe1b..a610015c3 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1281,8 +1281,7 @@ contains call fatal_error() end if - ! Copy value and units - call get_node_value(node_dens, "value", val) + ! Copy units call get_node_value(node_dens, "units", units) if (units == 'sum') then @@ -1317,6 +1316,9 @@ contains // "' specified on material " // trim(to_str(mat % id)) call fatal_error() end select + + ! Copy value + call get_node_value(node_dens, "value", val) end if ! ======================================================================= From 4b7d979364b66da662acab3f7423a799734a6a81 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 15:32:55 -0400 Subject: [PATCH 29/45] moved copying value before type of unit checked and initialize value to zero --- src/input_xml.F90 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index a610015c3..5bf877e25 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1281,6 +1281,9 @@ contains call fatal_error() end if + ! Initialize value to zero + val = ZERO + ! Copy units call get_node_value(node_dens, "units", units) @@ -1292,6 +1295,9 @@ contains sum_density = .true. else + ! Copy value + call get_node_value(node_dens, "value", val) + ! Check for erroneous density sum_density = .false. if (val <= ZERO) then @@ -1316,9 +1322,6 @@ contains // "' specified on material " // trim(to_str(mat % id)) call fatal_error() end select - - ! Copy value - call get_node_value(node_dens, "value", val) end if ! ======================================================================= From dda6ae55d998229eb8f96caa6dcab0cdcd1f3bd5 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 15:46:30 -0400 Subject: [PATCH 30/45] changed fixed source to particle when determining if node particle exists --- src/input_xml.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 5bf877e25..a6d6538db 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -165,7 +165,7 @@ contains call get_node_ptr(doc, "fixed_source", node_mode) ! Check number of particles - if (.not.check_for_node(node_mode, "fixed_source")) then + if (.not.check_for_node(node_mode, "particles")) then message = "Need to specify number of particles per batch." call fatal_error() end if From 33dbb27e66aade5248212fc49b6594ce14f48010 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 16:20:29 -0400 Subject: [PATCH 31/45] fixed node pointers to natural elements that were sometimes labeled as nuclides --- src/input_xml.F90 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index a6d6538db..0dd4fb3bc 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1412,7 +1412,7 @@ contains call get_node_value(node_ele, "name", name) ! Check for cross section - if (.not.check_for_node(node_nuc, "xs")) then + if (.not.check_for_node(node_ele, "xs")) then if (default_xs == '') then message = "No cross section specified for nuclide in material " & // trim(to_str(mat % id)) @@ -1424,21 +1424,21 @@ contains ! Check if no atom/weight percents were specified or if both atom and ! weight percents were specified - if (.not.check_for_node(node_nuc, "ao") .and. & - .not.check_for_node(node_nuc, "wo")) then + if (.not.check_for_node(node_ele, "ao") .and. & + .not.check_for_node(node_ele, "wo")) then message = "No atom or weight percent specified for element " // & trim(name) call fatal_error() - elseif (check_for_node(node_nuc, "ao") .and. & - check_for_node(node_nuc, "wo")) then + elseif (check_for_node(node_ele, "ao") .and. & + check_for_node(node_ele, "wo")) then message = "Cannot specify both atom and weight percents for a & &element: " // trim(name) call fatal_error() end if ! Expand element into naturally-occurring isotopes - if (check_for_node(node_nuc, "ao")) then - call get_node_value(node_nuc, "ao", temp_dble) + if (check_for_node(node_ele, "ao")) then + call get_node_value(node_ele, "ao", temp_dble) call expand_natural_element(name, temp_str, temp_dble, & list_names, list_density) else From 455a794b43caa16e23ee02cd4cbb8519215c0ad4 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 16:30:07 -0400 Subject: [PATCH 32/45] for plots, is now cell by default (according to users guide) --- src/input_xml.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 0dd4fb3bc..8c3df4f58 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2621,7 +2621,7 @@ contains end if ! Copy plot color type and initialize all colors randomly - temp_str = '' + temp_str = "cell" if (check_for_node(node_plot, "color")) & call get_node_value(node_plot, "color", temp_str) call lower_case(temp_str) From 5e8ac1b98a3d670abbec0dd509072a4d5925ca4c Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 16:35:34 -0400 Subject: [PATCH 33/45] added empty arg list on subroutines --- src/input_xml.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 8c3df4f58..f1363d85a 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1593,7 +1593,7 @@ contains ! for errors and placing properly-formatted data in the right data structures !=============================================================================== - subroutine read_tallies_xml + subroutine read_tallies_xml() integer :: i ! loop over user-specified tallies integer :: j ! loop over words @@ -2446,7 +2446,7 @@ contains ! READ_PLOTS_XML reads data from a plots.xml file !=============================================================================== - subroutine read_plots_xml + subroutine read_plots_xml() integer i, j integer n_cols, col_id, n_comp, n_masks From 43ba6f18588b2f773cd1b86705b2b28c4ae35d1e Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 13 Aug 2013 16:40:54 -0400 Subject: [PATCH 34/45] wrong pointer listed for reading in RGB in node --- src/input_xml.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index f1363d85a..b27cb2aeb 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2691,7 +2691,7 @@ contains if (cell_dict % has_key(col_id)) then col_id = cell_dict % get_key(col_id) - call get_node_array(node_plot, "rgb", pl % colors(col_id) % rgb) + call get_node_array(node_col, "rgb", pl % colors(col_id) % rgb) else message = "Could not find cell " // trim(to_str(col_id)) // & " specified in plot " // trim(to_str(pl % id)) @@ -2702,7 +2702,7 @@ contains if (material_dict % has_key(col_id)) then col_id = material_dict % get_key(col_id) - call get_node_array(node_plot, "rgb", pl % colors(col_id) % rgb) + call get_node_array(node_col, "rgb", pl % colors(col_id) % rgb) else message = "Could not find material " // trim(to_str(col_id)) // & " specified in plot " // trim(to_str(pl % id)) From a321ac83f83bc7368475726767aac6bf6299fe5f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 14 Aug 2013 08:20:34 -0400 Subject: [PATCH 35/45] Fix spacing in input_xml to conform to style guide. --- src/input_xml.F90 | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index b27cb2aeb..ece67151c 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -90,10 +90,10 @@ contains ! CROSS_SECTIONS environment variable if (run_mode /= MODE_PLOTTING) then if (.not. check_for_node(doc, "cross_sections") .and. & - run_mode /= MODE_PLOTTING) then - ! No cross_sections.xml file specified in settings.xml, check environment - ! variable - call get_environment_variable("CROSS_SECTIONS", env_variable) + run_mode /= MODE_PLOTTING) then + ! No cross_sections.xml file specified in settings.xml, check + ! environment variable + call get_environment_variable("CROSS_SECTIONS", env_variable) if (len_trim(env_variable) == 0) then message = "No cross_sections.xml file was specified in settings.xml & &or in the CROSS_SECTIONS environment variable." @@ -116,7 +116,7 @@ contains ! Make sure that either eigenvalue or fixed source was specified if (.not.check_for_node(doc, "eigenvalue") .and. & - .not.check_for_node(doc, "fixed_source")) then + .not.check_for_node(doc, "fixed_source")) then message = " or not specified." call fatal_error() end if @@ -145,10 +145,10 @@ contains ! Copy batch and generation information call get_node_value(node_mode, "batches", n_batches) call get_node_value(node_mode, "inactive", n_inactive) - n_active = n_batches - n_inactive - if (check_for_node(node_mode, "generations_per_batch")) & - call get_node_value(node_mode, "generations_per_batch", & - gen_per_batch) + n_active = n_batches - n_inactive + if (check_for_node(node_mode, "generations_per_batch")) then + call get_node_value(node_mode, "generations_per_batch", gen_per_batch) + end if ! Allocate array for batch keff and entropy allocate(k_generation(n_batches*gen_per_batch)) @@ -220,7 +220,7 @@ contains ! Verbosity if (check_for_node(doc, "verbosity")) & - call get_node_value(doc, "verbosity", verbosity) + call get_node_value(doc, "verbosity", verbosity) ! ========================================================================== ! EXTERNAL SOURCE @@ -257,7 +257,7 @@ contains ! Check for type of spatial distribution type = '' if (check_for_node(node_dist, "type")) & - call get_node_value(node_dist, "type", type) + call get_node_value(node_dist, "type", type) call lower_case(type) select case (trim(type)) case ('box') @@ -307,7 +307,7 @@ contains ! Check for type of angular distribution type = '' if (check_for_node(node_dist, "type")) & - call get_node_value(node_dist, "type", type) + call get_node_value(node_dist, "type", type) call lower_case(type) select case (trim(type)) case ('isotropic') @@ -413,7 +413,7 @@ contains call get_node_value(doc, "survival_biasing", temp_str) call lower_case(temp_str) if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') & - survival_biasing = .true. + survival_biasing = .true. end if ! Probability tables @@ -421,7 +421,7 @@ contains call get_node_value(doc, "ptables", temp_str) call lower_case(temp_str) if (trim(temp_str) == 'false' .or. trim(temp_str) == '0') & - urr_ptables_on = .false. + urr_ptables_on = .false. end if ! Cutoffs From 3af02080023e27b6e36076fc0bf0e920b13298a2 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Aug 2013 00:31:25 -0500 Subject: [PATCH 36/45] Use new format in test_output. --- tests/test_output/settings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_output/settings.xml b/tests/test_output/settings.xml index 70116af72..fef5e2fcf 100644 --- a/tests/test_output/settings.xml +++ b/tests/test_output/settings.xml @@ -1,7 +1,7 @@ - summary cross_sections + 10 From edfa49ef81a02b6646c1e8bbdf5c6debf24911ed Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 15 Aug 2013 00:36:49 -0500 Subject: [PATCH 37/45] Added back RELAX NG compact schemata. --- schemas.xml | 14 ++-- src/relaxng/cmfd.rnc | 58 +++++++++++++++++ src/relaxng/cross_sections.rnc | 23 +++++++ src/relaxng/geometry.rnc | 35 ++++++++++ src/relaxng/materials.rnc | 42 ++++++++++++ src/relaxng/plots.rnc | 31 +++++++++ src/relaxng/settings.rnc | 116 +++++++++++++++++++++++++++++++++ src/relaxng/tallies.rnc | 43 ++++++++++++ 8 files changed, 355 insertions(+), 7 deletions(-) create mode 100644 src/relaxng/cmfd.rnc create mode 100644 src/relaxng/cross_sections.rnc create mode 100644 src/relaxng/geometry.rnc create mode 100644 src/relaxng/materials.rnc create mode 100644 src/relaxng/plots.rnc create mode 100644 src/relaxng/settings.rnc create mode 100644 src/relaxng/tallies.rnc diff --git a/schemas.xml b/schemas.xml index 982979001..465b9b398 100644 --- a/schemas.xml +++ b/schemas.xml @@ -1,10 +1,10 @@ - - - - - - - + + + + + + + diff --git a/src/relaxng/cmfd.rnc b/src/relaxng/cmfd.rnc new file mode 100644 index 000000000..cbcb7709a --- /dev/null +++ b/src/relaxng/cmfd.rnc @@ -0,0 +1,58 @@ +element cmfd { + element mesh { + (element dimension { list { xsd:int+ } } | + attribute dimension { list { xsd:int+ } }) & + (element lower_left { list { xsd:double+ } } | + attribute lower_left { list { xsd:double+ } }) & + ( + (element upper_right { list { xsd:double+ } } | + attribute upper_right { list { xsd:double+ } }) | + (element width { list { xsd:double+ } } | + attribute width { list { xsd:double+ } }) + ) & + (element albedo { list { xsd:double+ } } | + attribute albedo { list { xsd:double+ } }) & + (element map { list { xsd:int+ } } | + attribute map { list { xsd:int+ } })? & + (element energy { list { xsd:double+ } } | + attribute energy { list { xsd:double+ } })? + } & + + element norm { xsd:double }? & + + element feedback { xsd:boolean }? & + + element n_cmfd_procs { xsd:int }? & + + element reset { xsd:boolean }? & + + element balance { xsd:boolean }? & + + element downscatter { xsd:boolean }? & + + element run_2grp { xsd:boolean }? & + + element solver { xsd:string }? & + + element snes_monitor { xsd:boolean }? & + + element ksp_monitor { xsd:boolean }? & + + element power_monitor { xsd:boolean }? & + + element write_balance { xsd:boolean }? & + + element write_matrices { xsd:boolean }? & + + element run_adjoint { xsd:boolean }? & + + element write_hdf5 { xsd:boolean }? & + + element begin { xsd:int }? & + + element inactive { xsd:boolean }? & + + element active_flush { xsd:int }? & + + element keff_tol { xsd:double }? +} diff --git a/src/relaxng/cross_sections.rnc b/src/relaxng/cross_sections.rnc new file mode 100644 index 000000000..e82bb986f --- /dev/null +++ b/src/relaxng/cross_sections.rnc @@ -0,0 +1,23 @@ +element cross_sections { + element ace_table { + (element name { xsd:string { maxLength = "15" } } | + attribute name { xsd:string { maxLength = "15" } }) & + (element alias { xsd:string { maxLength = "15" } } | + attribute alias { xsd:string { maxLength = "15" } })? & + (element zaid { xsd:int } | attribute zaid { xsd:int }) & + (element metastable { xsd:int } | attribute metastable { xsd:int })? & + (element awr { xsd:double } | attribute awr { xsd:double }) & + (element temperature { xsd:double } | attribute temperature { xsd:double }) & + (element path { xsd:string { maxLength = "255" } } | + attribute path { xsd:string { maxLength = "255" } }) & + (element location { xsd:int } | attribute location { xsd:int })? + }* & + + element directory { xsd:string { maxLength = "255" } }? & + + element filetype { ( "ascii" | "binary" ) } & + + element record_length { xsd:int }? & + + element entries { xsd:int }? +} \ No newline at end of file diff --git a/src/relaxng/geometry.rnc b/src/relaxng/geometry.rnc new file mode 100644 index 000000000..b7975d05f --- /dev/null +++ b/src/relaxng/geometry.rnc @@ -0,0 +1,35 @@ +element geometry { + element cell { + (element id { xsd:int } | attribute id { xsd:int }) & + (element universe { xsd:int } | attribute universe { xsd:int })? & + ( + (element fill { xsd:int } | attribute fill { xsd:int }) | + (element material { ( xsd:int | "void" ) } | + attribute material { ( xsd:int | "void" ) }) + ) & + (element surfaces { list { xsd:int+ } } | attribute surfaces { list { xsd:int+ } }) & + (element rotation { list { xsd:double+ } } | attribute rotation { list { xsd:double+ } })? & + (element translation { list { xsd:double+ } } | attribute translation { list { xsd:double+ } })? + }* + + & element surface { + (element id { xsd:int } | attribute id { xsd:int }) & + (element type { xsd:string { maxLength = "15" } } | + attribute type { xsd:string { maxLength = "15" } }) & + (element coeffs { list { xsd:double+ } } | attribute coeffs { list { xsd:double+ } }) & + (element boundary { ( "transmit" | "reflective" | "vacuum" ) } | + attribute boundary { ( "transmit" | "reflective" | "vacuum" ) })? + }* + + & element lattice { + (element id { xsd:int } | attribute id { xsd:int }) & + (element type { ( "rectangular" | "hexagonal" ) } | + attribute type { ( "rectangular" | "hexagonal" ) })? & + (element dimension { list { xsd:positiveInteger+ } } | + attribute dimension { list { xsd:positiveInteger+ } }) & + (element lower_left { list { xsd:double+ } } | attribute lower_left { list { xsd:double+ } }) & + (element width { list { xsd:double+ } } | attribute width { list { xsd:double+ } }) & + (element universes { list { xsd:int+ } } | attribute universes { list { xsd:int+ } }) & + (element outside { xsd:int } | attribute outside { xsd:int })? + }* +} diff --git a/src/relaxng/materials.rnc b/src/relaxng/materials.rnc new file mode 100644 index 000000000..d497fa745 --- /dev/null +++ b/src/relaxng/materials.rnc @@ -0,0 +1,42 @@ +element materials { + element material { + (element id { xsd:int } | attribute id { xsd:int }) & + + element density { + (element value { xsd:double } | attribute value { xsd:double })? & + (element units { xsd:string { maxLength = "10" } } | + attribute units { xsd:string { maxLength = "10" } }) + } & + + element nuclide { + (element name { xsd:string { maxLength = "7" } } | + attribute name { xsd:string { maxLength = "7" } }) & + (element xs { xsd:string { maxLength = "3" } } | + attribute xs { xsd:string { maxLength = "3" } })? & + ( + (element ao { xsd:double } | attribute ao { xsd:double }) | + (element wo { xsd:double } | attribute wo { xsd:double }) + ) + }* & + + element element { + (element name { xsd:string { maxLength = "2" } } | + attribute name { xsd:string { maxLength = "2" } }) & + (element xs { xsd:string { maxLength = "3" } } | + attribute xs { xsd:string { maxLength = "3" } })? & + ( + (element ao { xsd:double } | attribute ao { xsd:double }) | + (element wo { xsd:double } | attribute wo { xsd:double }) + ) + }* & + + element sab { + (element name { xsd:string { maxLength = "7" } } | + attribute name { xsd:string { maxLength = "7" } }) & + (element xs { xsd:string { maxLength = "3" } } | + attribute xs { xsd:string { maxLength = "3" } })? + }* + }+ & + + element default_xs { xsd:string { maxLength = "3" } }? +} diff --git a/src/relaxng/plots.rnc b/src/relaxng/plots.rnc new file mode 100644 index 000000000..3c29ba4e0 --- /dev/null +++ b/src/relaxng/plots.rnc @@ -0,0 +1,31 @@ +element plots { + element plot { + (element id { xsd:int } | attribute id { xsd:int })? & + (element filename { xsd:string { maxLength = "50" } } | + attribute filename { xsd:string { maxLength = "50" } })? & + (element type { "slice" } | attribute type { "slice" })? & + (element color { ( "cell" | "mat" | "material" ) } | + attribute color { ( "cell" | "mat" | "material" ) })? & + (element origin { list { xsd:double+ } } | + attribute origin { list { xsd:double+ } })? & + (element width { list { xsd:double+ } } | + attribute width { list { xsd:double+ } })? & + (element basis { ( "xy" | "yz" | "xz" ) } | + attribute basis { ( "xy" | "yz" | "xz" ) })? & + (element pixels { list { xsd:int+ } } | + attribute pixels { list { xsd:int+ } })? & + (element background { list { xsd:int+ } } | + attribute background { list { xsd:int+ } })? & + element col_spec { + (element id { xsd:int } | attribute id { xsd:int }) & + (element rgb { list { xsd:int+ } } | + attribute rgb { list { xsd:int+ } }) + }* & + element mask { + (element components { list { xsd:int+ } } | + attribute components { list { xsd:int+ } }) & + (element background { list { xsd:int+ } } | + attribute background { list { xsd:int+ } }) + }* + }* +} diff --git a/src/relaxng/settings.rnc b/src/relaxng/settings.rnc new file mode 100644 index 000000000..fd39c5cb5 --- /dev/null +++ b/src/relaxng/settings.rnc @@ -0,0 +1,116 @@ +element settings { + element confidence_intervals { xsd:boolean }? & + + ( + element eigenvalue { + (element batches { xsd:positiveInteger } | + attribute batches { xsd:positiveInteger }) & + (element inactive { xsd:nonNegativeInteger } | + attribute inactive { xsd:nonNegativeInteger }) & + (element particles { xsd:positiveInteger } | + attribute particles { xsd:positiveInteger }) & + (element generations_per_batch { xsd:positiveInteger } | + attribute generations_per_batch { xsd:positiveInteger })? + } | + element fixed_source { + (element batches { xsd:positiveInteger } | + attribute batches { xsd:positiveInteger }) & + (element particles { xsd:positiveInteger } | + attribute particles { xsd:positiveInteger }) + } + ) & + + element cross_sections { xsd:string { maxLength = "255" } }? & + + element cutoff { + (element weight { xsd:double } | attribute weight { xsd:double })? & + (element weight_avg { xsd:double } | attribute weight_avg { xsd:double })? + }? & + + element energy_grid { ( "nuclide" | "union" | "lethargy" ) }? & + + element entropy { + (element dimension { list { xsd:int+ } } | + attribute dimension { list { xsd:int+ } })? & + (element lower_left { list { xsd:double+ } } | + attribute lower_left { list { xsd:double+ } }) & + (element upper_right { list { xsd:double+ } } | + attribute upper_right { list { xsd:double+ } }) + }? & + + element no_reduce { xsd:boolean }? & + + element output { + (element summary { xsd:boolean } | attribute summary { xsd:boolean })? & + (element cross_sections { xsd:boolean } | + attribute cross_sections { xsd:boolean })? & + (element tallies { xsd:boolean } | attribute tallies { xsd:boolean })? + }? & + + element output_path { xsd:string { maxLength = "255" } }? & + + element ptables { xsd:boolean }? & + + element run_cmfd { xsd:boolean }? & + + element seed { xsd:positiveInteger }? & + + element source { + element file { xsd:string { maxLength = "255" } }? & + element space { + (element type { xsd:string { maxLength = "16" } } | + attribute type { xsd:string { maxLength = "16" } }) & + (element length { xsd:int } | attribute length { xsd:int })? & + (element interpolation { xsd:string { maxLength = "10" } } | + attribute interplation { xsd:string { maxLength = "10" } })? & + (element parameters { list { xsd:double+ } } | + attribute parameters { list { xsd:double+ } })? + }? & + element angle { + (element type { xsd:string { maxLength = "16" } } | + attribute type { xsd:string { maxLength = "16" } }) & + (element length { xsd:int } | attribute length { xsd:int })? & + (element interpolation { xsd:string { maxLength = "10" } } | + attribute interplation { xsd:string { maxLength = "10" } })? & + (element parameters { list { xsd:double+ } } | + attribute parameters { list { xsd:double+ } })? + }? & + element energy { + (element type { xsd:string { maxLength = "16" } } | + attribute type { xsd:string { maxLength = "16" } }) & + (element length { xsd:int } | attribute length { xsd:int })? & + (element interpolation { xsd:string { maxLength = "10" } } | + attribute interplation { xsd:string { maxLength = "10" } })? & + (element parameters { list { xsd:double+ } } | + attribute parameters { list { xsd:double+ } })? + }? + }? & + + element state_point { + ( + (element batches { list { xsd:positiveInteger+ } } | + attribute batches { list { xsd:positiveInteger+ } }) | + (element interval { xsd:positiveInteger } | + attribute interval { xsd:positiveInteger }) + ) & + (element source_separate { xsd:boolean } | + attribute source_separate { xsd:boolean })? & + (element source_write { xsd:boolean } | + attribute source_write { xsd:boolean })? + }? & + + element survival_biasing { xsd:boolean }? & + + element trace { list { xsd:positiveInteger+ } }? & + + element verbosity { xsd:positiveInteger }? & + + element uniform_fs{ + (element dimension { list { xsd:positiveInteger+ } } | + attribute dimension { list { xsd:positiveInteger+ } }) & + (element lower_left { list { xsd:double+ } } | + attribute lower_left { list { xsd:double+ } }) & + (element upper_right { list { xsd:double+ } } | + attribute upper_right { list { xsd:double+ } }) + }? +} diff --git a/src/relaxng/tallies.rnc b/src/relaxng/tallies.rnc new file mode 100644 index 000000000..06959276b --- /dev/null +++ b/src/relaxng/tallies.rnc @@ -0,0 +1,43 @@ +element tallies { + element mesh { + (element id { xsd:int } | attribute id { xsd:int }) & + (element type { ( "rectangular" | "hexagonal" ) } | + attribute type { ( "rectangular" | "hexagonal" ) }) & + (element dimension { list { xsd:positiveInteger+ } } | + attribute dimension { list { xsd:positiveInteger+ } }) & + (element lower_left { list { xsd:double+ } } | + attribute lower_left { list { xsd:double+ } }) & + ( + (element upper_right { list { xsd:double+ } } | + attribute upper_right { list { xsd:double+ } }) | + (element width { list { xsd:double+ } } | + attribute width { list { xsd:double+ } }) + ) + }* & + + element tally { + (element id { xsd:int } | attribute id { xsd:int }) & + (element label { xsd:string { maxLength="52" } } | + attribute label { xsd:string { maxLength="52" } })? & + (element estimator { ( "analog" | "tracklength" ) } | + attribute estimator { ( "analog" | "tracklength" ) })? & + element filter { + (element type { ( "cell" | "cellborn" | "material" | "universe" | + "surface" | "mesh" | "energy" | "energyout" ) } | + attribute type { ( "cell" | "cellborn" | "material" | "universe" | + "surface" | "mesh" | "energy" | "energyout" ) }) & + (element bins { list { xsd:string { maxLength = "20" }+ } } | + attribute bins { list { xsd:string { maxLength = "20" }+ } }) & + (element groups { xsd:string { maxLength = "20" }+ } | + attribute groups { xsd:string { maxLength = "20" }+ })? + }* & + element nuclides { + list { xsd:string { maxLength = "12" }+ } + }? & + element scores { + list { xsd:string { maxLength = "20" }+ } + } + }* & + + element assume_separate { xsd:boolean }? +} From dd1fa2946098935393f50670fd42ce681a5f344b Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 23 Aug 2013 12:34:35 -0400 Subject: [PATCH 38/45] alias is now checked with method check_for_node --- src/input_xml.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index ece67151c..d77a0e9a2 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -2936,7 +2936,7 @@ contains ! create dictionary entry for both name and alias call xs_listing_dict % add_key(listing % name, i) - if (listing % alias /= '') then + if (check_for_node(node_ace, "alias")) then call xs_listing_dict % add_key(listing % alias, i) end if end do From e688cfa8ee871fb2688541a6c113fd30b1476a83 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 23 Aug 2013 13:19:51 -0400 Subject: [PATCH 39/45] no alias tag for sab table entry --- src/input_xml.F90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index d77a0e9a2..51ada879e 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1568,7 +1568,6 @@ contains mat % i_sab_tables(j) = index_sab call sab_dict % add_key(name, index_sab) - call sab_dict % add_key(alias, index_sab) else mat % i_sab_tables(j) = sab_dict % get_key(name) end if From bcf2cee9cae381dafc544f390fe833f38a3d7ce6 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 23 Aug 2013 13:28:40 -0400 Subject: [PATCH 40/45] removed other alias variables in sab reading --- src/input_xml.F90 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 51ada879e..4267a9727 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -1544,7 +1544,6 @@ contains end if call get_node_value(node_sab, "name", name) call get_node_value(node_sab, "xs", temp_str) - name = trim(name) // "." // trim(temp_str) mat % sab_names(j) = name @@ -1559,14 +1558,12 @@ contains ! listing index_list = xs_listing_dict % get_key(name) name = xs_listings(index_list) % name - alias = xs_listings(index_list) % alias ! If this S(a,b) table hasn't been encountered yet, we need to add its ! name and alias to the sab_dict if (.not. sab_dict % has_key(name)) then - index_sab = index_sab + 1 + index_sab = index_sab + 1 mat % i_sab_tables(j) = index_sab - call sab_dict % add_key(name, index_sab) else mat % i_sab_tables(j) = sab_dict % get_key(name) From 8723ed61e3ae9c9f3671b273802b9d70f837ae9f Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 29 Aug 2013 14:42:37 -0400 Subject: [PATCH 41/45] fixed cmfd upper_right mesh tag and n_procs_cmfd --- src/cmfd_input.F90 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 26a7a3ae0..3a3a2ff4b 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -228,6 +228,7 @@ contains call create_cmfd_tally(doc) ! set number of CMFD processors and report to user + n_procs_cmfd = 1 if (check_for_node(doc, "n_cmfd_procs")) & call get_node_value(doc, "n_cmfd_procs", n_procs_cmfd) if (master) write(OUTPUT_UNIT,'(A,1X,I0,1X,A)') "CMFD Running on", & @@ -376,6 +377,10 @@ contains & coordinates on a tally mesh." call fatal_error() end if + + ! Set upper right coordinate and width + m % upper_right = rarray3(1:n) + m % width = (m % upper_right - m % lower_left) / real(m % dimension, 8) end if ! Set volume fraction From d4a217a10780142252f1143d265c4c1cdd1d1e23 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Tue, 3 Sep 2013 11:21:51 -0400 Subject: [PATCH 42/45] fixed verbosity input reading --- src/input_xml.F90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/input_xml.F90 b/src/input_xml.F90 index 4267a9727..06c12fad3 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -69,6 +69,7 @@ contains type(Node), pointer :: node_ufs => null() type(Node), pointer :: node_sp => null() type(Node), pointer :: node_output => null() + type(Node), pointer :: node_verb => null() ! Display output message message = "Reading settings XML file..." @@ -219,8 +220,10 @@ contains end select ! Verbosity - if (check_for_node(doc, "verbosity")) & - call get_node_value(doc, "verbosity", verbosity) + if (check_for_node(doc, "verbosity")) then + call get_node_ptr(doc, "verbosity", node_verb) + call get_node_value(node_verb, "value", verbosity) + end if ! ========================================================================== ! EXTERNAL SOURCE From 167226fd88f8becd9099e34e3c702a2b8b6414ff Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Mon, 16 Sep 2013 15:57:18 -0400 Subject: [PATCH 43/45] updated FoX XML source --- src/xml/dom/m_dom_dom.F90 | 22 +- src/xml/dom/m_dom_utils.F90 | 12 +- src/xml/fsys/fox_m_fsys_abort_flush.F90 | 7 +- src/xml/fsys/fox_m_fsys_array_str.F90 | 14 +- src/xml/fsys/fox_m_fsys_format.F90 | 182 +++---- src/xml/fsys/fox_m_fsys_varstr.F90 | 254 +++++++++ src/xml/sax/m_sax_parser.F90 | 658 ++++++++++++------------ src/xml/sax/m_sax_reader.F90 | 11 +- src/xml/sax/m_sax_tokenizer.F90 | 302 +++++------ src/xml/sax/m_sax_types.F90 | 18 +- src/xml/utils/fox_m_utils_uri.F90 | 28 +- src/xml/wxml/FoX_wxml.F90 | 2 + src/xml/wxml/m_wxml_core.F90 | 29 ++ 13 files changed, 913 insertions(+), 626 deletions(-) create mode 100644 src/xml/fsys/fox_m_fsys_varstr.F90 diff --git a/src/xml/dom/m_dom_dom.F90 b/src/xml/dom/m_dom_dom.F90 index 6725f5243..01f6265b0 100644 --- a/src/xml/dom/m_dom_dom.F90 +++ b/src/xml/dom/m_dom_dom.F90 @@ -750,13 +750,13 @@ endif select case(np%nodeType) case (ELEMENT_NODE, ATTRIBUTE_NODE, XPATH_NAMESPACE_NODE) - call destroyElementOrAttribute(np) + call destroyElementOrAttribute(np, ex) case (DOCUMENT_TYPE_NODE) - call destroyDocumentType(np) + call destroyDocumentType(np, ex) case (ENTITY_NODE, NOTATION_NODE) - call destroyEntityOrNotation(np) + call destroyEntityOrNotation(np, ex) case (DOCUMENT_NODE) - call destroyDocument(np) + call destroyDocument(np,ex) end select call destroyNodeContents(np) deallocate(np) @@ -5846,8 +5846,7 @@ endif endif ! Switch off all GC - since this is GC! - call setGCstate(arg, .false.) - + call setGCstate(arg, .false., ex) if (arg%nodeType/=DOCUMENT_NODE) then if (getFoX_checks().or.FoX_INVALID_NODE<200) then call throw_exception(FoX_INVALID_NODE, "destroyDocument", ex) @@ -5874,9 +5873,12 @@ endif if (associated(arg%docExtras%hangingNodes%nodes)) deallocate(arg%docExtras%hangingNodes%nodes) call destroy_xml_doc_state(arg%docExtras%xds) - deallocate(arg%docExtras%xds) - deallocate(arg%docExtras%domConfig) - deallocate(arg%docExtras) + if (present(ex)) then + if (inException(ex)) return + endif + if (associated(arg%docExtras%xds)) deallocate(arg%docExtras%xds) + if (associated(arg%docExtras%domConfig)) deallocate(arg%docExtras%domConfig) + if (associated(arg%docExtras)) deallocate(arg%docExtras) call destroyAllNodesRecursively(arg, except=.true.) @@ -8491,7 +8493,7 @@ endif endif endif - elseif (.not.associated(arg, getOwnerDocument(n))) then + elseif (.not.associated(getOwnerDocument(n), target=arg)) then if (getFoX_checks().or.WRONG_DOCUMENT_ERR<200) then call throw_exception(WRONG_DOCUMENT_ERR, "renameNode", ex) if (present(ex)) then diff --git a/src/xml/dom/m_dom_utils.F90 b/src/xml/dom/m_dom_utils.F90 index f0e443f21..1234c48ad 100644 --- a/src/xml/dom/m_dom_utils.F90 +++ b/src/xml/dom/m_dom_utils.F90 @@ -140,8 +140,11 @@ endif call normalizeDocument(startNode, ex) if (present(ex)) then ! Only possible error should be namespace error ... - if (getExceptionCode(ex)/=NAMESPACE_ERR) then - if (getFoX_checks().or.FoX_INTERNAL_ERROR<200) then + ! 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 @@ -150,8 +153,8 @@ endif endif endif - else - if (getFoX_checks().or.SERIALIZE_ERR<200) then + else + if (getFoX_checks().or.SERIALIZE_ERR<200) then call throw_exception(SERIALIZE_ERR, "serialize", ex) if (present(ex)) then if (inException(ex)) then @@ -160,6 +163,7 @@ endif endif endif + endif endif endif else diff --git a/src/xml/fsys/fox_m_fsys_abort_flush.F90 b/src/xml/fsys/fox_m_fsys_abort_flush.F90 index 612523bb9..29af292fc 100644 --- a/src/xml/fsys/fox_m_fsys_abort_flush.F90 +++ b/src/xml/fsys/fox_m_fsys_abort_flush.F90 @@ -5,6 +5,9 @@ module fox_m_fsys_abort_flush public :: pxfflush public :: pxfabort public :: pure_pxfabort + ! status values to write to stderr on termination + integer, public, parameter :: STDERR_SUCCESS_STATUS = 0 + integer, public, parameter :: STDERR_FAILURE_STATUS = 1 ! pxf.F90 - assortment of Fortran wrappers to various ! unix-y system calls. @@ -44,6 +47,7 @@ CONTAINS use f90_unix_io, only : flush #endif integer, intent(in) :: unit + integer :: i #if defined(F2003) flush(unit) @@ -52,6 +56,7 @@ CONTAINS #elif defined (FC_HAVE_FLUSH) call flush(unit) #else + i= unit ! pacify compiler continue #endif @@ -99,7 +104,7 @@ CONTAINS i=>null() Print*,i #endif - stop + stop STDERR_FAILURE_STATUS end subroutine pxfabort diff --git a/src/xml/fsys/fox_m_fsys_array_str.F90 b/src/xml/fsys/fox_m_fsys_array_str.F90 index b49554832..88d565256 100644 --- a/src/xml/fsys/fox_m_fsys_array_str.F90 +++ b/src/xml/fsys/fox_m_fsys_array_str.F90 @@ -31,15 +31,19 @@ contains pure function str_vs(vs) result(s) character, dimension(:), intent(in) :: vs character(len=size(vs)) :: s -#ifdef PGF90 -!PGI crashes on this use of transfer. Knob-ends. integer :: i + ! Note that we could use s = transfer(vs, s) + ! here and (in principle) this could be an O(1) + ! operation. However, this leakes memory on all + ! recent gfortrans and crashes on some versions of + ! PGF90. So the loop would need to be included with + ! an #ifdef PGF90. In any case, the looping version + ! seems to be quickers (probably due to the character + ! copying being vectorized in the loop). See: + ! http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51175 do i = 1, size(vs) s(i:i) = vs(i) enddo -#else - s = transfer(vs, s) -#endif end function str_vs diff --git a/src/xml/fsys/fox_m_fsys_format.F90 b/src/xml/fsys/fox_m_fsys_format.F90 index 448f44ebd..6d6a6d933 100644 --- a/src/xml/fsys/fox_m_fsys_format.F90 +++ b/src/xml/fsys/fox_m_fsys_format.F90 @@ -52,7 +52,7 @@ module fox_m_fsys_format #ifndef DUMMYLIB interface safestr -! This is for internal use only - no check is made on the validity of +! This is for internal use only - no check is made on the validity of ! any fmt input. module procedure str_string, str_string_array, str_string_matrix, & str_integer, str_integer_array, str_integer_matrix, & @@ -111,13 +111,13 @@ contains #ifndef DUMMYLIB ! NB: The len generic module procedure is used in - ! many initialisation statments (to set the + ! many initialisation statments (to set the ! length of the output string needed for the ! converted number). As of the Fortran 2008 ! spec every specific function belonging to ! a generic used in this way must be defined ! in the module before use. This is enforced - ! by at least version 7.4.4 of the Cray + ! by at least version 7.4.4 of the Cray ! Fortran compiler. Hence we put all the *_len ! functions here at the top of the file. pure function str_string_array_len(st) result(n) @@ -143,7 +143,7 @@ contains pure function str_integer_len(i) result(n) integer, intent(in) :: i integer :: n - + n = int(log10(real(max(abs(i),1)))) + 1 + dim(-i,0)/max(abs(i),1) end function str_integer_len @@ -151,7 +151,7 @@ contains pure function str_integer_base_len(i, b) result(n) integer, intent(in) :: i, b integer :: n - + n = int(log10(real(max(abs(i),1)))/log10(real(b))) & + 1 + dim(-i,0)/max(abs(i),1) @@ -161,7 +161,7 @@ contains integer, intent(in) :: i character(len=*), intent(in) :: fmt integer :: n - + select case (len(fmt)) case(0) n = 0 @@ -179,7 +179,7 @@ contains elseif (verify(fmt(2:), digit)==0) then n = str_to_int_10(fmt(2:)) else - n = 0 + n = 0 endif end select @@ -188,7 +188,7 @@ contains pure function str_integer_array_len(ia) result(n) integer, dimension(:), intent(in) :: ia integer :: n - + integer :: j n = size(ia) - 1 @@ -203,7 +203,7 @@ contains integer, dimension(:), intent(in) :: ia character(len=*), intent(in) :: fmt integer :: n - + integer :: j n = size(ia) - 1 @@ -250,7 +250,7 @@ contains pure function str_logical_len(l) result (n) logical, intent(in) :: l integer :: n - + if (l) then n = 4 else @@ -292,17 +292,17 @@ contains else e = floor(log10(abs(x))) endif - + if (x < 0.0_sp) then n = 1 else n = 0 endif - + if (len(fmt) == 0) then sig = sig_sp - n = n + sig + 2 + len(e) + n = n + sig + 2 + len(e) ! for the decimal point and the e elseif (fmt(1:1) == "s") then @@ -314,9 +314,9 @@ contains sig = max(sig, 1) sig = min(sig, digits(1.0_sp)) - if (sig > 1) n = n + 1 + if (sig > 1) n = n + 1 ! for the decimal point - + n = n + sig + 1 + len(e) elseif (fmt(1:1) == "r") then @@ -376,7 +376,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), fmt) enddo - + end function str_real_sp_array_fmt_len pure function str_real_sp_matrix_fmt_len(xa, fmt) result(n) @@ -420,17 +420,17 @@ contains else e = floor(log10(abs(x))) endif - + if (x < 0.0_dp) then n = 1 else n = 0 endif - + if (len(fmt) == 0) then sig = sig_dp - n = n + sig + 2 + len(e) + n = n + sig + 2 + len(e) ! for the decimal point and the e elseif (fmt(1:1) == "s") then @@ -442,9 +442,9 @@ contains sig = max(sig, 1) sig = min(sig, digits(1.0_dp)) - if (sig > 1) n = n + 1 + if (sig > 1) n = n + 1 ! for the decimal point - + n = n + sig + 1 + len(e) elseif (fmt(1:1) == "r") then @@ -490,7 +490,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), "") enddo - + end function str_real_dp_array_len pure function str_real_dp_array_fmt_len(xa, fmt) result(n) @@ -504,7 +504,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), fmt) enddo - + end function str_real_dp_array_fmt_len pure function str_real_dp_matrix_fmt_len(xa, fmt) result(n) @@ -558,7 +558,7 @@ contains n = size(ca) - 1 do i = 1, size(ca) - n = n + len(ca(i), fmt) + n = n + len(ca(i), fmt) enddo end function str_complex_sp_array_fmt_len @@ -590,7 +590,7 @@ contains n = len(ca, "") end function str_complex_sp_matrix_len - + pure function str_complex_dp_fmt_len(c, fmt) result(n) complex(dp), intent(in) :: c character(len=*), intent(in) :: fmt @@ -644,7 +644,7 @@ contains enddo enddo end function str_complex_dp_matrix_fmt_len - + pure function str_complex_dp_matrix_len(ca) result(n) complex(dp), dimension(:, :), intent(in) :: ca integer :: n @@ -705,7 +705,7 @@ contains ! Error is flagged by returning -1 character(len=*), intent(in) :: str integer :: n - + character(len=len(str)) :: str_l integer :: max_power, i, j @@ -739,7 +739,7 @@ contains endif enddo end function to_lower - + end function str_to_int_16 #endif @@ -747,7 +747,7 @@ contains character(len=*), intent(in) :: st #ifdef DUMMYLIB character(len=1) :: s - s = " " + s = " " #else character(len=len(st)) :: s s = st @@ -762,10 +762,10 @@ contains s = " " #else character(len=str_string_array_len(st)) :: s - + integer :: k, n character(len=1) :: d - + if (present(delimiter)) then d = delimiter else @@ -789,7 +789,7 @@ contains s = " " #else character(len=str_string_matrix_len(st)) :: s - + integer :: j, k, n character(len=1) :: d @@ -853,7 +853,7 @@ contains character :: f integer :: b, ii, j, k, n, ls - + if (len(fmt)>0) then if (fmt(1:1)=="d") then f = 'd' @@ -902,7 +902,7 @@ contains #ifdef DUMMYLIB character(len=1) :: s #else - character(len=len(ia, "d")) :: s + character(len=len(ia(:), "d")) :: s integer :: j, k, n @@ -924,7 +924,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia, fmt)) :: s + character(len=len(ia(:), fmt)) :: s integer :: j, k, n @@ -944,7 +944,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia, "d")) :: s + character(len=len(ia(:,:), "d")) :: s integer :: j, k, n @@ -954,7 +954,7 @@ contains s(n:n+len(ia(j,1))) = " "//str(ia(j,1)) n = n + len(ia(j,1)) + 1 enddo - do k = 2, size(ia, 2) + do k = 2, size(ia, 2) do j = 1, size(ia, 1) s(n:n+len(ia(j,k))) = " "//str(ia(j,k)) n = n + len(ia(j,k)) + 1 @@ -971,7 +971,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia, fmt)) :: s + character(len=len(ia(:,:), fmt)) :: s integer :: j, k, n @@ -981,7 +981,7 @@ contains s(n:n+len(ia(j,1), fmt)) = " "//str(ia(j,1), fmt) n = n + len(ia(j,1), fmt) + 1 enddo - do k = 2, size(ia, 2) + do k = 2, size(ia, 2) do j = 1, size(ia, 1) s(n:n+len(ia(j,k), fmt)) = " "//str(ia(j,k), fmt) n = n + len(ia(j,k), fmt) + 1 @@ -1000,7 +1000,7 @@ contains ! character(len=merge(4,5,l)) :: s ! And g95 (sep2007) cant resolve the generic here character(len=str_logical_len(l)) :: s - + if (l) then s="true" else @@ -1015,8 +1015,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(la)) :: s - + character(len=len(la(:))) :: s + integer :: k, n n = 1 @@ -1044,7 +1044,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(la)) :: s + character(len=len(la(:,:))) :: s integer :: j, k, n @@ -1079,12 +1079,12 @@ contains enddo #endif end function str_logical_matrix - + #ifndef DUMMYLIB ! In order to convert real numbers to strings, we need to - ! perform an internal write - but how long will the + ! perform an internal write - but how long will the ! resultant string be? We don't know & there is no way - ! to discover for an arbitrary format. Therefore, + ! to discover for an arbitrary format. Therefore, ! (if we have the capability; f95 or better) ! we assume it will be less than 100 characters, write ! it to a string of that length, then remove leading & @@ -1094,7 +1094,7 @@ contains ! If we are working with an F90-only compiler, then ! we cannot do this trick - the output string will ! always be 100 chars in length, though we will remove - ! leading whitespace. + ! leading whitespace. ! The standard Fortran format functions do not give us @@ -1104,7 +1104,7 @@ contains ! "r" which will produce output without an exponent, ! and digits after the decimal point. ! or - ! "s": which implies scientific notation, with an + ! "s": which implies scientific notation, with an ! exponent, with significant figures. ! If the integer is absent, then the precision will be ! half of the number of significant figures available @@ -1137,7 +1137,7 @@ contains real(sp) :: x_ if (sig < 1) then - s ="" + s ="" return endif @@ -1156,11 +1156,11 @@ contains enddo n = 1 do k = sig - 2, 0, -1 - ! This baroque way of taking int() ensures the optimizer + ! This baroque way of taking int() ensures the optimizer ! stores it in j without keeping a different value in cache. j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 if (j==10) then - ! This can happen if, on the previous cycle, int(x_) in + ! This can happen if, on the previous cycle, int(x_) in ! the line above gave a result approx. 1.0 less than ! expected. ! In this case we want to quit the cycle & just get 999... to the end @@ -1351,8 +1351,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa)) :: s - + character(len=len(xa(:))) :: s + integer :: j, k, n n = 1 @@ -1369,8 +1369,8 @@ contains pure function str_real_sp_array_fmt(xa, fmt) result(s) real(sp), dimension(:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa, fmt)) :: s - + character(len=len(xa(:), fmt)) :: s + integer :: j, k, n n = 1 @@ -1391,8 +1391,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa, fmt)) :: s - + character(len=len(xa(:), fmt)) :: s + if (checkFmt(fmt)) then s = safestr(xa, fmt) else @@ -1405,7 +1405,7 @@ contains pure function str_real_sp_matrix_fmt(xa, fmt) result(s) real(sp), dimension(:,:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa,fmt)) :: s + character(len=len(xa(:,:),fmt)) :: s integer :: i, j, k, n @@ -1435,7 +1435,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa,fmt)) :: s + character(len=len(xa(:,:),fmt)) :: s if (checkFmt(fmt)) then s = safestr(xa, fmt) @@ -1451,12 +1451,12 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa)) :: s + character(len=len(xa(:,:))) :: s s = safestr(xa, "") #endif end function str_real_sp_matrix - + #ifndef DUMMYLIB pure function real_dp_str(x, sig) result(s) real(dp), intent(in) :: x @@ -1467,7 +1467,7 @@ contains real(dp) :: x_ if (sig < 1) then - s ="" + s ="" return endif @@ -1490,7 +1490,7 @@ contains ! stores it in j without keeping a different value in cache. j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 if (j==10) then - ! This can happen if, on the previous cycle, int(x_) in + ! This can happen if, on the previous cycle, int(x_) in ! the line above gave a result almost exactly 1.0 less than ! expected - but FP arithmetic is not consistent. ! In this case we want to quit the cycle & just get 999... to the end @@ -1682,8 +1682,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa)) :: s - + character(len=len(xa(:))) :: s + integer :: j, k, n n = 1 @@ -1700,8 +1700,8 @@ contains pure function str_real_dp_array_fmt(xa, fmt) result(s) real(dp), dimension(:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa, fmt)) :: s - + character(len=len(xa(:), fmt)) :: s + integer :: j, k, n n = 1 @@ -1722,8 +1722,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa, fmt)) :: s - + character(len=len(xa(:), fmt)) :: s + if (checkFmt(fmt)) then s = safestr(xa, fmt) else @@ -1736,7 +1736,7 @@ contains function str_real_dp_matrix_fmt(xa, fmt) result(s) real(dp), dimension(:,:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa,fmt)) :: s + character(len=len(xa(:,:),fmt)) :: s integer :: i, j, k, n @@ -1766,7 +1766,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa,fmt)) :: s + character(len=len(xa(:,:),fmt)) :: s if (checkFmt(fmt)) then s = safestr(xa, fmt) @@ -1782,7 +1782,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa)) :: s + character(len=len(xa(:,:))) :: s s = safestr(xa, "") #endif @@ -1800,7 +1800,7 @@ contains s = " " #else character(len=len(c, fmt)) :: s - + if (checkFmt(fmt)) then s = safestr(c, fmt) else @@ -1814,7 +1814,7 @@ contains complex(sp), intent(in) :: c character(len=*), intent(in) :: fmt character(len=len(c, fmt)) :: s - + real(sp) :: re, im integer :: i re = real(c) @@ -1841,13 +1841,13 @@ contains pure function str_complex_sp_array_fmt(ca, fmt) result(s) complex(sp), dimension(:), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca, fmt)) :: s + character(len=len(ca(:), fmt)) :: s integer :: i, n - + s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) n = len(ca(1), fmt)+1 - do i = 2, size(ca) + do i = 2, size(ca) s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) n = n + len(ca(i), fmt)+1 enddo @@ -1861,7 +1861,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca, fmt)) :: s + character(len=len(ca(:), fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -1877,7 +1877,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca)) :: s + character(len=len(ca(:))) :: s s = safestr(ca, "") #endif @@ -1887,7 +1887,7 @@ contains pure function str_complex_sp_matrix_fmt(ca, fmt) result(s) complex(sp), dimension(:, :), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca, fmt)) :: s + character(len=len(ca(:,:), fmt)) :: s integer :: i, j, k, n @@ -1917,7 +1917,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca, fmt)) :: s + character(len=len(ca(:,:), fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -1933,7 +1933,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca)) :: s + character(len=len(ca(:,:))) :: s s = safestr(ca, "") #endif @@ -1947,7 +1947,7 @@ contains s = " " #else character(len=len(c, fmt)) :: s - + if (checkFmt(fmt)) then s = safestr(c, fmt) else @@ -1961,7 +1961,7 @@ contains complex(dp), intent(in) :: c character(len=*), intent(in) :: fmt character(len=len(c, fmt)) :: s - + real(dp) :: re, im integer :: i re = real(c) @@ -1988,13 +1988,13 @@ contains pure function str_complex_dp_array_fmt(ca, fmt) result(s) complex(dp), dimension(:), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca, fmt)) :: s + character(len=len(ca(:), fmt)) :: s integer :: i, n s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) n = len(ca(1), fmt)+1 - do i = 2, size(ca) + do i = 2, size(ca) s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) n = n + len(ca(i), fmt)+1 enddo @@ -2008,7 +2008,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca, fmt)) :: s + character(len=len(ca(:), fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -2024,7 +2024,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca)) :: s + character(len=len(ca(:))) :: s s = safestr(ca, "") #endif @@ -2034,7 +2034,7 @@ contains pure function str_complex_dp_matrix_fmt(ca, fmt) result(s) complex(dp), dimension(:, :), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca, fmt)) :: s + character(len=len(ca(:,:), fmt)) :: s integer :: i, j, k, n @@ -2064,7 +2064,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca, fmt)) :: s + character(len=len(ca(:,:), fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -2080,7 +2080,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca)) :: s + character(len=len(ca(:,:))) :: s s = safestr(ca, "") #endif diff --git a/src/xml/fsys/fox_m_fsys_varstr.F90 b/src/xml/fsys/fox_m_fsys_varstr.F90 new file mode 100644 index 000000000..52efa5fde --- /dev/null +++ b/src/xml/fsys/fox_m_fsys_varstr.F90 @@ -0,0 +1,254 @@ +module fox_m_fsys_varstr + implicit none + + private + + public :: varstr + public :: init_varstr + public :: destroy_varstr + public :: varstr_len + public :: is_varstr_empty + public :: set_varstr_empty + public :: is_varstr_null + public :: set_varstr_null + public :: vs_varstr_alloc + public :: move_varstr_vs + public :: move_varstr_varstr + public :: str_varstr + public :: append_varstr + public :: varstr_str + public :: varstr_vs + public :: equal_varstr_str + public :: equal_varstr_varstr + + ! Allocation step in which the data within varstr will be allocated + integer, parameter :: VARSTR_INIT_SIZE=1024 + integer, parameter :: VARSTR_ALLOC_SIZE=1024 + + ! Variable size string type. + ! It is used for token field, so that it can be grown + ! by one character at a time without incurring constant + ! penalty on allocating/deallocating vs data. + ! See functions and subroutines at the end of this module. + type varstr + private + character, dimension(:), pointer :: data + integer :: length + end type varstr + +contains + +! Initialise varstr type. The string is initialised as null (i.e. invalid) +subroutine init_varstr(vstr) + type(varstr), intent(inout) :: vstr + allocate(vstr%data(VARSTR_INIT_SIZE)) + vstr%length = -1 +end subroutine init_varstr + +! Clean up memory (leaves varstr null, and drops the data field) +subroutine destroy_varstr(vstr) + type(varstr), intent(inout) :: vstr + if (associated(vstr%data)) deallocate(vstr%data) + call set_varstr_null(vstr) +end subroutine destroy_varstr + +! Return real length of varstr +function varstr_len(vstr) result(l) + type(varstr), intent(in) :: vstr + integer :: l + if (vstr%length<0) print *, "WARNING: asking for length of null varstr" + l = vstr%length +end function varstr_len + + +! Make sure that varstr is at least size-n. +! The data will be kept (copied) if keep is true (default) +! This can be called on a null varstr, but should noty be called on +! one which was destroyed. +subroutine ensure_varstr_size(vstr,n,keep) + type(varstr), intent(inout) :: vstr + integer, intent(in) :: n + logical, optional, intent(in) :: keep + + character, pointer, dimension(:) :: new_data + integer :: new_size, old_size + logical :: keep_flag + + if (present(keep)) then + keep_flag = keep + else + keep_flag = .true. + end if + + old_size = size(vstr%data) + if (n <= old_size ) return + + new_size = old_size + ((n-old_size)/VARSTR_ALLOC_SIZE+1) * VARSTR_ALLOC_SIZE + allocate(new_data(new_size)) + + if (keep_flag) new_data(1:old_size) = vstr%data(1:old_size) + + deallocate( vstr%data ) + vstr%data => new_data +end subroutine ensure_varstr_size + +! Returns whether varstr is empty: "" +function is_varstr_empty(vstr) + type(varstr), intent(in) :: vstr + logical is_varstr_empty + is_varstr_empty = (vstr%length == 0) +end function is_varstr_empty + +! Set vstr to empty string +subroutine set_varstr_empty(vstr) + type(varstr), intent(inout) :: vstr + vstr%length = 0 +end subroutine set_varstr_empty + +! Returns whether varstr is null (i.e. invalid) +function is_varstr_null(vstr) + type(varstr), intent(in) :: vstr + logical is_varstr_null + is_varstr_null = (vstr%length < 0) +end function is_varstr_null + +! Set vstr to null +subroutine set_varstr_null(vstr) + type(varstr), intent(inout) :: vstr + vstr%length = -1 +end subroutine set_varstr_null + +! Convert varstr to newly allocated array of characters +function vs_varstr_alloc(vstr) result(vs) + type(varstr) :: vstr + character, dimension(:), pointer :: vs + + if (is_varstr_null(vstr)) then + print *, "WARNING: Converting null varstr to string... making it empty first" + call set_varstr_empty(vstr) + end if + + allocate(vs(vstr%length)) + vs = vstr%data(1:vstr%length) +end function vs_varstr_alloc + +! This call moves data from varstr to vs (i.e. vs is overwritten and vstr is made null) +subroutine move_varstr_vs(vstr,vs) + type(varstr), intent(inout) :: vstr + character, dimension(:), pointer, intent(inout) :: vs + + if (associated(vs)) deallocate(vs) + vs => vs_varstr_alloc(vstr) + call set_varstr_null(vstr) +end subroutine move_varstr_vs + +! This call moves data from varstr to varstr (src becomes null) +subroutine move_varstr_varstr(src,dst) + type(varstr), intent(inout) :: src + type(varstr), intent(inout) :: dst + character, dimension(:), pointer :: tmpdata + + tmpdata => dst%data + dst%data => src%data + src%data => tmpdata + dst%length = src%length + + call set_varstr_null(src) +end subroutine move_varstr_varstr + + +! Convert varstr to string type +function str_varstr(vstr) result(s) + type(varstr), intent(in) :: vstr + character(len=vstr%length) :: s + integer :: i + + if (is_varstr_null(vstr)) then + ! Can we really end-up here? Or will it blow on allocation with len=-1 ? + print *, "WARNING: Trying to convert null varstr to str... returning empty string" + s = "" + end if + + do i = 1, vstr%length + s(i:i) = vstr%data(i) + enddo +end function str_varstr + +! Append string to varstr +subroutine append_varstr(vstr,str) + type(varstr), intent(inout) :: vstr + character(len=*), intent(in) :: str + character, dimension(:), pointer :: tmp + integer :: i + + if (is_varstr_null(vstr)) then + print *, "WARNING: Trying to append to null varstr... making it empty first" + call set_varstr_empty(vstr) + end if + + call ensure_varstr_size(vstr,vstr%length+len(str)) + + ! Note: on a XML file with very large tokens, this loop + ! is consistently faster than equivalent 'transfer' intrinsic + do i=1,len(str) + vstr%data(vstr%length+i) = str(i:i) + end do + vstr%length = vstr%length + len(str) +end subroutine append_varstr + +! Convert string to varstr in place +subroutine varstr_str(vstr,str) + type(varstr), intent(inout) :: vstr + character(len=*), intent(in) :: str + integer :: i + + call ensure_varstr_size(vstr,len(str),.false.) + do i=1,len(str) + vstr%data(i) = str(i:i) + end do + vstr%length = len(str) +end subroutine varstr_str + +! Convert character array to varstr in place +subroutine varstr_vs(vstr,vs) + type(varstr), intent(inout) :: vstr + character, dimension(:), intent(in) :: vs + + call ensure_varstr_size(vstr,size(vs),.false.) + + vstr%length = size(vs) + vstr%data(1:size(vs)) = vs +end subroutine varstr_vs + +! Compare varstr to str (true if equal) +function equal_varstr_str(vstr,str) result(r) + type(varstr), intent(in) :: vstr + character(len=*) :: str + logical :: r + + integer :: i + + r = .false. + if ( len(str) /= varstr_len(vstr) ) return + do i=1,len(str) + if ( str(i:i) /= vstr%data(i) ) return + end do + r = .true. +end function equal_varstr_str + +! Compare varstr to varstr (true if equal) +function equal_varstr_varstr(vstr1,vstr2) result(r) + type(varstr), intent(in) :: vstr1, vstr2 + logical :: r + + integer :: i + + r = .false. + if ( varstr_len(vstr1) /= varstr_len(vstr2) ) return + do i=1,varstr_len(vstr1) + if ( vstr1%data(i) /= vstr2%data(i) ) return + end do + r = .true. +end function equal_varstr_varstr + +end module fox_m_fsys_varstr diff --git a/src/xml/sax/m_sax_parser.F90 b/src/xml/sax/m_sax_parser.F90 index 6d63fd046..c2f2283fb 100644 --- a/src/xml/sax/m_sax_parser.F90 +++ b/src/xml/sax/m_sax_parser.F90 @@ -5,6 +5,7 @@ module m_sax_parser use fox_m_fsys_string_list, only: string_list, destroy_string_list, & tokenize_to_string_list, registered_string, init_string_list, & add_string, tokenize_and_add_strings, destroy + use fox_m_fsys_varstr use m_common_attrs, only: init_dict, destroy_dict, reset_dict, & add_item_to_dict, has_key, get_value, get_att_index_pointer, & getLength, setIsId, setBase @@ -44,7 +45,7 @@ module m_sax_parser use m_sax_reader, only: file_buffer_t, pop_buffer_stack, open_new_string, & open_new_file, parse_xml_declaration, parse_text_declaration, & - reading_main_file, reading_first_entity + reading_main_file, reading_first_entity, add_error_position use m_sax_tokenizer, only: sax_tokenize, normalize_attribute_text, & expand_pe_text use m_sax_types ! everything, really @@ -76,7 +77,14 @@ contains nullURI => null() #endif - allocate(fx%token(0)) + call init_varstr(fx%token) + call init_varstr(fx%content) + call init_varstr(fx%name) + call init_varstr(fx%attname) + call init_varstr(fx%PublicId) + call init_varstr(fx%systemId) + call init_varstr(fx%Ndata) + call init_varstr(fx%root_element) call init_error_stack(fx%error_stack) call init_elstack(fx%elstack) @@ -123,8 +131,8 @@ contains fx%context = CTXT_NULL fx%state = ST_NULL - if (associated(fx%token)) deallocate(fx%token) - if (associated(fx%root_element)) deallocate(fx%root_element) + call destroy_varstr(fx%token) + call destroy_varstr(fx%root_element) call destroy_error_stack(fx%error_stack) call destroy_elstack(fx%elstack) @@ -140,13 +148,14 @@ contains call destroy_entity_list(fx%forbidden_pe_list) call destroy_entity_list(fx%predefined_e_list) - if (associated(fx%token)) deallocate(fx%token) - if (associated(fx%content)) deallocate(fx%content) - if (associated(fx%name)) deallocate(fx%name) - if (associated(fx%attname)) deallocate(fx%attname) - if (associated(fx%publicId)) deallocate(fx%publicId) - if (associated(fx%systemId)) deallocate(fx%systemId) - if (associated(fx%Ndata)) deallocate(fx%Ndata) + call destroy_varstr(fx%token) + call destroy_varstr(fx%content) + call destroy_varstr(fx%name) + call destroy_varstr(fx%attname) + call destroy_varstr(fx%publicId) + call destroy_varstr(fx%systemId) + call destroy_varstr(fx%Ndata) + call destroy_varstr(fx%root_element) end subroutine sax_parser_destroy @@ -373,7 +382,7 @@ contains logical :: validCheck, startInCharData_, processDTD, pe, nameOK, eof logical :: namespaces_, namespace_prefixes_, xmlns_uris_, externalEntity_ integer :: i, iostat, temp_i, nextState, ignoreDepth, declSepValue - character, pointer :: tempString(:) + character, pointer :: tempString(:), tempString2(:) character :: dummy type(element_t), pointer :: elem type(attribute_t), pointer :: attDecl @@ -387,6 +396,7 @@ contains nullURI => null() #endif tempString => null() + tempString2 => null() elem => null() attDecl => null() @@ -521,8 +531,7 @@ contains if (fx%state_dtd==ST_DTD_ATTLIST_CONTENTS & .or.fx%state_dtd==ST_DTD_ELEMENT_CONTENTS) then ! stick the token back in contents ... - fx%content => fx%token - fx%token => vs_str_alloc("") + call move_varstr_varstr(fx%token,fx%content) endif if (reading_main_file(fb)) & fx%inIntSubset = .true. @@ -565,7 +574,7 @@ contains select case (fx%state) case (ST_MISC) - !write(*,*) 'ST_MISC', str_vs(fx%token) + !write(*,*) 'ST_MISC', str_varstr(fx%token) select case (fx%tokenType) case (TOK_PI_TAG) wf_stack(1) = wf_stack(1) + 1 @@ -587,7 +596,7 @@ contains case (TOK_OPEN_COMMENT) nextState = ST_START_COMMENT case (TOK_NAME) - if (str_vs(fx%token)=='DOCTYPE') then + if (equal_varstr_str(fx%token,'DOCTYPE')) then fx%context = CTXT_IN_DTD nextState = ST_IN_DOCTYPE endif @@ -599,19 +608,18 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (nameOk) then - if (str_vs(fx%token)=='xml') then + if (equal_varstr_str(fx%token,'xml')) then call add_error(fx%error_stack, "XML declaration must be at start of document") goto 100 - elseif (checkPITarget(str_vs(fx%token), fx%xds%xml_version)) then + elseif (checkPITarget(str_varstr(fx%token), fx%xds%xml_version)) then nextState = ST_PI_CONTENTS - fx%name => fx%token - fx%token => null() - else + call move_varstr_varstr(fx%token,fx%name) + else call add_error(fx%error_stack, "Invalid PI target name") goto 100 endif @@ -631,17 +639,17 @@ contains select case(fx%tokenType) case (TOK_CHAR) if (present(processingInstruction_handler)) then - call processingInstruction_handler(str_vs(fx%name), str_vs(fx%token)) + call processingInstruction_handler(str_varstr(fx%name), str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextState = ST_PI_END case (TOK_PI_END) if (present(processingInstruction_handler)) then - call processingInstruction_handler(str_vs(fx%name), "") + call processingInstruction_handler(str_varstr(fx%name), "") if (fx%state==ST_STOP) goto 100 endif - deallocate(fx%name) + call set_varstr_null(fx%name) if (fx%context==CTXT_IN_CONTENT) then nextState = ST_CHAR_IN_CONTENT else @@ -664,8 +672,7 @@ contains !write(*,*)'ST_START_COMMENT' select case (fx%tokenType) case (TOK_CHAR) - fx%name => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%name) nextState = ST_COMMENT_END end select @@ -682,10 +689,10 @@ contains select case (fx%tokenType) case (TOK_COMMENT_END) if (present(comment_handler)) then - call comment_handler(str_vs(fx%name)) + call comment_handler(str_varstr(fx%name)) if (fx%state==ST_STOP) goto 100 endif - deallocate(fx%name) + call set_varstr_null(fx%name) if (fx%context==CTXT_IN_CONTENT) then nextState = ST_CHAR_IN_CONTENT else @@ -701,16 +708,15 @@ contains .or. fx%context==CTXT_BEFORE_CONTENT & .or. fx%context==CTXT_IN_CONTENT) then if (namespaces_) then - nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkQName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Illegal element name") goto 100 endif - fx%name => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%name) nextState = ST_IN_TAG elseif (fx%context == CTXT_AFTER_CONTENT) then call add_error(fx%error_stack, "Cannot open second root element") @@ -725,7 +731,7 @@ contains !write(*,*) "ST_START_CDATA_DECLARATION" select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token)=="CDATA") then + if (equal_varstr_str(fx%token,"CDATA")) then if (fx%context/=CTXT_IN_CONTENT) then call add_error(fx%error_stack, "CDATA section only allowed in text content.") goto 100 @@ -749,8 +755,7 @@ contains !write(*,*)'ST_CDATA_CONTENTS' select case (fx%tokenType) case (TOK_CHAR) - fx%name => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%name) nextState = ST_CDATA_END end select @@ -770,9 +775,9 @@ contains call startCdata_handler if (fx%state==ST_STOP) goto 100 endif - if (size(fx%name)>0) then + if (.not.is_varstr_empty(fx%name)) then if (present(characters_handler)) then - call characters_handler(str_vs(fx%name)) + call characters_handler(str_varstr(fx%name)) if (fx%state==ST_STOP) goto 100 endif endif @@ -780,7 +785,7 @@ contains call endCdata_handler if (fx%state==ST_STOP) goto 100 endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextState = ST_CHAR_IN_CONTENT end select @@ -789,14 +794,14 @@ contains select case (fx%tokenType) case (TOK_END_TAG) if (fx%context /= CTXT_IN_CONTENT) then - if (associated(fx%root_element)) then + if (.not.is_varstr_null(fx%root_element)) then if (validCheck) then - if (str_vs(fx%name)/=str_vs(fx%root_element)) then + if (.not.equal_varstr_varstr(fx%name,fx%root_element)) then call add_error(fx%error_stack, "Root element name does not match document name") goto 100 endif endif - deallocate(fx%root_element) + call set_varstr_null(fx%root_element) elseif (validCheck) then call add_error(fx%error_stack, "No DTD defined") goto 100 @@ -813,7 +818,7 @@ contains call open_tag if (in_error(fx%error_stack)) goto 100 if (fx%state==ST_STOP) goto 100 - deallocate(fx%name) + call set_varstr_null(fx%name) nextState = ST_CHAR_IN_CONTENT case (TOK_END_TAG_CLOSE) @@ -821,14 +826,14 @@ contains nextState = ST_CHAR_IN_CONTENT else ! only a single element in this doc - if (associated(fx%root_element)) then + if (.not.is_varstr_null(fx%root_element)) then if (validCheck) then - if (str_vs(fx%name)/=str_vs(fx%root_element)) then + if (.not.equal_varstr_varstr(fx%name,fx%root_element)) then call add_error(fx%error_stack, "Root element name does not match document name") goto 100 endif endif - deallocate(fx%root_element) + call set_varstr_null(fx%root_element) elseif (validCheck) then call add_error(fx%error_stack, "No DTD defined") goto 100 @@ -846,7 +851,7 @@ contains call close_tag if (in_error(fx%error_stack)) goto 100 if (fx%state==ST_STOP) goto 100 - deallocate(fx%name) + call set_varstr_null(fx%name) if (fx%context/=CTXT_IN_CONTENT) then fx%well_formed = .true. fx%context = CTXT_AFTER_CONTENT @@ -855,23 +860,22 @@ contains case (TOK_NAME) if (namespaces_) then - nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkQName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Illegal attribute name") goto 100 endif !Have we already had this dictionary item? - if (has_key(fx%attributes, str_vs(fx%token))) then + if (has_key(fx%attributes, str_varstr(fx%token))) then call add_error(fx%error_stack, "Duplicate attribute name") goto 100 endif - fx%attname => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%attname) if (associated(elem)) then - attDecl => get_attribute_declaration(elem, str_vs(fx%attname)) + attDecl => get_attribute_declaration(elem, str_varstr(fx%attname)) else attDecl => null() endif @@ -892,9 +896,11 @@ contains select case (fx%tokenType) case (TOK_CHAR) !First, expand all entities: - tempString => normalize_attribute_text(fx, fx%token) - deallocate(fx%token) - fx%token => tempString + tempString2 => vs_varstr_alloc(fx%token) + tempString => normalize_attribute_text(fx, tempString2, fb) + call varstr_vs( fx%token, tempString ) + deallocate(tempString) + deallocate(tempString2) tempString => null() !If this attribute is not CDATA, we must process further; if (associated(attDecl)) then @@ -903,23 +909,23 @@ contains temp_i = ATT_CDATA endif if (temp_i==ATT_CDATA) then - call add_item_to_dict(fx%attributes, str_vs(fx%attname), & - str_vs(fx%token), itype=ATT_CDATA, declared=associated(attDecl)) + call add_item_to_dict(fx%attributes, str_varstr(fx%attname), & + str_varstr(fx%token), itype=ATT_CDATA, declared=associated(attDecl)) else if (validCheck) then if (fx%xds%standalone.and..not.attDecl%internal & - .and.(str_vs(fx%token)//"x"/=att_value_normalize(str_vs(fx%token))//"x")) then + .and.(str_varstr(fx%token)//"x"/=att_value_normalize(str_varstr(fx%token))//"x")) then call add_error(fx%error_stack, & "Externally-declared attribute value normalization results in changed value "// & "in standalone document") goto 100 endif endif - call add_item_to_dict(fx%attributes, str_vs(fx%attname), & - att_value_normalize(str_vs(fx%token)), itype=temp_i, & + call add_item_to_dict(fx%attributes, str_varstr(fx%attname), & + att_value_normalize(str_varstr(fx%token)), itype=temp_i, & declared=.true.) endif - deallocate(fx%attname) + call set_varstr_null(fx%attname) nextState = ST_IN_TAG end select @@ -927,17 +933,17 @@ contains !write(*,*)'ST_CHAR_IN_CONTENT' select case (fx%tokenType) case (TOK_CHAR) - if (size(fx%token)>0) then + if (varstr_len(fx%token)>0) then if (validCheck) then if (elementContent(fx%elstack)) then - if (verify(str_vs(fx%token), XML_WHITESPACE)==0) then + if (verify(str_varstr(fx%token), XML_WHITESPACE)==0) then if (fx%xds%standalone.and..not.elem%internal) then call add_error(fx%error_stack, & "Externally-specified ignorable whitespace used in standalone document") goto 100 endif if (present(ignorableWhitespace_handler)) then - call ignorableWhitespace_handler(str_vs(fx%token)) + call ignorableWhitespace_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif else @@ -949,13 +955,13 @@ contains goto 100 else if (present(characters_handler)) then - call characters_handler(str_vs(fx%token)) + call characters_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif endif else if (present(characters_handler)) then - call characters_handler(str_vs(fx%token)) + call characters_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif endif @@ -998,15 +1004,15 @@ contains 'Encountered reference to undeclared entity') endif endif - ent => getEntityByName(fx%forbidden_ge_list, str_vs(fx%token)) + ent => getEntityByName(fx%forbidden_ge_list, str_varstr(fx%token)) if (associated(ent)) then call add_error(fx%error_stack, 'Recursive entity reference') goto 100 endif - ent => getEntityByName(fx%predefined_e_list, str_vs(fx%token)) + ent => getEntityByName(fx%predefined_e_list, str_varstr(fx%token)) if (associated(ent)) then if (present(startEntity_handler)) then - call startEntity_handler(str_vs(fx%token)) + call startEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif if (validCheck) then @@ -1019,15 +1025,15 @@ contains endif endif if (present(characters_handler)) then - call characters_handler(expand_entity(fx%predefined_e_list, str_vs(fx%token))) + call characters_handler(expand_entity(fx%predefined_e_list, str_varstr(fx%token))) if (fx%state==ST_STOP) goto 100 endif if (present(endEntity_handler)) then - call endEntity_handler(str_vs(fx%token)) + call endEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif - elseif (likeCharacterEntityReference(str_vs(fx%token))) then - if (checkRepCharEntityReference(str_vs(fx%token), fx%xds%xml_version)) then + elseif (likeCharacterEntityReference(str_varstr(fx%token))) then + if (checkRepCharEntityReference(str_varstr(fx%token), fx%xds%xml_version)) then if (validCheck) then if (associated(elem)) then if (.not.elem%mixed.and..not.elem%any) then @@ -1038,18 +1044,18 @@ contains endif endif if (present(characters_handler)) then - call characters_handler(expand_char_entity(str_vs(fx%token))) + call characters_handler(expand_char_entity(str_varstr(fx%token))) if (fx%state==ST_STOP) goto 100 endif - elseif (checkCharacterEntityReference(str_vs(fx%token), fx%xds%xml_version)) then + elseif (checkCharacterEntityReference(str_varstr(fx%token), fx%xds%xml_version)) then call add_error(fx%error_stack, "Unable to digest character entity reference in content, sorry.") goto 100 else call add_error(fx%error_stack, "Illegal character reference") goto 100 endif - elseif (existing_entity(fx%xds%entityList, str_vs(fx%token))) then - ent => getEntityByName(fx%xds%entityList, str_vs(fx%token)) + elseif (existing_entity(fx%xds%entityList, str_varstr(fx%token))) then + ent => getEntityByName(fx%xds%entityList, str_varstr(fx%token)) if (ent%wfc.and.fx%xds%standalone) then call add_error(fx%error_stack, & 'Externally declared entity referenced in standalone document') @@ -1063,22 +1069,22 @@ contains if (iostat/=0) then if (validCheck) then call add_error(fx%error_stack, & - "Unable to retrieve external entity "//str_vs(fx%token)) + "Unable to retrieve external entity "//str_varstr(fx%token)) goto 100 endif if (present(skippedEntity_handler)) then - call skippedEntity_handler(str_vs(fx%token)) + call skippedEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif else if (present(startEntity_handler)) then - call startEntity_handler(str_vs(fx%token)) + call startEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif #ifdef PGF90 - call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", nullURI, .false.) + call add_internal_entity(fx%forbidden_ge_list, str_varstr(fx%token), "", nullURI, .false.) #else - call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", null(), .false.) + call add_internal_entity(fx%forbidden_ge_list, str_varstr(fx%token), "", null(), .false.) #endif temp_wf_stack => wf_stack allocate(wf_stack(size(temp_wf_stack)+1)) @@ -1100,15 +1106,16 @@ contains endif endif if (present(startEntity_handler)) then - call startEntity_handler(str_vs(fx%token)) + call startEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif #ifdef PGF90 - call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", nullURI, .false.) + call add_internal_entity(fx%forbidden_ge_list, str_varstr(fx%token), "", nullURI, .false.) #else - call add_internal_entity(fx%forbidden_ge_list, str_vs(fx%token), "", null(), .false.) + call add_internal_entity(fx%forbidden_ge_list, str_varstr(fx%token), "", null(), .false.) #endif - call open_new_string(fb, expand_entity(fx%xds%entityList, str_vs(fx%token)), str_vs(fx%token), baseURI=ent%baseURI) + call open_new_string(fb, expand_entity(fx%xds%entityList, str_varstr(fx%token)), & + str_varstr(fx%token), baseURI=ent%baseURI) temp_wf_stack => wf_stack allocate(wf_stack(size(temp_wf_stack)+1)) wf_stack = (/0, temp_wf_stack/) @@ -1118,7 +1125,7 @@ contains ! Unknown entity check standalone etc if (fx%skippedExternal.and..not.fx%xds%standalone) then if (present(skippedEntity_handler)) then - call skippedEntity_handler(str_vs(fx%token)) + call skippedEntity_handler(str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif else @@ -1133,9 +1140,8 @@ contains !write(*,*)'ST_CLOSING_TAG' select case (fx%tokenType) case (TOK_NAME) - if (checkName(str_vs(fx%token), fx%xds%xml_version)) then - fx%name => fx%token - nullify(fx%token) + if (checkName(str_varstr(fx%token), fx%xds%xml_version)) then + call move_varstr_varstr(fx%token,fx%name) nextState = ST_IN_CLOSING_TAG else call add_error(fx%error_stack, "Closing tag: expecting a Name") @@ -1150,7 +1156,7 @@ contains call close_tag if (in_error(fx%error_stack)) goto 100 if (fx%state==ST_STOP) goto 100 - deallocate(fx%name) + call set_varstr_null(fx%name) if (is_empty(fx%elstack)) then if (startInCharData_) then fx%well_formed = .true. @@ -1175,31 +1181,30 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkQName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Invalid document name") goto 100 endif - fx%root_element => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%root_element) nextState = ST_DOC_NAME end select case (ST_DOC_NAME) - !write(*,*) 'ST_DOC_NAME ', str_vs(fx%token) + !write(*,*) 'ST_DOC_NAME ', str_varstr(fx%token) select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token)=='SYSTEM') then + if (equal_varstr_str(fx%token,'SYSTEM')) then nextState = ST_DOC_SYSTEM - elseif (str_vs(fx%token)=='PUBLIC') then + elseif (equal_varstr_str(fx%token,'PUBLIC')) then nextState = ST_DOC_PUBLIC endif case (TOK_OPEN_SB) if (present(startDTD_handler)) then - call startDTD_handler(str_vs(fx%root_element), "", "") + call startDTD_handler(str_varstr(fx%root_element), "", "") if (fx%state==ST_STOP) goto 100 endif wf_stack(1) = wf_stack(1) + 1 @@ -1207,7 +1212,7 @@ contains fx%inIntSubset = .true. case (TOK_END_TAG) if (present(startDTD_handler)) then - call startDTD_handler(str_vs(fx%root_element), "", "") + call startDTD_handler(str_varstr(fx%root_element), "", "") if (fx%state==ST_STOP) goto 100 endif wf_stack(1) = wf_stack(1) - 1 @@ -1225,9 +1230,8 @@ contains !write(*,*) 'ST_DOC_PUBLIC' select case (fx%tokenType) case (TOK_CHAR) - if (checkPublicId(str_vs(fx%token))) then - fx%publicId => fx%token - fx%token => null() + if (checkPublicId(str_varstr(fx%token))) then + call move_varstr_varstr(fx%token,fx%publicId) nextState = ST_DOC_SYSTEM else call add_error(fx%error_stack, "Invalid document public id") @@ -1239,8 +1243,7 @@ contains !write(*,*) 'ST_DOC_SYSTEM' select case (fx%tokenType) case (TOK_CHAR) - fx%systemId => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%systemId) nextState = ST_DOC_DECL end select @@ -1249,39 +1252,39 @@ contains select case (fx%tokenType) case (TOK_OPEN_SB) if (present(startDTD_handler)) then - if (associated(fx%publicId)) then - call startDTD_handler(str_vs(fx%root_element), & - publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) - elseif (associated(fx%systemId)) then - call startDTD_handler(str_vs(fx%root_element), & - publicId="", systemId=str_vs(fx%systemId)) + if (.not.is_varstr_null(fx%publicId)) then + call startDTD_handler(str_varstr(fx%root_element), & + publicId=str_varstr(fx%publicId), systemId=str_varstr(fx%systemId)) + elseif (.not.is_varstr_null(fx%systemId)) then + call startDTD_handler(str_varstr(fx%root_element), & + publicId="", systemId=str_varstr(fx%systemId)) else - call startDTD_handler(str_vs(fx%root_element), "", "") + call startDTD_handler(str_varstr(fx%root_element), "", "") endif if (fx%state==ST_STOP) goto 100 endif - if (associated(fx%systemId)) then - extSubsetURI => parseURI(str_vs(fx%systemId)) - deallocate(fx%systemId) + if (.not.is_varstr_null(fx%systemId)) then + extSubsetURI => parseURI(str_varstr(fx%systemId)) + call set_varstr_null(fx%systemId) endif - if (associated(fx%publicId)) deallocate(fx%publicId) + call set_varstr_null(fx%publicId) fx%inIntSubset = .true. wf_stack(1) = wf_stack(1) + 1 nextState = ST_IN_SUBSET case (TOK_END_TAG) if (present(startDTD_handler)) then - if (associated(fx%publicId)) then - call startDTD_handler(str_vs(fx%root_element), publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) - deallocate(fx%publicId) - elseif (associated(fx%systemId)) then - call startDTD_handler(str_vs(fx%root_element), publicId="", systemId=str_vs(fx%systemId)) + if (.not.is_varstr_null(fx%publicId)) then + call startDTD_handler(str_varstr(fx%root_element), publicId=str_varstr(fx%publicId), systemId=str_varstr(fx%systemId)) + call set_varstr_null(fx%publicId) + elseif (.not.is_varstr_null(fx%systemId)) then + call startDTD_handler(str_varstr(fx%root_element), publicId="", systemId=str_varstr(fx%systemId)) else - call startDTD_handler(str_vs(fx%root_element), "", "") + call startDTD_handler(str_varstr(fx%root_element), "", "") endif if (fx%state==ST_STOP) goto 100 endif - if (associated(fx%systemId)) then - extSubsetURI => parseURI(str_vs(fx%systemId)) + if (.not.is_varstr_null(fx%systemId)) then + extSubsetURI => parseURI(str_varstr(fx%systemId)) if (.not.associated(extSubsetURI)) then call add_error(fx%error_stack, "Invalid URI specified for DTD SYSTEM") goto 100 @@ -1300,7 +1303,7 @@ contains else if (validCheck) then call add_error(fx%error_stack, & - "Unable to retrieve external subset "//str_vs(fx%systemId)) + "Unable to retrieve external subset "//str_varstr(fx%systemId)) goto 100 endif call endDTDchecks @@ -1317,8 +1320,8 @@ contains fx%context = CTXT_BEFORE_CONTENT nextState = ST_MISC endif - if (associated(fx%systemId)) deallocate(fx%systemId) - if (associated(fx%publicId)) deallocate(fx%publicId) + call set_varstr_null(fx%systemId) + call set_varstr_null(fx%publicId) case default call add_error(fx%error_stack, "Unexpected token in DTD") goto 100 @@ -1385,12 +1388,12 @@ contains !write(*,*) 'ST_START_PE' select case (fx%tokenType) case (TOK_NAME) - if (existing_entity(fx%forbidden_pe_list, str_vs(fx%token))) then + if (existing_entity(fx%forbidden_pe_list, str_varstr(fx%token))) then call add_error(fx%error_stack, & 'Recursive entity reference') goto 100 endif - ent => getEntityByName(fx%xds%PEList, str_vs(fx%token)) + ent => getEntityByName(fx%xds%PEList, str_varstr(fx%token)) if (associated(ent)) then if (ent%wfc.and.fx%xds%standalone) then call add_error(fx%error_stack, & @@ -1398,25 +1401,25 @@ contains goto 100 elseif (ent%external) then if (present(startEntity_handler)) then - call startEntity_handler('%'//str_vs(fx%token)) + call startEntity_handler('%'//str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif #ifdef PGF90 call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", nullURI, .false.) + str_varstr(fx%token), "", nullURI, .false.) #else call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", null(), .false.) + str_varstr(fx%token), "", null(), .false.) #endif call open_new_file(fb, ent%baseURI, iostat, pe=.true.) if (iostat/=0) then if (validCheck) then call add_error(fx%error_stack, & - "Unable to retrieve external parameter entity "//str_vs(fx%token)) + "Unable to retrieve external parameter entity "//str_varstr(fx%token)) goto 100 endif if (present(skippedEntity_handler)) then - call skippedEntity_handler('%'//str_vs(fx%token)) + call skippedEntity_handler('%'//str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif ! having skipped a PE, we must now not process @@ -1427,15 +1430,15 @@ contains else fx%inIntSubset = .false. if (present(startEntity_handler)) then - call startEntity_handler('%'//str_vs(fx%token)) + call startEntity_handler('%'//str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif #ifdef PGF90 call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", nullURI, .false.) + str_varstr(fx%token), "", nullURI, .false.) #else call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", null(), .false.) + str_varstr(fx%token), "", null(), .false.) #endif call parse_text_declaration(fb, fx%error_stack) if (in_error(fx%error_stack)) goto 100 @@ -1449,19 +1452,19 @@ contains else ! Expand the entity, if (present(startEntity_handler)) then - call startEntity_handler('%'//str_vs(fx%token)) + call startEntity_handler('%'//str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif #ifdef PGF90 call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", nullURI, .false.) + str_varstr(fx%token), "", nullURI, .false.) call open_new_string(fb, & - expand_entity(fx%xds%PEList, str_vs(fx%token)), str_vs(fx%token), baseURI=nullURI, pe=.true.) + expand_entity(fx%xds%PEList, str_varstr(fx%token)), str_varstr(fx%token), baseURI=nullURI, pe=.true.) #else call add_internal_entity(fx%forbidden_pe_list, & - str_vs(fx%token), "", null(), .false.) + str_varstr(fx%token), "", null(), .false.) call open_new_string(fb, & - expand_entity(fx%xds%PEList, str_vs(fx%token)), str_vs(fx%token), baseURI=null(), pe=.true.) + expand_entity(fx%xds%PEList, str_varstr(fx%token)), str_varstr(fx%token), baseURI=null(), pe=.true.) #endif ! NB because we are just expanding a string here, anything ! evaluated as a result of this string is evaluated in the @@ -1480,7 +1483,7 @@ contains if (fx%skippedExternal.and..not.fx%xds%standalone) then if (processDTD) then if (present(skippedEntity_handler)) then - call skippedEntity_handler('%'//str_vs(fx%token)) + call skippedEntity_handler('%'//str_varstr(fx%token)) if (fx%state==ST_STOP) goto 100 endif endif @@ -1500,13 +1503,16 @@ contains if (nextState/=ST_NULL) then fx%state = nextState else - call add_error(fx%error_stack, "Internal error in parser - no suitable token found") + call add_error(fx%error_stack, "Internal error in parser - no suitable token found.") goto 100 endif end do -100 if (associated(tempString)) deallocate(tempString) +100 continue + if (in_error(fx%error_stack)) call add_error_position(fx%error_stack, fb) + if (associated(tempString)) deallocate(tempString) + if (associated(tempString2)) deallocate(tempString2) if (associated(extSubsetURI)) call destroyURI(extSubsetURI) call destroy_string_list(id_list) call destroy_string_list(idref_list) @@ -1530,9 +1536,13 @@ contains ! EOF of main file if (startInChardata_) then if (fx%well_formed) then - if (fx%state==ST_CHAR_IN_CONTENT.and.associated(fx%token)) then - if (size(fx%token)>0.and.present(characters_handler)) & - call characters_handler(str_vs(fx%token)) + !Note: it used to be as follows: + !if (fx%state==ST_CHAR_IN_CONTENT.and.associated(fx%token%data)) then + !It is probably safe now not to check if token%data is allocated, as in case it is not, + !token%length should be -1... but if it crashes, you know the culprit. + if (fx%state==ST_CHAR_IN_CONTENT) then + if (varstr_len(fx%token)>0.and.present(characters_handler)) & + call characters_handler(str_varstr(fx%token)) endif else if (present(fatalError_handler)) & @@ -1601,13 +1611,13 @@ contains case (TOK_OPEN_COMMENT) nextDTDState = ST_DTD_START_COMMENT case (TOK_NAME) - if (str_vs(fx%token)=='ATTLIST') then + if (equal_varstr_str(fx%token,'ATTLIST')) then nextDTDState = ST_DTD_ATTLIST - elseif (str_vs(fx%token)=='ELEMENT') then + elseif (equal_varstr_str(fx%token,'ELEMENT')) then nextDTDState = ST_DTD_ELEMENT - elseif (str_vs(fx%token)=='ENTITY') then + elseif (equal_varstr_str(fx%token,'ENTITY')) then nextDTDState = ST_DTD_ENTITY - elseif (str_vs(fx%token)=='NOTATION') then + elseif (equal_varstr_str(fx%token,'NOTATION')) then nextDTDState = ST_DTD_NOTATION endif end select @@ -1616,7 +1626,7 @@ contains !write(*,*) "ST_DTD_START_SECTION_DECL" select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token)=="IGNORE") then + if (equal_varstr_str(fx%token,"IGNORE")) then if (fx%context/=CTXT_IN_DTD.or.reading_main_file(fb)) then call add_error(fx%error_stack, "IGNORE section only allowed in external subset.") return @@ -1625,7 +1635,7 @@ contains fx%context = CTXT_IGNORE nextDTDState = ST_DTD_FINISH_SECTION_DECL endif - elseif (str_vs(fx%token)=="INCLUDE") then + elseif (equal_varstr_str(fx%token,"INCLUDE")) then if (fx%context/=CTXT_IN_DTD.or.reading_main_file(fb)) then call add_error(fx%error_stack, "INCLUDE section only allowed in external subset.") return @@ -1675,18 +1685,17 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (nameOk) then - if (str_vs(fx%token)=='xml') then + if (equal_varstr_str(fx%token,'xml')) then call add_error(fx%error_stack, "XML declaration must be at start of document") return - elseif (checkPITarget(str_vs(fx%token), fx%xds%xml_version)) then + elseif (checkPITarget(str_varstr(fx%token), fx%xds%xml_version)) then nextDTDState = ST_DTD_PI_CONTENTS - fx%name => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%name) else call add_error(fx%error_stack, "Invalid PI target name") return @@ -1717,17 +1726,17 @@ contains select case(fx%tokenType) case (TOK_CHAR) if (present(processingInstruction_handler)) then - call processingInstruction_handler(str_vs(fx%name), str_vs(fx%token)) + call processingInstruction_handler(str_varstr(fx%name), str_varstr(fx%token)) if (fx%state==ST_STOP) return endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextDTDState = ST_DTD_PI_END case (TOK_PI_END) if (present(processingInstruction_handler)) then - call processingInstruction_handler(str_vs(fx%name), '') + call processingInstruction_handler(str_varstr(fx%name), '') if (fx%state==ST_STOP) return endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextDTDState = ST_DTD_SUBSET end select @@ -1742,8 +1751,7 @@ contains !write(*,*)'ST_DTD_START_COMMENT' select case (fx%tokenType) case (TOK_CHAR) - fx%name => fx%token - nullify(fx%token) + call move_varstr_varstr(fx%token,fx%name) nextDTDState = ST_DTD_COMMENT_END end select @@ -1770,10 +1778,10 @@ contains select case (fx%tokenType) case (TOK_COMMENT_END) if (present(comment_handler)) then - call comment_handler(str_vs(fx%name)) + call comment_handler(str_varstr(fx%name)) if (fx%state==ST_STOP) return endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextDTDState = ST_DTD_SUBSET end select @@ -1782,18 +1790,18 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkQName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Invalid element name for ATTLIST") return endif - if (existing_element(fx%xds%element_list, str_vs(fx%token))) then - elem => get_element(fx%xds%element_list, str_vs(fx%token)) + if (existing_element(fx%xds%element_list, str_varstr(fx%token))) then + elem => get_element(fx%xds%element_list, str_varstr(fx%token)) else - elem => add_element(fx%xds%element_list, str_vs(fx%token)) + elem => add_element(fx%xds%element_list, str_varstr(fx%token)) endif nextDTDState = ST_DTD_ATTLIST_CONTENTS end select @@ -1807,16 +1815,16 @@ contains nextState = ST_START_PE case (TOK_DTD_CONTENTS) if (processDTD) then - call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + call parse_dtd_attlist(str_varstr(fx%token), fx%xds%xml_version, & namespaces_, validCheck, fx%error_stack, elem, & internal=reading_main_file(fb)) else #ifdef PGF90 - call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + call parse_dtd_attlist(str_varstr(fx%token), fx%xds%xml_version, & namespaces_, validCheck, fx%error_stack, nullElement, & internal=reading_main_file(fb)) #else - call parse_dtd_attlist(str_vs(fx%token), fx%xds%xml_version, & + call parse_dtd_attlist(str_varstr(fx%token), fx%xds%xml_version, & namespaces_, validCheck, fx%error_stack, null(), & internal=reading_main_file(fb)) #endif @@ -1828,7 +1836,7 @@ contains if (associated(elem%attlist%list(i)%default)) then tempString => elem%attlist%list(i)%default elem%attlist%list(i)%default => & - normalize_attribute_text(fx, tempString) + normalize_attribute_text(fx, tempString, fb) deallocate(tempString) if (in_error(fx%error_stack)) return endif @@ -1894,16 +1902,15 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkQName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkQName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Invalid name for ELEMENT") return endif - fx%name => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%name) nextDTDState = ST_DTD_ELEMENT_CONTENTS end select @@ -1923,7 +1930,7 @@ contains ! Leave DTD state as it is & expand the entity ... nextState = ST_START_PE case (TOK_DTD_CONTENTS) - if (declared_element(fx%xds%element_list, str_vs(fx%name))) then + if (declared_element(fx%xds%element_list, str_varstr(fx%name))) then if (validCheck) then call add_error(fx%error_stack, "Duplicate Element declaration") return @@ -1932,15 +1939,15 @@ contains elem => null() endif elseif (processDTD) then - if (existing_element(fx%xds%element_list, str_vs(fx%name))) then - elem => get_element(fx%xds%element_list, str_vs(fx%name)) + if (existing_element(fx%xds%element_list, str_varstr(fx%name))) then + elem => get_element(fx%xds%element_list, str_varstr(fx%name)) else - elem => add_element(fx%xds%element_list, str_vs(fx%name)) + elem => add_element(fx%xds%element_list, str_varstr(fx%name)) endif else elem => null() endif - call parse_dtd_element(str_vs(fx%token), fx%xds%xml_version, fx%error_stack, & + call parse_dtd_element(str_varstr(fx%token), fx%xds%xml_version, fx%error_stack, & elem, reading_main_file(fb)) if (in_error(fx%error_stack)) return nextDTDState = ST_DTD_ELEMENT_END @@ -1960,11 +1967,11 @@ contains wf_stack(1) = wf_stack(1) - 1 if (processDTD.and.associated(elem)) then if (present(elementDecl_handler)) then - call elementDecl_handler(str_vs(fx%name), str_vs(elem%model)) + call elementDecl_handler(str_varstr(fx%name), str_vs(elem%model)) if (fx%state==ST_STOP) return endif endif - deallocate(fx%name) + call set_varstr_null(fx%name) nextDTDState = ST_DTD_SUBSET end select @@ -1972,24 +1979,23 @@ contains !write(*,*) 'ST_DTD_ENTITY' select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token)=="%") then + if (equal_varstr_str(fx%token,"%")) then pe = .true. ! this will be a PE nextDTDState = ST_DTD_ENTITY_PE else pe = .false. if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, & "Illegal name for general entity") return endif - fx%name => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%name) nextDTDState = ST_DTD_ENTITY_ID endif end select @@ -1999,17 +2005,16 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, & "Illegal name for parameter entity") return endif - fx%name => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%name) nextDTDState = ST_DTD_ENTITY_ID end select @@ -2017,9 +2022,9 @@ contains !write(*,*) 'ST_DTD_ENTITY_ID' select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token) == "PUBLIC") then + if (equal_varstr_str(fx%token,"PUBLIC")) then nextDTDState = ST_DTD_ENTITY_PUBLIC - elseif (str_vs(fx%token) == "SYSTEM") then + elseif (equal_varstr_str(fx%token,"SYSTEM")) then nextDTDState = ST_DTD_ENTITY_SYSTEM else call add_error(fx%error_stack, "Unexpected token in ENTITY") @@ -2027,11 +2032,15 @@ contains endif case (TOK_CHAR) if (reading_main_file(fb)) then - tempString => fx%token + tempString => vs_varstr_alloc(fx%token) else - tempString => expand_pe_text(fx, fx%token, fb) + tempString2 => vs_varstr_alloc(fx%token) + tempString => expand_pe_text(fx, tempString2, fb) + deallocate(tempString2) endif - fx%attname => expand_entity_value_alloc(tempString, fx%xds, fx%error_stack) + tempString2 => expand_entity_value_alloc(tempString, fx%xds, fx%error_stack) + call varstr_vs( fx%attname, tempString2 ) + deallocate(tempString2) if (reading_main_file(fb)) then tempString => null() else @@ -2048,9 +2057,8 @@ contains !write(*,*) 'ST_DTD_ENTITY_PUBLIC' select case (fx%tokenType) case (TOK_CHAR) - if (checkPublicId(str_vs(fx%token))) then - fx%publicId => fx%token - fx%token => null() + if (checkPublicId(str_varstr(fx%token))) then + call move_varstr_varstr(fx%token,fx%publicId) nextDTDState = ST_DTD_ENTITY_SYSTEM else call add_error(fx%error_stack, "Invalid PUBLIC id in ENTITY") @@ -2065,8 +2073,7 @@ contains !write(*,*) 'ST_DTD_ENTITY_SYSTEM' select case (fx%tokenType) case (TOK_CHAR) - fx%systemId => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%systemId) nextDTDState = ST_DTD_ENTITY_NDATA case default call add_error(fx%error_stack, "Unexpected token in ENTITY") @@ -2090,14 +2097,14 @@ contains if (in_error(fx%error_stack)) return if (fx%state==ST_STOP) return endif - deallocate(fx%name) - if (associated(fx%attname)) deallocate(fx%attname) - if (associated(fx%systemId)) deallocate(fx%systemId) - if (associated(fx%publicId)) deallocate(fx%publicId) - if (associated(fx%Ndata)) deallocate(fx%Ndata) + call set_varstr_null(fx%name) + call set_varstr_null(fx%attname) + call set_varstr_null(fx%systemId) + call set_varstr_null(fx%publicId) + call set_varstr_null(fx%Ndata) nextDTDState = ST_DTD_SUBSET case (TOK_NAME) - if (str_vs(fx%token)=='NDATA') then + if (equal_varstr_str(fx%token,'NDATA')) then if (pe) then call add_error(fx%error_stack, "Parameter entity cannot have NDATA declaration") return @@ -2118,16 +2125,15 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Invalid name for Notation") return endif - fx%Ndata => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%Ndata) nextDTDState = ST_DTD_ENTITY_END case default call add_error(fx%error_stack, "Unexpected token in ENTITY") @@ -2151,11 +2157,11 @@ contains if (in_error(fx%error_stack)) return if (fx%state==ST_STOP) return endif - deallocate(fx%name) - if (associated(fx%attname)) deallocate(fx%attname) - if (associated(fx%systemId)) deallocate(fx%systemId) - if (associated(fx%publicId)) deallocate(fx%publicId) - if (associated(fx%Ndata)) deallocate(fx%Ndata) + call set_varstr_null(fx%name) + call set_varstr_null(fx%attname) + call set_varstr_null(fx%systemId) + call set_varstr_null(fx%publicId) + call set_varstr_null(fx%Ndata) nextDTDState = ST_DTD_SUBSET case default call add_error(fx%error_stack, "Unexpected token at end of ENTITY") @@ -2167,16 +2173,15 @@ contains select case (fx%tokenType) case (TOK_NAME) if (namespaces_) then - nameOk = checkNCName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkNCName(str_varstr(fx%token), fx%xds%xml_version) else - nameOk = checkName(str_vs(fx%token), fx%xds%xml_version) + nameOk = checkName(str_varstr(fx%token), fx%xds%xml_version) endif if (.not.nameOk) then call add_error(fx%error_stack, "Invalid name for Notation") return endif - fx%name => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%name) nextDTDState = ST_DTD_NOTATION_ID case default call add_error(fx%error_stack, "Unexpected token in NOTATION") @@ -2187,9 +2192,9 @@ contains !write(*,*)'ST_DTD_NOTATION_ID' select case (fx%tokenType) case (TOK_NAME) - if (str_vs(fx%token)=='SYSTEM') then + if (equal_varstr_str(fx%token,'SYSTEM')) then nextDTDState = ST_DTD_NOTATION_SYSTEM - elseif (str_vs(fx%token)=='PUBLIC') then + elseif (equal_varstr_str(fx%token,'PUBLIC')) then nextDTDState = ST_DTD_NOTATION_PUBLIC else call add_error(fx%error_stack, "Unexpected token after NOTATION") @@ -2204,8 +2209,7 @@ contains !write(*,*)'ST_DTD_NOTATION_SYSTEM' select case (fx%tokenType) case (TOK_CHAR) - fx%systemId => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%systemId) nextDTDState = ST_DTD_NOTATION_END case default call add_error(fx%error_stack, "Unexpected token in NOTATION") @@ -2216,9 +2220,8 @@ contains !write(*,*)'ST_DTD_NOTATION_PUBLIC' select case (fx%tokenType) case (TOK_CHAR) - if (checkPublicId(str_vs(fx%token))) then - fx%publicId => fx%token - fx%token => null() + if (checkPublicId(str_varstr(fx%token))) then + call move_varstr_varstr(fx%token,fx%publicId) nextDTDState = ST_DTD_NOTATION_PUBLIC_2 else call add_error(fx%error_stack, "Invalid PUBLIC id in NOTATION") @@ -2239,25 +2242,24 @@ contains "NOTATION not balanced in parameter entity") return endif - if (notation_exists(fx%nlist, str_vs(fx%name))) then + if (notation_exists(fx%nlist, str_varstr(fx%name))) then call add_error(fx%error_stack, "Duplicate notation declaration") return endif endif wf_stack(1) = wf_stack(1) - 1 if (processDTD) then - call add_notation(fx%nlist, str_vs(fx%name), publicId=str_vs(fx%publicId)) + call add_notation(fx%nlist, str_varstr(fx%name), publicId=str_varstr(fx%publicId)) if (present(notationDecl_handler)) then - call notationDecl_handler(str_vs(fx%name), publicId=str_vs(fx%publicId), systemId="") + call notationDecl_handler(str_varstr(fx%name), publicId=str_varstr(fx%publicId), systemId="") if (fx%state==ST_STOP) return endif endif - deallocate(fx%name) - deallocate(fx%publicId) + call set_varstr_null(fx%name) + call set_varstr_null(fx%publicId) nextDTDState = ST_DTD_SUBSET case (TOK_CHAR) - fx%systemId => fx%token - fx%token => null() + call move_varstr_varstr(fx%token,fx%systemId) nextDTDState = ST_DTD_NOTATION_END end select @@ -2271,14 +2273,14 @@ contains "NOTATION not balanced in parameter entity") return endif - if (notation_exists(fx%nlist, str_vs(fx%name))) then + if (notation_exists(fx%nlist, str_varstr(fx%name))) then call add_error(fx%error_stack, "Duplicate notation declaration") return endif endif wf_stack(1) = wf_stack(1) - 1 if (processDTD) then - URIref => parseURI(str_vs(fx%systemId)) + URIref => parseURI(str_varstr(fx%systemId)) if (.not.associated(URIref)) then call add_error(fx%error_stack, "Invalid SYSTEM literal") return @@ -2292,27 +2294,27 @@ contains ! since we don't do NOTATIONs. ! Throw it away again call destroyURI(URIref) - if (associated(fx%publicId)) then - call add_notation(fx%nlist, str_vs(fx%name), & - publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + if (.not.is_varstr_null(fx%publicId)) then + call add_notation(fx%nlist, str_varstr(fx%name), & + publicId=str_varstr(fx%publicId), systemId=str_varstr(fx%systemId)) if (present(notationDecl_handler)) then - call notationDecl_handler(str_vs(fx%name), & - publicId=str_vs(fx%publicId), systemId=str_vs(fx%systemId)) + call notationDecl_handler(str_varstr(fx%name), & + publicId=str_varstr(fx%publicId), systemId=str_varstr(fx%systemId)) if (fx%state==ST_STOP) return endif else - call add_notation(fx%nlist, str_vs(fx%name), & - systemId=str_vs(fx%systemId)) + call add_notation(fx%nlist, str_varstr(fx%name), & + systemId=str_varstr(fx%systemId)) if (present(notationDecl_handler)) then - call notationDecl_handler(str_vs(fx%name), & - publicId="", systemId=str_vs(fx%systemId)) + call notationDecl_handler(str_varstr(fx%name), & + publicId="", systemId=str_varstr(fx%systemId)) if (fx%state==ST_STOP) return endif endif endif - if (associated(fx%publicId)) deallocate(fx%publicId) - deallocate(fx%systemId) - deallocate(fx%name) + call set_varstr_null(fx%publicId) + call set_varstr_null(fx%systemId) + call set_varstr_null(fx%name) nextDTDState = ST_DTD_SUBSET case default call add_error(fx%error_stack, "Unexpected token in NOTATION") @@ -2333,13 +2335,13 @@ contains end subroutine parseDTD subroutine open_tag - elem => get_element(fx%xds%element_list, str_vs(fx%name)) + elem => get_element(fx%xds%element_list, str_varstr(fx%name)) if (associated(elem)) then if (validCheck) then call checkAttributes(elem, fx%attributes) - if (.not.checkContentModel(fx%elstack, str_vs(fx%name))) then + if (.not.checkContentModel(fx%elstack, str_varstr(fx%name))) then call add_error(fx%error_stack, & - "Element "//str_vs(fx%name)//" not permitted in this context") + "Element '"//str_varstr(fx%name)//"' not permitted in this context") return endif else @@ -2348,7 +2350,7 @@ contains else if (validCheck) then call add_error(fx%error_stack, & - "Trying to use an undeclared element") + "Trying to use an unrecognised element '"//str_varstr(fx%name)//"'") return endif endif @@ -2368,39 +2370,39 @@ contains ! This is a top-level element in the current entity call setBase(fx%attributes, expressURI(fb%f(1)%baseURI)) endif - if (namespaces_.and.getURIofQName(fx,str_vs(fx%name))==invalidNS) then + if (namespaces_.and.getURIofQName(fx,str_varstr(fx%name))==invalidNS) then ! no namespace was found for the current element if (.not.startInCharData_) then ! but we ignore this if we are parsing an entity through DOM - call add_error(fx%error_stack, "No namespace found for current element") + call add_error(fx%error_stack, "No namespace found for current element '"//str_varstr(fx%name)//"'") return elseif (present(startElement_handler)) then ! Record it as having an empty URI call startElement_handler("", & - getlocalNameofQName(str_vs(fx%name)), & - str_vs(fx%name), fx%attributes) + getlocalNameofQName(str_varstr(fx%name)), & + str_varstr(fx%name), fx%attributes) if (fx%state==ST_STOP) return endif elseif (namespaces_) then ! Normal state of affairs if (present(startElement_handler)) then - call startElement_handler(getURIofQName(fx, str_vs(fx%name)), & - getlocalNameofQName(str_vs(fx%name)), & - str_vs(fx%name), fx%attributes) + call startElement_handler(getURIofQName(fx, str_varstr(fx%name)), & + getlocalNameofQName(str_varstr(fx%name)), & + str_varstr(fx%name), fx%attributes) if (fx%state==ST_STOP) return endif else ! Non-namespace aware processing if (present(startElement_handler)) then call startElement_handler("", "", & - str_vs(fx%name), fx%attributes) + str_varstr(fx%name), fx%attributes) if (fx%state==ST_STOP) return endif endif if (validCheck) then - call push_elstack(fx%elstack, str_vs(fx%name), elem%cp) + call push_elstack(fx%elstack, str_varstr(fx%name), elem%cp) else - call push_elstack(fx%elstack, str_vs(fx%name)) + call push_elstack(fx%elstack, str_varstr(fx%name)) endif call reset_dict(fx%attributes) end subroutine open_tag @@ -2413,37 +2415,37 @@ contains 'Ill-formed entity') return endif - if (str_vs(fx%name)/=get_top_elstack(fx%elstack)) then + if (str_varstr(fx%name)/=get_top_elstack(fx%elstack)) then call add_error(fx%error_stack, & - "Mismatching close tag: trying to close "//get_top_elstack(fx%elstack) & - //" with "//str_vs(fx%name)) + "Mismatching close tag: trying to close entity '"//get_top_elstack(fx%elstack) & + //"' with '"//str_varstr(fx%name)//"'") return endif if (validCheck) then if (.not.checkContentModelToEnd(fx%elstack)) then call add_error(fx%error_stack, & - "Failed to fulfil content model for "//str_vs(fx%name)) + "Failed to fulfil content model for "//str_varstr(fx%name)) return endif endif dummy = pop_elstack(fx%elstack) if (present(endElement_handler)) then - if (namespaces_.and.getURIofQName(fx,str_vs(fx%name))==invalidNS) then + if (namespaces_.and.getURIofQName(fx,str_varstr(fx%name))==invalidNS) then ! no namespace was found for the current element, we must be ! closing inside a DOM entity. ! Record it as having an empty URI call endElement_handler("", & - getlocalNameofQName(str_vs(fx%name)), & - str_vs(fx%name)) + getlocalNameofQName(str_varstr(fx%name)), & + str_varstr(fx%name)) elseif (namespaces_) then ! Normal state of affairs - call endElement_handler(getURIofQName(fx, str_vs(fx%name)), & - getlocalnameofQName(str_vs(fx%name)), & - str_vs(fx%name)) + call endElement_handler(getURIofQName(fx, str_varstr(fx%name)), & + getlocalnameofQName(str_varstr(fx%name)), & + str_varstr(fx%name)) else ! Non-namespace-aware processing: call endElement_handler("", "", & - str_vs(fx%name)) + str_varstr(fx%name)) endif if (fx%state==ST_STOP) return endif @@ -2460,19 +2462,19 @@ contains wfc = fb%f(1)%pe.or.inExtSubset if (pe) then !Does entity with this name exist? - if (.not.existing_entity(fx%xds%PEList, str_vs(fx%name))) then + if (.not.existing_entity(fx%xds%PEList, str_varstr(fx%name))) then ! Internal or external? - if (associated(fx%attname)) then ! it's internal + if (.not.is_varstr_null(fx%attname)) then ! it's internal call register_internal_PE(fx%xds, & - name=str_vs(fx%name), text=str_vs(fx%attname), & + name=str_varstr(fx%name), text=str_varstr(fx%attname), & wfc=wfc, baseURI=copyURI(fb%f(1)%baseURI)) ! FIXME need to expand value here before reporting ... if (present(internalEntityDecl_handler)) then - call internalEntityDecl_handler('%'//str_vs(fx%name), str_vs(fx%attname)) + call internalEntityDecl_handler('%'//str_varstr(fx%name), str_varstr(fx%attname)) if (fx%state==ST_STOP) return endif else ! PE can't have Ndata declaration - URIref => parseURI(str_vs(fx%systemId)) + URIref => parseURI(str_varstr(fx%systemId)) if (.not.associated(URIref)) then call add_error(fx%error_stack, "Invalid URI specified for SYSTEM") elseif (hasFragment(URIref)) then @@ -2481,39 +2483,39 @@ contains else newURI => rebaseURI(fb%f(1)%baseURI, URIref) call destroyURI(URIref) - if (associated(fx%publicId)) then - call register_external_PE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + if (.not.is_varstr_null(fx%publicId)) then + call register_external_PE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId), & wfc=wfc, baseURI=newURI) if (present(externalEntityDecl_handler)) & - call externalEntityDecl_handler('%'//str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId)) + call externalEntityDecl_handler('%'//str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId)) else - call register_external_PE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), & + call register_external_PE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), & wfc=wfc, baseURI=newURI) if (present(externalEntityDecl_handler)) & - call externalEntityDecl_handler('%'//str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId="") + call externalEntityDecl_handler('%'//str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId="") endif endif endif ! else we ignore it endif else !It's a general entity - if (.not.existing_entity(fx%xds%entityList, str_vs(fx%name))) then + if (.not.existing_entity(fx%xds%entityList, str_varstr(fx%name))) then ! Internal or external? - if (associated(fx%attname)) then ! it's internal - call register_internal_GE(fx%xds, name=str_vs(fx%name), & - text=str_vs(fx%attname), & + if (.not.is_varstr_null(fx%attname)) then ! it's internal + call register_internal_GE(fx%xds, name=str_varstr(fx%name), & + text=str_varstr(fx%attname), & wfc=wfc, baseURI=copyURI(fb%f(1)%baseURI)) if (present(internalEntityDecl_handler)) then - call internalEntityDecl_handler(str_vs(fx%name),& - str_vs(fx%attname)) + call internalEntityDecl_handler(str_varstr(fx%name),& + str_varstr(fx%attname)) if (fx%state==ST_STOP) return endif else - URIref => parseURI(str_vs(fx%systemId)) + URIref => parseURI(str_varstr(fx%systemId)) if (.not.associated(URIref)) then call add_error(fx%error_stack, "Invalid URI specified for SYSTEM") elseif (hasFragment(URIref)) then @@ -2522,35 +2524,35 @@ contains else newURI => rebaseURI(fb%f(1)%baseURI, URIref) call destroyURI(URIref) - if (associated(fx%publicId).and.associated(fx%Ndata)) then - call register_external_GE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & - notation=str_vs(fx%Ndata), & + if ((.not.is_varstr_null(fx%publicId)).and.(.not.is_varstr_null(fx%Ndata))) then + call register_external_GE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId), & + notation=str_varstr(fx%Ndata), & wfc=wfc, baseURI=newURI) if (present(unparsedEntityDecl_handler)) & - call unparsedEntityDecl_handler(str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & - notation=str_vs(fx%Ndata)) - elseif (associated(fx%Ndata)) then - call register_external_GE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), notation=str_vs(fx%Ndata), & + call unparsedEntityDecl_handler(str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId), & + notation=str_varstr(fx%Ndata)) + elseif (.not.is_varstr_null(fx%Ndata)) then + call register_external_GE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), notation=str_varstr(fx%Ndata), & wfc=wfc, baseURI=newURI) if (present(unparsedEntityDecl_handler)) & - call unparsedEntityDecl_handler(str_vs(fx%name), publicId="", & - systemId=str_vs(fx%systemId), notation=str_vs(fx%Ndata)) - elseif (associated(fx%publicId)) then - call register_external_GE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId), & + call unparsedEntityDecl_handler(str_varstr(fx%name), publicId="", & + systemId=str_varstr(fx%systemId), notation=str_varstr(fx%Ndata)) + elseif (.not.is_varstr_null(fx%publicId)) then + call register_external_GE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId), & wfc=wfc, baseURI=newURI) if (present(externalEntityDecl_handler)) & - call externalEntityDecl_handler(str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId=str_vs(fx%publicId)) + call externalEntityDecl_handler(str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId=str_varstr(fx%publicId)) else - call register_external_GE(fx%xds, name=str_vs(fx%name), & - systemId=str_vs(fx%systemId), wfc=wfc, baseURI=newURI) + call register_external_GE(fx%xds, name=str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), wfc=wfc, baseURI=newURI) if (present(externalEntityDecl_handler)) & - call externalEntityDecl_handler(str_vs(fx%name), & - systemId=str_vs(fx%systemId), publicId="") + call externalEntityDecl_handler(str_varstr(fx%name), & + systemId=str_varstr(fx%systemId), publicId="") endif endif endif @@ -2802,7 +2804,9 @@ contains if (str_vs(att%default)//"x"/=str_vs(attValue)//"x") then ! Validity Constraint: Fixed Attribute Default call add_error(fx%error_stack, & - "FIXED attribute has wrong value") + "FIXED attribute has unexpected value." & + //" At Element='"//str_vs(el%name)//"' Attribute='"//str_vs(att%name)//"'" & + //" Expecting '"//str_vs(att%default)//"' Found '"//str_vs(attValue)//"'") return endif else @@ -2820,9 +2824,7 @@ contains end select enddo - if (any(attributesLeft)) & - call add_error(fx%error_stack, & - "Undeclared attributes forbidden") + if (any(attributesLeft)) call add_error(fx%error_stack, "Undeclared attributes forbidden. Element '"//str_vs(el%name)//"'") end subroutine checkAttributes diff --git a/src/xml/sax/m_sax_reader.F90 b/src/xml/sax/m_sax_reader.F90 index 4693ff7f4..72fa6584e 100644 --- a/src/xml/sax/m_sax_reader.F90 +++ b/src/xml/sax/m_sax_reader.F90 @@ -26,6 +26,7 @@ module m_sax_reader public :: file_buffer_t public :: line public :: column + public :: add_error_position public :: open_file public :: close_file @@ -73,6 +74,7 @@ contains fileURI => parseURI(file) if (.not.associated(fileURI)) then call add_error(es, "Could not open file "//file//" - not a valid URI") + iostat=1 return endif call open_new_file(fb, fileURI, iostat, lun) @@ -169,7 +171,7 @@ contains call close_actual_file(fb%f(i)) enddo - deallocate(fb%f) + if (associated(fb%f)) deallocate(fb%f) end subroutine close_file @@ -397,6 +399,13 @@ contains p = (size(fb%f)==2) end function reading_first_entity + + subroutine add_error_position(stack, fb) + type(error_stack), intent(inout) :: stack + type(file_buffer_t), intent(in) :: fb + call add_error(stack,"(Possibly near line="//line(fb)//" col="//column(fb)//")") + end subroutine add_error_position + #endif end module m_sax_reader diff --git a/src/xml/sax/m_sax_tokenizer.F90 b/src/xml/sax/m_sax_tokenizer.F90 index 97824fd7a..59ea631d3 100644 --- a/src/xml/sax/m_sax_tokenizer.F90 +++ b/src/xml/sax/m_sax_tokenizer.F90 @@ -2,6 +2,8 @@ module m_sax_tokenizer #ifndef DUMMYLIB use fox_m_fsys_array_str, only: vs_str, str_vs, vs_str_alloc + use fox_m_fsys_varstr + use m_common_charset, only: XML_WHITESPACE, & upperCase, isInitialNameChar use m_common_error, only: add_error, in_error @@ -12,7 +14,7 @@ module m_sax_tokenizer use m_sax_reader, only: file_buffer_t, open_new_file, pop_buffer_stack, & push_chars, get_character, get_all_characters, & - parse_text_declaration + parse_text_declaration, add_error_position use m_sax_types ! everything, really #ifdef PGF90 use fox_m_utils_uri, only: URI @@ -39,8 +41,7 @@ contains xv = fx%xds%xml_version - if (associated(fx%token)) deallocate(fx%token) - fx%token => vs_str_alloc("") + call set_varstr_empty(fx%token) if (fx%nextTokenType/=TOK_NULL) then eof = .false. fx%tokenType = fx%nextTokenType @@ -58,13 +59,9 @@ contains if (eof) then if (fx%state==ST_CHAR_IN_CONTENT) then if (phrase==1) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(fx%token)//"]") - deallocate(tempString) + call append_varstr(fx%token,']') elseif (phrase==2) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(fx%token)//"]]") - deallocate(tempString) + call append_varstr(fx%token,']]') endif fx%tokenType = TOK_CHAR endif @@ -90,6 +87,7 @@ contains ws_discard = .false. else call add_error(fx%error_stack, "Unexpected character found outside content") + call add_error_position(fx%error_stack,fb) endif endif else @@ -102,6 +100,7 @@ contains fx%tokenType = TOK_OPEN_TAG else call add_error(fx%error_stack, "Unexpected character after <") + call add_error_position(fx%error_stack,fb) endif endif @@ -112,21 +111,20 @@ contains elseif (c=="[") then fx%tokenType = TOK_OPEN_SB elseif (verify(c,upperCase)==0) then - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token,c) else call add_error(fx%error_stack, "Unexpected character after 0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr(fx%token,c) else call push_chars(fb, c) fx%tokenType = TOK_NAME @@ -135,9 +133,7 @@ contains case (ST_START_PI) ! grab until whitespace or ? if (verify(c, XML_WHITESPACE//"?")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else fx%tokenType = TOK_NAME if (c=="?") call push_chars(fb, c) @@ -158,21 +154,16 @@ contains fx%nextTokenType = TOK_PI_END elseif (c=="?") then ! The last ? didn't mean anything, but this one might. - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"?") - deallocate(tempString) + call append_varstr( fx%token, '?' ) else phrase = 0 - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"?"//c) - deallocate(tempString) + call append_varstr( fx%token, '?' ) + call append_varstr( fx%token, c ) endif elseif (c=="?") then phrase = 1 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (ST_START_COMMENT) @@ -181,17 +172,14 @@ contains if (c=="-") then phrase = 1 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (1) if (c=="-") then phrase = 2 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"-"//c) - deallocate(tempString) + call append_varstr( fx%token, '-' ) + call append_varstr( fx%token, c ) phrase = 0 endif case (2) @@ -202,15 +190,14 @@ contains else call add_error(fx%error_stack, & "Expecting > after -- inside a comment.") + call add_error_position(fx%error_stack,fb) endif end select case (ST_START_TAG) ! grab until whitespace or /, > if (verify(c, XML_WHITESPACE//"/>")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else fx%tokenType = TOK_NAME if (c==">") then @@ -225,22 +212,21 @@ contains if (verify(c, XML_WHITESPACE)==0) then call add_error(fx%error_stack, & "Whitespace not allowed around CDATA in section declaration") + call add_error_position(fx%error_stack,fb) else - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token, c) ws_discard = .false. endif else if (verify(c, XML_WHITESPACE)==0) then call add_error(fx%error_stack, & "Whitespace not allowed around CDATA in section declaration") + call add_error_position(fx%error_stack,fb) elseif (c=="[") then fx%tokenType = TOK_NAME if (c=="[") fx%nextTokenType = TOK_OPEN_SB else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif endif @@ -251,17 +237,14 @@ contains if (c=="]") then phrase = 1 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (1) if (c=="]") then phrase = 2 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]"//c) - deallocate(tempString) + call append_varstr( fx%token, ']' ) + call append_varstr( fx%token, c ) phrase = 0 endif case (2) @@ -269,13 +252,10 @@ contains fx%tokenType = TOK_CHAR fx%nextTokenType = TOK_SECTION_END elseif (c=="]") then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]") - deallocate(tempString) + call append_varstr( fx%token, ']' ) else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]]"//c) - deallocate(tempString) + call append_varstr( fx%token, ']]' ) + call append_varstr( fx%token, c ) phrase = 0 endif end select @@ -285,6 +265,7 @@ contains if (verify(c,XML_WHITESPACE//"/>")>0) then call add_error(fx%error_stack, & "Whitespace required inside tag") + call add_error_position(fx%error_stack,fb) exit endif ws_discard = .true. @@ -297,8 +278,7 @@ contains phrase = 1 ws_discard = .false. else - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token,c) ws_discard = .false. endif endif @@ -308,7 +288,8 @@ contains fx%tokenType = TOK_END_TAG_CLOSE else call add_error(fx%error_stack, & - "Unexpected character after / in element tag") + "Unexpected character after '/' ("//c//") in tag for element '"//str_varstr(fx%name)//"'") + call add_error_position(fx%error_stack,fb) exit endif else @@ -322,9 +303,7 @@ contains call push_chars(fb, c) endif else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif endif endif @@ -338,6 +317,7 @@ contains else call add_error(fx%error_stack, & "Unexpected character in element tag, expected =") + call add_error_position(fx%error_stack,fb) endif endif endif @@ -351,28 +331,23 @@ contains ws_discard = .false. else call add_error(fx%error_stack, "Expecting "" or '") + call add_error_position(fx%error_stack,fb) endif endif else if (c==q) then fx%tokenType = TOK_CHAR else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif endif case (ST_CHAR_IN_CONTENT) if (c=="<".or.c=="&") then if (phrase==1) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]") - deallocate(tempString) + call append_varstr( fx%token, ']' ) elseif (phrase==2) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]]") - deallocate(tempString) + call append_varstr( fx%token, ']]' ) endif fx%tokenType = TOK_CHAR if (c=="<") then @@ -386,35 +361,28 @@ contains elseif (phrase==1) then phrase = 2 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]") - deallocate(tempString) + call append_varstr( fx%token, ']' ) endif elseif (c==">") then if (phrase==1) then phrase = 0 - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]>") - deallocate(tempString) + call append_varstr( fx%token, ']>' ) elseif (phrase==2) then call add_error(fx%error_stack, "]]> forbidden in character context") + call add_error_position(fx%error_stack,fb) else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//">") - deallocate(tempString) + call append_varstr( fx%token, '>' ) endif elseif (phrase==1) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]"//c) - deallocate(tempString) + call append_varstr( fx%token, ']' ) + call append_varstr( fx%token, c ) + phrase = 0 elseif (phrase==2) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"]]"//c) - deallocate(tempString) + call append_varstr( fx%token, ']]' ) + call append_varstr( fx%token, c ) + phrase = 0 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (ST_TAG_IN_CONTENT) @@ -426,6 +394,7 @@ contains fx%tokenType = TOK_ENTITY else call add_error(fx%error_stack, "Unexpected character found in content") + call add_error_position(fx%error_stack,fb) endif elseif (phrase==1) then if (c=="?") then @@ -439,25 +408,23 @@ contains fx%tokenType = TOK_OPEN_TAG else call add_error(fx%error_stack, "Unexpected character after <") + call add_error_position(fx%error_stack,fb) endif endif case (ST_START_ENTITY) if (verify(c,XML_WHITESPACE//";")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) elseif (c==";") then fx%tokenType = TOK_NAME else call add_error(fx%error_stack, "Entity reference must be terminated with a ;") + call add_error_position(fx%error_stack,fb) endif case (ST_CLOSING_TAG) if (verify(c,XML_WHITESPACE//">")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else fx%tokenType = TOK_NAME if (c==">") fx%nextTokenType = TOK_END_TAG @@ -469,6 +436,7 @@ contains fx%tokenType = TOK_END_TAG else call add_error(fx%error_stack, "Unexpected character - expecting >") + call add_error_position(fx%error_stack,fb) endif endif @@ -483,6 +451,7 @@ contains else call add_error(fx%error_stack, & "Missing whitespace in doctype delcaration.") + call add_error_position(fx%error_stack,fb) endif else ws_discard = .true. @@ -492,16 +461,14 @@ contains if (verify(c, XML_WHITESPACE)>0) then if (verify(c, "'""")==0) then q = c - deallocate(fx%token) - fx%token => vs_str_alloc("") + call set_varstr_empty(fx%token) ws_discard = .false. elseif (c=="[") then fx%tokenType = TOK_OPEN_SB elseif (c==">") then fx%tokenType = TOK_END_TAG else - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token, c) ws_discard = .false. endif endif @@ -515,9 +482,7 @@ contains call push_chars(fb, c) fx%tokenType = TOK_NAME else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif endif @@ -526,13 +491,12 @@ contains case (ST_START_PE) if (verify(c,XML_WHITESPACE//";")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) elseif (c==";") then fx%tokenType = TOK_NAME else call add_error(fx%error_stack, "Entity reference must be terminated with a ;") + call add_error_position(fx%error_stack,fb) endif end select @@ -574,14 +538,14 @@ contains elseif (fx%inIntSubset) then call add_error(fx%error_stack, & "Parameter entity reference not permitted inside markup for internal subset") + call add_error_position(fx%error_stack,fb) return elseif (fx%state_dtd==ST_DTD_ATTLIST_CONTENTS & .or.fx%state_dtd==ST_DTD_ELEMENT_CONTENTS) then - if (.not.associated(fx%content)) then + if (is_varstr_null(fx%content)) then ! content will not always be empty here; ! if we have two PErefs bang next to each other. - fx%content => fx%token - fx%token => vs_str_alloc("") + call move_varstr_varstr(fx%token,fx%content) endif fx%tokenType = TOK_ENTITY return @@ -617,6 +581,7 @@ contains ws_discard = .false. else call add_error(fx%error_stack, "Unexpected character found in document subset") + call add_error_position(fx%error_stack,fb) endif endif elseif (phrase==1) then @@ -627,6 +592,7 @@ contains fx%tokenType = TOK_BANG_TAG else call add_error(fx%error_stack, "Unexpected character, expecting ! or ?") + call add_error_position(fx%error_stack,fb) endif elseif (q=="]") then if (c=="]") then @@ -647,6 +613,7 @@ contains fx%tokenType = TOK_SECTION_END else call add_error(fx%error_stack, "Unexpected character, expecting >") + call add_error_position(fx%error_stack,fb) endif endif @@ -658,21 +625,20 @@ contains elseif (c=="[") then fx%tokenType = TOK_OPEN_SB elseif (verify(c,upperCase)==0) then - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token,c) else call add_error(fx%error_stack, "Unexpected character after 0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else call push_chars(fb, c) fx%tokenType = TOK_NAME @@ -685,15 +651,12 @@ contains endif if (ws_discard) then if (verify(c, XML_WHITESPACE)/=0) then - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token, c) ws_discard = .false. endif else if (verify(c, XML_WHITESPACE//"[")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else fx%tokenType = TOK_NAME if (c=="[") fx%nextTokenType = TOK_OPEN_SB @@ -705,6 +668,7 @@ contains if (c/="[") then call add_error(fx%error_stack, & "Unexpected token found, expecting [") + call add_error_position(fx%error_stack,fb) else fx%tokenType = TOK_OPEN_SB endif @@ -737,9 +701,7 @@ contains case (ST_DTD_START_PI) ! grab until whitespace or ? if (verify(c, XML_WHITESPACE//"?")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else fx%tokenType = TOK_NAME if (c=="?") call push_chars(fb, c) @@ -760,21 +722,16 @@ contains fx%nextTokenType = TOK_PI_END elseif (c=="?") then ! The last ? didn't mean anything, but this one might. - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"?") - deallocate(tempString) + call append_varstr( fx%token, '?' ) else phrase = 0 - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"?"//c) - deallocate(tempString) + call append_varstr( fx%token, '?' ) + call append_varstr( fx%token, c ) endif elseif (c=="?") then phrase = 1 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (ST_DTD_START_COMMENT) @@ -783,29 +740,25 @@ contains if (c=="-") then phrase = 1 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif case (1) if (c=="-") then phrase = 2 else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//"-"//c) - deallocate(tempString) + call append_varstr( fx%token, '-' ) + call append_varstr( fx%token, c ) phrase = 0 endif case (2) if (c==">") then fx%tokenType = TOK_CHAR - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) fx%nextTokenType = TOK_COMMENT_END else call add_error(fx%error_stack, & "Expecting > after -- inside a comment.") + call add_error_position(fx%error_stack,fb) endif end select @@ -814,14 +767,11 @@ contains if (firstChar) ws_discard = .true. if (ws_discard) then if (verify(c,XML_WHITESPACE)>0) then - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str(fx%token, c) ws_discard = .false. endif elseif (verify(c,XML_WHITESPACE//">")>0) then - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) else if (c==">") then fx%nextTokenType = TOK_END_TAG @@ -833,31 +783,24 @@ contains case (ST_DTD_ELEMENT_CONTENTS) if (c==">") then - if (associated(fx%content)) then - deallocate(fx%token) - fx%token => fx%content - fx%content => null() + if (.not.is_varstr_null(fx%content)) then + call move_varstr_varstr(fx%content,fx%token) endif fx%tokenType = TOK_DTD_CONTENTS fx%nextTokenType = TOK_END_TAG else - if (associated(fx%content)) then - deallocate(fx%token) - fx%token => vs_str_alloc(str_vs(fx%content)//c) - deallocate(fx%content) + if (.not.is_varstr_null(fx%content)) then + call move_varstr_varstr(fx%content,fx%token) + call append_varstr(fx%token, c) else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr(fx%token, c) endif if (c=="(") then fx%tokenType = TOK_OPEN_PAR - fx%content => fx%token - fx%token => vs_str_alloc("") + call move_varstr_varstr(fx%token,fx%content) elseif (c==")") then fx%tokenType = TOK_CLOSE_PAR - fx%content => fx%token - fx%token => vs_str_alloc("") + call move_varstr_varstr(fx%token,fx%content) endif endif @@ -865,14 +808,11 @@ contains if (c==">") then fx%tokenType = TOK_DTD_CONTENTS fx%nextTokenType = TOK_END_TAG - elseif (associated(fx%content)) then - deallocate(fx%token) - fx%token => vs_str_alloc(str_vs(fx%content)//c) - deallocate(fx%content) + elseif (.not.is_varstr_null(fx%content)) then + call move_varstr_varstr(fx%content,fx%token) + call append_varstr(fx%token, c) else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif if (c=="'".or.c=="""") then if (q==c) then @@ -892,6 +832,7 @@ contains fx%tokenType = TOK_END_TAG else call add_error(fx%error_stack, "Missing whitespace in DTD.") + call add_error_position(fx%error_stack,fb) endif else ws_discard = .true. @@ -900,14 +841,12 @@ contains if (verify(c, XML_WHITESPACE)>0) then if (verify(c, "'""")==0) then q = c - deallocate(fx%token) - fx%token => vs_str_alloc("") + call set_varstr_empty(fx%token) ws_discard = .false. elseif (c==">") then fx%tokenType = TOK_END_TAG else - deallocate(fx%token) - fx%token => vs_str_alloc(c) + call varstr_str( fx%token, c ) ws_discard = .false. endif endif @@ -922,9 +861,7 @@ contains call push_chars(fb, c) endif else - tempString => fx%token - fx%token => vs_str_alloc(str_vs(tempString)//c) - deallocate(tempString) + call append_varstr( fx%token, c ) endif endif @@ -934,9 +871,10 @@ contains end subroutine sax_tokenize - recursive function normalize_attribute_text(fx, s_in) result(s_out) + recursive function normalize_attribute_text(fx, s_in, fb) result(s_out) type(sax_parser_t), intent(inout) :: fx character, dimension(:), intent(in) :: s_in + type(file_buffer_t), intent(in) :: fb character, dimension(:), pointer :: s_out character, dimension(:), pointer :: s_temp, s_temp2, s_ent, tempString @@ -968,16 +906,19 @@ contains i = i + 1 i2 = i2 + 1 elseif (s_in(i)=='<') then - call add_error(fx%error_stack, "Illegal < found in attribute.") + call add_error(fx%error_stack, "Illegal '<' found in attribute.") + call add_error_position(fx%error_stack,fb) goto 100 ! Then, expand < elseif (s_in(i)=='&') then j = index(str_vs(s_in(i+1:)), ';') if (j==0) then - call add_error(fx%error_stack, "Illegal & found in attribute") + call add_error(fx%error_stack, "Illegal '&' found in attribute") + call add_error_position(fx%error_stack,fb) goto 100 elseif (j==1) then call add_error(fx%error_stack, "No entity reference found") + call add_error_position(fx%error_stack,fb) goto 100 endif allocate(tempString(j-1)) @@ -996,6 +937,7 @@ contains ent => getEntityByName(fx%forbidden_ge_list, str_vs(tempString)) if (associated(ent)) then call add_error(fx%error_stack, 'Recursive entity expansion') + call add_error_position(fx%error_stack,fb) goto 100 else ent => getEntityByName(fx%xds%entityList, str_vs(tempString)) @@ -1003,11 +945,13 @@ contains if (associated(ent)) then if (ent%wfc.and.fx%xds%standalone) then call add_error(fx%error_stack, 'Externally declared entity referenced in standalone document') + call add_error_position(fx%error_stack,fb) goto 100 endif !is it the right sort of entity? if (ent%external) then call add_error(fx%error_stack, "External entity forbidden in attribute") + call add_error_position(fx%error_stack,fb) goto 100 endif #ifdef PGF90 @@ -1017,7 +961,7 @@ contains #endif ! Recursively expand entity, checking for errors. s_ent => normalize_attribute_text(fx, & - vs_str(expand_entity_text(fx%xds%entityList, str_vs(tempString)))) + vs_str(expand_entity_text(fx%xds%entityList, str_vs(tempString))),fb) dummy = pop_entity_list(fx%forbidden_ge_list) if (in_error(fx%error_stack)) then goto 100 @@ -1037,11 +981,13 @@ contains i2 = i2 + j + 1 if (.not.fx%skippedExternal.or.fx%xds%standalone) then call add_error(fx%error_stack, "Undeclared entity encountered in standalone document.") + call add_error_position(fx%error_stack,fb) goto 100 endif endif else call add_error(fx%error_stack, "Illegal entity reference") + call add_error_position(fx%error_stack,fb) goto 100 endif deallocate(tempString) @@ -1090,10 +1036,12 @@ contains if (s_in(i)=='%') then j = index(str_vs(s_in(i+1:)), ';') if (j==0) then - call add_error(fx%error_stack, "Illegal % found in attribute") + call add_error(fx%error_stack, "Illegal '%' found in attribute") + call add_error_position(fx%error_stack,fb) goto 100 elseif (j==1) then call add_error(fx%error_stack, "No entity reference found") + call add_error_position(fx%error_stack,fb) goto 100 endif allocate(tempString(j-1)) @@ -1102,6 +1050,7 @@ contains ent => getEntityByName(fx%forbidden_pe_list, str_vs(tempString)) if (associated(ent)) then call add_error(fx%error_stack, 'Recursive entity expansion') + call add_error_position(fx%error_stack,fb) goto 100 endif ent => getEntityByName(fx%xds%peList, str_vs(tempString)) @@ -1109,9 +1058,11 @@ contains if (ent%wfc.and.fx%xds%standalone) then call add_error(fx%error_stack, & "Externally declared entity used in standalone document") + call add_error_position(fx%error_stack,fb) goto 100 elseif (str_vs(ent%notation)/="") then call add_error(fx%error_stack, "Unparsed entity reference forbidden in entity value") + call add_error_position(fx%error_stack,fb) goto 100 endif #ifdef PGF90 @@ -1124,6 +1075,7 @@ contains call open_new_file(fb, ent%baseURI, iostat) if (iostat/=0) then call add_error(fx%error_stack, "Unable to access external parameter entity") + call add_error_position(fx%error_stack,fb) goto 100 endif call parse_text_declaration(fb, fx%error_stack) @@ -1156,11 +1108,13 @@ contains i2 = i2 + j + 1 if (.not.fx%skippedExternal.or.fx%xds%standalone) then call add_error(fx%error_stack, "Reference to undeclared parameter entity encountered in standalone document.") + call add_error_position(fx%error_stack,fb) goto 100 endif endif else call add_error(fx%error_stack, "Illegal parameter entity reference") + call add_error_position(fx%error_stack,fb) goto 100 endif deallocate(tempString) diff --git a/src/xml/sax/m_sax_types.F90 b/src/xml/sax/m_sax_types.F90 index b0148a693..eb123c781 100644 --- a/src/xml/sax/m_sax_types.F90 +++ b/src/xml/sax/m_sax_types.F90 @@ -9,6 +9,8 @@ module m_sax_types use m_common_notations, only: notation_list use m_common_struct, only: xml_doc_state + use fox_m_fsys_varstr, only: varstr + use m_sax_reader, only: file_buffer_t implicit none @@ -124,16 +126,16 @@ module m_sax_types integer :: state_dtd = ST_DTD_SUBSET logical :: well_formed = .false. logical :: skippedExternal = .false. - character, dimension(:), pointer :: token => null() - character, dimension(:), pointer :: content => null() + type(varstr) :: token + type(varstr) :: content integer :: tokenType = TOK_NULL integer :: nextTokenType = TOK_NULL - character, dimension(:), pointer :: name => null() - character, dimension(:), pointer :: attname => null() + type(varstr) :: name + type(varstr) :: attname logical :: error = .false. type(error_stack) :: error_stack ! Aspects of document structure - character, dimension(:), pointer :: root_element => null() + type(varstr) :: root_element type(elstack_t) :: elstack type(dictionary_t) :: attributes type(namespacedictionary) :: nsdict @@ -141,9 +143,9 @@ module m_sax_types type(entity_list) :: predefined_e_list type(entity_list) :: forbidden_pe_list type(entity_list) :: forbidden_ge_list - character(len=1), dimension(:), pointer :: PublicId => null() - character(len=1), dimension(:), pointer :: SystemId => null() - character(len=1), dimension(:), pointer :: Ndata => null() + type(varstr) :: PublicId + type(varstr) :: SystemId + type(varstr) :: Ndata logical :: inIntSubset = .false. logical :: spaceBeforeEntity = .false. end type sax_parser_t diff --git a/src/xml/utils/fox_m_utils_uri.F90 b/src/xml/utils/fox_m_utils_uri.F90 index be6fefcdd..a3f1d70ff 100644 --- a/src/xml/utils/fox_m_utils_uri.F90 +++ b/src/xml/utils/fox_m_utils_uri.F90 @@ -321,7 +321,7 @@ contains p = .true. i1 = index(path, "/") - if (i1==1) then + if (i1==1) then ! absolute path allocate(segments(1)) segments(1)%s => vs_str_alloc("/") else @@ -385,9 +385,10 @@ contains end function checkFragment #endif - function parseURI(URIstring) result(u) - character(len=*), intent(in) :: URIstring + function parseURI(inURIstring) result(u) + character(len=*), intent(in) :: inURIstring type(URI), pointer :: u + character(len=len_trim(inURIstring)) :: URIstring #ifndef DUMMYLIB character, pointer, dimension(:) :: scheme, authority, & userinfo, host, path, query, fragment @@ -398,6 +399,7 @@ contains #endif u => null() + URIstring = trim(inURIString) #ifndef DUMMYLIB scheme => null() @@ -410,6 +412,18 @@ contains query => null() fragment => null() + if (len(URIstring)>3) then + ! is this a M$ windoze absolute path ? eg of the form "C:/path_segments" + if ((scan(URIstring(1:1),alpha)>0).and.(URIstring(2:3)==':/') ) then + ! no point in attempting to decode as a uri, it contains only a windows path + scheme => vs_str_alloc("file") + path => unEscape_alloc(URIstring) + allocate(segments(1)) + segments(1)%s => vs_str_alloc("") + call produceResult + return + end if + end if i1 = index(URIstring, ":") if (i1>0) then p = checkScheme(URIstring(:i1-1)) @@ -460,6 +474,13 @@ contains call cleanUp return endif + if (len(URIstring(i2:i3-1))>3) then + ! is this a M$ windoze absolute path with a unix root ? eg of the form "/C:/path_segments" + if ( (URIstring(i2:i2)=='/').and.(scan(URIstring(i2+1:i2+1),alpha)>0).and.(URIstring(i2+2:i2+3)==':/') ) then + ! ignore the root slash (which would otherwise make sense on most systems) to yield a representation in windows canonical form + i2 = i2+1 + end if + end if path => unEscape_alloc(URIstring(i2:i3-1)) if (i3>len(URIstring)) then @@ -522,7 +543,6 @@ contains u%host => host u%port = port u%path => path - u%segments => segments u%query => query u%fragment => fragment u%segments => segments diff --git a/src/xml/wxml/FoX_wxml.F90 b/src/xml/wxml/FoX_wxml.F90 index 41c1e5652..cf4def0a7 100644 --- a/src/xml/wxml/FoX_wxml.F90 +++ b/src/xml/wxml/FoX_wxml.F90 @@ -36,6 +36,8 @@ module FoX_wxml public :: xmlf_GetPretty_print public :: xmlf_SetPretty_print + public :: xmlf_GetExtendedData + public :: xmlf_SetExtendedData public :: xmlf_Name public :: xmlf_OpenTag diff --git a/src/xml/wxml/m_wxml_core.F90 b/src/xml/wxml/m_wxml_core.F90 index 2afd30b06..7e905c2ab 100644 --- a/src/xml/wxml/m_wxml_core.F90 +++ b/src/xml/wxml/m_wxml_core.F90 @@ -87,6 +87,9 @@ module m_wxml_core integer :: state_1 = -1 integer :: state_2 = -1 integer :: state_3 = -1 + ! Holder for extra information for other writers. See + ! table with getter and setter below: + integer :: extended_data = 0 logical :: minimize_overrun = .true. logical :: pretty_print = .false. logical :: canonical = .false. @@ -128,6 +131,8 @@ module m_wxml_core public :: xmlf_SetPretty_print public :: xmlf_GetPretty_print + public :: xmlf_SetExtendedData + public :: xmlf_GetExtendedData interface xml_AddCharacters module procedure xml_AddCharacters_Ch @@ -1653,6 +1658,30 @@ contains #endif end function xmlf_GetPretty_print +! xf%extended data is an integer so that writers +! can change there behaviour depending on some +! stored information. Currently only used for +! wcml 'validate' argument (which is intended to +! check some of the more troublesome aspects of +! the CML schema + subroutine xmlf_SetExtendedData(xf, new_value) + type(xmlf_t), intent(inout) :: xf + integer, intent(in) :: new_value +#ifndef DUMMYLIB + xf%extended_data = new_value +#endif + end subroutine xmlf_SetExtendedData + + pure function xmlf_GetExtendedData(xf) result(value) + integer :: value + type(xmlf_t), intent(in) :: xf +#ifdef DUMMYLIB + value = .false. +#else + value = xf%extended_data +#endif + end function xmlf_GetExtendedData + pure function xmlf_name(xf) result(fn) type (xmlf_t), intent(in) :: xf #ifdef DUMMYLIB From e8792cdb442c41c884bfecec171e47749e518ded Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 25 Sep 2013 14:46:45 -0400 Subject: [PATCH 44/45] fixed addition conflict of threads from merge --- src/DEPENDENCIES | 1 - src/input_xml.F90 | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/DEPENDENCIES b/src/DEPENDENCIES index d2bb688c9..e958cc62a 100644 --- a/src/DEPENDENCIES +++ b/src/DEPENDENCIES @@ -197,7 +197,6 @@ initialize.o: global.o initialize.o: hdf5_interface.o initialize.o: hdf5_summary.o initialize.o: input_xml.o -initialize.o: omp_lib.o initialize.o: output.o initialize.o: output_interface.o initialize.o: random_lcg.o diff --git a/src/input_xml.F90 b/src/input_xml.F90 index ed7b845ec..deeae3c01 100644 --- a/src/input_xml.F90 +++ b/src/input_xml.F90 @@ -226,10 +226,10 @@ contains end if ! Number of OpenMP threads - if (threads_ /= NONE) then + if (check_for_node(doc, "threads")) then #ifdef OPENMP if (n_threads == NONE) then - n_threads = threads_ + call get_node_value(doc, "threads", n_threads) if (n_threads < 1) then message = "Invalid number of threads: " // to_str(n_threads) call fatal_error() From dccd186369af3aa3cdc58849b25d8101931f9adc Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Thu, 26 Sep 2013 11:56:01 -0400 Subject: [PATCH 45/45] added old version of fsys_format since there is a bug in the pre-release version --- src/xml/fsys/fox_m_fsys_format.F90 | 182 ++++++++++++++--------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/src/xml/fsys/fox_m_fsys_format.F90 b/src/xml/fsys/fox_m_fsys_format.F90 index 6d6a6d933..448f44ebd 100644 --- a/src/xml/fsys/fox_m_fsys_format.F90 +++ b/src/xml/fsys/fox_m_fsys_format.F90 @@ -52,7 +52,7 @@ module fox_m_fsys_format #ifndef DUMMYLIB interface safestr -! This is for internal use only - no check is made on the validity of +! This is for internal use only - no check is made on the validity of ! any fmt input. module procedure str_string, str_string_array, str_string_matrix, & str_integer, str_integer_array, str_integer_matrix, & @@ -111,13 +111,13 @@ contains #ifndef DUMMYLIB ! NB: The len generic module procedure is used in - ! many initialisation statments (to set the + ! many initialisation statments (to set the ! length of the output string needed for the ! converted number). As of the Fortran 2008 ! spec every specific function belonging to ! a generic used in this way must be defined ! in the module before use. This is enforced - ! by at least version 7.4.4 of the Cray + ! by at least version 7.4.4 of the Cray ! Fortran compiler. Hence we put all the *_len ! functions here at the top of the file. pure function str_string_array_len(st) result(n) @@ -143,7 +143,7 @@ contains pure function str_integer_len(i) result(n) integer, intent(in) :: i integer :: n - + n = int(log10(real(max(abs(i),1)))) + 1 + dim(-i,0)/max(abs(i),1) end function str_integer_len @@ -151,7 +151,7 @@ contains pure function str_integer_base_len(i, b) result(n) integer, intent(in) :: i, b integer :: n - + n = int(log10(real(max(abs(i),1)))/log10(real(b))) & + 1 + dim(-i,0)/max(abs(i),1) @@ -161,7 +161,7 @@ contains integer, intent(in) :: i character(len=*), intent(in) :: fmt integer :: n - + select case (len(fmt)) case(0) n = 0 @@ -179,7 +179,7 @@ contains elseif (verify(fmt(2:), digit)==0) then n = str_to_int_10(fmt(2:)) else - n = 0 + n = 0 endif end select @@ -188,7 +188,7 @@ contains pure function str_integer_array_len(ia) result(n) integer, dimension(:), intent(in) :: ia integer :: n - + integer :: j n = size(ia) - 1 @@ -203,7 +203,7 @@ contains integer, dimension(:), intent(in) :: ia character(len=*), intent(in) :: fmt integer :: n - + integer :: j n = size(ia) - 1 @@ -250,7 +250,7 @@ contains pure function str_logical_len(l) result (n) logical, intent(in) :: l integer :: n - + if (l) then n = 4 else @@ -292,17 +292,17 @@ contains else e = floor(log10(abs(x))) endif - + if (x < 0.0_sp) then n = 1 else n = 0 endif - + if (len(fmt) == 0) then sig = sig_sp - n = n + sig + 2 + len(e) + n = n + sig + 2 + len(e) ! for the decimal point and the e elseif (fmt(1:1) == "s") then @@ -314,9 +314,9 @@ contains sig = max(sig, 1) sig = min(sig, digits(1.0_sp)) - if (sig > 1) n = n + 1 + if (sig > 1) n = n + 1 ! for the decimal point - + n = n + sig + 1 + len(e) elseif (fmt(1:1) == "r") then @@ -376,7 +376,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), fmt) enddo - + end function str_real_sp_array_fmt_len pure function str_real_sp_matrix_fmt_len(xa, fmt) result(n) @@ -420,17 +420,17 @@ contains else e = floor(log10(abs(x))) endif - + if (x < 0.0_dp) then n = 1 else n = 0 endif - + if (len(fmt) == 0) then sig = sig_dp - n = n + sig + 2 + len(e) + n = n + sig + 2 + len(e) ! for the decimal point and the e elseif (fmt(1:1) == "s") then @@ -442,9 +442,9 @@ contains sig = max(sig, 1) sig = min(sig, digits(1.0_dp)) - if (sig > 1) n = n + 1 + if (sig > 1) n = n + 1 ! for the decimal point - + n = n + sig + 1 + len(e) elseif (fmt(1:1) == "r") then @@ -490,7 +490,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), "") enddo - + end function str_real_dp_array_len pure function str_real_dp_array_fmt_len(xa, fmt) result(n) @@ -504,7 +504,7 @@ contains do k = 1, size(xa) n = n + len(xa(k), fmt) enddo - + end function str_real_dp_array_fmt_len pure function str_real_dp_matrix_fmt_len(xa, fmt) result(n) @@ -558,7 +558,7 @@ contains n = size(ca) - 1 do i = 1, size(ca) - n = n + len(ca(i), fmt) + n = n + len(ca(i), fmt) enddo end function str_complex_sp_array_fmt_len @@ -590,7 +590,7 @@ contains n = len(ca, "") end function str_complex_sp_matrix_len - + pure function str_complex_dp_fmt_len(c, fmt) result(n) complex(dp), intent(in) :: c character(len=*), intent(in) :: fmt @@ -644,7 +644,7 @@ contains enddo enddo end function str_complex_dp_matrix_fmt_len - + pure function str_complex_dp_matrix_len(ca) result(n) complex(dp), dimension(:, :), intent(in) :: ca integer :: n @@ -705,7 +705,7 @@ contains ! Error is flagged by returning -1 character(len=*), intent(in) :: str integer :: n - + character(len=len(str)) :: str_l integer :: max_power, i, j @@ -739,7 +739,7 @@ contains endif enddo end function to_lower - + end function str_to_int_16 #endif @@ -747,7 +747,7 @@ contains character(len=*), intent(in) :: st #ifdef DUMMYLIB character(len=1) :: s - s = " " + s = " " #else character(len=len(st)) :: s s = st @@ -762,10 +762,10 @@ contains s = " " #else character(len=str_string_array_len(st)) :: s - + integer :: k, n character(len=1) :: d - + if (present(delimiter)) then d = delimiter else @@ -789,7 +789,7 @@ contains s = " " #else character(len=str_string_matrix_len(st)) :: s - + integer :: j, k, n character(len=1) :: d @@ -853,7 +853,7 @@ contains character :: f integer :: b, ii, j, k, n, ls - + if (len(fmt)>0) then if (fmt(1:1)=="d") then f = 'd' @@ -902,7 +902,7 @@ contains #ifdef DUMMYLIB character(len=1) :: s #else - character(len=len(ia(:), "d")) :: s + character(len=len(ia, "d")) :: s integer :: j, k, n @@ -924,7 +924,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia(:), fmt)) :: s + character(len=len(ia, fmt)) :: s integer :: j, k, n @@ -944,7 +944,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia(:,:), "d")) :: s + character(len=len(ia, "d")) :: s integer :: j, k, n @@ -954,7 +954,7 @@ contains s(n:n+len(ia(j,1))) = " "//str(ia(j,1)) n = n + len(ia(j,1)) + 1 enddo - do k = 2, size(ia, 2) + do k = 2, size(ia, 2) do j = 1, size(ia, 1) s(n:n+len(ia(j,k))) = " "//str(ia(j,k)) n = n + len(ia(j,k)) + 1 @@ -971,7 +971,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ia(:,:), fmt)) :: s + character(len=len(ia, fmt)) :: s integer :: j, k, n @@ -981,7 +981,7 @@ contains s(n:n+len(ia(j,1), fmt)) = " "//str(ia(j,1), fmt) n = n + len(ia(j,1), fmt) + 1 enddo - do k = 2, size(ia, 2) + do k = 2, size(ia, 2) do j = 1, size(ia, 1) s(n:n+len(ia(j,k), fmt)) = " "//str(ia(j,k), fmt) n = n + len(ia(j,k), fmt) + 1 @@ -1000,7 +1000,7 @@ contains ! character(len=merge(4,5,l)) :: s ! And g95 (sep2007) cant resolve the generic here character(len=str_logical_len(l)) :: s - + if (l) then s="true" else @@ -1015,8 +1015,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(la(:))) :: s - + character(len=len(la)) :: s + integer :: k, n n = 1 @@ -1044,7 +1044,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(la(:,:))) :: s + character(len=len(la)) :: s integer :: j, k, n @@ -1079,12 +1079,12 @@ contains enddo #endif end function str_logical_matrix - + #ifndef DUMMYLIB ! In order to convert real numbers to strings, we need to - ! perform an internal write - but how long will the + ! perform an internal write - but how long will the ! resultant string be? We don't know & there is no way - ! to discover for an arbitrary format. Therefore, + ! to discover for an arbitrary format. Therefore, ! (if we have the capability; f95 or better) ! we assume it will be less than 100 characters, write ! it to a string of that length, then remove leading & @@ -1094,7 +1094,7 @@ contains ! If we are working with an F90-only compiler, then ! we cannot do this trick - the output string will ! always be 100 chars in length, though we will remove - ! leading whitespace. + ! leading whitespace. ! The standard Fortran format functions do not give us @@ -1104,7 +1104,7 @@ contains ! "r" which will produce output without an exponent, ! and digits after the decimal point. ! or - ! "s": which implies scientific notation, with an + ! "s": which implies scientific notation, with an ! exponent, with significant figures. ! If the integer is absent, then the precision will be ! half of the number of significant figures available @@ -1137,7 +1137,7 @@ contains real(sp) :: x_ if (sig < 1) then - s ="" + s ="" return endif @@ -1156,11 +1156,11 @@ contains enddo n = 1 do k = sig - 2, 0, -1 - ! This baroque way of taking int() ensures the optimizer + ! This baroque way of taking int() ensures the optimizer ! stores it in j without keeping a different value in cache. j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 if (j==10) then - ! This can happen if, on the previous cycle, int(x_) in + ! This can happen if, on the previous cycle, int(x_) in ! the line above gave a result approx. 1.0 less than ! expected. ! In this case we want to quit the cycle & just get 999... to the end @@ -1351,8 +1351,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:))) :: s - + character(len=len(xa)) :: s + integer :: j, k, n n = 1 @@ -1369,8 +1369,8 @@ contains pure function str_real_sp_array_fmt(xa, fmt) result(s) real(sp), dimension(:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa(:), fmt)) :: s - + character(len=len(xa, fmt)) :: s + integer :: j, k, n n = 1 @@ -1391,8 +1391,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:), fmt)) :: s - + character(len=len(xa, fmt)) :: s + if (checkFmt(fmt)) then s = safestr(xa, fmt) else @@ -1405,7 +1405,7 @@ contains pure function str_real_sp_matrix_fmt(xa, fmt) result(s) real(sp), dimension(:,:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa(:,:),fmt)) :: s + character(len=len(xa,fmt)) :: s integer :: i, j, k, n @@ -1435,7 +1435,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:,:),fmt)) :: s + character(len=len(xa,fmt)) :: s if (checkFmt(fmt)) then s = safestr(xa, fmt) @@ -1451,12 +1451,12 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:,:))) :: s + character(len=len(xa)) :: s s = safestr(xa, "") #endif end function str_real_sp_matrix - + #ifndef DUMMYLIB pure function real_dp_str(x, sig) result(s) real(dp), intent(in) :: x @@ -1467,7 +1467,7 @@ contains real(dp) :: x_ if (sig < 1) then - s ="" + s ="" return endif @@ -1490,7 +1490,7 @@ contains ! stores it in j without keeping a different value in cache. j = iachar(digit(int(x_)+1:int(x_)+1)) - 48 if (j==10) then - ! This can happen if, on the previous cycle, int(x_) in + ! This can happen if, on the previous cycle, int(x_) in ! the line above gave a result almost exactly 1.0 less than ! expected - but FP arithmetic is not consistent. ! In this case we want to quit the cycle & just get 999... to the end @@ -1682,8 +1682,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:))) :: s - + character(len=len(xa)) :: s + integer :: j, k, n n = 1 @@ -1700,8 +1700,8 @@ contains pure function str_real_dp_array_fmt(xa, fmt) result(s) real(dp), dimension(:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa(:), fmt)) :: s - + character(len=len(xa, fmt)) :: s + integer :: j, k, n n = 1 @@ -1722,8 +1722,8 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:), fmt)) :: s - + character(len=len(xa, fmt)) :: s + if (checkFmt(fmt)) then s = safestr(xa, fmt) else @@ -1736,7 +1736,7 @@ contains function str_real_dp_matrix_fmt(xa, fmt) result(s) real(dp), dimension(:,:), intent(in) :: xa character(len=*), intent(in) :: fmt - character(len=len(xa(:,:),fmt)) :: s + character(len=len(xa,fmt)) :: s integer :: i, j, k, n @@ -1766,7 +1766,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:,:),fmt)) :: s + character(len=len(xa,fmt)) :: s if (checkFmt(fmt)) then s = safestr(xa, fmt) @@ -1782,7 +1782,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(xa(:,:))) :: s + character(len=len(xa)) :: s s = safestr(xa, "") #endif @@ -1800,7 +1800,7 @@ contains s = " " #else character(len=len(c, fmt)) :: s - + if (checkFmt(fmt)) then s = safestr(c, fmt) else @@ -1814,7 +1814,7 @@ contains complex(sp), intent(in) :: c character(len=*), intent(in) :: fmt character(len=len(c, fmt)) :: s - + real(sp) :: re, im integer :: i re = real(c) @@ -1841,13 +1841,13 @@ contains pure function str_complex_sp_array_fmt(ca, fmt) result(s) complex(sp), dimension(:), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca(:), fmt)) :: s + character(len=len(ca, fmt)) :: s integer :: i, n - + s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) n = len(ca(1), fmt)+1 - do i = 2, size(ca) + do i = 2, size(ca) s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) n = n + len(ca(i), fmt)+1 enddo @@ -1861,7 +1861,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:), fmt)) :: s + character(len=len(ca, fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -1877,7 +1877,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:))) :: s + character(len=len(ca)) :: s s = safestr(ca, "") #endif @@ -1887,7 +1887,7 @@ contains pure function str_complex_sp_matrix_fmt(ca, fmt) result(s) complex(sp), dimension(:, :), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca(:,:), fmt)) :: s + character(len=len(ca, fmt)) :: s integer :: i, j, k, n @@ -1917,7 +1917,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:,:), fmt)) :: s + character(len=len(ca, fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -1933,7 +1933,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:,:))) :: s + character(len=len(ca)) :: s s = safestr(ca, "") #endif @@ -1947,7 +1947,7 @@ contains s = " " #else character(len=len(c, fmt)) :: s - + if (checkFmt(fmt)) then s = safestr(c, fmt) else @@ -1961,7 +1961,7 @@ contains complex(dp), intent(in) :: c character(len=*), intent(in) :: fmt character(len=len(c, fmt)) :: s - + real(dp) :: re, im integer :: i re = real(c) @@ -1988,13 +1988,13 @@ contains pure function str_complex_dp_array_fmt(ca, fmt) result(s) complex(dp), dimension(:), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca(:), fmt)) :: s + character(len=len(ca, fmt)) :: s integer :: i, n s(1:len(ca(1), fmt)) = safestr(ca(1), fmt) n = len(ca(1), fmt)+1 - do i = 2, size(ca) + do i = 2, size(ca) s(n:n+len(ca(i), fmt)) = " "//safestr(ca(i), fmt) n = n + len(ca(i), fmt)+1 enddo @@ -2008,7 +2008,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:), fmt)) :: s + character(len=len(ca, fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -2024,7 +2024,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:))) :: s + character(len=len(ca)) :: s s = safestr(ca, "") #endif @@ -2034,7 +2034,7 @@ contains pure function str_complex_dp_matrix_fmt(ca, fmt) result(s) complex(dp), dimension(:, :), intent(in) :: ca character(len=*), intent(in) :: fmt - character(len=len(ca(:,:), fmt)) :: s + character(len=len(ca, fmt)) :: s integer :: i, j, k, n @@ -2064,7 +2064,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:,:), fmt)) :: s + character(len=len(ca, fmt)) :: s if (checkFmt(fmt)) then s = safestr(ca, fmt) @@ -2080,7 +2080,7 @@ contains character(len=1) :: s s = " " #else - character(len=len(ca(:,:))) :: s + character(len=len(ca)) :: s s = safestr(ca, "") #endif

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