Remove un-necessary PRINT macro
[mim] / util / converter.py
1 #!/usr/bin/python
2 ########################################################################
3 ##
4 ##  Copyright (C) 2009  MiM
5 ##
6 ##          Contact: Handspring <xhealer@gmail.com>
7 ##
8 ##          AUTHOR: Alsor Zhou <alsor.zhou@gmail.com>
9 ##
10 ##  This file is part of MiM Pinyin.
11 ##
12 ##  This is free software: you can redistribute it and/or modify
13 ##  it under the terms of the GNU General Public License as published by
14 ##  the Free Software Foundation, either version 3 of the License, or
15 ##  (at your option) any later version.
16 ##
17 ##  This is distributed in the hope that it will be useful,
18 ##  but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ##  GNU General Public License for more details.
21 ##
22 ##  You should have received a copy of the GNU General Public License
23 ##  along with Sigil.  If not, see <http://www.gnu.org/licenses/>.
24 ##
25 ########################################################################
26
27 import getopt
28 import sys
29 import os.path
30 import zlib
31
32 DEBUG           = True
33
34 # Global ERROR DEFINATION
35 ERR_PARAMS      = 2
36 ERR_UNKOWN      = 255
37
38 def PRINT(v):
39     '''Print wrapper with debug function supported
40     
41     Never use this function in production (always output) code '''
42     if DEBUG == True:
43         print v
44         
45 def license():
46     '''
47 Copyright (C) 2009  MiM
48
49       Contact: Handspring <xhealer@gmail.com>
50
51       AUTHOR:
52
53 This file is part of MiM Pinyin.
54
55 This is free software: you can redistribute it and/or modify
56 it under the terms of the GNU General Public License as published by
57 the Free Software Foundation, either version 3 of the License, or
58 (at your option) any later version.
59
60 This is distributed in the hope that it will be useful,
61 but WITHOUT ANY WARRANTY; without even the implied warranty of
62 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
63 GNU General Public License for more details.
64
65 You should have received a copy of the GNU General Public License
66 along with Sigil.  If not, see <http://www.gnu.org/licenses/>.
67     '''
68     print license.__doc__
69
70 def usage():
71     '''converter.py [options] SRC [options...DEST]
72     -s SRC  :   specify dictionary source
73     -t DEST :   save converted binary map into DEST
74     -c SRC  :   syntax check SRC, without converstion
75     -d DEST :   generate dummy dictionary bin map
76
77     GNU long option style
78     --source SRC : same with '-s SRC'
79     --target DEST: same with '-t DEST'    
80     --check  SRC : same with '-c SRC'
81     --dummy  DEST: same with '-d DEST'
82     '''
83     print usage.__doc__
84
85 def version():
86     '''MiM pinyin dictionary converter version 0.0.1 Handspring <xhealer@gmail.com>'''
87     print version.__doc__
88     
89 # Target file segmentation layout
90 tgt_delimitor           = "\n"
91 tgt_file_start_index    = 0
92 tgt_header_start_offset = 0
93 tgt_st1_offset          = 100
94 tgt_st2_offset          = 200
95 tgt_table_a_offset      = 300
96 tgt_table_b_offset      = 400
97
98 tgt_global_position_ind = 0
99 # Header definition
100 #  Example header fileds:
101 #    Fn:dictionary.bin\n
102 #    Ver:0.2\n
103 #    Authors:Jackson\n
104 #    ActiveChunkTableFlag:A\n
105 #    ChunkTableAOffset:300
106 #    ChunkTableBOffset:400
107 #    ChunkSize:65535\n
108 #    CRC32:\n
109 tgt_header_delemitor_str = ":"
110 tgt_header_fn_str        = "Fn"
111 tgt_header_version_str   = "Ver"
112 tgt_header_author_str    = "Authors"
113 tgt_header_actf_str      = "ActiveChunkTableFlag"
114 tgt_header_ctao_str      = "ChunkTableAOffset"
115 tgt_header_ctbo_str      = "ChunkTableBOffset"
116 tgt_header_chunk_size_str= "ChunkSize"
117 tgt_header_crc_str       = "CRC32"
118
119 # syllable array definition
120 tgt_sa_seperator         = "," # symbol between syllable word
121 tgt_sa_delimitor         = ":" # symbol between key and value
122
123 # Chunk Table offset
124 tgt_ctable_flag_offset           = 0
125 tgt_ctable_flag_fld_siz          = 2   # bytes
126 tgt_ctable_chk_base_offset       = 2
127 tgt_ctable_chk_base_fld_size     = 2   # bytes, 65535 maximize
128 tgt_ctable_chk_acroyn_fld_size   = 2
129 tgt_ctable_chk_offset_fld_size   = 2
130 tgt_ctable_chk_size_fld_size     = 2
131
132 # Internal function definition
133 def _generate_header(fn, ver, authors, actf, ctao, ctbo, csize):
134     '''Generate target file header.
135     @param fn: file name
136     @param ver: dictionary version
137     @param authors: dictionary authors
138     @param actf: active chunk table flag (A/B) 
139     @param ctao: chunk table A offset
140     @param ctbo: chunk table B offset
141     @param csize: chunk size (fixed)
142     
143     @return header: header string with crc32 computed
144     '''
145     crc32  = None
146     header = None
147     header += tgt_header_fn_str + tgt_header_delemitor_str + fn + tgt_delimitor
148     header += tgt_header_version_str + tgt_header_delemitor_str + ver + tgt_delimitor
149     header += tgt_header_version_str + tgt_header_delemitor_str + authors + tgt_delimitor
150     crc32  = crc32(header); # FIXME: should we crc the timestamp?
151     header += tgt_header_version_str + tgt_header_delemitor_str + actf + tgt_delimitor
152     header += tgt_header_version_str + tgt_header_delemitor_str + ctao + tgt_delimitor
153     header += tgt_header_version_str + tgt_header_delemitor_str + ctbo + tgt_delimitor
154     header += tgt_header_version_str + tgt_header_delemitor_str + csize + tgt_delimitor
155     header += tgt_header_version_str + tgt_header_delemitor_str + crc32
156     
157     PRINT(_generate_header.__doc__)
158     return header
159
160 def _generate_st1_sa(safile):
161     '''Generate static table 1 - Syllabale Array.'''
162     dict_obj = {}
163     
164     saf = file(safile)
165     if (saf == None):
166         return
167         
168     for line in saf:
169         PRINT(line)
170         # format is     {"key1:value1","key2:value2",...,null}
171         sal = saf.split(tgt_sa_seperator)
172         for item in sal:
173             # format is    {key:value}
174             if (item != None): 
175                 dict_obj[item.split(tgt_sa_delimitor)[0]] = item.split(tgt_sa_delimitor)[1]
176             
177     PRINT(dict_obj)
178     PRINT(_generate_st1_sa.__doc__)
179
180 def _generate_st2_cst(cstfile):
181     '''Generate static table 2 - Character-Syllable ID Pair Table.'''
182     PRINT(_generate_st2_cst.__doc__)
183
184 def _generate_ctable_a():
185     '''Chunk Table A generation.
186     Example chunk table:
187     0------------2------------4--------6--------8------10-------12-------14----16
188     | Table flag | Chunk Base | Acroyn | Offset | Size | Acroyn | Offset | Size |  
189     '''
190     PRINT(_generate_ctable_a.__doc__)
191
192 # FIXME: chunk table B holds the same contents with A in file storage?
193 def _generate_ctable_b():
194     '''Chunk Table B generation.
195     Example chunk table:
196     0------------2------------4--------6--------8------10-------12-------14----16
197     | Table flag | Chunk Base | Acroyn | Offset | Size | Acroyn | Offset | Size |  
198     '''
199     PRINT(_generate_ctable_b.__doc__)
200
201 def _generate_dictionary():
202     '''
203     Normally, target data file have only one dictionary map. Data integrity is 
204     guaranteed by a temp chunk at runtime.
205     '''
206     PRINT(_generate_dictionary.__doc__)
207
208 def gen_dummy_dict_binmap():
209     '''Generate dummy dictionary bin map.'''
210     
211     _generate_header("dictionary.bin", "0.2", "Jackson", "A", 300, 400, 65535)
212     _generate_st1_sa("SyllableArraySource.txt")
213     PRINT(gen_dummy_dict_binmap.__doc__)
214
215 def convert(src, dest):
216     '''Convertion from original text format dictionary to binary map.
217     
218     @param  src : text format dictionary
219     @param  dest: binary map dictionary
220     
221     @return None
222     '''
223     PRINT(convert.__doc__)
224
225 def check(src):
226     '''Check syntax format of orignal text format dictionary
227     
228     @param  src : text format dictionary
229     
230     @return True without syntax error, False else.
231     '''
232     PRINT(check.__doc__)
233     
234 def main(argv):
235     '''Main business logic
236     
237     @param  argv : sys.argv[1:]
238     @return error code if any
239     '''
240
241     # handle parameter parse
242     valid_args = "hvVt:c:s:d"
243     valid_long_args = ["help", "version", "source", "target", "check", "dummy"]
244     src = None
245     dest = None
246     
247     try:
248         opts, args = getopt.getopt(argv, valid_args, valid_long_args)
249     except getopt.GetoptError, err:
250         print str(err)
251         license()
252         usage()
253         sys.exit(ERR_PARAMS)
254     output = None
255     verbose = False
256     for o, a in opts:
257         if o in ("-s", "--source"):
258             if a == None:
259                 assert False, "No dictionary source specified"
260                 usage()
261                 sys.exit(ERR_PARAMS)
262             # no dest specified, use same filename as src to store file
263             if dest == None:
264                 basename = os.path.basename(src)
265                 dest = os.path.splitext(basename)[0]
266                 dest = os.path.join(dest, ".bin")
267             src = a
268             convert(src, dest)
269         elif o in ("-t", "--target"):
270             dest = a
271         elif o in ("-d", "--dummy"):
272             gen_dummy_dict_binmap()
273         elif o in ("-c", "--check"):
274             check(a)
275         elif o == "-v":
276             verbose = True
277         elif o in ("-h", "--help"):
278             usage()
279             sys.exit()
280         elif o in ("-V", "--version"):
281             version()
282         else:
283             usage()
284
285 if __name__ == "__main__":
286     main(sys.argv[1:])