8ec0caa6c2a7adad25aa0b6d2e68dbaae66262a1
[platform/upstream/doxygen.git] / src / configgen.py
1 #!/usr/bin/python
2 # python script to generate configoptions.cpp and config.doc from config.xml
3 #
4 # Copyright (C) 1997-2014 by Dimitri van Heesch.
5 #
6 # Permission to use, copy, modify, and distribute this software and its
7 # documentation under the terms of the GNU General Public License is hereby
8 # granted. No representations are made about the suitability of this software
9 # for any purpose. It is provided "as is" without express or implied warranty.
10 # See the GNU General Public License for more details.
11 #
12 # Documents produced by Doxygen are derivative works derived from the
13 # input used in their production; they are not affected by this license.
14 #
15 import xml.dom.minidom
16 import sys
17 import re
18 import textwrap
19 from xml.dom import minidom, Node
20
21
22 def transformDocs(doc):
23         # join lines, unless it is an empty line
24         # remove doxygen layout constructs
25         doc = doc.strip()
26         doc = doc.replace("\n", " ")
27         doc = doc.replace("\r", " ")
28         doc = doc.replace("\t", " ")
29         doc = doc.replace("\\&", "&")
30         doc = doc.replace("\\c ", " ")
31         doc = doc.replace("\\b ", " ")
32         doc = doc.replace("\\e ", " ")
33         doc = doc.replace("\\$", "$")
34         doc = doc.replace("\\#include ", "#include ")
35         doc = doc.replace("\\#undef ", "#undef ")
36         doc = doc.replace("-# ", "\n - ")
37         doc = doc.replace(" - ", "\n - ")
38         doc = doc.replace("\\sa", "\nSee also: ")
39         doc = doc.replace("\\par", "\n")
40         doc = doc.replace("@note", "\nNote:")
41         doc = doc.replace("\\note", "\nNote:")
42         doc = doc.replace("\\verbatim", "\n")
43         doc = doc.replace("\\endverbatim", "\n")
44         doc = doc.replace("<code>", "")
45         doc = doc.replace("</code>", "")
46         doc = doc.replace("`", "")
47         doc = doc.replace("\\<", "<")
48         doc = doc.replace("\\>", ">")
49         doc = doc.replace("\\@", "@")
50         doc = doc.replace("\\\\", "\\")
51         # \ref name "description" -> description
52         doc = re.sub('\\\\ref +[^ ]* +"([^"]*)"', '\\1', doc)
53         # \ref specials
54         # \ref <key> -> description
55         doc = re.sub('\\\\ref +doxygen_usage', '"Doxygen usage"', doc)
56         doc = re.sub('\\\\ref +extsearch', '"External Indexing and Searching"',
57                                  doc)
58         doc = re.sub('\\\\ref +external', '"Linking to external documentation"',
59                                  doc)
60         # fallback for not handled
61         doc = re.sub('\\\\ref', '', doc)
62         #<a href="address">description</a> -> description (see: address)
63         doc = re.sub('<a +href="([^"]*)" *>([^<]*)</a>', '\\2 (see: \\1)', doc)
64         # LaTeX name as formula -> LaTeX
65         doc = doc.replace("\\f$\\mbox{\\LaTeX}\\f$", "LaTeX")
66         # Other forula's (now just 2) so explicitely mentioned.
67         doc = doc.replace("\\f$2^{(16+\\mbox{LOOKUP\\_CACHE\\_SIZE})}\\f$",
68                                           "2^(16+LOOKUP_CACHE_SIZE)")
69         doc = doc.replace("\\f$2^{16} = 65536\\f$", "2^16=65536")
70         # remove consecutive spaces
71         doc = re.sub(" +", " ", doc)
72         # a dirty trick to get an extra empty line in Doxyfile documentation.
73         # <br> will be removed later on again, we need it here otherwise splitlines
74         # will filter the extra line.
75         doc = doc.replace("<br>", "\n<br>\n")
76         # a dirty trick to go to the next line in Doxyfile documentation.
77         # <br/> will be removed later on again, we need it here otherwise splitlines
78         # will filter the line break.
79         doc = doc.replace("<br/>", "\n<br/>\n")
80         #
81         doc = doc.splitlines()
82         split_doc = []
83         for line in doc:
84                 split_doc += textwrap.wrap(line, 78)
85         # replace \ by \\, replace " by \", and '  ' by a newline with end string
86         # and start string at next line
87         docC = []
88         for line in split_doc:
89                 if (line.strip() != "<br/>"):
90                         docC.append(line.strip().replace('\\', '\\\\').
91                                         replace('"', '\\"').replace("<br>", ""))
92         return docC
93
94
95 def collectValues(node):
96         values = []
97         for n in node.childNodes:
98                 if (n.nodeName == "value"):
99                         if n.nodeType == Node.ELEMENT_NODE:
100                                 if n.getAttribute('name') != "":
101                                         if n.getAttribute('show_docu') != "NO":
102                                                 name = "<code>" + n.getAttribute('name') + "</code>"
103                                                 desc = n.getAttribute('desc')
104                                                 if (desc != ""):
105                                                         name += " " + desc
106                                                 values.append(name)
107         return values
108
109
110 def addValues(var, node):
111         for n in node.childNodes:
112                 if (n.nodeName == "value"):
113                         if n.nodeType == Node.ELEMENT_NODE:
114                                 name = n.getAttribute('name')
115                                 print "  %s->addValue(\"%s\");" % (var, name)
116
117
118 def parseHeader(node,objName):
119         doc = ""
120         for n in node.childNodes:
121                 if n.nodeType == Node.ELEMENT_NODE:
122                         if (n.nodeName == "docs"):
123                                 if (n.getAttribute('doxyfile') != "0"):
124                                         doc += parseDocs(n)
125         docC = transformDocs(doc)
126         print "  %s->setHeader(" % (objName)
127         rng = len(docC)
128         for i in range(rng):
129                 line = docC[i]
130                 if i != rng - 1:  # since we go from 0 to rng-1
131                         print "              \"%s\\n\"" % (line)
132                 else:
133                         print "              \"%s\"" % (line)
134         print "             );"
135
136
137 def prepCDocs(node):
138         type = node.getAttribute('type')
139         format = node.getAttribute('format')
140         defval = node.getAttribute('defval')
141         adefval = node.getAttribute('altdefval')
142         doc = "";
143         if (type != 'obsolete'):
144                 for n in node.childNodes:
145                         if (n.nodeName == "docs"):
146                                 if (n.getAttribute('doxyfile') != "0"):
147                                         if n.nodeType == Node.ELEMENT_NODE:
148                                                 doc += parseDocs(n)
149                 if (type == 'enum'):
150                         values = collectValues(node)
151                         doc += "<br/>Possible values are: "
152                         rng = len(values)
153                         for i in range(rng):
154                                 val = values[i]
155                                 if i == rng - 2:
156                                         doc += "%s and " % (val)
157                                 elif i == rng - 1:
158                                         doc += "%s." % (val)
159                                 else:
160                                         doc += "%s, " % (val)
161                         if (defval != ""):
162                                 doc += "<br/>The default value is: <code>%s</code>." % (defval)
163                 elif (type == 'int'):
164                         minval = node.getAttribute('minval')
165                         maxval = node.getAttribute('maxval')
166                         doc += "<br/>%s: %s, %s: %s, %s: %s." % (" Minimum value", minval, 
167                                          "maximum value", maxval,
168                                          "default value", defval)
169                 elif (type == 'bool'):
170                         if (node.hasAttribute('altdefval')):
171                           doc += "<br/>%s: %s." % ("The default value is", "system dependent")
172                         else:
173                           doc += "<br/>%s: %s." % ("The default value is", "YES" if (defval == "1") else "NO")
174                 elif (type == 'list'):
175                         if format == 'string':
176                                 values = collectValues(node)
177                                 rng = len(values)
178                                 for i in range(rng):
179                                         val = values[i]
180                                         if i == rng - 2:
181                                                 doc += "%s and " % (val)
182                                         elif i == rng - 1:
183                                                 doc += "%s." % (val)
184                                         else:
185                                                 doc += "%s, " % (val)
186                 elif (type == 'string'):
187                         if format == 'dir':
188                                 if defval != '':
189                                         doc += "<br/>The default directory is: <code>%s</code>." % (
190                                                 defval)
191                         elif format == 'file':
192                                 abspath = node.getAttribute('abspath')
193                                 if defval != '':
194                                         if abspath != '1':
195                                                 doc += "<br/>The default file is: <code>%s</code>." % (
196                                                         defval)
197                                         else:
198                                                 doc += "<br/>%s: %s%s%s." % (
199                                                         "The default file (with absolute path) is",
200                                                         "<code>",defval,"</code>")
201                                 else:
202                                         if abspath == '1':
203                                                 doc += "<br/>The file has to be specified with full path."
204                         elif file =='image':
205                                 abspath = node.getAttribute('abspath')
206                                 if defval != '':
207                                         if abspath != '1':
208                                                 doc += "<br/>The default image is: <code>%s</code>." % (
209                                                         defval)
210                                         else:
211                                                 doc += "<br/>%s: %s%s%s." % (
212                                                         "The default image (with absolute path) is",
213                                                         "<code>",defval,"</code>")
214                                 else:
215                                         if abspath == '1':
216                                                 doc += "<br/>The image has to be specified with full path."
217                         else: # format == 'string':
218                                 if defval != '':
219                                         doc += "<br/>The default value is: <code>%s</code>." % (
220                                                 defval)
221                 # depends handling
222                 if (node.hasAttribute('depends')):
223                         depends = node.getAttribute('depends')
224                         doc += "<br/>%s \\ref cfg_%s \"%s\" is set to \\c YES." % (
225                                 "This tag requires that the tag", depends.lower(), depends.upper())
226
227         docC = transformDocs(doc)
228         return docC;
229
230 def parseOption(node):
231         # Handling part for Doxyfile
232         name = node.getAttribute('id')
233         type = node.getAttribute('type')
234         format = node.getAttribute('format')
235         defval = node.getAttribute('defval')
236         adefval = node.getAttribute('altdefval')
237         depends = node.getAttribute('depends')
238         setting = node.getAttribute('setting')
239         docC = prepCDocs(node);
240         if len(setting) > 0:
241                 print "#if %s" % (setting)
242         print "  //----"
243         if type == 'bool':
244                 if len(adefval) > 0:
245                         enabled = adefval
246                 elif defval == '1':
247                         enabled = "TRUE"
248                 else:
249                         enabled = "FALSE"
250                 print "  cb = cfg->addBool("
251                 print "             \"%s\"," % (name)
252                 rng = len(docC)
253                 for i in range(rng):
254                         line = docC[i]
255                         if i != rng - 1:  # since we go from 0 to rng-1
256                                 print "              \"%s\\n\"" % (line)
257                         else:
258                                 print "              \"%s\"," % (line)
259                 print "              %s" % (enabled)
260                 print "             );"
261                 if depends != '':
262                         print "  cb->addDependency(\"%s\");" % (depends)
263         elif type == 'string':
264                 print "  cs = cfg->addString("
265                 print "              \"%s\"," % (name)
266                 rng = len(docC)
267                 for i in range(rng):
268                         line = docC[i]
269                         if i != rng - 1:  # since we go from 0 to rng-1
270                                 print "              \"%s\\n\"" % (line)
271                         else:
272                                 print "              \"%s\"" % (line)
273                 print "             );"
274                 if defval != '':
275                         print "  cs->setDefaultValue(\"%s\");" % (defval)
276                 if format == 'file':
277                         print "  cs->setWidgetType(ConfigString::File);"
278                 elif format == 'image':
279                         print "  cs->setWidgetType(ConfigString::Image);"
280                 elif format == 'dir':
281                         print "  cs->setWidgetType(ConfigString::Dir);"
282                 if depends != '':
283                         print "  cs->addDependency(\"%s\");" % (depends)
284         elif type == 'enum':
285                 print "  ce = cfg->addEnum("
286                 print "              \"%s\"," % (name)
287                 rng = len(docC)
288                 for i in range(rng):
289                         line = docC[i]
290                         if i != rng - 1:  # since we go from 0 to rng-1
291                                 print "              \"%s\\n\"" % (line)
292                         else:
293                                 print "              \"%s\"," % (line)
294                 print "              \"%s\"" % (defval)
295                 print "             );"
296                 addValues("ce", node)
297                 if depends != '':
298                         print "  ce->addDependency(\"%s\");" % (depends)
299         elif type == 'int':
300                 minval = node.getAttribute('minval')
301                 maxval = node.getAttribute('maxval')
302                 print "  ci = cfg->addInt("
303                 print "              \"%s\"," % (name)
304                 rng = len(docC)
305                 for i in range(rng):
306                         line = docC[i]
307                         if i != rng - 1:  # since we go from 0 to rng-1
308                                 print "              \"%s\\n\"" % (line)
309                         else:
310                                 print "              \"%s\"," % (line)
311                 print "              %s,%s,%s" % (minval, maxval, defval)
312                 print "             );"
313                 if depends != '':
314                         print "  ci->addDependency(\"%s\");" % (depends)
315         elif type == 'list':
316                 print "  cl = cfg->addList("
317                 print "              \"%s\"," % (name)
318                 rng = len(docC)
319                 for i in range(rng):
320                         line = docC[i]
321                         if i != rng - 1:  # since we go from 0 to rng-1
322                                 print "              \"%s\\n\"" % (line)
323                         else:
324                                 print "              \"%s\"" % (line)
325                 print "             );"
326                 addValues("cl", node)
327                 if depends != '':
328                         print "  cl->addDependency(\"%s\");" % (depends)
329                 if format == 'file':
330                         print "  cl->setWidgetType(ConfigList::File);"
331                 elif format == 'dir':
332                         print "  cl->setWidgetType(ConfigList::Dir);"
333                 elif format == 'filedir':
334                         print "  cl->setWidgetType(ConfigList::FileAndDir);"
335         elif type == 'obsolete':
336                 print "  cfg->addObsolete(\"%s\");" % (name)
337         if len(setting) > 0:
338                 print "#else"
339                 print "  cfg->addDisabled(\"%s\");" % (name)
340                 print "#endif"
341
342
343 def parseGroups(node):
344         name = node.getAttribute('name')
345         doc = node.getAttribute('docs')
346         print "%s%s" % ("  //-----------------------------------------",
347                                         "----------------------------------")
348         print "  cfg->addInfo(\"%s\",\"%s\");" % (name, doc)
349         print "%s%s" % ("  //-----------------------------------------",
350                                         "----------------------------------")
351         print
352         for n in node.childNodes:
353                 if n.nodeType == Node.ELEMENT_NODE:
354                         parseOption(n)
355
356 def parseGroupCDocs(node):
357         for n in node.childNodes:
358                 if n.nodeType == Node.ELEMENT_NODE:
359                         type = n.getAttribute('type')
360                         name = n.getAttribute('id')
361                         docC = prepCDocs(n);
362                         if type != 'obsolete':
363                                 print "  doc->add("
364                                 print "              \"%s\"," % (name)
365                                 rng = len(docC)
366                                 for i in range(rng):
367                                         line = docC[i]
368                                         if i != rng - 1:  # since we go from 0 to rng-1
369                                                 print "              \"%s\\n\"" % (line)
370                                         else:
371                                                 print "              \"%s\"" % (line)
372                                 print "          );"
373
374 def parseOptionDoc(node, first):
375         # Handling part for documentation
376         name = node.getAttribute('id')
377         type = node.getAttribute('type')
378         format = node.getAttribute('format')
379         defval = node.getAttribute('defval')
380         adefval = node.getAttribute('altdefval')
381         depends = node.getAttribute('depends')
382         setting = node.getAttribute('setting')
383         doc = ""
384         if (type != 'obsolete'):
385                 for n in node.childNodes:
386                         if (n.nodeName == "docs"):
387                                 if (n.getAttribute('documentation') != "0"):
388                                         if n.nodeType == Node.ELEMENT_NODE:
389                                                 doc += parseDocs(n)
390                 if (first):
391                         print " \\anchor cfg_%s" % (name.lower())
392                         print "<dl>"
393                         print ""
394                         print "<dt>\\c %s <dd>" % (name)
395                 else:
396                         print " \\anchor cfg_%s" % (name.lower())
397                         print "<dt>\\c %s <dd>" % (name)
398                 print " \\addindex %s" % (name)
399                 print doc
400                 if (type == 'enum'):
401                         values = collectValues(node)
402                         print ""
403                         print "Possible values are: "
404                         rng = len(values)
405                         for i in range(rng):
406                                 val = values[i]
407                                 if i == rng - 2:
408                                         print "%s and " % (val)
409                                 elif i == rng - 1:
410                                         print "%s." % (val)
411                                 else:
412                                         print "%s, " % (val)
413                         if (defval != ""):
414                                 print ""
415                                 print ""
416                                 print "The default value is: <code>%s</code>." % (defval)
417                         print ""
418                 elif (type == 'int'):
419                         minval = node.getAttribute('minval')
420                         maxval = node.getAttribute('maxval')
421                         print ""
422                         print ""
423                         print "%s: %s%s%s, %s: %s%s%s, %s: %s%s%s." % (
424                                          " Minimum value", "<code>", minval, "</code>", 
425                                          "maximum value", "<code>", maxval, "</code>",
426                                          "default value", "<code>", defval, "</code>")
427                         print ""
428                 elif (type == 'bool'):
429                         print ""
430                         print ""
431                         if (node.hasAttribute('altdefval')):
432                                 print "The default value is: system dependent."
433                         else:
434                                 print "The default value is: <code>%s</code>." % (
435                                         "YES" if (defval == "1") else "NO")
436                         print ""
437                 elif (type == 'list'):
438                         if format == 'string':
439                                 values = collectValues(node)
440                                 rng = len(values)
441                                 for i in range(rng):
442                                         val = values[i]
443                                         if i == rng - 2:
444                                                 print "%s and " % (val)
445                                         elif i == rng - 1:
446                                                 print "%s." % (val)
447                                         else:
448                                                 print "%s, " % (val)
449                         print ""
450                 elif (type == 'string'):
451                         if format == 'dir':
452                                 if defval != '':
453                                         print ""
454                                         print "The default directory is: <code>%s</code>." % (
455                                                 defval)
456                         elif format == 'file':
457                                 abspath = node.getAttribute('abspath')
458                                 if defval != '':
459                                         print ""
460                                         if abspath != '1':
461                                                 print "The default file is: <code>%s</code>." % (
462                                                         defval)
463                                         else:
464                                                 print "%s: %s%s%s." % (
465                                                         "The default file (with absolute path) is",
466                                                         "<code>",defval,"</code>")
467                                 else:
468                                         if abspath == '1':
469                                                 print ""
470                                                 print "The file has to be specified with full path."
471                         elif file =='image':
472                                 abspath = node.getAttribute('abspath')
473                                 if defval != '':
474                                         print ""
475                                         if abspath != '1':
476                                                 print "The default image is: <code>%s</code>." % (
477                                                         defval)
478                                         else:
479                                                 print "%s: %s%s%s." % (
480                                                         "The default image (with absolute path) is",
481                                                         "<code>",defval,"</code>")
482                                 else:
483                                         if abspath == '1':
484                                                 print ""
485                                                 print "The image has to be specified with full path."
486                         else: # format == 'string':
487                                 if defval != '':
488                                         print ""
489                                         print "The default value is: <code>%s</code>." % (
490                                                 defval)
491                         print ""
492                 # depends handling
493                 if (node.hasAttribute('depends')):
494                         depends = node.getAttribute('depends')
495                         print ""
496                         print "%s \\ref cfg_%s \"%s\" is set to \\c YES." % (
497                                 "This tag requires that the tag", depends.lower(), depends.upper())
498                 return False
499
500
501 def parseGroupsDoc(node):
502         name = node.getAttribute('name')
503         doc = node.getAttribute('docs')
504         print "\section config_%s %s" % (name.lower(), doc)
505         # Start of list has been moved to the first option for better
506         # anchor placement
507         #  print "<dl>"
508         #  print ""
509         first = True
510         for n in node.childNodes:
511                 if n.nodeType == Node.ELEMENT_NODE:
512                         first = parseOptionDoc(n, first)
513         if (not first):
514                 print "</dl>"
515
516
517 def parseGroupsList(node, commandsList):
518         list = ()
519         for n in node.childNodes:
520                 if n.nodeType == Node.ELEMENT_NODE:
521                         type = n.getAttribute('type')
522                         if type != 'obsolete':
523                                 commandsList = commandsList + (n.getAttribute('id'),)
524         return commandsList
525
526
527 def parseDocs(node):
528         doc = ""
529         for n in node.childNodes:
530                 if n.nodeType == Node.TEXT_NODE:
531                         doc += n.nodeValue.strip()
532                 if n.nodeType == Node.CDATA_SECTION_NODE:
533                         doc += n.nodeValue.rstrip("\r\n ").lstrip("\r\n")
534         #doc += "<br>"
535         return doc
536
537
538 def parseHeaderDoc(node):
539         doc = ""
540         for n in node.childNodes:
541                 if n.nodeType == Node.ELEMENT_NODE:
542                         if (n.nodeName == "docs"):
543                                 if (n.getAttribute('documentation') != "0"):
544                                         doc += parseDocs(n)
545         print doc
546
547
548 def parseFooterDoc(node):
549         doc = ""
550         for n in node.childNodes:
551                 if n.nodeType == Node.ELEMENT_NODE:
552                         if (n.nodeName == "docs"):
553                                 if (n.getAttribute('documentation') != "0"):
554                                         doc += parseDocs(n)
555         print doc
556
557
558 def main():
559         if len(sys.argv)<3 or (not sys.argv[1] in ['-doc','-cpp','-wiz']):
560                 sys.exit('Usage: %s -doc|-cpp|-wiz config.xml' % sys.argv[0])
561         try:
562                 doc = xml.dom.minidom.parse(sys.argv[2])
563         except Exception as inst:
564                 print >> sys.stderr
565                 print >> sys.stderr, inst
566                 print >> sys.stderr
567                 sys.exit(1)
568         elem = doc.documentElement
569         if (sys.argv[1] == "-doc"):
570                 print "/* WARNING: This file is generated!"
571                 print " * Do not edit this file, but edit config.xml instead and run"
572                 print " * python configgen.py -doc config.xml to regenerate this file!"
573                 print " */"
574                 # process header
575                 for n in elem.childNodes:
576                         if n.nodeType == Node.ELEMENT_NODE:
577                                 if (n.nodeName == "header"):
578                                         parseHeaderDoc(n)
579                 # generate list with all commands
580                 commandsList = ()
581                 for n in elem.childNodes:
582                         if n.nodeType == Node.ELEMENT_NODE:
583                                 if (n.nodeName == "group"):
584                                         commandsList = parseGroupsList(n, commandsList)
585                 print "\\secreflist"
586                 for x in sorted(commandsList):
587                         print "\\refitem cfg_%s %s" % (x.lower(), x)
588                 print "\\endsecreflist"
589                 # process groups and options
590                 for n in elem.childNodes:
591                         if n.nodeType == Node.ELEMENT_NODE:
592                                 if (n.nodeName == "group"):
593                                         parseGroupsDoc(n)
594                 # process footers
595                 for n in elem.childNodes:
596                         if n.nodeType == Node.ELEMENT_NODE:
597                                 if (n.nodeName == "footer"):
598                                         parseFooterDoc(n)
599         elif (sys.argv[1] == "-cpp"):
600                 print "/* WARNING: This file is generated!"
601                 print " * Do not edit this file, but edit config.xml instead and run"
602                 print " * python configgen.py -cpp config.xml to regenerate this file!"
603                 print " */"
604                 print ""
605                 print "#include \"configoptions.h\""
606                 print "#include \"config.h\""
607                 print "#include \"portable.h\""
608                 print "#include \"settings.h\""
609                 print ""
610                 print "void addConfigOptions(Config *cfg)"
611                 print "{"
612                 print "  ConfigString *cs;"
613                 print "  ConfigEnum   *ce;"
614                 print "  ConfigList   *cl;"
615                 print "  ConfigInt    *ci;"
616                 print "  ConfigBool   *cb;"
617                 print ""
618                 # process header
619                 for n in elem.childNodes:
620                         if n.nodeType == Node.ELEMENT_NODE:
621                                 if (n.nodeName == "header"):
622                                         parseHeader(n,'cfg')
623                 for n in elem.childNodes:
624                         if n.nodeType == Node.ELEMENT_NODE:
625                                 if (n.nodeName == "group"):
626                                         parseGroups(n)
627                 print "}"
628         elif (sys.argv[1] == "-wiz"):
629                 print "/* WARNING: This file is generated!"
630                 print " * Do not edit this file, but edit config.xml instead and run"
631                 print " * python configgen.py -wiz config.xml to regenerate this file!"
632                 print " */"
633                 print "#include \"configdoc.h\""
634                 print "#include \"docintf.h\""
635                 print ""
636                 print "void addConfigDocs(DocIntf *doc)"
637                 print "{"
638                 for n in elem.childNodes:
639                         if n.nodeType == Node.ELEMENT_NODE:
640                                 if (n.nodeName == "header"):
641                                         parseHeader(n,'doc')
642                 for n in elem.childNodes:
643                         if n.nodeType == Node.ELEMENT_NODE:
644                                 if (n.nodeName == "group"):
645                                         parseGroupCDocs(n)
646                 print "}"
647
648 if __name__ == '__main__':
649         main()