mirror of
https://github.com/revanced/Apktool.git
synced 2024-12-12 05:47:46 +01:00
Fixing the mess-ups that was apktool v1.4.8 :(
This commit is contained in:
parent
b5a476bb1b
commit
97451619b8
@ -149,10 +149,9 @@ final public class AndrolibResources {
|
||||
inApk = apkFile.getDirectory();
|
||||
out = new FileDirectory(outDir);
|
||||
|
||||
LOGGER.info("Decoding AndroidManifest.xml with resources...");
|
||||
|
||||
fileDecoder.decodeManifest(
|
||||
inApk, "AndroidManifest.xml", out, "AndroidManifest.xml");
|
||||
fileDecoder.decode(
|
||||
inApk, "AndroidManifest.xml", out, "AndroidManifest.xml",
|
||||
"xml");
|
||||
|
||||
if (inApk.containsDir("res")) {
|
||||
in = inApk.getDir("res");
|
||||
|
@ -51,15 +51,7 @@ public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializ
|
||||
}
|
||||
serializer.startTag(null, "item");
|
||||
serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
|
||||
|
||||
String rawValue = item.encodeAsResXmlValueExt();
|
||||
//AAPT don`t parse formatted for item tag(only for string and string-array tag),
|
||||
// so adding "formatted='fasle'" is useless.
|
||||
/*if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
|
||||
serializer.attribute(null, "formatted", "false");
|
||||
}*/
|
||||
serializer.text(rawValue);
|
||||
|
||||
serializer.text(item.encodeAsResXmlValue());
|
||||
serializer.endTag(null, "item");
|
||||
}
|
||||
serializer.endTag(null, "plurals");
|
||||
|
@ -52,7 +52,7 @@ public abstract class ResScalarValue extends ResValue
|
||||
if (mRawValue != null) {
|
||||
return mRawValue;
|
||||
}
|
||||
return encodeAsResXml();
|
||||
return encodeAsResXmlValueExt();
|
||||
}
|
||||
|
||||
public String encodeAsResXmlValueExt() throws AndrolibException {
|
||||
@ -60,7 +60,7 @@ public abstract class ResScalarValue extends ResValue
|
||||
if (rawValue != null) {
|
||||
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
|
||||
int count = 1;
|
||||
StringBuffer result = new StringBuffer();
|
||||
StringBuilder result = new StringBuilder();
|
||||
String tmp1[] = rawValue.split("%%", -1);
|
||||
int tmp1_sz = tmp1.length;
|
||||
for(int i=0;i<tmp1_sz;i++) {
|
||||
|
@ -247,13 +247,13 @@ public class ARSCDecoder {
|
||||
byte keyboard = mIn.readByte();
|
||||
byte navigation = mIn.readByte();
|
||||
byte inputFlags = mIn.readByte();
|
||||
/*inputPad0*/ mIn.skipBytes(1);
|
||||
mIn.skipBytes(1);
|
||||
|
||||
short screenWidth = mIn.readShort();
|
||||
short screenHeight = mIn.readShort();
|
||||
|
||||
short sdkVersion = mIn.readShort();
|
||||
/*minorVersion, now must always be 0*/ mIn.skipBytes(2);
|
||||
mIn.skipBytes(2);
|
||||
|
||||
byte screenLayout = 0;
|
||||
byte uiMode = 0;
|
||||
|
@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package brut.androlib.res.decoder;
|
||||
|
||||
import android.content.res.XmlResourceParser;
|
||||
@ -40,14 +39,13 @@ import java.util.logging.Logger;
|
||||
*
|
||||
* Binary xml files parser.
|
||||
*
|
||||
* Parser has only two states:
|
||||
* (1) Operational state, which parser obtains after first successful call
|
||||
* to next() and retains until open(), close(), or failed call to next().
|
||||
* (2) Closed state, which parser obtains after open(), close(), or failed
|
||||
* call to next(). In this state methods return invalid values or throw exceptions.
|
||||
* Parser has only two states: (1) Operational state, which parser obtains after
|
||||
* first successful call to next() and retains until open(), close(), or failed
|
||||
* call to next(). (2) Closed state, which parser obtains after open(), close(),
|
||||
* or failed call to next(). In this state methods return invalid values or
|
||||
* throw exceptions.
|
||||
*
|
||||
* TODO:
|
||||
* * check all methods in closed state
|
||||
* TODO: * check all methods in closed state
|
||||
*
|
||||
*/
|
||||
public class AXmlResourceParser implements XmlResourceParser {
|
||||
@ -319,20 +317,19 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||
try {
|
||||
return mAttrDecoder.decode(valueType, valueData,
|
||||
valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(
|
||||
m_strings.getString(valueRaw)),
|
||||
m_strings.getString(valueRaw)),
|
||||
getAttributeNameResource(index));
|
||||
} catch (AndrolibException ex) {
|
||||
setFirstError(ex);
|
||||
LOGGER.log(Level.WARNING, String.format(
|
||||
"Could not decode attr value, using undecoded value " +
|
||||
"instead: ns=%s, name=%s, value=0x%08x",
|
||||
getAttributePrefix(index), getAttributeName(index),
|
||||
valueData
|
||||
), ex);
|
||||
"Could not decode attr value, using undecoded value "
|
||||
+ "instead: ns=%s, name=%s, value=0x%08x",
|
||||
getAttributePrefix(index), getAttributeName(index),
|
||||
valueData), ex);
|
||||
}
|
||||
} else {
|
||||
if (valueType==TypedValue.TYPE_STRING) {
|
||||
return m_strings.getString(valueRaw);
|
||||
if (valueType == TypedValue.TYPE_STRING) {
|
||||
return ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw));
|
||||
}
|
||||
}
|
||||
|
||||
@ -493,20 +490,16 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||
|
||||
///////////////////////////////////////////// implementation
|
||||
/**
|
||||
* Namespace stack, holds prefix+uri pairs, as well as
|
||||
* depth information.
|
||||
* All information is stored in one int[] array.
|
||||
* Array consists of depth frames:
|
||||
* Data=DepthFrame*;
|
||||
* DepthFrame=Count+[Prefix+Uri]*+Count;
|
||||
* Count='count of Prefix+Uri pairs';
|
||||
* Yes, count is stored twice, to enable bottom-up traversal.
|
||||
* increaseDepth adds depth frame, decreaseDepth removes it.
|
||||
* push/pop operations operate only in current depth frame.
|
||||
* decreaseDepth removes any remaining (not pop'ed) namespace pairs.
|
||||
* findXXX methods search all depth frames starting
|
||||
* from the last namespace pair of current depth frame.
|
||||
* All functions that operate with int, use -1 as 'invalid value'.
|
||||
* Namespace stack, holds prefix+uri pairs, as well as depth information.
|
||||
* All information is stored in one int[] array. Array consists of depth
|
||||
* frames: Data=DepthFrame*; DepthFrame=Count+[Prefix+Uri]*+Count;
|
||||
* Count='count of Prefix+Uri pairs'; Yes, count is stored twice, to enable
|
||||
* bottom-up traversal. increaseDepth adds depth frame, decreaseDepth
|
||||
* removes it. push/pop operations operate only in current depth frame.
|
||||
* decreaseDepth removes any remaining (not pop'ed) namespace pairs. findXXX
|
||||
* methods search all depth frames starting from the last namespace pair of
|
||||
* current depth frame. All functions that operate with int, use -1 as
|
||||
* 'invalid value'.
|
||||
*
|
||||
* !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !!
|
||||
*
|
||||
@ -764,7 +757,7 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||
if (m_event != 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) {
|
||||
throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ").");
|
||||
}
|
||||
@ -806,7 +799,9 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||
// Delayed initialization.
|
||||
if (m_strings == null) {
|
||||
m_reader.skipCheckInt(CHUNK_AXML_FILE);
|
||||
/*chunkSize*/ m_reader.skipInt();
|
||||
/*
|
||||
* chunkSize
|
||||
*/ m_reader.skipInt();
|
||||
m_strings = StringBlock.read(m_reader);
|
||||
m_namespaces.increaseDepth();
|
||||
m_operational = true;
|
||||
@ -856,9 +851,13 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||
}
|
||||
|
||||
// Common header.
|
||||
/*chunkSize*/ m_reader.skipInt();
|
||||
/*
|
||||
* chunkSize
|
||||
*/ m_reader.skipInt();
|
||||
int lineNumber = m_reader.readInt();
|
||||
/*0xFFFFFFFF*/ m_reader.skipInt();
|
||||
/*
|
||||
* 0xFFFFFFFF
|
||||
*/ m_reader.skipInt();
|
||||
|
||||
// Fake START_DOCUMENT event.
|
||||
if (chunkType == CHUNK_XML_START_TAG && event == -1) {
|
||||
@ -874,8 +873,12 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||
int uri = m_reader.readInt();
|
||||
m_namespaces.push(prefix, uri);
|
||||
} else {
|
||||
/*prefix*/ m_reader.skipInt();
|
||||
/*uri*/ m_reader.skipInt();
|
||||
/*
|
||||
* prefix
|
||||
*/ m_reader.skipInt();
|
||||
/*
|
||||
* uri
|
||||
*/ m_reader.skipInt();
|
||||
m_namespaces.pop();
|
||||
}
|
||||
continue;
|
||||
@ -888,7 +891,9 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||
if (chunkType == CHUNK_XML_START_TAG) {
|
||||
m_namespaceUri = m_reader.readInt();
|
||||
m_name = m_reader.readInt();
|
||||
/*flags?*/ m_reader.skipInt();
|
||||
/*
|
||||
* flags?
|
||||
*/ m_reader.skipInt();
|
||||
int attributeCount = m_reader.readInt();
|
||||
m_idAttribute = (attributeCount >>> 16) - 1;
|
||||
attributeCount &= 0xFFFF;
|
||||
@ -903,7 +908,7 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||
m_namespaces.increaseDepth();
|
||||
m_event = START_TAG;
|
||||
m_strings.touch(m_name, m_name);
|
||||
for(int i = 0; i<attributeCount; i++) {
|
||||
for (int i = 0; i < attributeCount; i++) {
|
||||
m_strings.touch(m_attributes[ATTRIBUTE_IX_NAME], m_name);
|
||||
m_strings.touch(m_attributes[ATTRIBUTE_IX_VALUE_STRING], m_name);
|
||||
}
|
||||
@ -922,119 +927,122 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||
|
||||
if (chunkType == CHUNK_XML_TEXT) {
|
||||
m_name = m_reader.readInt();
|
||||
/*?*/ m_reader.skipInt();
|
||||
/*?*/ m_reader.skipInt();
|
||||
/*
|
||||
* ?
|
||||
*/ m_reader.skipInt();
|
||||
/*
|
||||
* ?
|
||||
*/ m_reader.skipInt();
|
||||
m_event = TEXT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatArray(int[] array, int min, int max) {
|
||||
if(max > array.length)
|
||||
max = array.length;
|
||||
if(min < 0)
|
||||
min = 0;
|
||||
StringBuffer sb = new StringBuffer("[");
|
||||
int i = min;
|
||||
while(true) {
|
||||
sb.append(array[i]);
|
||||
i++;
|
||||
if(i < max) {
|
||||
sb.append(", ");
|
||||
} else {
|
||||
sb.append("]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private boolean compareAttr(int[] attr1, int[] attr2) {
|
||||
//TODO: sort Attrs
|
||||
/*
|
||||
* ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING
|
||||
* : ATTRIBUTE_IX_NAMESPACE_URI
|
||||
* ATTRIBUTE_IX_NAMESPACE_URI : ATTRIBUTE_IX_NAME
|
||||
* id
|
||||
*
|
||||
*/
|
||||
if(attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING &&
|
||||
attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE] &&
|
||||
//(m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) ||
|
||||
// m_strings.touch(attr2[ATTRIBUTE_IX_VALUE_STRING], m_name)) &&
|
||||
//m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) &&
|
||||
attr1[ATTRIBUTE_IX_VALUE_STRING] != attr2[ATTRIBUTE_IX_VALUE_STRING]) {
|
||||
return (attr1[ATTRIBUTE_IX_VALUE_STRING] < attr2[ATTRIBUTE_IX_VALUE_STRING]);
|
||||
} else if ((attr1[ATTRIBUTE_IX_NAMESPACE_URI] == attr2[ATTRIBUTE_IX_NAMESPACE_URI]) && (attr1[ATTRIBUTE_IX_NAMESPACE_URI] != -1) &&
|
||||
//(m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) ||
|
||||
// m_strings.touch(attr2[ATTRIBUTE_IX_NAME], m_name)) &&
|
||||
//m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) &&
|
||||
(attr1[ATTRIBUTE_IX_NAME] != attr2[ATTRIBUTE_IX_NAME])) {
|
||||
return (attr1[ATTRIBUTE_IX_NAME] < attr2[ATTRIBUTE_IX_NAME]);
|
||||
//} else if (attr1[ATTRIBUTE_IX_NAMESPACE_URI] < attr2[ATTRIBUTE_IX_NAMESPACE_URI]) {
|
||||
// return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void sortAttrs() {
|
||||
int attributeCount = m_attributes.length/ATTRIBUTE_LENGHT;
|
||||
int tmp1[][] = new int[attributeCount][];
|
||||
int tmp2[] = null;
|
||||
for (int i = 0; i < attributeCount;i++) {
|
||||
tmp1[i] = new int[ATTRIBUTE_LENGHT+1];
|
||||
for(int j = 0; j < ATTRIBUTE_LENGHT; j++) {
|
||||
tmp1[i][j] = m_attributes[i*ATTRIBUTE_LENGHT+j];
|
||||
}
|
||||
tmp1[i][ATTRIBUTE_LENGHT] = i;
|
||||
if(DBG) {
|
||||
try {
|
||||
if (dbgOut == null) {
|
||||
dbgOut = new BufferedWriter(new FileWriter("C:\\res.log",false));
|
||||
}
|
||||
dbgOut.write("Namespace: " + getAttributeNamespace (i) +
|
||||
", Name: " + getAttributeName (i)+
|
||||
", Value: " + getAttributeValue (i) + ", Array: " +
|
||||
formatArray(tmp1[i], 0, ATTRIBUTE_LENGHT) + "\n");
|
||||
dbgOut.flush();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
private static String formatArray(int[] array, int min, int max) {
|
||||
if (max > array.length) {
|
||||
max = array.length;
|
||||
}
|
||||
if (min < 0) {
|
||||
min = 0;
|
||||
}
|
||||
StringBuffer sb = new StringBuffer("[");
|
||||
int i = min;
|
||||
while (true) {
|
||||
sb.append(array[i]);
|
||||
i++;
|
||||
if (i < max) {
|
||||
sb.append(", ");
|
||||
} else {
|
||||
sb.append("]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < attributeCount;j++) {
|
||||
for (int i = 1; i < attributeCount;i++) {
|
||||
if(compareAttr(tmp1[i], tmp1[i-1])) {
|
||||
tmp2 = tmp1[i-1];
|
||||
tmp1[i-1] = tmp1[i];
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private boolean compareAttr(int[] attr1, int[] attr2) {
|
||||
//TODO: sort Attrs
|
||||
/*
|
||||
* ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING :
|
||||
* ATTRIBUTE_IX_NAMESPACE_URI ATTRIBUTE_IX_NAMESPACE_URI :
|
||||
* ATTRIBUTE_IX_NAME id
|
||||
*
|
||||
*/
|
||||
if (attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING
|
||||
&& attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE]
|
||||
&& //(m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) ||
|
||||
// m_strings.touch(attr2[ATTRIBUTE_IX_VALUE_STRING], m_name)) &&
|
||||
//m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) &&
|
||||
attr1[ATTRIBUTE_IX_VALUE_STRING] != attr2[ATTRIBUTE_IX_VALUE_STRING]) {
|
||||
return (attr1[ATTRIBUTE_IX_VALUE_STRING] < attr2[ATTRIBUTE_IX_VALUE_STRING]);
|
||||
} else if ((attr1[ATTRIBUTE_IX_NAMESPACE_URI] == attr2[ATTRIBUTE_IX_NAMESPACE_URI]) && (attr1[ATTRIBUTE_IX_NAMESPACE_URI] != -1)
|
||||
&& //(m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) ||
|
||||
// m_strings.touch(attr2[ATTRIBUTE_IX_NAME], m_name)) &&
|
||||
//m_strings.touch(attr1[ATTRIBUTE_IX_NAME], m_name) &&
|
||||
(attr1[ATTRIBUTE_IX_NAME] != attr2[ATTRIBUTE_IX_NAME])) {
|
||||
return (attr1[ATTRIBUTE_IX_NAME] < attr2[ATTRIBUTE_IX_NAME]);
|
||||
//} else if (attr1[ATTRIBUTE_IX_NAMESPACE_URI] < attr2[ATTRIBUTE_IX_NAMESPACE_URI]) {
|
||||
// return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void sortAttrs() {
|
||||
int attributeCount = m_attributes.length / ATTRIBUTE_LENGHT;
|
||||
int tmp1[][] = new int[attributeCount][];
|
||||
int tmp2[] = null;
|
||||
for (int i = 0; i < attributeCount; i++) {
|
||||
tmp1[i] = new int[ATTRIBUTE_LENGHT + 1];
|
||||
for (int j = 0; j < ATTRIBUTE_LENGHT; j++) {
|
||||
tmp1[i][j] = m_attributes[i * ATTRIBUTE_LENGHT + j];
|
||||
}
|
||||
tmp1[i][ATTRIBUTE_LENGHT] = i;
|
||||
if (DBG) {
|
||||
try {
|
||||
if (dbgOut == null) {
|
||||
dbgOut = new BufferedWriter(new FileWriter("C:\\res.log", false));
|
||||
}
|
||||
dbgOut.write("Namespace: " + getAttributeNamespace(i)
|
||||
+ ", Name: " + getAttributeName(i)
|
||||
+ ", Value: " + getAttributeValue(i) + ", Array: "
|
||||
+ formatArray(tmp1[i], 0, ATTRIBUTE_LENGHT) + "\n");
|
||||
dbgOut.flush();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < attributeCount; j++) {
|
||||
for (int i = 1; i < attributeCount; i++) {
|
||||
if (compareAttr(tmp1[i], tmp1[i - 1])) {
|
||||
tmp2 = tmp1[i - 1];
|
||||
tmp1[i - 1] = tmp1[i];
|
||||
tmp1[i] = tmp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < attributeCount;i++) {
|
||||
for(int j = 0; j < ATTRIBUTE_LENGHT; j++) {
|
||||
m_attributes[i*ATTRIBUTE_LENGHT+j] = tmp1[i][j];
|
||||
for (int i = 0; i < attributeCount; i++) {
|
||||
for (int j = 0; j < ATTRIBUTE_LENGHT; j++) {
|
||||
m_attributes[i * ATTRIBUTE_LENGHT + j] = tmp1[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setFirstError(AndrolibException error) {
|
||||
private void setFirstError(AndrolibException error) {
|
||||
if (mFirstError == null) {
|
||||
mFirstError = error;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////// data
|
||||
/*
|
||||
* All values are essentially indices, e.g. m_name is
|
||||
* an index of name in m_strings.
|
||||
* All values are essentially indices, e.g. m_name is an index of name in
|
||||
* m_strings.
|
||||
*/
|
||||
private ExtDataInput m_reader;
|
||||
private ResAttrDecoder mAttrDecoder;
|
||||
private AndrolibException mFirstError;
|
||||
|
||||
private boolean m_operational = false;
|
||||
private StringBlock m_strings;
|
||||
private int[] m_resourceIDs;
|
||||
@ -1048,28 +1056,24 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||
private int m_idAttribute;
|
||||
private int m_classAttribute;
|
||||
private int m_styleAttribute;
|
||||
|
||||
private final static Logger LOGGER =
|
||||
Logger.getLogger(AXmlResourceParser.class.getName());
|
||||
Logger.getLogger(AXmlResourceParser.class.getName());
|
||||
private static final String E_NOT_SUPPORTED = "Method is not supported.";
|
||||
private static final int
|
||||
ATTRIBUTE_IX_NAMESPACE_URI = 0,
|
||||
ATTRIBUTE_IX_NAME = 1,
|
||||
ATTRIBUTE_IX_VALUE_STRING = 2,
|
||||
ATTRIBUTE_IX_VALUE_TYPE = 3,
|
||||
ATTRIBUTE_IX_VALUE_DATA = 4,
|
||||
ATTRIBUTE_LENGHT = 5;
|
||||
|
||||
private static final int
|
||||
CHUNK_AXML_FILE = 0x00080003,
|
||||
CHUNK_RESOURCEIDS = 0x00080180,
|
||||
CHUNK_XML_FIRST = 0x00100100,
|
||||
private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0,
|
||||
ATTRIBUTE_IX_NAME = 1,
|
||||
ATTRIBUTE_IX_VALUE_STRING = 2,
|
||||
ATTRIBUTE_IX_VALUE_TYPE = 3,
|
||||
ATTRIBUTE_IX_VALUE_DATA = 4,
|
||||
ATTRIBUTE_LENGHT = 5;
|
||||
private static final int CHUNK_AXML_FILE = 0x00080003,
|
||||
CHUNK_RESOURCEIDS = 0x00080180,
|
||||
CHUNK_XML_FIRST = 0x00100100,
|
||||
CHUNK_XML_START_NAMESPACE = 0x00100100,
|
||||
CHUNK_XML_END_NAMESPACE = 0x00100101,
|
||||
CHUNK_XML_START_TAG = 0x00100102,
|
||||
CHUNK_XML_END_TAG = 0x00100103,
|
||||
CHUNK_XML_TEXT = 0x00100104,
|
||||
CHUNK_XML_LAST = 0x00100104;
|
||||
CHUNK_XML_END_NAMESPACE = 0x00100101,
|
||||
CHUNK_XML_START_TAG = 0x00100102,
|
||||
CHUNK_XML_END_TAG = 0x00100103,
|
||||
CHUNK_XML_TEXT = 0x00100104,
|
||||
CHUNK_XML_LAST = 0x00100104;
|
||||
private Writer dbgOut = null;
|
||||
private final static boolean DBG = false;
|
||||
}
|
@ -156,37 +156,24 @@ public final class ResXmlEncoders {
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* It searches for "%", but not "%%" nor "%(\d)+\$"
|
||||
*/
|
||||
private static List<Integer> findNonPositionalSubstitutions(String str,
|
||||
private static List<Integer> findNonPositionalSubstitutions(String str,
|
||||
int max) {
|
||||
int pos = 0;
|
||||
int pos2 = 0;
|
||||
int count = 0;
|
||||
int length = str.length();
|
||||
List<Integer> ret = new ArrayList<Integer>();
|
||||
while((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) {
|
||||
if (pos2 == length) {
|
||||
while((pos = str.indexOf('%', pos)) != -1) {
|
||||
if (pos + 1 == length) {
|
||||
break;
|
||||
}
|
||||
char c = str.charAt(pos2++);
|
||||
if (c == '%') {
|
||||
continue;
|
||||
}
|
||||
if (c >= '0' && c <= '9' && pos2 < length) {
|
||||
do {
|
||||
c = str.charAt(pos2++);
|
||||
} while (c >= '0' && c <= '9' && pos2 < length);
|
||||
if (c == '$') {
|
||||
continue;
|
||||
char c = str.charAt(pos + 1);
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
ret.add(pos);
|
||||
if (max != -1 && ++count >= max) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret.add(pos);
|
||||
if (max != -1 && ++count >= max) {
|
||||
break;
|
||||
}
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
Loading…
Reference in New Issue
Block a user