From e82c0754def4216687502fb51a8a599d99ce02c6 Mon Sep 17 00:00:00 2001 From: Connor Tumbleson Date: Tue, 12 Feb 2013 21:12:17 -0600 Subject: [PATCH] Code cleanup of 2013 Signed-off-by: Connor Tumbleson --- .../src/main/java/brut/apktool/Main.java | 588 ++--- .../content/res/XmlResourceParser.java | 19 +- .../main/java/android/util/AttributeSet.java | 80 +- .../main/java/android/util/TypedValue.java | 466 ++-- .../src/main/java/brut/androlib/Androlib.java | 979 ++++--- .../java/brut/androlib/AndrolibException.java | 22 +- .../main/java/brut/androlib/ApkDecoder.java | 20 +- .../java/brut/androlib/ApktoolProperties.java | 91 +- .../androlib/err/CantFind9PatchChunk.java | 22 +- .../err/CantFindFrameworkResException.java | 22 +- .../androlib/err/InFileNotFoundException.java | 22 +- .../androlib/err/OutDirExistsException.java | 22 +- .../brut/androlib/err/UndefinedResObject.java | 22 +- .../java/brut/androlib/java/AndrolibJava.java | 12 +- .../brut/androlib/mod/IndentingWriter.java | 6 +- .../main/java/brut/androlib/mod/SmaliMod.java | 77 +- .../brut/androlib/res/AndrolibResources.java | 59 +- .../brut/androlib/res/ResSmaliUpdater.java | 240 +- .../brut/androlib/res/data/ResConfig.java | 76 +- .../androlib/res/data/ResConfigFlags.java | 839 +++--- .../java/brut/androlib/res/data/ResID.java | 80 +- .../brut/androlib/res/data/ResPackage.java | 323 ++- .../brut/androlib/res/data/ResResSpec.java | 156 +- .../brut/androlib/res/data/ResResource.java | 61 +- .../java/brut/androlib/res/data/ResTable.java | 200 +- .../java/brut/androlib/res/data/ResType.java | 69 +- .../brut/androlib/res/data/ResValuesFile.java | 110 +- .../res/data/value/ResArrayValue.java | 110 +- .../brut/androlib/res/data/value/ResAttr.java | 262 +- .../androlib/res/data/value/ResBagValue.java | 61 +- .../androlib/res/data/value/ResBoolValue.java | 23 +- .../res/data/value/ResColorValue.java | 14 +- .../res/data/value/ResDimenValue.java | 14 +- .../androlib/res/data/value/ResEnumAttr.java | 94 +- .../androlib/res/data/value/ResFileValue.java | 28 +- .../androlib/res/data/value/ResFlagsAttr.java | 218 +- .../res/data/value/ResFloatValue.java | 23 +- .../res/data/value/ResFractionValue.java | 14 +- .../androlib/res/data/value/ResIdValue.java | 15 +- .../androlib/res/data/value/ResIntValue.java | 33 +- .../res/data/value/ResPluralsValue.java | 91 +- .../res/data/value/ResReferenceValue.java | 68 +- .../res/data/value/ResScalarValue.java | 178 +- .../res/data/value/ResStringValue.java | 61 +- .../res/data/value/ResStyleValue.java | 85 +- .../res/data/value/ResValueFactory.java | 128 +- .../androlib/res/decoder/ARSCDecoder.java | 810 +++--- .../res/decoder/AXmlResourceParser.java | 2017 +++++++------- .../res/decoder/Res9patchStreamDecoder.java | 186 +- .../androlib/res/decoder/ResAttrDecoder.java | 44 +- .../androlib/res/decoder/ResFileDecoder.java | 213 +- .../res/decoder/ResRawStreamDecoder.java | 17 +- .../res/decoder/ResStreamDecoder.java | 4 +- .../decoder/ResStreamDecoderContainer.java | 31 +- .../androlib/res/decoder/StringBlock.java | 575 ++-- .../res/decoder/XmlPullStreamDecoder.java | 233 +- .../java/brut/androlib/res/util/ExtFile.java | 53 +- .../androlib/res/util/ExtMXSerializer.java | 94 +- .../androlib/res/util/ExtXmlSerializer.java | 13 +- .../res/xml/ResValuesXmlSerializable.java | 4 +- .../androlib/res/xml/ResXmlEncodable.java | 5 +- .../brut/androlib/res/xml/ResXmlEncoders.java | 308 ++- .../java/brut/androlib/src/DebugInjector.java | 340 ++- .../brut/androlib/src/DexFileBuilder.java | 91 +- .../java/brut/androlib/src/SmaliBuilder.java | 141 +- .../java/brut/androlib/src/SmaliDecoder.java | 55 +- .../main/java/brut/androlib/src/TypeName.java | 316 ++- .../ledatastream/LEDataInputStream.java | 537 ++-- .../xmlpull/mxp1_serializer/MXSerializer.java | 2326 +++++++++-------- .../brut/androlib/BuildAndDecodeTest.java | 288 +- .../test/java/brut/androlib/TestUtils.java | 189 +- 71 files changed, 7649 insertions(+), 7444 deletions(-) diff --git a/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java b/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java index c517b661..c354b27c 100644 --- a/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java +++ b/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java @@ -36,197 +36,200 @@ import java.util.logging.*; * @author Ryszard Wiśniewski */ public class Main { - public static void main(String[] args) - throws IOException, InterruptedException, BrutException { - try { - Verbosity verbosity = Verbosity.NORMAL; - int i; - for (i = 0; i < args.length; i++) { - String opt = args[i]; - - if (opt.startsWith("--version") || (opt.startsWith("-version"))) { - version_print(); - System.exit(1); - } - if (! opt.startsWith("-")) { - break; - } - if ("-v".equals(opt) || "--verbose".equals(opt)) { - if (verbosity != Verbosity.NORMAL) { - throw new InvalidArgsError(); - } - verbosity = Verbosity.VERBOSE; - } else if ("-q".equals(opt) || "--quiet".equals(opt)) { - if (verbosity != Verbosity.NORMAL) { - throw new InvalidArgsError(); - } - verbosity = Verbosity.QUIET; - } else { - throw new InvalidArgsError(); - } - } - setupLogging(verbosity); + public static void main(String[] args) throws IOException, + InterruptedException, BrutException { + try { + Verbosity verbosity = Verbosity.NORMAL; + int i; + for (i = 0; i < args.length; i++) { + String opt = args[i]; - if (args.length <= i) { - throw new InvalidArgsError(); - } - String cmd = args[i]; - args = Arrays.copyOfRange(args, i + 1, args.length); + if (opt.startsWith("--version") || (opt.startsWith("-version"))) { + version_print(); + System.exit(1); + } + if (!opt.startsWith("-")) { + break; + } + if ("-v".equals(opt) || "--verbose".equals(opt)) { + if (verbosity != Verbosity.NORMAL) { + throw new InvalidArgsError(); + } + verbosity = Verbosity.VERBOSE; + } else if ("-q".equals(opt) || "--quiet".equals(opt)) { + if (verbosity != Verbosity.NORMAL) { + throw new InvalidArgsError(); + } + verbosity = Verbosity.QUIET; + } else { + throw new InvalidArgsError(); + } + } + setupLogging(verbosity); - if ("d".equals(cmd) || "decode".equals(cmd)) { - cmdDecode(args); - } else if ("b".equals(cmd) || "build".equals(cmd)) { - cmdBuild(args); - } else if ("if".equals(cmd) || "install-framework".equals(cmd)) { - cmdInstallFramework(args); - } else if ("publicize-resources".equals(cmd)) { - cmdPublicizeResources(args); - } else { - throw new InvalidArgsError(); - } - } catch (InvalidArgsError ex) { - usage(); - System.exit(1); - } - } + if (args.length <= i) { + throw new InvalidArgsError(); + } + String cmd = args[i]; + args = Arrays.copyOfRange(args, i + 1, args.length); - private static void cmdDecode(String[] args) throws InvalidArgsError, - AndrolibException { - ApkDecoder decoder = new ApkDecoder(); + if ("d".equals(cmd) || "decode".equals(cmd)) { + cmdDecode(args); + } else if ("b".equals(cmd) || "build".equals(cmd)) { + cmdBuild(args); + } else if ("if".equals(cmd) || "install-framework".equals(cmd)) { + cmdInstallFramework(args); + } else if ("publicize-resources".equals(cmd)) { + cmdPublicizeResources(args); + } else { + throw new InvalidArgsError(); + } + } catch (InvalidArgsError ex) { + usage(); + System.exit(1); + } + } - int i; - for (i = 0; i < args.length; i++) { - String opt = args[i]; - if (! opt.startsWith("-")) { - break; - } - if ("-s".equals(opt) || "--no-src".equals(opt)) { - decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_NONE); - } else if ("-d".equals(opt) || "--debug".equals(opt)) { - decoder.setDebugMode(true); - } else if ("-b".equals(opt) || "--no-debug-info".equals(opt)) { - decoder.setBaksmaliDebugMode(false); - } else if ("-t".equals(opt) || "--frame-tag".equals(opt)) { - i++; - if (i >= args.length) { - throw new InvalidArgsError(); - } - decoder.setFrameworkTag(args[i]); - } else if ("-f".equals(opt) || "--force".equals(opt)) { - decoder.setForceDelete(true); - } else if ("-r".equals(opt) || "--no-res".equals(opt)) { - decoder.setDecodeResources(ApkDecoder.DECODE_RESOURCES_NONE); - } else if ("--keep-broken-res".equals(opt)) { - decoder.setKeepBrokenResources(true); - } else if ("--frame-path".equals(opt)) { - i++; - System.out.println("Using Framework Directory: " + args[i]); - decoder.setFrameworkDir(args[i]); - } else { - throw new InvalidArgsError(); - } - } + private static void cmdDecode(String[] args) throws InvalidArgsError, + AndrolibException { + ApkDecoder decoder = new ApkDecoder(); - String outName = null; - if (args.length == i + 2) { - outName = args[i + 1]; - } else if (args.length == i + 1) { - outName = args[i]; - outName = outName.endsWith(".apk") ? - outName.substring(0, outName.length() - 4) : outName + ".out"; - outName = new File(outName).getName(); - } else { - throw new InvalidArgsError(); - } - File outDir = new File(outName); - decoder.setOutDir(outDir); - decoder.setApkFile(new File(args[i])); + int i; + for (i = 0; i < args.length; i++) { + String opt = args[i]; + if (!opt.startsWith("-")) { + break; + } + if ("-s".equals(opt) || "--no-src".equals(opt)) { + decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_NONE); + } else if ("-d".equals(opt) || "--debug".equals(opt)) { + decoder.setDebugMode(true); + } else if ("-b".equals(opt) || "--no-debug-info".equals(opt)) { + decoder.setBaksmaliDebugMode(false); + } else if ("-t".equals(opt) || "--frame-tag".equals(opt)) { + i++; + if (i >= args.length) { + throw new InvalidArgsError(); + } + decoder.setFrameworkTag(args[i]); + } else if ("-f".equals(opt) || "--force".equals(opt)) { + decoder.setForceDelete(true); + } else if ("-r".equals(opt) || "--no-res".equals(opt)) { + decoder.setDecodeResources(ApkDecoder.DECODE_RESOURCES_NONE); + } else if ("--keep-broken-res".equals(opt)) { + decoder.setKeepBrokenResources(true); + } else if ("--frame-path".equals(opt)) { + i++; + System.out.println("Using Framework Directory: " + args[i]); + decoder.setFrameworkDir(args[i]); + } else { + throw new InvalidArgsError(); + } + } - try { - decoder.decode(); - } catch (OutDirExistsException ex) { - System.out.println( - "Destination directory (" + outDir.getAbsolutePath() + ") " + - "already exists. Use -f switch if you want to overwrite it."); - System.exit(1); - } catch (InFileNotFoundException ex) { - System.out.println( - "Input file (" + args[i] + ") " + - "was not found or was not readable."); - System.exit(1); - } catch (CantFindFrameworkResException ex) { - System.out.println( - "Can't find framework resources for package of id: " + - String.valueOf(ex.getPkgId()) + ". You must install proper " + - "framework files, see project website for more info."); - System.exit(1); - } catch (IOException ex) { - System.out.println( - "Could not modify file. Please ensure you have permission."); - System.exit(1); - } - - } + String outName = null; + if (args.length == i + 2) { + outName = args[i + 1]; + } else if (args.length == i + 1) { + outName = args[i]; + outName = outName.endsWith(".apk") ? outName.substring(0, + outName.length() - 4) : outName + ".out"; + outName = new File(outName).getName(); + } else { + throw new InvalidArgsError(); + } + File outDir = new File(outName); + decoder.setOutDir(outDir); + decoder.setApkFile(new File(args[i])); - private static void cmdBuild(String[] args) throws BrutException { - - // hold all the fields - HashMap flags = new HashMap(); - flags.put("forceBuildAll", false); - flags.put("debug", false); - flags.put("verbose", false); - flags.put("injectOriginal", false); - flags.put("framework", false); - flags.put("update", false); - - int i; - int skip = 0; - ExtFile mOrigApk = null; - String mAaptPath = ""; - for (i = 0; i < args.length; i++) { - String opt = args[i]; - if (! opt.startsWith("-")) { - break; - } - if ("-f".equals(opt) || "--force-all".equals(opt)) { - flags.put("forceBuildAll", true); - } else if ("-d".equals(opt) || "--debug".equals(opt)) { - flags.put("debug", true); - } else if ("-v".equals(opt) || "--verbose".equals(opt)) { - flags.put("verbose", true); - } else if ("-a".equals(opt) || "--aapt".equals(opt)) { - mAaptPath = args[i + 1]; - skip = 1; - } else if ("-o".equals(opt) || "--original".equals(opt)) { - if (args.length >= 4) { - throw new InvalidArgsError(); - } else { - flags.put("injectOriginal", true); - mOrigApk = new ExtFile(args[i + 1]); - skip = 1; - } - } else { - throw new InvalidArgsError(); - } - } + try { + decoder.decode(); + } catch (OutDirExistsException ex) { + System.out + .println("Destination directory (" + + outDir.getAbsolutePath() + + ") " + + "already exists. Use -f switch if you want to overwrite it."); + System.exit(1); + } catch (InFileNotFoundException ex) { + System.out.println("Input file (" + args[i] + ") " + + "was not found or was not readable."); + System.exit(1); + } catch (CantFindFrameworkResException ex) { + System.out + .println("Can't find framework resources for package of id: " + + String.valueOf(ex.getPkgId()) + + ". You must install proper " + + "framework files, see project website for more info."); + System.exit(1); + } catch (IOException ex) { + System.out + .println("Could not modify file. Please ensure you have permission."); + System.exit(1); + } - String appDirName; - File outFile = null; - switch (args.length - i - skip) { - case 0: - appDirName = "."; - break; - case 2: - outFile = new File(args[i + 1 + skip]); - case 1: - appDirName = args[i + skip]; - break; - default: - throw new InvalidArgsError(); - } - - new Androlib().build(new File(appDirName), outFile, flags, mOrigApk, mAaptPath); - } + } + + private static void cmdBuild(String[] args) throws BrutException { + + // hold all the fields + HashMap flags = new HashMap(); + flags.put("forceBuildAll", false); + flags.put("debug", false); + flags.put("verbose", false); + flags.put("injectOriginal", false); + flags.put("framework", false); + flags.put("update", false); + + int i; + int skip = 0; + ExtFile mOrigApk = null; + String mAaptPath = ""; + for (i = 0; i < args.length; i++) { + String opt = args[i]; + if (!opt.startsWith("-")) { + break; + } + if ("-f".equals(opt) || "--force-all".equals(opt)) { + flags.put("forceBuildAll", true); + } else if ("-d".equals(opt) || "--debug".equals(opt)) { + flags.put("debug", true); + } else if ("-v".equals(opt) || "--verbose".equals(opt)) { + flags.put("verbose", true); + } else if ("-a".equals(opt) || "--aapt".equals(opt)) { + mAaptPath = args[i + 1]; + skip = 1; + } else if ("-o".equals(opt) || "--original".equals(opt)) { + if (args.length >= 4) { + throw new InvalidArgsError(); + } else { + flags.put("injectOriginal", true); + mOrigApk = new ExtFile(args[i + 1]); + skip = 1; + } + } else { + throw new InvalidArgsError(); + } + } + + String appDirName; + File outFile = null; + switch (args.length - i - skip) { + case 0: + appDirName = "."; + break; + case 2: + outFile = new File(args[i + 1 + skip]); + case 1: + appDirName = args[i + skip]; + break; + default: + throw new InvalidArgsError(); + } + + new Androlib().build(new File(appDirName), outFile, flags, mOrigApk, + mAaptPath); + } private static void cmdInstallFramework(String[] args) throws AndrolibException { @@ -254,120 +257,123 @@ public class Main { throw new InvalidArgsError(); } - private static void cmdPublicizeResources(String[] args) - throws InvalidArgsError, AndrolibException { - if (args.length != 1) { - throw new InvalidArgsError(); - } + private static void cmdPublicizeResources(String[] args) + throws InvalidArgsError, AndrolibException { + if (args.length != 1) { + throw new InvalidArgsError(); + } - new Androlib().publicizeResources(new File(args[0])); - } + new Androlib().publicizeResources(new File(args[0])); + } - private static void version_print() { - System.out.println( - Androlib.getVersion()); - } - - private static void usage() { - System.out.println( - "Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" + - "Copyright 2010 Ryszard Wiśniewski \n" + - "with smali v" + ApktoolProperties.get("smaliVersion") + - ", and baksmali v" + ApktoolProperties.get("baksmaliVersion") + "\n" + - "Updated by @iBotPeaches \n" + - "Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" + - "\n" + - "Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n" + - "\n" + - "COMMANDs are:\n" + - "\n" + - " d[ecode] [OPTS] []\n" + - " Decode to .\n" + - "\n" + - " OPTS:\n" + - "\n" + - " -s, --no-src\n" + - " Do not decode sources.\n" + - " -r, --no-res\n" + - " Do not decode resources.\n" + - " -d, --debug\n" + - " Decode in debug mode. Check project page for more info.\n" + - " -b, --no-debug-info\n" + - " Baksmali -- don't write out debug info (.local, .param, .line, etc.)\n" + - " -f, --force\n" + - " Force delete destination directory.\n" + - " -t , --frame-tag \n" + - " Try to use framework files tagged by .\n" + - " --frame-path \n" + - " Use the specified directory for framework files\n" + - " --keep-broken-res\n" + - " Use if there was an error and some resources were dropped, e.g.:\n" + - " \"Invalid config flags detected. Dropping resources\", but you\n" + - " want to decode them anyway, even with errors. You will have to\n" + - " fix them manually before building." + - "\n\n" + - " b[uild] [OPTS] [] []\n" + - " Build an apk from already decoded application located in .\n" + - "\n" + - " It will automatically detect, whether files was changed and perform\n" + - " needed steps only.\n" + - "\n" + - " If you omit then current directory will be used.\n" + - " If you omit then /dist/\n" + - " will be used.\n" + - "\n" + - " OPTS:\n" + - "\n" + - " -f, --force-all\n" + - " Skip changes detection and build all files.\n" + - " -d, --debug\n" + - " Build in debug mode. Check project page for more info.\n" + - " -a, --aapt\n" + - " Loads aapt from specified location.\n" + - "\n" + - " if|install-framework [] --frame-path [] \n" + - " Install framework file to your system.\n" + - "\n" + - "For additional info, see: http://code.google.com/p/android-apktool/" + - "\n" + - "For smali/baksmali info, see: http://code.google.com/p/smali/" - ); - } + private static void version_print() { + System.out.println(Androlib.getVersion()); + } - private static void setupLogging(Verbosity verbosity) { - Logger logger = Logger.getLogger(""); - for (Handler handler : logger.getHandlers()) { - logger.removeHandler(handler); - } - if (verbosity == Verbosity.QUIET) { - return; - } + private static void usage() { + System.out + .println("Apktool v" + + Androlib.getVersion() + + " - a tool for reengineering Android apk files\n" + + "Copyright 2010 Ryszard Wiśniewski \n" + + "with smali v" + + ApktoolProperties.get("smaliVersion") + + ", and baksmali v" + + ApktoolProperties.get("baksmaliVersion") + + "\n" + + "Updated by @iBotPeaches \n" + + "Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" + + "\n" + + "Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n" + + "\n" + + "COMMANDs are:\n" + + "\n" + + " d[ecode] [OPTS] []\n" + + " Decode to .\n" + + "\n" + + " OPTS:\n" + + "\n" + + " -s, --no-src\n" + + " Do not decode sources.\n" + + " -r, --no-res\n" + + " Do not decode resources.\n" + + " -d, --debug\n" + + " Decode in debug mode. Check project page for more info.\n" + + " -b, --no-debug-info\n" + + " Baksmali -- don't write out debug info (.local, .param, .line, etc.)\n" + + " -f, --force\n" + + " Force delete destination directory.\n" + + " -t , --frame-tag \n" + + " Try to use framework files tagged by .\n" + + " --frame-path \n" + + " Use the specified directory for framework files\n" + + " --keep-broken-res\n" + + " Use if there was an error and some resources were dropped, e.g.:\n" + + " \"Invalid config flags detected. Dropping resources\", but you\n" + + " want to decode them anyway, even with errors. You will have to\n" + + " fix them manually before building." + + "\n\n" + + " b[uild] [OPTS] [] []\n" + + " Build an apk from already decoded application located in .\n" + + "\n" + + " It will automatically detect, whether files was changed and perform\n" + + " needed steps only.\n" + + "\n" + + " If you omit then current directory will be used.\n" + + " If you omit then /dist/\n" + + " will be used.\n" + + "\n" + + " OPTS:\n" + + "\n" + + " -f, --force-all\n" + + " Skip changes detection and build all files.\n" + + " -d, --debug\n" + + " Build in debug mode. Check project page for more info.\n" + + " -a, --aapt\n" + + " Loads aapt from specified location.\n" + + "\n" + + " if|install-framework [] --frame-path [] \n" + + " Install framework file to your system.\n" + + "\n" + + "For additional info, see: http://code.google.com/p/android-apktool/" + + "\n" + + "For smali/baksmali info, see: http://code.google.com/p/smali/"); + } - Handler handler = new ConsoleHandler(); - logger.addHandler(handler); + private static void setupLogging(Verbosity verbosity) { + Logger logger = Logger.getLogger(""); + for (Handler handler : logger.getHandlers()) { + logger.removeHandler(handler); + } + if (verbosity == Verbosity.QUIET) { + return; + } - if (verbosity == Verbosity.VERBOSE) { - handler.setLevel(Level.ALL); - logger.setLevel(Level.ALL); - } else { - handler.setFormatter(new Formatter() { - @Override - public String format(LogRecord record) { - return record.getLevel().toString().charAt(0) + ": " - + record.getMessage() - + System.getProperty("line.separator"); - } - }); - } - } + Handler handler = new ConsoleHandler(); + logger.addHandler(handler); - private static enum Verbosity { - NORMAL, VERBOSE, QUIET; - } - - private static boolean Advanced = false; + if (verbosity == Verbosity.VERBOSE) { + handler.setLevel(Level.ALL); + logger.setLevel(Level.ALL); + } else { + handler.setFormatter(new Formatter() { + @Override + public String format(LogRecord record) { + return record.getLevel().toString().charAt(0) + ": " + + record.getMessage() + + System.getProperty("line.separator"); + } + }); + } + } - static class InvalidArgsError extends AndrolibException { + private static enum Verbosity { + NORMAL, VERBOSE, QUIET; + } - } + private static boolean Advanced = false; + + static class InvalidArgsError extends AndrolibException { + + } } diff --git a/brut.apktool/apktool-lib/src/main/java/android/content/res/XmlResourceParser.java b/brut.apktool/apktool-lib/src/main/java/android/content/res/XmlResourceParser.java index c59e6d4e..1d7b59de 100644 --- a/brut.apktool/apktool-lib/src/main/java/android/content/res/XmlResourceParser.java +++ b/brut.apktool/apktool-lib/src/main/java/android/content/res/XmlResourceParser.java @@ -21,16 +21,15 @@ import org.xmlpull.v1.XmlPullParser; import android.util.AttributeSet; /** - * The XML parsing interface returned for an XML resource. This is a standard - * XmlPullParser interface, as well as an extended AttributeSet interface and - * an additional close() method on this interface for the client to indicate - * when it is done reading the resource. + * The XML parsing interface returned for an XML resource. This is a standard + * XmlPullParser interface, as well as an extended AttributeSet interface and an + * additional close() method on this interface for the client to indicate when + * it is done reading the resource. */ public interface XmlResourceParser extends XmlPullParser, AttributeSet { - /** - * Close this interface to the resource. Calls on the interface are no - * longer value after this call. - */ - public void close(); + /** + * Close this interface to the resource. Calls on the interface are no + * longer value after this call. + */ + public void close(); } - diff --git a/brut.apktool/apktool-lib/src/main/java/android/util/AttributeSet.java b/brut.apktool/apktool-lib/src/main/java/android/util/AttributeSet.java index 1da422fd..55840a6c 100644 --- a/brut.apktool/apktool-lib/src/main/java/android/util/AttributeSet.java +++ b/brut.apktool/apktool-lib/src/main/java/android/util/AttributeSet.java @@ -17,33 +17,61 @@ package android.util; /** * @author Dmitry Skiba - * + * */ public interface AttributeSet { - int getAttributeCount(); - String getAttributeName(int index); - String getAttributeValue(int index); - String getPositionDescription(); - int getAttributeNameResource(int index); - int getAttributeListValue(int index,String options[],int defaultValue); - boolean getAttributeBooleanValue(int index,boolean defaultValue); - int getAttributeResourceValue(int index,int defaultValue); - int getAttributeIntValue(int index,int defaultValue); - int getAttributeUnsignedIntValue(int index,int defaultValue); - float getAttributeFloatValue(int index,float defaultValue); - String getIdAttribute(); - String getClassAttribute(); - int getIdAttributeResourceValue(int index); - int getStyleAttribute(); - String getAttributeValue(String namespace, String attribute); - int getAttributeListValue(String namespace,String attribute,String options[],int defaultValue); - boolean getAttributeBooleanValue(String namespace,String attribute,boolean defaultValue); - int getAttributeResourceValue(String namespace,String attribute,int defaultValue); - int getAttributeIntValue(String namespace,String attribute,int defaultValue); - int getAttributeUnsignedIntValue(String namespace,String attribute,int defaultValue); - float getAttributeFloatValue(String namespace,String attribute,float defaultValue); + int getAttributeCount(); - //TODO: remove - int getAttributeValueType(int index); - int getAttributeValueData(int index); + String getAttributeName(int index); + + String getAttributeValue(int index); + + String getPositionDescription(); + + int getAttributeNameResource(int index); + + int getAttributeListValue(int index, String options[], int defaultValue); + + boolean getAttributeBooleanValue(int index, boolean defaultValue); + + int getAttributeResourceValue(int index, int defaultValue); + + int getAttributeIntValue(int index, int defaultValue); + + int getAttributeUnsignedIntValue(int index, int defaultValue); + + float getAttributeFloatValue(int index, float defaultValue); + + String getIdAttribute(); + + String getClassAttribute(); + + int getIdAttributeResourceValue(int index); + + int getStyleAttribute(); + + String getAttributeValue(String namespace, String attribute); + + int getAttributeListValue(String namespace, String attribute, + String options[], int defaultValue); + + boolean getAttributeBooleanValue(String namespace, String attribute, + boolean defaultValue); + + int getAttributeResourceValue(String namespace, String attribute, + int defaultValue); + + int getAttributeIntValue(String namespace, String attribute, + int defaultValue); + + int getAttributeUnsignedIntValue(String namespace, String attribute, + int defaultValue); + + float getAttributeFloatValue(String namespace, String attribute, + float defaultValue); + + // TODO: remove + int getAttributeValueType(int index); + + int getAttributeValueData(int index); } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/android/util/TypedValue.java b/brut.apktool/apktool-lib/src/main/java/android/util/TypedValue.java index d7b5295d..5e364c67 100644 --- a/brut.apktool/apktool-lib/src/main/java/android/util/TypedValue.java +++ b/brut.apktool/apktool-lib/src/main/java/android/util/TypedValue.java @@ -17,247 +17,285 @@ package android.util; /** - * Container for a dynamically typed data value. Primarily used with + * Container for a dynamically typed data value. Primarily used with * {@link android.content.res.Resources} for holding resource values. */ public class TypedValue { - /** The value contains no data. */ - public static final int TYPE_NULL = 0x00; + /** The value contains no data. */ + public static final int TYPE_NULL = 0x00; - /** The data field holds a resource identifier. */ - public static final int TYPE_REFERENCE = 0x01; - /** The data field holds an attribute resource - * identifier (referencing an attribute in the current theme - * style, not a resource entry). */ - public static final int TYPE_ATTRIBUTE = 0x02; - /** The string field holds string data. In addition, if - * data is non-zero then it is the string block - * index of the string and assetCookie is the set of - * assets the string came from. */ - public static final int TYPE_STRING = 0x03; - /** The data field holds an IEEE 754 floating point number. */ - public static final int TYPE_FLOAT = 0x04; - /** The data field holds a complex number encoding a - * dimension value. */ - public static final int TYPE_DIMENSION = 0x05; - /** The data field holds a complex number encoding a fraction - * of a container. */ - public static final int TYPE_FRACTION = 0x06; + /** The data field holds a resource identifier. */ + public static final int TYPE_REFERENCE = 0x01; + /** + * The data field holds an attribute resource identifier + * (referencing an attribute in the current theme style, not a resource + * entry). + */ + public static final int TYPE_ATTRIBUTE = 0x02; + /** + * The string field holds string data. In addition, if + * data is non-zero then it is the string block index of the + * string and assetCookie is the set of assets the string came + * from. + */ + public static final int TYPE_STRING = 0x03; + /** The data field holds an IEEE 754 floating point number. */ + public static final int TYPE_FLOAT = 0x04; + /** + * The data field holds a complex number encoding a dimension + * value. + */ + public static final int TYPE_DIMENSION = 0x05; + /** + * The data field holds a complex number encoding a fraction of a + * container. + */ + public static final int TYPE_FRACTION = 0x06; - /** Identifies the start of plain integer values. Any type value - * from this to {@link #TYPE_LAST_INT} means the - * data field holds a generic integer value. */ - public static final int TYPE_FIRST_INT = 0x10; + /** + * Identifies the start of plain integer values. Any type value from this to + * {@link #TYPE_LAST_INT} means the data field holds a generic + * integer value. + */ + public static final int TYPE_FIRST_INT = 0x10; - /** The data field holds a number that was - * originally specified in decimal. */ - public static final int TYPE_INT_DEC = 0x10; - /** The data field holds a number that was - * originally specified in hexadecimal (0xn). */ - public static final int TYPE_INT_HEX = 0x11; - /** The data field holds 0 or 1 that was originally - * specified as "false" or "true". */ - public static final int TYPE_INT_BOOLEAN = 0x12; + /** + * The data field holds a number that was originally specified in + * decimal. + */ + public static final int TYPE_INT_DEC = 0x10; + /** + * The data field holds a number that was originally specified in + * hexadecimal (0xn). + */ + public static final int TYPE_INT_HEX = 0x11; + /** + * The data field holds 0 or 1 that was originally specified as + * "false" or "true". + */ + public static final int TYPE_INT_BOOLEAN = 0x12; - /** Identifies the start of integer values that were specified as - * color constants (starting with '#'). */ - public static final int TYPE_FIRST_COLOR_INT = 0x1c; + /** + * Identifies the start of integer values that were specified as color + * constants (starting with '#'). + */ + public static final int TYPE_FIRST_COLOR_INT = 0x1c; - /** The data field holds a color that was originally - * specified as #aarrggbb. */ - public static final int TYPE_INT_COLOR_ARGB8 = 0x1c; - /** The data field holds a color that was originally - * specified as #rrggbb. */ - public static final int TYPE_INT_COLOR_RGB8 = 0x1d; - /** The data field holds a color that was originally - * specified as #argb. */ - public static final int TYPE_INT_COLOR_ARGB4 = 0x1e; - /** The data field holds a color that was originally - * specified as #rgb. */ - public static final int TYPE_INT_COLOR_RGB4 = 0x1f; + /** + * The data field holds a color that was originally specified as + * #aarrggbb. + */ + public static final int TYPE_INT_COLOR_ARGB8 = 0x1c; + /** + * The data field holds a color that was originally specified as + * #rrggbb. + */ + public static final int TYPE_INT_COLOR_RGB8 = 0x1d; + /** + * The data field holds a color that was originally specified as + * #argb. + */ + public static final int TYPE_INT_COLOR_ARGB4 = 0x1e; + /** + * The data field holds a color that was originally specified as + * #rgb. + */ + public static final int TYPE_INT_COLOR_RGB4 = 0x1f; - /** Identifies the end of integer values that were specified as color - * constants. */ - public static final int TYPE_LAST_COLOR_INT = 0x1f; + /** + * Identifies the end of integer values that were specified as color + * constants. + */ + public static final int TYPE_LAST_COLOR_INT = 0x1f; - /** Identifies the end of plain integer values. */ - public static final int TYPE_LAST_INT = 0x1f; + /** Identifies the end of plain integer values. */ + public static final int TYPE_LAST_INT = 0x1f; - /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ - /** Complex data: bit location of unit information. */ - public static final int COMPLEX_UNIT_SHIFT = 0; - /** Complex data: mask to extract unit information (after shifting by - * {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as - * defined below. */ - public static final int COMPLEX_UNIT_MASK = 0xf; + /** Complex data: bit location of unit information. */ + public static final int COMPLEX_UNIT_SHIFT = 0; + /** + * Complex data: mask to extract unit information (after shifting by + * {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as defined + * below. + */ + public static final int COMPLEX_UNIT_MASK = 0xf; - /** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */ - public static final int COMPLEX_UNIT_PX = 0; - /** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent - * Pixels. */ - public static final int COMPLEX_UNIT_DIP = 1; - /** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */ - public static final int COMPLEX_UNIT_SP = 2; - /** {@link #TYPE_DIMENSION} complex unit: Value is in points. */ - public static final int COMPLEX_UNIT_PT = 3; - /** {@link #TYPE_DIMENSION} complex unit: Value is in inches. */ - public static final int COMPLEX_UNIT_IN = 4; - /** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */ - public static final int COMPLEX_UNIT_MM = 5; + /** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */ + public static final int COMPLEX_UNIT_PX = 0; + /** + * {@link #TYPE_DIMENSION} complex unit: Value is Device Independent Pixels. + */ + public static final int COMPLEX_UNIT_DIP = 1; + /** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */ + public static final int COMPLEX_UNIT_SP = 2; + /** {@link #TYPE_DIMENSION} complex unit: Value is in points. */ + public static final int COMPLEX_UNIT_PT = 3; + /** {@link #TYPE_DIMENSION} complex unit: Value is in inches. */ + public static final int COMPLEX_UNIT_IN = 4; + /** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */ + public static final int COMPLEX_UNIT_MM = 5; - /** {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall - * size. */ - public static final int COMPLEX_UNIT_FRACTION = 0; - /** {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. */ - public static final int COMPLEX_UNIT_FRACTION_PARENT = 1; + /** + * {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall size. + */ + public static final int COMPLEX_UNIT_FRACTION = 0; + /** {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. */ + public static final int COMPLEX_UNIT_FRACTION_PARENT = 1; - /** Complex data: where the radix information is, telling where the decimal - * place appears in the mantissa. */ - public static final int COMPLEX_RADIX_SHIFT = 4; - /** Complex data: mask to extract radix information (after shifting by - * {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point - * representations as defined below. */ - public static final int COMPLEX_RADIX_MASK = 0x3; + /** + * Complex data: where the radix information is, telling where the decimal + * place appears in the mantissa. + */ + public static final int COMPLEX_RADIX_SHIFT = 4; + /** + * Complex data: mask to extract radix information (after shifting by + * {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point + * representations as defined below. + */ + public static final int COMPLEX_RADIX_MASK = 0x3; - /** Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 */ - public static final int COMPLEX_RADIX_23p0 = 0; - /** Complex data: the mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn */ - public static final int COMPLEX_RADIX_16p7 = 1; - /** Complex data: the mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn */ - public static final int COMPLEX_RADIX_8p15 = 2; - /** Complex data: the mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn */ - public static final int COMPLEX_RADIX_0p23 = 3; + /** Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 */ + public static final int COMPLEX_RADIX_23p0 = 0; + /** Complex data: the mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn */ + public static final int COMPLEX_RADIX_16p7 = 1; + /** Complex data: the mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn */ + public static final int COMPLEX_RADIX_8p15 = 2; + /** Complex data: the mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn */ + public static final int COMPLEX_RADIX_0p23 = 3; - /** Complex data: bit location of mantissa information. */ - public static final int COMPLEX_MANTISSA_SHIFT = 8; - /** Complex data: mask to extract mantissa information (after shifting by - * {@link #COMPLEX_MANTISSA_SHIFT}). This gives us 23 bits of precision; - * the top bit is the sign. */ - public static final int COMPLEX_MANTISSA_MASK = 0xffffff; + /** Complex data: bit location of mantissa information. */ + public static final int COMPLEX_MANTISSA_SHIFT = 8; + /** + * Complex data: mask to extract mantissa information (after shifting by + * {@link #COMPLEX_MANTISSA_SHIFT}). This gives us 23 bits of precision; the + * top bit is the sign. + */ + public static final int COMPLEX_MANTISSA_MASK = 0xffffff; - /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ - /** - * If {@link #density} is equal to this value, then the density should be - * treated as the system's default density value: {@link DisplayMetrics#DENSITY_DEFAULT}. - */ - public static final int DENSITY_DEFAULT = 0; + /** + * If {@link #density} is equal to this value, then the density should be + * treated as the system's default density value: + * {@link DisplayMetrics#DENSITY_DEFAULT}. + */ + public static final int DENSITY_DEFAULT = 0; - /** - * If {@link #density} is equal to this value, then there is no density - * associated with the resource and it should not be scaled. - */ - public static final int DENSITY_NONE = 0xffff; + /** + * If {@link #density} is equal to this value, then there is no density + * associated with the resource and it should not be scaled. + */ + public static final int DENSITY_NONE = 0xffff; - /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ - /** The type held by this value, as defined by the constants here. - * This tells you how to interpret the other fields in the object. */ - public int type; + /** + * The type held by this value, as defined by the constants here. This tells + * you how to interpret the other fields in the object. + */ + public int type; - private static final float MANTISSA_MULT = - 1.0f / (1<>TypedValue.COMPLEX_RADIX_SHIFT) - & TypedValue.COMPLEX_RADIX_MASK]; - } + /** + * Retrieve the base value from a complex data integer. This uses the + * {@link #COMPLEX_MANTISSA_MASK} and {@link #COMPLEX_RADIX_MASK} fields of + * the data to compute a floating point representation of the number they + * describe. The units are ignored. + * + * @param complex + * A complex data value. + * + * @return A floating point value corresponding to the complex data. + */ + public static float complexToFloat(int complex) { + return (complex & (TypedValue.COMPLEX_MANTISSA_MASK << TypedValue.COMPLEX_MANTISSA_SHIFT)) + * RADIX_MULTS[(complex >> TypedValue.COMPLEX_RADIX_SHIFT) + & TypedValue.COMPLEX_RADIX_MASK]; + } - private static final String[] DIMENSION_UNIT_STRS = new String[] { - "px", "dip", "sp", "pt", "in", "mm" - }; - private static final String[] FRACTION_UNIT_STRS = new String[] { - "%", "%p" - }; + private static final String[] DIMENSION_UNIT_STRS = new String[] { "px", + "dip", "sp", "pt", "in", "mm" }; + private static final String[] FRACTION_UNIT_STRS = new String[] { "%", "%p" }; - /** - * Perform type conversion as per {@link #coerceToString()} on an - * explicitly supplied type and data. - * - * @param type The data type identifier. - * @param data The data value. - * - * @return String The coerced string value. If the value is - * null or the type is not known, null is returned. - */ - public static final String coerceToString(int type, int data) - { - switch (type) { - case TYPE_NULL: - return null; - case TYPE_REFERENCE: - return "@" + data; - case TYPE_ATTRIBUTE: - return "?" + data; - case TYPE_FLOAT: - return Float.toString(Float.intBitsToFloat(data)); - case TYPE_DIMENSION: - return Float.toString(complexToFloat(data)) + DIMENSION_UNIT_STRS[ - (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; - case TYPE_FRACTION: - return Float.toString(complexToFloat(data)*100) + FRACTION_UNIT_STRS[ - (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; - case TYPE_INT_HEX: - return "0x" + Integer.toHexString(data); - case TYPE_INT_BOOLEAN: - return data != 0 ? "true" : "false"; - } + /** + * Perform type conversion as per {@link #coerceToString()} on an explicitly + * supplied type and data. + * + * @param type + * The data type identifier. + * @param data + * The data value. + * + * @return String The coerced string value. If the value is null or the type + * is not known, null is returned. + */ + public static final String coerceToString(int type, int data) { + switch (type) { + case TYPE_NULL: + return null; + case TYPE_REFERENCE: + return "@" + data; + case TYPE_ATTRIBUTE: + return "?" + data; + case TYPE_FLOAT: + return Float.toString(Float.intBitsToFloat(data)); + case TYPE_DIMENSION: + return Float.toString(complexToFloat(data)) + + DIMENSION_UNIT_STRS[(data >> COMPLEX_UNIT_SHIFT) + & COMPLEX_UNIT_MASK]; + case TYPE_FRACTION: + return Float.toString(complexToFloat(data) * 100) + + FRACTION_UNIT_STRS[(data >> COMPLEX_UNIT_SHIFT) + & COMPLEX_UNIT_MASK]; + case TYPE_INT_HEX: + return "0x" + Integer.toHexString(data); + case TYPE_INT_BOOLEAN: + return data != 0 ? "true" : "false"; + } - if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) { - String res =String.format("%08x", data); - char[] vals = res.toCharArray(); - switch (type) { - default: - case TYPE_INT_COLOR_ARGB8://#AaRrGgBb - break; - case TYPE_INT_COLOR_RGB8://#FFRrGgBb->#RrGgBb - res = res.substring(2); - break; - case TYPE_INT_COLOR_ARGB4://#AARRGGBB->#ARGB - res = new StringBuffer().append(vals[0]).append(vals[2]).append(vals[4]).append(vals[6]).toString(); - break; - case TYPE_INT_COLOR_RGB4://#FFRRGGBB->#RGB - res = new StringBuffer().append(vals[2]).append(vals[4]).append(vals[6]).toString(); - break; - } - return "#" + res; - } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) { - String res; - switch (type) { - default: - case TYPE_INT_DEC: - res = Integer.toString(data); - break; - //defined before - /*case TYPE_INT_HEX: - res = "0x" + Integer.toHexString(data); - break; - case TYPE_INT_BOOLEAN: - res = (data != 0) ? "true":"false"; - break;*/ - } - return res; - } + if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) { + String res = String.format("%08x", data); + char[] vals = res.toCharArray(); + switch (type) { + default: + case TYPE_INT_COLOR_ARGB8:// #AaRrGgBb + break; + case TYPE_INT_COLOR_RGB8:// #FFRrGgBb->#RrGgBb + res = res.substring(2); + break; + case TYPE_INT_COLOR_ARGB4:// #AARRGGBB->#ARGB + res = new StringBuffer().append(vals[0]).append(vals[2]) + .append(vals[4]).append(vals[6]).toString(); + break; + case TYPE_INT_COLOR_RGB4:// #FFRRGGBB->#RGB + res = new StringBuffer().append(vals[2]).append(vals[4]) + .append(vals[6]).toString(); + break; + } + return "#" + res; + } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) { + String res; + switch (type) { + default: + case TYPE_INT_DEC: + res = Integer.toString(data); + break; + // defined before + /* + * case TYPE_INT_HEX: res = "0x" + Integer.toHexString(data); break; + * case TYPE_INT_BOOLEAN: res = (data != 0) ? "true":"false"; break; + */ + } + return res; + } - return null; - } + return null; + } }; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java index 1d1d62d8..77b48143 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java @@ -40,521 +40,514 @@ import org.yaml.snakeyaml.Yaml; * @author Ryszard Wiśniewski */ public class Androlib { - private final AndrolibResources mAndRes = new AndrolibResources(); + private final AndrolibResources mAndRes = new AndrolibResources(); - public ResTable getResTable(ExtFile apkFile) throws AndrolibException { - return mAndRes.getResTable(apkFile, true); - } + public ResTable getResTable(ExtFile apkFile) throws AndrolibException { + return mAndRes.getResTable(apkFile, true); + } - public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) throws AndrolibException { - return mAndRes.getResTable(apkFile, loadMainPkg); - } + public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) + throws AndrolibException { + return mAndRes.getResTable(apkFile, loadMainPkg); + } - public void decodeSourcesRaw(ExtFile apkFile, File outDir, boolean debug) - throws AndrolibException { - try { - if (debug) { - LOGGER.warning("Debug mode not available."); - } - Directory apk = apkFile.getDirectory(); - LOGGER.info("Copying raw classes.dex file..."); - apkFile.getDirectory().copyToDir(outDir, "classes.dex"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public void decodeSourcesSmali(File apkFile, File outDir, boolean debug, boolean bakdeb) - throws AndrolibException { - try { - File smaliDir = new File(outDir, SMALI_DIRNAME); - OS.rmdir(smaliDir); - smaliDir.mkdirs(); - LOGGER.info("Baksmaling..."); - SmaliDecoder.decode(apkFile, smaliDir, debug, bakdeb); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } - - public void decodeSourcesJava(ExtFile apkFile, File outDir, boolean debug) - throws AndrolibException { - LOGGER.info("Decoding Java sources..."); - new AndrolibJava().decode(apkFile, outDir); - } - - public void decodeManifestRaw(ExtFile apkFile, File outDir) - throws AndrolibException { - try { - Directory apk = apkFile.getDirectory(); - LOGGER.info("Copying raw manifest..."); - apkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public void decodeManifestFull(ExtFile apkFile, File outDir, - ResTable resTable) throws AndrolibException { - mAndRes.decodeManifest(resTable, apkFile, outDir); - } - - public void decodeResourcesRaw(ExtFile apkFile, File outDir) - throws AndrolibException { - try { - //Directory apk = apkFile.getDirectory(); - LOGGER.info("Copying raw resources..."); - apkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public void decodeResourcesFull(ExtFile apkFile, File outDir, - ResTable resTable) throws AndrolibException { - mAndRes.decode(resTable, apkFile, outDir); - } - - public void decodeRawFiles(ExtFile apkFile, File outDir) - throws AndrolibException { - LOGGER.info("Copying assets and libs..."); - try { - Directory in = apkFile.getDirectory(); - if (in.containsDir("assets")) { - in.copyToDir(outDir, "assets"); - } - if (in.containsDir("lib")) { - in.copyToDir(outDir, "lib"); - } - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public void writeMetaFile(File mOutDir, Map meta) - throws AndrolibException { - DumperOptions options = new DumperOptions(); - options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); -// options.setIndent(4); - Yaml yaml = new Yaml(options); - - FileWriter writer = null; - try { - writer = new FileWriter(new File(mOutDir, "apktool.yml")); - yaml.dump(meta, writer); - } catch (IOException ex) { - throw new AndrolibException(ex); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException ex) {} - } - } - } - - public Map readMetaFile(ExtFile appDir) - throws AndrolibException { - InputStream in = null; - try { - in = appDir.getDirectory().getFileInput("apktool.yml"); - Yaml yaml = new Yaml(); - return (Map) yaml.load(in); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ex) {} - } - } - } - - public void build(File appDir, File outFile, - HashMap flags, ExtFile origApk, String aaptPath) throws BrutException { - build(new ExtFile(appDir), outFile, flags, origApk, aaptPath); - } - - public void build(ExtFile appDir, File outFile, - HashMap flags, ExtFile origApk, String aaptPath) throws BrutException { - - mAaptPath = aaptPath; - Map meta = readMetaFile(appDir); - Object t1 = meta.get("isFrameworkApk"); - flags.put("framework", t1 == null ? false : (Boolean) t1); - flags.put("compression", meta.get("compressionType") == null ? false : (Boolean) meta.get("compressionType")); - mAndRes.setSdkInfo((Map) meta.get("sdkInfo")); - - // check the orig apk - if (flags.get("injectOriginal")) { - if (!origApk.isFile() || !origApk.canRead()) { - throw new InFileNotFoundException(); - } else { - mOrigApkFile = origApk; - } - } - - if (outFile == null) { - String outFileName = (String) meta.get("apkFileName"); - outFile = new File(appDir, "dist" + File.separator + - (outFileName == null ? "out.apk" : outFileName)); - } - - new File(appDir, APK_DIRNAME).mkdirs(); - buildSources(appDir, flags); - buildResources(appDir, flags, - (Map) meta.get("usesFramework")); - buildLib(appDir, flags); - buildApk(appDir, outFile,flags); - } - - public void buildSources(File appDir, HashMap flags) - throws AndrolibException { - if (! buildSourcesRaw(appDir, flags) - && ! buildSourcesSmali(appDir, flags) - && ! buildSourcesJava(appDir, flags) - ) { - LOGGER.warning("Could not find sources"); - } - } - - public boolean buildSourcesRaw(File appDir, - HashMap flags) throws AndrolibException { - try { - File working = new File(appDir, "classes.dex"); - if (! working.exists()) { - return false; - } - if (flags.get("debug")) { - LOGGER.warning("Debug mode not available."); - } - File stored = new File(appDir, APK_DIRNAME + "/classes.dex"); - if (flags.get("forceBuildAll") || isModified(working, stored)) { - LOGGER.info("Copying classes.dex file..."); - BrutIO.copyAndClose(new FileInputStream(working), - new FileOutputStream(stored)); - } - return true; - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } - - public boolean buildSourcesSmali(File appDir, - HashMap flags) throws AndrolibException { - ExtFile smaliDir = new ExtFile(appDir, "smali"); - if (! smaliDir.exists()) { - return false; - } - File dex = new File(appDir, APK_DIRNAME + "/classes.dex"); - if (! flags.get("forceBuildAll")) { - LOGGER.info("Checking whether sources has changed..."); - } - if (flags.get("forceBuildAll") || isModified(smaliDir, dex)) { - LOGGER.info("Smaling..."); - dex.delete(); - SmaliBuilder.build(smaliDir, dex, flags); - } - return true; - } - - public boolean buildSourcesJava(File appDir, - HashMap flags) throws AndrolibException { - File javaDir = new File(appDir, "src"); - if (! javaDir.exists()) { - return false; - } - File dex = new File(appDir, APK_DIRNAME + "/classes.dex"); - if (! flags.get("forceBuildAll")) { - LOGGER.info("Checking whether sources has changed..."); - } - if (flags.get("forceBuildAll") || isModified(javaDir, dex)) { - LOGGER.info("Building java sources..."); - dex.delete(); - new AndrolibJava().build(javaDir, dex); - } - return true; - } - - public void buildResources(ExtFile appDir, HashMap flags, - Map usesFramework) - throws BrutException { - if (! buildResourcesRaw(appDir, flags) - && ! buildResourcesFull(appDir, flags, usesFramework) - && ! buildManifest(appDir, flags, usesFramework)) { - LOGGER.warning("Could not find resources"); - } - } - - public boolean buildResourcesRaw(ExtFile appDir, HashMap flags) - throws AndrolibException { - try { - if (! new File(appDir, "resources.arsc").exists()) { - return false; - } - File apkDir = new File(appDir, APK_DIRNAME); - if (! flags.get("forceBuildAll")) { - LOGGER.info("Checking whether resources has changed..."); - } - if (flags.get("forceBuildAll") || isModified( - newFiles(APK_RESOURCES_FILENAMES, appDir), - newFiles(APK_RESOURCES_FILENAMES, apkDir))) { - LOGGER.info("Copying raw resources..."); - appDir.getDirectory() - .copyToDir(apkDir, APK_RESOURCES_FILENAMES); - } - return true; - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public boolean buildResourcesFull(File appDir, HashMap flags, - Map usesFramework) - throws AndrolibException { - try { - if (! new File(appDir, "res").exists()) { - return false; - } - if (! flags.get("forceBuildAll")) { - LOGGER.info("Checking whether resources has changed..."); - } - File apkDir = new File(appDir, APK_DIRNAME); - if (flags.get("forceBuildAll") || isModified( - newFiles(APP_RESOURCES_FILENAMES, appDir), - newFiles(APK_RESOURCES_FILENAMES, apkDir))) { - LOGGER.info("Building resources..."); - - File apkFile = File.createTempFile("APKTOOL", null); - apkFile.delete(); - - File ninePatch = new File(appDir, "9patch"); - if (! ninePatch.exists()) { - ninePatch = null; - } - mAndRes.aaptPackage( - apkFile, - new File(appDir, "AndroidManifest.xml"), - new File(appDir, "res"), - ninePatch, null, parseUsesFramework(usesFramework), - flags, mAaptPath - ); - - Directory tmpDir = new ExtFile(apkFile).getDirectory(); - tmpDir.copyToDir(apkDir, - tmpDir.containsDir("res") ? APK_RESOURCES_FILENAMES : - APK_RESOURCES_WITHOUT_RES_FILENAMES); - - // delete tmpDir - apkFile.delete(); - } - return true; - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } catch (BrutException ex) { - throw new AndrolibException(ex); + public void decodeSourcesRaw(ExtFile apkFile, File outDir, boolean debug) + throws AndrolibException { + try { + if (debug) { + LOGGER.warning("Debug mode not available."); + } + Directory apk = apkFile.getDirectory(); + LOGGER.info("Copying raw classes.dex file..."); + apkFile.getDirectory().copyToDir(outDir, "classes.dex"); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); } - } + } - public boolean buildManifestRaw(ExtFile appDir, HashMap flags) - throws AndrolibException { - try { - File apkDir = new File(appDir, APK_DIRNAME); - LOGGER.info("Copying raw AndroidManifest.xml..."); - appDir.getDirectory() - .copyToDir(apkDir, APK_MANIFEST_FILENAMES); - return true; - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + public void decodeSourcesSmali(File apkFile, File outDir, boolean debug, + boolean bakdeb) throws AndrolibException { + try { + File smaliDir = new File(outDir, SMALI_DIRNAME); + OS.rmdir(smaliDir); + smaliDir.mkdirs(); + LOGGER.info("Baksmaling..."); + SmaliDecoder.decode(apkFile, smaliDir, debug, bakdeb); + } catch (BrutException ex) { + throw new AndrolibException(ex); + } + } - public boolean buildManifest(ExtFile appDir, HashMap flags, - Map usesFramework) - throws BrutException { - try { - if (! new File(appDir, "AndroidManifest.xml").exists()) { - return false; - } - if (! flags.get("forceBuildAll")) { - LOGGER.info("Checking whether resources has changed..."); - } - File apkDir = new File(appDir, APK_DIRNAME); - if (flags.get("forceBuildAll") || isModified( - newFiles(APK_MANIFEST_FILENAMES, appDir), - newFiles(APK_MANIFEST_FILENAMES, apkDir))) { - LOGGER.info("Building AndroidManifest.xml..."); + public void decodeSourcesJava(ExtFile apkFile, File outDir, boolean debug) + throws AndrolibException { + LOGGER.info("Decoding Java sources..."); + new AndrolibJava().decode(apkFile, outDir); + } - File apkFile = File.createTempFile("APKTOOL", null); - apkFile.delete(); + public void decodeManifestRaw(ExtFile apkFile, File outDir) + throws AndrolibException { + try { + Directory apk = apkFile.getDirectory(); + LOGGER.info("Copying raw manifest..."); + apkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - File ninePatch = new File(appDir, "9patch"); - if (! ninePatch.exists()) { - ninePatch = null; - } + public void decodeManifestFull(ExtFile apkFile, File outDir, + ResTable resTable) throws AndrolibException { + mAndRes.decodeManifest(resTable, apkFile, outDir); + } - mAndRes.aaptPackage( - apkFile, - new File(appDir, "AndroidManifest.xml"), - null, - ninePatch, null, parseUsesFramework(usesFramework), - flags, mAaptPath - ); + public void decodeResourcesRaw(ExtFile apkFile, File outDir) + throws AndrolibException { + try { + // Directory apk = apkFile.getDirectory(); + LOGGER.info("Copying raw resources..."); + apkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - Directory tmpDir = new ExtFile(apkFile).getDirectory(); - tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES); - - } - return true; - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } catch (AndrolibException ex) { - LOGGER.warning("Parse AndroidManifest.xml failed, treat it as raw file."); - return buildManifestRaw(appDir, flags); - } - } + public void decodeResourcesFull(ExtFile apkFile, File outDir, + ResTable resTable) throws AndrolibException { + mAndRes.decode(resTable, apkFile, outDir); + } - public void buildLib(File appDir, HashMap flags) - throws AndrolibException { - File working = new File(appDir, "lib"); - if (! working.exists()) { - return; - } - File stored = new File(appDir, APK_DIRNAME + "/lib"); - if (flags.get("forceBuildAll") || isModified(working, stored)) { - LOGGER.info("Copying libs..."); - try { - OS.rmdir(stored); - OS.cpdir(working, stored); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } - } + public void decodeRawFiles(ExtFile apkFile, File outDir) + throws AndrolibException { + LOGGER.info("Copying assets and libs..."); + try { + Directory in = apkFile.getDirectory(); + if (in.containsDir("assets")) { + in.copyToDir(outDir, "assets"); + } + if (in.containsDir("lib")) { + in.copyToDir(outDir, "lib"); + } + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - public void buildApk(File appDir, File outApk, HashMap flags) - throws AndrolibException { - LOGGER.info("Building apk file..."); - if (outApk.exists()) { - outApk.delete(); - } else { - File outDir = outApk.getParentFile(); - if (outDir != null && ! outDir.exists()) { - outDir.mkdirs(); - } - } - File assetDir = new File(appDir, "assets"); - if (! assetDir.exists()) { - assetDir = null; - } - mAndRes.aaptPackage(outApk, null, null, - new File(appDir, APK_DIRNAME), assetDir, null, flags, mAaptPath); - - // retain signature - // aapt r (remove) - // aapt a (add) - if (flags.get("injectOriginal")) { - LOGGER.info("Injecting contents isn't ready yet. Sorry :("); - } - } + public void writeMetaFile(File mOutDir, Map meta) + throws AndrolibException { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + // options.setIndent(4); + Yaml yaml = new Yaml(options); - public void publicizeResources(File arscFile) throws AndrolibException { - mAndRes.publicizeResources(arscFile); - } + FileWriter writer = null; + try { + writer = new FileWriter(new File(mOutDir, "apktool.yml")); + yaml.dump(meta, writer); + } catch (IOException ex) { + throw new AndrolibException(ex); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException ex) { + } + } + } + } - public void installFramework(File frameFile, String tag, String frame_path) - throws AndrolibException { - mAndRes.setFrameworkFolder(frame_path); - mAndRes.installFramework(frameFile, tag); - } + public Map readMetaFile(ExtFile appDir) + throws AndrolibException { + InputStream in = null; + try { + in = appDir.getDirectory().getFileInput("apktool.yml"); + Yaml yaml = new Yaml(); + return (Map) yaml.load(in); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ex) { + } + } + } + } - public boolean isFrameworkApk(ResTable resTable) { - for (ResPackage pkg : resTable.listMainPackages()) { - if (pkg.getId() < 64) { - return true; - } - } - return false; - } + public void build(File appDir, File outFile, + HashMap flags, ExtFile origApk, String aaptPath) + throws BrutException { + build(new ExtFile(appDir), outFile, flags, origApk, aaptPath); + } - public static String getVersion() { - String version = ApktoolProperties.get("application.version"); - return version.endsWith("-SNAPSHOT") ? - version.substring(0, version.length() - 9) + '.' + - ApktoolProperties.get("git.commit.id.abbrev") - : version; - } + public void build(ExtFile appDir, File outFile, + HashMap flags, ExtFile origApk, String aaptPath) + throws BrutException { - private File[] parseUsesFramework(Map usesFramework) - throws AndrolibException { - if (usesFramework == null) { - return null; - } + mAaptPath = aaptPath; + Map meta = readMetaFile(appDir); + Object t1 = meta.get("isFrameworkApk"); + flags.put("framework", t1 == null ? false : (Boolean) t1); + flags.put("compression", meta.get("compressionType") == null ? false + : (Boolean) meta.get("compressionType")); + mAndRes.setSdkInfo((Map) meta.get("sdkInfo")); - List ids = (List) usesFramework.get("ids"); - if (ids == null || ids.isEmpty()) { - return null; - } + // check the orig apk + if (flags.get("injectOriginal")) { + if (!origApk.isFile() || !origApk.canRead()) { + throw new InFileNotFoundException(); + } else { + mOrigApkFile = origApk; + } + } - String tag = (String) usesFramework.get("tag"); - File[] files = new File[ids.size()]; - int i = 0; - for (int id : ids) { - files[i++] = mAndRes.getFrameworkApk(id, tag); - } - return files; - } + if (outFile == null) { + String outFileName = (String) meta.get("apkFileName"); + outFile = new File(appDir, "dist" + File.separator + + (outFileName == null ? "out.apk" : outFileName)); + } - private boolean isModified(File working, File stored) { - if (! stored.exists()) { - return true; - } - return BrutIO.recursiveModifiedTime(working) > - BrutIO.recursiveModifiedTime(stored); - } + new File(appDir, APK_DIRNAME).mkdirs(); + buildSources(appDir, flags); + buildResources(appDir, flags, + (Map) meta.get("usesFramework")); + buildLib(appDir, flags); + buildApk(appDir, outFile, flags); + } - private boolean isModified(File[] working, File[] stored) { - for (int i = 0; i < stored.length; i++) { - if (! stored[i].exists()) { - return true; - } - } - return BrutIO.recursiveModifiedTime(working) > - BrutIO.recursiveModifiedTime(stored); - } + public void buildSources(File appDir, HashMap flags) + throws AndrolibException { + if (!buildSourcesRaw(appDir, flags) + && !buildSourcesSmali(appDir, flags) + && !buildSourcesJava(appDir, flags)) { + LOGGER.warning("Could not find sources"); + } + } - private File[] newFiles(String[] names, File dir) { - File[] files = new File[names.length]; - for (int i = 0; i < names.length; i++) { - files[i] = new File(dir, names[i]); - } - return files; - } - - public void setApkFile(File apkFile) { - mOrigApkFile = new ExtFile(apkFile); - } - + public boolean buildSourcesRaw(File appDir, HashMap flags) + throws AndrolibException { + try { + File working = new File(appDir, "classes.dex"); + if (!working.exists()) { + return false; + } + if (flags.get("debug")) { + LOGGER.warning("Debug mode not available."); + } + File stored = new File(appDir, APK_DIRNAME + "/classes.dex"); + if (flags.get("forceBuildAll") || isModified(working, stored)) { + LOGGER.info("Copying classes.dex file..."); + BrutIO.copyAndClose(new FileInputStream(working), + new FileOutputStream(stored)); + } + return true; + } catch (IOException ex) { + throw new AndrolibException(ex); + } + } - private ExtFile mOrigApkFile = null; - private String mAaptPath = null; - - private final static Logger LOGGER = - Logger.getLogger(Androlib.class.getName()); + public boolean buildSourcesSmali(File appDir, HashMap flags) + throws AndrolibException { + ExtFile smaliDir = new ExtFile(appDir, "smali"); + if (!smaliDir.exists()) { + return false; + } + File dex = new File(appDir, APK_DIRNAME + "/classes.dex"); + if (!flags.get("forceBuildAll")) { + LOGGER.info("Checking whether sources has changed..."); + } + if (flags.get("forceBuildAll") || isModified(smaliDir, dex)) { + LOGGER.info("Smaling..."); + dex.delete(); + SmaliBuilder.build(smaliDir, dex, flags); + } + return true; + } - private final static String SMALI_DIRNAME = "smali"; - private final static String APK_DIRNAME = "build/apk"; - private final static String[] APK_RESOURCES_FILENAMES = - new String[]{"resources.arsc", "AndroidManifest.xml", "res"}; - private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = - new String[]{"resources.arsc", "AndroidManifest.xml"}; - private final static String[] APP_RESOURCES_FILENAMES = - new String[]{"AndroidManifest.xml", "res"}; - private final static String[] APK_MANIFEST_FILENAMES = - new String[]{"AndroidManifest.xml"}; + public boolean buildSourcesJava(File appDir, HashMap flags) + throws AndrolibException { + File javaDir = new File(appDir, "src"); + if (!javaDir.exists()) { + return false; + } + File dex = new File(appDir, APK_DIRNAME + "/classes.dex"); + if (!flags.get("forceBuildAll")) { + LOGGER.info("Checking whether sources has changed..."); + } + if (flags.get("forceBuildAll") || isModified(javaDir, dex)) { + LOGGER.info("Building java sources..."); + dex.delete(); + new AndrolibJava().build(javaDir, dex); + } + return true; + } + + public void buildResources(ExtFile appDir, HashMap flags, + Map usesFramework) throws BrutException { + if (!buildResourcesRaw(appDir, flags) + && !buildResourcesFull(appDir, flags, usesFramework) + && !buildManifest(appDir, flags, usesFramework)) { + LOGGER.warning("Could not find resources"); + } + } + + public boolean buildResourcesRaw(ExtFile appDir, + HashMap flags) throws AndrolibException { + try { + if (!new File(appDir, "resources.arsc").exists()) { + return false; + } + File apkDir = new File(appDir, APK_DIRNAME); + if (!flags.get("forceBuildAll")) { + LOGGER.info("Checking whether resources has changed..."); + } + if (flags.get("forceBuildAll") + || isModified(newFiles(APK_RESOURCES_FILENAMES, appDir), + newFiles(APK_RESOURCES_FILENAMES, apkDir))) { + LOGGER.info("Copying raw resources..."); + appDir.getDirectory() + .copyToDir(apkDir, APK_RESOURCES_FILENAMES); + } + return true; + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } + + public boolean buildResourcesFull(File appDir, + HashMap flags, Map usesFramework) + throws AndrolibException { + try { + if (!new File(appDir, "res").exists()) { + return false; + } + if (!flags.get("forceBuildAll")) { + LOGGER.info("Checking whether resources has changed..."); + } + File apkDir = new File(appDir, APK_DIRNAME); + if (flags.get("forceBuildAll") + || isModified(newFiles(APP_RESOURCES_FILENAMES, appDir), + newFiles(APK_RESOURCES_FILENAMES, apkDir))) { + LOGGER.info("Building resources..."); + + File apkFile = File.createTempFile("APKTOOL", null); + apkFile.delete(); + + File ninePatch = new File(appDir, "9patch"); + if (!ninePatch.exists()) { + ninePatch = null; + } + mAndRes.aaptPackage(apkFile, new File(appDir, + "AndroidManifest.xml"), new File(appDir, "res"), + ninePatch, null, parseUsesFramework(usesFramework), + flags, mAaptPath); + + Directory tmpDir = new ExtFile(apkFile).getDirectory(); + tmpDir.copyToDir(apkDir, + tmpDir.containsDir("res") ? APK_RESOURCES_FILENAMES + : APK_RESOURCES_WITHOUT_RES_FILENAMES); + + // delete tmpDir + apkFile.delete(); + } + return true; + } catch (IOException ex) { + throw new AndrolibException(ex); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } catch (BrutException ex) { + throw new AndrolibException(ex); + } + } + + public boolean buildManifestRaw(ExtFile appDir, + HashMap flags) throws AndrolibException { + try { + File apkDir = new File(appDir, APK_DIRNAME); + LOGGER.info("Copying raw AndroidManifest.xml..."); + appDir.getDirectory().copyToDir(apkDir, APK_MANIFEST_FILENAMES); + return true; + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } + + public boolean buildManifest(ExtFile appDir, + HashMap flags, Map usesFramework) + throws BrutException { + try { + if (!new File(appDir, "AndroidManifest.xml").exists()) { + return false; + } + if (!flags.get("forceBuildAll")) { + LOGGER.info("Checking whether resources has changed..."); + } + File apkDir = new File(appDir, APK_DIRNAME); + if (flags.get("forceBuildAll") + || isModified(newFiles(APK_MANIFEST_FILENAMES, appDir), + newFiles(APK_MANIFEST_FILENAMES, apkDir))) { + LOGGER.info("Building AndroidManifest.xml..."); + + File apkFile = File.createTempFile("APKTOOL", null); + apkFile.delete(); + + File ninePatch = new File(appDir, "9patch"); + if (!ninePatch.exists()) { + ninePatch = null; + } + + mAndRes.aaptPackage(apkFile, new File(appDir, + "AndroidManifest.xml"), null, ninePatch, null, + parseUsesFramework(usesFramework), flags, mAaptPath); + + Directory tmpDir = new ExtFile(apkFile).getDirectory(); + tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES); + + } + return true; + } catch (IOException ex) { + throw new AndrolibException(ex); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } catch (AndrolibException ex) { + LOGGER.warning("Parse AndroidManifest.xml failed, treat it as raw file."); + return buildManifestRaw(appDir, flags); + } + } + + public void buildLib(File appDir, HashMap flags) + throws AndrolibException { + File working = new File(appDir, "lib"); + if (!working.exists()) { + return; + } + File stored = new File(appDir, APK_DIRNAME + "/lib"); + if (flags.get("forceBuildAll") || isModified(working, stored)) { + LOGGER.info("Copying libs..."); + try { + OS.rmdir(stored); + OS.cpdir(working, stored); + } catch (BrutException ex) { + throw new AndrolibException(ex); + } + } + } + + public void buildApk(File appDir, File outApk, + HashMap flags) throws AndrolibException { + LOGGER.info("Building apk file..."); + if (outApk.exists()) { + outApk.delete(); + } else { + File outDir = outApk.getParentFile(); + if (outDir != null && !outDir.exists()) { + outDir.mkdirs(); + } + } + File assetDir = new File(appDir, "assets"); + if (!assetDir.exists()) { + assetDir = null; + } + mAndRes.aaptPackage(outApk, null, null, new File(appDir, APK_DIRNAME), + assetDir, null, flags, mAaptPath); + + // retain signature + // aapt r (remove) + // aapt a (add) + if (flags.get("injectOriginal")) { + LOGGER.info("Injecting contents isn't ready yet. Sorry :("); + } + } + + public void publicizeResources(File arscFile) throws AndrolibException { + mAndRes.publicizeResources(arscFile); + } + + public void installFramework(File frameFile, String tag, String frame_path) + throws AndrolibException { + mAndRes.setFrameworkFolder(frame_path); + mAndRes.installFramework(frameFile, tag); + } + + public boolean isFrameworkApk(ResTable resTable) { + for (ResPackage pkg : resTable.listMainPackages()) { + if (pkg.getId() < 64) { + return true; + } + } + return false; + } + + public static String getVersion() { + String version = ApktoolProperties.get("application.version"); + return version.endsWith("-SNAPSHOT") ? version.substring(0, + version.length() - 9) + + '.' + ApktoolProperties.get("git.commit.id.abbrev") : version; + } + + private File[] parseUsesFramework(Map usesFramework) + throws AndrolibException { + if (usesFramework == null) { + return null; + } + + List ids = (List) usesFramework.get("ids"); + if (ids == null || ids.isEmpty()) { + return null; + } + + String tag = (String) usesFramework.get("tag"); + File[] files = new File[ids.size()]; + int i = 0; + for (int id : ids) { + files[i++] = mAndRes.getFrameworkApk(id, tag); + } + return files; + } + + private boolean isModified(File working, File stored) { + if (!stored.exists()) { + return true; + } + return BrutIO.recursiveModifiedTime(working) > BrutIO + .recursiveModifiedTime(stored); + } + + private boolean isModified(File[] working, File[] stored) { + for (int i = 0; i < stored.length; i++) { + if (!stored[i].exists()) { + return true; + } + } + return BrutIO.recursiveModifiedTime(working) > BrutIO + .recursiveModifiedTime(stored); + } + + private File[] newFiles(String[] names, File dir) { + File[] files = new File[names.length]; + for (int i = 0; i < names.length; i++) { + files[i] = new File(dir, names[i]); + } + return files; + } + + public void setApkFile(File apkFile) { + mOrigApkFile = new ExtFile(apkFile); + } + + private ExtFile mOrigApkFile = null; + private String mAaptPath = null; + + private final static Logger LOGGER = Logger.getLogger(Androlib.class + .getName()); + + private final static String SMALI_DIRNAME = "smali"; + private final static String APK_DIRNAME = "build/apk"; + private final static String[] APK_RESOURCES_FILENAMES = new String[] { + "resources.arsc", "AndroidManifest.xml", "res" }; + private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = new String[] { + "resources.arsc", "AndroidManifest.xml" }; + private final static String[] APP_RESOURCES_FILENAMES = new String[] { + "AndroidManifest.xml", "res" }; + private final static String[] APK_MANIFEST_FILENAMES = new String[] { "AndroidManifest.xml" }; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/AndrolibException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/AndrolibException.java index d1601341..a68e4680 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/AndrolibException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/AndrolibException.java @@ -22,18 +22,18 @@ import brut.common.BrutException; * @author Ryszard Wiśniewski */ public class AndrolibException extends BrutException { - public AndrolibException() { - } + public AndrolibException() { + } - public AndrolibException(String message) { - super(message); - } + public AndrolibException(String message) { + super(message); + } - public AndrolibException(String message, Throwable cause) { - super(message, cause); - } + public AndrolibException(String message, Throwable cause) { + super(message, cause); + } - public AndrolibException(Throwable cause) { - super(cause); - } + public AndrolibException(Throwable cause) { + super(cause); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java index 6ebe348b..672b2b72 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java @@ -95,17 +95,19 @@ public class ApkDecoder { } if (hasResources()) { - - // read the resources.arsc checking for STORED vs DEFLATE compression + + // read the resources.arsc checking for STORED vs DEFLATE + // compression // this will determine whether we compress on rebuild or not. JarFile jf = new JarFile(mApkFile.getAbsoluteFile()); - JarEntry je = jf.getJarEntry("resources.arsc"); - if (je != null) { - int compression = je.getMethod(); - mCompressResources = (compression != ZipEntry.STORED) && (compression == ZipEntry.DEFLATED); + JarEntry je = jf.getJarEntry("resources.arsc"); + if (je != null) { + int compression = je.getMethod(); + mCompressResources = (compression != ZipEntry.STORED) + && (compression == ZipEntry.DEFLATED); } jf.close(); - + switch (mDecodeResources) { case DECODE_RESOURCES_NONE: mAndrolib.decodeResourcesRaw(mApkFile, outDir); @@ -286,12 +288,12 @@ public class ApkDecoder { meta.put("packageInfo", info); } } - + private void putCompressionInfo(Map meta) throws AndrolibException { meta.put("compressionType", getCompressionType()); } - + private boolean getCompressionType() { return mCompressResources; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java index e72b7992..01be7808 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java @@ -20,56 +20,57 @@ import java.io.IOException; import java.io.InputStream; import java.util.Properties; import java.util.logging.Logger; -import org.jf.baksmali.baksmali; -import org.jf.smali.main; /** * @author Ryszard Wiśniewski */ public class ApktoolProperties { - public static String get(String key) { - return get().getProperty(key); - } - - public static Properties get() { - if (sProps == null) { - loadProps(); - } - return sProps; - } + public static String get(String key) { + return get().getProperty(key); + } - private static void loadProps() { - InputStream in = ApktoolProperties.class.getResourceAsStream("/properties/apktool.properties"); - sProps = new Properties(); - try { - sProps.load(in); - in.close(); - } catch (IOException ex) { - LOGGER.warning("Can't load properties."); - } + public static Properties get() { + if (sProps == null) { + loadProps(); + } + return sProps; + } - InputStream templateStream = ApktoolProperties.class.getResourceAsStream("/properties/baksmali.properties"); - Properties properties = new Properties(); - String version = "(unknown)"; - try { - properties.load(templateStream); - version = properties.getProperty("application.version"); - } catch (IOException ex) { - } - sProps.put("baksmaliVersion", version); - templateStream = ApktoolProperties.class.getResourceAsStream("/properties/smali.properties"); - properties = new Properties(); - version = "(unknown)"; - try { - properties.load(templateStream); - version = properties.getProperty("application.version"); - } catch (IOException ex) { - } - sProps.put("smaliVersion", version); - } - - private static Properties sProps; - - private static final Logger LOGGER = - Logger.getLogger(ApktoolProperties.class.getName()); + private static void loadProps() { + InputStream in = ApktoolProperties.class + .getResourceAsStream("/properties/apktool.properties"); + sProps = new Properties(); + try { + sProps.load(in); + in.close(); + } catch (IOException ex) { + LOGGER.warning("Can't load properties."); + } + + InputStream templateStream = ApktoolProperties.class + .getResourceAsStream("/properties/baksmali.properties"); + Properties properties = new Properties(); + String version = "(unknown)"; + try { + properties.load(templateStream); + version = properties.getProperty("application.version"); + } catch (IOException ex) { + } + sProps.put("baksmaliVersion", version); + templateStream = ApktoolProperties.class + .getResourceAsStream("/properties/smali.properties"); + properties = new Properties(); + version = "(unknown)"; + try { + properties.load(templateStream); + version = properties.getProperty("application.version"); + } catch (IOException ex) { + } + sProps.put("smaliVersion", version); + } + + private static Properties sProps; + + private static final Logger LOGGER = Logger + .getLogger(ApktoolProperties.class.getName()); } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/CantFind9PatchChunk.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/CantFind9PatchChunk.java index 0e80bc66..90c2634a 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/CantFind9PatchChunk.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/CantFind9PatchChunk.java @@ -23,18 +23,18 @@ import brut.androlib.AndrolibException; */ public class CantFind9PatchChunk extends AndrolibException { - public CantFind9PatchChunk(Throwable cause) { - super(cause); - } + public CantFind9PatchChunk(Throwable cause) { + super(cause); + } - public CantFind9PatchChunk(String message, Throwable cause) { - super(message, cause); - } + public CantFind9PatchChunk(String message, Throwable cause) { + super(message, cause); + } - public CantFind9PatchChunk(String message) { - super(message); - } + public CantFind9PatchChunk(String message) { + super(message); + } - public CantFind9PatchChunk() { - } + public CantFind9PatchChunk() { + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/CantFindFrameworkResException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/CantFindFrameworkResException.java index 0c0c10e3..4b05bd96 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/CantFindFrameworkResException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/CantFindFrameworkResException.java @@ -23,18 +23,18 @@ import brut.androlib.AndrolibException; */ public class CantFindFrameworkResException extends AndrolibException { - public CantFindFrameworkResException(Throwable cause, int id) { - super(cause); - mPkgId = id; - } + public CantFindFrameworkResException(Throwable cause, int id) { + super(cause); + mPkgId = id; + } - public CantFindFrameworkResException(int id) { - mPkgId = id; - } + public CantFindFrameworkResException(int id) { + mPkgId = id; + } - public int getPkgId() { - return mPkgId; - } + public int getPkgId() { + return mPkgId; + } - private final int mPkgId; + private final int mPkgId; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/InFileNotFoundException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/InFileNotFoundException.java index af8de523..2abcf87b 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/InFileNotFoundException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/InFileNotFoundException.java @@ -23,18 +23,18 @@ import brut.androlib.AndrolibException; */ public class InFileNotFoundException extends AndrolibException { - public InFileNotFoundException(Throwable cause) { - super(cause); - } + public InFileNotFoundException(Throwable cause) { + super(cause); + } - public InFileNotFoundException(String message, Throwable cause) { - super(message, cause); - } + public InFileNotFoundException(String message, Throwable cause) { + super(message, cause); + } - public InFileNotFoundException(String message) { - super(message); - } + public InFileNotFoundException(String message) { + super(message); + } - public InFileNotFoundException() { - } + public InFileNotFoundException() { + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/OutDirExistsException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/OutDirExistsException.java index 59b26acd..affd3d74 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/OutDirExistsException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/OutDirExistsException.java @@ -23,18 +23,18 @@ import brut.androlib.AndrolibException; */ public class OutDirExistsException extends AndrolibException { - public OutDirExistsException(Throwable cause) { - super(cause); - } + public OutDirExistsException(Throwable cause) { + super(cause); + } - public OutDirExistsException(String message, Throwable cause) { - super(message, cause); - } + public OutDirExistsException(String message, Throwable cause) { + super(message, cause); + } - public OutDirExistsException(String message) { - super(message); - } + public OutDirExistsException(String message) { + super(message); + } - public OutDirExistsException() { - } + public OutDirExistsException() { + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/UndefinedResObject.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/UndefinedResObject.java index 3bcfa356..207a4cd9 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/UndefinedResObject.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/UndefinedResObject.java @@ -22,18 +22,18 @@ import brut.androlib.AndrolibException; * @author Ryszard Wiśniewski */ public class UndefinedResObject extends AndrolibException { - public UndefinedResObject(Throwable cause) { - super(cause); - } + public UndefinedResObject(Throwable cause) { + super(cause); + } - public UndefinedResObject(String message, Throwable cause) { - super(message, cause); - } + public UndefinedResObject(String message, Throwable cause) { + super(message, cause); + } - public UndefinedResObject(String message) { - super(message); - } + public UndefinedResObject(String message) { + super(message); + } - public UndefinedResObject() { - } + public UndefinedResObject() { + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/java/AndrolibJava.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/java/AndrolibJava.java index f3be8f7e..7161ffe7 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/java/AndrolibJava.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/java/AndrolibJava.java @@ -24,11 +24,11 @@ import java.io.File; */ public class AndrolibJava { - public void decode(ExtFile apkFile, File outDir) { - throw new UnsupportedOperationException("Not yet implemented"); - } + public void decode(ExtFile apkFile, File outDir) { + throw new UnsupportedOperationException("Not yet implemented"); + } - public void build(File javaDir, File dex) { - throw new UnsupportedOperationException("Not yet implemented"); - } + public void build(File javaDir, File dex) { + throw new UnsupportedOperationException("Not yet implemented"); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/IndentingWriter.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/IndentingWriter.java index 99b1ce21..8f0cc226 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/IndentingWriter.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/IndentingWriter.java @@ -23,7 +23,7 @@ import java.io.Writer; */ public class IndentingWriter extends org.jf.util.IndentingWriter { - public IndentingWriter(Writer writer) { - super(writer); - } + public IndentingWriter(Writer writer) { + super(writer); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java index 591030e9..c65faac1 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java @@ -28,57 +28,58 @@ import org.jf.smali.*; */ public class SmaliMod { - public static boolean assembleSmaliFile(InputStream smaliStream, - String name, DexFile dexFile, boolean verboseErrors, - boolean oldLexer, boolean printTokens) - throws IOException, RecognitionException { - CommonTokenStream tokens; + public static boolean assembleSmaliFile(InputStream smaliStream, + String name, DexFile dexFile, boolean verboseErrors, + boolean oldLexer, boolean printTokens) throws IOException, + RecognitionException { + CommonTokenStream tokens; + boolean lexerErrors = false; + LexerErrorInterface lexer; - boolean lexerErrors = false; - LexerErrorInterface lexer; + InputStreamReader reader = new InputStreamReader(smaliStream, "UTF-8"); - InputStreamReader reader = new InputStreamReader(smaliStream, "UTF-8"); + lexer = new smaliFlexLexer(reader); + tokens = new CommonTokenStream((TokenSource) lexer); - lexer = new smaliFlexLexer(reader); - tokens = new CommonTokenStream((TokenSource)lexer); + if (printTokens) { + tokens.getTokens(); - if (printTokens) { - tokens.getTokens(); + for (int i = 0; i < tokens.size(); i++) { + Token token = tokens.get(i); + if (token.getChannel() == BaseRecognizer.HIDDEN) { + continue; + } - for (int i=0; i 0 + || lexer.getNumberOfSyntaxErrors() > 0) { + return false; + } - if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) { - return false; - } + CommonTree t = (CommonTree) result.getTree(); - CommonTree t = (CommonTree) result.getTree(); + CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); + treeStream.setTokenStream(tokens); - CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); - treeStream.setTokenStream(tokens); + smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); - smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); + dexGen.dexFile = dexFile; + dexGen.smali_file(); - dexGen.dexFile = dexFile; - dexGen.smali_file(); + if (dexGen.getNumberOfSyntaxErrors() > 0) { + return false; + } - if (dexGen.getNumberOfSyntaxErrors() > 0) { - return false; - } - - return true; - } + return true; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java index b49c6988..2553b226 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java @@ -145,25 +145,27 @@ final public class AndrolibResources { LOGGER.info("Decoding AndroidManifest.xml with only framework resources..."); fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out, "AndroidManifest.xml"); - + } catch (DirectoryException ex) { throw new AndrolibException(ex); } } - - public void adjust_package_manifest(ResTable resTable, String filePath) + + public void adjust_package_manifest(ResTable resTable, String filePath) throws AndrolibException { - // check if packages different, and that package is not equal to "android" + // check if packages different, and that package is not equal to + // "android" Map packageInfo = resTable.getPackageInfo(); - if ((packageInfo.get("cur_package").equalsIgnoreCase(packageInfo.get("orig_package")) - || ("android".equalsIgnoreCase(packageInfo.get("cur_package")) - || ("com.htc".equalsIgnoreCase(packageInfo.get("cur_package")))))) { - - LOGGER.info("Regular manifest package..."); - } else { + if ((packageInfo.get("cur_package").equalsIgnoreCase( + packageInfo.get("orig_package")) || ("android" + .equalsIgnoreCase(packageInfo.get("cur_package")) || ("com.htc" + .equalsIgnoreCase(packageInfo.get("cur_package")))))) { + + LOGGER.info("Regular manifest package..."); + } else { try { - + LOGGER.info("Renamed manifest package found! Fixing..."); DocumentBuilderFactory docFactory = DocumentBuilderFactory .newInstance(); @@ -178,15 +180,16 @@ final public class AndrolibResources { Node nodeAttr = attr.getNamedItem("package"); mPackageRenamed = nodeAttr.getNodeValue(); nodeAttr.setNodeValue(packageInfo.get("cur_package")); - + // re-save manifest. // fancy an auto-sort :p - TransformerFactory transformerFactory = TransformerFactory.newInstance(); + TransformerFactory transformerFactory = TransformerFactory + .newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource source = new DOMSource(doc); StreamResult result = new StreamResult(new File(filePath)); transformer.transform(source, result); - + } catch (ParserConfigurationException ex) { throw new AndrolibException(ex); } catch (TransformerException ex) { @@ -217,9 +220,10 @@ final public class AndrolibResources { fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out, "AndroidManifest.xml"); - + // fix package if needed - adjust_package_manifest(resTable, outDir.getAbsolutePath() + "/AndroidManifest.xml"); + adjust_package_manifest(resTable, outDir.getAbsolutePath() + + "/AndroidManifest.xml"); if (inApk.containsDir("res")) { in = inApk.getDir("res"); @@ -268,19 +272,21 @@ final public class AndrolibResources { public void aaptPackage(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include, - HashMap flags, String aaptPath) throws AndrolibException { + HashMap flags, String aaptPath) + throws AndrolibException { List cmd = new ArrayList(); - + // path for aapt binary if (!aaptPath.isEmpty()) { File aaptFile = new File(aaptPath); if (aaptFile.canRead() && aaptFile.exists()) { aaptFile.setExecutable(true); cmd.add(aaptFile.getPath()); - + if (flags.get("verbose")) { - LOGGER.info(aaptFile.getPath() + " being used as aapt location."); + LOGGER.info(aaptFile.getPath() + + " being used as aapt location."); } } else { LOGGER.warning("aapt location could not be found. Defaulting back to default"); @@ -323,10 +329,10 @@ final public class AndrolibResources { if (flags.get("framework")) { cmd.add("-x"); } - + if (!(flags.get("compression"))) { - cmd.add("-0"); - cmd.add("arsc"); + cmd.add("-0"); + cmd.add("arsc"); } if (include != null) { @@ -420,7 +426,7 @@ final public class AndrolibResources { " "); serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR, System.getProperty("line.separator")); - serial.setProperty(ExtMXSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8"); + serial.setProperty(ExtXmlSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8"); serial.setDisabledAttrEscape(true); return serial; } @@ -665,7 +671,8 @@ final public class AndrolibResources { if (!dir.exists()) { if (!dir.mkdirs()) { if (sFrameworkFolder != null) { - System.out.println("Can't create Framework directory: " + dir); + System.out.println("Can't create Framework directory: " + + dir); } throw new AndrolibException("Can't create directory: " + dir); } @@ -681,7 +688,7 @@ final public class AndrolibResources { throw new AndrolibException(ex); } } - + public void setFrameworkFolder(String path) { sFrameworkFolder = path; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java index d2cd8eb5..22845a00 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java @@ -36,132 +36,128 @@ import org.apache.commons.io.IOUtils; * @author Ryszard Wiśniewski */ public class ResSmaliUpdater { - public void tagResIDs(ResTable resTable, File smaliDir) - throws AndrolibException { - Directory dir = null; - try { - dir = new FileDirectory(smaliDir); - } catch (DirectoryException ex) { - throw new AndrolibException( - "Could not tag res IDs", ex); - } - for (String fileName : dir.getFiles(true)) { - try { - tagResIdsForFile(resTable, dir, fileName); - } catch (IOException ex) { - throw new AndrolibException( - "Could not tag resIDs for file: " + fileName, ex); - } catch (DirectoryException ex) { - throw new AndrolibException( - "Could not tag resIDs for file: " + fileName, ex); - } catch (AndrolibException ex) { - throw new AndrolibException( - "Could not tag resIDs for file: " + fileName, ex); - } - } - } + public void tagResIDs(ResTable resTable, File smaliDir) + throws AndrolibException { + Directory dir = null; + try { + dir = new FileDirectory(smaliDir); + } catch (DirectoryException ex) { + throw new AndrolibException("Could not tag res IDs", ex); + } + for (String fileName : dir.getFiles(true)) { + try { + tagResIdsForFile(resTable, dir, fileName); + } catch (IOException ex) { + throw new AndrolibException("Could not tag resIDs for file: " + + fileName, ex); + } catch (DirectoryException ex) { + throw new AndrolibException("Could not tag resIDs for file: " + + fileName, ex); + } catch (AndrolibException ex) { + throw new AndrolibException("Could not tag resIDs for file: " + + fileName, ex); + } + } + } - public void updateResIDs(ResTable resTable, File smaliDir) - throws AndrolibException { - try { - Directory dir = new FileDirectory(smaliDir); - for (String fileName : dir.getFiles(true)) { - Iterator it = - IOUtils.readLines(dir.getFileInput(fileName)).iterator(); - PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); - while (it.hasNext()) { - String line = it.next(); - out.println(line); - Matcher m1 = RES_NAME_PATTERN.matcher(line); - if (! m1.matches()) { - continue; - } - Matcher m2 = RES_ID_PATTERN.matcher(it.next()); - if (! m2.matches()) { - throw new AndrolibException(); - } - int resID = resTable.getPackage(m1.group(1)) - .getType(m1.group(2)).getResSpec(m1.group(3)) - .getId().id; - if (m2.group(1) != null) { - out.println(String.format( - RES_ID_FORMAT_FIELD, m2.group(1), resID)); - } else { - out.println(String.format( - RES_ID_FORMAT_CONST, m2.group(2), resID)); - } - } - out.close(); - } - } catch (IOException ex) { - throw new AndrolibException( - "Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex); - } catch (DirectoryException ex) { - throw new AndrolibException( - "Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex); - } - } + public void updateResIDs(ResTable resTable, File smaliDir) + throws AndrolibException { + try { + Directory dir = new FileDirectory(smaliDir); + for (String fileName : dir.getFiles(true)) { + Iterator it = IOUtils.readLines( + dir.getFileInput(fileName)).iterator(); + PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); + while (it.hasNext()) { + String line = it.next(); + out.println(line); + Matcher m1 = RES_NAME_PATTERN.matcher(line); + if (!m1.matches()) { + continue; + } + Matcher m2 = RES_ID_PATTERN.matcher(it.next()); + if (!m2.matches()) { + throw new AndrolibException(); + } + int resID = resTable.getPackage(m1.group(1)) + .getType(m1.group(2)).getResSpec(m1.group(3)) + .getId().id; + if (m2.group(1) != null) { + out.println(String.format(RES_ID_FORMAT_FIELD, + m2.group(1), resID)); + } else { + out.println(String.format(RES_ID_FORMAT_CONST, + m2.group(2), resID)); + } + } + out.close(); + } + } catch (IOException ex) { + throw new AndrolibException("Could not tag res IDs for: " + + smaliDir.getAbsolutePath(), ex); + } catch (DirectoryException ex) { + throw new AndrolibException("Could not tag res IDs for: " + + smaliDir.getAbsolutePath(), ex); + } + } - private void tagResIdsForFile(ResTable resTable, Directory dir, - String fileName) throws IOException, DirectoryException, - AndrolibException { - Iterator it = - IOUtils.readLines(dir.getFileInput(fileName)).iterator(); - PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); - while (it.hasNext()) { - String line = it.next(); - if (RES_NAME_PATTERN.matcher(line).matches()) { - out.println(line); - out.println(it.next()); - continue; - } - Matcher m = RES_ID_PATTERN.matcher(line); - if (m.matches()) { - int resID = parseResID(m.group(3)); - if (resID != -1) { - try { - ResResSpec spec = resTable.getResSpec(resID); - out.println(String.format( - RES_NAME_FORMAT, spec.getFullName())); - } catch (UndefinedResObject ex) { - if (! R_FILE_PATTERN.matcher(fileName).matches()) { - LOGGER.warning(String.format( - "Undefined resource spec in %s: 0x%08x" - , fileName, resID)); - } - } - } - } - out.println(line); - } - out.close(); - } + private void tagResIdsForFile(ResTable resTable, Directory dir, + String fileName) throws IOException, DirectoryException, + AndrolibException { + Iterator it = IOUtils.readLines(dir.getFileInput(fileName)) + .iterator(); + PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); + while (it.hasNext()) { + String line = it.next(); + if (RES_NAME_PATTERN.matcher(line).matches()) { + out.println(line); + out.println(it.next()); + continue; + } + Matcher m = RES_ID_PATTERN.matcher(line); + if (m.matches()) { + int resID = parseResID(m.group(3)); + if (resID != -1) { + try { + ResResSpec spec = resTable.getResSpec(resID); + out.println(String.format(RES_NAME_FORMAT, + spec.getFullName())); + } catch (UndefinedResObject ex) { + if (!R_FILE_PATTERN.matcher(fileName).matches()) { + LOGGER.warning(String.format( + "Undefined resource spec in %s: 0x%08x", + fileName, resID)); + } + } + } + } + out.println(line); + } + out.close(); + } - private int parseResID(String resIDHex) { - if (resIDHex.endsWith("ff")) { - return -1; - } - int resID = Integer.valueOf(resIDHex, 16); - if (resIDHex.length() == 4) { - resID = resID << 16; - } - return resID; - } + private int parseResID(String resIDHex) { + if (resIDHex.endsWith("ff")) { + return -1; + } + int resID = Integer.valueOf(resIDHex, 16); + if (resIDHex.length() == 4) { + resID = resID << 16; + } + return resID; + } - private final static String RES_ID_FORMAT_FIELD = - ".field %s:I = 0x%08x"; - private final static String RES_ID_FORMAT_CONST = - " const %s, 0x%08x"; - private final static Pattern RES_ID_PATTERN = Pattern.compile( - "^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$"); - private final static String RES_NAME_FORMAT = - "# APKTOOL/RES_NAME: %s"; - private final static Pattern RES_NAME_PATTERN = Pattern.compile( - "^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$"); + private final static String RES_ID_FORMAT_FIELD = ".field %s:I = 0x%08x"; + private final static String RES_ID_FORMAT_CONST = " const %s, 0x%08x"; + private final static Pattern RES_ID_PATTERN = Pattern + .compile("^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$"); + private final static String RES_NAME_FORMAT = "# APKTOOL/RES_NAME: %s"; + private final static Pattern RES_NAME_PATTERN = Pattern + .compile("^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$"); - private final static Pattern R_FILE_PATTERN = Pattern.compile( - ".*R\\$[a-z]+\\.smali$"); + private final static Pattern R_FILE_PATTERN = Pattern + .compile(".*R\\$[a-z]+\\.smali$"); - private final static Logger LOGGER = - Logger.getLogger(ResSmaliUpdater.class.getName()); + private final static Logger LOGGER = Logger.getLogger(ResSmaliUpdater.class + .getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java index 917ec504..2d7f7bbd 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java @@ -24,51 +24,49 @@ import java.util.*; * @author Ryszard Wiśniewski */ public class ResConfig { - private final ResConfigFlags mFlags; - private final Map mResources = - new LinkedHashMap(); + private final ResConfigFlags mFlags; + private final Map mResources = new LinkedHashMap(); - public ResConfig(ResConfigFlags flags) { - this.mFlags = flags; - } + public ResConfig(ResConfigFlags flags) { + this.mFlags = flags; + } - public Set listResources() { - return new LinkedHashSet(mResources.values()); - } + public Set listResources() { + return new LinkedHashSet(mResources.values()); + } - public ResResource getResource(ResResSpec spec) throws AndrolibException { - ResResource res = mResources.get(spec); - if (res == null) { - throw new UndefinedResObject(String.format( - "resource: spec=%s, config=%s", spec, this)); - } - return res; - } + public ResResource getResource(ResResSpec spec) throws AndrolibException { + ResResource res = mResources.get(spec); + if (res == null) { + throw new UndefinedResObject(String.format( + "resource: spec=%s, config=%s", spec, this)); + } + return res; + } - public Set listResSpecs() { - return mResources.keySet(); - } + public Set listResSpecs() { + return mResources.keySet(); + } - public ResConfigFlags getFlags() { - return mFlags; - } + public ResConfigFlags getFlags() { + return mFlags; + } - public void addResource(ResResource res) - throws AndrolibException { - addResource(res, false); - } + public void addResource(ResResource res) throws AndrolibException { + addResource(res, false); + } - public void addResource(ResResource res, boolean overwrite) - throws AndrolibException { - ResResSpec spec = res.getResSpec(); - if (mResources.put(spec, res) != null && ! overwrite) { - throw new AndrolibException(String.format( - "Multiple resources: spec=%s, config=%s", spec, this)); - } - } + public void addResource(ResResource res, boolean overwrite) + throws AndrolibException { + ResResSpec spec = res.getResSpec(); + if (mResources.put(spec, res) != null && !overwrite) { + throw new AndrolibException(String.format( + "Multiple resources: spec=%s, config=%s", spec, this)); + } + } - @Override - public String toString() { - return mFlags.toString(); - } + @Override + public String toString() { + return mFlags.toString(); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java index 2e7a0b9b..28f18522 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java @@ -22,457 +22,456 @@ import java.util.logging.Logger; * @author Ryszard Wiśniewski */ public class ResConfigFlags { - public final short mcc; - public final short mnc; + public final short mcc; + public final short mnc; - public final char[] language; - public final char[] country; - - public final short layoutDirection; + public final char[] language; + public final char[] country; - public final byte orientation; - public final byte touchscreen; - public final short density; + public final short layoutDirection; - public final byte keyboard; - public final byte navigation; - public final byte inputFlags; + public final byte orientation; + public final byte touchscreen; + public final short density; - public final short screenWidth; - public final short screenHeight; + public final byte keyboard; + public final byte navigation; + public final byte inputFlags; - public final short sdkVersion; + public final short screenWidth; + public final short screenHeight; - public final byte screenLayout; - public final byte uiMode; - public final short smallestScreenWidthDp; + public final short sdkVersion; - public final short screenWidthDp; - public final short screenHeightDp; + public final byte screenLayout; + public final byte uiMode; + public final short smallestScreenWidthDp; - public final boolean isInvalid; + public final short screenWidthDp; + public final short screenHeightDp; - private final String mQualifiers; + public final boolean isInvalid; - public ResConfigFlags() { - mcc = 0; - mnc = 0; - language = new char[]{'\00', '\00'}; - country = new char[]{'\00', '\00'}; - layoutDirection = SCREENLAYOUT_LAYOUTDIR_ANY; - orientation = ORIENTATION_ANY; - touchscreen = TOUCHSCREEN_ANY; - density = DENSITY_DEFAULT; - keyboard = KEYBOARD_ANY; - navigation = NAVIGATION_ANY; - inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY; - screenWidth = 0; - screenHeight = 0; - sdkVersion = 0; - screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY; - uiMode = UI_MODE_TYPE_ANY | UI_MODE_NIGHT_ANY; - smallestScreenWidthDp = 0; - screenWidthDp = 0; - screenHeightDp = 0; - isInvalid = false; - mQualifiers = ""; - } + private final String mQualifiers; - public ResConfigFlags(short mcc, short mnc, char[] language, char[] country, - short layoutDirection, byte orientation, byte touchscreen, - short density, byte keyboard, byte navigation, byte inputFlags, - short screenWidth, short screenHeight, short sdkVersion, byte screenLayout, - byte uiMode, short smallestScreenWidthDp, short screenWidthDp, - short screenHeightDp, boolean isInvalid) { - if (orientation < 0 || orientation > 3) { - LOGGER.warning("Invalid orientation value: " + orientation); - orientation = 0; - isInvalid = true; - } - if (touchscreen < 0 || touchscreen > 3) { - LOGGER.warning("Invalid touchscreen value: " + touchscreen); - touchscreen = 0; - isInvalid = true; - } - if (density < -1) { - LOGGER.warning("Invalid density value: " + density); - density = 0; - isInvalid = true; - } - if (keyboard < 0 || keyboard > 3) { - LOGGER.warning("Invalid keyboard value: " + keyboard); - keyboard = 0; - isInvalid = true; - } - if (navigation < 0 || navigation > 4) { - LOGGER.warning("Invalid navigation value: " + navigation); - navigation = 0; - isInvalid = true; - } + public ResConfigFlags() { + mcc = 0; + mnc = 0; + language = new char[] { '\00', '\00' }; + country = new char[] { '\00', '\00' }; + layoutDirection = SCREENLAYOUT_LAYOUTDIR_ANY; + orientation = ORIENTATION_ANY; + touchscreen = TOUCHSCREEN_ANY; + density = DENSITY_DEFAULT; + keyboard = KEYBOARD_ANY; + navigation = NAVIGATION_ANY; + inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY; + screenWidth = 0; + screenHeight = 0; + sdkVersion = 0; + screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY; + uiMode = UI_MODE_TYPE_ANY | UI_MODE_NIGHT_ANY; + smallestScreenWidthDp = 0; + screenWidthDp = 0; + screenHeightDp = 0; + isInvalid = false; + mQualifiers = ""; + } - this.mcc = mcc; - this.mnc = mnc; - this.language = language; - this.country = country; - this.layoutDirection = layoutDirection; - this.orientation = orientation; - this.touchscreen = touchscreen; - this.density = density; - this.keyboard = keyboard; - this.navigation = navigation; - this.inputFlags = inputFlags; - this.screenWidth = screenWidth; - this.screenHeight = screenHeight; - this.sdkVersion = sdkVersion; - this.screenLayout = screenLayout; - this.uiMode = uiMode; - this.smallestScreenWidthDp = smallestScreenWidthDp; - this.screenWidthDp = screenWidthDp; - this.screenHeightDp = screenHeightDp; - this.isInvalid = isInvalid; - mQualifiers = generateQualifiers(); - } - - public String getQualifiers() { - return mQualifiers; - } - - private String generateQualifiers() { - StringBuilder ret = new StringBuilder(); - if (mcc != 0) { - ret.append("-mcc").append(String.format("%03d", mcc)); - if (mnc != 0) { - ret.append("-mnc").append(mnc); - } - } - if (language[0] != '\00') { - ret.append('-').append(language); - if (country[0] != '\00') { - ret.append("-r").append(country); - } - } - switch (screenLayout & MASK_LAYOUTDIR) { - case SCREENLAYOUT_LAYOUTDIR_RTL: - ret.append("-ldrtl"); - break; - case SCREENLAYOUT_LAYOUTDIR_LTR: - ret.append("-ldltr"); - break; + public ResConfigFlags(short mcc, short mnc, char[] language, + char[] country, short layoutDirection, byte orientation, + byte touchscreen, short density, byte keyboard, byte navigation, + byte inputFlags, short screenWidth, short screenHeight, + short sdkVersion, byte screenLayout, byte uiMode, + short smallestScreenWidthDp, short screenWidthDp, + short screenHeightDp, boolean isInvalid) { + if (orientation < 0 || orientation > 3) { + LOGGER.warning("Invalid orientation value: " + orientation); + orientation = 0; + isInvalid = true; + } + if (touchscreen < 0 || touchscreen > 3) { + LOGGER.warning("Invalid touchscreen value: " + touchscreen); + touchscreen = 0; + isInvalid = true; + } + if (density < -1) { + LOGGER.warning("Invalid density value: " + density); + density = 0; + isInvalid = true; + } + if (keyboard < 0 || keyboard > 3) { + LOGGER.warning("Invalid keyboard value: " + keyboard); + keyboard = 0; + isInvalid = true; + } + if (navigation < 0 || navigation > 4) { + LOGGER.warning("Invalid navigation value: " + navigation); + navigation = 0; + isInvalid = true; } - if (smallestScreenWidthDp != 0) { - ret.append("-sw").append(smallestScreenWidthDp).append("dp"); - } - if (screenWidthDp != 0) { - ret.append("-w").append(screenWidthDp).append("dp"); - } - if (screenHeightDp != 0) { - ret.append("-h").append(screenHeightDp).append("dp"); - } - switch (screenLayout & MASK_SCREENSIZE) { - case SCREENSIZE_SMALL: - ret.append("-small"); - break; - case SCREENSIZE_NORMAL: - ret.append("-normal"); - break; - case SCREENSIZE_LARGE: - ret.append("-large"); - break; - case SCREENSIZE_XLARGE: - ret.append("-xlarge"); - break; - } - switch (screenLayout & MASK_SCREENLONG) { - case SCREENLONG_YES: - ret.append("-long"); - break; - case SCREENLONG_NO: - ret.append("-notlong"); - break; - } - switch (orientation) { - case ORIENTATION_PORT: - ret.append("-port"); - break; - case ORIENTATION_LAND: - ret.append("-land"); - break; - case ORIENTATION_SQUARE: - ret.append("-square"); - break; - } - switch (uiMode & MASK_UI_MODE_TYPE) { - case UI_MODE_TYPE_CAR: - ret.append("-car"); - break; - case UI_MODE_TYPE_DESK: - ret.append("-desk"); - break; - case UI_MODE_TYPE_TELEVISION: - ret.append("-television"); - break; - case UI_MODE_TYPE_SMALLUI: - ret.append("-smallui"); - break; - case UI_MODE_TYPE_MEDIUMUI: - ret.append("-mediumui"); - break; - case UI_MODE_TYPE_LARGEUI: - ret.append("-largeui"); - break; - case UI_MODE_TYPE_HUGEUI: - ret.append("-hugeui"); - break; - case UI_MODE_TYPE_APPLIANCE: - ret.append("-appliance"); - break; - } - switch (uiMode & MASK_UI_MODE_NIGHT) { - case UI_MODE_NIGHT_YES: - ret.append("-night"); - break; - case UI_MODE_NIGHT_NO: - ret.append("-notnight"); - break; - } - switch (density) { - case DENSITY_DEFAULT: - break; - case DENSITY_LOW: - ret.append("-ldpi"); - break; - case DENSITY_MEDIUM: - ret.append("-mdpi"); - break; - case DENSITY_HIGH: - ret.append("-hdpi"); - break; - case DENSITY_TV: - ret.append("-tvdpi"); - break; - case DENSITY_XHIGH: - ret.append("-xhdpi"); - break; - case DENSITY_XXHIGH: - ret.append("-xxhdpi"); - break; - case DENSITY_NONE: - ret.append("-nodpi"); - break; - default: - ret.append('-').append(density).append("dpi"); - } - switch (touchscreen) { - case TOUCHSCREEN_NOTOUCH: - ret.append("-notouch"); - break; - case TOUCHSCREEN_STYLUS: - ret.append("-stylus"); - break; - case TOUCHSCREEN_FINGER: - ret.append("-finger"); - break; - } - switch (inputFlags & MASK_KEYSHIDDEN) { - case KEYSHIDDEN_NO: - ret.append("-keysexposed"); - break; - case KEYSHIDDEN_YES: - ret.append("-keyshidden"); - break; - case KEYSHIDDEN_SOFT: - ret.append("-keyssoft"); - break; - } - switch (keyboard) { - case KEYBOARD_NOKEYS: - ret.append("-nokeys"); - break; - case KEYBOARD_QWERTY: - ret.append("-qwerty"); - break; - case KEYBOARD_12KEY: - ret.append("-12key"); - break; - } - switch (inputFlags & MASK_NAVHIDDEN) { - case NAVHIDDEN_NO: - ret.append("-navexposed"); - break; - case NAVHIDDEN_YES: - ret.append("-navhidden"); - break; - } - switch (navigation) { - case NAVIGATION_NONAV: - ret.append("-nonav"); - break; - case NAVIGATION_DPAD: - ret.append("-dpad"); - break; - case NAVIGATION_TRACKBALL: - ret.append("-trackball"); - break; - case NAVIGATION_WHEEL: - ret.append("-wheel"); - break; - } - if (screenWidth != 0 && screenHeight != 0) { - if (screenWidth > screenHeight) { - ret.append(String.format("-%dx%d", screenWidth, screenHeight)); - } else { - ret.append(String.format("-%dx%d", screenHeight, screenWidth)); - } - } - if (sdkVersion > getNaturalSdkVersionRequirement()) { - ret.append("-v").append(sdkVersion); - } - if (isInvalid) { - ret.append("-ERR" + sErrCounter++); - } - return ret.toString(); - } + this.mcc = mcc; + this.mnc = mnc; + this.language = language; + this.country = country; + this.layoutDirection = layoutDirection; + this.orientation = orientation; + this.touchscreen = touchscreen; + this.density = density; + this.keyboard = keyboard; + this.navigation = navigation; + this.inputFlags = inputFlags; + this.screenWidth = screenWidth; + this.screenHeight = screenHeight; + this.sdkVersion = sdkVersion; + this.screenLayout = screenLayout; + this.uiMode = uiMode; + this.smallestScreenWidthDp = smallestScreenWidthDp; + this.screenWidthDp = screenWidthDp; + this.screenHeightDp = screenHeightDp; + this.isInvalid = isInvalid; + mQualifiers = generateQualifiers(); + } - private short getNaturalSdkVersionRequirement() { - if (smallestScreenWidthDp != 0 || screenWidthDp != 0 - || screenHeightDp != 0) { - return SDK_HONEYCOMB_MR2; - } - if ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != 0) { - return SDK_FROYO; - } - if ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != 0 - || density != DENSITY_DEFAULT) { - return SDK_DONUT; - } - return 0; - } + public String getQualifiers() { + return mQualifiers; + } - @Override - public String toString() { - return ! getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]"; - } + private String generateQualifiers() { + StringBuilder ret = new StringBuilder(); + if (mcc != 0) { + ret.append("-mcc").append(String.format("%03d", mcc)); + if (mnc != 0) { + ret.append("-mnc").append(mnc); + } + } + if (language[0] != '\00') { + ret.append('-').append(language); + if (country[0] != '\00') { + ret.append("-r").append(country); + } + } + switch (screenLayout & MASK_LAYOUTDIR) { + case SCREENLAYOUT_LAYOUTDIR_RTL: + ret.append("-ldrtl"); + break; + case SCREENLAYOUT_LAYOUTDIR_LTR: + ret.append("-ldltr"); + break; + } + if (smallestScreenWidthDp != 0) { + ret.append("-sw").append(smallestScreenWidthDp).append("dp"); + } + if (screenWidthDp != 0) { + ret.append("-w").append(screenWidthDp).append("dp"); + } + if (screenHeightDp != 0) { + ret.append("-h").append(screenHeightDp).append("dp"); + } + switch (screenLayout & MASK_SCREENSIZE) { + case SCREENSIZE_SMALL: + ret.append("-small"); + break; + case SCREENSIZE_NORMAL: + ret.append("-normal"); + break; + case SCREENSIZE_LARGE: + ret.append("-large"); + break; + case SCREENSIZE_XLARGE: + ret.append("-xlarge"); + break; + } + switch (screenLayout & MASK_SCREENLONG) { + case SCREENLONG_YES: + ret.append("-long"); + break; + case SCREENLONG_NO: + ret.append("-notlong"); + break; + } + switch (orientation) { + case ORIENTATION_PORT: + ret.append("-port"); + break; + case ORIENTATION_LAND: + ret.append("-land"); + break; + case ORIENTATION_SQUARE: + ret.append("-square"); + break; + } + switch (uiMode & MASK_UI_MODE_TYPE) { + case UI_MODE_TYPE_CAR: + ret.append("-car"); + break; + case UI_MODE_TYPE_DESK: + ret.append("-desk"); + break; + case UI_MODE_TYPE_TELEVISION: + ret.append("-television"); + break; + case UI_MODE_TYPE_SMALLUI: + ret.append("-smallui"); + break; + case UI_MODE_TYPE_MEDIUMUI: + ret.append("-mediumui"); + break; + case UI_MODE_TYPE_LARGEUI: + ret.append("-largeui"); + break; + case UI_MODE_TYPE_HUGEUI: + ret.append("-hugeui"); + break; + case UI_MODE_TYPE_APPLIANCE: + ret.append("-appliance"); + break; + } + switch (uiMode & MASK_UI_MODE_NIGHT) { + case UI_MODE_NIGHT_YES: + ret.append("-night"); + break; + case UI_MODE_NIGHT_NO: + ret.append("-notnight"); + break; + } + switch (density) { + case DENSITY_DEFAULT: + break; + case DENSITY_LOW: + ret.append("-ldpi"); + break; + case DENSITY_MEDIUM: + ret.append("-mdpi"); + break; + case DENSITY_HIGH: + ret.append("-hdpi"); + break; + case DENSITY_TV: + ret.append("-tvdpi"); + break; + case DENSITY_XHIGH: + ret.append("-xhdpi"); + break; + case DENSITY_XXHIGH: + ret.append("-xxhdpi"); + break; + case DENSITY_NONE: + ret.append("-nodpi"); + break; + default: + ret.append('-').append(density).append("dpi"); + } + switch (touchscreen) { + case TOUCHSCREEN_NOTOUCH: + ret.append("-notouch"); + break; + case TOUCHSCREEN_STYLUS: + ret.append("-stylus"); + break; + case TOUCHSCREEN_FINGER: + ret.append("-finger"); + break; + } + switch (inputFlags & MASK_KEYSHIDDEN) { + case KEYSHIDDEN_NO: + ret.append("-keysexposed"); + break; + case KEYSHIDDEN_YES: + ret.append("-keyshidden"); + break; + case KEYSHIDDEN_SOFT: + ret.append("-keyssoft"); + break; + } + switch (keyboard) { + case KEYBOARD_NOKEYS: + ret.append("-nokeys"); + break; + case KEYBOARD_QWERTY: + ret.append("-qwerty"); + break; + case KEYBOARD_12KEY: + ret.append("-12key"); + break; + } + switch (inputFlags & MASK_NAVHIDDEN) { + case NAVHIDDEN_NO: + ret.append("-navexposed"); + break; + case NAVHIDDEN_YES: + ret.append("-navhidden"); + break; + } + switch (navigation) { + case NAVIGATION_NONAV: + ret.append("-nonav"); + break; + case NAVIGATION_DPAD: + ret.append("-dpad"); + break; + case NAVIGATION_TRACKBALL: + ret.append("-trackball"); + break; + case NAVIGATION_WHEEL: + ret.append("-wheel"); + break; + } + if (screenWidth != 0 && screenHeight != 0) { + if (screenWidth > screenHeight) { + ret.append(String.format("-%dx%d", screenWidth, screenHeight)); + } else { + ret.append(String.format("-%dx%d", screenHeight, screenWidth)); + } + } + if (sdkVersion > getNaturalSdkVersionRequirement()) { + ret.append("-v").append(sdkVersion); + } + if (isInvalid) { + ret.append("-ERR" + sErrCounter++); + } - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ResConfigFlags other = (ResConfigFlags) obj; - return this.mQualifiers.equals(other.mQualifiers); - } + return ret.toString(); + } - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash + this.mQualifiers.hashCode(); - return hash; - } + private short getNaturalSdkVersionRequirement() { + if (smallestScreenWidthDp != 0 || screenWidthDp != 0 + || screenHeightDp != 0) { + return SDK_HONEYCOMB_MR2; + } + if ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != 0) { + return SDK_FROYO; + } + if ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != 0 + || density != DENSITY_DEFAULT) { + return SDK_DONUT; + } + return 0; + } + @Override + public String toString() { + return !getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]"; + } - // TODO: Dirty static hack. This counter should be a part of ResPackage, - // but it would be hard right now and this feature is very rarely used. - private static int sErrCounter = 0; + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ResConfigFlags other = (ResConfigFlags) obj; + return this.mQualifiers.equals(other.mQualifiers); + } - public final static byte SDK_BASE = 1; - public final static byte SDK_BASE_1_1 = 2; - public final static byte SDK_CUPCAKE = 3; - public final static byte SDK_DONUT = 4; - public final static byte SDK_ECLAIR = 5; - public final static byte SDK_ECLAIR_0_1 = 6; - public final static byte SDK_ECLAIR_MR1 = 7; - public final static byte SDK_FROYO = 8; - public final static byte SDK_GINGERBREAD = 9; - public final static byte SDK_GINGERBREAD_MR1 = 10; - public final static byte SDK_HONEYCOMB = 11; - public final static byte SDK_HONEYCOMB_MR1 = 12; - public final static byte SDK_HONEYCOMB_MR2 = 13; - public final static byte SDK_ICE_CREAM_SANDWICH = 14; - public final static byte SDK_ICE_CREAM_SANDWICH_MR1 = 15; - public final static byte SDK_JELLY_BEAN = 16; - public final static byte SDK_JELLY_BEAN_MR1 = 17; + @Override + public int hashCode() { + int hash = 17; + hash = 31 * hash + this.mQualifiers.hashCode(); + return hash; + } - public final static byte ORIENTATION_ANY = 0; - public final static byte ORIENTATION_PORT = 1; - public final static byte ORIENTATION_LAND = 2; - public final static byte ORIENTATION_SQUARE = 3; + // TODO: Dirty static hack. This counter should be a part of ResPackage, + // but it would be hard right now and this feature is very rarely used. + private static int sErrCounter = 0; - public final static byte TOUCHSCREEN_ANY = 0; - public final static byte TOUCHSCREEN_NOTOUCH = 1; - public final static byte TOUCHSCREEN_STYLUS = 2; - public final static byte TOUCHSCREEN_FINGER = 3; + public final static byte SDK_BASE = 1; + public final static byte SDK_BASE_1_1 = 2; + public final static byte SDK_CUPCAKE = 3; + public final static byte SDK_DONUT = 4; + public final static byte SDK_ECLAIR = 5; + public final static byte SDK_ECLAIR_0_1 = 6; + public final static byte SDK_ECLAIR_MR1 = 7; + public final static byte SDK_FROYO = 8; + public final static byte SDK_GINGERBREAD = 9; + public final static byte SDK_GINGERBREAD_MR1 = 10; + public final static byte SDK_HONEYCOMB = 11; + public final static byte SDK_HONEYCOMB_MR1 = 12; + public final static byte SDK_HONEYCOMB_MR2 = 13; + public final static byte SDK_ICE_CREAM_SANDWICH = 14; + public final static byte SDK_ICE_CREAM_SANDWICH_MR1 = 15; + public final static byte SDK_JELLY_BEAN = 16; + public final static byte SDK_JELLY_BEAN_MR1 = 17; - public final static short DENSITY_DEFAULT = 0; - public final static short DENSITY_LOW = 120; - public final static short DENSITY_MEDIUM = 160; - public final static short DENSITY_TV = 213; - public final static short DENSITY_HIGH = 240; - public final static short DENSITY_XHIGH = 320; - public final static short DENSITY_XXHIGH = 480; - public final static short DENSITY_NONE = -1; - - public final static short MASK_LAYOUTDIR = 0xc0; - public final static short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00; - public final static short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40; - public final static short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80; - public final static short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06; + public final static byte ORIENTATION_ANY = 0; + public final static byte ORIENTATION_PORT = 1; + public final static byte ORIENTATION_LAND = 2; + public final static byte ORIENTATION_SQUARE = 3; - public final static byte KEYBOARD_ANY = 0; - public final static byte KEYBOARD_NOKEYS = 1; - public final static byte KEYBOARD_QWERTY = 2; - public final static byte KEYBOARD_12KEY = 3; + public final static byte TOUCHSCREEN_ANY = 0; + public final static byte TOUCHSCREEN_NOTOUCH = 1; + public final static byte TOUCHSCREEN_STYLUS = 2; + public final static byte TOUCHSCREEN_FINGER = 3; - public final static byte NAVIGATION_ANY = 0; - public final static byte NAVIGATION_NONAV = 1; - public final static byte NAVIGATION_DPAD = 2; - public final static byte NAVIGATION_TRACKBALL = 3; - public final static byte NAVIGATION_WHEEL = 4; + public final static short DENSITY_DEFAULT = 0; + public final static short DENSITY_LOW = 120; + public final static short DENSITY_MEDIUM = 160; + public final static short DENSITY_TV = 213; + public final static short DENSITY_HIGH = 240; + public final static short DENSITY_XHIGH = 320; + public final static short DENSITY_XXHIGH = 480; + public final static short DENSITY_NONE = -1; - public final static byte MASK_KEYSHIDDEN = 0x3; - public final static byte KEYSHIDDEN_ANY = 0x0; - public final static byte KEYSHIDDEN_NO = 0x1; - public final static byte KEYSHIDDEN_YES = 0x2; - public final static byte KEYSHIDDEN_SOFT = 0x3; + public final static short MASK_LAYOUTDIR = 0xc0; + public final static short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00; + public final static short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40; + public final static short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80; + public final static short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06; - public final static byte MASK_NAVHIDDEN = 0xc; - public final static byte NAVHIDDEN_ANY = 0x0; - public final static byte NAVHIDDEN_NO = 0x4; - public final static byte NAVHIDDEN_YES = 0x8; + public final static byte KEYBOARD_ANY = 0; + public final static byte KEYBOARD_NOKEYS = 1; + public final static byte KEYBOARD_QWERTY = 2; + public final static byte KEYBOARD_12KEY = 3; - public final static byte MASK_SCREENSIZE = 0x0f; - public final static byte SCREENSIZE_ANY = 0x00; - public final static byte SCREENSIZE_SMALL = 0x01; - public final static byte SCREENSIZE_NORMAL = 0x02; - public final static byte SCREENSIZE_LARGE = 0x03; - public final static byte SCREENSIZE_XLARGE = 0x04; - - public final static byte MASK_SCREENLONG = 0x30; - public final static byte SCREENLONG_ANY = 0x00; - public final static byte SCREENLONG_NO = 0x10; - public final static byte SCREENLONG_YES = 0x20; + public final static byte NAVIGATION_ANY = 0; + public final static byte NAVIGATION_NONAV = 1; + public final static byte NAVIGATION_DPAD = 2; + public final static byte NAVIGATION_TRACKBALL = 3; + public final static byte NAVIGATION_WHEEL = 4; - public final static byte MASK_UI_MODE_TYPE = 0x0f; - public final static byte UI_MODE_TYPE_ANY = 0x00; - public final static byte UI_MODE_TYPE_NORMAL = 0x01; - public final static byte UI_MODE_TYPE_DESK = 0x02; - public final static byte UI_MODE_TYPE_CAR = 0x03; - public final static byte UI_MODE_TYPE_TELEVISION = 0x04; - public final static byte UI_MODE_TYPE_APPLIANCE = 0x05; - public final static byte UI_MODE_TYPE_SMALLUI = 0x0c; - public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d; - public final static byte UI_MODE_TYPE_LARGEUI = 0x0e; - public final static byte UI_MODE_TYPE_HUGEUI = 0x0f; + public final static byte MASK_KEYSHIDDEN = 0x3; + public final static byte KEYSHIDDEN_ANY = 0x0; + public final static byte KEYSHIDDEN_NO = 0x1; + public final static byte KEYSHIDDEN_YES = 0x2; + public final static byte KEYSHIDDEN_SOFT = 0x3; - public final static byte MASK_UI_MODE_NIGHT = 0x30; - public final static byte UI_MODE_NIGHT_ANY = 0x00; - public final static byte UI_MODE_NIGHT_NO = 0x10; - public final static byte UI_MODE_NIGHT_YES = 0x20; + public final static byte MASK_NAVHIDDEN = 0xc; + public final static byte NAVHIDDEN_ANY = 0x0; + public final static byte NAVHIDDEN_NO = 0x4; + public final static byte NAVHIDDEN_YES = 0x8; + public final static byte MASK_SCREENSIZE = 0x0f; + public final static byte SCREENSIZE_ANY = 0x00; + public final static byte SCREENSIZE_SMALL = 0x01; + public final static byte SCREENSIZE_NORMAL = 0x02; + public final static byte SCREENSIZE_LARGE = 0x03; + public final static byte SCREENSIZE_XLARGE = 0x04; - private static final Logger LOGGER = - Logger.getLogger(ResConfigFlags.class.getName()); + public final static byte MASK_SCREENLONG = 0x30; + public final static byte SCREENLONG_ANY = 0x00; + public final static byte SCREENLONG_NO = 0x10; + public final static byte SCREENLONG_YES = 0x20; + + public final static byte MASK_UI_MODE_TYPE = 0x0f; + public final static byte UI_MODE_TYPE_ANY = 0x00; + public final static byte UI_MODE_TYPE_NORMAL = 0x01; + public final static byte UI_MODE_TYPE_DESK = 0x02; + public final static byte UI_MODE_TYPE_CAR = 0x03; + public final static byte UI_MODE_TYPE_TELEVISION = 0x04; + public final static byte UI_MODE_TYPE_APPLIANCE = 0x05; + public final static byte UI_MODE_TYPE_SMALLUI = 0x0c; + public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d; + public final static byte UI_MODE_TYPE_LARGEUI = 0x0e; + public final static byte UI_MODE_TYPE_HUGEUI = 0x0f; + + public final static byte MASK_UI_MODE_NIGHT = 0x30; + public final static byte UI_MODE_NIGHT_ANY = 0x00; + public final static byte UI_MODE_NIGHT_NO = 0x10; + public final static byte UI_MODE_NIGHT_YES = 0x20; + + private static final Logger LOGGER = Logger.getLogger(ResConfigFlags.class + .getName()); } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java index 24158030..43f4ee83 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java @@ -20,51 +20,51 @@ package brut.androlib.res.data; * @author Ryszard Wiśniewski */ public class ResID { - public final int package_; - public final int type; - public final int entry; + public final int package_; + public final int type; + public final int entry; - public final int id; + public final int id; - public ResID(int package_, int type, int entry) { - this(package_, type, entry, (package_ << 24) + (type << 16) + entry); - } + public ResID(int package_, int type, int entry) { + this(package_, type, entry, (package_ << 24) + (type << 16) + entry); + } - public ResID(int id) { - this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id); - } + public ResID(int id) { + this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id); + } - public ResID(int package_, int type, int entry, int id) { - this.package_ = package_; - this.type = type; - this.entry = entry; - this.id = id; - } + public ResID(int package_, int type, int entry, int id) { + this.package_ = package_; + this.type = type; + this.entry = entry; + this.id = id; + } - @Override - public String toString() { - return String.format("0x%08x", id); - } + @Override + public String toString() { + return String.format("0x%08x", id); + } - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash + this.id; - return hash; - } + @Override + public int hashCode() { + int hash = 17; + hash = 31 * hash + this.id; + return hash; + } - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ResID other = (ResID) obj; - if (this.id != other.id) { - return false; - } - return true; - } + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ResID other = (ResID) obj; + if (this.id != other.id) { + return false; + } + return true; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java index 2fee2bc2..0dd8d117 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java @@ -28,193 +28,192 @@ import java.util.*; * @author Ryszard Wiśniewski */ public class ResPackage { - private final ResTable mResTable; - private final int mId; - private final String mName; - private final Map mResSpecs = - new LinkedHashMap(); - private final Map mConfigs = - new LinkedHashMap(); - private final Map mTypes = - new LinkedHashMap(); - private final Set mSynthesizedRes = new HashSet(); + private final ResTable mResTable; + private final int mId; + private final String mName; + private final Map mResSpecs = new LinkedHashMap(); + private final Map mConfigs = new LinkedHashMap(); + private final Map mTypes = new LinkedHashMap(); + private final Set mSynthesizedRes = new HashSet(); - private ResValueFactory mValueFactory; + private ResValueFactory mValueFactory; - public ResPackage(ResTable resTable, int id, String name) { - this.mResTable = resTable; - this.mId = id; - this.mName = name; - } + public ResPackage(ResTable resTable, int id, String name) { + this.mResTable = resTable; + this.mId = id; + this.mName = name; + } - public List listResSpecs() { - return new ArrayList(mResSpecs.values()); - } + public List listResSpecs() { + return new ArrayList(mResSpecs.values()); + } - public boolean hasResSpec(ResID resID) { - return mResSpecs.containsKey(resID); - } + public boolean hasResSpec(ResID resID) { + return mResSpecs.containsKey(resID); + } - public ResResSpec getResSpec(ResID resID) throws UndefinedResObject { - ResResSpec spec = mResSpecs.get(resID); - if (spec == null) { - throw new UndefinedResObject("resource spec: " + resID.toString()); - } - return spec; - } + public ResResSpec getResSpec(ResID resID) throws UndefinedResObject { + ResResSpec spec = mResSpecs.get(resID); + if (spec == null) { + throw new UndefinedResObject("resource spec: " + resID.toString()); + } + return spec; + } - public List getConfigs() { - return new ArrayList(mConfigs.values()); - } + public List getConfigs() { + return new ArrayList(mConfigs.values()); + } - public boolean hasConfig(ResConfigFlags flags) { - return mConfigs.containsKey(flags); - } + public boolean hasConfig(ResConfigFlags flags) { + return mConfigs.containsKey(flags); + } - public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException { - ResConfig config = mConfigs.get(flags); - if (config == null) { - throw new UndefinedResObject("config: " + flags); - } - return config; - } + public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException { + ResConfig config = mConfigs.get(flags); + if (config == null) { + throw new UndefinedResObject("config: " + flags); + } + return config; + } - public ResConfig getOrCreateConfig(ResConfigFlags flags) - throws AndrolibException { - ResConfig config = mConfigs.get(flags); - if (config == null) { - config = new ResConfig(flags); - mConfigs.put(flags, config); - } - return config; - } + public ResConfig getOrCreateConfig(ResConfigFlags flags) + throws AndrolibException { + ResConfig config = mConfigs.get(flags); + if (config == null) { + config = new ResConfig(flags); + mConfigs.put(flags, config); + } + return config; + } - public List listTypes() { - return new ArrayList(mTypes.values()); - } + public List listTypes() { + return new ArrayList(mTypes.values()); + } - public boolean hasType(String typeName) { - return mTypes.containsKey(typeName); - } + public boolean hasType(String typeName) { + return mTypes.containsKey(typeName); + } - public ResType getType(String typeName) throws AndrolibException { - ResType type = mTypes.get(typeName); - if (type == null) { - throw new UndefinedResObject("type: " + typeName); - } - return type; - } + public ResType getType(String typeName) throws AndrolibException { + ResType type = mTypes.get(typeName); + if (type == null) { + throw new UndefinedResObject("type: " + typeName); + } + return type; + } - public Set listFiles() { - Set ret = new HashSet(); - for (ResResSpec spec : mResSpecs.values()) { - for (ResResource res : spec.listResources()) { - if (res.getValue() instanceof ResFileValue) { - ret.add(res); - } - } - } - return ret; - } + public Set listFiles() { + Set ret = new HashSet(); + for (ResResSpec spec : mResSpecs.values()) { + for (ResResource res : spec.listResources()) { + if (res.getValue() instanceof ResFileValue) { + ret.add(res); + } + } + } + return ret; + } - public Collection listValuesFiles() { - Map, ResValuesFile> ret = - new HashMap, ResValuesFile>(); - for (ResResSpec spec : mResSpecs.values()) { - for (ResResource res : spec.listResources()) { - if (res.getValue() instanceof ResValuesXmlSerializable) { - ResType type = res.getResSpec().getType(); - ResConfig config = res.getConfig(); - Duo key = - new Duo(type, config); - ResValuesFile values = ret.get(key); - if (values == null) { - values = new ResValuesFile(this, type, config); - ret.put(key, values); - } - values.addResource(res); - } - } - } - return ret.values(); - } + public Collection listValuesFiles() { + Map, ResValuesFile> ret = new HashMap, ResValuesFile>(); + for (ResResSpec spec : mResSpecs.values()) { + for (ResResource res : spec.listResources()) { + if (res.getValue() instanceof ResValuesXmlSerializable) { + ResType type = res.getResSpec().getType(); + ResConfig config = res.getConfig(); + Duo key = new Duo( + type, config); + ResValuesFile values = ret.get(key); + if (values == null) { + values = new ResValuesFile(this, type, config); + ret.put(key, values); + } + values.addResource(res); + } + } + } + return ret.values(); + } - public ResTable getResTable() { - return mResTable; - } + public ResTable getResTable() { + return mResTable; + } - public int getId() { - return mId; - } + public int getId() { + return mId; + } - public String getName() { - return mName; - } + public String getName() { + return mName; + } - boolean isSynthesized(ResID resId) { - return mSynthesizedRes.contains(resId); - } + boolean isSynthesized(ResID resId) { + return mSynthesizedRes.contains(resId); + } - public void addResSpec(ResResSpec spec) throws AndrolibException { - if (mResSpecs.put(spec.getId(), spec) != null) { - throw new AndrolibException("Multiple resource specs: " + spec); - } - } + public void addResSpec(ResResSpec spec) throws AndrolibException { + if (mResSpecs.put(spec.getId(), spec) != null) { + throw new AndrolibException("Multiple resource specs: " + spec); + } + } - public void addConfig(ResConfig config) throws AndrolibException { - if (mConfigs.put(config.getFlags(), config) != null) { - throw new AndrolibException("Multiple configs: " + config); - } - } + public void addConfig(ResConfig config) throws AndrolibException { + if (mConfigs.put(config.getFlags(), config) != null) { + throw new AndrolibException("Multiple configs: " + config); + } + } - public void addType(ResType type) throws AndrolibException { - if (mTypes.put(type.getName(), type) != null) { - throw new AndrolibException("Multiple types: " + type); - } - } + public void addType(ResType type) throws AndrolibException { + if (mTypes.put(type.getName(), type) != null) { + throw new AndrolibException("Multiple types: " + type); + } + } - public void addResource(ResResource res) { - } + public void addResource(ResResource res) { + } - public void addSynthesizedRes(int resId) { - mSynthesizedRes.add(new ResID(resId)); - } + public void addSynthesizedRes(int resId) { + mSynthesizedRes.add(new ResID(resId)); + } - @Override - public String toString() { - return mName; - } + @Override + public String toString() { + return mName; + } - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ResPackage other = (ResPackage) obj; - if (this.mResTable != other.mResTable && (this.mResTable == null || !this.mResTable.equals(other.mResTable))) { - return false; - } - if (this.mId != other.mId) { - return false; - } - return true; - } + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ResPackage other = (ResPackage) obj; + if (this.mResTable != other.mResTable + && (this.mResTable == null || !this.mResTable + .equals(other.mResTable))) { + return false; + } + if (this.mId != other.mId) { + return false; + } + return true; + } - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash + (this.mResTable != null ? this.mResTable.hashCode() : 0); - hash = 31 * hash + this.mId; - return hash; - } + @Override + public int hashCode() { + int hash = 17; + hash = 31 * hash + + (this.mResTable != null ? this.mResTable.hashCode() : 0); + hash = 31 * hash + this.mId; + return hash; + } - public ResValueFactory getValueFactory() { - if (mValueFactory == null) { - mValueFactory = new ResValueFactory(this); - } - return mValueFactory; - } + public ResValueFactory getValueFactory() { + if (mValueFactory == null) { + mValueFactory = new ResValueFactory(this); + } + return mValueFactory; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java index f39f561e..658c9166 100755 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java @@ -25,101 +25,97 @@ import org.apache.commons.lang3.StringUtils; * @author Ryszard Wiśniewski */ public class ResResSpec { - private final ResID mId; - private final String mName; - private final ResPackage mPackage; - private final ResType mType; - private final Map mResources = - new LinkedHashMap(); + private final ResID mId; + private final String mName; + private final ResPackage mPackage; + private final ResType mType; + private final Map mResources = new LinkedHashMap(); - public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) { - this.mId = id; - this.mName = name; - this.mPackage = pkg; - this.mType = type; - } + public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) { + this.mId = id; + this.mName = name; + this.mPackage = pkg; + this.mType = type; + } - public Set listResources() { - return new LinkedHashSet(mResources.values()); - } + public Set listResources() { + return new LinkedHashSet(mResources.values()); + } - public ResResource getResource(ResConfig config) throws AndrolibException { - return getResource(config.getFlags()); - } - - public ResResource getResource(ResConfigFlags config) - throws AndrolibException { - ResResource res = mResources.get(config); - if (res == null) { - throw new UndefinedResObject(String.format( - "resource: spec=%s, config=%s", this, config)); - } - return res; - } + public ResResource getResource(ResConfig config) throws AndrolibException { + return getResource(config.getFlags()); + } - public boolean hasResource(ResConfig config) { - return hasResource(config.getFlags()); - } + public ResResource getResource(ResConfigFlags config) + throws AndrolibException { + ResResource res = mResources.get(config); + if (res == null) { + throw new UndefinedResObject(String.format( + "resource: spec=%s, config=%s", this, config)); + } + return res; + } - private boolean hasResource(ResConfigFlags flags) { - return mResources.containsKey(flags); - } + public boolean hasResource(ResConfig config) { + return hasResource(config.getFlags()); + } - public ResResource getDefaultResource() throws AndrolibException { - return getResource(new ResConfigFlags()); - } + private boolean hasResource(ResConfigFlags flags) { + return mResources.containsKey(flags); + } - public boolean hasDefaultResource() { - return mResources.containsKey(new ResConfigFlags()); - } + public ResResource getDefaultResource() throws AndrolibException { + return getResource(new ResConfigFlags()); + } - public String getFullName() { - return getFullName(false, false); - } + public boolean hasDefaultResource() { + return mResources.containsKey(new ResConfigFlags()); + } - public String getFullName(ResPackage relativeToPackage, - boolean excludeType) { - return getFullName( - getPackage().equals(relativeToPackage), excludeType); - } + public String getFullName() { + return getFullName(false, false); + } - public String getFullName(boolean excludePackage, boolean excludeType) { - return - (excludePackage ? "" : getPackage().getName() + ":") + - (excludeType ? "" : getType().getName() + "/") + getName(); - } + public String getFullName(ResPackage relativeToPackage, boolean excludeType) { + return getFullName(getPackage().equals(relativeToPackage), excludeType); + } - public ResID getId() { - return mId; - } + public String getFullName(boolean excludePackage, boolean excludeType) { + return (excludePackage ? "" : getPackage().getName() + ":") + + (excludeType ? "" : getType().getName() + "/") + getName(); + } - public String getName() { - return StringUtils.replace(mName, "\"", "q"); - } + public ResID getId() { + return mId; + } - public ResPackage getPackage() { - return mPackage; - } + public String getName() { + return StringUtils.replace(mName, "\"", "q"); + } - public ResType getType() { - return mType; - } + public ResPackage getPackage() { + return mPackage; + } - public void addResource(ResResource res) - throws AndrolibException { - addResource(res, false); - } + public ResType getType() { + return mType; + } - public void addResource(ResResource res, boolean overwrite) - throws AndrolibException { - ResConfigFlags flags = res.getConfig().getFlags(); - if (mResources.put(flags, res) != null && ! overwrite) { - throw new AndrolibException(String.format("Multiple resources: spec=%s, config=%s", this, flags)); - } - } + public void addResource(ResResource res) throws AndrolibException { + addResource(res, false); + } - @Override - public String toString() { - return mId.toString() + " " + mType.toString() + "/" + mName; - } + public void addResource(ResResource res, boolean overwrite) + throws AndrolibException { + ResConfigFlags flags = res.getConfig().getFlags(); + if (mResources.put(flags, res) != null && !overwrite) { + throw new AndrolibException(String.format( + "Multiple resources: spec=%s, config=%s", this, flags)); + } + } + + @Override + public String toString() { + return mId.toString() + " " + mType.toString() + "/" + mName; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java index 2351b3c8..38c35d68 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java @@ -23,42 +23,41 @@ import brut.androlib.res.data.value.ResValue; * @author Ryszard Wiśniewski */ public class ResResource { - private final ResConfig mConfig; - private final ResResSpec mResSpec; - private final ResValue mValue; + private final ResConfig mConfig; + private final ResResSpec mResSpec; + private final ResValue mValue; - public ResResource(ResConfig config, ResResSpec spec, - ResValue value) { - this.mConfig = config; - this.mResSpec = spec; - this.mValue = value; - } + public ResResource(ResConfig config, ResResSpec spec, ResValue value) { + this.mConfig = config; + this.mResSpec = spec; + this.mValue = value; + } - public String getFilePath() { - return mResSpec.getType().getName() + - mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName(); - } + public String getFilePath() { + return mResSpec.getType().getName() + + mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName(); + } - public ResConfig getConfig() { - return mConfig; - } + public ResConfig getConfig() { + return mConfig; + } - public ResResSpec getResSpec() { - return mResSpec; - } + public ResResSpec getResSpec() { + return mResSpec; + } - public ResValue getValue() { - return mValue; - } + public ResValue getValue() { + return mValue; + } - public void replace(ResValue value) throws AndrolibException { - ResResource res = new ResResource(mConfig, mResSpec, value); - mConfig.addResource(res, true); - mResSpec.addResource(res, true); - } + public void replace(ResValue value) throws AndrolibException { + ResResource res = new ResResource(mConfig, mResSpec, value); + mConfig.addResource(res, true); + mResSpec.addResource(res, true); + } - @Override - public String toString() { - return getFilePath(); - } + @Override + public String toString() { + return getFilePath(); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java index bd383908..b3c752d0 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java @@ -26,125 +26,121 @@ import java.util.*; * @author Ryszard Wiśniewski */ public class ResTable { - private final AndrolibResources mAndRes; + private final AndrolibResources mAndRes; - private final Map mPackagesById = - new HashMap(); - private final Map mPackagesByName = - new HashMap(); - private final Set mMainPackages = - new LinkedHashSet(); - private final Set mFramePackages = - new LinkedHashSet(); + private final Map mPackagesById = new HashMap(); + private final Map mPackagesByName = new HashMap(); + private final Set mMainPackages = new LinkedHashSet(); + private final Set mFramePackages = new LinkedHashSet(); - private String mFrameTag; + private String mFrameTag; - private Map mSdkInfo = new LinkedHashMap(); - private Map mPackageInfo = new LinkedHashMap(); + private Map mSdkInfo = new LinkedHashMap(); + private Map mPackageInfo = new LinkedHashMap(); - public ResTable() { - mAndRes = null; - } + public ResTable() { + mAndRes = null; + } - public ResTable(AndrolibResources andRes) { - mAndRes = andRes; - } + public ResTable(AndrolibResources andRes) { + mAndRes = andRes; + } - public ResResSpec getResSpec(int resID) throws AndrolibException { - return getResSpec(new ResID(resID)); - } - - public ResResSpec getResSpec(ResID resID) throws AndrolibException { - return getPackage(resID.package_).getResSpec(resID); - } + public ResResSpec getResSpec(int resID) throws AndrolibException { + return getResSpec(new ResID(resID)); + } - public Set listMainPackages() { - return mMainPackages; - } + public ResResSpec getResSpec(ResID resID) throws AndrolibException { + return getPackage(resID.package_).getResSpec(resID); + } - public Set listFramePackages() { - return mFramePackages; - } + public Set listMainPackages() { + return mMainPackages; + } - public ResPackage getPackage(int id) throws AndrolibException { - ResPackage pkg = mPackagesById.get(id); - if (pkg != null) { - return pkg; - } - if (mAndRes != null) { - return mAndRes.loadFrameworkPkg(this, id, mFrameTag); - } - throw new UndefinedResObject(String.format("package: id=%d", id)); - } + public Set listFramePackages() { + return mFramePackages; + } - public ResPackage getPackage(String name) throws AndrolibException { - ResPackage pkg = mPackagesByName.get(name); - if (pkg == null) { - throw new UndefinedResObject("package: name=" + name); - } - return pkg; - } + public ResPackage getPackage(int id) throws AndrolibException { + ResPackage pkg = mPackagesById.get(id); + if (pkg != null) { + return pkg; + } + if (mAndRes != null) { + return mAndRes.loadFrameworkPkg(this, id, mFrameTag); + } + throw new UndefinedResObject(String.format("package: id=%d", id)); + } - public boolean hasPackage(int id) { - return mPackagesById.containsKey(id); - } + public ResPackage getPackage(String name) throws AndrolibException { + ResPackage pkg = mPackagesByName.get(name); + if (pkg == null) { + throw new UndefinedResObject("package: name=" + name); + } + return pkg; + } - public boolean hasPackage(String name) { - return mPackagesByName.containsKey(name); - } + public boolean hasPackage(int id) { + return mPackagesById.containsKey(id); + } - public ResValue getValue(String package_, String type, String name) - throws AndrolibException { - return getPackage(package_).getType(type).getResSpec(name) - .getDefaultResource().getValue(); - } + public boolean hasPackage(String name) { + return mPackagesByName.containsKey(name); + } - public void addPackage(ResPackage pkg, boolean main) - throws AndrolibException { - Integer id = pkg.getId(); - if (mPackagesById.containsKey(id)) { - throw new AndrolibException( - "Multiple packages: id=" + id.toString()); - } - String name = pkg.getName(); - if (mPackagesByName.containsKey(name)) { - throw new AndrolibException("Multiple packages: name=" + name); - } + public ResValue getValue(String package_, String type, String name) + throws AndrolibException { + return getPackage(package_).getType(type).getResSpec(name) + .getDefaultResource().getValue(); + } - mPackagesById.put(id, pkg); - mPackagesByName.put(name, pkg); - if (main) { - mMainPackages.add(pkg); - } else { - mFramePackages.add(pkg); - } - } + public void addPackage(ResPackage pkg, boolean main) + throws AndrolibException { + Integer id = pkg.getId(); + if (mPackagesById.containsKey(id)) { + throw new AndrolibException("Multiple packages: id=" + + id.toString()); + } + String name = pkg.getName(); + if (mPackagesByName.containsKey(name)) { + throw new AndrolibException("Multiple packages: name=" + name); + } - public void setFrameTag(String tag) { - mFrameTag = tag; - } + mPackagesById.put(id, pkg); + mPackagesByName.put(name, pkg); + if (main) { + mMainPackages.add(pkg); + } else { + mFramePackages.add(pkg); + } + } - public Map getSdkInfo() { - return mSdkInfo; - } + public void setFrameTag(String tag) { + mFrameTag = tag; + } - public void addSdkInfo(String key, String value) { - mSdkInfo.put(key, value); - } + public Map getSdkInfo() { + return mSdkInfo; + } - public void clearSdkInfo() { - mSdkInfo.clear(); - } - - public void addPackageInfo(String key, String value) { - mPackageInfo.put(key, value); - } - - public Map getPackageInfo() { - return mPackageInfo; - } - - public boolean isPackageInfoValueSet(String key) { - return (mPackageInfo.containsKey(key)); - } + public void addSdkInfo(String key, String value) { + mSdkInfo.put(key, value); + } + + public void clearSdkInfo() { + mSdkInfo.clear(); + } + + public void addPackageInfo(String key, String value) { + mPackageInfo.put(key, value); + } + + public Map getPackageInfo() { + return mPackageInfo; + } + + public boolean isPackageInfoValueSet(String key) { + return (mPackageInfo.containsKey(key)); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java index c73b7f8f..e87615bf 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java @@ -24,47 +24,44 @@ import java.util.*; * @author Ryszard Wiśniewski */ public final class ResType { - private final String mName; - private final Map mResSpecs = - new LinkedHashMap(); + private final String mName; + private final Map mResSpecs = new LinkedHashMap(); - private final ResTable mResTable; - private final ResPackage mPackage; + private final ResTable mResTable; + private final ResPackage mPackage; - public ResType(String name, ResTable resTable, - ResPackage package_) { - this.mName = name; - this.mResTable = resTable; - this.mPackage = package_; - } + public ResType(String name, ResTable resTable, ResPackage package_) { + this.mName = name; + this.mResTable = resTable; + this.mPackage = package_; + } - public String getName() { - return mName; - } + public String getName() { + return mName; + } - public Set listResSpecs() { - return new LinkedHashSet(mResSpecs.values()); - } + public Set listResSpecs() { + return new LinkedHashSet(mResSpecs.values()); + } - public ResResSpec getResSpec(String name) throws AndrolibException { - ResResSpec spec = mResSpecs.get(name); - if (spec == null) { - throw new UndefinedResObject(String.format( - "resource spec: %s/%s", getName(), name)); - } - return spec; - } + public ResResSpec getResSpec(String name) throws AndrolibException { + ResResSpec spec = mResSpecs.get(name); + if (spec == null) { + throw new UndefinedResObject(String.format("resource spec: %s/%s", + getName(), name)); + } + return spec; + } - public void addResSpec(ResResSpec spec) - throws AndrolibException { - if (mResSpecs.put(spec.getName(), spec) != null) { - throw new AndrolibException(String.format( - "Multiple res specs: %s/%s", getName(), spec.getName())); - } - } + public void addResSpec(ResResSpec spec) throws AndrolibException { + if (mResSpecs.put(spec.getName(), spec) != null) { + throw new AndrolibException(String.format( + "Multiple res specs: %s/%s", getName(), spec.getName())); + } + } - @Override - public String toString() { - return mName; - } + @Override + public String toString() { + return mName; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java index 0b7946a8..c1be40c8 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java @@ -23,68 +23,68 @@ import java.util.Set; * @author Ryszard Wiśniewski */ public class ResValuesFile { - private final ResPackage mPackage; - private final ResType mType; - private final ResConfig mConfig; - private final Set mResources = - new LinkedHashSet(); + private final ResPackage mPackage; + private final ResType mType; + private final ResConfig mConfig; + private final Set mResources = new LinkedHashSet(); - public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) { - this.mPackage = pkg; - this.mType = type; - this.mConfig = config; - } + public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) { + this.mPackage = pkg; + this.mType = type; + this.mConfig = config; + } - public String getPath() { - return "values" + mConfig.getFlags().getQualifiers() - + "/" + mType.getName() - + (mType.getName().endsWith("s") ? "" : "s") - + ".xml"; - } + public String getPath() { + return "values" + mConfig.getFlags().getQualifiers() + "/" + + mType.getName() + (mType.getName().endsWith("s") ? "" : "s") + + ".xml"; + } - public Set listResources() { - return mResources; - } + public Set listResources() { + return mResources; + } - public ResType getType() { - return mType; - } + public ResType getType() { + return mType; + } - public ResConfig getConfig() { - return mConfig; - } + public ResConfig getConfig() { + return mConfig; + } - public boolean isSynthesized(ResResource res) { - return mPackage.isSynthesized(res.getResSpec().getId()); - } + public boolean isSynthesized(ResResource res) { + return mPackage.isSynthesized(res.getResSpec().getId()); + } - public void addResource(ResResource res) { - mResources.add(res); - } + public void addResource(ResResource res) { + mResources.add(res); + } - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ResValuesFile other = (ResValuesFile) obj; - if (this.mType != other.mType && (this.mType == null || !this.mType.equals(other.mType))) { - return false; - } - if (this.mConfig != other.mConfig && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) { - return false; - } - return true; - } + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ResValuesFile other = (ResValuesFile) obj; + if (this.mType != other.mType + && (this.mType == null || !this.mType.equals(other.mType))) { + return false; + } + if (this.mConfig != other.mConfig + && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) { + return false; + } + return true; + } - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0); - hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0); - return hash; - } + @Override + public int hashCode() { + int hash = 17; + hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0); + hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0); + return hash; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java index dfdfcfe9..a35791be 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java @@ -22,70 +22,70 @@ import brut.androlib.res.xml.ResValuesXmlSerializable; import brut.util.Duo; import java.io.IOException; import org.xmlpull.v1.XmlSerializer; -import org.apache.commons.lang3.StringUtils; /** * @author Ryszard Wiśniewski */ -public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializable { - private String mRawItems; - ResArrayValue(ResReferenceValue parent, - Duo[] items) { - super(parent); +public class ResArrayValue extends ResBagValue implements + ResValuesXmlSerializable { + private String mRawItems; - mItems = new ResScalarValue[items.length]; - for (int i = 0; i < items.length; i++) { - mItems[i] = items[i].m2; - } - } + ResArrayValue(ResReferenceValue parent, Duo[] items) { + super(parent); - public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) { - super(parent); - mItems = items; - } + mItems = new ResScalarValue[items.length]; + for (int i = 0; i < items.length; i++) { + mItems[i] = items[i].m2; + } + } - @Override - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException { - String type = getType(); - type = (type == null ? "" : type + "-") + "array"; -// reference array (04 10 2012, BurgerZ) - if ("reference-array".equals(type)) { - type = "string-array"; - } -// reference array (04 10 2012, BurgerZ) - serializer.startTag(null, type); - serializer.attribute(null, "name", res.getResSpec().getName()); - for (int i = 0; i < mItems.length; i++) { - serializer.startTag(null, "item"); - serializer.text(mItems[i].encodeAsResXmlItemValue()); - serializer.endTag(null, "item"); - } - serializer.endTag(null, type); - } + public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) { + super(parent); + mItems = items; + } - public String getType() throws AndrolibException { - if (mItems.length == 0) { - return null; - } - String type = mItems[0].getType(); - for (int i = 1; i < mItems.length; i++) { - - if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) { - return "string"; - } else if (mItems[i].encodeAsResXmlItemValue().startsWith("@drawable")) { - return null; - } else if (!"string".equals(type) && !"integer".equals(type)) { - return null; - } else if (!type.equals(mItems[i].getType())) { - return null; - } - } - return type; - } + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + String type = getType(); + type = (type == null ? "" : type + "-") + "array"; + // reference array (04 10 2012, BurgerZ) + if ("reference-array".equals(type)) { + type = "string-array"; + } + // reference array (04 10 2012, BurgerZ) + serializer.startTag(null, type); + serializer.attribute(null, "name", res.getResSpec().getName()); + for (int i = 0; i < mItems.length; i++) { + serializer.startTag(null, "item"); + serializer.text(mItems[i].encodeAsResXmlItemValue()); + serializer.endTag(null, "item"); + } + serializer.endTag(null, type); + } - private final ResScalarValue[] mItems; + public String getType() throws AndrolibException { + if (mItems.length == 0) { + return null; + } + String type = mItems[0].getType(); + for (int i = 1; i < mItems.length; i++) { + if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) { + return "string"; + } else if (mItems[i].encodeAsResXmlItemValue().startsWith( + "@drawable")) { + return null; + } else if (!"string".equals(type) && !"integer".equals(type)) { + return null; + } else if (!type.equals(mItems[i].getType())) { + return null; + } + } + return type; + } - public static final int BAG_KEY_ARRAY_START = 0x02000000; + private final ResScalarValue[] mItems; + + public static final int BAG_KEY_ARRAY_START = 0x02000000; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java index 24282d31..3fac36d5 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java @@ -28,148 +28,148 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable { - ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, - Boolean l10n) { - super(parentVal); - mType = type; - mMin = min; - mMax = max; - mL10n = l10n; - } + ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, + Boolean l10n) { + super(parentVal); + mType = type; + mMin = min; + mMax = max; + mL10n = l10n; + } - public String convertToResXmlFormat(ResScalarValue value) - throws AndrolibException { - return null; - } + public String convertToResXmlFormat(ResScalarValue value) + throws AndrolibException { + return null; + } - @Override - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException { - String type = getTypeAsString(); + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + String type = getTypeAsString(); - serializer.startTag(null, "attr"); - serializer.attribute(null, "name", res.getResSpec().getName()); - if (type != null) { - serializer.attribute(null, "format", type); - } - if (mMin != null) { - serializer.attribute(null, "min", mMin.toString()); - } - if (mMax != null) { - serializer.attribute(null, "max", mMax.toString()); - } - if (mL10n != null && mL10n) { - serializer.attribute(null, "localization", "suggested"); - } - serializeBody(serializer, res); - serializer.endTag(null, "attr"); - } + serializer.startTag(null, "attr"); + serializer.attribute(null, "name", res.getResSpec().getName()); + if (type != null) { + serializer.attribute(null, "format", type); + } + if (mMin != null) { + serializer.attribute(null, "min", mMin.toString()); + } + if (mMax != null) { + serializer.attribute(null, "max", mMax.toString()); + } + if (mL10n != null && mL10n) { + serializer.attribute(null, "localization", "suggested"); + } + serializeBody(serializer, res); + serializer.endTag(null, "attr"); + } + public static ResAttr factory(ResReferenceValue parent, + Duo[] items, ResValueFactory factory, + ResPackage pkg) throws AndrolibException { - public static ResAttr factory(ResReferenceValue parent, - Duo[] items, ResValueFactory factory, - ResPackage pkg) throws AndrolibException { + int type = ((ResIntValue) items[0].m2).getValue(); + int scalarType = type & 0xffff; + Integer min = null, max = null; + Boolean l10n = null; + int i; + for (i = 1; i < items.length; i++) { + switch (items[i].m1) { + case BAG_KEY_ATTR_MIN: + min = ((ResIntValue) items[i].m2).getValue(); + continue; + case BAG_KEY_ATTR_MAX: + max = ((ResIntValue) items[i].m2).getValue(); + continue; + case BAG_KEY_ATTR_L10N: + l10n = ((ResIntValue) items[i].m2).getValue() != 0; + continue; + } + break; + } - int type = ((ResIntValue) items[0].m2).getValue(); - int scalarType = type & 0xffff; - Integer min = null, max = null; - Boolean l10n = null; - int i; - for (i = 1; i < items.length; i++) { - switch (items[i].m1) { - case BAG_KEY_ATTR_MIN: - min = ((ResIntValue) items[i].m2).getValue(); - continue; - case BAG_KEY_ATTR_MAX: - max = ((ResIntValue) items[i].m2).getValue(); - continue; - case BAG_KEY_ATTR_L10N: - l10n = ((ResIntValue) items[i].m2).getValue() != 0; - continue; - } - break; - } + if (i == items.length) { + return new ResAttr(parent, scalarType, min, max, l10n); + } + Duo[] attrItems = new Duo[items.length + - i]; + int j = 0; + for (; i < items.length; i++) { + int resId = items[i].m1; + pkg.addSynthesizedRes(resId); + attrItems[j++] = new Duo( + factory.newReference(resId, null), + (ResIntValue) items[i].m2); + } + switch (type & 0xff0000) { + case TYPE_ENUM: + return new ResEnumAttr(parent, scalarType, min, max, l10n, + attrItems); + case TYPE_FLAGS: + return new ResFlagsAttr(parent, scalarType, min, max, l10n, + attrItems); + } - if (i == items.length) { - return new ResAttr(parent, scalarType, min, max, l10n); - } - Duo[] attrItems = - new Duo[items.length - i]; - int j = 0; - for (; i < items.length; i++) { - int resId = items[i].m1; - pkg.addSynthesizedRes(resId); - attrItems[j++] = new Duo( - factory.newReference(resId, null), (ResIntValue) items[i].m2); - } - switch (type & 0xff0000) { - case TYPE_ENUM: - return new ResEnumAttr( - parent, scalarType, min, max, l10n, attrItems); - case TYPE_FLAGS: - return new ResFlagsAttr( - parent, scalarType, min, max, l10n, attrItems); - } + throw new AndrolibException("Could not decode attr value"); + } - throw new AndrolibException("Could not decode attr value"); - } + protected void serializeBody(XmlSerializer serializer, ResResource res) + throws AndrolibException, IOException { + } - protected void serializeBody(XmlSerializer serializer, ResResource res) - throws AndrolibException, IOException {} + protected String getTypeAsString() { + String s = ""; + if ((mType & TYPE_REFERENCE) != 0) { + s += "|reference"; + } + if ((mType & TYPE_STRING) != 0) { + s += "|string"; + } + if ((mType & TYPE_INT) != 0) { + s += "|integer"; + } + if ((mType & TYPE_BOOL) != 0) { + s += "|boolean"; + } + if ((mType & TYPE_COLOR) != 0) { + s += "|color"; + } + if ((mType & TYPE_FLOAT) != 0) { + s += "|float"; + } + if ((mType & TYPE_DIMEN) != 0) { + s += "|dimension"; + } + if ((mType & TYPE_FRACTION) != 0) { + s += "|fraction"; + } + if (s.isEmpty()) { + return null; + } + return s.substring(1); + } - protected String getTypeAsString() { - String s = ""; - if ((mType & TYPE_REFERENCE) != 0) { - s += "|reference"; - } - if ((mType & TYPE_STRING) != 0) { - s += "|string"; - } - if ((mType & TYPE_INT) != 0) { - s += "|integer"; - } - if ((mType & TYPE_BOOL) != 0) { - s += "|boolean"; - } - if ((mType & TYPE_COLOR) != 0) { - s += "|color"; - } - if ((mType & TYPE_FLOAT) != 0) { - s += "|float"; - } - if ((mType & TYPE_DIMEN) != 0) { - s += "|dimension"; - } - if ((mType & TYPE_FRACTION) != 0) { - s += "|fraction"; - } - if (s.isEmpty()) { - return null; - } - return s.substring(1); - } + private final int mType; + private final Integer mMin; + private final Integer mMax; + private final Boolean mL10n; - private final int mType; - private final Integer mMin; - private final Integer mMax; - private final Boolean mL10n; + public static final int BAG_KEY_ATTR_TYPE = 0x01000000; + private static final int BAG_KEY_ATTR_MIN = 0x01000001; + private static final int BAG_KEY_ATTR_MAX = 0x01000002; + private static final int BAG_KEY_ATTR_L10N = 0x01000003; + private final static int TYPE_REFERENCE = 0x01; + private final static int TYPE_STRING = 0x02; + private final static int TYPE_INT = 0x04; + private final static int TYPE_BOOL = 0x08; + private final static int TYPE_COLOR = 0x10; + private final static int TYPE_FLOAT = 0x20; + private final static int TYPE_DIMEN = 0x40; + private final static int TYPE_FRACTION = 0x80; + private final static int TYPE_ANY_STRING = 0xee; - public static final int BAG_KEY_ATTR_TYPE = 0x01000000; - private static final int BAG_KEY_ATTR_MIN = 0x01000001; - private static final int BAG_KEY_ATTR_MAX = 0x01000002; - private static final int BAG_KEY_ATTR_L10N = 0x01000003; - - private final static int TYPE_REFERENCE = 0x01; - private final static int TYPE_STRING = 0x02; - private final static int TYPE_INT = 0x04; - private final static int TYPE_BOOL = 0x08; - private final static int TYPE_COLOR = 0x10; - private final static int TYPE_FLOAT = 0x20; - private final static int TYPE_DIMEN = 0x40; - private final static int TYPE_FRACTION = 0x80; - private final static int TYPE_ANY_STRING = 0xee; - - private static final int TYPE_ENUM = 0x00010000; - private static final int TYPE_FLAGS = 0x00020000; + private static final int TYPE_ENUM = 0x00010000; + private static final int TYPE_FLAGS = 0x00020000; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java index f8bdeed8..0e01624c 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java @@ -27,38 +27,39 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResBagValue extends ResValue implements ResValuesXmlSerializable { - protected final ResReferenceValue mParent; + protected final ResReferenceValue mParent; - public ResBagValue(ResReferenceValue parent) { - this.mParent = parent; - } + public ResBagValue(ResReferenceValue parent) { + this.mParent = parent; + } - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException { - String type = res.getResSpec().getType().getName(); - if ("style".equals(type)) { - new ResStyleValue(mParent, new Duo[0], null) - .serializeToResValuesXml(serializer, res); - return; - } - if ("array".equals(type)) { - new ResArrayValue(mParent, new Duo[0]) - .serializeToResValuesXml(serializer, res); - return; - } - if ("plurals".equals(type)) { - new ResPluralsValue(mParent, new Duo[0]) - .serializeToResValuesXml(serializer, res); - return; - } + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + String type = res.getResSpec().getType().getName(); + if ("style".equals(type)) { + new ResStyleValue(mParent, new Duo[0], null) + .serializeToResValuesXml(serializer, res); + return; + } + if ("array".equals(type)) { + new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml( + serializer, res); + return; + } + if ("plurals".equals(type)) { + new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml( + serializer, res); + return; + } - serializer.startTag(null, "item"); - serializer.attribute(null, "type", type); - serializer.attribute(null, "name", res.getResSpec().getName()); - serializer.endTag(null, "item"); - } + serializer.startTag(null, "item"); + serializer.attribute(null, "type", type); + serializer.attribute(null, "name", res.getResSpec().getName()); + serializer.endTag(null, "item"); + } - public ResReferenceValue getParent() { - return mParent; - } + public ResReferenceValue getParent() { + return mParent; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java index 807196fe..c8334eeb 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java @@ -20,18 +20,19 @@ package brut.androlib.res.data.value; * @author Ryszard Wiśniewski */ public class ResBoolValue extends ResScalarValue { - private final boolean mValue; + private final boolean mValue; - public ResBoolValue(boolean value, String rawValue) { - super("bool", rawValue); - this.mValue = value; - } + public ResBoolValue(boolean value, String rawValue) { + super("bool", rawValue); + this.mValue = value; + } - public boolean getValue() { - return mValue; - } + public boolean getValue() { + return mValue; + } - protected String encodeAsResXml() { - return mValue ? "true" : "false"; - } + @Override + protected String encodeAsResXml() { + return mValue ? "true" : "false"; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java index 224186f3..fc83da95 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java @@ -20,12 +20,12 @@ package brut.androlib.res.data.value; * @author Ryszard Wiśniewski */ public class ResColorValue extends ResIntValue { - public ResColorValue(int value, String rawValue) { - super(value, rawValue, "color"); - } + public ResColorValue(int value, String rawValue) { + super(value, rawValue, "color"); + } - @Override - protected String encodeAsResXml() { - return String.format("#%08x", mValue); - } + @Override + protected String encodeAsResXml() { + return String.format("#%08x", mValue); + } } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java index d00a6077..24959a44 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java @@ -23,12 +23,12 @@ import brut.androlib.AndrolibException; * @author Ryszard Wiśniewski */ public class ResDimenValue extends ResIntValue { - public ResDimenValue(int value, String rawValue) { - super(value, rawValue, "dimen"); - } + public ResDimenValue(int value, String rawValue) { + super(value, rawValue, "dimen"); + } - @Override - protected String encodeAsResXml() throws AndrolibException { - return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue); - } + @Override + protected String encodeAsResXml() throws AndrolibException { + return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java index bf550d3f..1dd5410b 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java @@ -28,57 +28,55 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResEnumAttr extends ResAttr { - ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max, - Boolean l10n, Duo[] items) { - super(parent, type, min, max, l10n); - mItems = items; - } + ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max, + Boolean l10n, Duo[] items) { + super(parent, type, min, max, l10n); + mItems = items; + } - @Override - public String convertToResXmlFormat(ResScalarValue value) - throws AndrolibException { - if (value instanceof ResIntValue) { - String ret = decodeValue(((ResIntValue) value).getValue()); - if (ret != null) { - return ret; - } - } - return super.convertToResXmlFormat(value); - } + @Override + public String convertToResXmlFormat(ResScalarValue value) + throws AndrolibException { + if (value instanceof ResIntValue) { + String ret = decodeValue(((ResIntValue) value).getValue()); + if (ret != null) { + return ret; + } + } + return super.convertToResXmlFormat(value); + } - @Override - protected void serializeBody(XmlSerializer serializer, ResResource res) - throws AndrolibException, IOException { - for (Duo duo : mItems) { - int intVal = duo.m2.getValue(); + @Override + protected void serializeBody(XmlSerializer serializer, ResResource res) + throws AndrolibException, IOException { + for (Duo duo : mItems) { + int intVal = duo.m2.getValue(); - serializer.startTag(null, "enum"); - serializer.attribute(null, "name", duo.m1.getReferent().getName()); - serializer.attribute(null, "value", String.valueOf(intVal)); - serializer.endTag(null, "enum"); - } - } + serializer.startTag(null, "enum"); + serializer.attribute(null, "name", duo.m1.getReferent().getName()); + serializer.attribute(null, "value", String.valueOf(intVal)); + serializer.endTag(null, "enum"); + } + } - private String decodeValue(int value) throws AndrolibException { - String value2 = mItemsCache.get(value); - if (value2 == null) { - ResReferenceValue ref = null; - for (Duo duo : mItems) { - if (duo.m2.getValue() == value) { - ref = duo.m1; - break; - } - } - if (ref != null) { - value2 = ref.getReferent().getName(); - mItemsCache.put(value, value2); - } - } - return value2; - } + private String decodeValue(int value) throws AndrolibException { + String value2 = mItemsCache.get(value); + if (value2 == null) { + ResReferenceValue ref = null; + for (Duo duo : mItems) { + if (duo.m2.getValue() == value) { + ref = duo.m1; + break; + } + } + if (ref != null) { + value2 = ref.getReferent().getName(); + mItemsCache.put(value, value2); + } + } + return value2; + } - - private final Duo[] mItems; - private final Map mItemsCache = - new HashMap(); + private final Duo[] mItems; + private final Map mItemsCache = new HashMap(); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java index e6ddff64..21656e29 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java @@ -22,21 +22,21 @@ import brut.androlib.AndrolibException; * @author Ryszard Wiśniewski */ public class ResFileValue extends ResValue { - private final String mPath; + private final String mPath; - public ResFileValue(String path) { - this.mPath = path; - } + public ResFileValue(String path) { + this.mPath = path; + } - public String getPath() { - return mPath; - } + public String getPath() { + return mPath; + } - public String getStrippedPath() throws AndrolibException { - if (! mPath.startsWith("res/")) { - throw new AndrolibException( - "File path does not start with \"res/\": " + mPath); - } - return mPath.substring(4); - } + public String getStrippedPath() throws AndrolibException { + if (!mPath.startsWith("res/")) { + throw new AndrolibException( + "File path does not start with \"res/\": " + mPath); + } + return mPath.substring(4); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java index 7761562c..f73b85b9 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java @@ -28,133 +28,133 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResFlagsAttr extends ResAttr { - ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, Boolean l10n, Duo[] items) { - super(parent, type, min, max, l10n); + ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, + Boolean l10n, Duo[] items) { + super(parent, type, min, max, l10n); - mItems = new FlagItem[items.length]; - for (int i = 0; i < items.length; i++) { - mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue()); - } - } + mItems = new FlagItem[items.length]; + for (int i = 0; i < items.length; i++) { + mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue()); + } + } - @Override - public String convertToResXmlFormat(ResScalarValue value) - throws AndrolibException { - if (! (value instanceof ResIntValue)) { - return super.convertToResXmlFormat(value); - } - loadFlags(); - int intVal = ((ResIntValue) value).getValue(); + @Override + public String convertToResXmlFormat(ResScalarValue value) + throws AndrolibException { + if (!(value instanceof ResIntValue)) { + return super.convertToResXmlFormat(value); + } + loadFlags(); + int intVal = ((ResIntValue) value).getValue(); - if (intVal == 0) { - return renderFlags(mZeroFlags); - } + if (intVal == 0) { + return renderFlags(mZeroFlags); + } - FlagItem[] flagItems = new FlagItem[mFlags.length]; - int[] flags = new int[mFlags.length]; - int flagsCount = 0; - for (int i = 0; i < mFlags.length; i++) { - FlagItem flagItem = mFlags[i]; - int flag = flagItem.flag; + FlagItem[] flagItems = new FlagItem[mFlags.length]; + int[] flags = new int[mFlags.length]; + int flagsCount = 0; + for (int i = 0; i < mFlags.length; i++) { + FlagItem flagItem = mFlags[i]; + int flag = flagItem.flag; - if ((intVal & flag) != flag) { - continue; - } + if ((intVal & flag) != flag) { + continue; + } - if (! isSubpartOf(flag, flags)) { - flags[flagsCount] = flag; - flagItems[flagsCount++] = flagItem; - } - } - return renderFlags(Arrays.copyOf(flagItems, flagsCount)); - } + if (!isSubpartOf(flag, flags)) { + flags[flagsCount] = flag; + flagItems[flagsCount++] = flagItem; + } + } + return renderFlags(Arrays.copyOf(flagItems, flagsCount)); + } - @Override - protected void serializeBody(XmlSerializer serializer, ResResource res) - throws AndrolibException, IOException { - for (int i = 0; i < mItems.length; i++) { - FlagItem item = mItems[i]; + @Override + protected void serializeBody(XmlSerializer serializer, ResResource res) + throws AndrolibException, IOException { + for (int i = 0; i < mItems.length; i++) { + FlagItem item = mItems[i]; - serializer.startTag(null, "flag"); - serializer.attribute(null, "name", item.getValue()); - serializer.attribute(null, "value", - String.format("0x%08x", item.flag)); - serializer.endTag(null, "flag"); - } - } + serializer.startTag(null, "flag"); + serializer.attribute(null, "name", item.getValue()); + serializer.attribute(null, "value", + String.format("0x%08x", item.flag)); + serializer.endTag(null, "flag"); + } + } - private boolean isSubpartOf(int flag, int[] flags) { - for (int i = 0; i < flags.length; i++) { - if ((flags[i] & flag) == flag) { - return true; - } - } - return false; - } + private boolean isSubpartOf(int flag, int[] flags) { + for (int i = 0; i < flags.length; i++) { + if ((flags[i] & flag) == flag) { + return true; + } + } + return false; + } - private String renderFlags(FlagItem[] flags) throws AndrolibException { - String ret = ""; - for (int i = 0; i < flags.length; i++) { - ret += "|" + flags[i].getValue(); - } - if (ret.isEmpty()) { - return ret; - } - return ret.substring(1); - } + private String renderFlags(FlagItem[] flags) throws AndrolibException { + String ret = ""; + for (int i = 0; i < flags.length; i++) { + ret += "|" + flags[i].getValue(); + } + if (ret.isEmpty()) { + return ret; + } + return ret.substring(1); + } - private void loadFlags() { - if (mFlags != null) { - return; - } + private void loadFlags() { + if (mFlags != null) { + return; + } - FlagItem[] zeroFlags = new FlagItem[mItems.length]; - int zeroFlagsCount = 0; - FlagItem[] flags = new FlagItem[mItems.length]; - int flagsCount = 0; + FlagItem[] zeroFlags = new FlagItem[mItems.length]; + int zeroFlagsCount = 0; + FlagItem[] flags = new FlagItem[mItems.length]; + int flagsCount = 0; - for (int i = 0; i < mItems.length; i++) { - FlagItem item = mItems[i]; - if (item.flag == 0) { - zeroFlags[zeroFlagsCount++] = item; - } else { - flags[flagsCount++] = item; - } - } + for (int i = 0; i < mItems.length; i++) { + FlagItem item = mItems[i]; + if (item.flag == 0) { + zeroFlags[zeroFlagsCount++] = item; + } else { + flags[flagsCount++] = item; + } + } - mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount); - mFlags = Arrays.copyOf(flags, flagsCount); + mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount); + mFlags = Arrays.copyOf(flags, flagsCount); - Arrays.sort(mFlags, new Comparator() { - public int compare(FlagItem o1, FlagItem o2) { - return Integer.valueOf(Integer.bitCount(o2.flag)) - .compareTo(Integer.bitCount(o1.flag)); - } - }); - } + Arrays.sort(mFlags, new Comparator() { + @Override + public int compare(FlagItem o1, FlagItem o2) { + return Integer.valueOf(Integer.bitCount(o2.flag)).compareTo( + Integer.bitCount(o1.flag)); + } + }); + } + private final FlagItem[] mItems; - private final FlagItem[] mItems; + private FlagItem[] mZeroFlags; + private FlagItem[] mFlags; - private FlagItem[] mZeroFlags; - private FlagItem[] mFlags; + private static class FlagItem { + public final ResReferenceValue ref; + public final int flag; + public String value; + public FlagItem(ResReferenceValue ref, int flag) { + this.ref = ref; + this.flag = flag; + } - private static class FlagItem { - public final ResReferenceValue ref; - public final int flag; - public String value; - - public FlagItem(ResReferenceValue ref, int flag) { - this.ref = ref; - this.flag = flag; - } - - public String getValue() throws AndrolibException { - if (value == null) { - value = ref.getReferent().getName(); - } - return value; - } - } + public String getValue() throws AndrolibException { + if (value == null) { + value = ref.getReferent().getName(); + } + return value; + } + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java index bbd77219..c7680e6e 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java @@ -20,18 +20,19 @@ package brut.androlib.res.data.value; * @author Ryszard Wiśniewski */ public class ResFloatValue extends ResScalarValue { - private final float mValue; + private final float mValue; - public ResFloatValue(float value, String rawValue) { - super("float", rawValue); - this.mValue = value; - } + public ResFloatValue(float value, String rawValue) { + super("float", rawValue); + this.mValue = value; + } - public float getValue() { - return mValue; - } + public float getValue() { + return mValue; + } - protected String encodeAsResXml() { - return String.valueOf(mValue); - } + @Override + protected String encodeAsResXml() { + return String.valueOf(mValue); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java index c3325834..3fddbfad 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java @@ -23,12 +23,12 @@ import brut.androlib.AndrolibException; * @author Ryszard Wiśniewski */ public class ResFractionValue extends ResIntValue { - public ResFractionValue(int value, String rawValue) { - super(value, rawValue, "fraction"); - } + public ResFractionValue(int value, String rawValue) { + super(value, rawValue, "fraction"); + } - @Override - protected String encodeAsResXml() throws AndrolibException { - return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue); - } + @Override + protected String encodeAsResXml() throws AndrolibException { + return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java index cb08ae89..38eaaed9 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java @@ -26,10 +26,13 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResIdValue extends ResValue implements ResValuesXmlSerializable { - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) throws IOException, AndrolibException { - serializer.startTag(null, "item"); - serializer.attribute(null, "type", res.getResSpec().getType().getName()); - serializer.attribute(null, "name", res.getResSpec().getName()); - serializer.endTag(null, "item"); - } + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + serializer.startTag(null, "item"); + serializer + .attribute(null, "type", res.getResSpec().getType().getName()); + serializer.attribute(null, "name", res.getResSpec().getName()); + serializer.endTag(null, "item"); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java index d1139995..d1662657 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java @@ -23,24 +23,25 @@ import brut.androlib.AndrolibException; * @author Ryszard Wiśniewski */ public class ResIntValue extends ResScalarValue { - protected final int mValue; - private int type; + protected final int mValue; + private int type; - public ResIntValue(int value, String rawValue, int type) { - this(value, rawValue, "integer"); - this.type = type; - } + public ResIntValue(int value, String rawValue, int type) { + this(value, rawValue, "integer"); + this.type = type; + } - public ResIntValue(int value, String rawValue, String type) { - super(type, rawValue); - this.mValue = value; - } + public ResIntValue(int value, String rawValue, String type) { + super(type, rawValue); + this.mValue = value; + } - public int getValue() { - return mValue; - } + public int getValue() { + return mValue; + } - protected String encodeAsResXml() throws AndrolibException { - return TypedValue.coerceToString(type, mValue); - } + @Override + protected String encodeAsResXml() throws AndrolibException { + return TypedValue.coerceToString(type, mValue); + } } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java index b5902924..40b89a05 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java @@ -28,54 +28,57 @@ import org.xmlpull.v1.XmlSerializer; /** * @author Ryszard Wiśniewski */ -public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializable { - ResPluralsValue(ResReferenceValue parent, - Duo[] items) { - super(parent); +public class ResPluralsValue extends ResBagValue implements + ResValuesXmlSerializable { + ResPluralsValue(ResReferenceValue parent, + Duo[] items) { + super(parent); - mItems = new ResScalarValue[6]; - for (int i = 0; i < items.length; i++) { - mItems[items[i].m1 - BAG_KEY_PLURALS_START] = - (ResScalarValue) items[i].m2; - } - } + mItems = new ResScalarValue[6]; + for (int i = 0; i < items.length; i++) { + mItems[items[i].m1 - BAG_KEY_PLURALS_START] = items[i].m2; + } + } - @Override - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException { - serializer.startTag(null, "plurals"); - serializer.attribute(null, "name", res.getResSpec().getName()); - for (int i = 0; i < mItems.length; i++) { - ResScalarValue item = mItems[i]; - if (item == null) { - continue; - } + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + serializer.startTag(null, "plurals"); + serializer.attribute(null, "name", res.getResSpec().getName()); + for (int i = 0; i < mItems.length; i++) { + ResScalarValue item = mItems[i]; + if (item == null) { + continue; + } - ResScalarValue rawValue = item; + ResScalarValue rawValue = item; - serializer.startTag(null, "item"); - serializer.attribute(null, "quantity", QUANTITY_MAP[i]); - if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue.encodeAsResXmlValue())) { - serializer.text(item.encodeAsResXmlValueExt()); - } else { - String recode = item.encodeAsResXmlValue(); - //Dirty, but working fix @miuirussia - for (int j = 0; j < 10; j++) { - recode = StringUtils.replace(recode, "%" + Integer.toString(j) + "$" + Integer.toString(j) + "$", "%" + Integer.toString(j) + "$"); - } - serializer.text(recode); - } - serializer.endTag(null, "item"); - } - serializer.endTag(null, "plurals"); - } + serializer.startTag(null, "item"); + serializer.attribute(null, "quantity", QUANTITY_MAP[i]); + if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue + .encodeAsResXmlValue())) { + serializer.text(item.encodeAsResXmlValueExt()); + } else { + String recode = item.encodeAsResXmlValue(); + // Dirty, but working fix @miuirussia + for (int j = 0; j < 10; j++) { + recode = StringUtils.replace( + recode, + "%" + Integer.toString(j) + "$" + + Integer.toString(j) + "$", + "%" + Integer.toString(j) + "$"); + } + serializer.text(recode); + } + serializer.endTag(null, "item"); + } + serializer.endTag(null, "plurals"); + } + private final ResScalarValue[] mItems; - private final ResScalarValue[] mItems; - - - public static final int BAG_KEY_PLURALS_START = 0x01000004; - public static final int BAG_KEY_PLURALS_END = 0x01000009; - private static final String[] QUANTITY_MAP = - new String[] {"other", "zero", "one", "two", "few", "many"}; + public static final int BAG_KEY_PLURALS_START = 0x01000004; + public static final int BAG_KEY_PLURALS_END = 0x01000009; + private static final String[] QUANTITY_MAP = new String[] { "other", + "zero", "one", "two", "few", "many" }; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java index 22837ede..96398768 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java @@ -24,44 +24,44 @@ import brut.androlib.res.data.ResResSpec; * @author Ryszard Wiśniewski */ public class ResReferenceValue extends ResIntValue { - private final ResPackage mPackage; - private final boolean mTheme; + private final ResPackage mPackage; + private final boolean mTheme; - public ResReferenceValue(ResPackage package_, int value, String rawValue) { - this(package_, value, rawValue, false); - } - - public ResReferenceValue(ResPackage package_, int value, String rawValue, - boolean theme) { - super(value, rawValue, "reference"); - mPackage = package_; - mTheme = theme; - } + public ResReferenceValue(ResPackage package_, int value, String rawValue) { + this(package_, value, rawValue, false); + } - protected String encodeAsResXml() throws AndrolibException { - if (isNull()) { - return "@null"; - } + public ResReferenceValue(ResPackage package_, int value, String rawValue, + boolean theme) { + super(value, rawValue, "reference"); + mPackage = package_; + mTheme = theme; + } - ResResSpec spec = getReferent(); - boolean newId = - spec.hasDefaultResource() && - spec.getDefaultResource().getValue() instanceof ResIdValue; + @Override + protected String encodeAsResXml() throws AndrolibException { + if (isNull()) { + return "@null"; + } - // generate the beginning to fix @android - String mStart = (mTheme ? '?' : '@') + (newId ? "+" : ""); - //mStart = mStart.replace("@android", "@*android"); - - return mStart + - spec.getFullName(mPackage, - mTheme && spec.getType().getName().equals("attr")); - } + ResResSpec spec = getReferent(); + boolean newId = spec.hasDefaultResource() + && spec.getDefaultResource().getValue() instanceof ResIdValue; - public ResResSpec getReferent() throws AndrolibException { - return mPackage.getResTable().getResSpec(getValue()); - } + // generate the beginning to fix @android + String mStart = (mTheme ? '?' : '@') + (newId ? "+" : ""); + // mStart = mStart.replace("@android", "@*android"); - public boolean isNull() { - return mValue == 0; - } + return mStart + + spec.getFullName(mPackage, mTheme + && spec.getType().getName().equals("attr")); + } + + public ResResSpec getReferent() throws AndrolibException { + return mPackage.getResTable().getResSpec(getValue()); + } + + public boolean isNull() { + return mValue == 0; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java index 98424ae3..c540eee8 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java @@ -27,105 +27,107 @@ import org.xmlpull.v1.XmlSerializer; /** * @author Ryszard Wiśniewski */ -public abstract class ResScalarValue extends ResValue - implements ResXmlEncodable, ResValuesXmlSerializable { - protected final String mType; - protected final String mRawValue; +public abstract class ResScalarValue extends ResValue implements + ResXmlEncodable, ResValuesXmlSerializable { + protected final String mType; + protected final String mRawValue; - protected ResScalarValue(String type, String rawValue) { - mType = type; - mRawValue = rawValue; - } + protected ResScalarValue(String type, String rawValue) { + mType = type; + mRawValue = rawValue; + } - public String encodeAsResXmlAttr() throws AndrolibException { - if (mRawValue != null) { - return mRawValue; - } - return encodeAsResXml().replace("@android:", "@*android:"); - } + @Override + public String encodeAsResXmlAttr() throws AndrolibException { + if (mRawValue != null) { + return mRawValue; + } + return encodeAsResXml().replace("@android:", "@*android:"); + } - public String encodeAsResXmlItemValue() throws AndrolibException { - return encodeAsResXmlValue().replace("@android:", "@*android:"); - } + public String encodeAsResXmlItemValue() throws AndrolibException { + return encodeAsResXmlValue().replace("@android:", "@*android:"); + } - public String encodeAsResXmlValue() throws AndrolibException { - if (mRawValue != null) { - return mRawValue; - } - return encodeAsResXmlValueExt().replace("@android:", "@*android:"); - } + @Override + public String encodeAsResXmlValue() throws AndrolibException { + if (mRawValue != null) { + return mRawValue; + } + return encodeAsResXmlValueExt().replace("@android:", "@*android:"); + } - public String encodeAsResXmlValueExt() throws AndrolibException { - String rawValue = mRawValue; - if (rawValue != null) { - if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) { - int count = 1; - StringBuilder result = new StringBuilder(); - String tmp1[] = rawValue.split("%%", -1); - int tmp1_sz = tmp1.length; - for(int i=0;i */ -public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializable { - ResStyleValue(ResReferenceValue parent, - Duo[] items, ResValueFactory factory) { - super(parent); +public class ResStyleValue extends ResBagValue implements + ResValuesXmlSerializable { + ResStyleValue(ResReferenceValue parent, + Duo[] items, ResValueFactory factory) { + super(parent); - mItems = new Duo[items.length]; - for (int i = 0; i < items.length; i++) { - mItems[i] = new Duo( - factory.newReference(items[i].m1, null), items[i].m2); - } - } + mItems = new Duo[items.length]; + for (int i = 0; i < items.length; i++) { + mItems[i] = new Duo( + factory.newReference(items[i].m1, null), items[i].m2); + } + } - @Override - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException { - serializer.startTag(null, "style"); - serializer.attribute(null, "name", res.getResSpec().getName()); - if (! mParent.isNull()) { - serializer.attribute(null, "parent", mParent.encodeAsResXmlAttr()); - } - for (int i = 0; i < mItems.length; i++) { - ResResSpec spec = mItems[i].m1.getReferent(); - - // hacky-fix remove bad ReferenceVars - if (spec.getDefaultResource().getValue().toString().contains("ResReferenceValue@")) { - continue; - } - ResAttr attr = (ResAttr) spec.getDefaultResource().getValue(); - String value = attr.convertToResXmlFormat(mItems[i].m2); + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + serializer.startTag(null, "style"); + serializer.attribute(null, "name", res.getResSpec().getName()); + if (!mParent.isNull()) { + serializer.attribute(null, "parent", mParent.encodeAsResXmlAttr()); + } + for (int i = 0; i < mItems.length; i++) { + ResResSpec spec = mItems[i].m1.getReferent(); - if (value == null) { - value = mItems[i].m2.encodeAsResXmlValue(); - } + // hacky-fix remove bad ReferenceVars + if (spec.getDefaultResource().getValue().toString() + .contains("ResReferenceValue@")) { + continue; + } + ResAttr attr = (ResAttr) spec.getDefaultResource().getValue(); + String value = attr.convertToResXmlFormat(mItems[i].m2); - if (value == null) { - continue; - } + if (value == null) { + value = mItems[i].m2.encodeAsResXmlValue(); + } - serializer.startTag(null, "item"); - serializer.attribute(null, "name", - spec.getFullName(res.getResSpec().getPackage(), true)); - serializer.text(value); - serializer.endTag(null, "item"); - } - serializer.endTag(null, "style"); - } + if (value == null) { + continue; + } + serializer.startTag(null, "item"); + serializer.attribute(null, "name", + spec.getFullName(res.getResSpec().getPackage(), true)); + serializer.text(value); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "style"); + } - private final Duo[] mItems; + private final Duo[] mItems; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java index a123002a..3c4693f6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java @@ -25,77 +25,77 @@ import brut.util.Duo; * @author Ryszard Wiśniewski */ public class ResValueFactory { - private final ResPackage mPackage; + private final ResPackage mPackage; - public ResValueFactory(ResPackage pakage_) { - this.mPackage = pakage_; - } + public ResValueFactory(ResPackage pakage_) { + this.mPackage = pakage_; + } - public ResScalarValue factory(int type, int value, String rawValue) - throws AndrolibException { - switch (type) { - case TypedValue.TYPE_REFERENCE: - return newReference(value, rawValue); - case TypedValue.TYPE_ATTRIBUTE: - return newReference(value, rawValue, true); - case TypedValue.TYPE_STRING: - return new ResStringValue(rawValue); - case TypedValue.TYPE_FLOAT: - return new ResFloatValue(Float.intBitsToFloat(value), rawValue); - case TypedValue.TYPE_DIMENSION: - return new ResDimenValue(value, rawValue); - case TypedValue.TYPE_FRACTION: - return new ResFractionValue(value, rawValue); - case TypedValue.TYPE_INT_BOOLEAN: - return new ResBoolValue(value != 0, rawValue); - } + public ResScalarValue factory(int type, int value, String rawValue) + throws AndrolibException { + switch (type) { + case TypedValue.TYPE_REFERENCE: + return newReference(value, rawValue); + case TypedValue.TYPE_ATTRIBUTE: + return newReference(value, rawValue, true); + case TypedValue.TYPE_STRING: + return new ResStringValue(rawValue); + case TypedValue.TYPE_FLOAT: + return new ResFloatValue(Float.intBitsToFloat(value), rawValue); + case TypedValue.TYPE_DIMENSION: + return new ResDimenValue(value, rawValue); + case TypedValue.TYPE_FRACTION: + return new ResFractionValue(value, rawValue); + case TypedValue.TYPE_INT_BOOLEAN: + return new ResBoolValue(value != 0, rawValue); + } - if (type >= TypedValue.TYPE_FIRST_COLOR_INT - && type <= TypedValue.TYPE_LAST_COLOR_INT) { - return new ResColorValue(value, rawValue); - } - if (type >= TypedValue.TYPE_FIRST_INT - && type <= TypedValue.TYPE_LAST_INT) { - return new ResIntValue(value, rawValue, type); - } + if (type >= TypedValue.TYPE_FIRST_COLOR_INT + && type <= TypedValue.TYPE_LAST_COLOR_INT) { + return new ResColorValue(value, rawValue); + } + if (type >= TypedValue.TYPE_FIRST_INT + && type <= TypedValue.TYPE_LAST_INT) { + return new ResIntValue(value, rawValue, type); + } - throw new AndrolibException("Invalid value type: "+ type); - } + throw new AndrolibException("Invalid value type: " + type); + } - public ResValue factory(String value) { - if (value.startsWith("res/")) { - return new ResFileValue(value); - } - return new ResStringValue(value); - } + public ResValue factory(String value) { + if (value.startsWith("res/")) { + return new ResFileValue(value); + } + return new ResStringValue(value); + } - public ResBagValue bagFactory(int parent, - Duo[] items) throws AndrolibException { - ResReferenceValue parentVal = newReference(parent, null); + public ResBagValue bagFactory(int parent, + Duo[] items) throws AndrolibException { + ResReferenceValue parentVal = newReference(parent, null); - if (items.length == 0) { - return new ResBagValue(parentVal); - } - int key = items[0].m1; - if (key == ResAttr.BAG_KEY_ATTR_TYPE) { - return ResAttr.factory(parentVal, items, this, mPackage); - } - if (key == ResArrayValue.BAG_KEY_ARRAY_START) { - return new ResArrayValue(parentVal, items); - } - if (key >= ResPluralsValue.BAG_KEY_PLURALS_START - && key <= ResPluralsValue.BAG_KEY_PLURALS_END) { - return new ResPluralsValue(parentVal, items); - } - return new ResStyleValue(parentVal, items, this); - } + if (items.length == 0) { + return new ResBagValue(parentVal); + } + int key = items[0].m1; + if (key == ResAttr.BAG_KEY_ATTR_TYPE) { + return ResAttr.factory(parentVal, items, this, mPackage); + } + if (key == ResArrayValue.BAG_KEY_ARRAY_START) { + return new ResArrayValue(parentVal, items); + } + if (key >= ResPluralsValue.BAG_KEY_PLURALS_START + && key <= ResPluralsValue.BAG_KEY_PLURALS_END) { + return new ResPluralsValue(parentVal, items); + } + return new ResStyleValue(parentVal, items, this); + } - public ResReferenceValue newReference(int resID, String rawValue) { - return newReference(resID, rawValue, false); - } + public ResReferenceValue newReference(int resID, String rawValue) { + return newReference(resID, rawValue, false); + } - public ResReferenceValue newReference(int resID, String rawValue, - boolean theme) { - return new ResReferenceValue(mPackage, resID, rawValue, theme); - } + public ResReferenceValue newReference(int resID, String rawValue, + boolean theme) { + return new ResReferenceValue(mPackage, resID, rawValue, theme); + } } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java index 945440ff..c684b520 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java @@ -34,412 +34,406 @@ import org.apache.commons.io.input.CountingInputStream; * @author Ryszard Wiśniewski */ public class ARSCDecoder { - public static ARSCData decode(InputStream arscStream, - boolean findFlagsOffsets, boolean keepBroken) - throws AndrolibException { - return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable()); - } - - public static ARSCData decode(InputStream arscStream, - boolean findFlagsOffsets, boolean keepBroken, ResTable resTable) - throws AndrolibException { - try { - ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable, - findFlagsOffsets, keepBroken); - ResPackage[] pkgs = decoder.readTable(); - return new ARSCData( - pkgs, - decoder.mFlagsOffsets == null ? null : - decoder.mFlagsOffsets.toArray(new FlagsOffset[0]), - resTable); - } catch (IOException ex) { - throw new AndrolibException("Could not decode arsc file", ex); - } - } - - private ARSCDecoder(InputStream arscStream, ResTable resTable, - boolean storeFlagsOffsets, boolean keepBroken) { - if (storeFlagsOffsets) { - arscStream = mCountIn = new CountingInputStream(arscStream); - mFlagsOffsets = new ArrayList(); - } else { - mCountIn = null; - mFlagsOffsets = null; - } - mIn = new ExtDataInput(new LEDataInputStream(arscStream)); - mResTable = resTable; - mKeepBroken = keepBroken; - } - - private ResPackage[] readTable() throws IOException, AndrolibException { - nextChunkCheckType(Header.TYPE_TABLE); - int packageCount = mIn.readInt(); - - mTableStrings = StringBlock.read(mIn); - ResPackage[] packages = new ResPackage[packageCount]; - - nextChunk(); - for (int i = 0; i < packageCount; i++) { - packages[i] = readPackage(); - } - - // store package - if (this.mResTable.isPackageInfoValueSet("cur_package") != true) { - this.mResTable.addPackageInfo("cur_package", packages[0].getName()); - } - return packages; - } - - private ResPackage readPackage() throws IOException, AndrolibException { - checkChunkType(Header.TYPE_PACKAGE); - int id = (byte) mIn.readInt(); - String name = mIn.readNulEndedString(128, true); - /*typeNameStrings*/ mIn.skipInt(); - /*typeNameCount*/ mIn.skipInt(); - /*specNameStrings*/ mIn.skipInt(); - /*specNameCount*/ mIn.skipInt(); - - mTypeNames = StringBlock.read(mIn); - mSpecNames = StringBlock.read(mIn); - - mResId = id << 24; - mPkg = new ResPackage(mResTable, id, name); - - nextChunk(); - while (mHeader.type == Header.TYPE_TYPE) { - readType(); - } - - return mPkg; - } - - private ResType readType() throws AndrolibException, IOException { - checkChunkType(Header.TYPE_TYPE); - byte id = mIn.readByte(); - mIn.skipBytes(3); - int entryCount = mIn.readInt(); - - mMissingResSpecs = new boolean[entryCount]; - Arrays.fill(mMissingResSpecs, true); - - if (mFlagsOffsets != null) { - mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount)); - } - /*flags*/ mIn.skipBytes(entryCount * 4); - - mResId = (0xff000000 & mResId) | id << 16; - mType = new ResType(mTypeNames.getString(id - 1), mResTable, mPkg); - mPkg.addType(mType); - - while (nextChunk().type == Header.TYPE_CONFIG) { - readConfig(); - } - - addMissingResSpecs(); - - return mType; - } - - private ResConfig readConfig() throws IOException, AndrolibException { - checkChunkType(Header.TYPE_CONFIG); - /*typeId*/ mIn.skipInt(); - int entryCount = mIn.readInt(); - /*entriesStart*/ mIn.skipInt(); - - ResConfigFlags flags = readConfigFlags(); - int[] entryOffsets = mIn.readIntArray(entryCount); - - if (flags.isInvalid) { - String resName = mType.getName() + flags.getQualifiers(); - if (mKeepBroken) { - LOGGER.warning( - "Invalid config flags detected: " + resName); - } else { - LOGGER.warning( - "Invalid config flags detected. Dropping resources: " + resName); - } - } - - mConfig = flags.isInvalid && ! mKeepBroken ? - null : mPkg.getOrCreateConfig(flags); - - for (int i = 0; i < entryOffsets.length; i++) { - if (entryOffsets[i] != -1) { - mMissingResSpecs[i] = false; - mResId = (mResId & 0xffff0000) | i; - readEntry(); - } - } - - return mConfig; - } - - private void readEntry() throws IOException, AndrolibException { - /*size*/ mIn.skipBytes(2); - short flags = mIn.readShort(); - int specNamesId = mIn.readInt(); - - ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? - readValue() : readComplexEntry(); - - if (mConfig == null) { - return; - } - - ResID resId = new ResID(mResId); - ResResSpec spec; - if (mPkg.hasResSpec(resId)) { - spec = mPkg.getResSpec(resId); - } else { - spec = new ResResSpec( - resId, mSpecNames.getString(specNamesId), mPkg, mType); - mPkg.addResSpec(spec); - mType.addResSpec(spec); - } - ResResource res = new ResResource(mConfig, spec, value); - - mConfig.addResource(res); - spec.addResource(res); - mPkg.addResource(res); - } - - private ResBagValue readComplexEntry() throws IOException, - AndrolibException { - int parent = mIn.readInt(); - int count = mIn.readInt(); - - ResValueFactory factory = mPkg.getValueFactory(); - Duo[] items = new Duo[count]; - for (int i = 0; i < count; i++) { - items[i] = new Duo( - mIn.readInt(), (ResScalarValue) readValue()); - } - - return factory.bagFactory(parent, items); - } - - private ResValue readValue() throws IOException, AndrolibException { - /*size*/ mIn.skipCheckShort((short) 8); - /*zero*/ mIn.skipCheckByte((byte) 0); - byte type = mIn.readByte(); - int data = mIn.readInt(); - - return type == TypedValue.TYPE_STRING ? - mPkg.getValueFactory().factory(mTableStrings.getHTML(data)) : - mPkg.getValueFactory().factory(type, data, null); - } - - private ResConfigFlags readConfigFlags() throws IOException, AndrolibException { - int size = mIn.readInt(); - if (size < 28) { - throw new AndrolibException("Config size < 28"); - } - - boolean isInvalid = false; - - short mcc = mIn.readShort(); - short mnc = mIn.readShort(); - - char[] language = new char[]{ - (char) mIn.readByte(), (char) mIn.readByte()}; - char[] country = new char[]{ - (char) mIn.readByte(), (char) mIn.readByte()}; - - byte orientation = mIn.readByte(); - byte touchscreen = mIn.readByte(); - short density = mIn.readShort(); - - byte keyboard = mIn.readByte(); - byte navigation = mIn.readByte(); - byte inputFlags = mIn.readByte(); - /*inputPad0*/ mIn.skipBytes(1); - - short screenWidth = mIn.readShort(); - short screenHeight = mIn.readShort(); - - short sdkVersion = mIn.readShort(); - /*minorVersion, now must always be 0*/ mIn.skipBytes(2); - - byte screenLayout = 0; - byte uiMode = 0; - short smallestScreenWidthDp = 0; - if (size >= 32) { - screenLayout = mIn.readByte(); - uiMode = mIn.readByte(); - smallestScreenWidthDp = mIn.readShort(); - } - - short screenWidthDp = 0; - short screenHeightDp = 0; - - if (size >= 36) { - screenWidthDp = mIn.readShort(); - screenHeightDp = mIn.readShort(); - } - - short layoutDirection = 0; - if (size >= 38 && sdkVersion >= 17 && !this.mPkg.getName().equalsIgnoreCase("com.htc")) { - layoutDirection = mIn.readShort(); - } - - int exceedingSize = size - KNOWN_CONFIG_BYTES; - if (exceedingSize > 0) { - byte[] buf = new byte[exceedingSize]; - mIn.readFully(buf); - BigInteger exceedingBI = new BigInteger(1, buf); - - if (exceedingBI.equals(BigInteger.ZERO)) { - LOGGER.fine(String.format( - "Config flags size > %d, but exceeding bytes are all zero, so it should be ok.", - KNOWN_CONFIG_BYTES)); - } else { - LOGGER.warning(String.format( - "Config flags size > %d. Exceeding bytes: 0x%X.", - KNOWN_CONFIG_BYTES, exceedingBI)); - isInvalid = true; - } - } - - return new ResConfigFlags(mcc, mnc, language, country, layoutDirection, orientation, - touchscreen, density, keyboard, navigation, inputFlags, - screenWidth, screenHeight, sdkVersion, screenLayout, uiMode, - smallestScreenWidthDp, screenWidthDp, screenHeightDp, isInvalid); - } - - private void addMissingResSpecs() throws AndrolibException { - int resId = mResId & 0xffff0000; - - for (int i = 0; i < mMissingResSpecs.length; i++) { - if (! mMissingResSpecs[i]) { - continue; - } - - ResResSpec spec = new ResResSpec(new ResID(resId | i), - String.format("APKTOOL_DUMMY_%04x", i), mPkg, mType); - mPkg.addResSpec(spec); - mType.addResSpec(spec); - - ResValue value = new ResBoolValue(false, null); - ResResource res = new ResResource( - mPkg.getOrCreateConfig(new ResConfigFlags()), spec, value); - mPkg.addResource(res); - mConfig.addResource(res); - spec.addResource(res); - } - } - - private Header nextChunk() throws IOException { - return mHeader = Header.read(mIn); - } - - private void checkChunkType(int expectedType) throws AndrolibException { - if (mHeader.type != expectedType) { - throw new AndrolibException(String.format( - "Invalid chunk type: expected=0x%08x, got=0x%08x", - expectedType, mHeader.type)); - } - } - - private void nextChunkCheckType(int expectedType) - throws IOException, AndrolibException { - nextChunk(); - checkChunkType(expectedType); - } - - private final ExtDataInput mIn; - private final ResTable mResTable; - private final CountingInputStream mCountIn; - private final List mFlagsOffsets; - private final boolean mKeepBroken; - - private Header mHeader; - private StringBlock mTableStrings; - private StringBlock mTypeNames; - private StringBlock mSpecNames; - private ResPackage mPkg; - private ResType mType; - private ResConfig mConfig; - private int mResId; - private boolean[] mMissingResSpecs; - - - private final static short ENTRY_FLAG_COMPLEX = 0x0001; - - - public static class Header { - public final short type; - public final int chunkSize; - - public Header(short type, int size) { - this.type = type; - this.chunkSize = size; - } - - public static Header read(ExtDataInput in) throws IOException { - short type; - try { - type = in.readShort(); - } catch (EOFException ex) { - return new Header(TYPE_NONE, 0); - } - in.skipBytes(2); - return new Header(type, in.readInt()); - } - - public final static short - TYPE_NONE = -1, - TYPE_TABLE = 0x0002, - TYPE_PACKAGE = 0x0200, - TYPE_TYPE = 0x0202, - TYPE_CONFIG = 0x0201; - } - - public static class FlagsOffset { - public final int offset; - public final int count; - - public FlagsOffset(int offset, int count) { - this.offset = offset; - this.count = count; - } - } - - private static final Logger LOGGER = - Logger.getLogger(ARSCDecoder.class.getName()); - private static final int KNOWN_CONFIG_BYTES = 36; - - - public static class ARSCData { - - public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets, - ResTable resTable) { - mPackages = packages; - mFlagsOffsets = flagsOffsets; - mResTable = resTable; - } - - public FlagsOffset[] getFlagsOffsets() { - return mFlagsOffsets; - } - - public ResPackage[] getPackages() { - return mPackages; - } - - public ResPackage getOnePackage() throws AndrolibException { - if (mPackages.length != 1) { - throw new AndrolibException( - "Arsc file contains zero or multiple packages"); - } - return mPackages[0]; - } - - public ResTable getResTable() { - return mResTable; - } - - private final ResPackage[] mPackages; - private final FlagsOffset[] mFlagsOffsets; - private final ResTable mResTable; - } + public static ARSCData decode(InputStream arscStream, + boolean findFlagsOffsets, boolean keepBroken) + throws AndrolibException { + return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable()); + } + + public static ARSCData decode(InputStream arscStream, + boolean findFlagsOffsets, boolean keepBroken, ResTable resTable) + throws AndrolibException { + try { + ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable, + findFlagsOffsets, keepBroken); + ResPackage[] pkgs = decoder.readTable(); + return new ARSCData(pkgs, decoder.mFlagsOffsets == null ? null + : decoder.mFlagsOffsets.toArray(new FlagsOffset[0]), + resTable); + } catch (IOException ex) { + throw new AndrolibException("Could not decode arsc file", ex); + } + } + + private ARSCDecoder(InputStream arscStream, ResTable resTable, + boolean storeFlagsOffsets, boolean keepBroken) { + if (storeFlagsOffsets) { + arscStream = mCountIn = new CountingInputStream(arscStream); + mFlagsOffsets = new ArrayList(); + } else { + mCountIn = null; + mFlagsOffsets = null; + } + mIn = new ExtDataInput(new LEDataInputStream(arscStream)); + mResTable = resTable; + mKeepBroken = keepBroken; + } + + private ResPackage[] readTable() throws IOException, AndrolibException { + nextChunkCheckType(Header.TYPE_TABLE); + int packageCount = mIn.readInt(); + + mTableStrings = StringBlock.read(mIn); + ResPackage[] packages = new ResPackage[packageCount]; + + nextChunk(); + for (int i = 0; i < packageCount; i++) { + packages[i] = readPackage(); + } + + // store package + if (this.mResTable.isPackageInfoValueSet("cur_package") != true) { + this.mResTable.addPackageInfo("cur_package", packages[0].getName()); + } + return packages; + } + + private ResPackage readPackage() throws IOException, AndrolibException { + checkChunkType(Header.TYPE_PACKAGE); + int id = (byte) mIn.readInt(); + String name = mIn.readNulEndedString(128, true); + /* typeNameStrings */mIn.skipInt(); + /* typeNameCount */mIn.skipInt(); + /* specNameStrings */mIn.skipInt(); + /* specNameCount */mIn.skipInt(); + + mTypeNames = StringBlock.read(mIn); + mSpecNames = StringBlock.read(mIn); + + mResId = id << 24; + mPkg = new ResPackage(mResTable, id, name); + + nextChunk(); + while (mHeader.type == Header.TYPE_TYPE) { + readType(); + } + + return mPkg; + } + + private ResType readType() throws AndrolibException, IOException { + checkChunkType(Header.TYPE_TYPE); + byte id = mIn.readByte(); + mIn.skipBytes(3); + int entryCount = mIn.readInt(); + + mMissingResSpecs = new boolean[entryCount]; + Arrays.fill(mMissingResSpecs, true); + + if (mFlagsOffsets != null) { + mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount)); + } + /* flags */mIn.skipBytes(entryCount * 4); + + mResId = (0xff000000 & mResId) | id << 16; + mType = new ResType(mTypeNames.getString(id - 1), mResTable, mPkg); + mPkg.addType(mType); + + while (nextChunk().type == Header.TYPE_CONFIG) { + readConfig(); + } + + addMissingResSpecs(); + + return mType; + } + + private ResConfig readConfig() throws IOException, AndrolibException { + checkChunkType(Header.TYPE_CONFIG); + /* typeId */mIn.skipInt(); + int entryCount = mIn.readInt(); + /* entriesStart */mIn.skipInt(); + + ResConfigFlags flags = readConfigFlags(); + int[] entryOffsets = mIn.readIntArray(entryCount); + + if (flags.isInvalid) { + String resName = mType.getName() + flags.getQualifiers(); + if (mKeepBroken) { + LOGGER.warning("Invalid config flags detected: " + resName); + } else { + LOGGER.warning("Invalid config flags detected. Dropping resources: " + + resName); + } + } + + mConfig = flags.isInvalid && !mKeepBroken ? null : mPkg + .getOrCreateConfig(flags); + + for (int i = 0; i < entryOffsets.length; i++) { + if (entryOffsets[i] != -1) { + mMissingResSpecs[i] = false; + mResId = (mResId & 0xffff0000) | i; + readEntry(); + } + } + + return mConfig; + } + + private void readEntry() throws IOException, AndrolibException { + /* size */mIn.skipBytes(2); + short flags = mIn.readShort(); + int specNamesId = mIn.readInt(); + + ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? readValue() + : readComplexEntry(); + + if (mConfig == null) { + return; + } + + ResID resId = new ResID(mResId); + ResResSpec spec; + if (mPkg.hasResSpec(resId)) { + spec = mPkg.getResSpec(resId); + } else { + spec = new ResResSpec(resId, mSpecNames.getString(specNamesId), + mPkg, mType); + mPkg.addResSpec(spec); + mType.addResSpec(spec); + } + ResResource res = new ResResource(mConfig, spec, value); + + mConfig.addResource(res); + spec.addResource(res); + mPkg.addResource(res); + } + + private ResBagValue readComplexEntry() throws IOException, + AndrolibException { + int parent = mIn.readInt(); + int count = mIn.readInt(); + + ResValueFactory factory = mPkg.getValueFactory(); + Duo[] items = new Duo[count]; + for (int i = 0; i < count; i++) { + items[i] = new Duo(mIn.readInt(), + (ResScalarValue) readValue()); + } + + return factory.bagFactory(parent, items); + } + + private ResValue readValue() throws IOException, AndrolibException { + /* size */mIn.skipCheckShort((short) 8); + /* zero */mIn.skipCheckByte((byte) 0); + byte type = mIn.readByte(); + int data = mIn.readInt(); + + return type == TypedValue.TYPE_STRING ? mPkg.getValueFactory().factory( + mTableStrings.getHTML(data)) : mPkg.getValueFactory().factory( + type, data, null); + } + + private ResConfigFlags readConfigFlags() throws IOException, + AndrolibException { + int size = mIn.readInt(); + if (size < 28) { + throw new AndrolibException("Config size < 28"); + } + + boolean isInvalid = false; + + short mcc = mIn.readShort(); + short mnc = mIn.readShort(); + + char[] language = new char[] { (char) mIn.readByte(), + (char) mIn.readByte() }; + char[] country = new char[] { (char) mIn.readByte(), + (char) mIn.readByte() }; + + byte orientation = mIn.readByte(); + byte touchscreen = mIn.readByte(); + short density = mIn.readShort(); + + byte keyboard = mIn.readByte(); + byte navigation = mIn.readByte(); + byte inputFlags = mIn.readByte(); + /* inputPad0 */mIn.skipBytes(1); + + short screenWidth = mIn.readShort(); + short screenHeight = mIn.readShort(); + + short sdkVersion = mIn.readShort(); + /* minorVersion, now must always be 0 */mIn.skipBytes(2); + + byte screenLayout = 0; + byte uiMode = 0; + short smallestScreenWidthDp = 0; + if (size >= 32) { + screenLayout = mIn.readByte(); + uiMode = mIn.readByte(); + smallestScreenWidthDp = mIn.readShort(); + } + + short screenWidthDp = 0; + short screenHeightDp = 0; + + if (size >= 36) { + screenWidthDp = mIn.readShort(); + screenHeightDp = mIn.readShort(); + } + + short layoutDirection = 0; + if (size >= 38 && sdkVersion >= 17 + && !this.mPkg.getName().equalsIgnoreCase("com.htc")) { + layoutDirection = mIn.readShort(); + } + + int exceedingSize = size - KNOWN_CONFIG_BYTES; + if (exceedingSize > 0) { + byte[] buf = new byte[exceedingSize]; + mIn.readFully(buf); + BigInteger exceedingBI = new BigInteger(1, buf); + + if (exceedingBI.equals(BigInteger.ZERO)) { + LOGGER.fine(String + .format("Config flags size > %d, but exceeding bytes are all zero, so it should be ok.", + KNOWN_CONFIG_BYTES)); + } else { + LOGGER.warning(String.format( + "Config flags size > %d. Exceeding bytes: 0x%X.", + KNOWN_CONFIG_BYTES, exceedingBI)); + isInvalid = true; + } + } + + return new ResConfigFlags(mcc, mnc, language, country, layoutDirection, + orientation, touchscreen, density, keyboard, navigation, + inputFlags, screenWidth, screenHeight, sdkVersion, + screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp, + screenHeightDp, isInvalid); + } + + private void addMissingResSpecs() throws AndrolibException { + int resId = mResId & 0xffff0000; + + for (int i = 0; i < mMissingResSpecs.length; i++) { + if (!mMissingResSpecs[i]) { + continue; + } + + ResResSpec spec = new ResResSpec(new ResID(resId | i), + String.format("APKTOOL_DUMMY_%04x", i), mPkg, mType); + mPkg.addResSpec(spec); + mType.addResSpec(spec); + + ResValue value = new ResBoolValue(false, null); + ResResource res = new ResResource( + mPkg.getOrCreateConfig(new ResConfigFlags()), spec, value); + mPkg.addResource(res); + mConfig.addResource(res); + spec.addResource(res); + } + } + + private Header nextChunk() throws IOException { + return mHeader = Header.read(mIn); + } + + private void checkChunkType(int expectedType) throws AndrolibException { + if (mHeader.type != expectedType) { + throw new AndrolibException(String.format( + "Invalid chunk type: expected=0x%08x, got=0x%08x", + expectedType, mHeader.type)); + } + } + + private void nextChunkCheckType(int expectedType) throws IOException, + AndrolibException { + nextChunk(); + checkChunkType(expectedType); + } + + private final ExtDataInput mIn; + private final ResTable mResTable; + private final CountingInputStream mCountIn; + private final List mFlagsOffsets; + private final boolean mKeepBroken; + + private Header mHeader; + private StringBlock mTableStrings; + private StringBlock mTypeNames; + private StringBlock mSpecNames; + private ResPackage mPkg; + private ResType mType; + private ResConfig mConfig; + private int mResId; + private boolean[] mMissingResSpecs; + + private final static short ENTRY_FLAG_COMPLEX = 0x0001; + + public static class Header { + public final short type; + public final int chunkSize; + + public Header(short type, int size) { + this.type = type; + this.chunkSize = size; + } + + public static Header read(ExtDataInput in) throws IOException { + short type; + try { + type = in.readShort(); + } catch (EOFException ex) { + return new Header(TYPE_NONE, 0); + } + in.skipBytes(2); + return new Header(type, in.readInt()); + } + + public final static short TYPE_NONE = -1, TYPE_TABLE = 0x0002, + TYPE_PACKAGE = 0x0200, TYPE_TYPE = 0x0202, + TYPE_CONFIG = 0x0201; + } + + public static class FlagsOffset { + public final int offset; + public final int count; + + public FlagsOffset(int offset, int count) { + this.offset = offset; + this.count = count; + } + } + + private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class + .getName()); + private static final int KNOWN_CONFIG_BYTES = 36; + + public static class ARSCData { + + public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets, + ResTable resTable) { + mPackages = packages; + mFlagsOffsets = flagsOffsets; + mResTable = resTable; + } + + public FlagsOffset[] getFlagsOffsets() { + return mFlagsOffsets; + } + + public ResPackage[] getPackages() { + return mPackages; + } + + public ResPackage getOnePackage() throws AndrolibException { + if (mPackages.length != 1) { + throw new AndrolibException( + "Arsc file contains zero or multiple packages"); + } + return mPackages[0]; + } + + public ResTable getResTable() { + return mResTable; + } + + private final ResPackage[] mPackages; + private final FlagsOffset[] mFlagsOffsets; + private final ResTable mResTable; + } } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java index 33e7ba1c..aee643a5 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java @@ -31,983 +31,1060 @@ import org.xmlpull.v1.XmlPullParserException; /** * @author Ryszard Wiśniewski * @author Dmitry Skiba - * - * Binary xml files parser. - * - * Parser has only two states: (1) Operational state, which parser obtains after - * first successful call to next() and retains until open(), close(), or failed - * call to next(). (2) Closed state, which parser obtains after open(), close(), - * or failed call to next(). In this state methods return invalid values or - * throw exceptions. - * - * TODO: * check all methods in closed state - * + * + * Binary xml files parser. + * + * Parser has only two states: (1) Operational state, which parser + * obtains after first successful call to next() and retains until + * open(), close(), or failed call to next(). (2) Closed state, which + * parser obtains after open(), close(), or failed call to next(). In + * this state methods return invalid values or throw exceptions. + * + * TODO: * check all methods in closed state + * */ public class AXmlResourceParser implements XmlResourceParser { - public AXmlResourceParser() { - resetEventInfo(); - } - - public AXmlResourceParser(InputStream stream) { - this(); - open(stream); - } - - public AndrolibException getFirstError() { - return mFirstError; - } - - public ResAttrDecoder getAttrDecoder() { - return mAttrDecoder; - } - - public void setAttrDecoder(ResAttrDecoder attrDecoder) { - mAttrDecoder = attrDecoder; - } - - public void open(InputStream stream) { - close(); - if (stream != null) { - m_reader = new ExtDataInput( - new LEDataInputStream(stream)); - } - } - - public void close() { - if (!m_operational) { - return; - } - m_operational = false; -// m_reader.close(); - m_reader = null; - m_strings = null; - m_resourceIDs = null; - m_namespaces.reset(); - resetEventInfo(); - } - - /////////////////////////////////// iteration - public int next() throws XmlPullParserException, IOException { - if (m_reader == null) { - throw new XmlPullParserException("Parser is not opened.", this, null); - } - try { - doNext(); - return m_event; - } catch (IOException e) { - close(); - throw e; - } - } - - public int nextToken() throws XmlPullParserException, IOException { - return next(); - } - - public int nextTag() throws XmlPullParserException, IOException { - int eventType = next(); - if (eventType == TEXT && isWhitespace()) { - eventType = next(); - } - if (eventType != START_TAG && eventType != END_TAG) { - throw new XmlPullParserException("Expected start or end tag.", this, null); - } - return eventType; - } - - public String nextText() throws XmlPullParserException, IOException { - if (getEventType() != START_TAG) { - throw new XmlPullParserException("Parser must be on START_TAG to read next text.", this, null); - } - int eventType = next(); - if (eventType == TEXT) { - String result = getText(); - eventType = next(); - if (eventType != END_TAG) { - throw new XmlPullParserException("Event TEXT must be immediately followed by END_TAG.", this, null); - } - return result; - } else if (eventType == END_TAG) { - return ""; - } else { - throw new XmlPullParserException("Parser must be on START_TAG or TEXT to read text.", this, null); - } - } - - public void require(int type, String namespace, String name) throws XmlPullParserException, IOException { - if (type != getEventType() - || (namespace != null && !namespace.equals(getNamespace())) - || (name != null && !name.equals(getName()))) { - throw new XmlPullParserException(TYPES[type] + " is expected.", this, null); - } - } - - public int getDepth() { - return m_namespaces.getDepth() - 1; - } - - public int getEventType() throws XmlPullParserException { - return m_event; - } - - public int getLineNumber() { - return m_lineNumber; - } - - public String getName() { - if (m_name == -1 || (m_event != START_TAG && m_event != END_TAG)) { - return null; - } - return m_strings.getString(m_name); - } - - public String getText() { - if (m_name == -1 || m_event != TEXT) { - return null; - } - return m_strings.getString(m_name); - } - - public char[] getTextCharacters(int[] holderForStartAndLength) { - String text = getText(); - if (text == null) { - return null; - } - holderForStartAndLength[0] = 0; - holderForStartAndLength[1] = text.length(); - char[] chars = new char[text.length()]; - text.getChars(0, text.length(), chars, 0); - return chars; - } - - public String getNamespace() { - return m_strings.getString(m_namespaceUri); - } - - public String getPrefix() { - int prefix = m_namespaces.findPrefix(m_namespaceUri); - return m_strings.getString(prefix); - } - - public String getPositionDescription() { - return "XML line #" + getLineNumber(); - } - - public int getNamespaceCount(int depth) throws XmlPullParserException { - return m_namespaces.getAccumulatedCount(depth); - } - - public String getNamespacePrefix(int pos) throws XmlPullParserException { - int prefix = m_namespaces.getPrefix(pos); - return m_strings.getString(prefix); - } - - public String getNamespaceUri(int pos) throws XmlPullParserException { - int uri = m_namespaces.getUri(pos); - return m_strings.getString(uri); - } - - /////////////////////////////////// attributes - public String getClassAttribute() { - if (m_classAttribute == -1) { - return null; - } - int offset = getAttributeOffset(m_classAttribute); - int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - return m_strings.getString(value); - } - - public String getIdAttribute() { - if (m_idAttribute == -1) { - return null; - } - int offset = getAttributeOffset(m_idAttribute); - int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - return m_strings.getString(value); - } - - public int getIdAttributeResourceValue(int defaultValue) { - if (m_idAttribute == -1) { - return defaultValue; - } - int offset = getAttributeOffset(m_idAttribute); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType != TypedValue.TYPE_REFERENCE) { - return defaultValue; - } - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - public int getStyleAttribute() { - if (m_styleAttribute == -1) { - return 0; - } - int offset = getAttributeOffset(m_styleAttribute); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - public int getAttributeCount() { - if (m_event != START_TAG) { - return -1; - } - return m_attributes.length / ATTRIBUTE_LENGHT; - } - - public String getAttributeNamespace(int index) { - int offset = getAttributeOffset(index); - int namespace = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; - if (namespace == -1) { - return ""; - } - return m_strings.getString(namespace); - } - - public String getAttributePrefix(int index) { - int offset = getAttributeOffset(index); - int uri = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; - int prefix = m_namespaces.findPrefix(uri); - if (prefix == -1) { - return ""; - } - return m_strings.getString(prefix); - } - - public String getAttributeName(int index) { - int offset = getAttributeOffset(index); - int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; - if (name == -1) { - return ""; - } - return m_strings.getString(name); - } - - public int getAttributeNameResource(int index) { - int offset = getAttributeOffset(index); - int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; - if (m_resourceIDs == null - || name < 0 || name >= m_resourceIDs.length) { - return 0; - } - return m_resourceIDs[name]; - } - - public int getAttributeValueType(int index) { - int offset = getAttributeOffset(index); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - } - - public int getAttributeValueData(int index) { - int offset = getAttributeOffset(index); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - public String getAttributeValue(int index) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - int valueRaw = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - - if (mAttrDecoder != null) { - try { - return mAttrDecoder.decode(valueType, valueData, - valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars( - m_strings.getString(valueRaw)), - getAttributeNameResource(index)); - } catch (AndrolibException ex) { - setFirstError(ex); - LOGGER.log(Level.WARNING, String.format( - "Could not decode attr value, using undecoded value " - + "instead: ns=%s, name=%s, value=0x%08x", - getAttributePrefix(index), getAttributeName(index), - valueData), ex); - } - } else { - if (valueType == TypedValue.TYPE_STRING) { - return ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw)); - } - } - - return TypedValue.coerceToString(valueType, valueData); - } - - public boolean getAttributeBooleanValue(int index, boolean defaultValue) { - return getAttributeIntValue(index, defaultValue ? 1 : 0) != 0; - } - - public float getAttributeFloatValue(int index, float defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType == TypedValue.TYPE_FLOAT) { - int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - return Float.intBitsToFloat(valueData); - } - return defaultValue; - } - - public int getAttributeIntValue(int index, int defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType >= TypedValue.TYPE_FIRST_INT - && valueType <= TypedValue.TYPE_LAST_INT) { - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - return defaultValue; - } - - public int getAttributeUnsignedIntValue(int index, int defaultValue) { - return getAttributeIntValue(index, defaultValue); - } - - public int getAttributeResourceValue(int index, int defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType == TypedValue.TYPE_REFERENCE) { - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - return defaultValue; - } - - public String getAttributeValue(String namespace, String attribute) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return ""; - } - return getAttributeValue(index); - } - - public boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeBooleanValue(index, defaultValue); - } - - public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeFloatValue(index, defaultValue); - } - - public int getAttributeIntValue(String namespace, String attribute, int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeIntValue(index, defaultValue); - } - - public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeUnsignedIntValue(index, defaultValue); - } - - public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeResourceValue(index, defaultValue); - } - - public int getAttributeListValue(int index, String[] options, int defaultValue) { - // TODO implement - return 0; - } - - public int getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue) { - // TODO implement - return 0; - } - - public String getAttributeType(int index) { - return "CDATA"; - } - - public boolean isAttributeDefault(int index) { - return false; - } - - /////////////////////////////////// dummies - public void setInput(InputStream stream, String inputEncoding) throws XmlPullParserException { - open(stream); - } - - public void setInput(Reader reader) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - public String getInputEncoding() { - return null; - } - - public int getColumnNumber() { - return -1; - } - - public boolean isEmptyElementTag() throws XmlPullParserException { - return false; - } - - public boolean isWhitespace() throws XmlPullParserException { - return false; - } - - public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - public String getNamespace(String prefix) { - throw new RuntimeException(E_NOT_SUPPORTED); - } - - public Object getProperty(String name) { - return null; - } - - public void setProperty(String name, Object value) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - public boolean getFeature(String feature) { - return false; - } - - public void setFeature(String name, boolean value) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - ///////////////////////////////////////////// implementation - /** - * Namespace stack, holds prefix+uri pairs, as well as depth information. - * All information is stored in one int[] array. Array consists of depth - * frames: Data=DepthFrame*; DepthFrame=Count+[Prefix+Uri]*+Count; - * Count='count of Prefix+Uri pairs'; Yes, count is stored twice, to enable - * bottom-up traversal. increaseDepth adds depth frame, decreaseDepth - * removes it. push/pop operations operate only in current depth frame. - * decreaseDepth removes any remaining (not pop'ed) namespace pairs. findXXX - * methods search all depth frames starting from the last namespace pair of - * current depth frame. All functions that operate with int, use -1 as - * 'invalid value'. - * - * !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !! - * - */ - private static final class NamespaceStack { - - public NamespaceStack() { - m_data = new int[32]; - } - - public final void reset() { - m_dataLength = 0; - m_count = 0; - m_depth = 0; - } - - public final int getTotalCount() { - return m_count; - } - - public final int getCurrentCount() { - if (m_dataLength == 0) { - return 0; - } - int offset = m_dataLength - 1; - return m_data[offset]; - } - - public final int getAccumulatedCount(int depth) { - if (m_dataLength == 0 || depth < 0) { - return 0; - } - if (depth > m_depth) { - depth = m_depth; - } - int accumulatedCount = 0; - int offset = 0; - for (; depth != 0; --depth) { - int count = m_data[offset]; - accumulatedCount += count; - offset += (2 + count * 2); - } - return accumulatedCount; - } - - public final void push(int prefix, int uri) { - if (m_depth == 0) { - increaseDepth(); - } - ensureDataCapacity(2); - int offset = m_dataLength - 1; - int count = m_data[offset]; - m_data[offset - 1 - count * 2] = count + 1; - m_data[offset] = prefix; - m_data[offset + 1] = uri; - m_data[offset + 2] = count + 1; - m_dataLength += 2; - m_count += 1; - } - - public final boolean pop(int prefix, int uri) { - if (m_dataLength == 0) { - return false; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - for (int i = 0, o = offset - 2; i != count; ++i, o -= 2) { - if (m_data[o] != prefix || m_data[o + 1] != uri) { - continue; - } - count -= 1; - if (i == 0) { - m_data[o] = count; - o -= (1 + count * 2); - m_data[o] = count; - } else { - m_data[offset] = count; - offset -= (1 + 2 + count * 2); - m_data[offset] = count; - System.arraycopy( - m_data, o + 2, - m_data, o, - m_dataLength - o); - } - m_dataLength -= 2; - m_count -= 1; - return true; - } - return false; - } - - public final boolean pop() { - if (m_dataLength == 0) { - return false; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - if (count == 0) { - return false; - } - count -= 1; - offset -= 2; - m_data[offset] = count; - offset -= (1 + count * 2); - m_data[offset] = count; - m_dataLength -= 2; - m_count -= 1; - return true; - } - - public final int getPrefix(int index) { - return get(index, true); - } - - public final int getUri(int index) { - return get(index, false); - } - - public final int findPrefix(int uri) { - return find(uri, false); - } - - public final int findUri(int prefix) { - return find(prefix, true); - } - - public final int getDepth() { - return m_depth; - } - - public final void increaseDepth() { - ensureDataCapacity(2); - int offset = m_dataLength; - m_data[offset] = 0; - m_data[offset + 1] = 0; - m_dataLength += 2; - m_depth += 1; - } - - public final void decreaseDepth() { - if (m_dataLength == 0) { - return; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - if ((offset - 1 - count * 2) == 0) { - return; - } - m_dataLength -= 2 + count * 2; - m_count -= count; - m_depth -= 1; - } - - private void ensureDataCapacity(int capacity) { - int available = (m_data.length - m_dataLength); - if (available > capacity) { - return; - } - int newLength = (m_data.length + available) * 2; - int[] newData = new int[newLength]; - System.arraycopy(m_data, 0, newData, 0, m_dataLength); - m_data = newData; - } - - private final int find(int prefixOrUri, boolean prefix) { - if (m_dataLength == 0) { - return -1; - } - int offset = m_dataLength - 1; - for (int i = m_depth; i != 0; --i) { - int count = m_data[offset]; - offset -= 2; - for (; count != 0; --count) { - if (prefix) { - if (m_data[offset] == prefixOrUri) { - return m_data[offset + 1]; - } - } else { - if (m_data[offset + 1] == prefixOrUri) { - return m_data[offset]; - } - } - offset -= 2; - } - } - return -1; - } - - private final int get(int index, boolean prefix) { - if (m_dataLength == 0 || index < 0) { - return -1; - } - int offset = 0; - for (int i = m_depth; i != 0; --i) { - int count = m_data[offset]; - if (index >= count) { - index -= count; - offset += (2 + count * 2); - continue; - } - offset += (1 + index * 2); - if (!prefix) { - offset += 1; - } - return m_data[offset]; - } - return -1; - } - private int[] m_data; - private int m_dataLength; - private int m_count; - private int m_depth; - } - - /////////////////////////////////// package-visible -// final void fetchAttributes(int[] styleableIDs,TypedArray result) { -// result.resetIndices(); -// if (m_attributes==null || m_resourceIDs==null) { -// return; -// } -// boolean needStrings=false; -// for (int i=0,e=styleableIDs.length;i!=e;++i) { -// int id=styleableIDs[i]; -// for (int o=0;o!=m_attributes.length;o+=ATTRIBUTE_LENGHT) { -// int name=m_attributes[o+ATTRIBUTE_IX_NAME]; -// if (name>=m_resourceIDs.length || -// m_resourceIDs[name]!=id) -// { -// continue; -// } -// int valueType=m_attributes[o+ATTRIBUTE_IX_VALUE_TYPE]; -// int valueData; -// int assetCookie; -// if (valueType==TypedValue.TYPE_STRING) { -// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_STRING]; -// assetCookie=-1; -// needStrings=true; -// } else { -// valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_DATA]; -// assetCookie=0; -// } -// result.addValue(i,valueType,valueData,assetCookie,id,0); -// } -// } -// if (needStrings) { -// result.setStrings(m_strings); -// } -// } - final StringBlock getStrings() { - return m_strings; - } - - /////////////////////////////////// - private final int getAttributeOffset(int index) { - if (m_event != START_TAG) { - throw new IndexOutOfBoundsException("Current event is not START_TAG."); - } - int offset = index * ATTRIBUTE_LENGHT; - if (offset >= m_attributes.length) { - throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ")."); - } - return offset; - } - - private final int findAttribute(String namespace, String attribute) { - if (m_strings == null || attribute == null) { - return -1; - } - int name = m_strings.find(attribute); - if (name == -1) { - return -1; - } - int uri = (namespace != null) - ? m_strings.find(namespace) - : -1; - for (int o = 0; o != m_attributes.length; o+=ATTRIBUTE_LENGHT) { - if (name == m_attributes[o + ATTRIBUTE_IX_NAME] - && (uri == -1 || uri == m_attributes[o + ATTRIBUTE_IX_NAMESPACE_URI])) { - return o / ATTRIBUTE_LENGHT; - } - } - return -1; - } - - private final void resetEventInfo() { - m_event = -1; - m_lineNumber = -1; - m_name = -1; - m_namespaceUri = -1; - m_attributes = null; - m_idAttribute = -1; - m_classAttribute = -1; - m_styleAttribute = -1; - } - - private final void doNext() throws IOException { - // Delayed initialization. - if (m_strings == null) { - m_reader.skipCheckInt(CHUNK_AXML_FILE); - /* - * chunkSize - */ m_reader.skipInt(); - m_strings = StringBlock.read(m_reader); - m_namespaces.increaseDepth(); - m_operational = true; - } - - if (m_event == END_DOCUMENT) { - return; - } - - int event = m_event; - resetEventInfo(); - - while (true) { - if (m_decreaseDepth) { - m_decreaseDepth = false; - m_namespaces.decreaseDepth(); - } - - // Fake END_DOCUMENT event. - if (event == END_TAG - && m_namespaces.getDepth() == 1 - && m_namespaces.getCurrentCount() == 0) { - m_event = END_DOCUMENT; - break; - } - - int chunkType; - if (event == START_DOCUMENT) { - // Fake event, see CHUNK_XML_START_TAG handler. - chunkType = CHUNK_XML_START_TAG; - } else { - chunkType = m_reader.readInt(); - } - - if (chunkType == CHUNK_RESOURCEIDS) { - int chunkSize = m_reader.readInt(); - if (chunkSize < 8 || (chunkSize % 4) != 0) { - throw new IOException("Invalid resource ids size (" + chunkSize + ")."); - } - m_resourceIDs = m_reader.readIntArray(chunkSize / 4 - 2); - continue; - } - - if (chunkType < CHUNK_XML_FIRST || chunkType > CHUNK_XML_LAST) { - throw new IOException("Invalid chunk type (" + chunkType + ")."); - } - - // Fake START_DOCUMENT event. - if (chunkType == CHUNK_XML_START_TAG && event == -1) { - m_event = START_DOCUMENT; - break; - } - - // Common header. - /*chunkSize*/ m_reader.skipInt(); - int lineNumber = m_reader.readInt(); - /*0xFFFFFFFF*/ m_reader.skipInt(); - - if (chunkType == CHUNK_XML_START_NAMESPACE - || chunkType == CHUNK_XML_END_NAMESPACE) { - if (chunkType == CHUNK_XML_START_NAMESPACE) { - int prefix = m_reader.readInt(); - int uri = m_reader.readInt(); - m_namespaces.push(prefix, uri); - } else { - /*prefix*/ m_reader.skipInt(); - /*uri*/ m_reader.skipInt(); - m_namespaces.pop(); - } - continue; - } - - m_lineNumber = lineNumber; - - if (chunkType == CHUNK_XML_START_TAG) { - m_namespaceUri = m_reader.readInt(); - m_name = m_reader.readInt(); - /*flags?*/ m_reader.skipInt(); - int attributeCount = m_reader.readInt(); - m_idAttribute = (attributeCount >>> 16) - 1; - attributeCount &= 0xFFFF; - m_classAttribute = m_reader.readInt(); - m_styleAttribute = (m_classAttribute >>> 16) - 1; - m_classAttribute = (m_classAttribute & 0xFFFF) - 1; - m_attributes = m_reader.readIntArray(attributeCount * ATTRIBUTE_LENGHT); - for (int i = ATTRIBUTE_IX_VALUE_TYPE; i < m_attributes.length;) { - m_attributes[i] = (m_attributes[i] >>> 24); - i += ATTRIBUTE_LENGHT; - } - m_namespaces.increaseDepth(); - m_event = START_TAG; - break; - } - - if (chunkType == CHUNK_XML_END_TAG) { - m_namespaceUri = m_reader.readInt(); - m_name = m_reader.readInt(); - m_event = END_TAG; - m_decreaseDepth = true; - break; - } - - if (chunkType == CHUNK_XML_TEXT) { - m_name = m_reader.readInt(); - /*?*/ m_reader.skipInt(); - /*?*/ m_reader.skipInt(); - m_event = TEXT; - break; - } - } - } - - private static String formatArray(int[] array, int min, int max) { - if (max > array.length) { - max = array.length; - } - if (min < 0) { - min = 0; - } - StringBuffer sb = new StringBuffer("["); - int i = min; - while (true) { - sb.append(array[i]); - i++; - if (i < max) { - sb.append(", "); - } else { - sb.append("]"); - break; - } - } - return sb.toString(); - } - - private boolean compareAttr(int[] attr1, int[] attr2) { - //TODO: sort Attrs + public AXmlResourceParser() { + resetEventInfo(); + } + + public AXmlResourceParser(InputStream stream) { + this(); + open(stream); + } + + public AndrolibException getFirstError() { + return mFirstError; + } + + public ResAttrDecoder getAttrDecoder() { + return mAttrDecoder; + } + + public void setAttrDecoder(ResAttrDecoder attrDecoder) { + mAttrDecoder = attrDecoder; + } + + public void open(InputStream stream) { + close(); + if (stream != null) { + m_reader = new ExtDataInput(new LEDataInputStream(stream)); + } + } + + @Override + public void close() { + if (!m_operational) { + return; + } + m_operational = false; + // m_reader.close(); + m_reader = null; + m_strings = null; + m_resourceIDs = null; + m_namespaces.reset(); + resetEventInfo(); + } + + // ///////////////////////////////// iteration + @Override + public int next() throws XmlPullParserException, IOException { + if (m_reader == null) { + throw new XmlPullParserException("Parser is not opened.", this, + null); + } + try { + doNext(); + return m_event; + } catch (IOException e) { + close(); + throw e; + } + } + + @Override + public int nextToken() throws XmlPullParserException, IOException { + return next(); + } + + @Override + public int nextTag() throws XmlPullParserException, IOException { + int eventType = next(); + if (eventType == TEXT && isWhitespace()) { + eventType = next(); + } + if (eventType != START_TAG && eventType != END_TAG) { + throw new XmlPullParserException("Expected start or end tag.", + this, null); + } + return eventType; + } + + @Override + public String nextText() throws XmlPullParserException, IOException { + if (getEventType() != START_TAG) { + throw new XmlPullParserException( + "Parser must be on START_TAG to read next text.", this, + null); + } + int eventType = next(); + if (eventType == TEXT) { + String result = getText(); + eventType = next(); + if (eventType != END_TAG) { + throw new XmlPullParserException( + "Event TEXT must be immediately followed by END_TAG.", + this, null); + } + return result; + } else if (eventType == END_TAG) { + return ""; + } else { + throw new XmlPullParserException( + "Parser must be on START_TAG or TEXT to read text.", this, + null); + } + } + + @Override + public void require(int type, String namespace, String name) + throws XmlPullParserException, IOException { + if (type != getEventType() + || (namespace != null && !namespace.equals(getNamespace())) + || (name != null && !name.equals(getName()))) { + throw new XmlPullParserException(TYPES[type] + " is expected.", + this, null); + } + } + + @Override + public int getDepth() { + return m_namespaces.getDepth() - 1; + } + + @Override + public int getEventType() throws XmlPullParserException { + return m_event; + } + + @Override + public int getLineNumber() { + return m_lineNumber; + } + + @Override + public String getName() { + if (m_name == -1 || (m_event != START_TAG && m_event != END_TAG)) { + return null; + } + return m_strings.getString(m_name); + } + + @Override + public String getText() { + if (m_name == -1 || m_event != TEXT) { + return null; + } + return m_strings.getString(m_name); + } + + @Override + public char[] getTextCharacters(int[] holderForStartAndLength) { + String text = getText(); + if (text == null) { + return null; + } + holderForStartAndLength[0] = 0; + holderForStartAndLength[1] = text.length(); + char[] chars = new char[text.length()]; + text.getChars(0, text.length(), chars, 0); + return chars; + } + + @Override + public String getNamespace() { + return m_strings.getString(m_namespaceUri); + } + + @Override + public String getPrefix() { + int prefix = m_namespaces.findPrefix(m_namespaceUri); + return m_strings.getString(prefix); + } + + @Override + public String getPositionDescription() { + return "XML line #" + getLineNumber(); + } + + @Override + public int getNamespaceCount(int depth) throws XmlPullParserException { + return m_namespaces.getAccumulatedCount(depth); + } + + @Override + public String getNamespacePrefix(int pos) throws XmlPullParserException { + int prefix = m_namespaces.getPrefix(pos); + return m_strings.getString(prefix); + } + + @Override + public String getNamespaceUri(int pos) throws XmlPullParserException { + int uri = m_namespaces.getUri(pos); + return m_strings.getString(uri); + } + + // ///////////////////////////////// attributes + @Override + public String getClassAttribute() { + if (m_classAttribute == -1) { + return null; + } + int offset = getAttributeOffset(m_classAttribute); + int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; + return m_strings.getString(value); + } + + @Override + public String getIdAttribute() { + if (m_idAttribute == -1) { + return null; + } + int offset = getAttributeOffset(m_idAttribute); + int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; + return m_strings.getString(value); + } + + @Override + public int getIdAttributeResourceValue(int defaultValue) { + if (m_idAttribute == -1) { + return defaultValue; + } + int offset = getAttributeOffset(m_idAttribute); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType != TypedValue.TYPE_REFERENCE) { + return defaultValue; + } + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + + @Override + public int getStyleAttribute() { + if (m_styleAttribute == -1) { + return 0; + } + int offset = getAttributeOffset(m_styleAttribute); + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + + @Override + public int getAttributeCount() { + if (m_event != START_TAG) { + return -1; + } + return m_attributes.length / ATTRIBUTE_LENGHT; + } + + @Override + public String getAttributeNamespace(int index) { + int offset = getAttributeOffset(index); + int namespace = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; + if (namespace == -1) { + return ""; + } + return m_strings.getString(namespace); + } + + @Override + public String getAttributePrefix(int index) { + int offset = getAttributeOffset(index); + int uri = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; + int prefix = m_namespaces.findPrefix(uri); + if (prefix == -1) { + return ""; + } + return m_strings.getString(prefix); + } + + @Override + public String getAttributeName(int index) { + int offset = getAttributeOffset(index); + int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; + if (name == -1) { + return ""; + } + return m_strings.getString(name); + } + + @Override + public int getAttributeNameResource(int index) { + int offset = getAttributeOffset(index); + int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; + if (m_resourceIDs == null || name < 0 || name >= m_resourceIDs.length) { + return 0; + } + return m_resourceIDs[name]; + } + + @Override + public int getAttributeValueType(int index) { + int offset = getAttributeOffset(index); + return m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + } + + @Override + public int getAttributeValueData(int index) { + int offset = getAttributeOffset(index); + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + + @Override + public String getAttributeValue(int index) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + int valueRaw = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; + + if (mAttrDecoder != null) { + try { + return mAttrDecoder.decode( + valueType, + valueData, + valueRaw == -1 ? null : ResXmlEncoders + .escapeXmlChars(m_strings.getString(valueRaw)), + getAttributeNameResource(index)); + } catch (AndrolibException ex) { + setFirstError(ex); + LOGGER.log(Level.WARNING, String.format( + "Could not decode attr value, using undecoded value " + + "instead: ns=%s, name=%s, value=0x%08x", + getAttributePrefix(index), getAttributeName(index), + valueData), ex); + } + } else { + if (valueType == TypedValue.TYPE_STRING) { + return ResXmlEncoders.escapeXmlChars(m_strings + .getString(valueRaw)); + } + } + + return TypedValue.coerceToString(valueType, valueData); + } + + @Override + public boolean getAttributeBooleanValue(int index, boolean defaultValue) { + return getAttributeIntValue(index, defaultValue ? 1 : 0) != 0; + } + + @Override + public float getAttributeFloatValue(int index, float defaultValue) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType == TypedValue.TYPE_FLOAT) { + int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + return Float.intBitsToFloat(valueData); + } + return defaultValue; + } + + @Override + public int getAttributeIntValue(int index, int defaultValue) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType >= TypedValue.TYPE_FIRST_INT + && valueType <= TypedValue.TYPE_LAST_INT) { + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + return defaultValue; + } + + @Override + public int getAttributeUnsignedIntValue(int index, int defaultValue) { + return getAttributeIntValue(index, defaultValue); + } + + @Override + public int getAttributeResourceValue(int index, int defaultValue) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType == TypedValue.TYPE_REFERENCE) { + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + return defaultValue; + } + + @Override + public String getAttributeValue(String namespace, String attribute) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return ""; + } + return getAttributeValue(index); + } + + @Override + public boolean getAttributeBooleanValue(String namespace, String attribute, + boolean defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeBooleanValue(index, defaultValue); + } + + @Override + public float getAttributeFloatValue(String namespace, String attribute, + float defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeFloatValue(index, defaultValue); + } + + @Override + public int getAttributeIntValue(String namespace, String attribute, + int defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeIntValue(index, defaultValue); + } + + @Override + public int getAttributeUnsignedIntValue(String namespace, String attribute, + int defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeUnsignedIntValue(index, defaultValue); + } + + @Override + public int getAttributeResourceValue(String namespace, String attribute, + int defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeResourceValue(index, defaultValue); + } + + @Override + public int getAttributeListValue(int index, String[] options, + int defaultValue) { + // TODO implement + return 0; + } + + @Override + public int getAttributeListValue(String namespace, String attribute, + String[] options, int defaultValue) { + // TODO implement + return 0; + } + + @Override + public String getAttributeType(int index) { + return "CDATA"; + } + + @Override + public boolean isAttributeDefault(int index) { + return false; + } + + // ///////////////////////////////// dummies + @Override + public void setInput(InputStream stream, String inputEncoding) + throws XmlPullParserException { + open(stream); + } + + @Override + public void setInput(Reader reader) throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + @Override + public String getInputEncoding() { + return null; + } + + @Override + public int getColumnNumber() { + return -1; + } + + @Override + public boolean isEmptyElementTag() throws XmlPullParserException { + return false; + } + + @Override + public boolean isWhitespace() throws XmlPullParserException { + return false; + } + + @Override + public void defineEntityReplacementText(String entityName, + String replacementText) throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + @Override + public String getNamespace(String prefix) { + throw new RuntimeException(E_NOT_SUPPORTED); + } + + @Override + public Object getProperty(String name) { + return null; + } + + @Override + public void setProperty(String name, Object value) + throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + @Override + public boolean getFeature(String feature) { + return false; + } + + @Override + public void setFeature(String name, boolean value) + throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + // /////////////////////////////////////////// implementation + /** + * Namespace stack, holds prefix+uri pairs, as well as depth information. + * All information is stored in one int[] array. Array consists of depth + * frames: Data=DepthFrame*; DepthFrame=Count+[Prefix+Uri]*+Count; + * Count='count of Prefix+Uri pairs'; Yes, count is stored twice, to enable + * bottom-up traversal. increaseDepth adds depth frame, decreaseDepth + * removes it. push/pop operations operate only in current depth frame. + * decreaseDepth removes any remaining (not pop'ed) namespace pairs. findXXX + * methods search all depth frames starting from the last namespace pair of + * current depth frame. All functions that operate with int, use -1 as + * 'invalid value'. + * + * !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !! + * + */ + private static final class NamespaceStack { + + public NamespaceStack() { + m_data = new int[32]; + } + + public final void reset() { + m_dataLength = 0; + m_count = 0; + m_depth = 0; + } + + public final int getTotalCount() { + return m_count; + } + + public final int getCurrentCount() { + if (m_dataLength == 0) { + return 0; + } + int offset = m_dataLength - 1; + return m_data[offset]; + } + + public final int getAccumulatedCount(int depth) { + if (m_dataLength == 0 || depth < 0) { + return 0; + } + if (depth > m_depth) { + depth = m_depth; + } + int accumulatedCount = 0; + int offset = 0; + for (; depth != 0; --depth) { + int count = m_data[offset]; + accumulatedCount += count; + offset += (2 + count * 2); + } + return accumulatedCount; + } + + public final void push(int prefix, int uri) { + if (m_depth == 0) { + increaseDepth(); + } + ensureDataCapacity(2); + int offset = m_dataLength - 1; + int count = m_data[offset]; + m_data[offset - 1 - count * 2] = count + 1; + m_data[offset] = prefix; + m_data[offset + 1] = uri; + m_data[offset + 2] = count + 1; + m_dataLength += 2; + m_count += 1; + } + + public final boolean pop(int prefix, int uri) { + if (m_dataLength == 0) { + return false; + } + int offset = m_dataLength - 1; + int count = m_data[offset]; + for (int i = 0, o = offset - 2; i != count; ++i, o -= 2) { + if (m_data[o] != prefix || m_data[o + 1] != uri) { + continue; + } + count -= 1; + if (i == 0) { + m_data[o] = count; + o -= (1 + count * 2); + m_data[o] = count; + } else { + m_data[offset] = count; + offset -= (1 + 2 + count * 2); + m_data[offset] = count; + System.arraycopy(m_data, o + 2, m_data, o, m_dataLength - o); + } + m_dataLength -= 2; + m_count -= 1; + return true; + } + return false; + } + + public final boolean pop() { + if (m_dataLength == 0) { + return false; + } + int offset = m_dataLength - 1; + int count = m_data[offset]; + if (count == 0) { + return false; + } + count -= 1; + offset -= 2; + m_data[offset] = count; + offset -= (1 + count * 2); + m_data[offset] = count; + m_dataLength -= 2; + m_count -= 1; + return true; + } + + public final int getPrefix(int index) { + return get(index, true); + } + + public final int getUri(int index) { + return get(index, false); + } + + public final int findPrefix(int uri) { + return find(uri, false); + } + + public final int findUri(int prefix) { + return find(prefix, true); + } + + public final int getDepth() { + return m_depth; + } + + public final void increaseDepth() { + ensureDataCapacity(2); + int offset = m_dataLength; + m_data[offset] = 0; + m_data[offset + 1] = 0; + m_dataLength += 2; + m_depth += 1; + } + + public final void decreaseDepth() { + if (m_dataLength == 0) { + return; + } + int offset = m_dataLength - 1; + int count = m_data[offset]; + if ((offset - 1 - count * 2) == 0) { + return; + } + m_dataLength -= 2 + count * 2; + m_count -= count; + m_depth -= 1; + } + + private void ensureDataCapacity(int capacity) { + int available = (m_data.length - m_dataLength); + if (available > capacity) { + return; + } + int newLength = (m_data.length + available) * 2; + int[] newData = new int[newLength]; + System.arraycopy(m_data, 0, newData, 0, m_dataLength); + m_data = newData; + } + + private final int find(int prefixOrUri, boolean prefix) { + if (m_dataLength == 0) { + return -1; + } + int offset = m_dataLength - 1; + for (int i = m_depth; i != 0; --i) { + int count = m_data[offset]; + offset -= 2; + for (; count != 0; --count) { + if (prefix) { + if (m_data[offset] == prefixOrUri) { + return m_data[offset + 1]; + } + } else { + if (m_data[offset + 1] == prefixOrUri) { + return m_data[offset]; + } + } + offset -= 2; + } + } + return -1; + } + + private final int get(int index, boolean prefix) { + if (m_dataLength == 0 || index < 0) { + return -1; + } + int offset = 0; + for (int i = m_depth; i != 0; --i) { + int count = m_data[offset]; + if (index >= count) { + index -= count; + offset += (2 + count * 2); + continue; + } + offset += (1 + index * 2); + if (!prefix) { + offset += 1; + } + return m_data[offset]; + } + return -1; + } + + private int[] m_data; + private int m_dataLength; + private int m_count; + private int m_depth; + } + + // ///////////////////////////////// package-visible + // final void fetchAttributes(int[] styleableIDs,TypedArray result) { + // result.resetIndices(); + // if (m_attributes==null || m_resourceIDs==null) { + // return; + // } + // boolean needStrings=false; + // for (int i=0,e=styleableIDs.length;i!=e;++i) { + // int id=styleableIDs[i]; + // for (int o=0;o!=m_attributes.length;o+=ATTRIBUTE_LENGHT) { + // int name=m_attributes[o+ATTRIBUTE_IX_NAME]; + // if (name>=m_resourceIDs.length || + // m_resourceIDs[name]!=id) + // { + // continue; + // } + // int valueType=m_attributes[o+ATTRIBUTE_IX_VALUE_TYPE]; + // int valueData; + // int assetCookie; + // if (valueType==TypedValue.TYPE_STRING) { + // valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_STRING]; + // assetCookie=-1; + // needStrings=true; + // } else { + // valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_DATA]; + // assetCookie=0; + // } + // result.addValue(i,valueType,valueData,assetCookie,id,0); + // } + // } + // if (needStrings) { + // result.setStrings(m_strings); + // } + // } + final StringBlock getStrings() { + return m_strings; + } + + // ///////////////////////////////// + private final int getAttributeOffset(int index) { + if (m_event != START_TAG) { + throw new IndexOutOfBoundsException( + "Current event is not START_TAG."); + } + int offset = index * ATTRIBUTE_LENGHT; + if (offset >= m_attributes.length) { + throw new IndexOutOfBoundsException("Invalid attribute index (" + + index + ")."); + } + return offset; + } + + private final int findAttribute(String namespace, String attribute) { + if (m_strings == null || attribute == null) { + return -1; + } + int name = m_strings.find(attribute); + if (name == -1) { + return -1; + } + int uri = (namespace != null) ? m_strings.find(namespace) : -1; + for (int o = 0; o != m_attributes.length; o += ATTRIBUTE_LENGHT) { + if (name == m_attributes[o + ATTRIBUTE_IX_NAME] + && (uri == -1 || uri == m_attributes[o + + ATTRIBUTE_IX_NAMESPACE_URI])) { + return o / ATTRIBUTE_LENGHT; + } + } + return -1; + } + + private final void resetEventInfo() { + m_event = -1; + m_lineNumber = -1; + m_name = -1; + m_namespaceUri = -1; + m_attributes = null; + m_idAttribute = -1; + m_classAttribute = -1; + m_styleAttribute = -1; + } + + private final void doNext() throws IOException { + // Delayed initialization. + if (m_strings == null) { + m_reader.skipCheckInt(CHUNK_AXML_FILE); + /* + * chunkSize + */m_reader.skipInt(); + m_strings = StringBlock.read(m_reader); + m_namespaces.increaseDepth(); + m_operational = true; + } + + if (m_event == END_DOCUMENT) { + return; + } + + int event = m_event; + resetEventInfo(); + + while (true) { + if (m_decreaseDepth) { + m_decreaseDepth = false; + m_namespaces.decreaseDepth(); + } + + // Fake END_DOCUMENT event. + if (event == END_TAG && m_namespaces.getDepth() == 1 + && m_namespaces.getCurrentCount() == 0) { + m_event = END_DOCUMENT; + break; + } + + int chunkType; + if (event == START_DOCUMENT) { + // Fake event, see CHUNK_XML_START_TAG handler. + chunkType = CHUNK_XML_START_TAG; + } else { + chunkType = m_reader.readInt(); + } + + if (chunkType == CHUNK_RESOURCEIDS) { + int chunkSize = m_reader.readInt(); + if (chunkSize < 8 || (chunkSize % 4) != 0) { + throw new IOException("Invalid resource ids size (" + + chunkSize + ")."); + } + m_resourceIDs = m_reader.readIntArray(chunkSize / 4 - 2); + continue; + } + + if (chunkType < CHUNK_XML_FIRST || chunkType > CHUNK_XML_LAST) { + throw new IOException("Invalid chunk type (" + chunkType + ")."); + } + + // Fake START_DOCUMENT event. + if (chunkType == CHUNK_XML_START_TAG && event == -1) { + m_event = START_DOCUMENT; + break; + } + + // Common header. + /* chunkSize */m_reader.skipInt(); + int lineNumber = m_reader.readInt(); + /* 0xFFFFFFFF */m_reader.skipInt(); + + if (chunkType == CHUNK_XML_START_NAMESPACE + || chunkType == CHUNK_XML_END_NAMESPACE) { + if (chunkType == CHUNK_XML_START_NAMESPACE) { + int prefix = m_reader.readInt(); + int uri = m_reader.readInt(); + m_namespaces.push(prefix, uri); + } else { + /* prefix */m_reader.skipInt(); + /* uri */m_reader.skipInt(); + m_namespaces.pop(); + } + continue; + } + + m_lineNumber = lineNumber; + + if (chunkType == CHUNK_XML_START_TAG) { + m_namespaceUri = m_reader.readInt(); + m_name = m_reader.readInt(); + /* flags? */m_reader.skipInt(); + int attributeCount = m_reader.readInt(); + m_idAttribute = (attributeCount >>> 16) - 1; + attributeCount &= 0xFFFF; + m_classAttribute = m_reader.readInt(); + m_styleAttribute = (m_classAttribute >>> 16) - 1; + m_classAttribute = (m_classAttribute & 0xFFFF) - 1; + m_attributes = m_reader.readIntArray(attributeCount + * ATTRIBUTE_LENGHT); + for (int i = ATTRIBUTE_IX_VALUE_TYPE; i < m_attributes.length;) { + m_attributes[i] = (m_attributes[i] >>> 24); + i += ATTRIBUTE_LENGHT; + } + m_namespaces.increaseDepth(); + m_event = START_TAG; + break; + } + + if (chunkType == CHUNK_XML_END_TAG) { + m_namespaceUri = m_reader.readInt(); + m_name = m_reader.readInt(); + m_event = END_TAG; + m_decreaseDepth = true; + break; + } + + if (chunkType == CHUNK_XML_TEXT) { + m_name = m_reader.readInt(); + /* ? */m_reader.skipInt(); + /* ? */m_reader.skipInt(); + m_event = TEXT; + break; + } + } + } + + private static String formatArray(int[] array, int min, int max) { + if (max > array.length) { + max = array.length; + } + if (min < 0) { + min = 0; + } + StringBuffer sb = new StringBuffer("["); + int i = min; + while (true) { + sb.append(array[i]); + i++; + if (i < max) { + sb.append(", "); + } else { + sb.append("]"); + break; + } + } + return sb.toString(); + } + + private boolean compareAttr(int[] attr1, int[] attr2) { + // TODO: sort Attrs /* - * ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING : - * ATTRIBUTE_IX_NAMESPACE_URI ATTRIBUTE_IX_NAMESPACE_URI : - * ATTRIBUTE_IX_NAME id - * - */ - if (attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING - && attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE] - && //(m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) || - // m_strings.touch(attr2[ATTRIBUTE_IX_VALUE_STRING], m_name)) && - //m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) && - attr1[ATTRIBUTE_IX_VALUE_STRING] != attr2[ATTRIBUTE_IX_VALUE_STRING]) { - return (attr1[ATTRIBUTE_IX_VALUE_STRING] < attr2[ATTRIBUTE_IX_VALUE_STRING]); - } else if ((attr1[ATTRIBUTE_IX_NAMESPACE_URI] == attr2[ATTRIBUTE_IX_NAMESPACE_URI]) && (attr1[ATTRIBUTE_IX_NAMESPACE_URI] != -1) - && //(m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) || - // m_strings.touch(attr2[ATTRIBUTE_IX_NAME], m_name)) && - //m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) && - (attr1[ATTRIBUTE_IX_NAME] != attr2[ATTRIBUTE_IX_NAME])) { - return (attr1[ATTRIBUTE_IX_NAME] < attr2[ATTRIBUTE_IX_NAME]); - //} else if (attr1[ATTRIBUTE_IX_NAMESPACE_URI] < attr2[ATTRIBUTE_IX_NAMESPACE_URI]) { - // return true; - } else { - return false; - } - } + * ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING : + * ATTRIBUTE_IX_NAMESPACE_URI ATTRIBUTE_IX_NAMESPACE_URI : + * ATTRIBUTE_IX_NAME id + */ + if (attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING + && attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE] + && // (m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) + // || + // m_strings.touch(attr2[ATTRIBUTE_IX_VALUE_STRING], + // m_name)) && + // m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) + // && + attr1[ATTRIBUTE_IX_VALUE_STRING] != attr2[ATTRIBUTE_IX_VALUE_STRING]) { + return (attr1[ATTRIBUTE_IX_VALUE_STRING] < attr2[ATTRIBUTE_IX_VALUE_STRING]); + } else if ((attr1[ATTRIBUTE_IX_NAMESPACE_URI] == attr2[ATTRIBUTE_IX_NAMESPACE_URI]) + && (attr1[ATTRIBUTE_IX_NAMESPACE_URI] != -1) && // (m_strings.touch(attr1[ATTRIBUTE_IX_NAME], + // m_name) || + // m_strings.touch(attr2[ATTRIBUTE_IX_NAME], + // m_name)) && + // m_strings.touch(attr1[ATTRIBUTE_IX_NAME], + // m_name) && + (attr1[ATTRIBUTE_IX_NAME] != attr2[ATTRIBUTE_IX_NAME])) { + return (attr1[ATTRIBUTE_IX_NAME] < attr2[ATTRIBUTE_IX_NAME]); + // } else if (attr1[ATTRIBUTE_IX_NAMESPACE_URI] < + // attr2[ATTRIBUTE_IX_NAMESPACE_URI]) { + // return true; + } else { + return false; + } + } + private void setFirstError(AndrolibException error) { + if (mFirstError == null) { + mFirstError = error; + } + } - private void setFirstError(AndrolibException error) { - if (mFirstError == null) { - mFirstError = error; - } - } + // ///////////////////////////////// data + /* + * All values are essentially indices, e.g. m_name is an index of name in + * m_strings. + */ + private ExtDataInput m_reader; + private ResAttrDecoder mAttrDecoder; + private AndrolibException mFirstError; - /////////////////////////////////// data - /* - * All values are essentially indices, e.g. m_name is - * an index of name in m_strings. - */ - private ExtDataInput m_reader; - private ResAttrDecoder mAttrDecoder; - private AndrolibException mFirstError; + private boolean m_operational = false; + private StringBlock m_strings; + private int[] m_resourceIDs; + private NamespaceStack m_namespaces = new NamespaceStack(); + private boolean m_decreaseDepth; + private int m_event; + private int m_lineNumber; + private int m_name; + private int m_namespaceUri; + private int[] m_attributes; + private int m_idAttribute; + private int m_classAttribute; + private int m_styleAttribute; - private boolean m_operational = false; - private StringBlock m_strings; - private int[] m_resourceIDs; - private NamespaceStack m_namespaces = new NamespaceStack(); - private boolean m_decreaseDepth; - private int m_event; - private int m_lineNumber; - private int m_name; - private int m_namespaceUri; - private int[] m_attributes; - private int m_idAttribute; - private int m_classAttribute; - private int m_styleAttribute; + private final static Logger LOGGER = Logger + .getLogger(AXmlResourceParser.class.getName()); + private static final String E_NOT_SUPPORTED = "Method is not supported."; + private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0, + ATTRIBUTE_IX_NAME = 1, ATTRIBUTE_IX_VALUE_STRING = 2, + ATTRIBUTE_IX_VALUE_TYPE = 3, ATTRIBUTE_IX_VALUE_DATA = 4, + ATTRIBUTE_LENGHT = 5; - private final static Logger LOGGER = - Logger.getLogger(AXmlResourceParser.class.getName()); - private static final String E_NOT_SUPPORTED = "Method is not supported."; - private static final int - ATTRIBUTE_IX_NAMESPACE_URI = 0, - ATTRIBUTE_IX_NAME = 1, - ATTRIBUTE_IX_VALUE_STRING = 2, - ATTRIBUTE_IX_VALUE_TYPE = 3, - ATTRIBUTE_IX_VALUE_DATA = 4, - ATTRIBUTE_LENGHT = 5; - - private static final int - CHUNK_AXML_FILE = 0x00080003, - CHUNK_RESOURCEIDS = 0x00080180, - CHUNK_XML_FIRST = 0x00100100, - CHUNK_XML_START_NAMESPACE = 0x00100100, - CHUNK_XML_END_NAMESPACE = 0x00100101, - CHUNK_XML_START_TAG = 0x00100102, - CHUNK_XML_END_TAG = 0x00100103, - CHUNK_XML_TEXT = 0x00100104, - CHUNK_XML_LAST = 0x00100104; + private static final int CHUNK_AXML_FILE = 0x00080003, + CHUNK_RESOURCEIDS = 0x00080180, CHUNK_XML_FIRST = 0x00100100, + CHUNK_XML_START_NAMESPACE = 0x00100100, + CHUNK_XML_END_NAMESPACE = 0x00100101, + CHUNK_XML_START_TAG = 0x00100102, CHUNK_XML_END_TAG = 0x00100103, + CHUNK_XML_TEXT = 0x00100104, CHUNK_XML_LAST = 0x00100104; } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java index 53b1b4a3..472f8524 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java @@ -28,112 +28,112 @@ import org.apache.commons.io.IOUtils; * @author Ryszard Wiśniewski */ public class Res9patchStreamDecoder implements ResStreamDecoder { - public void decode(InputStream in, OutputStream out) - throws AndrolibException { - try { - byte[] data = IOUtils.toByteArray(in); + @Override + public void decode(InputStream in, OutputStream out) + throws AndrolibException { + try { + byte[] data = IOUtils.toByteArray(in); - BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); - int w = im.getWidth(), h = im.getHeight(); + BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); + int w = im.getWidth(), h = im.getHeight(); - BufferedImage im2 = new BufferedImage( - w + 2, h + 2, BufferedImage.TYPE_4BYTE_ABGR); - if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) { - im2.getRaster().setRect(1, 1, im.getRaster()); - } else { - im2.getGraphics().drawImage(im, 1, 1, null); - } + BufferedImage im2 = new BufferedImage(w + 2, h + 2, + BufferedImage.TYPE_4BYTE_ABGR); + if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) { + im2.getRaster().setRect(1, 1, im.getRaster()); + } else { + im2.getGraphics().drawImage(im, 1, 1, null); + } - NinePatch np = getNinePatch(data); - drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight); - drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom); + NinePatch np = getNinePatch(data); + drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight); + drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom); - int[] xDivs = np.xDivs; - for (int i = 0; i < xDivs.length; i += 2) { - drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]); - } + int[] xDivs = np.xDivs; + for (int i = 0; i < xDivs.length; i += 2) { + drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]); + } - int[] yDivs = np.yDivs; - for (int i = 0; i < yDivs.length; i += 2) { - drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]); - } + int[] yDivs = np.yDivs; + for (int i = 0; i < yDivs.length; i += 2) { + drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]); + } - ImageIO.write(im2, "png", out); - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } + ImageIO.write(im2, "png", out); + } catch (IOException ex) { + throw new AndrolibException(ex); + } + } - private NinePatch getNinePatch(byte[] data) - throws AndrolibException, IOException { - ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); - find9patchChunk(di); - return NinePatch.decode(di); - } + private NinePatch getNinePatch(byte[] data) throws AndrolibException, + IOException { + ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); + find9patchChunk(di); + return NinePatch.decode(di); + } - private void find9patchChunk(DataInput di) - throws AndrolibException, IOException { - di.skipBytes(8); - while (true) { - int size; - try { - size = di.readInt(); - } catch (IOException ex) { - throw new CantFind9PatchChunk("Cant find nine patch chunk", ex); - } - if (di.readInt() == NP_CHUNK_TYPE) { - return; - } - di.skipBytes(size + 4); - } - } + private void find9patchChunk(DataInput di) throws AndrolibException, + IOException { + di.skipBytes(8); + while (true) { + int size; + try { + size = di.readInt(); + } catch (IOException ex) { + throw new CantFind9PatchChunk("Cant find nine patch chunk", ex); + } + if (di.readInt() == NP_CHUNK_TYPE) { + return; + } + di.skipBytes(size + 4); + } + } - private void drawHLine(BufferedImage im, int y, int x1, int x2) { - for (int x = x1; x <= x2; x++) { - im.setRGB(x, y, NP_COLOR); - } - } + private void drawHLine(BufferedImage im, int y, int x1, int x2) { + for (int x = x1; x <= x2; x++) { + im.setRGB(x, y, NP_COLOR); + } + } - private void drawVLine(BufferedImage im, int x, int y1, int y2) { - for (int y = y1; y <= y2; y++) { - im.setRGB(x, y, NP_COLOR); - } - } + private void drawVLine(BufferedImage im, int x, int y1, int y2) { + for (int y = y1; y <= y2; y++) { + im.setRGB(x, y, NP_COLOR); + } + } - private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc - private static final int NP_COLOR = 0xff000000; + private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc + private static final int NP_COLOR = 0xff000000; + private static class NinePatch { + public final int padLeft, padRight, padTop, padBottom; + public final int[] xDivs, yDivs; - private static class NinePatch { - public final int padLeft, padRight, padTop, padBottom; - public final int[] xDivs, yDivs; + public NinePatch(int padLeft, int padRight, int padTop, int padBottom, + int[] xDivs, int[] yDivs) { + this.padLeft = padLeft; + this.padRight = padRight; + this.padTop = padTop; + this.padBottom = padBottom; + this.xDivs = xDivs; + this.yDivs = yDivs; + } - public NinePatch(int padLeft, int padRight, int padTop, int padBottom, - int[] xDivs, int[] yDivs) { - this.padLeft = padLeft; - this.padRight = padRight; - this.padTop = padTop; - this.padBottom = padBottom; - this.xDivs = xDivs; - this.yDivs = yDivs; - } + public static NinePatch decode(ExtDataInput di) throws IOException { + di.skipBytes(1); + byte numXDivs = di.readByte(); + byte numYDivs = di.readByte(); + di.skipBytes(1); + di.skipBytes(8); + int padLeft = di.readInt(); + int padRight = di.readInt(); + int padTop = di.readInt(); + int padBottom = di.readInt(); + di.skipBytes(4); + int[] xDivs = di.readIntArray(numXDivs); + int[] yDivs = di.readIntArray(numYDivs); - public static NinePatch decode(ExtDataInput di) throws IOException { - di.skipBytes(1); - byte numXDivs = di.readByte(); - byte numYDivs = di.readByte(); - di.skipBytes(1); - di.skipBytes(8); - int padLeft = di.readInt(); - int padRight = di.readInt(); - int padTop = di.readInt(); - int padBottom = di.readInt(); - di.skipBytes(4); - int[] xDivs = di.readIntArray(numXDivs); - int[] yDivs = di.readIntArray(numYDivs); - - return new NinePatch(padLeft, padRight, padTop, padBottom, - xDivs, yDivs); - } - } + return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs, + yDivs); + } + } } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java index 75c601e1..8287d613 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java @@ -25,31 +25,31 @@ import brut.androlib.res.data.value.ResScalarValue; * @author Ryszard Wiśniewski */ public class ResAttrDecoder { - public String decode(int type, int value, String rawValue, int attrResId) - throws AndrolibException { - ResScalarValue resValue = mCurrentPackage.getValueFactory() - .factory(type, value, rawValue); + public String decode(int type, int value, String rawValue, int attrResId) + throws AndrolibException { + ResScalarValue resValue = mCurrentPackage.getValueFactory().factory( + type, value, rawValue); - String decoded = null; - if (attrResId != 0) { - ResAttr attr = (ResAttr) getCurrentPackage().getResTable() - .getResSpec(attrResId).getDefaultResource().getValue(); - decoded = attr.convertToResXmlFormat(resValue); - } + String decoded = null; + if (attrResId != 0) { + ResAttr attr = (ResAttr) getCurrentPackage().getResTable() + .getResSpec(attrResId).getDefaultResource().getValue(); + decoded = attr.convertToResXmlFormat(resValue); + } - return decoded != null ? decoded : resValue.encodeAsResXmlAttr(); - } + return decoded != null ? decoded : resValue.encodeAsResXmlAttr(); + } - public ResPackage getCurrentPackage() throws AndrolibException { - if (mCurrentPackage == null) { - throw new AndrolibException("Current package not set"); - } - return mCurrentPackage; - } + public ResPackage getCurrentPackage() throws AndrolibException { + if (mCurrentPackage == null) { + throw new AndrolibException("Current package not set"); + } + return mCurrentPackage; + } - public void setCurrentPackage(ResPackage currentPackage) { - mCurrentPackage = currentPackage; - } + public void setCurrentPackage(ResPackage currentPackage) { + mCurrentPackage = currentPackage; + } - private ResPackage mCurrentPackage; + private ResPackage mCurrentPackage; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java index b8241e2e..18575630 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java @@ -31,120 +31,121 @@ import java.util.logging.Logger; * @author Ryszard Wiśniewski */ public class ResFileDecoder { - private final ResStreamDecoderContainer mDecoders; + private final ResStreamDecoderContainer mDecoders; - public ResFileDecoder(ResStreamDecoderContainer decoders) { - this.mDecoders = decoders; - } + public ResFileDecoder(ResStreamDecoderContainer decoders) { + this.mDecoders = decoders; + } - public void decode(ResResource res, Directory inDir, Directory outDir) - throws AndrolibException { + public void decode(ResResource res, Directory inDir, Directory outDir) + throws AndrolibException { - ResFileValue fileValue = (ResFileValue) res.getValue(); - String inFileName = fileValue.getStrippedPath(); - String outResName = res.getFilePath(); - String typeName = res.getResSpec().getType().getName(); + ResFileValue fileValue = (ResFileValue) res.getValue(); + String inFileName = fileValue.getStrippedPath(); + String outResName = res.getFilePath(); + String typeName = res.getResSpec().getType().getName(); - String ext = null; - String outFileName; - int extPos = inFileName.lastIndexOf("."); - if (extPos == -1) { - outFileName = outResName; - } else { - ext = inFileName.substring(extPos); - outFileName = outResName + ext; - } + String ext = null; + String outFileName; + int extPos = inFileName.lastIndexOf("."); + if (extPos == -1) { + outFileName = outResName; + } else { + ext = inFileName.substring(extPos); + outFileName = outResName + ext; + } - try { - if (typeName.equals("raw")) { - decode(inDir, inFileName, outDir, outFileName, "raw"); - return; - } - if (typeName.equals("drawable") || typeName.equals("mipmap")) { - if (inFileName.toLowerCase().endsWith(".9.png")) { - outFileName = outResName + ".9" + ext; - - // check for htc .r.9.png - if (inFileName.toLowerCase().endsWith(".r.9.png")) { - outFileName = outResName + ".r.9" + ext; - } + try { + if (typeName.equals("raw")) { + decode(inDir, inFileName, outDir, outFileName, "raw"); + return; + } + if (typeName.equals("drawable") || typeName.equals("mipmap")) { + if (inFileName.toLowerCase().endsWith(".9.png")) { + outFileName = outResName + ".9" + ext; - try { - decode( - inDir, inFileName, outDir, outFileName, "9patch"); - return; - } catch (CantFind9PatchChunk ex) { - LOGGER.log(Level.WARNING, String.format( - "Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.", - inFileName - ), ex); - outDir.removeFile(outFileName); - outFileName = outResName + ext; - } - } - if (! ".xml".equals(ext)) { - decode(inDir, inFileName, outDir, outFileName, "raw"); - return; - } - } + // check for htc .r.9.png + if (inFileName.toLowerCase().endsWith(".r.9.png")) { + outFileName = outResName + ".r.9" + ext; + } - decode(inDir, inFileName, outDir, outFileName, "xml"); - } catch (AndrolibException ex) { - LOGGER.log(Level.SEVERE, String.format( - "Could not decode file, replacing by FALSE value: %s", - inFileName, outFileName), ex); - res.replace(new ResBoolValue(false, null)); - } - } + try { + decode(inDir, inFileName, outDir, outFileName, "9patch"); + return; + } catch (CantFind9PatchChunk ex) { + LOGGER.log( + Level.WARNING, + String.format( + "Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.", + inFileName), ex); + outDir.removeFile(outFileName); + outFileName = outResName + ext; + } + } + if (!".xml".equals(ext)) { + decode(inDir, inFileName, outDir, outFileName, "raw"); + return; + } + } - public void decode(Directory inDir, String inFileName, Directory outDir, - String outFileName, String decoder) throws AndrolibException { - InputStream in = null; - OutputStream out = null; - try { - in = inDir.getFileInput(inFileName); - out = outDir.getFileOutput(outFileName); - mDecoders.decode(in, out, decoder); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } finally { - try{ - if (in != null) { - in.close(); - } - if (out != null) { - out.close(); - } - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } - } + decode(inDir, inFileName, outDir, outFileName, "xml"); + } catch (AndrolibException ex) { + LOGGER.log(Level.SEVERE, String.format( + "Could not decode file, replacing by FALSE value: %s", + inFileName, outFileName), ex); + res.replace(new ResBoolValue(false, null)); + } + } - public void decodeManifest(Directory inDir, String inFileName, Directory outDir, - String outFileName) throws AndrolibException { - InputStream in = null; - OutputStream out = null; - try { - in = inDir.getFileInput(inFileName); - out = outDir.getFileOutput(outFileName); - ((XmlPullStreamDecoder)mDecoders.getDecoder("xml")).decodeManifest(in, out); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } finally { - try{ - if (in != null) { - in.close(); - } - if (out != null) { - out.close(); - } - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } - } + public void decode(Directory inDir, String inFileName, Directory outDir, + String outFileName, String decoder) throws AndrolibException { + InputStream in = null; + OutputStream out = null; + try { + in = inDir.getFileInput(inFileName); + out = outDir.getFileOutput(outFileName); + mDecoders.decode(in, out, decoder); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } finally { + try { + if (in != null) { + in.close(); + } + if (out != null) { + out.close(); + } + } catch (IOException ex) { + throw new AndrolibException(ex); + } + } + } - private final static Logger LOGGER = - Logger.getLogger(ResFileDecoder.class.getName()); + public void decodeManifest(Directory inDir, String inFileName, + Directory outDir, String outFileName) throws AndrolibException { + InputStream in = null; + OutputStream out = null; + try { + in = inDir.getFileInput(inFileName); + out = outDir.getFileOutput(outFileName); + ((XmlPullStreamDecoder) mDecoders.getDecoder("xml")) + .decodeManifest(in, out); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } finally { + try { + if (in != null) { + in.close(); + } + if (out != null) { + out.close(); + } + } catch (IOException ex) { + throw new AndrolibException(ex); + } + } + } + + private final static Logger LOGGER = Logger.getLogger(ResFileDecoder.class + .getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java index 6c5d0e3a..3ee66657 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java @@ -26,12 +26,13 @@ import org.apache.commons.io.IOUtils; * @author Ryszard Wiśniewski */ public class ResRawStreamDecoder implements ResStreamDecoder { - public void decode(InputStream in, OutputStream out) - throws AndrolibException { - try { - IOUtils.copy(in, out); - } catch (IOException ex) { - throw new AndrolibException("Could not decode raw stream", ex); - } - } + @Override + public void decode(InputStream in, OutputStream out) + throws AndrolibException { + try { + IOUtils.copy(in, out); + } catch (IOException ex) { + throw new AndrolibException("Could not decode raw stream", ex); + } + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java index e7bbcd68..2541baaa 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java @@ -24,6 +24,6 @@ import java.io.OutputStream; * @author Ryszard Wiśniewski */ public interface ResStreamDecoder { - public void decode(InputStream in, OutputStream out) - throws AndrolibException; + public void decode(InputStream in, OutputStream out) + throws AndrolibException; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java index d478bf98..ce26e5a2 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java @@ -26,23 +26,22 @@ import java.util.Map; * @author Ryszard Wiśniewski */ public class ResStreamDecoderContainer { - private final Map mDecoders = - new HashMap(); + private final Map mDecoders = new HashMap(); - public void decode(InputStream in, OutputStream out, String decoderName) - throws AndrolibException { - getDecoder(decoderName).decode(in, out); - } + public void decode(InputStream in, OutputStream out, String decoderName) + throws AndrolibException { + getDecoder(decoderName).decode(in, out); + } - public ResStreamDecoder getDecoder(String name) throws AndrolibException { - ResStreamDecoder decoder = mDecoders.get(name); - if (decoder == null) { - throw new AndrolibException("Undefined decoder: " + name); - } - return decoder; - } + public ResStreamDecoder getDecoder(String name) throws AndrolibException { + ResStreamDecoder decoder = mDecoders.get(name); + if (decoder == null) { + throw new AndrolibException("Undefined decoder: " + name); + } + return decoder; + } - public void setDecoder(String name, ResStreamDecoder decoder) { - mDecoders.put(name, decoder); - } + public void setDecoder(String name, ResStreamDecoder decoder) { + mDecoders.put(name, decoder); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java index 36ecda52..d892e13b 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java @@ -28,320 +28,313 @@ import java.util.logging.Logger; * @author Ryszard Wiśniewski * @author Dmitry Skiba * - * Block of strings, used in binary xml and arsc. + * Block of strings, used in binary xml and arsc. + * + * TODO: - implement get() * - * TODO: - * - implement get() - * */ public class StringBlock { - /** - * Reads whole (including chunk type) string block from stream. - * Stream must be at the chunk type. - */ - public static StringBlock read(ExtDataInput reader) throws IOException { - reader.skipCheckInt(CHUNK_TYPE); - int chunkSize = reader.readInt(); - int stringCount = reader.readInt(); - int styleOffsetCount = reader.readInt(); - int flags = reader.readInt(); - int stringsOffset = reader.readInt(); - int stylesOffset = reader.readInt(); + /** + * Reads whole (including chunk type) string block from stream. Stream must + * be at the chunk type. + */ + public static StringBlock read(ExtDataInput reader) throws IOException { + reader.skipCheckInt(CHUNK_TYPE); + int chunkSize = reader.readInt(); + int stringCount = reader.readInt(); + int styleOffsetCount = reader.readInt(); + int flags = reader.readInt(); + int stringsOffset = reader.readInt(); + int stylesOffset = reader.readInt(); - StringBlock block = new StringBlock(); - block.m_isUTF8 = (flags & UTF8_FLAG) != 0; - block.m_stringOffsets = reader.readIntArray(stringCount); - block.m_stringOwns = new int[stringCount]; - for (int i=0;i= m_stringOffsets.length) { - return null; - } - int offset = m_stringOffsets[index]; - int length; + /** + * Returns raw string (without any styling information) at specified index. + */ + public String getString(int index) { + if (index < 0 || m_stringOffsets == null + || index >= m_stringOffsets.length) { + return null; + } + int offset = m_stringOffsets[index]; + int length; - if (! m_isUTF8) { - length = getShort(m_strings, offset) * 2; - offset += 2; - } else { - offset += getVarint(m_strings, offset)[1]; - int[] varint = getVarint(m_strings, offset); - offset += varint[1]; - length = varint[0]; - } - return decodeString(offset, length); - } + if (!m_isUTF8) { + length = getShort(m_strings, offset) * 2; + offset += 2; + } else { + offset += getVarint(m_strings, offset)[1]; + int[] varint = getVarint(m_strings, offset); + offset += varint[1]; + length = varint[0]; + } + return decodeString(offset, length); + } - /** - * Not yet implemented. - * - * Returns string with style information (if any). - */ - public CharSequence get(int index) { - return getString(index); - } + /** + * Not yet implemented. + * + * Returns string with style information (if any). + */ + public CharSequence get(int index) { + return getString(index); + } - /** - * Returns string with style tags (html-like). - */ - public String getHTML(int index) { - String raw = getString(index); - if (raw == null) { - return raw; - } - int[] style = getStyle(index); - if (style == null) { - return ResXmlEncoders.escapeXmlChars(raw); - } - StringBuilder html = new StringBuilder(raw.length() + 32); - int[] opened = new int[style.length / 3]; - int offset = 0, depth = 0; - while (true) { - int i = -1, j; - for (j = 0; j != style.length; j += 3) { - if (style[j + 1] == -1) { - continue; - } - if (i == -1 || style[i + 1] > style[j + 1]) { - i = j; - } - } - int start = ((i != -1) ? style[i + 1] : raw.length()); - for (j = depth - 1; j >= 0; j--) { - int last = opened[j]; - int end = style[last + 2]; - if (end >= start) { - break; - } - if (offset <= end) { - html.append(ResXmlEncoders.escapeXmlChars( - raw.substring(offset, end + 1))); - offset = end + 1; - } - outputStyleTag(getString(style[last]), html, true); - } - depth = j + 1; - if (offset < start) { - html.append(ResXmlEncoders.escapeXmlChars( - raw.substring(offset, start))); - offset = start; - } - if (i == -1) { - break; - } - outputStyleTag(getString(style[i]), html, false); - style[i + 1] = -1; - opened[depth++] = i; - } - return html.toString(); - } + /** + * Returns string with style tags (html-like). + */ + public String getHTML(int index) { + String raw = getString(index); + if (raw == null) { + return raw; + } + int[] style = getStyle(index); + if (style == null) { + return ResXmlEncoders.escapeXmlChars(raw); + } + StringBuilder html = new StringBuilder(raw.length() + 32); + int[] opened = new int[style.length / 3]; + int offset = 0, depth = 0; + while (true) { + int i = -1, j; + for (j = 0; j != style.length; j += 3) { + if (style[j + 1] == -1) { + continue; + } + if (i == -1 || style[i + 1] > style[j + 1]) { + i = j; + } + } + int start = ((i != -1) ? style[i + 1] : raw.length()); + for (j = depth - 1; j >= 0; j--) { + int last = opened[j]; + int end = style[last + 2]; + if (end >= start) { + break; + } + if (offset <= end) { + html.append(ResXmlEncoders.escapeXmlChars(raw.substring( + offset, end + 1))); + offset = end + 1; + } + outputStyleTag(getString(style[last]), html, true); + } + depth = j + 1; + if (offset < start) { + html.append(ResXmlEncoders.escapeXmlChars(raw.substring(offset, + start))); + offset = start; + } + if (i == -1) { + break; + } + outputStyleTag(getString(style[i]), html, false); + style[i + 1] = -1; + opened[depth++] = i; + } + return html.toString(); + } - private void outputStyleTag(String tag, StringBuilder builder, - boolean close) { - builder.append('<'); - if (close) { - builder.append('/'); - } + private void outputStyleTag(String tag, StringBuilder builder, boolean close) { + builder.append('<'); + if (close) { + builder.append('/'); + } - int pos = tag.indexOf(';'); - if (pos == -1) { - builder.append(tag); - } else { - builder.append(tag.substring(0, pos)); - if (! close) { - boolean loop = true; - while (loop) { - int pos2 = tag.indexOf('=', pos + 1); - builder.append(' ').append(tag.substring(pos + 1, pos2)) - .append("=\""); - pos = tag.indexOf(';', pos2 + 1); + int pos = tag.indexOf(';'); + if (pos == -1) { + builder.append(tag); + } else { + builder.append(tag.substring(0, pos)); + if (!close) { + boolean loop = true; + while (loop) { + int pos2 = tag.indexOf('=', pos + 1); + builder.append(' ').append(tag.substring(pos + 1, pos2)) + .append("=\""); + pos = tag.indexOf(';', pos2 + 1); - String val; - if (pos != -1) { - val = tag.substring(pos2 + 1, pos); - } else { - loop = false; - val = tag.substring(pos2 + 1); - } + String val; + if (pos != -1) { + val = tag.substring(pos2 + 1, pos); + } else { + loop = false; + val = tag.substring(pos2 + 1); + } - builder.append(ResXmlEncoders.escapeXmlChars(val)) - .append('"'); - } - } - } - builder.append('>'); - } + builder.append(ResXmlEncoders.escapeXmlChars(val)).append( + '"'); + } + } + } + builder.append('>'); + } - /** - * Finds index of the string. - * Returns -1 if the string was not found. - */ - public int find(String string) { - if (string == null) { - return -1; - } - for (int i = 0; i != m_stringOffsets.length; ++i) { - int offset = m_stringOffsets[i]; - int length = getShort(m_strings, offset); - if (length != string.length()) { - continue; - } - int j = 0; - for (; j != length; ++j) { - offset += 2; - if (string.charAt(j) != getShort(m_strings, offset)) { - break; - } - } - if (j == length) { - return i; - } - } - return -1; - } + /** + * Finds index of the string. Returns -1 if the string was not found. + */ + public int find(String string) { + if (string == null) { + return -1; + } + for (int i = 0; i != m_stringOffsets.length; ++i) { + int offset = m_stringOffsets[i]; + int length = getShort(m_strings, offset); + if (length != string.length()) { + continue; + } + int j = 0; + for (; j != length; ++j) { + offset += 2; + if (string.charAt(j) != getShort(m_strings, offset)) { + break; + } + } + if (j == length) { + return i; + } + } + return -1; + } - ///////////////////////////////////////////// implementation - private StringBlock() { - } + // /////////////////////////////////////////// implementation + private StringBlock() { + } - /** - * Returns style information - array of int triplets, - * where in each triplet: - * * first int is index of tag name ('b','i', etc.) - * * second int is tag start index in string - * * third int is tag end index in string - */ - private int[] getStyle(int index) { - if (m_styleOffsets == null || m_styles == null - || index >= m_styleOffsets.length) { - return null; - } - int offset = m_styleOffsets[index] / 4; - int style[]; - { - int count = 0; - for (int i = offset; i < m_styles.length; ++i) { - if (m_styles[i] == -1) { - break; - } - count += 1; - } - if (count == 0 || (count % 3) != 0) { - return null; - } - style = new int[count]; - } - for (int i = offset, j = 0; i < m_styles.length;) { - if (m_styles[i] == -1) { - break; - } - style[j++] = m_styles[i++]; - } - return style; - } + /** + * Returns style information - array of int triplets, where in each triplet: + * * first int is index of tag name ('b','i', etc.) * second int is tag + * start index in string * third int is tag end index in string + */ + private int[] getStyle(int index) { + if (m_styleOffsets == null || m_styles == null + || index >= m_styleOffsets.length) { + return null; + } + int offset = m_styleOffsets[index] / 4; + int style[]; + { + int count = 0; + for (int i = offset; i < m_styles.length; ++i) { + if (m_styles[i] == -1) { + break; + } + count += 1; + } + if (count == 0 || (count % 3) != 0) { + return null; + } + style = new int[count]; + } + for (int i = offset, j = 0; i < m_styles.length;) { + if (m_styles[i] == -1) { + break; + } + style[j++] = m_styles[i++]; + } + return style; + } - private String decodeString(int offset, int length) { - try { - return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode( - ByteBuffer.wrap(m_strings, offset, length)).toString(); - } catch (CharacterCodingException ex) { - LOGGER.log(Level.WARNING, null, ex); - return null; - } - } + private String decodeString(int offset, int length) { + try { + return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode( + ByteBuffer.wrap(m_strings, offset, length)).toString(); + } catch (CharacterCodingException ex) { + LOGGER.log(Level.WARNING, null, ex); + return null; + } + } - private static final int getShort(byte[] array, int offset) { - return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff; - } + private static final int getShort(byte[] array, int offset) { + return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff; + } - private static final int getShort(int[] array, int offset) { - int value = array[offset / 4]; - if ((offset % 4) / 2 == 0) { - return (value & 0xFFFF); - } else { - return (value >>> 16); - } - } + private static final int getShort(int[] array, int offset) { + int value = array[offset / 4]; + if ((offset % 4) / 2 == 0) { + return (value & 0xFFFF); + } else { + return (value >>> 16); + } + } - private static final int[] getVarint(byte[] array, int offset) { - int val = array[offset]; - boolean more = (val & 0x80) != 0; - val &= 0x7f; + private static final int[] getVarint(byte[] array, int offset) { + int val = array[offset]; + boolean more = (val & 0x80) != 0; + val &= 0x7f; - if (! more) { - return new int[]{val, 1}; - } else { - return new int[]{val << 8 | array[offset + 1] & 0xff, 2}; - } - } + if (!more) { + return new int[] { val, 1 }; + } else { + return new int[] { val << 8 | array[offset + 1] & 0xff, 2 }; + } + } - public boolean touch(int index, int own) { - if (index < 0 - || m_stringOwns == null - || index >= m_stringOwns.length) { - return false; - } - if(m_stringOwns[index] == -1) { - m_stringOwns[index] = own; - return true; - } else if (m_stringOwns[index] == own) { - return true; - } else { - return false; - } - } + public boolean touch(int index, int own) { + if (index < 0 || m_stringOwns == null || index >= m_stringOwns.length) { + return false; + } + if (m_stringOwns[index] == -1) { + m_stringOwns[index] = own; + return true; + } else if (m_stringOwns[index] == own) { + return true; + } else { + return false; + } + } - private int[] m_stringOffsets; - private byte[] m_strings; - private int[] m_styleOffsets; - private int[] m_styles; - private boolean m_isUTF8; - private int[] m_stringOwns; - private static final CharsetDecoder UTF16LE_DECODER = - Charset.forName("UTF-16LE").newDecoder(); - private static final CharsetDecoder UTF8_DECODER = - Charset.forName("UTF-8").newDecoder(); - private static final Logger LOGGER = - Logger.getLogger(StringBlock.class.getName()); - private static final int CHUNK_TYPE = 0x001C0001; - private static final int UTF8_FLAG = 0x00000100; + private int[] m_stringOffsets; + private byte[] m_strings; + private int[] m_styleOffsets; + private int[] m_styles; + private boolean m_isUTF8; + private int[] m_stringOwns; + private static final CharsetDecoder UTF16LE_DECODER = Charset.forName( + "UTF-16LE").newDecoder(); + private static final CharsetDecoder UTF8_DECODER = Charset.forName("UTF-8") + .newDecoder(); + private static final Logger LOGGER = Logger.getLogger(StringBlock.class + .getName()); + private static final int CHUNK_TYPE = 0x001C0001; + private static final int UTF8_FLAG = 0x00000100; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java index 92c5a3bd..89643fc3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java @@ -36,122 +36,133 @@ import brut.androlib.res.util.ExtXmlSerializer; * @author Ryszard Wiśniewski */ public class XmlPullStreamDecoder implements ResStreamDecoder { - public XmlPullStreamDecoder(XmlPullParser parser, - ExtXmlSerializer serializer) { - this.mParser = parser; - this.mSerial = serializer; - } + public XmlPullStreamDecoder(XmlPullParser parser, + ExtXmlSerializer serializer) { + this.mParser = parser; + this.mSerial = serializer; + } - public void decode(InputStream in, OutputStream out) - throws AndrolibException { - try { - XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance(); - XmlPullParserWrapper par = factory.newPullParserWrapper(mParser); - final ResTable resTable = ((AXmlResourceParser)mParser).getAttrDecoder().getCurrentPackage().getResTable(); - - XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, factory){ - boolean hideSdkInfo = false; - boolean hidePackageInfo = false; - @Override - public void event(XmlPullParser pp) throws XmlPullParserException, IOException { - int type = pp.getEventType(); - - if (type == XmlPullParser.START_TAG) { - if ("manifest".equalsIgnoreCase(pp.getName())) { - try { - hidePackageInfo = parseManifest(pp); - } catch (AndrolibException e) {} - }else if ("uses-sdk".equalsIgnoreCase(pp.getName())) { - try { - hideSdkInfo = parseAttr(pp); - if(hideSdkInfo) { - return; - } - } catch (AndrolibException e) {} - } - } else if (hideSdkInfo && type == XmlPullParser.END_TAG && - "uses-sdk".equalsIgnoreCase(pp.getName())) { - return; - } else if (hidePackageInfo && type == XmlPullParser.END_TAG && - "manifest".equalsIgnoreCase(pp.getName())) { - super.event(pp); - return; - } - super.event(pp); - } - - private boolean parseManifest(XmlPullParser pp) throws AndrolibException { - ResTable restable = resTable; - - // read for package: - for (int i = 0; i < pp.getAttributeCount(); i++) { - if (pp.getAttributeName(i).equalsIgnoreCase(("package"))) { - restable.addPackageInfo("orig_package", pp.getAttributeValue(i)); - } - } - return true; - } - - private boolean parseAttr(XmlPullParser pp) throws AndrolibException { - ResTable restable = resTable; - for (int i = 0; i < pp.getAttributeCount(); i++) { - final String a_ns = "http://schemas.android.com/apk/res/android"; - String ns = pp.getAttributeNamespace (i); - - if (a_ns.equalsIgnoreCase(ns)) { - String name = pp.getAttributeName (i); - String value = pp.getAttributeValue (i); - if (name != null && value != null) { - if (name.equalsIgnoreCase("minSdkVersion") || - name.equalsIgnoreCase("targetSdkVersion") || - name.equalsIgnoreCase("maxSdkVersion")) { - restable.addSdkInfo(name, value); - } else { - restable.clearSdkInfo(); - return false;//Found unknown flags - } - } - } else { - resTable.clearSdkInfo(); - - if (i >= pp.getAttributeCount()) { - return false;//Found unknown flags - } - } - } - return true; - } - }; + @Override + public void decode(InputStream in, OutputStream out) + throws AndrolibException { + try { + XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance(); + XmlPullParserWrapper par = factory.newPullParserWrapper(mParser); + final ResTable resTable = ((AXmlResourceParser) mParser) + .getAttrDecoder().getCurrentPackage().getResTable(); - par.setInput(in, null); - ser.setOutput(out, null); + XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, + factory) { + boolean hideSdkInfo = false; + boolean hidePackageInfo = false; - while (par.nextToken() != XmlPullParser.END_DOCUMENT) { - ser.event(par); - } - ser.flush(); - } catch (XmlPullParserException ex) { - throw new AndrolibException("Could not decode XML", ex); - } catch (IOException ex) { - throw new AndrolibException("Could not decode XML", ex); - } - } + @Override + public void event(XmlPullParser pp) + throws XmlPullParserException, IOException { + int type = pp.getEventType(); - public void decodeManifest(InputStream in, OutputStream out) - throws AndrolibException { - mOptimizeForManifest = true; - try { - decode(in, out); - } finally { - mOptimizeForManifest = false; - } - } + if (type == XmlPullParser.START_TAG) { + if ("manifest".equalsIgnoreCase(pp.getName())) { + try { + hidePackageInfo = parseManifest(pp); + } catch (AndrolibException e) { + } + } else if ("uses-sdk".equalsIgnoreCase(pp.getName())) { + try { + hideSdkInfo = parseAttr(pp); + if (hideSdkInfo) { + return; + } + } catch (AndrolibException e) { + } + } + } else if (hideSdkInfo && type == XmlPullParser.END_TAG + && "uses-sdk".equalsIgnoreCase(pp.getName())) { + return; + } else if (hidePackageInfo && type == XmlPullParser.END_TAG + && "manifest".equalsIgnoreCase(pp.getName())) { + super.event(pp); + return; + } + super.event(pp); + } - private final XmlPullParser mParser; - private final ExtXmlSerializer mSerial; + private boolean parseManifest(XmlPullParser pp) + throws AndrolibException { + ResTable restable = resTable; - private boolean mOptimizeForManifest = false; + // read for package: + for (int i = 0; i < pp.getAttributeCount(); i++) { + if (pp.getAttributeName(i) + .equalsIgnoreCase(("package"))) { + restable.addPackageInfo("orig_package", + pp.getAttributeValue(i)); + } + } + return true; + } - private final static Logger LOGGER = - Logger.getLogger(XmlPullStreamDecoder.class.getName()); + private boolean parseAttr(XmlPullParser pp) + throws AndrolibException { + ResTable restable = resTable; + for (int i = 0; i < pp.getAttributeCount(); i++) { + final String a_ns = "http://schemas.android.com/apk/res/android"; + String ns = pp.getAttributeNamespace(i); + + if (a_ns.equalsIgnoreCase(ns)) { + String name = pp.getAttributeName(i); + String value = pp.getAttributeValue(i); + if (name != null && value != null) { + if (name.equalsIgnoreCase("minSdkVersion") + || name.equalsIgnoreCase("targetSdkVersion") + || name.equalsIgnoreCase("maxSdkVersion")) { + restable.addSdkInfo(name, value); + } else { + restable.clearSdkInfo(); + return false;// Found unknown flags + } + } + } else { + resTable.clearSdkInfo(); + + if (i >= pp.getAttributeCount()) { + return false;// Found unknown flags + } + } + } + return true; + } + }; + + par.setInput(in, null); + ser.setOutput(out, null); + + while (par.nextToken() != XmlPullParser.END_DOCUMENT) { + ser.event(par); + } + ser.flush(); + } catch (XmlPullParserException ex) { + throw new AndrolibException("Could not decode XML", ex); + } catch (IOException ex) { + throw new AndrolibException("Could not decode XML", ex); + } + } + + public void decodeManifest(InputStream in, OutputStream out) + throws AndrolibException { + mOptimizeForManifest = true; + try { + decode(in, out); + } finally { + mOptimizeForManifest = false; + } + } + + private final XmlPullParser mParser; + private final ExtXmlSerializer mSerial; + + private boolean mOptimizeForManifest = false; + + private final static Logger LOGGER = Logger + .getLogger(XmlPullStreamDecoder.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java index 1d042b10..0532c524 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java @@ -27,37 +27,36 @@ import java.net.URI; * @author Ryszard Wiśniewski */ public class ExtFile extends File { - public ExtFile(File file) { - super(file.getPath()); - } + public ExtFile(File file) { + super(file.getPath()); + } - public ExtFile(URI uri) { - super(uri); - } + public ExtFile(URI uri) { + super(uri); + } - public ExtFile(File parent, String child) { - super(parent, child); - } + public ExtFile(File parent, String child) { + super(parent, child); + } - public ExtFile(String parent, String child) { - super(parent, child); - } + public ExtFile(String parent, String child) { + super(parent, child); + } - public ExtFile(String pathname) { - super(pathname); - } + public ExtFile(String pathname) { + super(pathname); + } - public Directory getDirectory() throws DirectoryException { - if (mDirectory == null) { - if (isDirectory()) { - mDirectory = new FileDirectory(this); - } else { - mDirectory = new ZipRODirectory(this); - } - } - return mDirectory; - } + public Directory getDirectory() throws DirectoryException { + if (mDirectory == null) { + if (isDirectory()) { + mDirectory = new FileDirectory(this); + } else { + mDirectory = new ZipRODirectory(this); + } + } + return mDirectory; + } - - private Directory mDirectory; + private Directory mDirectory; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java index 4fd001da..b776919b 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java @@ -23,57 +23,59 @@ import org.xmlpull.mxp1_serializer.MXSerializer; * @author Ryszard Wiśniewski */ public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer { - @Override - public void startDocument(String encoding, Boolean standalone) throws - IOException, IllegalArgumentException, IllegalStateException { - super.startDocument(encoding != null ? encoding : mDefaultEncoding, - standalone); - this.newLine(); - } + @Override + public void startDocument(String encoding, Boolean standalone) + throws IOException, IllegalArgumentException, IllegalStateException { + super.startDocument(encoding != null ? encoding : mDefaultEncoding, + standalone); + this.newLine(); + } - @Override - protected void writeAttributeValue(String value, Writer out) - throws IOException { - if (mIsDisabledAttrEscape) { - out.write(value); - return; - } - super.writeAttributeValue(value, out); - } + @Override + protected void writeAttributeValue(String value, Writer out) + throws IOException { + if (mIsDisabledAttrEscape) { + out.write(value); + return; + } + super.writeAttributeValue(value, out); + } - @Override - public void setOutput(OutputStream os, String encoding) throws IOException { - super.setOutput(os, encoding != null ? encoding : mDefaultEncoding); - } + @Override + public void setOutput(OutputStream os, String encoding) throws IOException { + super.setOutput(os, encoding != null ? encoding : mDefaultEncoding); + } - @Override - public Object getProperty(String name) throws IllegalArgumentException { - if (PROPERTY_DEFAULT_ENCODING.equals(name)) { - return mDefaultEncoding; - } - return super.getProperty(name); - } + @Override + public Object getProperty(String name) throws IllegalArgumentException { + if (PROPERTY_DEFAULT_ENCODING.equals(name)) { + return mDefaultEncoding; + } + return super.getProperty(name); + } - @Override - public void setProperty(String name, Object value) - throws IllegalArgumentException, IllegalStateException { - if (PROPERTY_DEFAULT_ENCODING.equals(name)) { - mDefaultEncoding = (String) value; - } else { - super.setProperty(name, value); - } - } + @Override + public void setProperty(String name, Object value) + throws IllegalArgumentException, IllegalStateException { + if (PROPERTY_DEFAULT_ENCODING.equals(name)) { + mDefaultEncoding = (String) value; + } else { + super.setProperty(name, value); + } + } - public ExtXmlSerializer newLine() throws IOException { - super.out.write(lineSeparator); - return this; - } + @Override + public ExtXmlSerializer newLine() throws IOException { + super.out.write(lineSeparator); + return this; + } - public void setDisabledAttrEscape(boolean disabled) { - mIsDisabledAttrEscape = disabled; - } + @Override + public void setDisabledAttrEscape(boolean disabled) { + mIsDisabledAttrEscape = disabled; + } + + private String mDefaultEncoding; + private boolean mIsDisabledAttrEscape = false; - private String mDefaultEncoding; - private boolean mIsDisabledAttrEscape = false; - } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java index 3c8ffed6..0fdeb9b5 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java @@ -24,12 +24,11 @@ import org.xmlpull.v1.XmlSerializer; */ public interface ExtXmlSerializer extends XmlSerializer { - public ExtXmlSerializer newLine() throws IOException; - public void setDisabledAttrEscape(boolean disabled); + public ExtXmlSerializer newLine() throws IOException; - public static final String PROPERTY_SERIALIZER_INDENTATION = - "http://xmlpull.org/v1/doc/properties.html#serializer-indentation"; - public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = - "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator"; - public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING"; + public void setDisabledAttrEscape(boolean disabled); + + public static final String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation"; + public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator"; + public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING"; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java index 00d6a0ca..39764c20 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java @@ -25,6 +25,6 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public interface ResValuesXmlSerializable { - public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) - throws IOException, AndrolibException; + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java index 86d40a50..6d26fe22 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java @@ -22,6 +22,7 @@ import brut.androlib.AndrolibException; * @author Ryszard Wiśniewski */ public interface ResXmlEncodable { - public String encodeAsResXmlAttr() throws AndrolibException; - public String encodeAsResXmlValue() throws AndrolibException; + public String encodeAsResXmlAttr() throws AndrolibException; + + public String encodeAsResXmlValue() throws AndrolibException; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java index 46226e39..ea46364f 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java @@ -25,178 +25,176 @@ import java.util.List; */ public final class ResXmlEncoders { - public static String escapeXmlChars(String str) { - return str.replace("&", "&").replace("<", "<"); - } + public static String escapeXmlChars(String str) { + return str.replace("&", "&").replace("<", "<"); + } - public static String encodeAsResXmlAttr(String str) { - if (str.isEmpty()) { - return str; - } + public static String encodeAsResXmlAttr(String str) { + if (str.isEmpty()) { + return str; + } - char[] chars = str.toCharArray(); - StringBuilder out = new StringBuilder(str.length() + 10); + char[] chars = str.toCharArray(); + StringBuilder out = new StringBuilder(str.length() + 10); - switch (chars[0]) { - case '#': - case '@': - case '?': - out.append('\\'); - } + switch (chars[0]) { + case '#': + case '@': + case '?': + out.append('\\'); + } - for (char c : chars) { - switch (c) { - case '\\': - out.append('\\'); - break; - case '"': - out.append("""); - continue; - case '\n': - out.append("\\n"); - continue; - default: - if (!isPrintableChar(c)) { - out.append(String.format("\\u%04x", (int) c)); - continue; - } - } - out.append(c); - } + for (char c : chars) { + switch (c) { + case '\\': + out.append('\\'); + break; + case '"': + out.append("""); + continue; + case '\n': + out.append("\\n"); + continue; + default: + if (!isPrintableChar(c)) { + out.append(String.format("\\u%04x", (int) c)); + continue; + } + } + out.append(c); + } - return out.toString(); - } + return out.toString(); + } - public static String encodeAsXmlValue(String str) { - if (str.isEmpty()) { - return str; - } + public static String encodeAsXmlValue(String str) { + if (str.isEmpty()) { + return str; + } - char[] chars = str.toCharArray(); - StringBuilder out = new StringBuilder(str.length() + 10); + char[] chars = str.toCharArray(); + StringBuilder out = new StringBuilder(str.length() + 10); - switch (chars[0]) { - case '#': - case '@': - case '?': - out.append('\\'); - } + switch (chars[0]) { + case '#': + case '@': + case '?': + out.append('\\'); + } - boolean isInStyleTag = false; - int startPos = 0; - boolean enclose = false; - boolean wasSpace = true; - for (char c : chars) { - if (isInStyleTag) { - if (c == '>') { - isInStyleTag = false; - startPos = out.length() + 1; - enclose = false; - } - } else if (c == ' ') { - if (wasSpace) { - enclose = true; - } - wasSpace = true; - } else { - wasSpace = false; - switch (c) { - case '\\': - out.append('\\'); - break; - case '\'': - case '\n': - enclose = true; - break; - case '"': - out.append('\\'); - break; - case '<': - isInStyleTag = true; - if (enclose) { - out.insert(startPos, '"').append('"'); - } - break; - default: - if (!isPrintableChar(c)) { - out.append(String.format("\\u%04x", (int) c)); - continue; - } - } - } - out.append(c); - } + boolean isInStyleTag = false; + int startPos = 0; + boolean enclose = false; + boolean wasSpace = true; + for (char c : chars) { + if (isInStyleTag) { + if (c == '>') { + isInStyleTag = false; + startPos = out.length() + 1; + enclose = false; + } + } else if (c == ' ') { + if (wasSpace) { + enclose = true; + } + wasSpace = true; + } else { + wasSpace = false; + switch (c) { + case '\\': + out.append('\\'); + break; + case '\'': + case '\n': + enclose = true; + break; + case '"': + out.append('\\'); + break; + case '<': + isInStyleTag = true; + if (enclose) { + out.insert(startPos, '"').append('"'); + } + break; + default: + if (!isPrintableChar(c)) { + out.append(String.format("\\u%04x", (int) c)); + continue; + } + } + } + out.append(c); + } - if (enclose || wasSpace) { - out.insert(startPos, '"').append('"'); - } + if (enclose || wasSpace) { + out.insert(startPos, '"').append('"'); + } - return out.toString(); - } + return out.toString(); + } - public static boolean hasMultipleNonPositionalSubstitutions(String str) { - return findNonPositionalSubstitutions(str, 2).size() > 1; - } + public static boolean hasMultipleNonPositionalSubstitutions(String str) { + return findNonPositionalSubstitutions(str, 2).size() > 1; + } - public static String enumerateNonPositionalSubstitutions(String str) { - List subs = findNonPositionalSubstitutions(str, -1); - if (subs.size() < 2) { - return str; - } + public static String enumerateNonPositionalSubstitutions(String str) { + List subs = findNonPositionalSubstitutions(str, -1); + if (subs.size() < 2) { + return str; + } - StringBuilder out = new StringBuilder(); - int pos = 0; - int count = 0; - for (Integer sub : subs) { - out.append(str.substring(pos, ++sub)).append(++count).append('$'); - pos = sub; - } - out.append(str.substring(pos)); + StringBuilder out = new StringBuilder(); + int pos = 0; + int count = 0; + for (Integer sub : subs) { + out.append(str.substring(pos, ++sub)).append(++count).append('$'); + pos = sub; + } + out.append(str.substring(pos)); - return out.toString(); - } + return out.toString(); + } - /** - * It searches for "%", but not "%%" nor "%(\d)+\$" - */ - private static List findNonPositionalSubstitutions(String str, - int max) { - int pos = 0; - int pos2 = 0; - int count = 0; - int length = str.length(); - List ret = new ArrayList(); - while((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) { - if (pos2 == length) { - break; - } - char c = str.charAt(pos2++); - if (c == '%') { - continue; - } - if (c >= '0' && c <= '9' && pos2 < length) { - do { - c = str.charAt(pos2++); - } while (c >= '0' && c <= '9' && pos2 < length); - if (c == '$') { - continue; - } - } + /** + * It searches for "%", but not "%%" nor "%(\d)+\$" + */ + private static List findNonPositionalSubstitutions(String str, + int max) { + int pos = 0; + int pos2 = 0; + int count = 0; + int length = str.length(); + List ret = new ArrayList(); + while ((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) { + if (pos2 == length) { + break; + } + char c = str.charAt(pos2++); + if (c == '%') { + continue; + } + if (c >= '0' && c <= '9' && pos2 < length) { + do { + c = str.charAt(pos2++); + } while (c >= '0' && c <= '9' && pos2 < length); + if (c == '$') { + continue; + } + } - ret.add(pos); - if (max != -1 && ++count >= max) { - break; - } - } + ret.add(pos); + if (max != -1 && ++count >= max) { + break; + } + } - return ret; - } + return ret; + } - private static boolean isPrintableChar(char c) { - Character.UnicodeBlock block = Character.UnicodeBlock.of(c); - return !Character.isISOControl(c) - && c != KeyEvent.CHAR_UNDEFINED - && block != null - && block != Character.UnicodeBlock.SPECIALS; - } + private static boolean isPrintableChar(char c) { + Character.UnicodeBlock block = Character.UnicodeBlock.of(c); + return !Character.isISOControl(c) && c != KeyEvent.CHAR_UNDEFINED + && block != null && block != Character.UnicodeBlock.SPECIALS; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java index 86c89366..75fe7284 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java @@ -21,207 +21,197 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jf.dexlib.Code.Analysis.RegisterType; -import org.jf.dexlib.Code.Opcode; /** * @author Ryszard Wiśniewski */ public class DebugInjector { - public static void inject(ListIterator it, StringBuilder out) - throws AndrolibException { - new DebugInjector(it, out).inject(); - } + public static void inject(ListIterator it, StringBuilder out) + throws AndrolibException { + new DebugInjector(it, out).inject(); + } - private DebugInjector(ListIterator it, StringBuilder out) { - mIt = it; - mOut = out; - } + private DebugInjector(ListIterator it, StringBuilder out) { + mIt = it; + mOut = out; + } - private void inject() throws AndrolibException { - String definition = nextAndAppend(); - if ( - definition.contains(" abstract ") || - definition.contains(" native ") - ) { - nextAndAppend(); - return; - } - injectParameters(definition); + private void inject() throws AndrolibException { + String definition = nextAndAppend(); + if (definition.contains(" abstract ") + || definition.contains(" native ")) { + nextAndAppend(); + return; + } + injectParameters(definition); - boolean end = false; - while (!end) { - end = step(); - } - } + boolean end = false; + while (!end) { + end = step(); + } + } - private void injectParameters(String definition) throws AndrolibException { - int pos = definition.indexOf('('); - if (pos == -1) { - throw new AndrolibException(); - } - int pos2 = definition.indexOf(')', pos); - if (pos2 == -1) { - throw new AndrolibException(); - } - String params = definition.substring(pos + 1, pos2); + private void injectParameters(String definition) throws AndrolibException { + int pos = definition.indexOf('('); + if (pos == -1) { + throw new AndrolibException(); + } + int pos2 = definition.indexOf(')', pos); + if (pos2 == -1) { + throw new AndrolibException(); + } + String params = definition.substring(pos + 1, pos2); - int i = definition.contains(" static ") ? 0 : 1; - int argc = TypeName.listFromInternalName(params).size() + i; - while(i < argc) { - mOut.append(".parameter \"p").append(i).append("\"\n"); - i++; - } - } + int i = definition.contains(" static ") ? 0 : 1; + int argc = TypeName.listFromInternalName(params).size() + i; + while (i < argc) { + mOut.append(".parameter \"p").append(i).append("\"\n"); + i++; + } + } - private boolean step() { - String line = next(); - if (line.isEmpty()) { - return false; - } + private boolean step() { + String line = next(); + if (line.isEmpty()) { + return false; + } - switch (line.charAt(0)) { - case '#': - return processComment(line); - case ':': - append(line); - return false; - case '.': - return processDirective(line); - default: - return processInstruction(line); - } - } + switch (line.charAt(0)) { + case '#': + return processComment(line); + case ':': + append(line); + return false; + case '.': + return processDirective(line); + default: + return processInstruction(line); + } + } - private boolean processComment(String line) { - if (mFirstInstruction) { - return false; - } + private boolean processComment(String line) { + if (mFirstInstruction) { + return false; + } - Matcher m = REGISTER_INFO_PATTERN.matcher(line); + Matcher m = REGISTER_INFO_PATTERN.matcher(line); - while (m.find()) { - String localName = m.group(1); - String localType = null; - switch (RegisterType.Category.valueOf(m.group(2))) { - case Reference: - case Null: - case UninitRef: - case UninitThis: - localType = "Ljava/lang/Object;"; - break; - case Boolean: - localType = "Z"; - break; - case Integer: - case One: - case Unknown: - localType = "I"; - break; - case Uninit: - case Conflicted: - if (mInitializedRegisters.remove(localName)) { - mOut.append(".end local ").append(localName) - .append('\n'); - } - continue; - case Short: - case PosShort: - localType = "S"; - break; - case Byte: - case PosByte: - localType = "B"; - break; - case Char: - localType = "C"; - break; - case Float: - localType = "F"; - break; - case LongHi: - case LongLo: - localType = "J"; - break; - case DoubleHi: - case DoubleLo: - localType = "D"; - break; - default: - assert false; - } + while (m.find()) { + String localName = m.group(1); + String localType = null; + switch (RegisterType.Category.valueOf(m.group(2))) { + case Reference: + case Null: + case UninitRef: + case UninitThis: + localType = "Ljava/lang/Object;"; + break; + case Boolean: + localType = "Z"; + break; + case Integer: + case One: + case Unknown: + localType = "I"; + break; + case Uninit: + case Conflicted: + if (mInitializedRegisters.remove(localName)) { + mOut.append(".end local ").append(localName).append('\n'); + } + continue; + case Short: + case PosShort: + localType = "S"; + break; + case Byte: + case PosByte: + localType = "B"; + break; + case Char: + localType = "C"; + break; + case Float: + localType = "F"; + break; + case LongHi: + case LongLo: + localType = "J"; + break; + case DoubleHi: + case DoubleLo: + localType = "D"; + break; + default: + assert false; + } - mInitializedRegisters.add(localName); - mOut.append(".local ").append(localName).append(", ") - .append(localName).append(':').append(localType).append('\n'); - } + mInitializedRegisters.add(localName); + mOut.append(".local ").append(localName).append(", ") + .append(localName).append(':').append(localType) + .append('\n'); + } - return false; - } + return false; + } - private boolean processDirective(String line) { - String line2 = line.substring(1); - if ( - line2.startsWith("line ") || - line2.equals("prologue") || - line2.startsWith("parameter") || - line2.startsWith("local ") || - line2.startsWith("end local ") - ) { - return false; - } + private boolean processDirective(String line) { + String line2 = line.substring(1); + if (line2.startsWith("line ") || line2.equals("prologue") + || line2.startsWith("parameter") || line2.startsWith("local ") + || line2.startsWith("end local ")) { + return false; + } - append(line); - if (line2.equals("end method")) { - return true; - } - if ( - line2.startsWith("annotation ") || - line2.equals("sparse-switch") || - line2.startsWith("packed-switch ") || - line2.startsWith("array-data ") - ) { - while(true) { - line2 = nextAndAppend(); - if (line2.startsWith(".end ")) { - break; - } - } - } - return false; - } + append(line); + if (line2.equals("end method")) { + return true; + } + if (line2.startsWith("annotation ") || line2.equals("sparse-switch") + || line2.startsWith("packed-switch ") + || line2.startsWith("array-data ")) { + while (true) { + line2 = nextAndAppend(); + if (line2.startsWith(".end ")) { + break; + } + } + } + return false; + } - private boolean processInstruction(String line) { - if (mFirstInstruction) { - mOut.append(".prologue\n"); - mFirstInstruction = false; - } - mOut.append(".line ").append(mIt.nextIndex()).append('\n') - .append(line).append('\n'); + private boolean processInstruction(String line) { + if (mFirstInstruction) { + mOut.append(".prologue\n"); + mFirstInstruction = false; + } + mOut.append(".line ").append(mIt.nextIndex()).append('\n').append(line) + .append('\n'); - return false; - } + return false; + } - private String next() { - return mIt.next().trim(); - } + private String next() { + return mIt.next().trim(); + } - private String nextAndAppend() { - String line = next(); - append(line); - return line; - } + private String nextAndAppend() { + String line = next(); + append(line); + return line; + } - private void append(String append) { - mOut.append(append).append('\n'); - } + private void append(String append) { + mOut.append(append).append('\n'); + } - private final ListIterator mIt; - private final StringBuilder mOut; + private final ListIterator mIt; + private final StringBuilder mOut; - private boolean mFirstInstruction = true; - private final Set mInitializedRegisters = new HashSet(); + private boolean mFirstInstruction = true; + private final Set mInitializedRegisters = new HashSet(); - private static final Pattern REGISTER_INFO_PATTERN = - Pattern.compile("((?:p|v)\\d+)=\\(([^)]+)\\);"); + private static final Pattern REGISTER_INFO_PATTERN = Pattern + .compile("((?:p|v)\\d+)=\\(([^)]+)\\);"); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DexFileBuilder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DexFileBuilder.java index 8a0904b5..6059db64 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DexFileBuilder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DexFileBuilder.java @@ -28,58 +28,57 @@ import org.jf.dexlib.Util.ByteArrayAnnotatedOutput; * @author Ryszard Wiśniewski */ public class DexFileBuilder { - public void addSmaliFile(File smaliFile) throws AndrolibException { - try { - addSmaliFile(new FileInputStream(smaliFile), - smaliFile.getAbsolutePath()); - } catch (FileNotFoundException ex) { - throw new AndrolibException(ex); - } - } + public void addSmaliFile(File smaliFile) throws AndrolibException { + try { + addSmaliFile(new FileInputStream(smaliFile), + smaliFile.getAbsolutePath()); + } catch (FileNotFoundException ex) { + throw new AndrolibException(ex); + } + } - public void addSmaliFile(InputStream smaliStream, String name) - throws AndrolibException { - try { - if (! SmaliMod.assembleSmaliFile( - smaliStream, name, mDexFile, false, false, false)) { - throw new AndrolibException( - "Could not smali file: " + name); - } - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (RecognitionException ex) { - throw new AndrolibException(ex); - } - } + public void addSmaliFile(InputStream smaliStream, String name) + throws AndrolibException { + try { + if (!SmaliMod.assembleSmaliFile(smaliStream, name, mDexFile, false, + false, false)) { + throw new AndrolibException("Could not smali file: " + name); + } + } catch (IOException ex) { + throw new AndrolibException(ex); + } catch (RecognitionException ex) { + throw new AndrolibException(ex); + } + } - public void writeTo(File dexFile) throws AndrolibException { - try { - OutputStream out = new FileOutputStream(dexFile); - out.write(getAsByteArray()); - out.close(); - } catch (IOException ex) { - throw new AndrolibException( - "Could not write dex to file: " + dexFile, ex); - } - } + public void writeTo(File dexFile) throws AndrolibException { + try { + OutputStream out = new FileOutputStream(dexFile); + out.write(getAsByteArray()); + out.close(); + } catch (IOException ex) { + throw new AndrolibException("Could not write dex to file: " + + dexFile, ex); + } + } - public byte[] getAsByteArray() { - mDexFile.place(); - for (CodeItem codeItem: mDexFile.CodeItemsSection.getItems()) { - codeItem.fixInstructions(true, true); - } + public byte[] getAsByteArray() { + mDexFile.place(); + for (CodeItem codeItem : mDexFile.CodeItemsSection.getItems()) { + codeItem.fixInstructions(true, true); + } - mDexFile.place(); + mDexFile.place(); - ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); - mDexFile.writeTo(out); - byte[] bytes = out.toByteArray(); + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); + mDexFile.writeTo(out); + byte[] bytes = out.toByteArray(); - DexFile.calcSignature(bytes); - DexFile.calcChecksum(bytes); + DexFile.calcSignature(bytes); + DexFile.calcChecksum(bytes); - return bytes; - } + return bytes; + } - private final DexFile mDexFile = new DexFile(); + private final DexFile mDexFile = new DexFile(); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java index 8fd32808..16992c29 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java @@ -31,86 +31,85 @@ import org.apache.commons.io.IOUtils; */ public class SmaliBuilder { - public static void build(ExtFile smaliDir, File dexFile, - HashMap flags) - throws AndrolibException { - new SmaliBuilder(smaliDir, dexFile, flags).build(); - } + public static void build(ExtFile smaliDir, File dexFile, + HashMap flags) throws AndrolibException { + new SmaliBuilder(smaliDir, dexFile, flags).build(); + } - private SmaliBuilder(ExtFile smaliDir, File dexFile, HashMap flags) { - mSmaliDir = smaliDir; - mDexFile = dexFile; - mFlags = flags; - } + private SmaliBuilder(ExtFile smaliDir, File dexFile, + HashMap flags) { + mSmaliDir = smaliDir; + mDexFile = dexFile; + mFlags = flags; + } - private void build() throws AndrolibException { - try { - mDexBuilder = new DexFileBuilder(); - for (String fileName : mSmaliDir.getDirectory().getFiles(true)) { - buildFile(fileName); - } - mDexBuilder.writeTo(mDexFile); - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + private void build() throws AndrolibException { + try { + mDexBuilder = new DexFileBuilder(); + for (String fileName : mSmaliDir.getDirectory().getFiles(true)) { + buildFile(fileName); + } + mDexBuilder.writeTo(mDexFile); + } catch (IOException ex) { + throw new AndrolibException(ex); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - private void buildFile(String fileName) throws AndrolibException, - IOException { - File inFile = new File(mSmaliDir, fileName); - InputStream inStream = new FileInputStream(inFile); + private void buildFile(String fileName) throws AndrolibException, + IOException { + File inFile = new File(mSmaliDir, fileName); + InputStream inStream = new FileInputStream(inFile); - if (fileName.endsWith(".smali")) { - mDexBuilder.addSmaliFile(inFile); - return; - } - if (! fileName.endsWith(".java")) { - LOGGER.warning("Unknown file type, ignoring: " + inFile); - return; - } + if (fileName.endsWith(".smali")) { + mDexBuilder.addSmaliFile(inFile); + return; + } + if (!fileName.endsWith(".java")) { + LOGGER.warning("Unknown file type, ignoring: " + inFile); + return; + } - StringBuilder out = new StringBuilder(); - List lines = IOUtils.readLines(inStream); + StringBuilder out = new StringBuilder(); + List lines = IOUtils.readLines(inStream); - if (!mFlags.containsKey("debug")) { - final String[] linesArray = lines.toArray(new String[0]); - for (int i = 2; i < linesArray.length - 2; i++) { - out.append(linesArray[i]).append('\n'); - } - } else { - lines.remove(lines.size() - 1); - lines.remove(lines.size() - 1); - ListIterator it = lines.listIterator(2); + if (!mFlags.containsKey("debug")) { + final String[] linesArray = lines.toArray(new String[0]); + for (int i = 2; i < linesArray.length - 2; i++) { + out.append(linesArray[i]).append('\n'); + } + } else { + lines.remove(lines.size() - 1); + lines.remove(lines.size() - 1); + ListIterator it = lines.listIterator(2); - out.append(".source \"").append(inFile.getName()).append("\"\n"); - while (it.hasNext()) { - String line = it.next().trim(); - if (line.isEmpty() || line.charAt(0) == '#' || - line.startsWith(".source")) { - continue; - } - if (line.startsWith(".method ")) { - it.previous(); - DebugInjector.inject(it, out); - continue; - } + out.append(".source \"").append(inFile.getName()).append("\"\n"); + while (it.hasNext()) { + String line = it.next().trim(); + if (line.isEmpty() || line.charAt(0) == '#' + || line.startsWith(".source")) { + continue; + } + if (line.startsWith(".method ")) { + it.previous(); + DebugInjector.inject(it, out); + continue; + } - out.append(line).append('\n'); - } - } - mDexBuilder.addSmaliFile( - IOUtils.toInputStream(out.toString()), fileName); - } + out.append(line).append('\n'); + } + } + mDexBuilder.addSmaliFile(IOUtils.toInputStream(out.toString()), + fileName); + } - private final ExtFile mSmaliDir; - private final File mDexFile; - private final HashMap mFlags; + private final ExtFile mSmaliDir; + private final File mDexFile; + private final HashMap mFlags; - private DexFileBuilder mDexBuilder; + private DexFileBuilder mDexBuilder; - - private final static Logger LOGGER = - Logger.getLogger(SmaliBuilder.class.getName()); + private final static Logger LOGGER = Logger.getLogger(SmaliBuilder.class + .getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java index 98ed63e9..ee882a6d 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java @@ -29,34 +29,35 @@ import org.jf.dexlib.DexFile; */ public class SmaliDecoder { - public static void decode(File apkFile, File outDir, boolean debug, boolean bakdeb) - throws AndrolibException { - new SmaliDecoder(apkFile, outDir, debug, bakdeb).decode(); - } + public static void decode(File apkFile, File outDir, boolean debug, + boolean bakdeb) throws AndrolibException { + new SmaliDecoder(apkFile, outDir, debug, bakdeb).decode(); + } - private SmaliDecoder(File apkFile, File outDir, boolean debug, boolean bakdeb) { - mApkFile = apkFile; - mOutDir = outDir; - mDebug = debug; - mBakDeb = bakdeb; - } + private SmaliDecoder(File apkFile, File outDir, boolean debug, + boolean bakdeb) { + mApkFile = apkFile; + mOutDir = outDir; + mDebug = debug; + mBakDeb = bakdeb; + } - private void decode() throws AndrolibException { - if (mDebug) { - ClassPath.dontLoadClassPath = true; - } - try { - baksmali.disassembleDexFile(mApkFile.getAbsolutePath(), - new DexFile(mApkFile), false, mOutDir.getAbsolutePath(), null, - null, null, false, true, true, mBakDeb, false, false, - mDebug ? main.DIFFPRE: 0, false, false, null, false); - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } + private void decode() throws AndrolibException { + if (mDebug) { + ClassPath.dontLoadClassPath = true; + } + try { + baksmali.disassembleDexFile(mApkFile.getAbsolutePath(), + new DexFile(mApkFile), false, mOutDir.getAbsolutePath(), + null, null, null, false, true, true, mBakDeb, false, false, + mDebug ? main.DIFFPRE : 0, false, false, null, false); + } catch (IOException ex) { + throw new AndrolibException(ex); + } + } - private final File mApkFile; - private final File mOutDir; - private final boolean mDebug; - private final boolean mBakDeb; + private final File mApkFile; + private final File mOutDir; + private final boolean mDebug; + private final boolean mBakDeb; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/TypeName.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/TypeName.java index ffc7bb1a..f790d03a 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/TypeName.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/TypeName.java @@ -26,184 +26,182 @@ import java.util.List; * @author Ryszard Wiśniewski */ public class TypeName { - public final String package_; - public final String type; - public final String innerType; - public final int array; + public final String package_; + public final String type; + public final String innerType; + public final int array; - public TypeName(String type, int array) { - this(null, type, null, array); - } + public TypeName(String type, int array) { + this(null, type, null, array); + } - public TypeName(String package_, String type, String innerType, int array) { - this.package_ = package_; - this.type = type; - this.innerType = innerType; - this.array = array; - } + public TypeName(String package_, String type, String innerType, int array) { + this.package_ = package_; + this.type = type; + this.innerType = innerType; + this.array = array; + } - public String getShortenedName() { - return getName("java.lang".equals(package_), isFileOwner()); - } + public String getShortenedName() { + return getName("java.lang".equals(package_), isFileOwner()); + } - public String getName() { - return getName(false, false); - } + public String getName() { + return getName(false, false); + } - public String getName(boolean excludePackage, boolean separateInner) { - String name = - (package_ == null || excludePackage ? "" : package_ + '.') + - type + - (innerType != null ? (separateInner ? '$' : '.') + innerType : ""); - for (int i = 0; i < array; i++) { - name += "[]"; - } - return name; - } + public String getName(boolean excludePackage, boolean separateInner) { + String name = (package_ == null || excludePackage ? "" : package_ + '.') + + type + + (innerType != null ? (separateInner ? '$' : '.') + innerType + : ""); + for (int i = 0; i < array; i++) { + name += "[]"; + } + return name; + } - public String getJavaFilePath() { - return getFilePath(isFileOwner()) + ".java"; - } + public String getJavaFilePath() { + return getFilePath(isFileOwner()) + ".java"; + } - public String getSmaliFilePath() { - return getFilePath(true) + ".smali"; - } + public String getSmaliFilePath() { + return getFilePath(true) + ".smali"; + } - public String getFilePath(boolean separateInner) { - return package_.replace('.', File.separatorChar) + File.separatorChar - + type + (separateInner && isInner() ? "$" + innerType : ""); - } + public String getFilePath(boolean separateInner) { + return package_.replace('.', File.separatorChar) + File.separatorChar + + type + (separateInner && isInner() ? "$" + innerType : ""); + } - public boolean isInner() { - return innerType != null; - } + public boolean isInner() { + return innerType != null; + } - public boolean isArray() { - return array != 0; - } + public boolean isArray() { + return array != 0; + } - public boolean isFileOwner() { - if (mIsFileOwner == null) { - mIsFileOwner = true; - if (isInner()) { - char c = innerType.charAt(0); - if (c < '0' || c > '9') { - mIsFileOwner = false; - } - } - } - return mIsFileOwner; - } + public boolean isFileOwner() { + if (mIsFileOwner == null) { + mIsFileOwner = true; + if (isInner()) { + char c = innerType.charAt(0); + if (c < '0' || c > '9') { + mIsFileOwner = false; + } + } + } + return mIsFileOwner; + } - @Override - public String toString() { - return getName(); - } + @Override + public String toString() { + return getName(); + } - public static TypeName fromInternalName(String internal) - throws AndrolibException { - Duo duo = fetchFromInternalName(internal); - if (duo.m2 != internal.length()) { - throw new AndrolibException( - "Invalid internal name: " + internal); - } - return duo.m1; - } + public static TypeName fromInternalName(String internal) + throws AndrolibException { + Duo duo = fetchFromInternalName(internal); + if (duo.m2 != internal.length()) { + throw new AndrolibException("Invalid internal name: " + internal); + } + return duo.m1; + } - public static List listFromInternalName(String internal) - throws AndrolibException { - List types = new ArrayList(); - while (! internal.isEmpty()) { - Duo duo = fetchFromInternalName(internal); - types.add(duo.m1); - internal = internal.substring(duo.m2); - } - return types; - } + public static List listFromInternalName(String internal) + throws AndrolibException { + List types = new ArrayList(); + while (!internal.isEmpty()) { + Duo duo = fetchFromInternalName(internal); + types.add(duo.m1); + internal = internal.substring(duo.m2); + } + return types; + } - public static Duo fetchFromInternalName(String internal) - throws AndrolibException { - String origInternal = internal; - int array = 0; + public static Duo fetchFromInternalName(String internal) + throws AndrolibException { + String origInternal = internal; + int array = 0; - boolean isArray = false; - do { - if (internal.isEmpty()) { - throw new AndrolibException( - "Invalid internal name: " + origInternal); - } - isArray = internal.charAt(0) == '['; - if (isArray) { - array++; - internal = internal.substring(1); - } - } while (isArray); + boolean isArray = false; + do { + if (internal.isEmpty()) { + throw new AndrolibException("Invalid internal name: " + + origInternal); + } + isArray = internal.charAt(0) == '['; + if (isArray) { + array++; + internal = internal.substring(1); + } + } while (isArray); - int length = array + 1; - String package_ = null; - String type = null; - String innerType = null; - switch (internal.charAt(0)) { - case 'B': - type = "byte"; - break; - case 'C': - type = "char"; - break; - case 'D': - type = "double"; - break; - case 'F': - type = "float"; - break; - case 'I': - type = "int"; - break; - case 'J': - type = "long"; - break; - case 'S': - type = "short"; - break; - case 'Z': - type = "boolean"; - break; - case 'V': - type = "void"; - break; - case 'L': - int pos = internal.indexOf(';'); - if (pos == -1) { - throw new AndrolibException( - "Invalid internal name: " + origInternal); - } - length += pos; - internal = internal.substring(1, pos); + int length = array + 1; + String package_ = null; + String type = null; + String innerType = null; + switch (internal.charAt(0)) { + case 'B': + type = "byte"; + break; + case 'C': + type = "char"; + break; + case 'D': + type = "double"; + break; + case 'F': + type = "float"; + break; + case 'I': + type = "int"; + break; + case 'J': + type = "long"; + break; + case 'S': + type = "short"; + break; + case 'Z': + type = "boolean"; + break; + case 'V': + type = "void"; + break; + case 'L': + int pos = internal.indexOf(';'); + if (pos == -1) { + throw new AndrolibException("Invalid internal name: " + + origInternal); + } + length += pos; + internal = internal.substring(1, pos); - pos = internal.lastIndexOf('/'); - if (pos == -1) { - package_ = ""; - type = internal; - } else { - package_ = internal.substring(0, pos).replace('/', '.'); - type = internal.substring(pos + 1); - } + pos = internal.lastIndexOf('/'); + if (pos == -1) { + package_ = ""; + type = internal; + } else { + package_ = internal.substring(0, pos).replace('/', '.'); + type = internal.substring(pos + 1); + } - pos = type.indexOf('$'); - if (pos != -1) { - innerType = type.substring(pos + 1); - type = type.substring(0, pos); - } - break; - default: - throw new AndrolibException( - "Invalid internal name: " + origInternal); - } + pos = type.indexOf('$'); + if (pos != -1) { + innerType = type.substring(pos + 1); + type = type.substring(0, pos); + } + break; + default: + throw new AndrolibException("Invalid internal name: " + + origInternal); + } - return new Duo( - new TypeName(package_, type, innerType, array), length); - } + return new Duo(new TypeName(package_, type, + innerType, array), length); + } - - private Boolean mIsFileOwner; + private Boolean mIsFileOwner; } diff --git a/brut.apktool/apktool-lib/src/main/java/com/mindprod/ledatastream/LEDataInputStream.java b/brut.apktool/apktool-lib/src/main/java/com/mindprod/ledatastream/LEDataInputStream.java index 83656341..b650e4b3 100644 --- a/brut.apktool/apktool-lib/src/main/java/com/mindprod/ledatastream/LEDataInputStream.java +++ b/brut.apktool/apktool-lib/src/main/java/com/mindprod/ledatastream/LEDataInputStream.java @@ -25,295 +25,306 @@ import java.io.InputStream; /** * Little-Endian version of DataInputStream. *

- * Very similar to DataInputStream except it reads - * little-endian instead of big-endian binary data. We can't extend - * DataInputStream directly since it has only final methods, though - * DataInputStream itself is not final. This forces us implement - * LEDataInputStream with a DataInputStream object, and use wrapper methods. - * + * Very similar to DataInputStream except it reads little-endian instead of + * big-endian binary data. We can't extend DataInputStream directly since it has + * only final methods, though DataInputStream itself is not final. This forces + * us implement LEDataInputStream with a DataInputStream object, and use wrapper + * methods. + * * @author Roedy Green, Canadian Mind Products * @version 1.8 2007-05-24 * @since 1998 */ -public final class LEDataInputStream implements DataInput - { - // ------------------------------ CONSTANTS ------------------------------ +public final class LEDataInputStream implements DataInput { + // ------------------------------ CONSTANTS ------------------------------ - /** - * undisplayed copyright notice. - * - * @noinspection UnusedDeclaration - */ - private static final String EMBEDDED_COPYRIGHT = - "copyright (c) 1999-2010 Roedy Green, Canadian Mind Products, http://mindprod.com"; + /** + * undisplayed copyright notice. + * + * @noinspection UnusedDeclaration + */ + private static final String EMBEDDED_COPYRIGHT = "copyright (c) 1999-2010 Roedy Green, Canadian Mind Products, http://mindprod.com"; - // ------------------------------ FIELDS ------------------------------ + // ------------------------------ FIELDS ------------------------------ - /** - * to get at the big-Endian methods of a basic DataInputStream - * - * @noinspection WeakerAccess - */ - protected final DataInputStream dis; + /** + * to get at the big-Endian methods of a basic DataInputStream + * + * @noinspection WeakerAccess + */ + protected final DataInputStream dis; - /** - * to get at the a basic readBytes method. - * - * @noinspection WeakerAccess - */ - protected final InputStream is; + /** + * to get at the a basic readBytes method. + * + * @noinspection WeakerAccess + */ + protected final InputStream is; - /** - * work array for buffering input. - * - * @noinspection WeakerAccess - */ - protected final byte[] work; - // -------------------------- PUBLIC STATIC METHODS -------------------------- + /** + * work array for buffering input. + * + * @noinspection WeakerAccess + */ + protected final byte[] work; - /** - * Note. This is a STATIC method! - * - * @param in stream to read UTF chars from (endian irrelevant) - * - * @return string from stream - * @throws IOException if read fails. - */ - public static String readUTF( DataInput in ) throws IOException - { - return DataInputStream.readUTF( in ); - } + // -------------------------- PUBLIC STATIC METHODS + // -------------------------- - // -------------------------- PUBLIC INSTANCE METHODS -------------------------- + /** + * Note. This is a STATIC method! + * + * @param in + * stream to read UTF chars from (endian irrelevant) + * + * @return string from stream + * @throws IOException + * if read fails. + */ + public static String readUTF(DataInput in) throws IOException { + return DataInputStream.readUTF(in); + } - /** - * constructor. - * - * @param in binary inputstream of little-endian data. - */ - public LEDataInputStream( InputStream in ) - { - this.is = in; - this.dis = new DataInputStream( in ); - work = new byte[8]; - } + // -------------------------- PUBLIC INSTANCE METHODS + // -------------------------- - /** - * close. - * - * @throws IOException if close fails. - */ - public final void close() throws IOException - { - dis.close(); - } + /** + * constructor. + * + * @param in + * binary inputstream of little-endian data. + */ + public LEDataInputStream(InputStream in) { + this.is = in; + this.dis = new DataInputStream(in); + work = new byte[8]; + } - /** - * Read bytes. Watch out, read may return fewer bytes than requested. - * - * @param ba where the bytes go. - * @param off offset in buffer, not offset in file. - * @param len count of bytes to read. - * - * @return how many bytes read. - * @throws IOException if read fails. - */ - public final int read( byte ba[], int off, int len ) throws IOException - { - // For efficiency, we avoid one layer of wrapper - return is.read( ba, off, len ); - } + /** + * close. + * + * @throws IOException + * if close fails. + */ + public final void close() throws IOException { + dis.close(); + } - /** - * read only a one-byte boolean. - * - * @return true or false. - * @throws IOException if read fails. - * @see java.io.DataInput#readBoolean() - */ - public final boolean readBoolean() throws IOException - { - return dis.readBoolean(); - } + /** + * Read bytes. Watch out, read may return fewer bytes than requested. + * + * @param ba + * where the bytes go. + * @param off + * offset in buffer, not offset in file. + * @param len + * count of bytes to read. + * + * @return how many bytes read. + * @throws IOException + * if read fails. + */ + public final int read(byte ba[], int off, int len) throws IOException { + // For efficiency, we avoid one layer of wrapper + return is.read(ba, off, len); + } - /** - * read byte. - * - * @return the byte read. - * @throws IOException if read fails. - * @see java.io.DataInput#readByte() - */ - public final byte readByte() throws IOException - { - return dis.readByte(); - } + /** + * read only a one-byte boolean. + * + * @return true or false. + * @throws IOException + * if read fails. + * @see java.io.DataInput#readBoolean() + */ + @Override + public final boolean readBoolean() throws IOException { + return dis.readBoolean(); + } - /** - * Read on char. like DataInputStream.readChar except little endian. - * - * @return little endian 16-bit unicode char from the stream. - * @throws IOException if read fails. - */ - public final char readChar() throws IOException - { - dis.readFully( work, 0, 2 ); - return ( char ) ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) ); - } + /** + * read byte. + * + * @return the byte read. + * @throws IOException + * if read fails. + * @see java.io.DataInput#readByte() + */ + @Override + public final byte readByte() throws IOException { + return dis.readByte(); + } - /** - * Read a double. like DataInputStream.readDouble except little endian. - * - * @return little endian IEEE double from the datastream. - * @throws IOException - */ - public final double readDouble() throws IOException - { - return Double.longBitsToDouble( readLong() ); - } + /** + * Read on char. like DataInputStream.readChar except little endian. + * + * @return little endian 16-bit unicode char from the stream. + * @throws IOException + * if read fails. + */ + @Override + public final char readChar() throws IOException { + dis.readFully(work, 0, 2); + return (char) ((work[1] & 0xff) << 8 | (work[0] & 0xff)); + } - /** - * Read one float. Like DataInputStream.readFloat except little endian. - * - * @return little endian IEEE float from the datastream. - * @throws IOException if read fails. - */ - public final float readFloat() throws IOException - { - return Float.intBitsToFloat( readInt() ); - } + /** + * Read a double. like DataInputStream.readDouble except little endian. + * + * @return little endian IEEE double from the datastream. + * @throws IOException + */ + @Override + public final double readDouble() throws IOException { + return Double.longBitsToDouble(readLong()); + } - /** - * Read bytes until the array is filled. - * - * @see java.io.DataInput#readFully(byte[]) - */ - public final void readFully( byte ba[] ) throws IOException - { - dis.readFully( ba, 0, ba.length ); - } + /** + * Read one float. Like DataInputStream.readFloat except little endian. + * + * @return little endian IEEE float from the datastream. + * @throws IOException + * if read fails. + */ + @Override + public final float readFloat() throws IOException { + return Float.intBitsToFloat(readInt()); + } - /** - * Read bytes until the count is satisfied. - * - * @throws IOException if read fails. - * @see java.io.DataInput#readFully(byte[],int,int) - */ - public final void readFully( byte ba[], - int off, - int len ) throws IOException - { - dis.readFully( ba, off, len ); - } + /** + * Read bytes until the array is filled. + * + * @see java.io.DataInput#readFully(byte[]) + */ + @Override + public final void readFully(byte ba[]) throws IOException { + dis.readFully(ba, 0, ba.length); + } - /** - * Read an int, 32-bits. Like DataInputStream.readInt except little endian. - * - * @return little-endian binary int from the datastream - * @throws IOException if read fails. - */ - public final int readInt() throws IOException - { - dis.readFully( work, 0, 4 ); - return ( work[ 3 ] ) << 24 - | ( work[ 2 ] & 0xff ) << 16 - | ( work[ 1 ] & 0xff ) << 8 - | ( work[ 0 ] & 0xff ); - } + /** + * Read bytes until the count is satisfied. + * + * @throws IOException + * if read fails. + * @see java.io.DataInput#readFully(byte[],int,int) + */ + @Override + public final void readFully(byte ba[], int off, int len) throws IOException { + dis.readFully(ba, off, len); + } - /** - * Read a line. - * - * @return a rough approximation of the 8-bit stream as a 16-bit unicode string - * @throws IOException - * @noinspection deprecation - * @deprecated This method does not properly convert bytes to characters. Use a Reader instead with a little-endian - * encoding. - */ - public final String readLine() throws IOException - { - return dis.readLine(); - } + /** + * Read an int, 32-bits. Like DataInputStream.readInt except little endian. + * + * @return little-endian binary int from the datastream + * @throws IOException + * if read fails. + */ + @Override + public final int readInt() throws IOException { + dis.readFully(work, 0, 4); + return (work[3]) << 24 | (work[2] & 0xff) << 16 | (work[1] & 0xff) << 8 + | (work[0] & 0xff); + } - /** - * read a long, 64-bits. Like DataInputStream.readLong except little endian. - * - * @return little-endian binary long from the datastream. - * @throws IOException - */ - public final long readLong() throws IOException - { - dis.readFully( work, 0, 8 ); - return ( long ) ( work[ 7 ] ) << 56 - | - /* long cast needed or shift done modulo 32 */ - ( long ) ( work[ 6 ] & 0xff ) << 48 - | ( long ) ( work[ 5 ] & 0xff ) << 40 - | ( long ) ( work[ 4 ] & 0xff ) << 32 - | ( long ) ( work[ 3 ] & 0xff ) << 24 - | ( long ) ( work[ 2 ] & 0xff ) << 16 - | ( long ) ( work[ 1 ] & 0xff ) << 8 - | ( long ) ( work[ 0 ] & 0xff ); - } + /** + * Read a line. + * + * @return a rough approximation of the 8-bit stream as a 16-bit unicode + * string + * @throws IOException + * @noinspection deprecation + * @deprecated This method does not properly convert bytes to characters. + * Use a Reader instead with a little-endian encoding. + */ + @Deprecated + @Override + public final String readLine() throws IOException { + return dis.readLine(); + } - /** - * Read short, 16-bits. Like DataInputStream.readShort except little endian. - * - * @return little endian binary short from stream. - * @throws IOException if read fails. - */ - public final short readShort() throws IOException - { - dis.readFully( work, 0, 2 ); - return ( short ) ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) ); - } + /** + * read a long, 64-bits. Like DataInputStream.readLong except little endian. + * + * @return little-endian binary long from the datastream. + * @throws IOException + */ + @Override + public final long readLong() throws IOException { + dis.readFully(work, 0, 8); + return (long) (work[7]) << 56 | + /* long cast needed or shift done modulo 32 */ + (long) (work[6] & 0xff) << 48 | (long) (work[5] & 0xff) << 40 + | (long) (work[4] & 0xff) << 32 | (long) (work[3] & 0xff) << 24 + | (long) (work[2] & 0xff) << 16 | (long) (work[1] & 0xff) << 8 + | work[0] & 0xff; + } - /** - * Read UTF counted string. - * - * @return String read. - */ - public final String readUTF() throws IOException - { - return dis.readUTF(); - } + /** + * Read short, 16-bits. Like DataInputStream.readShort except little endian. + * + * @return little endian binary short from stream. + * @throws IOException + * if read fails. + */ + @Override + public final short readShort() throws IOException { + dis.readFully(work, 0, 2); + return (short) ((work[1] & 0xff) << 8 | (work[0] & 0xff)); + } - /** - * Read an unsigned byte. Note: returns an int, even though says Byte (non-Javadoc) - * - * @throws IOException if read fails. - * @see java.io.DataInput#readUnsignedByte() - */ - public final int readUnsignedByte() throws IOException - { - return dis.readUnsignedByte(); - } + /** + * Read UTF counted string. + * + * @return String read. + */ + @Override + public final String readUTF() throws IOException { + return dis.readUTF(); + } - /** - * Read an unsigned short, 16 bits. Like DataInputStream.readUnsignedShort except little endian. Note, returns int - * even though it reads a short. - * - * @return little-endian int from the stream. - * @throws IOException if read fails. - */ - public final int readUnsignedShort() throws IOException - { - dis.readFully( work, 0, 2 ); - return ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) ); - } + /** + * Read an unsigned byte. Note: returns an int, even though says Byte + * (non-Javadoc) + * + * @throws IOException + * if read fails. + * @see java.io.DataInput#readUnsignedByte() + */ + @Override + public final int readUnsignedByte() throws IOException { + return dis.readUnsignedByte(); + } - /** - * Skip over bytes in the stream. See the general contract of the skipBytes method of - * DataInput. - *

- * Bytes for this operation are read from the contained input stream. - * - * @param n the number of bytes to be skipped. - * - * @return the actual number of bytes skipped. - * @throws IOException if an I/O error occurs. - */ - public final int skipBytes( int n ) throws IOException - { - return dis.skipBytes( n ); - } - } \ No newline at end of file + /** + * Read an unsigned short, 16 bits. Like DataInputStream.readUnsignedShort + * except little endian. Note, returns int even though it reads a short. + * + * @return little-endian int from the stream. + * @throws IOException + * if read fails. + */ + @Override + public final int readUnsignedShort() throws IOException { + dis.readFully(work, 0, 2); + return ((work[1] & 0xff) << 8 | (work[0] & 0xff)); + } + + /** + * Skip over bytes in the stream. See the general contract of the + * skipBytes method of DataInput. + *

+ * Bytes for this operation are read from the contained input stream. + * + * @param n + * the number of bytes to be skipped. + * + * @return the actual number of bytes skipped. + * @throws IOException + * if an I/O error occurs. + */ + @Override + public final int skipBytes(int n) throws IOException { + return dis.skipBytes(n); + } +} \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/org/xmlpull/mxp1_serializer/MXSerializer.java b/brut.apktool/apktool-lib/src/main/java/org/xmlpull/mxp1_serializer/MXSerializer.java index 7a4f881b..cee38c7f 100644 --- a/brut.apktool/apktool-lib/src/main/java/org/xmlpull/mxp1_serializer/MXSerializer.java +++ b/brut.apktool/apktool-lib/src/main/java/org/xmlpull/mxp1_serializer/MXSerializer.java @@ -8,1137 +8,1201 @@ import java.io.Writer; import org.xmlpull.v1.XmlSerializer; /** - * Implementation of XmlSerializer interface from XmlPull V1 API. - * This implementation is optimzied for performance and low memory footprint. - * - *

Implemented features:

    - *
  • FEATURE_NAMES_INTERNED - when enabled all returned names - * (namespaces, prefixes) will be interned and it is required that - * all names passed as arguments MUST be interned - *
  • FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE - *
- *

Implemented properties:

    - *
  • PROPERTY_SERIALIZER_INDENTATION - *
  • PROPERTY_SERIALIZER_LINE_SEPARATOR - *
- * + * Implementation of XmlSerializer interface from XmlPull V1 API. This + * implementation is optimzied for performance and low memory footprint. + * + *

+ * Implemented features: + *

    + *
  • FEATURE_NAMES_INTERNED - when enabled all returned names (namespaces, + * prefixes) will be interned and it is required that all names passed as + * arguments MUST be interned + *
  • FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE + *
+ *

+ * Implemented properties: + *

    + *
  • PROPERTY_SERIALIZER_INDENTATION + *
  • PROPERTY_SERIALIZER_LINE_SEPARATOR + *
+ * */ public class MXSerializer implements XmlSerializer { - protected final static String XML_URI = "http://www.w3.org/XML/1998/namespace"; - protected final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; - private static final boolean TRACE_SIZING = false; - private static final boolean TRACE_ESCAPING = false; - - protected final String FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE = - "http://xmlpull.org/v1/doc/features.html#serializer-attvalue-use-apostrophe"; - protected final String FEATURE_NAMES_INTERNED = - "http://xmlpull.org/v1/doc/features.html#names-interned"; - protected final String PROPERTY_SERIALIZER_INDENTATION = - "http://xmlpull.org/v1/doc/properties.html#serializer-indentation"; - protected final String PROPERTY_SERIALIZER_LINE_SEPARATOR = - "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator"; - protected final static String PROPERTY_LOCATION = - "http://xmlpull.org/v1/doc/properties.html#location"; - - // properties/features - protected boolean namesInterned; - protected boolean attributeUseApostrophe; - protected String indentationString = null; //" "; - protected String lineSeparator = "\n"; - - protected String location; - protected Writer out; - - protected int autoDeclaredPrefixes; - - protected int depth = 0; - - // element stack - protected String elNamespace[] = new String[ 2 ]; - protected String elName[] = new String[ elNamespace.length ]; - protected String elPrefix[] = new String[ elNamespace.length ]; - protected int elNamespaceCount[] = new int[ elNamespace.length ]; - - //namespace stack - protected int namespaceEnd = 0; - protected String namespacePrefix[] = new String[ 8 ]; - protected String namespaceUri[] = new String[ namespacePrefix.length ]; - - protected boolean finished; - protected boolean pastRoot; - protected boolean setPrefixCalled; - protected boolean startTagIncomplete; - - protected boolean doIndent; - protected boolean seenTag; - - protected boolean seenBracket; - protected boolean seenBracketBracket; - - // buffer output if neede to write escaped String see text(String) - private static final int BUF_LEN = Runtime.getRuntime().freeMemory() > 1000000L ? 8*1024 : 256; - protected char buf[] = new char[ BUF_LEN ]; - - - protected static final String precomputedPrefixes[]; - - static { - precomputedPrefixes = new String[32]; //arbitrary number ... - for (int i = 0; i < precomputedPrefixes.length; i++) - { - precomputedPrefixes[i] = ("n"+i).intern(); - } - } - - private boolean checkNamesInterned = false; - - private void checkInterning(String name) { - if(namesInterned && name != name.intern()) { - throw new IllegalArgumentException( - "all names passed as arguments must be interned" - +"when NAMES INTERNED feature is enabled"); - } - } - - protected void reset() { - location = null; - out = null; - autoDeclaredPrefixes = 0; - depth = 0; - - // nullify references on all levels to allow it to be GCed - for (int i = 0; i < elNamespaceCount.length; i++) - { - elName[ i ] = null; - elPrefix[ i ] = null; - elNamespace[ i ] = null; - elNamespaceCount[ i ] = 2; - } - - - namespaceEnd = 0; - - - //NOTE: no need to intern() as all literal strings and string-valued constant expressions - //are interned. String literals are defined in 3.10.5 of the Java Language Specification - // just checking ... - //assert "xmlns" == "xmlns".intern(); - //assert XMLNS_URI == XMLNS_URI.intern(); - - //TODO: how to prevent from reporting this namespace? - // this is special namespace declared for consistensy with XML infoset - namespacePrefix[ namespaceEnd ] = "xmlns"; - namespaceUri[ namespaceEnd ] = XMLNS_URI; - ++namespaceEnd; - - namespacePrefix[ namespaceEnd ] = "xml"; - namespaceUri[ namespaceEnd ] = XML_URI; - ++namespaceEnd; - - finished = false; - pastRoot = false; - setPrefixCalled = false; - startTagIncomplete = false; - //doIntent is not changed - seenTag = false; - - seenBracket = false; - seenBracketBracket = false; - } - - - protected void ensureElementsCapacity() { - final int elStackSize = elName.length; - //assert (depth + 1) >= elName.length; - // we add at least one extra slot ... - final int newSize = (depth >= 7 ? 2 * depth : 8) + 2; // = lucky 7 + 1 //25 - if(TRACE_SIZING) { - System.err.println( - getClass().getName()+" elStackSize "+elStackSize+" ==> "+newSize); - } - final boolean needsCopying = elStackSize > 0; - String[] arr = null; - // reuse arr local variable slot - arr = new String[newSize]; - if(needsCopying) System.arraycopy(elName, 0, arr, 0, elStackSize); - elName = arr; - - arr = new String[newSize]; - if(needsCopying) System.arraycopy(elPrefix, 0, arr, 0, elStackSize); - elPrefix = arr; - - arr = new String[newSize]; - if(needsCopying) System.arraycopy(elNamespace, 0, arr, 0, elStackSize); - elNamespace = arr; - - final int[] iarr = new int[newSize]; - if(needsCopying) { - System.arraycopy(elNamespaceCount, 0, iarr, 0, elStackSize); - } else { - // special initialization - iarr[0] = 0; - } - elNamespaceCount = iarr; - } - - protected void ensureNamespacesCapacity() { //int size) { - //int namespaceSize = namespacePrefix != null ? namespacePrefix.length : 0; - //assert (namespaceEnd >= namespacePrefix.length); - - //if(size >= namespaceSize) { - //int newSize = size > 7 ? 2 * size : 8; // = lucky 7 + 1 //25 - final int newSize = namespaceEnd > 7 ? 2 * namespaceEnd : 8; - if(TRACE_SIZING) { - System.err.println( - getClass().getName()+" namespaceSize "+namespacePrefix.length+" ==> "+newSize); - } - final String[] newNamespacePrefix = new String[newSize]; - final String[] newNamespaceUri = new String[newSize]; - if(namespacePrefix != null) { - System.arraycopy( - namespacePrefix, 0, newNamespacePrefix, 0, namespaceEnd); - System.arraycopy( - namespaceUri, 0, newNamespaceUri, 0, namespaceEnd); - } - namespacePrefix = newNamespacePrefix; - namespaceUri = newNamespaceUri; - - // TODO use hashes for quick namespace->prefix lookups - // if( ! allStringsInterned ) { - // int[] newNamespacePrefixHash = new int[newSize]; - // if(namespacePrefixHash != null) { - // System.arraycopy( - // namespacePrefixHash, 0, newNamespacePrefixHash, 0, namespaceEnd); - // } - // namespacePrefixHash = newNamespacePrefixHash; - // } - //prefixesSize = newSize; - // ////assert nsPrefixes.length > size && nsPrefixes.length == newSize - //} - } - - - public void setFeature(String name, - boolean state) throws IllegalArgumentException, IllegalStateException - { - if(name == null) { - throw new IllegalArgumentException("feature name can not be null"); - } - if(FEATURE_NAMES_INTERNED.equals(name)) { - namesInterned = state; - } else if(FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) { - attributeUseApostrophe = state; - } else { - throw new IllegalStateException("unsupported feature "+name); - } - } - - public boolean getFeature(String name) throws IllegalArgumentException - { - if(name == null) { - throw new IllegalArgumentException("feature name can not be null"); - } - if(FEATURE_NAMES_INTERNED.equals(name)) { - return namesInterned; - } else if(FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) { - return attributeUseApostrophe; - } else { - return false; - } - } - - // precomputed variables to simplify writing indentation - protected int offsetNewLine; - protected int indentationJump; - protected char[] indentationBuf; - protected int maxIndentLevel; - protected boolean writeLineSepartor; //should end-of-line be written - protected boolean writeIndentation; // is indentation used? - - /** - * For maximum efficiency when writing indents the required output is pre-computed - * This is internal function that recomputes buffer after user requested chnages. - */ - protected void rebuildIndentationBuf() { - if(doIndent == false) return; - final int maxIndent = 65; //hardcoded maximum indentation size in characters - int bufSize = 0; - offsetNewLine = 0; - if(writeLineSepartor) { - offsetNewLine = lineSeparator.length(); - bufSize += offsetNewLine; - } - maxIndentLevel = 0; - if(writeIndentation) { - indentationJump = indentationString.length(); - maxIndentLevel = maxIndent / indentationJump; - bufSize += maxIndentLevel * indentationJump; - } - if(indentationBuf == null || indentationBuf.length < bufSize) { - indentationBuf = new char[bufSize + 8]; - } - int bufPos = 0; - if(writeLineSepartor) { - for (int i = 0; i < lineSeparator.length(); i++) - { - indentationBuf[ bufPos++ ] = lineSeparator.charAt(i); - } - } - if(writeIndentation) { - for (int i = 0; i < maxIndentLevel; i++) - { - for (int j = 0; j < indentationString.length(); j++) - { - indentationBuf[ bufPos++ ] = indentationString.charAt(j); - } - } - } - } - - // if(doIndent) writeIndent(); - protected void writeIndent() throws IOException { - final int start = writeLineSepartor ? 0 : offsetNewLine; - final int level = (depth > maxIndentLevel) ? maxIndentLevel : depth; - out.write( indentationBuf, start, ( (level - 1) * indentationJump) + offsetNewLine); - } - - public void setProperty(String name, - Object value) throws IllegalArgumentException, IllegalStateException - { - if(name == null) { - throw new IllegalArgumentException("property name can not be null"); - } - if(PROPERTY_SERIALIZER_INDENTATION.equals(name)) { - indentationString = (String)value; - } else if(PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) { - lineSeparator = (String)value; - } else if(PROPERTY_LOCATION.equals(name)) { - location = (String) value; - } else { - throw new IllegalStateException("unsupported property "+name); - } - writeLineSepartor = lineSeparator != null && lineSeparator.length() > 0; - writeIndentation = indentationString != null && indentationString.length() > 0; - // optimize - do not write when nothing to write ... - doIndent = indentationString != null && (writeLineSepartor || writeIndentation); - //NOTE: when indentationString == null there is no indentation - // (even though writeLineSeparator may be true ...) - rebuildIndentationBuf(); - seenTag = false; // for consistency - } - - public Object getProperty(String name) throws IllegalArgumentException - { - if(name == null) { - throw new IllegalArgumentException("property name can not be null"); - } - if(PROPERTY_SERIALIZER_INDENTATION.equals(name)) { - return indentationString; - } else if(PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) { - return lineSeparator; - } else if(PROPERTY_LOCATION.equals(name)) { - return location; - } else { - return null; - } - } - - private String getLocation() { - return location != null ? " @"+location : ""; - } - - // this is special method that can be accessed directly to retrieve Writer serializer is using - public Writer getWriter() - { - return out; - } - - public void setOutput(Writer writer) - { - reset(); - out = writer; - } - - public void setOutput(OutputStream os, String encoding) throws IOException - { - if(os == null) throw new IllegalArgumentException("output stream can not be null"); - reset(); - if(encoding != null) { - out = new OutputStreamWriter(os, encoding); - } else { - out = new OutputStreamWriter(os); - } - } - - public void startDocument (String encoding, Boolean standalone) throws IOException - { - char apos = attributeUseApostrophe ? '\'' : '"'; - if(attributeUseApostrophe) { - out.write(""); - } - - public void endDocument() throws IOException - { - // close all unclosed tag; - while(depth > 0) { - endTag(elNamespace[ depth ], elName[ depth ]); - } - //assert depth == 0; - //assert startTagIncomplete == false; - finished = pastRoot = startTagIncomplete = true; - out.flush(); - } - - public void setPrefix(String prefix, String namespace) throws IOException - { - if(startTagIncomplete) closeStartTag(); - //assert prefix != null; - //assert namespace != null; - if (prefix == null) { - prefix = ""; - } - if(!namesInterned) { - prefix = prefix.intern(); //will throw NPE if prefix==null - } else if(checkNamesInterned) { - checkInterning(prefix); - } else if(prefix == null) { - throw new IllegalArgumentException("prefix must be not null"+getLocation()); - } - - //check that prefix is not duplicated ... - for (int i = elNamespaceCount[ depth ]; i < namespaceEnd; i++) - { - if(prefix == namespacePrefix[ i ]) { - throw new IllegalStateException("duplicated prefix "+printable(prefix)+getLocation()); - } - } - - if(!namesInterned) { - namespace = namespace.intern(); - } else if(checkNamesInterned) { - checkInterning(namespace); - } else if(namespace == null) { - throw new IllegalArgumentException("namespace must be not null"+getLocation()); - } - - if(namespaceEnd >= namespacePrefix.length) { - ensureNamespacesCapacity(); - } - namespacePrefix[ namespaceEnd ] = prefix; - namespaceUri[ namespaceEnd ] = namespace; - ++namespaceEnd; - setPrefixCalled = true; - } - - protected String lookupOrDeclarePrefix( String namespace ) { - return getPrefix(namespace, true); - } - - public String getPrefix(String namespace, boolean generatePrefix) - { - return getPrefix(namespace, generatePrefix, false); - } - - protected String getPrefix(String namespace, boolean generatePrefix, boolean nonEmpty) - { - //assert namespace != null; - if(!namesInterned) { - // when String is interned we can do much faster namespace stack lookups ... - namespace = namespace.intern(); - } else if(checkNamesInterned) { - checkInterning(namespace); - //assert namespace != namespace.intern(); - } - if(namespace == null) { - throw new IllegalArgumentException("namespace must be not null"+getLocation()); - } else if(namespace.length() == 0) { - throw new IllegalArgumentException("default namespace cannot have prefix"+getLocation()); - } - - // first check if namespace is already in scope - for (int i = namespaceEnd - 1; i >= 0 ; --i) - { - if(namespace == namespaceUri[ i ]) { - final String prefix = namespacePrefix[ i ]; - if(nonEmpty && prefix.length() == 0) continue; - // now check that prefix is still in scope - for (int p = namespaceEnd - 1; p > i ; --p) - { - if(prefix == namespacePrefix[ p ]) - continue; // too bad - prefix is redeclared with different namespace - } - return prefix; - } - } - - // so not found it ... - if(!generatePrefix) { - return null; - } - return generatePrefix(namespace); - } - - private String generatePrefix(String namespace) { - //assert namespace == namespace.intern(); - while(true) { - ++autoDeclaredPrefixes; - //fast lookup uses table that was pre-initialized in static{} .... - final String prefix = autoDeclaredPrefixes < precomputedPrefixes.length - ? precomputedPrefixes[autoDeclaredPrefixes] : ("n"+autoDeclaredPrefixes).intern(); - // make sure this prefix is not declared in any scope (avoid hiding in-scope prefixes)! - for (int i = namespaceEnd - 1; i >= 0 ; --i) - { - if(prefix == namespacePrefix[ i ]) { - continue; // prefix is already declared - generate new and try again - } - } - // declare prefix - - if(namespaceEnd >= namespacePrefix.length) { - ensureNamespacesCapacity(); - } - namespacePrefix[ namespaceEnd ] = prefix; - namespaceUri[ namespaceEnd ] = namespace; - ++namespaceEnd; - - return prefix; - } - } - - public int getDepth() - { - return depth; - } - - public String getNamespace () - { - return elNamespace[depth]; - } - - public String getName() - { - return elName[depth]; - } - - public XmlSerializer startTag (String namespace, String name) throws IOException - { - if(startTagIncomplete) { - closeStartTag(); - } - seenBracket = seenBracketBracket = false; - ++depth; - if(doIndent && depth > 0 && seenTag) { - writeIndent(); - } - seenTag = true; - setPrefixCalled = false; - startTagIncomplete = true; - if( (depth + 1) >= elName.length) { - ensureElementsCapacity(); - } - ////assert namespace != null; - - if(checkNamesInterned && namesInterned) checkInterning(namespace); - elNamespace[ depth ] = (namesInterned || namespace == null) ? namespace : namespace.intern(); - //assert name != null; - //elName[ depth ] = name; - if(checkNamesInterned && namesInterned) checkInterning(name); - elName[ depth ] = (namesInterned || name == null) ? name : name.intern(); - if(out == null) { - throw new IllegalStateException("setOutput() must called set before serialization can start"); - } - out.write('<'); - if(namespace != null) { - if(namespace.length() > 0) { - //ALEK: in future make this algo a feature on serializer - String prefix = null; - if(depth > 0 && (namespaceEnd - elNamespaceCount[depth-1]) == 1) { - // if only one prefix was declared un-declare it if the prefix is already declared on parent el with the same URI - String uri = namespaceUri[namespaceEnd-1]; - if(uri == namespace || uri.equals(namespace)) { - String elPfx = namespacePrefix[namespaceEnd-1]; - // 2 == to skip predefined namesapces (xml and xmlns ...) - for(int pos = elNamespaceCount[depth-1] - 1; pos >= 2; --pos ) { - String pf = namespacePrefix[pos]; - if(pf == elPfx || pf.equals(elPfx)) { - String n = namespaceUri[pos]; - if(n == uri || n.equals(uri)) { - --namespaceEnd; //un-declare namespace: this is kludge! - prefix = elPfx; - } - break; - } - } - } - } - if(prefix == null) { - prefix = lookupOrDeclarePrefix( namespace ); - } - //assert prefix != null; - // make sure that default ("") namespace to not print ":" - if(prefix.length() > 0) { - elPrefix[ depth ] = prefix; - out.write(prefix); - out.write(':'); - } else { - elPrefix[ depth ] = ""; - } - } else { - // make sure that default namespace can be declared - for (int i = namespaceEnd - 1; i >= 0 ; --i) { - if(namespacePrefix[ i ] == "") { - final String uri = namespaceUri[ i ]; - if(uri == null) { - // declare default namespace - setPrefix("", ""); - } else if(uri.length() > 0) { - throw new IllegalStateException( - "start tag can not be written in empty default namespace "+ - "as default namespace is currently bound to '"+uri+"'"+getLocation()); - } - break; - } - } - elPrefix[ depth ] = ""; - } - } else { - elPrefix[ depth ] = ""; - } - out.write(name); - return this; - } - - public XmlSerializer attribute (String namespace, String name, - String value) throws IOException - { - if(!startTagIncomplete) { - throw new IllegalArgumentException("startTag() must be called before attribute()"+getLocation()); - } - //assert setPrefixCalled == false; - out.write(' '); - ////assert namespace != null; - if(namespace != null && namespace.length() > 0) { - //namespace = namespace.intern(); - if(!namesInterned) { - namespace = namespace.intern(); - } else if(checkNamesInterned) { - checkInterning(namespace); - } - String prefix = getPrefix( namespace, false, true ); - //assert( prefix != null); - //if(prefix.length() == 0) { - if(prefix == null) { - // needs to declare prefix to hold default namespace - //NOTE: attributes such as a='b' are in NO namespace - prefix = generatePrefix(namespace); - } - out.write(prefix); - out.write(':'); - // if(prefix.length() > 0) { - // out.write(prefix); - // out.write(':'); - // } - } - //assert name != null; - out.write(name); - out.write('='); - //assert value != null; - out.write( attributeUseApostrophe ? '\'' : '"'); - writeAttributeValue(value, out); - out.write( attributeUseApostrophe ? '\'' : '"'); - return this; - } - - protected void closeStartTag() throws IOException { - if(finished) { - throw new IllegalArgumentException("trying to write past already finished output"+getLocation()); - } - if(seenBracket) { - seenBracket = seenBracketBracket = false; - } - if( startTagIncomplete || setPrefixCalled ) { - if(setPrefixCalled) { - throw new IllegalArgumentException( - "startTag() must be called immediately after setPrefix()"+getLocation()); - } - if(!startTagIncomplete) { - throw new IllegalArgumentException("trying to close start tag that is not opened"+getLocation()); - } - - // write all namespace delcarations! - writeNamespaceDeclarations(); - out.write('>'); - elNamespaceCount[ depth ] = namespaceEnd; - startTagIncomplete = false; - } - } - - protected void writeNamespaceDeclarations() throws IOException - { - //int start = elNamespaceCount[ depth - 1 ]; - for (int i = elNamespaceCount[ depth - 1 ]; i < namespaceEnd; i++) - { - if(doIndent && namespaceUri[ i ].length() > 40) { - writeIndent(); - out.write(" "); - } - if(namespacePrefix[ i ] != "") { - out.write(" xmlns:"); - out.write(namespacePrefix[ i ]); - out.write('='); - } else { - out.write(" xmlns="); - } - out.write( attributeUseApostrophe ? '\'' : '"'); - - //NOTE: escaping of namespace value the same way as attributes!!!! - writeAttributeValue(namespaceUri[ i ], out); - - out.write( attributeUseApostrophe ? '\'' : '"'); - } - } - - public XmlSerializer endTag(String namespace, String name) throws IOException - { - // check that level is valid - ////assert namespace != null; - //if(namespace != null) { - // namespace = namespace.intern(); - //} - seenBracket = seenBracketBracket = false; - if(namespace != null) { - if(!namesInterned) { - namespace = namespace.intern(); - } else if(checkNamesInterned) { - checkInterning(namespace); - } - } - - if(namespace != elNamespace[ depth ]) - { - throw new IllegalArgumentException( - "expected namespace "+printable(elNamespace[ depth ]) - +" and not "+printable(namespace)+getLocation()); - } - if(name == null) { - throw new IllegalArgumentException("end tag name can not be null"+getLocation()); - } - if(checkNamesInterned && namesInterned) { - checkInterning(name); - } - String startTagName = elName[ depth ]; - if((!namesInterned && !name.equals(startTagName)) - || (namesInterned && name != startTagName )) - { - throw new IllegalArgumentException( - "expected element name "+printable(elName[ depth ])+" and not "+printable(name)+getLocation()); - } - if(startTagIncomplete) { - writeNamespaceDeclarations(); - out.write(" />"); //space is added to make it easier to work in XHTML!!! - --depth; - } else { - //assert startTagIncomplete == false; - if(doIndent && seenTag) { writeIndent(); } - out.write(" 0) { - out.write(startTagPrefix); - out.write(':'); - } - - // if(namespace != null && namespace.length() > 0) { - // //TODO prefix should be alredy known from matching start tag ... - // final String prefix = lookupOrDeclarePrefix( namespace ); - // //assert( prefix != null); - // if(prefix.length() > 0) { - // out.write(prefix); - // out.write(':'); - // } - // } - out.write(name); - out.write('>'); - --depth; - } - namespaceEnd = elNamespaceCount[ depth ]; - startTagIncomplete = false; - seenTag = true; - return this; - } - - public XmlSerializer text (String text) throws IOException - { - //assert text != null; - if(startTagIncomplete || setPrefixCalled) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - writeElementContent(text, out); - return this; - } - - public XmlSerializer text (char [] buf, int start, int len) throws IOException - { - if(startTagIncomplete || setPrefixCalled) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - writeElementContent(buf, start, len, out); - return this; - } - - public void cdsect (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - out.write(""); - } - - public void entityRef (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - out.write('&'); - out.write(text); //escape? - out.write(';'); - } - - public void processingInstruction (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - out.write(""); - } - - public void comment (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - out.write(""); - } - - public void docdecl (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - out.write(""); - } - - public void ignorableWhitespace (String text) throws IOException - { - if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); - if(doIndent && seenTag) seenTag = false; - if(text.length() == 0) { - throw new IllegalArgumentException( - "empty string is not allowed for ignorable whitespace"+getLocation()); - } - out.write(text); //no escape? - } - - public void flush () throws IOException - { - if(!finished && startTagIncomplete) closeStartTag(); - out.flush(); - } - - // --- utility methods - - protected void writeAttributeValue(String value, Writer out) throws IOException - { - //.[apostrophe and <, & escaped], - final char quot = attributeUseApostrophe ? '\'' : '"'; - final String quotEntity = attributeUseApostrophe ? "'" : """; - - int pos = 0; - for (int i = 0; i < value.length(); i++) - { - char ch = value.charAt(i); - if(ch == '&') { - if(i > pos) out.write(value.substring(pos, i)); - out.write("&"); - pos = i + 1; - } if(ch == '<') { - if(i > pos) out.write(value.substring(pos, i)); - out.write("<"); - pos = i + 1; - }else if(ch == quot) { - if(i > pos) out.write(value.substring(pos, i)); - out.write(quotEntity); - pos = i + 1; - } else if(ch < 32) { - //in XML 1.0 only legal character are #x9 | #xA | #xD - // and they must be escaped otherwise in attribute value they are normalized to spaces - if(ch == 13 || ch == 10 || ch == 9) { - if(i > pos) out.write(value.substring(pos, i)); - out.write("&#"); - out.write(Integer.toString(ch)); - out.write(';'); - pos = i + 1; - } else { - if(TRACE_ESCAPING) System.err.println(getClass().getName()+" DEBUG ATTR value.len="+value.length()+" "+printable(value)); - - throw new IllegalStateException( - //"character "+Integer.toString(ch)+" is not allowed in output"+getLocation()); - "character "+printable(ch)+" ("+Integer.toString(ch)+") is not allowed in output"+getLocation() - +" (attr value="+printable(value)+")"); - // in XML 1.1 legal are [#x1-#xD7FF] - // if(ch > 0) { - // if(i > pos) out.write(text.substring(pos, i)); - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; - // } else { - // throw new IllegalStateException( - // "character zero is not allowed in XML 1.1 output"+getLocation()); - // } - } - } - } - if(pos > 0) { - out.write(value.substring(pos)); - } else { - out.write(value); // this is shortcut to the most common case - } - - } - - protected void writeElementContent(String text, Writer out) throws IOException - { - // esccape '<', '&', ']]>', <32 if necessary - int pos = 0; - for (int i = 0; i < text.length(); i++) - { - //TODO: check if doing char[] text.getChars() would be faster than getCharAt(i) ... - char ch = text.charAt(i); - if(ch == ']') { - if(seenBracket) { - seenBracketBracket = true; - } else { - seenBracket = true; - } - } else { - if(ch == '&') { - if(i > pos) out.write(text.substring(pos, i)); - out.write("&"); - pos = i + 1; - } else if(ch == '<') { - if(i > pos) out.write(text.substring(pos, i)); - out.write("<"); - pos = i + 1; - } else if(seenBracketBracket && ch == '>') { - if(i > pos) out.write(text.substring(pos, i)); - out.write(">"); - pos = i + 1; - } else if(ch < 32) { - //in XML 1.0 only legal character are #x9 | #xA | #xD - if( ch == 9 || ch == 10 || ch == 13) { - // pass through - - // } else if(ch == 13) { //escape - // if(i > pos) out.write(text.substring(pos, i)); - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; - } else { - if(TRACE_ESCAPING) System.err.println(getClass().getName()+" DEBUG TEXT value.len="+text.length()+" "+printable(text)); - throw new IllegalStateException( - "character "+Integer.toString(ch)+" is not allowed in output"+getLocation() - +" (text value="+printable(text)+")"); - // in XML 1.1 legal are [#x1-#xD7FF] - // if(ch > 0) { - // if(i > pos) out.write(text.substring(pos, i)); - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; - // } else { - // throw new IllegalStateException( - // "character zero is not allowed in XML 1.1 output"+getLocation()); - // } - } - } - if(seenBracket) { - seenBracketBracket = seenBracket = false; - } - - } - } - if(pos > 0) { - out.write(text.substring(pos)); - } else { - out.write(text); // this is shortcut to the most common case - } - - - - } - - protected void writeElementContent(char[] buf, int off, int len, Writer out) throws IOException - { - // esccape '<', '&', ']]>' - final int end = off + len; - int pos = off; - for (int i = off; i < end; i++) - { - final char ch = buf[i]; - if(ch == ']') { - if(seenBracket) { - seenBracketBracket = true; - } else { - seenBracket = true; - } - } else { - if(ch == '&') { - if(i > pos) { - out.write(buf, pos, i - pos); - } - out.write("&"); - pos = i + 1; - } else if(ch == '<') { - if(i > pos) { - out.write(buf, pos, i - pos); - } - out.write("<"); - pos = i + 1; - - } else if(seenBracketBracket && ch == '>') { - if(i > pos) { - out.write(buf, pos, i - pos); - } - out.write(">"); - pos = i + 1; - } else if(ch < 32) { - //in XML 1.0 only legal character are #x9 | #xA | #xD - if( ch == 9 || ch == 10 || ch == 13) { - // pass through - - - // } else if(ch == 13 ) { //if(ch == '\r') { - // if(i > pos) { - // out.write(buf, pos, i - pos); - // } - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; - } else { - if(TRACE_ESCAPING) System.err.println( - getClass().getName()+" DEBUG TEXT value.len=" - +len+" "+printable(new String(buf,off,len))); - throw new IllegalStateException( - "character "+printable(ch)+" ("+Integer.toString(ch)+") is not allowed in output"+getLocation()); - // in XML 1.1 legal are [#x1-#xD7FF] - // if(ch > 0) { - // if(i > pos) out.write(text.substring(pos, i)); - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; - // } else { - // throw new IllegalStateException( - // "character zero is not allowed in XML 1.1 output"+getLocation()); - // } - } - } - if(seenBracket) { - seenBracketBracket = seenBracket = false; - } - // assert seenBracketBracket == seenBracket == false; - } - } - if(end > pos) { - out.write(buf, pos, end - pos); - } - } - - /** simple utility method -- good for debugging */ - protected static final String printable(String s) { - if(s == null) return "null"; - StringBuffer retval = new StringBuffer(s.length() + 16); - retval.append("'"); - char ch; - for (int i = 0; i < s.length(); i++) { - addPrintable(retval, s.charAt(i)); - } - retval.append("'"); - return retval.toString(); - } - - protected static final String printable(char ch) { - StringBuffer retval = new StringBuffer(); - addPrintable(retval, ch); - return retval.toString(); - } - - private static void addPrintable(StringBuffer retval, char ch) - { - switch (ch) - { - case '\b': - retval.append("\\b"); - break; - case '\t': - retval.append("\\t"); - break; - case '\n': - retval.append("\\n"); - break; - case '\f': - retval.append("\\f"); - break; - case '\r': - retval.append("\\r"); - break; - case '\"': - retval.append("\\\""); - break; - case '\'': - retval.append("\\\'"); - break; - case '\\': - retval.append("\\\\"); - break; - default: - if (ch < 0x20 || ch > 0x7e) { - final String ss = "0000" + Integer.toString(ch, 16); - retval.append("\\u" + ss.substring(ss.length() - 4, ss.length())); - } else { - retval.append(ch); - } - } - } - -} + protected final static String XML_URI = "http://www.w3.org/XML/1998/namespace"; + protected final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; + private static final boolean TRACE_SIZING = false; + private static final boolean TRACE_ESCAPING = false; + protected final String FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE = "http://xmlpull.org/v1/doc/features.html#serializer-attvalue-use-apostrophe"; + protected final String FEATURE_NAMES_INTERNED = "http://xmlpull.org/v1/doc/features.html#names-interned"; + protected final String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation"; + protected final String PROPERTY_SERIALIZER_LINE_SEPARATOR = "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator"; + protected final static String PROPERTY_LOCATION = "http://xmlpull.org/v1/doc/properties.html#location"; + + // properties/features + protected boolean namesInterned; + protected boolean attributeUseApostrophe; + protected String indentationString = null; // " "; + protected String lineSeparator = "\n"; + + protected String location; + protected Writer out; + + protected int autoDeclaredPrefixes; + + protected int depth = 0; + + // element stack + protected String elNamespace[] = new String[2]; + protected String elName[] = new String[elNamespace.length]; + protected String elPrefix[] = new String[elNamespace.length]; + protected int elNamespaceCount[] = new int[elNamespace.length]; + + // namespace stack + protected int namespaceEnd = 0; + protected String namespacePrefix[] = new String[8]; + protected String namespaceUri[] = new String[namespacePrefix.length]; + + protected boolean finished; + protected boolean pastRoot; + protected boolean setPrefixCalled; + protected boolean startTagIncomplete; + + protected boolean doIndent; + protected boolean seenTag; + + protected boolean seenBracket; + protected boolean seenBracketBracket; + + // buffer output if neede to write escaped String see text(String) + private static final int BUF_LEN = Runtime.getRuntime().freeMemory() > 1000000L ? 8 * 1024 + : 256; + protected char buf[] = new char[BUF_LEN]; + + protected static final String precomputedPrefixes[]; + + static { + precomputedPrefixes = new String[32]; // arbitrary number ... + for (int i = 0; i < precomputedPrefixes.length; i++) { + precomputedPrefixes[i] = ("n" + i).intern(); + } + } + + private boolean checkNamesInterned = false; + + private void checkInterning(String name) { + if (namesInterned && name != name.intern()) { + throw new IllegalArgumentException( + "all names passed as arguments must be interned" + + "when NAMES INTERNED feature is enabled"); + } + } + + protected void reset() { + location = null; + out = null; + autoDeclaredPrefixes = 0; + depth = 0; + + // nullify references on all levels to allow it to be GCed + for (int i = 0; i < elNamespaceCount.length; i++) { + elName[i] = null; + elPrefix[i] = null; + elNamespace[i] = null; + elNamespaceCount[i] = 2; + } + + namespaceEnd = 0; + + // NOTE: no need to intern() as all literal strings and string-valued + // constant expressions + // are interned. String literals are defined in 3.10.5 of the Java + // Language Specification + // just checking ... + // assert "xmlns" == "xmlns".intern(); + // assert XMLNS_URI == XMLNS_URI.intern(); + + // TODO: how to prevent from reporting this namespace? + // this is special namespace declared for consistensy with XML infoset + namespacePrefix[namespaceEnd] = "xmlns"; + namespaceUri[namespaceEnd] = XMLNS_URI; + ++namespaceEnd; + + namespacePrefix[namespaceEnd] = "xml"; + namespaceUri[namespaceEnd] = XML_URI; + ++namespaceEnd; + + finished = false; + pastRoot = false; + setPrefixCalled = false; + startTagIncomplete = false; + // doIntent is not changed + seenTag = false; + + seenBracket = false; + seenBracketBracket = false; + } + + protected void ensureElementsCapacity() { + final int elStackSize = elName.length; + // assert (depth + 1) >= elName.length; + // we add at least one extra slot ... + final int newSize = (depth >= 7 ? 2 * depth : 8) + 2; // = lucky 7 + 1 + // //25 + if (TRACE_SIZING) { + System.err.println(getClass().getName() + " elStackSize " + + elStackSize + " ==> " + newSize); + } + final boolean needsCopying = elStackSize > 0; + String[] arr = null; + // reuse arr local variable slot + arr = new String[newSize]; + if (needsCopying) + System.arraycopy(elName, 0, arr, 0, elStackSize); + elName = arr; + + arr = new String[newSize]; + if (needsCopying) + System.arraycopy(elPrefix, 0, arr, 0, elStackSize); + elPrefix = arr; + + arr = new String[newSize]; + if (needsCopying) + System.arraycopy(elNamespace, 0, arr, 0, elStackSize); + elNamespace = arr; + + final int[] iarr = new int[newSize]; + if (needsCopying) { + System.arraycopy(elNamespaceCount, 0, iarr, 0, elStackSize); + } else { + // special initialization + iarr[0] = 0; + } + elNamespaceCount = iarr; + } + + protected void ensureNamespacesCapacity() { // int size) { + // int namespaceSize = namespacePrefix != null ? namespacePrefix.length + // : 0; + // assert (namespaceEnd >= namespacePrefix.length); + + // if(size >= namespaceSize) { + // int newSize = size > 7 ? 2 * size : 8; // = lucky 7 + 1 //25 + final int newSize = namespaceEnd > 7 ? 2 * namespaceEnd : 8; + if (TRACE_SIZING) { + System.err.println(getClass().getName() + " namespaceSize " + + namespacePrefix.length + " ==> " + newSize); + } + final String[] newNamespacePrefix = new String[newSize]; + final String[] newNamespaceUri = new String[newSize]; + if (namespacePrefix != null) { + System.arraycopy(namespacePrefix, 0, newNamespacePrefix, 0, + namespaceEnd); + System.arraycopy(namespaceUri, 0, newNamespaceUri, 0, namespaceEnd); + } + namespacePrefix = newNamespacePrefix; + namespaceUri = newNamespaceUri; + + // TODO use hashes for quick namespace->prefix lookups + // if( ! allStringsInterned ) { + // int[] newNamespacePrefixHash = new int[newSize]; + // if(namespacePrefixHash != null) { + // System.arraycopy( + // namespacePrefixHash, 0, newNamespacePrefixHash, 0, namespaceEnd); + // } + // namespacePrefixHash = newNamespacePrefixHash; + // } + // prefixesSize = newSize; + // ////assert nsPrefixes.length > size && nsPrefixes.length == newSize + // } + } + + @Override + public void setFeature(String name, boolean state) + throws IllegalArgumentException, IllegalStateException { + if (name == null) { + throw new IllegalArgumentException("feature name can not be null"); + } + if (FEATURE_NAMES_INTERNED.equals(name)) { + namesInterned = state; + } else if (FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) { + attributeUseApostrophe = state; + } else { + throw new IllegalStateException("unsupported feature " + name); + } + } + + @Override + public boolean getFeature(String name) throws IllegalArgumentException { + if (name == null) { + throw new IllegalArgumentException("feature name can not be null"); + } + if (FEATURE_NAMES_INTERNED.equals(name)) { + return namesInterned; + } else if (FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE.equals(name)) { + return attributeUseApostrophe; + } else { + return false; + } + } + + // precomputed variables to simplify writing indentation + protected int offsetNewLine; + protected int indentationJump; + protected char[] indentationBuf; + protected int maxIndentLevel; + protected boolean writeLineSepartor; // should end-of-line be written + protected boolean writeIndentation; // is indentation used? + + /** + * For maximum efficiency when writing indents the required output is + * pre-computed This is internal function that recomputes buffer after user + * requested chnages. + */ + protected void rebuildIndentationBuf() { + if (doIndent == false) + return; + final int maxIndent = 65; // hardcoded maximum indentation size in + // characters + int bufSize = 0; + offsetNewLine = 0; + if (writeLineSepartor) { + offsetNewLine = lineSeparator.length(); + bufSize += offsetNewLine; + } + maxIndentLevel = 0; + if (writeIndentation) { + indentationJump = indentationString.length(); + maxIndentLevel = maxIndent / indentationJump; + bufSize += maxIndentLevel * indentationJump; + } + if (indentationBuf == null || indentationBuf.length < bufSize) { + indentationBuf = new char[bufSize + 8]; + } + int bufPos = 0; + if (writeLineSepartor) { + for (int i = 0; i < lineSeparator.length(); i++) { + indentationBuf[bufPos++] = lineSeparator.charAt(i); + } + } + if (writeIndentation) { + for (int i = 0; i < maxIndentLevel; i++) { + for (int j = 0; j < indentationString.length(); j++) { + indentationBuf[bufPos++] = indentationString.charAt(j); + } + } + } + } + + // if(doIndent) writeIndent(); + protected void writeIndent() throws IOException { + final int start = writeLineSepartor ? 0 : offsetNewLine; + final int level = (depth > maxIndentLevel) ? maxIndentLevel : depth; + out.write(indentationBuf, start, ((level - 1) * indentationJump) + + offsetNewLine); + } + + @Override + public void setProperty(String name, Object value) + throws IllegalArgumentException, IllegalStateException { + if (name == null) { + throw new IllegalArgumentException("property name can not be null"); + } + if (PROPERTY_SERIALIZER_INDENTATION.equals(name)) { + indentationString = (String) value; + } else if (PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) { + lineSeparator = (String) value; + } else if (PROPERTY_LOCATION.equals(name)) { + location = (String) value; + } else { + throw new IllegalStateException("unsupported property " + name); + } + writeLineSepartor = lineSeparator != null && lineSeparator.length() > 0; + writeIndentation = indentationString != null + && indentationString.length() > 0; + // optimize - do not write when nothing to write ... + doIndent = indentationString != null + && (writeLineSepartor || writeIndentation); + // NOTE: when indentationString == null there is no indentation + // (even though writeLineSeparator may be true ...) + rebuildIndentationBuf(); + seenTag = false; // for consistency + } + + @Override + public Object getProperty(String name) throws IllegalArgumentException { + if (name == null) { + throw new IllegalArgumentException("property name can not be null"); + } + if (PROPERTY_SERIALIZER_INDENTATION.equals(name)) { + return indentationString; + } else if (PROPERTY_SERIALIZER_LINE_SEPARATOR.equals(name)) { + return lineSeparator; + } else if (PROPERTY_LOCATION.equals(name)) { + return location; + } else { + return null; + } + } + + private String getLocation() { + return location != null ? " @" + location : ""; + } + + // this is special method that can be accessed directly to retrieve Writer + // serializer is using + public Writer getWriter() { + return out; + } + + @Override + public void setOutput(Writer writer) { + reset(); + out = writer; + } + + @Override + public void setOutput(OutputStream os, String encoding) throws IOException { + if (os == null) + throw new IllegalArgumentException("output stream can not be null"); + reset(); + if (encoding != null) { + out = new OutputStreamWriter(os, encoding); + } else { + out = new OutputStreamWriter(os); + } + } + + @Override + public void startDocument(String encoding, Boolean standalone) + throws IOException { + char apos = attributeUseApostrophe ? '\'' : '"'; + if (attributeUseApostrophe) { + out.write(""); + } + + @Override + public void endDocument() throws IOException { + // close all unclosed tag; + while (depth > 0) { + endTag(elNamespace[depth], elName[depth]); + } + // assert depth == 0; + // assert startTagIncomplete == false; + finished = pastRoot = startTagIncomplete = true; + out.flush(); + } + + @Override + public void setPrefix(String prefix, String namespace) throws IOException { + if (startTagIncomplete) + closeStartTag(); + // assert prefix != null; + // assert namespace != null; + if (prefix == null) { + prefix = ""; + } + if (!namesInterned) { + prefix = prefix.intern(); // will throw NPE if prefix==null + } else if (checkNamesInterned) { + checkInterning(prefix); + } else if (prefix == null) { + throw new IllegalArgumentException("prefix must be not null" + + getLocation()); + } + + // check that prefix is not duplicated ... + for (int i = elNamespaceCount[depth]; i < namespaceEnd; i++) { + if (prefix == namespacePrefix[i]) { + throw new IllegalStateException("duplicated prefix " + + printable(prefix) + getLocation()); + } + } + + if (!namesInterned) { + namespace = namespace.intern(); + } else if (checkNamesInterned) { + checkInterning(namespace); + } else if (namespace == null) { + throw new IllegalArgumentException("namespace must be not null" + + getLocation()); + } + + if (namespaceEnd >= namespacePrefix.length) { + ensureNamespacesCapacity(); + } + namespacePrefix[namespaceEnd] = prefix; + namespaceUri[namespaceEnd] = namespace; + ++namespaceEnd; + setPrefixCalled = true; + } + + protected String lookupOrDeclarePrefix(String namespace) { + return getPrefix(namespace, true); + } + + @Override + public String getPrefix(String namespace, boolean generatePrefix) { + return getPrefix(namespace, generatePrefix, false); + } + + protected String getPrefix(String namespace, boolean generatePrefix, + boolean nonEmpty) { + // assert namespace != null; + if (!namesInterned) { + // when String is interned we can do much faster namespace stack + // lookups ... + namespace = namespace.intern(); + } else if (checkNamesInterned) { + checkInterning(namespace); + // assert namespace != namespace.intern(); + } + if (namespace == null) { + throw new IllegalArgumentException("namespace must be not null" + + getLocation()); + } else if (namespace.length() == 0) { + throw new IllegalArgumentException( + "default namespace cannot have prefix" + getLocation()); + } + + // first check if namespace is already in scope + for (int i = namespaceEnd - 1; i >= 0; --i) { + if (namespace == namespaceUri[i]) { + final String prefix = namespacePrefix[i]; + if (nonEmpty && prefix.length() == 0) + continue; + // now check that prefix is still in scope + for (int p = namespaceEnd - 1; p > i; --p) { + if (prefix == namespacePrefix[p]) + continue; // too bad - prefix is redeclared with + // different namespace + } + return prefix; + } + } + + // so not found it ... + if (!generatePrefix) { + return null; + } + return generatePrefix(namespace); + } + + private String generatePrefix(String namespace) { + // assert namespace == namespace.intern(); + while (true) { + ++autoDeclaredPrefixes; + // fast lookup uses table that was pre-initialized in static{} .... + final String prefix = autoDeclaredPrefixes < precomputedPrefixes.length ? precomputedPrefixes[autoDeclaredPrefixes] + : ("n" + autoDeclaredPrefixes).intern(); + // make sure this prefix is not declared in any scope (avoid hiding + // in-scope prefixes)! + for (int i = namespaceEnd - 1; i >= 0; --i) { + if (prefix == namespacePrefix[i]) { + continue; // prefix is already declared - generate new and + // try again + } + } + // declare prefix + + if (namespaceEnd >= namespacePrefix.length) { + ensureNamespacesCapacity(); + } + namespacePrefix[namespaceEnd] = prefix; + namespaceUri[namespaceEnd] = namespace; + ++namespaceEnd; + + return prefix; + } + } + + @Override + public int getDepth() { + return depth; + } + + @Override + public String getNamespace() { + return elNamespace[depth]; + } + + @Override + public String getName() { + return elName[depth]; + } + + @Override + public XmlSerializer startTag(String namespace, String name) + throws IOException { + if (startTagIncomplete) { + closeStartTag(); + } + seenBracket = seenBracketBracket = false; + ++depth; + if (doIndent && depth > 0 && seenTag) { + writeIndent(); + } + seenTag = true; + setPrefixCalled = false; + startTagIncomplete = true; + if ((depth + 1) >= elName.length) { + ensureElementsCapacity(); + } + // //assert namespace != null; + + if (checkNamesInterned && namesInterned) + checkInterning(namespace); + elNamespace[depth] = (namesInterned || namespace == null) ? namespace + : namespace.intern(); + // assert name != null; + // elName[ depth ] = name; + if (checkNamesInterned && namesInterned) + checkInterning(name); + elName[depth] = (namesInterned || name == null) ? name : name.intern(); + if (out == null) { + throw new IllegalStateException( + "setOutput() must called set before serialization can start"); + } + out.write('<'); + if (namespace != null) { + if (namespace.length() > 0) { + // ALEK: in future make this algo a feature on serializer + String prefix = null; + if (depth > 0 + && (namespaceEnd - elNamespaceCount[depth - 1]) == 1) { + // if only one prefix was declared un-declare it if the + // prefix is already declared on parent el with the same URI + String uri = namespaceUri[namespaceEnd - 1]; + if (uri == namespace || uri.equals(namespace)) { + String elPfx = namespacePrefix[namespaceEnd - 1]; + // 2 == to skip predefined namesapces (xml and xmlns + // ...) + for (int pos = elNamespaceCount[depth - 1] - 1; pos >= 2; --pos) { + String pf = namespacePrefix[pos]; + if (pf == elPfx || pf.equals(elPfx)) { + String n = namespaceUri[pos]; + if (n == uri || n.equals(uri)) { + --namespaceEnd; // un-declare namespace: + // this is kludge! + prefix = elPfx; + } + break; + } + } + } + } + if (prefix == null) { + prefix = lookupOrDeclarePrefix(namespace); + } + // assert prefix != null; + // make sure that default ("") namespace to not print ":" + if (prefix.length() > 0) { + elPrefix[depth] = prefix; + out.write(prefix); + out.write(':'); + } else { + elPrefix[depth] = ""; + } + } else { + // make sure that default namespace can be declared + for (int i = namespaceEnd - 1; i >= 0; --i) { + if (namespacePrefix[i] == "") { + final String uri = namespaceUri[i]; + if (uri == null) { + // declare default namespace + setPrefix("", ""); + } else if (uri.length() > 0) { + throw new IllegalStateException( + "start tag can not be written in empty default namespace " + + "as default namespace is currently bound to '" + + uri + "'" + getLocation()); + } + break; + } + } + elPrefix[depth] = ""; + } + } else { + elPrefix[depth] = ""; + } + out.write(name); + return this; + } + + @Override + public XmlSerializer attribute(String namespace, String name, String value) + throws IOException { + if (!startTagIncomplete) { + throw new IllegalArgumentException( + "startTag() must be called before attribute()" + + getLocation()); + } + // assert setPrefixCalled == false; + out.write(' '); + // //assert namespace != null; + if (namespace != null && namespace.length() > 0) { + // namespace = namespace.intern(); + if (!namesInterned) { + namespace = namespace.intern(); + } else if (checkNamesInterned) { + checkInterning(namespace); + } + String prefix = getPrefix(namespace, false, true); + // assert( prefix != null); + // if(prefix.length() == 0) { + if (prefix == null) { + // needs to declare prefix to hold default namespace + // NOTE: attributes such as a='b' are in NO namespace + prefix = generatePrefix(namespace); + } + out.write(prefix); + out.write(':'); + // if(prefix.length() > 0) { + // out.write(prefix); + // out.write(':'); + // } + } + // assert name != null; + out.write(name); + out.write('='); + // assert value != null; + out.write(attributeUseApostrophe ? '\'' : '"'); + writeAttributeValue(value, out); + out.write(attributeUseApostrophe ? '\'' : '"'); + return this; + } + + protected void closeStartTag() throws IOException { + if (finished) { + throw new IllegalArgumentException( + "trying to write past already finished output" + + getLocation()); + } + if (seenBracket) { + seenBracket = seenBracketBracket = false; + } + if (startTagIncomplete || setPrefixCalled) { + if (setPrefixCalled) { + throw new IllegalArgumentException( + "startTag() must be called immediately after setPrefix()" + + getLocation()); + } + if (!startTagIncomplete) { + throw new IllegalArgumentException( + "trying to close start tag that is not opened" + + getLocation()); + } + + // write all namespace delcarations! + writeNamespaceDeclarations(); + out.write('>'); + elNamespaceCount[depth] = namespaceEnd; + startTagIncomplete = false; + } + } + + protected void writeNamespaceDeclarations() throws IOException { + // int start = elNamespaceCount[ depth - 1 ]; + for (int i = elNamespaceCount[depth - 1]; i < namespaceEnd; i++) { + if (doIndent && namespaceUri[i].length() > 40) { + writeIndent(); + out.write(" "); + } + if (namespacePrefix[i] != "") { + out.write(" xmlns:"); + out.write(namespacePrefix[i]); + out.write('='); + } else { + out.write(" xmlns="); + } + out.write(attributeUseApostrophe ? '\'' : '"'); + + // NOTE: escaping of namespace value the same way as attributes!!!! + writeAttributeValue(namespaceUri[i], out); + + out.write(attributeUseApostrophe ? '\'' : '"'); + } + } + + @Override + public XmlSerializer endTag(String namespace, String name) + throws IOException { + // check that level is valid + // //assert namespace != null; + // if(namespace != null) { + // namespace = namespace.intern(); + // } + seenBracket = seenBracketBracket = false; + if (namespace != null) { + if (!namesInterned) { + namespace = namespace.intern(); + } else if (checkNamesInterned) { + checkInterning(namespace); + } + } + + if (namespace != elNamespace[depth]) { + throw new IllegalArgumentException("expected namespace " + + printable(elNamespace[depth]) + " and not " + + printable(namespace) + getLocation()); + } + if (name == null) { + throw new IllegalArgumentException("end tag name can not be null" + + getLocation()); + } + if (checkNamesInterned && namesInterned) { + checkInterning(name); + } + String startTagName = elName[depth]; + if ((!namesInterned && !name.equals(startTagName)) + || (namesInterned && name != startTagName)) { + throw new IllegalArgumentException("expected element name " + + printable(elName[depth]) + " and not " + printable(name) + + getLocation()); + } + if (startTagIncomplete) { + writeNamespaceDeclarations(); + out.write(" />"); // space is added to make it easier to work in + // XHTML!!! + --depth; + } else { + // assert startTagIncomplete == false; + if (doIndent && seenTag) { + writeIndent(); + } + out.write(" 0) { + out.write(startTagPrefix); + out.write(':'); + } + + // if(namespace != null && namespace.length() > 0) { + // //TODO prefix should be alredy known from matching start tag ... + // final String prefix = lookupOrDeclarePrefix( namespace ); + // //assert( prefix != null); + // if(prefix.length() > 0) { + // out.write(prefix); + // out.write(':'); + // } + // } + out.write(name); + out.write('>'); + --depth; + } + namespaceEnd = elNamespaceCount[depth]; + startTagIncomplete = false; + seenTag = true; + return this; + } + + @Override + public XmlSerializer text(String text) throws IOException { + // assert text != null; + if (startTagIncomplete || setPrefixCalled) + closeStartTag(); + if (doIndent && seenTag) + seenTag = false; + writeElementContent(text, out); + return this; + } + + @Override + public XmlSerializer text(char[] buf, int start, int len) + throws IOException { + if (startTagIncomplete || setPrefixCalled) + closeStartTag(); + if (doIndent && seenTag) + seenTag = false; + writeElementContent(buf, start, len, out); + return this; + } + + @Override + public void cdsect(String text) throws IOException { + if (startTagIncomplete || setPrefixCalled || seenBracket) + closeStartTag(); + if (doIndent && seenTag) + seenTag = false; + out.write(""); + } + + @Override + public void entityRef(String text) throws IOException { + if (startTagIncomplete || setPrefixCalled || seenBracket) + closeStartTag(); + if (doIndent && seenTag) + seenTag = false; + out.write('&'); + out.write(text); // escape? + out.write(';'); + } + + @Override + public void processingInstruction(String text) throws IOException { + if (startTagIncomplete || setPrefixCalled || seenBracket) + closeStartTag(); + if (doIndent && seenTag) + seenTag = false; + out.write(""); + } + + @Override + public void comment(String text) throws IOException { + if (startTagIncomplete || setPrefixCalled || seenBracket) + closeStartTag(); + if (doIndent && seenTag) + seenTag = false; + out.write(""); + } + + @Override + public void docdecl(String text) throws IOException { + if (startTagIncomplete || setPrefixCalled || seenBracket) + closeStartTag(); + if (doIndent && seenTag) + seenTag = false; + out.write(""); + } + + @Override + public void ignorableWhitespace(String text) throws IOException { + if (startTagIncomplete || setPrefixCalled || seenBracket) + closeStartTag(); + if (doIndent && seenTag) + seenTag = false; + if (text.length() == 0) { + throw new IllegalArgumentException( + "empty string is not allowed for ignorable whitespace" + + getLocation()); + } + out.write(text); // no escape? + } + + @Override + public void flush() throws IOException { + if (!finished && startTagIncomplete) + closeStartTag(); + out.flush(); + } + + // --- utility methods + + protected void writeAttributeValue(String value, Writer out) + throws IOException { + // .[apostrophe and <, & escaped], + final char quot = attributeUseApostrophe ? '\'' : '"'; + final String quotEntity = attributeUseApostrophe ? "'" : """; + + int pos = 0; + for (int i = 0; i < value.length(); i++) { + char ch = value.charAt(i); + if (ch == '&') { + if (i > pos) + out.write(value.substring(pos, i)); + out.write("&"); + pos = i + 1; + } + if (ch == '<') { + if (i > pos) + out.write(value.substring(pos, i)); + out.write("<"); + pos = i + 1; + } else if (ch == quot) { + if (i > pos) + out.write(value.substring(pos, i)); + out.write(quotEntity); + pos = i + 1; + } else if (ch < 32) { + // in XML 1.0 only legal character are #x9 | #xA | #xD + // and they must be escaped otherwise in attribute value they + // are normalized to spaces + if (ch == 13 || ch == 10 || ch == 9) { + if (i > pos) + out.write(value.substring(pos, i)); + out.write("&#"); + out.write(Integer.toString(ch)); + out.write(';'); + pos = i + 1; + } else { + if (TRACE_ESCAPING) + System.err.println(getClass().getName() + + " DEBUG ATTR value.len=" + value.length() + + " " + printable(value)); + + throw new IllegalStateException( + // "character "+Integer.toString(ch)+" is not allowed in output"+getLocation()); + "character " + printable(ch) + " (" + + Integer.toString(ch) + + ") is not allowed in output" + + getLocation() + " (attr value=" + + printable(value) + ")"); + // in XML 1.1 legal are [#x1-#xD7FF] + // if(ch > 0) { + // if(i > pos) out.write(text.substring(pos, i)); + // out.write("&#"); + // out.write(Integer.toString(ch)); + // out.write(';'); + // pos = i + 1; + // } else { + // throw new IllegalStateException( + // "character zero is not allowed in XML 1.1 output"+getLocation()); + // } + } + } + } + if (pos > 0) { + out.write(value.substring(pos)); + } else { + out.write(value); // this is shortcut to the most common case + } + + } + + protected void writeElementContent(String text, Writer out) + throws IOException { + // esccape '<', '&', ']]>', <32 if necessary + int pos = 0; + for (int i = 0; i < text.length(); i++) { + // TODO: check if doing char[] text.getChars() would be faster than + // getCharAt(i) ... + char ch = text.charAt(i); + if (ch == ']') { + if (seenBracket) { + seenBracketBracket = true; + } else { + seenBracket = true; + } + } else { + if (ch == '&') { + if (i > pos) + out.write(text.substring(pos, i)); + out.write("&"); + pos = i + 1; + } else if (ch == '<') { + if (i > pos) + out.write(text.substring(pos, i)); + out.write("<"); + pos = i + 1; + } else if (seenBracketBracket && ch == '>') { + if (i > pos) + out.write(text.substring(pos, i)); + out.write(">"); + pos = i + 1; + } else if (ch < 32) { + // in XML 1.0 only legal character are #x9 | #xA | #xD + if (ch == 9 || ch == 10 || ch == 13) { + // pass through + + // } else if(ch == 13) { //escape + // if(i > pos) out.write(text.substring(pos, i)); + // out.write("&#"); + // out.write(Integer.toString(ch)); + // out.write(';'); + // pos = i + 1; + } else { + if (TRACE_ESCAPING) + System.err.println(getClass().getName() + + " DEBUG TEXT value.len=" + text.length() + + " " + printable(text)); + throw new IllegalStateException("character " + + Integer.toString(ch) + + " is not allowed in output" + getLocation() + + " (text value=" + printable(text) + ")"); + // in XML 1.1 legal are [#x1-#xD7FF] + // if(ch > 0) { + // if(i > pos) out.write(text.substring(pos, i)); + // out.write("&#"); + // out.write(Integer.toString(ch)); + // out.write(';'); + // pos = i + 1; + // } else { + // throw new IllegalStateException( + // "character zero is not allowed in XML 1.1 output"+getLocation()); + // } + } + } + if (seenBracket) { + seenBracketBracket = seenBracket = false; + } + + } + } + if (pos > 0) { + out.write(text.substring(pos)); + } else { + out.write(text); // this is shortcut to the most common case + } + + } + + protected void writeElementContent(char[] buf, int off, int len, Writer out) + throws IOException { + // esccape '<', '&', ']]>' + final int end = off + len; + int pos = off; + for (int i = off; i < end; i++) { + final char ch = buf[i]; + if (ch == ']') { + if (seenBracket) { + seenBracketBracket = true; + } else { + seenBracket = true; + } + } else { + if (ch == '&') { + if (i > pos) { + out.write(buf, pos, i - pos); + } + out.write("&"); + pos = i + 1; + } else if (ch == '<') { + if (i > pos) { + out.write(buf, pos, i - pos); + } + out.write("<"); + pos = i + 1; + + } else if (seenBracketBracket && ch == '>') { + if (i > pos) { + out.write(buf, pos, i - pos); + } + out.write(">"); + pos = i + 1; + } else if (ch < 32) { + // in XML 1.0 only legal character are #x9 | #xA | #xD + if (ch == 9 || ch == 10 || ch == 13) { + // pass through + + // } else if(ch == 13 ) { //if(ch == '\r') { + // if(i > pos) { + // out.write(buf, pos, i - pos); + // } + // out.write("&#"); + // out.write(Integer.toString(ch)); + // out.write(';'); + // pos = i + 1; + } else { + if (TRACE_ESCAPING) + System.err.println(getClass().getName() + + " DEBUG TEXT value.len=" + len + " " + + printable(new String(buf, off, len))); + throw new IllegalStateException("character " + + printable(ch) + " (" + Integer.toString(ch) + + ") is not allowed in output" + getLocation()); + // in XML 1.1 legal are [#x1-#xD7FF] + // if(ch > 0) { + // if(i > pos) out.write(text.substring(pos, i)); + // out.write("&#"); + // out.write(Integer.toString(ch)); + // out.write(';'); + // pos = i + 1; + // } else { + // throw new IllegalStateException( + // "character zero is not allowed in XML 1.1 output"+getLocation()); + // } + } + } + if (seenBracket) { + seenBracketBracket = seenBracket = false; + } + // assert seenBracketBracket == seenBracket == false; + } + } + if (end > pos) { + out.write(buf, pos, end - pos); + } + } + + /** simple utility method -- good for debugging */ + protected static final String printable(String s) { + if (s == null) + return "null"; + StringBuffer retval = new StringBuffer(s.length() + 16); + retval.append("'"); + char ch; + for (int i = 0; i < s.length(); i++) { + addPrintable(retval, s.charAt(i)); + } + retval.append("'"); + return retval.toString(); + } + + protected static final String printable(char ch) { + StringBuffer retval = new StringBuffer(); + addPrintable(retval, ch); + return retval.toString(); + } + + private static void addPrintable(StringBuffer retval, char ch) { + switch (ch) { + case '\b': + retval.append("\\b"); + break; + case '\t': + retval.append("\\t"); + break; + case '\n': + retval.append("\\n"); + break; + case '\f': + retval.append("\\f"); + break; + case '\r': + retval.append("\\r"); + break; + case '\"': + retval.append("\\\""); + break; + case '\'': + retval.append("\\\'"); + break; + case '\\': + retval.append("\\\\"); + break; + default: + if (ch < 0x20 || ch > 0x7e) { + final String ss = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + + ss.substring(ss.length() - 4, ss.length())); + } else { + retval.append(ch); + } + } + } + +} diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java index fe54793a..19831387 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java @@ -26,175 +26,173 @@ import org.junit.*; import static org.junit.Assert.*; import org.xml.sax.SAXException; - /** * @author Ryszard Wiśniewski */ public class BuildAndDecodeTest { - @BeforeClass - public static void beforeClass() throws Exception, BrutException { - sTmpDir = new ExtFile(OS.createTempDirectory()); - sTestOrigDir = new ExtFile(sTmpDir, "testapp-orig"); - sTestNewDir = new ExtFile(sTmpDir, "testapp-new"); - LOGGER.info("Unpacking testapp..."); - TestUtils.copyResourceDir(BuildAndDecodeTest.class, - "brut/apktool/testapp/", sTestOrigDir); - } - - @AfterClass - public static void afterClass() throws BrutException { - OS.rmdir(sTmpDir); - } - - @Test - public void isAaptInstalledTest() throws Exception { - assertEquals(true, isAaptPresent()); - } - - - @Test - public void encodeAndDecodeTest() throws BrutException, IOException { + @BeforeClass + public static void beforeClass() throws Exception, BrutException { + sTmpDir = new ExtFile(OS.createTempDirectory()); + sTestOrigDir = new ExtFile(sTmpDir, "testapp-orig"); + sTestNewDir = new ExtFile(sTmpDir, "testapp-new"); + LOGGER.info("Unpacking testapp..."); + TestUtils.copyResourceDir(BuildAndDecodeTest.class, + "brut/apktool/testapp/", sTestOrigDir); + } - LOGGER.info("Building testapp.apk..."); - File testApk = new File(sTmpDir, "testapp.apk"); - ExtFile blank = null; - new Androlib().build(sTestOrigDir, testApk, BuildAndDecodeTest.returnStock(),blank,""); + @AfterClass + public static void afterClass() throws BrutException { + OS.rmdir(sTmpDir); + } - LOGGER.info("Decoding testapp.apk..."); - ApkDecoder apkDecoder = new ApkDecoder(testApk); - apkDecoder.setOutDir(sTestNewDir); - apkDecoder.decode(); - } + @Test + public void isAaptInstalledTest() throws Exception { + assertEquals(true, isAaptPresent()); + } + @Test + public void encodeAndDecodeTest() throws BrutException, IOException { - @Test - public void valuesArraysTest() throws BrutException { - compareValuesFiles("values-mcc001/arrays.xml"); - compareValuesFiles("values-mcc002/arrays.xml"); - } + LOGGER.info("Building testapp.apk..."); + File testApk = new File(sTmpDir, "testapp.apk"); + ExtFile blank = null; + new Androlib().build(sTestOrigDir, testApk, + BuildAndDecodeTest.returnStock(), blank, ""); - @Test - public void valuesBoolsTest() throws BrutException { - compareValuesFiles("values-mcc001/bools.xml"); - } + LOGGER.info("Decoding testapp.apk..."); + ApkDecoder apkDecoder = new ApkDecoder(testApk); + apkDecoder.setOutDir(sTestNewDir); + apkDecoder.decode(); + } - @Test - public void valuesColorsTest() throws BrutException { - compareValuesFiles("values-mcc001/colors.xml"); - } + @Test + public void valuesArraysTest() throws BrutException { + compareValuesFiles("values-mcc001/arrays.xml"); + compareValuesFiles("values-mcc002/arrays.xml"); + } - @Test - public void valuesDimensTest() throws BrutException { - compareValuesFiles("values-mcc001/dimens.xml"); - } + @Test + public void valuesBoolsTest() throws BrutException { + compareValuesFiles("values-mcc001/bools.xml"); + } - @Test - public void valuesIdsTest() throws BrutException { - compareValuesFiles("values-mcc001/ids.xml"); - } + @Test + public void valuesColorsTest() throws BrutException { + compareValuesFiles("values-mcc001/colors.xml"); + } - @Test - public void valuesIntegersTest() throws BrutException { - compareValuesFiles("values-mcc001/integers.xml"); - } + @Test + public void valuesDimensTest() throws BrutException { + compareValuesFiles("values-mcc001/dimens.xml"); + } - @Test - public void valuesStringsTest() throws BrutException { - compareValuesFiles("values-mcc001/strings.xml"); - } + @Test + public void valuesIdsTest() throws BrutException { + compareValuesFiles("values-mcc001/ids.xml"); + } - @Test - public void valuesReferencesTest() throws BrutException { - compareValuesFiles("values-mcc002/strings.xml"); - } + @Test + public void valuesIntegersTest() throws BrutException { + compareValuesFiles("values-mcc001/integers.xml"); + } - @Test - public void crossTypeTest() throws BrutException { - compareValuesFiles("values-mcc003/strings.xml"); - compareValuesFiles("values-mcc003/integers.xml"); - compareValuesFiles("values-mcc003/bools.xml"); - } + @Test + public void valuesStringsTest() throws BrutException { + compareValuesFiles("values-mcc001/strings.xml"); + } - @Test - public void xmlLiteralsTest() throws BrutException { - compareXmlFiles("res/xml/literals.xml"); - } + @Test + public void valuesReferencesTest() throws BrutException { + compareValuesFiles("values-mcc002/strings.xml"); + } - @Test - public void xmlReferencesTest() throws BrutException { - compareXmlFiles("res/xml/references.xml"); - } - - @Test - public void qualifiersTest() throws BrutException { - compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp" + - "-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key" + - "-navhidden-dpad/strings.xml"); - } - - private static boolean isAaptPresent() throws Exception { - boolean result = true; - try - { - Process proc = Runtime.getRuntime().exec("aapt"); - BufferedReader br = new BufferedReader(new InputStreamReader(proc.getErrorStream())); - String line = null; - while ( (line = br.readLine()) != null){} - } catch (Exception ex){ - result = false; - } - return result; - } + @Test + public void crossTypeTest() throws BrutException { + compareValuesFiles("values-mcc003/strings.xml"); + compareValuesFiles("values-mcc003/integers.xml"); + compareValuesFiles("values-mcc003/bools.xml"); + } - private void compareValuesFiles(String path) throws BrutException { - compareXmlFiles("res/" + path, - new ElementNameAndAttributeQualifier("name")); - } + @Test + public void xmlLiteralsTest() throws BrutException { + compareXmlFiles("res/xml/literals.xml"); + } - private void compareXmlFiles(String path) throws BrutException { - compareXmlFiles(path, null); - } + @Test + public void xmlReferencesTest() throws BrutException { + compareXmlFiles("res/xml/references.xml"); + } - private void compareXmlFiles(String path, - ElementQualifier qualifier) throws BrutException { - DetailedDiff diff; - try { - Reader control = new FileReader( - new File(sTestOrigDir, path)); - Reader test = new FileReader(new File(sTestNewDir, path)); + @Test + public void qualifiersTest() throws BrutException { + compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp" + + "-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key" + + "-navhidden-dpad/strings.xml"); + } - diff = new DetailedDiff(new Diff(control, test)); - } catch (SAXException ex) { - throw new BrutException(ex); - } catch (IOException ex) { - throw new BrutException(ex); - } + private static boolean isAaptPresent() throws Exception { + boolean result = true; + try { + Process proc = Runtime.getRuntime().exec("aapt"); + BufferedReader br = new BufferedReader(new InputStreamReader( + proc.getErrorStream())); + String line = null; + while ((line = br.readLine()) != null) { + } + } catch (Exception ex) { + result = false; + } + return result; + } - if (qualifier != null) { - diff.overrideElementQualifier(qualifier); - } + private void compareValuesFiles(String path) throws BrutException { + compareXmlFiles("res/" + path, new ElementNameAndAttributeQualifier( + "name")); + } - assertTrue(path + ": " + - diff.getAllDifferences().toString(), diff.similar()); - } - - private static HashMap returnStock() throws BrutException { - HashMap tmp = new HashMap(); - tmp.put("forceBuildAll", false); - tmp.put("debug", false); - tmp.put("verbose", false); - tmp.put("injectOriginal", false); - tmp.put("framework", false); - tmp.put("update", false); - - return tmp; - } + private void compareXmlFiles(String path) throws BrutException { + compareXmlFiles(path, null); + } - private static ExtFile sTmpDir; - private static ExtFile sTestOrigDir; - private static ExtFile sTestNewDir; - - private final static Logger LOGGER = - Logger.getLogger(BuildAndDecodeTest.class.getName()); + private void compareXmlFiles(String path, ElementQualifier qualifier) + throws BrutException { + DetailedDiff diff; + try { + Reader control = new FileReader(new File(sTestOrigDir, path)); + Reader test = new FileReader(new File(sTestNewDir, path)); + + diff = new DetailedDiff(new Diff(control, test)); + } catch (SAXException ex) { + throw new BrutException(ex); + } catch (IOException ex) { + throw new BrutException(ex); + } + + if (qualifier != null) { + diff.overrideElementQualifier(qualifier); + } + + assertTrue(path + ": " + diff.getAllDifferences().toString(), + diff.similar()); + } + + private static HashMap returnStock() throws BrutException { + HashMap tmp = new HashMap(); + tmp.put("forceBuildAll", false); + tmp.put("debug", false); + tmp.put("verbose", false); + tmp.put("injectOriginal", false); + tmp.put("framework", false); + tmp.put("update", false); + + return tmp; + } + + private static ExtFile sTmpDir; + private static ExtFile sTestOrigDir; + private static ExtFile sTestNewDir; + + private final static Logger LOGGER = Logger + .getLogger(BuildAndDecodeTest.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java index d6efc275..f422c165 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java @@ -1,4 +1,4 @@ - /** +/** * Copyright 2011 Ryszard Wiśniewski * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,107 +32,110 @@ import org.xmlpull.v1.*; */ public abstract class TestUtils { - public static Map parseStringsXml(File file) - throws BrutException { - try { - XmlPullParser xpp = XmlPullParserFactory.newInstance().newPullParser(); - xpp.setInput(new FileReader(file)); + public static Map parseStringsXml(File file) + throws BrutException { + try { + XmlPullParser xpp = XmlPullParserFactory.newInstance() + .newPullParser(); + xpp.setInput(new FileReader(file)); - int eventType; - String key = null; - Map map = new HashMap(); - while ((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) { - switch (eventType) { - case XmlPullParser.START_TAG: - if ("string".equals(xpp.getName())) { - int attrCount = xpp.getAttributeCount(); - for (int i = 0; i < attrCount; i++) { - if ("name".equals(xpp.getAttributeName(i))) { - key = xpp.getAttributeValue(i); - break; - } - } - } - break; - case XmlPullParser.END_TAG: - if ("string".equals(xpp.getName())) { - key = null; - } - break; - case XmlPullParser.TEXT: - if (key != null) { - map.put(key, xpp.getText()); - } - break; - } - } + int eventType; + String key = null; + Map map = new HashMap(); + while ((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) { + switch (eventType) { + case XmlPullParser.START_TAG: + if ("string".equals(xpp.getName())) { + int attrCount = xpp.getAttributeCount(); + for (int i = 0; i < attrCount; i++) { + if ("name".equals(xpp.getAttributeName(i))) { + key = xpp.getAttributeValue(i); + break; + } + } + } + break; + case XmlPullParser.END_TAG: + if ("string".equals(xpp.getName())) { + key = null; + } + break; + case XmlPullParser.TEXT: + if (key != null) { + map.put(key, xpp.getText()); + } + break; + } + } - return map; - } catch (IOException ex) { - throw new BrutException(ex); - } catch (XmlPullParserException ex) { - throw new BrutException(ex); - } - } + return map; + } catch (IOException ex) { + throw new BrutException(ex); + } catch (XmlPullParserException ex) { + throw new BrutException(ex); + } + } - /* TODO: move to brut.util.Jar - it's not possible for now, because below - * implementation uses brut.dir. I think I should merge all my projects to - * single brut.common . - */ - public static void copyResourceDir(Class class_, String dirPath, File out) - throws BrutException { - if (! out.exists()) { - out.mkdirs(); - } - copyResourceDir(class_, dirPath, new FileDirectory(out)); - } + /* + * TODO: move to brut.util.Jar - it's not possible for now, because below + * implementation uses brut.dir. I think I should merge all my projects to + * single brut.common . + */ + public static void copyResourceDir(Class class_, String dirPath, File out) + throws BrutException { + if (!out.exists()) { + out.mkdirs(); + } + copyResourceDir(class_, dirPath, new FileDirectory(out)); + } - public static void copyResourceDir(Class class_, String dirPath, - Directory out) throws BrutException { - if (class_ == null) { - class_ = Class.class; - } + public static void copyResourceDir(Class class_, String dirPath, + Directory out) throws BrutException { + if (class_ == null) { + class_ = Class.class; + } - URL dirURL = class_.getClassLoader().getResource(dirPath); - if (dirURL != null && dirURL.getProtocol().equals("file")) { - DirUtil.copyToDir(new FileDirectory(dirURL.getFile()), out); - return; - } + URL dirURL = class_.getClassLoader().getResource(dirPath); + if (dirURL != null && dirURL.getProtocol().equals("file")) { + DirUtil.copyToDir(new FileDirectory(dirURL.getFile()), out); + return; + } - if (dirURL == null) { - String className = class_.getName().replace(".", "/") + ".class"; - dirURL = class_.getClassLoader().getResource(className); - } + if (dirURL == null) { + String className = class_.getName().replace(".", "/") + ".class"; + dirURL = class_.getClassLoader().getResource(className); + } + if (dirURL.getProtocol().equals("jar")) { + String jarPath; + try { + jarPath = URLDecoder.decode( + dirURL.getPath().substring(5, + dirURL.getPath().indexOf("!")), "UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new BrutException(ex); + } + DirUtil.copyToDir(new FileDirectory(jarPath), out); + } + } - if (dirURL.getProtocol().equals("jar")) { - String jarPath; - try { - jarPath = URLDecoder.decode(dirURL.getPath().substring( - 5, dirURL.getPath().indexOf("!")), "UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw new BrutException(ex); - } - DirUtil.copyToDir(new FileDirectory(jarPath), out); - } - } + public static class ResValueElementQualifier implements ElementQualifier { + @Override + public boolean qualifyForComparison(Element control, Element test) { + String controlType = control.getTagName(); + if ("item".equals(controlType)) { + controlType = control.getAttribute("type"); + } - public static class ResValueElementQualifier implements ElementQualifier { + String testType = test.getTagName(); + if ("item".equals(testType)) { + testType = test.getAttribute("type"); + } - public boolean qualifyForComparison(Element control, Element test) { - String controlType = control.getTagName(); - if ("item".equals(controlType)) { - controlType = control.getAttribute("type"); - } - - String testType = test.getTagName(); - if ("item".equals(testType)) { - testType = test.getAttribute("type"); - } - - return controlType.equals(testType) && control.getAttribute("name") - .equals(test.getAttribute("name")); - } - } + return controlType.equals(testType) + && control.getAttribute("name").equals( + test.getAttribute("name")); + } + } }