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 as 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'&\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