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