From bb46abb499a1604c5675aeba3d12b7ca58ab007c Mon Sep 17 00:00:00 2001 From: REAndroid Date: Thu, 12 Jan 2023 13:46:58 -0500 Subject: [PATCH] encode XML source correctly * add 's' suffix on type directories * write uncompressed files at decoding --- .../lib/apk/ApkModuleXmlDecoder.java | 7 + .../lib/apk/ApkModuleXmlEncoder.java | 7 + .../java/com/reandroid/lib/apk/ResFile.java | 6 +- .../lib/apk/xmlencoder/EncodeUtil.java | 3 + .../lib/apk/xmlencoder/XMLFileEncoder.java | 15 +- .../apk/xmlencoder/XMLValuesEncoderAttr.java | 2 +- .../apk/xmlencoder/XMLValuesEncoderId.java | 4 +- .../lib/arsc/chunk/xml/ResXmlElement.java | 180 +++++++++++++----- .../lib/arsc/chunk/xml/ResXmlNode.java | 30 +++ .../arsc/chunk/xml/ResXmlStartNamespace.java | 4 +- .../lib/arsc/chunk/xml/ResXmlTextNode.java | 79 ++++++++ .../reandroid/lib/arsc/item/ByteArray.java | 17 ++ .../lib/arsc/value/BaseResValueItem.java | 2 +- .../reandroid/lib/arsc/value/EntryBlock.java | 126 +++++------- .../lib/arsc/value/ResValueBagItem.java | 4 +- .../java/com/reandroid/xml/XMLElement.java | 41 +++- src/main/java/com/reandroid/xml/XMLNode.java | 31 ++- src/main/java/com/reandroid/xml/XMLText.java | 5 + .../com/reandroid/xml/parser/MXParser.java | 4 + .../xml/parser/XMLDocumentParser.java | 31 ++- 20 files changed, 444 insertions(+), 154 deletions(-) create mode 100644 src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlNode.java create mode 100644 src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlTextNode.java diff --git a/src/main/java/com/reandroid/lib/apk/ApkModuleXmlDecoder.java b/src/main/java/com/reandroid/lib/apk/ApkModuleXmlDecoder.java index 395962a..12ee302 100644 --- a/src/main/java/com/reandroid/lib/apk/ApkModuleXmlDecoder.java +++ b/src/main/java/com/reandroid/lib/apk/ApkModuleXmlDecoder.java @@ -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()){ diff --git a/src/main/java/com/reandroid/lib/apk/ApkModuleXmlEncoder.java b/src/main/java/com/reandroid/lib/apk/ApkModuleXmlEncoder.java index bc3b82e..1791990 100644 --- a/src/main/java/com/reandroid/lib/apk/ApkModuleXmlEncoder.java +++ b/src/main/java/com/reandroid/lib/apk/ApkModuleXmlEncoder.java @@ -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); } diff --git a/src/main/java/com/reandroid/lib/apk/ResFile.java b/src/main/java/com/reandroid/lib/apk/ResFile.java index 11d6204..189bb33 100644 --- a/src/main/java/com/reandroid/lib/apk/ResFile.java +++ b/src/main/java/com/reandroid/lib/apk/ResFile.java @@ -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()); diff --git a/src/main/java/com/reandroid/lib/apk/xmlencoder/EncodeUtil.java b/src/main/java/com/reandroid/lib/apk/xmlencoder/EncodeUtil.java index 7073c34..5f52f04 100644 --- a/src/main/java/com/reandroid/lib/apk/xmlencoder/EncodeUtil.java +++ b/src/main/java/com/reandroid/lib/apk/xmlencoder/EncodeUtil.java @@ -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){ diff --git a/src/main/java/com/reandroid/lib/apk/xmlencoder/XMLFileEncoder.java b/src/main/java/com/reandroid/lib/apk/xmlencoder/XMLFileEncoder.java index 53d4cde..bbf38aa 100644 --- a/src/main/java/com/reandroid/lib/apk/xmlencoder/XMLFileEncoder.java +++ b/src/main/java/com/reandroid/lib/apk/xmlencoder/XMLFileEncoder.java @@ -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 { + public class ResXmlElement extends ResXmlNode implements JSONConvert { private final BlockList mStartNamespaceList; private final SingleBlockContainer mStartElementContainer; - private final BlockList mBody; - private final SingleBlockContainer mResXmlTextContainer; + private final BlockList mBody; private final SingleBlockContainer mEndElementContainer; private final BlockList 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 copyOfNodeList=new ArrayList<>(mBody.getChildes()); + for(ResXmlNode xmlNode:copyOfNodeList){ + if(xmlNode==null){ + continue; + } + xmlNode.onRemove(); + mBody.remove(xmlNode); + } + } + public List listXmlNodes(){ + return mBody.getChildes(); + } + public List listXmlText(){ + List results=new ArrayList<>(); + for(ResXmlNode xmlNode:listXmlNodes()){ + if(xmlNode instanceof ResXmlTextNode){ + results.add(((ResXmlTextNode) xmlNode).getResXmlText()); + } + } + return results; + } + public List listXmlTextNodes(){ + List results=new ArrayList<>(); + for(ResXmlNode xmlNode:listXmlNodes()){ + if(xmlNode instanceof ResXmlTextNode){ + results.add((ResXmlTextNode) xmlNode); + } + } + return results; } public List listElements(){ - return mBody.getChildes(); + List results=new ArrayList<>(); + for(ResXmlNode xmlNode:listXmlNodes()){ + if(xmlNode instanceof ResXmlElement){ + results.add((ResXmlElement) xmlNode); + } + } + return results; } public List listElements(String name){ List 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 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 { + ResXmlNode(int childesCount) { + super(childesCount); + } + void onRemove(){ + } + + public static final String NAME_node_type="node_type"; +} diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartNamespace.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartNamespace.java index 7b36b65..d45b689 100755 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartNamespace.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartNamespace.java @@ -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){ diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlTextNode.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlTextNode.java new file mode 100644 index 0000000..6e05272 --- /dev/null +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlTextNode.java @@ -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"; +} diff --git a/src/main/java/com/reandroid/lib/arsc/item/ByteArray.java b/src/main/java/com/reandroid/lib/arsc/item/ByteArray.java index 3fcc1fa..f8053ec 100755 --- a/src/main/java/com/reandroid/lib/arsc/item/ByteArray.java +++ b/src/main/java/com/reandroid/lib/arsc/item/ByteArray.java @@ -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) & 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); diff --git a/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java b/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java index ea1bfb4..673becd 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java +++ b/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java @@ -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){ diff --git a/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java b/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java index c8a982e..bbce731 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java @@ -35,9 +35,7 @@ import java.util.ArrayList; import java.util.List; public class EntryBlock extends Block implements JSONConvert { - 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 { 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 { 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 { 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 { 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 { } } 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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"; } diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java b/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java index fcca494..c7d88b5 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java +++ b/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java @@ -139,7 +139,9 @@ public class ResValueBagItem extends BaseResValueItem{ } void refreshTableReference(){ if(getValueType()==ValueType.STRING){ - addTableReference(getTableStringReference()); + if(!hasTableReference()){ + addTableReference(getTableStringReference()); + } }else { removeTableReference(); } diff --git a/src/main/java/com/reandroid/xml/XMLElement.java b/src/main/java/com/reandroid/xml/XMLElement.java index 39c3bb1..945f1ee 100755 --- a/src/main/java/com/reandroid/xml/XMLElement.java +++ b/src/main/java/com/reandroid/xml/XMLElement.java @@ -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 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); } diff --git a/src/main/java/com/reandroid/xml/XMLNode.java b/src/main/java/com/reandroid/xml/XMLNode.java index a92bf5b..7ff67f5 100644 --- a/src/main/java/com/reandroid/xml/XMLNode.java +++ b/src/main/java/com/reandroid/xml/XMLNode.java @@ -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 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); diff --git a/src/main/java/com/reandroid/xml/XMLText.java b/src/main/java/com/reandroid/xml/XMLText.java index 94fd9db..8d5b7a8 100644 --- a/src/main/java/com/reandroid/xml/XMLText.java +++ b/src/main/java/com/reandroid/xml/XMLText.java @@ -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); } diff --git a/src/main/java/com/reandroid/xml/parser/MXParser.java b/src/main/java/com/reandroid/xml/parser/MXParser.java index 5b1ad6c..e1257a2 100644 --- a/src/main/java/com/reandroid/xml/parser/MXParser.java +++ b/src/main/java/com/reandroid/xml/parser/MXParser.java @@ -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; diff --git a/src/main/java/com/reandroid/xml/parser/XMLDocumentParser.java b/src/main/java/com/reandroid/xml/parser/XMLDocumentParser.java index df4b061..62901a8 100755 --- a/src/main/java/com/reandroid/xml/parser/XMLDocumentParser.java +++ b/src/main/java/com/reandroid/xml/parser/XMLDocumentParser.java @@ -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){