/**
AndroidInfoUtils:安卓游戏包信息工具类
**/
1 public class AndroidInfoUtils 2 { 3 @SuppressWarnings("unchecked") 4 public static ApkInfo getAndroidInfo(String apkPath) throws DocumentException 5 { 6 ApkInfo apkinfo = new ApkInfo(); 7 File file = new File(apkPath); 8 // 获得APK文件名称 9 apkinfo.setApkName(file.getName()); 10 // 获得APK文件大小 11 apkinfo.setApkSize(convertFileSize(file.length())); 12 // 获得APK文件原始大小 13 apkinfo.setApkByteSize(file.length()); 14 15 String xml = AXMLPrinter2.getManifestXMLFromAPK(apkPath); 16 //System.out.println(xml); 17 StringReader read = new StringReader(xml); 18 InputSource scource = new InputSource(read); 19 SAXReader sax = new SAXReader(); 20 Document root = sax.read(scource); 21 Element uses_sdk = (Element) root.selectSingleNode("//uses-sdk"); 22 // 获取APK支持的Android SDK版本 23 apkinfo.setApkMinsdkversion(uses_sdk.attributeValue("minSdkVersion")); 24 25 // zhouning 设置sdk目标版本 26 apkinfo.setApkTargetSdkVersion(uses_sdk.attributeValue("targetSdkVersion")); 27 28 Element manifest = (Element) root.selectSingleNode("//manifest"); 29 // 获取APK版本 30 apkinfo.setApkVersion(manifest.attributeValue("versionName")); 31 // 获取APK包名 32 apkinfo.setApkPageage(manifest.attributeValue("package")); 33 // 获取APK版本代码 34 apkinfo.setApkVersionCode(manifest.attributeValue("versionCode")); 35 List<Element> elements = root.selectNodes("//uses-permission"); 36 37 38 if (CommonUtil.isNotEmpty(elements)) { 39 StringBuffer apkUserPermissionBuff = new StringBuffer(); 40 for (Element el : elements) 41 { 42 apkUserPermissionBuff.append("|").append(el.attributeValue("name")); 43 } 44 45 String apkUserStr = apkUserPermissionBuff.toString(); 46 47 if (CommonUtil.isNotEmpty(apkUserStr)) { 48 apkinfo.setApkUserpermission(apkUserStr.substring(1)); 49 } 50 } 51 52 53 //增加是否存在SDK的设置 54 apkinfo.setExistSdk(AXMLPrinter2.isExistSdk(apkPath)); 55 // 设置SDK版本 56 apkinfo.setSdkVersion(AXMLPrinter2.getSdkVersion(apkPath)); 57 // 设置SDK类型 58 apkinfo.setApkSdkType(AXMLPrinter2.getSdkType(apkPath)); 59 60 return apkinfo; 61 } 62 63 public static String convertFileSize(long filesize) 64 { 65 String strUnit = "Bytes"; 66 String strAfterComma = ""; 67 int intDivisor = 1; 68 if (filesize >= 1024 * 1024) 69 { 70 strUnit = "MB"; 71 intDivisor = 1024 * 1024; 72 } else if (filesize >= 1024) 73 { 74 strUnit = "KB"; 75 intDivisor = 1024; 76 } 77 if (intDivisor == 1){ 78 return filesize + " " + strUnit; 79 } 80 81 strAfterComma = "" + 100 * (filesize % intDivisor) / intDivisor; 82 if (strAfterComma.equals("")) 83 strAfterComma = ".0"; 84 85 return filesize / intDivisor + "." + strAfterComma + " " + strUnit; 86 } 87 88 public static ThreeNetsContent getThreeNetsContent(String apkPath) 89 { 90 return AXMLPrinter2.getThreeNetsContent(apkPath); 91 } 92 }
所用到的ApkInfo
1 public class ApkInfo { 2 3 // APK游戏名称 4 private String apkName; 5 6 // APK游戏大小 7 private String apkSize; 8 9 // APK游戏包名称 10 private String apkPageage; 11 12 // APK游戏版本 13 private String apkVersion; 14 15 // APK游戏版本代码 16 private String apkVersionCode; 17 18 // APK游戏适应Android SDK版本 19 private String apkMinsdkversion; 20 21 // APK游戏用户权限 22 private String apkUserpermission; 23 24 // APK游戏字节大小 25 private long apkByteSize; 26 27 // zhouning SDK目标版本 28 private String apkTargetSdkVersion; 29 30 private boolean isExistSdk; 31 32 //add by mzz sdk类型 33 private String apkSdkType; 34 35 /** 36 * SDK版本 37 */ 38 private String sdkVersion; 39 40 41 42 public boolean isExistSdk() { 43 return isExistSdk; 44 } 45 46 public void setExistSdk(boolean isExistSdk) { 47 this.isExistSdk = isExistSdk; 48 } 49 50 public String getApkName() { 51 return apkName; 52 } 53 54 public void setApkName(String apkName) { 55 this.apkName = apkName; 56 } 57 58 public String getApkSize() { 59 return apkSize; 60 } 61 62 public void setApkSize(String apkSize) { 63 this.apkSize = apkSize; 64 } 65 66 public String getApkPageage() { 67 return apkPageage; 68 } 69 70 public void setApkPageage(String apkPageage) { 71 this.apkPageage = apkPageage; 72 } 73 74 public String getApkVersion() { 75 return apkVersion; 76 } 77 78 public void setApkVersion(String apkVersion) { 79 this.apkVersion = apkVersion; 80 } 81 82 public String getApkVersionCode() { 83 return apkVersionCode; 84 } 85 86 public void setApkVersionCode(String apkVersionCode) { 87 this.apkVersionCode = apkVersionCode; 88 } 89 90 public String getApkMinsdkversion() { 91 return apkMinsdkversion; 92 } 93 94 public void setApkMinsdkversion(String apkMinsdkversion) { 95 this.apkMinsdkversion = apkMinsdkversion; 96 } 97 98 public String getApkUserpermission() { 99 return apkUserpermission; 100 } 101 102 public void setApkUserpermission(String apkUserpermission) { 103 this.apkUserpermission = apkUserpermission; 104 } 105 106 public long getApkByteSize() { 107 return apkByteSize; 108 } 109 110 public void setApkByteSize(long apkByteSize) { 111 this.apkByteSize = apkByteSize; 112 } 113 114 public String getApkTargetSdkVersion() { 115 return apkTargetSdkVersion; 116 } 117 118 public void setApkTargetSdkVersion(String apkTargetSdkVersion) { 119 this.apkTargetSdkVersion = apkTargetSdkVersion; 120 } 121 122 public String getSdkVersion() 123 { 124 return sdkVersion; 125 } 126 127 public void setSdkVersion(String sdkVersion) 128 { 129 this.sdkVersion = sdkVersion; 130 } 131 132 public void setApkSdkType(String apkSdkType) { 133 this.apkSdkType = apkSdkType; 134 } 135 136 public String getApkSdkType() { 137 return apkSdkType; 138 } 139 140 141 }
解析Android 主文件 AndroidManifest.xml
1 import java.io.BufferedReader; 2 import java.io.ByteArrayInputStream; 3 import java.io.File; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.security.interfaces.RSAPublicKey; 7 import java.security.spec.X509EncodedKeySpec; 8 import java.util.zip.ZipEntry; 9 import java.util.zip.ZipFile; 10 11 import org.apache.commons.codec.binary.Base64; 12 import org.jdom.Document; 13 import org.jdom.Element; 14 import org.jdom.input.SAXBuilder; 15 16 import android.content.res.AXmlResourceParser; 17 18 import com.huawei.bme.commons.util.debug.DebugLog; 19 import com.huawei.bme.commons.util.debug.LogFactory; 20 import com.huawei.igop.common.constants.PropertyKey; 21 import com.huawei.igop.common.plist.NSObject; 22 import com.huawei.igop.common.plist.PropertyListParser; 23 import com.huawei.igop.partner.bean.ThreeNetsContent; 24 import com.huawei.igop2.common.utils.RSAUtils; 25 26 public class AXMLPrinter2 { 27 28 29 @SuppressWarnings("unused") 30 private static final String DEFAULT_XML = "AndroidManifest.xml"; 31 private static final float[] RADIX_MULTS = { 0.0039063F, 3.051758E-005F, 32 1.192093E-007F, 4.656613E-010F }; 33 private static final String[] DIMENSION_UNITS = { "px", "dip", "sp", "pt", 34 "in", "mm", "", "" }; 35 private static final String[] FRACTION_UNITS = { "%", "%p", "", "", "", "", 36 "", "" }; 37 38 public static String getManifestXMLFromAPK(String apkPath) { 39 ZipFile file = null; 40 StringBuilder xmlSb = new StringBuilder(100); 41 try { 42 int type; 43 File apkFile = new File(apkPath); 44 file = new ZipFile(apkFile, 1); 45 ZipEntry entry = file.getEntry("AndroidManifest.xml"); 46 47 AXmlResourceParser parser = new AXmlResourceParser(); 48 parser.open(file.getInputStream(entry)); 49 50 StringBuilder sb = new StringBuilder(10); 51 @SuppressWarnings("unused") 52 String indentStep = " "; 53 54 while ((type = parser.next()) != 1) { 55 switch (type) { 56 case 0: 57 log(xmlSb, "<?xml version="1.0" encoding="utf-8"?>", 58 new Object[0]); 59 break; 60 case 2: 61 log(false, 62 xmlSb, 63 "%s<%s%s", 64 new Object[] { sb, 65 getNamespacePrefix(parser.getPrefix()), 66 parser.getName() }); 67 sb.append(" "); 68 69 int namespaceCountBefore = parser.getNamespaceCount(parser 70 .getDepth() - 1); 71 int namespaceCount = parser.getNamespaceCount(parser 72 .getDepth()); 73 74 for (int i = namespaceCountBefore; i != namespaceCount; ++i) { 75 log(xmlSb, 76 "%sxmlns:%s="%s"", 77 new Object[] { 78 (i == namespaceCountBefore) ? " " : sb, 79 parser.getNamespacePrefix(i), 80 parser.getNamespaceUri(i) }); 81 } 82 83 int i = 0; 84 for (int size = parser.getAttributeCount(); i != size; ++i) { 85 log(false, 86 xmlSb, 87 "%s%s%s="%s"", 88 new Object[] { 89 " ", 90 getNamespacePrefix(parser 91 .getAttributePrefix(i)), 92 parser.getAttributeName(i), 93 getAttributeValue(parser, i) }); 94 } 95 96 log(xmlSb, ">", new Object[0]); 97 break; 98 case 3: 99 sb.setLength(sb.length() - " ".length()); 100 log(xmlSb, 101 "%s</%s%s>", 102 new Object[] { sb, 103 getNamespacePrefix(parser.getPrefix()), 104 parser.getName() }); 105 break; 106 case 4: 107 log(xmlSb, "%s%s", new Object[] { sb, parser.getText() }); 108 case 1: 109 } 110 111 } 112 113 parser.close(); 114 } catch (Exception e) { 115 e.printStackTrace(); 116 } finally { 117 if (file != null) { 118 try { 119 file.close(); 120 } catch (IOException e) { 121 122 } 123 } 124 } 125 return xmlSb.toString(); 126 } 127 128 public static boolean isExistSdk(String apkPath) { 129 ZipFile file = null; 130 try { 131 File apkFile = new File(apkPath); 132 file = new ZipFile(apkFile, 1); 133 ZipEntry entryCharge = file.getEntry("assets/Charge.xml"); 134 ZipEntry entryConsumeCode = file 135 .getEntry("assets/ConsumeCodeInfo.xml"); 136 if (entryCharge != null && entryConsumeCode != null) { 137 return true; 138 } 139 } catch (Exception e) { 140 e.printStackTrace(); 141 } finally { 142 if (file != null) { 143 try { 144 file.close(); 145 } catch (IOException e) { 146 e.printStackTrace(); 147 } 148 } 149 } 150 return false; 151 } 152 153 private static String getNamespacePrefix(String prefix) { 154 if ((prefix == null) || (prefix.length() == 0)) { 155 return ""; 156 } 157 return prefix + ":"; 158 } 159 160 private static String getAttributeValue(AXmlResourceParser parser, int index) { 161 int type = parser.getAttributeValueType(index); 162 int data = parser.getAttributeValueData(index); 163 if (type == 3) { 164 return parser.getAttributeValue(index); 165 } 166 if (type == 2) { 167 return String.format("?%s%08X", new Object[] { getPackage(data), 168 Integer.valueOf(data) }); 169 } 170 if (type == 1) { 171 return String.format("@%s%08X", new Object[] { getPackage(data), 172 Integer.valueOf(data) }); 173 } 174 if (type == 4) { 175 return String.valueOf(Float.intBitsToFloat(data)); 176 } 177 if (type == 17) { 178 return String.format("0x%08X", 179 new Object[] { Integer.valueOf(data) }); 180 } 181 if (type == 18) { 182 return ((data != 0) ? "true" : "false"); 183 } 184 if (type == 5) { 185 return Float.toString(complexToFloat(data)) 186 + DIMENSION_UNITS[(data & 0xF)]; 187 } 188 if (type == 6) { 189 return Float.toString(complexToFloat(data)) 190 + FRACTION_UNITS[(data & 0xF)]; 191 } 192 if ((type >= 28) && (type <= 31)) { 193 return String.format("#%08X", 194 new Object[] { Integer.valueOf(data) }); 195 } 196 if ((type >= 16) && (type <= 31)) { 197 return String.valueOf(data); 198 } 199 return String.format("<0x%X, type 0x%02X>", 200 new Object[] { Integer.valueOf(data), Integer.valueOf(type) }); 201 } 202 203 private static String getPackage(int id) { 204 if (id >>> 24 == 1) { 205 return "android:"; 206 } 207 return ""; 208 } 209 210 private static void log(StringBuilder xmlSb, String format, 211 Object[] arguments) { 212 log(true, xmlSb, format, arguments); 213 } 214 215 private static void log(boolean newLine, StringBuilder xmlSb, 216 String format, Object[] arguments) { 217 xmlSb.append(String.format(format, arguments)); 218 if (newLine) 219 xmlSb.append(" "); 220 } 221 222 public static float complexToFloat(int complex) { 223 return ((complex & 0xFFFFFF00) * RADIX_MULTS[(complex >> 4 & 0x3)]); 224 } 225 226 public static String getSdkVersion(String apkPath) 227 { 228 String sdkVersion = null; 229 ZipFile zipfile = null; 230 try 231 { 232 File apkFile = new File(apkPath); 233 zipfile = new ZipFile(apkFile, 1); 234 ZipEntry config = zipfile.getEntry("assets/Config.xml"); 235 if (null != config) 236 { 237 Document doc = new SAXBuilder().build(zipfile.getInputStream(config)); 238 sdkVersion = doc.getRootElement().getChildTextTrim("SDKVersion"); 239 } 240 } 241 catch (Exception e) 242 { 243 DEBUGGER.error("Failed to getSdkVersion", e); 244 } 245 finally 246 { 247 closeZipFile(zipfile); 248 } 249 return sdkVersion; 250 } 251 252 public static String getSdkType(String apkPath) 253 { 254 String SdkType = null; 255 ZipFile zipfile = null; 256 try 257 { 258 File apkFile = new File(apkPath); 259 zipfile = new ZipFile(apkFile, 1); 260 ZipEntry config = zipfile.getEntry("assets/ConfigExt.xml"); 261 if (null != config) 262 { 263 //配置文件内容,由SDK通过RSA 私钥加密,iGop从包体读出时,通过RSA公钥解密(防止CP篡改)。 264 BufferedReader in = new BufferedReader(new InputStreamReader(zipfile.getInputStream(config))); 265 StringBuffer strBuffer = new StringBuffer(); 266 String buffer = null; 267 while (null != (buffer = in.readLine())) 268 { 269 //得到密文 270 strBuffer.append(buffer); 271 } 272 String key = PropertyUtil.getString(PropertyKey.SDK_TYPE_PUBLICKEY); 273 X509EncodedKeySpec pubX509new = new X509EncodedKeySpec(Base64.decodeBase64(key.getBytes())); 274 RSAPublicKey pukey1 = RSAUtils.getPublicKey(pubX509new); 275 String jiewen = RSAUtils.decryptByPublicKey(strBuffer.toString(), pukey1); 276 Document doc = new SAXBuilder().build(new ByteArrayInputStream(jiewen.getBytes())); 277 SdkType = doc.getRootElement().getChildTextTrim("SDKType"); 278 279 } 280 } 281 catch (Exception e) 282 { 283 DEBUGGER.error("Failed to getSdkType", e); 284 } 285 finally 286 { 287 closeZipFile(zipfile); 288 } 289 return SdkType; 290 } 291 292 public static void closeZipFile(ZipFile zipfile) 293 { 294 if (null != zipfile) 295 { 296 try 297 { 298 zipfile.close(); 299 } 300 catch (IOException e) 301 { 302 DEBUGGER.error("Failed to close ZipFile", e); 303 } 304 } 305 } 306 307 public static ThreeNetsContent getThreeNetsContent(String apkPath) 308 { 309 ZipFile zipfile = null; 310 ThreeNetsContent threeNetsContent = null; 311 try 312 { 313 File file = new File(apkPath); 314 zipfile = new ZipFile(file, 1); 315 ZipEntry zipEntry = zipfile.getEntry("assets/touchpayservice.xml"); 316 if (null == zipEntry) 317 { 318 return null; 319 } 320 321 threeNetsContent = new ThreeNetsContent(); 322 Document doc = new SAXBuilder().build(zipfile.getInputStream(zipEntry)); 323 Element element = doc.getRootElement(); 324 threeNetsContent.setCmServiceId(element.getChildTextTrim("cm_serviceid")); 325 threeNetsContent.setCuServiceId(element.getChildTextTrim("cu_serviceid")); 326 threeNetsContent.setCtServiceId(element.getChildTextTrim("ct_serviceid")); 327 } 328 catch (Exception e) 329 { 330 DEBUGGER.error("Failed to getThreeNetsContent", e); 331 } 332 finally 333 { 334 closeZipFile(zipfile); 335 } 336 337 return threeNetsContent; 338 } 339 340 public static boolean isExistsSdkFromIOS(String filePath, String projectName) 341 { 342 ZipFile file = null; 343 try { 344 File iosFile = new File(filePath); 345 file = new ZipFile(iosFile, 1); 346 ZipEntry entryCharge = file.getEntry("Payload/"+ projectName +"/Res.bundle/Charge.xml"); 347 ZipEntry entryConsumeCode = file.getEntry("Payload/"+ projectName +"/Res.bundle/ConsumeCodeInfo.xml"); 348 if (entryCharge != null && entryConsumeCode != null) { 349 return true; 350 } 351 } catch (Exception e) { 352 e.printStackTrace(); 353 } finally { 354 closeZipFile(file); 355 } 356 return false; 357 } 358 359 public static String getSdkVersionFromIOS(String filePath, String projectName) 360 { 361 String sdkVersion = null; 362 ZipFile zipfile = null; 363 try 364 { 365 File file = new File(filePath); 366 zipfile = new ZipFile(file, 1); 367 ZipEntry config = zipfile.getEntry("Payload/"+ projectName +"/Res.bundle/CM_iOS_Config.xml"); 368 if (null != config) 369 { 370 Document doc = new SAXBuilder().build(zipfile.getInputStream(config)); 371 sdkVersion = doc.getRootElement().getChildTextTrim("SDKVersion"); 372 } 373 } 374 catch (Exception e) 375 { 376 DEBUGGER.error("Failed to getSdkVersion4Ios", e); 377 } 378 finally 379 { 380 closeZipFile(zipfile); 381 } 382 return sdkVersion; 383 } 384 385 public static String getXmlFromIOS(String filePath, String projectName) 386 { 387 ZipFile zipfile = null; 388 NSObject nsObject = null; 389 try 390 { 391 File file = new File(filePath); 392 zipfile = new ZipFile(file, 1); 393 ZipEntry entry = zipfile.getEntry("Payload/"+ projectName +"/Info.plist"); 394 nsObject = PropertyListParser.parse(zipfile.getInputStream(entry)); 395 } 396 catch (Exception e) 397 { 398 DEBUGGER.error("Failed to getXmlFromIOS", e); 399 } 400 finally 401 { 402 closeZipFile(zipfile); 403 } 404 return nsObject.toXMLPropertyList(); 405 } 406 }
1 import java.io.IOException; 2 3 /** 4 * Abstract interface for any object contained in a property list. 5 * The names and functions of the various objects orient themselves 6 * towards Apple's Cocoa API. 7 * @author Daniel 8 */ 9 public abstract class NSObject { 10 11 public final static String NEWLINE = System.getProperty("line.separator"); 12 public final static String INDENT = " "; 13 14 /** 15 * Generates the XML representation of the object (without XML headers or enclosing plist-tags). 16 * @param xml The StringBuilder onto which the XML representation is appended. 17 * @param level The indentation level of the object. 18 */ 19 public abstract void toXML(StringBuilder xml, int level); 20 21 /** 22 * Assigns IDs to all the objects in this NSObject subtree. 23 */ 24 void assignIDs(BinaryPropertyListWriter out) { 25 out.assignID(this); 26 } 27 28 /** 29 * Generates the binary representation of the object. 30 * @param out The output stream to serialize the object to. 31 */ 32 abstract void toBinary(BinaryPropertyListWriter out) throws IOException; 33 34 /** 35 * Generates a valid XML property list including headers using this object as root. 36 * @return The XML representation of the property list 37 */ 38 public String toXMLPropertyList() { 39 StringBuilder xml = new StringBuilder("<?xml version="1.0" encoding="UTF-8"?>"); 40 xml.append(NSObject.NEWLINE); 41 xml.append("<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">"); 42 xml.append(NSObject.NEWLINE); 43 xml.append("<plist version="1.0">"); 44 xml.append(NSObject.NEWLINE); 45 toXML(xml, 0); 46 xml.append(NSObject.NEWLINE); 47 xml.append("</plist>"); 48 return xml.toString(); 49 } 50 51 protected void indent(StringBuilder xml, int level) { 52 for(int i=0;i<level;i++) 53 xml.append(INDENT); 54 } 55 }
PropertyListParser.java
1 /** 2 * Parses any given property list 3 * @author Daniel Dreibrodt 4 */ 5 public class PropertyListParser { 6 7 /** 8 * Reads all bytes from an InputStream and stores them in an array, up to 9 * a maximum count. 10 * @param in The InputStream 11 * @param max The maximum number of bytes to read. 12 **/ 13 static byte[] readAll(InputStream in, int max) throws IOException { 14 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 15 while (max > 0) { 16 int n = in.read(); 17 if (n == -1) break; // EOF 18 buf.write(n); 19 max--; 20 } 21 return buf.toByteArray(); 22 } 23 24 /** 25 * Parses a property list from a file. It can either be in XML or binary format. 26 * @param f The property list file 27 * @return The root object in the property list 28 * @throws Exception If an error occurred while parsing 29 */ 30 public static NSObject parse(File f) throws Exception { 31 FileInputStream fis = new FileInputStream(f); 32 String magicString = new String(readAll(fis, 8), 0, 8); 33 fis.close(); 34 if (magicString.startsWith("bplist00")) { 35 return BinaryPropertyListParser.parse(f); 36 } else if (magicString.startsWith("<?xml")) { 37 return XMLPropertyListParser.parse(f); 38 } else { 39 throw new UnsupportedOperationException("The given data is neither a binary nor a XML property list. ASCII property lists are not supported."); 40 } 41 } 42 43 /** 44 * Parses a property list from a byte array. It can either be in XML or binary format. 45 * @param bytes The property list data 46 * @return The root object in the property list 47 * @throws Exception If an error occurred while parsing. 48 */ 49 public static NSObject parse(byte[] bytes) throws Exception { 50 String magicString = new String(bytes, 0, 8); 51 if (magicString.startsWith("bplist00")) { 52 return BinaryPropertyListParser.parse(bytes); 53 } else if (magicString.startsWith("<?xml")) { 54 return XMLPropertyListParser.parse(bytes); 55 } else { 56 throw new UnsupportedOperationException("The given data is neither a binary nor a XML property list. ASCII property lists are not supported."); 57 } 58 } 59 60 /** 61 * Parses a property list from an InputStream. It can either be in XML or binary format. 62 * @param is The InputStream delivering the property list data 63 * @return The root object of the property list 64 * @throws Exception If an error occurred while parsing. 65 */ 66 public static NSObject parse(InputStream is) throws Exception { 67 if(is.markSupported()) { 68 is.mark(10); 69 String magicString = new String(readAll(is, 8), 0, 8); 70 is.reset(); 71 if (magicString.startsWith("bplist00")) { 72 return BinaryPropertyListParser.parse(is); 73 } else if (magicString.startsWith("<?xml")) { 74 return XMLPropertyListParser.parse(is); 75 } else { 76 throw new UnsupportedOperationException("The given data is neither a binary nor a XML property list. ASCII property lists are not supported."); 77 } 78 } else { 79 //Now we have to read everything, because if one parsing method fails 80 //the whole InputStream is lost as we can't reset it 81 return parse(readAll(is, Integer.MAX_VALUE)); 82 } 83 } 84 85 /** 86 * Saves a property list with the given object as root into a XML file 87 * @param root the root object 88 * @param out the output file 89 * @throws IOException if an error occurs during writing 90 */ 91 public static void saveAsXML(NSObject root, File out) throws IOException { 92 OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(out), "UTF-8"); 93 w.write(root.toXMLPropertyList()); 94 w.close(); 95 } 96 97 /** 98 * Converts a given property list file into the xml format 99 * @param in the source file 100 * @param out the target file 101 * @throws Exception if an error occurs during parsing 102 * @throws IOException if an error occurs during writing 103 */ 104 public static void convertToXml(File in, File out) throws Exception { 105 NSObject root = parse(in); 106 saveAsXML(root, out); 107 } 108 109 /** 110 * Saves a property list with the given object as root into a binary file 111 * @param root the root object 112 * @param out the output file 113 * @throws IOException if an error occurs during writing 114 */ 115 public static void saveAsBinary(NSObject root, File out) throws IOException { 116 BinaryPropertyListWriter.write(out, root); 117 } 118 119 /** 120 * Converts a given property list file into the binary format 121 * @param in the source file 122 * @param out the target file 123 * @throws Exception if an error occurs during parsing 124 * @throws IOException if an error occurs during writing 125 */ 126 public static void convertToBinary(File in, File out) throws Exception { 127 NSObject root = parse(in); 128 saveAsBinary(root, out); 129 } 130 }