Code cleanup of 2013

Signed-off-by: Connor Tumbleson <connor.tumbleson@gmail.com>
This commit is contained in:
Connor Tumbleson 2013-02-12 21:12:17 -06:00
parent f504ceca43
commit e82c0754de
71 changed files with 7649 additions and 7444 deletions

View File

@ -36,8 +36,8 @@ import java.util.logging.*;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class Main { public class Main {
public static void main(String[] args) public static void main(String[] args) throws IOException,
throws IOException, InterruptedException, BrutException { InterruptedException, BrutException {
try { try {
Verbosity verbosity = Verbosity.NORMAL; Verbosity verbosity = Verbosity.NORMAL;
int i; int i;
@ -132,8 +132,8 @@ public class Main {
outName = args[i + 1]; outName = args[i + 1];
} else if (args.length == i + 1) { } else if (args.length == i + 1) {
outName = args[i]; outName = args[i];
outName = outName.endsWith(".apk") ? outName = outName.endsWith(".apk") ? outName.substring(0,
outName.substring(0, outName.length() - 4) : outName + ".out"; outName.length() - 4) : outName + ".out";
outName = new File(outName).getName(); outName = new File(outName).getName();
} else { } else {
throw new InvalidArgsError(); throw new InvalidArgsError();
@ -145,24 +145,26 @@ public class Main {
try { try {
decoder.decode(); decoder.decode();
} catch (OutDirExistsException ex) { } catch (OutDirExistsException ex) {
System.out.println( System.out
"Destination directory (" + outDir.getAbsolutePath() + ") " + .println("Destination directory ("
"already exists. Use -f switch if you want to overwrite it."); + outDir.getAbsolutePath()
+ ") "
+ "already exists. Use -f switch if you want to overwrite it.");
System.exit(1); System.exit(1);
} catch (InFileNotFoundException ex) { } catch (InFileNotFoundException ex) {
System.out.println( System.out.println("Input file (" + args[i] + ") "
"Input file (" + args[i] + ") " + + "was not found or was not readable.");
"was not found or was not readable.");
System.exit(1); System.exit(1);
} catch (CantFindFrameworkResException ex) { } catch (CantFindFrameworkResException ex) {
System.out.println( System.out
"Can't find framework resources for package of id: " + .println("Can't find framework resources for package of id: "
String.valueOf(ex.getPkgId()) + ". You must install proper " + + String.valueOf(ex.getPkgId())
"framework files, see project website for more info."); + ". You must install proper "
+ "framework files, see project website for more info.");
System.exit(1); System.exit(1);
} catch (IOException ex) { } catch (IOException ex) {
System.out.println( System.out
"Could not modify file. Please ensure you have permission."); .println("Could not modify file. Please ensure you have permission.");
System.exit(1); System.exit(1);
} }
@ -225,7 +227,8 @@ public class Main {
throw new InvalidArgsError(); throw new InvalidArgsError();
} }
new Androlib().build(new File(appDirName), outFile, flags, mOrigApk, mAaptPath); new Androlib().build(new File(appDirName), outFile, flags, mOrigApk,
mAaptPath);
} }
private static void cmdInstallFramework(String[] args) private static void cmdInstallFramework(String[] args)
@ -264,74 +267,77 @@ public class Main {
} }
private static void version_print() { private static void version_print() {
System.out.println( System.out.println(Androlib.getVersion());
Androlib.getVersion());
} }
private static void usage() { private static void usage() {
System.out.println( System.out
"Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" + .println("Apktool v"
"Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>\n" + + Androlib.getVersion()
"with smali v" + ApktoolProperties.get("smaliVersion") + + " - a tool for reengineering Android apk files\n"
", and baksmali v" + ApktoolProperties.get("baksmaliVersion") + "\n" + + "Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>\n"
"Updated by @iBotPeaches <connor.tumbleson@gmail.com> \n" + + "with smali v"
"Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" + + ApktoolProperties.get("smaliVersion")
"\n" + + ", and baksmali v"
"Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n" + + ApktoolProperties.get("baksmaliVersion")
"\n" + + "\n"
"COMMANDs are:\n" + + "Updated by @iBotPeaches <connor.tumbleson@gmail.com> \n"
"\n" + + "Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n"
" d[ecode] [OPTS] <file.apk> [<dir>]\n" + + "\n"
" Decode <file.apk> to <dir>.\n" + + "Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n"
"\n" + + "\n"
" OPTS:\n" + + "COMMANDs are:\n"
"\n" + + "\n"
" -s, --no-src\n" + + " d[ecode] [OPTS] <file.apk> [<dir>]\n"
" Do not decode sources.\n" + + " Decode <file.apk> to <dir>.\n"
" -r, --no-res\n" + + "\n"
" Do not decode resources.\n" + + " OPTS:\n"
" -d, --debug\n" + + "\n"
" Decode in debug mode. Check project page for more info.\n" + + " -s, --no-src\n"
" -b, --no-debug-info\n" + + " Do not decode sources.\n"
" Baksmali -- don't write out debug info (.local, .param, .line, etc.)\n" + + " -r, --no-res\n"
" -f, --force\n" + + " Do not decode resources.\n"
" Force delete destination directory.\n" + + " -d, --debug\n"
" -t <tag>, --frame-tag <tag>\n" + + " Decode in debug mode. Check project page for more info.\n"
" Try to use framework files tagged by <tag>.\n" + + " -b, --no-debug-info\n"
" --frame-path <dir>\n" + + " Baksmali -- don't write out debug info (.local, .param, .line, etc.)\n"
" Use the specified directory for framework files\n" + + " -f, --force\n"
" --keep-broken-res\n" + + " Force delete destination directory.\n"
" Use if there was an error and some resources were dropped, e.g.:\n" + + " -t <tag>, --frame-tag <tag>\n"
" \"Invalid config flags detected. Dropping resources\", but you\n" + + " Try to use framework files tagged by <tag>.\n"
" want to decode them anyway, even with errors. You will have to\n" + + " --frame-path <dir>\n"
" fix them manually before building." + + " Use the specified directory for framework files\n"
"\n\n" + + " --keep-broken-res\n"
" b[uild] [OPTS] [<app_path>] [<out_file>]\n" + + " Use if there was an error and some resources were dropped, e.g.:\n"
" Build an apk from already decoded application located in <app_path>.\n" + + " \"Invalid config flags detected. Dropping resources\", but you\n"
"\n" + + " want to decode them anyway, even with errors. You will have to\n"
" It will automatically detect, whether files was changed and perform\n" + + " fix them manually before building."
" needed steps only.\n" + + "\n\n"
"\n" + + " b[uild] [OPTS] [<app_path>] [<out_file>]\n"
" If you omit <app_path> then current directory will be used.\n" + + " Build an apk from already decoded application located in <app_path>.\n"
" If you omit <out_file> then <app_path>/dist/<name_of_original.apk>\n" + + "\n"
" will be used.\n" + + " It will automatically detect, whether files was changed and perform\n"
"\n" + + " needed steps only.\n"
" OPTS:\n" + + "\n"
"\n" + + " If you omit <app_path> then current directory will be used.\n"
" -f, --force-all\n" + + " If you omit <out_file> then <app_path>/dist/<name_of_original.apk>\n"
" Skip changes detection and build all files.\n" + + " will be used.\n"
" -d, --debug\n" + + "\n"
" Build in debug mode. Check project page for more info.\n" + + " OPTS:\n"
" -a, --aapt\n" + + "\n"
" Loads aapt from specified location.\n" + + " -f, --force-all\n"
"\n" + + " Skip changes detection and build all files.\n"
" if|install-framework <framework.apk> [<tag>] --frame-path [<location>] \n" + + " -d, --debug\n"
" Install framework file to your system.\n" + + " Build in debug mode. Check project page for more info.\n"
"\n" + + " -a, --aapt\n"
"For additional info, see: http://code.google.com/p/android-apktool/" + + " Loads aapt from specified location.\n"
"\n" + + "\n"
"For smali/baksmali info, see: http://code.google.com/p/smali/" + " if|install-framework <framework.apk> [<tag>] --frame-path [<location>] \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 setupLogging(Verbosity verbosity) { private static void setupLogging(Verbosity verbosity) {

View File

@ -22,9 +22,9 @@ import android.util.AttributeSet;
/** /**
* The XML parsing interface returned for an XML resource. This is a standard * The XML parsing interface returned for an XML resource. This is a standard
* XmlPullParser interface, as well as an extended AttributeSet interface and * XmlPullParser interface, as well as an extended AttributeSet interface and an
* an additional close() method on this interface for the client to indicate * additional close() method on this interface for the client to indicate when
* when it is done reading the resource. * it is done reading the resource.
*/ */
public interface XmlResourceParser extends XmlPullParser, AttributeSet { public interface XmlResourceParser extends XmlPullParser, AttributeSet {
/** /**
@ -33,4 +33,3 @@ public interface XmlResourceParser extends XmlPullParser, AttributeSet {
*/ */
public void close(); public void close();
} }

View File

@ -21,29 +21,57 @@ package android.util;
*/ */
public interface AttributeSet { public interface AttributeSet {
int getAttributeCount(); int getAttributeCount();
String getAttributeName(int index); String getAttributeName(int index);
String getAttributeValue(int index); String getAttributeValue(int index);
String getPositionDescription(); String getPositionDescription();
int getAttributeNameResource(int index); int getAttributeNameResource(int index);
int getAttributeListValue(int index, String options[], int defaultValue); int getAttributeListValue(int index, String options[], int defaultValue);
boolean getAttributeBooleanValue(int index, boolean defaultValue); boolean getAttributeBooleanValue(int index, boolean defaultValue);
int getAttributeResourceValue(int index, int defaultValue); int getAttributeResourceValue(int index, int defaultValue);
int getAttributeIntValue(int index, int defaultValue); int getAttributeIntValue(int index, int defaultValue);
int getAttributeUnsignedIntValue(int index, int defaultValue); int getAttributeUnsignedIntValue(int index, int defaultValue);
float getAttributeFloatValue(int index, float defaultValue); float getAttributeFloatValue(int index, float defaultValue);
String getIdAttribute(); String getIdAttribute();
String getClassAttribute(); String getClassAttribute();
int getIdAttributeResourceValue(int index); int getIdAttributeResourceValue(int index);
int getStyleAttribute(); int getStyleAttribute();
String getAttributeValue(String namespace, String attribute); 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 getAttributeListValue(String namespace, String attribute,
int getAttributeResourceValue(String namespace,String attribute,int defaultValue); String options[], int defaultValue);
int getAttributeIntValue(String namespace,String attribute,int defaultValue);
int getAttributeUnsignedIntValue(String namespace,String attribute,int defaultValue); boolean getAttributeBooleanValue(String namespace, String attribute,
float getAttributeFloatValue(String namespace,String attribute,float defaultValue); 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 // TODO: remove
int getAttributeValueType(int index); int getAttributeValueType(int index);
int getAttributeValueData(int index); int getAttributeValueData(int index);
} }

View File

@ -26,58 +26,86 @@ public class TypedValue {
/** The <var>data</var> field holds a resource identifier. */ /** The <var>data</var> field holds a resource identifier. */
public static final int TYPE_REFERENCE = 0x01; public static final int TYPE_REFERENCE = 0x01;
/** The <var>data</var> field holds an attribute resource /**
* identifier (referencing an attribute in the current theme * The <var>data</var> field holds an attribute resource identifier
* style, not a resource entry). */ * (referencing an attribute in the current theme style, not a resource
* entry).
*/
public static final int TYPE_ATTRIBUTE = 0x02; public static final int TYPE_ATTRIBUTE = 0x02;
/** The <var>string</var> field holds string data. In addition, if /**
* <var>data</var> is non-zero then it is the string block * The <var>string</var> field holds string data. In addition, if
* index of the string and <var>assetCookie</var> is the set of * <var>data</var> is non-zero then it is the string block index of the
* assets the string came from. */ * string and <var>assetCookie</var> is the set of assets the string came
* from.
*/
public static final int TYPE_STRING = 0x03; public static final int TYPE_STRING = 0x03;
/** The <var>data</var> field holds an IEEE 754 floating point number. */ /** The <var>data</var> field holds an IEEE 754 floating point number. */
public static final int TYPE_FLOAT = 0x04; public static final int TYPE_FLOAT = 0x04;
/** The <var>data</var> field holds a complex number encoding a /**
* dimension value. */ * The <var>data</var> field holds a complex number encoding a dimension
* value.
*/
public static final int TYPE_DIMENSION = 0x05; public static final int TYPE_DIMENSION = 0x05;
/** The <var>data</var> field holds a complex number encoding a fraction /**
* of a container. */ * The <var>data</var> field holds a complex number encoding a fraction of a
* container.
*/
public static final int TYPE_FRACTION = 0x06; 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 * Identifies the start of plain integer values. Any type value from this to
* <var>data</var> field holds a generic integer value. */ * {@link #TYPE_LAST_INT} means the <var>data</var> field holds a generic
* integer value.
*/
public static final int TYPE_FIRST_INT = 0x10; public static final int TYPE_FIRST_INT = 0x10;
/** The <var>data</var> field holds a number that was /**
* originally specified in decimal. */ * The <var>data</var> field holds a number that was originally specified in
* decimal.
*/
public static final int TYPE_INT_DEC = 0x10; public static final int TYPE_INT_DEC = 0x10;
/** The <var>data</var> field holds a number that was /**
* originally specified in hexadecimal (0xn). */ * The <var>data</var> field holds a number that was originally specified in
* hexadecimal (0xn).
*/
public static final int TYPE_INT_HEX = 0x11; public static final int TYPE_INT_HEX = 0x11;
/** The <var>data</var> field holds 0 or 1 that was originally /**
* specified as "false" or "true". */ * The <var>data</var> field holds 0 or 1 that was originally specified as
* "false" or "true".
*/
public static final int TYPE_INT_BOOLEAN = 0x12; public static final int TYPE_INT_BOOLEAN = 0x12;
/** Identifies the start of integer values that were specified as /**
* color constants (starting with '#'). */ * Identifies the start of integer values that were specified as color
* constants (starting with '#').
*/
public static final int TYPE_FIRST_COLOR_INT = 0x1c; public static final int TYPE_FIRST_COLOR_INT = 0x1c;
/** The <var>data</var> field holds a color that was originally /**
* specified as #aarrggbb. */ * The <var>data</var> field holds a color that was originally specified as
* #aarrggbb.
*/
public static final int TYPE_INT_COLOR_ARGB8 = 0x1c; public static final int TYPE_INT_COLOR_ARGB8 = 0x1c;
/** The <var>data</var> field holds a color that was originally /**
* specified as #rrggbb. */ * The <var>data</var> field holds a color that was originally specified as
* #rrggbb.
*/
public static final int TYPE_INT_COLOR_RGB8 = 0x1d; public static final int TYPE_INT_COLOR_RGB8 = 0x1d;
/** The <var>data</var> field holds a color that was originally /**
* specified as #argb. */ * The <var>data</var> field holds a color that was originally specified as
* #argb.
*/
public static final int TYPE_INT_COLOR_ARGB4 = 0x1e; public static final int TYPE_INT_COLOR_ARGB4 = 0x1e;
/** The <var>data</var> field holds a color that was originally /**
* specified as #rgb. */ * The <var>data</var> field holds a color that was originally specified as
* #rgb.
*/
public static final int TYPE_INT_COLOR_RGB4 = 0x1f; public static final int TYPE_INT_COLOR_RGB4 = 0x1f;
/** Identifies the end of integer values that were specified as color /**
* constants. */ * Identifies the end of integer values that were specified as color
* constants.
*/
public static final int TYPE_LAST_COLOR_INT = 0x1f; public static final int TYPE_LAST_COLOR_INT = 0x1f;
/** Identifies the end of plain integer values. */ /** Identifies the end of plain integer values. */
@ -87,15 +115,18 @@ public class TypedValue {
/** Complex data: bit location of unit information. */ /** Complex data: bit location of unit information. */
public static final int COMPLEX_UNIT_SHIFT = 0; 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 * Complex data: mask to extract unit information (after shifting by
* defined below. */ * {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as defined
* below.
*/
public static final int COMPLEX_UNIT_MASK = 0xf; public static final int COMPLEX_UNIT_MASK = 0xf;
/** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */ /** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */
public static final int COMPLEX_UNIT_PX = 0; public static final int COMPLEX_UNIT_PX = 0;
/** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent /**
* Pixels. */ * {@link #TYPE_DIMENSION} complex unit: Value is Device Independent Pixels.
*/
public static final int COMPLEX_UNIT_DIP = 1; public static final int COMPLEX_UNIT_DIP = 1;
/** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */ /** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */
public static final int COMPLEX_UNIT_SP = 2; public static final int COMPLEX_UNIT_SP = 2;
@ -106,18 +137,23 @@ public class TypedValue {
/** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */ /** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */
public static final int COMPLEX_UNIT_MM = 5; public static final int COMPLEX_UNIT_MM = 5;
/** {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall /**
* size. */ * {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall size.
*/
public static final int COMPLEX_UNIT_FRACTION = 0; public static final int COMPLEX_UNIT_FRACTION = 0;
/** {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. */ /** {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. */
public static final int COMPLEX_UNIT_FRACTION_PARENT = 1; 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. */ * Complex data: where the radix information is, telling where the decimal
* place appears in the mantissa.
*/
public static final int COMPLEX_RADIX_SHIFT = 4; public static final int COMPLEX_RADIX_SHIFT = 4;
/** Complex data: mask to extract radix information (after shifting by /**
* Complex data: mask to extract radix information (after shifting by
* {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point * {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point
* representations as defined below. */ * representations as defined below.
*/
public static final int COMPLEX_RADIX_MASK = 0x3; public static final int COMPLEX_RADIX_MASK = 0x3;
/** Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 */ /** Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 */
@ -131,16 +167,19 @@ public class TypedValue {
/** Complex data: bit location of mantissa information. */ /** Complex data: bit location of mantissa information. */
public static final int COMPLEX_MANTISSA_SHIFT = 8; 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; * Complex data: mask to extract mantissa information (after shifting by
* the top bit is the sign. */ * {@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; public static final int COMPLEX_MANTISSA_MASK = 0xffffff;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** /**
* If {@link #density} is equal to this value, then the density should be * 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}. * treated as the system's default density value:
* {@link DisplayMetrics#DENSITY_DEFAULT}.
*/ */
public static final int DENSITY_DEFAULT = 0; public static final int DENSITY_DEFAULT = 0;
@ -152,16 +191,16 @@ public class TypedValue {
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** The type held by this value, as defined by the constants here. /**
* This tells you how to interpret the other fields in the object. */ * 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; public int type;
private static final float MANTISSA_MULT = private static final float MANTISSA_MULT = 1.0f / (1 << TypedValue.COMPLEX_MANTISSA_SHIFT);
1.0f / (1<<TypedValue.COMPLEX_MANTISSA_SHIFT);
private static final float[] RADIX_MULTS = new float[] { private static final float[] RADIX_MULTS = new float[] {
1.0f * MANTISSA_MULT, 1.0f / (1 << 7) * MANTISSA_MULT, 1.0f * MANTISSA_MULT, 1.0f / (1 << 7) * MANTISSA_MULT,
1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT 1.0f / (1 << 15) * MANTISSA_MULT, 1.0f / (1 << 23) * MANTISSA_MULT };
};
/** /**
* Retrieve the base value from a complex data integer. This uses the * Retrieve the base value from a complex data integer. This uses the
@ -169,37 +208,34 @@ public class TypedValue {
* the data to compute a floating point representation of the number they * the data to compute a floating point representation of the number they
* describe. The units are ignored. * describe. The units are ignored.
* *
* @param complex A complex data value. * @param complex
* A complex data value.
* *
* @return A floating point value corresponding to the complex data. * @return A floating point value corresponding to the complex data.
*/ */
public static float complexToFloat(int complex) public static float complexToFloat(int complex) {
{ return (complex & (TypedValue.COMPLEX_MANTISSA_MASK << TypedValue.COMPLEX_MANTISSA_SHIFT))
return (complex&(TypedValue.COMPLEX_MANTISSA_MASK
<<TypedValue.COMPLEX_MANTISSA_SHIFT))
* RADIX_MULTS[(complex >> TypedValue.COMPLEX_RADIX_SHIFT) * RADIX_MULTS[(complex >> TypedValue.COMPLEX_RADIX_SHIFT)
& TypedValue.COMPLEX_RADIX_MASK]; & TypedValue.COMPLEX_RADIX_MASK];
} }
private static final String[] DIMENSION_UNIT_STRS = new String[] { private static final String[] DIMENSION_UNIT_STRS = new String[] { "px",
"px", "dip", "sp", "pt", "in", "mm" "dip", "sp", "pt", "in", "mm" };
}; private static final String[] FRACTION_UNIT_STRS = new String[] { "%", "%p" };
private static final String[] FRACTION_UNIT_STRS = new String[] {
"%", "%p"
};
/** /**
* Perform type conversion as per {@link #coerceToString()} on an * Perform type conversion as per {@link #coerceToString()} on an explicitly
* explicitly supplied type and data. * supplied type and data.
* *
* @param type The data type identifier. * @param type
* @param data The data value. * The data type identifier.
* @param data
* The data value.
* *
* @return String The coerced string value. If the value is * @return String The coerced string value. If the value is null or the type
* null or the type is not known, null is returned. * is not known, null is returned.
*/ */
public static final String coerceToString(int type, int data) public static final String coerceToString(int type, int data) {
{
switch (type) { switch (type) {
case TYPE_NULL: case TYPE_NULL:
return null; return null;
@ -210,11 +246,13 @@ public class TypedValue {
case TYPE_FLOAT: case TYPE_FLOAT:
return Float.toString(Float.intBitsToFloat(data)); return Float.toString(Float.intBitsToFloat(data));
case TYPE_DIMENSION: case TYPE_DIMENSION:
return Float.toString(complexToFloat(data)) + DIMENSION_UNIT_STRS[ return Float.toString(complexToFloat(data))
(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; + DIMENSION_UNIT_STRS[(data >> COMPLEX_UNIT_SHIFT)
& COMPLEX_UNIT_MASK];
case TYPE_FRACTION: case TYPE_FRACTION:
return Float.toString(complexToFloat(data)*100) + FRACTION_UNIT_STRS[ return Float.toString(complexToFloat(data) * 100)
(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; + FRACTION_UNIT_STRS[(data >> COMPLEX_UNIT_SHIFT)
& COMPLEX_UNIT_MASK];
case TYPE_INT_HEX: case TYPE_INT_HEX:
return "0x" + Integer.toHexString(data); return "0x" + Integer.toHexString(data);
case TYPE_INT_BOOLEAN: case TYPE_INT_BOOLEAN:
@ -232,10 +270,12 @@ public class TypedValue {
res = res.substring(2); res = res.substring(2);
break; break;
case TYPE_INT_COLOR_ARGB4:// #AARRGGBB->#ARGB case TYPE_INT_COLOR_ARGB4:// #AARRGGBB->#ARGB
res = new StringBuffer().append(vals[0]).append(vals[2]).append(vals[4]).append(vals[6]).toString(); res = new StringBuffer().append(vals[0]).append(vals[2])
.append(vals[4]).append(vals[6]).toString();
break; break;
case TYPE_INT_COLOR_RGB4:// #FFRRGGBB->#RGB case TYPE_INT_COLOR_RGB4:// #FFRRGGBB->#RGB
res = new StringBuffer().append(vals[2]).append(vals[4]).append(vals[6]).toString(); res = new StringBuffer().append(vals[2]).append(vals[4])
.append(vals[6]).toString();
break; break;
} }
return "#" + res; return "#" + res;
@ -247,12 +287,10 @@ public class TypedValue {
res = Integer.toString(data); res = Integer.toString(data);
break; break;
// defined before // defined before
/*case TYPE_INT_HEX: /*
res = "0x" + Integer.toHexString(data); * case TYPE_INT_HEX: res = "0x" + Integer.toHexString(data); break;
break; * case TYPE_INT_BOOLEAN: res = (data != 0) ? "true":"false"; break;
case TYPE_INT_BOOLEAN: */
res = (data != 0) ? "true":"false";
break;*/
} }
return res; return res;
} }

View File

@ -46,7 +46,8 @@ public class Androlib {
return mAndRes.getResTable(apkFile, true); return mAndRes.getResTable(apkFile, true);
} }
public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) throws AndrolibException { public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg)
throws AndrolibException {
return mAndRes.getResTable(apkFile, loadMainPkg); return mAndRes.getResTable(apkFile, loadMainPkg);
} }
@ -64,8 +65,8 @@ public class Androlib {
} }
} }
public void decodeSourcesSmali(File apkFile, File outDir, boolean debug, boolean bakdeb) public void decodeSourcesSmali(File apkFile, File outDir, boolean debug,
throws AndrolibException { boolean bakdeb) throws AndrolibException {
try { try {
File smaliDir = new File(outDir, SMALI_DIRNAME); File smaliDir = new File(outDir, SMALI_DIRNAME);
OS.rmdir(smaliDir); OS.rmdir(smaliDir);
@ -148,7 +149,8 @@ public class Androlib {
if (writer != null) { if (writer != null) {
try { try {
writer.close(); writer.close();
} catch (IOException ex) {} } catch (IOException ex) {
}
} }
} }
} }
@ -166,24 +168,28 @@ public class Androlib {
if (in != null) { if (in != null) {
try { try {
in.close(); in.close();
} catch (IOException ex) {} } catch (IOException ex) {
}
} }
} }
} }
public void build(File appDir, File outFile, public void build(File appDir, File outFile,
HashMap<String, Boolean> flags, ExtFile origApk, String aaptPath) throws BrutException { HashMap<String, Boolean> flags, ExtFile origApk, String aaptPath)
throws BrutException {
build(new ExtFile(appDir), outFile, flags, origApk, aaptPath); build(new ExtFile(appDir), outFile, flags, origApk, aaptPath);
} }
public void build(ExtFile appDir, File outFile, public void build(ExtFile appDir, File outFile,
HashMap<String, Boolean> flags, ExtFile origApk, String aaptPath) throws BrutException { HashMap<String, Boolean> flags, ExtFile origApk, String aaptPath)
throws BrutException {
mAaptPath = aaptPath; mAaptPath = aaptPath;
Map<String, Object> meta = readMetaFile(appDir); Map<String, Object> meta = readMetaFile(appDir);
Object t1 = meta.get("isFrameworkApk"); Object t1 = meta.get("isFrameworkApk");
flags.put("framework", t1 == null ? false : (Boolean) t1); flags.put("framework", t1 == null ? false : (Boolean) t1);
flags.put("compression", meta.get("compressionType") == null ? false : (Boolean) meta.get("compressionType")); flags.put("compression", meta.get("compressionType") == null ? false
: (Boolean) meta.get("compressionType"));
mAndRes.setSdkInfo((Map<String, String>) meta.get("sdkInfo")); mAndRes.setSdkInfo((Map<String, String>) meta.get("sdkInfo"));
// check the orig apk // check the orig apk
@ -197,8 +203,8 @@ public class Androlib {
if (outFile == null) { if (outFile == null) {
String outFileName = (String) meta.get("apkFileName"); String outFileName = (String) meta.get("apkFileName");
outFile = new File(appDir, "dist" + File.separator + outFile = new File(appDir, "dist" + File.separator
(outFileName == null ? "out.apk" : outFileName)); + (outFileName == null ? "out.apk" : outFileName));
} }
new File(appDir, APK_DIRNAME).mkdirs(); new File(appDir, APK_DIRNAME).mkdirs();
@ -213,14 +219,13 @@ public class Androlib {
throws AndrolibException { throws AndrolibException {
if (!buildSourcesRaw(appDir, flags) if (!buildSourcesRaw(appDir, flags)
&& !buildSourcesSmali(appDir, flags) && !buildSourcesSmali(appDir, flags)
&& ! buildSourcesJava(appDir, flags) && !buildSourcesJava(appDir, flags)) {
) {
LOGGER.warning("Could not find sources"); LOGGER.warning("Could not find sources");
} }
} }
public boolean buildSourcesRaw(File appDir, public boolean buildSourcesRaw(File appDir, HashMap<String, Boolean> flags)
HashMap<String, Boolean> flags) throws AndrolibException { throws AndrolibException {
try { try {
File working = new File(appDir, "classes.dex"); File working = new File(appDir, "classes.dex");
if (!working.exists()) { if (!working.exists()) {
@ -241,8 +246,8 @@ public class Androlib {
} }
} }
public boolean buildSourcesSmali(File appDir, public boolean buildSourcesSmali(File appDir, HashMap<String, Boolean> flags)
HashMap<String, Boolean> flags) throws AndrolibException { throws AndrolibException {
ExtFile smaliDir = new ExtFile(appDir, "smali"); ExtFile smaliDir = new ExtFile(appDir, "smali");
if (!smaliDir.exists()) { if (!smaliDir.exists()) {
return false; return false;
@ -259,8 +264,8 @@ public class Androlib {
return true; return true;
} }
public boolean buildSourcesJava(File appDir, public boolean buildSourcesJava(File appDir, HashMap<String, Boolean> flags)
HashMap<String, Boolean> flags) throws AndrolibException { throws AndrolibException {
File javaDir = new File(appDir, "src"); File javaDir = new File(appDir, "src");
if (!javaDir.exists()) { if (!javaDir.exists()) {
return false; return false;
@ -278,8 +283,7 @@ public class Androlib {
} }
public void buildResources(ExtFile appDir, HashMap<String, Boolean> flags, public void buildResources(ExtFile appDir, HashMap<String, Boolean> flags,
Map<String, Object> usesFramework) Map<String, Object> usesFramework) throws BrutException {
throws BrutException {
if (!buildResourcesRaw(appDir, flags) if (!buildResourcesRaw(appDir, flags)
&& !buildResourcesFull(appDir, flags, usesFramework) && !buildResourcesFull(appDir, flags, usesFramework)
&& !buildManifest(appDir, flags, usesFramework)) { && !buildManifest(appDir, flags, usesFramework)) {
@ -287,8 +291,8 @@ public class Androlib {
} }
} }
public boolean buildResourcesRaw(ExtFile appDir, HashMap<String, Boolean> flags) public boolean buildResourcesRaw(ExtFile appDir,
throws AndrolibException { HashMap<String, Boolean> flags) throws AndrolibException {
try { try {
if (!new File(appDir, "resources.arsc").exists()) { if (!new File(appDir, "resources.arsc").exists()) {
return false; return false;
@ -297,8 +301,8 @@ public class Androlib {
if (!flags.get("forceBuildAll")) { if (!flags.get("forceBuildAll")) {
LOGGER.info("Checking whether resources has changed..."); LOGGER.info("Checking whether resources has changed...");
} }
if (flags.get("forceBuildAll") || isModified( if (flags.get("forceBuildAll")
newFiles(APK_RESOURCES_FILENAMES, appDir), || isModified(newFiles(APK_RESOURCES_FILENAMES, appDir),
newFiles(APK_RESOURCES_FILENAMES, apkDir))) { newFiles(APK_RESOURCES_FILENAMES, apkDir))) {
LOGGER.info("Copying raw resources..."); LOGGER.info("Copying raw resources...");
appDir.getDirectory() appDir.getDirectory()
@ -310,8 +314,8 @@ public class Androlib {
} }
} }
public boolean buildResourcesFull(File appDir, HashMap<String, Boolean> flags, public boolean buildResourcesFull(File appDir,
Map<String, Object> usesFramework) HashMap<String, Boolean> flags, Map<String, Object> usesFramework)
throws AndrolibException { throws AndrolibException {
try { try {
if (!new File(appDir, "res").exists()) { if (!new File(appDir, "res").exists()) {
@ -321,8 +325,8 @@ public class Androlib {
LOGGER.info("Checking whether resources has changed..."); LOGGER.info("Checking whether resources has changed...");
} }
File apkDir = new File(appDir, APK_DIRNAME); File apkDir = new File(appDir, APK_DIRNAME);
if (flags.get("forceBuildAll") || isModified( if (flags.get("forceBuildAll")
newFiles(APP_RESOURCES_FILENAMES, appDir), || isModified(newFiles(APP_RESOURCES_FILENAMES, appDir),
newFiles(APK_RESOURCES_FILENAMES, apkDir))) { newFiles(APK_RESOURCES_FILENAMES, apkDir))) {
LOGGER.info("Building resources..."); LOGGER.info("Building resources...");
@ -333,18 +337,15 @@ public class Androlib {
if (!ninePatch.exists()) { if (!ninePatch.exists()) {
ninePatch = null; ninePatch = null;
} }
mAndRes.aaptPackage( mAndRes.aaptPackage(apkFile, new File(appDir,
apkFile, "AndroidManifest.xml"), new File(appDir, "res"),
new File(appDir, "AndroidManifest.xml"),
new File(appDir, "res"),
ninePatch, null, parseUsesFramework(usesFramework), ninePatch, null, parseUsesFramework(usesFramework),
flags, mAaptPath flags, mAaptPath);
);
Directory tmpDir = new ExtFile(apkFile).getDirectory(); Directory tmpDir = new ExtFile(apkFile).getDirectory();
tmpDir.copyToDir(apkDir, tmpDir.copyToDir(apkDir,
tmpDir.containsDir("res") ? APK_RESOURCES_FILENAMES : tmpDir.containsDir("res") ? APK_RESOURCES_FILENAMES
APK_RESOURCES_WITHOUT_RES_FILENAMES); : APK_RESOURCES_WITHOUT_RES_FILENAMES);
// delete tmpDir // delete tmpDir
apkFile.delete(); apkFile.delete();
@ -359,21 +360,20 @@ public class Androlib {
} }
} }
public boolean buildManifestRaw(ExtFile appDir, HashMap<String, Boolean> flags) public boolean buildManifestRaw(ExtFile appDir,
throws AndrolibException { HashMap<String, Boolean> flags) throws AndrolibException {
try { try {
File apkDir = new File(appDir, APK_DIRNAME); File apkDir = new File(appDir, APK_DIRNAME);
LOGGER.info("Copying raw AndroidManifest.xml..."); LOGGER.info("Copying raw AndroidManifest.xml...");
appDir.getDirectory() appDir.getDirectory().copyToDir(apkDir, APK_MANIFEST_FILENAMES);
.copyToDir(apkDir, APK_MANIFEST_FILENAMES);
return true; return true;
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} }
} }
public boolean buildManifest(ExtFile appDir, HashMap<String, Boolean> flags, public boolean buildManifest(ExtFile appDir,
Map<String, Object> usesFramework) HashMap<String, Boolean> flags, Map<String, Object> usesFramework)
throws BrutException { throws BrutException {
try { try {
if (!new File(appDir, "AndroidManifest.xml").exists()) { if (!new File(appDir, "AndroidManifest.xml").exists()) {
@ -383,8 +383,8 @@ public class Androlib {
LOGGER.info("Checking whether resources has changed..."); LOGGER.info("Checking whether resources has changed...");
} }
File apkDir = new File(appDir, APK_DIRNAME); File apkDir = new File(appDir, APK_DIRNAME);
if (flags.get("forceBuildAll") || isModified( if (flags.get("forceBuildAll")
newFiles(APK_MANIFEST_FILENAMES, appDir), || isModified(newFiles(APK_MANIFEST_FILENAMES, appDir),
newFiles(APK_MANIFEST_FILENAMES, apkDir))) { newFiles(APK_MANIFEST_FILENAMES, apkDir))) {
LOGGER.info("Building AndroidManifest.xml..."); LOGGER.info("Building AndroidManifest.xml...");
@ -396,13 +396,9 @@ public class Androlib {
ninePatch = null; ninePatch = null;
} }
mAndRes.aaptPackage( mAndRes.aaptPackage(apkFile, new File(appDir,
apkFile, "AndroidManifest.xml"), null, ninePatch, null,
new File(appDir, "AndroidManifest.xml"), parseUsesFramework(usesFramework), flags, mAaptPath);
null,
ninePatch, null, parseUsesFramework(usesFramework),
flags, mAaptPath
);
Directory tmpDir = new ExtFile(apkFile).getDirectory(); Directory tmpDir = new ExtFile(apkFile).getDirectory();
tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES); tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES);
@ -437,8 +433,8 @@ public class Androlib {
} }
} }
public void buildApk(File appDir, File outApk, HashMap<String, Boolean> flags) public void buildApk(File appDir, File outApk,
throws AndrolibException { HashMap<String, Boolean> flags) throws AndrolibException {
LOGGER.info("Building apk file..."); LOGGER.info("Building apk file...");
if (outApk.exists()) { if (outApk.exists()) {
outApk.delete(); outApk.delete();
@ -452,8 +448,8 @@ public class Androlib {
if (!assetDir.exists()) { if (!assetDir.exists()) {
assetDir = null; assetDir = null;
} }
mAndRes.aaptPackage(outApk, null, null, mAndRes.aaptPackage(outApk, null, null, new File(appDir, APK_DIRNAME),
new File(appDir, APK_DIRNAME), assetDir, null, flags, mAaptPath); assetDir, null, flags, mAaptPath);
// retain signature // retain signature
// aapt r (remove) // aapt r (remove)
@ -484,10 +480,9 @@ public class Androlib {
public static String getVersion() { public static String getVersion() {
String version = ApktoolProperties.get("application.version"); String version = ApktoolProperties.get("application.version");
return version.endsWith("-SNAPSHOT") ? return version.endsWith("-SNAPSHOT") ? version.substring(0,
version.substring(0, version.length() - 9) + '.' + version.length() - 9)
ApktoolProperties.get("git.commit.id.abbrev") + '.' + ApktoolProperties.get("git.commit.id.abbrev") : version;
: version;
} }
private File[] parseUsesFramework(Map<String, Object> usesFramework) private File[] parseUsesFramework(Map<String, Object> usesFramework)
@ -514,8 +509,8 @@ public class Androlib {
if (!stored.exists()) { if (!stored.exists()) {
return true; return true;
} }
return BrutIO.recursiveModifiedTime(working) > return BrutIO.recursiveModifiedTime(working) > BrutIO
BrutIO.recursiveModifiedTime(stored); .recursiveModifiedTime(stored);
} }
private boolean isModified(File[] working, File[] stored) { private boolean isModified(File[] working, File[] stored) {
@ -524,8 +519,8 @@ public class Androlib {
return true; return true;
} }
} }
return BrutIO.recursiveModifiedTime(working) > return BrutIO.recursiveModifiedTime(working) > BrutIO
BrutIO.recursiveModifiedTime(stored); .recursiveModifiedTime(stored);
} }
private File[] newFiles(String[] names, File dir) { private File[] newFiles(String[] names, File dir) {
@ -540,21 +535,19 @@ public class Androlib {
mOrigApkFile = new ExtFile(apkFile); mOrigApkFile = new ExtFile(apkFile);
} }
private ExtFile mOrigApkFile = null; private ExtFile mOrigApkFile = null;
private String mAaptPath = null; private String mAaptPath = null;
private final static Logger LOGGER = private final static Logger LOGGER = Logger.getLogger(Androlib.class
Logger.getLogger(Androlib.class.getName()); .getName());
private final static String SMALI_DIRNAME = "smali"; private final static String SMALI_DIRNAME = "smali";
private final static String APK_DIRNAME = "build/apk"; private final static String APK_DIRNAME = "build/apk";
private final static String[] APK_RESOURCES_FILENAMES = private final static String[] APK_RESOURCES_FILENAMES = new String[] {
new String[]{"resources.arsc", "AndroidManifest.xml", "res"}; "resources.arsc", "AndroidManifest.xml", "res" };
private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = new String[] {
new String[]{"resources.arsc", "AndroidManifest.xml"}; "resources.arsc", "AndroidManifest.xml" };
private final static String[] APP_RESOURCES_FILENAMES = private final static String[] APP_RESOURCES_FILENAMES = new String[] {
new String[]{"AndroidManifest.xml", "res"}; "AndroidManifest.xml", "res" };
private final static String[] APK_MANIFEST_FILENAMES = private final static String[] APK_MANIFEST_FILENAMES = new String[] { "AndroidManifest.xml" };
new String[]{"AndroidManifest.xml"};
} }

View File

@ -96,13 +96,15 @@ public class ApkDecoder {
if (hasResources()) { 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. // this will determine whether we compress on rebuild or not.
JarFile jf = new JarFile(mApkFile.getAbsoluteFile()); JarFile jf = new JarFile(mApkFile.getAbsoluteFile());
JarEntry je = jf.getJarEntry("resources.arsc"); JarEntry je = jf.getJarEntry("resources.arsc");
if (je != null) { if (je != null) {
int compression = je.getMethod(); int compression = je.getMethod();
mCompressResources = (compression != ZipEntry.STORED) && (compression == ZipEntry.DEFLATED); mCompressResources = (compression != ZipEntry.STORED)
&& (compression == ZipEntry.DEFLATED);
} }
jf.close(); jf.close();

View File

@ -20,8 +20,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jf.baksmali.baksmali;
import org.jf.smali.main;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
@ -39,7 +37,8 @@ public class ApktoolProperties {
} }
private static void loadProps() { private static void loadProps() {
InputStream in = ApktoolProperties.class.getResourceAsStream("/properties/apktool.properties"); InputStream in = ApktoolProperties.class
.getResourceAsStream("/properties/apktool.properties");
sProps = new Properties(); sProps = new Properties();
try { try {
sProps.load(in); sProps.load(in);
@ -48,7 +47,8 @@ public class ApktoolProperties {
LOGGER.warning("Can't load properties."); LOGGER.warning("Can't load properties.");
} }
InputStream templateStream = ApktoolProperties.class.getResourceAsStream("/properties/baksmali.properties"); InputStream templateStream = ApktoolProperties.class
.getResourceAsStream("/properties/baksmali.properties");
Properties properties = new Properties(); Properties properties = new Properties();
String version = "(unknown)"; String version = "(unknown)";
try { try {
@ -57,7 +57,8 @@ public class ApktoolProperties {
} catch (IOException ex) { } catch (IOException ex) {
} }
sProps.put("baksmaliVersion", version); sProps.put("baksmaliVersion", version);
templateStream = ApktoolProperties.class.getResourceAsStream("/properties/smali.properties"); templateStream = ApktoolProperties.class
.getResourceAsStream("/properties/smali.properties");
properties = new Properties(); properties = new Properties();
version = "(unknown)"; version = "(unknown)";
try { try {
@ -70,6 +71,6 @@ public class ApktoolProperties {
private static Properties sProps; private static Properties sProps;
private static final Logger LOGGER = private static final Logger LOGGER = Logger
Logger.getLogger(ApktoolProperties.class.getName()); .getLogger(ApktoolProperties.class.getName());
} }

View File

@ -30,11 +30,10 @@ public class SmaliMod {
public static boolean assembleSmaliFile(InputStream smaliStream, public static boolean assembleSmaliFile(InputStream smaliStream,
String name, DexFile dexFile, boolean verboseErrors, String name, DexFile dexFile, boolean verboseErrors,
boolean oldLexer, boolean printTokens) boolean oldLexer, boolean printTokens) throws IOException,
throws IOException, RecognitionException { RecognitionException {
CommonTokenStream tokens; CommonTokenStream tokens;
boolean lexerErrors = false; boolean lexerErrors = false;
LexerErrorInterface lexer; LexerErrorInterface lexer;
@ -48,11 +47,12 @@ public class SmaliMod {
for (int i = 0; i < tokens.size(); i++) { for (int i = 0; i < tokens.size(); i++) {
Token token = tokens.get(i); Token token = tokens.get(i);
if (token.getChannel() == smaliParser.HIDDEN) { if (token.getChannel() == BaseRecognizer.HIDDEN) {
continue; continue;
} }
System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText()); System.out.println(smaliParser.tokenNames[token.getType()]
+ ": " + token.getText());
} }
} }
@ -61,7 +61,8 @@ public class SmaliMod {
smaliParser.smali_file_return result = parser.smali_file(); smaliParser.smali_file_return result = parser.smali_file();
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) { if (parser.getNumberOfSyntaxErrors() > 0
|| lexer.getNumberOfSyntaxErrors() > 0) {
return false; return false;
} }

View File

@ -154,11 +154,13 @@ final public class AndrolibResources {
public void adjust_package_manifest(ResTable resTable, String filePath) public void adjust_package_manifest(ResTable resTable, String filePath)
throws AndrolibException { 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<String, String> packageInfo = resTable.getPackageInfo(); Map<String, String> packageInfo = resTable.getPackageInfo();
if ((packageInfo.get("cur_package").equalsIgnoreCase(packageInfo.get("orig_package")) if ((packageInfo.get("cur_package").equalsIgnoreCase(
|| ("android".equalsIgnoreCase(packageInfo.get("cur_package")) packageInfo.get("orig_package")) || ("android"
|| ("com.htc".equalsIgnoreCase(packageInfo.get("cur_package")))))) { .equalsIgnoreCase(packageInfo.get("cur_package")) || ("com.htc"
.equalsIgnoreCase(packageInfo.get("cur_package")))))) {
LOGGER.info("Regular manifest package..."); LOGGER.info("Regular manifest package...");
} else { } else {
@ -181,7 +183,8 @@ final public class AndrolibResources {
// re-save manifest. // re-save manifest.
// fancy an auto-sort :p // fancy an auto-sort :p
TransformerFactory transformerFactory = TransformerFactory.newInstance(); TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory.newTransformer(); Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc); DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(filePath)); StreamResult result = new StreamResult(new File(filePath));
@ -219,7 +222,8 @@ final public class AndrolibResources {
"AndroidManifest.xml"); "AndroidManifest.xml");
// fix package if needed // fix package if needed
adjust_package_manifest(resTable, outDir.getAbsolutePath() + "/AndroidManifest.xml"); adjust_package_manifest(resTable, outDir.getAbsolutePath()
+ "/AndroidManifest.xml");
if (inApk.containsDir("res")) { if (inApk.containsDir("res")) {
in = inApk.getDir("res"); in = inApk.getDir("res");
@ -268,7 +272,8 @@ final public class AndrolibResources {
public void aaptPackage(File apkFile, File manifest, File resDir, public void aaptPackage(File apkFile, File manifest, File resDir,
File rawDir, File assetDir, File[] include, File rawDir, File assetDir, File[] include,
HashMap<String, Boolean> flags, String aaptPath) throws AndrolibException { HashMap<String, Boolean> flags, String aaptPath)
throws AndrolibException {
List<String> cmd = new ArrayList<String>(); List<String> cmd = new ArrayList<String>();
@ -280,7 +285,8 @@ final public class AndrolibResources {
cmd.add(aaptFile.getPath()); cmd.add(aaptFile.getPath());
if (flags.get("verbose")) { if (flags.get("verbose")) {
LOGGER.info(aaptFile.getPath() + " being used as aapt location."); LOGGER.info(aaptFile.getPath()
+ " being used as aapt location.");
} }
} else { } else {
LOGGER.warning("aapt location could not be found. Defaulting back to default"); LOGGER.warning("aapt location could not be found. Defaulting back to default");
@ -420,7 +426,7 @@ final public class AndrolibResources {
" "); " ");
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR, serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR,
System.getProperty("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); serial.setDisabledAttrEscape(true);
return serial; return serial;
} }
@ -665,7 +671,8 @@ final public class AndrolibResources {
if (!dir.exists()) { if (!dir.exists()) {
if (!dir.mkdirs()) { if (!dir.mkdirs()) {
if (sFrameworkFolder != null) { 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); throw new AndrolibException("Can't create directory: " + dir);
} }

View File

@ -42,21 +42,20 @@ public class ResSmaliUpdater {
try { try {
dir = new FileDirectory(smaliDir); dir = new FileDirectory(smaliDir);
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException( throw new AndrolibException("Could not tag res IDs", ex);
"Could not tag res IDs", ex);
} }
for (String fileName : dir.getFiles(true)) { for (String fileName : dir.getFiles(true)) {
try { try {
tagResIdsForFile(resTable, dir, fileName); tagResIdsForFile(resTable, dir, fileName);
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException( throw new AndrolibException("Could not tag resIDs for file: "
"Could not tag resIDs for file: " + fileName, ex); + fileName, ex);
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException( throw new AndrolibException("Could not tag resIDs for file: "
"Could not tag resIDs for file: " + fileName, ex); + fileName, ex);
} catch (AndrolibException ex) { } catch (AndrolibException ex) {
throw new AndrolibException( throw new AndrolibException("Could not tag resIDs for file: "
"Could not tag resIDs for file: " + fileName, ex); + fileName, ex);
} }
} }
} }
@ -66,8 +65,8 @@ public class ResSmaliUpdater {
try { try {
Directory dir = new FileDirectory(smaliDir); Directory dir = new FileDirectory(smaliDir);
for (String fileName : dir.getFiles(true)) { for (String fileName : dir.getFiles(true)) {
Iterator<String> it = Iterator<String> it = IOUtils.readLines(
IOUtils.readLines(dir.getFileInput(fileName)).iterator(); dir.getFileInput(fileName)).iterator();
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
while (it.hasNext()) { while (it.hasNext()) {
String line = it.next(); String line = it.next();
@ -84,29 +83,29 @@ public class ResSmaliUpdater {
.getType(m1.group(2)).getResSpec(m1.group(3)) .getType(m1.group(2)).getResSpec(m1.group(3))
.getId().id; .getId().id;
if (m2.group(1) != null) { if (m2.group(1) != null) {
out.println(String.format( out.println(String.format(RES_ID_FORMAT_FIELD,
RES_ID_FORMAT_FIELD, m2.group(1), resID)); m2.group(1), resID));
} else { } else {
out.println(String.format( out.println(String.format(RES_ID_FORMAT_CONST,
RES_ID_FORMAT_CONST, m2.group(2), resID)); m2.group(2), resID));
} }
} }
out.close(); out.close();
} }
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException( throw new AndrolibException("Could not tag res IDs for: "
"Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex); + smaliDir.getAbsolutePath(), ex);
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException( throw new AndrolibException("Could not tag res IDs for: "
"Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex); + smaliDir.getAbsolutePath(), ex);
} }
} }
private void tagResIdsForFile(ResTable resTable, Directory dir, private void tagResIdsForFile(ResTable resTable, Directory dir,
String fileName) throws IOException, DirectoryException, String fileName) throws IOException, DirectoryException,
AndrolibException { AndrolibException {
Iterator<String> it = Iterator<String> it = IOUtils.readLines(dir.getFileInput(fileName))
IOUtils.readLines(dir.getFileInput(fileName)).iterator(); .iterator();
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
while (it.hasNext()) { while (it.hasNext()) {
String line = it.next(); String line = it.next();
@ -121,13 +120,13 @@ public class ResSmaliUpdater {
if (resID != -1) { if (resID != -1) {
try { try {
ResResSpec spec = resTable.getResSpec(resID); ResResSpec spec = resTable.getResSpec(resID);
out.println(String.format( out.println(String.format(RES_NAME_FORMAT,
RES_NAME_FORMAT, spec.getFullName())); spec.getFullName()));
} catch (UndefinedResObject ex) { } catch (UndefinedResObject ex) {
if (!R_FILE_PATTERN.matcher(fileName).matches()) { if (!R_FILE_PATTERN.matcher(fileName).matches()) {
LOGGER.warning(String.format( LOGGER.warning(String.format(
"Undefined resource spec in %s: 0x%08x" "Undefined resource spec in %s: 0x%08x",
, fileName, resID)); fileName, resID));
} }
} }
} }
@ -148,20 +147,17 @@ public class ResSmaliUpdater {
return resID; return resID;
} }
private final static String RES_ID_FORMAT_FIELD = private final static String RES_ID_FORMAT_FIELD = ".field %s:I = 0x%08x";
".field %s:I = 0x%08x"; private final static String RES_ID_FORMAT_CONST = " const %s, 0x%08x";
private final static String RES_ID_FORMAT_CONST = private final static Pattern RES_ID_PATTERN = Pattern
" const %s, 0x%08x"; .compile("^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$");
private final static Pattern RES_ID_PATTERN = Pattern.compile( private final static String RES_NAME_FORMAT = "# APKTOOL/RES_NAME: %s";
"^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$"); private final static Pattern RES_NAME_PATTERN = Pattern
private final static String RES_NAME_FORMAT = .compile("^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$");
"# 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( private final static Pattern R_FILE_PATTERN = Pattern
".*R\\$[a-z]+\\.smali$"); .compile(".*R\\$[a-z]+\\.smali$");
private final static Logger LOGGER = private final static Logger LOGGER = Logger.getLogger(ResSmaliUpdater.class
Logger.getLogger(ResSmaliUpdater.class.getName()); .getName());
} }

View File

@ -25,8 +25,7 @@ import java.util.*;
*/ */
public class ResConfig { public class ResConfig {
private final ResConfigFlags mFlags; private final ResConfigFlags mFlags;
private final Map<ResResSpec, ResResource> mResources = private final Map<ResResSpec, ResResource> mResources = new LinkedHashMap<ResResSpec, ResResource>();
new LinkedHashMap<ResResSpec, ResResource>();
public ResConfig(ResConfigFlags flags) { public ResConfig(ResConfigFlags flags) {
this.mFlags = flags; this.mFlags = flags;
@ -53,8 +52,7 @@ public class ResConfig {
return mFlags; return mFlags;
} }
public void addResource(ResResource res) public void addResource(ResResource res) throws AndrolibException {
throws AndrolibException {
addResource(res, false); addResource(res, false);
} }

View File

@ -78,11 +78,12 @@ public class ResConfigFlags {
mQualifiers = ""; mQualifiers = "";
} }
public ResConfigFlags(short mcc, short mnc, char[] language, char[] country, public ResConfigFlags(short mcc, short mnc, char[] language,
short layoutDirection, byte orientation, byte touchscreen, char[] country, short layoutDirection, byte orientation,
short density, byte keyboard, byte navigation, byte inputFlags, byte touchscreen, short density, byte keyboard, byte navigation,
short screenWidth, short screenHeight, short sdkVersion, byte screenLayout, byte inputFlags, short screenWidth, short screenHeight,
byte uiMode, short smallestScreenWidthDp, short screenWidthDp, short sdkVersion, byte screenLayout, byte uiMode,
short smallestScreenWidthDp, short screenWidthDp,
short screenHeightDp, boolean isInvalid) { short screenHeightDp, boolean isInvalid) {
if (orientation < 0 || orientation > 3) { if (orientation < 0 || orientation > 3) {
LOGGER.warning("Invalid orientation value: " + orientation); LOGGER.warning("Invalid orientation value: " + orientation);
@ -373,7 +374,6 @@ public class ResConfigFlags {
return hash; return hash;
} }
// TODO: Dirty static hack. This counter should be a part of ResPackage, // 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. // but it would be hard right now and this feature is very rarely used.
private static int sErrCounter = 0; private static int sErrCounter = 0;
@ -472,7 +472,6 @@ public class ResConfigFlags {
public final static byte UI_MODE_NIGHT_NO = 0x10; public final static byte UI_MODE_NIGHT_NO = 0x10;
public final static byte UI_MODE_NIGHT_YES = 0x20; public final static byte UI_MODE_NIGHT_YES = 0x20;
private static final Logger LOGGER = Logger.getLogger(ResConfigFlags.class
private static final Logger LOGGER = .getName());
Logger.getLogger(ResConfigFlags.class.getName());
} }

View File

@ -31,12 +31,9 @@ public class ResPackage {
private final ResTable mResTable; private final ResTable mResTable;
private final int mId; private final int mId;
private final String mName; private final String mName;
private final Map<ResID, ResResSpec> mResSpecs = private final Map<ResID, ResResSpec> mResSpecs = new LinkedHashMap<ResID, ResResSpec>();
new LinkedHashMap<ResID, ResResSpec>(); private final Map<ResConfigFlags, ResConfig> mConfigs = new LinkedHashMap<ResConfigFlags, ResConfig>();
private final Map<ResConfigFlags, ResConfig> mConfigs = private final Map<String, ResType> mTypes = new LinkedHashMap<String, ResType>();
new LinkedHashMap<ResConfigFlags, ResConfig>();
private final Map<String, ResType> mTypes =
new LinkedHashMap<String, ResType>();
private final Set<ResID> mSynthesizedRes = new HashSet<ResID>(); private final Set<ResID> mSynthesizedRes = new HashSet<ResID>();
private ResValueFactory mValueFactory; private ResValueFactory mValueFactory;
@ -118,15 +115,14 @@ public class ResPackage {
} }
public Collection<ResValuesFile> listValuesFiles() { public Collection<ResValuesFile> listValuesFiles() {
Map<Duo<ResType, ResConfig>, ResValuesFile> ret = Map<Duo<ResType, ResConfig>, ResValuesFile> ret = new HashMap<Duo<ResType, ResConfig>, ResValuesFile>();
new HashMap<Duo<ResType, ResConfig>, ResValuesFile>();
for (ResResSpec spec : mResSpecs.values()) { for (ResResSpec spec : mResSpecs.values()) {
for (ResResource res : spec.listResources()) { for (ResResource res : spec.listResources()) {
if (res.getValue() instanceof ResValuesXmlSerializable) { if (res.getValue() instanceof ResValuesXmlSerializable) {
ResType type = res.getResSpec().getType(); ResType type = res.getResSpec().getType();
ResConfig config = res.getConfig(); ResConfig config = res.getConfig();
Duo<ResType, ResConfig> key = Duo<ResType, ResConfig> key = new Duo<ResType, ResConfig>(
new Duo<ResType, ResConfig>(type, config); type, config);
ResValuesFile values = ret.get(key); ResValuesFile values = ret.get(key);
if (values == null) { if (values == null) {
values = new ResValuesFile(this, type, config); values = new ResValuesFile(this, type, config);
@ -194,7 +190,9 @@ public class ResPackage {
return false; return false;
} }
final ResPackage other = (ResPackage) obj; final ResPackage other = (ResPackage) obj;
if (this.mResTable != other.mResTable && (this.mResTable == null || !this.mResTable.equals(other.mResTable))) { if (this.mResTable != other.mResTable
&& (this.mResTable == null || !this.mResTable
.equals(other.mResTable))) {
return false; return false;
} }
if (this.mId != other.mId) { if (this.mId != other.mId) {
@ -206,7 +204,8 @@ public class ResPackage {
@Override @Override
public int hashCode() { public int hashCode() {
int hash = 17; int hash = 17;
hash = 31 * hash + (this.mResTable != null ? this.mResTable.hashCode() : 0); hash = 31 * hash
+ (this.mResTable != null ? this.mResTable.hashCode() : 0);
hash = 31 * hash + this.mId; hash = 31 * hash + this.mId;
return hash; return hash;
} }

View File

@ -29,8 +29,7 @@ public class ResResSpec {
private final String mName; private final String mName;
private final ResPackage mPackage; private final ResPackage mPackage;
private final ResType mType; private final ResType mType;
private final Map<ResConfigFlags, ResResource> mResources = private final Map<ResConfigFlags, ResResource> mResources = new LinkedHashMap<ResConfigFlags, ResResource>();
new LinkedHashMap<ResConfigFlags, ResResource>();
public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) { public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) {
this.mId = id; this.mId = id;
@ -77,16 +76,13 @@ public class ResResSpec {
return getFullName(false, false); return getFullName(false, false);
} }
public String getFullName(ResPackage relativeToPackage, public String getFullName(ResPackage relativeToPackage, boolean excludeType) {
boolean excludeType) { return getFullName(getPackage().equals(relativeToPackage), excludeType);
return getFullName(
getPackage().equals(relativeToPackage), excludeType);
} }
public String getFullName(boolean excludePackage, boolean excludeType) { public String getFullName(boolean excludePackage, boolean excludeType) {
return return (excludePackage ? "" : getPackage().getName() + ":")
(excludePackage ? "" : getPackage().getName() + ":") + + (excludeType ? "" : getType().getName() + "/") + getName();
(excludeType ? "" : getType().getName() + "/") + getName();
} }
public ResID getId() { public ResID getId() {
@ -105,8 +101,7 @@ public class ResResSpec {
return mType; return mType;
} }
public void addResource(ResResource res) public void addResource(ResResource res) throws AndrolibException {
throws AndrolibException {
addResource(res, false); addResource(res, false);
} }
@ -114,7 +109,8 @@ public class ResResSpec {
throws AndrolibException { throws AndrolibException {
ResConfigFlags flags = res.getConfig().getFlags(); ResConfigFlags flags = res.getConfig().getFlags();
if (mResources.put(flags, res) != null && !overwrite) { if (mResources.put(flags, res) != null && !overwrite) {
throw new AndrolibException(String.format("Multiple resources: spec=%s, config=%s", this, flags)); throw new AndrolibException(String.format(
"Multiple resources: spec=%s, config=%s", this, flags));
} }
} }

View File

@ -27,16 +27,15 @@ public class ResResource {
private final ResResSpec mResSpec; private final ResResSpec mResSpec;
private final ResValue mValue; private final ResValue mValue;
public ResResource(ResConfig config, ResResSpec spec, public ResResource(ResConfig config, ResResSpec spec, ResValue value) {
ResValue value) {
this.mConfig = config; this.mConfig = config;
this.mResSpec = spec; this.mResSpec = spec;
this.mValue = value; this.mValue = value;
} }
public String getFilePath() { public String getFilePath() {
return mResSpec.getType().getName() + return mResSpec.getType().getName()
mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName(); + mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName();
} }
public ResConfig getConfig() { public ResConfig getConfig() {

View File

@ -28,14 +28,10 @@ import java.util.*;
public class ResTable { public class ResTable {
private final AndrolibResources mAndRes; private final AndrolibResources mAndRes;
private final Map<Integer, ResPackage> mPackagesById = private final Map<Integer, ResPackage> mPackagesById = new HashMap<Integer, ResPackage>();
new HashMap<Integer, ResPackage>(); private final Map<String, ResPackage> mPackagesByName = new HashMap<String, ResPackage>();
private final Map<String, ResPackage> mPackagesByName = private final Set<ResPackage> mMainPackages = new LinkedHashSet<ResPackage>();
new HashMap<String, ResPackage>(); private final Set<ResPackage> mFramePackages = new LinkedHashSet<ResPackage>();
private final Set<ResPackage> mMainPackages =
new LinkedHashSet<ResPackage>();
private final Set<ResPackage> mFramePackages =
new LinkedHashSet<ResPackage>();
private String mFrameTag; private String mFrameTag;
@ -103,8 +99,8 @@ public class ResTable {
throws AndrolibException { throws AndrolibException {
Integer id = pkg.getId(); Integer id = pkg.getId();
if (mPackagesById.containsKey(id)) { if (mPackagesById.containsKey(id)) {
throw new AndrolibException( throw new AndrolibException("Multiple packages: id="
"Multiple packages: id=" + id.toString()); + id.toString());
} }
String name = pkg.getName(); String name = pkg.getName();
if (mPackagesByName.containsKey(name)) { if (mPackagesByName.containsKey(name)) {

View File

@ -25,14 +25,12 @@ import java.util.*;
*/ */
public final class ResType { public final class ResType {
private final String mName; private final String mName;
private final Map<String, ResResSpec> mResSpecs = private final Map<String, ResResSpec> mResSpecs = new LinkedHashMap<String, ResResSpec>();
new LinkedHashMap<String, ResResSpec>();
private final ResTable mResTable; private final ResTable mResTable;
private final ResPackage mPackage; private final ResPackage mPackage;
public ResType(String name, ResTable resTable, public ResType(String name, ResTable resTable, ResPackage package_) {
ResPackage package_) {
this.mName = name; this.mName = name;
this.mResTable = resTable; this.mResTable = resTable;
this.mPackage = package_; this.mPackage = package_;
@ -49,14 +47,13 @@ public final class ResType {
public ResResSpec getResSpec(String name) throws AndrolibException { public ResResSpec getResSpec(String name) throws AndrolibException {
ResResSpec spec = mResSpecs.get(name); ResResSpec spec = mResSpecs.get(name);
if (spec == null) { if (spec == null) {
throw new UndefinedResObject(String.format( throw new UndefinedResObject(String.format("resource spec: %s/%s",
"resource spec: %s/%s", getName(), name)); getName(), name));
} }
return spec; return spec;
} }
public void addResSpec(ResResSpec spec) public void addResSpec(ResResSpec spec) throws AndrolibException {
throws AndrolibException {
if (mResSpecs.put(spec.getName(), spec) != null) { if (mResSpecs.put(spec.getName(), spec) != null) {
throw new AndrolibException(String.format( throw new AndrolibException(String.format(
"Multiple res specs: %s/%s", getName(), spec.getName())); "Multiple res specs: %s/%s", getName(), spec.getName()));

View File

@ -26,8 +26,7 @@ public class ResValuesFile {
private final ResPackage mPackage; private final ResPackage mPackage;
private final ResType mType; private final ResType mType;
private final ResConfig mConfig; private final ResConfig mConfig;
private final Set<ResResource> mResources = private final Set<ResResource> mResources = new LinkedHashSet<ResResource>();
new LinkedHashSet<ResResource>();
public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) { public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) {
this.mPackage = pkg; this.mPackage = pkg;
@ -36,9 +35,8 @@ public class ResValuesFile {
} }
public String getPath() { public String getPath() {
return "values" + mConfig.getFlags().getQualifiers() return "values" + mConfig.getFlags().getQualifiers() + "/"
+ "/" + mType.getName() + mType.getName() + (mType.getName().endsWith("s") ? "" : "s")
+ (mType.getName().endsWith("s") ? "" : "s")
+ ".xml"; + ".xml";
} }
@ -71,10 +69,12 @@ public class ResValuesFile {
return false; return false;
} }
final ResValuesFile other = (ResValuesFile) obj; final ResValuesFile other = (ResValuesFile) obj;
if (this.mType != other.mType && (this.mType == null || !this.mType.equals(other.mType))) { if (this.mType != other.mType
&& (this.mType == null || !this.mType.equals(other.mType))) {
return false; return false;
} }
if (this.mConfig != other.mConfig && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) { if (this.mConfig != other.mConfig
&& (this.mConfig == null || !this.mConfig.equals(other.mConfig))) {
return false; return false;
} }
return true; return true;

View File

@ -22,15 +22,15 @@ import brut.androlib.res.xml.ResValuesXmlSerializable;
import brut.util.Duo; import brut.util.Duo;
import java.io.IOException; import java.io.IOException;
import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlSerializer;
import org.apache.commons.lang3.StringUtils;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializable { public class ResArrayValue extends ResBagValue implements
ResValuesXmlSerializable {
private String mRawItems; private String mRawItems;
ResArrayValue(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items) { ResArrayValue(ResReferenceValue parent, Duo<Integer, ResScalarValue>[] items) {
super(parent); super(parent);
mItems = new ResScalarValue[items.length]; mItems = new ResScalarValue[items.length];
@ -45,8 +45,8 @@ public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializab
} }
@Override @Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) public void serializeToResValuesXml(XmlSerializer serializer,
throws IOException, AndrolibException { ResResource res) throws IOException, AndrolibException {
String type = getType(); String type = getType();
type = (type == null ? "" : type + "-") + "array"; type = (type == null ? "" : type + "-") + "array";
// reference array (04 10 2012, BurgerZ) // reference array (04 10 2012, BurgerZ)
@ -73,7 +73,8 @@ public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializab
if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) { if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) {
return "string"; return "string";
} else if (mItems[i].encodeAsResXmlItemValue().startsWith("@drawable")) { } else if (mItems[i].encodeAsResXmlItemValue().startsWith(
"@drawable")) {
return null; return null;
} else if (!"string".equals(type) && !"integer".equals(type)) { } else if (!"string".equals(type) && !"integer".equals(type)) {
return null; return null;
@ -86,6 +87,5 @@ public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializab
private final ResScalarValue[] mItems; private final ResScalarValue[] mItems;
public static final int BAG_KEY_ARRAY_START = 0x02000000; public static final int BAG_KEY_ARRAY_START = 0x02000000;
} }

View File

@ -43,8 +43,8 @@ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
} }
@Override @Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) public void serializeToResValuesXml(XmlSerializer serializer,
throws IOException, AndrolibException { ResResource res) throws IOException, AndrolibException {
String type = getTypeAsString(); String type = getTypeAsString();
serializer.startTag(null, "attr"); serializer.startTag(null, "attr");
@ -65,7 +65,6 @@ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
serializer.endTag(null, "attr"); serializer.endTag(null, "attr");
} }
public static ResAttr factory(ResReferenceValue parent, public static ResAttr factory(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory, Duo<Integer, ResScalarValue>[] items, ResValueFactory factory,
ResPackage pkg) throws AndrolibException { ResPackage pkg) throws AndrolibException {
@ -93,29 +92,31 @@ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
if (i == items.length) { if (i == items.length) {
return new ResAttr(parent, scalarType, min, max, l10n); return new ResAttr(parent, scalarType, min, max, l10n);
} }
Duo<ResReferenceValue, ResIntValue>[] attrItems = Duo<ResReferenceValue, ResIntValue>[] attrItems = new Duo[items.length
new Duo[items.length - i]; - i];
int j = 0; int j = 0;
for (; i < items.length; i++) { for (; i < items.length; i++) {
int resId = items[i].m1; int resId = items[i].m1;
pkg.addSynthesizedRes(resId); pkg.addSynthesizedRes(resId);
attrItems[j++] = new Duo<ResReferenceValue, ResIntValue>( attrItems[j++] = new Duo<ResReferenceValue, ResIntValue>(
factory.newReference(resId, null), (ResIntValue) items[i].m2); factory.newReference(resId, null),
(ResIntValue) items[i].m2);
} }
switch (type & 0xff0000) { switch (type & 0xff0000) {
case TYPE_ENUM: case TYPE_ENUM:
return new ResEnumAttr( return new ResEnumAttr(parent, scalarType, min, max, l10n,
parent, scalarType, min, max, l10n, attrItems); attrItems);
case TYPE_FLAGS: case TYPE_FLAGS:
return new ResFlagsAttr( return new ResFlagsAttr(parent, scalarType, min, max, l10n,
parent, scalarType, min, max, l10n, attrItems); attrItems);
} }
throw new AndrolibException("Could not decode attr value"); throw new AndrolibException("Could not decode attr value");
} }
protected void serializeBody(XmlSerializer serializer, ResResource res) protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {} throws AndrolibException, IOException {
}
protected String getTypeAsString() { protected String getTypeAsString() {
String s = ""; String s = "";
@ -154,7 +155,6 @@ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
private final Integer mMax; private final Integer mMax;
private final Boolean mL10n; private final Boolean mL10n;
public static final int BAG_KEY_ATTR_TYPE = 0x01000000; 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_MIN = 0x01000001;
private static final int BAG_KEY_ATTR_MAX = 0x01000002; private static final int BAG_KEY_ATTR_MAX = 0x01000002;

View File

@ -33,8 +33,9 @@ public class ResBagValue extends ResValue implements ResValuesXmlSerializable {
this.mParent = parent; this.mParent = parent;
} }
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) @Override
throws IOException, AndrolibException { public void serializeToResValuesXml(XmlSerializer serializer,
ResResource res) throws IOException, AndrolibException {
String type = res.getResSpec().getType().getName(); String type = res.getResSpec().getType().getName();
if ("style".equals(type)) { if ("style".equals(type)) {
new ResStyleValue(mParent, new Duo[0], null) new ResStyleValue(mParent, new Duo[0], null)
@ -42,13 +43,13 @@ public class ResBagValue extends ResValue implements ResValuesXmlSerializable {
return; return;
} }
if ("array".equals(type)) { if ("array".equals(type)) {
new ResArrayValue(mParent, new Duo[0]) new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml(
.serializeToResValuesXml(serializer, res); serializer, res);
return; return;
} }
if ("plurals".equals(type)) { if ("plurals".equals(type)) {
new ResPluralsValue(mParent, new Duo[0]) new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml(
.serializeToResValuesXml(serializer, res); serializer, res);
return; return;
} }

View File

@ -31,6 +31,7 @@ public class ResBoolValue extends ResScalarValue {
return mValue; return mValue;
} }
@Override
protected String encodeAsResXml() { protected String encodeAsResXml() {
return mValue ? "true" : "false"; return mValue ? "true" : "false";
} }

View File

@ -77,8 +77,6 @@ public class ResEnumAttr extends ResAttr {
return value2; return value2;
} }
private final Duo<ResReferenceValue, ResIntValue>[] mItems; private final Duo<ResReferenceValue, ResIntValue>[] mItems;
private final Map<Integer, String> mItemsCache = private final Map<Integer, String> mItemsCache = new HashMap<Integer, String>();
new HashMap<Integer, String>();
} }

View File

@ -28,7 +28,8 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResFlagsAttr extends ResAttr { public class ResFlagsAttr extends ResAttr {
ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) { ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max,
Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
super(parent, type, min, max, l10n); super(parent, type, min, max, l10n);
mItems = new FlagItem[items.length]; mItems = new FlagItem[items.length];
@ -126,20 +127,19 @@ public class ResFlagsAttr extends ResAttr {
mFlags = Arrays.copyOf(flags, flagsCount); mFlags = Arrays.copyOf(flags, flagsCount);
Arrays.sort(mFlags, new Comparator<FlagItem>() { Arrays.sort(mFlags, new Comparator<FlagItem>() {
@Override
public int compare(FlagItem o1, FlagItem o2) { public int compare(FlagItem o1, FlagItem o2) {
return Integer.valueOf(Integer.bitCount(o2.flag)) return Integer.valueOf(Integer.bitCount(o2.flag)).compareTo(
.compareTo(Integer.bitCount(o1.flag)); Integer.bitCount(o1.flag));
} }
}); });
} }
private final FlagItem[] mItems; private final FlagItem[] mItems;
private FlagItem[] mZeroFlags; private FlagItem[] mZeroFlags;
private FlagItem[] mFlags; private FlagItem[] mFlags;
private static class FlagItem { private static class FlagItem {
public final ResReferenceValue ref; public final ResReferenceValue ref;
public final int flag; public final int flag;

View File

@ -31,6 +31,7 @@ public class ResFloatValue extends ResScalarValue {
return mValue; return mValue;
} }
@Override
protected String encodeAsResXml() { protected String encodeAsResXml() {
return String.valueOf(mValue); return String.valueOf(mValue);
} }

View File

@ -26,9 +26,12 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResIdValue extends ResValue implements ResValuesXmlSerializable { public class ResIdValue extends ResValue implements ResValuesXmlSerializable {
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) throws IOException, AndrolibException { @Override
public void serializeToResValuesXml(XmlSerializer serializer,
ResResource res) throws IOException, AndrolibException {
serializer.startTag(null, "item"); serializer.startTag(null, "item");
serializer.attribute(null, "type", res.getResSpec().getType().getName()); serializer
.attribute(null, "type", res.getResSpec().getType().getName());
serializer.attribute(null, "name", res.getResSpec().getName()); serializer.attribute(null, "name", res.getResSpec().getName());
serializer.endTag(null, "item"); serializer.endTag(null, "item");
} }

View File

@ -40,6 +40,7 @@ public class ResIntValue extends ResScalarValue {
return mValue; return mValue;
} }
@Override
protected String encodeAsResXml() throws AndrolibException { protected String encodeAsResXml() throws AndrolibException {
return TypedValue.coerceToString(type, mValue); return TypedValue.coerceToString(type, mValue);
} }

View File

@ -28,21 +28,21 @@ import org.xmlpull.v1.XmlSerializer;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializable { public class ResPluralsValue extends ResBagValue implements
ResValuesXmlSerializable {
ResPluralsValue(ResReferenceValue parent, ResPluralsValue(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items) { Duo<Integer, ResScalarValue>[] items) {
super(parent); super(parent);
mItems = new ResScalarValue[6]; mItems = new ResScalarValue[6];
for (int i = 0; i < items.length; i++) { for (int i = 0; i < items.length; i++) {
mItems[items[i].m1 - BAG_KEY_PLURALS_START] = mItems[items[i].m1 - BAG_KEY_PLURALS_START] = items[i].m2;
(ResScalarValue) items[i].m2;
} }
} }
@Override @Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) public void serializeToResValuesXml(XmlSerializer serializer,
throws IOException, AndrolibException { ResResource res) throws IOException, AndrolibException {
serializer.startTag(null, "plurals"); serializer.startTag(null, "plurals");
serializer.attribute(null, "name", res.getResSpec().getName()); serializer.attribute(null, "name", res.getResSpec().getName());
for (int i = 0; i < mItems.length; i++) { for (int i = 0; i < mItems.length; i++) {
@ -55,13 +55,18 @@ public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializ
serializer.startTag(null, "item"); serializer.startTag(null, "item");
serializer.attribute(null, "quantity", QUANTITY_MAP[i]); serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue.encodeAsResXmlValue())) { if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue
.encodeAsResXmlValue())) {
serializer.text(item.encodeAsResXmlValueExt()); serializer.text(item.encodeAsResXmlValueExt());
} else { } else {
String recode = item.encodeAsResXmlValue(); String recode = item.encodeAsResXmlValue();
// Dirty, but working fix @miuirussia // Dirty, but working fix @miuirussia
for (int j = 0; j < 10; j++) { for (int j = 0; j < 10; j++) {
recode = StringUtils.replace(recode, "%" + Integer.toString(j) + "$" + Integer.toString(j) + "$", "%" + Integer.toString(j) + "$"); recode = StringUtils.replace(
recode,
"%" + Integer.toString(j) + "$"
+ Integer.toString(j) + "$",
"%" + Integer.toString(j) + "$");
} }
serializer.text(recode); serializer.text(recode);
} }
@ -70,12 +75,10 @@ public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializ
serializer.endTag(null, "plurals"); 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_START = 0x01000004;
public static final int BAG_KEY_PLURALS_END = 0x01000009; public static final int BAG_KEY_PLURALS_END = 0x01000009;
private static final String[] QUANTITY_MAP = private static final String[] QUANTITY_MAP = new String[] { "other",
new String[] {"other", "zero", "one", "two", "few", "many"}; "zero", "one", "two", "few", "many" };
} }

View File

@ -38,23 +38,23 @@ public class ResReferenceValue extends ResIntValue {
mTheme = theme; mTheme = theme;
} }
@Override
protected String encodeAsResXml() throws AndrolibException { protected String encodeAsResXml() throws AndrolibException {
if (isNull()) { if (isNull()) {
return "@null"; return "@null";
} }
ResResSpec spec = getReferent(); ResResSpec spec = getReferent();
boolean newId = boolean newId = spec.hasDefaultResource()
spec.hasDefaultResource() && && spec.getDefaultResource().getValue() instanceof ResIdValue;
spec.getDefaultResource().getValue() instanceof ResIdValue;
// generate the beginning to fix @android // generate the beginning to fix @android
String mStart = (mTheme ? '?' : '@') + (newId ? "+" : ""); String mStart = (mTheme ? '?' : '@') + (newId ? "+" : "");
// mStart = mStart.replace("@android", "@*android"); // mStart = mStart.replace("@android", "@*android");
return mStart + return mStart
spec.getFullName(mPackage, + spec.getFullName(mPackage, mTheme
mTheme && spec.getType().getName().equals("attr")); && spec.getType().getName().equals("attr"));
} }
public ResResSpec getReferent() throws AndrolibException { public ResResSpec getReferent() throws AndrolibException {

View File

@ -27,8 +27,8 @@ import org.xmlpull.v1.XmlSerializer;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public abstract class ResScalarValue extends ResValue public abstract class ResScalarValue extends ResValue implements
implements ResXmlEncodable, ResValuesXmlSerializable { ResXmlEncodable, ResValuesXmlSerializable {
protected final String mType; protected final String mType;
protected final String mRawValue; protected final String mRawValue;
@ -37,6 +37,7 @@ public abstract class ResScalarValue extends ResValue
mRawValue = rawValue; mRawValue = rawValue;
} }
@Override
public String encodeAsResXmlAttr() throws AndrolibException { public String encodeAsResXmlAttr() throws AndrolibException {
if (mRawValue != null) { if (mRawValue != null) {
return mRawValue; return mRawValue;
@ -48,6 +49,7 @@ public abstract class ResScalarValue extends ResValue
return encodeAsResXmlValue().replace("@android:", "@*android:"); return encodeAsResXmlValue().replace("@android:", "@*android:");
} }
@Override
public String encodeAsResXmlValue() throws AndrolibException { public String encodeAsResXmlValue() throws AndrolibException {
if (mRawValue != null) { if (mRawValue != null) {
return mRawValue; return mRawValue;
@ -86,9 +88,9 @@ public abstract class ResScalarValue extends ResValue
return encodeAsResXml(); return encodeAsResXml();
} }
@Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) public void serializeToResValuesXml(XmlSerializer serializer,
throws IOException, AndrolibException { ResResource res) throws IOException, AndrolibException {
String type = res.getResSpec().getType().getName(); String type = res.getResSpec().getType().getName();
boolean item = !"reference".equals(mType) && !type.equals(mType); boolean item = !"reference".equals(mType) && !type.equals(mType);

View File

@ -42,8 +42,9 @@ public class ResStringValue extends ResScalarValue {
@Override @Override
public String encodeAsResXmlItemValue() { public String encodeAsResXmlItemValue() {
return ResXmlEncoders.enumerateNonPositionalSubstitutions( return ResXmlEncoders
ResXmlEncoders.encodeAsXmlValue(mRawValue)); .enumerateNonPositionalSubstitutions(ResXmlEncoders
.encodeAsXmlValue(mRawValue));
} }
@Override @Override

View File

@ -27,7 +27,8 @@ import org.xmlpull.v1.XmlSerializer;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializable { public class ResStyleValue extends ResBagValue implements
ResValuesXmlSerializable {
ResStyleValue(ResReferenceValue parent, ResStyleValue(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory) { Duo<Integer, ResScalarValue>[] items, ResValueFactory factory) {
super(parent); super(parent);
@ -40,8 +41,8 @@ public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializab
} }
@Override @Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) public void serializeToResValuesXml(XmlSerializer serializer,
throws IOException, AndrolibException { ResResource res) throws IOException, AndrolibException {
serializer.startTag(null, "style"); serializer.startTag(null, "style");
serializer.attribute(null, "name", res.getResSpec().getName()); serializer.attribute(null, "name", res.getResSpec().getName());
if (!mParent.isNull()) { if (!mParent.isNull()) {
@ -51,7 +52,8 @@ public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializab
ResResSpec spec = mItems[i].m1.getReferent(); ResResSpec spec = mItems[i].m1.getReferent();
// hacky-fix remove bad ReferenceVars // hacky-fix remove bad ReferenceVars
if (spec.getDefaultResource().getValue().toString().contains("ResReferenceValue@")) { if (spec.getDefaultResource().getValue().toString()
.contains("ResReferenceValue@")) {
continue; continue;
} }
ResAttr attr = (ResAttr) spec.getDefaultResource().getValue(); ResAttr attr = (ResAttr) spec.getDefaultResource().getValue();
@ -74,6 +76,5 @@ public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializab
serializer.endTag(null, "style"); serializer.endTag(null, "style");
} }
private final Duo<ResReferenceValue, ResScalarValue>[] mItems; private final Duo<ResReferenceValue, ResScalarValue>[] mItems;
} }

View File

@ -47,10 +47,8 @@ public class ARSCDecoder {
ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable, ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable,
findFlagsOffsets, keepBroken); findFlagsOffsets, keepBroken);
ResPackage[] pkgs = decoder.readTable(); ResPackage[] pkgs = decoder.readTable();
return new ARSCData( return new ARSCData(pkgs, decoder.mFlagsOffsets == null ? null
pkgs, : decoder.mFlagsOffsets.toArray(new FlagsOffset[0]),
decoder.mFlagsOffsets == null ? null :
decoder.mFlagsOffsets.toArray(new FlagsOffset[0]),
resTable); resTable);
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException("Could not decode arsc file", ex); throw new AndrolibException("Could not decode arsc file", ex);
@ -152,16 +150,15 @@ public class ARSCDecoder {
if (flags.isInvalid) { if (flags.isInvalid) {
String resName = mType.getName() + flags.getQualifiers(); String resName = mType.getName() + flags.getQualifiers();
if (mKeepBroken) { if (mKeepBroken) {
LOGGER.warning( LOGGER.warning("Invalid config flags detected: " + resName);
"Invalid config flags detected: " + resName);
} else { } else {
LOGGER.warning( LOGGER.warning("Invalid config flags detected. Dropping resources: "
"Invalid config flags detected. Dropping resources: " + resName); + resName);
} }
} }
mConfig = flags.isInvalid && ! mKeepBroken ? mConfig = flags.isInvalid && !mKeepBroken ? null : mPkg
null : mPkg.getOrCreateConfig(flags); .getOrCreateConfig(flags);
for (int i = 0; i < entryOffsets.length; i++) { for (int i = 0; i < entryOffsets.length; i++) {
if (entryOffsets[i] != -1) { if (entryOffsets[i] != -1) {
@ -179,8 +176,8 @@ public class ARSCDecoder {
short flags = mIn.readShort(); short flags = mIn.readShort();
int specNamesId = mIn.readInt(); int specNamesId = mIn.readInt();
ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? readValue()
readValue() : readComplexEntry(); : readComplexEntry();
if (mConfig == null) { if (mConfig == null) {
return; return;
@ -191,8 +188,8 @@ public class ARSCDecoder {
if (mPkg.hasResSpec(resId)) { if (mPkg.hasResSpec(resId)) {
spec = mPkg.getResSpec(resId); spec = mPkg.getResSpec(resId);
} else { } else {
spec = new ResResSpec( spec = new ResResSpec(resId, mSpecNames.getString(specNamesId),
resId, mSpecNames.getString(specNamesId), mPkg, mType); mPkg, mType);
mPkg.addResSpec(spec); mPkg.addResSpec(spec);
mType.addResSpec(spec); mType.addResSpec(spec);
} }
@ -211,8 +208,8 @@ public class ARSCDecoder {
ResValueFactory factory = mPkg.getValueFactory(); ResValueFactory factory = mPkg.getValueFactory();
Duo<Integer, ResScalarValue>[] items = new Duo[count]; Duo<Integer, ResScalarValue>[] items = new Duo[count];
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
items[i] = new Duo<Integer, ResScalarValue>( items[i] = new Duo<Integer, ResScalarValue>(mIn.readInt(),
mIn.readInt(), (ResScalarValue) readValue()); (ResScalarValue) readValue());
} }
return factory.bagFactory(parent, items); return factory.bagFactory(parent, items);
@ -224,12 +221,13 @@ public class ARSCDecoder {
byte type = mIn.readByte(); byte type = mIn.readByte();
int data = mIn.readInt(); int data = mIn.readInt();
return type == TypedValue.TYPE_STRING ? return type == TypedValue.TYPE_STRING ? mPkg.getValueFactory().factory(
mPkg.getValueFactory().factory(mTableStrings.getHTML(data)) : mTableStrings.getHTML(data)) : mPkg.getValueFactory().factory(
mPkg.getValueFactory().factory(type, data, null); type, data, null);
} }
private ResConfigFlags readConfigFlags() throws IOException, AndrolibException { private ResConfigFlags readConfigFlags() throws IOException,
AndrolibException {
int size = mIn.readInt(); int size = mIn.readInt();
if (size < 28) { if (size < 28) {
throw new AndrolibException("Config size < 28"); throw new AndrolibException("Config size < 28");
@ -240,10 +238,10 @@ public class ARSCDecoder {
short mcc = mIn.readShort(); short mcc = mIn.readShort();
short mnc = mIn.readShort(); short mnc = mIn.readShort();
char[] language = new char[]{ char[] language = new char[] { (char) mIn.readByte(),
(char) mIn.readByte(), (char) mIn.readByte()}; (char) mIn.readByte() };
char[] country = new char[]{ char[] country = new char[] { (char) mIn.readByte(),
(char) mIn.readByte(), (char) mIn.readByte()}; (char) mIn.readByte() };
byte orientation = mIn.readByte(); byte orientation = mIn.readByte();
byte touchscreen = mIn.readByte(); byte touchscreen = mIn.readByte();
@ -278,7 +276,8 @@ public class ARSCDecoder {
} }
short layoutDirection = 0; short layoutDirection = 0;
if (size >= 38 && sdkVersion >= 17 && !this.mPkg.getName().equalsIgnoreCase("com.htc")) { if (size >= 38 && sdkVersion >= 17
&& !this.mPkg.getName().equalsIgnoreCase("com.htc")) {
layoutDirection = mIn.readShort(); layoutDirection = mIn.readShort();
} }
@ -289,8 +288,8 @@ public class ARSCDecoder {
BigInteger exceedingBI = new BigInteger(1, buf); BigInteger exceedingBI = new BigInteger(1, buf);
if (exceedingBI.equals(BigInteger.ZERO)) { if (exceedingBI.equals(BigInteger.ZERO)) {
LOGGER.fine(String.format( LOGGER.fine(String
"Config flags size > %d, but exceeding bytes are all zero, so it should be ok.", .format("Config flags size > %d, but exceeding bytes are all zero, so it should be ok.",
KNOWN_CONFIG_BYTES)); KNOWN_CONFIG_BYTES));
} else { } else {
LOGGER.warning(String.format( LOGGER.warning(String.format(
@ -300,10 +299,11 @@ public class ARSCDecoder {
} }
} }
return new ResConfigFlags(mcc, mnc, language, country, layoutDirection, orientation, return new ResConfigFlags(mcc, mnc, language, country, layoutDirection,
touchscreen, density, keyboard, navigation, inputFlags, orientation, touchscreen, density, keyboard, navigation,
screenWidth, screenHeight, sdkVersion, screenLayout, uiMode, inputFlags, screenWidth, screenHeight, sdkVersion,
smallestScreenWidthDp, screenWidthDp, screenHeightDp, isInvalid); screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,
screenHeightDp, isInvalid);
} }
private void addMissingResSpecs() throws AndrolibException { private void addMissingResSpecs() throws AndrolibException {
@ -340,8 +340,8 @@ public class ARSCDecoder {
} }
} }
private void nextChunkCheckType(int expectedType) private void nextChunkCheckType(int expectedType) throws IOException,
throws IOException, AndrolibException { AndrolibException {
nextChunk(); nextChunk();
checkChunkType(expectedType); checkChunkType(expectedType);
} }
@ -362,10 +362,8 @@ public class ARSCDecoder {
private int mResId; private int mResId;
private boolean[] mMissingResSpecs; private boolean[] mMissingResSpecs;
private final static short ENTRY_FLAG_COMPLEX = 0x0001; private final static short ENTRY_FLAG_COMPLEX = 0x0001;
public static class Header { public static class Header {
public final short type; public final short type;
public final int chunkSize; public final int chunkSize;
@ -386,11 +384,8 @@ public class ARSCDecoder {
return new Header(type, in.readInt()); return new Header(type, in.readInt());
} }
public final static short public final static short TYPE_NONE = -1, TYPE_TABLE = 0x0002,
TYPE_NONE = -1, TYPE_PACKAGE = 0x0200, TYPE_TYPE = 0x0202,
TYPE_TABLE = 0x0002,
TYPE_PACKAGE = 0x0200,
TYPE_TYPE = 0x0202,
TYPE_CONFIG = 0x0201; TYPE_CONFIG = 0x0201;
} }
@ -404,11 +399,10 @@ public class ARSCDecoder {
} }
} }
private static final Logger LOGGER = private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class
Logger.getLogger(ARSCDecoder.class.getName()); .getName());
private static final int KNOWN_CONFIG_BYTES = 36; private static final int KNOWN_CONFIG_BYTES = 36;
public static class ARSCData { public static class ARSCData {
public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets, public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets,

View File

@ -34,11 +34,11 @@ import org.xmlpull.v1.XmlPullParserException;
* *
* Binary xml files parser. * Binary xml files parser.
* *
* Parser has only two states: (1) Operational state, which parser obtains after * Parser has only two states: (1) Operational state, which parser
* first successful call to next() and retains until open(), close(), or failed * obtains after first successful call to next() and retains until
* call to next(). (2) Closed state, which parser obtains after open(), close(), * open(), close(), or failed call to next(). (2) Closed state, which
* or failed call to next(). In this state methods return invalid values or * parser obtains after open(), close(), or failed call to next(). In
* throw exceptions. * this state methods return invalid values or throw exceptions.
* *
* TODO: * check all methods in closed state * TODO: * check all methods in closed state
* *
@ -69,11 +69,11 @@ public class AXmlResourceParser implements XmlResourceParser {
public void open(InputStream stream) { public void open(InputStream stream) {
close(); close();
if (stream != null) { if (stream != null) {
m_reader = new ExtDataInput( m_reader = new ExtDataInput(new LEDataInputStream(stream));
new LEDataInputStream(stream));
} }
} }
@Override
public void close() { public void close() {
if (!m_operational) { if (!m_operational) {
return; return;
@ -88,9 +88,11 @@ public class AXmlResourceParser implements XmlResourceParser {
} }
// ///////////////////////////////// iteration // ///////////////////////////////// iteration
@Override
public int next() throws XmlPullParserException, IOException { public int next() throws XmlPullParserException, IOException {
if (m_reader == null) { if (m_reader == null) {
throw new XmlPullParserException("Parser is not opened.", this, null); throw new XmlPullParserException("Parser is not opened.", this,
null);
} }
try { try {
doNext(); doNext();
@ -101,60 +103,77 @@ public class AXmlResourceParser implements XmlResourceParser {
} }
} }
@Override
public int nextToken() throws XmlPullParserException, IOException { public int nextToken() throws XmlPullParserException, IOException {
return next(); return next();
} }
@Override
public int nextTag() throws XmlPullParserException, IOException { public int nextTag() throws XmlPullParserException, IOException {
int eventType = next(); int eventType = next();
if (eventType == TEXT && isWhitespace()) { if (eventType == TEXT && isWhitespace()) {
eventType = next(); eventType = next();
} }
if (eventType != START_TAG && eventType != END_TAG) { if (eventType != START_TAG && eventType != END_TAG) {
throw new XmlPullParserException("Expected start or end tag.", this, null); throw new XmlPullParserException("Expected start or end tag.",
this, null);
} }
return eventType; return eventType;
} }
@Override
public String nextText() throws XmlPullParserException, IOException { public String nextText() throws XmlPullParserException, IOException {
if (getEventType() != START_TAG) { if (getEventType() != START_TAG) {
throw new XmlPullParserException("Parser must be on START_TAG to read next text.", this, null); throw new XmlPullParserException(
"Parser must be on START_TAG to read next text.", this,
null);
} }
int eventType = next(); int eventType = next();
if (eventType == TEXT) { if (eventType == TEXT) {
String result = getText(); String result = getText();
eventType = next(); eventType = next();
if (eventType != END_TAG) { if (eventType != END_TAG) {
throw new XmlPullParserException("Event TEXT must be immediately followed by END_TAG.", this, null); throw new XmlPullParserException(
"Event TEXT must be immediately followed by END_TAG.",
this, null);
} }
return result; return result;
} else if (eventType == END_TAG) { } else if (eventType == END_TAG) {
return ""; return "";
} else { } else {
throw new XmlPullParserException("Parser must be on START_TAG or TEXT to read text.", this, null); 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 { @Override
public void require(int type, String namespace, String name)
throws XmlPullParserException, IOException {
if (type != getEventType() if (type != getEventType()
|| (namespace != null && !namespace.equals(getNamespace())) || (namespace != null && !namespace.equals(getNamespace()))
|| (name != null && !name.equals(getName()))) { || (name != null && !name.equals(getName()))) {
throw new XmlPullParserException(TYPES[type] + " is expected.", this, null); throw new XmlPullParserException(TYPES[type] + " is expected.",
this, null);
} }
} }
@Override
public int getDepth() { public int getDepth() {
return m_namespaces.getDepth() - 1; return m_namespaces.getDepth() - 1;
} }
@Override
public int getEventType() throws XmlPullParserException { public int getEventType() throws XmlPullParserException {
return m_event; return m_event;
} }
@Override
public int getLineNumber() { public int getLineNumber() {
return m_lineNumber; return m_lineNumber;
} }
@Override
public String getName() { public String getName() {
if (m_name == -1 || (m_event != START_TAG && m_event != END_TAG)) { if (m_name == -1 || (m_event != START_TAG && m_event != END_TAG)) {
return null; return null;
@ -162,6 +181,7 @@ public class AXmlResourceParser implements XmlResourceParser {
return m_strings.getString(m_name); return m_strings.getString(m_name);
} }
@Override
public String getText() { public String getText() {
if (m_name == -1 || m_event != TEXT) { if (m_name == -1 || m_event != TEXT) {
return null; return null;
@ -169,6 +189,7 @@ public class AXmlResourceParser implements XmlResourceParser {
return m_strings.getString(m_name); return m_strings.getString(m_name);
} }
@Override
public char[] getTextCharacters(int[] holderForStartAndLength) { public char[] getTextCharacters(int[] holderForStartAndLength) {
String text = getText(); String text = getText();
if (text == null) { if (text == null) {
@ -181,34 +202,41 @@ public class AXmlResourceParser implements XmlResourceParser {
return chars; return chars;
} }
@Override
public String getNamespace() { public String getNamespace() {
return m_strings.getString(m_namespaceUri); return m_strings.getString(m_namespaceUri);
} }
@Override
public String getPrefix() { public String getPrefix() {
int prefix = m_namespaces.findPrefix(m_namespaceUri); int prefix = m_namespaces.findPrefix(m_namespaceUri);
return m_strings.getString(prefix); return m_strings.getString(prefix);
} }
@Override
public String getPositionDescription() { public String getPositionDescription() {
return "XML line #" + getLineNumber(); return "XML line #" + getLineNumber();
} }
@Override
public int getNamespaceCount(int depth) throws XmlPullParserException { public int getNamespaceCount(int depth) throws XmlPullParserException {
return m_namespaces.getAccumulatedCount(depth); return m_namespaces.getAccumulatedCount(depth);
} }
@Override
public String getNamespacePrefix(int pos) throws XmlPullParserException { public String getNamespacePrefix(int pos) throws XmlPullParserException {
int prefix = m_namespaces.getPrefix(pos); int prefix = m_namespaces.getPrefix(pos);
return m_strings.getString(prefix); return m_strings.getString(prefix);
} }
@Override
public String getNamespaceUri(int pos) throws XmlPullParserException { public String getNamespaceUri(int pos) throws XmlPullParserException {
int uri = m_namespaces.getUri(pos); int uri = m_namespaces.getUri(pos);
return m_strings.getString(uri); return m_strings.getString(uri);
} }
// ///////////////////////////////// attributes // ///////////////////////////////// attributes
@Override
public String getClassAttribute() { public String getClassAttribute() {
if (m_classAttribute == -1) { if (m_classAttribute == -1) {
return null; return null;
@ -218,6 +246,7 @@ public class AXmlResourceParser implements XmlResourceParser {
return m_strings.getString(value); return m_strings.getString(value);
} }
@Override
public String getIdAttribute() { public String getIdAttribute() {
if (m_idAttribute == -1) { if (m_idAttribute == -1) {
return null; return null;
@ -227,6 +256,7 @@ public class AXmlResourceParser implements XmlResourceParser {
return m_strings.getString(value); return m_strings.getString(value);
} }
@Override
public int getIdAttributeResourceValue(int defaultValue) { public int getIdAttributeResourceValue(int defaultValue) {
if (m_idAttribute == -1) { if (m_idAttribute == -1) {
return defaultValue; return defaultValue;
@ -239,6 +269,7 @@ public class AXmlResourceParser implements XmlResourceParser {
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
} }
@Override
public int getStyleAttribute() { public int getStyleAttribute() {
if (m_styleAttribute == -1) { if (m_styleAttribute == -1) {
return 0; return 0;
@ -247,6 +278,7 @@ public class AXmlResourceParser implements XmlResourceParser {
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
} }
@Override
public int getAttributeCount() { public int getAttributeCount() {
if (m_event != START_TAG) { if (m_event != START_TAG) {
return -1; return -1;
@ -254,6 +286,7 @@ public class AXmlResourceParser implements XmlResourceParser {
return m_attributes.length / ATTRIBUTE_LENGHT; return m_attributes.length / ATTRIBUTE_LENGHT;
} }
@Override
public String getAttributeNamespace(int index) { public String getAttributeNamespace(int index) {
int offset = getAttributeOffset(index); int offset = getAttributeOffset(index);
int namespace = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; int namespace = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI];
@ -263,6 +296,7 @@ public class AXmlResourceParser implements XmlResourceParser {
return m_strings.getString(namespace); return m_strings.getString(namespace);
} }
@Override
public String getAttributePrefix(int index) { public String getAttributePrefix(int index) {
int offset = getAttributeOffset(index); int offset = getAttributeOffset(index);
int uri = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; int uri = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI];
@ -273,6 +307,7 @@ public class AXmlResourceParser implements XmlResourceParser {
return m_strings.getString(prefix); return m_strings.getString(prefix);
} }
@Override
public String getAttributeName(int index) { public String getAttributeName(int index) {
int offset = getAttributeOffset(index); int offset = getAttributeOffset(index);
int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; int name = m_attributes[offset + ATTRIBUTE_IX_NAME];
@ -282,26 +317,29 @@ public class AXmlResourceParser implements XmlResourceParser {
return m_strings.getString(name); return m_strings.getString(name);
} }
@Override
public int getAttributeNameResource(int index) { public int getAttributeNameResource(int index) {
int offset = getAttributeOffset(index); int offset = getAttributeOffset(index);
int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; int name = m_attributes[offset + ATTRIBUTE_IX_NAME];
if (m_resourceIDs == null if (m_resourceIDs == null || name < 0 || name >= m_resourceIDs.length) {
|| name < 0 || name >= m_resourceIDs.length) {
return 0; return 0;
} }
return m_resourceIDs[name]; return m_resourceIDs[name];
} }
@Override
public int getAttributeValueType(int index) { public int getAttributeValueType(int index) {
int offset = getAttributeOffset(index); int offset = getAttributeOffset(index);
return m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; return m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
} }
@Override
public int getAttributeValueData(int index) { public int getAttributeValueData(int index) {
int offset = getAttributeOffset(index); int offset = getAttributeOffset(index);
return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA];
} }
@Override
public String getAttributeValue(int index) { public String getAttributeValue(int index) {
int offset = getAttributeOffset(index); int offset = getAttributeOffset(index);
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
@ -310,9 +348,11 @@ public class AXmlResourceParser implements XmlResourceParser {
if (mAttrDecoder != null) { if (mAttrDecoder != null) {
try { try {
return mAttrDecoder.decode(valueType, valueData, return mAttrDecoder.decode(
valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars( valueType,
m_strings.getString(valueRaw)), valueData,
valueRaw == -1 ? null : ResXmlEncoders
.escapeXmlChars(m_strings.getString(valueRaw)),
getAttributeNameResource(index)); getAttributeNameResource(index));
} catch (AndrolibException ex) { } catch (AndrolibException ex) {
setFirstError(ex); setFirstError(ex);
@ -324,17 +364,20 @@ public class AXmlResourceParser implements XmlResourceParser {
} }
} else { } else {
if (valueType == TypedValue.TYPE_STRING) { if (valueType == TypedValue.TYPE_STRING) {
return ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw)); return ResXmlEncoders.escapeXmlChars(m_strings
.getString(valueRaw));
} }
} }
return TypedValue.coerceToString(valueType, valueData); return TypedValue.coerceToString(valueType, valueData);
} }
@Override
public boolean getAttributeBooleanValue(int index, boolean defaultValue) { public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
return getAttributeIntValue(index, defaultValue ? 1 : 0) != 0; return getAttributeIntValue(index, defaultValue ? 1 : 0) != 0;
} }
@Override
public float getAttributeFloatValue(int index, float defaultValue) { public float getAttributeFloatValue(int index, float defaultValue) {
int offset = getAttributeOffset(index); int offset = getAttributeOffset(index);
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
@ -345,6 +388,7 @@ public class AXmlResourceParser implements XmlResourceParser {
return defaultValue; return defaultValue;
} }
@Override
public int getAttributeIntValue(int index, int defaultValue) { public int getAttributeIntValue(int index, int defaultValue) {
int offset = getAttributeOffset(index); int offset = getAttributeOffset(index);
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
@ -355,10 +399,12 @@ public class AXmlResourceParser implements XmlResourceParser {
return defaultValue; return defaultValue;
} }
@Override
public int getAttributeUnsignedIntValue(int index, int defaultValue) { public int getAttributeUnsignedIntValue(int index, int defaultValue) {
return getAttributeIntValue(index, defaultValue); return getAttributeIntValue(index, defaultValue);
} }
@Override
public int getAttributeResourceValue(int index, int defaultValue) { public int getAttributeResourceValue(int index, int defaultValue) {
int offset = getAttributeOffset(index); int offset = getAttributeOffset(index);
int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE];
@ -368,6 +414,7 @@ public class AXmlResourceParser implements XmlResourceParser {
return defaultValue; return defaultValue;
} }
@Override
public String getAttributeValue(String namespace, String attribute) { public String getAttributeValue(String namespace, String attribute) {
int index = findAttribute(namespace, attribute); int index = findAttribute(namespace, attribute);
if (index == -1) { if (index == -1) {
@ -376,7 +423,9 @@ public class AXmlResourceParser implements XmlResourceParser {
return getAttributeValue(index); return getAttributeValue(index);
} }
public boolean getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue) { @Override
public boolean getAttributeBooleanValue(String namespace, String attribute,
boolean defaultValue) {
int index = findAttribute(namespace, attribute); int index = findAttribute(namespace, attribute);
if (index == -1) { if (index == -1) {
return defaultValue; return defaultValue;
@ -384,7 +433,9 @@ public class AXmlResourceParser implements XmlResourceParser {
return getAttributeBooleanValue(index, defaultValue); return getAttributeBooleanValue(index, defaultValue);
} }
public float getAttributeFloatValue(String namespace, String attribute, float defaultValue) { @Override
public float getAttributeFloatValue(String namespace, String attribute,
float defaultValue) {
int index = findAttribute(namespace, attribute); int index = findAttribute(namespace, attribute);
if (index == -1) { if (index == -1) {
return defaultValue; return defaultValue;
@ -392,7 +443,9 @@ public class AXmlResourceParser implements XmlResourceParser {
return getAttributeFloatValue(index, defaultValue); return getAttributeFloatValue(index, defaultValue);
} }
public int getAttributeIntValue(String namespace, String attribute, int defaultValue) { @Override
public int getAttributeIntValue(String namespace, String attribute,
int defaultValue) {
int index = findAttribute(namespace, attribute); int index = findAttribute(namespace, attribute);
if (index == -1) { if (index == -1) {
return defaultValue; return defaultValue;
@ -400,7 +453,9 @@ public class AXmlResourceParser implements XmlResourceParser {
return getAttributeIntValue(index, defaultValue); return getAttributeIntValue(index, defaultValue);
} }
public int getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue) { @Override
public int getAttributeUnsignedIntValue(String namespace, String attribute,
int defaultValue) {
int index = findAttribute(namespace, attribute); int index = findAttribute(namespace, attribute);
if (index == -1) { if (index == -1) {
return defaultValue; return defaultValue;
@ -408,7 +463,9 @@ public class AXmlResourceParser implements XmlResourceParser {
return getAttributeUnsignedIntValue(index, defaultValue); return getAttributeUnsignedIntValue(index, defaultValue);
} }
public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) { @Override
public int getAttributeResourceValue(String namespace, String attribute,
int defaultValue) {
int index = findAttribute(namespace, attribute); int index = findAttribute(namespace, attribute);
if (index == -1) { if (index == -1) {
return defaultValue; return defaultValue;
@ -416,70 +473,92 @@ public class AXmlResourceParser implements XmlResourceParser {
return getAttributeResourceValue(index, defaultValue); return getAttributeResourceValue(index, defaultValue);
} }
public int getAttributeListValue(int index, String[] options, int defaultValue) { @Override
public int getAttributeListValue(int index, String[] options,
int defaultValue) {
// TODO implement // TODO implement
return 0; return 0;
} }
public int getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue) { @Override
public int getAttributeListValue(String namespace, String attribute,
String[] options, int defaultValue) {
// TODO implement // TODO implement
return 0; return 0;
} }
@Override
public String getAttributeType(int index) { public String getAttributeType(int index) {
return "CDATA"; return "CDATA";
} }
@Override
public boolean isAttributeDefault(int index) { public boolean isAttributeDefault(int index) {
return false; return false;
} }
// ///////////////////////////////// dummies // ///////////////////////////////// dummies
public void setInput(InputStream stream, String inputEncoding) throws XmlPullParserException { @Override
public void setInput(InputStream stream, String inputEncoding)
throws XmlPullParserException {
open(stream); open(stream);
} }
@Override
public void setInput(Reader reader) throws XmlPullParserException { public void setInput(Reader reader) throws XmlPullParserException {
throw new XmlPullParserException(E_NOT_SUPPORTED); throw new XmlPullParserException(E_NOT_SUPPORTED);
} }
@Override
public String getInputEncoding() { public String getInputEncoding() {
return null; return null;
} }
@Override
public int getColumnNumber() { public int getColumnNumber() {
return -1; return -1;
} }
@Override
public boolean isEmptyElementTag() throws XmlPullParserException { public boolean isEmptyElementTag() throws XmlPullParserException {
return false; return false;
} }
@Override
public boolean isWhitespace() throws XmlPullParserException { public boolean isWhitespace() throws XmlPullParserException {
return false; return false;
} }
public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException { @Override
public void defineEntityReplacementText(String entityName,
String replacementText) throws XmlPullParserException {
throw new XmlPullParserException(E_NOT_SUPPORTED); throw new XmlPullParserException(E_NOT_SUPPORTED);
} }
@Override
public String getNamespace(String prefix) { public String getNamespace(String prefix) {
throw new RuntimeException(E_NOT_SUPPORTED); throw new RuntimeException(E_NOT_SUPPORTED);
} }
@Override
public Object getProperty(String name) { public Object getProperty(String name) {
return null; return null;
} }
public void setProperty(String name, Object value) throws XmlPullParserException { @Override
public void setProperty(String name, Object value)
throws XmlPullParserException {
throw new XmlPullParserException(E_NOT_SUPPORTED); throw new XmlPullParserException(E_NOT_SUPPORTED);
} }
@Override
public boolean getFeature(String feature) { public boolean getFeature(String feature) {
return false; return false;
} }
public void setFeature(String name, boolean value) throws XmlPullParserException { @Override
public void setFeature(String name, boolean value)
throws XmlPullParserException {
throw new XmlPullParserException(E_NOT_SUPPORTED); throw new XmlPullParserException(E_NOT_SUPPORTED);
} }
@ -574,10 +653,7 @@ public class AXmlResourceParser implements XmlResourceParser {
m_data[offset] = count; m_data[offset] = count;
offset -= (1 + 2 + count * 2); offset -= (1 + 2 + count * 2);
m_data[offset] = count; m_data[offset] = count;
System.arraycopy( System.arraycopy(m_data, o + 2, m_data, o, m_dataLength - o);
m_data, o + 2,
m_data, o,
m_dataLength - o);
} }
m_dataLength -= 2; m_dataLength -= 2;
m_count -= 1; m_count -= 1;
@ -703,6 +779,7 @@ public class AXmlResourceParser implements XmlResourceParser {
} }
return -1; return -1;
} }
private int[] m_data; private int[] m_data;
private int m_dataLength; private int m_dataLength;
private int m_count; private int m_count;
@ -750,11 +827,13 @@ public class AXmlResourceParser implements XmlResourceParser {
// ///////////////////////////////// // /////////////////////////////////
private final int getAttributeOffset(int index) { private final int getAttributeOffset(int index) {
if (m_event != START_TAG) { if (m_event != START_TAG) {
throw new IndexOutOfBoundsException("Current event is not START_TAG."); throw new IndexOutOfBoundsException(
"Current event is not START_TAG.");
} }
int offset = index * ATTRIBUTE_LENGHT; int offset = index * ATTRIBUTE_LENGHT;
if (offset >= m_attributes.length) { if (offset >= m_attributes.length) {
throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ")."); throw new IndexOutOfBoundsException("Invalid attribute index ("
+ index + ").");
} }
return offset; return offset;
} }
@ -767,12 +846,11 @@ public class AXmlResourceParser implements XmlResourceParser {
if (name == -1) { if (name == -1) {
return -1; return -1;
} }
int uri = (namespace != null) int uri = (namespace != null) ? m_strings.find(namespace) : -1;
? m_strings.find(namespace)
: -1;
for (int o = 0; o != m_attributes.length; o += ATTRIBUTE_LENGHT) { for (int o = 0; o != m_attributes.length; o += ATTRIBUTE_LENGHT) {
if (name == m_attributes[o + ATTRIBUTE_IX_NAME] if (name == m_attributes[o + ATTRIBUTE_IX_NAME]
&& (uri == -1 || uri == m_attributes[o + ATTRIBUTE_IX_NAMESPACE_URI])) { && (uri == -1 || uri == m_attributes[o
+ ATTRIBUTE_IX_NAMESPACE_URI])) {
return o / ATTRIBUTE_LENGHT; return o / ATTRIBUTE_LENGHT;
} }
} }
@ -816,8 +894,7 @@ public class AXmlResourceParser implements XmlResourceParser {
} }
// Fake END_DOCUMENT event. // Fake END_DOCUMENT event.
if (event == END_TAG if (event == END_TAG && m_namespaces.getDepth() == 1
&& m_namespaces.getDepth() == 1
&& m_namespaces.getCurrentCount() == 0) { && m_namespaces.getCurrentCount() == 0) {
m_event = END_DOCUMENT; m_event = END_DOCUMENT;
break; break;
@ -834,7 +911,8 @@ public class AXmlResourceParser implements XmlResourceParser {
if (chunkType == CHUNK_RESOURCEIDS) { if (chunkType == CHUNK_RESOURCEIDS) {
int chunkSize = m_reader.readInt(); int chunkSize = m_reader.readInt();
if (chunkSize < 8 || (chunkSize % 4) != 0) { if (chunkSize < 8 || (chunkSize % 4) != 0) {
throw new IOException("Invalid resource ids size (" + chunkSize + ")."); throw new IOException("Invalid resource ids size ("
+ chunkSize + ").");
} }
m_resourceIDs = m_reader.readIntArray(chunkSize / 4 - 2); m_resourceIDs = m_reader.readIntArray(chunkSize / 4 - 2);
continue; continue;
@ -881,7 +959,8 @@ public class AXmlResourceParser implements XmlResourceParser {
m_classAttribute = m_reader.readInt(); m_classAttribute = m_reader.readInt();
m_styleAttribute = (m_classAttribute >>> 16) - 1; m_styleAttribute = (m_classAttribute >>> 16) - 1;
m_classAttribute = (m_classAttribute & 0xFFFF) - 1; m_classAttribute = (m_classAttribute & 0xFFFF) - 1;
m_attributes = m_reader.readIntArray(attributeCount * ATTRIBUTE_LENGHT); m_attributes = m_reader.readIntArray(attributeCount
* ATTRIBUTE_LENGHT);
for (int i = ATTRIBUTE_IX_VALUE_TYPE; i < m_attributes.length;) { for (int i = ATTRIBUTE_IX_VALUE_TYPE; i < m_attributes.length;) {
m_attributes[i] = (m_attributes[i] >>> 24); m_attributes[i] = (m_attributes[i] >>> 24);
i += ATTRIBUTE_LENGHT; i += ATTRIBUTE_LENGHT;
@ -937,29 +1016,34 @@ public class AXmlResourceParser implements XmlResourceParser {
* ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING : * ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING :
* ATTRIBUTE_IX_NAMESPACE_URI ATTRIBUTE_IX_NAMESPACE_URI : * ATTRIBUTE_IX_NAMESPACE_URI ATTRIBUTE_IX_NAMESPACE_URI :
* ATTRIBUTE_IX_NAME id * ATTRIBUTE_IX_NAME id
*
*/ */
if (attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING if (attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING
&& attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE] && attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE]
&& //(m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) || && // (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) && // 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]) { attr1[ATTRIBUTE_IX_VALUE_STRING] != attr2[ATTRIBUTE_IX_VALUE_STRING]) {
return (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) } else if ((attr1[ATTRIBUTE_IX_NAMESPACE_URI] == attr2[ATTRIBUTE_IX_NAMESPACE_URI])
&& //(m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) || && (attr1[ATTRIBUTE_IX_NAMESPACE_URI] != -1) && // (m_strings.touch(attr1[ATTRIBUTE_IX_NAME],
// m_strings.touch(attr2[ATTRIBUTE_IX_NAME], m_name)) && // m_name) ||
//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])) { (attr1[ATTRIBUTE_IX_NAME] != attr2[ATTRIBUTE_IX_NAME])) {
return (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]) { // } else if (attr1[ATTRIBUTE_IX_NAMESPACE_URI] <
// attr2[ATTRIBUTE_IX_NAMESPACE_URI]) {
// return true; // return true;
} else { } else {
return false; return false;
} }
} }
private void setFirstError(AndrolibException error) { private void setFirstError(AndrolibException error) {
if (mFirstError == null) { if (mFirstError == null) {
mFirstError = error; mFirstError = error;
@ -968,8 +1052,8 @@ public class AXmlResourceParser implements XmlResourceParser {
// ///////////////////////////////// data // ///////////////////////////////// data
/* /*
* All values are essentially indices, e.g. m_name is * All values are essentially indices, e.g. m_name is an index of name in
* an index of name in m_strings. * m_strings.
*/ */
private ExtDataInput m_reader; private ExtDataInput m_reader;
private ResAttrDecoder mAttrDecoder; private ResAttrDecoder mAttrDecoder;
@ -989,25 +1073,18 @@ public class AXmlResourceParser implements XmlResourceParser {
private int m_classAttribute; private int m_classAttribute;
private int m_styleAttribute; private int m_styleAttribute;
private final static Logger LOGGER = private final static Logger LOGGER = Logger
Logger.getLogger(AXmlResourceParser.class.getName()); .getLogger(AXmlResourceParser.class.getName());
private static final String E_NOT_SUPPORTED = "Method is not supported."; private static final String E_NOT_SUPPORTED = "Method is not supported.";
private static final int private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0,
ATTRIBUTE_IX_NAMESPACE_URI = 0, ATTRIBUTE_IX_NAME = 1, ATTRIBUTE_IX_VALUE_STRING = 2,
ATTRIBUTE_IX_NAME = 1, ATTRIBUTE_IX_VALUE_TYPE = 3, ATTRIBUTE_IX_VALUE_DATA = 4,
ATTRIBUTE_IX_VALUE_STRING = 2,
ATTRIBUTE_IX_VALUE_TYPE = 3,
ATTRIBUTE_IX_VALUE_DATA = 4,
ATTRIBUTE_LENGHT = 5; ATTRIBUTE_LENGHT = 5;
private static final int private static final int CHUNK_AXML_FILE = 0x00080003,
CHUNK_AXML_FILE = 0x00080003, CHUNK_RESOURCEIDS = 0x00080180, CHUNK_XML_FIRST = 0x00100100,
CHUNK_RESOURCEIDS = 0x00080180,
CHUNK_XML_FIRST = 0x00100100,
CHUNK_XML_START_NAMESPACE = 0x00100100, CHUNK_XML_START_NAMESPACE = 0x00100100,
CHUNK_XML_END_NAMESPACE = 0x00100101, CHUNK_XML_END_NAMESPACE = 0x00100101,
CHUNK_XML_START_TAG = 0x00100102, CHUNK_XML_START_TAG = 0x00100102, CHUNK_XML_END_TAG = 0x00100103,
CHUNK_XML_END_TAG = 0x00100103, CHUNK_XML_TEXT = 0x00100104, CHUNK_XML_LAST = 0x00100104;
CHUNK_XML_TEXT = 0x00100104,
CHUNK_XML_LAST = 0x00100104;
} }

View File

@ -28,6 +28,7 @@ import org.apache.commons.io.IOUtils;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class Res9patchStreamDecoder implements ResStreamDecoder { public class Res9patchStreamDecoder implements ResStreamDecoder {
@Override
public void decode(InputStream in, OutputStream out) public void decode(InputStream in, OutputStream out)
throws AndrolibException { throws AndrolibException {
try { try {
@ -36,8 +37,8 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
int w = im.getWidth(), h = im.getHeight(); int w = im.getWidth(), h = im.getHeight();
BufferedImage im2 = new BufferedImage( BufferedImage im2 = new BufferedImage(w + 2, h + 2,
w + 2, h + 2, BufferedImage.TYPE_4BYTE_ABGR); BufferedImage.TYPE_4BYTE_ABGR);
if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) { if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) {
im2.getRaster().setRect(1, 1, im.getRaster()); im2.getRaster().setRect(1, 1, im.getRaster());
} else { } else {
@ -64,15 +65,15 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
} }
} }
private NinePatch getNinePatch(byte[] data) private NinePatch getNinePatch(byte[] data) throws AndrolibException,
throws AndrolibException, IOException { IOException {
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
find9patchChunk(di); find9patchChunk(di);
return NinePatch.decode(di); return NinePatch.decode(di);
} }
private void find9patchChunk(DataInput di) private void find9patchChunk(DataInput di) throws AndrolibException,
throws AndrolibException, IOException { IOException {
di.skipBytes(8); di.skipBytes(8);
while (true) { while (true) {
int size; int size;
@ -103,7 +104,6 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc
private static final int NP_COLOR = 0xff000000; private static final int NP_COLOR = 0xff000000;
private static class NinePatch { private static class NinePatch {
public final int padLeft, padRight, padTop, padBottom; public final int padLeft, padRight, padTop, padBottom;
public final int[] xDivs, yDivs; public final int[] xDivs, yDivs;
@ -132,8 +132,8 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
int[] xDivs = di.readIntArray(numXDivs); int[] xDivs = di.readIntArray(numXDivs);
int[] yDivs = di.readIntArray(numYDivs); int[] yDivs = di.readIntArray(numYDivs);
return new NinePatch(padLeft, padRight, padTop, padBottom, return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs,
xDivs, yDivs); yDivs);
} }
} }
} }

View File

@ -27,8 +27,8 @@ import brut.androlib.res.data.value.ResScalarValue;
public class ResAttrDecoder { public class ResAttrDecoder {
public String decode(int type, int value, String rawValue, int attrResId) public String decode(int type, int value, String rawValue, int attrResId)
throws AndrolibException { throws AndrolibException {
ResScalarValue resValue = mCurrentPackage.getValueFactory() ResScalarValue resValue = mCurrentPackage.getValueFactory().factory(
.factory(type, value, rawValue); type, value, rawValue);
String decoded = null; String decoded = null;
if (attrResId != 0) { if (attrResId != 0) {

View File

@ -70,14 +70,14 @@ public class ResFileDecoder {
} }
try { try {
decode( decode(inDir, inFileName, outDir, outFileName, "9patch");
inDir, inFileName, outDir, outFileName, "9patch");
return; return;
} catch (CantFind9PatchChunk ex) { } catch (CantFind9PatchChunk ex) {
LOGGER.log(Level.WARNING, String.format( LOGGER.log(
Level.WARNING,
String.format(
"Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.", "Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.",
inFileName inFileName), ex);
), ex);
outDir.removeFile(outFileName); outDir.removeFile(outFileName);
outFileName = outResName + ext; outFileName = outResName + ext;
} }
@ -121,14 +121,15 @@ public class ResFileDecoder {
} }
} }
public void decodeManifest(Directory inDir, String inFileName, Directory outDir, public void decodeManifest(Directory inDir, String inFileName,
String outFileName) throws AndrolibException { Directory outDir, String outFileName) throws AndrolibException {
InputStream in = null; InputStream in = null;
OutputStream out = null; OutputStream out = null;
try { try {
in = inDir.getFileInput(inFileName); in = inDir.getFileInput(inFileName);
out = outDir.getFileOutput(outFileName); out = outDir.getFileOutput(outFileName);
((XmlPullStreamDecoder)mDecoders.getDecoder("xml")).decodeManifest(in, out); ((XmlPullStreamDecoder) mDecoders.getDecoder("xml"))
.decodeManifest(in, out);
} catch (DirectoryException ex) { } catch (DirectoryException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
} finally { } finally {
@ -145,6 +146,6 @@ public class ResFileDecoder {
} }
} }
private final static Logger LOGGER = private final static Logger LOGGER = Logger.getLogger(ResFileDecoder.class
Logger.getLogger(ResFileDecoder.class.getName()); .getName());
} }

View File

@ -26,6 +26,7 @@ import org.apache.commons.io.IOUtils;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResRawStreamDecoder implements ResStreamDecoder { public class ResRawStreamDecoder implements ResStreamDecoder {
@Override
public void decode(InputStream in, OutputStream out) public void decode(InputStream in, OutputStream out)
throws AndrolibException { throws AndrolibException {
try { try {

View File

@ -26,8 +26,7 @@ import java.util.Map;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public class ResStreamDecoderContainer { public class ResStreamDecoderContainer {
private final Map<String, ResStreamDecoder> mDecoders = private final Map<String, ResStreamDecoder> mDecoders = new HashMap<String, ResStreamDecoder>();
new HashMap<String, ResStreamDecoder>();
public void decode(InputStream in, OutputStream out, String decoderName) public void decode(InputStream in, OutputStream out, String decoderName)
throws AndrolibException { throws AndrolibException {

View File

@ -30,15 +30,14 @@ import java.util.logging.Logger;
* *
* Block of strings, used in binary xml and arsc. * Block of strings, used in binary xml and arsc.
* *
* TODO: * TODO: - implement get()
* - implement get()
* *
*/ */
public class StringBlock { public class StringBlock {
/** /**
* Reads whole (including chunk type) string block from stream. * Reads whole (including chunk type) string block from stream. Stream must
* Stream must be at the chunk type. * be at the chunk type.
*/ */
public static StringBlock read(ExtDataInput reader) throws IOException { public static StringBlock read(ExtDataInput reader) throws IOException {
reader.skipCheckInt(CHUNK_TYPE); reader.skipCheckInt(CHUNK_TYPE);
@ -60,9 +59,11 @@ public class StringBlock {
block.m_styleOffsets = reader.readIntArray(styleOffsetCount); block.m_styleOffsets = reader.readIntArray(styleOffsetCount);
} }
{ {
int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) - stringsOffset; int size = ((stylesOffset == 0) ? chunkSize : stylesOffset)
- stringsOffset;
if ((size % 4) != 0) { if ((size % 4) != 0) {
throw new IOException("String data size is not multiple of 4 (" + size + ")."); throw new IOException("String data size is not multiple of 4 ("
+ size + ").");
} }
block.m_strings = new byte[size]; block.m_strings = new byte[size];
reader.readFully(block.m_strings); reader.readFully(block.m_strings);
@ -70,7 +71,8 @@ public class StringBlock {
if (stylesOffset != 0) { if (stylesOffset != 0) {
int size = (chunkSize - stylesOffset); int size = (chunkSize - stylesOffset);
if ((size % 4) != 0) { if ((size % 4) != 0) {
throw new IOException("Style data size is not multiple of 4 (" + size + ")."); throw new IOException("Style data size is not multiple of 4 ("
+ size + ").");
} }
block.m_styles = reader.readIntArray(size / 4); block.m_styles = reader.readIntArray(size / 4);
} }
@ -82,17 +84,14 @@ public class StringBlock {
* Returns number of strings in block. * Returns number of strings in block.
*/ */
public int getCount() { public int getCount() {
return m_stringOffsets != null return m_stringOffsets != null ? m_stringOffsets.length : 0;
? m_stringOffsets.length
: 0;
} }
/** /**
* Returns raw string (without any styling information) at specified index. * Returns raw string (without any styling information) at specified index.
*/ */
public String getString(int index) { public String getString(int index) {
if (index < 0 if (index < 0 || m_stringOffsets == null
|| m_stringOffsets == null
|| index >= m_stringOffsets.length) { || index >= m_stringOffsets.length) {
return null; return null;
} }
@ -153,16 +152,16 @@ public class StringBlock {
break; break;
} }
if (offset <= end) { if (offset <= end) {
html.append(ResXmlEncoders.escapeXmlChars( html.append(ResXmlEncoders.escapeXmlChars(raw.substring(
raw.substring(offset, end + 1))); offset, end + 1)));
offset = end + 1; offset = end + 1;
} }
outputStyleTag(getString(style[last]), html, true); outputStyleTag(getString(style[last]), html, true);
} }
depth = j + 1; depth = j + 1;
if (offset < start) { if (offset < start) {
html.append(ResXmlEncoders.escapeXmlChars( html.append(ResXmlEncoders.escapeXmlChars(raw.substring(offset,
raw.substring(offset, start))); start)));
offset = start; offset = start;
} }
if (i == -1) { if (i == -1) {
@ -175,8 +174,7 @@ public class StringBlock {
return html.toString(); return html.toString();
} }
private void outputStyleTag(String tag, StringBuilder builder, private void outputStyleTag(String tag, StringBuilder builder, boolean close) {
boolean close) {
builder.append('<'); builder.append('<');
if (close) { if (close) {
builder.append('/'); builder.append('/');
@ -203,8 +201,8 @@ public class StringBlock {
val = tag.substring(pos2 + 1); val = tag.substring(pos2 + 1);
} }
builder.append(ResXmlEncoders.escapeXmlChars(val)) builder.append(ResXmlEncoders.escapeXmlChars(val)).append(
.append('"'); '"');
} }
} }
} }
@ -212,8 +210,7 @@ public class StringBlock {
} }
/** /**
* Finds index of the string. * Finds index of the string. Returns -1 if the string was not found.
* Returns -1 if the string was not found.
*/ */
public int find(String string) { public int find(String string) {
if (string == null) { if (string == null) {
@ -244,11 +241,9 @@ public class StringBlock {
} }
/** /**
* Returns style information - array of int triplets, * Returns style information - array of int triplets, where in each triplet:
* where in each triplet: * * first int is index of tag name ('b','i', etc.) * second int is tag
* * first int is index of tag name ('b','i', etc.) * start index in string * third int is tag end index in string
* * second int is tag start index in string
* * third int is tag end index in string
*/ */
private int[] getStyle(int index) { private int[] getStyle(int index) {
if (m_styleOffsets == null || m_styles == null if (m_styleOffsets == null || m_styles == null
@ -315,9 +310,7 @@ public class StringBlock {
} }
public boolean touch(int index, int own) { public boolean touch(int index, int own) {
if (index < 0 if (index < 0 || m_stringOwns == null || index >= m_stringOwns.length) {
|| m_stringOwns == null
|| index >= m_stringOwns.length) {
return false; return false;
} }
if (m_stringOwns[index] == -1) { if (m_stringOwns[index] == -1) {
@ -336,12 +329,12 @@ public class StringBlock {
private int[] m_styles; private int[] m_styles;
private boolean m_isUTF8; private boolean m_isUTF8;
private int[] m_stringOwns; private int[] m_stringOwns;
private static final CharsetDecoder UTF16LE_DECODER = private static final CharsetDecoder UTF16LE_DECODER = Charset.forName(
Charset.forName("UTF-16LE").newDecoder(); "UTF-16LE").newDecoder();
private static final CharsetDecoder UTF8_DECODER = private static final CharsetDecoder UTF8_DECODER = Charset.forName("UTF-8")
Charset.forName("UTF-8").newDecoder(); .newDecoder();
private static final Logger LOGGER = private static final Logger LOGGER = Logger.getLogger(StringBlock.class
Logger.getLogger(StringBlock.class.getName()); .getName());
private static final int CHUNK_TYPE = 0x001C0001; private static final int CHUNK_TYPE = 0x001C0001;
private static final int UTF8_FLAG = 0x00000100; private static final int UTF8_FLAG = 0x00000100;
} }

View File

@ -42,57 +42,68 @@ public class XmlPullStreamDecoder implements ResStreamDecoder {
this.mSerial = serializer; this.mSerial = serializer;
} }
@Override
public void decode(InputStream in, OutputStream out) public void decode(InputStream in, OutputStream out)
throws AndrolibException { throws AndrolibException {
try { try {
XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance(); XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance();
XmlPullParserWrapper par = factory.newPullParserWrapper(mParser); XmlPullParserWrapper par = factory.newPullParserWrapper(mParser);
final ResTable resTable = ((AXmlResourceParser)mParser).getAttrDecoder().getCurrentPackage().getResTable(); final ResTable resTable = ((AXmlResourceParser) mParser)
.getAttrDecoder().getCurrentPackage().getResTable();
XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, factory){ XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial,
factory) {
boolean hideSdkInfo = false; boolean hideSdkInfo = false;
boolean hidePackageInfo = false; boolean hidePackageInfo = false;
@Override @Override
public void event(XmlPullParser pp) throws XmlPullParserException, IOException { public void event(XmlPullParser pp)
throws XmlPullParserException, IOException {
int type = pp.getEventType(); int type = pp.getEventType();
if (type == XmlPullParser.START_TAG) { if (type == XmlPullParser.START_TAG) {
if ("manifest".equalsIgnoreCase(pp.getName())) { if ("manifest".equalsIgnoreCase(pp.getName())) {
try { try {
hidePackageInfo = parseManifest(pp); hidePackageInfo = parseManifest(pp);
} catch (AndrolibException e) {} } catch (AndrolibException e) {
}
} else if ("uses-sdk".equalsIgnoreCase(pp.getName())) { } else if ("uses-sdk".equalsIgnoreCase(pp.getName())) {
try { try {
hideSdkInfo = parseAttr(pp); hideSdkInfo = parseAttr(pp);
if (hideSdkInfo) { if (hideSdkInfo) {
return; return;
} }
} catch (AndrolibException e) {} } catch (AndrolibException e) {
} }
} else if (hideSdkInfo && type == XmlPullParser.END_TAG && }
"uses-sdk".equalsIgnoreCase(pp.getName())) { } else if (hideSdkInfo && type == XmlPullParser.END_TAG
&& "uses-sdk".equalsIgnoreCase(pp.getName())) {
return; return;
} else if (hidePackageInfo && type == XmlPullParser.END_TAG && } else if (hidePackageInfo && type == XmlPullParser.END_TAG
"manifest".equalsIgnoreCase(pp.getName())) { && "manifest".equalsIgnoreCase(pp.getName())) {
super.event(pp); super.event(pp);
return; return;
} }
super.event(pp); super.event(pp);
} }
private boolean parseManifest(XmlPullParser pp) throws AndrolibException { private boolean parseManifest(XmlPullParser pp)
throws AndrolibException {
ResTable restable = resTable; ResTable restable = resTable;
// read <manifest> for package: // read <manifest> for package:
for (int i = 0; i < pp.getAttributeCount(); i++) { for (int i = 0; i < pp.getAttributeCount(); i++) {
if (pp.getAttributeName(i).equalsIgnoreCase(("package"))) { if (pp.getAttributeName(i)
restable.addPackageInfo("orig_package", pp.getAttributeValue(i)); .equalsIgnoreCase(("package"))) {
restable.addPackageInfo("orig_package",
pp.getAttributeValue(i));
} }
} }
return true; return true;
} }
private boolean parseAttr(XmlPullParser pp) throws AndrolibException { private boolean parseAttr(XmlPullParser pp)
throws AndrolibException {
ResTable restable = resTable; ResTable restable = resTable;
for (int i = 0; i < pp.getAttributeCount(); i++) { for (int i = 0; i < pp.getAttributeCount(); i++) {
final String a_ns = "http://schemas.android.com/apk/res/android"; final String a_ns = "http://schemas.android.com/apk/res/android";
@ -102,9 +113,9 @@ public class XmlPullStreamDecoder implements ResStreamDecoder {
String name = pp.getAttributeName(i); String name = pp.getAttributeName(i);
String value = pp.getAttributeValue(i); String value = pp.getAttributeValue(i);
if (name != null && value != null) { if (name != null && value != null) {
if (name.equalsIgnoreCase("minSdkVersion") || if (name.equalsIgnoreCase("minSdkVersion")
name.equalsIgnoreCase("targetSdkVersion") || || name.equalsIgnoreCase("targetSdkVersion")
name.equalsIgnoreCase("maxSdkVersion")) { || name.equalsIgnoreCase("maxSdkVersion")) {
restable.addSdkInfo(name, value); restable.addSdkInfo(name, value);
} else { } else {
restable.clearSdkInfo(); restable.clearSdkInfo();
@ -152,6 +163,6 @@ public class XmlPullStreamDecoder implements ResStreamDecoder {
private boolean mOptimizeForManifest = false; private boolean mOptimizeForManifest = false;
private final static Logger LOGGER = private final static Logger LOGGER = Logger
Logger.getLogger(XmlPullStreamDecoder.class.getName()); .getLogger(XmlPullStreamDecoder.class.getName());
} }

View File

@ -58,6 +58,5 @@ public class ExtFile extends File {
return mDirectory; return mDirectory;
} }
private Directory mDirectory; private Directory mDirectory;
} }

View File

@ -24,8 +24,8 @@ import org.xmlpull.mxp1_serializer.MXSerializer;
*/ */
public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer { public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer {
@Override @Override
public void startDocument(String encoding, Boolean standalone) throws public void startDocument(String encoding, Boolean standalone)
IOException, IllegalArgumentException, IllegalStateException { throws IOException, IllegalArgumentException, IllegalStateException {
super.startDocument(encoding != null ? encoding : mDefaultEncoding, super.startDocument(encoding != null ? encoding : mDefaultEncoding,
standalone); standalone);
this.newLine(); this.newLine();
@ -64,11 +64,13 @@ public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer {
} }
} }
@Override
public ExtXmlSerializer newLine() throws IOException { public ExtXmlSerializer newLine() throws IOException {
super.out.write(lineSeparator); super.out.write(lineSeparator);
return this; return this;
} }
@Override
public void setDisabledAttrEscape(boolean disabled) { public void setDisabledAttrEscape(boolean disabled) {
mIsDisabledAttrEscape = disabled; mIsDisabledAttrEscape = disabled;
} }

View File

@ -25,11 +25,10 @@ import org.xmlpull.v1.XmlSerializer;
public interface ExtXmlSerializer extends XmlSerializer { public interface ExtXmlSerializer extends XmlSerializer {
public ExtXmlSerializer newLine() throws IOException; public ExtXmlSerializer newLine() throws IOException;
public void setDisabledAttrEscape(boolean disabled); public void setDisabledAttrEscape(boolean disabled);
public static final String PROPERTY_SERIALIZER_INDENTATION = public static final String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#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_SERIALIZER_LINE_SEPARATOR =
"http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING"; public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING";
} }

View File

@ -25,6 +25,6 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
public interface ResValuesXmlSerializable { public interface ResValuesXmlSerializable {
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) public void serializeToResValuesXml(XmlSerializer serializer,
throws IOException, AndrolibException; ResResource res) throws IOException, AndrolibException;
} }

View File

@ -23,5 +23,6 @@ import brut.androlib.AndrolibException;
*/ */
public interface ResXmlEncodable { public interface ResXmlEncodable {
public String encodeAsResXmlAttr() throws AndrolibException; public String encodeAsResXmlAttr() throws AndrolibException;
public String encodeAsResXmlValue() throws AndrolibException; public String encodeAsResXmlValue() throws AndrolibException;
} }

View File

@ -194,9 +194,7 @@ public final class ResXmlEncoders {
private static boolean isPrintableChar(char c) { private static boolean isPrintableChar(char c) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(c); Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
return !Character.isISOControl(c) return !Character.isISOControl(c) && c != KeyEvent.CHAR_UNDEFINED
&& c != KeyEvent.CHAR_UNDEFINED && block != null && block != Character.UnicodeBlock.SPECIALS;
&& block != null
&& block != Character.UnicodeBlock.SPECIALS;
} }
} }

View File

@ -21,7 +21,6 @@ import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.jf.dexlib.Code.Analysis.RegisterType; import org.jf.dexlib.Code.Analysis.RegisterType;
import org.jf.dexlib.Code.Opcode;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
@ -40,10 +39,8 @@ public class DebugInjector {
private void inject() throws AndrolibException { private void inject() throws AndrolibException {
String definition = nextAndAppend(); String definition = nextAndAppend();
if ( if (definition.contains(" abstract ")
definition.contains(" abstract ") || || definition.contains(" native ")) {
definition.contains(" native ")
) {
nextAndAppend(); nextAndAppend();
return; return;
} }
@ -121,8 +118,7 @@ public class DebugInjector {
case Uninit: case Uninit:
case Conflicted: case Conflicted:
if (mInitializedRegisters.remove(localName)) { if (mInitializedRegisters.remove(localName)) {
mOut.append(".end local ").append(localName) mOut.append(".end local ").append(localName).append('\n');
.append('\n');
} }
continue; continue;
case Short: case Short:
@ -153,7 +149,8 @@ public class DebugInjector {
mInitializedRegisters.add(localName); mInitializedRegisters.add(localName);
mOut.append(".local ").append(localName).append(", ") mOut.append(".local ").append(localName).append(", ")
.append(localName).append(':').append(localType).append('\n'); .append(localName).append(':').append(localType)
.append('\n');
} }
return false; return false;
@ -161,13 +158,9 @@ public class DebugInjector {
private boolean processDirective(String line) { private boolean processDirective(String line) {
String line2 = line.substring(1); String line2 = line.substring(1);
if ( if (line2.startsWith("line ") || line2.equals("prologue")
line2.startsWith("line ") || || line2.startsWith("parameter") || line2.startsWith("local ")
line2.equals("prologue") || || line2.startsWith("end local ")) {
line2.startsWith("parameter") ||
line2.startsWith("local ") ||
line2.startsWith("end local ")
) {
return false; return false;
} }
@ -175,12 +168,9 @@ public class DebugInjector {
if (line2.equals("end method")) { if (line2.equals("end method")) {
return true; return true;
} }
if ( if (line2.startsWith("annotation ") || line2.equals("sparse-switch")
line2.startsWith("annotation ") || || line2.startsWith("packed-switch ")
line2.equals("sparse-switch") || || line2.startsWith("array-data ")) {
line2.startsWith("packed-switch ") ||
line2.startsWith("array-data ")
) {
while (true) { while (true) {
line2 = nextAndAppend(); line2 = nextAndAppend();
if (line2.startsWith(".end ")) { if (line2.startsWith(".end ")) {
@ -196,8 +186,8 @@ public class DebugInjector {
mOut.append(".prologue\n"); mOut.append(".prologue\n");
mFirstInstruction = false; mFirstInstruction = false;
} }
mOut.append(".line ").append(mIt.nextIndex()).append('\n') mOut.append(".line ").append(mIt.nextIndex()).append('\n').append(line)
.append(line).append('\n'); .append('\n');
return false; return false;
} }
@ -222,6 +212,6 @@ public class DebugInjector {
private boolean mFirstInstruction = true; private boolean mFirstInstruction = true;
private final Set<String> mInitializedRegisters = new HashSet<String>(); private final Set<String> mInitializedRegisters = new HashSet<String>();
private static final Pattern REGISTER_INFO_PATTERN = private static final Pattern REGISTER_INFO_PATTERN = Pattern
Pattern.compile("((?:p|v)\\d+)=\\(([^)]+)\\);"); .compile("((?:p|v)\\d+)=\\(([^)]+)\\);");
} }

View File

@ -40,10 +40,9 @@ public class DexFileBuilder {
public void addSmaliFile(InputStream smaliStream, String name) public void addSmaliFile(InputStream smaliStream, String name)
throws AndrolibException { throws AndrolibException {
try { try {
if (! SmaliMod.assembleSmaliFile( if (!SmaliMod.assembleSmaliFile(smaliStream, name, mDexFile, false,
smaliStream, name, mDexFile, false, false, false)) { false, false)) {
throw new AndrolibException( throw new AndrolibException("Could not smali file: " + name);
"Could not smali file: " + name);
} }
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);
@ -58,8 +57,8 @@ public class DexFileBuilder {
out.write(getAsByteArray()); out.write(getAsByteArray());
out.close(); out.close();
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException( throw new AndrolibException("Could not write dex to file: "
"Could not write dex to file: " + dexFile, ex); + dexFile, ex);
} }
} }

View File

@ -32,12 +32,12 @@ import org.apache.commons.io.IOUtils;
public class SmaliBuilder { public class SmaliBuilder {
public static void build(ExtFile smaliDir, File dexFile, public static void build(ExtFile smaliDir, File dexFile,
HashMap<String, Boolean> flags) HashMap<String, Boolean> flags) throws AndrolibException {
throws AndrolibException {
new SmaliBuilder(smaliDir, dexFile, flags).build(); new SmaliBuilder(smaliDir, dexFile, flags).build();
} }
private SmaliBuilder(ExtFile smaliDir, File dexFile, HashMap<String, Boolean> flags) { private SmaliBuilder(ExtFile smaliDir, File dexFile,
HashMap<String, Boolean> flags) {
mSmaliDir = smaliDir; mSmaliDir = smaliDir;
mDexFile = dexFile; mDexFile = dexFile;
mFlags = flags; mFlags = flags;
@ -87,8 +87,8 @@ public class SmaliBuilder {
out.append(".source \"").append(inFile.getName()).append("\"\n"); out.append(".source \"").append(inFile.getName()).append("\"\n");
while (it.hasNext()) { while (it.hasNext()) {
String line = it.next().trim(); String line = it.next().trim();
if (line.isEmpty() || line.charAt(0) == '#' || if (line.isEmpty() || line.charAt(0) == '#'
line.startsWith(".source")) { || line.startsWith(".source")) {
continue; continue;
} }
if (line.startsWith(".method ")) { if (line.startsWith(".method ")) {
@ -100,8 +100,8 @@ public class SmaliBuilder {
out.append(line).append('\n'); out.append(line).append('\n');
} }
} }
mDexBuilder.addSmaliFile( mDexBuilder.addSmaliFile(IOUtils.toInputStream(out.toString()),
IOUtils.toInputStream(out.toString()), fileName); fileName);
} }
private final ExtFile mSmaliDir; private final ExtFile mSmaliDir;
@ -110,7 +110,6 @@ public class SmaliBuilder {
private DexFileBuilder mDexBuilder; private DexFileBuilder mDexBuilder;
private final static Logger LOGGER = Logger.getLogger(SmaliBuilder.class
private final static Logger LOGGER = .getName());
Logger.getLogger(SmaliBuilder.class.getName());
} }

View File

@ -29,12 +29,13 @@ import org.jf.dexlib.DexFile;
*/ */
public class SmaliDecoder { public class SmaliDecoder {
public static void decode(File apkFile, File outDir, boolean debug, boolean bakdeb) public static void decode(File apkFile, File outDir, boolean debug,
throws AndrolibException { boolean bakdeb) throws AndrolibException {
new SmaliDecoder(apkFile, outDir, debug, bakdeb).decode(); new SmaliDecoder(apkFile, outDir, debug, bakdeb).decode();
} }
private SmaliDecoder(File apkFile, File outDir, boolean debug, boolean bakdeb) { private SmaliDecoder(File apkFile, File outDir, boolean debug,
boolean bakdeb) {
mApkFile = apkFile; mApkFile = apkFile;
mOutDir = outDir; mOutDir = outDir;
mDebug = debug; mDebug = debug;
@ -47,8 +48,8 @@ public class SmaliDecoder {
} }
try { try {
baksmali.disassembleDexFile(mApkFile.getAbsolutePath(), baksmali.disassembleDexFile(mApkFile.getAbsolutePath(),
new DexFile(mApkFile), false, mOutDir.getAbsolutePath(), null, new DexFile(mApkFile), false, mOutDir.getAbsolutePath(),
null, null, false, true, true, mBakDeb, false, false, null, null, null, false, true, true, mBakDeb, false, false,
mDebug ? main.DIFFPRE : 0, false, false, null, false); mDebug ? main.DIFFPRE : 0, false, false, null, false);
} catch (IOException ex) { } catch (IOException ex) {
throw new AndrolibException(ex); throw new AndrolibException(ex);

View File

@ -51,10 +51,10 @@ public class TypeName {
} }
public String getName(boolean excludePackage, boolean separateInner) { public String getName(boolean excludePackage, boolean separateInner) {
String name = String name = (package_ == null || excludePackage ? "" : package_ + '.')
(package_ == null || excludePackage ? "" : package_ + '.') + + type
type + + (innerType != null ? (separateInner ? '$' : '.') + innerType
(innerType != null ? (separateInner ? '$' : '.') + innerType : ""); : "");
for (int i = 0; i < array; i++) { for (int i = 0; i < array; i++) {
name += "[]"; name += "[]";
} }
@ -104,8 +104,7 @@ public class TypeName {
throws AndrolibException { throws AndrolibException {
Duo<TypeName, Integer> duo = fetchFromInternalName(internal); Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
if (duo.m2 != internal.length()) { if (duo.m2 != internal.length()) {
throw new AndrolibException( throw new AndrolibException("Invalid internal name: " + internal);
"Invalid internal name: " + internal);
} }
return duo.m1; return duo.m1;
} }
@ -129,8 +128,8 @@ public class TypeName {
boolean isArray = false; boolean isArray = false;
do { do {
if (internal.isEmpty()) { if (internal.isEmpty()) {
throw new AndrolibException( throw new AndrolibException("Invalid internal name: "
"Invalid internal name: " + origInternal); + origInternal);
} }
isArray = internal.charAt(0) == '['; isArray = internal.charAt(0) == '[';
if (isArray) { if (isArray) {
@ -174,8 +173,8 @@ public class TypeName {
case 'L': case 'L':
int pos = internal.indexOf(';'); int pos = internal.indexOf(';');
if (pos == -1) { if (pos == -1) {
throw new AndrolibException( throw new AndrolibException("Invalid internal name: "
"Invalid internal name: " + origInternal); + origInternal);
} }
length += pos; length += pos;
internal = internal.substring(1, pos); internal = internal.substring(1, pos);
@ -196,14 +195,13 @@ public class TypeName {
} }
break; break;
default: default:
throw new AndrolibException( throw new AndrolibException("Invalid internal name: "
"Invalid internal name: " + origInternal); + origInternal);
} }
return new Duo<TypeName, Integer>( return new Duo<TypeName, Integer>(new TypeName(package_, type,
new TypeName(package_, type, innerType, array), length); innerType, array), length);
} }
private Boolean mIsFileOwner; private Boolean mIsFileOwner;
} }

View File

@ -25,18 +25,17 @@ import java.io.InputStream;
/** /**
* Little-Endian version of DataInputStream. * Little-Endian version of DataInputStream.
* <p/> * <p/>
* Very similar to DataInputStream except it reads * Very similar to DataInputStream except it reads little-endian instead of
* little-endian instead of big-endian binary data. We can't extend * big-endian binary data. We can't extend DataInputStream directly since it has
* DataInputStream directly since it has only final methods, though * only final methods, though DataInputStream itself is not final. This forces
* DataInputStream itself is not final. This forces us implement * us implement LEDataInputStream with a DataInputStream object, and use wrapper
* LEDataInputStream with a DataInputStream object, and use wrapper methods. * methods.
* *
* @author Roedy Green, Canadian Mind Products * @author Roedy Green, Canadian Mind Products
* @version 1.8 2007-05-24 * @version 1.8 2007-05-24
* @since 1998 * @since 1998
*/ */
public final class LEDataInputStream implements DataInput public final class LEDataInputStream implements DataInput {
{
// ------------------------------ CONSTANTS ------------------------------ // ------------------------------ CONSTANTS ------------------------------
/** /**
@ -44,8 +43,7 @@ public final class LEDataInputStream implements DataInput
* *
* @noinspection UnusedDeclaration * @noinspection UnusedDeclaration
*/ */
private static final String EMBEDDED_COPYRIGHT = private static final String EMBEDDED_COPYRIGHT = "copyright (c) 1999-2010 Roedy Green, Canadian Mind Products, http://mindprod.com";
"copyright (c) 1999-2010 Roedy Green, Canadian Mind Products, http://mindprod.com";
// ------------------------------ FIELDS ------------------------------ // ------------------------------ FIELDS ------------------------------
@ -69,30 +67,34 @@ public final class LEDataInputStream implements DataInput
* @noinspection WeakerAccess * @noinspection WeakerAccess
*/ */
protected final byte[] work; protected final byte[] work;
// -------------------------- PUBLIC STATIC METHODS --------------------------
// -------------------------- PUBLIC STATIC METHODS
// --------------------------
/** /**
* Note. This is a STATIC method! * Note. This is a STATIC method!
* *
* @param in stream to read UTF chars from (endian irrelevant) * @param in
* stream to read UTF chars from (endian irrelevant)
* *
* @return string from stream * @return string from stream
* @throws IOException if read fails. * @throws IOException
* if read fails.
*/ */
public static String readUTF( DataInput in ) throws IOException public static String readUTF(DataInput in) throws IOException {
{
return DataInputStream.readUTF(in); return DataInputStream.readUTF(in);
} }
// -------------------------- PUBLIC INSTANCE METHODS -------------------------- // -------------------------- PUBLIC INSTANCE METHODS
// --------------------------
/** /**
* constructor. * constructor.
* *
* @param in binary inputstream of little-endian data. * @param in
* binary inputstream of little-endian data.
*/ */
public LEDataInputStream( InputStream in ) public LEDataInputStream(InputStream in) {
{
this.is = in; this.is = in;
this.dis = new DataInputStream(in); this.dis = new DataInputStream(in);
work = new byte[8]; work = new byte[8];
@ -101,25 +103,28 @@ public final class LEDataInputStream implements DataInput
/** /**
* close. * close.
* *
* @throws IOException if close fails. * @throws IOException
* if close fails.
*/ */
public final void close() throws IOException public final void close() throws IOException {
{
dis.close(); dis.close();
} }
/** /**
* Read bytes. Watch out, read may return fewer bytes than requested. * Read bytes. Watch out, read may return fewer bytes than requested.
* *
* @param ba where the bytes go. * @param ba
* @param off offset in buffer, not offset in file. * where the bytes go.
* @param len count of bytes to read. * @param off
* offset in buffer, not offset in file.
* @param len
* count of bytes to read.
* *
* @return how many bytes read. * @return how many bytes read.
* @throws IOException if read fails. * @throws IOException
* if read fails.
*/ */
public final int read( byte ba[], int off, int len ) throws IOException public final int read(byte ba[], int off, int len) throws IOException {
{
// For efficiency, we avoid one layer of wrapper // For efficiency, we avoid one layer of wrapper
return is.read(ba, off, len); return is.read(ba, off, len);
} }
@ -128,11 +133,12 @@ public final class LEDataInputStream implements DataInput
* read only a one-byte boolean. * read only a one-byte boolean.
* *
* @return true or false. * @return true or false.
* @throws IOException if read fails. * @throws IOException
* if read fails.
* @see java.io.DataInput#readBoolean() * @see java.io.DataInput#readBoolean()
*/ */
public final boolean readBoolean() throws IOException @Override
{ public final boolean readBoolean() throws IOException {
return dis.readBoolean(); return dis.readBoolean();
} }
@ -140,11 +146,12 @@ public final class LEDataInputStream implements DataInput
* read byte. * read byte.
* *
* @return the byte read. * @return the byte read.
* @throws IOException if read fails. * @throws IOException
* if read fails.
* @see java.io.DataInput#readByte() * @see java.io.DataInput#readByte()
*/ */
public final byte readByte() throws IOException @Override
{ public final byte readByte() throws IOException {
return dis.readByte(); return dis.readByte();
} }
@ -152,10 +159,11 @@ public final class LEDataInputStream implements DataInput
* Read on char. like DataInputStream.readChar except little endian. * Read on char. like DataInputStream.readChar except little endian.
* *
* @return little endian 16-bit unicode char from the stream. * @return little endian 16-bit unicode char from the stream.
* @throws IOException if read fails. * @throws IOException
* if read fails.
*/ */
public final char readChar() throws IOException @Override
{ public final char readChar() throws IOException {
dis.readFully(work, 0, 2); dis.readFully(work, 0, 2);
return (char) ((work[1] & 0xff) << 8 | (work[0] & 0xff)); return (char) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
} }
@ -166,8 +174,8 @@ public final class LEDataInputStream implements DataInput
* @return little endian IEEE double from the datastream. * @return little endian IEEE double from the datastream.
* @throws IOException * @throws IOException
*/ */
public final double readDouble() throws IOException @Override
{ public final double readDouble() throws IOException {
return Double.longBitsToDouble(readLong()); return Double.longBitsToDouble(readLong());
} }
@ -175,10 +183,11 @@ public final class LEDataInputStream implements DataInput
* Read one float. Like DataInputStream.readFloat except little endian. * Read one float. Like DataInputStream.readFloat except little endian.
* *
* @return little endian IEEE float from the datastream. * @return little endian IEEE float from the datastream.
* @throws IOException if read fails. * @throws IOException
* if read fails.
*/ */
public final float readFloat() throws IOException @Override
{ public final float readFloat() throws IOException {
return Float.intBitsToFloat(readInt()); return Float.intBitsToFloat(readInt());
} }
@ -187,21 +196,20 @@ public final class LEDataInputStream implements DataInput
* *
* @see java.io.DataInput#readFully(byte[]) * @see java.io.DataInput#readFully(byte[])
*/ */
public final void readFully( byte ba[] ) throws IOException @Override
{ public final void readFully(byte ba[]) throws IOException {
dis.readFully(ba, 0, ba.length); dis.readFully(ba, 0, ba.length);
} }
/** /**
* Read bytes until the count is satisfied. * Read bytes until the count is satisfied.
* *
* @throws IOException if read fails. * @throws IOException
* if read fails.
* @see java.io.DataInput#readFully(byte[],int,int) * @see java.io.DataInput#readFully(byte[],int,int)
*/ */
public final void readFully( byte ba[], @Override
int off, public final void readFully(byte ba[], int off, int len) throws IOException {
int len ) throws IOException
{
dis.readFully(ba, off, len); dis.readFully(ba, off, len);
} }
@ -209,28 +217,29 @@ public final class LEDataInputStream implements DataInput
* Read an int, 32-bits. Like DataInputStream.readInt except little endian. * Read an int, 32-bits. Like DataInputStream.readInt except little endian.
* *
* @return little-endian binary int from the datastream * @return little-endian binary int from the datastream
* @throws IOException if read fails. * @throws IOException
* if read fails.
*/ */
public final int readInt() throws IOException @Override
{ public final int readInt() throws IOException {
dis.readFully(work, 0, 4); dis.readFully(work, 0, 4);
return ( work[ 3 ] ) << 24 return (work[3]) << 24 | (work[2] & 0xff) << 16 | (work[1] & 0xff) << 8
| ( work[ 2 ] & 0xff ) << 16
| ( work[ 1 ] & 0xff ) << 8
| (work[0] & 0xff); | (work[0] & 0xff);
} }
/** /**
* Read a line. * Read a line.
* *
* @return a rough approximation of the 8-bit stream as a 16-bit unicode string * @return a rough approximation of the 8-bit stream as a 16-bit unicode
* string
* @throws IOException * @throws IOException
* @noinspection deprecation * @noinspection deprecation
* @deprecated This method does not properly convert bytes to characters. Use a Reader instead with a little-endian * @deprecated This method does not properly convert bytes to characters.
* encoding. * Use a Reader instead with a little-endian encoding.
*/ */
public final String readLine() throws IOException @Deprecated
{ @Override
public final String readLine() throws IOException {
return dis.readLine(); return dis.readLine();
} }
@ -240,29 +249,26 @@ public final class LEDataInputStream implements DataInput
* @return little-endian binary long from the datastream. * @return little-endian binary long from the datastream.
* @throws IOException * @throws IOException
*/ */
public final long readLong() throws IOException @Override
{ public final long readLong() throws IOException {
dis.readFully(work, 0, 8); dis.readFully(work, 0, 8);
return ( long ) ( work[ 7 ] ) << 56 return (long) (work[7]) << 56 |
|
/* long cast needed or shift done modulo 32 */ /* long cast needed or shift done modulo 32 */
( long ) ( work[ 6 ] & 0xff ) << 48 (long) (work[6] & 0xff) << 48 | (long) (work[5] & 0xff) << 40
| ( long ) ( work[ 5 ] & 0xff ) << 40 | (long) (work[4] & 0xff) << 32 | (long) (work[3] & 0xff) << 24
| ( long ) ( work[ 4 ] & 0xff ) << 32 | (long) (work[2] & 0xff) << 16 | (long) (work[1] & 0xff) << 8
| ( long ) ( work[ 3 ] & 0xff ) << 24 | work[0] & 0xff;
| ( long ) ( work[ 2 ] & 0xff ) << 16
| ( long ) ( work[ 1 ] & 0xff ) << 8
| ( long ) ( work[ 0 ] & 0xff );
} }
/** /**
* Read short, 16-bits. Like DataInputStream.readShort except little endian. * Read short, 16-bits. Like DataInputStream.readShort except little endian.
* *
* @return little endian binary short from stream. * @return little endian binary short from stream.
* @throws IOException if read fails. * @throws IOException
* if read fails.
*/ */
public final short readShort() throws IOException @Override
{ public final short readShort() throws IOException {
dis.readFully(work, 0, 2); dis.readFully(work, 0, 2);
return (short) ((work[1] & 0xff) << 8 | (work[0] & 0xff)); return (short) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
} }
@ -272,48 +278,53 @@ public final class LEDataInputStream implements DataInput
* *
* @return String read. * @return String read.
*/ */
public final String readUTF() throws IOException @Override
{ public final String readUTF() throws IOException {
return dis.readUTF(); return dis.readUTF();
} }
/** /**
* Read an unsigned byte. Note: returns an int, even though says Byte (non-Javadoc) * Read an unsigned byte. Note: returns an int, even though says Byte
* (non-Javadoc)
* *
* @throws IOException if read fails. * @throws IOException
* if read fails.
* @see java.io.DataInput#readUnsignedByte() * @see java.io.DataInput#readUnsignedByte()
*/ */
public final int readUnsignedByte() throws IOException @Override
{ public final int readUnsignedByte() throws IOException {
return dis.readUnsignedByte(); return dis.readUnsignedByte();
} }
/** /**
* Read an unsigned short, 16 bits. Like DataInputStream.readUnsignedShort except little endian. Note, returns int * Read an unsigned short, 16 bits. Like DataInputStream.readUnsignedShort
* even though it reads a short. * except little endian. Note, returns int even though it reads a short.
* *
* @return little-endian int from the stream. * @return little-endian int from the stream.
* @throws IOException if read fails. * @throws IOException
* if read fails.
*/ */
public final int readUnsignedShort() throws IOException @Override
{ public final int readUnsignedShort() throws IOException {
dis.readFully(work, 0, 2); dis.readFully(work, 0, 2);
return ((work[1] & 0xff) << 8 | (work[0] & 0xff)); return ((work[1] & 0xff) << 8 | (work[0] & 0xff));
} }
/** /**
* Skip over bytes in the stream. See the general contract of the <code>skipBytes</code> method of * Skip over bytes in the stream. See the general contract of the
* <code>DataInput</code>. * <code>skipBytes</code> method of <code>DataInput</code>.
* <p/> * <p/>
* Bytes for this operation are read from the contained input stream. * Bytes for this operation are read from the contained input stream.
* *
* @param n the number of bytes to be skipped. * @param n
* the number of bytes to be skipped.
* *
* @return the actual number of bytes skipped. * @return the actual number of bytes skipped.
* @throws IOException if an I/O error occurs. * @throws IOException
* if an I/O error occurs.
*/ */
public final int skipBytes( int n ) throws IOException @Override
{ public final int skipBytes(int n) throws IOException {
return dis.skipBytes(n); return dis.skipBytes(n);
} }
} }

View File

@ -8,16 +8,20 @@ import java.io.Writer;
import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlSerializer;
/** /**
* Implementation of XmlSerializer interface from XmlPull V1 API. * Implementation of XmlSerializer interface from XmlPull V1 API. This
* This implementation is optimzied for performance and low memory footprint. * implementation is optimzied for performance and low memory footprint.
* *
* <p>Implemented features:<ul> * <p>
* <li> FEATURE_NAMES_INTERNED - when enabled all returned names * Implemented features:
* (namespaces, prefixes) will be interned and it is required that * <ul>
* all names passed as arguments MUST be interned * <li>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
* <li>FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE * <li>FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE
* </ul> * </ul>
* <p>Implemented properties:<ul> * <p>
* Implemented properties:
* <ul>
* <li>PROPERTY_SERIALIZER_INDENTATION * <li>PROPERTY_SERIALIZER_INDENTATION
* <li>PROPERTY_SERIALIZER_LINE_SEPARATOR * <li>PROPERTY_SERIALIZER_LINE_SEPARATOR
* </ul> * </ul>
@ -29,16 +33,11 @@ public class MXSerializer implements XmlSerializer {
private static final boolean TRACE_SIZING = false; private static final boolean TRACE_SIZING = false;
private static final boolean TRACE_ESCAPING = false; private static final boolean TRACE_ESCAPING = false;
protected final String FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE = protected final String FEATURE_SERIALIZER_ATTVALUE_USE_APOSTROPHE = "http://xmlpull.org/v1/doc/features.html#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 FEATURE_NAMES_INTERNED = protected final String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
"http://xmlpull.org/v1/doc/features.html#names-interned"; protected final String PROPERTY_SERIALIZER_LINE_SEPARATOR = "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
protected final String PROPERTY_SERIALIZER_INDENTATION = protected final static String PROPERTY_LOCATION = "http://xmlpull.org/v1/doc/properties.html#location";
"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 // properties/features
protected boolean namesInterned; protected boolean namesInterned;
@ -76,16 +75,15 @@ public class MXSerializer implements XmlSerializer {
protected boolean seenBracketBracket; protected boolean seenBracketBracket;
// buffer output if neede to write escaped String see text(String) // buffer output if neede to write escaped String see text(String)
private static final int BUF_LEN = Runtime.getRuntime().freeMemory() > 1000000L ? 8*1024 : 256; private static final int BUF_LEN = Runtime.getRuntime().freeMemory() > 1000000L ? 8 * 1024
: 256;
protected char buf[] = new char[BUF_LEN]; protected char buf[] = new char[BUF_LEN];
protected static final String precomputedPrefixes[]; protected static final String precomputedPrefixes[];
static { static {
precomputedPrefixes = new String[32]; // arbitrary number ... precomputedPrefixes = new String[32]; // arbitrary number ...
for (int i = 0; i < precomputedPrefixes.length; i++) for (int i = 0; i < precomputedPrefixes.length; i++) {
{
precomputedPrefixes[i] = ("n" + i).intern(); precomputedPrefixes[i] = ("n" + i).intern();
} }
} }
@ -107,20 +105,19 @@ public class MXSerializer implements XmlSerializer {
depth = 0; depth = 0;
// nullify references on all levels to allow it to be GCed // nullify references on all levels to allow it to be GCed
for (int i = 0; i < elNamespaceCount.length; i++) for (int i = 0; i < elNamespaceCount.length; i++) {
{
elName[i] = null; elName[i] = null;
elPrefix[i] = null; elPrefix[i] = null;
elNamespace[i] = null; elNamespace[i] = null;
elNamespaceCount[i] = 2; elNamespaceCount[i] = 2;
} }
namespaceEnd = 0; namespaceEnd = 0;
// NOTE: no need to intern() as all literal strings and string-valued
//NOTE: no need to intern() as all literal strings and string-valued constant expressions // constant expressions
//are interned. String literals are defined in 3.10.5 of the Java Language Specification // are interned. String literals are defined in 3.10.5 of the Java
// Language Specification
// just checking ... // just checking ...
// assert "xmlns" == "xmlns".intern(); // assert "xmlns" == "xmlns".intern();
// assert XMLNS_URI == XMLNS_URI.intern(); // assert XMLNS_URI == XMLNS_URI.intern();
@ -146,29 +143,32 @@ public class MXSerializer implements XmlSerializer {
seenBracketBracket = false; seenBracketBracket = false;
} }
protected void ensureElementsCapacity() { protected void ensureElementsCapacity() {
final int elStackSize = elName.length; final int elStackSize = elName.length;
// assert (depth + 1) >= elName.length; // assert (depth + 1) >= elName.length;
// we add at least one extra slot ... // we add at least one extra slot ...
final int newSize = (depth >= 7 ? 2 * depth : 8) + 2; // = lucky 7 + 1 //25 final int newSize = (depth >= 7 ? 2 * depth : 8) + 2; // = lucky 7 + 1
// //25
if (TRACE_SIZING) { if (TRACE_SIZING) {
System.err.println( System.err.println(getClass().getName() + " elStackSize "
getClass().getName()+" elStackSize "+elStackSize+" ==> "+newSize); + elStackSize + " ==> " + newSize);
} }
final boolean needsCopying = elStackSize > 0; final boolean needsCopying = elStackSize > 0;
String[] arr = null; String[] arr = null;
// reuse arr local variable slot // reuse arr local variable slot
arr = new String[newSize]; arr = new String[newSize];
if(needsCopying) System.arraycopy(elName, 0, arr, 0, elStackSize); if (needsCopying)
System.arraycopy(elName, 0, arr, 0, elStackSize);
elName = arr; elName = arr;
arr = new String[newSize]; arr = new String[newSize];
if(needsCopying) System.arraycopy(elPrefix, 0, arr, 0, elStackSize); if (needsCopying)
System.arraycopy(elPrefix, 0, arr, 0, elStackSize);
elPrefix = arr; elPrefix = arr;
arr = new String[newSize]; arr = new String[newSize];
if(needsCopying) System.arraycopy(elNamespace, 0, arr, 0, elStackSize); if (needsCopying)
System.arraycopy(elNamespace, 0, arr, 0, elStackSize);
elNamespace = arr; elNamespace = arr;
final int[] iarr = new int[newSize]; final int[] iarr = new int[newSize];
@ -182,23 +182,23 @@ public class MXSerializer implements XmlSerializer {
} }
protected void ensureNamespacesCapacity() { // int size) { protected void ensureNamespacesCapacity() { // int size) {
//int namespaceSize = namespacePrefix != null ? namespacePrefix.length : 0; // int namespaceSize = namespacePrefix != null ? namespacePrefix.length
// : 0;
// assert (namespaceEnd >= namespacePrefix.length); // assert (namespaceEnd >= namespacePrefix.length);
// if(size >= namespaceSize) { // if(size >= namespaceSize) {
// int newSize = size > 7 ? 2 * size : 8; // = lucky 7 + 1 //25 // int newSize = size > 7 ? 2 * size : 8; // = lucky 7 + 1 //25
final int newSize = namespaceEnd > 7 ? 2 * namespaceEnd : 8; final int newSize = namespaceEnd > 7 ? 2 * namespaceEnd : 8;
if (TRACE_SIZING) { if (TRACE_SIZING) {
System.err.println( System.err.println(getClass().getName() + " namespaceSize "
getClass().getName()+" namespaceSize "+namespacePrefix.length+" ==> "+newSize); + namespacePrefix.length + " ==> " + newSize);
} }
final String[] newNamespacePrefix = new String[newSize]; final String[] newNamespacePrefix = new String[newSize];
final String[] newNamespaceUri = new String[newSize]; final String[] newNamespaceUri = new String[newSize];
if (namespacePrefix != null) { if (namespacePrefix != null) {
System.arraycopy( System.arraycopy(namespacePrefix, 0, newNamespacePrefix, 0,
namespacePrefix, 0, newNamespacePrefix, 0, namespaceEnd); namespaceEnd);
System.arraycopy( System.arraycopy(namespaceUri, 0, newNamespaceUri, 0, namespaceEnd);
namespaceUri, 0, newNamespaceUri, 0, namespaceEnd);
} }
namespacePrefix = newNamespacePrefix; namespacePrefix = newNamespacePrefix;
namespaceUri = newNamespaceUri; namespaceUri = newNamespaceUri;
@ -217,10 +217,9 @@ public class MXSerializer implements XmlSerializer {
// } // }
} }
@Override
public void setFeature(String name, public void setFeature(String name, boolean state)
boolean state) throws IllegalArgumentException, IllegalStateException throws IllegalArgumentException, IllegalStateException {
{
if (name == null) { if (name == null) {
throw new IllegalArgumentException("feature name can not be null"); throw new IllegalArgumentException("feature name can not be null");
} }
@ -233,8 +232,8 @@ public class MXSerializer implements XmlSerializer {
} }
} }
public boolean getFeature(String name) throws IllegalArgumentException @Override
{ public boolean getFeature(String name) throws IllegalArgumentException {
if (name == null) { if (name == null) {
throw new IllegalArgumentException("feature name can not be null"); throw new IllegalArgumentException("feature name can not be null");
} }
@ -256,12 +255,15 @@ public class MXSerializer implements XmlSerializer {
protected boolean writeIndentation; // is indentation used? protected boolean writeIndentation; // is indentation used?
/** /**
* For maximum efficiency when writing indents the required output is pre-computed * For maximum efficiency when writing indents the required output is
* This is internal function that recomputes buffer after user requested chnages. * pre-computed This is internal function that recomputes buffer after user
* requested chnages.
*/ */
protected void rebuildIndentationBuf() { protected void rebuildIndentationBuf() {
if(doIndent == false) return; if (doIndent == false)
final int maxIndent = 65; //hardcoded maximum indentation size in characters return;
final int maxIndent = 65; // hardcoded maximum indentation size in
// characters
int bufSize = 0; int bufSize = 0;
offsetNewLine = 0; offsetNewLine = 0;
if (writeLineSepartor) { if (writeLineSepartor) {
@ -279,16 +281,13 @@ public class MXSerializer implements XmlSerializer {
} }
int bufPos = 0; int bufPos = 0;
if (writeLineSepartor) { if (writeLineSepartor) {
for (int i = 0; i < lineSeparator.length(); i++) for (int i = 0; i < lineSeparator.length(); i++) {
{
indentationBuf[bufPos++] = lineSeparator.charAt(i); indentationBuf[bufPos++] = lineSeparator.charAt(i);
} }
} }
if (writeIndentation) { if (writeIndentation) {
for (int i = 0; i < maxIndentLevel; i++) for (int i = 0; i < maxIndentLevel; i++) {
{ for (int j = 0; j < indentationString.length(); j++) {
for (int j = 0; j < indentationString.length(); j++)
{
indentationBuf[bufPos++] = indentationString.charAt(j); indentationBuf[bufPos++] = indentationString.charAt(j);
} }
} }
@ -299,12 +298,13 @@ public class MXSerializer implements XmlSerializer {
protected void writeIndent() throws IOException { protected void writeIndent() throws IOException {
final int start = writeLineSepartor ? 0 : offsetNewLine; final int start = writeLineSepartor ? 0 : offsetNewLine;
final int level = (depth > maxIndentLevel) ? maxIndentLevel : depth; final int level = (depth > maxIndentLevel) ? maxIndentLevel : depth;
out.write( indentationBuf, start, ( (level - 1) * indentationJump) + offsetNewLine); out.write(indentationBuf, start, ((level - 1) * indentationJump)
+ offsetNewLine);
} }
public void setProperty(String name, @Override
Object value) throws IllegalArgumentException, IllegalStateException public void setProperty(String name, Object value)
{ throws IllegalArgumentException, IllegalStateException {
if (name == null) { if (name == null) {
throw new IllegalArgumentException("property name can not be null"); throw new IllegalArgumentException("property name can not be null");
} }
@ -318,17 +318,19 @@ public class MXSerializer implements XmlSerializer {
throw new IllegalStateException("unsupported property " + name); throw new IllegalStateException("unsupported property " + name);
} }
writeLineSepartor = lineSeparator != null && lineSeparator.length() > 0; writeLineSepartor = lineSeparator != null && lineSeparator.length() > 0;
writeIndentation = indentationString != null && indentationString.length() > 0; writeIndentation = indentationString != null
&& indentationString.length() > 0;
// optimize - do not write when nothing to write ... // optimize - do not write when nothing to write ...
doIndent = indentationString != null && (writeLineSepartor || writeIndentation); doIndent = indentationString != null
&& (writeLineSepartor || writeIndentation);
// NOTE: when indentationString == null there is no indentation // NOTE: when indentationString == null there is no indentation
// (even though writeLineSeparator may be true ...) // (even though writeLineSeparator may be true ...)
rebuildIndentationBuf(); rebuildIndentationBuf();
seenTag = false; // for consistency seenTag = false; // for consistency
} }
public Object getProperty(String name) throws IllegalArgumentException @Override
{ public Object getProperty(String name) throws IllegalArgumentException {
if (name == null) { if (name == null) {
throw new IllegalArgumentException("property name can not be null"); throw new IllegalArgumentException("property name can not be null");
} }
@ -347,21 +349,22 @@ public class MXSerializer implements XmlSerializer {
return location != null ? " @" + location : ""; return location != null ? " @" + location : "";
} }
// this is special method that can be accessed directly to retrieve Writer serializer is using // this is special method that can be accessed directly to retrieve Writer
public Writer getWriter() // serializer is using
{ public Writer getWriter() {
return out; return out;
} }
public void setOutput(Writer writer) @Override
{ public void setOutput(Writer writer) {
reset(); reset();
out = writer; out = writer;
} }
public void setOutput(OutputStream os, String encoding) throws IOException @Override
{ public void setOutput(OutputStream os, String encoding) throws IOException {
if(os == null) throw new IllegalArgumentException("output stream can not be null"); if (os == null)
throw new IllegalArgumentException("output stream can not be null");
reset(); reset();
if (encoding != null) { if (encoding != null) {
out = new OutputStreamWriter(os, encoding); out = new OutputStreamWriter(os, encoding);
@ -370,8 +373,9 @@ public class MXSerializer implements XmlSerializer {
} }
} }
public void startDocument (String encoding, Boolean standalone) throws IOException @Override
{ public void startDocument(String encoding, Boolean standalone)
throws IOException {
char apos = attributeUseApostrophe ? '\'' : '"'; char apos = attributeUseApostrophe ? '\'' : '"';
if (attributeUseApostrophe) { if (attributeUseApostrophe) {
out.write("<?xml version='1.0'"); out.write("<?xml version='1.0'");
@ -403,8 +407,8 @@ public class MXSerializer implements XmlSerializer {
out.write("?>"); out.write("?>");
} }
public void endDocument() throws IOException @Override
{ public void endDocument() throws IOException {
// close all unclosed tag; // close all unclosed tag;
while (depth > 0) { while (depth > 0) {
endTag(elNamespace[depth], elName[depth]); endTag(elNamespace[depth], elName[depth]);
@ -415,9 +419,10 @@ public class MXSerializer implements XmlSerializer {
out.flush(); out.flush();
} }
public void setPrefix(String prefix, String namespace) throws IOException @Override
{ public void setPrefix(String prefix, String namespace) throws IOException {
if(startTagIncomplete) closeStartTag(); if (startTagIncomplete)
closeStartTag();
// assert prefix != null; // assert prefix != null;
// assert namespace != null; // assert namespace != null;
if (prefix == null) { if (prefix == null) {
@ -428,14 +433,15 @@ public class MXSerializer implements XmlSerializer {
} else if (checkNamesInterned) { } else if (checkNamesInterned) {
checkInterning(prefix); checkInterning(prefix);
} else if (prefix == null) { } else if (prefix == null) {
throw new IllegalArgumentException("prefix must be not null"+getLocation()); throw new IllegalArgumentException("prefix must be not null"
+ getLocation());
} }
// check that prefix is not duplicated ... // check that prefix is not duplicated ...
for (int i = elNamespaceCount[ depth ]; i < namespaceEnd; i++) for (int i = elNamespaceCount[depth]; i < namespaceEnd; i++) {
{
if (prefix == namespacePrefix[i]) { if (prefix == namespacePrefix[i]) {
throw new IllegalStateException("duplicated prefix "+printable(prefix)+getLocation()); throw new IllegalStateException("duplicated prefix "
+ printable(prefix) + getLocation());
} }
} }
@ -444,7 +450,8 @@ public class MXSerializer implements XmlSerializer {
} else if (checkNamesInterned) { } else if (checkNamesInterned) {
checkInterning(namespace); checkInterning(namespace);
} else if (namespace == null) { } else if (namespace == null) {
throw new IllegalArgumentException("namespace must be not null"+getLocation()); throw new IllegalArgumentException("namespace must be not null"
+ getLocation());
} }
if (namespaceEnd >= namespacePrefix.length) { if (namespaceEnd >= namespacePrefix.length) {
@ -460,38 +467,41 @@ public class MXSerializer implements XmlSerializer {
return getPrefix(namespace, true); return getPrefix(namespace, true);
} }
public String getPrefix(String namespace, boolean generatePrefix) @Override
{ public String getPrefix(String namespace, boolean generatePrefix) {
return getPrefix(namespace, generatePrefix, false); return getPrefix(namespace, generatePrefix, false);
} }
protected String getPrefix(String namespace, boolean generatePrefix, boolean nonEmpty) protected String getPrefix(String namespace, boolean generatePrefix,
{ boolean nonEmpty) {
// assert namespace != null; // assert namespace != null;
if (!namesInterned) { if (!namesInterned) {
// when String is interned we can do much faster namespace stack lookups ... // when String is interned we can do much faster namespace stack
// lookups ...
namespace = namespace.intern(); namespace = namespace.intern();
} else if (checkNamesInterned) { } else if (checkNamesInterned) {
checkInterning(namespace); checkInterning(namespace);
// assert namespace != namespace.intern(); // assert namespace != namespace.intern();
} }
if (namespace == null) { if (namespace == null) {
throw new IllegalArgumentException("namespace must be not null"+getLocation()); throw new IllegalArgumentException("namespace must be not null"
+ getLocation());
} else if (namespace.length() == 0) { } else if (namespace.length() == 0) {
throw new IllegalArgumentException("default namespace cannot have prefix"+getLocation()); throw new IllegalArgumentException(
"default namespace cannot have prefix" + getLocation());
} }
// first check if namespace is already in scope // first check if namespace is already in scope
for (int i = namespaceEnd - 1; i >= 0 ; --i) for (int i = namespaceEnd - 1; i >= 0; --i) {
{
if (namespace == namespaceUri[i]) { if (namespace == namespaceUri[i]) {
final String prefix = namespacePrefix[i]; final String prefix = namespacePrefix[i];
if(nonEmpty && prefix.length() == 0) continue; if (nonEmpty && prefix.length() == 0)
continue;
// now check that prefix is still in scope // now check that prefix is still in scope
for (int p = namespaceEnd - 1; p > i ; --p) for (int p = namespaceEnd - 1; p > i; --p) {
{
if (prefix == namespacePrefix[p]) if (prefix == namespacePrefix[p])
continue; // too bad - prefix is redeclared with different namespace continue; // too bad - prefix is redeclared with
// different namespace
} }
return prefix; return prefix;
} }
@ -509,13 +519,14 @@ public class MXSerializer implements XmlSerializer {
while (true) { while (true) {
++autoDeclaredPrefixes; ++autoDeclaredPrefixes;
// fast lookup uses table that was pre-initialized in static{} .... // fast lookup uses table that was pre-initialized in static{} ....
final String prefix = autoDeclaredPrefixes < precomputedPrefixes.length final String prefix = autoDeclaredPrefixes < precomputedPrefixes.length ? precomputedPrefixes[autoDeclaredPrefixes]
? precomputedPrefixes[autoDeclaredPrefixes] : ("n"+autoDeclaredPrefixes).intern(); : ("n" + autoDeclaredPrefixes).intern();
// make sure this prefix is not declared in any scope (avoid hiding in-scope prefixes)! // make sure this prefix is not declared in any scope (avoid hiding
for (int i = namespaceEnd - 1; i >= 0 ; --i) // in-scope prefixes)!
{ for (int i = namespaceEnd - 1; i >= 0; --i) {
if (prefix == namespacePrefix[i]) { if (prefix == namespacePrefix[i]) {
continue; // prefix is already declared - generate new and try again continue; // prefix is already declared - generate new and
// try again
} }
} }
// declare prefix // declare prefix
@ -531,23 +542,24 @@ public class MXSerializer implements XmlSerializer {
} }
} }
public int getDepth() @Override
{ public int getDepth() {
return depth; return depth;
} }
public String getNamespace () @Override
{ public String getNamespace() {
return elNamespace[depth]; return elNamespace[depth];
} }
public String getName() @Override
{ public String getName() {
return elName[depth]; return elName[depth];
} }
public XmlSerializer startTag (String namespace, String name) throws IOException @Override
{ public XmlSerializer startTag(String namespace, String name)
throws IOException {
if (startTagIncomplete) { if (startTagIncomplete) {
closeStartTag(); closeStartTag();
} }
@ -564,32 +576,40 @@ public class MXSerializer implements XmlSerializer {
} }
// //assert namespace != null; // //assert namespace != null;
if(checkNamesInterned && namesInterned) checkInterning(namespace); if (checkNamesInterned && namesInterned)
elNamespace[ depth ] = (namesInterned || namespace == null) ? namespace : namespace.intern(); checkInterning(namespace);
elNamespace[depth] = (namesInterned || namespace == null) ? namespace
: namespace.intern();
// assert name != null; // assert name != null;
// elName[ depth ] = name; // elName[ depth ] = name;
if(checkNamesInterned && namesInterned) checkInterning(name); if (checkNamesInterned && namesInterned)
checkInterning(name);
elName[depth] = (namesInterned || name == null) ? name : name.intern(); elName[depth] = (namesInterned || name == null) ? name : name.intern();
if (out == null) { if (out == null) {
throw new IllegalStateException("setOutput() must called set before serialization can start"); throw new IllegalStateException(
"setOutput() must called set before serialization can start");
} }
out.write('<'); out.write('<');
if (namespace != null) { if (namespace != null) {
if (namespace.length() > 0) { if (namespace.length() > 0) {
// ALEK: in future make this algo a feature on serializer // ALEK: in future make this algo a feature on serializer
String prefix = null; String prefix = null;
if(depth > 0 && (namespaceEnd - elNamespaceCount[depth-1]) == 1) { if (depth > 0
// if only one prefix was declared un-declare it if the prefix is already declared on parent el with the same URI && (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]; String uri = namespaceUri[namespaceEnd - 1];
if (uri == namespace || uri.equals(namespace)) { if (uri == namespace || uri.equals(namespace)) {
String elPfx = namespacePrefix[namespaceEnd - 1]; String elPfx = namespacePrefix[namespaceEnd - 1];
// 2 == to skip predefined namesapces (xml and xmlns ...) // 2 == to skip predefined namesapces (xml and xmlns
// ...)
for (int pos = elNamespaceCount[depth - 1] - 1; pos >= 2; --pos) { for (int pos = elNamespaceCount[depth - 1] - 1; pos >= 2; --pos) {
String pf = namespacePrefix[pos]; String pf = namespacePrefix[pos];
if (pf == elPfx || pf.equals(elPfx)) { if (pf == elPfx || pf.equals(elPfx)) {
String n = namespaceUri[pos]; String n = namespaceUri[pos];
if (n == uri || n.equals(uri)) { if (n == uri || n.equals(uri)) {
--namespaceEnd; //un-declare namespace: this is kludge! --namespaceEnd; // un-declare namespace:
// this is kludge!
prefix = elPfx; prefix = elPfx;
} }
break; break;
@ -619,8 +639,9 @@ public class MXSerializer implements XmlSerializer {
setPrefix("", ""); setPrefix("", "");
} else if (uri.length() > 0) { } else if (uri.length() > 0) {
throw new IllegalStateException( throw new IllegalStateException(
"start tag can not be written in empty default namespace "+ "start tag can not be written in empty default namespace "
"as default namespace is currently bound to '"+uri+"'"+getLocation()); + "as default namespace is currently bound to '"
+ uri + "'" + getLocation());
} }
break; break;
} }
@ -634,11 +655,13 @@ public class MXSerializer implements XmlSerializer {
return this; return this;
} }
public XmlSerializer attribute (String namespace, String name, @Override
String value) throws IOException public XmlSerializer attribute(String namespace, String name, String value)
{ throws IOException {
if (!startTagIncomplete) { if (!startTagIncomplete) {
throw new IllegalArgumentException("startTag() must be called before attribute()"+getLocation()); throw new IllegalArgumentException(
"startTag() must be called before attribute()"
+ getLocation());
} }
// assert setPrefixCalled == false; // assert setPrefixCalled == false;
out.write(' '); out.write(' ');
@ -677,7 +700,9 @@ public class MXSerializer implements XmlSerializer {
protected void closeStartTag() throws IOException { protected void closeStartTag() throws IOException {
if (finished) { if (finished) {
throw new IllegalArgumentException("trying to write past already finished output"+getLocation()); throw new IllegalArgumentException(
"trying to write past already finished output"
+ getLocation());
} }
if (seenBracket) { if (seenBracket) {
seenBracket = seenBracketBracket = false; seenBracket = seenBracketBracket = false;
@ -685,10 +710,13 @@ public class MXSerializer implements XmlSerializer {
if (startTagIncomplete || setPrefixCalled) { if (startTagIncomplete || setPrefixCalled) {
if (setPrefixCalled) { if (setPrefixCalled) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"startTag() must be called immediately after setPrefix()"+getLocation()); "startTag() must be called immediately after setPrefix()"
+ getLocation());
} }
if (!startTagIncomplete) { if (!startTagIncomplete) {
throw new IllegalArgumentException("trying to close start tag that is not opened"+getLocation()); throw new IllegalArgumentException(
"trying to close start tag that is not opened"
+ getLocation());
} }
// write all namespace delcarations! // write all namespace delcarations!
@ -699,11 +727,9 @@ public class MXSerializer implements XmlSerializer {
} }
} }
protected void writeNamespaceDeclarations() throws IOException protected void writeNamespaceDeclarations() throws IOException {
{
// int start = elNamespaceCount[ depth - 1 ]; // int start = elNamespaceCount[ depth - 1 ];
for (int i = elNamespaceCount[ depth - 1 ]; i < namespaceEnd; i++) for (int i = elNamespaceCount[depth - 1]; i < namespaceEnd; i++) {
{
if (doIndent && namespaceUri[i].length() > 40) { if (doIndent && namespaceUri[i].length() > 40) {
writeIndent(); writeIndent();
out.write(" "); out.write(" ");
@ -724,8 +750,9 @@ public class MXSerializer implements XmlSerializer {
} }
} }
public XmlSerializer endTag(String namespace, String name) throws IOException @Override
{ public XmlSerializer endTag(String namespace, String name)
throws IOException {
// check that level is valid // check that level is valid
// //assert namespace != null; // //assert namespace != null;
// if(namespace != null) { // if(namespace != null) {
@ -740,32 +767,35 @@ public class MXSerializer implements XmlSerializer {
} }
} }
if(namespace != elNamespace[ depth ]) if (namespace != elNamespace[depth]) {
{ throw new IllegalArgumentException("expected namespace "
throw new IllegalArgumentException( + printable(elNamespace[depth]) + " and not "
"expected namespace "+printable(elNamespace[ depth ]) + printable(namespace) + getLocation());
+" and not "+printable(namespace)+getLocation());
} }
if (name == null) { if (name == null) {
throw new IllegalArgumentException("end tag name can not be null"+getLocation()); throw new IllegalArgumentException("end tag name can not be null"
+ getLocation());
} }
if (checkNamesInterned && namesInterned) { if (checkNamesInterned && namesInterned) {
checkInterning(name); checkInterning(name);
} }
String startTagName = elName[depth]; String startTagName = elName[depth];
if ((!namesInterned && !name.equals(startTagName)) if ((!namesInterned && !name.equals(startTagName))
|| (namesInterned && name != startTagName )) || (namesInterned && name != startTagName)) {
{ throw new IllegalArgumentException("expected element name "
throw new IllegalArgumentException( + printable(elName[depth]) + " and not " + printable(name)
"expected element name "+printable(elName[ depth ])+" and not "+printable(name)+getLocation()); + getLocation());
} }
if (startTagIncomplete) { if (startTagIncomplete) {
writeNamespaceDeclarations(); writeNamespaceDeclarations();
out.write(" />"); //space is added to make it easier to work in XHTML!!! out.write(" />"); // space is added to make it easier to work in
// XHTML!!!
--depth; --depth;
} else { } else {
// assert startTagIncomplete == false; // assert startTagIncomplete == false;
if(doIndent && seenTag) { writeIndent(); } if (doIndent && seenTag) {
writeIndent();
}
out.write("</"); out.write("</");
String startTagPrefix = elPrefix[depth]; String startTagPrefix = elPrefix[depth];
if (startTagPrefix.length() > 0) { if (startTagPrefix.length() > 0) {
@ -792,125 +822,155 @@ public class MXSerializer implements XmlSerializer {
return this; return this;
} }
public XmlSerializer text (String text) throws IOException @Override
{ public XmlSerializer text(String text) throws IOException {
// assert text != null; // assert text != null;
if(startTagIncomplete || setPrefixCalled) closeStartTag(); if (startTagIncomplete || setPrefixCalled)
if(doIndent && seenTag) seenTag = false; closeStartTag();
if (doIndent && seenTag)
seenTag = false;
writeElementContent(text, out); writeElementContent(text, out);
return this; return this;
} }
public XmlSerializer text (char [] buf, int start, int len) throws IOException @Override
{ public XmlSerializer text(char[] buf, int start, int len)
if(startTagIncomplete || setPrefixCalled) closeStartTag(); throws IOException {
if(doIndent && seenTag) seenTag = false; if (startTagIncomplete || setPrefixCalled)
closeStartTag();
if (doIndent && seenTag)
seenTag = false;
writeElementContent(buf, start, len, out); writeElementContent(buf, start, len, out);
return this; return this;
} }
public void cdsect (String text) throws IOException @Override
{ public void cdsect(String text) throws IOException {
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); if (startTagIncomplete || setPrefixCalled || seenBracket)
if(doIndent && seenTag) seenTag = false; closeStartTag();
if (doIndent && seenTag)
seenTag = false;
out.write("<![CDATA["); out.write("<![CDATA[");
out.write(text); // escape? out.write(text); // escape?
out.write("]]>"); out.write("]]>");
} }
public void entityRef (String text) throws IOException @Override
{ public void entityRef(String text) throws IOException {
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); if (startTagIncomplete || setPrefixCalled || seenBracket)
if(doIndent && seenTag) seenTag = false; closeStartTag();
if (doIndent && seenTag)
seenTag = false;
out.write('&'); out.write('&');
out.write(text); // escape? out.write(text); // escape?
out.write(';'); out.write(';');
} }
public void processingInstruction (String text) throws IOException @Override
{ public void processingInstruction(String text) throws IOException {
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); if (startTagIncomplete || setPrefixCalled || seenBracket)
if(doIndent && seenTag) seenTag = false; closeStartTag();
if (doIndent && seenTag)
seenTag = false;
out.write("<?"); out.write("<?");
out.write(text); // escape? out.write(text); // escape?
out.write("?>"); out.write("?>");
} }
public void comment (String text) throws IOException @Override
{ public void comment(String text) throws IOException {
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); if (startTagIncomplete || setPrefixCalled || seenBracket)
if(doIndent && seenTag) seenTag = false; closeStartTag();
if (doIndent && seenTag)
seenTag = false;
out.write("<!--"); out.write("<!--");
out.write(text); // escape? out.write(text); // escape?
out.write("-->"); out.write("-->");
} }
public void docdecl (String text) throws IOException @Override
{ public void docdecl(String text) throws IOException {
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); if (startTagIncomplete || setPrefixCalled || seenBracket)
if(doIndent && seenTag) seenTag = false; closeStartTag();
if (doIndent && seenTag)
seenTag = false;
out.write("<!DOCTYPE"); out.write("<!DOCTYPE");
out.write(text); // escape? out.write(text); // escape?
out.write(">"); out.write(">");
} }
public void ignorableWhitespace (String text) throws IOException @Override
{ public void ignorableWhitespace(String text) throws IOException {
if(startTagIncomplete || setPrefixCalled || seenBracket) closeStartTag(); if (startTagIncomplete || setPrefixCalled || seenBracket)
if(doIndent && seenTag) seenTag = false; closeStartTag();
if (doIndent && seenTag)
seenTag = false;
if (text.length() == 0) { if (text.length() == 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"empty string is not allowed for ignorable whitespace"+getLocation()); "empty string is not allowed for ignorable whitespace"
+ getLocation());
} }
out.write(text); // no escape? out.write(text); // no escape?
} }
public void flush () throws IOException @Override
{ public void flush() throws IOException {
if(!finished && startTagIncomplete) closeStartTag(); if (!finished && startTagIncomplete)
closeStartTag();
out.flush(); out.flush();
} }
// --- utility methods // --- utility methods
protected void writeAttributeValue(String value, Writer out) throws IOException protected void writeAttributeValue(String value, Writer out)
{ throws IOException {
// .[apostrophe and <, & escaped], // .[apostrophe and <, & escaped],
final char quot = attributeUseApostrophe ? '\'' : '"'; final char quot = attributeUseApostrophe ? '\'' : '"';
final String quotEntity = attributeUseApostrophe ? "&apos;" : "&quot;"; final String quotEntity = attributeUseApostrophe ? "&apos;" : "&quot;";
int pos = 0; int pos = 0;
for (int i = 0; i < value.length(); i++) for (int i = 0; i < value.length(); i++) {
{
char ch = value.charAt(i); char ch = value.charAt(i);
if (ch == '&') { if (ch == '&') {
if(i > pos) out.write(value.substring(pos, i)); if (i > pos)
out.write(value.substring(pos, i));
out.write("&amp;"); out.write("&amp;");
pos = i + 1; pos = i + 1;
} if(ch == '<') { }
if(i > pos) out.write(value.substring(pos, i)); if (ch == '<') {
if (i > pos)
out.write(value.substring(pos, i));
out.write("&lt;"); out.write("&lt;");
pos = i + 1; pos = i + 1;
} else if (ch == quot) { } else if (ch == quot) {
if(i > pos) out.write(value.substring(pos, i)); if (i > pos)
out.write(value.substring(pos, i));
out.write(quotEntity); out.write(quotEntity);
pos = i + 1; pos = i + 1;
} else if (ch < 32) { } else if (ch < 32) {
// in XML 1.0 only legal character are #x9 | #xA | #xD // 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 // and they must be escaped otherwise in attribute value they
// are normalized to spaces
if (ch == 13 || ch == 10 || ch == 9) { if (ch == 13 || ch == 10 || ch == 9) {
if(i > pos) out.write(value.substring(pos, i)); if (i > pos)
out.write(value.substring(pos, i));
out.write("&#"); out.write("&#");
out.write(Integer.toString(ch)); out.write(Integer.toString(ch));
out.write(';'); out.write(';');
pos = i + 1; pos = i + 1;
} else { } else {
if(TRACE_ESCAPING) System.err.println(getClass().getName()+" DEBUG ATTR value.len="+value.length()+" "+printable(value)); if (TRACE_ESCAPING)
System.err.println(getClass().getName()
+ " DEBUG ATTR value.len=" + value.length()
+ " " + printable(value));
throw new IllegalStateException( throw new IllegalStateException(
// "character "+Integer.toString(ch)+" is not allowed in output"+getLocation()); // "character "+Integer.toString(ch)+" is not allowed in output"+getLocation());
"character "+printable(ch)+" ("+Integer.toString(ch)+") is not allowed in output"+getLocation() "character " + printable(ch) + " ("
+" (attr value="+printable(value)+")"); + Integer.toString(ch)
+ ") is not allowed in output"
+ getLocation() + " (attr value="
+ printable(value) + ")");
// in XML 1.1 legal are [#x1-#xD7FF] // in XML 1.1 legal are [#x1-#xD7FF]
// if(ch > 0) { // if(ch > 0) {
// if(i > pos) out.write(text.substring(pos, i)); // if(i > pos) out.write(text.substring(pos, i));
@ -933,13 +993,13 @@ public class MXSerializer implements XmlSerializer {
} }
protected void writeElementContent(String text, Writer out) throws IOException protected void writeElementContent(String text, Writer out)
{ throws IOException {
// esccape '<', '&', ']]>', <32 if necessary // esccape '<', '&', ']]>', <32 if necessary
int pos = 0; int pos = 0;
for (int i = 0; i < text.length(); i++) for (int i = 0; i < text.length(); i++) {
{ // TODO: check if doing char[] text.getChars() would be faster than
//TODO: check if doing char[] text.getChars() would be faster than getCharAt(i) ... // getCharAt(i) ...
char ch = text.charAt(i); char ch = text.charAt(i);
if (ch == ']') { if (ch == ']') {
if (seenBracket) { if (seenBracket) {
@ -949,15 +1009,18 @@ public class MXSerializer implements XmlSerializer {
} }
} else { } else {
if (ch == '&') { if (ch == '&') {
if(i > pos) out.write(text.substring(pos, i)); if (i > pos)
out.write(text.substring(pos, i));
out.write("&amp;"); out.write("&amp;");
pos = i + 1; pos = i + 1;
} else if (ch == '<') { } else if (ch == '<') {
if(i > pos) out.write(text.substring(pos, i)); if (i > pos)
out.write(text.substring(pos, i));
out.write("&lt;"); out.write("&lt;");
pos = i + 1; pos = i + 1;
} else if (seenBracketBracket && ch == '>') { } else if (seenBracketBracket && ch == '>') {
if(i > pos) out.write(text.substring(pos, i)); if (i > pos)
out.write(text.substring(pos, i));
out.write("&gt;"); out.write("&gt;");
pos = i + 1; pos = i + 1;
} else if (ch < 32) { } else if (ch < 32) {
@ -972,9 +1035,13 @@ public class MXSerializer implements XmlSerializer {
// out.write(';'); // out.write(';');
// pos = i + 1; // pos = i + 1;
} else { } else {
if(TRACE_ESCAPING) System.err.println(getClass().getName()+" DEBUG TEXT value.len="+text.length()+" "+printable(text)); if (TRACE_ESCAPING)
throw new IllegalStateException( System.err.println(getClass().getName()
"character "+Integer.toString(ch)+" is not allowed in output"+getLocation() + " 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) + ")"); + " (text value=" + printable(text) + ")");
// in XML 1.1 legal are [#x1-#xD7FF] // in XML 1.1 legal are [#x1-#xD7FF]
// if(ch > 0) { // if(ch > 0) {
@ -1001,17 +1068,14 @@ public class MXSerializer implements XmlSerializer {
out.write(text); // this is shortcut to the most common case out.write(text); // this is shortcut to the most common case
} }
} }
protected void writeElementContent(char[] buf, int off, int len, Writer out) throws IOException protected void writeElementContent(char[] buf, int off, int len, Writer out)
{ throws IOException {
// esccape '<', '&', ']]>' // esccape '<', '&', ']]>'
final int end = off + len; final int end = off + len;
int pos = off; int pos = off;
for (int i = off; i < end; i++) for (int i = off; i < end; i++) {
{
final char ch = buf[i]; final char ch = buf[i];
if (ch == ']') { if (ch == ']') {
if (seenBracket) { if (seenBracket) {
@ -1044,7 +1108,6 @@ public class MXSerializer implements XmlSerializer {
if (ch == 9 || ch == 10 || ch == 13) { if (ch == 9 || ch == 10 || ch == 13) {
// pass through // pass through
// } else if(ch == 13 ) { //if(ch == '\r') { // } else if(ch == 13 ) { //if(ch == '\r') {
// if(i > pos) { // if(i > pos) {
// out.write(buf, pos, i - pos); // out.write(buf, pos, i - pos);
@ -1054,11 +1117,13 @@ public class MXSerializer implements XmlSerializer {
// out.write(';'); // out.write(';');
// pos = i + 1; // pos = i + 1;
} else { } else {
if(TRACE_ESCAPING) System.err.println( if (TRACE_ESCAPING)
getClass().getName()+" DEBUG TEXT value.len=" System.err.println(getClass().getName()
+len+" "+printable(new String(buf,off,len))); + " DEBUG TEXT value.len=" + len + " "
throw new IllegalStateException( + printable(new String(buf, off, len)));
"character "+printable(ch)+" ("+Integer.toString(ch)+") is not allowed in output"+getLocation()); throw new IllegalStateException("character "
+ printable(ch) + " (" + Integer.toString(ch)
+ ") is not allowed in output" + getLocation());
// in XML 1.1 legal are [#x1-#xD7FF] // in XML 1.1 legal are [#x1-#xD7FF]
// if(ch > 0) { // if(ch > 0) {
// if(i > pos) out.write(text.substring(pos, i)); // if(i > pos) out.write(text.substring(pos, i));
@ -1085,7 +1150,8 @@ public class MXSerializer implements XmlSerializer {
/** simple utility method -- good for debugging */ /** simple utility method -- good for debugging */
protected static final String printable(String s) { protected static final String printable(String s) {
if(s == null) return "null"; if (s == null)
return "null";
StringBuffer retval = new StringBuffer(s.length() + 16); StringBuffer retval = new StringBuffer(s.length() + 16);
retval.append("'"); retval.append("'");
char ch; char ch;
@ -1102,10 +1168,8 @@ public class MXSerializer implements XmlSerializer {
return retval.toString(); return retval.toString();
} }
private static void addPrintable(StringBuffer retval, char ch) private static void addPrintable(StringBuffer retval, char ch) {
{ switch (ch) {
switch (ch)
{
case '\b': case '\b':
retval.append("\\b"); retval.append("\\b");
break; break;
@ -1133,7 +1197,8 @@ public class MXSerializer implements XmlSerializer {
default: default:
if (ch < 0x20 || ch > 0x7e) { if (ch < 0x20 || ch > 0x7e) {
final String ss = "0000" + Integer.toString(ch, 16); final String ss = "0000" + Integer.toString(ch, 16);
retval.append("\\u" + ss.substring(ss.length() - 4, ss.length())); retval.append("\\u"
+ ss.substring(ss.length() - 4, ss.length()));
} else { } else {
retval.append(ch); retval.append(ch);
} }
@ -1141,4 +1206,3 @@ public class MXSerializer implements XmlSerializer {
} }
} }

View File

@ -26,7 +26,6 @@ import org.junit.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
@ -52,14 +51,14 @@ public class BuildAndDecodeTest {
assertEquals(true, isAaptPresent()); assertEquals(true, isAaptPresent());
} }
@Test @Test
public void encodeAndDecodeTest() throws BrutException, IOException { public void encodeAndDecodeTest() throws BrutException, IOException {
LOGGER.info("Building testapp.apk..."); LOGGER.info("Building testapp.apk...");
File testApk = new File(sTmpDir, "testapp.apk"); File testApk = new File(sTmpDir, "testapp.apk");
ExtFile blank = null; ExtFile blank = null;
new Androlib().build(sTestOrigDir, testApk, BuildAndDecodeTest.returnStock(),blank,""); new Androlib().build(sTestOrigDir, testApk,
BuildAndDecodeTest.returnStock(), blank, "");
LOGGER.info("Decoding testapp.apk..."); LOGGER.info("Decoding testapp.apk...");
ApkDecoder apkDecoder = new ApkDecoder(testApk); ApkDecoder apkDecoder = new ApkDecoder(testApk);
@ -67,7 +66,6 @@ public class BuildAndDecodeTest {
apkDecoder.decode(); apkDecoder.decode();
} }
@Test @Test
public void valuesArraysTest() throws BrutException { public void valuesArraysTest() throws BrutException {
compareValuesFiles("values-mcc001/arrays.xml"); compareValuesFiles("values-mcc001/arrays.xml");
@ -128,19 +126,20 @@ public class BuildAndDecodeTest {
@Test @Test
public void qualifiersTest() throws BrutException { public void qualifiersTest() throws BrutException {
compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp" + compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp"
"-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key" + + "-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key"
"-navhidden-dpad/strings.xml"); + "-navhidden-dpad/strings.xml");
} }
private static boolean isAaptPresent() throws Exception { private static boolean isAaptPresent() throws Exception {
boolean result = true; boolean result = true;
try try {
{
Process proc = Runtime.getRuntime().exec("aapt"); Process proc = Runtime.getRuntime().exec("aapt");
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getErrorStream())); BufferedReader br = new BufferedReader(new InputStreamReader(
proc.getErrorStream()));
String line = null; String line = null;
while ( (line = br.readLine()) != null){} while ((line = br.readLine()) != null) {
}
} catch (Exception ex) { } catch (Exception ex) {
result = false; result = false;
} }
@ -148,20 +147,19 @@ public class BuildAndDecodeTest {
} }
private void compareValuesFiles(String path) throws BrutException { private void compareValuesFiles(String path) throws BrutException {
compareXmlFiles("res/" + path, compareXmlFiles("res/" + path, new ElementNameAndAttributeQualifier(
new ElementNameAndAttributeQualifier("name")); "name"));
} }
private void compareXmlFiles(String path) throws BrutException { private void compareXmlFiles(String path) throws BrutException {
compareXmlFiles(path, null); compareXmlFiles(path, null);
} }
private void compareXmlFiles(String path, private void compareXmlFiles(String path, ElementQualifier qualifier)
ElementQualifier qualifier) throws BrutException { throws BrutException {
DetailedDiff diff; DetailedDiff diff;
try { try {
Reader control = new FileReader( Reader control = new FileReader(new File(sTestOrigDir, path));
new File(sTestOrigDir, path));
Reader test = new FileReader(new File(sTestNewDir, path)); Reader test = new FileReader(new File(sTestNewDir, path));
diff = new DetailedDiff(new Diff(control, test)); diff = new DetailedDiff(new Diff(control, test));
@ -175,8 +173,8 @@ public class BuildAndDecodeTest {
diff.overrideElementQualifier(qualifier); diff.overrideElementQualifier(qualifier);
} }
assertTrue(path + ": " + assertTrue(path + ": " + diff.getAllDifferences().toString(),
diff.getAllDifferences().toString(), diff.similar()); diff.similar());
} }
private static HashMap<String, Boolean> returnStock() throws BrutException { private static HashMap<String, Boolean> returnStock() throws BrutException {
@ -195,6 +193,6 @@ public class BuildAndDecodeTest {
private static ExtFile sTestOrigDir; private static ExtFile sTestOrigDir;
private static ExtFile sTestNewDir; private static ExtFile sTestNewDir;
private final static Logger LOGGER = private final static Logger LOGGER = Logger
Logger.getLogger(BuildAndDecodeTest.class.getName()); .getLogger(BuildAndDecodeTest.class.getName());
} }

View File

@ -35,7 +35,8 @@ public abstract class TestUtils {
public static Map<String, String> parseStringsXml(File file) public static Map<String, String> parseStringsXml(File file)
throws BrutException { throws BrutException {
try { try {
XmlPullParser xpp = XmlPullParserFactory.newInstance().newPullParser(); XmlPullParser xpp = XmlPullParserFactory.newInstance()
.newPullParser();
xpp.setInput(new FileReader(file)); xpp.setInput(new FileReader(file));
int eventType; int eventType;
@ -75,7 +76,8 @@ public abstract class TestUtils {
} }
} }
/* TODO: move to brut.util.Jar - it's not possible for now, because below /*
* 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 * implementation uses brut.dir. I think I should merge all my projects to
* single brut.common . * single brut.common .
*/ */
@ -104,12 +106,12 @@ public abstract class TestUtils {
dirURL = class_.getClassLoader().getResource(className); dirURL = class_.getClassLoader().getResource(className);
} }
if (dirURL.getProtocol().equals("jar")) { if (dirURL.getProtocol().equals("jar")) {
String jarPath; String jarPath;
try { try {
jarPath = URLDecoder.decode(dirURL.getPath().substring( jarPath = URLDecoder.decode(
5, dirURL.getPath().indexOf("!")), "UTF-8"); dirURL.getPath().substring(5,
dirURL.getPath().indexOf("!")), "UTF-8");
} catch (UnsupportedEncodingException ex) { } catch (UnsupportedEncodingException ex) {
throw new BrutException(ex); throw new BrutException(ex);
} }
@ -117,9 +119,9 @@ public abstract class TestUtils {
} }
} }
public static class ResValueElementQualifier implements ElementQualifier { public static class ResValueElementQualifier implements ElementQualifier {
@Override
public boolean qualifyForComparison(Element control, Element test) { public boolean qualifyForComparison(Element control, Element test) {
String controlType = control.getTagName(); String controlType = control.getTagName();
if ("item".equals(controlType)) { if ("item".equals(controlType)) {
@ -131,8 +133,9 @@ public abstract class TestUtils {
testType = test.getAttribute("type"); testType = test.getAttribute("type");
} }
return controlType.equals(testType) && control.getAttribute("name") return controlType.equals(testType)
.equals(test.getAttribute("name")); && control.getAttribute("name").equals(
test.getAttribute("name"));
} }
} }
} }