[XML] clean unused strings and res id from pool

This commit is contained in:
REAndroid 2023-03-12 14:11:43 -04:00
parent 9914722bac
commit dfe85947b4
12 changed files with 214 additions and 45 deletions

View File

@ -15,15 +15,45 @@
*/ */
package com.reandroid.arsc.array; 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.IntegerArray;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.ResXmlString; import com.reandroid.arsc.item.ResXmlString;
import java.util.ArrayList;
import java.util.List;
public class ResXmlStringArray extends StringArray<ResXmlString> { public class ResXmlStringArray extends StringArray<ResXmlString> {
public ResXmlStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { public ResXmlStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
super(offsets, itemCount, itemStart, is_utf8); super(offsets, itemCount, itemStart, is_utf8);
} }
@Override @Override
List<ResXmlString> listUnusedStringsToRemove(){
List<ResXmlString> 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()<lastIndex){
continue;
}
results.add(item);
}
return results;
}
private ResXmlIDMap getResXmlIDMap(){
ResXmlDocument xmlDocument = getParentInstance(ResXmlDocument.class);
if(xmlDocument!=null){
return xmlDocument.getResXmlIDMap();
}
return null;
}
@Override
public ResXmlString newInstance() { public ResXmlString newInstance() {
return new ResXmlString(isUtf8()); return new ResXmlString(isUtf8());
} }

View File

@ -51,13 +51,16 @@ public abstract class StringArray<T extends StringItem> extends OffsetBlockArray
}; };
} }
public List<T> removeUnusedStrings(){ public List<T> removeUnusedStrings(){
List<T> unusedList=listUnusedStrings(); List<T> unusedList = listUnusedStringsToRemove();
remove(unusedList); remove(unusedList);
for(T item:unusedList){ for(T item:unusedList){
item.onRemoved(); item.onRemoved();
} }
return unusedList; return unusedList;
} }
List<T> listUnusedStringsToRemove(){
return listUnusedStrings();
}
public List<T> listUnusedStrings(){ public List<T> listUnusedStrings(){
List<T> results=new ArrayList<>(); List<T> results=new ArrayList<>();
for(T item:listItems()){ for(T item:listItems()){

View File

@ -221,7 +221,7 @@ import java.util.*;
} }
return 0; return 0;
} }
public Set<TableBlock> getFrameWorks(){ public List<TableBlock> getFrameWorks(){
return mFrameWorks; return mFrameWorks;
} }
public void addFramework(TableBlock tableBlock){ public void addFramework(TableBlock tableBlock){
@ -229,12 +229,18 @@ import java.util.*;
return; return;
} }
for(TableBlock frm:tableBlock.getFrameWorks()){ for(TableBlock frm:tableBlock.getFrameWorks()){
if(frm==this){ if(frm==this || frm==tableBlock || tableBlock.equals(frm)){
return; return;
} }
} }
mFrameWorks.add(tableBlock); mFrameWorks.add(tableBlock);
} }
public void removeFramework(TableBlock tableBlock){
mFrameWorks.remove(tableBlock);
}
public void clearFrameworks(){
mFrameWorks.clear();
}
@Override @Override
public JSONObject toJson() { public JSONObject toJson() {
JSONObject jsonObject=new JSONObject(); JSONObject jsonObject=new JSONObject();

View File

@ -40,9 +40,13 @@
addChild(mStringReference); addChild(mStringReference);
} }
void onRemoved(){ void onRemoved(){
unLinkStringReference(getHeaderBlock().getCommentReference()); ResXmlStringPool stringPool = getStringPool();
unLinkStringReference(mNamespaceReference); if(stringPool==null){
unLinkStringReference(mStringReference); return;
}
stringPool.removeReference(getHeaderBlock().getCommentReference());
stringPool.removeReference(mNamespaceReference);
stringPool.removeReference(mStringReference);
} }
void linkStringReferences(){ void linkStringReferences(){
linkStringReference(getHeaderBlock().getCommentReference()); linkStringReference(getHeaderBlock().getCommentReference());
@ -68,6 +72,9 @@
return getHeaderBlock().getLineNumber().get(); return getHeaderBlock().getLineNumber().get();
} }
public void setCommentReference(int val){ public void setCommentReference(int val){
if(val == getCommentReference()){
return;
}
IntegerItem comment=getHeaderBlock().getCommentReference(); IntegerItem comment=getHeaderBlock().getCommentReference();
unLinkStringReference(comment); unLinkStringReference(comment);
getHeaderBlock().getCommentReference().set(val); getHeaderBlock().getCommentReference().set(val);
@ -77,6 +84,9 @@
return getHeaderBlock().getCommentReference().get(); return getHeaderBlock().getCommentReference().get();
} }
public void setNamespaceReference(int val){ public void setNamespaceReference(int val){
if(val == getNamespaceReference()){
return;
}
unLinkStringReference(mNamespaceReference); unLinkStringReference(mNamespaceReference);
mNamespaceReference.set(val); mNamespaceReference.set(val);
linkStringReference(mNamespaceReference); linkStringReference(mNamespaceReference);
@ -85,6 +95,9 @@
return mNamespaceReference.get(); return mNamespaceReference.get();
} }
public void setStringReference(int val){ public void setStringReference(int val){
if(val == getStringReference()){
return;
}
unLinkStringReference(mStringReference); unLinkStringReference(mStringReference);
mStringReference.set(val); mStringReference.set(val);
linkStringReference(mStringReference); linkStringReference(mStringReference);

View File

@ -29,6 +29,7 @@
import com.reandroid.xml.XMLException; import com.reandroid.xml.XMLException;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
public class ResXmlAttribute extends ValueItem implements Comparable<ResXmlAttribute>{ public class ResXmlAttribute extends ValueItem implements Comparable<ResXmlAttribute>{
private ReferenceItem mNSReference; private ReferenceItem mNSReference;
@ -94,6 +95,9 @@
setNameReference(xmlID.getIndex()); setNameReference(xmlID.getIndex());
} }
public void setName(String name, int resourceId){ public void setName(String name, int resourceId){
if(Objects.equals(name, getName()) && resourceId==getNameResourceID()){
return;
}
unlink(mNameReference); unlink(mNameReference);
unLinkNameId(getResXmlID()); unLinkNameId(getResXmlID());
ResXmlString xmlString = getOrCreateAttributeName(name, resourceId); ResXmlString xmlString = getOrCreateAttributeName(name, resourceId);
@ -104,13 +108,34 @@
mNameReference = link(OFFSET_NAME); mNameReference = link(OFFSET_NAME);
linkNameId(); 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){ private ResXmlString getOrCreateAttributeName(String name, int resourceId){
StringPool<?> stringPool = getStringPool(); ResXmlStringPool stringPool = getStringPool();
if(stringPool==null){ if(stringPool==null){
return null; return null;
} }
ResXmlStringPool resXmlStringPool = (ResXmlStringPool) stringPool; return stringPool.getOrCreateAttribute(resourceId, name);
return resXmlStringPool.getOrCreateAttribute(resourceId, name);
} }
public ResXmlElement getParentResXmlElement(){ public ResXmlElement getParentResXmlElement(){
return getParent(ResXmlElement.class); return getParent(ResXmlElement.class);
@ -161,6 +186,7 @@
unlink(mNSReference); unlink(mNSReference);
putInteger(getBytesInternal(), OFFSET_NS, ref); putInteger(getBytesInternal(), OFFSET_NS, ref);
mNSReference = link(OFFSET_NS); mNSReference = link(OFFSET_NS);
linkStartNameSpace();
} }
int getNameReference(){ int getNameReference(){
return getInteger(getBytesInternal(), OFFSET_NAME); return getInteger(getBytesInternal(), OFFSET_NAME);
@ -191,17 +217,17 @@
public void onReadBytes(BlockReader reader) throws IOException { public void onReadBytes(BlockReader reader) throws IOException {
super.onReadBytes(reader); super.onReadBytes(reader);
linkAll(); linkAll();
linkStartNameSpace();
} }
@Override @Override
public void onRemoved(){ public void onRemoved(){
super.onRemoved(); super.onRemoved();
unLinkStartNameSpace();
unlinkAll(); unlinkAll();
} }
@Override @Override
protected void onUnlinkDataString(StringItem stringItem){ protected void onUnlinkDataString(ReferenceItem referenceItem){
if(!stringItem.hasReference()){ unlink(referenceItem);
stringItem.set("");
}
} }
@Override @Override
protected void onDataChanged(){ protected void onDataChanged(){
@ -228,6 +254,14 @@
} }
xmlID.removeReference(referenceItem); xmlID.removeReference(referenceItem);
mNameIdReference = null; mNameIdReference = null;
if(xmlID.hasReference()){
return;
}
ResXmlIDMap xmlIDMap = getResXmlIDMap();
if(xmlIDMap == null){
return;
}
xmlIDMap.removeSafely(xmlID);
} }
private void linkAll(){ private void linkAll(){
unlink(mNSReference); unlink(mNSReference);
@ -270,21 +304,20 @@
if(reference == null){ if(reference == null){
return; return;
} }
StringPool<?> stringPool = getStringPool(); ResXmlStringPool stringPool = getStringPool();
if(stringPool==null){ if(stringPool==null){
return; return;
} }
StringItem stringItem = stringPool.get(reference.get()); stringPool.removeReference(reference);
if(stringItem==null){
return;
} }
stringItem.removeReference(reference); @Override
if(!stringItem.hasReference()){ public ResXmlStringPool getStringPool(){
stringItem.set(""); StringPool<?> stringPool = super.getStringPool();
if(stringPool instanceof ResXmlStringPool){
return (ResXmlStringPool) stringPool;
} }
return null;
} }
@Override @Override
public int compareTo(ResXmlAttribute other) { public int compareTo(ResXmlAttribute other) {
int id1=getNameResourceID(); int id1=getNameResourceID();

View File

@ -329,14 +329,20 @@
return getStartElement().getResXmlAttributeArray().remove(resXmlAttribute); return getStartElement().getResXmlAttributeArray().remove(resXmlAttribute);
} }
public boolean removeElement(ResXmlElement element){ public boolean removeElement(ResXmlElement element){
if(element.getParent()!=null){ if(element !=null && element.getParent()!=null){
element.onRemoved(); element.onRemoved();
} }
return mBody.remove(element); return mBody.remove(element);
} }
public boolean removeNode(ResXmlNode node){
if(node instanceof ResXmlElement){
return removeElement((ResXmlElement) node);
}
return mBody.remove(node);
}
public int countElements(){ public int countElements(){
int result = 0; int result = 0;
for(ResXmlNode xmlNode:listXmlNodes()){ for(ResXmlNode xmlNode: getXmlNodes()){
if(xmlNode instanceof ResXmlElement){ if(xmlNode instanceof ResXmlElement){
result++; result++;
} }
@ -354,11 +360,14 @@
} }
} }
public List<ResXmlNode> listXmlNodes(){ public List<ResXmlNode> listXmlNodes(){
return new ArrayList<>(getXmlNodes());
}
private List<ResXmlNode> getXmlNodes(){
return mBody.getChildes(); return mBody.getChildes();
} }
public List<ResXmlText> listXmlText(){ public List<ResXmlText> listXmlText(){
List<ResXmlText> results=new ArrayList<>(); List<ResXmlText> results=new ArrayList<>();
for(ResXmlNode xmlNode:listXmlNodes()){ for(ResXmlNode xmlNode: getXmlNodes()){
if(xmlNode instanceof ResXmlTextNode){ if(xmlNode instanceof ResXmlTextNode){
results.add(((ResXmlTextNode) xmlNode).getResXmlText()); results.add(((ResXmlTextNode) xmlNode).getResXmlText());
} }
@ -367,7 +376,7 @@
} }
public List<ResXmlTextNode> listXmlTextNodes(){ public List<ResXmlTextNode> listXmlTextNodes(){
List<ResXmlTextNode> results=new ArrayList<>(); List<ResXmlTextNode> results=new ArrayList<>();
for(ResXmlNode xmlNode:listXmlNodes()){ for(ResXmlNode xmlNode: getXmlNodes()){
if(xmlNode instanceof ResXmlTextNode){ if(xmlNode instanceof ResXmlTextNode){
results.add((ResXmlTextNode) xmlNode); results.add((ResXmlTextNode) xmlNode);
} }
@ -376,7 +385,7 @@
} }
public List<ResXmlElement> listElements(){ public List<ResXmlElement> listElements(){
List<ResXmlElement> results=new ArrayList<>(); List<ResXmlElement> results=new ArrayList<>();
for(ResXmlNode xmlNode:listXmlNodes()){ for(ResXmlNode xmlNode: getXmlNodes()){
if(xmlNode instanceof ResXmlElement){ if(xmlNode instanceof ResXmlElement){
results.add((ResXmlElement) xmlNode); results.add((ResXmlElement) xmlNode);
} }
@ -782,7 +791,7 @@
jsonObject.put(NAME_attributes, attrArray); jsonObject.put(NAME_attributes, attrArray);
i=0; i=0;
JSONArray childes=new JSONArray(); JSONArray childes=new JSONArray();
for(ResXmlNode xmlNode:listXmlNodes()){ for(ResXmlNode xmlNode: getXmlNodes()){
childes.put(i, xmlNode.toJson()); childes.put(i, xmlNode.toJson());
i++; i++;
} }
@ -879,7 +888,7 @@
if(comment!=null){ if(comment!=null){
xmlElement.addComment(new XMLComment(comment)); xmlElement.addComment(new XMLComment(comment));
} }
for(ResXmlNode xmlNode:listXmlNodes()){ for(ResXmlNode xmlNode: getXmlNodes()){
if(xmlNode instanceof ResXmlElement){ if(xmlNode instanceof ResXmlElement){
ResXmlElement childResXmlElement=(ResXmlElement)xmlNode; ResXmlElement childResXmlElement=(ResXmlElement)xmlNode;
XMLElement childXMLElement = XMLElement childXMLElement =

View File

@ -20,6 +20,7 @@
import com.reandroid.arsc.chunk.Chunk; import com.reandroid.arsc.chunk.Chunk;
import com.reandroid.arsc.header.HeaderBlock; import com.reandroid.arsc.header.HeaderBlock;
import com.reandroid.arsc.item.ResXmlID; import com.reandroid.arsc.item.ResXmlID;
import com.reandroid.arsc.item.ResXmlString;
import com.reandroid.arsc.pool.ResXmlStringPool; import com.reandroid.arsc.pool.ResXmlStringPool;
import java.util.Collection; import java.util.Collection;
@ -31,6 +32,32 @@
this.mResXmlIDArray=new ResXmlIDArray(getHeaderBlock()); this.mResXmlIDArray=new ResXmlIDArray(getHeaderBlock());
addChild(mResXmlIDArray); 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(){ public void destroy(){
getResXmlIDArray().clearChildes(); getResXmlIDArray().clearChildes();
} }

View File

@ -16,15 +16,17 @@
package com.reandroid.arsc.chunk.xml; package com.reandroid.arsc.chunk.xml;
import com.reandroid.arsc.chunk.ChunkType; import com.reandroid.arsc.chunk.ChunkType;
import com.reandroid.arsc.item.ResXmlString;
import com.reandroid.xml.SchemaAttr; import com.reandroid.xml.SchemaAttr;
import com.reandroid.xml.XMLAttribute; import com.reandroid.xml.XMLAttribute;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class ResXmlStartNamespace extends ResXmlNamespace { public class ResXmlStartNamespace extends ResXmlNamespace {
private final Set<ResXmlAttribute> mReferencedAttributes;
public ResXmlStartNamespace() { public ResXmlStartNamespace() {
super(ChunkType.XML_START_NAMESPACE); super(ChunkType.XML_START_NAMESPACE);
this.mReferencedAttributes = new HashSet<>();
} }
public ResXmlEndNamespace getEnd(){ public ResXmlEndNamespace getEnd(){
return (ResXmlEndNamespace) getPair(); return (ResXmlEndNamespace) getPair();
@ -46,6 +48,26 @@
if(end!=null){ if(end!=null){
end.onRemoved(); end.onRemoved();
} }
mReferencedAttributes.clear();
}
public boolean hasReferencedAttributes(){
return mReferencedAttributes.size()>0;
}
public void clearReferencedAttributes(){
mReferencedAttributes.clear();
}
public Set<ResXmlAttribute> 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(){ public XMLAttribute decodeToXml(){
String uri=getUri(); String uri=getUri();

View File

@ -15,7 +15,6 @@
*/ */
package com.reandroid.arsc.item; package com.reandroid.arsc.item;
import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.chunk.xml.ResXmlDocument; import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import com.reandroid.arsc.pool.ResXmlStringPool; import com.reandroid.arsc.pool.ResXmlStringPool;
@ -55,7 +54,7 @@ public class ResXmlID extends IntegerItem {
} }
@Override @Override
public void onIndexChanged(int oldIndex, int newIndex){ public void onIndexChanged(int oldIndex, int newIndex){
reUpdateReferences(newIndex); //TODO: We have to ignore this to avoid conflict with ResXmlIDMap.removeSafely
} }
public String getName(){ public String getName(){
ResXmlString xmlString = getResXmlString(); ResXmlString xmlString = getResXmlString();

View File

@ -22,10 +22,7 @@ import com.reandroid.arsc.array.StyleArray;
import com.reandroid.arsc.chunk.xml.ResXmlDocument; import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import com.reandroid.arsc.chunk.xml.ResXmlIDMap; import com.reandroid.arsc.chunk.xml.ResXmlIDMap;
import com.reandroid.arsc.group.StringGroup; import com.reandroid.arsc.group.StringGroup;
import com.reandroid.arsc.item.IntegerArray; import com.reandroid.arsc.item.*;
import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.ResXmlID;
import com.reandroid.arsc.item.ResXmlString;
import java.util.Objects; import java.util.Objects;
@ -34,6 +31,30 @@ import java.util.Objects;
super(is_utf8); super(is_utf8);
} }
@Override @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<ResXmlString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { StringArray<ResXmlString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
return new ResXmlStringArray(offsets, itemCount, itemStart, is_utf8); return new ResXmlStringArray(offsets, itemCount, itemStart, is_utf8);
} }

View File

@ -68,6 +68,9 @@ package com.reandroid.arsc.pool;
mUniqueMap = new HashMap<>(); mUniqueMap = new HashMap<>();
} }
public void removeString(T item){
getStringsArray().remove(item);
}
public void destroy(){ public void destroy(){
getStyleArray().clearChildes(); getStyleArray().clearChildes();
getStringsArray().clearChildes(); getStringsArray().clearChildes();

View File

@ -27,6 +27,7 @@ import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
public abstract class ValueItem extends BlockItem implements Value, public abstract class ValueItem extends BlockItem implements Value,
JSONConvert<JSONObject>{ JSONConvert<JSONObject>{
@ -150,16 +151,14 @@ import java.io.IOException;
return; return;
} }
mStringReference = null; mStringReference = null;
onUnlinkDataString(stringReference);
}
protected void onUnlinkDataString(ReferenceItem referenceItem){
StringPool<?> stringPool = getStringPool(); StringPool<?> stringPool = getStringPool();
if(stringPool == null){ if(stringPool == null){
return; return;
} }
StringItem stringItem = stringPool.removeReference(stringReference); stringPool.removeReference(referenceItem);
if(stringItem!=null){
onUnlinkDataString(stringItem);
}
}
protected void onUnlinkDataString(StringItem stringItem){
} }
public StringPool<?> getStringPool(){ public StringPool<?> getStringPool(){
Block parent = getParent(); Block parent = getParent();
@ -204,6 +203,10 @@ import java.io.IOException;
return null; return null;
} }
public void setValueAsString(String str){ public void setValueAsString(String str){
if(getValueType() != ValueType.STRING
&& Objects.equals(str, getValueAsString())){
return;
}
StringItem stringItem = getStringPool().getOrCreate(str); StringItem stringItem = getStringPool().getOrCreate(str);
setData(stringItem.getIndex()); setData(stringItem.getIndex());
setValueType(ValueType.STRING); setValueType(ValueType.STRING);