encode XML source correctly

* add 's' suffix on type directories
* write uncompressed files at decoding
This commit is contained in:
REAndroid 2023-01-12 13:46:58 -05:00
parent 7651efae55
commit bb46abb499
20 changed files with 444 additions and 154 deletions

View File

@ -53,6 +53,7 @@ import java.util.*;
throws IOException, XMLException {
this.decodedEntries.clear();
logMessage("Decoding ...");
decodeUncompressedFiles(outDir);
TableEntryStore entryStore=new TableEntryStore();
entryStore.add(Frameworks.getAndroid());
TableBlock tableBlock=apkModule.getTableBlock();
@ -74,6 +75,12 @@ import java.util.*;
extractRootFiles(outDir);
}
private void decodeUncompressedFiles(File outDir)
throws IOException {
File file=new File(outDir, UncompressedFiles.JSON_FILE);
UncompressedFiles uncompressedFiles = apkModule.getUncompressedFiles();
uncompressedFiles.toJson().write(file);
}
private void decodeResFile(EntryStore entryStore, File outDir, ResFile resFile)
throws IOException, XMLException {
if(resFile.isBinaryXml()){

View File

@ -19,6 +19,7 @@ package com.reandroid.lib.apk;
import com.reandroid.archive.FileInputSource;
import com.reandroid.lib.apk.xmlencoder.RESEncoder;
import com.reandroid.lib.arsc.chunk.TableBlock;
import com.reandroid.lib.json.JSONObject;
import com.reandroid.xml.XMLException;
import java.io.File;
@ -34,6 +35,7 @@ package com.reandroid.lib.apk;
this.resEncoder = new RESEncoder(module, tableBlock);
}
public void scanDirectory(File mainDirectory) throws IOException, XMLException {
loadUncompressedFiles(mainDirectory);
resEncoder.scanDirectory(mainDirectory);
File rootDir=new File(mainDirectory, "root");
scanRootDir(rootDir);
@ -51,6 +53,11 @@ package com.reandroid.lib.apk;
archive.add(inputSource);
}
}
private void loadUncompressedFiles(File mainDirectory) throws IOException, XMLException {
File file=new File(mainDirectory, UncompressedFiles.JSON_FILE);
UncompressedFiles uncompressedFiles = getApkModule().getUncompressedFiles();
uncompressedFiles.fromJson(file);
}
public void setApkLogger(APKLogger apkLogger) {
this.resEncoder.setAPKLogger(apkLogger);
}

View File

@ -139,7 +139,11 @@ public class ResFile {
EntryBlock entryBlock=pickOne();
TypeBlock typeBlock=entryBlock.getTypeBlock();
StringBuilder builder=new StringBuilder();
builder.append(typeBlock.getTypeName());
String type=typeBlock.getTypeName();
builder.append(type);
if(!type.equals("plurals") && !type.endsWith("s")){
builder.append('s');
}
builder.append(typeBlock.getQualifiers());
builder.append('/');
builder.append(entryBlock.getName());

View File

@ -114,6 +114,9 @@ package com.reandroid.lib.apk.xmlencoder;
if(i>0){
name=name.substring(0, i);
}
if(!name.equals("plurals") && name.endsWith("s")){
name=name.substring(0, name.length()-1);
}
return name;
}
public static String getTypeNameFromValuesXml(File valuesXml){

View File

@ -83,11 +83,16 @@ public class XMLFileEncoder {
ensureNamespaces(element, resXmlElement);
resXmlElement.setTag(element.getTagName());
buildAttributes(element, resXmlElement);
int count=element.getChildesCount();
for(int i=0;i<count;i++){
XMLElement child=element.getChildAt(i);
ResXmlElement childXml=resXmlElement.createChildElement();
buildElement(child, childXml);
for(XMLNode node:element.getChildNodes()){
if(node instanceof XMLText){
resXmlElement.addResXmlText(((XMLText)node).getText(true));
}else if(node instanceof XMLComment){
resXmlElement.setComment(((XMLComment)node).getCommentText());
}else if(node instanceof XMLElement){
XMLElement child=(XMLElement) node;
ResXmlElement childXml=resXmlElement.createChildElement();
buildElement(child, childXml);
}
}
}
private void buildAttributes(XMLElement element, ResXmlElement resXmlElement){

View File

@ -43,7 +43,7 @@ class XMLValuesEncoderAttr extends XMLValuesEncoderBag{
encodeAttributes(parentElement, resValueBag);
encodeEnumOrFlag(parentElement, resValueBag);
// TODO: re-check if this is necessary
resValueBag.getEntryBlock().setEntryTypeShared(true);
resValueBag.getEntryBlock().setPublic(true);
}
private void encodeAttributes(XMLElement parentElement, ResValueBag resValueBag){
int count=parentElement.getAttributeCount();

View File

@ -37,7 +37,7 @@ class XMLValuesEncoderId extends XMLValuesEncoder{
setVisibility(entryBlock);
}
private void setVisibility(EntryBlock entryBlock){
entryBlock.setEntryTypePublic(true);
entryBlock.setEntryTypeShared(true);
entryBlock.setWeak(true);
entryBlock.setPublic(true);
}
}

View File

@ -18,7 +18,6 @@ package com.reandroid.lib.arsc.chunk.xml;
import com.reandroid.lib.arsc.chunk.ChunkType;
import com.reandroid.lib.arsc.base.Block;
import com.reandroid.lib.arsc.container.BlockList;
import com.reandroid.lib.arsc.container.FixedBlockContainer;
import com.reandroid.lib.arsc.container.SingleBlockContainer;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.header.HeaderBlock;
@ -29,37 +28,37 @@ import com.reandroid.lib.common.EntryStore;
import com.reandroid.lib.json.JSONConvert;
import com.reandroid.lib.json.JSONArray;
import com.reandroid.lib.json.JSONObject;
import com.reandroid.xml.NameSpaceItem;
import com.reandroid.xml.XMLAttribute;
import com.reandroid.xml.XMLElement;
import com.reandroid.xml.XMLException;
import com.reandroid.xml.*;
import java.io.IOException;
import java.util.*;
public class ResXmlElement extends FixedBlockContainer implements JSONConvert<JSONObject> {
public class ResXmlElement extends ResXmlNode implements JSONConvert<JSONObject> {
private final BlockList<ResXmlStartNamespace> mStartNamespaceList;
private final SingleBlockContainer<ResXmlStartElement> mStartElementContainer;
private final BlockList<ResXmlElement> mBody;
private final SingleBlockContainer<ResXmlText> mResXmlTextContainer;
private final BlockList<ResXmlNode> mBody;
private final SingleBlockContainer<ResXmlEndElement> mEndElementContainer;
private final BlockList<ResXmlEndNamespace> mEndNamespaceList;
private int mDepth;
public ResXmlElement() {
super(6);
super(5);
this.mStartNamespaceList = new BlockList<>();
this.mStartElementContainer= new SingleBlockContainer<>();
this.mBody = new BlockList<>();
this.mResXmlTextContainer = new SingleBlockContainer<>();
this.mEndElementContainer = new SingleBlockContainer<>();
this.mEndNamespaceList = new BlockList<>();
addChild(0, mStartNamespaceList);
addChild(1, mStartElementContainer);
addChild(2, mBody);
addChild(3, mResXmlTextContainer);
addChild(4, mEndElementContainer);
addChild(5, mEndNamespaceList);
addChild(3, mEndElementContainer);
addChild(4, mEndNamespaceList);
}
public String getComment(){
return getStartElement().getComment();
}
public void setComment(String comment){
getStartElement().setComment(comment);
}
public void calculatePositions(){
ResXmlStartElement start = getStartElement();
@ -293,10 +292,53 @@ import java.util.*;
return mBody.remove(element);
}
public int countElements(){
return mBody.size();
int result = 0;
for(ResXmlNode xmlNode:listXmlNodes()){
if(xmlNode instanceof ResXmlElement){
result++;
}
}
return result;
}
public void clearChildes(){
List<ResXmlNode> copyOfNodeList=new ArrayList<>(mBody.getChildes());
for(ResXmlNode xmlNode:copyOfNodeList){
if(xmlNode==null){
continue;
}
xmlNode.onRemove();
mBody.remove(xmlNode);
}
}
public List<ResXmlNode> listXmlNodes(){
return mBody.getChildes();
}
public List<ResXmlText> listXmlText(){
List<ResXmlText> results=new ArrayList<>();
for(ResXmlNode xmlNode:listXmlNodes()){
if(xmlNode instanceof ResXmlTextNode){
results.add(((ResXmlTextNode) xmlNode).getResXmlText());
}
}
return results;
}
public List<ResXmlTextNode> listXmlTextNodes(){
List<ResXmlTextNode> results=new ArrayList<>();
for(ResXmlNode xmlNode:listXmlNodes()){
if(xmlNode instanceof ResXmlTextNode){
results.add((ResXmlTextNode) xmlNode);
}
}
return results;
}
public List<ResXmlElement> listElements(){
return mBody.getChildes();
List<ResXmlElement> results=new ArrayList<>();
for(ResXmlNode xmlNode:listXmlNodes()){
if(xmlNode instanceof ResXmlElement){
results.add((ResXmlElement) xmlNode);
}
}
return results;
}
public List<ResXmlElement> listElements(String name){
List<ResXmlElement> results=new ArrayList<>();
@ -432,25 +474,40 @@ import java.util.*;
mEndElementContainer.setItem(item);
}
// Use listXmlText() instead to be removed on next version
@Deprecated
public ResXmlText getResXmlText(){
return mResXmlTextContainer.getItem();
}
public void setResXmlText(ResXmlText xmlText){
mResXmlTextContainer.setItem(xmlText);
}
public void setResXmlText(String text){
if(text==null){
mResXmlTextContainer.setItem(null);
}else {
ResXmlText xmlText=mResXmlTextContainer.getItem();
if(xmlText==null){
xmlText=new ResXmlText();
mResXmlTextContainer.setItem(xmlText);
ResXmlStartElement start = getStartElement();
xmlText.setLineNumber(start.getLineNumber());
}
xmlText.setText(text);
List<ResXmlText> xmlTextList=listXmlText();
if(xmlTextList.size()==0){
return null;
}
return xmlTextList.get(0);
}
public void addResXmlTextNode(ResXmlTextNode xmlTextNode){
mBody.add(xmlTextNode);
}
public void addResXmlText(ResXmlText xmlText){
if(xmlText!=null){
addResXmlTextNode(new ResXmlTextNode(xmlText));
}
}
// Use addResXmlText()
@Deprecated
public void setResXmlText(ResXmlText xmlText){
addResXmlText(xmlText);
}
@Deprecated
public void setResXmlText(String text){
clearChildes();
addResXmlText(text);
}
public void addResXmlText(String text){
if(text==null){
return;
}
ResXmlTextNode xmlTextNode=new ResXmlTextNode();
addResXmlTextNode(xmlTextNode);
xmlTextNode.setText(text);
}
private boolean isBalanced(){
@ -602,7 +659,7 @@ import java.util.*;
}
private void onXmlText(BlockReader reader) throws IOException{
ResXmlText xmlText=new ResXmlText();
setResXmlText(xmlText);
addResXmlText(xmlText);
xmlText.readBytes(reader);
}
@ -644,6 +701,7 @@ import java.util.*;
@Override
public JSONObject toJson() {
JSONObject jsonObject=new JSONObject();
jsonObject.put(NAME_node_type, NAME_element);
ResXmlStartElement start = getStartElement();
jsonObject.put(NAME_line, start.getLineNumber());
int i=0;
@ -663,13 +721,6 @@ import java.util.*;
if(comment!=null){
jsonObject.put(NAME_comment, comment);
}
ResXmlText xmlText=getResXmlText();
if(xmlText!=null){
String text=xmlText.getText();
if(text!=null){
jsonObject.put(NAME_text, text);
}
}
String uri=start.getUri();
if(uri!=null){
jsonObject.put(NAME_namespace_uri, uri);
@ -678,8 +729,8 @@ import java.util.*;
jsonObject.put(NAME_attributes, attrArray);
i=0;
JSONArray childes=new JSONArray();
for(ResXmlElement element:listElements()){
childes.put(i, element.toJson());
for(ResXmlNode xmlNode:listXmlNodes()){
childes.put(i, xmlNode.toJson());
i++;
}
if(i>0){
@ -730,12 +781,29 @@ import java.util.*;
int length=childArray.length();
for(int i=0;i<length;i++){
JSONObject childObject=childArray.getJSONObject(i);
ResXmlElement child = createChildElement();
child.fromJson(childObject);
if(isTextNode(childObject)){
ResXmlTextNode xmlTextNode=new ResXmlTextNode();
addResXmlTextNode(xmlTextNode);
xmlTextNode.fromJson(childObject);
}else {
ResXmlElement childElement = createChildElement();
childElement.fromJson(childObject);
}
}
}
start.calculatePositions();
}
private boolean isTextNode(JSONObject childObject){
String type=childObject.optString(NAME_node_type, null);
if(ResXmlTextNode.NAME_text.equals(type)){
return true;
}
if(NAME_element.equals(type)){
return false;
}
// support older ARSCLib versions
return childObject.has(NAME_text);
}
/**
* Decodes binary {@link ResXmlElement} to readable {@link XMLElement}
@ -745,6 +813,7 @@ import java.util.*;
* */
public XMLElement decodeToXml(EntryStore entryStore, int currentPackageId) throws XMLException {
XMLElement xmlElement = new XMLElement(getTagName());
xmlElement.setLineNumber(getStartElement().getLineNumber());
for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){
xmlElement.addAttribute(startNamespace.decodeToXml());
}
@ -753,15 +822,21 @@ import java.util.*;
resXmlAttribute.decodeToXml(entryStore, currentPackageId);
xmlElement.addAttribute(xmlAttribute);
}
for(ResXmlElement childResXmlElement:listElements()){
XMLElement childXMLElement =
childResXmlElement.decodeToXml(entryStore, currentPackageId);
xmlElement.addChild(childXMLElement);
String comment=getComment();
if(comment!=null){
xmlElement.addComment(new XMLComment(comment));
}
ResXmlText resXmlText = getResXmlText();
if(resXmlText!=null){
xmlElement.setTextContent(
ValueDecoder.escapeSpecialCharacter(resXmlText.getText()));
for(ResXmlNode xmlNode:listXmlNodes()){
if(xmlNode instanceof ResXmlElement){
ResXmlElement childResXmlElement=(ResXmlElement)xmlNode;
XMLElement childXMLElement =
childResXmlElement.decodeToXml(entryStore, currentPackageId);
xmlElement.addChild(childXMLElement);
}else if(xmlNode instanceof ResXmlTextNode){
ResXmlTextNode childResXmlTextNode=(ResXmlTextNode)xmlNode;
XMLText xmlText = childResXmlTextNode.decodeToXml();
xmlElement.addText(xmlText);
}
}
return xmlElement;
}
@ -799,6 +874,7 @@ import java.util.*;
public static final String NS_ANDROID_URI = "http://schemas.android.com/apk/res/android";
public static final String NS_ANDROID_PREFIX = "android";
static final String NAME_element = "element";
static final String NAME_name = "name";
static final String NAME_comment = "comment";
static final String NAME_text = "text";

View File

@ -0,0 +1,30 @@
/*
* 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.arsc.chunk.xml;
import com.reandroid.lib.arsc.container.FixedBlockContainer;
import com.reandroid.lib.json.JSONConvert;
import com.reandroid.lib.json.JSONObject;
public abstract class ResXmlNode extends FixedBlockContainer implements JSONConvert<JSONObject> {
ResXmlNode(int childesCount) {
super(childesCount);
}
void onRemove(){
}
public static final String NAME_node_type="node_type";
}

View File

@ -56,7 +56,9 @@ import java.util.Set;
if(isEmpty(uri) || isEmpty(prefix)){
return null;
}
return new SchemaAttr(prefix, uri);
SchemaAttr schemaAttr=new SchemaAttr(prefix, uri);
schemaAttr.setLineNumber(getLineNumber());
return schemaAttr;
}
private boolean isEmpty(String txt){
if(txt==null){

View File

@ -0,0 +1,79 @@
/*
* 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.arsc.chunk.xml;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.json.JSONObject;
import com.reandroid.xml.XMLText;
public class ResXmlTextNode extends ResXmlNode {
private final ResXmlText resXmlText;
public ResXmlTextNode(ResXmlText resXmlText) {
super(1);
this.resXmlText = resXmlText;
addChild(0, resXmlText);
}
public ResXmlTextNode() {
this(new ResXmlText());
}
public ResXmlText getResXmlText() {
return resXmlText;
}
public int getLineNumber(){
return getResXmlText().getLineNumber();
}
public void setLineNumber(int lineNumber){
getResXmlText().setLineNumber(lineNumber);
}
public String getText(){
return getResXmlText().getText();
}
public void setText(String text){
getResXmlText().setText(text);
}
public int getTextReference(){
return getResXmlText().getTextReference();
}
public void setTextReference(int ref){
getResXmlText().setTextReference(ref);
}
@Override
public String toString(){
String txt=getText();
if(txt!=null){
return txt;
}
return super.toString();
}
@Override
public JSONObject toJson() {
JSONObject jsonObject=new JSONObject();
jsonObject.put(NAME_node_type, NAME_text);
jsonObject.put(NAME_text, getText());
return jsonObject;
}
@Override
public void fromJson(JSONObject json) {
setText(json.optString(NAME_text, null));
}
public XMLText decodeToXml() {
XMLText xmlText=new XMLText(ValueDecoder.escapeSpecialCharacter(getText()));
xmlText.setLineNumber(getLineNumber());
return xmlText;
}
public static final String NAME_text="text";
}

View File

@ -77,6 +77,23 @@ public class ByteArray extends BlockItem {
byte[] bts = getBytesInternal();
bts[index]=value;
}
public boolean getBit(int byteOffset, int bitIndex){
return ((get(byteOffset)>>bitIndex) & 0x1) == 1;
}
public void putBit(int byteOffset, int bitIndex, boolean bit){
int val=get(byteOffset);
int left=val>>bitIndex;
if(bit){
left=left|0x1;
}else {
left=left & 0xFE;
}
left=left<<bitIndex;
bitIndex=8-bitIndex;
int right=(0xFF>>bitIndex) & val;
val=left|right;
put(byteOffset, (byte) val);
}
public final void putShort(int offset, short val){
byte[] bts = getBytesInternal();
bts[offset+1]= (byte) (val >>> 8 & 0xff);

View File

@ -57,7 +57,7 @@ public abstract class BaseResValueItem extends BaseResValue implements ResValueI
return null;
}
public ReferenceItem getTableStringReference(){
if(getValueType()!=ValueType.STRING){
if(getValueType()!=ValueType.STRING || getEntryBlock()==null){
return null;
}
if(mReferenceItem==null){

View File

@ -35,9 +35,7 @@ import java.util.ArrayList;
import java.util.List;
public class EntryBlock extends Block implements JSONConvert<JSONObject> {
private ShortItem mHeaderSize;
private ByteItem mFlagEntryType;
private ByteItem mByteFlagsB;
private ByteArray entryHeader;
private IntegerItem mSpecReference;
private BaseResValue mResValue;
private boolean mUnLocked;
@ -127,20 +125,16 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
return tableString.getHtml();
}
public TableString getValueAsTableString(){
TableStringPool stringPool=getTableStringPool();
TableStringPool stringPool = getTableStringPool();
if(stringPool==null){
return null;
}
BaseResValue res = getResValue();
if(!(res instanceof ResValueInt)){
BaseResValue baseResValue = getResValue();
if(!(baseResValue instanceof ResValueInt)){
return null;
}
ResValueInt resValueInt=(ResValueInt)res;
TableString tableString= stringPool.get(resValueInt.getData());
if(tableString==null){
return null;
}
return tableString;
ResValueInt resValueInt = (ResValueInt)baseResValue;
return stringPool.get(resValueInt.getData());
}
private TableStringPool getTableStringPool(){
PackageBlock pkg=getPackageBlock();
@ -256,27 +250,24 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
removeTableReferences();
removeSpecReferences();
}
public void setEntryTypeBag(boolean b){
mFlagEntryType.putBit(0, b);
private void setEntryTypeBag(boolean b){
entryHeader.putBit(OFFSET_FLAGS, 0, b);
refreshHeaderSize();
}
public boolean isEntryTypeBag(){
return mFlagEntryType.getBit(0);
return entryHeader.getBit(OFFSET_FLAGS,0);
}
public void setEntryTypeShared(boolean b){
mFlagEntryType.putBit(1, b);
public void setPublic(boolean b){
entryHeader.putBit(OFFSET_FLAGS,1, b);
}
public boolean isEntryTypeShared(){
return mFlagEntryType.getBit(1);
public boolean isPublic(){
return entryHeader.getBit(OFFSET_FLAGS,1);
}
public void setEntryTypePublic(boolean b){
mFlagEntryType.putBit(2, b);
public void setWeak(boolean b){
entryHeader.putBit(OFFSET_FLAGS, 2, b);
}
public boolean isEntryTypePublic(){
return mFlagEntryType.getBit(2);
}
private void setByteFlagsB(byte b){
mByteFlagsB.set(b);
public boolean isWeak(){
return entryHeader.getBit(OFFSET_FLAGS,2);
}
private IntegerItem getSpecReferenceBlock(){
return mSpecReference;
@ -341,7 +332,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
return;
}
if(resValue!=null){
resValue.setIndex(4);
resValue.setIndex(2);
resValue.setParent(this);
}
if(mResValue!=null){
@ -509,47 +500,32 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
if(mUnLocked){
return;
}
mUnLocked =true;
this.mHeaderSize =new ShortItem();
this.mFlagEntryType =new ByteItem();
this.mByteFlagsB=new ByteItem();
mUnLocked = true;
entryHeader = new ByteArray(4);
if(mSpecReference==null){
this.mSpecReference = new IntegerItem();
}else if(mSpecReference.isNull()){
mSpecReference.setNull(false);
}
mHeaderSize.setIndex(0);
mFlagEntryType.setIndex(1);
mByteFlagsB.setIndex(2);
mSpecReference.setIndex(3);
mHeaderSize.setParent(this);
mFlagEntryType.setParent(this);
mByteFlagsB.setParent(this);
entryHeader.setIndex(0);
mSpecReference.setIndex(1);
entryHeader.setParent(this);
mSpecReference.setParent(this);
}
private void lockEntry(){
if(!mUnLocked){
return;
}
removeAllReferences();
mUnLocked =false;
mHeaderSize.setParent(null);
mFlagEntryType.setParent(null);
mByteFlagsB.setParent(null);
mUnLocked = false;
entryHeader.setParent(null);
mSpecReference.setParent(null);
mHeaderSize.setIndex(-1);
mFlagEntryType.setIndex(-1);
mByteFlagsB.setIndex(-1);
entryHeader.setIndex(-1);
mSpecReference.setIndex(-1);
removeResValue();
this.mHeaderSize =null;
this.mFlagEntryType =null;
this.mByteFlagsB =null;
this.entryHeader = null;
this.mSpecReference =null;
}
private void removeResValue(){
@ -560,11 +536,13 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
}
}
private void refreshHeaderSize(){
short size;
if(isEntryTypeBag()){
mHeaderSize.set(HEADER_SIZE_BAG);
size=HEADER_SIZE_BAG;
}else {
mHeaderSize.set(HEADER_SIZE_INT);
size=HEADER_SIZE_INT;
}
entryHeader.putShort(OFFSET_SIZE, size);
}
private void createResValue(){
if(getResValue()!=null){
@ -599,9 +577,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
if(isNull()){
return null;
}
byte[] results=mHeaderSize.getBytes();
results=addBytes(results, mFlagEntryType.getBytes());
results=addBytes(results, mByteFlagsB.getBytes());
byte[] results=entryHeader.getBytes();
results=addBytes(results, mSpecReference.getBytes());
results=addBytes(results, mResValue.getBytes());
return results;
@ -612,8 +588,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
return 0;
}
/*
mHeaderSize -> 2 bytes
mFlags -> 2 bytes
entryHeader -> 4 bytes
mSpecReference -> 4 bytes
-------
Total = 8 bytes, thus this value is always fixed no need to re-count
@ -633,9 +608,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
return;
}
counter.addCount(countBytes());
mHeaderSize.onCountUpTo(counter);
mFlagEntryType.onCountUpTo(counter);
mByteFlagsB.onCountUpTo(counter);
entryHeader.onCountUpTo(counter);
mSpecReference.onCountUpTo(counter);
mResValue.onCountUpTo(counter);
}
@ -644,9 +617,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
if(isNull()){
return 0;
}
int result=mHeaderSize.writeBytes(stream);
result+= mFlagEntryType.writeBytes(stream);
result+=mByteFlagsB.writeBytes(stream);
int result=entryHeader.writeBytes(stream);
result+= mSpecReference.writeBytes(stream);
result+=mResValue.writeBytes(stream);
return result;
@ -695,15 +666,12 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
public void onReadBytes(BlockReader reader) throws IOException{
setNull(false);
removeResValue();
mHeaderSize.readBytes(reader);
mFlagEntryType.readBytes(reader);
mByteFlagsB.readBytes(reader);
entryHeader.readBytes(reader);
mSpecReference.readBytes(reader);
createResValue();
mResValue.readBytes(reader);
updatePackage();
updateSpecRef();
mResValue.onDataLoaded();
}
@Override
public JSONObject toJson() {
@ -715,10 +683,10 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
if(isEntryTypeBag()){
jsonObject.put(NAME_is_bag, true);
}
if(isEntryTypePublic()){
jsonObject.put(NAME_is_public, true);
if(isWeak()){
jsonObject.put(NAME_is_weak, true);
}
if(isEntryTypeShared()){
if(isPublic()){
jsonObject.put(NAME_is_shared, true);
}
jsonObject.put(NAME_value, getResValue().toJson());
@ -737,8 +705,8 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
baseResValue=new ResValueInt();
}
setResValue(baseResValue);
setEntryTypeShared(json.optBoolean(NAME_is_shared, false));
setEntryTypePublic(json.optBoolean(NAME_is_public, false));
setPublic(json.optBoolean(NAME_is_shared, false));
setWeak(json.optBoolean(NAME_is_weak, false));
setName(json.getString(NAME_entry_name));
baseResValue.fromJson(json.getJSONObject(NAME_value));
mResValue.onDataLoaded();
@ -765,8 +733,8 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
SpecString spec = getPackageBlock()
.getSpecStringPool().getOrCreate(name);
setSpecReference(spec.getIndex());
setEntryTypeShared(entryBlock.isEntryTypeShared());
setEntryTypePublic(entryBlock.isEntryTypePublic());
setPublic(entryBlock.isPublic());
setWeak(entryBlock.isWeak());
}
private ResValueBag getOrCreateResValueBag(){
if(mResValue instanceof ResValueBag){
@ -833,13 +801,15 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
return builder.toString();
}
private final static short HEADER_SIZE_BAG = 0x0010;
private final static short HEADER_SIZE_INT = 0x0008;
private static final int OFFSET_SIZE = 0;
private static final int OFFSET_FLAGS = 2;
private static final short HEADER_SIZE_BAG = 0x0010;
private static final short HEADER_SIZE_INT = 0x0008;
public static final String NAME_entry_name ="entry_name";
private static final String NAME_is_bag="is_bag";
private static final String NAME_is_shared="is_shared";
private static final String NAME_is_public="is_public";
private static final String NAME_is_weak = "is_weak";
private static final String NAME_value="value";
}

View File

@ -139,7 +139,9 @@ public class ResValueBagItem extends BaseResValueItem{
}
void refreshTableReference(){
if(getValueType()==ValueType.STRING){
addTableReference(getTableStringReference());
if(!hasTableReference()){
addTableReference(getTableStringReference());
}
}else {
removeTableReference();
}

View File

@ -48,11 +48,16 @@ public class XMLElement extends XMLNode{
}
public void addText(XMLText text){
addTextInternal(text, true);
}
private void addTextInternal(XMLText text, boolean addSupper){
if(text==null){
return;
}
mTexts.add(text);
super.addChildNode(text);
if(addSupper){
super.addChildNodeInternal(text);
}
}
private void appendText(String text){
if(text==null || text.length()==0){
@ -171,7 +176,7 @@ public class XMLElement extends XMLNode{
}
public XMLElement createElement(String tag) {
XMLElement baseElement=new XMLElement(tag);
addChildNoCheck(baseElement);
addChildNoCheck(baseElement, true);
return baseElement;
}
public void addChild(Collection<XMLElement> elements) {
@ -183,7 +188,7 @@ public class XMLElement extends XMLNode{
}
}
public void addChild(XMLElement child) {
addChildNoCheck(child);
addChildNoCheck(child, true);
}
private void clearChildElements(){
mChildes.clear();
@ -238,6 +243,9 @@ public class XMLElement extends XMLNode{
mComments=null;
}
public void addComment(XMLComment commentElement) {
addCommentInternal(commentElement, true);
}
void addCommentInternal(XMLComment commentElement, boolean addSuper) {
if(commentElement==null){
return;
}
@ -247,7 +255,9 @@ public class XMLElement extends XMLNode{
mComments.add(commentElement);
commentElement.setIndent(getIndent());
commentElement.setParent(this);
super.addChildNode(commentElement);
if(addSuper){
super.addChildNodeInternal(commentElement);
}
}
public void removeChildElements(){
mChildes.clear();
@ -413,14 +423,26 @@ public class XMLElement extends XMLNode{
void setParent(XMLElement baseElement){
mParent=baseElement;
}
private void addChildNoCheck(XMLElement child){
@Override
void onChildAdded(XMLNode xmlNode){
if(xmlNode instanceof XMLComment){
addCommentInternal((XMLComment) xmlNode, false);
}else if(xmlNode instanceof XMLElement){
addChildNoCheck((XMLElement) xmlNode, false);
}else if(xmlNode instanceof XMLText){
addTextInternal((XMLText) xmlNode, false);
}
}
private void addChildNoCheck(XMLElement child, boolean addSupper){
if(child==null || child == this){
return;
}
child.setParent(this);
child.setIndent(getChildIndent());
mChildes.add(child);
super.addChildNode(child);
if(addSupper){
super.addChildNodeInternal(child);
}
}
public int getLevel(){
int rs=0;
@ -579,12 +601,19 @@ public class XMLElement extends XMLNode{
public boolean hasTextContent() {
return mTexts.size()>0;
}
public String getText(){
if(mTexts.size()==0){
return null;
}
return mTexts.get(0).getText();
}
public void setTextContent(String text){
setTextContent(text, true);
}
public void setTextContent(String text, boolean escape){
clearChildElements();
clearTexts();
super.getChildNodes().clear();
if(escape){
text=XMLUtil.escapeXmlChars(text);
}

View File

@ -21,12 +21,37 @@ import java.util.ArrayList;
import java.util.List;
public abstract class XMLNode {
private int mLineNumber;
private int mColumnNumber;
private final List<XMLNode> mChildNodes = new ArrayList<>();
void addChildNode(XMLNode xmlNode){
if(xmlNode!=null && canAdd(xmlNode)){
mChildNodes.add(xmlNode);
public int getColumnNumber() {
return mColumnNumber;
}
public void setColumnNumber(int columnNumber) {
this.mColumnNumber = columnNumber;
}
public int getLineNumber() {
return mLineNumber;
}
public void setLineNumber(int lineNumber) {
this.mLineNumber = lineNumber;
}
public void addChildNode(XMLNode xmlNode){
boolean addOk=addChildNodeInternal(xmlNode);
if(addOk){
onChildAdded(xmlNode);
}
}
boolean addChildNodeInternal(XMLNode xmlNode){
if(xmlNode!=null && canAdd(xmlNode)){
return mChildNodes.add(xmlNode);
}
return false;
}
void onChildAdded(XMLNode xmlNode){
}
boolean canAdd(XMLNode xmlNode){
return !mChildNodes.contains(xmlNode);

View File

@ -26,6 +26,11 @@ public class XMLText extends XMLNode{
public XMLText(){
this(null);
}
@Override
public void addChildNode(XMLNode xmlNode){
throw new IllegalArgumentException("Can not add xml node on text: "+xmlNode);
}
public String getText(){
return getText(true);
}

View File

@ -425,6 +425,10 @@ public class MXParser implements XmlPullParser
public InputStream getInputStream(){
return inputStream;
}
public Reader getReader(){
reset();
return reader;
}
public String getInputEncoding() {
return inputEncoding;

View File

@ -45,7 +45,7 @@ public class XMLDocumentParser {
public XMLDocument parse() throws XMLParseException {
try {
XMLDocument document= parseDocument();
closeFileInputStream();
close();
return document;
} catch (XmlPullParserException | IOException e) {
XMLParseException ex=new XMLParseException(e.getMessage());
@ -53,12 +53,20 @@ public class XMLDocumentParser {
throw ex;
}
}
private void close(){
closeReader();
closeFileInputStream();
mResDocument=null;
mCurrentElement=null;
mCurrentText=null;
mComments=null;
}
private void closeFileInputStream(){
if(!(mParser instanceof MXParser)){
return;
}
MXParser mxParser=(MXParser) mParser;
InputStream inputStream = mxParser.getInputStream();
MXParser parser=(MXParser) mParser;
InputStream inputStream = parser.getInputStream();
if(!(inputStream instanceof FileInputStream)){
return;
}
@ -67,6 +75,19 @@ public class XMLDocumentParser {
} catch (IOException ignored) {
}
}
private void closeReader(){
if(!(mParser instanceof MXParser)){
return;
}
MXParser parser=(MXParser) mParser;
Reader reader = parser.getReader();
if(reader!=null){
try {
reader.close();
} catch (IOException ignored) {
}
}
}
private XMLDocument parseDocument() throws XmlPullParserException, IOException {
mResDocument=null;
@ -121,6 +142,8 @@ public class XMLDocumentParser {
}else {
mCurrentElement=mCurrentElement.createElement(name);
}
mCurrentElement.setColumnNumber(mParser.getColumnNumber());
mCurrentElement.setLineNumber(mParser.getLineNumber());
checkIndent();
flushComments(mCurrentElement);
String ns=mParser.getNamespace();
@ -284,6 +307,8 @@ public class XMLDocumentParser {
}
XMLComment commentElement=new XMLComment();
commentElement.setCommentText(commentText);
commentElement.setColumnNumber(mParser.getColumnNumber());
commentElement.setLineNumber(mParser.getLineNumber());
addComment(commentElement);
}
private void addComment(XMLComment ce){