├── .gitignore
├── CHANGES
├── LICENSE
├── README
├── TODO
├── doc
├── gettingstarted.pdf
├── using_xml_files.pdf
├── xml_again.pdf
└── xmlparse.html
├── examples
├── array_of_words
│ ├── Makefile
│ ├── array_of_words.xml
│ ├── example_words.xml
│ └── tst_array_of_words.f90
├── grid
│ ├── Makefile
│ ├── grid.xml
│ ├── grid_example.xml
│ └── tst_grid.f90
├── menu
│ ├── Makefile
│ ├── menu.xml
│ ├── menuitems.xml
│ └── tst_menu.f90
├── process
│ ├── Makefile
│ ├── simple.xml
│ └── tst_process.f90
├── readint
│ ├── Makefile
│ ├── readint.xml
│ ├── readint_example.xml
│ └── tst_readint.f90
└── writexml
│ ├── Makefile
│ └── writexml.f90
└── src
├── Makefile
├── read_from_buffer.inc
├── read_xml_array.inc
├── read_xml_primitives.f90
├── read_xml_scalar.inc
├── read_xml_word.inc
├── test_read_prims.f90
├── test_write_prims.f90
├── tree_find.f90
├── tree_struct.f90
├── tst_prims.f90
├── tstparse.f90
├── tstparse.inp
├── write_xml_primitives.f90
├── xmlparse.f90
├── xmlreader.conf
├── xmlreader.f90
├── xmlreader.inp
├── xmlreader_noprint.f90
└── xsdconvert.f90
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiler objects, modules, and executables
2 | examples/*.o
3 | examples/*.mod
4 | src/*.o
5 | src/*.mod
6 | src/libxmlparse.a
7 | src/xmlreader
8 |
9 | # Installed libraries
10 | lib/
11 |
12 | # Emacs backups
13 | *~
14 |
--------------------------------------------------------------------------------
/CHANGES:
--------------------------------------------------------------------------------
1 | Changes wrt previous versions:
2 | ------------------------------
3 |
4 | dd. 7 april 2008
5 | Corrected support for writing the data - the placeholder problem
6 | has been solved.
7 | This is now version 1.00
8 |
9 | dd. 27 december 2007
10 | Added support for writing the data to an XML file in
11 | xmlreader.f90 (the program now generates a routine write_xml_* as well
12 | that writes a file with the same structure as the routine read_xl_*
13 | reads). There is one problem left: if the definition contains
14 | "placeholders", the code is not written in the correct order.
15 |
16 | dd. 7 december 2007
17 | Added new predefined types, so that a specification like:
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | works as well (for all primitive types)
26 |
27 | Added the start of a program to convert XSD-files.
28 |
29 | dd. 25 june 2007
30 | Adjusted the xmlreader program to properly accept smaller arrays
31 | for fixed shape variables.
32 |
33 | dd. 19 june 2007
34 | Corrected a mistake with character variables with fixed shape.
35 | Allow larger arrays than according to shape
36 |
37 | dd. 17 june 2007
38 | Updated the documentation, prepared release of version 0.97
39 |
40 | dd. 12 june 2007
41 | Added support for arrays of fixed "shape" (see
42 | the menuitems example)
43 |
44 | Fixed a bug with handling the default values for variables
45 | (the code ended up in the wrong place)
46 |
47 | dd 10. june 2007
48 | Added routines to write basic data to an XML file.
49 | (Start of implementation of generating export routines
50 | in the xmlreader program)
51 |
52 | Corrected bug in xmlreader program (had to do with
53 | whitespace that interfered with the processing in the
54 | generated reader code)
55 |
56 | NOTE: Still need to check that this solves the
57 | problem entirely!
58 |
59 | Applied patch by Davide Cesari: array of strings and
60 | initialising pointer components.
61 |
62 | dd 26. february 2007
63 | Fixed a bug in the xmlreader program wrt reading
64 | attributes as data (self-contained elements were
65 | sometimes skipped - i.e.
66 | was not treated properly in all cases)
67 |
68 | dd 1. july 2006
69 | Corrected the initialisation of the array "entities"
70 | - the strings must all be the same length (not
71 | all compilers will complain about it though).
72 |
73 | And some corrections by Jacques Germishuizen for the
74 | examples.
75 |
76 | dd. 26 june 2006
77 | Fixed a few bugs pointed out by
78 | Jacques Germishuizen (rootname, double precision
79 | variables)
80 |
81 | dd. 16 june 2006
82 | Added a small configuration file to the
83 | xmlreader program - some global settings and
84 | a way to work around a limitation in some
85 | compilers.
86 | Also added a facility whereby attribute
87 | values are treated as data for a subelement:
88 | In the menu example (tst_menu.f90, menu.xml,
89 | menuitems.xml) the following fragments are
90 | equivalent:
91 |
92 |
93 |
94 | and
95 |
96 | -
97 | item
98 | Open ...
99 |
100 |
101 | dd. 9 june 2006
102 | Corrected a severe misunderstanding about XML -
103 | the requirement that there is a root element
104 | This has now been built into the xmlreader
105 | program.
106 | Some refinements to xmlparse.f90 (extra check
107 | on elements) and added proper
108 | comments.
109 |
110 | dd. 5 june 2006
111 | Added two examples
112 | Added subroutine xml_process
113 | Updated the documentation
114 | Corrected bugs in xmlreader.f90
115 |
116 | dd. 30 may 2006
117 | Added TODO file to the project
118 | Added support for "strict" parsing of the
119 | XML file via the generated reader routine.
120 |
121 | dd. 28 may 2006
122 | Solved a problem with place holder tags
123 | (anything after the closing tag was
124 | ignored)
125 |
126 | dd. 17 may 2006
127 | Corrected a few glitches in xmlreader.f90 -
128 | The code generated for place holder tags
129 | was incorrect.
130 |
131 | Note: There is still a bug which appears
132 | in example tst_grid. With the tag "general"
133 | in place, the gridarray tags are not seen:
134 |
135 |
136 |
137 | Read a couple of integers and a derived type
138 |
139 | 1020
140 |
141 |
142 | 1
143 | 2
144 | 2
145 |
146 | 1122
147 | 1223
148 | 23
149 |
150 | Maybe an ordering/loop problem?
151 |
152 | dd. 7 february 2006
153 | Corrected a stupid mistake in xmlreader.f90 -
154 | the spurious error message is gone
155 |
156 | dd. 6 february 2006
157 | Added placeholders to the xmlreader utility. More
158 | thorough tests required: the generated source code is
159 | composed of several pieces, the ordering can be
160 | wrong when the XML file is ordered differently.
161 |
162 | Note:
163 | The generated code is not tested yet. It looks okay,
164 | but that is all.
165 |
166 | dd. 24 january 2006
167 | Extended the xmlreader utility with new features.
168 | (mostly support for default values)
169 |
170 |
171 | dd. 19 october 2005
172 | Added support for "entities" (like an ampersand) in
173 | character data.
174 |
175 | Expanded the capabilities of the xmlreader program
176 | with arrays of primitives and derived types.
177 |
178 | Updated the project documents at http://xml-fortran.sf.net
179 |
180 |
181 | dd. 3 october 2005
182 | Integrated a patch by cinonet with the file xmlparse.f90.
183 |
184 | Updated the documentation with information on the xmlreader
185 | program.
186 |
187 | New version of the test program (more flexible) for the
188 | parser. Not finished yet.
189 |
190 | New version of the xmlreader program - added support for
191 | derived types.
192 |
193 |
194 | dd. 29 september 2005
195 | Added two articles on the use of XML in Fortran that appeared in
196 | the Fortran Forum.
197 |
198 | Updated the project page - new information on available libraries among
199 | other things.
200 |
201 | Added new code:
202 | - tree_struct.f90 and tree_find.f90:
203 | Module for manipulating general tree structures
204 |
205 | - read_xml_prims.f90 and test_read_prims.f90
206 | Code to read (integer) data returned from xml_get.
207 | Will be part of a new approach to dealing with XML files
208 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Note: The official xml-fortran repository on sourceforge
2 | only lists the license as "BSD License". It
3 | is assumed that this refers to the 3-clause BSD license ("modified" or "new")
4 | that is shown below.
5 |
6 | ================================================================================
7 |
8 | Copyright (c) 2011-2012 Paul K. Romano
9 |
10 | Copyright (c) 2008 Arjen Markus
11 | All rights reserved.
12 |
13 | Redistribution and use in source and binary forms, with or without modification,
14 | are permitted provided that the following conditions are met:
15 |
16 | * Redistributions of source code must retain the above copyright notice,
17 | this list of conditions and the following disclaimer.
18 |
19 | * Redistributions in binary form must reproduce the above copyright notice,
20 | this list of conditions and the following disclaimer in the documentation
21 | and/or other materials provided with the distribution.
22 |
23 | * Neither the name of the copyright holders nor the names of its
24 | contributors may be used to endorse or promote products derived from this
25 | software without specific prior written permission.
26 |
27 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
28 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
29 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT,
31 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
32 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
35 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Parser for XML-files in Fortran:
2 | --------------------------------
3 |
4 | CONTENTS
5 |
6 | The directory structure is straightforward:
7 |
8 | README
9 | This file
10 | CHANGES
11 | Version and release information
12 | doc/
13 | Contains the relevant documents describing how to use the parser module
14 | and the xmlreader utility that can generate the source of a module
15 | to read XML files with a particular structure.
16 | src/
17 | Contains the sources and a simple makefile for creating the library
18 | and test programs. Also contains the source for the xmlreader
19 | utility that
20 | examples/
21 | Contains example programs and input files
22 | lib/
23 | After installation, contains the library and the module file, so
24 | that other programs can use it
25 |
26 |
27 | INSTALLATION
28 |
29 | This depends on the compiler you use and the platform, but the following
30 | steps are required:
31 |
32 | 1. Make the library and the module file via the makefile or something
33 | equivalent:
34 |
35 | > make
36 |
37 | 2. Copy the library and the module file to a place where the compiler
38 | will be able to find them. Within the limits of the simple makefile
39 | that is delivered here, this should do the trick:
40 |
41 | > make install
42 |
43 | Notes:
44 | Compilers vary in the naming convention for the module files. It is
45 | virtually impossible to support them all, so the makefile supports
46 | only a few of the possibilities.
47 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | TODO
2 |
3 | dd. 7 april 2008
4 | Most issues listed below have been solved.
5 |
6 | Major thing to do right now: finish the xsdconvert program
7 |
8 |
9 | dd. 12 june 2007
10 | - Document the shape= option
11 |
12 | dd. 5 march 2007
13 | Found a number of limitations to the parser generated via the
14 | xmlreader program:
15 | + Comments at the beginning (before the root element) are not
16 | recognised as such
17 | + There is no message about the tag that was found
18 | + No indication of the line number at which an error occurred
19 | - Defining optional elements is tricky: defaults are not enough,
20 | as there is no indication of what element was filled and
21 | defining defaults for derived types is awkward (especially
22 | if they contain derived types as fields)
23 | - There is no facility to create a proper deallocation routine or a
24 | print/write routine at the moment.
25 | + If the element contains both attributes and data, the data are
26 | lost
27 |
28 | Found one bug as well:
29 | - The placeholder facility causes incomplete reading routines.
30 |
31 | Solving these issues will require a new attribute and an update
32 | of the manual.
33 |
34 | (Issues with + have been solved)
35 |
36 | Hm, yet another problem: comment in the menu example.
37 | Solution:
38 | Handle the complete comment element in that case
39 |
40 | dd. 26 february 2007
41 | - A checking facility can easily be built using
42 | the xmlreader program. It would take the same
43 | input as the xmlreader program but check that
44 | all elements contain the proper subelements and
45 | attributes.
46 |
47 | dd. 1 july 2006
48 | - Add a better checking facility - balanced open-close
49 | elements.
50 |
51 | dd. 9 june 2006
52 | - Fix a bug regarding the well-formedness of
53 | Done
54 | - Fix an oversight in the xmlreader program: there _must_ be
55 | a root element
56 | Done
57 | - Fix a bug regarding the number of data lines _after_ a
58 | closing tag ... Or is that not a bug?
59 |
60 | dd. 5 june 2006
61 | Added this TODO file as an "official" list of things that are to done.
62 |
63 | The following functionality should be added:
64 |
65 | - In xmlreader: a facility for generating an appropriate writing
66 | routine.
67 |
68 | - In xmlreader: the options globaltype and version number
69 | and a small settings file to set certain parameters
70 | separately from the definition of the data structure
71 | (one parameter: to circumvent an apparent restriction of
72 | certain compilers vis-a-vis dynamic string length)
73 |
74 | - More (realistic) examples
75 |
76 | - A more flexible way of storing the character data:
77 | Allocated arrays and strings of arbitrary length.
78 |
79 |
80 | Limitations and bugs to be overcome:
81 |
82 | - In xmlreader: optional placeholder tags have not been
83 | implemented yet
84 | - Decided not to implement that feature. In retrospect
85 | I see little use for this.
86 |
87 | - Not all aspects of XML are yet supported (CDATA and process
88 | instructions for instance)
89 |
90 | - Writing XML files is not well-tested. Add an example
91 | for this.
92 |
93 |
94 | Things that probably will never be done:
95 |
96 | - XSLT, XPath and the like.
97 |
--------------------------------------------------------------------------------
/doc/gettingstarted.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulromano/xml-fortran/ffe63a0591e86fc1c17a6cb673b56e633b15f906/doc/gettingstarted.pdf
--------------------------------------------------------------------------------
/doc/xmlparse.html:
--------------------------------------------------------------------------------
1 |
2 | XMLPARSE - XML
3 |
4 |
6 |
8 |
9 |
10 | XMLPARSE(n) 0.9 "XML"
11 |
12 |
13 |
XMLPARSE - Parser for XML files in Fortran
14 |
15 |
16 |
17 | TABLE OF CONTENTS
18 | SYNOPSIS
19 | DESCRIPTION
20 | PROCEDURES
21 | MOTIVATION
22 | PARAMETERS AND DERIVED TYPES
23 | GENERATING A READING ROUTINE
24 | EXAMPLES
25 | LIMITATIONS
26 | RELEASE NOTES
27 | TO DO
28 | KEYWORDS
29 |
30 |
31 |
subroutine xml_open( info, filename, mustread ) |
32 | subroutine xml_close( info ) |
33 | subroutine xml_options( info, ... ) |
34 | subroutine xml_get( info, tag, endtag, attribs, no_attribs, data, no_data ) |
35 | subroutine xml_put( info, tag, attribs, no_attribs, data, no_data, type ) |
36 | logical function xml_ok( info ) |
37 | logical function xml_error( info ) |
38 | logical function xml_data_trunc( info ) |
39 | integer function xml_find_attrib( attribs, no_attribs, name, value ) |
40 | subroutine read_xml_file_xxx( filename, lurep, error ) |
41 | subroutine xml_process( filename, attribs, data, startfunc, datafunc, endfunc, lurep, error ) |
42 |
|
43 |
44 |
45 | The XML parser provided by this module has been written entirely in
46 | Fortran, making it possible to read and write XML files without the need
47 | to use mixed-language programming techniques.
48 |
49 | It should be noted that the implementation has a number of limitations
50 | (cf. the section Limitations). The module has the following features:
51 |
52 |
53 | -
54 | Reading an XML-file (within certain limitations) in a stream-oriented
55 | manner.
56 |
57 |
58 | -
59 | Writing an XML-file in a stream-oriented manner.
60 |
61 |
62 | -
63 | Creating a reading routine that will fill a data structure. The data
64 | structure is described via an XML file and all necessary code to read
65 | files that conform to that structure is generated.
66 |
67 |
68 |
69 |
70 | The module has been implemented in standard Fortran 90. It is the
71 | intention to make it compilable by the F compiler as well, so that
72 | it can be used in conjunction to a wide set of Fortran compilers.
73 |
74 | (It should even be possible to convert the parsing routines to an
75 | equivalent library in FORTRAN 77, though with the availability of
76 | several free Fortran 95 compilers, there seems little need for that.)
77 |
78 |
79 |
80 | The module defines the following public routines and functions:
81 |
82 |
83 | - subroutine xml_open( info, filename, mustread )
-
84 |
85 | Open an XML-file and fill the structure info, so that it can be
86 | used to refer to the opened file.
87 |
88 | To check if all is well, (errors could be: the file can not be opened
89 | for some reason), the function xml_error() is available.
90 |
91 | Arguments:
92 |
93 |
94 | info - TYPE(XML_PARSE) structure used to identify the file
95 |
96 | filename - CHARACTER(LEN=*) name of the file to be opened
97 |
98 | mustread - LOGICAL whether to read the file or to write to it
99 |
100 |
101 |
102 | - subroutine xml_close( info )
-
103 |
104 | Close an opened XML-file. If the file was not opened, this routine has
105 | no effect.
106 |
107 | info - TYPE(XML_PARSE) structure used to identify the file
108 |
109 |
110 |
111 | - subroutine xml_options( info, ... )
-
112 |
113 | Set one or more options. These are all defined as optional arguments, so
114 | that the name=value convention can be used to select an option
115 | and to set its value. The first argument is fixed:
116 |
117 | info - TYPE(XML_PARSE) structure used to identify the file
118 |
119 | All other arguments are optional and include:
120 |
121 |
122 |
123 | ignore_whitespace - LOGICAL compress the array of strings (remove
124 | empty lines and remove leading blanks) for easier processing
125 |
126 | no_data_truncation - LOGICAL if data truncation occurs (too many
127 | lines of data or too many attributes, so that they can not all be stored
128 | in the arrays), this can be marked as an error or not. If the option is
129 | set to true, it is considered an error.
130 |
131 | report_lun - INTEGER LU-number of a file to which messages can be
132 | logged (use XML_STDOUT for output to screen)
133 |
134 | report_errors - LOGICAL write error messages to the report
135 |
136 | report_details - LOGICAL write detailed messages to the report,
137 | useful for debugging
138 |
139 |
140 | Note that these options are off by default. They should be set
141 | after the file has been opened. The reporting options can be set before
142 | an XML file has been opened, they hold globally (that is, they are in
143 | effect for all reading and writing, independent of the files).
144 |
145 |
146 |
147 | - subroutine xml_get( info, tag, endtag, attribs, no_attribs, data, no_data )
-
148 |
149 | Read the current tag in the file up to the next one or the end-of-file.
150 | Store the attributes in the given array and do the same for the
151 | character data that may be present after the tag.
152 |
153 |
154 | info - TYPE(XML_PARSE) structure used to identify the file
155 |
156 | tag - CHARACTER(LEN=*) string that will hold the tag's name
157 |
158 | endtag - LOGICAL indicates whether the current tag has ended or
159 | not
160 |
161 | attribs - CHARACTER(LEN=*), DIMENSION(:,:) array of strings that
162 | will hold the attributes given to the tag
163 |
164 | no_attribs - INTEGER number of attributes that were found
165 |
166 | data - CHARACTER(LEN=*), DIMENSION(:) array of strings that
167 | will hold the character data (one element per line)
168 |
169 | no_data - INTEGER number of lines of character data
170 |
171 | Note:
172 |
173 | If an error occurs or end-of-file is found, then use the functions
174 | xml_ok() and xml_error() to find out the conditions.
175 |
176 |
177 |
178 | - subroutine xml_put( info, tag, attribs, no_attribs, data, no_data, type )
-
179 |
180 | Write the information for the current tag to the file. This subroutine
181 | is the inverse, so to speak, of the subroutine xml_get that
182 | parses the XML input.
183 |
184 | For a description of the arguments, other than type: see above.
185 |
186 | type - CHARACTER(LEN=*) string having one the following values:
187 |
188 |
189 | -
190 | 'open' - Write an opening tag with attributes and data (if there
191 | are any). Useful for creating a hierarchy of tags.
192 |
193 | -
194 | 'close' - Write a closing tag
195 |
196 | -
197 | 'elem' - Write the element data
198 |
199 |
200 |
201 | - logical function xml_ok( info )
-
202 |
203 | Returns whether the parser is still okay (no read errors or
204 | end-of-file).
205 |
206 | info - TYPE(XML_PARSE) structure used to identify the file
207 |
208 |
209 |
210 | - logical function xml_error( info )
-
211 |
212 | Returns whether the parser has encountered some error (see also the
213 | options).
214 |
215 | info - TYPE(XML_PARSE) structure used to identify the file
216 |
217 |
218 |
219 | - logical function xml_data_trunc( info )
-
220 |
221 | Returns whether the parser has had to truncate the data or the
222 | attributes.
223 |
224 | info - TYPE(XML_PARSE) structure used to identify the file
225 |
226 |
227 |
228 | - integer function xml_find_attrib( attribs, no_attribs, name, value )
-
229 |
230 | Convenience function that searches the list of attributes and returns
231 | the index of the sought attribute in the array or -1 if not present.
232 | In that case the argument value is not set, so that you can use
233 | this to supply a default.
234 |
235 |
236 | attribs - CHARACTER(LEN=*), DIMENSION(:,:) array of strings that
237 | hold the attributes
238 |
239 | no_attribs - INTEGER number of attributes that was found
240 |
241 | name - CHARACTER(LEN=*) name of the attribute to be found
242 |
243 | value - CHARACTER(LEN=*) actual or default value of the attribute
244 | upon return
245 |
246 |
247 | - subroutine read_xml_file_xxx( filename, lurep, error )
-
248 |
249 | Subroutine generated via the method described below to read an XML
250 | file of a particular structure.
251 |
252 |
253 | filename - CHARACTER(LEN=*) name of the XML file to read
254 |
255 | lurep - INTEGER LU-number to use for reporting errors (use 0 to
256 | write to the screen; optional)
257 |
258 | error - LOGICAL variable that indicates if an error occurred
259 | while reading (optional).
260 |
261 |
262 | - subroutine xml_process( filename, attribs, data, startfunc, datafunc, endfunc, lurep, error )
-
263 |
264 | Subroutine that reads the XML file and calls three user-defined
265 | subroutines to take care of the actual processing. This is a
266 | routine that implements the so-called SAX approach.
267 |
268 |
269 | filename - CHARACTER(LEN=*) name of the XML file to read
270 |
271 | attribs - CHARACTER(LEN=*), DIMENSION(:,:) work array to store the
272 | attributes
273 |
274 | data - CHARACTER(LEN=*), DIMENSION(:) work array to store the
275 | character data associated with a tag
276 |
277 |
278 | startfunc - Subroutine that is called to handle the start
279 | of a tag:
280 | |
281 | subroutine startfunc( tag, attribs, error )
282 | character(len=*) :: tag
283 | character(len=*), dimension(:,:) :: attribs
284 | logical :: error
285 | |
286 |
287 | If the argument error is set to true (because the tag was unexpected or
288 | something similar), the reading is interrupted and the routine returns.
289 | Only the fact that something was wrong is recorded. You need to use
290 | other means to convey more information if that is needed.
291 |
292 |
293 | datafunc - Subroutine that is called to handle the character data
294 | associated with a tag:
295 | |
296 | subroutine datafunc( tag, attribs, error )
297 | character(len=*) :: tag
298 | character(len=*), dimension(:) :: data
299 | logical :: error
300 | |
301 |
302 |
303 | endfunc - Subroutine that is called to handle the end
304 | of a tag:
305 | |
306 | subroutine endfunc( tag, error )
307 | character(len=*) :: tag
308 | logical :: error
309 | |
310 |
311 | lurep - INTEGER LU-number to use for reporting errors (use 0 to
312 | write to the screen; optional)
313 |
314 | error - LOGICAL variable that indicates if an error occurred
315 | while reading (optional).
316 |
317 |
318 |
319 |
320 |
321 |
322 | The use of XML-files as a means to store data and more importantly to
323 | transfer data between very disparate applications and organisations has
324 | been growing these last few years. Standard implementations of libraries
325 | that deal with all features of XML or a significant part of them are
326 | available in many languages, but as far as we know there was no
327 | implementation in Fortran.
328 |
329 | One could of course use, say, the well-known Expat library by ... and
330 | provide a Fortran interface, but this is slightly awkward as it forces
331 | one to have a compatible C compiler. More importantly, this introduces
332 | platform-dependencies because the interfacing between Fortran and C
333 | depends strongly on the used compilers and this introduces a way of
334 | working that is alien to Fortran programmers: Expat requires the
335 | programmer to register a callback function, to be called when some
336 | "event" occurs while reading the file (a begin tag is found, character
337 | data are found and so on).
338 |
339 | The alternative is even more awkward: build a tree of tags and
340 | associated data and ask for these data. To a Fortran programmer, one of
341 | the first things they will want to do with an XML-file is to get all the
342 | information out - so a stream-oriented parsing method is more
343 | appropriate.
344 |
345 | Among the two predominant types of XML-parsing, SAX or stream-oriented
346 | parsing and DOM or object-oriented parsing, the stream-oriented approach
347 | is more suitable to the frame of mind of the average Fortran programmer.
348 | But instead of registering callbacks, this module uses the method known
349 | from, for instance, GNU's getopt() function: parse the data and return
350 | to the caller to have it process the information. The caller calls the
351 | function again and again, letting getopt() take care of the details.
352 |
353 | This is exactly the approach taken by the xmlparse module:
354 |
|
355 | call xml_open(info, ... )
356 |
357 | do while ( xml_ok(info) )
358 | call xml_get(info, ... ) ! Get the first/next tag
359 | ... identify the tag (via xml_check_tag for instance)
360 | ... process the information
361 | enddo
362 |
363 | call xml_close(info)
364 |
365 | ... proceed with the rest of the program
366 | |
367 |
368 |
369 | For convenience, the module does supply the routine xml_process
370 | that takes three user-defined subroutines to perform the actual
371 | processing. The file will be processed in its entirety.
372 |
373 |
374 |
375 | The module defines several parameters and derived types for use by the
376 | programmer:
377 |
378 | - XML_BUFFER_LENGTH
-
379 | the length of the internal buffer, representing
380 | the maximum length of any individual line in an XML file and the maximum
381 | length for a tag including all its attributes.
382 |
383 |
384 | - XML_STDOUT
-
385 | a parameter to indicate the standard output (or *) as the file to
386 | write messages to.
387 |
388 |
389 | - type(XML_PARSE)
-
390 | the data structure that holds information about
391 | the XML file to be read or written. Its contents are partially
392 | accessible via functions such as XML_OK() and XML_ERROR().
393 | Note: do not use its contents directly, as these may change in
394 | future.
395 |
396 |
397 |
398 |
399 |
400 | Reading an XML file and making sure the data are structured the way
401 | they are supposed to, generally requires a lot of code. This can not be
402 | avoided: you will want to make sure everything you need is there and
403 | anything else is dealt with appropriately.
404 |
405 | There is a way out: by automatically generating the reading routine
406 | you can reduce the amount of manual coding to a minimum. This has two
407 | advantages:
408 |
409 | -
410 | It is much less work to define the data and their place in an XML file
411 | than it is to encode the reading routine.
412 |
413 | -
414 | It is much less error-prone, if the logic is generated for you and
415 | therefore you need much less testing.
416 |
417 | The idea is simple:
418 |
419 | In an XML-file you define the data structure and the way this data
420 | structure should appear in an input XML file for your program.
421 | The process is probably best explained via an example.
422 |
423 | Say, you want to read addresses (a classical example). Each address
424 | consists of the name of the person, street name and the number of
425 | the house, city (let us keep it simple). Of course we have multiple
426 | addresses, so they are stored in an array. Then via the
427 | xmlreader program you can generate a reading routine that
428 | deals with this type of information.
429 |
430 | The program takes an XML file as input and produces a Fortran 90 module
431 | that reads input files and stores the data in the designated variables.
432 | It also creates a writing routine to write the data to an XML file.
433 |
434 | In our case, we want a derived type to hold the various pieces
435 | that form a complete address and we want an array of that type:
436 |
|
437 | <typedef name="address_type">
438 | <component name="person" type="character" length="40">
439 | <component name="street" type="character" length="40">
440 | <component name="number" type="integer">
441 | <component name="city" type="character" length="40">
442 | </typedef>
443 | <variable name="adress" dimension="1">
444 | |
445 |
446 | This will produce the following derived type:
447 | |
448 | type address_type
449 | character(len=40) :: person
450 | character(len=40) :: street
451 | integer :: number
452 | character(len=40) :: city
453 | end type address_type
454 | |
455 | and a variable "address":
456 | |
457 | type(address_type), dimension(:), pointer :: address
458 | |
459 |
460 | The reading routine will be able to read such XML files as the
461 | following:
462 | |
463 | <address>
464 | <person>John Doe</person>
465 | <street>Wherever street</street>
466 | <number>30</number>
467 | <city>Erewhon</city>
468 | </address>
469 | <address>
470 | ...
471 | </address>
472 | ...
473 | |
474 | If in some address the number was forgotten, the reading routine will
475 | report this, as by default all variables and components in a derived
476 | type must be present.
477 |
478 |
479 | Here is a more detailed description of the XML files accepted by the
480 | xmlreader program:
481 |
482 | -
483 | Use the comment tag to insert comments in the input file to
484 | reader (or the input to the resulting reading routines)
485 |
486 |
487 | -
488 | The options tag can be used to influence the generated code:
489 |
490 |
491 | -
492 | The attribute "strict" determines whether unknown tags are
493 | regarded as an error (strict="yes") or not (strict="no",
494 | the default).
495 |
496 | -
497 | The attribute "globaltype" is used to indicate that all variables should
498 | belong to a single derived type, whose name defaults to the name of the
499 | file. Use the "typename" attribute to set the name to a different value.
500 |
501 |
502 | -
503 | If you want to group tags for several variables, but you do not
504 | want to introduce a special derived type, you can do so with the
505 | placeholder tag. Its effect is to require an additional
506 | tag - end tag surrounding the data. Any tags defined within the
507 | placeholder - end placeholder tags will have to be put in the
508 | corresponding tags in the input file for the resulting program.
509 |
|
510 | <placeholder tag="grid">
511 | <variable x ...>
512 | <variable y ...>
513 | </placeholder>
514 | |
515 |
516 |
517 | -
518 | variable tags correspond directly to module variables.
519 | They are used to declare these variables and to generate the code that will
520 | read them.
521 |
522 | Variable tags can appear anywhere except within a type definition.
523 | Variables can be of a previously defined derived type or of a
524 | primitive type.
525 | |
526 | <variable name="x" type="integer" default="1" />
527 | |
528 | Variables can have a number of attributes:
529 |
530 |
531 | -
532 | Required attributes:
533 |
534 |
535 | name - the name of the variable in the actual program
536 |
537 | type - the type of the variable
538 |
539 | length - for character types only, the length of the string
540 |
541 |
542 |
543 | -
544 | Optional attributes:
545 |
546 |
547 | default - the default value to be used if information is missing
548 |
549 | dimension - the number of dimensions (up to 3), gives rise to a
550 | pointer component
551 |
552 | shape - the fixed size of an array, if this is present, the
553 | number of dimensions is taken from this attribute.
554 |
555 | tag - the name of the tag that holds the data (default to
556 | the name of the variable)
557 |
558 |
559 |
560 | -
561 | Basic types for the variables include:
562 |
563 |
564 | integer - a single integer value
565 |
566 | integer-array - a one-dimensional array of integer values (the
567 | values must appear between an opening and ending tag)
568 | real - a single-precision real value
569 |
570 | real-array - a one-dimensional array of real values (the
571 | values must appear between an opening and ending tag)
572 |
573 | double - a double-precision real value
574 |
575 | double-array - a one-dimensional array of double-precision values
576 | (the values must appear between an opening and ending tag)
577 |
578 | logical - a single logical value (represented as "T" or "F")
579 |
580 | logical-array - a one-dimensional array of logical values
581 | (the values must appear between an opening and ending tag)
582 |
583 | word - a character string as can be read via list-directed input
584 | (if it should contain spaces, surround it with single or double quotes)
585 |
586 | word-array - a one-dimensional array of strings
587 | (the values must appear between an opening and ending tag)
588 |
589 | line - a character string as can be read from a single line
590 | of text (via the '(A)' format)
591 |
592 | line-array - a one-dimensional array of strings, read as
593 | individual lines between the opening and closing tag
594 |
595 | character - a character string (synonym for "line")
596 |
597 | character-array - a one-dimensional array of character strings,
598 | synonym for line-array
599 |
600 |
601 |
602 |
603 | -
604 | Type definitions (typedef)allow the xmlreader program to
605 | define the derived types that you want to use in your reader.
606 |
607 | The typedef tag may only contain component tags. They
608 | are synonym to variable tags with the same restrictions.
609 |
610 |
611 |
612 |
613 | Future versions may also include options for:
614 |
615 | -
616 | Adding code to handle certain data in a particular way
617 |
618 | -
619 | Version checking (so that an input file is explicitly identified
620 | as being of a particular version of the software)
621 |
622 |
623 |
624 |
625 | The directory "examples" contains some example programs.
626 |
627 | -
628 | The tst_grid program demonstrates how to create a reader
629 | for an array of "grids", each consisting of two integers.
630 |
631 | -
632 | The tst_menu program uses a more elaborate structure,
633 | a menubar with menus and each menu having an array of items.
634 | Items in a menu can have a submenu. This leads to an XML file with
635 | multiple hierarchical layers.
636 |
637 | -
638 | The tst_process program uses the xml_process routine to
639 | read in an XML file (a "docbook" file) and turn it into an HTML file for
640 | viewing.
641 |
642 |
643 |
644 |
645 |
646 | Basic limitations:
647 |
648 | -
649 | The lines in the XML-file should not exceed 1000 characters. For tags
650 | that span more than one line, the limit holds for all the lines together
651 | (without leading or trailing blanks).
652 |
653 |
654 | -
655 | There is no support for DTDs or namespaces, XSLT, XPath and
656 | other more advanced features around XML.
657 |
658 |
659 | -
660 | There is currently no support for the object-oriented approach. It is up
661 | to the application to store the information that is needed, while the
662 | parsing is going on.
663 |
664 |
665 | -
666 | No support (yet) for a single quote as delimiter
667 |
668 |
669 | -
670 | No support (yet) for conversion of escape sequences (>. for instance)
671 |
672 |
673 | -
674 | The parser may not handle malformed XML-files properly
675 |
676 |
677 | -
678 | The parser does not (yet) handle different line-endings properly (that
679 | is: reading XML-files that were written under MS Windows in a UNIX or
680 | Linux environment)
681 |
682 |
683 |
684 |
685 |
686 | This document belongs to version 1.00 of the module.
687 |
688 | History:
689 |
690 | version 0.1: Proof of concept, august 2003
691 |
692 | A very preliminary version meant to show that it is indeed possible to
693 | read and write XML files using Fortran only. It was published on the
694 | comp.lang.fortran newsgroup and generated enough interest to encourage
695 | further development.
696 |
697 | version 0.2: First public release, august 2003
698 |
699 | After some additional testing with practical XML-files, a number of bugs
700 | were found and solved, several enhancements were made:
701 |
702 | -
703 | Handling attributes (especially when tags span more than one line and
704 | correctly handling the case that too many attributes are present).
705 |
706 | -
707 | Options for parsing and error handling added, as well as functions to
708 | check the status.
709 |
710 | -
711 | Revision of the API, for more uniform names (prefix: xml_)
712 |
713 | -
714 | Setting up the documentation (this document in particular)
715 |
716 |
717 | version 0.3: Improvements, september 2003
718 |
719 | -
720 | Added the function xml_error()
721 |
722 | -
723 | Implemented the report options
724 |
725 | -
726 | Corrected a bug in xml_close (causing an infinite loop in the
727 | test program).
728 |
729 | -
730 | Revised the test program to run through a number of test
731 | files.
732 |
733 |
734 | version 0.4: Corrected xml_put(), october 2003
735 |
736 | -
737 | Adjusted the interface and implementation of the subroutine xml_put()
738 | It will now produce correct and reasonably looking XML files.
739 |
740 | -
741 | Added a test program, tstwrite.f90, for this.
742 |
743 |
744 |
745 | version 0.9: Added new approach, october 2005
746 |
747 | -
748 | Changes to the interface and implementation of the subroutine xml_put(),
749 | from a patch by cinonet.
750 |
751 | -
752 | Added a program, xmlreader, to generate complete reading routines for
753 | particular XML files (cf. GENERATING A READING ROUTINE
754 |
755 |
756 |
757 | version 0.94: Gradually expanding the capabilities, june 2006
758 |
759 | -
760 | Added a routine xml_process that enables you to use an
761 | event-based approach like in the famous Expat library.
762 |
763 | -
764 | Added the option strict and the tag placeholder.
765 |
766 | -
767 | Corrected a number of bugs associated with the xmlreader program
768 |
769 |
770 |
771 | version 0.97: Added the following capabilities to the
772 | xmlreader program since 0.94, june 2007
773 |
774 | -
775 | Support for the shape option
776 |
777 | -
778 | Defaults for both components of a derived type and for
779 | independent variables.
780 |
781 | -
782 | The generated reading routine takes care of elements that have
783 | attributes and character data now. The character data is treated as if
784 | it were an attribute with the name "value"
785 |
786 | -
787 | Several bugs corrected in the xmlreader program
788 |
789 |
790 |
791 | version 1.00: Added the following capabilities to the
792 | xmlreader program since 0.97, april 2008
793 |
794 | -
795 | Write a writing routine to write the data to a XML file
796 |
797 | The project now also contains a first version of a program to convert an
798 | XSD file to a file accepted by the xmlreader program. This is called
799 | "xsdconvert".
800 |
801 |
802 |
803 | The following items remain on the "to do" list:
804 |
805 | -
806 | Adding checks for truncation of strings (attribute names/values too
807 | long, data lines too long; now only the number is checked).
808 |
809 | -
810 | Documenting details about structures and parameters that may be of
811 | interest.
812 |
813 |
814 |
815 |
816 |
817 |
818 | Fortran, XML, parsing
819 |
820 |
821 |
--------------------------------------------------------------------------------
/examples/array_of_words/Makefile:
--------------------------------------------------------------------------------
1 | program = tst_array_of_words
2 | template = array_of_words
3 | objects = $(program).o $(template).o ../../src/xmlparse.a
4 |
5 | #===============================================================================
6 | # Compiler Options
7 | #===============================================================================
8 |
9 | F90 = gfortran
10 | F90FLAGS = -g -fbacktrace -I../../src
11 | LDFLAGS =
12 |
13 | #===============================================================================
14 | # Targets
15 | #===============================================================================
16 |
17 | all: $(program)
18 | $(program): $(objects)
19 | $(F90) $(objects) -o $@ $(LDFLAGS)
20 | clean:
21 | @rm -f *.o *.mod *.out $(template).f90 $(program)
22 | neat:
23 | @rm -f *.o *.mod *.out $(template).f90
24 |
25 | #===============================================================================
26 | # Rules
27 | #===============================================================================
28 |
29 | .SUFFIXES: .f90 .o
30 | .PHONY: all clean neat
31 |
32 | #===============================================================================
33 | # Dependencies
34 | #===============================================================================
35 |
36 | $(program).o: $(template).o
37 | $(F90) -c $(F90FLAGS) $(program).f90
38 |
39 | $(template).o: $(template).xml
40 | ../../src/xmlreader $(template)
41 | $(F90) -c $(F90FLAGS) $(template).f90
42 |
--------------------------------------------------------------------------------
/examples/array_of_words/array_of_words.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/examples/array_of_words/example_words.xml:
--------------------------------------------------------------------------------
1 |
2 | one
3 | two
4 | three
5 | four
6 | 1.0
7 | 2.0
8 | 3.0
9 | 4.0
10 | 5.0
11 |
12 |
--------------------------------------------------------------------------------
/examples/array_of_words/tst_array_of_words.f90:
--------------------------------------------------------------------------------
1 | ! Test program for generated code:
2 | ! Test the option of one word or real per element
3 | !
4 | program tst_array_of_words
5 | use xml_data_array_of_words
6 | implicit none
7 |
8 | call read_xml_file_array_of_words( 'example_words.xml' )
9 |
10 | write(*,*) 'Words:'
11 | write(*,'(a)') word
12 | write(*,*) 'Reals:'
13 | write(*,'(f10.4)') real_value
14 |
15 | call write_xml_file_array_of_words( 'output_words.xml' )
16 | end program
17 |
--------------------------------------------------------------------------------
/examples/grid/Makefile:
--------------------------------------------------------------------------------
1 | program = tst_grid
2 | template = grid
3 | objects = $(program).o $(template).o ../../src/xmlparse.a
4 |
5 | #===============================================================================
6 | # Compiler Options
7 | #===============================================================================
8 |
9 | F90 = gfortran
10 | F90FLAGS = -g -fbacktrace -I../../src
11 | LDFLAGS =
12 |
13 | #===============================================================================
14 | # Targets
15 | #===============================================================================
16 |
17 | all: $(program)
18 | $(program): $(objects)
19 | $(F90) $(objects) -o $@ $(LDFLAGS)
20 | clean:
21 | @rm -f *.o *.mod *.out $(template).f90 $(program)
22 | neat:
23 | @rm -f *.o *.mod *.out $(template).f90
24 |
25 | #===============================================================================
26 | # Rules
27 | #===============================================================================
28 |
29 | .SUFFIXES: .f90 .o
30 | .PHONY: all clean neat
31 |
32 | #===============================================================================
33 | # Dependencies
34 | #===============================================================================
35 |
36 | $(program).o: $(template).o
37 | $(F90) -c $(F90FLAGS) $(program).f90
38 |
39 | $(template).o: $(template).xml
40 | ../../src/xmlreader $(template)
41 | $(F90) -c $(F90FLAGS) $(template).f90
42 |
--------------------------------------------------------------------------------
/examples/grid/grid.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | This is an example of a template for the xmlreader program:
5 | The template defines (a part of) the data structure for the
6 | program that will use the generated reader routines. The
7 | xmlreader program can generate:
8 | - the Fortran code to read the XML file containing the
9 | actual data (this is put in a module)
10 | - an example of an acceptable XML file, so that you can check
11 | that all is as expected
12 |
13 | Use the "comment" tag to insert comments in this template.
14 |
15 | Additional comments:
16 | - Extend with version information
17 | - Options for treating unknown tags
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | The name of a variable can not be the same as that
34 | of a derived type. So, make sure there are two
35 | different names ...
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/examples/grid/grid_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Read a couple of integers and a derived type
5 |
6 | 1020
7 |
8 |
9 | 1
10 | 2
11 | 2
12 |
13 | 1122
14 | 1223
15 | 23
16 |
17 |
--------------------------------------------------------------------------------
/examples/grid/tst_grid.f90:
--------------------------------------------------------------------------------
1 | ! Test program for generated code
2 | !
3 | program tst_grid
4 | use xml_data_grid
5 | call read_xml_file_grid( 'grid_example.xml' )
6 | write(*,*) 'A,B,C:', a, b, c
7 | write(*,*) 'grid:', grid
8 | write(*,*) 'grid_array:', grid_array
9 | call write_xml_file_grid( 'out_grid.xml', 20 )
10 | end program
11 |
--------------------------------------------------------------------------------
/examples/menu/Makefile:
--------------------------------------------------------------------------------
1 | program = tst_menu
2 | template = menu
3 | objects = $(program).o $(template).o ../../src/xmlparse.a
4 |
5 | #===============================================================================
6 | # Compiler Options
7 | #===============================================================================
8 |
9 | F90 = gfortran
10 | F90FLAGS = -g -fbacktrace -I../../src
11 | LDFLAGS =
12 |
13 | #===============================================================================
14 | # Targets
15 | #===============================================================================
16 |
17 | all: $(program)
18 | $(program): $(objects)
19 | $(F90) $(objects) -o $@ $(LDFLAGS)
20 | clean:
21 | @rm -f *.o *.mod *.out $(template).f90 $(program)
22 | neat:
23 | @rm -f *.o *.mod *.out $(template).f90
24 |
25 | #===============================================================================
26 | # Rules
27 | #===============================================================================
28 |
29 | .SUFFIXES: .f90 .o
30 | .PHONY: all clean neat
31 |
32 | #===============================================================================
33 | # Dependencies
34 | #===============================================================================
35 |
36 | $(program).o: $(template).o
37 | $(F90) -c $(F90FLAGS) $(program).f90
38 |
39 | $(template).o: $(template).xml
40 | ../../src/xmlreader $(template)
41 | $(F90) -c $(F90FLAGS) $(template).f90
42 |
--------------------------------------------------------------------------------
/examples/menu/menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | This file defines a fairly elaborate data structure:
5 | A menubar that contains menus. Each menu contains several
6 | items and an item can hold a submenu. Submenu only contain
7 | items.
8 |
9 |
10 |
11 | Hm, optional components of a derived type ... How to treat these?
12 | Simple: default="submenu_t('', '')", as the value will be
13 | written on the right-hand side of the assignment
14 |
15 | Note:
16 | When I added an array of items, I wanted to set the submenu
17 | default to:
18 | default="submenu_t('','',subitem_t('',''))"
19 | The resulting code made two compilers complain in a nasty
20 | way. Apparently you can not nest constructors this way.
21 | (And the subitem is a pointer to an array ... this
22 | will require a more subtle approach!)
23 |
24 | TODO: add a keyword "optional"
25 |
26 | To save the data:
27 | #typedef name="subitem_t"#
28 | #component name="type" type="line" length="20"##/component#
29 | #component name="name" type="line" length="20"##/component#
30 | #/typedef#
31 | In submenu:
32 | #component name="item" type="submenu_t" dimension="1"##/component#
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | Now the variables themselves ... just a single menubar
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/examples/menu/menuitems.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | This is an example of a more elaborate XML file:
6 | It defines a menubar - nothing very realistic, mind you,
7 | but it shows that the xmlreader program can handle fairly
8 | complex structures
9 |
10 | General
11 | 1 2 3
12 | 4 5 6
13 |
24 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/examples/menu/tst_menu.f90:
--------------------------------------------------------------------------------
1 | ! Test program for generated code:
2 | ! A menubar with menus and submenus
3 | !
4 | program tst_menu
5 | use xml_data_menu
6 | implicit none
7 | integer :: i, j, k
8 |
9 | call read_xml_file_menu( 'menuitems.xml' )
10 | write(*,*) 'Just a value: ', just_a_value
11 | write(*,*) 'Menubar: ', menubar%name
12 | write(*,*) ' Number:', menubar%number
13 | write(*,*) ' (Shape:', shape(menubar%number), ')'
14 | write(*,*) ' Menus: '
15 | do i = 1,size(menubar%menu)
16 | write(*,*) ' Menu: ', menubar%menu(i)%name
17 | write(*,*) ' Items: '
18 | do j = 1,size(menubar%menu(i)%item)
19 | write(*,*) ' Item: ', menubar%menu(i)%item(j)%name,' - ', &
20 | menubar%menu(i)%item(j)%type
21 | write(*,*) ' Data:', menubar%menu(i)%item(j)%data
22 | if ( menubar%menu(i)%item(j)%type == 'submenu' ) then
23 | write(*,*) ' Submenu: '
24 | !
25 | ! Should be an array ... but there is a problem with defaults
26 | !
27 | write(*,*) ' Item: ', menubar%menu(i)%item(j)%submenu%item
28 |
29 | ! do k = 1,size(item)
30 | ! write(*,*) ' Item: ', menubar%menu(i)%item(j)%submenu%item(k)%name &
31 | ! '- ', menubar%menu(i)%item(j)%submenu%item(k)%(name,type)
32 | ! enddo
33 | endif
34 | enddo
35 | enddo
36 | end program
37 |
--------------------------------------------------------------------------------
/examples/process/Makefile:
--------------------------------------------------------------------------------
1 | program = tst_process
2 | objects = $(program).o ../../src/xmlparse.a
3 |
4 | #===============================================================================
5 | # Compiler Options
6 | #===============================================================================
7 |
8 | F90 = gfortran
9 | F90FLAGS = -g -fbacktrace -I../../src
10 | LDFLAGS =
11 |
12 | #===============================================================================
13 | # Targets
14 | #===============================================================================
15 |
16 | all: $(program)
17 | $(program): $(objects)
18 | $(F90) $(objects) -o $@ $(LDFLAGS)
19 | clean:
20 | @rm -f *.o *.mod *.html $(program)
21 | neat:
22 | @rm -f *.o *.mod *.html
23 |
24 | #===============================================================================
25 | # Rules
26 | #===============================================================================
27 |
28 | .SUFFIXES: .f90 .o
29 | .PHONY: all clean neat
30 |
31 | %.o: %.f90
32 | $(F90) -c $(F90FLAGS) $<
33 |
--------------------------------------------------------------------------------
/examples/process/simple.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Simple Use of PLplot
4 |
5 |
6 | Plotting a Simple Graph
7 |
8 |
9 | We shall first consider plotting simple graphs showing the dependence
10 | of one variable upon another. Such a graph may be composed of
11 | several elements:
12 |
13 |
14 |
15 |
16 | A box which defines the ranges of the variables, perhaps with
17 | axes and numeric labels along its edges.
18 |
19 |
20 |
21 |
22 | A set of points or lines within the box showing the functional
23 | dependence.
24 |
25 |
26 |
27 |
28 | A set of labels for the variables and a title for the graph.
29 |
30 |
31 |
32 |
33 | In order to draw such a graph, it is necessary to call at least four
34 | of the PLplot functions:
35 |
36 |
37 |
38 |
39 |
40 | &plinit;, to initialize PLplot.
41 |
42 |
43 |
44 |
45 | &plenv;, to define the range and scale of the
46 | graph, and draw labels, axes, etc.
47 |
48 |
49 |
50 |
51 | One or more calls to &plline; or &plpoin; to draw lines or points
52 | as needed. Other more complex routines include &plbin; and
53 | &plhist; to draw histograms, &plerrx; and &plerry; to draw
54 | error-bars.
55 |
56 |
57 |
58 |
59 | &plend;, to close the plot.
60 |
61 |
62 |
63 |
64 |
65 | More than one graph can be drawn on a single set of axes by making
66 | repeated calls to the routines listed in item 3 above. PLplot only
67 | needs to be initialized once unless plotting to multiple output
68 | devices.
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/examples/process/tst_process.f90:
--------------------------------------------------------------------------------
1 | ! Test program for xml_process
2 | !
3 | ! The program reads a small file in the docbook format
4 | ! (extracted from the PLplot project - http://plplot.sf.net)
5 | ! and writes it out as an HTML file
6 | !
7 | ! Note:
8 | ! In this example we do not check the structure of the
9 | ! XML file.
10 | ! Of course the conversion is very, very simple.
11 | !
12 | module convert_html
13 | character(len=20) :: section_type
14 | character(len=20), dimension(10) :: end_tag
15 | logical :: title
16 | integer :: level = 0
17 | contains
18 |
19 | subroutine startfunc( tag, attribs, error )
20 | character(len=*) :: tag
21 | character(len=*), dimension(:,:) :: attribs
22 | logical :: error
23 |
24 | level = level + 1
25 | select case( tag )
26 | case( 'chapter' )
27 | section_type = '
'
28 | case( 'sect1' )
29 | section_type = ''
30 | case( 'title' )
31 | title = .true.
32 | end_tag(level) = section_type(1:1) // '/' // section_type(2:)
33 | case( 'para')
34 | write( 20, * ) '
'
35 | end_tag(level) = '
'
36 | case( 'itemizedlist' )
37 | write( 20, * ) ''
38 | end_tag(level) = '
'
39 | case( 'orderedlist' )
40 | write( 20, * ) ''
41 | end_tag(level) = '
'
42 | case( 'listitem' )
43 | write( 20, * ) ''
44 | end_tag(level) = ''
45 | end select
46 | end subroutine
47 |
48 | subroutine datafunc( tag, data, error )
49 | character(len=*) :: tag
50 | character(len=*), dimension(:) :: data
51 | logical :: error
52 |
53 | integer :: i
54 |
55 | ! Nothing much to do ...
56 |
57 | if ( .not. title ) then
58 | if ( any( data .ne. ' ' ) ) then
59 | write(20,'(a)') ( data(i), i=1,size(data) )
60 | endif
61 | else
62 | write(20,'(a)') section_type, ( data(i), i=1,size(data) ), end_tag(level)
63 | level = level - 1
64 | endif
65 |
66 | end subroutine
67 |
68 | subroutine endfunc( tag, error )
69 | character(len=*) :: tag
70 | logical :: error
71 |
72 | integer :: i
73 |
74 | ! Nothing much to do ...
75 | if ( title ) then
76 | title = .false.
77 | else
78 | write(20,*) trim(end_tag(level))
79 | level = level - 1
80 | endif
81 |
82 | end subroutine
83 |
84 | end module
85 |
86 | program tst_process
87 | use xmlparse
88 | use convert_html
89 |
90 | character(len=40), dimension(2,10) :: attribs
91 | character(len=80), dimension(100) :: data
92 | logical :: error
93 |
94 | open( 20, file = 'simple.html' )
95 | write( 20 , * ) 'Example of converting XML to HTML'
96 | write( 20 , * ) ''
97 |
98 | call xml_process( 'simple.xml', attribs, data, startfunc, datafunc, endfunc, 0, error )
99 |
100 | write( 20 , * ) ''
101 |
102 | end program
103 |
--------------------------------------------------------------------------------
/examples/readint/Makefile:
--------------------------------------------------------------------------------
1 | program = tst_readint
2 | template = readint
3 | objects = $(program).o $(template).o ../../src/xmlparse.a
4 |
5 | #===============================================================================
6 | # Compiler Options
7 | #===============================================================================
8 |
9 | F90 = gfortran
10 | F90FLAGS = -g -fbacktrace -I../../src
11 | LDFLAGS =
12 |
13 | #===============================================================================
14 | # Targets
15 | #===============================================================================
16 |
17 | all: $(program)
18 | $(program): $(objects)
19 | $(F90) $(objects) -o $@ $(LDFLAGS)
20 | clean:
21 | @rm -f *.o *.mod *.out $(template).f90 $(program)
22 | neat:
23 | @rm -f *.o *.mod *.out $(template).f90
24 |
25 | #===============================================================================
26 | # Rules
27 | #===============================================================================
28 |
29 | .SUFFIXES: .f90 .o
30 | .PHONY: all clean neat
31 |
32 | #===============================================================================
33 | # Dependencies
34 | #===============================================================================
35 |
36 | $(program).o: $(template).o
37 | $(F90) -c $(F90FLAGS) $(program).f90
38 |
39 | $(template).o: $(template).xml
40 | ../../src/xmlreader $(template)
41 | $(F90) -c $(F90FLAGS) $(template).f90
42 |
--------------------------------------------------------------------------------
/examples/readint/readint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Read a couple of integers
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/readint/readint_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Read a couple of integers
5 |
6 | 10
7 | 23
8 |
9 |
10 | 1 2 3 4 5 6
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/readint/tst_readint.f90:
--------------------------------------------------------------------------------
1 | ! Test program for generated code
2 | !
3 | program tst_readint
4 | use xml_data_readint
5 | call read_xml_file_readint( 'readint_example.xml' )
6 | write(*,*) 'x,y,z:', x, y, z
7 | write(*,*) 'w:', w
8 | end program
9 |
--------------------------------------------------------------------------------
/examples/writexml/Makefile:
--------------------------------------------------------------------------------
1 | program = writexml
2 | objects = $(program).o ../../src/xmlparse.a
3 |
4 | #===============================================================================
5 | # Compiler Options
6 | #===============================================================================
7 |
8 | F90 = gfortran
9 | F90FLAGS = -g -fbacktrace -I../../src
10 | LDFLAGS =
11 |
12 | #===============================================================================
13 | # Targets
14 | #===============================================================================
15 |
16 | all: $(program)
17 | $(program): $(objects)
18 | $(F90) $(objects) -o $@ $(LDFLAGS)
19 | clean:
20 | @rm -f *.o *.mod $(program)
21 | neat:
22 | @rm -f *.o *.mod
23 |
24 | #===============================================================================
25 | # Rules
26 | #===============================================================================
27 |
28 | .SUFFIXES: .f90 .o
29 | .PHONY: all clean neat
30 |
31 | %.o: %.f90
32 | $(F90) -c $(F90FLAGS) $<
33 |
--------------------------------------------------------------------------------
/examples/writexml/writexml.f90:
--------------------------------------------------------------------------------
1 | ! writexml.f90 --
2 | ! Example of writing a simple XML file
3 | !
4 | ! Note:
5 | ! This example uses the fairly low-level routine xml_put.
6 | ! In the near future the xmlreader program should be able
7 | ! to produce a writer routine automatically in much the
8 | ! same way as it can produce a reader routine from the
9 | ! definition.
10 | !
11 | program writexml
12 | use xmlparse
13 |
14 | implicit none
15 | type(xml_parse) :: info
16 | character(len=20) :: tag
17 | character(len=20), dimension(2,10) :: attribs
18 | character(len=50), dimension(10) :: data
19 | integer :: no_attribs
20 | integer :: no_data
21 | character(len=20) :: type
22 |
23 | !
24 | ! Open the file for writing (.false.)
25 | !
26 | call xml_open( info, "example.xml", .false. )
27 |
28 | !
29 | ! Write the root element (only the open tag)
30 | !
31 | no_attribs = 0
32 | no_data = 0
33 | call xml_put( info, "examples", attribs, no_attribs, data, no_data, "open" )
34 |
35 | !
36 | ! Write a few elements:
37 | ! Each element is given with all information that is required, so we
38 | ! can write the opening and closing tags at once.
39 | !
40 | no_attribs = 1
41 | attribs(1,1) = "name"
42 | attribs(2,1) = "tst_menu.f90"
43 | no_data = 2
44 | data(1) = "Define a menubar with nested menus"
45 | data(2) = "Shows how to deal with arrays"
46 | call xml_put( info, "example", attribs, no_attribs, data, no_data, "elem")
47 | !
48 | no_attribs = 3
49 | attribs(1,1) = "name"
50 | attribs(2,1) = "tst_grid.f90"
51 | attribs(1,2) = "definition"
52 | attribs(2,2) = "grid.xml"
53 | attribs(1,3) = "input"
54 | attribs(2,3) = "grid_example.xml"
55 | no_data = 2
56 | data(1) = "Conglomerate of options, nothing really structured"
57 | data(2) = "- despite the name"
58 | call xml_put( info, "example", attribs, no_attribs, data, no_data, "elem")
59 | !
60 | ! Close the root tag
61 | !
62 | no_attribs = 0
63 | no_data = 0
64 | call xml_put( info, "examples", attribs, no_attribs, data, no_data, "close")
65 | !
66 | ! Close the file
67 | !
68 | call xml_close( info )
69 |
70 | end program
71 |
--------------------------------------------------------------------------------
/src/Makefile:
--------------------------------------------------------------------------------
1 | # Naive makefile for the simple test program "tstparse"
2 | #
3 | # Coarse test for the XML-Fortran module
4 | #
5 |
6 | #===============================================================================
7 | # User Options
8 | #===============================================================================
9 |
10 | COMPILER = intel
11 | DEBUG = no
12 |
13 | #===============================================================================
14 | # Compiler Options
15 | #===============================================================================
16 |
17 | # Intel Fortran compiler options
18 |
19 | ifeq ($(COMPILER),intel)
20 | F90 = ifort
21 | F90FLAGS += -fpp -warn
22 | endif
23 |
24 | # GNU Fortran compiler options
25 |
26 | ifeq ($(COMPILER),gfortran)
27 | F90 = gfortran
28 | F90FLAGS += -cpp -Wall
29 | endif
30 |
31 | # Set compiler flags for debugging
32 |
33 | ifeq ($(DEBUG),yes)
34 | F90FLAGS += -g
35 | LDFLAGS += -g
36 | ifeq ($(COMPILER),intel)
37 | F90FLAGS += -traceback -ftrapuv -fp-stack-check -check all
38 | endif
39 | ifeq ($(COMPILER),gfortran)
40 | F90FLAGS += -pedantic -std=f2008 -fbacktrace -fbounds-check \
41 | -ffpe-trap=invalid,zero,overflow,underflow
42 | endif
43 | endif
44 |
45 | #===============================================================================
46 | # Targets
47 | #===============================================================================
48 |
49 | all: xmlreader
50 | test: tstparse
51 | install:
52 | mkdir -p ../lib
53 | cp libxmlparse.a ../lib
54 | cp xmlparse.mod read_xml_primitives.mod write_xml_primitives.mod ../lib
55 | clean:
56 | @rm -f *.o *.mod *.a xmlreader tstparse
57 | neat:
58 | @rm -f *.o *.mod *.a
59 |
60 | #===============================================================================
61 | # Rules
62 | #===============================================================================
63 |
64 | .SUFFIXES: .f90 .o
65 | .PHONY: all clean neat
66 |
67 | %.o: %.f90
68 | $(F90) -c $(F90FLAGS) $<
69 |
70 | #===============================================================================
71 | # Dependencies
72 | #===============================================================================
73 |
74 | libxmlparse.a: xmlparse.o read_xml_primitives.o write_xml_primitives.o
75 | ar r libxmlparse.a xmlparse.o read_xml_primitives.o write_xml_primitives.o
76 |
77 | tstparse: tstparse.o libxmlparse.a
78 | $(F90) tstparse.o libxmlparse.a -o $@
79 |
80 | xmlreader: xmlreader.o libxmlparse.a
81 | $(F90) xmlreader.o libxmlparse.a -o $@
82 |
83 | tstparse.o: xmlparse.o
84 | xmlreader.o: xmlparse.o
85 |
--------------------------------------------------------------------------------
/src/read_from_buffer.inc:
--------------------------------------------------------------------------------
1 | ! Part of XML-Fortran library:
2 | !
3 | ! $Id: read_from_buffer.inc,v 1.2 2006/03/26 19:05:48 arjenmarkus Exp $
4 | !
5 | character(len=*), intent(in) :: buffer
6 | integer, intent(inout) :: ierror
7 |
8 | integer :: n
9 | integer :: i
10 | integer :: step
11 | integer :: ierr
12 | !
13 | ! First allocate an array that is surely large enough
14 | ! Note:
15 | ! This is not completely failsafe: with list-directed
16 | ! input you can also use repeat counts (10000*1.0 for
17 | ! instance).
18 | !
19 | allocate( work(len(buffer)/2+1) )
20 |
21 | !
22 | ! NOTE:
23 | ! This is not portable!!
24 | !
25 | ! read( buffer, *, iostat = ierror ) (work(n), n=1,size(work))
26 | !
27 | ! So, use a different strategy: a binary search
28 | ! First: establish that we have at least one item to read
29 | ! Second: do the binary search
30 | !
31 | ! read( buffer, *, iostat = ierr ) work(1)
32 | ! if ( ierr /= 0 ) then
33 | ! n = 0
34 | ! else
35 | n = 1
36 | do while ( n <= size(work) )
37 | n = 2 * n
38 | enddo
39 | n = n / 2
40 | step = n / 2
41 | ! step = n / 2
42 |
43 | do while ( step > 0 )
44 | read( buffer, *, iostat = ierr ) (work(i), i = 1,n)
45 | if ( ierr /= 0 ) then
46 | ierror = ierr ! Store the error code for later use
47 | n = n - step
48 | else
49 | n = n + step
50 | endif
51 | step = step / 2
52 | enddo
53 | ! endif
54 |
55 | !
56 | ! Then allocate an array of the actual size needed
57 | ! and copy the data
58 | !
59 | !
60 | if ( associated( var ) ) then
61 | deallocate( var )
62 | endif
63 | !
64 | ! One complication: we may have one too many
65 | ! (consequence of the binary search)
66 | !
67 | read( buffer, *, iostat = ierr ) (work(i), i = 1,n)
68 | if ( ierr < 0 ) then
69 | n = n - 1
70 | endif
71 |
72 | allocate( var(n) )
73 | var(1:n) = work(1:n)
74 | deallocate( work )
75 |
76 | if ( ierror .lt. 0 ) then
77 | ierror = 0
78 | endif
79 |
80 |
--------------------------------------------------------------------------------
/src/read_xml_array.inc:
--------------------------------------------------------------------------------
1 | ! Part of XML-Fortran library:
2 | !
3 | ! $Id: read_xml_array.inc,v 1.3 2007/02/26 20:33:38 arjenmarkus Exp $
4 | !
5 | type(XML_PARSE), intent(inout) :: info
6 | character(len=*), intent(in) :: tag
7 | logical, intent(inout) :: endtag
8 | character(len=*), dimension(:,:), intent(in) :: attribs
9 | integer, intent(in) :: noattribs
10 | character(len=*), dimension(:), intent(in) :: data
11 | integer, intent(in) :: nodata
12 | logical, intent(inout) :: has_var
13 |
14 | character(len=len(attribs(1,1))) :: buffer
15 | integer :: idx
16 | integer :: ierr
17 |
18 | !
19 | ! The big trick:
20 | ! A string long enough to hold all data strings
21 | !
22 | character(len=nodata*(len(data(1))+1)) :: bufferd
23 | integer :: start
24 |
25 | !
26 | ! The value can be stored in an attribute values="..." or in
27 | ! the data
28 | !
29 | has_var = .false.
30 | idx = xml_find_attrib( attribs, noattribs, 'values', buffer )
31 | if ( idx .gt. 0 ) then
32 | call read_from_buffer( buffer, var, ierr )
33 | if ( buffer .ne. ' ' ) then
34 | has_var = .true.
35 | endif
36 | else
37 | bufferd = ' '
38 | start = 1
39 | do idx = 1,nodata
40 | if ( data(idx) .ne. ' ' ) then
41 | bufferd(start:) = data(idx)
42 | start = start + len(data(idx)) + 1
43 | endif
44 | enddo
45 | call read_from_buffer( bufferd, var, ierr )
46 | if ( bufferd .ne. ' ' ) then
47 | has_var = .true.
48 | endif
49 | endif
50 |
51 | if ( ierr .ne. 0 ) then
52 | write(*,*) 'Error reading variable - tag = ', trim(tag)
53 | has_var = .false.
54 | endif
55 |
--------------------------------------------------------------------------------
/src/read_xml_primitives.f90:
--------------------------------------------------------------------------------
1 | ! read_xml_prims.f90 - Read routines for primitive data
2 | !
3 | ! $Id: read_xml_prims.f90,v 1.7 2007/12/07 10:38:41 arjenmarkus Exp $
4 | !
5 | ! Arjen Markus
6 | !
7 | ! General information:
8 | ! This module is part of the XML-Fortran library. Its
9 | ! purpose is to help read individual items from an XML
10 | ! file into the variables that have been connected to
11 | ! the various tags. It is used by the code generated
12 | ! by the make_xml_reader program.
13 | !
14 | ! Because the routines differ mostly by the type of the
15 | ! output variable, the body is included, to prevent
16 | ! too much repeated blocks of code with all the maintenance
17 | ! issues that causes.
18 | !
19 | module read_xml_primitives
20 | use xmlparse
21 | implicit none
22 |
23 | private :: read_from_buffer
24 | private :: read_from_buffer_integers
25 | private :: read_from_buffer_reals
26 | private :: read_from_buffer_doubles
27 | private :: read_from_buffer_logicals
28 | private :: read_from_buffer_words
29 |
30 | interface read_from_buffer
31 | module procedure read_from_buffer_integers
32 | module procedure read_from_buffer_reals
33 | module procedure read_from_buffer_doubles
34 | module procedure read_from_buffer_logicals
35 | module procedure read_from_buffer_words
36 | end interface
37 |
38 | contains
39 |
40 | ! skip_until_endtag --
41 | ! Routine to read the XML file until the end tag is encountered
42 | !
43 | ! Arguments:
44 | ! info The XML file data structure
45 | ! tag The tag in question
46 | ! attribs Array of attributes and their values
47 | ! data Array of strings, representing the data
48 | ! error Has an error occurred?
49 | !
50 | subroutine skip_until_endtag( info, tag, attribs, data, error )
51 | type(XML_PARSE), intent(inout) :: info
52 | character(len=*), intent(in) :: tag
53 | character(len=*), dimension(:,:), intent(inout) :: attribs
54 | character(len=*), dimension(:), intent(inout) :: data
55 | logical, intent(out) :: error
56 |
57 | integer :: noattribs
58 | integer :: nodata
59 | integer :: ierr
60 | logical :: endtag
61 | character(len=len(tag)) :: newtag
62 |
63 | error = .true.
64 | do
65 | call xml_get( info, newtag, endtag, attribs, noattribs, &
66 | data, nodata )
67 | if ( xml_error(info) ) then
68 | error = .true.
69 | exit
70 | endif
71 | if ( endtag .and. newtag == tag ) then
72 | exit
73 | endif
74 | enddo
75 | end subroutine skip_until_endtag
76 |
77 | ! read_xml_integer --
78 | ! Routine to read a single integer from the parsed data
79 | !
80 | ! Arguments:
81 | ! info XML parser structure
82 | ! tag The tag in question (error message only)
83 | ! endtag End tag found? (Dummy argument, actually)
84 | ! attribs Array of attributes and their values
85 | ! noattribs Number of attributes found
86 | ! data Array of strings, representing the data
87 | ! nodata Number of data strings
88 | ! var Variable to be filled
89 | ! has_var Has the variable been set?
90 | !
91 | subroutine read_xml_integer( info, tag, endtag, attribs, noattribs, data, nodata, &
92 | var, has_var )
93 | integer, intent(inout) :: var
94 |
95 | include 'read_xml_scalar.inc'
96 |
97 | end subroutine read_xml_integer
98 |
99 | ! read_xml_line --
100 | ! Routine to read a single line of text from the parsed data
101 | !
102 | ! Arguments:
103 | ! info XML parser structure
104 | ! tag The tag in question (error message only)
105 | ! endtag End tag found? (Dummy argument, actually)
106 | ! attribs Array of attributes and their values
107 | ! noattribs Number of attributes found
108 | ! data Array of strings, representing the data
109 | ! nodata Number of data strings
110 | ! var Variable to be filled
111 | ! has_var Has the variable been set?
112 | !
113 | subroutine read_xml_line( info, tag, endtag, attribs, noattribs, data, nodata, &
114 | var, has_var )
115 | type(XML_PARSE), intent(inout) :: info
116 | character(len=*), intent(in) :: tag
117 | logical, intent(inout) :: endtag
118 | character(len=*), dimension(:,:), intent(in) :: attribs
119 | integer, intent(in) :: noattribs
120 | character(len=*), dimension(:), intent(in) :: data
121 | integer, intent(in) :: nodata
122 | character(len=*), intent(inout) :: var
123 | logical, intent(inout) :: has_var
124 |
125 | character(len=len(attribs(1,1))) :: buffer
126 | integer :: idx
127 | integer :: ierr
128 |
129 | !
130 | ! The value can be stored in an attribute value="..." or in
131 | ! the data
132 | !
133 | has_var = .false.
134 | idx = xml_find_attrib( attribs, noattribs, 'value', buffer )
135 | if ( idx > 0 ) then
136 | var = buffer
137 | has_var = .true.
138 | else
139 | do idx = 1,nodata
140 | if ( data(idx) /= ' ' ) then
141 | var = data(idx)
142 | has_var = .true.
143 | exit
144 | endif
145 | enddo
146 | endif
147 | end subroutine read_xml_line
148 |
149 | ! read_xml_real, ... --
150 | ! See read_xml_integer for an explanation
151 | !
152 | subroutine read_xml_real( info, tag, endtag, attribs, noattribs, data, nodata, &
153 | var, has_var )
154 | real, intent(inout) :: var
155 |
156 | include 'read_xml_scalar.inc'
157 |
158 | end subroutine read_xml_real
159 |
160 | subroutine read_xml_double( info, tag, endtag, attribs, noattribs, data, nodata, &
161 | var, has_var )
162 | real(kind=kind(1.0d00)), intent(inout) :: var
163 |
164 | include 'read_xml_scalar.inc'
165 |
166 | end subroutine read_xml_double
167 |
168 | subroutine read_xml_logical( info, tag, endtag, attribs, noattribs, data, nodata, &
169 | var, has_var )
170 | logical, intent(inout) :: var
171 |
172 | include 'read_xml_scalar.inc'
173 |
174 | end subroutine read_xml_logical
175 |
176 | subroutine read_xml_word( info, tag, endtag, attribs, noattribs, data, nodata, &
177 | var, has_var )
178 | character(len=*), intent(inout) :: var
179 |
180 | include 'read_xml_word.inc'
181 |
182 | end subroutine read_xml_word
183 |
184 | ! read_xml_integer_array --
185 | ! Routine to read a one-dimensional integer array from the parsed
186 | ! ata
187 | !
188 | ! Arguments:
189 | ! info XML parser structure
190 | ! tag The tag in question (error message only)
191 | ! endtag End tag found? (Dummy argument, actually)
192 | ! attribs Array of attributes and their values
193 | ! noattribs Number of attributes found
194 | ! data Array of strings, representing the data
195 | ! nodata Number of data strings
196 | ! var Variable to be filled
197 | ! has_var Has the variable been set?
198 | !
199 | subroutine read_xml_integer_array( info, tag, endtag, attribs, noattribs, data, &
200 | nodata, var, has_var )
201 | integer, dimension(:), pointer :: var
202 |
203 | include 'read_xml_array.inc'
204 |
205 | end subroutine read_xml_integer_array
206 |
207 | ! read_xml_line_array --
208 | ! Routine to read an array of lines of text from the parsed data
209 | !
210 | ! Arguments:
211 | ! info XML parser structure
212 | ! tag The tag in question (error message only)
213 | ! attribs Array of attributes and their values
214 | ! noattribs Number of attributes found
215 | ! data Array of strings, representing the data
216 | ! nodata Number of data strings
217 | ! var Variable to be filled
218 | ! has_var Has the variable been set?
219 | !
220 | subroutine read_xml_line_array( info, tag, endtag, attribs, noattribs, data, &
221 | nodata, var, has_var )
222 | type(XML_PARSE), intent(inout) :: info
223 | character(len=*), intent(in) :: tag
224 | logical, intent(inout) :: endtag
225 | character(len=*), dimension(:,:), intent(in) :: attribs
226 | integer, intent(in) :: noattribs
227 | character(len=*), dimension(:), intent(in) :: data
228 | integer, intent(in) :: nodata
229 | character(len=*), dimension(:), pointer :: var
230 | logical, intent(inout) :: has_var
231 |
232 | character(len=len(attribs(1,1))) :: buffer
233 | integer :: idx
234 | integer :: idxv
235 | integer :: ierr
236 | logical :: started
237 |
238 | !
239 | ! The value can be stored in an attribute values="..." or in
240 | ! the data
241 | !
242 | has_var = .false.
243 | idx = xml_find_attrib( attribs, noattribs, 'values', buffer )
244 | if ( idx > 0 ) then
245 | allocate( var(1:1) )
246 | var(1) = buffer
247 | if ( buffer /= ' ' ) then
248 | has_var = .true.
249 | endif
250 | else
251 | idxv = 0
252 | started = .false.
253 | do idx = 1,nodata
254 | if ( data(idx) /= ' ' .or. started ) then
255 | if ( .not. started ) then
256 | allocate( var(1:nodata-idx+1) )
257 | started = .true.
258 | endif
259 | idxv = idxv + 1
260 | var(idxv) = data(idx)
261 | endif
262 | enddo
263 | if ( started ) then
264 | has_var = .true.
265 | endif
266 | endif
267 | end subroutine read_xml_line_array
268 |
269 | ! read_xml_real_array, ... --
270 | ! See read_xml_integer_array for an explanation
271 | !
272 | subroutine read_xml_real_array( info, tag, endtag, attribs, noattribs, data, &
273 | nodata, var, has_var )
274 | real, dimension(:), pointer :: var
275 |
276 | include 'read_xml_array.inc'
277 |
278 | end subroutine read_xml_real_array
279 |
280 | subroutine read_xml_double_array( info, tag, endtag, attribs, noattribs, data, &
281 | nodata, var, has_var )
282 | real(kind=kind(1.0d00)), dimension(:), pointer :: var
283 |
284 | include 'read_xml_array.inc'
285 |
286 | end subroutine read_xml_double_array
287 |
288 | subroutine read_xml_logical_array( info, tag, endtag, attribs, noattribs, data, &
289 | nodata, var, has_var )
290 | logical, dimension(:), pointer :: var
291 |
292 | include 'read_xml_array.inc'
293 |
294 | end subroutine read_xml_logical_array
295 |
296 | subroutine read_xml_word_array( info, tag, endtag, attribs, noattribs, data, &
297 | nodata, var, has_var )
298 | character(len=*), dimension(:), pointer :: var
299 |
300 | include 'read_xml_array.inc'
301 |
302 | end subroutine read_xml_word_array
303 |
304 | ! read_from_buffer_integers --
305 | ! Routine to read all integers from a long string
306 | !
307 | ! Arguments:
308 | ! buffer String containing the data
309 | ! var Variable to be filled
310 | ! ierror Error flag
311 | !
312 | subroutine read_from_buffer_integers( buffer, var, ierror )
313 | integer, dimension(:), pointer :: var
314 | integer, dimension(:), pointer :: work
315 |
316 | include 'read_from_buffer.inc'
317 |
318 | end subroutine read_from_buffer_integers
319 |
320 | ! read_xml_from_buffer_reals, ... -
321 | ! See read_xml_from_buffer_integers for an explanation
322 | !
323 | subroutine read_from_buffer_reals( buffer, var, ierror )
324 | real, dimension(:), pointer :: var
325 | real, dimension(:), pointer :: work
326 |
327 | include 'read_from_buffer.inc'
328 |
329 | end subroutine read_from_buffer_reals
330 |
331 | subroutine read_from_buffer_doubles( buffer, var, ierror )
332 | real(kind=kind(1.0d00)), dimension(:), pointer :: var
333 | real(kind=kind(1.0d00)), dimension(:), pointer :: work
334 |
335 | include 'read_from_buffer.inc'
336 |
337 | end subroutine read_from_buffer_doubles
338 |
339 | subroutine read_from_buffer_logicals( buffer, var, ierror )
340 | logical, dimension(:), pointer :: var
341 | logical, dimension(:), pointer :: work
342 |
343 | include 'read_from_buffer.inc'
344 |
345 | end subroutine read_from_buffer_logicals
346 |
347 | subroutine read_from_buffer_words( buffer, var, ierror )
348 | character(len=*), dimension(:), pointer :: var
349 | character(len=len(var)), dimension(:), pointer :: work
350 |
351 | include 'read_from_buffer.inc'
352 |
353 | end subroutine read_from_buffer_words
354 |
355 | ! read_xml_word_1dim, ... -
356 | ! Read an array of "words" (or ...) but from different elements
357 | !
358 | subroutine read_xml_integer_1dim( info, tag, endtag, attribs, noattribs, data, nodata, &
359 | var, has_var )
360 | type(XML_PARSE), intent(inout) :: info
361 | character(len=*), intent(in) :: tag
362 | logical, intent(inout) :: endtag
363 | character(len=*), dimension(:,:), intent(in) :: attribs
364 | integer, intent(in) :: noattribs
365 | character(len=*), dimension(:), intent(in) :: data
366 | integer, intent(in) :: nodata
367 | integer, dimension(:), pointer :: var
368 | logical, intent(inout) :: has_var
369 |
370 | integer,dimension(:), pointer :: newvar
371 | character(len=len(attribs(1,1))) :: buffer
372 | integer :: newsize
373 | integer :: ierr
374 |
375 | newsize = size(var) + 1
376 | allocate( newvar(1:newsize) )
377 | newvar(1:newsize-1) = var
378 | deallocate( var )
379 | var => newvar
380 |
381 | call read_xml_integer( info, tag, endtag, attribs, noattribs, data, nodata, &
382 | var(newsize), has_var )
383 |
384 | end subroutine read_xml_integer_1dim
385 |
386 | subroutine read_xml_real_1dim( info, tag, endtag, attribs, noattribs, data, nodata, &
387 | var, has_var )
388 | type(XML_PARSE), intent(inout) :: info
389 | character(len=*), intent(in) :: tag
390 | logical, intent(inout) :: endtag
391 | character(len=*), dimension(:,:), intent(in) :: attribs
392 | integer, intent(in) :: noattribs
393 | character(len=*), dimension(:), intent(in) :: data
394 | integer, intent(in) :: nodata
395 | real, dimension(:), pointer :: var
396 | logical, intent(inout) :: has_var
397 |
398 | real, dimension(:), pointer :: newvar
399 | character(len=len(attribs(1,1))) :: buffer
400 | integer :: newsize
401 | integer :: ierr
402 |
403 | newsize = size(var) + 1
404 | allocate( newvar(1:newsize) )
405 | newvar(1:newsize-1) = var
406 | deallocate( var )
407 | var => newvar
408 |
409 | call read_xml_real( info, tag, endtag, attribs, noattribs, data, nodata, &
410 | var(newsize), has_var )
411 |
412 | end subroutine read_xml_real_1dim
413 |
414 | subroutine read_xml_double_1dim( info, tag, endtag, attribs, noattribs, data, nodata, &
415 | var, has_var )
416 | type(XML_PARSE), intent(inout) :: info
417 | character(len=*), intent(in) :: tag
418 | logical, intent(inout) :: endtag
419 | character(len=*), dimension(:,:), intent(in) :: attribs
420 | integer, intent(in) :: noattribs
421 | character(len=*), dimension(:), intent(in) :: data
422 | integer, intent(in) :: nodata
423 | real(kind=kind(1.0d00)), dimension(:), pointer:: var
424 | logical, intent(inout) :: has_var
425 |
426 | real(kind=kind(1.0d00)), dimension(:), pointer:: newvar
427 | character(len=len(attribs(1,1))) :: buffer
428 | integer :: newsize
429 | integer :: ierr
430 |
431 | newsize = size(var) + 1
432 | allocate( newvar(1:newsize) )
433 | newvar(1:newsize-1) = var
434 | deallocate( var )
435 | var => newvar
436 |
437 | call read_xml_double( info, tag, endtag, attribs, noattribs, data, nodata, &
438 | var(newsize), has_var )
439 |
440 | end subroutine read_xml_double_1dim
441 |
442 | subroutine read_xml_logical_1dim( info, tag, endtag, attribs, noattribs, data, nodata, &
443 | var, has_var )
444 | type(XML_PARSE), intent(inout) :: info
445 | character(len=*), intent(in) :: tag
446 | logical, intent(inout) :: endtag
447 | character(len=*), dimension(:,:), intent(in) :: attribs
448 | integer, intent(in) :: noattribs
449 | character(len=*), dimension(:), intent(in) :: data
450 | integer, intent(in) :: nodata
451 | logical, dimension(:), pointer :: var
452 | logical, intent(inout) :: has_var
453 |
454 | logical, dimension(:), pointer :: newvar
455 | character(len=len(attribs(1,1))) :: buffer
456 | integer :: newsize
457 | integer :: ierr
458 |
459 | newsize = size(var) + 1
460 | allocate( newvar(1:newsize) )
461 | newvar(1:newsize-1) = var
462 | deallocate( var )
463 | var => newvar
464 |
465 | call read_xml_logical( info, tag, endtag, attribs, noattribs, data, nodata, &
466 | var(newsize), has_var )
467 |
468 | end subroutine read_xml_logical_1dim
469 |
470 | subroutine read_xml_word_1dim( info, tag, endtag, attribs, noattribs, data, nodata, &
471 | var, has_var )
472 | type(XML_PARSE), intent(inout) :: info
473 | character(len=*), intent(in) :: tag
474 | logical, intent(inout) :: endtag
475 | character(len=*), dimension(:,:), intent(in) :: attribs
476 | integer, intent(in) :: noattribs
477 | character(len=*), dimension(:), intent(in) :: data
478 | integer, intent(in) :: nodata
479 | character(len=*), dimension(:), pointer :: var
480 | logical, intent(inout) :: has_var
481 |
482 | character(len=len(var)),dimension(:), pointer :: newvar
483 | character(len=len(attribs(1,1))) :: buffer
484 | integer :: newsize
485 | integer :: ierr
486 |
487 | newsize = size(var) + 1
488 | allocate( newvar(1:newsize) )
489 | newvar(1:newsize-1) = var
490 | deallocate( var )
491 | var => newvar
492 |
493 | call read_xml_word( info, tag, endtag, attribs, noattribs, data, nodata, &
494 | var(newsize), has_var )
495 |
496 | end subroutine read_xml_word_1dim
497 |
498 | subroutine read_xml_line_1dim( info, tag, endtag, attribs, noattribs, data, nodata, &
499 | var, has_var )
500 | type(XML_PARSE), intent(inout) :: info
501 | character(len=*), intent(in) :: tag
502 | logical, intent(inout) :: endtag
503 | character(len=*), dimension(:,:), intent(in) :: attribs
504 | integer, intent(in) :: noattribs
505 | character(len=*), dimension(:), intent(in) :: data
506 | integer, intent(in) :: nodata
507 | character(len=*), dimension(:), pointer :: var
508 | logical, intent(inout) :: has_var
509 |
510 | character(len=len(var)),dimension(:), pointer :: newvar
511 | character(len=len(attribs(1,1))) :: buffer
512 | integer :: newsize
513 | integer :: ierr
514 |
515 | newsize = size(var) + 1
516 | allocate( newvar(1:newsize) )
517 | newvar(1:newsize-1) = var
518 | deallocate( var )
519 | var => newvar
520 |
521 | call read_xml_line( info, tag, endtag, attribs, noattribs, data, nodata, &
522 | var(newsize), has_var )
523 |
524 | end subroutine read_xml_line_1dim
525 |
526 |
527 | end module read_xml_primitives
528 |
--------------------------------------------------------------------------------
/src/read_xml_scalar.inc:
--------------------------------------------------------------------------------
1 | ! Part of XML-Fortran library:
2 | !
3 | ! $Id: read_xml_scalar.inc,v 1.3 2007/02/26 20:33:38 arjenmarkus Exp $
4 | !
5 | type(XML_PARSE), intent(inout) :: info
6 | character(len=*), intent(in) :: tag
7 | logical, intent(inout) :: endtag
8 | character(len=*), dimension(:,:), intent(in) :: attribs
9 | integer, intent(in) :: noattribs
10 | character(len=*), dimension(:), intent(in) :: data
11 | integer, intent(in) :: nodata
12 | logical, intent(inout) :: has_var
13 |
14 | character(len=len(attribs(1,1))) :: buffer
15 | integer :: idx
16 | integer :: ierr
17 |
18 | !
19 | ! The value can be stored in an attribute value="..." or in
20 | ! the data
21 | !
22 | has_var = .false.
23 | idx = xml_find_attrib( attribs, noattribs, 'value', buffer )
24 | if ( idx .gt. 0 ) then
25 | read( buffer, *, iostat=ierr ) var
26 | has_var = .true.
27 | else
28 | do idx = 1,nodata
29 | if ( data(idx) .ne. ' ' ) then
30 | read( data(idx), *, iostat=ierr ) var
31 | has_var = .true.
32 | exit
33 | endif
34 | enddo
35 | endif
36 |
37 | if ( ierr .ne. 0 ) then
38 | write(*,*) 'Error reading variable - tag = ', trim(tag)
39 | has_var = .false.
40 | endif
41 |
--------------------------------------------------------------------------------
/src/read_xml_word.inc:
--------------------------------------------------------------------------------
1 | ! Part of XML-Fortran library:
2 | !
3 | type(XML_PARSE), intent(inout) :: info
4 | character(len=*), intent(in) :: tag
5 | logical, intent(inout) :: endtag
6 | character(len=*), dimension(:,:), intent(in) :: attribs
7 | integer, intent(in) :: noattribs
8 | character(len=*), dimension(:), intent(in) :: data
9 | integer, intent(in) :: nodata
10 | logical, intent(inout) :: has_var
11 |
12 | character(len=len(attribs(1,1))) :: buffer
13 | integer :: idx
14 | integer :: ierr
15 |
16 | !
17 | ! The value can be stored in an attribute value="..." or in
18 | ! the data
19 | !
20 | has_var = .false.
21 | idx = xml_find_attrib( attribs, noattribs, 'value', buffer )
22 | if ( idx .gt. 0 ) then
23 | read( buffer, *, iostat=ierr ) var
24 | has_var = .true.
25 | else
26 | do idx = 1,nodata
27 | if ( data(idx) .ne. ' ' ) then
28 | read( data(idx), '(A)', iostat=ierr ) var
29 | has_var = .true.
30 | exit
31 | endif
32 | enddo
33 | endif
34 |
35 | if ( ierr .ne. 0 ) then
36 | write(*,*) 'Error reading variable - tag = ', trim(tag)
37 | has_var = .false.
38 | endif
39 |
--------------------------------------------------------------------------------
/src/test_read_prims.f90:
--------------------------------------------------------------------------------
1 | ! test_read_prims.f90 - Test program for reading primitive data
2 | !
3 | ! $Id: test_read_prims.f90,v 1.3 2006/03/26 19:05:48 arjenmarkus Exp $
4 | !
5 | ! Arjen Markus
6 | !
7 | ! General information:
8 | ! This is a test program for the module read_xml_primitives,
9 | ! part of the XML-Fortran library.
10 | !
11 | program test_read_prims
12 | use read_xml_primitives
13 |
14 | implicit none
15 |
16 | type(XML_PARSE) :: info
17 | character(len=40) :: tag
18 | character(len=40), dimension(2,10) :: attribs
19 | integer :: noattribs
20 | character(len=80), dimension(100) :: data
21 | integer :: nodata
22 | integer :: var
23 | integer, dimension(:), pointer :: array
24 | logical :: has_var
25 |
26 | !
27 | ! Test cases regarding reading integers
28 | !
29 | attribs(1,1) = 'value'
30 | attribs(2,1) = ' 1'
31 | noattribs = 1
32 | data(1) = ' 2'
33 | nodata = 1
34 | var = -1
35 | has_var = .false.
36 |
37 | tag = 'integer-test'
38 |
39 | var = -1
40 | noattribs = 1
41 | nodata = 1
42 | call read_xml_integer( info, tag, attribs, noattribs, data, nodata, &
43 | var, has_var )
44 | if ( var .ne. 1 ) then
45 | write(*,*) 'Error 1: var should be 1, is', var
46 | endif
47 | if ( .not. has_var ) then
48 | write(*,*) 'Error 1a: has_var should be true'
49 | endif
50 |
51 | var = -1
52 | noattribs = 0
53 | nodata = 1
54 | call read_xml_integer( info, tag, attribs, noattribs, data, nodata, &
55 | var, has_var )
56 | if ( var .ne. 2 ) then
57 | write(*,*) 'Error 2: var should be 2, is', var
58 | endif
59 | if ( .not. has_var ) then
60 | write(*,*) 'Error 2a: has_var should be true'
61 | endif
62 |
63 | var = -1
64 | noattribs = 0
65 | nodata = 0
66 | call read_xml_integer( info, tag, attribs, noattribs, data, nodata, &
67 | var, has_var )
68 | if ( var .ne. -1 ) then
69 | write(*,*) 'Error 3: var should be -1, is', var
70 | endif
71 | if ( has_var ) then
72 | write(*,*) 'Error 3a: has_var should be FALSE'
73 | endif
74 | if ( has_var ) then
75 | write(*,*) 'Error 3a: has_var should be FALSE'
76 | endif
77 |
78 | var = -1
79 | noattribs = 0
80 | nodata = 3
81 | data(1) = ' '
82 | data(2) = ' '
83 | data(3) = ' 3'
84 | call read_xml_integer( info, tag, attribs, noattribs, data, nodata, &
85 | var, has_var )
86 | if ( var .ne. 3 ) then
87 | write(*,*) 'Error 4: var should be 3, is', var
88 | endif
89 | if ( .not. has_var ) then
90 | write(*,*) 'Error 4a: has_var should be true'
91 | endif
92 |
93 | var = -1
94 | noattribs = 0
95 | nodata = 3
96 | data(1) = ' '
97 | data(2) = ' '
98 | data(3) = 'xxx'
99 | call read_xml_integer( info, tag, attribs, noattribs, data, nodata, &
100 | var, has_var )
101 | if ( var .ne. -1 ) then
102 | write(*,*) 'Error 5: var should be -1, is', var
103 | endif
104 | if ( has_var ) then
105 | write(*,*) 'Error 5a: has_var should be FALSE'
106 | endif
107 |
108 | nullify( array )
109 | noattribs = 1
110 | attribs(1,1) = 'values'
111 | attribs(2,1) = '1 2 3'
112 | nodata = 3
113 | data(1) = ' '
114 | data(2) = ' '
115 | data(3) = 'xxx'
116 | call read_xml_integer_array( info, tag, attribs, noattribs, data, &
117 | nodata, array, has_var )
118 | if ( size(array) .ne. 3 ) then
119 | write(*,*) 'Error 6: array should be size 3, is', size(array)
120 | endif
121 | if ( any(array .ne. (/1,2,3/)) ) then
122 | write(*,*) 'Error 6: array should be 1,2,3, is', array
123 | endif
124 | if ( .not. has_var ) then
125 | write(*,*) 'Error 6a: has_var should be TRUE'
126 | endif
127 |
128 | noattribs = 1
129 | attribs(1,1) = 'xxxxxx'
130 | attribs(2,1) = '3 4 5'
131 | nodata = 3
132 | data(1) = '1 '
133 | data(2) = '2 '
134 | data(3) = '3 '
135 | call read_xml_integer_array( info, tag, attribs, noattribs, data, &
136 | nodata, array, has_var )
137 | if ( size(array) .ne. 3 ) then
138 | write(*,*) 'Error 7: array should be size 3, is', size(array)
139 | endif
140 | if ( any(array .ne. (/1,2,3/)) ) then
141 | write(*,*) 'Error 7: array should be 1,2,3, is', array
142 | endif
143 | if ( .not. has_var ) then
144 | write(*,*) 'Error 7a: has_var should be TRUE'
145 | endif
146 |
147 | noattribs = 0
148 | attribs(1,1) = 'values'
149 | attribs(2,1) = '1 2 3'
150 | nodata = 3
151 | data(1) = '1 2'
152 | data(2) = ' '
153 | data(3) = '3 '
154 | call read_xml_integer_array( info, tag, attribs, noattribs, data, &
155 | nodata, array, has_var )
156 | if ( size(array) .ne. 3 ) then
157 | write(*,*) 'Error 8: array should be size 3, is', size(array)
158 | endif
159 | if ( any(array .ne. (/1,2,3/)) ) then
160 | write(*,*) 'Error 8: array should be 1,2,3, is', array
161 | endif
162 | if ( .not. has_var ) then
163 | write(*,*) 'Error 8a: has_var should be TRUE'
164 | endif
165 |
166 | noattribs = 1
167 | attribs(1,1) = 'values'
168 | attribs(2,1) = 'xxx'
169 | nodata = 3
170 | data(1) = '1 2'
171 | data(2) = ' '
172 | data(3) = '3 '
173 | call read_xml_integer_array( info, tag, attribs, noattribs, data, &
174 | nodata, array, has_var )
175 | if ( has_var ) then
176 | write(*,*) 'Error 9a: has_var should be FALSE'
177 | endif
178 |
179 | end program
180 |
--------------------------------------------------------------------------------
/src/test_write_prims.f90:
--------------------------------------------------------------------------------
1 | ! test_write_prims.f90 - Test program for writing primitive data
2 | !
3 | ! $Id: test_write_prims.f90,v 1.1 2007/06/10 10:08:38 arjenmarkus Exp $
4 | !
5 | ! Arjen Markus
6 | !
7 | ! General information:
8 | ! This is a test program for the module write_xml_primitives,
9 | ! part of the XML-Fortran library.
10 | !
11 | program test_write_prims
12 | use write_xml_primitives
13 |
14 | implicit none
15 |
16 | type(XML_PARSE) :: info
17 | character(len=40) :: tag
18 | integer :: var
19 | integer, dimension(:), pointer :: array
20 | logical :: has_var
21 |
22 | info%lun = 10
23 | open( info%lun, file='test_write.xml' )
24 |
25 | call write_to_xml_integer( info, 'integer', 4, -123456789 )
26 | call write_to_xml_real( info, 'real', 3, -1.23456789 )
27 | call write_to_xml_double( info, 'double', 3, -1.23456789d0 )
28 | call write_to_xml_logical( info, 'logical', 3, .true. )
29 | call write_to_xml_logical( info, 'logical', 4, .false. )
30 | call write_to_xml_string( info, 'string', 4, 'Hm, some string' )
31 |
32 | call write_to_xml_integer_array( info, 'integers', 4, &
33 | (/ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 /) )
34 | call write_to_xml_real_array( info, 'reals', 4, &
35 | (/ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 /) )
36 | call write_to_xml_double_array( info, 'doubles', 4, &
37 | (/ 1.0d0, 2.0d0, 3.0d0, 4.0d0, 5.0d0, 6.0d0, &
38 | 7.0d0, 8.0d0, 9.0d0, 10.0d0, 11.0d0, 12.0d0 /) )
39 | call write_to_xml_logical_array( info, 'logicals', 4, &
40 | (/ .true., .true., .true., .false., .false., .false., &
41 | .true., .true., .true., .false., .false., .false. /) )
42 |
43 | close( info%lun )
44 |
45 | end program
46 |
--------------------------------------------------------------------------------
/src/tree_find.f90:
--------------------------------------------------------------------------------
1 | ! tree_find.f90 --
2 | ! Include file for tree_struct.f90: "advanced" search routines
3 | !
4 | ! $Id: tree_find.f90,v 1.2 2006/03/26 19:05:48 arjenmarkus Exp $
5 | !
6 | ! General information:
7 | ! This file contains the following routines:
8 | ! - tree_next_segment: strip a segment from the search path
9 | ! - tree_append_handles: append to an array of nodes
10 | ! - tree_find_descendants: find all nodes with a given name and type
11 | ! - tree_find: find all nodes given by a path
12 | !
13 |
14 | ! tree_next_segment --
15 | ! Strip a segment from the search path
16 | !
17 | ! Arguments:
18 | ! path Full path (or partially stripped)
19 | ! segment First segment in the path or nothing
20 | ! Result:
21 | ! The first segment (that is a non-empty string between /'s) is
22 | ! returned in the argument "segment", the path is updated
23 | ! For instance:
24 | ! //A/B/C ==> segment=A, new path=B/C
25 | !
26 | subroutine tree_next_segment( path, segment )
27 | character(len=*), intent(inout) :: path
28 | character(len=*), intent(out) :: segment
29 |
30 | integer :: idx
31 | !
32 | ! Strip off any leading slashes first
33 | !
34 | do while ( path(1:1) == '/' )
35 | path(1:) = path(2:)
36 | enddo
37 |
38 | !
39 | ! Find the next segment
40 | !
41 | idx = index( path, '/' )
42 | if ( idx > 0 ) then
43 | segment = path(1:idx-1)
44 | path(1:) = path(idx+1:)
45 | else
46 | if ( path == ' ' ) then
47 | segment = ' '
48 | else
49 | segment = path
50 | path = ' '
51 | endif
52 | endif
53 | end subroutine tree_next_segment
54 |
55 | ! tree_append_handles --
56 | ! Append to an array of handles
57 | !
58 | ! Arguments:
59 | ! all_handles Pointer to array holding all handles
60 | ! handles New array to be appended
61 | ! Result:
62 | ! A new array with all handles included
63 | !
64 | subroutine tree_append_handles( all_handles, handles )
65 | type(TREE_DATA_PTR), dimension(:), pointer :: all_handles
66 | type(TREE_DATA_PTR), dimension(:) :: handles
67 |
68 | type(TREE_DATA_PTR), dimension(:), pointer :: new_array
69 |
70 | integer :: total_size
71 |
72 | total_size = size(all_handles) + size(handles)
73 | allocate( new_array(1:total_size) )
74 |
75 | new_array(1:size(all_handles)) = all_handles
76 | new_array(size(all_handles)+1:total_size) = handles
77 |
78 | deallocate( all_handles )
79 | all_handles => new_array
80 |
81 | end subroutine tree_append_handles
82 |
83 | ! tree_find_descendants --
84 | ! Find all nodes with a given name and type
85 | !
86 | ! Arguments:
87 | ! tree The node in the tree to start the search
88 | ! name The name of the node (empty for all)
89 | ! type The type of the node (empty for all)
90 | ! all Descend the tree (.true.) or only look at children
91 | ! handles A returned array of handles to the nodes that
92 | ! were found
93 | ! Result:
94 | ! An array of node handles - each node is found on the specified
95 | ! path. The user should deallocate the array when done
96 | !
97 | recursive subroutine tree_find_descendants_priv( &
98 | tree, name, type, all, handles )
99 | type(TREE_DATA), pointer :: tree
100 | type(TREE_DATA_PTR), dimension(:), pointer :: handles
101 | character(len=*) :: name
102 | character(len=*) :: type
103 | logical :: all
104 |
105 | type(TREE_DATA_PTR), dimension(1:1) :: new_handle
106 | character(len=80) :: node_name
107 | character(len=80) :: node_type
108 | integer :: i
109 |
110 | if ( associated(tree%child_nodes) ) then
111 | do i = 1,size(tree%child_nodes)
112 | node_name = tree_get_name( tree%child_nodes(i)%node_ptr )
113 | node_type = tree_get_datatype( tree%child_nodes(i)%node_ptr )
114 |
115 | if ( (node_name .eq. name .or. name .eq. ' ') .and. &
116 | (node_type .eq. type .or. type .eq. ' ') ) then
117 | new_handle(1)%node_ptr => tree%child_nodes(i)%node_ptr
118 | call tree_append_handles( handles, new_handle )
119 | else
120 | if ( all ) then
121 | call tree_find_descendants_priv( &
122 | tree%child_nodes(i)%node_ptr, &
123 | name, type, all, handles )
124 | endif
125 | endif
126 | enddo
127 | endif
128 |
129 | end subroutine tree_find_descendants_priv
130 |
131 | ! tree_find_descendants --
132 | ! Find all nodes with a given name and type
133 | !
134 | ! Arguments:
135 | ! tree The node in the tree to start the search
136 | ! name The name of the node (empty for all)
137 | ! type The type of the node (empty for all)
138 | ! all Descend the tree (.true.) or only look at children
139 | ! handles A returned array of handles to the nodes that
140 | ! were found
141 | ! Result:
142 | ! An array of node handles - each node is found on the specified
143 | ! path. The user should deallocate the array when done
144 | !
145 | subroutine tree_find_descendants( tree, name, type, all, handles )
146 | type(TREE_DATA), pointer :: tree
147 | type(TREE_DATA_PTR), dimension(:), pointer :: handles
148 | character(len=*) :: name
149 | character(len=*) :: type
150 | logical :: all
151 |
152 | type(TREE_DATA_PTR), dimension(:), pointer :: new_handle
153 | character(len=80) :: node_name
154 | character(len=80) :: node_type
155 |
156 | allocate( handles(1:0) )
157 | call tree_find_descendants_priv( tree, name, type, all, handles )
158 |
159 | end subroutine tree_find_descendants
160 |
161 | ! tree_find --
162 | ! Find all nodes given by a path
163 | !
164 | ! Arguments:
165 | ! tree The node in the tree to start the search
166 | ! path The path (a string of names separated by /)
167 | ! handles A returned array of handles to the nodes that
168 | ! were found
169 | ! Result:
170 | ! An array of node handles - each node is found on the specified
171 | ! path. The user should deallocate the array when done
172 | !
173 | subroutine tree_find( tree, path, handles )
174 | type(TREE_DATA), pointer :: tree
175 | type(TREE_DATA_PTR), dimension(:), pointer :: handles
176 | character(len=*) :: path
177 |
178 | !
179 | ! Local variables: use the string length of path!
180 | !
181 | character(len=len(path)) :: path_stripped
182 | character(len=len(path)) :: segment
183 |
184 | type(TREE_DATA_PTR), dimension(:), pointer :: result_handles
185 | type(TREE_DATA_PTR), dimension(:), pointer :: new_handles
186 |
187 | logical :: all
188 | integer :: i
189 |
190 | !
191 | ! For the first segment we will search the whole tree,
192 | ! after that we are only interested in direct children
193 | !
194 | all = .true.
195 | path_stripped = path
196 |
197 | allocate( handles(1:1) )
198 | handles(1)%node_ptr => tree
199 |
200 | do
201 | call tree_next_segment( path_stripped, segment )
202 |
203 | if ( segment /= ' ' ) then
204 | allocate( result_handles(1:0) )
205 | do i = 1,size(handles)
206 | call tree_find_descendants( handles(i)%node_ptr, segment, &
207 | "", all, new_handles )
208 | call tree_append_handles( result_handles, new_handles )
209 | deallocate( new_handles )
210 | enddo
211 | all = .false.
212 | else
213 | exit
214 | endif
215 | deallocate( handles )
216 | handles => result_handles
217 | enddo
218 |
219 | end subroutine tree_find
220 |
--------------------------------------------------------------------------------
/src/tree_struct.f90:
--------------------------------------------------------------------------------
1 | ! tree_struct.f90 --
2 | ! Module that implements a general tree structure in Fortran 90
3 | !
4 | ! General information:
5 | ! The tree is stored via a derived type TREE_DATA. A tree can
6 | ! have an arbitrary number of nodes, each of which can again
7 | ! have an arbitrary number of subnodes and so on.
8 | ! The data type for a node is the same as for a tree - there
9 | ! is no difference, except that the routine tree_create()
10 | ! returns the initialised root of a new tree.
11 | ! A node (and the root as well) can have the following
12 | ! properties:
13 | ! - A name (at most 80 characters)
14 | ! - Arbitrary data
15 | ! - A string indicating the type of data
16 | ! - Zero, one or more subnodes
17 | ! The storage is fairly efficient (via the TRANSFER() function
18 | ! all data and strings are converted to arrays of default
19 | ! integers). A node does not "know" its parent though and
20 | ! there is no check on circularity.
21 | !
22 | ! $Id: tree_struct.f90,v 1.2 2006/03/26 19:05:48 arjenmarkus Exp $
23 | !
24 | module TREE_DATA_TYPES
25 | type TREE_DATA
26 | character(len=1), dimension(:), pointer :: node_name
27 | character(len=1), dimension(:), pointer :: node_data
28 | character(len=1), dimension(:), pointer :: node_data_type
29 | type(TREE_DATA_PTR), dimension(:), pointer :: child_nodes
30 | end type
31 |
32 | type TREE_DATA_PTR
33 | type(TREE_DATA), pointer :: node_ptr
34 | end type
35 | end module
36 |
37 | module TREE_STRUCTURES
38 | use TREE_DATA_TYPES
39 | implicit none
40 |
41 | private
42 | !
43 | ! A variable that indicates the type of all data
44 | !
45 | character(len=1), dimension(1:1), public :: node_value
46 |
47 | !
48 | ! Auxiliary variable
49 | !
50 | integer, private :: traverse_level = 0
51 |
52 | !
53 | ! Public routines, types and parameters
54 | !
55 | public :: TREE_DATA, TREE_DATA_PTR
56 | public :: tree_create, tree_create_node, tree_get_node_by_name, &
57 | tree_get_data_ptr, tree_put_data, tree_get_name, &
58 | tree_get_datatype, tree_get_data_string, &
59 | tree_find, tree_find_descendants, &
60 | tree_traverse, tree_traverse_level
61 |
62 | !
63 | ! For testing purposes - should actually be private
64 | !
65 | public :: tree_next_segment, tree_append_handles
66 |
67 | contains
68 |
69 | ! tree_create --
70 | ! Create a new tree
71 | !
72 | ! Arguments:
73 | ! name Name of the new tree
74 | ! tree Pointer to the new tree
75 | ! Result:
76 | ! The argument tree points to a new, empty tree structure or is
77 | ! not associated
78 | !
79 | subroutine tree_create( name, tree )
80 | character(len=*), intent(in) :: name
81 | type(TREE_DATA), pointer :: tree
82 |
83 | integer :: error
84 | integer :: newsize
85 |
86 | allocate( tree, stat = error )
87 |
88 | if ( error .ne. 0 ) then
89 | nullify( tree )
90 | else
91 | newsize = size( transfer( name, node_value ) )
92 | allocate( tree%node_name(1:newsize), stat = error )
93 | if ( error .ne. 0 ) then
94 | deallocate( tree )
95 | return
96 | else
97 | tree%node_name(1:newsize) = transfer( name, node_value )
98 | nullify( tree%node_data )
99 | nullify( tree%node_data_type )
100 | nullify( tree%child_nodes )
101 | endif
102 | endif
103 | end subroutine tree_create
104 |
105 | ! tree_create_node --
106 | ! Create a new node to the given tree or node
107 | !
108 | ! Arguments:
109 | ! tree The tree or node to which to append the new node
110 | ! name Name of the new node
111 | ! node Pointer to the new node
112 | ! Result:
113 | ! The argument node points to a new, empty node or is
114 | ! not associated
115 | !
116 | subroutine tree_create_node( tree, name, node )
117 | character(len=*), intent(in) :: name
118 | type(TREE_DATA), pointer :: tree
119 | type(TREE_DATA), pointer :: node
120 |
121 | type(TREE_DATA_PTR), dimension(:), pointer :: children
122 |
123 | integer :: error
124 | integer :: newsize
125 |
126 | !
127 | ! Check for uniqueness -- no: do not do that!
128 | !
129 | !call tree_get_node_by_name( tree, name, node )
130 | !if ( associated( node ) ) then
131 | ! return
132 | !endif
133 |
134 | !
135 | ! Create a new node, store it in the array of child nodes
136 | ! for this (sub)tree
137 | !
138 | call tree_create( name, node )
139 |
140 | if ( associated( node ) ) then
141 | newsize = 1
142 | if ( associated( tree%child_nodes ) ) then
143 | newsize = 1 + size( tree%child_nodes )
144 | endif
145 |
146 | allocate( children(1:newsize), stat = error )
147 | if ( error .ne. 0 ) then
148 | deallocate( node )
149 | return
150 | else
151 | if ( newsize .gt. 1 ) then
152 | children(1:newsize-1) = tree%child_nodes
153 | deallocate( tree%child_nodes )
154 | endif
155 |
156 | tree%child_nodes => children
157 | tree%child_nodes(newsize)%node_ptr => node
158 | endif
159 | endif
160 | end subroutine tree_create_node
161 |
162 | ! tree_get_name --
163 | ! Return the name of the tree or node
164 | !
165 | ! Arguments:
166 | ! tree The tree or node
167 | !
168 | function tree_get_name( tree ) result( node_name )
169 | type(TREE_DATA), pointer :: tree
170 | character(len=80) :: node_name
171 |
172 | integer :: length
173 |
174 | length = size( tree%node_name )
175 | node_name = ' '
176 | node_name(1:length) = transfer( tree%node_name, node_name )
177 | end function tree_get_name
178 |
179 | ! tree_get_datatype --
180 | ! Return the data type for the data stored in the tree or node
181 | !
182 | ! Arguments:
183 | ! tree The tree or node
184 | !
185 | function tree_get_datatype( tree ) result( data_type )
186 | type(TREE_DATA), pointer :: tree
187 | character(len=40) :: data_type
188 |
189 | integer :: length
190 |
191 | data_type = '?'
192 | if ( associated( tree%node_data_type ) ) then
193 | length = size( tree%node_data_type )
194 | data_type(1:length) = transfer( tree%node_data_type, data_type )
195 | endif
196 | end function tree_get_datatype
197 |
198 | ! tree_get_node_by_name --
199 | ! Return the child node by name
200 | !
201 | ! Arguments:
202 | ! tree The tree or node to which to append the new node
203 | ! name Name of the node to find
204 | ! node Pointer to the node or "null"
205 | ! Result:
206 | ! The argument node points to a new, empty node or is
207 | ! not associated
208 | !
209 | subroutine tree_get_node_by_name( tree, name, node )
210 | character(len=*), intent(in) :: name
211 | type(TREE_DATA), pointer :: tree
212 | type(TREE_DATA), pointer :: node
213 |
214 | character(len=80) :: node_name
215 |
216 | integer :: i
217 |
218 | nullify( node )
219 |
220 | if ( associated(tree%child_nodes) ) then
221 | do i = 1,size(tree%child_nodes)
222 | node_name = tree_get_name( tree%child_nodes(i)%node_ptr )
223 |
224 | if ( node_name .eq. name ) then
225 | node => tree%child_nodes(i)%node_ptr
226 | exit
227 | endif
228 | enddo
229 | endif
230 |
231 | end subroutine tree_get_node_by_name
232 |
233 | ! tree_get_data_ptr --
234 | ! Return a pointer to the tree/node's data
235 | !
236 | ! Arguments:
237 | ! tree The tree or node from which to get the data
238 | ! data_ptr Pointer to the node/tree data
239 | ! data_type String indicating the type
240 | ! Result:
241 | ! The argument data_ptr points to the stored data or is
242 | ! not associated
243 | !
244 | subroutine tree_get_data_ptr( tree, data_ptr, data_type )
245 | type(TREE_DATA), pointer :: tree
246 | character(len=1), dimension(:), pointer :: data_ptr
247 | character(len=*) :: data_type
248 |
249 | nullify( data_ptr )
250 |
251 | data_type = '?'
252 | if ( associated( tree%node_data) ) then
253 | data_ptr => tree%node_data
254 | data_type = tree_get_datatype( tree )
255 | endif
256 |
257 | end subroutine tree_get_data_ptr
258 |
259 | ! tree_put_data --
260 | ! Put (a copy of) the data in the tree/node
261 | !
262 | ! Arguments:
263 | ! tree The tree or node with which to attach the data
264 | ! data Array of integers
265 | ! data_type Optional string indicating the type
266 | ! success True if all went well, false otherwise
267 | ! Result:
268 | ! The tree structure points to a copy of the data
269 | ! Note:
270 | ! A direct call to this routine will look something like:
271 | !
272 | ! call tree_put_data( tree, transfer( some_data, node_value ) )
273 | !
274 | ! where node_value acts as the mold for transferring the data
275 | !
276 | subroutine tree_put_data( tree, data, data_type, success )
277 | type(TREE_DATA), pointer :: tree
278 | character(len=1), dimension(:) :: data
279 | character(len=*), optional :: data_type
280 | logical, intent(out), optional :: success
281 |
282 | integer :: error
283 |
284 | if ( associated(tree%node_data) ) then
285 | deallocate( tree%node_data )
286 | endif
287 |
288 | if ( associated(tree%node_data_type) ) then
289 | deallocate( tree%node_data_type )
290 | endif
291 |
292 | allocate( tree%node_data(1:size(data)), stat = error )
293 | if ( error .eq. 0 ) then
294 | tree%node_data = data
295 | allocate( tree%node_data_type(1:len_trim(data_type)), &
296 | stat = error )
297 | if ( error .eq. 0 ) then
298 | tree%node_data_type = transfer( data_type, tree%node_data_type )
299 | endif
300 | endif
301 |
302 | if ( present( success ) ) then
303 | success = error .eq. 0
304 | endif
305 |
306 | end subroutine tree_put_data
307 |
308 | ! tree_traverse_level --
309 | ! Convenience function: level of the node during traversal
310 | !
311 | ! Arguments:
312 | ! None
313 | !
314 | ! Note:
315 | ! Functions without arguments can be optimised away!
316 | ! I should turn this into a subroutine instead
317 | !
318 | integer function tree_traverse_level( )
319 | tree_traverse_level = traverse_level
320 | end function tree_traverse_level
321 |
322 | ! tree_traverse --
323 | ! Traverse a tree and handle the nodes by a depth-first method
324 | !
325 | ! Arguments:
326 | ! tree The tree or node to traverse
327 | ! handler Routine to handle each node
328 | ! data Arbitrary data to be passed to the handler
329 | ! stop Whether to continue or stop (if set true)
330 | ! Result:
331 | ! Each tree node is visited (unless the traversal is
332 | ! prematurely ended by setting "stop" to true)
333 | !
334 | recursive subroutine tree_traverse( tree, handler, data, stop )
335 | type(TREE_DATA), pointer :: tree
336 | character(len=1), dimension(:) :: data
337 | logical, intent(out) :: stop
338 |
339 | interface
340 | subroutine handler( node, data, stop )
341 | use TREE_DATA_TYPES
342 | type(TREE_DATA), pointer :: node
343 | character(len=1), dimension(:) :: data
344 | logical, intent(inout) :: stop
345 | end subroutine handler
346 | end interface
347 |
348 | integer :: i
349 |
350 | stop = .false.
351 | if ( .not. associated( tree ) ) then
352 | return
353 | endif
354 |
355 | !
356 | ! First call the handler for the current node/tree
357 | !
358 | call handler( tree, data, stop )
359 | if ( stop ) then
360 | return
361 | endif
362 |
363 | !
364 | ! Then recurse through the child nodes (if any)
365 | !
366 | if ( associated( tree%child_nodes) ) then
367 | do i = 1,size(tree%child_nodes)
368 | traverse_level = traverse_level + 1
369 | call tree_traverse( tree%child_nodes(i)%node_ptr, &
370 | handler, data, stop )
371 | traverse_level = traverse_level - 1
372 | if ( stop ) then
373 | exit
374 | endif
375 | enddo
376 | endif
377 |
378 | end subroutine tree_traverse
379 |
380 | ! tree_get_data_string --
381 | ! Return data as a simple string
382 | !
383 | ! Arguments:
384 | ! tree The tree or node from which to get the data
385 | ! string String to be filled
386 | ! success Whether successful or not
387 | ! Result:
388 | ! The string is filled with the data stored in the node
389 | ! not associated. The routine is successful if:
390 | ! - there is data associated with the node/tree
391 | ! - the data type is "STRING" or "ATTRIBUTE"
392 | ! If the routine is not successful, the string is
393 | ! not changed.
394 | !
395 | subroutine tree_get_data_string( tree, string, success )
396 | type(TREE_DATA), pointer :: tree
397 | character(len=*), intent(inout) :: string
398 | logical, intent(out) :: success
399 |
400 | character(len=1), dimension(:), pointer :: data_ptr
401 | character(len=40) :: data_type
402 | integer :: length
403 |
404 | success = .false.
405 | if ( associated(tree) ) then
406 | call tree_get_data_ptr( tree, data_ptr, data_type )
407 |
408 | if ( .not. associated(data_ptr) ) then
409 | return
410 | endif
411 | if ( data_type .ne. 'STRING' .and. data_type .ne. 'ATTRIBUTE' ) then
412 | return
413 | endif
414 |
415 | success = .true.
416 | length = size(data_ptr)
417 | string = ' '
418 | string(1:length) = transfer(data_ptr,string)
419 | endif
420 |
421 | end subroutine tree_get_data_string
422 |
423 | include 'tree_find.f90'
424 |
425 | end module TREE_STRUCTURES
426 |
427 |
428 | ! ===================================================================
429 | ! Put it to the test
430 | !
431 | program test_tree
432 | use TREE_STRUCTURES
433 |
434 | implicit none
435 | type(TREE_DATA), pointer :: tree
436 | type(TREE_DATA), pointer :: node1, node2, node3
437 |
438 | character(len=1), dimension(1) :: dummy
439 | character(len=1), dimension(:), pointer :: data_ptr
440 | character(len=40) :: node_name, node_type, stored_data, type
441 | character(len=40) :: string, name
442 |
443 | logical :: stop
444 | logical :: success
445 |
446 | character(len=80) :: path
447 | character(len=80) :: segment
448 | integer :: i, j
449 | type(TREE_DATA_PTR), dimension(:), pointer :: all_handles
450 | type(TREE_DATA_PTR), dimension(:), pointer :: handles
451 | integer :: status
452 |
453 | interface
454 | subroutine handler( tree, data, stop )
455 | use TREE_STRUCTURES
456 | type(TREE_DATA), pointer :: tree
457 | character(len=1), dimension(:) :: data
458 | logical, intent(inout) :: stop
459 | end subroutine handler
460 | end interface
461 |
462 | !
463 | ! TREE:
464 | ! NODE A (data: STORED A)
465 | ! NODE C (data: AC)
466 | ! NODE D (data: AD)
467 | ! NODE B (data: --)
468 | ! NODE C (data: BC1)
469 | ! NODE D (data: BCD1)
470 | ! NODE C (data: BC2)
471 | ! NODE C (data: BC3)
472 | ! NODE D (data: --)
473 | !
474 |
475 | call tree_create( "TREE", tree )
476 |
477 | call tree_create_node( tree, "NODE A", node1 )
478 | call tree_put_data( node1, transfer("STORED A",node_value), "STRING" )
479 |
480 | call tree_create_node( node1, "NODE C", node2 )
481 | call tree_put_data( node2, transfer("AC",node_value), "STRING" )
482 | call tree_create_node( node1, "NODE D", node2 )
483 | call tree_put_data( node2, transfer("AD",node_value), "STRING" )
484 |
485 | call tree_create_node( tree, "NODE B", node1 )
486 |
487 | call tree_create_node( node1, "NODE C", node3 )
488 | call tree_put_data( node3, transfer("BC1",node_value), "STRING" )
489 | call tree_create_node( node1, "NODE C", node2 )
490 | call tree_put_data( node2, transfer("BC2",node_value), "STRING" )
491 | call tree_create_node( node1, "NODE C", node2 )
492 | call tree_put_data( node2, transfer("BC3",node_value), "STRING" )
493 |
494 | call tree_create_node( node3, "NODE D", node2 )
495 | call tree_put_data( node2, transfer("BCD1",node_value), "STRING" )
496 |
497 | call tree_create_node( tree, "NODE D", node1 )
498 |
499 | call tree_get_node_by_name( tree, "NODE A", node1 )
500 | call tree_get_data_ptr( node1, data_ptr, type )
501 | stored_data = transfer( data_ptr, stored_data )
502 | write(*,*) 'NODE A:', stored_data
503 |
504 | call tree_get_node_by_name( tree, "NODE B", node1 )
505 | call tree_get_data_ptr( node1, data_ptr, type )
506 | if ( associated( data_ptr ) ) then
507 | stored_data = transfer( data_ptr, stored_data )
508 | write(*,*) 'NODE B:', stored_data
509 | else
510 | write(*,*) 'NODE B - no data'
511 | endif
512 |
513 | call tree_get_node_by_name( node1, "NODE C", node2 )
514 | call tree_get_data_ptr( node2, data_ptr, type )
515 | stored_data = transfer( data_ptr, stored_data )
516 | write(*,*) 'NODE C:', stored_data
517 |
518 | write(*,*) ' '
519 | write(*,*) 'Traverse the tree:'
520 |
521 | call tree_traverse( tree, handler, dummy, stop )
522 |
523 |
524 | path = '//A/B/C///D'
525 | do while ( path /= ' ' )
526 | call tree_next_segment( path, segment )
527 | write(*,*) 'path: >',trim(path),'<'
528 | write(*,*) 'segment: >',trim(segment),'<'
529 | enddo
530 |
531 | !
532 | ! allocate( all_handles(1:0), stat=status ) ! A zero-length array!
533 | !
534 | ! write(*,*) 'Status: ', status
535 | ! allocate( handles(1:10) )
536 | ! handles(1:10) = (/ (i, i=1,10) /)
537 | ! call tree_append_handles( all_handles, handles )
538 | ! write(*,*) 'First step: ', all_handles
539 | ! call tree_append_handles( all_handles, handles )
540 | ! write(*,*) 'Second step: ', all_handles
541 |
542 | !
543 | ! 1. Find all nodes "A" - just one
544 | ! 2. Find all nodes "C" - four
545 | ! 3. Find all nodes "A/C" - just one
546 | ! 4. Find all nodes "B/C" - three
547 | !
548 | do i = 1,4
549 | select case (i)
550 | case (1)
551 | path = "NODE A"
552 | case (2)
553 | path = "NODE C"
554 | case (3)
555 | path = "NODE A/NODE C"
556 | case (4)
557 | path = "NODE B/NODE C"
558 | end select
559 | call tree_find( tree, path, handles )
560 | write(*,*) 'Path: ',trim(path)
561 | do j = 1,size(handles)
562 | name = tree_get_name( handles(j)%node_ptr )
563 | call tree_get_data_string( handles(j)%node_ptr, string, success )
564 | write(*,*) 'Name: ', name, ' - ',trim(string)
565 | enddo
566 | enddo
567 |
568 | end program
569 |
570 | subroutine handler( tree, data, stop )
571 | use TREE_STRUCTURES
572 | type(TREE_DATA), pointer :: tree
573 | character(len=1), dimension(:) :: data
574 | logical, intent(inout) :: stop
575 |
576 | character(len=1), dimension(:),pointer :: data_ptr
577 | character(len=60) :: string
578 | character(len=20) :: type_string
579 |
580 | integer :: level
581 | integer :: i
582 | logical :: success
583 |
584 | level = tree_traverse_level()
585 |
586 | write(*,*) (' ', i=1,level), 'Node: ', trim(tree_get_name(tree))
587 | call tree_get_data_ptr( tree, data_ptr, type_string )
588 |
589 | string = '(no data)'
590 |
591 | call tree_get_data_string( tree, string, success )
592 | write(*,*) (' ', i=1,level+1), trim(string), ' -- ', &
593 | trim(type_string), ' -- ', success
594 |
595 | end subroutine handler
596 |
--------------------------------------------------------------------------------
/src/tst_prims.f90:
--------------------------------------------------------------------------------
1 | ! tst_prims.f90 - Test program for reading the primitive data
2 | !
3 | ! $Id: tst_prims.f90,v 1.2 2006/03/26 19:05:48 arjenmarkus Exp $
4 | !
5 | ! Arjen Markus
6 | !
7 | ! General information:
8 | ! This program tests the data reading routines in
9 | ! read_xml_prims.f90
10 | ! To avoid editing the private/public clauses in the
11 | ! module I have copied the code in here.
12 | !
13 | program tst_prims
14 |
15 | character(len=20) :: buffer
16 | integer, dimension(:), pointer :: var
17 | integer :: ierror
18 | integer :: nofail
19 |
20 | !
21 | ! Tests: correct numbers returned?
22 | ! 1. Empty string, size(var) = 0
23 | ! 2. One value, size(var) = 1
24 | ! 3. Four values, size(var) = 4
25 | ! 4. Five values, size(var) = 5
26 | ! 5. Invalid data, ierror /= 0
27 | !
28 | nofail = 0
29 | var => null()
30 |
31 | buffer = ' '
32 | call read_from_buffer_integers( buffer, var, ierror )
33 | if ( size(var) /= 0 .or. ierror /= 0 ) then
34 | write(*,*) 'Test 1 failed - expected:'
35 | write(*,*) 'Size: ', 0, ' - got:', size(var)
36 | write(*,*) 'Error:', 0, ' - got:', ierror
37 | nofail = nofail+1
38 | endif
39 |
40 | buffer = ' 1 '
41 | call read_from_buffer_integers( buffer, var, ierror )
42 | if ( size(var) /= 1 .or. ierror /= 0 ) then
43 | write(*,*) 'Test 2 failed - expected:'
44 | write(*,*) 'Size: ', 2, ' - got:', size(var)
45 | write(*,*) 'Error:', 0, ' - got:', ierror
46 | write(*,*) 'Values:', var
47 | nofail = nofail+1
48 | endif
49 |
50 | buffer = ' 1 2 3 4 '
51 | call read_from_buffer_integers( buffer, var, ierror )
52 | if ( size(var) /= 4 .or. ierror /= 0 ) then
53 | write(*,*) 'Test 3 failed - expected:'
54 | write(*,*) 'Size: ', 4, ' - got:', size(var)
55 | write(*,*) 'Error:', 0, ' - got:', ierror
56 | write(*,*) 'Values:', var
57 | nofail = nofail+1
58 | endif
59 |
60 | buffer = ' 1 2 3 4 5 '
61 | call read_from_buffer_integers( buffer, var, ierror )
62 | if ( size(var) /= 5 .or. ierror /= 0 ) then
63 | write(*,*) 'Test 4 failed - expected:'
64 | write(*,*) 'Size: ', 5, ' - got:', size(var)
65 | write(*,*) 'Error:', 0, ' - got:', ierror
66 | write(*,*) 'Values:', var
67 | nofail = nofail+1
68 | endif
69 |
70 | buffer = ' 1 A 3 4 5 '
71 | call read_from_buffer_integers( buffer, var, ierror )
72 | if ( size(var) /= 1 .or. ierror == 0 ) then
73 | write(*,*) 'Test 5 failed - expected:'
74 | write(*,*) 'Size: ', 1, ' - got:', size(var)
75 | write(*,*) 'Error:', '/= 0', ' - got:', ierror
76 | write(*,*) 'Values:', var
77 | nofail = nofail+1
78 | endif
79 |
80 | if ( nofail == 0 ) then
81 | write(*,*) 'All tests succeeded'
82 | endif
83 | contains
84 |
85 | ! read_from_buffer_integers --
86 | ! Routine to read all integers from a long string
87 | !
88 | ! Arguments:
89 | ! buffer String containing the data
90 | ! var Variable to be filled
91 | ! ierror Error flag
92 | !
93 | subroutine read_from_buffer_integers( buffer, var, ierror )
94 | character(len=*), intent(in) :: buffer
95 | integer, dimension(:), pointer :: var
96 | integer, intent(inout) :: ierror
97 |
98 | integer, dimension(:), pointer :: work
99 | integer :: n
100 | integer :: i
101 | integer :: step
102 | integer :: ierr
103 | !
104 | ! First allocate an array that is surely large enough
105 | ! Note:
106 | ! This is not completely failsafe: with list-directed
107 | ! input you can also use repeat counts (10000*1.0 for
108 | ! instance).
109 | !
110 | allocate( work(len(buffer)/2+1) )
111 |
112 | !
113 | ! NOTE:
114 | ! This is not portable!!
115 | !
116 | ! read( buffer, *, iostat = ierror ) (work(n), n=1,size(work))
117 | !
118 | ! So, use a different strategy: a binary search
119 | ! First: establish that we have at least one item to read
120 | ! Second: do the binary search
121 | !
122 | n = 1
123 | do while ( n <= size(work) )
124 | n = 2 * n
125 | enddo
126 | n = n / 2
127 | step = n / 2
128 |
129 | do while ( step > 0 )
130 | read( buffer, *, iostat = ierr ) (work(i), i = 1,n)
131 | if ( ierr /= 0 ) then
132 | ierror = ierr ! Store the error code for later use
133 | n = n - step
134 | else
135 | n = n + step
136 | endif
137 | step = step / 2
138 | enddo
139 |
140 | !
141 | ! Then allocate an array of the actual size needed
142 | ! and copy the data
143 | !
144 | !
145 | if ( associated( var ) ) then
146 | deallocate( var )
147 | endif
148 | !
149 | ! One complication: we may have one too many
150 | ! (consequence of the binary search)
151 | !
152 | read( buffer, *, iostat = ierr ) (work(i), i = 1,n)
153 | if ( ierr < 0 ) then
154 | n = n - 1
155 | endif
156 |
157 | allocate( var(n) )
158 | var(1:n) = work(1:n)
159 | deallocate( work )
160 |
161 | if ( ierror .lt. 0 ) then
162 | ierror = 0
163 | endif
164 | end subroutine read_from_buffer_integers
165 |
166 | end program
167 |
--------------------------------------------------------------------------------
/src/tstparse.f90:
--------------------------------------------------------------------------------
1 | ! Test program for the module "XMLPARSE"
2 | ! Read a set of sample XML-files and report the
3 | ! possible failure to the screen
4 | !
5 | ! $Id: tstparse.f90,v 1.3 2006/03/26 19:05:48 arjenmarkus Exp $
6 | !
7 | ! Per test case read:
8 | ! - The title
9 | ! - The XML file to be
10 | ! - The expected parse data:
11 | ! - which tags and the attributes that belong to them
12 | ! - which data lines
13 | !
14 | ! If the XML file was read but the actually reported data did
15 | ! not match the expected ones, then the title of the test
16 | ! case is printed on screen.
17 | !
18 | ! So, a successful test does not produce output.
19 | !
20 | ! The test cases are defined in a file "tstpars.inp"
21 | ! which has the following format:
22 | ! - Title line
23 | ! - A line which starts with "-----"
24 | ! - The contents of the XML file
25 | ! - Another line with "-----"
26 | ! - Lines with keywords (in order) that define what
27 | ! tags, attributes and data to expect:
28 | ! tag: string
29 | ! endtag: T/F
30 | ! attribute: A=B
31 | ! data: string
32 | ! - Another line with "-----" to separate it from the
33 | ! next case
34 | ! - Lines with "#" in the first column are considered
35 | ! comment and are therefore ignored
36 | !
37 | program tstparse
38 |
39 | use xmlparse
40 |
41 | implicit none
42 |
43 | character(len=60) :: title
44 | character(len=80) :: string
45 | logical :: has_title
46 | integer :: ierr
47 |
48 |
49 | !
50 | ! Start the loop
51 | !
52 | open( 10, file = 'tstparse.inp', status = 'old' )
53 | open( 20, file = 'tstparse.out' )
54 |
55 | has_title = .false.
56 |
57 | do while ( ierr .eq. 0 )
58 | read( 10, '(a)', iostat = ierr ) string
59 | !
60 | ! End of file or some error condition
61 | !
62 | if ( ierr .ne. 0 ) then
63 | exit
64 | endif
65 | !
66 | ! Skip comments
67 | !
68 | if ( string(1:1) .eq. '#' ) then
69 | cycle
70 | endif
71 | !
72 | ! Store the title and start reading the XML data
73 | !
74 | if ( .not. has_title ) then
75 | title = string
76 | has_title = .true.
77 | endif
78 | if ( has_title .and. string(1:5) .eq. '-----' ) then
79 | open( 30, file = 'tstparse.xml' )
80 | do
81 | read( 10, '(a)', iostat = ierr ) string
82 | if ( ierr .ne. 0 ) then
83 | exit
84 | endif
85 | if ( string(1:5) .eq. '-----' ) then
86 | exit
87 | endif
88 | write( 30, '(a)' ) string
89 | enddo
90 |
91 | !
92 | ! Now we are ready to read the XML file
93 | ! and analyse the report
94 | !
95 | close( 30 )
96 | call check_xml_file
97 | endif
98 | enddo
99 |
100 | contains
101 |
102 | subroutine check_xml_file
103 | logical :: mustread
104 | type(XML_PARSE) :: info
105 |
106 | character(len=80) :: tag
107 | logical :: endtag
108 | character(len=80), dimension(1:2,1:20) :: attribs
109 | integer :: no_attribs
110 | character(len=200), dimension(1:100) :: data
111 | integer :: no_data
112 | integer :: i
113 |
114 | call xml_open( info, 'tstparse.xml', .true. )
115 | call xml_options( info, report_lun = 20, report_details = .true. )
116 |
117 | do
118 | call xml_get( info, tag, endtag, attribs, no_attribs, data, no_data )
119 | if ( xml_error(info) ) then
120 | write(*,*) 'Error ', title
121 | write(20,*) 'Error ', title
122 | endif
123 |
124 | write( 20,* ) tag, endtag
125 | do i = 1,no_attribs
126 | write( 20,'(i3,1x,3a)') &
127 | i, trim(attribs(1,i)), '=', trim(attribs(2,i))
128 | enddo
129 | write( 20,'(i3,1x,3a)') (i, '>',trim(data(i)), '<', i=1,no_data)
130 | if ( .not. xml_ok(info) ) exit
131 | enddo
132 |
133 | do
134 | read(10,'(a)' ) string
135 | if ( string(1:5) .eq. '-----' ) then
136 | exit
137 | endif
138 | enddo
139 |
140 | call xml_close( info )
141 |
142 | end subroutine check_xml_file
143 | end program tstparse
144 |
--------------------------------------------------------------------------------
/src/tstparse.inp:
--------------------------------------------------------------------------------
1 | Check first tag: no attributes
2 | --------------------------------
3 |
4 | Some data
5 |
6 | --------------------------------
7 | tag: first-tag
8 | endtag: F
9 | data:
10 | data: Some data
11 | data:
12 | tag: first-tag
13 | endtag: T
14 | --------------------------------
15 | Check tag with embedded /
16 | --------------------------------
17 |
18 | Some data
19 |
20 | --------------------------------
21 | tag: first-tag
22 | endtag: F
23 | attribute: value=m/s
24 | data:
25 | data: Some data
26 | data:
27 | tag: first-tag
28 | endtag: T
29 | --------------------------------
30 | Multiple tags on one line
31 | --------------------------------
32 |
33 | --------------------------------
34 | tag: tag1
35 | endtag: T
36 | attribute: value=a
37 | data:
38 | tag: tag2
39 | endtag: T
40 | attribute: value=b
41 | --------------------------------
42 | Data with "entities"
43 | --------------------------------
44 | ampersand &
45 | greater than >lower than <
46 |
47 | --------------------------------
48 | tag: tag1
49 | endtag: F
50 | data: ampersand &
51 | data: greater than >lower than <
52 | tag: tag1
53 | endtag: T
54 | --------------------------------
55 | Data directly after tag - ERROR!
56 | --------------------------------
57 | ampersand
58 | --------------------------------
59 | tag: tag1
60 | endtag: F
61 | data: ampersand
62 | tag: tag1
63 | endtag: T
64 | --------------------------------
65 | Comments containing an equal-sign
66 | --------------------------------
67 | ampersand
68 |
70 | last tag
71 | --------------------------------
72 | tag: tag1
73 | endtag: F
74 | data: ampersand
75 | tag: tag1
76 | endtag: T
77 | tag: !--
78 | data: This = true
79 | endtag: F
80 | tag: --
81 | endtag: T
82 | tag: tag2
83 | data: last tag
84 | endtag: F
85 | tag: tag2
86 | endtag: T
87 | --------------------------------
88 |
--------------------------------------------------------------------------------
/src/write_xml_primitives.f90:
--------------------------------------------------------------------------------
1 | ! write_xml_prims.f90 - Write routines for primitive data
2 | !
3 | ! $Id: write_xml_prims.f90,v 1.2 2007/12/27 05:13:59 arjenmarkus Exp $
4 | !
5 | ! Arjen Markus
6 | !
7 | ! General information:
8 | ! This module is part of the XML-Fortran library. Its
9 | ! purpose is to write individual items to an XML
10 | ! file using the right tag. It is used by the code generated
11 | ! by the make_xml_reader program.
12 | !
13 | module write_xml_primitives
14 | use xmlparse
15 | implicit none
16 |
17 | ! interface write_to_xml
18 | ! module procedure write_to_xml_integers
19 | ! module procedure write_to_xml_reals
20 | ! module procedure write_to_xml_doubles
21 | ! module procedure write_to_xml_logicals
22 | ! module procedure write_to_xml_words
23 | ! end interface
24 | interface write_to_xml_word
25 | module procedure write_to_xml_string
26 | end interface
27 | interface write_to_xml_line
28 | module procedure write_to_xml_string
29 | end interface
30 |
31 | contains
32 |
33 | ! write_to_xml_integer --
34 | ! Routine to write a single integer to the XML file
35 | !
36 | ! Arguments:
37 | ! info XML parser structure
38 | ! tag The tag in question
39 | ! indent Number of spaces for indentation
40 | ! value Value to be written
41 | !
42 | subroutine write_to_xml_integer( info, tag, indent, value )
43 | type(XML_PARSE), intent(in) :: info
44 | character(len=*), intent(in) :: tag
45 | integer, intent(in) :: indent
46 | integer, intent(in) :: value
47 |
48 | character(len=100) :: indentation
49 |
50 | indentation = ' '
51 | write( info%lun, '(4a,i0,3a)' ) indentation(1:min(indent,100)), &
52 | '<', trim(tag), '>', value, '', trim(tag), '>'
53 |
54 | end subroutine write_to_xml_integer
55 |
56 | ! write_to_xml_integer_1dim --
57 | ! Routine to write an array of integers to the XML file
58 | !
59 | ! Arguments:
60 | ! info XML parser structure
61 | ! tag The tag in question
62 | ! indent Number of spaces for indentation
63 | ! values Values to be written
64 | !
65 | subroutine write_to_xml_integer_1dim( info, tag, indent, values )
66 | type(XML_PARSE), intent(in) :: info
67 | character(len=*), intent(in) :: tag
68 | integer, intent(in) :: indent
69 | integer, dimension(:), intent(in) :: values
70 |
71 | integer :: i
72 |
73 | do i = 1,size(values)
74 | call write_to_xml_integer( info, tag, indent, values(i) )
75 | enddo
76 |
77 | end subroutine write_to_xml_integer_1dim
78 |
79 | ! write_to_xml_real --
80 | ! Routine to write a single real value (single precision) to the XML file
81 | !
82 | ! Arguments:
83 | ! info XML parser structure
84 | ! tag The tag in question
85 | ! indent Number of spaces for indentation
86 | ! value Value to be written
87 | !
88 | subroutine write_to_xml_real( info, tag, indent, value )
89 | type(XML_PARSE), intent(in) :: info
90 | character(len=*), intent(in) :: tag
91 | integer, intent(in) :: indent
92 | real, intent(in) :: value
93 |
94 | character(len=100) :: indentation
95 | character(len=12) :: buffer
96 |
97 | indentation = ' '
98 | write( buffer, '(1pg12.4)' ) value
99 | write( info%lun, '(8a)' ) indentation(1:min(indent,100)), &
100 | '<', trim(tag), '>', trim(adjustl(buffer)), '', trim(tag), '>'
101 |
102 | end subroutine write_to_xml_real
103 |
104 | ! write_to_xml_real_1dim --
105 | ! Routine to write an array of reals to the XML file
106 | !
107 | ! Arguments:
108 | ! info XML parser structure
109 | ! tag The tag in question
110 | ! indent Number of spaces for indentation
111 | ! values Values to be written
112 | !
113 | subroutine write_to_xml_real_1dim( info, tag, indent, values )
114 | type(XML_PARSE), intent(in) :: info
115 | character(len=*), intent(in) :: tag
116 | integer, intent(in) :: indent
117 | real, dimension(:), intent(in) :: values
118 |
119 | integer :: i
120 |
121 | do i = 1,size(values)
122 | call write_to_xml_real( info, tag, indent, values(i) )
123 | enddo
124 |
125 | end subroutine write_to_xml_real_1dim
126 |
127 | ! write_to_xml_double --
128 | ! Routine to write one real value (double precision) to the XML file
129 | !
130 | ! Arguments:
131 | ! info XML parser structure
132 | ! tag The tag in question
133 | ! indent Number of spaces for indentation
134 | ! value Value to be written
135 | !
136 | subroutine write_to_xml_double( info, tag, indent, value )
137 | type(XML_PARSE), intent(in) :: info
138 | character(len=*), intent(in) :: tag
139 | integer, intent(in) :: indent
140 | real(kind=kind(1.0d0)), intent(in) :: value
141 |
142 | character(len=100) :: indentation
143 | character(len=16) :: buffer
144 |
145 | indentation = ' '
146 | write( buffer, '(1pg16.7)' ) value
147 | write( info%lun, '(8a)' ) indentation(1:min(indent,100)), &
148 | '<', trim(tag), '>', trim(adjustl(buffer)), '', trim(tag), '>'
149 |
150 | end subroutine write_to_xml_double
151 |
152 | ! write_to_xml_double_1dim --
153 | ! Routine to write an array of double precision reals to the XML file
154 | !
155 | ! Arguments:
156 | ! info XML parser structure
157 | ! tag The tag in question
158 | ! indent Number of spaces for indentation
159 | ! values Values to be written
160 | !
161 | subroutine write_to_xml_double_1dim( info, tag, indent, values )
162 | type(XML_PARSE), intent(in) :: info
163 | character(len=*), intent(in) :: tag
164 | integer, intent(in) :: indent
165 | real(kind=kind(1.0d00)), dimension(:), intent(in) :: values
166 |
167 | integer :: i
168 |
169 | do i = 1,size(values)
170 | call write_to_xml_double( info, tag, indent, values(i) )
171 | enddo
172 |
173 | end subroutine write_to_xml_double_1dim
174 |
175 | ! write_to_xml_string --
176 | ! Routine to write one string to the XML file
177 | !
178 | ! Arguments:
179 | ! info XML parser structure
180 | ! tag The tag in question
181 | ! indent Number of spaces for indentation
182 | ! value Value to be written
183 | !
184 | subroutine write_to_xml_string( info, tag, indent, value )
185 | type(XML_PARSE), intent(in) :: info
186 | character(len=*), intent(in) :: tag
187 | integer, intent(in) :: indent
188 | character(len=*), intent(in) :: value
189 |
190 | character(len=100) :: indentation
191 |
192 | !
193 | ! NOTE: No guards against <, >, & and " yet!
194 | ! NOTE: difference needed between words and lines?
195 | !
196 | indentation = ' '
197 | write( info%lun, '(8a)' ) indentation(1:min(indent,100)), &
198 | '<', trim(tag), '>', trim(value), '', trim(tag), '>'
199 |
200 | end subroutine write_to_xml_string
201 |
202 | ! write_to_xml_word_1dim --
203 | ! Routine to write an array of single words to the XML file
204 | !
205 | ! Arguments:
206 | ! info XML parser structure
207 | ! tag The tag in question
208 | ! indent Number of spaces for indentation
209 | ! value Value to be written
210 | !
211 | subroutine write_to_xml_word_1dim( info, tag, indent, values )
212 | type(XML_PARSE), intent(in) :: info
213 | character(len=*), intent(in) :: tag
214 | integer, intent(in) :: indent
215 | character(len=*), dimension(:), intent(in) :: values
216 |
217 | integer :: i
218 |
219 | do i = 1,size(values)
220 | call write_to_xml_string( info, tag, indent, values(i) )
221 | enddo
222 | end subroutine write_to_xml_word_1dim
223 |
224 | ! write_to_xml_string_1dim --
225 | ! Routine to write an array of strings to the XML file
226 | !
227 | ! Arguments:
228 | ! info XML parser structure
229 | ! tag The tag in question
230 | ! indent Number of spaces for indentation
231 | ! values Values to be written
232 | !
233 | subroutine write_to_xml_string_1dim( info, tag, indent, values )
234 | type(XML_PARSE), intent(in) :: info
235 | character(len=*), intent(in) :: tag
236 | integer, intent(in) :: indent
237 | character(len=*), dimension(:), intent(in) :: values
238 |
239 | integer :: i
240 |
241 | do i = 1,size(values)
242 | call write_to_xml_string( info, tag, indent, values(i) )
243 | enddo
244 |
245 | end subroutine write_to_xml_string_1dim
246 |
247 | ! write_to_xml_logical --
248 | ! Routine to write one logical to the XML file
249 | !
250 | ! Arguments:
251 | ! info XML parser structure
252 | ! tag The tag in question
253 | ! indent Number of spaces for indentation
254 | ! value Value to be written
255 | !
256 | subroutine write_to_xml_logical( info, tag, indent, value )
257 | type(XML_PARSE), intent(in) :: info
258 | character(len=*), intent(in) :: tag
259 | integer, intent(in) :: indent
260 | logical, intent(in) :: value
261 |
262 | character(len=100) :: indentation
263 |
264 | indentation = ' '
265 | if ( value ) then
266 | write( info%lun, '(8a)' ) indentation(1:min(indent,100)), &
267 | '<', trim(tag), '>true', trim(tag), '>'
268 | else
269 | write( info%lun, '(8a)' ) indentation(1:min(indent,100)), &
270 | '<', trim(tag), '>false', trim(tag), '>'
271 | endif
272 |
273 | end subroutine write_to_xml_logical
274 |
275 | ! write_to_xml_logical_1dim --
276 | ! Routine to write an array of logicals to the XML file
277 | !
278 | ! Arguments:
279 | ! info XML parser structure
280 | ! tag The tag in question
281 | ! indent Number of spaces for indentation
282 | ! values Values to be written
283 | !
284 | subroutine write_to_xml_logical_1dim( info, tag, indent, values )
285 | type(XML_PARSE), intent(in) :: info
286 | character(len=*), intent(in) :: tag
287 | integer, intent(in) :: indent
288 | logical, dimension(:), intent(in) :: values
289 |
290 | integer :: i
291 |
292 | do i = 1,size(values)
293 | call write_to_xml_logical( info, tag, indent, values(i) )
294 | enddo
295 |
296 | end subroutine write_to_xml_logical_1dim
297 |
298 | ! write_to_xml_integer_array --
299 | ! Routine to write an array of integers to the XML file
300 | !
301 | ! Arguments:
302 | ! info XML parser structure
303 | ! tag The tag in question
304 | ! indent Number of spaces for indentation
305 | ! array Values to be written
306 | !
307 | subroutine write_to_xml_integer_array( info, tag, indent, array )
308 | type(XML_PARSE), intent(in) :: info
309 | character(len=*), intent(in) :: tag
310 | integer, intent(in) :: indent
311 | integer, dimension(:), intent(in) :: array
312 |
313 | character(len=100) :: indentation
314 | integer :: i, i2, j
315 |
316 | indentation = ' '
317 |
318 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
319 | '<', trim(tag), '>'
320 | do i = 1,size(array),10
321 | i2 = min( i + 9, size(array) )
322 | write( info%lun, '(a,10i12)' ) indentation(1:min(indent+4,100)), &
323 | ( array(j) ,j = i,i2 )
324 | enddo
325 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
326 | '', trim(tag), '>'
327 |
328 | end subroutine write_to_xml_integer_array
329 |
330 | ! write_to_xml_real_array --
331 | ! Routine to write an array of single precision reals to the XML file
332 | !
333 | ! Arguments:
334 | ! info XML parser structure
335 | ! tag The tag in question
336 | ! indent Number of spaces for indentation
337 | ! array Values to be written
338 | !
339 | subroutine write_to_xml_real_array( info, tag, indent, array )
340 | type(XML_PARSE), intent(in) :: info
341 | character(len=*), intent(in) :: tag
342 | integer, intent(in) :: indent
343 | real, dimension(:), intent(in) :: array
344 |
345 | character(len=100) :: indentation
346 | integer :: i, i2, j
347 |
348 | indentation = ' '
349 |
350 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
351 | '<', trim(tag), '>'
352 | do i = 1,size(array),10
353 | i2 = min( i + 9, size(array) )
354 | write( info%lun, '(a,10g12.4)' ) indentation(1:min(indent+4,100)), &
355 | ( array(j) ,j = i,i2 )
356 | enddo
357 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
358 | '', trim(tag), '>'
359 |
360 | end subroutine write_to_xml_real_array
361 |
362 | ! write_to_xml_double_array --
363 | ! Routine to write an array of double precision reals to the XML file
364 | !
365 | ! Arguments:
366 | ! info XML parser structure
367 | ! tag The tag in question
368 | ! indent Number of spaces for indentation
369 | ! array Values to be written
370 | !
371 | subroutine write_to_xml_double_array( info, tag, indent, array )
372 | type(XML_PARSE), intent(in) :: info
373 | character(len=*), intent(in) :: tag
374 | integer, intent(in) :: indent
375 | real(kind=kind(1.0d0)), dimension(:), intent(in) :: array
376 |
377 | character(len=100) :: indentation
378 | integer :: i, i2, j
379 |
380 | indentation = ' '
381 |
382 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
383 | '<', trim(tag), '>'
384 | do i = 1,size(array),5
385 | i2 = min( i + 4, size(array) )
386 | write( info%lun, '(a,5g20.7)' ) indentation(1:min(indent+4,100)), &
387 | ( array(j) ,j = i,i2 )
388 | enddo
389 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
390 | '', trim(tag), '>'
391 |
392 | end subroutine write_to_xml_double_array
393 |
394 | ! write_to_xml_logical_array --
395 | ! Routine to write an array of logicals to the XML file
396 | !
397 | ! Arguments:
398 | ! info XML parser structure
399 | ! tag The tag in question
400 | ! indent Number of spaces for indentation
401 | ! array Values to be written
402 | !
403 | subroutine write_to_xml_logical_array( info, tag, indent, array )
404 | type(XML_PARSE), intent(in) :: info
405 | character(len=*), intent(in) :: tag
406 | integer, intent(in) :: indent
407 | logical, dimension(:), intent(in) :: array
408 |
409 | character(len=100) :: indentation
410 | integer :: i, i2, j
411 |
412 | indentation = ' '
413 |
414 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
415 | '<', trim(tag), '>'
416 | do i = 1,size(array),10
417 | i2 = min( i + 9, size(array) )
418 | write( info%lun, '(a,10a)' ) indentation(1:min(indent+4,100)), &
419 | ( merge('true ', 'false ', array(j)) ,j = i,i2 )
420 | enddo
421 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
422 | '', trim(tag), '>'
423 |
424 | end subroutine write_to_xml_logical_array
425 |
426 | ! write_to_xml_word_array --
427 | ! Routine to write an array of words to the XML file
428 | !
429 | ! Arguments:
430 | ! info XML parser structure
431 | ! tag The tag in question
432 | ! indent Number of spaces for indentation
433 | ! array Values to be written
434 | !
435 | subroutine write_to_xml_word_array( info, tag, indent, array )
436 | type(XML_PARSE), intent(in) :: info
437 | character(len=*), intent(in) :: tag
438 | integer, intent(in) :: indent
439 | character(len=*), dimension(:), intent(in) :: array
440 |
441 | character(len=100) :: indentation
442 | integer :: i, i2, j
443 |
444 | indentation = ' '
445 |
446 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
447 | '<', trim(tag), '>'
448 | do i = 1,size(array),10
449 | i2 = min( i + 9, size(array) )
450 | write( info%lun, '(a,20a)' ) indentation(1:min(indent+4,100)), &
451 | ( trim(array(j)) , ' ' ,j = i,i2 )
452 | enddo
453 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
454 | '', trim(tag), '>'
455 |
456 | end subroutine write_to_xml_word_array
457 |
458 | ! write_to_xml_line_array --
459 | ! Routine to write an array of lines to the XML file
460 | !
461 | ! Arguments:
462 | ! info XML parser structure
463 | ! tag The tag in question
464 | ! indent Number of spaces for indentation
465 | ! array Values to be written
466 | !
467 | subroutine write_to_xml_line_array( info, tag, indent, array )
468 | type(XML_PARSE), intent(in) :: info
469 | character(len=*), intent(in) :: tag
470 | integer, intent(in) :: indent
471 | logical, dimension(:), intent(in) :: array
472 |
473 | character(len=100) :: indentation
474 | integer :: i
475 |
476 | indentation = ' '
477 |
478 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
479 | '<', trim(tag), '>'
480 | do i = 1,size(array)
481 | write( info%lun, '(a)' ) indentation(1:min(indent+4,100)), &
482 | array(i)
483 | enddo
484 | write( info%lun, '(4a)' ) indentation(1:min(indent,100)), &
485 | '', trim(tag), '>'
486 |
487 | end subroutine write_to_xml_line_array
488 |
489 | end module write_xml_primitives
490 |
--------------------------------------------------------------------------------
/src/xmlparse.f90:
--------------------------------------------------------------------------------
1 | !===============================================================================
2 | ! XMLPARSE - Simple, limited XML parser in Fortran
3 | !
4 | ! General information:
5 | ! The module reads XML files by:
6 | ! - Identifying the tag and all attributes and data belonging
7 | ! to the tag.
8 | ! - Returning to the calling subprogram to let it take care of
9 | ! the tag, attributes and data.
10 | ! - If the tag is actually an ending tag, then this is flagged
11 | ! too.
12 | ! - Handling all the data is left to the calling subprogram,
13 | ! the module merely facilitates in the parsing.
14 | !
15 | ! Note:
16 | ! The module in its current version has a number of limitations:
17 | ! - It does not handle escape sequences (like >. to signify
18 | ! a ">" sign)
19 | ! - It does not handle tags with attributes that are spread
20 | ! over more than one line
21 | ! - The maximum length of a line is 1000 characters
22 | ! - It may report too many lines of data (empty lines)
23 | ! - No DOM support nor support for an object tree
24 | ! - It is probably not very robust in detecting malformed XML files
25 | !
26 | ! Some questions:
27 | ! - What to do with leading blanks?
28 | !
29 | ! Update - several ideas:
30 | ! - Introduce at least two options (via xml_options):
31 | ! - ignore_whitespace - remove leading blanks and leading and trailing
32 | ! empty lines from the PCDATA
33 | ! - no_data_truncation - consider truncation of data (more
34 | ! attributes or lines of character data than
35 | ! can be stored) a read error
36 | ! - Introduce convenience functions and subroutines:
37 | ! - xml_ok() - all is well, reading can continue
38 | ! - xml_data_trunc() - was there truncation of the data?
39 | ! - xml_find_attrib() - find an attribute by name
40 | !
41 | ! Further ideas:
42 | ! - simple checking via a table: parent, tag, id, min, max
43 | !===============================================================================
44 |
45 | module xmlparse
46 |
47 | implicit none
48 |
49 | integer, parameter :: XML_BUFFER_LENGTH = 10000
50 |
51 | !===============================================================================
52 | ! XML_PARSE defines the data type that holds the parser information
53 | !===============================================================================
54 |
55 | type XML_PARSE
56 | integer :: lun ! LU-number of the XML-file
57 | integer :: level ! Indentation level (output)
58 | integer :: lineno ! Line in file
59 | logical :: ignore_whitespace ! Ignore leading blanks etc.
60 | logical :: no_data_truncation ! Do not allow data truncation
61 | logical :: too_many_attribs ! More attributes than could be stored?
62 | logical :: too_many_data ! More lines of data than could be stored?
63 | logical :: eof ! End of file?
64 | logical :: error ! Invalid XML file or other error?
65 | character(len=XML_BUFFER_LENGTH) :: line ! Buffer
66 | end type XML_PARSE
67 |
68 | !===============================================================================
69 | ! Global options
70 | !===============================================================================
71 |
72 | integer, parameter :: XML_STDOUT = -1
73 | integer, private :: report_lun_ = XML_STDOUT
74 | logical, private :: report_errors_ = .false.
75 | logical, private :: report_details_ = .false.
76 |
77 | !===============================================================================
78 | ! Global data (the ampersand must come first)
79 | !===============================================================================
80 |
81 | character(len=10), dimension(2,3), save, private :: entities = &
82 | reshape( (/ '& ', '&', &
83 | '> ', '> ', &
84 | '< ', '< ' /), (/2,3/) )
85 |
86 | !===============================================================================
87 | ! Auxiliary routines - private
88 | !===============================================================================
89 |
90 | private :: xml_compress_
91 | private :: xml_put_open_tag_
92 | private :: xml_put_element_
93 | private :: xml_put_close_tag_
94 | private :: xml_replace_entities_
95 | private :: xml_remove_tabs_
96 |
97 | !===============================================================================
98 | ! Interfaces to reporting routines
99 | !===============================================================================
100 |
101 | private :: xml_report_details_int_
102 | private :: xml_report_details_string_
103 | private :: xml_report_errors_int_
104 | private :: xml_report_errors_string_
105 |
106 | interface xml_report_details
107 | module procedure xml_report_details_int_
108 | module procedure xml_report_details_string_
109 | end interface
110 | interface xml_report_errors
111 | module procedure xml_report_errors_int_
112 | module procedure xml_report_errors_string_
113 | module procedure xml_report_errors_extern_
114 | end interface
115 |
116 | contains
117 |
118 | !===============================================================================
119 | ! XML_REPORT_DETAILS_INT_ --
120 | ! Routine to write a text with an integer value
121 | ! Arguments:
122 | ! text Text to be written
123 | ! int Integer value to be added
124 | !===============================================================================
125 |
126 | subroutine xml_report_details_int_( text, int )
127 | character(len=*), intent(in) :: text
128 | integer, intent(in) :: int
129 |
130 | if ( report_details_ ) then
131 | if ( report_lun_ == XML_STDOUT ) then
132 | write(*,*) trim(text), int
133 | else
134 | write(report_lun_,*) trim(text), int
135 | endif
136 | endif
137 | end subroutine xml_report_details_int_
138 |
139 | !===============================================================================
140 | ! XML_REPORT_DETAILS_STRING_ --
141 | ! Routine to write a text with a string value
142 | ! Arguments:
143 | ! text Text to be written
144 | ! string String to be added
145 | !===============================================================================
146 |
147 | subroutine xml_report_details_string_( text, string )
148 | character(len=*), intent(in) :: text
149 | character(len=*), intent(in) :: string
150 |
151 | if ( report_details_ ) then
152 | if ( report_lun_ == XML_STDOUT ) then
153 | write(*,*) trim(text), ' ', trim(string)
154 | else
155 | write(report_lun_,*) trim(text), ' ', trim(string)
156 | endif
157 | endif
158 | end subroutine xml_report_details_string_
159 |
160 | !===============================================================================
161 | ! XML_REPORT_ERRORS_INT_ --
162 | ! Routine to write an error message text with an integer value
163 | ! Arguments:
164 | ! text Text to be written
165 | ! int Integer value to be added
166 | ! lineno Line number in the file
167 | !===============================================================================
168 |
169 | subroutine xml_report_errors_int_( text, int, lineno )
170 | character(len=*), intent(in) :: text
171 | integer, intent(in) :: int
172 | integer, optional, intent(in) :: lineno
173 |
174 | if ( report_errors_ .or. report_details_ ) then
175 | if ( report_lun_ == XML_STDOUT ) then
176 | write(*,*) trim(text), int
177 | if ( present(lineno) ) then
178 | write(*,*) ' At or near line', lineno
179 | endif
180 | else
181 | write(report_lun_,*) trim(text), int
182 | if ( present(lineno) ) then
183 | write(report_lun_,*) ' At or near line', lineno
184 | endif
185 | endif
186 | endif
187 | end subroutine xml_report_errors_int_
188 |
189 | !===============================================================================
190 | ! XML_REPORT_ERRORS_STRING_ --
191 | ! Routine to write an error message text with a string value
192 | ! Arguments:
193 | ! text Text to be written
194 | ! string String to be added
195 | ! lineno Line number in the file
196 | !===============================================================================
197 |
198 | subroutine xml_report_errors_string_( text, string, lineno )
199 | character(len=*), intent(in) :: text
200 | character(len=*), intent(in) :: string
201 | integer, optional, intent(in) :: lineno
202 |
203 | if ( report_errors_ .or. report_details_ ) then
204 | if ( report_lun_ == XML_STDOUT ) then
205 | write(*,*) trim(text), ' ', trim(string)
206 | if ( present(lineno) ) then
207 | write(*,*) ' At or near line', lineno
208 | endif
209 | else
210 | write(report_lun_,*) trim(text), ' ', trim(string)
211 | if ( present(lineno) ) then
212 | write(report_lun_,*) ' At or near line', lineno
213 | endif
214 | endif
215 | endif
216 | end subroutine xml_report_errors_string_
217 |
218 | !===============================================================================
219 | ! XML_REPORT_ERRORS_EXTERN_ --
220 | ! Routine to write an error message text with a string value
221 | ! Arguments:
222 | ! info Structure holding information on the XML-file
223 | ! text Text to be written
224 | ! Note:
225 | ! This routine is meant for use by routines outside
226 | ! this module
227 | !===============================================================================
228 |
229 | subroutine xml_report_errors_extern_( info, text )
230 | type(XML_PARSE), intent(in) :: info
231 | character(len=*), intent(in) :: text
232 |
233 | if ( report_errors_ .or. report_details_ ) then
234 | if ( report_lun_ == XML_STDOUT ) then
235 | write(*,*) trim(text), ' - at or near line', info%lineno
236 | else
237 | write(report_lun_,*) trim(text), ' - at or near line', info%lineno
238 | endif
239 | end if
240 |
241 | end subroutine xml_report_errors_extern_
242 |
243 | !===============================================================================
244 | ! XML_OPEN --
245 | ! Routine to open an XML file for reading or writing
246 | ! Arguments:
247 | ! info Structure holding information on the XML-file
248 | ! fname Name of the file
249 | ! mustread The file will be read (.true.) or written (.false.)
250 | !===============================================================================
251 |
252 | subroutine xml_open( info, fname, mustread )
253 | character(len=*), intent(in) :: fname
254 | logical, intent(in) :: mustread
255 | type(XML_PARSE), intent(out) :: info
256 |
257 | integer :: i
258 | integer :: k
259 | integer :: kend
260 | integer :: ierr
261 | logical :: opend
262 | logical :: exists
263 |
264 | info%lun = 10
265 | info%ignore_whitespace = .false.
266 | info%no_data_truncation = .false.
267 | info%too_many_attribs = .false.
268 | info%too_many_data = .false.
269 | info%eof = .false.
270 | info%error = .false.
271 | info%level = -1
272 | info%lineno = 0
273 |
274 | do i = 10,99
275 | inquire( unit = i, opened = opend )
276 | if ( .not. opend ) then
277 | info%lun = i
278 | inquire( file = fname, exist = exists )
279 | if ( .not. exists .and. mustread ) then
280 | call xml_report_errors( 'XML_OPEN: file does not exist:', trim(fname))
281 | info%lun = -1
282 | info%error = .true.
283 | else
284 | open( unit = info%lun, file = fname )
285 | call xml_report_details( 'XML_OPEN: opened file ', trim(fname) )
286 | call xml_report_details( 'at LU-number: ', info%lun )
287 | endif
288 | exit
289 | endif
290 | enddo
291 | if ( .not. info%error .and. mustread ) then
292 | k = 1
293 | do while ( k >= 1 )
294 | read( info%lun, '(a)', iostat = ierr ) info%line
295 |
296 | ! If we encounter a blank line, skip it and read the next line
297 | if (len_trim(info%line) == 0) cycle
298 |
299 | call xml_remove_tabs_(info%line)
300 | if ( ierr == 0 ) then
301 | info%line = adjustl( info%line )
302 | k = index( info%line, '' )
303 | !
304 | ! Assume (for now at least) that appears on a single line!
305 | !
306 | if ( k >= 1 ) then
307 | kend = index( info%line, '?>' )
308 | if ( kend <= 0 ) then
309 | call xml_report_errors( 'XML_OPEN: error reading file with LU-number: ', info%lun )
310 | call xml_report_errors( 'Line starting with ""', ' ' )
311 | info%error = .true.
312 | exit
313 | endif
314 | endif
315 | else
316 | call xml_report_errors( 'XML_OPEN: error reading file with LU-number: ', info%lun )
317 | call xml_report_errors( 'Possibly no line starting with "'
326 | endif
327 | end subroutine xml_open
328 |
329 | !===============================================================================
330 | ! XML_CLOSE --
331 | ! Routine to close an XML file
332 | ! Arguments:
333 | ! info Structure holding information on the XML-file
334 | !===============================================================================
335 |
336 | subroutine xml_close( info )
337 | type(XML_PARSE), intent(inout) :: info
338 |
339 | close( info%lun )
340 |
341 | !
342 | ! Only clean up the LU-number, so that the calling program
343 | ! can examine the last condition
344 | !
345 | call xml_report_details( 'XML_CLOSE: Closing file with LU-number ', info%lun )
346 | info%lun = -1
347 | end subroutine xml_close
348 |
349 | !===============================================================================
350 | ! XML_GET --
351 | ! Routine to get the next bit of information from an XML file
352 | ! Arguments:
353 | ! info Structure holding information on the XML-file
354 | ! tag Tag that was encountered
355 | ! endtag Whether the end of the element was encountered
356 | ! attribs List of attribute-value pairs
357 | ! no_attribs Number of pairs in the list
358 | ! data Lines of character data found
359 | ! no_data Number of lines of character data
360 | !===============================================================================
361 |
362 | subroutine xml_get( info, tag, endtag, attribs, no_attribs, &
363 | data, no_data )
364 | type(XML_PARSE), intent(inout) :: info
365 | character(len=*), intent(out) :: tag
366 | logical, intent(out) :: endtag
367 | character(len=*), intent(out), dimension(:,:) :: attribs
368 | integer, intent(out) :: no_attribs
369 | character(len=*), intent(out), dimension(:) :: data
370 | integer, intent(out) :: no_data
371 |
372 | integer :: kspace
373 | integer :: kend
374 | integer :: kcend
375 | integer :: keq
376 | integer :: kfirst
377 | integer :: ksecond
378 | integer :: idxat
379 | integer :: idxdat
380 | integer :: ierr
381 | logical :: close_bracket
382 | logical :: comment_tag
383 | character(len=XML_BUFFER_LENGTH) :: nextline
384 |
385 | !
386 | ! Initialise the output
387 | !
388 | endtag = .false.
389 | no_attribs = 0
390 | no_data = 0
391 |
392 | info%too_many_attribs = .false.
393 | info%too_many_data = .false.
394 |
395 | if ( info%lun < 0 ) then
396 | call xml_report_details( 'XML_GET on closed file ', ' ' )
397 | return
398 | endif
399 |
400 | !
401 | ! From the previous call or the call to xmlopen we have
402 | ! the line that we need to parse already in memory:
403 | !
404 | !
405 | comment_tag = .false.
406 | close_bracket = .false.
407 | kspace = index( info%line, ' ' )
408 | kend = index( info%line, '>' )
409 | kcend = index( info%line, '-->' )
410 | do while ( kend <= 0 )
411 | read( info%lun, '(a)', iostat = ierr ) nextline
412 | call xml_remove_tabs_(nextline)
413 | info%lineno = info%lineno + 1
414 |
415 | if ( ierr == 0 ) then
416 | info%line = trim(info%line) // ' ' // adjustl(nextline)
417 | else
418 | info%error = .true.
419 | call xml_report_errors( 'XML_GET - end of tag not found ', &
420 | '(buffer too small?)', info%lineno )
421 | call xml_close( info )
422 | return
423 | endif
424 | kend = index( info%line, '>' )
425 | enddo
426 | if ( kend > kspace ) then
427 | kend = kspace
428 | else if (info%line(1:4) == '' ) then
439 | endtag = .true.
440 | tag = info%line(4:kend-1)
441 | else if ( info%line(1:2) == '' ) then
442 | endtag = .true.
443 | tag = info%line(3:kend-1)
444 | else
445 | if ( info%line(1:1) == '<' ) then
446 | tag = info%line(2:kend-1)
447 | call xml_report_details( 'XML_GET - tag found: ', trim(tag) )
448 | else
449 | kend = 0 ! Beginning of data!
450 | endif
451 | endif
452 |
453 | info%line = adjustl( info%line(kend+1:) )
454 |
455 | idxat = 0
456 | idxdat = 0
457 |
458 | if ( tag(1:3) == '!--' ) comment_tag = .true.
459 |
460 | do while ( info%line /= ' ' .and. .not. close_bracket .and. .not. comment_tag )
461 |
462 | keq = index( info%line, '=' )
463 | kend = index( info%line, '>' )
464 | if ( keq > kend ) keq = 0 ! Guard against multiple tags
465 | ! with attributes on one line
466 |
467 | !
468 | ! No attributes any more?
469 | !
470 | if ( keq < 1 ) then
471 | kend = index( info%line, '/>' )
472 | if ( kend >= 1 ) then
473 | kend = kend + 1 ! To go beyond the ">" character
474 | endtag = .true.
475 | else
476 | kend = index( info%line, '>' )
477 | if ( kend < 1 ) then
478 | call xml_report_errors( 'XML_GET - wrong ending of tag ', &
479 | trim(info%line), info%lineno )
480 | info%error = .true. ! Wrong ending of line!
481 | call xml_close( info )
482 | return
483 | else
484 | close_bracket = .true.
485 | endif
486 | endif
487 | if ( kend >= 1 ) then
488 | info%line = adjustl( info%line(kend+1:) )
489 | endif
490 | exit
491 | endif
492 |
493 | idxat = idxat + 1
494 | if ( idxat <= size(attribs,2) ) then
495 | no_attribs = idxat
496 | attribs(1,idxat) = adjustl(info%line(1:keq-1)) ! Use adjustl() to avoid
497 | ! multiple spaces, etc
498 | info%line = adjustl( info%line(keq+1:) )
499 |
500 | !
501 | ! We have almost found the start of the attribute's value
502 | !
503 | kfirst = index( info%line, '"' )
504 | if ( kfirst < 1 ) then
505 | call xml_report_errors( 'XML_GET - malformed attribute-value pair: ', &
506 | trim(info%line), info%lineno )
507 | info%error = .true. ! Wrong form of attribute-value pair
508 | call xml_close( info )
509 | return
510 | endif
511 |
512 | ksecond = index( info%line(kfirst+1:), '"' ) + kfirst
513 | if ( ksecond < 1 ) then
514 | call xml_report_errors( 'XML_GET - malformed attribute-value pair: ', &
515 | trim(info%line), info%lineno )
516 | info%error = .true. ! Wrong form of attribute-value pair
517 | call xml_close( info )
518 | return
519 | endif
520 |
521 | attribs(2,idxat) = info%line(kfirst+1:ksecond-1)
522 | info%line = adjustl( info%line(ksecond+1:) )
523 | endif
524 |
525 | if ( idxat > size(attribs,2) ) then
526 | call xml_report_errors( 'XML_GET - more attributes than could be stored: ', &
527 | trim(info%line), info%lineno )
528 | info%too_many_attribs = .true.
529 | info%line = ' '
530 | exit
531 | endif
532 | enddo
533 |
534 | !
535 | ! Now read the data associated with the current tag
536 | ! - all the way to the next "<" character
537 | !
538 | ! To do: reduce the number of data lines - empty ones
539 | ! at the end should not count.
540 | !
541 | do
542 | if ( comment_tag ) then
543 | kend = index( info%line, '-->' )
544 | else
545 | kend = index( info%line, '<' )
546 | endif
547 | idxdat = idxdat + 1
548 | if ( idxdat <= size(data) ) then
549 | no_data = idxdat
550 | if ( kend >= 1 ) then
551 | data(idxdat) = info%line(1:kend-1)
552 | info%line = info%line(kend:)
553 | else
554 | data(idxdat) = info%line
555 | endif
556 | else
557 | call xml_report_errors( 'XML_GET - more data lines than could be stored: ', &
558 | trim(info%line), info%lineno )
559 | info%too_many_data = .true.
560 | exit
561 | endif
562 |
563 | !
564 | ! No more data? Otherwise, read on
565 | !
566 | if ( kend >= 1 ) then
567 | exit
568 | else
569 | read( info%lun, '(a)', iostat = ierr ) info%line
570 | call xml_remove_tabs_(info%line)
571 | info%lineno = info%lineno + 1
572 |
573 | if ( ierr < 0 ) then
574 | call xml_report_details( 'XML_GET - end of file found - LU-number: ', &
575 | info%lun )
576 | info%eof = .true.
577 | elseif ( ierr > 0 ) then
578 | call xml_report_errors( 'XML_GET - error reading file with LU-number ', &
579 | info%lun, info%lineno )
580 | info%error = .true.
581 | endif
582 | if ( ierr /= 0 ) then
583 | exit
584 | endif
585 | endif
586 | enddo
587 |
588 | !
589 | ! Compress the data?
590 | !
591 | if ( info%ignore_whitespace ) then
592 | call xml_compress_( data, no_data )
593 | endif
594 |
595 | !
596 | ! Replace the entities, if any
597 | !
598 | call xml_replace_entities_( data, no_data )
599 |
600 | call xml_report_details( 'XML_GET - number of attributes: ', no_attribs )
601 | call xml_report_details( 'XML_GET - number of data lines: ', no_data )
602 |
603 | end subroutine xml_get
604 |
605 | !===============================================================================
606 | ! XML_PUT --
607 | ! Routine to write a tag with the associated data to an XML file
608 | ! Arguments:
609 | ! info Structure holding information on the XML-file
610 | ! tag Tag that was encountered
611 | ! endtag Whether the end of the element was encountered
612 | ! attribs List of attribute-value pairs
613 | ! no_attribs Number of pairs in the list
614 | ! data Lines of character data found
615 | ! no_data Number of lines of character data
616 | ! type Type of action:
617 | ! open - just the opening tag with attributes
618 | ! elem - complete element
619 | ! close - just the closing tag
620 | !===============================================================================
621 |
622 | subroutine xml_put(info, tag, attribs, no_attribs, &
623 | data, no_data, type)
624 |
625 | type(XML_PARSE), intent(inout) :: info
626 | character(len=*), intent(in) :: tag
627 | character(len=*), intent(in), dimension(:,:) :: attribs
628 | integer, intent(in) :: no_attribs
629 | character(len=*), intent(in), dimension(:) :: data
630 | integer, intent(in) :: no_data
631 | character(len=*) :: type
632 |
633 | select case(type)
634 | case('open')
635 | call xml_put_open_tag_(info, tag, attribs, no_attribs)
636 | case('elem')
637 | call xml_put_element_(info, tag, attribs, no_attribs, &
638 | data, no_data)
639 | case('close')
640 | call xml_put_close_tag_(info, tag)
641 | end select
642 |
643 | end subroutine xml_put
644 |
645 | !===============================================================================
646 | ! XML_PUT_OPEN_TAG_ --
647 | ! Routine to write the opening tag with the attributes
648 | ! Arguments:
649 | ! info Structure holding information on the XML-file
650 | ! tag Tag that was encountered
651 | ! endtag Whether the end of the element was encountered
652 | ! attribs List of attribute-value pairs
653 | ! no_attribs Number of pairs in the list
654 | ! data Lines of character data found
655 | ! no_data Number of lines of character data
656 | !===============================================================================
657 |
658 | subroutine xml_put_open_tag_(info, tag, attribs, no_attribs)
659 |
660 | type(XML_PARSE), intent(inout) :: info
661 | character(len=*), intent(in) :: tag
662 | character(len=*), intent(in), dimension(:,:) :: attribs
663 | integer, intent(in) :: no_attribs
664 |
665 | integer :: i
666 | character(len=300), parameter :: indent = ' '
667 |
668 | write( info%lun, '(3a)', advance = 'no' ) &
669 | indent(1:3*info%level), '<', adjustl(tag)
670 | do i=1,no_attribs
671 | if (attribs(2,i)/='') then
672 | write( info%lun, '(5a)', advance = 'no' ) &
673 | ' ',trim(attribs(1,i)),'="', trim(attribs(2,i)),'"'
674 | endif
675 | enddo
676 | write( info%lun, '(a)' ) '>'
677 | info%level = info%level + 1
678 |
679 | end subroutine xml_put_open_tag_
680 |
681 | !===============================================================================
682 | ! XML_PUT_ELEMENT_ --
683 | ! Routine to write the complete element
684 | ! Arguments:
685 | ! info Structure holding information on the XML-file
686 | ! tag Tag that was encountered
687 | ! endtag Whether the end of the element was encountered
688 | ! attribs List of attribute-value pairs
689 | ! no_attribs Number of pairs in the list
690 | ! data Lines of character data found
691 | ! no_data Number of lines of character data
692 | !===============================================================================
693 |
694 | subroutine xml_put_element_(info, tag, attribs, no_attribs, &
695 | data, no_data)
696 |
697 | type(XML_PARSE), intent(inout) :: info
698 | character(len=*), intent(in) :: tag
699 | character(len=*), intent(in), dimension(:,:) :: attribs
700 | integer, intent(in) :: no_attribs
701 | character(len=*), intent(in), dimension(:) :: data
702 | integer, intent(in) :: no_data
703 |
704 | logical :: logic
705 | character(len=1) :: aa
706 | integer :: i, ii
707 |
708 | character(len=300), parameter :: indent = ' '
709 |
710 | if ( (no_attribs==0 .and. no_data==0) ) then
711 | return
712 | else
713 | logic = .true.
714 | do ii = 1,no_attribs
715 | logic = logic .and. (attribs(2,ii)=='')
716 | enddo
717 | do ii = 1,no_data
718 | logic = logic .and. (data(ii)=='')
719 | enddo
720 | if ( logic ) then
721 | return
722 | else
723 | write( info%lun, '(3a)', advance = 'no' ) &
724 | indent(1:3*info%level), '<', adjustl(tag)
725 | do i = 1,no_attribs
726 | if (attribs(2,i)/='') then
727 | write( info%lun, '(5a)', advance = 'no' ) &
728 | ' ',trim(attribs(1,i)),'="', trim(attribs(2,i)),'"'
729 | endif
730 | enddo
731 | if ( no_attribs>0 .and. no_data==0 ) then
732 | aa='a'
733 | elseif ( (no_attribs>0 .and. no_data>0) .or. &
734 | (no_attribs==0 .and. no_data>0) ) then
735 | aa='b'
736 | else
737 | write(*,*) no_attribs, no_data
738 | endif
739 | endif
740 | endif
741 |
742 | select case(aa)
743 | case('a')
744 | write( info%lun, '(a)' ) '/>'
745 | case('b')
746 | write( info%lun, '(a)',advance='no' ) '>'
747 | write( info%lun, '(2a)', advance='no') ( ' ', trim(data(i)), i=1,no_data )
748 | write( info%lun, '(4a)' ) ' ','', tag, '>'
749 | end select
750 |
751 | end subroutine xml_put_element_
752 |
753 | !===============================================================================
754 | ! XML_PUT_CLOSE_TAG_ --
755 | ! Routine to write the closing tag
756 | ! Arguments:
757 | ! info Structure holding information on the XML-file
758 | ! tag Tag that was encountered
759 | ! endtag Whether the end of the element was encountered
760 | ! attribs List of attribute-value pairs
761 | ! no_attribs Number of pairs in the list
762 | ! data Lines of character data found
763 | ! no_data Number of lines of character data
764 | !===============================================================================
765 |
766 | subroutine xml_put_close_tag_(info, tag)
767 |
768 | type(XML_PARSE), intent(inout) :: info
769 | character(len=*), intent(in) :: tag
770 |
771 | character(len=300), parameter :: indent = ' '
772 |
773 | info%level = info%level - 1
774 | write(info%lun, '(4a)') indent(1:3*info%level), '', adjustl(tag), '>'
775 |
776 | end subroutine xml_put_close_tag_
777 |
778 | !===============================================================================
779 | ! XML_COMPRESS_ --
780 | ! Routine to remove empty lines from the character data
781 | ! Arguments:
782 | ! data Lines of character data found
783 | ! no_data (Nett) number of lines of character data
784 | !===============================================================================
785 |
786 | subroutine xml_compress_( data, no_data )
787 | character(len=*), intent(inout), dimension(:) :: data
788 | integer, intent(inout) :: no_data
789 |
790 | integer :: i
791 | integer :: j
792 | logical :: empty
793 |
794 | j = 0
795 | empty = .true.
796 | do i = 1,no_data
797 | if ( len_trim(data(i)) /= 0 .or. .not. empty ) then
798 | j = j + 1
799 | data(j) = adjustl(data(i))
800 | empty = .false.
801 | endif
802 | enddo
803 |
804 | no_data = j
805 |
806 | do i = no_data,1,-1
807 | if ( len_trim(data(i)) /= 0 ) then
808 | exit
809 | else
810 | no_data = no_data - 1
811 | endif
812 | enddo
813 |
814 | end subroutine xml_compress_
815 |
816 | !===============================================================================
817 | ! XML_REPLACE_ENTITIES_ --
818 | ! Routine to replace entities such as > by their
819 | ! proper character representation
820 | ! Arguments:
821 | ! data Lines of character data found
822 | ! no_data (Nett) number of lines of character data
823 | !===============================================================================
824 |
825 | subroutine xml_replace_entities_( data, no_data )
826 | character(len=*), intent(inout), dimension(:) :: data
827 | integer, intent(inout) :: no_data
828 |
829 | integer :: i
830 | integer :: j
831 | integer :: j2
832 | integer :: k
833 | integer :: pos
834 | logical :: found
835 |
836 | do i = 1,no_data
837 | j = 1
838 | do
839 | do k = 1,size(entities,2)
840 | found = .false.
841 | pos = index( data(i)(j:), trim(entities(2,k)) )
842 | if ( pos > 0 ) then
843 | found = .true.
844 | j = j + pos - 1
845 | j2 = j + len_trim(entities(2,k))
846 | data(i)(j:) = trim(entities(1,k)) // data(i)(j2:)
847 | j = j2
848 | endif
849 | enddo
850 | if ( .not. found ) exit
851 | enddo
852 | enddo
853 |
854 | end subroutine xml_replace_entities_
855 |
856 | !===============================================================================
857 | ! XML_OPTIONS --
858 | ! Routine to handle the parser options
859 | ! Arguments:
860 | ! info Structure holding information on the XML-file
861 | ! ignore_whitespace Ignore whitespace (leading blanks, empty lines) or not
862 | ! no_data_truncation Consider truncation of strings an error or not
863 | ! report_lun LU-number for reporting information
864 | ! report_errors Write messages about errors or not
865 | ! report_details Write messages about all kinds of actions or not
866 | !===============================================================================
867 |
868 | subroutine xml_options( info, ignore_whitespace, no_data_truncation, &
869 | report_lun, report_errors, &
870 | report_details )
871 | type(XML_PARSE), intent(inout) :: info
872 | logical, intent(in), optional :: ignore_whitespace
873 | logical, intent(in), optional :: no_data_truncation
874 |
875 | integer, intent(in), optional :: report_lun
876 | logical, intent(in), optional :: report_errors
877 | logical, intent(in), optional :: report_details
878 |
879 | if ( present(ignore_whitespace) ) then
880 | info%ignore_whitespace = ignore_whitespace
881 | endif
882 | if ( present(no_data_truncation) ) then
883 | info%no_data_truncation = no_data_truncation
884 | endif
885 | if ( present(report_lun) ) then
886 | report_lun_ = report_lun
887 | endif
888 | if ( present(report_errors) ) then
889 | report_errors_ = report_errors
890 | endif
891 | if ( present(report_details) ) then
892 | report_details_ = report_details
893 | endif
894 | end subroutine xml_options
895 |
896 | !===============================================================================
897 | ! XML_OK --
898 | ! Function that returns whether all was okay or not
899 | ! Arguments:
900 | ! info Structure holding information on the XML-file
901 | ! Returns:
902 | ! .true. if there was no error, .false. otherwise
903 | !===============================================================================
904 |
905 | logical function xml_ok( info )
906 | type(XML_PARSE), intent(in) :: info
907 |
908 | xml_ok = info%eof .or. info%error .or. &
909 | ( info%no_data_truncation .and. &
910 | ( info%too_many_attribs .or. info%too_many_data ) )
911 | xml_ok = .not. xml_ok
912 | end function xml_ok
913 |
914 | !===============================================================================
915 | ! XML_ERROR --
916 | ! Function that returns whether there was an error
917 | ! Arguments:
918 | ! info Structure holding information on the XML-file
919 | ! Returns:
920 | ! .true. if there was an error, .false. if there was none
921 | !===============================================================================
922 |
923 | logical function xml_error( info )
924 | type(XML_PARSE), intent(in) :: info
925 |
926 | xml_error = info%error .or. &
927 | ( info%no_data_truncation .and. &
928 | ( info%too_many_attribs .or. info%too_many_data ) )
929 | end function xml_error
930 |
931 | !===============================================================================
932 | ! XML_DATA_TRUNC --
933 | ! Function that returns whether data were truncated or not
934 | ! Arguments:
935 | ! info Structure holding information on the XML-file
936 | ! Returns:
937 | ! .true. if data were truncated, .false. otherwise
938 | !===============================================================================
939 |
940 | logical function xml_data_trunc( info )
941 | type(XML_PARSE), intent(in) :: info
942 |
943 | xml_data_trunc = info%too_many_attribs .or. info%too_many_data
944 | end function xml_data_trunc
945 |
946 | !===============================================================================
947 | ! XML_FIND_ATTRIB
948 | !===============================================================================
949 |
950 | integer function xml_find_attrib( attribs, no_attribs, name, value )
951 | character(len=*), dimension(:,:) :: attribs
952 | integer :: no_attribs
953 | character(len=*) :: name
954 | character(len=*) :: value
955 |
956 | integer :: i
957 |
958 | xml_find_attrib = -1
959 | do i = 1,no_attribs
960 | if ( name == attribs(1,i) ) then
961 | value = attribs(2,i)
962 | xml_find_attrib = i
963 | exit
964 | endif
965 | enddo
966 |
967 | end function xml_find_attrib
968 |
969 | !===============================================================================
970 | ! XML_PROCESS --
971 | ! Routine to read the XML file as a whole and distribute processing
972 | ! the contents over three user-defined subroutines
973 | ! Arguments:
974 | ! filename Name of the file to process
975 | ! attribs Array for holding the attributes
976 | ! data Array for holding the character data
977 | ! startfunc Subroutine to handle the start of elements
978 | ! datafunc Subroutine to handle the character data
979 | ! endfunc Subroutine to handle the end of elements
980 | ! error Indicates if there was an error or not
981 | ! Note:
982 | ! The routine is declared recursive to allow inclusion of XML files
983 | ! (common with XSD schemas). This extends to the auxiliary routines.
984 | !===============================================================================
985 |
986 | recursive &
987 | subroutine xml_process( filename, attribs, data, startfunc, datafunc, endfunc, lunrep, error )
988 | character(len=*) :: filename
989 | character(len=*), dimension(:,:) :: attribs
990 | character(len=*), dimension(:) :: data
991 | integer :: lunrep
992 | logical :: error
993 |
994 | interface
995 | recursive subroutine startfunc( tag, attribs, error )
996 | character(len=*) :: tag
997 | character(len=*), dimension(:,:) :: attribs
998 | logical :: error
999 | end subroutine
1000 | end interface
1001 |
1002 | interface
1003 | recursive subroutine datafunc( tag, data, error )
1004 | character(len=*) :: tag
1005 | character(len=*), dimension(:) :: data
1006 | logical :: error
1007 | end subroutine
1008 | end interface
1009 |
1010 | interface
1011 | recursive subroutine endfunc( tag, error )
1012 | character(len=*) :: tag
1013 | logical :: error
1014 | end subroutine
1015 | end interface
1016 |
1017 | type(XML_PARSE) :: info
1018 | character(len=80) :: tag
1019 | logical :: endtag
1020 | integer :: noattribs
1021 | integer :: nodata
1022 |
1023 | call xml_options( info, report_lun = lunrep, report_details = .false. )
1024 | call xml_open( info, filename, .true. )
1025 |
1026 | error = .false.
1027 | do
1028 | call xml_get( info, tag, endtag, attribs, noattribs, data, nodata )
1029 | if ( .not. xml_ok(info) ) then
1030 | exit
1031 | endif
1032 |
1033 | if ( xml_error(info) ) then
1034 | write(lunrep,*) 'Error reading XML file!'
1035 | error = .true.
1036 | exit
1037 | endif
1038 |
1039 | if ( .not. endtag .or. noattribs /= 0 ) then
1040 | call startfunc( tag, attribs(:,1:noattribs), error )
1041 | if ( error ) exit
1042 |
1043 | call datafunc( tag, data(1:nodata), error )
1044 | if ( error ) exit
1045 | endif
1046 |
1047 | if ( endtag ) then
1048 | call endfunc( tag, error )
1049 | if ( error ) exit
1050 | endif
1051 | enddo
1052 | call xml_close( info )
1053 | end subroutine xml_process
1054 |
1055 | !===============================================================================
1056 | ! XML_REMOVE_TABS_ --
1057 | ! Routine to change any horizontal tab characters to spaces when reading a
1058 | ! new line of data
1059 | ! Arguments:
1060 | ! line Line of character data to modify
1061 | !===============================================================================
1062 |
1063 | subroutine xml_remove_tabs_(line)
1064 | character(len=*), intent(inout) :: line
1065 |
1066 | integer :: i
1067 |
1068 | do i = 1, len_trim(line)
1069 | if (line(i:i) == achar(9)) then
1070 | line(i:i) = ' '
1071 | end if
1072 | end do
1073 |
1074 | end subroutine xml_remove_tabs_
1075 |
1076 | end module xmlparse
1077 |
--------------------------------------------------------------------------------
/src/xmlreader.conf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
--------------------------------------------------------------------------------
/src/xmlreader.inp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulromano/xml-fortran/ffe63a0591e86fc1c17a6cb673b56e633b15f906/src/xmlreader.inp
--------------------------------------------------------------------------------
/src/xsdconvert.f90:
--------------------------------------------------------------------------------
1 | ! xsdconvert.f90 --
2 | ! Auxiliary program to convert an XSD file to a schema file
3 | ! as used by xmlreader
4 | !
5 | module handle_elems
6 | use xmlparse
7 |
8 | implicit none
9 |
10 | integer :: lunout
11 |
12 | logical, dimension(0:10), save :: intype = .false.
13 | logical, save :: rootelement = .false.
14 | logical, dimension(0:10), save :: optional = .false.
15 | integer, save :: type_level = 0
16 | integer, save :: level = 0
17 | integer, save :: type_count = 0
18 | character(len=40), save :: rootname
19 |
20 | type complextype_definition
21 | character(len=40) :: type_name
22 | character(len=40), dimension(20) :: attrib_name
23 | character(len=40), dimension(20) :: attrib_type
24 | logical, dimension(20) :: attrib_use
25 | character(len=40), dimension(20) :: attrib_default
26 | logical, dimension(20) :: has_default
27 | integer :: number_attribs
28 | logical :: is_array
29 | end type complextype_definition
30 |
31 | type(complextype_definition), dimension(10), save :: type_def
32 | logical, dimension(10), save :: type_write
33 |
34 | contains
35 |
36 | ! start_elem --
37 | ! Handle the start of the elements in the XSD file
38 | !
39 | ! Arguments:
40 | ! tag Tag that was found
41 | ! attribs List of attributes and their values
42 | ! error Error parameter (output)
43 | !
44 | ! Note:
45 | ! Required because CVF 6.6 gave a strange error message when
46 | ! trying to use start_elem directly.
47 | !
48 | recursive subroutine start_elemx( tag, attribs, error )
49 |
50 | character(len=*) :: tag
51 | character(len=*), dimension(:,:) :: attribs
52 | logical :: error
53 |
54 | call start_elem( tag, attribs, error )
55 | end subroutine
56 |
57 | recursive subroutine start_elem( tag, attribs, error )
58 |
59 | character(len=*) :: tag
60 | character(len=*), dimension(:,:) :: attribs
61 | logical :: error
62 |
63 | character(len=100), dimension(100) :: new_data
64 | character(len=100), dimension(2,100) :: new_attribs
65 | character(len=300) :: filename
66 | character(len=40) :: name
67 | character(len=40) :: type
68 | integer :: idx
69 |
70 | !! write( lunout, * ) '>>Tag: ', tag, ' - level:', type_level
71 | select case( tag )
72 | case( 'schema', 'annotation', 'documentation', '!--' )
73 | ! Ignore
74 | case( 'xs:schema', 'xs:annotation', 'xs:documentation' )
75 | ! Ignore
76 | case( 'xs:include', 'include' )
77 | idx = xml_find_attrib( attribs, size(attribs,2), 'schemaLocation', filename )
78 |
79 | ! We now use the fact that all routines are recursive to
80 | ! implement the inclusion of other XSD schemas
81 | ! (For the "module variables" this makes no difference -
82 | ! they should be globally updated anyway
83 |
84 | call xml_process( filename, new_attribs, new_data, start_elemx, data_elem, &
85 | end_elem, 20, error )
86 |
87 | case( 'xs:element', 'element' )
88 |
89 | !
90 | ! If this is the first time we encounter "element", it
91 | ! defines the rootname
92 | ! In all other cases, examine the rest of the information
93 | !
94 | if ( .not. rootelement ) then
95 | rootelement = .true.
96 | idx = xml_find_attrib( attribs, size(attribs,2), 'name', name )
97 | write( lunout, * ) ''
98 | else
99 | type_level = type_level + 1
100 | type_write(type_level) = .true.
101 | call convert_elem( attribs, .false., optional(level) )
102 | endif
103 |
104 | case( 'xs:attribute', 'attribute' )
105 | call convert_attrib( attribs, optional(level) )
106 |
107 | case( 'xs:sequence', 'sequence' )
108 | level = level + 1
109 | optional(level) = .false.
110 |
111 | case( 'xs:choice', 'choice' )
112 | level = level + 1
113 | optional(level) = .true.
114 |
115 | case( 'xs:complexType', 'complexType' )
116 | if ( size(attribs,2) > 0 ) then
117 | type_level = type_level + 1
118 | type_write(type_level) = .true.
119 | call convert_elem( attribs, .true., optional(level) )
120 | endif
121 |
122 | case( 'xs:simpleType', 'simpleType' )
123 | idx = xml_find_attrib( attribs, size(attribs,2), 'name', name )
124 | write( lunout, * ) ''
125 |
126 | case( 'xs:restriction', 'restriction' )
127 | idx = xml_find_attrib( attribs, size(attribs,2), 'base', name )
128 | write( lunout, * ) ' '
129 |
130 | case( 'xs:enumeration', 'enumeration' )
131 | ! Ignore
132 | case default
133 | write( *, * ) 'Ignoring: ', tag
134 | end select
135 |
136 | end subroutine
137 |
138 | ! data_elem --
139 | ! Handle the data belonging to the elements in the XSD file
140 | !
141 | ! Arguments:
142 | ! tag Tag that was found
143 | ! data Array of data strings
144 | ! error Error parameter (output)
145 | !
146 | recursive subroutine data_elem( tag, data, error )
147 |
148 | character(len=*) :: tag
149 | character(len=*), dimension(:) :: data
150 | logical :: error
151 |
152 | ! Dummy in this case
153 |
154 | return
155 |
156 | end subroutine data_elem
157 |
158 | ! end_elem --
159 | ! Handle the end of the elements in the XSD file
160 | !
161 | ! Arguments:
162 | ! tag Tag that was found
163 | ! error Error parameter (output)
164 | !
165 | ! Note:
166 | ! It is easier to write all the relevant information in this
167 | ! routine. The others simply gather the information.
168 | !
169 | recursive subroutine end_elem( tag, error )
170 |
171 | character(len=*) :: tag
172 | logical :: error
173 |
174 | !! write( lunout, * ) '>>Endtag: ', trim(tag), ' - level: ',type_level
175 | select case( tag )
176 | case( 'xs:complexType', 'complexType' )
177 | if ( type_level == 1 ) then
178 | if ( type_write(type_level) ) then
179 | call write_elem
180 | endif
181 | type_write(type_level) = .false.
182 | type_level = type_level - 1
183 | endif
184 |
185 | case( 'xs:simpleType', 'simpleType' )
186 | write( lunout, * ) ''
187 |
188 | case( 'xs:sequence', 'sequence', 'xs:choice', 'choice' )
189 | level = level - 1
190 |
191 | case( 'xs:element', 'element' )
192 | if ( type_level > 0 ) then
193 | if ( type_write(type_level) ) then
194 | call write_elem
195 | endif
196 | type_write(type_level) = .false.
197 | type_level = type_level - 1
198 | endif
199 |
200 | end select
201 |
202 | return
203 |
204 | end subroutine end_elem
205 |
206 | ! convert_elem --
207 | ! Convert the definition of an element
208 | !
209 | ! Arguments:
210 | ! attrib Attributes making up the definition
211 | ! complextype Is the item a complexType or not
212 | ! optional Is the item optional or not?
213 | !
214 | subroutine convert_elem( attribs, complextype, optional )
215 |
216 | character(len=*), dimension(:,:) :: attribs
217 | logical :: complextype
218 | logical :: optional
219 |
220 | character(len=40) :: name
221 | character(len=40) :: type
222 | character(len=40) :: minoccurs
223 | character(len=40) :: maxoccurs
224 | integer :: idx
225 | integer :: att
226 | integer :: off
227 |
228 | type_def(type_level)%number_attribs = 0
229 |
230 | !
231 | ! If the type is anonymous, we must construct a type name
232 | ! and there will be an extra element complexType inbetween
233 | !
234 | idx = xml_find_attrib( attribs, size(attribs,2), 'name', name )
235 |
236 | idx = xml_find_attrib( attribs, size(attribs,2), 'type', type )
237 |
238 | off = 1
239 | if ( idx <= 0 .and. .not. complextype ) then
240 | type_count = type_count + 1
241 | write( type_def(type_level)%type_name, '(a,i0)' ) 'type', type_count
242 | else
243 | if ( complextype ) then
244 | type_def(type_level)%type_name = name
245 | else
246 | type_write(type_level) = .false.
247 | endif
248 | endif
249 |
250 | if ( .not. complextype ) then
251 | att = type_def(type_level-off)%number_attribs + 1
252 | type_def(type_level-off)%number_attribs = att
253 | type_def(type_level-off)%attrib_name(att) = name
254 | type_def(type_level-off)%attrib_type(att) = type_def(type_level)%type_name
255 | endif
256 |
257 | minoccurs = '1'
258 | maxoccurs = '1'
259 | idx = xml_find_attrib( attribs, size(attribs,2), 'minOccurs', minoccurs )
260 | idx = xml_find_attrib( attribs, size(attribs,2), 'maxOccurs', maxoccurs )
261 |
262 | !
263 | ! TODO: fixed shape
264 | !
265 | type_def(type_level)%is_array = minoccurs /= maxoccurs
266 |
267 | ! TODO:
268 | ! - optional parameters
269 | ! - array-type or not
270 | ! - map the type to the xmlreader/Fortran type
271 |
272 | return
273 |
274 | end subroutine convert_elem
275 |
276 | ! convert_attrib --
277 | ! Convert the definition of an attribute
278 | !
279 | ! Arguments:
280 | ! attrib Attributes making up the definition
281 | ! optional Is the item optional or not?
282 | !
283 | subroutine convert_attrib( attribs, optional )
284 |
285 | character(len=*), dimension(:,:) :: attribs
286 | logical :: optional
287 |
288 | character(len=40) :: name
289 | character(len=40) :: type
290 | character(len=40) :: use
291 | character(len=40) :: defvalue
292 | integer :: idx
293 | integer :: att
294 |
295 | idx = xml_find_attrib( attribs, size(attribs,2), 'name', name )
296 | idx = xml_find_attrib( attribs, size(attribs,2), 'type', type )
297 |
298 | if ( idx <= 0 ) then
299 | type = 'line'
300 | endif
301 |
302 | att = type_def(type_level)%number_attribs + 1
303 | type_def(type_level)%number_attribs = att
304 |
305 | type_def(type_level)%attrib_name(att) = name
306 | type_def(type_level)%attrib_type(att) = type
307 |
308 | use = 'required'
309 | defvalue = ' '
310 |
311 | idx = xml_find_attrib( attribs, size(attribs,2), 'use' , use )
312 | type_def(type_level)%attrib_use(att) = use == 'required'
313 |
314 | idx = xml_find_attrib( attribs, size(attribs,2), 'default' , defvalue )
315 | type_def(type_level)%attrib_default(att) = defvalue
316 | type_def(type_level)%has_default(att) = idx > 0
317 |
318 | return
319 |
320 | end subroutine convert_attrib
321 |
322 | ! write_elem --
323 | ! Write the definition of an element to the file
324 | !
325 | ! Arguments:
326 | ! None
327 | !
328 | subroutine write_elem
329 |
330 | character(len=40) :: type
331 | character(len=40) :: name
332 | integer :: i
333 |
334 | type = type_def(type_level)%type_name
335 |
336 | !
337 | ! Write the element definition
338 | !
339 | write( lunout, '(a)' ) ''
340 |
341 | if ( type_def(type_level)%number_attribs > 0 ) then
342 | do i = 1,type_def(type_level)%number_attribs
343 | name = type_def(type_level)%attrib_name(i)
344 | type = type_def(type_level)%attrib_type(i)
345 |
346 | write( lunout, '(a)', advance = 'no' ) ' '
362 | enddo
363 | else
364 | !
365 | ! No attributes given, so make it a string by default
366 | !
367 | write( lunout, '(a)' ) ' '
368 | endif
369 |
370 | write( lunout, '(a)' ) ''
371 |
372 | end subroutine write_elem
373 |
374 | end module handle_elems
375 |
376 | program xsdconvert
377 |
378 | use xmlparse
379 | use handle_elems
380 |
381 | character(len=80) :: filename
382 | logical :: error
383 |
384 | character(len=100), dimension(2,100) :: attribs
385 | character(len=100), dimension(100) :: data
386 |
387 | open( 10, file = 'xsdconvert.inp' )
388 | read( 10, '(a)' ) filename
389 | open( 12, file = trim(filename) // '.xml' )
390 |
391 | lunout = 20
392 | open( lunout, file = 'xsdconvert.out' )
393 | write( lunout, * ) ''
394 |
395 | filename = trim(filename) // '.xsd'
396 |
397 | call xml_process( filename, attribs, data, start_elem, data_elem, &
398 | end_elem, 20, error )
399 |
400 | end program
401 |
--------------------------------------------------------------------------------