xref: /trunk/main/toolkit/src2xml/source/src2xml.py (revision cdf0e10c)
1#!/usr/bin/env python
2
3import getopt
4import os
5import re
6import sys
7#
8from srclexer import SrcLexer
9from srcparser import SrcParser
10from boxer import Boxer
11# FIXME
12from globals import *
13
14def option_parser ():
15    import optparse
16    p = optparse.OptionParser ()
17
18    p.usage = '''src2xml.py [OPTION]... SRC-FILE...'''
19
20    examples = '''
21Examples:
22  src2xml.py --output-dir=. --post-process --ignore-includes zoom.src
23  src2xml.py --dry-run -I svx/inc -I svx/source/dialog zoom.src
24'''
25
26    def format_examples (self):
27        return examples
28
29    if 'epilog' in  p.__dict__:
30        p.formatter.format_epilog = format_examples
31        p.epilog = examples
32    else:
33        p.formatter.format_description = format_examples
34        p.description = examples
35
36    p.description = '''OOo SRC To Layout XML Converter.
37
38Convert OO.o's existing dialog resource files into XML layout files.
39'''
40
41    p.add_option ('-l', '--debug-lexer', action='store_true',
42                  dest='debug_lexer', default=False,
43                  help='debug lexer')
44
45    p.add_option ('-p', '--debug-parser', action='store_true',
46                  dest='debug_parser', default=False,
47                  help='debug parser')
48
49    p.add_option ('-m', '--debug-macro', action='store_true',
50                  dest='debug_macro', default=False,
51                  help='debug macro')
52
53    p.add_option ('-n', '--dry-run', action='store_true',
54                  dest='dry_run', default=False,
55                  help='dry run')
56
57    p.add_option ('-k', '--keep-going', action='store_true',
58                  dest='keep_going', default=False,
59                  help='continue after error')
60
61    p.add_option ('-i', '--ignore-includes', action='store_true',
62                  dest='ignore_includes', default=False,
63                  help='ignore #include directives')
64
65    p.add_option ('-I', '--include-dir', action='append',
66                  dest='include_path',
67                  default=[],
68                  metavar='DIR',
69                  help='append DIR to include path')
70
71    def from_file (option, opt_str, value, parser):
72        lst = getattr (parser.values, option.dest)
73        lst += file (value).read ().split ('\n')
74        setattr (parser.values, option.dest, lst)
75
76    def from_path (option, opt_str, value, parser):
77        lst = getattr (parser.values, option.dest)
78        lst += value.split (':')
79        setattr (parser.values, option.dest, lst)
80
81    # Junk me?
82    p.add_option ('--includes-from-file', action='callback', callback=from_file,
83                  dest='include_path',
84                  default=[],
85                  type='string',
86                  metavar='FILE',
87                  help='append directory list from FILE to include path')
88
89    p.add_option ('--include-path', action='callback', callback=from_path,
90                  dest='include_path',
91                  type='string',
92                  default=[],
93                  metavar='PATH',
94                  help='append PATH to include path')
95
96    p.add_option ('--only-expand-macros', action='store_true',
97                  dest='only_expand_macros', default=False,
98                  help='FIXME: better to say what NOT to expand?')
99
100    p.add_option ('-o', '--output-dir', action='store',
101                  dest='output_dir', default=None,
102                  metavar='DIR',
103                  help='Output to DIR')
104
105    p.add_option ('-s', '--post-process', action='store_true',
106                  dest='post_process', default=False,
107                  help='post process output for use in Layout')
108
109    p.add_option ('--stop-on-header', action='store_true',
110                  dest='stopOnHeader', default=False,
111                  help='FIXME: remove this?')
112
113    return p
114
115
116def convert (file_name, options):
117    progress ("parsing %(file_name)s ..." % locals ())
118    fullpath = os.path.abspath(file_name)
119    if not os.path.isfile(fullpath):
120        error("no such file", exit=True)
121
122    ##options.include_path.append (os.path.dirname (fullpath))
123
124    input = file (fullpath, 'r').read()
125    lexer = SrcLexer(input, fullpath)
126    lexer.expandHeaders = not options.ignore_includes
127    lexer.includeDirs = options.include_path
128    lexer.stopOnHeader = options.stopOnHeader
129    lexer.debugMacro = options.debug_macro
130    if options.debug_lexer:
131        lexer.debug = True
132        lexer.tokenize()
133        progress ("-"*68 + "\n")
134        progress ("** token dump\n")
135        lexer.dumpTokens()
136        progress ("** end of token dump\n")
137        return
138
139    # Tokenize it using lexer
140    lexer.tokenize()
141
142    parser = SrcParser(lexer.getTokens(), lexer.getDefines())
143    parser.only_expand_macros = options.only_expand_macros
144    if options.debug_parser:
145        parser.debug = True
146        root = parser.parse()
147        s = root.dump()
148        return s
149
150    # Parse the tokens.
151    root = parser.parse()
152
153    # Box it, and return the XML tree.
154    root = Boxer(root).layout()
155    output = root.dump()
156    if not options.dry_run:
157        progress ("\n")
158    return output
159
160def dry_one_file (file_name, options):
161    try:
162        str = convert(file_name, options)
163        progress ("  SUCCESS\n")
164    except Exception, e:
165        if options.keep_going:
166            progress ("  FAILED\n")
167        else:
168            import traceback
169            print traceback.format_exc (None)
170            raise e
171
172def post_process (s):
173    """Make output directly usable by layout module."""
174    s = re.sub ('(</?)([a-z]+)-([a-z]+)-([a-z]+)', r'\1\2\3\4', s)
175    s = re.sub ('(</?)([a-z]+)-([a-z]+)', r'\1\2\3', s)
176    s = re.sub ('(<(checkbox|(cancel|help|ignore|ok|push|more|no|radio|reset|retry|yes)button|(fixed(info|text)))[^>]*) text=', r'\1 label=', s)
177    s = re.sub (' (height|width|x|y)="[0-9]*"', '', s)
178    s = re.sub (' (label|text|title)="', r' _\1="', s)
179    s = re.sub ('&([^m][^p]*[^;]*)', r'&amp;\1', s)
180    s = re.sub (' hide="(TRUE|true|1)"', ' show="false"', s)
181
182    s = s.replace ('<modaldialog', '<modaldialog sizeable="true"')
183    s = s.replace (' rid=', ' id=')
184    s = s.replace (' border="true"', ' has_border="true"')
185    s = s.replace (' def-button="true"', ' defbutton="true"')
186    s = s.replace (' drop-down="', ' dropdown="')
187    s = s.replace (' tab-stop="', ' tabstop="')
188    return s
189
190XML_HEADER = '''<?xml version="1.0" encoding="UTF-8"?>
191<!-- This is a template.  i18n translation is not performed in-place;
192     i18n translated XML files are generated from this template by
193     transex3/layout/tralay.  !-->
194'''
195
196def do_one_file (file_name, options):
197    str = XML_HEADER
198    str += convert(file_name, options)
199    str += '\n'
200
201    if options.post_process:
202        str = post_process (str)
203    h = sys.stdout
204    if options.output_dir:
205        base = os.path.basename (file_name)
206        root, ext = os.path.splitext (base)
207        out_name = options.output_dir + '/' + root + '.xml'
208        progress ("writing %(out_name)s ..." % locals ())
209        h = file (out_name, 'w')
210    h.write (str)
211    h.flush ()
212    progress ("\n")
213
214def main ():
215    p = option_parser ()
216    (options, files) = option_parser ().parse_args ()
217    if not files:
218        p.error ("no input files")
219
220    for f in files:
221        if options.dry_run:
222            dry_one_file (f, options)
223        else:
224            do_one_file (f, options)
225
226if __name__ == '__main__':
227    main ()
228