From dfe85947b41c94b49340ae84b4b8415f743eff6e Mon Sep 17 00:00:00 2001 From: REAndroid Date: Sun, 12 Mar 2023 14:11:43 -0400 Subject: [PATCH] [XML] clean unused strings and res id from pool --- .../arsc/array/ResXmlStringArray.java | 32 ++++++++- .../com/reandroid/arsc/array/StringArray.java | 5 +- .../com/reandroid/arsc/chunk/TableBlock.java | 10 ++- .../arsc/chunk/xml/BaseXmlChunk.java | 19 ++++- .../arsc/chunk/xml/ResXmlAttribute.java | 69 ++++++++++++++----- .../arsc/chunk/xml/ResXmlElement.java | 23 +++++-- .../reandroid/arsc/chunk/xml/ResXmlIDMap.java | 27 ++++++++ .../arsc/chunk/xml/ResXmlStartNamespace.java | 24 ++++++- .../com/reandroid/arsc/item/ResXmlID.java | 3 +- .../reandroid/arsc/pool/ResXmlStringPool.java | 29 ++++++-- .../com/reandroid/arsc/pool/StringPool.java | 3 + .../com/reandroid/arsc/value/ValueItem.java | 15 ++-- 12 files changed, 214 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/reandroid/arsc/array/ResXmlStringArray.java b/src/main/java/com/reandroid/arsc/array/ResXmlStringArray.java index 39b26b1..8ed95bb 100755 --- a/src/main/java/com/reandroid/arsc/array/ResXmlStringArray.java +++ b/src/main/java/com/reandroid/arsc/array/ResXmlStringArray.java @@ -15,15 +15,45 @@ */ package com.reandroid.arsc.array; +import com.reandroid.arsc.chunk.xml.ResXmlDocument; +import com.reandroid.arsc.chunk.xml.ResXmlIDMap; import com.reandroid.arsc.item.IntegerArray; import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.ResXmlString; -public class ResXmlStringArray extends StringArray { +import java.util.ArrayList; +import java.util.List; + + public class ResXmlStringArray extends StringArray { public ResXmlStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { super(offsets, itemCount, itemStart, is_utf8); } @Override + List listUnusedStringsToRemove(){ + List results=new ArrayList<>(); + ResXmlIDMap idMap = getResXmlIDMap(); + int lastIndex = -1; + if(idMap!=null){ + lastIndex = idMap.countId(); + } + for(ResXmlString item:listItems()){ + if(item == null + || item.hasReference() + || item.getIndex() extends OffsetBlockArray }; } public List removeUnusedStrings(){ - List unusedList=listUnusedStrings(); + List unusedList = listUnusedStringsToRemove(); remove(unusedList); for(T item:unusedList){ item.onRemoved(); } return unusedList; } + List listUnusedStringsToRemove(){ + return listUnusedStrings(); + } public List listUnusedStrings(){ List results=new ArrayList<>(); for(T item:listItems()){ diff --git a/src/main/java/com/reandroid/arsc/chunk/TableBlock.java b/src/main/java/com/reandroid/arsc/chunk/TableBlock.java index 88de6c6..df0e2f9 100755 --- a/src/main/java/com/reandroid/arsc/chunk/TableBlock.java +++ b/src/main/java/com/reandroid/arsc/chunk/TableBlock.java @@ -221,7 +221,7 @@ import java.util.*; } return 0; } - public Set getFrameWorks(){ + public List getFrameWorks(){ return mFrameWorks; } public void addFramework(TableBlock tableBlock){ @@ -229,12 +229,18 @@ import java.util.*; return; } for(TableBlock frm:tableBlock.getFrameWorks()){ - if(frm==this){ + if(frm==this || frm==tableBlock || tableBlock.equals(frm)){ return; } } mFrameWorks.add(tableBlock); } + public void removeFramework(TableBlock tableBlock){ + mFrameWorks.remove(tableBlock); + } + public void clearFrameworks(){ + mFrameWorks.clear(); + } @Override public JSONObject toJson() { JSONObject jsonObject=new JSONObject(); diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/BaseXmlChunk.java b/src/main/java/com/reandroid/arsc/chunk/xml/BaseXmlChunk.java index 13676f7..f91b362 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/BaseXmlChunk.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/BaseXmlChunk.java @@ -40,9 +40,13 @@ addChild(mStringReference); } void onRemoved(){ - unLinkStringReference(getHeaderBlock().getCommentReference()); - unLinkStringReference(mNamespaceReference); - unLinkStringReference(mStringReference); + ResXmlStringPool stringPool = getStringPool(); + if(stringPool==null){ + return; + } + stringPool.removeReference(getHeaderBlock().getCommentReference()); + stringPool.removeReference(mNamespaceReference); + stringPool.removeReference(mStringReference); } void linkStringReferences(){ linkStringReference(getHeaderBlock().getCommentReference()); @@ -68,6 +72,9 @@ return getHeaderBlock().getLineNumber().get(); } public void setCommentReference(int val){ + if(val == getCommentReference()){ + return; + } IntegerItem comment=getHeaderBlock().getCommentReference(); unLinkStringReference(comment); getHeaderBlock().getCommentReference().set(val); @@ -77,6 +84,9 @@ return getHeaderBlock().getCommentReference().get(); } public void setNamespaceReference(int val){ + if(val == getNamespaceReference()){ + return; + } unLinkStringReference(mNamespaceReference); mNamespaceReference.set(val); linkStringReference(mNamespaceReference); @@ -85,6 +95,9 @@ return mNamespaceReference.get(); } public void setStringReference(int val){ + if(val == getStringReference()){ + return; + } unLinkStringReference(mStringReference); mStringReference.set(val); linkStringReference(mStringReference); diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java index 6dec711..6942e10 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java @@ -29,6 +29,7 @@ import com.reandroid.xml.XMLException; import java.io.IOException; + import java.util.Objects; public class ResXmlAttribute extends ValueItem implements Comparable{ private ReferenceItem mNSReference; @@ -94,6 +95,9 @@ setNameReference(xmlID.getIndex()); } public void setName(String name, int resourceId){ + if(Objects.equals(name, getName()) && resourceId==getNameResourceID()){ + return; + } unlink(mNameReference); unLinkNameId(getResXmlID()); ResXmlString xmlString = getOrCreateAttributeName(name, resourceId); @@ -104,13 +108,34 @@ mNameReference = link(OFFSET_NAME); linkNameId(); } + private void linkStartNameSpace(){ + ResXmlElement xmlElement=getParentResXmlElement(); + if(xmlElement==null){ + return; + } + ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference()); + if(startNamespace==null){ + return; + } + startNamespace.addAttributeReference(this); + } + private void unLinkStartNameSpace(){ + ResXmlElement xmlElement=getParentResXmlElement(); + if(xmlElement==null){ + return; + } + ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference()); + if(startNamespace==null){ + return; + } + startNamespace.removeAttributeReference(this); + } private ResXmlString getOrCreateAttributeName(String name, int resourceId){ - StringPool stringPool = getStringPool(); + ResXmlStringPool stringPool = getStringPool(); if(stringPool==null){ return null; } - ResXmlStringPool resXmlStringPool = (ResXmlStringPool) stringPool; - return resXmlStringPool.getOrCreateAttribute(resourceId, name); + return stringPool.getOrCreateAttribute(resourceId, name); } public ResXmlElement getParentResXmlElement(){ return getParent(ResXmlElement.class); @@ -161,6 +186,7 @@ unlink(mNSReference); putInteger(getBytesInternal(), OFFSET_NS, ref); mNSReference = link(OFFSET_NS); + linkStartNameSpace(); } int getNameReference(){ return getInteger(getBytesInternal(), OFFSET_NAME); @@ -191,17 +217,17 @@ public void onReadBytes(BlockReader reader) throws IOException { super.onReadBytes(reader); linkAll(); + linkStartNameSpace(); } @Override public void onRemoved(){ super.onRemoved(); + unLinkStartNameSpace(); unlinkAll(); } @Override - protected void onUnlinkDataString(StringItem stringItem){ - if(!stringItem.hasReference()){ - stringItem.set(""); - } + protected void onUnlinkDataString(ReferenceItem referenceItem){ + unlink(referenceItem); } @Override protected void onDataChanged(){ @@ -228,6 +254,14 @@ } xmlID.removeReference(referenceItem); mNameIdReference = null; + if(xmlID.hasReference()){ + return; + } + ResXmlIDMap xmlIDMap = getResXmlIDMap(); + if(xmlIDMap == null){ + return; + } + xmlIDMap.removeSafely(xmlID); } private void linkAll(){ unlink(mNSReference); @@ -270,21 +304,20 @@ if(reference == null){ return; } - StringPool stringPool = getStringPool(); + ResXmlStringPool stringPool = getStringPool(); if(stringPool==null){ return; } - StringItem stringItem = stringPool.get(reference.get()); - if(stringItem==null){ - return; - } - stringItem.removeReference(reference); - if(!stringItem.hasReference()){ - stringItem.set(""); - } + stringPool.removeReference(reference); + } + @Override + public ResXmlStringPool getStringPool(){ + StringPool stringPool = super.getStringPool(); + if(stringPool instanceof ResXmlStringPool){ + return (ResXmlStringPool) stringPool; + } + return null; } - - @Override public int compareTo(ResXmlAttribute other) { int id1=getNameResourceID(); diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java index e0da7e3..9ad7235 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java @@ -329,14 +329,20 @@ return getStartElement().getResXmlAttributeArray().remove(resXmlAttribute); } public boolean removeElement(ResXmlElement element){ - if(element.getParent()!=null){ + if(element !=null && element.getParent()!=null){ element.onRemoved(); } return mBody.remove(element); } + public boolean removeNode(ResXmlNode node){ + if(node instanceof ResXmlElement){ + return removeElement((ResXmlElement) node); + } + return mBody.remove(node); + } public int countElements(){ int result = 0; - for(ResXmlNode xmlNode:listXmlNodes()){ + for(ResXmlNode xmlNode: getXmlNodes()){ if(xmlNode instanceof ResXmlElement){ result++; } @@ -354,11 +360,14 @@ } } public List listXmlNodes(){ + return new ArrayList<>(getXmlNodes()); + } + private List getXmlNodes(){ return mBody.getChildes(); } public List listXmlText(){ List results=new ArrayList<>(); - for(ResXmlNode xmlNode:listXmlNodes()){ + for(ResXmlNode xmlNode: getXmlNodes()){ if(xmlNode instanceof ResXmlTextNode){ results.add(((ResXmlTextNode) xmlNode).getResXmlText()); } @@ -367,7 +376,7 @@ } public List listXmlTextNodes(){ List results=new ArrayList<>(); - for(ResXmlNode xmlNode:listXmlNodes()){ + for(ResXmlNode xmlNode: getXmlNodes()){ if(xmlNode instanceof ResXmlTextNode){ results.add((ResXmlTextNode) xmlNode); } @@ -376,7 +385,7 @@ } public List listElements(){ List results=new ArrayList<>(); - for(ResXmlNode xmlNode:listXmlNodes()){ + for(ResXmlNode xmlNode: getXmlNodes()){ if(xmlNode instanceof ResXmlElement){ results.add((ResXmlElement) xmlNode); } @@ -782,7 +791,7 @@ jsonObject.put(NAME_attributes, attrArray); i=0; JSONArray childes=new JSONArray(); - for(ResXmlNode xmlNode:listXmlNodes()){ + for(ResXmlNode xmlNode: getXmlNodes()){ childes.put(i, xmlNode.toJson()); i++; } @@ -879,7 +888,7 @@ if(comment!=null){ xmlElement.addComment(new XMLComment(comment)); } - for(ResXmlNode xmlNode:listXmlNodes()){ + for(ResXmlNode xmlNode: getXmlNodes()){ if(xmlNode instanceof ResXmlElement){ ResXmlElement childResXmlElement=(ResXmlElement)xmlNode; XMLElement childXMLElement = diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlIDMap.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlIDMap.java index 678261c..c1f78c6 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlIDMap.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlIDMap.java @@ -20,6 +20,7 @@ import com.reandroid.arsc.chunk.Chunk; import com.reandroid.arsc.header.HeaderBlock; import com.reandroid.arsc.item.ResXmlID; + import com.reandroid.arsc.item.ResXmlString; import com.reandroid.arsc.pool.ResXmlStringPool; import java.util.Collection; @@ -31,6 +32,32 @@ this.mResXmlIDArray=new ResXmlIDArray(getHeaderBlock()); addChild(mResXmlIDArray); } + void removeSafely(ResXmlID resXmlID){ + if(resXmlID==null + || resXmlID.getParent()==null + || resXmlID.getIndex()<0 + || resXmlID.hasReference()){ + return; + } + ResXmlString xmlString = resXmlID.getResXmlString(); + if(xmlString == null + || xmlString.getParent()==null + || xmlString.getIndex()<0 + || xmlString.hasReference()){ + return; + } + ResXmlStringPool stringPool = getXmlStringPool(); + if(stringPool == null){ + return; + } + resXmlID.set(0); + ResXmlIDArray idArray = getResXmlIDArray(); + idArray.remove(resXmlID); + stringPool.removeString(xmlString); + } + public int countId(){ + return getResXmlIDArray().childesCount(); + } public void destroy(){ getResXmlIDArray().clearChildes(); } diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java index d65f8da..eb5460f 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java @@ -16,15 +16,17 @@ package com.reandroid.arsc.chunk.xml; import com.reandroid.arsc.chunk.ChunkType; - import com.reandroid.arsc.item.ResXmlString; import com.reandroid.xml.SchemaAttr; import com.reandroid.xml.XMLAttribute; + import java.util.HashSet; import java.util.Set; public class ResXmlStartNamespace extends ResXmlNamespace { + private final Set mReferencedAttributes; public ResXmlStartNamespace() { super(ChunkType.XML_START_NAMESPACE); + this.mReferencedAttributes = new HashSet<>(); } public ResXmlEndNamespace getEnd(){ return (ResXmlEndNamespace) getPair(); @@ -46,6 +48,26 @@ if(end!=null){ end.onRemoved(); } + mReferencedAttributes.clear(); + } + public boolean hasReferencedAttributes(){ + return mReferencedAttributes.size()>0; + } + public void clearReferencedAttributes(){ + mReferencedAttributes.clear(); + } + public Set getReferencedAttributes(){ + return mReferencedAttributes; + } + void addAttributeReference(ResXmlAttribute attribute){ + if(attribute!=null){ + mReferencedAttributes.add(attribute); + } + } + void removeAttributeReference(ResXmlAttribute attribute){ + if(attribute!=null){ + mReferencedAttributes.remove(attribute); + } } public XMLAttribute decodeToXml(){ String uri=getUri(); diff --git a/src/main/java/com/reandroid/arsc/item/ResXmlID.java b/src/main/java/com/reandroid/arsc/item/ResXmlID.java index 514c715..b41aae5 100755 --- a/src/main/java/com/reandroid/arsc/item/ResXmlID.java +++ b/src/main/java/com/reandroid/arsc/item/ResXmlID.java @@ -15,7 +15,6 @@ */ package com.reandroid.arsc.item; -import com.reandroid.arsc.base.Block; import com.reandroid.arsc.chunk.xml.ResXmlDocument; import com.reandroid.arsc.pool.ResXmlStringPool; @@ -55,7 +54,7 @@ public class ResXmlID extends IntegerItem { } @Override public void onIndexChanged(int oldIndex, int newIndex){ - reUpdateReferences(newIndex); + //TODO: We have to ignore this to avoid conflict with ResXmlIDMap.removeSafely } public String getName(){ ResXmlString xmlString = getResXmlString(); diff --git a/src/main/java/com/reandroid/arsc/pool/ResXmlStringPool.java b/src/main/java/com/reandroid/arsc/pool/ResXmlStringPool.java index ab625de..be7ee18 100755 --- a/src/main/java/com/reandroid/arsc/pool/ResXmlStringPool.java +++ b/src/main/java/com/reandroid/arsc/pool/ResXmlStringPool.java @@ -22,10 +22,7 @@ import com.reandroid.arsc.array.StyleArray; import com.reandroid.arsc.chunk.xml.ResXmlDocument; import com.reandroid.arsc.chunk.xml.ResXmlIDMap; import com.reandroid.arsc.group.StringGroup; -import com.reandroid.arsc.item.IntegerArray; -import com.reandroid.arsc.item.IntegerItem; -import com.reandroid.arsc.item.ResXmlID; -import com.reandroid.arsc.item.ResXmlString; +import com.reandroid.arsc.item.*; import java.util.Objects; @@ -34,6 +31,30 @@ import java.util.Objects; super(is_utf8); } @Override + public ResXmlString removeReference(ReferenceItem referenceItem){ + if(referenceItem==null){ + return null; + } + ResXmlString stringItem = super.removeReference(referenceItem); + removeNotUsedItem(stringItem); + return stringItem; + } + private void removeNotUsedItem(ResXmlString xmlString){ + if(xmlString == null || xmlString.hasReference()){ + return; + } + ResXmlIDMap idMap = getResXmlIDMap(); + int lastIdIndex = -1; + if(idMap!=null){ + lastIdIndex = idMap.countId() - 1; + } + if(idMap!=null && xmlString.getIndex()>lastIdIndex){ + removeString(xmlString); + }else { + xmlString.set(""); + } + } + @Override StringArray newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { return new ResXmlStringArray(offsets, itemCount, itemStart, is_utf8); } diff --git a/src/main/java/com/reandroid/arsc/pool/StringPool.java b/src/main/java/com/reandroid/arsc/pool/StringPool.java index 6145b87..9818711 100755 --- a/src/main/java/com/reandroid/arsc/pool/StringPool.java +++ b/src/main/java/com/reandroid/arsc/pool/StringPool.java @@ -68,6 +68,9 @@ package com.reandroid.arsc.pool; mUniqueMap = new HashMap<>(); } + public void removeString(T item){ + getStringsArray().remove(item); + } public void destroy(){ getStyleArray().clearChildes(); getStringsArray().clearChildes(); diff --git a/src/main/java/com/reandroid/arsc/value/ValueItem.java b/src/main/java/com/reandroid/arsc/value/ValueItem.java index 1d1fd5a..c269063 100755 --- a/src/main/java/com/reandroid/arsc/value/ValueItem.java +++ b/src/main/java/com/reandroid/arsc/value/ValueItem.java @@ -27,6 +27,7 @@ import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONObject; import java.io.IOException; +import java.util.Objects; public abstract class ValueItem extends BlockItem implements Value, JSONConvert{ @@ -150,16 +151,14 @@ import java.io.IOException; return; } mStringReference = null; + onUnlinkDataString(stringReference); + } + protected void onUnlinkDataString(ReferenceItem referenceItem){ StringPool stringPool = getStringPool(); if(stringPool == null){ return; } - StringItem stringItem = stringPool.removeReference(stringReference); - if(stringItem!=null){ - onUnlinkDataString(stringItem); - } - } - protected void onUnlinkDataString(StringItem stringItem){ + stringPool.removeReference(referenceItem); } public StringPool getStringPool(){ Block parent = getParent(); @@ -204,6 +203,10 @@ import java.io.IOException; return null; } public void setValueAsString(String str){ + if(getValueType() != ValueType.STRING + && Objects.equals(str, getValueAsString())){ + return; + } StringItem stringItem = getStringPool().getOrCreate(str); setData(stringItem.getIndex()); setValueType(ValueType.STRING);