create xml res encoder

This commit is contained in:
REAndroid 2023-01-02 11:42:52 -05:00
parent 0f29fa6596
commit 9f7b2a7e7f
16 changed files with 422 additions and 13 deletions

View File

@ -16,10 +16,36 @@
package com.reandroid.lib.apk.xmlencoder;
import java.io.File;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EncodeUtil {
public static void sortValuesXml(List<File> fileList){
Comparator<File> cmp=new Comparator<File>() {
@Override
public int compare(File f1, File f2) {
String n1=getValuesXmlCompare(f1);
String n2=getValuesXmlCompare(f2);
return n1.compareTo(n2);
}
};
fileList.sort(cmp);
}
private static String getValuesXmlCompare(File file){
String name=file.getName().toLowerCase();
if(name.equals("public.xml")){
return "0";
}
if(name.equals("ids.xml")){
return "1";
}
if(name.contains("attr")){
return "2";
}
return "3 "+name;
}
public static boolean isEmpty(String text){
if(text==null){
return true;

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* 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
*
* http://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 com.reandroid.lib.apk.xmlencoder;
import com.reandroid.lib.apk.ResourceIds;
import com.reandroid.lib.arsc.chunk.PackageBlock;
import com.reandroid.lib.arsc.chunk.TableBlock;
import com.reandroid.lib.arsc.pool.TypeStringPool;
import java.util.ArrayList;
import java.util.List;
class PackageCreator {
private List<String> mSpecNames;
private String mPackageName;
private int mPackageId;
public PackageCreator(){
}
public void setPackageName(String name){
this.mPackageName=name;
}
public PackageBlock createNew(TableBlock tableBlock, ResourceIds.Table.Package pkgResourceIds){
loadNames(pkgResourceIds);
PackageBlock packageBlock=new PackageBlock();
packageBlock.setName(mPackageName);
packageBlock.setId(mPackageId);
tableBlock.getPackageArray()
.add(packageBlock);
packageBlock.getSpecStringPool()
.addStrings(mSpecNames);
initTypeStringPool(packageBlock, pkgResourceIds);
return packageBlock;
}
private void initTypeStringPool(PackageBlock packageBlock,
ResourceIds.Table.Package pkgResourceIds){
TypeStringPool typeStringPool = packageBlock.getTypeStringPool();
for(ResourceIds.Table.Package.Type type:pkgResourceIds.listTypes()){
typeStringPool.getOrCreate(type.getIdInt(), type.getName());
}
}
private void loadNames(ResourceIds.Table.Package pkg){
this.mSpecNames = new ArrayList<>();
if(pkg.name!=null){
this.mPackageName=pkg.name;
}
if(this.mPackageName==null){
this.mPackageName = EncodeUtil.NULL_PACKAGE_NAME;
}
this.mPackageId=pkg.getIdInt();
for(ResourceIds.Table.Package.Type.Entry entry:pkg.listEntries()){
mSpecNames.add(entry.getName());
}
}
}

View File

@ -0,0 +1,170 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* 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
*
* http://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 com.reandroid.lib.apk.xmlencoder;
import com.reandroid.lib.apk.APKLogger;
import com.reandroid.lib.apk.ApkUtil;
import com.reandroid.lib.apk.ResourceIds;
import com.reandroid.lib.arsc.chunk.PackageBlock;
import com.reandroid.lib.arsc.chunk.TableBlock;
import com.reandroid.lib.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.lib.common.Frameworks;
import com.reandroid.xml.XMLDocument;
import com.reandroid.xml.XMLException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class RESEncoder {
private APKLogger apkLogger;
private final TableBlock tableBlock;
private final Set<File> parsedFiles;
public RESEncoder(){
this.tableBlock = new TableBlock();
this.parsedFiles = new HashSet<>();
}
public TableBlock getTableBlock(){
return tableBlock;
}
public void scanDirectory(File rootDir) throws IOException, XMLException {
List<File> pubXmlFileList = searchPublicXmlFiles(rootDir);
if(pubXmlFileList.size()==0){
throw new IOException("No .*/values/"
+ApkUtil.FILE_NAME_PUBLIC_XML+" file found in '"+rootDir);
}
for(File pubXmlFile:pubXmlFileList){
EncodeMaterials encodeMaterials = loadPublicXml(pubXmlFile);
addParsedFiles(pubXmlFile);
File resDir=toResDirectory(pubXmlFile);
encodeResDir(encodeMaterials, resDir);
}
tableBlock.refresh();
}
private EncodeMaterials loadPublicXml(File pubXmlFile) throws IOException, XMLException {
ResourceIds resourceIds=new ResourceIds();
resourceIds.fromXml(pubXmlFile);
List<ResourceIds.Table.Package> pkgList = resourceIds
.getTable().listPackages();
if(pkgList.size()!=1){
throw new IOException("Package count should be 1, count="
+pkgList.size()+", in file: "+pubXmlFile);
}
XMLDocument manifestDocument = XMLDocument.load(toAndroidManifest(pubXmlFile));
String packageName = manifestDocument
.getDocumentElement().getAttributeValue("package");
ResourceIds.Table.Package pkgResourceIds = pkgList.get(0);
PackageCreator packageCreator = new PackageCreator();
packageCreator.setPackageName(packageName);
PackageBlock packageBlock = packageCreator.createNew(tableBlock, pkgResourceIds);
return new EncodeMaterials()
.addFramework(Frameworks.getAndroid())
.setCurrentPackage(packageBlock)
.setPackageIds(pkgResourceIds)
.setAPKLogger(apkLogger);
}
private void encodeResDir(EncodeMaterials materials, File resDir) throws XMLException {
List<File> valuesDirList = listValuesDir(resDir);
for(File valuesDir:valuesDirList){
encodeValuesDir(materials, valuesDir);
}
}
private void encodeValuesDir(EncodeMaterials materials, File valuesDir) throws XMLException {
ValuesEncoder valuesEncoder = new ValuesEncoder(materials);
List<File> xmlFiles = ApkUtil.listFiles(valuesDir, ".xml");
EncodeUtil.sortValuesXml(xmlFiles);
for(File file:xmlFiles){
if(isAlreadyParsed(file)){
continue;
}
addParsedFiles(file);
valuesEncoder.encodeValuesXml(file);
}
}
private File toAndroidManifest(File pubXmlFile){
File resDirectory = toResDirectory(pubXmlFile);
File packageDirectory = resDirectory.getParentFile();
File root = packageDirectory.getParentFile();
return new File(root, AndroidManifestBlock.FILE_NAME);
}
private File toResDirectory(File pubXmlFile){
return pubXmlFile
.getParentFile()
.getParentFile();
}
private List<File> listValuesDir(File resDir){
List<File> results=new ArrayList<>();
File def=new File(resDir, "values");
results.add(def);
File[] dirList=resDir.listFiles();
if(dirList!=null){
for(File dir:dirList){
if(def.equals(dir) || !dir.isDirectory()){
continue;
}
if(dir.getName().startsWith("values-")){
results.add(dir);
}
}
}
return results;
}
private List<File> searchPublicXmlFiles(File rootDir){
logVerbose("Searching public.xml: "+rootDir);
List<File> xmlFiles = ApkUtil.recursiveFiles(rootDir, ApkUtil.FILE_NAME_PUBLIC_XML);
List<File> results = new ArrayList<>();
for(File file:xmlFiles){
if(toAndroidManifest(file).isFile()){
results.add(file);
}
}
return results;
}
private boolean isAlreadyParsed(File file){
return parsedFiles.contains(file);
}
private void addParsedFiles(File file){
parsedFiles.add(file);
}
public void setAPKLogger(APKLogger logger) {
this.apkLogger = logger;
}
private void logMessage(String msg) {
if(apkLogger!=null){
apkLogger.logMessage(msg);
}
}
private void logError(String msg, Throwable tr) {
if(apkLogger!=null){
apkLogger.logError(msg, tr);
}
}
private void logVerbose(String msg) {
if(apkLogger!=null){
apkLogger.logVerbose(msg);
}
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* 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
*
* http://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 com.reandroid.lib.apk.xmlencoder;
import com.reandroid.xml.XMLDocument;
import com.reandroid.xml.XMLElement;
import com.reandroid.xml.XMLException;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
class ValuesEncoder {
private final EncodeMaterials materials;
private final Map<String, XMLValuesEncoder> xmlEncodersMap;
private final Map<String, XMLValuesEncoderBag> xmlBagEncodersMap;
private final XMLValuesEncoderCommon commonEncoder;
private final XMLValuesEncoderBag bagCommonEncoder;
public ValuesEncoder(EncodeMaterials materials){
this.materials=materials;
Map<String, XMLValuesEncoder> map = new HashMap<>();
map.put("id", new XMLValuesEncoderId(materials));
map.put("string", new XMLValuesEncoderString(materials));
XMLValuesEncoderDimen encoderDimen=new XMLValuesEncoderDimen(materials);
map.put("dimen", encoderDimen);
map.put("fraction", encoderDimen);
map.put("color", new XMLValuesEncoderColor(materials));
map.put("integer", new XMLValuesEncoderInteger(materials));
this.xmlEncodersMap=map;
this.commonEncoder=new XMLValuesEncoderCommon(materials);
Map<String, XMLValuesEncoderBag> mapBag=new HashMap<>();
mapBag.put("attr", new XMLValuesEncoderAttr(materials));
mapBag.put("plurals", new XMLValuesEncoderPlurals(materials));
mapBag.put("array", new XMLValuesEncoderArray(materials));
mapBag.put("style", new XMLValuesEncoderStyle(materials));
this.xmlBagEncodersMap=mapBag;
this.bagCommonEncoder=new XMLValuesEncoderBag(materials);
}
public void encodeValuesXml(File valuesXmlFile) throws XMLException {
if(valuesXmlFile.getName().equals("public.xml")){
return;
}
String simpleName = valuesXmlFile.getParentFile().getName()
+File.separator+valuesXmlFile.getName();
materials.logMessage("Encoding: "+simpleName);
String type = EncodeUtil.getTypeNameFromValuesXml(valuesXmlFile);
String qualifiers = EncodeUtil.getQualifiersFromValuesXml(valuesXmlFile);
XMLDocument xmlDocument = XMLDocument.load(valuesXmlFile);
encodeValuesXml(type, qualifiers, xmlDocument);
}
private void encodeValuesXml(String type, String qualifiers, XMLDocument xmlDocument) {
type=getType(xmlDocument, type);
XMLValuesEncoder encoder;
if(isBag(xmlDocument, type)){
encoder=getBagEncoder(type);
}else{
encoder=getEncoder(type);
}
encoder.encode(type, qualifiers, xmlDocument);
}
private boolean isBag(XMLDocument xmlDocument, String type){
if(type.startsWith("attr")){
return true;
}
if(type.startsWith("style")){
return true;
}
if(type.startsWith("plurals")){
return true;
}
if(type.startsWith("string")){
return false;
}
XMLElement documentElement=xmlDocument.getDocumentElement();
int count=documentElement.getChildesCount();
for(int i=0;i<count;i++){
XMLElement element=documentElement.getChildAt(0);
if(element.getChildesCount()>0){
return true;
}
}
return false;
}
private String getType(XMLDocument xmlDocument, String def){
XMLElement documentElement=xmlDocument.getDocumentElement();
if(documentElement.getChildesCount()==0){
return def;
}
XMLElement first=documentElement.getChildAt(0);
String type=first.getAttributeValue("type");
if(type==null){
type=first.getTagName();
}
if(type==null||type.equals("item")){
return def;
}
return type;
}
private XMLValuesEncoder getEncoder(String type){
type=EncodeUtil.sanitizeType(type);
XMLValuesEncoder encoder=xmlEncodersMap.get(type);
if(encoder!=null){
return encoder;
}
return commonEncoder;
}
private XMLValuesEncoderBag getBagEncoder(String type){
type=EncodeUtil.sanitizeType(type);
XMLValuesEncoderBag encoder=xmlBagEncodersMap.get(type);
if(encoder!=null){
return encoder;
}
return bagCommonEncoder;
}
}

View File

@ -25,7 +25,7 @@ import com.reandroid.lib.arsc.value.ValueType;
import com.reandroid.xml.XMLDocument;
import com.reandroid.xml.XMLElement;
public class XMLValuesEncoder {
class XMLValuesEncoder {
private final EncodeMaterials materials;
XMLValuesEncoder(EncodeMaterials materials){
this.materials=materials;

View File

@ -22,7 +22,7 @@ import com.reandroid.lib.arsc.value.ResValueBagItem;
import com.reandroid.lib.arsc.value.ValueType;
import com.reandroid.xml.XMLElement;
public class XMLValuesEncoderArray extends XMLValuesEncoderBag{
class XMLValuesEncoderArray extends XMLValuesEncoderBag{
XMLValuesEncoderArray(EncodeMaterials materials) {
super(materials);
}

View File

@ -26,7 +26,7 @@ import com.reandroid.lib.arsc.value.attribute.AttributeValueType;
import com.reandroid.xml.XMLAttribute;
import com.reandroid.xml.XMLElement;
public class XMLValuesEncoderAttr extends XMLValuesEncoderBag{
class XMLValuesEncoderAttr extends XMLValuesEncoderBag{
XMLValuesEncoderAttr(EncodeMaterials materials) {
super(materials);
}

View File

@ -19,7 +19,7 @@ import com.reandroid.lib.arsc.value.EntryBlock;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.xml.XMLElement;
public class XMLValuesEncoderBag extends XMLValuesEncoder{
class XMLValuesEncoderBag extends XMLValuesEncoder{
XMLValuesEncoderBag(EncodeMaterials materials) {
super(materials);
}

View File

@ -18,7 +18,7 @@ package com.reandroid.lib.apk.xmlencoder;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.EntryBlock;
public class XMLValuesEncoderColor extends XMLValuesEncoder{
class XMLValuesEncoderColor extends XMLValuesEncoder{
XMLValuesEncoderColor(EncodeMaterials materials) {
super(materials);
}

View File

@ -15,14 +15,24 @@
*/
package com.reandroid.lib.apk.xmlencoder;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.EntryBlock;
public class XMLValuesEncoderCommon extends XMLValuesEncoder{
class XMLValuesEncoderCommon extends XMLValuesEncoder{
XMLValuesEncoderCommon(EncodeMaterials materials) {
super(materials);
}
@Override
void encodeStringValue(EntryBlock entryBlock, String value){
entryBlock.setValueAsString(value);
if(ValueDecoder.isReference(value)){
entryBlock.setValueAsReference(getMaterials().resolveReference(value));
}else {
ValueDecoder.EncodeResult encodeResult=ValueDecoder.encodeGuessAny(value);
if(encodeResult!=null){
entryBlock.setValueAsRaw(encodeResult.valueType, encodeResult.value);
}else {
entryBlock.setValueAsString(value);
}
}
}
}

View File

@ -18,7 +18,7 @@ package com.reandroid.lib.apk.xmlencoder;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.EntryBlock;
public class XMLValuesEncoderDimen extends XMLValuesEncoder{
class XMLValuesEncoderDimen extends XMLValuesEncoder{
XMLValuesEncoderDimen(EncodeMaterials materials) {
super(materials);
}

View File

@ -17,7 +17,7 @@ package com.reandroid.lib.apk.xmlencoder;
import com.reandroid.lib.arsc.value.EntryBlock;
public class XMLValuesEncoderId extends XMLValuesEncoder{
class XMLValuesEncoderId extends XMLValuesEncoder{
public XMLValuesEncoderId(EncodeMaterials materials) {
super(materials);
}

View File

@ -19,7 +19,7 @@ import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.EntryBlock;
import com.reandroid.lib.arsc.value.ValueType;
public class XMLValuesEncoderInteger extends XMLValuesEncoder{
class XMLValuesEncoderInteger extends XMLValuesEncoder{
XMLValuesEncoderInteger(EncodeMaterials materials) {
super(materials);
}

View File

@ -23,7 +23,7 @@ import com.reandroid.lib.arsc.value.ValueType;
import com.reandroid.lib.arsc.value.plurals.PluralsQuantity;
import com.reandroid.xml.XMLElement;
public class XMLValuesEncoderPlurals extends XMLValuesEncoderBag{
class XMLValuesEncoderPlurals extends XMLValuesEncoderBag{
XMLValuesEncoderPlurals(EncodeMaterials materials) {
super(materials);
}

View File

@ -23,7 +23,7 @@ import com.reandroid.xml.XMLElement;
import java.util.ArrayList;
import java.util.List;
public class XMLValuesEncoderString extends XMLValuesEncoder{
class XMLValuesEncoderString extends XMLValuesEncoder{
XMLValuesEncoderString(EncodeMaterials materials) {
super(materials);
}

View File

@ -25,7 +25,7 @@ import com.reandroid.lib.arsc.value.attribute.AttributeBag;
import com.reandroid.xml.XMLElement;
public class XMLValuesEncoderStyle extends XMLValuesEncoderBag{
class XMLValuesEncoderStyle extends XMLValuesEncoderBag{
XMLValuesEncoderStyle(EncodeMaterials materials) {
super(materials);
}