1package com.mot.dm.dbtool;
2
3import java.util.*;
4import java.io.*;
5
6public class Generator {
7
8  public static boolean REMOVE_WORKING_DIR = true;
9  public static boolean USE_ZIP = true;
10  public static boolean IS_INPUT_TXT_FILE;
11  public static String ENCODING; // UTF-8 for csv and UTF-16 for txt
12
13  public static String pathMMSFile = null;
14  public static String pathBrowserFile = null;
15  public static String pathJavaAppFile = null;
16  public static String pathIMFile = null;
17
18  public static ArrayList arrMMSLines = new ArrayList();
19  public static ArrayList arrBrowserLines = new ArrayList();
20  public static ArrayList arrJavaAppLines = new ArrayList();
21  public static ArrayList arrIMLines = new ArrayList();
22
23  //for one combine primary key for all settings
24  public static HashMap hashXmlRecords = new HashMap();
25  public static Record[] arraySortedRecords;
26  public static StringBuffer operatorsNamesBuffer = new StringBuffer();
27  public static ByteArray operatorsNamesBytes = new ByteArray();
28
29  //### tables
30  public static BlockTable operatorsNamesTable;
31  public static BlockTable applicationsSettingsTable;
32  public static BlockTable carrierIndexTable;
33  public static BlockTable headerTable;
34
35  public void proceed() throws Exception {
36    Util.deleteDir(Const.WORKING_DIR);
37    Util.deleteFile(Const.DB_FILE_NAME);
38    Util.deleteFile(Const.ERROR_FILE_NAME);
39
40    if (! (new File(Const.WORKING_DIR)).mkdir()) {
41      throw new Exception(
42          "Error!  Cannot create working directory for xml, wbxml and zip files");
43    }
44
45    fillArrLines();
46    validateArrLines();
47    createXmlMessages();
48    Util.writeRecordsToFiles();
49    Util.sortAllRecordsIntoArray();
50    genenerateOperatorsNamesTable();
51    Arrays.sort(arraySortedRecords); //re-sort again including newlly generated names offset
52    // Util.printRecords(); // for debugging !!!!!!!!!!!!!
53    genenerateApplicationsSettingsTable();
54    genenerateCarrierIndexTable();
55    genenerateHeaderTable();
56    Validator.postValidate();
57    writeTablesToFile();
58
59    //clean working directory
60    if (REMOVE_WORKING_DIR) {
61      Util.deleteDir(Const.WORKING_DIR);
62    }
63
64    /// ------------- test --------------
65    // Util.printRecords();
66  }
67
68  public void validateArrLines() throws Exception {
69
70    String err = "";
71    err += Worker.validateAndCreateMMSs(arrMMSLines);
72    err += Worker.validateAndCreateBrowsers(arrBrowserLines);
73    err += Worker.validateAndCreateJavaApps(arrJavaAppLines);
74    err += Worker.validateAndCreateIMs(arrIMLines);
75
76    if (err.length() > 0) {
77      throw new Exception(err);
78    }
79  }
80
81  public void createXmlMessages() throws Exception {
82    Browser.createXmlMessages();
83    MMS.createXmlMessages();
84    JavaApp.createXmlMessages();
85
86    // IM should be last to generate id_name for all other applications!!!
87    // set flag to avoid duplicationNAP names before calling createXmlMessages()
88    // The method setNAPFlagForIMAndValidate() should be changed for new  applications
89    Validator.setNAPFlagForIMAndValidate();
90    IM.createXmlMessages();
91  }
92
93  public void fillArrLines() throws Exception {
94    if (pathMMSFile != null) {
95      arrMMSLines = Util.readFile(pathMMSFile);
96    }
97    if (pathBrowserFile != null) {
98      arrBrowserLines = Util.readFile(pathBrowserFile);
99    }
100    if (pathJavaAppFile != null) {
101      arrJavaAppLines = Util.readFile(pathJavaAppFile);
102    }
103    if (pathIMFile != null) {
104      arrIMLines = Util.readFile(pathIMFile);
105    }
106  }
107
108  // set offset for Applications Settings for each record and generate Applications Setting Table
109  public void genenerateApplicationsSettingsTable() throws Exception {
110    int totalDataLength = 0;
111    String err = ""; // collected all errors for setBlockApplicationsForOneRecord();
112
113    // set offset and binary data for Applications Settings for each record
114    for (int i = 0; i < arraySortedRecords.length; i++) {
115      try {
116        arraySortedRecords[i].application_settings_offset = totalDataLength;
117        int currDataLength = setBlockApplicationsForOneRecord(
118            arraySortedRecords[
119            i]);
120        totalDataLength += currDataLength;
121      }
122      catch (Exception ex) {
123        err += ex.getMessage() + "\n";
124      }
125    }
126    if (err.length() > 0) {
127      throw new Exception(err);
128    }
129
130    // create appl settings table
131    byte[] b;
132    applicationsSettingsTable = new BlockTable(totalDataLength);
133    for (int i = 0; i < arraySortedRecords.length; i++) {
134      b = arraySortedRecords[i].application_settings;
135      applicationsSettingsTable.appendData(b);
136      arraySortedRecords[i].application_settings = null; // release memory !!!!
137
138      ////////////////-- test --///////////////////
139      /* int iii = ( ( ( (int) b[0]) & 0xff) << 16) +
140           ( ( ( (int) b[1]) & 0xff) << 8) + ( ( (int) b[2]) & 0xff);
141       System.out.println(arraySortedRecords[i].key.replaceAll(":", "_") +
142                          " :  " + b.length + "  iii= " + (iii >>> 1) +
143                          "  flag: " + (iii & 1));*/
144      /////////////////////-- end test-- ///////////////////
145
146    }
147
148    Validator.validateMaxAppsSettingsLength(applicationsSettingsTable.length);
149    int lastOffset = arraySortedRecords[arraySortedRecords.length -
150        1].application_settings_offset;
151    Validator.validateMaxAppsSettingsOffset(lastOffset);
152  }
153
154  // Choose between wbxml or zip (the smallest one) to be used;
155  // adds 3 additional bytes with metadata and set binary data for the record
156  // returns length for the application settings + 3 bytes
157  public int setBlockApplicationsForOneRecord(Record record) throws Exception {
158    String key = record.key;
159    //String generalPath = Const.WORKING_DIR + Const.PATH_SEP +
160    //    key.replaceAll(":", "_");
161    String generalPath = Const.WORKING_DIR + Const.PATH_SEP +
162        Util.generateFileNameFromKey(key);
163
164    File w = new File(generalPath + ".wbxml");
165    if (!w.exists()) {
166      throw new Exception("Error: The file " + generalPath +
167                          ".wbxml doesn't exist!");
168    }
169
170    FileInputStream in;
171    boolean usedZip;
172    // Choose between wbxml or zip (the smallest one) to be used in case if USE_ZIP
173    // has not been set to false in function main().
174    if (USE_ZIP) { //zip wbxml files
175      File z = new File(generalPath + ".zip");
176      if (!z.exists()) {
177        throw new Exception("Error: The file " + generalPath +
178                            ".zip doesn't exist!");
179      }
180      FileInputStream inw = new FileInputStream(w);
181      FileInputStream inz = new FileInputStream(z);
182      usedZip = inz.available() < inw.available();
183      in = (usedZip) ? inz : inw;
184    }
185    else { // do not zip wbxml files
186      usedZip = false;
187      in = new FileInputStream(w);
188    }
189
190    byte[] appWbxmlSettings = new byte[in.available()];
191    in.read(appWbxmlSettings);
192    in.close();
193
194    //get 3 bytes that provide info: for data length (23 bits) and compressed flag (1 bit)
195    //and write it into metaInfo
196    BlockTable metaInfo = new BlockTable( (Const.APP_BLOCK_LENGTH +
197                                           Const.APP_COMPRESSWD_FLAG) / 8); // 3 bytes
198    int appBlockSettingsLength = metaInfo.data.length + appWbxmlSettings.length;
199    int compressFlag = (usedZip) ? 1 : 0;
200    Util.addBitsToTable(metaInfo, appBlockSettingsLength,
201                        Const.APP_BLOCK_LENGTH);
202    Util.addBitsToTable(metaInfo, compressFlag,
203                        Const.APP_COMPRESSWD_FLAG);
204    record.setBlockApplicationSettings(metaInfo.data, appWbxmlSettings);
205
206    Validator.validateAppBlockSettingsLength(appBlockSettingsLength,
207                                             generalPath);
208
209    return appBlockSettingsLength;
210  }
211
212  // generate operators names table with name length. assign offset for each record
213  public static void genenerateOperatorsNamesTable() throws Exception {
214    //operatorsNamesBytes
215    HashMap hashNames = new HashMap();
216    String name;
217
218    for (int i = 0; i < arraySortedRecords.length; i++) {
219      name = arraySortedRecords[i].operator_name;
220
221      if (hashNames.containsKey(name)) {
222        Integer offset = (Integer) hashNames.get(name);
223        arraySortedRecords[i].operator_name_offset = offset.intValue();
224      }
225      else {
226
227        byte[] nameByteArr = name.getBytes();
228        // since operatorsNamesBytes.length are always even...
229        int half_offset = operatorsNamesBytes.length() / 2;
230        Validator.validateOperatorNameOffset(half_offset); //validate half_offset
231        arraySortedRecords[i].operator_name_offset = half_offset;
232
233        int nameLength = nameByteArr.length + 1; //length of name  + one byte to present this length.
234        operatorsNamesBytes.addByte( (byte) nameLength);
235        operatorsNamesBytes.addBytes(nameByteArr);
236        if ( (operatorsNamesBytes.length() % 2) > 0) {
237          //add one dummy byte to make it even
238          operatorsNamesBytes.addByte( (byte) 0);
239        }
240        hashNames.put(name, new Integer(half_offset));
241      }
242    }
243
244    //create operators Names Table
245    operatorsNamesTable = new BlockTable(operatorsNamesBytes.length());
246    operatorsNamesTable.appendData(operatorsNamesBytes.getBytes());
247
248    Validator.validateOperatorNamesTableSize(operatorsNamesTable.length);
249
250  }
251
252  /*
253//generate operators names table with name length. assign offset for each record
254    public static void genenerateOperatorsNamesTable() throws Exception {
255      HashMap hashNames = new HashMap();
256      String name;
257      boolean needAddDummyChar;
258      for (int i = 0; i < arraySortedRecords.length; i++) {
259        needAddDummyChar = false;
260        name = arraySortedRecords[i].operator_name;
261
262        if (hashNames.containsKey(name)) {
263          Integer offset = (Integer) hashNames.get(name);
264          arraySortedRecords[i].operator_name_offset = offset.intValue();
265        }
266        else {
267          //length of name should be odd since we need to addd one byte which will
268          //present this length. so length of name + one byte will be even.
269          //then offset will be stored as half  offset/2
270
271   int half_offset = operatorsNamesBuffer.toString().getBytes(ENCODING).length / 2;
272          arraySortedRecords[i].operator_name_offset = half_offset;
273          int nameLength = name.getBytes(ENCODING).length;
274          if((nameLength % 2) == 0){
275            //add one dummy char to make it odd
276            needAddDummyChar = true;
277            nameLength++;
278          }
279         // char c = (char) (name.getBytes().length + 1); //length of name  + one byte to present this length.
280          char c = (char) (nameLength + 1); //length of name  + one byte to present this length.
281          operatorsNamesBuffer.append(c);
282          operatorsNamesBuffer.append(name);
283          if(needAddDummyChar){
284            operatorsNamesBuffer.append(0);
285          }
286          hashNames.put(name, new Integer(half_offset));
287        }
288        //validate real offset (half_offset * 2)
289        Validator.validateOperatorNameOffset(arraySortedRecords[i].operator_name_offset * 2);
290      }
291
292      //create operators Names Table
293      operatorsNamesTable = new BlockTable(operatorsNamesBuffer.toString().
294                                           getBytes(ENCODING).length);
295      operatorsNamesTable.appendData(operatorsNamesBuffer.toString().getBytes(ENCODING));
296
297      Validator.validateOperatorNamesTableSize(operatorsNamesTable.length);
298
299    }
300   */
301
302// generate carrier index table
303  public static void genenerateCarrierIndexTable() throws Exception {
304    //get size for all records and init carrierIndexTable
305    int recordLengthBits = Const.MCC_BITS + Const.MNC_BITS +
306        Const.OPERATOR_NAME_OFFSET_BITS + Const.ACC_TYPE_BITS +
307        Const.APP_AVAIL_BITMAP_BITS + Const.APP_SETTINGS_OFFSET_BITS;
308    int oneRecordLengthBytes = (recordLengthBits + 7) / 8;
309    int tableLengthBytes = oneRecordLengthBytes * arraySortedRecords.length;
310    carrierIndexTable = new BlockTable(tableLengthBytes);
311
312    //write data from each record into arrier index table
313    Record record;
314    for (int i = 0; i < arraySortedRecords.length; i++) {
315      record = arraySortedRecords[i];
316      int appAvailBitmap = 0;
317      //set mask for available applications
318      if (record.containsBrowser) {
319        appAvailBitmap |= Const.BROWSER_MASK;
320      }
321      if (record.containsIM) {
322        appAvailBitmap |= Const.IM_MASK;
323      }
324      if (record.containsJavaApp) {
325        appAvailBitmap |= Const.JAVA_MASK;
326      }
327      if (record.containsMMS) {
328        appAvailBitmap |= Const.MMS_MASK;
329      }
330      //find value for mnc length: if mns 3 digits it is 1, otherwise - it is 0
331      int mncLengthBitValue = (record.mncLen == 3) ? 1 : 0;
332
333      Util.addBitsToTable(carrierIndexTable, record.mcc,
334                          Const.MCC_BITS);
335      Util.addBitsToTable(carrierIndexTable, mncLengthBitValue,
336                          Const.MNC_LENGTH_BITS);
337      Util.addBitsToTable(carrierIndexTable, record.mnc,
338                          Const.MNC_BITS);
339      Util.addBitsToTable(carrierIndexTable,
340                          record.operator_name_offset,
341                          Const.OPERATOR_NAME_OFFSET_BITS);
342      Util.addBitsToTable(carrierIndexTable, record.account_type,
343                          Const.ACC_TYPE_BITS);
344      Util.addBitsToTable(carrierIndexTable, appAvailBitmap,
345                          Const.APP_AVAIL_BITMAP_BITS);
346      Util.addBitsToTable(carrierIndexTable,
347                          record.application_settings_offset,
348                          Const.APP_SETTINGS_OFFSET_BITS);
349    }
350  }
351
352//### ==========  generate Header table ============  ###########
353  public static void genenerateHeaderTable() throws Exception {
354    int tableLengthBytes = (
355        Const.HEAD_HEAD_SISE + Const.HEAD_OPERATOR_NAMES_TAB_SISE +
356        Const.HEAD_CARRIER_INDEX_TAB_SISE + Const.HEAD_APP_SETTINGS_TAB_SIZE +
357        Const.HEAD_DB_VERSION + 7) / 8;
358    headerTable = new BlockTable(tableLengthBytes);
359    //write header table
360    Util.addBitsToTable(headerTable, tableLengthBytes,
361                        Const.HEAD_HEAD_SISE);
362    Util.addBitsToTable(headerTable, operatorsNamesTable.length,
363                        Const.HEAD_OPERATOR_NAMES_TAB_SISE);
364    Util.addBitsToTable(headerTable, carrierIndexTable.length,
365                        Const.HEAD_CARRIER_INDEX_TAB_SISE);
366    Util.addBitsToTable(headerTable,
367                        applicationsSettingsTable.length,
368                        Const.HEAD_APP_SETTINGS_TAB_SIZE);
369    Util.addBitsToTable(headerTable, Const.HEAD_DB_VERSION,
370                        Const.HEAD_DB_VERSION_TAB_SIZE);
371  }
372
373//### ==========  write main DB File and all tables ============  ###########
374  public static void writeTablesToFile() throws Exception {
375    // write sepparate files for the future testing
376    Util.writeFile(Const.WORKING_DIR + Const.PATH_SEP + "header.dat",
377                   headerTable.data);
378    Util.writeFile(Const.WORKING_DIR + Const.PATH_SEP + "operators_names.dat",
379                   operatorsNamesTable.data);
380    Util.writeFile(Const.WORKING_DIR + Const.PATH_SEP + "app_settings.dat",
381                   applicationsSettingsTable.data);
382    Util.writeFile(Const.WORKING_DIR + Const.PATH_SEP + "carrier_index.dat",
383                   carrierIndexTable.data);
384    // write main DB file
385    Util.writeDBFile();
386  }
387
388  public static void main(String[] args) {
389    Const.HEAD_DB_VERSION = 1;
390
391    for (int i = 0; i < args.length; i++) {
392      if ("-mms".equals(args[i]) && args.length > i + 1) {
393        pathMMSFile = args[++i];
394        if (! (pathMMSFile.endsWith(".txt") || pathMMSFile.endsWith(".csv"))) {
395          System.out.println(
396              "Wrong input file type with MMS setting. Supported types: .txt or .csv");
397          System.exit(1);
398        }
399      }
400      else if ("-browser".equals(args[i]) && args.length > i + 1) {
401        pathBrowserFile = args[++i];
402        if (! (pathBrowserFile.endsWith(".txt") ||
403               pathBrowserFile.endsWith(".csv"))) {
404          System.out.println(
405              "Wrong input file type with Browser setting. Supported types: .txt or .csv");
406          System.exit(1);
407        }
408      }
409      else if ("-java".equals(args[i]) && args.length > i + 1) {
410        pathJavaAppFile = args[++i];
411        if (! (pathJavaAppFile.endsWith(".txt") ||
412               pathJavaAppFile.endsWith(".csv"))) {
413          System.out.println(
414              "Wrong input file type with Java setting. Supported types: .txt or .csv");
415          System.exit(1);
416        }
417      }
418      else if ("-im".equals(args[i]) && args.length > i + 1) {
419        pathIMFile = args[++i];
420        if (! (pathIMFile.endsWith(".txt") || pathIMFile.endsWith(".csv"))) {
421          System.out.println(
422              "Wrong input file type with IM setting. Supported types: .txt or .csv");
423          System.exit(1);
424        }
425      }
426      else if ("-nozip".equals(args[i])) {
427        USE_ZIP = false;
428      }
429      else if ("-debug".equals(args[i])) {
430        REMOVE_WORKING_DIR = false;
431      }
432      else if ("-dump".equals(args[i]) && args.length == 2) {
433        DBDumper dbdumper = new DBDumper();
434        System.out.println("============== Dumping DB File ============== ");
435        dbdumper.dump(args[i + 1]);
436        System.out.println(
437            "============== End dumping DB File ============== \n");
438        System.exit(0);
439      }
440      else {
441        System.out.println(" Wrong parameters...");
442        usage();
443        System.exit(1);
444      }
445    }
446
447    // validate input files and set file type (IS_INPUT_TXT_FILE)
448    String err = Util.validateAndSetInputParms();
449    if (err != null) {
450      System.out.println(err);
451      usage();
452      System.exit(1);
453    }
454
455    Generator g = new Generator();
456    try {
457      g.proceed();
458      System.out.println(
459          "\n========================================================\n" +
460          "DB generation finished successfully.\n" +
461          "File '" + Const.DB_FILE_NAME + "' has been created.\n" +
462          "========================================================\n");
463    }
464    catch (Exception e) {
465      Util.deleteFile(Const.DB_FILE_NAME);
466
467      System.out.println(
468          "\n===================== Error ===============================");
469      System.out.println(" DB generation failed.\n" +
470                         " Please check file '" + Const.ERROR_FILE_NAME +
471                         "' for more details\n");
472      //System.out.println(e.getMessage());
473      System.out.println(
474          "===========================================================\n");
475      //e.printStackTrace();
476      try {
477        Util.writeFile(Const.ERROR_FILE_NAME, e.getMessage());
478      }
479      catch (Exception ex) {
480        ex.printStackTrace();
481      }
482    }
483  }
484
485  public static void usage() {
486    System.out.println("\n usage:\n" +
487                       "java com.mot.dm.dbtool.Generator [-mms <path>] [-browser <path>] [-java <path>] [-im <path>] \n" +
488                       "or for test\n" +
489                       "java com.mot.dm.dbtool.Generator -dump <path>/" +
490                       Const.DB_FILE_NAME
491                       );
492  }
493}
494