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