Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //==================================================================================================
     18 //
     19 // Module Name: Xml2WBXml
     20 //
     21 // General Description: Convert XML file to WBXML
     22 //
     23 //==================================================================================================
     24 
     25 package com.mot.dm.core;
     26 
     27 import java.util.*;
     28 import java.io.*;
     29 import javax.xml.parsers.*;
     30 import org.w3c.dom.*;
     31 import org.xml.sax.InputSource;
     32 
     33 public class Xml2WBXml {
     34   HashMap accessMap = null;
     35   HashMap formatMap = null;
     36   ArrayList arrResult = new ArrayList();
     37   String gformat = null;
     38 
     39   public Xml2WBXml() {
     40     initAccessMap();
     41     initFormatMap();
     42     resetArrResult();
     43 
     44   }
     45 
     46   private void resetArrResult() {
     47     //Emit WBXML header with version number, doc ID, charset, and string table.
     48     //['\x01\x01\x6A\x00']
     49     arrResult.clear();
     50     arrResult.add(new Character( (char) 0x01));
     51     arrResult.add(new Character( (char) 0x01));
     52     arrResult.add(new Character( (char) 0x6A));
     53     arrResult.add(new Character( (char) 0x00));
     54   }
     55 
     56   //Access Types.
     57   //TODO: Should add "Local" later...
     58   private void initAccessMap() {
     59     accessMap = new HashMap();
     60     accessMap.put("Add", new Integer(0x01));
     61     accessMap.put("Delete", new Integer(0x02));
     62     accessMap.put("Exec", new Integer(0x04));
     63     accessMap.put("Get", new Integer(0x08));
     64     accessMap.put("Replace", new Integer(0x10));
     65   }
     66 
     67   private void initFormatMap() {
     68     formatMap = new HashMap();
     69     formatMap.put("bin", new Character( (char) 0));
     70     formatMap.put("bool", new Character( (char) 1));
     71     formatMap.put("b64", new Character( (char) 2));
     72     formatMap.put("chr", new Character( (char) 3));
     73     formatMap.put("int", new Character( (char) 4));
     74     formatMap.put("node", new Character( (char) 5));
     75     formatMap.put("null", new Character( (char) 6));
     76     formatMap.put("xml", new Character( (char) 7));
     77     formatMap.put("test", new Character( (char) 9));
     78     formatMap.put("float", new Character( (char) 10));
     79     formatMap.put("date", new Character( (char) 11));
     80     formatMap.put("time", new Character( (char) 12));
     81   }
     82 
     83   private Document getDocument(String filename) throws Exception {
     84     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
     85     factory.setValidating(false);
     86 
     87     // The following if way around (ugly but working) to support chinese chars for DOM parser:
     88     //   - read  file to string
     89     //   - get UTF8 byte array
     90     //   - send this array to xmp parser.
     91 
     92     FileInputStream fis = new FileInputStream(filename);
     93     int x = fis.available();
     94     byte raw_bytes[] = new byte[x];
     95     fis.read(raw_bytes);
     96     String content = new String(raw_bytes);
     97     raw_bytes = null;
     98     byte[] utf8_bytes = content.getBytes("UTF8");
     99 
    100     return factory.newDocumentBuilder().parse(new InputSource(new
    101         ByteArrayInputStream(utf8_bytes)));
    102   }
    103 
    104   public void convert(File file) throws Exception {
    105     resetArrResult();
    106 
    107     ArrayList arrNodes = new ArrayList();
    108     Document document = getDocument(file.getCanonicalPath());
    109     NodeList list;
    110     list = document.getDocumentElement().getChildNodes();
    111 
    112     for (int i = 0; i < list.getLength(); i++) {
    113       Node node = list.item(i);
    114       if (node.getNodeType() == Node.ELEMENT_NODE &&
    115           node.getNodeName().equals("Node")) {
    116         arrNodes.add(node);
    117       }
    118     }
    119 
    120     if (arrNodes.size() > 0) {
    121       this.arrResult.add(new Character( (char) 0x5E));
    122 
    123       for (int i = 0; i < arrNodes.size(); i++) {
    124         Node node = (Node) arrNodes.get(i);
    125         arrResult.addAll(processNode(node));
    126       }
    127       this.arrResult.add(new Character( (char) 0x01));
    128     }
    129     else {
    130       this.arrResult.add(new Character( (char) 0x1E));
    131     }
    132 
    133     // write to file wbxml from arrResult
    134     String wbxmlPath = file.getCanonicalPath();
    135     wbxmlPath = (wbxmlPath.substring(0, wbxmlPath.length() - 3)).concat(
    136         "wbxml");
    137     byte[] arrByte = new byte[arrResult.size()];
    138     //convert chars to bytes
    139     for (int i = 0; i < arrResult.size(); i++) {
    140       char c = ( (Character) arrResult.get(i)).charValue();
    141       arrByte[i] = (byte) c;
    142     }
    143     // write
    144     FileOutputStream binaryMetaDataOut = new FileOutputStream(wbxmlPath);
    145     DataOutputStream bmdfOut = new DataOutputStream(binaryMetaDataOut);
    146     bmdfOut.write(arrByte);
    147     bmdfOut.close();
    148   }
    149 
    150   private ArrayList processNode(Node node) throws Exception {
    151     ArrayList arrCurrResult = new ArrayList();
    152     NodeList list = node.getChildNodes();
    153     ArrayList arrNodes = new ArrayList();
    154 
    155     for (int i = 0; i < list.getLength(); i++) {
    156       Node n = list.item(i);
    157       if (n.getNodeType() == Node.ELEMENT_NODE) { ///@@@ add node validation here NodeChildren
    158         arrNodes.add(n);
    159       }
    160     }
    161 
    162     if (arrNodes.size() > 0) {
    163       arrCurrResult.add(new Character( (char) 0x5D));
    164 
    165       for (int i = 0; i < arrNodes.size(); i++) {
    166         Node n = (Node) arrNodes.get(i);
    167 
    168         //  result.append(NodeChildren[child.tagName](child))
    169         if (n.getNodeName().equals("Node")) {
    170           arrCurrResult.addAll(processNode(n));
    171         }
    172         else if (n.getNodeName().equals("Type")) {
    173           arrCurrResult.addAll(processNodeType(n));
    174         }
    175         else if (n.getNodeName().equals("Data")) {
    176           arrCurrResult.addAll(processNodeData(n));
    177         }
    178         else if (n.getNodeName().equals("Plural")) {
    179           arrCurrResult.addAll(processNodePlural(n));
    180         }
    181         else if (n.getNodeName().equals("ClassID")) {
    182           arrCurrResult.addAll(processClassID(n));
    183         }
    184         else if (n.getNodeName().equals("NodeName")) {
    185           arrCurrResult.addAll(processNodeName(n));
    186         }
    187         else if (n.getNodeName().equals("Path")) {
    188           arrCurrResult.addAll(processNodePath(n));
    189         }
    190         else if (n.getNodeName().equals("RTProperties")) {
    191           arrCurrResult.addAll(processRTProperties(n));
    192         }
    193         else if (n.getNodeName().equals("DFProperties")) {
    194           arrCurrResult.addAll(processDFProperties(n));
    195         }
    196         else {
    197           throw new Exception("Unsupported node: " + n.getNodeName());
    198         }
    199       }
    200       arrCurrResult.add(new Character( (char) 0x01));
    201     }
    202     else {
    203       arrCurrResult.add(new Character( (char) 0x1D));
    204     }
    205     return arrCurrResult;
    206   }
    207 
    208   private ArrayList processNodeType(Node node) throws Exception {
    209     ArrayList arrCurrResult = new ArrayList();
    210     String text = getText(node);
    211     arrCurrResult.add(new Character( (char) 0x56));
    212     arrCurrResult.addAll(opaqueStringData(text));
    213     arrCurrResult.add(new Character( (char) 0x01));
    214     return arrCurrResult;
    215   }
    216 
    217   private ArrayList processNodeData(Node node) throws Exception {
    218     ArrayList arrCurrResult = new ArrayList();
    219     String nodeData = getText(node);
    220     arrCurrResult.add(new Character( (char) 0x48));
    221     if ("bin".equals(gformat)) {
    222       arrCurrResult.addAll(opaqueHexData(nodeData));
    223     }
    224     else {
    225       arrCurrResult.addAll(opaqueStringData(nodeData));
    226     }
    227     arrCurrResult.add(new Character( (char) 0x01));
    228     return arrCurrResult;
    229   }
    230 
    231   private ArrayList processNodePlural(Node node) throws Exception {
    232     ArrayList arrCurrResult = new ArrayList();
    233     Node nodeYesNo = null;
    234     NodeList list = node.getChildNodes();
    235 
    236     for (int i = 0; i < list.getLength(); i++) {
    237       Node n = list.item(i);
    238       if (n.getNodeType() == Node.ELEMENT_NODE &&
    239           (n.getNodeName().equals("Yes") || n.getNodeName().equals("No"))) {
    240         nodeYesNo = n;
    241         break;
    242       }
    243     }
    244 
    245     if (nodeYesNo == null) {
    246       throw new Exception("Plural element missing Yes or No tag");
    247     }
    248     else {
    249       char yesOrNo = (nodeYesNo.getNodeName().equals("Yes")) ? (char) 1 :
    250           (char) 0;
    251       arrCurrResult.add(new Character( (char) 0x49));
    252       arrCurrResult.addAll(opaqueCharData(yesOrNo));
    253       arrCurrResult.add(new Character( (char) 0x01));
    254     }
    255     return arrCurrResult;
    256   }
    257 
    258   private ArrayList processClassID(Node node) throws Exception {
    259     ArrayList arrCurrResult = new ArrayList();
    260     String strClassId = getText(node);
    261     int intClassId = Integer.parseInt(strClassId);
    262     arrCurrResult.add(new Character( (char) (0x18 | 0x40)));
    263     arrCurrResult.addAll(opaquePackedData(intClassId));
    264     arrCurrResult.add(new Character( (char) 0x01));
    265     return arrCurrResult;
    266   }
    267 
    268   private ArrayList processNodeName(Node node) throws Exception {
    269     //text = getText(element)
    270     //element.parentNode.SyncMLDM_URI += '/' + text
    271     return processTextElement(node, 0x1C);
    272   }
    273 
    274   private ArrayList processNodePath(Node node) throws Exception {
    275     return processTextElement(node, 0x12);
    276   }
    277 
    278   private ArrayList processRTProperties(Node node) throws Exception {
    279     throw new Exception("Support for RTProperties not implemented !!!");
    280   }
    281 
    282   private ArrayList processDFProperties(Node node) throws Exception {
    283     ArrayList arrCurrResult = new ArrayList();
    284 
    285     NodeList list = node.getChildNodes();
    286     for (int i = 0; i < list.getLength(); i++) {
    287       Node n = list.item(i);
    288       if (n.getNodeType() == Node.ELEMENT_NODE) {
    289         if (n.getNodeName().equals("AccessType")) {
    290           arrCurrResult.addAll(processAccessType(n));
    291         }
    292         else if (n.getNodeName().equals("DFFormat")) {
    293           arrCurrResult.addAll(processDFFormat(n));
    294         }
    295         else if (n.getNodeName().equals("Scope")) {
    296           arrCurrResult.addAll(processScope(n));
    297         }
    298         else {
    299           throw new Exception("The tag " + n.getNodeName() +
    300                               " not supprted for DFProperties !!!");
    301         }
    302       }
    303     }
    304     return arrCurrResult;
    305   }
    306 
    307   private ArrayList processAccessType(Node node) throws Exception {
    308     ArrayList arrCurrResult = new ArrayList();
    309     NodeList list = node.getChildNodes();
    310     int access = 0;
    311     int nodeHasGetAccess = 0;
    312     Integer integer;
    313     for (int i = 0; i < list.getLength(); i++) {
    314       Node n = list.item(i);
    315       if (n.getNodeType() == Node.ELEMENT_NODE) {
    316         integer = (Integer) accessMap.get(n.getNodeName());
    317         if (integer != null) {
    318           access |= integer.intValue();
    319           if ("Get".equalsIgnoreCase(n.getNodeName())) {
    320             nodeHasGetAccess = 1;
    321           }
    322         }
    323       }
    324     }
    325     arrCurrResult.add(new Character( (char) 0x5A));
    326     arrCurrResult.addAll(opaquePackedData(access));
    327     arrCurrResult.add(new Character( (char) 0x01));
    328     if (nodeHasGetAccess == 0) {
    329       //result = result + '\x55'+ opaqueData(chr(180))  + '\x01'
    330       arrCurrResult.add(new Character( (char) 0x55));
    331       arrCurrResult.addAll(opaqueCharData( (char) 180));
    332       arrCurrResult.add(new Character( (char) 0x01));
    333     }
    334     return arrCurrResult;
    335   }
    336 
    337   private ArrayList processDFFormat(Node node) throws Exception {
    338     ArrayList arrCurrResult = new ArrayList();
    339     Character format = null;
    340     NodeList list = node.getChildNodes();
    341 
    342     for (int i = 0; i < list.getLength(); i++) {
    343       Node n = list.item(i);
    344       if (n.getNodeType() == Node.ELEMENT_NODE) {
    345         format = (Character) formatMap.get(n.getNodeName());
    346         if (format != null) {
    347           gformat = n.getNodeName();
    348           //WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
    349           //System.out.println("format: " + gformat + "  value: " + format.charValue() + " opaq: " + opaqueCharData(format.charValue()));
    350 
    351           //@@@@@ py ??? gformat = format
    352           //@@@@@ py ??? element.parentNode.parentNode.SyncMLDM_Format = format;
    353           break;
    354         }
    355       }
    356     }
    357 
    358     if (format == null) {
    359       throw new Exception("DFFormat is an unknown or missing format tag");
    360     }
    361     else {
    362       arrCurrResult.add(new Character( (char) 0x57));
    363       arrCurrResult.addAll(opaqueCharData(format.charValue()));
    364       arrCurrResult.add(new Character( (char) 0x01));
    365     }
    366     return arrCurrResult;
    367   }
    368 
    369   private ArrayList processScope(Node node) throws Exception {
    370     ArrayList arrCurrResult = new ArrayList();
    371     Node scope = null;
    372     NodeList list = node.getChildNodes();
    373 
    374     for (int i = 0; i < list.getLength(); i++) {
    375       Node n = list.item(i);
    376       if (n.getNodeType() == Node.ELEMENT_NODE &&
    377           (n.getNodeName().equals("Permanent") ||
    378            n.getNodeName().equals("Dynamic"))) {
    379         scope = n;
    380         break;
    381       }
    382     }
    383 
    384     if (scope == null) {
    385       throw new Exception("Scope element missing Permanent or Dynamic tag");
    386     }
    387     else {
    388       char charScope = (scope.getNodeName().equals("Permanent")) ? (char) 1 :
    389           (char) 2;
    390       arrCurrResult.add(new Character( (char) 0x59));
    391       arrCurrResult.addAll(opaqueCharData(charScope));
    392       arrCurrResult.add(new Character( (char) 0x01));
    393     }
    394     return arrCurrResult;
    395   }
    396 
    397   private String getText(Node node) throws Exception {
    398     NodeList list = node.getChildNodes();
    399     for (int i = 0; i < list.getLength(); i++) {
    400       Node n = list.item(i);
    401       if (n.getNodeType() == Node.TEXT_NODE) {
    402         return n.getNodeValue();
    403       }
    404     }
    405     return "";
    406   }
    407 
    408   public ArrayList opaqueStringData(String data) throws Exception {
    409       if(data.startsWith(FactBootEnc.GUID_HEX_BOOTSTRAP)){
    410           return opaqueHexData(data.replaceFirst(FactBootEnc.GUID_HEX_BOOTSTRAP, ""));
    411       }
    412     byte[] utf8_bytes = data.getBytes("UTF8");
    413     ArrayList arrOpaqueDate = new ArrayList();
    414     arrOpaqueDate.add(new Character( (char) 0xC3));
    415     arrOpaqueDate.addAll(multiByte(utf8_bytes.length));
    416     for (int i = 0; i < utf8_bytes.length; i++) {
    417       arrOpaqueDate.add(new Character(  (char) (((int)utf8_bytes[i]) & 0xFF)  ) );
    418     }
    419     return arrOpaqueDate;
    420 
    421    /* ArrayList arrOpaqueDate = new ArrayList();
    422     arrOpaqueDate.add(new Character( (char) 0xC3));
    423     arrOpaqueDate.addAll(multiByte(data.length()));
    424     char[] arrChar = data.toCharArray();
    425     for (int i = 0; i < arrChar.length; i++) {
    426       arrOpaqueDate.add(new Character(arrChar[i]));
    427     }
    428     return arrOpaqueDate;*/
    429   }
    430 
    431   public ArrayList opaqueCharData(char data) throws Exception {
    432     ArrayList arrOpaqueDate = new ArrayList();
    433     arrOpaqueDate.add(new Character( (char) 0xC3));
    434     arrOpaqueDate.addAll(multiByte(1));
    435     arrOpaqueDate.add(new Character(data));
    436     return arrOpaqueDate;
    437   }
    438 
    439   public ArrayList opaquePackedData(int data) throws Exception {
    440     ArrayList arrOpaqueDate = new ArrayList();
    441     arrOpaqueDate.add(new Character( (char) 0xC3));
    442     arrOpaqueDate.addAll(multiByte(2));
    443     arrOpaqueDate.add(new Character( (char) (data / 256)));
    444     arrOpaqueDate.add(new Character( (char) (data % 256)));
    445     return arrOpaqueDate;
    446   }
    447 
    448   public ArrayList opaqueHexData(String data) throws Exception {
    449     if (data.length() % 2 != 0) {
    450       throw new Exception(
    451           "HEX-encoded data has incorrect format: data length is not even number.");
    452     }
    453     ArrayList arrOpaqueDate = new ArrayList();
    454     arrOpaqueDate.add(new Character( (char) 0xC3));
    455     arrOpaqueDate.addAll(multiByte(data.length() / 2));
    456     arrOpaqueDate.addAll(hexToBin(data));
    457     return arrOpaqueDate;
    458   }
    459 
    460   public ArrayList hexToBin(String data) {
    461     ArrayList arrBinDate = new ArrayList();
    462     int tmp;
    463     for (int i = 0; i < data.length(); i += 2) {
    464       tmp = Integer.decode("0x" + data.substring(i, i + 2)).intValue();
    465       arrBinDate.add(new Character( (char) tmp));
    466     }
    467     return arrBinDate;
    468   }
    469 
    470   public ArrayList multiByte(int value) throws Exception {
    471     ArrayList result = new ArrayList();
    472     int continuation = 0;
    473     int bits = 0;
    474 
    475     for (int shift = 28; shift > 0; shift -= 7) {
    476       bits = (value >> shift) & 0x7F;
    477       if (bits > 0 || continuation > 0) {
    478         result.add(new Character( (char) (bits | 0x80)));
    479       }
    480       if (bits > 0) {
    481         continuation = 1;
    482       }
    483     }
    484     result.add(new Character( (char) (value & 0x7F)));
    485     return result;
    486   }
    487 
    488   //Return the WBXML representation of a element with text content
    489   // Takes a node and the value of the WBXML tag without content.
    490   public ArrayList processTextElement(Node node, int wbxmlTag) throws Exception {
    491     ArrayList arrText = new ArrayList();
    492     String text = getText(node);
    493     if (text.length() > 0) {
    494       arrText.add(new Character( (char) (wbxmlTag | 0x40)));
    495       arrText.addAll(opaqueStringData(text));
    496       arrText.add(new Character( (char) 0x01));
    497     }
    498     else {
    499       arrText.add(new Character( (char) wbxmlTag));
    500     }
    501     return arrText;
    502   }
    503 
    504   public static void main(String[] args) {
    505     try {
    506       Xml2WBXml x2b = new Xml2WBXml();
    507       File file = new File(args[0]);
    508       x2b.convert(file);
    509     }
    510     catch (Exception e) {
    511       e.printStackTrace();
    512     }
    513   }
    514 }
    515