diff --git a/src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java b/src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java index cfe52d7..7da21f6 100644 --- a/src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java +++ b/src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java @@ -4,11 +4,16 @@ import com.reandroid.lib.arsc.item.IntegerArray; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.value.EntryBlock; +import java.util.Iterator; + public class EntryBlockArray extends OffsetBlockArray { public EntryBlockArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart){ super(offsets, itemCount, itemStart); } + public boolean isEmpty(){ + return !iterator(true).hasNext(); + } public void setEntry(short entryId, EntryBlock entryBlock){ setItem(entryId, entryBlock); } diff --git a/src/main/java/com/reandroid/lib/arsc/array/OffsetBlockArray.java b/src/main/java/com/reandroid/lib/arsc/array/OffsetBlockArray.java index 5af4929..bab0c4c 100644 --- a/src/main/java/com/reandroid/lib/arsc/array/OffsetBlockArray.java +++ b/src/main/java/com/reandroid/lib/arsc/array/OffsetBlockArray.java @@ -32,6 +32,14 @@ public abstract class OffsetBlockArray extends BlockArray im this.mEnd4Block.fill(b); } @Override + public void clearChildes(){ + super.clearChildes(); + mOffsets.clear(); + mItemStart.set(0); + mItemCount.set(0); + mEnd4Block.clear(); + } + @Override public int countBytes(){ int result=super.countBytes(); int endCount=mEnd4Block.countBytes(); @@ -58,7 +66,8 @@ public abstract class OffsetBlockArray extends BlockArray im } @Override protected void onRefreshed() { - mOffsets.setSize(childesCount()); + int count=childesCount(); + mOffsets.setSize(count); T[] childes=getChildes(); int sum=0; if(childes!=null){ @@ -69,6 +78,10 @@ public abstract class OffsetBlockArray extends BlockArray im if(item==null || item.isNull()){ offset=-1; }else { + // slow but accurate + //offset=countUpTo(item); + + // fast but fails for duplicate items offset=sum; sum+=item.countBytes(); } @@ -79,10 +92,20 @@ public abstract class OffsetBlockArray extends BlockArray im refreshStart(); refreshEnd4Block(); } + public void refreshCountAndStart(){ + refreshCount(); + refreshStart(); + } private void refreshCount(){ mItemCount.set(childesCount()); } private void refreshStart(){ + int count=childesCount(); + if(count==0){ + mItemStart.set(0); + mEnd4Block.clear(); + return; + } Block parent=getParent(); if(parent==null){ return; diff --git a/src/main/java/com/reandroid/lib/arsc/array/PackageArray.java b/src/main/java/com/reandroid/lib/arsc/array/PackageArray.java index 2cc92ce..a93914b 100644 --- a/src/main/java/com/reandroid/lib/arsc/array/PackageArray.java +++ b/src/main/java/com/reandroid/lib/arsc/array/PackageArray.java @@ -8,6 +8,7 @@ import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.IntegerItem; import java.io.IOException; +import java.util.Iterator; public class PackageArray extends BlockArray implements BlockLoad { private final IntegerItem mPackageCount; @@ -15,6 +16,26 @@ public class PackageArray extends BlockArray implements BlockLoad this.mPackageCount=packageCount; mPackageCount.setBlockLoad(this); } + public PackageBlock getOrCreate(byte pkgId){ + PackageBlock packageBlock=getPackageBlockById(pkgId); + if(packageBlock!=null){ + return packageBlock; + } + packageBlock=createNext(); + packageBlock.setId(pkgId); + packageBlock.setName("PACKAGE NAME"); + return packageBlock; + } + public PackageBlock getPackageBlockById(byte pkgId){ + Iterator itr=iterator(true); + while (itr.hasNext()){ + PackageBlock packageBlock=itr.next(); + if(packageBlock.getId()==pkgId){ + return packageBlock; + } + } + return null; + } @Override public PackageBlock newInstance() { return new PackageBlock(); diff --git a/src/main/java/com/reandroid/lib/arsc/array/ResXmlAttributeArray.java b/src/main/java/com/reandroid/lib/arsc/array/ResXmlAttributeArray.java index 6411b5c..9c529ba 100644 --- a/src/main/java/com/reandroid/lib/arsc/array/ResXmlAttributeArray.java +++ b/src/main/java/com/reandroid/lib/arsc/array/ResXmlAttributeArray.java @@ -1,6 +1,8 @@ package com.reandroid.lib.arsc.array; +import com.reandroid.lib.arsc.base.Block; import com.reandroid.lib.arsc.base.BlockArray; +import com.reandroid.lib.arsc.chunk.xml.ResXmlStartElement; import com.reandroid.lib.arsc.header.HeaderBlock; import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.chunk.xml.ResXmlAttribute; @@ -17,6 +19,19 @@ public class ResXmlAttributeArray extends BlockArray { this.mAttributeStart=attributeStart; this.mAttributeCount=attributeCount; } + private void refreshCount(){ + short count= (short) childesCount(); + mAttributeCount.set(count); + } + private void refreshStart(){ + Block parent=getParent(); + if(parent==null){ + return; + } + int start = parent.countUpTo(this); + start=start-mHeaderBlock.countBytes(); + mAttributeStart.set((short) start); + } @Override public ResXmlAttribute newInstance() { return new ResXmlAttribute(); @@ -27,7 +42,8 @@ public class ResXmlAttributeArray extends BlockArray { } @Override protected void onRefreshed() { - + refreshCount(); + refreshStart(); } @Override public void onReadBytes(BlockReader reader) throws IOException { diff --git a/src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java b/src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java index d67ef04..24d10ad 100644 --- a/src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java +++ b/src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java @@ -5,13 +5,39 @@ import com.reandroid.lib.arsc.chunk.TypeBlock; import com.reandroid.lib.arsc.container.SpecTypePair; import com.reandroid.lib.arsc.value.EntryBlock; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; public class SpecTypePairArray extends BlockArray { public SpecTypePairArray(){ super(); } + public void removeEmptyPairs(){ + List allPairs=new ArrayList<>(listItems()); + boolean foundEmpty=false; + for(SpecTypePair typePair:allPairs){ + typePair.removeEmptyTypeBlocks(); + if(typePair.isEmpty()){ + super.remove(typePair, false); + foundEmpty=true; + } + } + if(foundEmpty){ + trimNullBlocks(); + } + } + public boolean isEmpty(){ + Iterator iterator=iterator(true); + while (iterator.hasNext()){ + SpecTypePair pair=iterator.next(); + if(!pair.isEmpty()){ + return false; + } + } + return true; + } public EntryBlock getOrCreateEntry(byte typeId, short entryId, String qualifiers){ TypeBlock typeBlock=getOrCreateTypeBlock(typeId, qualifiers); return typeBlock.getOrCreateEntry(entryId); diff --git a/src/main/java/com/reandroid/lib/arsc/array/StringArray.java b/src/main/java/com/reandroid/lib/arsc/array/StringArray.java index 3e61123..877d169 100644 --- a/src/main/java/com/reandroid/lib/arsc/array/StringArray.java +++ b/src/main/java/com/reandroid/lib/arsc/array/StringArray.java @@ -4,6 +4,9 @@ import com.reandroid.lib.arsc.item.IntegerArray; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.item.StringItem; +import java.util.ArrayList; +import java.util.List; + public abstract class StringArray extends OffsetBlockArray{ private boolean mUtf8; public StringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { @@ -11,6 +14,20 @@ public abstract class StringArray extends OffsetBlockArray this.mUtf8=is_utf8; setEndBytes((byte)0x00); } + public List removeUnusedStrings(){ + List allUnused=listUnusedStrings(); + remove(allUnused); + return allUnused; + } + public List listUnusedStrings(){ + List results=new ArrayList<>(); + for(T item:listItems()){ + if(item.getReferencedList().size()==0){ + results.add(item); + } + } + return results; + } public void setUtf8(boolean is_utf8){ if(mUtf8==is_utf8){ return; diff --git a/src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java b/src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java index 27dc6ed..6518d16 100644 --- a/src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java +++ b/src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java @@ -12,6 +12,7 @@ import com.reandroid.lib.arsc.value.ResConfig; import java.io.IOException; import java.util.AbstractList; +import java.util.ArrayList; import java.util.List; public class TypeBlockArray extends BlockArray { @@ -19,10 +20,31 @@ public class TypeBlockArray extends BlockArray { public TypeBlockArray(){ super(); } + public void removeEmptyBlocks(){ + List allTypes=new ArrayList<>(listItems()); + boolean foundEmpty=false; + for(TypeBlock typeBlock:allTypes){ + if(typeBlock.isEmpty()){ + super.remove(typeBlock, false); + foundEmpty=true; + } + } + if(foundEmpty){ + trimNullBlocks(); + } + } public EntryBlock getOrCreateEntry(short entryId, String qualifiers){ TypeBlock typeBlock=getOrCreate(qualifiers); return typeBlock.getOrCreateEntry(entryId); } + public boolean isEmpty(){ + for(TypeBlock typeBlock:listItems()){ + if(typeBlock!=null && !typeBlock.isEmpty()){ + return false; + } + } + return true; + } public EntryBlock getEntry(short entryId, String qualifiers){ TypeBlock typeBlock=getTypeBlock(qualifiers); if(typeBlock==null){ @@ -142,6 +164,14 @@ public class TypeBlockArray extends BlockArray { return null; } @Override + protected boolean remove(TypeBlock block, boolean trim){ + if(block==null){ + return false; + } + block.cleanEntries(); + return super.remove(block, trim); + } + @Override public TypeBlock newInstance() { byte id=getTypeId(); TypeBlock typeBlock=new TypeBlock(); diff --git a/src/main/java/com/reandroid/lib/arsc/base/BlockArray.java b/src/main/java/com/reandroid/lib/arsc/base/BlockArray.java index e10ba73..1667c8f 100644 --- a/src/main/java/com/reandroid/lib/arsc/base/BlockArray.java +++ b/src/main/java/com/reandroid/lib/arsc/base/BlockArray.java @@ -49,7 +49,7 @@ public abstract class BlockArray extends BlockContainer impl } changeSize(diff); } - public final void clearChildes(){ + public void clearChildes(){ T[] allChildes=elementData; if(allChildes==null || allChildes.length==0){ return; @@ -118,6 +118,9 @@ public abstract class BlockArray extends BlockContainer impl block.setIndex(index); block.setParent(this); } + public final int countNonNull(){ + return countNonNull(true); + } public final int childesCount(){ return elementData.length; } @@ -179,7 +182,16 @@ public abstract class BlockArray extends BlockContainer impl } return false; } + public void remove(Collection blockList){ + for(T block:blockList){ + remove(block, false); + } + trimNullBlocks(); + } public boolean remove(T block){ + return remove(block, true); + } + protected boolean remove(T block, boolean trim){ T[] items=elementData; if(block==null||items==null){ return false; @@ -192,17 +204,17 @@ public abstract class BlockArray extends BlockContainer impl found=true; } } - if(found){ + if(found && trim){ trimNullBlocks(); } return found; } - private void trimNullBlocks(){ + protected void trimNullBlocks(){ T[] items=elementData; if(items==null){ return; } - int count=countNonNull(); + int count=countNonNull(false); int len=items.length; if(count==len){ return; @@ -219,7 +231,7 @@ public abstract class BlockArray extends BlockContainer impl } elementData=update; } - private int countNonNull(){ + private int countNonNull(boolean is_null_check){ T[] items=elementData; if(items==null){ return 0; @@ -227,6 +239,9 @@ public abstract class BlockArray extends BlockContainer impl int result=0; for(T block:items){ if(block!=null){ + if(is_null_check && block.isNull()){ + continue; + } result++; } } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/BaseChunk.java b/src/main/java/com/reandroid/lib/arsc/chunk/BaseChunk.java index 3334816..e1b6a02 100644 --- a/src/main/java/com/reandroid/lib/arsc/chunk/BaseChunk.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/BaseChunk.java @@ -32,7 +32,6 @@ public abstract class BaseChunk extends ExpandableBlockContainer { public void onChunkLoaded(){ } - @Override public void onReadBytes(BlockReader reader) throws IOException { HeaderBlock headerBlock=reader.readHeaderBlock(); diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java index c31b529..edf0b0a 100644 --- a/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java @@ -4,22 +4,23 @@ import com.reandroid.lib.arsc.array.SpecTypePairArray; import com.reandroid.lib.arsc.base.Block; import com.reandroid.lib.arsc.container.PackageLastBlocks; import com.reandroid.lib.arsc.container.SpecTypePair; +import com.reandroid.lib.arsc.decoder.ResourceNameProvider; import com.reandroid.lib.arsc.group.EntryGroup; -import com.reandroid.lib.arsc.group.ItemGroup; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.item.PackageName; import com.reandroid.lib.arsc.item.ReferenceItem; -import com.reandroid.lib.arsc.item.SpecString; import com.reandroid.lib.arsc.pool.SpecStringPool; import com.reandroid.lib.arsc.pool.TableStringPool; import com.reandroid.lib.arsc.pool.TypeStringPool; +import com.reandroid.lib.arsc.value.BaseResValue; import com.reandroid.lib.arsc.value.EntryBlock; import com.reandroid.lib.arsc.value.LibraryInfo; +import com.reandroid.lib.arsc.value.ResValueBag; import java.util.*; -public class PackageBlock extends BaseChunk { +public class PackageBlock extends BaseChunk implements ResourceNameProvider { private final IntegerItem mPackageId; private final PackageName mPackageName; @@ -72,6 +73,12 @@ public class PackageBlock extends BaseChunk { addChild(mPackageLastBlocks); } + public void removeEmpty(){ + getSpecTypePairArray().removeEmptyPairs(); + } + public boolean isEmpty(){ + return getSpecTypePairArray().isEmpty(); + } public int getId(){ return mPackageId.get(); } @@ -178,11 +185,14 @@ public class PackageBlock extends BaseChunk { public TypeBlock getTypeBlock(byte typeId, String qualifiers){ return getSpecTypePairArray().getTypeBlock(typeId, qualifiers); } + public Map getEntriesGroupMap(){ + return mEntriesGroup; + } public Collection listEntryGroup(){ - return mEntriesGroup.values(); + return getEntriesGroupMap().values(); } public EntryGroup getEntryGroup(int resId){ - return mEntriesGroup.get(resId); + return getEntriesGroupMap().get(resId); } public void updateEntry(EntryBlock entryBlock){ if(entryBlock==null||entryBlock.isNull()){ @@ -191,6 +201,20 @@ public class PackageBlock extends BaseChunk { updateEntryGroup(entryBlock); updateEntryTableReferences(entryBlock); } + public void removeEntryGroup(EntryBlock entryBlock){ + if(entryBlock==null){ + return; + } + int id=entryBlock.getResourceId(); + EntryGroup group=getEntriesGroupMap().get(id); + if(group==null){ + return; + } + group.remove(entryBlock); + if(group.size()==0){ + getEntriesGroupMap().remove(id); + } + } private void updateEntryTableReferences(EntryBlock entryBlock){ TableBlock tableBlock=getTableBlock(); if(tableBlock==null){ @@ -202,10 +226,10 @@ public class PackageBlock extends BaseChunk { } private void updateEntryGroup(EntryBlock entryBlock){ int resId=entryBlock.getResourceId(); - EntryGroup group=mEntriesGroup.get(resId); + EntryGroup group=getEntriesGroupMap().get(resId); if(group==null){ group=new EntryGroup(resId); - mEntriesGroup.put(resId, group); + getEntriesGroupMap().put(resId, group); } group.add(entryBlock); } @@ -246,6 +270,67 @@ public class PackageBlock extends BaseChunk { refreshKeyStrings(); } + @Override + public String getResourceFullName(int resId, boolean includePackageName) { + EntryGroup entryGroup=getEntryGroup(resId); + if(entryGroup==null){ + return null; + } + String type=entryGroup.getTypeName(); + if(type==null){ + return null; + } + String spec=entryGroup.getSpecName(); + if(spec==null){ + return null; + } + StringBuilder builder=new StringBuilder(); + builder.append('@'); + if(includePackageName){ + builder.append(getPackageName()); + builder.append(':'); + } + builder.append(type); + builder.append('/'); + builder.append(spec); + return builder.toString(); + } + + @Override + public String getResourceName(int resId, boolean includePackageName) { + EntryGroup entryGroup=getEntryGroup(resId); + if(entryGroup==null){ + return null; + } + String spec=entryGroup.getSpecName(); + if(spec==null){ + return null; + } + StringBuilder builder=new StringBuilder(); + if(includePackageName){ + builder.append(getPackageName()); + builder.append(':'); + } + builder.append(spec); + return builder.toString(); + } + @Override + public ResValueBag getAttributeBag(int resId){ + EntryGroup entryGroup=getEntryGroup(resId); + if(entryGroup==null){ + return null; + } + EntryBlock entryBlock=entryGroup.pickOne(); + if(entryBlock==null){ + return null; + } + BaseResValue resValue=entryBlock.getResValue(); + if(resValue instanceof ResValueBag){ + return (ResValueBag)resValue; + } + return null; + } + @Override public String toString(){ StringBuilder builder=new StringBuilder(); diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java index 2a9d317..586c479 100644 --- a/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java @@ -1,10 +1,14 @@ package com.reandroid.lib.arsc.chunk; import com.reandroid.lib.arsc.array.PackageArray; +import com.reandroid.lib.arsc.decoder.ResourceNameProvider; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.pool.TableStringPool; +import com.reandroid.lib.arsc.value.ResValueBag; -public class TableBlock extends BaseChunk { +import java.util.Collection; + +public class TableBlock extends BaseChunk implements ResourceNameProvider { private final IntegerItem mPackageCount; private final TableStringPool mTableStringPool; private final PackageArray mPackageArray; @@ -17,16 +21,63 @@ public class TableBlock extends BaseChunk { addChild(mTableStringPool); addChild(mPackageArray); } + public Collection listPackages(){ + return getPackageArray().listItems(); + } public TableStringPool getTableStringPool(){ return mTableStringPool; } + public PackageBlock getPackageBlockById(byte pkgId){ + return getPackageArray().getPackageBlockById(pkgId); + } public PackageArray getPackageArray(){ return mPackageArray; } + private void refreshPackageCount(){ + int count = getPackageArray().childesCount(); + mPackageCount.set(count); + } @Override protected void onChunkRefreshed() { + refreshPackageCount(); + } + @Override + public String getResourceFullName(int resId, boolean includePackageName) { + byte pkgId= (byte) ((resId>>24)&0xFF); + if(pkgId==0){ + return null; + } + PackageBlock packageBlock=getPackageBlockById(pkgId); + if(packageBlock!=null){ + return packageBlock.getResourceFullName(resId, includePackageName); + } + return null; + } + @Override + public String getResourceName(int resId, boolean includePackageName) { + byte pkgId= (byte) ((resId>>24)&0xFF); + if(pkgId==0){ + return null; + } + PackageBlock packageBlock=getPackageBlockById(pkgId); + if(packageBlock!=null){ + return packageBlock.getResourceName(resId, includePackageName); + } + return null; + } + @Override + public ResValueBag getAttributeBag(int resId){ + byte pkgId= (byte) ((resId>>24)&0xFF); + if(pkgId==0){ + return null; + } + PackageBlock packageBlock=getPackageBlockById(pkgId); + if(packageBlock!=null){ + return packageBlock.getAttributeBag(resId); + } + return null; } @Override public String toString(){ @@ -37,4 +88,5 @@ public class TableBlock extends BaseChunk { builder.append(pkgCount); return builder.toString(); } + } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java b/src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java index 354517b..d75cebd 100644 --- a/src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java @@ -1,6 +1,8 @@ package com.reandroid.lib.arsc.chunk; import com.reandroid.lib.arsc.array.EntryBlockArray; +import com.reandroid.lib.arsc.base.Block; +import com.reandroid.lib.arsc.container.SpecTypePair; import com.reandroid.lib.arsc.item.IntegerArray; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.value.EntryBlock; @@ -28,6 +30,48 @@ public class TypeBlock extends BaseTypeBlock { addChild(mEntryOffsets); addChild(mEntryArray); } + public boolean isEmpty(){ + return getEntryBlockArray().isEmpty(); + } + public boolean isDefault(){ + return getResConfig().isDefault(); + } + public String getQualifiers(){ + return getResConfig().getQualifiers(); + } + public void setQualifiers(String qualifiers){ + getResConfig().parseQualifiers(qualifiers); + } + public int countNonNullEntries(){ + return getEntryBlockArray().countNonNull(); + } + public SpecTypePair getParentSpecTypePair(){ + Block parent=getParent(); + while (parent!=null){ + if(parent instanceof SpecTypePair){ + return (SpecTypePair)parent; + } + parent=parent.getParent(); + } + return null; + } + public void cleanEntries(){ + PackageBlock packageBlock=getPackageBlock(); + List allEntries=listEntries(true); + for(EntryBlock entryBlock:allEntries){ + if(packageBlock!=null){ + packageBlock.removeEntryGroup(entryBlock); + } + entryBlock.setNull(true); + } + } + public void removeEntry(EntryBlock entryBlock){ + PackageBlock packageBlock=getPackageBlock(); + if(packageBlock!=null){ + packageBlock.removeEntryGroup(entryBlock); + } + entryBlock.setNull(true); + } public EntryBlock getOrCreateEntry(short entryId){ return getEntryBlockArray().getOrCreate(entryId); } @@ -45,7 +89,7 @@ public class TypeBlock extends BaseTypeBlock { } public List listEntries(boolean skipNullBlock){ List results=new ArrayList<>(); - Iterator itr = mEntryArray.iterator(skipNullBlock); + Iterator itr = getEntryBlockArray().iterator(skipNullBlock); while (itr.hasNext()){ EntryBlock block=itr.next(); results.add(block); @@ -53,22 +97,22 @@ public class TypeBlock extends BaseTypeBlock { return results; } public EntryBlock getEntryBlock(int entryId){ - return mEntryArray.get(entryId); + return getEntryBlockArray().get(entryId); } @Override void onSetEntryCount(int count) { - mEntryArray.setChildesCount(count); + getEntryBlockArray().setChildesCount(count); } @Override protected void onChunkRefreshed() { - + getEntryBlockArray().refreshCountAndStart(); } @Override public String toString(){ StringBuilder builder=new StringBuilder(); - builder.append(super.toString()); - builder.append(", config="); builder.append(getResConfig().toString()); + builder.append(" "); + builder.append(super.toString()); return builder.toString(); } } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java index f68370e..3bb60fa 100644 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java @@ -69,6 +69,9 @@ public class ResXmlAttribute extends FixedBlockContainer { mRawValue.set(val); } + public ResXmlString getNameString(){ + return getResXmlString(getNameReference()); + } public ValueType getValueType(){ return ValueType.valueOf(getValueTypeByte()); } @@ -104,7 +107,7 @@ public class ResXmlAttribute extends FixedBlockContainer { return getResourceId(getNameReference()); } private int getResourceId(int ref){ - if(ref<=0){ + if(ref<0){ return 0; } ResXmlIDMap xmlIDMap=getResXmlIDMap(); @@ -171,9 +174,9 @@ public class ResXmlAttribute extends FixedBlockContainer { } String valStr=getValueString(); if(valStr!=null){ - return getIndex()+" {"+fullName+"=\""+valStr+"\""+"}"; + return fullName+"=\""+valStr+"\""; } - return getIndex()+" {"+fullName+"}"+"["+getValueType()+"]=\""+getRawValue()+"\""; + return fullName+"["+getValueType()+"]=\""+getRawValue()+"\""; } StringBuilder builder=new StringBuilder(); builder.append(getClass().getSimpleName()); diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java index a1e8f95..99747a8 100644 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java @@ -11,6 +11,8 @@ import com.reandroid.lib.arsc.pool.ResXmlStringPool; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; public class ResXmlElement extends FixedBlockContainer { @@ -36,6 +38,20 @@ public class ResXmlElement extends FixedBlockContainer { addChild(4, mEndElementContainer); addChild(5, mEndNamespaceList); } + public String getTagName(){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.getTagName(); + } + return null; + } + public Collection listResXmlAttributes(){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.listResXmlAttributes(); + } + return new ArrayList<>(); + } public ResXmlStringPool getStringPool(){ Block parent=getParent(); while (parent!=null){ @@ -287,4 +303,25 @@ public class ResXmlElement extends FixedBlockContainer { +hasStartElement()+", hasEnd="+hasEndElement()); } } + @Override + public String toString(){ + ResXmlStartElement start = getStartElement(); + if(start!=null){ + ResXmlText text=getResXmlText(); + StringBuilder builder=new StringBuilder(); + builder.append("<"); + builder.append(start.toString()); + if(text!=null){ + builder.append(">"); + builder.append(text.toString()); + builder.append(""); + }else { + builder.append("/>"); + } + return builder.toString(); + } + return "NULL"; + } } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlIDMap.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlIDMap.java index 2469248..05ca9af 100644 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlIDMap.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlIDMap.java @@ -4,8 +4,10 @@ import com.reandroid.lib.arsc.chunk.ChunkType; import com.reandroid.lib.arsc.array.ResXmlIDArray; import com.reandroid.lib.arsc.chunk.BaseChunk; import com.reandroid.lib.arsc.io.BlockReader; +import com.reandroid.lib.arsc.item.ResXmlID; import java.io.IOException; +import java.util.Collection; public class ResXmlIDMap extends BaseChunk { private final ResXmlIDArray mResXmlIDArray; @@ -18,6 +20,21 @@ public class ResXmlIDMap extends BaseChunk { return mResXmlIDArray; } + public Collection listResXmlID(){ + return getResXmlIDArray().listItems(); + } + public void addResourceId(int index, int resId){ + getResXmlIDArray().addResourceId(index, resId); + } + public ResXmlID getResXmlID(int ref){ + return getResXmlIDArray().get(ref); + } + public ResXmlID getOrCreate(int resId){ + return getResXmlIDArray().getOrCreate(resId); + } + public ResXmlID getByResId(int resId){ + return getResXmlIDArray().getByResId(resId); + } @Override protected void onChunkRefreshed() { diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java index acab3e1..3b9bd74 100644 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java @@ -5,6 +5,8 @@ import com.reandroid.lib.arsc.array.ResXmlAttributeArray; import com.reandroid.lib.arsc.item.IntegerItem; import com.reandroid.lib.arsc.item.ShortItem; +import java.util.Collection; + public class ResXmlStartElement extends BaseXmlChunk { private final ShortItem mAttributeStart; @@ -37,6 +39,12 @@ public class ResXmlStartElement extends BaseXmlChunk { } return prefix+":"+name; } + public Collection listResXmlAttributes(){ + return getResXmlAttributeArray().listItems(); + } + public ResXmlAttributeArray getResXmlAttributeArray(){ + return mAttributeArray; + } public String getPrefix(){ int uriRef=getNamespaceReference(); if(uriRef<0){ @@ -78,14 +86,14 @@ public class ResXmlStartElement extends BaseXmlChunk { return super.toString(); } StringBuilder builder=new StringBuilder(); - builder.append("TAG: line=").append(getLineNumber()).append(" <").append(txt).append(">"); + builder.append(txt); ResXmlAttribute[] allAttr=mAttributeArray.getChildes(); if(allAttr!=null){ for(int i=0;i10){ break; } - builder.append(", "); + builder.append(" "); builder.append(allAttr[i].toString()); } } diff --git a/src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java b/src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java index 2a737ad..f85b51c 100644 --- a/src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java +++ b/src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java @@ -34,6 +34,15 @@ public class SpecTypePair extends BlockContainer { public SpecTypePair(){ this(new SpecBlock(), new TypeBlockArray()); } + public void removeEmptyTypeBlocks(){ + getTypeBlockArray().removeEmptyBlocks(); + } + public boolean isEmpty(){ + return getTypeBlockArray().isEmpty(); + } + public int countTypeBlocks(){ + return getTypeBlockArray().childesCount(); + } public EntryBlock getOrCreateEntry(short entryId, String qualifiers){ return getTypeBlockArray().getOrCreateEntry(entryId, qualifiers); } @@ -49,6 +58,7 @@ public class SpecTypePair extends BlockContainer { public List listResConfig(){ return mTypeBlockArray.listResConfig(); } + public byte getTypeId(){ return mSpecBlock.getTypeId(); } diff --git a/src/main/java/com/reandroid/lib/arsc/decoder/ResDecoder.java b/src/main/java/com/reandroid/lib/arsc/decoder/ResDecoder.java new file mode 100644 index 0000000..18e1cd9 --- /dev/null +++ b/src/main/java/com/reandroid/lib/arsc/decoder/ResDecoder.java @@ -0,0 +1,85 @@ +package com.reandroid.lib.arsc.decoder; + +import com.reandroid.lib.arsc.base.Block; +import com.reandroid.lib.arsc.value.ResValueBag; +import com.reandroid.lib.arsc.value.ResValueBagItem; +import com.reandroid.lib.arsc.value.ValueType; + +public abstract class ResDecoder implements ResourceNameProvider{ + private final ResourceNameStore resourceNameStore; + public ResDecoder(ResourceNameStore store){ + this.resourceNameStore=store; + } + public String decodeAttribute(int attrResId, int rawValue){ + ResValueBag valueBag=getAttributeBag(attrResId); + if(valueBag==null){ + return null; + } + ResValueBagItem[] bagItems=valueBag.getBagItems(); + if(bagItems==null||bagItems.length==0){ + return null; + } + int len=bagItems.length; + ResValueBagItem firstAttrChild=bagItems[0]; + if(len==1){ + return null; + } + if(isFlagAttr(firstAttrChild)){ + for(int i=1;i resourceNameProviders; + public ResourceNameStore(){ + this.resourceNameProviders=new HashSet<>(); + } + public void addResourceNameProvider(ResourceNameProvider provider){ + if(provider!=null){ + resourceNameProviders.add(provider); + } + } + public Set getResourceNameProviders(){ + return resourceNameProviders; + } + @Override + public String getResourceFullName(int resId, boolean includePackageName) { + for(ResourceNameProvider provider:this.resourceNameProviders){ + String name=provider.getResourceFullName(resId, includePackageName); + if(name!=null){ + return name; + } + } + return null; + } + + @Override + public String getResourceName(int resId, boolean includePackageName) { + for(ResourceNameProvider provider:this.resourceNameProviders){ + String name=provider.getResourceName(resId, includePackageName); + if(name!=null){ + return name; + } + } + return null; + } + @Override + public ResValueBag getAttributeBag(int resId) { + for(ResourceNameProvider provider:this.resourceNameProviders){ + ResValueBag resValueBag=provider.getAttributeBag(resId); + if(resValueBag!=null){ + return resValueBag; + } + } + return null; + } +} diff --git a/src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java b/src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java new file mode 100644 index 0000000..362b388 --- /dev/null +++ b/src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java @@ -0,0 +1,108 @@ +package com.reandroid.lib.arsc.decoder; + +import com.reandroid.lib.arsc.value.ValueType; + +public class ValueDecoder { + public static String decode(ValueType valueType, int data){ + if(valueType==null){ + return null; + } + switch (valueType){ + case INT_BOOLEAN: + return decodeBoolean(data); + case FIRST_COLOR_INT: + case INT_COLOR_ARGB4: + case INT_COLOR_ARGB8: + case INT_COLOR_RGB4: + case INT_COLOR_RGB8: + case LAST_COLOR_INT: + return decodeColor(data); + case DIMENSION: + case FLOAT: + case FRACTION: + return decodeDimensionOrFloat(valueType, data); + case INT_HEX: + return decodeHex(data); + case INT_DEC: + case FIRST_INT: + case LAST_INT: + return decodeInt(data); + } + return null; + } + private static String decodeHex(int rawVal){ + return String.format("0x%x", rawVal); + } + private static String decodeInt(int rawVal){ + return String.valueOf(rawVal); + } + + private static String decodeBoolean(int data){ + if(data == 0xFFFFFFFF){ + return "true"; + } + return "false"; + } + + private static String decodeColor(int rawVal){ + String hex=String.format("%x", rawVal); + if(hex.length()<=6){ + return String.format("#%06x", rawVal); + } + return String.format("#%08x", rawVal); + } + private static String decodeDimensionOrFloat(ValueType valueType, int rawVal){ + return decodeFloat(rawVal, valueType); + } + private static String decodeFloat(int val, ValueType valueType){ + if(valueType==ValueType.FLOAT){ + float f=Float.intBitsToFloat(val); + return Float.toString(f); + } + float f=complexToFloat(val); + String unit=""; + switch (valueType){ + case FRACTION: + f=f*100; + if((val & 0x3)==0){ + unit="%"; + }else { + unit="%p"; + } + break; + case DIMENSION: + int i=(val & 0xf); + unit=getDimensionUnit(i); + break; + } + return Float.toString(f)+unit; + } + private static float complexToFloat(int complex) { + int y=(complex >> 4) & 0x3; + float result=complex & 0xffffff00; + float y2=RADIX_MULTS[y]; + result=result * y2; + return result; + } + private static String getDimensionUnit(int index){ + if(index<0 || index>DIMENSION_UNIT_STRS.length){ + index=1; + } + return DIMENSION_UNIT_STRS[index]; + } + private static int getDimensionIndex(String unit){ + String[] dims=DIMENSION_UNIT_STRS; + for(int i=0;i { + private byte mCurrentPackageId; + public ResXmlDecoder(ResourceNameStore store){ + super(store); + } + private String decodeToString(ResXmlBlock resXmlBlock){ + StringBuilder builder=new StringBuilder(); + builder.append(""); + builder.append("\n"); + String body=decodeToString(resXmlBlock.getResXmlElement()); + builder.append(body); + return builder.toString(); + } + private String decodeToString(ResXmlElement resXmlElement){ + if(resXmlElement==null){ + return null; + } + StringBuilder builder=new StringBuilder(); + for(int i=0;i childElements=resXmlElement.listElements(); + if(!useEndTag){ + useEndTag=childElements.size()>0; + } + if(!useEndTag){ + builder.append("/>"); + return builder.toString(); + } + builder.append(">"); + if(text!=null){ + builder.append(text); + } + for(ResXmlElement child:childElements){ + builder.append("\n"); + String txtChild=decodeToString(child); + builder.append(txtChild); + } + if(childElements.size()>0){ + builder.append("\n"); + for(int i=0;i"); + return builder.toString(); + } + private String decodeAttributeValue(ResXmlAttribute attribute){ + ValueType valueType=attribute.getValueType(); + if(valueType==ValueType.FIRST_INT || valueType==ValueType.INT_HEX){ + int nameId=attribute.getNameResourceID(); + String val=decodeAttribute(nameId, attribute.getRawValue()); + if(val!=null){ + return val; + } + } + if(valueType==ValueType.REFERENCE){ + int id=attribute.getRawValue(); + byte pkgId= (byte) ((id>>24)&0xFF); + return getResourceFullName(id, pkgId!=getCurrentPackageId()); + } + if(valueType==ValueType.STRING){ + return attribute.getValueString(); + } + String val= ValueDecoder.decode(valueType, attribute.getRawValue()); + if(val!=null){ + return val; + } + return attribute.getRawValue()+"["+valueType+"]"; + } + private String decodeAttributeFullName(ResXmlAttribute attribute){ + StringBuilder builder=new StringBuilder(); + String prefix=getRealAttributeNamePrefix(attribute); + String name=getRealAttributeName(attribute); + if(prefix!=null&&!name.contains(":")){ + builder.append(prefix); + builder.append(':'); + } + builder.append(name); + return builder.toString(); + } + private String getRealAttributeNamePrefix(ResXmlAttribute attribute){ + /* TODO readjust wrong/stripped attribute name prefix prefix; */ + return attribute.getNamePrefix(); + } + private String getRealAttributeName(ResXmlAttribute attribute){ + int nameId=attribute.getNameResourceID(); + ResourceNameStore store=getResourceNameStore(); + byte pkgId= (byte) ((nameId>>24)&0xFF); + String name=store.getResourceName(nameId, getCurrentPackageId()!=pkgId); + if(name!=null){ + return name; + } + return attribute.getName(); + } + private byte getCurrentPackageId(){ + return mCurrentPackageId; + } + @Override + public String decode(byte currentPackageId, ResXmlBlock resXmlBlock) { + this.mCurrentPackageId=currentPackageId; + return decodeToString(resXmlBlock); + } +} diff --git a/src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java b/src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java index ca6b216..bebad35 100644 --- a/src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java +++ b/src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java @@ -53,18 +53,37 @@ public class EntryGroup extends ItemGroup { } return renameOk; } - public TypeString getTypeString(){ + public EntryBlock pickOne(){ + EntryBlock defEntryBlock=getDefault(); + if(defEntryBlock!=null){ + return defEntryBlock; + } + Iterator itr=iterator(true); + while (itr.hasNext()){ + return itr.next(); + } + return null; + } + public EntryBlock getDefault(){ Iterator itr=iterator(true); while (itr.hasNext()){ EntryBlock entryBlock=itr.next(); + if(entryBlock.isDefault()){ + return entryBlock; + } + } + return null; + } + public TypeString getTypeString(){ + EntryBlock entryBlock=pickOne(); + if(entryBlock!=null){ return entryBlock.getTypeString(); } return null; } public SpecString getSpecString(){ - Iterator itr=iterator(true); - while (itr.hasNext()){ - EntryBlock entryBlock=itr.next(); + EntryBlock entryBlock=pickOne(); + if(entryBlock!=null){ return entryBlock.getSpecString(); } return null; @@ -98,6 +117,14 @@ public class EntryGroup extends ItemGroup { } return packageBlock.getSpecStringPool(); } + @Override + public String toString(){ + EntryBlock entryBlock=pickOne(); + if(entryBlock==null){ + return super.toString(); + } + return super.toString()+"{"+entryBlock.toString()+"}"; + } private static BlockArrayCreator create(){ return new BlockArrayCreator(){ @Override @@ -111,4 +138,5 @@ public class EntryGroup extends ItemGroup { } }; } + } diff --git a/src/main/java/com/reandroid/lib/arsc/item/PackageName.java b/src/main/java/com/reandroid/lib/arsc/item/PackageName.java index 882f0b8..2d3f09c 100644 --- a/src/main/java/com/reandroid/lib/arsc/item/PackageName.java +++ b/src/main/java/com/reandroid/lib/arsc/item/PackageName.java @@ -28,7 +28,7 @@ public class PackageName extends StringItem { return decodeUtf16Bytes(getBytesInternal()); } @Override - StyleItem getStyle(){ + public StyleItem getStyle(){ return null; } @Override diff --git a/src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java b/src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java index 70a2673..4199305 100644 --- a/src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java +++ b/src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java @@ -1,11 +1,51 @@ package com.reandroid.lib.arsc.item; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + public class ResXmlID extends IntegerItem { - public ResXmlID(){ - super(); - } + private final List mReferencedList; public ResXmlID(int resId){ super(resId); + this.mReferencedList=new ArrayList<>(); + } + public ResXmlID(){ + this(0); + } + public boolean removeReference(ReferenceItem ref){ + return mReferencedList.remove(ref); + } + public boolean removeAllReference(Collection referenceItems){ + return mReferencedList.removeAll(referenceItems); + } + public void removeAllReference(){ + mReferencedList.clear(); + } + public List getReferencedList(){ + return mReferencedList; + } + public void addReference(ReferenceItem ref){ + if(ref!=null){ + mReferencedList.add(ref); + } + } + public void addReference(Collection refList){ + if(refList==null){ + return; + } + for(ReferenceItem ref:refList){ + addReference(ref); + } + } + private void reUpdateReferences(int newIndex){ + for(ReferenceItem ref:mReferencedList){ + ref.set(newIndex); + } + } + @Override + public void onIndexChanged(int oldIndex, int newIndex){ + reUpdateReferences(newIndex); } @Override public String toString(){ diff --git a/src/main/java/com/reandroid/lib/arsc/item/SpecString.java b/src/main/java/com/reandroid/lib/arsc/item/SpecString.java index a3519b2..d51cb9e 100644 --- a/src/main/java/com/reandroid/lib/arsc/item/SpecString.java +++ b/src/main/java/com/reandroid/lib/arsc/item/SpecString.java @@ -5,7 +5,7 @@ public class SpecString extends StringItem { super(utf8); } @Override - StyleItem getStyle(){ + public StyleItem getStyle(){ return null; } } diff --git a/src/main/java/com/reandroid/lib/arsc/item/StringItem.java b/src/main/java/com/reandroid/lib/arsc/item/StringItem.java index d92ff65..d233bc4 100644 --- a/src/main/java/com/reandroid/lib/arsc/item/StringItem.java +++ b/src/main/java/com/reandroid/lib/arsc/item/StringItem.java @@ -50,7 +50,8 @@ public class StringItem extends BlockItem { } } private void reUpdateReferences(int newIndex){ - for(ReferenceItem ref:mReferencedList){ + List referenceItems=new ArrayList<>(mReferencedList); + for(ReferenceItem ref:referenceItems){ ref.set(newIndex); } } @@ -103,6 +104,9 @@ public class StringItem extends BlockItem { @Override public void onReadBytes(BlockReader reader) throws IOException { + if(reader.available()<4){ + return; + } int len=calculateReadLength(reader); setBytesLength(len, false); byte[] bts=getBytesInternal(); @@ -110,6 +114,9 @@ public class StringItem extends BlockItem { onBytesChanged(); } int calculateReadLength(BlockReader reader) throws IOException { + if(reader.available()<4){ + return reader.available(); + } byte[] bts=new byte[4]; reader.readFully(bts); reader.offset(-4); @@ -156,12 +163,12 @@ public class StringItem extends BlockItem { CharBuffer charBuffer=charsetDecoder.decode(buf); return charBuffer.toString(); } catch (CharacterCodingException ex) { - return new String(allStringBytes, offLen[0], offLen[1]); + return new String(allStringBytes, offLen[0], offLen[1], StandardCharsets.UTF_16LE); } } - StyleItem getStyle(){ + public StyleItem getStyle(){ BaseStringPool stringPool=getStringPool(); if(stringPool==null){ return null; diff --git a/src/main/java/com/reandroid/lib/arsc/item/TypeString.java b/src/main/java/com/reandroid/lib/arsc/item/TypeString.java index 98beaba..3e087d7 100644 --- a/src/main/java/com/reandroid/lib/arsc/item/TypeString.java +++ b/src/main/java/com/reandroid/lib/arsc/item/TypeString.java @@ -9,7 +9,7 @@ public class TypeString extends StringItem { return (byte) (getIndex()+1); } @Override - StyleItem getStyle(){ + public StyleItem getStyle(){ return null; } } diff --git a/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java b/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java index 0f1d73c..f78c0bd 100644 --- a/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java +++ b/src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java @@ -11,9 +11,7 @@ import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.*; import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.*; public abstract class BaseStringPool extends BaseChunk implements BlockLoad { @@ -65,7 +63,26 @@ public abstract class BaseStringPool extends BaseChunk imp mUniqueMap=new HashMap<>(); } - + public List removeUnusedStrings(){ + return getStringsArray().removeUnusedStrings(); + } + public List listUnusedStrings(){ + return getStringsArray().listUnusedStrings(); + } + public StyleArray getStyleArray(){ + return mArrayStyles; + } + public StringArray getStringsArray(){ + return mArrayStrings; + } + public void removeReferences(Collection referenceList){ + if(referenceList==null){ + return; + } + for(ReferenceItem ref:referenceList){ + removeReference(ref); + } + } public boolean removeReference(ReferenceItem ref){ if(ref==null){ return false; @@ -207,7 +224,8 @@ public abstract class BaseStringPool extends BaseChunk imp abstract StringArray newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8); @Override protected void onChunkRefreshed() { - + mArrayStrings.refreshCountAndStart(); + mArrayStyles.refreshCountAndStart(); } @Override public void onChunkLoaded() { diff --git a/src/main/java/com/reandroid/lib/arsc/util/FrameworkTable.java b/src/main/java/com/reandroid/lib/arsc/util/FrameworkTable.java new file mode 100644 index 0000000..4521fce --- /dev/null +++ b/src/main/java/com/reandroid/lib/arsc/util/FrameworkTable.java @@ -0,0 +1,144 @@ +package com.reandroid.lib.arsc.util; + +import com.reandroid.lib.arsc.chunk.PackageBlock; +import com.reandroid.lib.arsc.chunk.TableBlock; +import com.reandroid.lib.arsc.chunk.TypeBlock; +import com.reandroid.lib.arsc.group.EntryGroup; +import com.reandroid.lib.arsc.io.BlockReader; +import com.reandroid.lib.arsc.item.ReferenceItem; +import com.reandroid.lib.arsc.item.TableString; +import com.reandroid.lib.arsc.pool.TableStringPool; +import com.reandroid.lib.arsc.value.EntryBlock; + +import java.io.*; +import java.util.*; + +public class FrameworkTable extends TableBlock { + + public FrameworkTable(){ + super(); + } + public int writeTable(File resourcesArscFile) throws IOException{ + File dir=resourcesArscFile.getParentFile(); + if(dir!=null && !dir.exists()){ + dir.mkdirs(); + } + FileOutputStream outputStream=new FileOutputStream(resourcesArscFile, false); + return writeTable(outputStream); + } + public int writeTable(OutputStream outputStream) throws IOException{ + return writeBytes(outputStream); + } + public void readTable(File resourcesArscFile) throws IOException{ + FileInputStream inputStream=new FileInputStream(resourcesArscFile); + readTable(inputStream); + } + public void readTable(InputStream inputStream) throws IOException{ + BlockReader reader=new BlockReader(inputStream); + super.readBytes(reader); + } + @Override + public int onWriteBytes(OutputStream stream) throws IOException{ + int length=super.onWriteBytes(stream); + stream.flush(); + stream.close(); + return length; + } + @Override + public void onReadBytes(BlockReader reader) throws IOException { + super.onReadBytes(reader); + reader.close(); + } + public void optimize(){ + Map groupMap=scanAllEntryGroups(); + for(EntryGroup group:groupMap.values()){ + List entryBlockList=getEntriesToRemove(group); + removeEntryBlocks(entryBlockList); + } + for(PackageBlock pkg:listPackages()){ + pkg.removeEmpty(); + pkg.refresh(); + } + optimizeTableString(); + refresh(); + } + private void optimizeTableString(){ + removeUnusedTableString(); + shrinkTableString(); + removeUnusedTableString(); + } + private void removeUnusedTableString(){ + TableStringPool tableStringPool=getTableStringPool(); + tableStringPool.getStyleArray().clearChildes(); + tableStringPool.removeUnusedStrings(); + tableStringPool.refresh(); + } + private void shrinkTableString(){ + TableStringPool tableStringPool=getTableStringPool(); + TableString zero=tableStringPool.get(0); + zero.set("Framework string table"); + for(TableString tableString:tableStringPool.getStringsArray().listItems()){ + if(tableString==zero){ + continue; + } + shrinkTableString(zero, tableString); + } + tableStringPool.refresh(); + } + private void shrinkTableString(TableString zero, TableString tableString){ + List allRef = new ArrayList<>(tableString.getReferencedList()); + tableString.removeAllReference(); + for(ReferenceItem item:allRef){ + item.set(zero.getIndex()); + } + zero.addReference(allRef); + } + private void removeEntryBlocks(List removeList){ + for(EntryBlock entryBlock:removeList){ + removeEntryBlock(entryBlock); + } + } + private void removeEntryBlock(EntryBlock entryBlock){ + TypeBlock typeBlock=entryBlock.getTypeBlock(); + if(typeBlock==null){ + return; + } + typeBlock.removeEntry(entryBlock); + + } + private List getEntriesToRemove(EntryGroup group){ + List results=new ArrayList<>(); + EntryBlock mainEntry=group.pickOne(); + if(mainEntry==null){ + return results; + } + Iterator itr = group.iterator(true); + while (itr.hasNext()){ + EntryBlock entryBlock=itr.next(); + if(entryBlock==mainEntry){ + continue; + } + results.add(entryBlock); + } + return results; + } + private Map scanAllEntryGroups(){ + Map results=new HashMap<>(); + for(PackageBlock packageBlock:listPackages()){ + Map map=packageBlock.getEntriesGroupMap(); + for(Map.Entry entry:map.entrySet()){ + int id=entry.getKey(); + EntryGroup group=entry.getValue(); + EntryGroup exist=results.get(id); + if(exist!=null && exist.getDefault()!=null){ + if(exist.getDefault()!=null){ + continue; + } + results.remove(id); + } + results.put(id, group); + } + } + return results; + } +} 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 6b53677..ea61098 100644 --- a/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java +++ b/src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java @@ -1,6 +1,10 @@ package com.reandroid.lib.arsc.value; +import com.reandroid.lib.arsc.chunk.PackageBlock; +import com.reandroid.lib.arsc.chunk.TableBlock; import com.reandroid.lib.arsc.item.ReferenceItem; +import com.reandroid.lib.arsc.item.TableString; +import com.reandroid.lib.arsc.pool.TableStringPool; public abstract class BaseResValueItem extends BaseResValue implements ResValueItem { @@ -8,6 +12,35 @@ public abstract class BaseResValueItem extends BaseResValue implements ResValueI BaseResValueItem(int bytesLength) { super(bytesLength); } + String getString(int ref){ + TableString tableString=getTableString(ref); + if(tableString==null){ + return null; + } + return tableString.getHtml(); + } + TableString getTableString(int ref){ + TableStringPool stringPool=getTableStringPool(); + if(stringPool==null){ + return null; + } + return stringPool.get(ref); + } + TableStringPool getTableStringPool(){ + EntryBlock entryBlock=getEntryBlock(); + if(entryBlock==null){ + return null; + } + PackageBlock packageBlock=entryBlock.getPackageBlock(); + if(packageBlock==null){ + return null; + } + TableBlock tableBlock=packageBlock.getTableBlock(); + if(tableBlock!=null){ + return tableBlock.getTableStringPool(); + } + return null; + } public ReferenceItem getTableStringReference(){ if(getValueType()!=ValueType.STRING){ return 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 e4e16d5..e777599 100644 --- a/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java @@ -26,6 +26,13 @@ public class EntryBlock extends Block { super(); } + public boolean isDefault(){ + TypeBlock typeBlock=getTypeBlock(); + if(typeBlock!=null){ + return typeBlock.isDefault(); + } + return false; + } public List getTableStringReferences(){ if(isNull()){ return null; @@ -98,6 +105,30 @@ public class EntryBlock extends Block { TableStringPool tableStringPool=tableBlock.getTableStringPool(); tableStringPool.addReference(ref); } + private void removeTableReferences(){ + PackageBlock packageBlock=getPackageBlock(); + if(packageBlock==null){ + return; + } + TableBlock tableBlock=packageBlock.getTableBlock(); + if(tableBlock==null){ + return; + } + TableStringPool tableStringPool=tableBlock.getTableStringPool(); + tableStringPool.removeReferences(getTableStringReferences()); + } + private void removeSpecReferences(){ + PackageBlock packageBlock=getPackageBlock(); + if(packageBlock==null){ + return; + } + SpecStringPool specStringPool=packageBlock.getSpecStringPool(); + specStringPool.removeReference(getSpecReferenceBlock()); + } + private void removeAllReferences(){ + removeTableReferences(); + removeSpecReferences(); + } public short getFlags(){ return mFlags.get(); @@ -271,6 +302,7 @@ public class EntryBlock extends Block { if(!mUnLocked){ return; } + removeAllReferences(); mUnLocked =false; mHeaderSize.setParent(null); mFlags.setParent(null); @@ -456,6 +488,13 @@ public class EntryBlock extends Block { builder.append(name); builder.append(')'); } + BaseResValue baseResValue=getResValue(); + if(baseResValue instanceof ResValueInt){ + ResValueInt resValueInt=(ResValueInt)baseResValue; + builder.append(" '"); + builder.append(resValueInt.toString()); + builder.append(" '"); + } return builder.toString(); } diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResConfig.java b/src/main/java/com/reandroid/lib/arsc/value/ResConfig.java index 1d07bfc..f179427 100644 --- a/src/main/java/com/reandroid/lib/arsc/value/ResConfig.java +++ b/src/main/java/com/reandroid/lib/arsc/value/ResConfig.java @@ -425,7 +425,7 @@ public class ResConfig extends BlockArray implements BlockLoad { public String getQualifiers(){ if(mQualifiers==null){ - mQualifiers= ResConfigHelper.toQualifier(this); + mQualifiers = ResConfigHelper.toQualifier(this).trim(); } return mQualifiers; } @@ -503,13 +503,16 @@ public class ResConfig extends BlockArray implements BlockLoad { } return false; } + public boolean isDefault(){ + return getQualifiers().length()==0; + } @Override public String toString(){ String q=getQualifiers(); - if(q.trim().length()==0){ - return "[DEFAULT]"; + if(q.length()==0){ + q="DEFAULT"; } - return q; + return "["+q+"]"; } diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java b/src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java index a5049ee..88a8f46 100644 --- a/src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java +++ b/src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java @@ -9,8 +9,7 @@ import com.reandroid.lib.arsc.item.ReferenceItem; import java.io.IOException; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; +import java.util.*; public class ResValueBag extends BaseResValue { @@ -30,6 +29,9 @@ public class ResValueBag extends BaseResValue { mResValueBagItemArray.setParent(this); mResValueBagItemArray.setIndex(2); } + public ResValueBagItem[] getBagItems(){ + return getResValueBagItemArray().getChildes(); + } public List getTableStringReferences(){ List results=null; for(ResValueBagItem bagItem:getResValueBagItemArray().listItems()){ diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResValueInt.java b/src/main/java/com/reandroid/lib/arsc/value/ResValueInt.java index 789b053..ca8dc9e 100644 --- a/src/main/java/com/reandroid/lib/arsc/value/ResValueInt.java +++ b/src/main/java/com/reandroid/lib/arsc/value/ResValueInt.java @@ -1,5 +1,6 @@ package com.reandroid.lib.arsc.value; +import com.reandroid.lib.arsc.decoder.ValueDecoder; import com.reandroid.lib.arsc.io.BlockReader; import com.reandroid.lib.arsc.item.ReferenceItem; @@ -76,10 +77,19 @@ public class ResValueInt extends BaseResValueItem { }else { builder.append("Unknown"); } + String data=null; + if(vt==ValueType.STRING){ + data=getString(getData()); + }else{ + data= ValueDecoder.decode(vt, getData()); + } + if(data==null){ + data=String.format("0x%08x", getData()); + } builder.append('('); builder.append(String.format("0x%02x", getType())); builder.append("), data="); - builder.append(String.format("0x%08x", getData())); + builder.append(data); return builder.toString(); }