create XMLEncoder

This commit is contained in:
REAndroid 2023-01-02 10:06:04 -05:00
parent a7424bbf96
commit 0f29fa6596
15 changed files with 1120 additions and 0 deletions

View File

@ -0,0 +1,25 @@
/*
* 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;
public class EncodeException extends IllegalArgumentException{
public EncodeException(String message){
super(message);
}
public EncodeException(String message, Throwable cause){
super(message, cause);
}
}

View File

@ -0,0 +1,317 @@
/*
* 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.ResourceIds;
import com.reandroid.lib.arsc.chunk.PackageBlock;
import com.reandroid.lib.arsc.chunk.TypeBlock;
import com.reandroid.lib.arsc.container.SpecTypePair;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.group.EntryGroup;
import com.reandroid.lib.arsc.item.SpecString;
import com.reandroid.lib.arsc.util.FrameworkTable;
import com.reandroid.lib.arsc.value.EntryBlock;
import com.reandroid.lib.common.ResourceResolver;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
public class EncodeMaterials implements ResourceResolver {
private ResourceIds.Table.Package packageIds;
private PackageBlock currentPackage;
private final Set<FrameworkTable> frameworkTables = new HashSet<>();
private APKLogger apkLogger;
public EncodeMaterials(){
}
public SpecString getSpecString(String name){
return currentPackage.getSpecStringPool()
.get(name)
.get(0);
}
public void addTableStringPool(Collection<String> stringList){
getCurrentPackage()
.getTableBlock()
.getTableStringPool()
.addStrings(stringList);
}
public int resolveAttributeNameReference(String refString){
String packageName = null;
String type = "attr";
String name = refString;
int i=refString.lastIndexOf(':');
if(i>=0){
packageName=refString.substring(0, i);
name=refString.substring(i+1);
}
if(EncodeUtil.isEmpty(packageName)
|| packageName.equals(getCurrentPackageName())
|| !isFrameworkPackageName(packageName)){
return resolveLocalResourceId(type, name);
}
return resolveFrameworkResourceId(packageName, type, name);
}
public EntryBlock getAttributeBlock(String refString){
String packageName = null;
String type = "attr";
String name = refString;
int i=refString.lastIndexOf(':');
if(i>=0){
packageName=refString.substring(0, i);
name=refString.substring(i+1);
}
if(EncodeUtil.isEmpty(packageName)
|| packageName.equals(getCurrentPackageName())
|| !isFrameworkPackageName(packageName)){
return getLocalEntryBlock(type, name);
}
return getFrameworkEntry(type, name);
}
public int resolveReference(String refString){
if("@null".equals(refString)){
return 0;
}
Matcher matcher = ValueDecoder.PATTERN_REFERENCE.matcher(refString);
if(!matcher.find()){
throw new EncodeException(
"Not proper reference string: '"+refString+"'");
}
String prefix=matcher.group(1);
String packageName = matcher.group(2);
if(packageName!=null && packageName.endsWith(":")){
packageName=packageName.substring(0, packageName.length()-1);
}
String type = matcher.group(4);
String name = matcher.group(5);
if(EncodeUtil.isEmpty(packageName)
|| packageName.equals(getCurrentPackageName())
|| !isFrameworkPackageName(packageName)){
return resolveLocalResourceId(type, name);
}
return resolveFrameworkResourceId(packageName, type, name);
}
public int resolveLocalResourceId(String type, String name){
ResourceIds.Table.Package.Type.Entry entry =
this.packageIds.getEntry(type, name);
if(entry!=null){
return entry.getResourceId();
}
EntryGroup entryGroup=getLocalEntryGroup(type, name);
if(entryGroup!=null){
return entryGroup.getResourceId();
}
throw new EncodeException("Local entry not found: " +
"type="+type+
", name="+name);
}
public int resolveFrameworkResourceId(String type, String name){
EntryBlock entryBlock = getFrameworkEntry(type, name);
if(entryBlock!=null){
return entryBlock.getResourceId();
}
throw new EncodeException("Framework entry not found: " +
"type="+type+
", name="+name);
}
public int resolveFrameworkResourceId(String packageName, String type, String name){
EntryBlock entryBlock = getFrameworkEntry(packageName, type, name);
if(entryBlock!=null){
return entryBlock.getResourceId();
}
throw new EncodeException("Framework entry not found: " +
"package="+packageName+
", type="+type+
", name="+name);
}
public int resolveFrameworkResourceId(int packageId, String type, String name){
EntryBlock entryBlock = getFrameworkEntry(packageId, type, name);
if(entryBlock!=null){
return entryBlock.getResourceId();
}
throw new EncodeException("Framework entry not found: " +
"packageId="+String.format("0x%02x", packageId)+
", type="+type+
", name="+name);
}
public EntryGroup getLocalEntryGroup(String type, String name){
for(EntryGroup entryGroup : currentPackage.listEntryGroup()){
if(type.equals(entryGroup.getTypeName()) &&
name.equals(entryGroup.getSpecName())){
return entryGroup;
}
}
for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){
if(packageBlock==currentPackage ||
packageBlock.getId()!=currentPackage.getId()){
continue;
}
for(EntryGroup entryGroup : currentPackage.listEntryGroup()){
if(type.equals(entryGroup.getTypeName()) &&
name.equals(entryGroup.getSpecName())){
return entryGroup;
}
}
}
return null;
}
public EntryBlock getLocalEntryBlock(String type, String name){
for(EntryGroup entryGroup : currentPackage.listEntryGroup()){
if(type.equals(entryGroup.getTypeName()) &&
name.equals(entryGroup.getSpecName())){
return entryGroup.pickOne();
}
}
SpecTypePair specTypePair=currentPackage.searchByTypeName(type);
if(specTypePair!=null){
for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){
for(EntryBlock entryBlock:typeBlock.listEntries()){
if(name.equals(entryBlock.getName())){
return entryBlock;
}
}
break;
}
}
for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){
if(packageBlock==currentPackage ||
packageBlock.getId()!=currentPackage.getId()){
continue;
}
specTypePair=packageBlock.searchByTypeName(type);
if(specTypePair!=null){
for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){
for(EntryBlock entryBlock:typeBlock.listEntries()){
if(name.equals(entryBlock.getName())){
return entryBlock;
}
}
break;
}
}
}
return null;
}
public EntryBlock getFrameworkEntry(String type, String name){
for(FrameworkTable table:frameworkTables){
EntryBlock entryBlock = table.searchEntryBlock(type, name);
if(entryBlock!=null){
return entryBlock;
}
}
return null;
}
private boolean isFrameworkPackageName(String packageName){
for(FrameworkTable table:frameworkTables){
for(PackageBlock packageBlock:table.listPackages()){
if(packageName.equals(packageBlock.getName())){
return true;
}
}
}
return false;
}
public EntryBlock getFrameworkEntry(String packageName, String type, String name){
for(FrameworkTable table:frameworkTables){
for(PackageBlock packageBlock:table.listPackages()){
if(packageName.equals(packageBlock.getName())){
EntryBlock entryBlock = table.searchEntryBlock(type, name);
if(entryBlock!=null){
return entryBlock;
}
}
}
}
return null;
}
public EntryBlock getFrameworkEntry(int packageId, String type, String name){
for(FrameworkTable table:frameworkTables){
for(PackageBlock packageBlock:table.listPackages()){
if(packageId==packageBlock.getId()){
EntryBlock entryBlock = table.searchEntryBlock(type, name);
if(entryBlock!=null){
return entryBlock;
}
}
}
}
return null;
}
public EncodeMaterials setPackageIds(ResourceIds.Table.Package packageIds) {
this.packageIds = packageIds;
return this;
}
public EncodeMaterials setCurrentPackage(PackageBlock currentPackage) {
this.currentPackage = currentPackage;
return this;
}
public EncodeMaterials addFramework(FrameworkTable frameworkTable) {
frameworkTable.loadResourceNameMap();
this.frameworkTables.add(frameworkTable);
return this;
}
public EncodeMaterials setAPKLogger(APKLogger logger) {
this.apkLogger = logger;
return this;
}
public ResourceIds.Table.Package getPackageIds() {
return packageIds;
}
public PackageBlock getCurrentPackage() {
return currentPackage;
}
public String getCurrentPackageName(){
return currentPackage.getName();
}
public int getCurrentPackageId(){
return currentPackage.getId();
}
@Override
public int resolveResourceId(String packageName, String type, String name) {
if(packageName==null || packageName.equals(getCurrentPackageName())){
return resolveLocalResourceId(type, name);
}
return resolveFrameworkResourceId(packageName, type, name);
}
@Override
public int resolveResourceId(int packageId, String type, String name) {
if(packageId==getCurrentPackageId()){
return resolveLocalResourceId(type, name);
}
return resolveFrameworkResourceId(packageId, type, name);
}
public void logMessage(String msg) {
if(apkLogger!=null){
apkLogger.logMessage(msg);
}
}
public void logError(String msg, Throwable tr) {
if(apkLogger!=null){
apkLogger.logError(msg, tr);
}
}
public void logVerbose(String msg) {
if(apkLogger!=null){
apkLogger.logVerbose(msg);
}
}
}

View File

@ -0,0 +1,55 @@
/*
* 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 java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EncodeUtil {
public static boolean isEmpty(String text){
if(text==null){
return true;
}
text=text.trim();
return text.length()==0;
}
public static String getQualifiersFromValuesXml(File valuesXml){
String dirName=valuesXml.getParentFile().getName();
int i=dirName.indexOf('-');
if(i>0){
return dirName.substring(i);
}
return "";
}
public static String getTypeNameFromValuesXml(File valuesXml){
String name=valuesXml.getName();
name=name.substring(0, name.length()-4);
if(!name.equals("plurals") && name.endsWith("s")){
name=name.substring(0, name.length()-1);
}
return name;
}
public static String sanitizeType(String type){
Matcher matcher=PATTERN_TYPE.matcher(type);
if(!matcher.find()){
return "";
}
return matcher.group(1);
}
public static final String NULL_PACKAGE_NAME = "NULL_PACKAGE_NAME";
private static final Pattern PATTERN_TYPE=Pattern.compile("^([a-z]+)[^a-z]*.*$");
}

View File

@ -0,0 +1,126 @@
/*
* 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.arsc.chunk.PackageBlock;
import com.reandroid.lib.arsc.chunk.TypeBlock;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.item.SpecString;
import com.reandroid.lib.arsc.pool.TypeStringPool;
import com.reandroid.lib.arsc.value.EntryBlock;
import com.reandroid.lib.arsc.value.ValueType;
import com.reandroid.xml.XMLDocument;
import com.reandroid.xml.XMLElement;
public class XMLValuesEncoder {
private final EncodeMaterials materials;
XMLValuesEncoder(EncodeMaterials materials){
this.materials=materials;
}
public void encode(String type, String qualifiers, XMLDocument xmlDocument){
XMLElement documentElement = xmlDocument.getDocumentElement();
TypeBlock typeBlock = getTypeBlock(type, qualifiers);
int count = documentElement.getChildesCount();
typeBlock.getEntryBlockArray().ensureSize(count);
for(int i=0;i<count;i++){
XMLElement element = documentElement.getChildAt(i);
encode(typeBlock, element);
}
}
private void encode(TypeBlock typeBlock, XMLElement element){
String name = element.getAttributeValue("name");
int resourceId = getMaterials()
.resolveLocalResourceId(typeBlock.getTypeName(), name);
EntryBlock entryBlock = typeBlock
.getOrCreateEntry((short) (0xffff & resourceId));
encodeValue(entryBlock, element);
if(!entryBlock.isNull()){
SpecString specString = getMaterials().getSpecString(name);
entryBlock.setSpecReference(specString);
}
}
void encodeValue(EntryBlock entryBlock, XMLElement element){
String value = getValue(element);
encodeValue(entryBlock, value);
}
void encodeValue(EntryBlock entryBlock, String value){
if(EncodeUtil.isEmpty(value)){
encodeNullValue(entryBlock);
}else if(isLiteralEmpty(value)){
encodeLiteralEmptyValue(entryBlock, value);
}else if(isBoolean(value)){
encodeBooleanValue(entryBlock, value);
}else if(ValueDecoder.isReference(value)){
encodeReferenceValue(entryBlock, value);
}else {
encodeStringValue(entryBlock, value);
}
}
void encodeNullValue(EntryBlock entryBlock){
// Nothing to do
}
void encodeLiteralEmptyValue(EntryBlock entryBlock, String value){
entryBlock.setValueAsRaw(ValueType.NULL, 0);
}
void encodeBooleanValue(EntryBlock entryBlock, String value){
entryBlock.setValueAsBoolean("true".equals(value.toLowerCase()));
}
void encodeReferenceValue(EntryBlock entryBlock, String value){
int resourceId = getMaterials().resolveReference(value);
entryBlock.setValueAsReference(resourceId);
}
void encodeStringValue(EntryBlock entryBlock, String value){
}
private TypeBlock getTypeBlock(String type, String qualifiers){
PackageBlock packageBlock = getMaterials().getCurrentPackage();
TypeStringPool typeStringPool = packageBlock.getTypeStringPool();
byte typeId = typeStringPool.idOf(type);
return packageBlock.getSpecTypePairArray()
.getOrCreateTypeBlock(typeId, qualifiers);
}
EncodeMaterials getMaterials() {
return materials;
}
static String getValue(XMLElement element){
String value=element.getTextContent();
if(value!=null){
return value;
}
return element.getAttributeValue("value");
}
static boolean isLiteralEmpty(String value){
if(value==null){
return false;
}
value=value.trim().toLowerCase();
return value.equals("@empty");
}
static boolean isBoolean(String value){
if(value==null){
return false;
}
value=value.trim().toLowerCase();
return value.equals("true")||value.equals("false");
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.arsc.array.ResValueBagItemArray;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.lib.arsc.value.ResValueBagItem;
import com.reandroid.lib.arsc.value.ValueType;
import com.reandroid.xml.XMLElement;
public class XMLValuesEncoderArray extends XMLValuesEncoderBag{
XMLValuesEncoderArray(EncodeMaterials materials) {
super(materials);
}
@Override
void encodeChildes(XMLElement parentElement, ResValueBag resValueBag){
int count = parentElement.getChildesCount();
boolean tag_string="string-array".equals(parentElement.getTagName());
ResValueBagItemArray itemArray = resValueBag.getResValueBagItemArray();
for(int i=0;i<count;i++){
XMLElement child=parentElement.getChildAt(i);
ResValueBagItem bagItem = itemArray.get(i);
bagItem.setIdHigh((short) 0x0100);
bagItem.setIdLow((short) (i+1));
String valueText=child.getTextContent();
if(ValueDecoder.isReference(valueText)){
bagItem.setType(ValueType.REFERENCE);
bagItem.setData(getMaterials().resolveReference(valueText));
}else if(EncodeUtil.isEmpty(valueText)) {
bagItem.setType(ValueType.NULL);
bagItem.setData(0);
}else if(!tag_string){
ValueDecoder.EncodeResult encodeResult =
ValueDecoder.encodeGuessAny(valueText);
if(encodeResult!=null){
bagItem.setType(encodeResult.valueType);
bagItem.setData(encodeResult.value);
}else {
bagItem.setValueAsString(valueText);
}
}else {
bagItem.setValueAsString(valueText);
}
}
}
}

View File

@ -0,0 +1,120 @@
/*
* 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.arsc.array.ResValueBagItemArray;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.lib.arsc.value.ResValueBagItem;
import com.reandroid.lib.arsc.value.ValueType;
import com.reandroid.lib.arsc.value.attribute.AttributeBag;
import com.reandroid.lib.arsc.value.attribute.AttributeItemType;
import com.reandroid.lib.arsc.value.attribute.AttributeValueType;
import com.reandroid.xml.XMLAttribute;
import com.reandroid.xml.XMLElement;
public class XMLValuesEncoderAttr extends XMLValuesEncoderBag{
XMLValuesEncoderAttr(EncodeMaterials materials) {
super(materials);
}
@Override
int getChildesCount(XMLElement element){
int count = element.getChildesCount() + element.getAttributeCount();
if(element.getAttribute("formats")!=null){
count = count-1;
}
return count;
}
@Override
void encodeChildes(XMLElement parentElement, ResValueBag resValueBag){
encodeAttributes(parentElement, resValueBag);
encodeEnumOrFlag(parentElement, resValueBag);
AttributeBag attributeBag=AttributeBag.create(resValueBag);
getMaterials().logMessage(attributeBag.toString());
getMaterials().logMessage(attributeBag.toString());
}
private void encodeAttributes(XMLElement parentElement, ResValueBag resValueBag){
int count=parentElement.getAttributeCount();
ResValueBagItemArray bagItemArray = resValueBag.getResValueBagItemArray();
int bagIndex=0;
ResValueBagItem bagItem = bagItemArray.get(bagIndex);
bagItem.setIdHigh((short) 0x0100);
bagItem.setIdLow(AttributeItemType.FORMAT.getValue());
bagItem.setType(ValueType.INT_DEC);
AttributeValueType[] valueTypes = AttributeValueType
.valuesOf(parentElement.getAttributeValue("formats"));
bagItem.setDataLow((short) (0xffff &
AttributeValueType.getByte(valueTypes)));
bagIndex++;
for(int i=0;i<count;i++){
XMLAttribute attribute = parentElement.getAttributeAt(i);
String name = attribute.getName();
if("name".equals(name) || "formats".equals(name)){
continue;
}
AttributeItemType itemType = AttributeItemType.fromName(name);
if(itemType==null){
throw new EncodeException("Unknown attribute: '"+name
+"', on attribute: "+attribute.toString());
}
bagItem = bagItemArray.get(bagIndex);
bagItem.setIdHigh((short) 0x0100);
bagItem.setIdLow(itemType.getValue());
bagItem.setType(ValueType.INT_DEC);
bagItem.setData(ValueDecoder.parseInteger(attribute.getValue()));
bagIndex++;
}
}
private void encodeEnumOrFlag(XMLElement element, ResValueBag resValueBag){
int count=element.getChildesCount();
if(count==0){
return;
}
int offset = element.getAttributeCount();
if(element.getAttribute("formats")!=null){
offset = offset-1;
}
EncodeMaterials materials = getMaterials();
ResValueBagItemArray bagItemArray = resValueBag.getResValueBagItemArray();
for(int i=0;i<count;i++){
XMLElement child=element.getChildAt(i);
int resourceId=materials.resolveLocalResourceId("id",
child.getAttributeValue("name"));
ValueDecoder.EncodeResult encodeResult =
ValueDecoder.encodeHexOrInt(child.getTextContent());
if(encodeResult == null){
throw new EncodeException("Unknown value for element '"+child.toText()+"'");
}
ResValueBagItem bagItem = bagItemArray.get(i+offset);
bagItem.setId(resourceId);
bagItem.setType(encodeResult.valueType);
bagItem.setData(encodeResult.value);
}
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.arsc.value.EntryBlock;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.xml.XMLElement;
public class XMLValuesEncoderBag extends XMLValuesEncoder{
XMLValuesEncoderBag(EncodeMaterials materials) {
super(materials);
}
@Override
void encodeValue(EntryBlock entryBlock, XMLElement element){
ResValueBag resValueBag=new ResValueBag();
entryBlock.setResValue(resValueBag);
String parent=element.getAttributeValue("parent");
if(!EncodeUtil.isEmpty(parent)){
int parentId=getMaterials().resolveReference(parent);
resValueBag.setParentId(parentId);
}
resValueBag.setCount(getChildesCount(element));
encodeChildes(element, resValueBag);
}
void encodeChildes(XMLElement element, ResValueBag resValueBag){
}
int getChildesCount(XMLElement element){
return element.getChildesCount();
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.EntryBlock;
public class XMLValuesEncoderColor extends XMLValuesEncoder{
XMLValuesEncoderColor(EncodeMaterials materials) {
super(materials);
}
@Override
void encodeStringValue(EntryBlock entryBlock, String value){
ValueDecoder.EncodeResult encodeResult=ValueDecoder.encodeColor(value);
if(encodeResult!=null){
entryBlock.setValueAsRaw(encodeResult.valueType, encodeResult.value);
}else {
throw new EncodeException("Unknown color value: "+value);
}
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.arsc.value.EntryBlock;
public class XMLValuesEncoderCommon extends XMLValuesEncoder{
XMLValuesEncoderCommon(EncodeMaterials materials) {
super(materials);
}
@Override
void encodeStringValue(EntryBlock entryBlock, String value){
entryBlock.setValueAsString(value);
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.EntryBlock;
public class XMLValuesEncoderDimen extends XMLValuesEncoder{
XMLValuesEncoderDimen(EncodeMaterials materials) {
super(materials);
}
@Override
void encodeStringValue(EntryBlock entryBlock, String value){
ValueDecoder.EncodeResult encodeResult =
ValueDecoder.encodeDimensionOrFloat(value);
if(encodeResult!=null){
entryBlock.setValueAsRaw(encodeResult.valueType, encodeResult.value);
}else {
throw new EncodeException("Unknown dimen value: "+value);
}
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.arsc.value.EntryBlock;
public class XMLValuesEncoderId extends XMLValuesEncoder{
public XMLValuesEncoderId(EncodeMaterials materials) {
super(materials);
}
@Override
void encodeStringValue(EntryBlock entryBlock, String value){
throw new EncodeException("Unexpected value for id: "+value);
}
@Override
void encodeNullValue(EntryBlock entryBlock){
entryBlock.setValueAsString("");
setVisibility(entryBlock);
}
@Override
void encodeBooleanValue(EntryBlock entryBlock, String value){
super.encodeBooleanValue(entryBlock, value);
setVisibility(entryBlock);
}
private void setVisibility(EntryBlock entryBlock){
entryBlock.setEntryTypePublic(true);
entryBlock.setEntryTypeShared(true);
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.EntryBlock;
import com.reandroid.lib.arsc.value.ValueType;
public class XMLValuesEncoderInteger extends XMLValuesEncoder{
XMLValuesEncoderInteger(EncodeMaterials materials) {
super(materials);
}
@Override
void encodeStringValue(EntryBlock entryBlock, String value){
value=value.trim();
if(ValueDecoder.isInteger(value)){
entryBlock.setValueAsRaw(ValueType.INT_DEC, ValueDecoder.parseInteger(value));
}else if(ValueDecoder.isHex(value)){
entryBlock.setValueAsRaw(ValueType.INT_HEX, ValueDecoder.parseHex(value));
}else {
throw new EncodeException("Unknown value for type <integer>: '"+value+"'");
}
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.arsc.array.ResValueBagItemArray;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.lib.arsc.value.ResValueBagItem;
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{
XMLValuesEncoderPlurals(EncodeMaterials materials) {
super(materials);
}
@Override
void encodeChildes(XMLElement parentElement, ResValueBag resValueBag){
int count = parentElement.getChildesCount();
ResValueBagItemArray itemArray = resValueBag.getResValueBagItemArray();
for(int i=0;i<count;i++){
XMLElement child=parentElement.getChildAt(i);
ResValueBagItem bagItem = itemArray.get(i);
PluralsQuantity quantity = PluralsQuantity
.value(child.getAttributeValue("quantity"));
if(quantity==null){
throw new EncodeException("Unknown plurals quantity: "
+child.toText());
}
bagItem.setIdHigh((short) 0x0100);
bagItem.setIdLow(quantity.getId());
String valueText=child.getTextContent();
if(ValueDecoder.isReference(valueText)){
bagItem.setType(ValueType.REFERENCE);
bagItem.setData(getMaterials().resolveReference(valueText));
}else if(EncodeUtil.isEmpty(valueText)) {
bagItem.setType(ValueType.NULL);
bagItem.setData(0);
}else{
bagItem.setValueAsString(valueText);
}
}
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.EntryBlock;
import com.reandroid.xml.XMLDocument;
import com.reandroid.xml.XMLElement;
import java.util.ArrayList;
import java.util.List;
public class XMLValuesEncoderString extends XMLValuesEncoder{
XMLValuesEncoderString(EncodeMaterials materials) {
super(materials);
}
@Override
public void encode(String type, String qualifiers, XMLDocument xmlDocument){
preloadStringPool(xmlDocument);
super.encode(type, qualifiers, xmlDocument);
}
@Override
void encodeStringValue(EntryBlock entryBlock, String value){
entryBlock.setValueAsString(value);
}
@Override
void encodeNullValue(EntryBlock entryBlock){
entryBlock.setValueAsString("");
}
@Override
void encodeBooleanValue(EntryBlock entryBlock, String value){
entryBlock.setValueAsString(value);
}
private void preloadStringPool(XMLDocument xmlDocument){
XMLElement documentElement = xmlDocument.getDocumentElement();
List<String> stringList = new ArrayList<>();
int count = documentElement.getChildesCount();
for(int i=0;i<count;i++){
String value=getValue(documentElement.getChildAt(i));
if(value==null || ValueDecoder.isReference(value)){
continue;
}
stringList.add(value);
}
getMaterials().addTableStringPool(stringList);
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.arsc.array.ResValueBagItemArray;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.EntryBlock;
import com.reandroid.lib.arsc.value.ResValueBag;
import com.reandroid.lib.arsc.value.ResValueBagItem;
import com.reandroid.lib.arsc.value.ValueType;
import com.reandroid.lib.arsc.value.attribute.AttributeBag;
import com.reandroid.xml.XMLElement;
public class XMLValuesEncoderStyle extends XMLValuesEncoderBag{
XMLValuesEncoderStyle(EncodeMaterials materials) {
super(materials);
}
@Override
void encodeChildes(XMLElement parentElement, ResValueBag resValueBag){
int count = parentElement.getChildesCount();
ResValueBagItemArray itemArray = resValueBag.getResValueBagItemArray();
EncodeMaterials materials=getMaterials();
for(int i=0;i<count;i++){
XMLElement child=parentElement.getChildAt(i);
ResValueBagItem bagItem = itemArray.get(i);
EntryBlock entryBlock=materials
.getAttributeBlock(child.getAttributeValue("name"));
if(entryBlock==null){
throw new EncodeException("Unknown attribute name: '"+child.toText()+"'");
}
bagItem.setId(entryBlock.getResourceId());
AttributeBag attributeBag=AttributeBag
.create((ResValueBag) entryBlock.getResValue());
String valueText=child.getTextContent();
ValueDecoder.EncodeResult encodeResult =
attributeBag.encodeName(valueText);
if(encodeResult!=null){
bagItem.setType(encodeResult.valueType);
bagItem.setData(encodeResult.value);
continue;
}
if(ValueDecoder.isReference(valueText)){
bagItem.setType(ValueType.REFERENCE);
bagItem.setData(getMaterials().resolveReference(valueText));
}else if(EncodeUtil.isEmpty(valueText)) {
bagItem.setType(ValueType.NULL);
bagItem.setData(0);
}else{
encodeResult=ValueDecoder.encodeGuessAny(valueText);
if(encodeResult!=null){
bagItem.setType(encodeResult.valueType);
bagItem.setData(encodeResult.value);
}else {
bagItem.setValueAsString(valueText);
}
}
}
}
}