mirror of
https://github.com/iBotPeaches/Apktool.git
synced 2024-11-23 04:30:04 +00:00
refactor: clean up external pull parser and introduce brut.j.xml (#3709)
Some checks failed
Analyze / Analyze (push) Has been cancelled
CI / analyze-mac-aapt (aapt2_64) (push) Has been cancelled
CI / analyze-mac-aapt (aapt_64) (push) Has been cancelled
CI / analyze-linux-aapt (aapt) (push) Has been cancelled
CI / analyze-linux-aapt (aapt2) (push) Has been cancelled
CI / analyze-linux-aapt (aapt2_64) (push) Has been cancelled
CI / analyze-linux-aapt (aapt_64) (push) Has been cancelled
CI / analyze-windows-aapt (aapt.exe) (push) Has been cancelled
CI / analyze-windows-aapt (aapt2.exe) (push) Has been cancelled
CI / analyze-windows-aapt (aapt2_64.exe) (push) Has been cancelled
CI / analyze-windows-aapt (aapt_64.exe) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (11, macOS-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (11, ubuntu-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (11, windows-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (17, macOS-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (17, ubuntu-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (17, windows-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (21, macOS-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (21, ubuntu-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (21, windows-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (8, macOS-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (8, ubuntu-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (8, windows-latest) (push) Has been cancelled
CI / Build apktool.jar (push) Has been cancelled
Some checks failed
Analyze / Analyze (push) Has been cancelled
CI / analyze-mac-aapt (aapt2_64) (push) Has been cancelled
CI / analyze-mac-aapt (aapt_64) (push) Has been cancelled
CI / analyze-linux-aapt (aapt) (push) Has been cancelled
CI / analyze-linux-aapt (aapt2) (push) Has been cancelled
CI / analyze-linux-aapt (aapt2_64) (push) Has been cancelled
CI / analyze-linux-aapt (aapt_64) (push) Has been cancelled
CI / analyze-windows-aapt (aapt.exe) (push) Has been cancelled
CI / analyze-windows-aapt (aapt2.exe) (push) Has been cancelled
CI / analyze-windows-aapt (aapt2_64.exe) (push) Has been cancelled
CI / analyze-windows-aapt (aapt_64.exe) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (11, macOS-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (11, ubuntu-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (11, windows-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (17, macOS-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (17, ubuntu-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (17, windows-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (21, macOS-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (21, ubuntu-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (21, windows-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (8, macOS-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (8, ubuntu-latest) (push) Has been cancelled
CI / Build/Test (JDK ${{ matrix.java }}, ${{ matrix.os }}) (8, windows-latest) (push) Has been cancelled
CI / Build apktool.jar (push) Has been cancelled
* refactor: clean up external pull parser and introduce brut.j.xml We have no need for an XML pull parser in the project, it was only used for testing, which is now done with XPath. The external xpp3 library from org.ogce is obsolete and has the issue of including javax.xml.namespace.QName which conflicts with the JRE implementation that exists for a very long time now. This makes direct usages of QName produce very obscure NPEs that took me hours to figure out. This patch will allow further optimization that is WIP. The external library was replaced by the basic xmlpull API. The MXSerializer has been cleaned and the features used by apktool have been integrated into the custom implementation, now part of a separate module called brut.j.xml. Writing has been optimized by buffering write operations, inspired by KXmlSerializer used by Android itself. A class XmlPullUtils also written that allows copying from a XmlPullParser into a XmlSerializer with or without an EventHandler. We use it for AndroidManifestPullStreamDecoder (with EventHandler, to allow omitting the uses-sdk tag), and for ResXmlPullStreamDecoder (direct copy, without EventHandler). saveDocument in ResXmlPatcher was tweaked to output proper output - a new line after declaration and a new line after root element's end tag. TL;DR mostly behind the scene refactor, no end user changes.
This commit is contained in:
parent
7033f4ee2f
commit
b49e77087d
3
brut.apktool/apktool-cli/proguard-rules.pro
vendored
3
brut.apktool/apktool-cli/proguard-rules.pro
vendored
@ -6,9 +6,6 @@
|
||||
static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
# https://github.com/iBotPeaches/Apktool/issues/3602#issuecomment-2117317880
|
||||
-dontwarn org.xmlpull.mxp1**
|
||||
|
||||
# https://github.com/iBotPeaches/Apktool/pull/3670#issuecomment-2296326878
|
||||
-dontwarn com.google.j2objc.annotations.Weak
|
||||
-dontwarn com.google.j2objc.annotations.RetainedWith
|
||||
|
@ -25,13 +25,13 @@ tasks {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":brut.j.dir"))
|
||||
api(project(":brut.j.util"))
|
||||
api(project(":brut.j.common"))
|
||||
api(project(":brut.j.util"))
|
||||
api(project(":brut.j.dir"))
|
||||
api(project(":brut.j.xml"))
|
||||
|
||||
implementation(libs.baksmali)
|
||||
implementation(libs.smali)
|
||||
implementation(libs.xmlpull)
|
||||
implementation(libs.guava)
|
||||
implementation(libs.commons.lang3)
|
||||
implementation(libs.commons.io)
|
||||
|
@ -21,13 +21,12 @@ import brut.androlib.apk.ApkInfo;
|
||||
import brut.androlib.exceptions.AndrolibException;
|
||||
import brut.androlib.res.data.*;
|
||||
import brut.androlib.res.decoder.*;
|
||||
import brut.androlib.res.util.ExtMXSerializer;
|
||||
import brut.androlib.res.util.ExtXmlSerializer;
|
||||
import brut.androlib.res.xml.ResValuesXmlSerializable;
|
||||
import brut.androlib.res.xml.ResXmlPatcher;
|
||||
import brut.directory.Directory;
|
||||
import brut.directory.DirectoryException;
|
||||
import brut.directory.FileDirectory;
|
||||
import brut.xmlpull.MXSerializer;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
@ -75,7 +74,8 @@ public class ResourcesDecoder {
|
||||
}
|
||||
|
||||
AXmlResourceParser axmlParser = new AndroidManifestResourceParser(mResTable);
|
||||
ResStreamDecoder fileDecoder = new AndroidManifestPullStreamDecoder(axmlParser, getResXmlSerializer());
|
||||
XmlSerializer xmlSerializer = newXmlSerializer();
|
||||
ResStreamDecoder fileDecoder = new AndroidManifestPullStreamDecoder(axmlParser, xmlSerializer);
|
||||
|
||||
Directory inApk, out;
|
||||
InputStream inputStream = null;
|
||||
@ -157,7 +157,8 @@ public class ResourcesDecoder {
|
||||
decoders.setDecoder("9patch", new Res9patchStreamDecoder());
|
||||
|
||||
AXmlResourceParser axmlParser = new AXmlResourceParser(mResTable);
|
||||
decoders.setDecoder("xml", new ResXmlPullStreamDecoder(axmlParser, getResXmlSerializer()));
|
||||
XmlSerializer xmlSerializer = newXmlSerializer();
|
||||
decoders.setDecoder("xml", new ResXmlPullStreamDecoder(axmlParser, xmlSerializer));
|
||||
|
||||
ResFileDecoder fileDecoder = new ResFileDecoder(decoders);
|
||||
Directory in, out, outRes;
|
||||
@ -170,7 +171,6 @@ public class ResourcesDecoder {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
|
||||
ExtMXSerializer xmlSerializer = getResXmlSerializer();
|
||||
for (ResPackage pkg : mResTable.listMainPackages()) {
|
||||
|
||||
LOGGER.info("Decoding file-resources...");
|
||||
@ -191,20 +191,24 @@ public class ResourcesDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
private ExtMXSerializer getResXmlSerializer() {
|
||||
ExtMXSerializer serial = new ExtMXSerializer();
|
||||
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_INDENTATION, " ");
|
||||
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR, System.getProperty("line.separator"));
|
||||
serial.setProperty(ExtXmlSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8");
|
||||
serial.setDisabledAttrEscape(true);
|
||||
return serial;
|
||||
private XmlSerializer newXmlSerializer() throws AndrolibException {
|
||||
try {
|
||||
XmlSerializer serial = new MXSerializer();
|
||||
serial.setFeature(MXSerializer.FEATURE_ATTR_VALUE_NO_ESCAPE, true);
|
||||
serial.setProperty(MXSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8");
|
||||
serial.setProperty(MXSerializer.PROPERTY_INDENTATION, " ");
|
||||
serial.setProperty(MXSerializer.PROPERTY_LINE_SEPARATOR, System.getProperty("line.separator"));
|
||||
return serial;
|
||||
} catch (IllegalArgumentException | IllegalStateException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateValuesFile(ResValuesFile valuesFile, Directory out,
|
||||
ExtXmlSerializer serial) throws AndrolibException {
|
||||
XmlSerializer serial) throws AndrolibException {
|
||||
try {
|
||||
OutputStream outStream = out.getFileOutput(valuesFile.getPath());
|
||||
serial.setOutput((outStream), null);
|
||||
serial.setOutput(outStream, null);
|
||||
serial.startDocument(null, null);
|
||||
serial.startTag(null, "resources");
|
||||
|
||||
@ -216,7 +220,6 @@ public class ResourcesDecoder {
|
||||
}
|
||||
|
||||
serial.endTag(null, "resources");
|
||||
serial.newLine();
|
||||
serial.endDocument();
|
||||
serial.flush();
|
||||
outStream.close();
|
||||
|
@ -611,12 +611,12 @@ public class AXmlResourceParser implements XmlResourceParser {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getFeature(String feature) {
|
||||
public boolean getFeature(String name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeature(String name, boolean value) throws XmlPullParserException {
|
||||
public void setFeature(String name, boolean state) throws XmlPullParserException {
|
||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
|
@ -20,122 +20,28 @@ import brut.androlib.exceptions.AndrolibException;
|
||||
import brut.androlib.exceptions.AXmlDecodingException;
|
||||
import brut.androlib.exceptions.RawXmlEncounteredException;
|
||||
import brut.androlib.res.data.ResTable;
|
||||
import brut.androlib.res.util.ExtXmlSerializer;
|
||||
import brut.xmlpull.XmlPullUtils;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.wrapper.XmlPullParserWrapper;
|
||||
import org.xmlpull.v1.wrapper.XmlPullWrapperFactory;
|
||||
import org.xmlpull.v1.wrapper.XmlSerializerWrapper;
|
||||
import org.xmlpull.v1.wrapper.classic.StaticXmlSerializerWrapper;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class AndroidManifestPullStreamDecoder implements ResStreamDecoder {
|
||||
public AndroidManifestPullStreamDecoder(AXmlResourceParser parser,
|
||||
ExtXmlSerializer serializer) {
|
||||
this.mParser = parser;
|
||||
this.mSerial = serializer;
|
||||
private final AXmlResourceParser mParser;
|
||||
private final XmlSerializer mSerial;
|
||||
|
||||
public AndroidManifestPullStreamDecoder(AXmlResourceParser parser, XmlSerializer serial) {
|
||||
mParser = parser;
|
||||
mSerial = serial;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(InputStream in, OutputStream out)
|
||||
throws AndrolibException {
|
||||
public void decode(InputStream in, OutputStream out) throws AndrolibException {
|
||||
try {
|
||||
XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance();
|
||||
XmlPullParserWrapper par = factory.newPullParserWrapper(mParser);
|
||||
final ResTable resTable = mParser.getResTable();
|
||||
|
||||
XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, factory) {
|
||||
final boolean hideSdkInfo = !resTable.getAnalysisMode();
|
||||
|
||||
@Override
|
||||
public void event(XmlPullParser pp)
|
||||
throws XmlPullParserException, IOException {
|
||||
int type = pp.getEventType();
|
||||
|
||||
if (type == XmlPullParser.START_TAG) {
|
||||
if ("manifest".equals(pp.getName())) {
|
||||
try {
|
||||
parseManifest(pp);
|
||||
} catch (AndrolibException ignored) {}
|
||||
} else if ("uses-sdk".equals(pp.getName())) {
|
||||
try {
|
||||
parseUsesSdk(pp);
|
||||
} catch (AndrolibException ignored) {}
|
||||
if (hideSdkInfo) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (type == XmlPullParser.END_TAG
|
||||
&& "uses-sdk".equals(pp.getName())) {
|
||||
if (hideSdkInfo) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
super.event(pp);
|
||||
}
|
||||
|
||||
private void parseManifest(XmlPullParser pp)
|
||||
throws AndrolibException {
|
||||
for (int i = 0; i < pp.getAttributeCount(); i++) {
|
||||
String ns = pp.getAttributeNamespace(i);
|
||||
String name = pp.getAttributeName(i);
|
||||
String value = pp.getAttributeValue(i);
|
||||
|
||||
if (value.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ns.isEmpty()) {
|
||||
if (name.equals("package")) {
|
||||
resTable.setPackageRenamed(value);
|
||||
}
|
||||
} else if (ns.equals(AXmlResourceParser.ANDROID_RES_NS)) {
|
||||
switch (name) {
|
||||
case "versionCode":
|
||||
resTable.setVersionCode(value);
|
||||
break;
|
||||
case "versionName":
|
||||
resTable.setVersionName(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseUsesSdk(XmlPullParser pp)
|
||||
throws AndrolibException {
|
||||
for (int i = 0; i < pp.getAttributeCount(); i++) {
|
||||
String ns = pp.getAttributeNamespace(i);
|
||||
String name = pp.getAttributeName(i);
|
||||
String value = pp.getAttributeValue(i);
|
||||
|
||||
if (value.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ns.equals(AXmlResourceParser.ANDROID_RES_NS)) {
|
||||
switch (name) {
|
||||
case "minSdkVersion":
|
||||
case "targetSdkVersion":
|
||||
case "maxSdkVersion":
|
||||
case "compileSdkVersion":
|
||||
resTable.addSdkInfo(name, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
par.setInput(in, null);
|
||||
ser.setOutput(out, null);
|
||||
|
||||
while (par.nextToken() != XmlPullParser.END_DOCUMENT) {
|
||||
ser.event(par);
|
||||
}
|
||||
ser.flush();
|
||||
mParser.setInput(in, null);
|
||||
mSerial.setOutput(out, null);
|
||||
XmlPullUtils.copy(mParser, mSerial, new EventHandler(mParser.getResTable()));
|
||||
} catch (XmlPullParserException ex) {
|
||||
throw new AXmlDecodingException("Could not decode XML", ex);
|
||||
} catch (IOException ex) {
|
||||
@ -143,6 +49,90 @@ public class AndroidManifestPullStreamDecoder implements ResStreamDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
private final AXmlResourceParser mParser;
|
||||
private final ExtXmlSerializer mSerial;
|
||||
private static class EventHandler implements XmlPullUtils.EventHandler {
|
||||
private final ResTable mResTable;
|
||||
private final boolean mHideSdkInfo;
|
||||
|
||||
public EventHandler(ResTable resTable) {
|
||||
mResTable = resTable;
|
||||
mHideSdkInfo = !resTable.getAnalysisMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEvent(XmlPullParser in, XmlSerializer out) throws XmlPullParserException {
|
||||
int type = in.getEventType();
|
||||
|
||||
if (type == XmlPullParser.START_TAG) {
|
||||
String name = in.getName();
|
||||
|
||||
if (name.equals("manifest")) {
|
||||
parseManifest(in);
|
||||
} else if (name.equals("uses-sdk")) {
|
||||
parseUsesSdk(in);
|
||||
|
||||
if (mHideSdkInfo) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (type == XmlPullParser.END_TAG) {
|
||||
String name = in.getName();
|
||||
|
||||
if (name.equals("uses-sdk")) {
|
||||
if (mHideSdkInfo) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void parseManifest(XmlPullParser in) {
|
||||
for (int i = 0; i < in.getAttributeCount(); i++) {
|
||||
String ns = in.getAttributeNamespace(i);
|
||||
String name = in.getAttributeName(i);
|
||||
String value = in.getAttributeValue(i);
|
||||
|
||||
if (value.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (ns.isEmpty()) {
|
||||
if (name.equals("package")) {
|
||||
mResTable.setPackageRenamed(value);
|
||||
}
|
||||
} else if (ns.equals(AXmlResourceParser.ANDROID_RES_NS)) {
|
||||
switch (name) {
|
||||
case "versionCode":
|
||||
mResTable.setVersionCode(value);
|
||||
break;
|
||||
case "versionName":
|
||||
mResTable.setVersionName(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseUsesSdk(XmlPullParser in) {
|
||||
for (int i = 0; i < in.getAttributeCount(); i++) {
|
||||
String ns = in.getAttributeNamespace(i);
|
||||
String name = in.getAttributeName(i);
|
||||
String value = in.getAttributeValue(i);
|
||||
|
||||
if (value.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (ns.equals(AXmlResourceParser.ANDROID_RES_NS)) {
|
||||
switch (name) {
|
||||
case "minSdkVersion":
|
||||
case "targetSdkVersion":
|
||||
case "maxSdkVersion":
|
||||
case "compileSdkVersion":
|
||||
mResTable.addSdkInfo(name, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,45 +19,31 @@ package brut.androlib.res.decoder;
|
||||
import brut.androlib.exceptions.AndrolibException;
|
||||
import brut.androlib.exceptions.AXmlDecodingException;
|
||||
import brut.androlib.exceptions.RawXmlEncounteredException;
|
||||
import brut.androlib.res.util.ExtXmlSerializer;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import brut.xmlpull.XmlPullUtils;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.wrapper.XmlPullParserWrapper;
|
||||
import org.xmlpull.v1.wrapper.XmlPullWrapperFactory;
|
||||
import org.xmlpull.v1.wrapper.XmlSerializerWrapper;
|
||||
import org.xmlpull.v1.wrapper.classic.StaticXmlSerializerWrapper;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ResXmlPullStreamDecoder implements ResStreamDecoder {
|
||||
public ResXmlPullStreamDecoder(AXmlResourceParser parser,
|
||||
ExtXmlSerializer serializer) {
|
||||
this.mParser = parser;
|
||||
this.mSerial = serializer;
|
||||
private final AXmlResourceParser mParser;
|
||||
private final XmlSerializer mSerial;
|
||||
|
||||
public ResXmlPullStreamDecoder(AXmlResourceParser parser, XmlSerializer serial) {
|
||||
mParser = parser;
|
||||
mSerial = serial;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(InputStream in, OutputStream out)
|
||||
throws AndrolibException {
|
||||
public void decode(InputStream in, OutputStream out) throws AndrolibException {
|
||||
try {
|
||||
XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance();
|
||||
XmlPullParserWrapper par = factory.newPullParserWrapper(mParser);
|
||||
XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, factory);
|
||||
|
||||
par.setInput(in, null);
|
||||
ser.setOutput(out, null);
|
||||
|
||||
while (par.nextToken() != XmlPullParser.END_DOCUMENT) {
|
||||
ser.event(par);
|
||||
}
|
||||
ser.flush();
|
||||
mParser.setInput(in, null);
|
||||
mSerial.setOutput(out, null);
|
||||
XmlPullUtils.copy(mParser, mSerial);
|
||||
} catch (XmlPullParserException ex) {
|
||||
throw new AXmlDecodingException("Could not decode XML", ex);
|
||||
} catch (IOException ex) {
|
||||
throw new RawXmlEncounteredException("Could not decode XML", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private final AXmlResourceParser mParser;
|
||||
private final ExtXmlSerializer mSerial;
|
||||
}
|
||||
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package brut.androlib.res.util;
|
||||
|
||||
import org.xmlpull.renamed.MXSerializer;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer {
|
||||
@Override
|
||||
public void startDocument(String encoding, Boolean standalone)
|
||||
throws IOException, IllegalArgumentException, IllegalStateException {
|
||||
super.startDocument(encoding != null ? encoding : mDefaultEncoding, standalone);
|
||||
this.newLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeAttributeValue(String value, Writer out) throws IOException {
|
||||
if (mIsDisabledAttrEscape) {
|
||||
out.write(value == null ? "" : value);
|
||||
return;
|
||||
}
|
||||
super.writeAttributeValue(value, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOutput(OutputStream os, String encoding) throws IOException {
|
||||
super.setOutput(os, encoding != null ? encoding : mDefaultEncoding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(String name) throws IllegalArgumentException {
|
||||
if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
|
||||
return mDefaultEncoding;
|
||||
}
|
||||
return super.getProperty(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(String name, Object value) throws IllegalArgumentException, IllegalStateException {
|
||||
if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
|
||||
mDefaultEncoding = (String) value;
|
||||
} else {
|
||||
super.setProperty(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtXmlSerializer newLine() throws IOException {
|
||||
super.out.write(lineSeparator);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisabledAttrEscape(boolean disabled) {
|
||||
mIsDisabledAttrEscape = disabled;
|
||||
}
|
||||
|
||||
private String mDefaultEncoding;
|
||||
private boolean mIsDisabledAttrEscape = false;
|
||||
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package brut.androlib.res.util;
|
||||
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface ExtXmlSerializer extends XmlSerializer {
|
||||
|
||||
ExtXmlSerializer newLine() throws IOException;
|
||||
|
||||
void setDisabledAttrEscape(boolean disabled);
|
||||
|
||||
String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
|
||||
String PROPERTY_SERIALIZER_LINE_SEPARATOR = "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
|
||||
String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING";
|
||||
}
|
@ -23,12 +23,15 @@ import org.xml.sax.SAXException;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.xpath.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
@ -436,11 +439,19 @@ public final class ResXmlPatcher {
|
||||
private static void saveDocument(File file, Document doc)
|
||||
throws IOException, SAXException, ParserConfigurationException, TransformerException {
|
||||
|
||||
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
DOMSource source = new DOMSource(doc);
|
||||
StreamResult result = new StreamResult(file);
|
||||
transformer.transform(source, result);
|
||||
TransformerFactory factory = TransformerFactory.newInstance();
|
||||
Transformer transformer = factory.newTransformer();
|
||||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||
|
||||
byte[] xmlDecl = "<?xml version=\"1.0\" encoding=\"utf-8\"?>".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] newLine = System.getProperty("line.separator").getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
try (OutputStream output = Files.newOutputStream(file.toPath())) {
|
||||
output.write(xmlDecl);
|
||||
output.write(newLine);
|
||||
transformer.transform(new DOMSource(doc), new StreamResult(output));
|
||||
output.write(newLine);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String ACCESS_EXTERNAL_DTD = "http://javax.xml.XMLConstants/property/accessExternalDTD";
|
||||
|
@ -25,12 +25,16 @@ import brut.directory.Directory;
|
||||
import brut.directory.FileDirectory;
|
||||
import brut.util.OS;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
@ -40,43 +44,24 @@ import java.util.Map;
|
||||
|
||||
public abstract class TestUtils {
|
||||
|
||||
public static Map<String, String> parseStringsXml(File file)
|
||||
throws BrutException {
|
||||
public static Map<String, String> parseStringsXml(File file) throws BrutException {
|
||||
try {
|
||||
XmlPullParser xpp = XmlPullParserFactory.newInstance().newPullParser();
|
||||
xpp.setInput(new FileReader(file));
|
||||
Document doc = getDocumentFromFile(file);
|
||||
XPath xPath = XPathFactory.newInstance().newXPath();
|
||||
String expression = "/resources/string[@name]";
|
||||
NodeList nodes = (NodeList) xPath.evaluate(expression, doc, XPathConstants.NODESET);
|
||||
|
||||
int eventType;
|
||||
String key = null;
|
||||
Map<String, String> map = new HashMap<>();
|
||||
while ((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) {
|
||||
switch (eventType) {
|
||||
case XmlPullParser.START_TAG:
|
||||
if ("string".equals(xpp.getName())) {
|
||||
int attrCount = xpp.getAttributeCount();
|
||||
for (int i = 0; i < attrCount; i++) {
|
||||
if ("name".equals(xpp.getAttributeName(i))) {
|
||||
key = xpp.getAttributeValue(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case XmlPullParser.END_TAG:
|
||||
if ("string".equals(xpp.getName())) {
|
||||
key = null;
|
||||
}
|
||||
break;
|
||||
case XmlPullParser.TEXT:
|
||||
if (key != null) {
|
||||
map.put(key, xpp.getText());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
Node node = nodes.item(i);
|
||||
NamedNodeMap attrs = node.getAttributes();
|
||||
Node nameAttr = attrs.getNamedItem("name");
|
||||
map.put(nameAttr.getNodeValue(), node.getTextContent());
|
||||
}
|
||||
|
||||
return map;
|
||||
} catch (IOException | XmlPullParserException ex) {
|
||||
} catch (XPathExpressionException ex) {
|
||||
throw new BrutException(ex);
|
||||
}
|
||||
}
|
||||
@ -84,7 +69,7 @@ public abstract class TestUtils {
|
||||
public static Document getDocumentFromFile(File file) throws BrutException {
|
||||
try {
|
||||
return ResXmlPatcher.loadDocument(file);
|
||||
} catch (ParserConfigurationException | SAXException | IOException ex) {
|
||||
} catch (IOException | SAXException | ParserConfigurationException ex) {
|
||||
throw new BrutException(ex);
|
||||
}
|
||||
}
|
||||
|
@ -11,5 +11,5 @@
|
||||
<meta-data name="test_int" value="12345" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
|
||||
<permission android:featureFlag="brut.feature.flag" android:label="Test Permission" android:name="brut.permission.TEST" android:permissionGroup="android.permission-group.UNDEFINED" android:protectionLevel="signature"/>
|
||||
<permission android:featureFlag="brut.feature.flag" android:label="Test Permission" android:name="brut.permission.TEST" android:permissionGroup="android.permission-group.UNDEFINED" android:protectionLevel="signature" />
|
||||
</manifest>
|
||||
|
@ -1,5 +1,5 @@
|
||||
dependencies {
|
||||
implementation(project(":brut.j.common"))
|
||||
implementation(project(":brut.j.util"))
|
||||
implementation(libs.commons.io)
|
||||
implementation(project(":brut.j.common"))
|
||||
implementation(project(":brut.j.util"))
|
||||
implementation(libs.commons.io)
|
||||
}
|
||||
|
3
brut.j.xml/build.gradle.kts
Normal file
3
brut.j.xml/build.gradle.kts
Normal file
@ -0,0 +1,3 @@
|
||||
dependencies {
|
||||
api(libs.xmlpull)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
122
brut.j.xml/src/main/java/brut/xmlpull/XmlPullUtils.java
Normal file
122
brut.j.xml/src/main/java/brut/xmlpull/XmlPullUtils.java
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package brut.xmlpull;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class XmlPullUtils {
|
||||
private static final String PROPERTY_XMLDECL_STANDALONE
|
||||
= "http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone";
|
||||
|
||||
public interface EventHandler {
|
||||
boolean onEvent(XmlPullParser in, XmlSerializer out) throws XmlPullParserException;
|
||||
}
|
||||
|
||||
public static void copy(XmlPullParser in, XmlSerializer out)
|
||||
throws XmlPullParserException, IOException {
|
||||
copy(in, out, null);
|
||||
}
|
||||
|
||||
public static void copy(XmlPullParser in, XmlSerializer out, EventHandler handler)
|
||||
throws XmlPullParserException, IOException {
|
||||
Boolean standalone = (Boolean) in.getProperty(PROPERTY_XMLDECL_STANDALONE);
|
||||
|
||||
// Some parsers may have already consumed the event that starts the
|
||||
// document, so we manually emit that event here for consistency
|
||||
if (in.getEventType() == XmlPullParser.START_DOCUMENT) {
|
||||
out.startDocument(in.getInputEncoding(), standalone);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
int event = in.nextToken();
|
||||
if (event == XmlPullParser.START_DOCUMENT) {
|
||||
out.startDocument(in.getInputEncoding(), standalone);
|
||||
continue;
|
||||
}
|
||||
if (event == XmlPullParser.END_DOCUMENT) {
|
||||
out.endDocument();
|
||||
break;
|
||||
}
|
||||
if (handler != null && handler.onEvent(in, out)) {
|
||||
continue;
|
||||
}
|
||||
switch (event) {
|
||||
case XmlPullParser.START_TAG:
|
||||
if (!in.getFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES)) {
|
||||
int nsStart = in.getNamespaceCount(in.getDepth() - 1);
|
||||
int nsEnd = in.getNamespaceCount(in.getDepth());
|
||||
for (int i = nsStart; i < nsEnd; i++) {
|
||||
String prefix = in.getNamespacePrefix(i);
|
||||
String ns = in.getNamespaceUri(i);
|
||||
out.setPrefix(prefix, ns);
|
||||
}
|
||||
}
|
||||
out.startTag(normalizeNamespace(in.getNamespace()), in.getName());
|
||||
for (int i = 0; i < in.getAttributeCount(); i++) {
|
||||
String ns = normalizeNamespace(in.getAttributeNamespace(i));
|
||||
String name = in.getAttributeName(i);
|
||||
String value = in.getAttributeValue(i);
|
||||
out.attribute(ns, name, value);
|
||||
}
|
||||
break;
|
||||
case XmlPullParser.END_TAG:
|
||||
out.endTag(normalizeNamespace(in.getNamespace()), in.getName());
|
||||
break;
|
||||
case XmlPullParser.TEXT:
|
||||
out.text(in.getText());
|
||||
break;
|
||||
case XmlPullParser.CDSECT:
|
||||
out.cdsect(in.getText());
|
||||
break;
|
||||
case XmlPullParser.ENTITY_REF:
|
||||
out.entityRef(in.getName());
|
||||
break;
|
||||
case XmlPullParser.IGNORABLE_WHITESPACE:
|
||||
out.ignorableWhitespace(in.getText());
|
||||
break;
|
||||
case XmlPullParser.PROCESSING_INSTRUCTION:
|
||||
out.processingInstruction(in.getText());
|
||||
break;
|
||||
case XmlPullParser.COMMENT:
|
||||
out.comment(in.getText());
|
||||
break;
|
||||
case XmlPullParser.DOCDECL:
|
||||
out.docdecl(in.getText());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown event: " + event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some parsers may return an empty string when a namespace in unsupported,
|
||||
* which can confuse serializers. This method normalizes empty strings to
|
||||
* be null.
|
||||
*/
|
||||
private static String normalizeNamespace(String namespace) {
|
||||
if (namespace == null || namespace.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return namespace;
|
||||
}
|
||||
}
|
||||
}
|
@ -83,7 +83,7 @@ subprojects {
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
val mavenProjects = arrayOf("apktool-lib", "apktool-cli", "brut.j.common", "brut.j.util", "brut.j.dir")
|
||||
val mavenProjects = arrayOf("brut.j.common", "brut.j.util", "brut.j.dir", "brut.j.xml", "apktool-lib", "apktool-cli")
|
||||
|
||||
if (project.name in mavenProjects) {
|
||||
apply(plugin = "maven-publish")
|
||||
|
@ -8,7 +8,7 @@ guava = "33.3.1-jre"
|
||||
junit = "4.13.2"
|
||||
r8 = "8.5.35"
|
||||
smali = "3.0.8"
|
||||
xmlpull = "1.1.6"
|
||||
xmlpull = "1.1.3.1"
|
||||
xmlunit = "2.10.0"
|
||||
|
||||
[libraries]
|
||||
@ -21,5 +21,5 @@ guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||
junit = { module = "junit:junit", version.ref = "junit" }
|
||||
r8 = { module = "com.android.tools:r8", version.ref = "r8" }
|
||||
smali = { module = "com.android.tools.smali:smali", version.ref = "smali" }
|
||||
xmlpull = { module = "org.ogce:xpp3", version.ref = "xmlpull" }
|
||||
xmlpull = { module = "xmlpull:xmlpull", version.ref = "xmlpull" }
|
||||
xmlunit = { module = "org.xmlunit:xmlunit-legacy", version.ref = "xmlunit" }
|
||||
|
@ -1,5 +1,5 @@
|
||||
rootProject.name = "apktool-cli"
|
||||
include("brut.j.common", "brut.j.util", "brut.j.dir", "brut.apktool:apktool-lib", "brut.apktool:apktool-cli")
|
||||
include("brut.j.common", "brut.j.util", "brut.j.dir", "brut.j.xml", "brut.apktool:apktool-lib", "brut.apktool:apktool-cli")
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
|
Loading…
Reference in New Issue
Block a user