From b110635b47938283e30ab34423d6f4f0b1151c83 Mon Sep 17 00:00:00 2001 From: Ax333l Date: Sat, 8 Apr 2023 22:22:00 +0200 Subject: [PATCH] feat: finish the bag apis --- .../java/com/reandroid/arsc/value/Entry.java | 15 +- .../reandroid/arsc/value/array/ArrayBag.java | 241 +++++++++++------ .../arsc/value/array/ArrayBagItem.java | 171 ++++++------ .../com/reandroid/arsc/value/bag/Bag.java | 21 ++ .../com/reandroid/arsc/value/bag/BagItem.java | 91 +++++++ .../com/reandroid/arsc/value/bag/MapBag.java | 196 ++++++++++++++ .../arsc/value/plurals/PluralsBag.java | 165 +++++++----- .../arsc/value/plurals/PluralsBagItem.java | 193 ++++++-------- .../arsc/value/plurals/PluralsQuantity.java | 11 +- .../reandroid/arsc/value/style/StyleBag.java | 220 ++++++++------- .../arsc/value/style/StyleBagItem.java | 252 ++++++++++-------- 11 files changed, 990 insertions(+), 586 deletions(-) create mode 100644 src/main/java/com/reandroid/arsc/value/bag/Bag.java create mode 100644 src/main/java/com/reandroid/arsc/value/bag/BagItem.java create mode 100644 src/main/java/com/reandroid/arsc/value/bag/MapBag.java diff --git a/src/main/java/com/reandroid/arsc/value/Entry.java b/src/main/java/com/reandroid/arsc/value/Entry.java index 443b7c2..3dff33d 100755 --- a/src/main/java/com/reandroid/arsc/value/Entry.java +++ b/src/main/java/com/reandroid/arsc/value/Entry.java @@ -196,21 +196,12 @@ } private TableEntry ensureTableEntry(boolean is_complex){ TableEntry tableEntry = getTableEntry(); - if(tableEntry == null){ + + boolean is_correct_type = (is_complex && tableEntry instanceof ResTableMapEntry) || (!is_complex && tableEntry instanceof ResTableEntry); + if (tableEntry == null || !is_correct_type) { tableEntry = createTableEntry(is_complex); setTableEntry(tableEntry); - return tableEntry; } - if(is_complex){ - if(tableEntry instanceof ResTableMapEntry){ - return tableEntry; - } - tableEntry = createTableEntry(true); - setTableEntry(tableEntry); - return tableEntry; - } - tableEntry = createTableEntry(false); - setTableEntry(tableEntry); return tableEntry; } diff --git a/src/main/java/com/reandroid/arsc/value/array/ArrayBag.java b/src/main/java/com/reandroid/arsc/value/array/ArrayBag.java index 108a0cb..4ed0a3e 100644 --- a/src/main/java/com/reandroid/arsc/value/array/ArrayBag.java +++ b/src/main/java/com/reandroid/arsc/value/array/ArrayBag.java @@ -13,91 +13,164 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.reandroid.arsc.value.array; + package com.reandroid.arsc.value.array; -import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.ResTableMapEntry; -import com.reandroid.arsc.value.ResValueMap; + import com.reandroid.arsc.array.ResValueMapArray; + import com.reandroid.arsc.value.Entry; + import com.reandroid.arsc.value.ResTableMapEntry; + import com.reandroid.arsc.value.ResValueMap; + import com.reandroid.arsc.value.bag.Bag; -public class ArrayBag { - private final ArrayBagItem[] mBagItems; - private ArrayBag(ArrayBagItem[] bagItems){ - this.mBagItems=bagItems; - } - public ArrayBagItem[] getBagItems() { - return mBagItems; - } - public String getName(){ - Entry entry =getBagItems()[0].getBagItem().getEntry(); - if(entry ==null){ - return null; - } - return entry.getName(); - } - public String getTypeName(){ - Entry entry =getBagItems()[0].getBagItem().getEntry(); - if(entry ==null){ - return null; - } - return entry.getTypeName(); - } - @Override - public String toString() { - StringBuilder builder=new StringBuilder(); - builder.append("<"); - String type=getTypeName(); - builder.append(type); - builder.append(" name=\""); - builder.append(getName()); - builder.append("\">"); - ArrayBagItem[] allItems = getBagItems(); - for(int i=0;i"); - return builder.toString(); - } + import java.util.AbstractList; + import java.util.RandomAccess; - /** The result of this is not always 100% accurate, - * in addition to this use your methods to cross check like type-name == "array"**/ - public static boolean isArray(ResTableMapEntry mapEntry){ - if(mapEntry==null){ - return false; - } - if(mapEntry.getParentId()!=0){ - return false; - } - ArrayBagItem[] arrayBagItems = ArrayBagItem.create(mapEntry.listResValueMap()); - if(arrayBagItems==null || arrayBagItems.length==0){ - return false; - } - for(int i=0;i< arrayBagItems.length; i++){ - ArrayBagItem arrayBagItem = arrayBagItems[i]; - ResValueMap resValueMap = arrayBagItem.getBagItem(); - int name = resValueMap.getName(); - int high = (name >> 16) & 0xffff; - if(high!=0x0100){ - return false; - } - int low = name & 0xffff; - if(low != (i+1)){ - return false; - } - } - return true; - } + public class ArrayBag extends AbstractList implements Bag, RandomAccess { + private final Entry entry; - public static ArrayBag create(ResTableMapEntry mapEntry){ - if(mapEntry==null){ - return null; - } - ArrayBagItem[] bagItems=ArrayBagItem.create(mapEntry.listResValueMap()); - if(bagItems==null){ - return null; - } - return new ArrayBag(bagItems); - } -} + private ArrayBag(Entry entry) { + this.entry = entry; + } + + private ResTableMapEntry getTableEntry() { + return (ResTableMapEntry) entry.getTableEntry(); + } + + private ResValueMapArray getMapArray() { + return getTableEntry().getValue(); + } + + private void updateStructure(int regenStart) { + getTableEntry().setValuesCount(size()); + modCount += 1; + if (regenStart < 1) { + return; + } + + ResValueMapArray array = getMapArray(); + for (int i = regenStart; i < array.childesCount(); i++) { + setIndex(array.get(i), i); + } + } + + @Override + public Entry getEntry() { + return entry; + } + + public ArrayBagItem[] getBagItems() { + return toArray(new ArrayBagItem[0]); + } + + @Override + public int size() { + return getMapArray().childesCount(); + } + + @Override + public ArrayBagItem get(int i) { + return ArrayBagItem.create(getMapArray().get(i)); + } + + @Override + public ArrayBagItem set(int index, ArrayBagItem value) { + ArrayBagItem target = get(index); + value.copyTo(target.getBagItem()); + return target; + } + + private void setIndex(ResValueMap valueMap, int index) { + valueMap.setNameHigh((short) 0x0100); + valueMap.setNameLow((short) (index + 1)); + } + + @Override + public void add(int index, ArrayBagItem value) { + if (index < 0 || index > size()) { + throw new IndexOutOfBoundsException(); + } + if (value == null) { + throw new NullPointerException("value is null"); + } + + ResValueMap valueMap = new ResValueMap(); + setIndex(valueMap, index); + getMapArray().insertItem(index, valueMap); + value.copyTo(valueMap); + updateStructure(index); + } + + @Override + public ArrayBagItem remove(int index) { + ResValueMapArray array = getMapArray(); + ResValueMap target = array.getChildes()[index]; + array.remove(target); + updateStructure(index); + return ArrayBagItem.copyOf(target); + } + + @Override + public void clear() { + getMapArray().clearChildes(); + updateStructure(-1); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("<"); + String type = getTypeName(); + builder.append(type); + builder.append(" name=\""); + builder.append(getName()); + builder.append("\">"); + ArrayBagItem[] allItems = getBagItems(); + for (ArrayBagItem allItem : allItems) { + builder.append("\n "); + builder.append(allItem.toString()); + } + builder.append("\n"); + return builder.toString(); + } + + /** + * The result of this is not always 100% accurate, + * in addition to this use your methods to cross check like type-name == "array" + **/ + public static boolean isArray(Entry entry) { + ArrayBag array = create(entry); + if (array == null) { + return false; + } + ResTableMapEntry tableEntry = array.getTableEntry(); + if (tableEntry.getParentId() != 0) { + return false; + } + ResValueMap[] items = tableEntry.listResValueMap(); + if (items.length == 0) { + return false; + } + + for (int i = 0; i < items.length; i++) { + ResValueMap resValueMap = items[i]; + int name = resValueMap.getName(); + int high = (name >> 16) & 0xffff; + if(high!=0x0100){ + return false; + } + int low = name & 0xffff; + if(low != (i+1)){ + return false; + } + } + return true; + } + + public static ArrayBag create(Entry entry) { + if (entry == null || !entry.isComplex()) { + return null; + } + return new ArrayBag(entry); + } + } diff --git a/src/main/java/com/reandroid/arsc/value/array/ArrayBagItem.java b/src/main/java/com/reandroid/arsc/value/array/ArrayBagItem.java index 4a91758..fdb9022 100644 --- a/src/main/java/com/reandroid/arsc/value/array/ArrayBagItem.java +++ b/src/main/java/com/reandroid/arsc/value/array/ArrayBagItem.java @@ -13,104 +13,83 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.reandroid.arsc.value.array; + package com.reandroid.arsc.value.array; -import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.chunk.TableBlock; -import com.reandroid.arsc.item.TableString; -import com.reandroid.arsc.pool.TableStringPool; -import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.ResValueMap; -import com.reandroid.arsc.value.ValueType; + import com.reandroid.arsc.decoder.ValueDecoder; + import com.reandroid.arsc.item.StringItem; + import com.reandroid.arsc.item.TableString; + import com.reandroid.arsc.value.ValueType; + import com.reandroid.arsc.value.bag.BagItem; + import com.reandroid.arsc.value.ResValueMap; -import java.util.ArrayList; -import java.util.List; + public class ArrayBagItem extends BagItem { + private ArrayBagItem(ResValueMap valueMap) { + super(valueMap); + } - public class ArrayBagItem { - private final ResValueMap mBagItem; - public ArrayBagItem(ResValueMap bagItem){ - this.mBagItem=bagItem; - } - public ResValueMap getBagItem() { - return mBagItem; - } + private ArrayBagItem(StringItem str) { + super(str); + } - public ValueType getValueType(){ - return getBagItem().getValueType(); - } - private TableStringPool getStringPool(){ - Entry entry =getBagItem().getEntry(); - if(entry ==null){ - return null; - } - PackageBlock pkg = entry.getPackageBlock(); - if(pkg==null){ - return null; - } - TableBlock tableBlock= pkg.getTableBlock(); - if(tableBlock==null){ - return null; - } - return tableBlock.getTableStringPool(); - } - public int getValue(){ - return getBagItem().getData(); - } - public boolean hasStringValue(){ - return getValueType()==ValueType.STRING; - } - public boolean hasReferenceValue(){ - return getValueType()==ValueType.REFERENCE; - } - public String getStringValue(){ - ValueType valueType=getValueType(); - if(valueType!=ValueType.STRING){ - throw new IllegalArgumentException("Not STRING ValueType="+valueType); - } - TableStringPool stringPool=getStringPool(); - if(stringPool==null){ - return null; - } - int ref=getValue(); - TableString tableString = stringPool.get(ref); - return tableString.getHtml(); - } + private ArrayBagItem(ValueType valueType, int value) { + super(valueType, value); + } - @Override - public String toString() { - StringBuilder builder=new StringBuilder(); - builder.append(""); - if(hasStringValue()){ - builder.append(getStringValue()); - }else { - builder.append(String.format("0x%08x", getValue())); - } - builder.append(""); - return builder.toString(); - } - public static ArrayBagItem[] create(ResValueMap[] resValueMaps){ - if(resValueMaps ==null){ - return null; - } - int len= resValueMaps.length; - if(len==0){ - return null; - } - List results=new ArrayList<>(); - for(int i=0;i"); + if (hasStringValue()) { + builder.append(getStringValue()); + } else { + builder.append(String.format("0x%08x", getValue())); + } + builder.append(""); + return builder.toString(); + } + + protected static ArrayBagItem create(ResValueMap valueMap) { + if (valueMap == null) { + return null; + } + return new ArrayBagItem(valueMap); + } + + public static ArrayBagItem create(ValueType valueType, int value) { + if (valueType == null || valueType == ValueType.STRING) { + return null; + } + return new ArrayBagItem(valueType, value); + } + + protected static ArrayBagItem copyOf(ResValueMap resValueMap) { + ValueType valueType = resValueMap.getValueType(); + if (valueType == ValueType.STRING) { + return new ArrayBagItem(resValueMap.getDataAsPoolString()); + } else { + return new ArrayBagItem(valueType, resValueMap.getData()); + } + } + + public static ArrayBagItem encoded(ValueDecoder.EncodeResult encodeResult) { + if (encodeResult == null) { + return null; + } + return create(encodeResult.valueType, encodeResult.value); + } + + public static ArrayBagItem integer(int n) { + return create(ValueType.INT_DEC, n); + } + + public static ArrayBagItem string(TableString str) { + if (str == null) { + return null; + } + return new ArrayBagItem(str); + } + + public static ArrayBagItem reference(int resourceId) { + return create(ValueType.REFERENCE, resourceId); + } + } diff --git a/src/main/java/com/reandroid/arsc/value/bag/Bag.java b/src/main/java/com/reandroid/arsc/value/bag/Bag.java new file mode 100644 index 0000000..9958687 --- /dev/null +++ b/src/main/java/com/reandroid/arsc/value/bag/Bag.java @@ -0,0 +1,21 @@ +package com.reandroid.arsc.value.bag; + +import com.reandroid.arsc.value.Entry; + +public interface Bag { + Entry getEntry(); + default String getName(){ + Entry entry =getEntry(); + if(entry ==null){ + return null; + } + return entry.getName(); + } + default String getTypeName(){ + Entry entry =getEntry(); + if(entry ==null){ + return null; + } + return entry.getTypeName(); + } +} diff --git a/src/main/java/com/reandroid/arsc/value/bag/BagItem.java b/src/main/java/com/reandroid/arsc/value/bag/BagItem.java new file mode 100644 index 0000000..b990608 --- /dev/null +++ b/src/main/java/com/reandroid/arsc/value/bag/BagItem.java @@ -0,0 +1,91 @@ +package com.reandroid.arsc.value.bag; + +import com.reandroid.arsc.item.StringItem; +import com.reandroid.arsc.pool.TableStringPool; +import com.reandroid.arsc.value.ResValueMap; +import com.reandroid.arsc.value.ValueType; + +public abstract class BagItem { + protected final ResValueMap mBagItem; + private final ValueType valueType; + private final int data; + private final StringItem string; + + protected BagItem(ResValueMap bagItem) { + this.mBagItem = bagItem; + this.valueType = null; + this.string = null; + this.data = 0; + } + + protected BagItem(ValueType valueType, int data) { + if (valueType == ValueType.STRING) { + throw new IllegalArgumentException("Use the string constructor instead"); + } + this.mBagItem = null; + this.string = null; + this.valueType = valueType; + this.data = data; + } + + protected BagItem(StringItem str) { + this.string = str; + this.mBagItem = null; + this.valueType = ValueType.STRING; + this.data = 0; + } + + public ValueType getValueType() { + if (mBagItem != null) { + return mBagItem.getValueType(); + } + return valueType; + } + + public int getValue() { + if (mBagItem != null) { + return mBagItem.getData(); + } else if (valueType == ValueType.STRING) { + return string.getIndex(); + } else { + return data; + } + } + + public void copyTo(ResValueMap target) { + if (mBagItem != null) { + target.setTypeAndData(mBagItem.getValueType(), mBagItem.getData()); + } else if (valueType == ValueType.STRING) { + TableStringPool targetStrPool = (TableStringPool) target.getStringPool(); + if (targetStrPool == string.getParent(TableStringPool.class)) { + target.setTypeAndData(ValueType.STRING, string.getIndex()); + } else { + target.setTypeAndData(ValueType.STRING, targetStrPool.getOrCreate(string.get()).getIndex()); + } + } else { + target.setTypeAndData(valueType, data); + } + } + + public ResValueMap getBagItem() { + return mBagItem; + } + + public boolean hasStringValue() { + return getValueType() == ValueType.STRING; + } + + public boolean hasReferenceValue() { + return getValueType() == ValueType.REFERENCE; + } + + public String getStringValue() { + if (mBagItem != null) { + return mBagItem.getValueAsString(); + } else if (valueType == ValueType.STRING) { + return string.getHtml(); + } else { + throw new IllegalArgumentException("Not a string"); + } + } +} diff --git a/src/main/java/com/reandroid/arsc/value/bag/MapBag.java b/src/main/java/com/reandroid/arsc/value/bag/MapBag.java new file mode 100644 index 0000000..f0880a0 --- /dev/null +++ b/src/main/java/com/reandroid/arsc/value/bag/MapBag.java @@ -0,0 +1,196 @@ +package com.reandroid.arsc.value.bag; + +import com.reandroid.arsc.array.ResValueMapArray; +import com.reandroid.arsc.chunk.PackageBlock; +import com.reandroid.arsc.chunk.TableBlock; +import com.reandroid.arsc.pool.TableStringPool; +import com.reandroid.arsc.value.ResTableMapEntry; +import com.reandroid.arsc.value.ResValueMap; + +import java.util.*; + +public abstract class MapBag extends AbstractMap implements Bag { + protected final com.reandroid.arsc.value.Entry entry; + private int modCount = 0; + + protected MapBag(com.reandroid.arsc.value.Entry entry) { + this.entry = entry; + } + + protected ResTableMapEntry getTableEntry() { + return (ResTableMapEntry) entry.getTableEntry(); + } + + protected ResValueMapArray getMapArray() { + return getTableEntry().getValue(); + } + + private void updateSize() { + getTableEntry().setValuesCount(size()); + modCount += 1; + } + + @Override + public com.reandroid.arsc.value.Entry getEntry() { + return entry; + } + + protected abstract V createBagItem(ResValueMap valueMap, boolean copied); + + protected abstract ResValueMap newKey(K key); + + protected abstract K getKeyFor(ResValueMap valueMap); + + protected TableStringPool getStringPool() { + com.reandroid.arsc.value.Entry entry = getEntry(); + if (entry == null) { + return null; + } + PackageBlock pkg = entry.getPackageBlock(); + if (pkg == null) { + return null; + } + TableBlock tableBlock = pkg.getTableBlock(); + if (tableBlock == null) { + return null; + } + return tableBlock.getTableStringPool(); + } + + private class MapEntry implements Map.Entry { + private final ResValueMap item; + + private MapEntry(ResValueMap item) { + this.item = item; + } + + @Override + public K getKey() { + return getKeyFor(item); + } + + @Override + public V getValue() { + return createBagItem(item, false); + } + + @Override + public V setValue(V v) { + v.copyTo(item); + return getValue(); + } + } + + private class EntrySet extends AbstractSet> { + @Override + public Iterator> iterator() { + return new Iterator>() { + private final Iterator iterator = getMapArray().iterator(); + private final int expectedModCount = modCount; + + private void checkValidity() { + if (expectedModCount != modCount) { + throw new ConcurrentModificationException("Iterator is no longer valid because the size has changed."); + } + } + + @Override + public boolean hasNext() { + checkValidity(); + return iterator.hasNext(); + } + + @Override + public Entry next() { + checkValidity(); + return new MapEntry(iterator.next()); + } + }; + } + + @Override + public int size() { + return getMapArray().childesCount(); + } + } + + @Override + public V remove(Object key) { + ResValueMapArray array = getMapArray(); + for (ResValueMap item : array.getChildes()) { + if (getKeyFor(item).equals(key)) { + if (!array.remove(item)) { + throw new IllegalStateException("Could not remove item"); + } + updateSize(); + return createBagItem(item, true); + } + } + return null; + } + + @Override + public void clear() { + getMapArray().clearChildes(); + updateSize(); + } + + @Override + public Set> entrySet() { + return new EntrySet(); + } + + @Override + public V put(K key, V value) { + if (key == null) { + throw new NullPointerException("key is null"); + } + if (value == null) { + throw new NullPointerException("value is null"); + } + ResValueMapArray array = getMapArray(); + ResValueMap valueMap = null; + for (ResValueMap item : array.getChildes()) { + if (getKeyFor(item).equals(key)) { + valueMap = item; + break; + } + } + + if (valueMap == null) { + valueMap = newKey(key); + array.add(valueMap); + updateSize(); + } + + value.copyTo(valueMap); + return createBagItem(valueMap, false); + } + + @Override + public void putAll(Map m) { + LinkedHashSet keys = new LinkedHashSet<>(m.keySet()); + ResValueMapArray array = getMapArray(); + + for (ResValueMap item : array.getChildes()) { + K currentKey = getKeyFor(item); + + if (keys.remove(currentKey)) { + V src = m.get(currentKey); + src.copyTo(item); + } + } + + for (K key : keys) { + if (key == null) { + throw new NullPointerException("Key is null"); + } + ResValueMap item = newKey(key); + array.add(item); + V src = m.get(key); + src.copyTo(item); + } + + updateSize(); + } +} diff --git a/src/main/java/com/reandroid/arsc/value/plurals/PluralsBag.java b/src/main/java/com/reandroid/arsc/value/plurals/PluralsBag.java index 8f523ea..50d2774 100644 --- a/src/main/java/com/reandroid/arsc/value/plurals/PluralsBag.java +++ b/src/main/java/com/reandroid/arsc/value/plurals/PluralsBag.java @@ -13,71 +13,110 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.reandroid.arsc.value.plurals; + package com.reandroid.arsc.value.plurals; -import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.ResTableMapEntry; + import com.reandroid.arsc.value.ResValueMap; + import com.reandroid.arsc.value.ValueType; + import com.reandroid.arsc.value.bag.MapBag; - public class PluralsBag { - private final PluralsBagItem[] mBagItems; - private PluralsBag(PluralsBagItem[] bagItems){ - this.mBagItems=bagItems; - } - public PluralsBagItem[] getBagItems() { - return mBagItems; - } - public String getName(){ - Entry entry =getBagItems()[0].getBagItem().getEntry(); - if(entry ==null){ - return null; - } - return entry.getName(); - } - public String getTypeName(){ - Entry entry =getBagItems()[0].getBagItem().getEntry(); - if(entry ==null){ - return null; - } - return entry.getTypeName(); - } + import java.util.Arrays; + import java.util.HashSet; + import java.util.Set; - @Override - public String toString() { - StringBuilder builder=new StringBuilder(); - builder.append("<"); - String type=getTypeName(); - builder.append(type); - builder.append(" name=\""); - builder.append(getName()); - builder.append("\">"); - PluralsBagItem[] allItems = getBagItems(); - for(int i=0;i"); - return builder.toString(); - } + public class PluralsBag extends MapBag { + private PluralsBag(com.reandroid.arsc.value.Entry entry) { + super(entry); + } - /** The result of this is not always 100% accurate, - * in addition to this use your methods to cross check like type-name == "plurals"**/ - public static boolean isPlurals(ResTableMapEntry mapEntry){ - if(mapEntry==null){ - return false; - } - return PluralsBagItem.create(mapEntry.listResValueMap()) != null; - } + @Override + protected PluralsBagItem createBagItem(ResValueMap valueMap, boolean copied) { + if (copied) { + return PluralsBagItem.copyOf(valueMap); + } + return PluralsBagItem.create(valueMap); + } - public static PluralsBag create(ResTableMapEntry mapEntry){ - if(mapEntry==null){ - return null; - } - PluralsBagItem[] bagItems=PluralsBagItem.create(mapEntry.listResValueMap()); - if(bagItems==null){ - return null; - } - return new PluralsBag(bagItems); - } -} + @Override + protected ResValueMap newKey(PluralsQuantity key) { + ResValueMap valueMap = new ResValueMap(); + valueMap.setParent(getMapArray()); + valueMap.setNameHigh((short) 0x0100); + valueMap.setNameLow(key.getId()); + return valueMap; + } + + @Override + protected PluralsQuantity getKeyFor(ResValueMap valueMap) { + return PluralsQuantity.valueOf(valueMap); + } + + public String getQuantityString(PluralsQuantity quantity) { + PluralsBagItem item = get(quantity); + if (item == null) { + return null; + } + return item.getQualityString(); + } + + public void setQuantityString(PluralsQuantity quantity, String str) { + if (quantity == null || str == null) { + return; + } + put(quantity, PluralsBagItem.string(getStringPool().getOrCreate(str))); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("<"); + String type = getTypeName(); + builder.append(type); + builder.append(" name=\""); + builder.append(getName()); + builder.append("\">"); + for (PluralsBagItem pluralsBagItem : values()) { + builder.append("\n "); + builder.append(pluralsBagItem.toString()); + } + builder.append("\n"); + return builder.toString(); + } + + private final static Set validTypes = new HashSet<>(Arrays.asList(ValueType.NULL, ValueType.STRING, ValueType.REFERENCE)); + + /** + * The result of this is not always 100% accurate, + * in addition to this use your methods to cross check like type-name == "plurals" + **/ + public static boolean isPlurals(com.reandroid.arsc.value.Entry entry) { + PluralsBag plurals = create(entry); + if (plurals == null) { + return false; + } + ResValueMap[] items = plurals.getMapArray().getChildes(); + if (items.length == 0) { + return false; + } + + for (ResValueMap item : items) { + if (item == null || !validTypes.contains(item.getValueType())) { + return false; + } + int name = item.getName(); + int high = (name >> 16) & 0xffff; + if (PluralsQuantity.valueOf(item) == null || high != 0x0100) { + return false; + } + } + return true; + } + + public static PluralsBag create(com.reandroid.arsc.value.Entry entry) { + if (entry == null || !entry.isComplex()) { + return null; + } + return new PluralsBag(entry); + } + } diff --git a/src/main/java/com/reandroid/arsc/value/plurals/PluralsBagItem.java b/src/main/java/com/reandroid/arsc/value/plurals/PluralsBagItem.java index af4df26..8b72d0b 100644 --- a/src/main/java/com/reandroid/arsc/value/plurals/PluralsBagItem.java +++ b/src/main/java/com/reandroid/arsc/value/plurals/PluralsBagItem.java @@ -13,125 +13,84 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.reandroid.arsc.value.plurals; + package com.reandroid.arsc.value.plurals; -import com.reandroid.arsc.chunk.PackageBlock; -import com.reandroid.arsc.chunk.TableBlock; -import com.reandroid.arsc.item.TableString; -import com.reandroid.arsc.pool.TableStringPool; -import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.ResValueMap; -import com.reandroid.arsc.value.ValueType; + import com.reandroid.arsc.item.StringItem; + import com.reandroid.arsc.item.TableString; + import com.reandroid.arsc.value.ResValueMap; + import com.reandroid.arsc.value.ValueType; + import com.reandroid.arsc.value.bag.BagItem; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; + public class PluralsBagItem extends BagItem { + private PluralsBagItem(ResValueMap bagItem) { + super(bagItem); + } -public class PluralsBagItem { - private final ResValueMap mBagItem; - private PluralsBagItem(ResValueMap bagItem){ - this.mBagItem=bagItem; - } - public ResValueMap getBagItem() { - return mBagItem; - } - public PluralsQuantity getQuantity(){ - ResValueMap item=getBagItem(); - int low = item.getName() & 0xffff; - return PluralsQuantity.valueOf((short) low); - } - public ValueType getValueType(){ - return getBagItem().getValueType(); - } - private TableStringPool getStringPool(){ - Entry entry =getBagItem().getEntry(); - if(entry ==null){ - return null; - } - PackageBlock pkg = entry.getPackageBlock(); - if(pkg==null){ - return null; - } - TableBlock tableBlock= pkg.getTableBlock(); - if(tableBlock==null){ - return null; - } - return tableBlock.getTableStringPool(); - } - public int getValue(){ - return getBagItem().getData(); - } - public boolean hasStringValue(){ - return getValueType()==ValueType.STRING; - } - public boolean hasReferenceValue(){ - return getValueType()==ValueType.REFERENCE; - } - public String getStringValue(){ - ValueType valueType=getValueType(); - if(valueType!=ValueType.STRING){ - throw new IllegalArgumentException("Not STRING ValueType="+valueType); - } - TableStringPool stringPool=getStringPool(); - if(stringPool==null){ - return null; - } - int ref=getValue(); - TableString tableString = stringPool.get(ref); - return tableString.getHtml(); - } + private PluralsBagItem(StringItem str) { + super(str); + } - @Override - public String toString() { - StringBuilder builder=new StringBuilder(); - builder.append(""); - if(hasStringValue()){ - builder.append(getStringValue()); - }else { - builder.append(String.format("@0x%08x", getValue())); - } - builder.append(""); - return builder.toString(); - } + private PluralsBagItem(ValueType valueType, int data) { + super(valueType, data); + } - public static PluralsBagItem[] create(ResValueMap[] resValueMaps){ - if(resValueMaps ==null){ - return null; - } - int len= resValueMaps.length; - if(len==0){ - return null; - } - Set duplicates=new HashSet<>(); - List results=new ArrayList<>(); - for(int i=0;i> 16) & 0xffff; - if(high != 0x0100){ - return null; - } - PluralsBagItem item=create(resValueMap); - if(item==null){ - // If it reaches here type name is obfuscated - return null; - } - PluralsQuantity quantity=item.getQuantity(); - if(duplicates.contains(quantity)){ - return null; - } - duplicates.add(quantity); - results.add(item); - } - return results.toArray(new PluralsBagItem[0]); - } - public static PluralsBagItem create(ResValueMap resValueMap){ - PluralsBagItem item=new PluralsBagItem(resValueMap); - if(item.getQuantity()==null){ - return null; - } - return item; - } -} + public PluralsQuantity getQuantity() { + if (mBagItem == null || mBagItem.getName() == 0) { + return null; + } + return PluralsQuantity.valueOf(mBagItem); + } + + public String getQualityString() { + switch (getValueType()) { + case STRING: + return getStringValue(); + case REFERENCE: + // TODO: resolve string reference based on language + default: + throw new IllegalArgumentException("Not STR/REFERENCE ValueType=" + getValueType()); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(""); + if (hasStringValue()) { + builder.append(getStringValue()); + } else { + builder.append(String.format("@0x%08x", getValue())); + } + builder.append(""); + return builder.toString(); + } + + protected static PluralsBagItem create(ResValueMap resValueMap) { + if (resValueMap == null) { + return null; + } + return new PluralsBagItem(resValueMap); + } + + protected static PluralsBagItem copyOf(ResValueMap resValueMap) { + ValueType valueType = resValueMap.getValueType(); + if (valueType == ValueType.STRING) { + return new PluralsBagItem(resValueMap.getDataAsPoolString()); + } else { + return new PluralsBagItem(valueType, resValueMap.getData()); + } + } + + public static PluralsBagItem string(TableString str) { + if (str == null) { + return null; + } + return new PluralsBagItem(str); + } + + public static PluralsBagItem reference(int resourceId) { + return new PluralsBagItem(ValueType.REFERENCE, resourceId); + } + } diff --git a/src/main/java/com/reandroid/arsc/value/plurals/PluralsQuantity.java b/src/main/java/com/reandroid/arsc/value/plurals/PluralsQuantity.java index ee0c92f..d02f05e 100755 --- a/src/main/java/com/reandroid/arsc/value/plurals/PluralsQuantity.java +++ b/src/main/java/com/reandroid/arsc/value/plurals/PluralsQuantity.java @@ -16,7 +16,9 @@ package com.reandroid.arsc.value.plurals; -public enum PluralsQuantity { + import com.reandroid.arsc.value.ResValueMap; + + public enum PluralsQuantity { OTHER((short) 0x0004), ZERO((short) 0x0005), ONE((short) 0x0006), @@ -44,6 +46,13 @@ public enum PluralsQuantity { } return null; } + public static PluralsQuantity valueOf(ResValueMap valueMap){ + if (valueMap == null) { + return null; + } + int low = valueMap.getName() & 0xffff; + return valueOf((short) low); + } public static PluralsQuantity value(String name){ if(name==null){ return null; diff --git a/src/main/java/com/reandroid/arsc/value/style/StyleBag.java b/src/main/java/com/reandroid/arsc/value/style/StyleBag.java index 1c7e627..8704a74 100644 --- a/src/main/java/com/reandroid/arsc/value/style/StyleBag.java +++ b/src/main/java/com/reandroid/arsc/value/style/StyleBag.java @@ -13,110 +13,124 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.reandroid.arsc.value.style; + package com.reandroid.arsc.value.style; -import com.reandroid.arsc.item.SpecString; -import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.ResTableMapEntry; + import com.reandroid.apk.xmlencoder.EncodeMaterials; + import com.reandroid.arsc.chunk.TableBlock; + import com.reandroid.arsc.value.ResValueMap; + import com.reandroid.arsc.value.bag.MapBag; -public class StyleBag { - private final StyleBagItem[] mBagItems; - private StyleBag(StyleBagItem[] bagItems){ - this.mBagItems=bagItems; - } - public StyleBagItem[] getBagItems() { - return mBagItems; - } - public String getName(){ - Entry entry = getEntry(); - if(entry ==null){ - return null; - } - SpecString spec = entry.getSpecString(); - if(spec==null){ - return null; - } - return spec.get(); - } - public String getParentResourceName(){ - int id=getParentId(); - if(id==0){ - return null; - } - Entry entry = getEntry(); - if(entry ==null){ - return null; - } - return entry.buildResourceName(id, '@', true); - } - public int getParentId(){ - ResTableMapEntry mapEntry = getBagItems()[0].getBagItem().getParentMapEntry(); - if(mapEntry==null){ - return 0; - } - return mapEntry.getParentId(); - } - public int getResourceId(){ - Entry entry = getEntry(); - if(entry ==null){ - return 0; - } - return entry.getResourceId(); - } - public String getTypeName(){ - Entry entry =getBagItems()[0].getBagItem().getEntry(); - if(entry ==null){ - return null; - } - return entry.getTypeName(); - } - private Entry getEntry(){ - return getBagItems()[0].getBagItem().getEntry(); - } - @Override - public String toString() { - StringBuilder builder=new StringBuilder(); - builder.append("<"); - String type=getTypeName(); - builder.append(type); - builder.append(" name=\""); - builder.append(getName()); - builder.append("\""); - String parent=getParentResourceName(); - if(parent!=null){ - builder.append(" parent=\""); - builder.append(parent); - builder.append("\""); - } - builder.append("\">"); - StyleBagItem[] allItems = getBagItems(); - for(int i=0;i"); - return builder.toString(); - } + public class StyleBag extends MapBag { + private StyleBag(com.reandroid.arsc.value.Entry entry) { + super(entry); + } - /** The result of this is not always 100% accurate, - * in addition to this use your methods to cross check like type-name == "plurals"**/ - public static boolean isStyle(ResTableMapEntry mapEntry){ - if(mapEntry==null){ - return false; - } - return StyleBag.create(mapEntry) != null; - } + public String getParentResourceName() { + int id = getParentId(); + if (id == 0) { + return null; + } + com.reandroid.arsc.value.Entry entry = getEntry(); + if (entry == null) { + return null; + } + return entry.buildResourceName(id, '@', true); + } - public static StyleBag create(ResTableMapEntry mapEntry){ - if(mapEntry==null){ - return null; - } - StyleBagItem[] bagItems=StyleBagItem.create(mapEntry.getValue().getChildes()); - if(bagItems==null){ - return null; - } - return new StyleBag(bagItems); - } -} + public int getParentId() { + return getTableEntry().getParentId(); + } + + public int getResourceId() { + com.reandroid.arsc.value.Entry entry = getEntry(); + if (entry == null) { + return 0; + } + return entry.getResourceId(); + } + + @Override + protected StyleBagItem createBagItem(ResValueMap valueMap, boolean copied) { + if (copied) { + return StyleBagItem.copyOf(valueMap); + } + return StyleBagItem.create(valueMap); + } + + @Override + protected ResValueMap newKey(Integer attrId) { + ResValueMap valueMap = new ResValueMap(); + valueMap.setParent(getMapArray()); + valueMap.setName(attrId); + return valueMap; + } + + @Override + protected Integer getKeyFor(ResValueMap valueMap) { + return valueMap.getName(); + } + + public static int resolve(EncodeMaterials materials, String name) { + return materials.getAttributeBlock(name).getResourceId(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("<"); + String type = getTypeName(); + builder.append(type); + builder.append(" name=\""); + builder.append(getName()); + builder.append("\""); + String parent = getParentResourceName(); + if (parent != null) { + builder.append(" parent=\""); + builder.append(parent); + builder.append("\""); + } + builder.append("\">"); + for (StyleBagItem item : values()) { + builder.append("\n "); + builder.append(item.toString()); + } + builder.append("\n"); + return builder.toString(); + } + + /** + * The result of this is not always 100% accurate, + * in addition to this use your methods to cross check like type-name == "plurals" + **/ + public static boolean isStyle(com.reandroid.arsc.value.Entry entry) { + StyleBag style = create(entry); + if (style == null) { + return false; + } + + TableBlock tableBlock = entry.getPackageBlock().getTableBlock(); + if (tableBlock == null) { + return false; + } + ResValueMap[] items = style.getMapArray().getChildes(); + if (items.length == 0) { + return false; + } + + for (ResValueMap item : items) { + if (item == null || tableBlock.search(item.getNameResourceID()) == null) { + return false; + } + } + return true; + } + + public static StyleBag create(com.reandroid.arsc.value.Entry entry) { + if (entry == null || !entry.isComplex()) { + return null; + } + return new StyleBag(entry); + } + } diff --git a/src/main/java/com/reandroid/arsc/value/style/StyleBagItem.java b/src/main/java/com/reandroid/arsc/value/style/StyleBagItem.java index 1348e0c..12d98c0 100644 --- a/src/main/java/com/reandroid/arsc/value/style/StyleBagItem.java +++ b/src/main/java/com/reandroid/arsc/value/style/StyleBagItem.java @@ -13,117 +13,149 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.reandroid.arsc.value.style; + package com.reandroid.arsc.value.style; -import com.reandroid.arsc.value.Entry; -import com.reandroid.arsc.value.ResValueMap; -import com.reandroid.arsc.value.ValueType; + import com.reandroid.arsc.decoder.ValueDecoder; + import com.reandroid.arsc.item.StringItem; + import com.reandroid.arsc.item.TableString; + import com.reandroid.arsc.value.bag.BagItem; + import com.reandroid.arsc.value.Entry; + import com.reandroid.arsc.value.ResValueMap; + import com.reandroid.arsc.value.ValueType; -import java.util.ArrayList; -import java.util.List; + public class StyleBagItem extends BagItem { + private StyleBagItem(ResValueMap bagItem) { + super(bagItem); + } -public class StyleBagItem { - private final ResValueMap mBagItem; - public StyleBagItem(ResValueMap bagItem){ - this.mBagItem=bagItem; - } - public ResValueMap getBagItem() { - return mBagItem; - } + private StyleBagItem(ValueType valueType, int data) { + super(valueType, data); + } - public String getName(){ - Entry block=getBagItem().getEntry(); - if(block==null){ - return null; - } - char prefix=0; - return block.buildResourceName(getNameId(), prefix, false); - } - public int getNameId(){ - return getBagItem().getName(); - } - public boolean hasStringValue(){ - return getValueType()== ValueType.STRING; - } - public boolean hasReferenceValue(){ - return getValueType()==ValueType.REFERENCE; - } - public boolean hasAttributeValue(){ - return getValueType()==ValueType.REFERENCE; - } - public String getValueAsReference(){ - ValueType valueType=getValueType(); - if(valueType!=ValueType.REFERENCE && valueType!=ValueType.ATTRIBUTE){ - throw new IllegalArgumentException("Not REF ValueType="+valueType); - } - Entry entry =getBagItem().getEntry(); - if(entry ==null){ - return null; - } - char prefix='@'; - boolean includeType=true; - if(valueType==ValueType.ATTRIBUTE){ - prefix='?'; - includeType=false; - } - int id=getValue(); - return entry.buildResourceName(id, prefix, includeType); - } - public String getStringValue(){ - return mBagItem.getValueAsString(); - } - public ValueType getValueType(){ - return getBagItem().getValueType(); - } - public int getValue(){ - return getBagItem().getData(); - } - @Override - public String toString() { - StringBuilder builder=new StringBuilder(); - builder.append(""); - if(hasStringValue()){ - builder.append(getStringValue()); - } - String val=null; - if(hasReferenceValue()||hasAttributeValue()) { - val=getValueAsReference(); - } - if(val==null) { - val=String.format("0x%08x", getValue()); - } - builder.append(val); - builder.append(""); - return builder.toString(); - } - public static StyleBagItem[] create(ResValueMap[] resValueMaps){ - if(resValueMaps ==null){ - return null; - } - int len= resValueMaps.length; - if(len==0){ - return null; - } - List results=new ArrayList<>(); - for(int i=0;i"); + if (hasStringValue()) { + builder.append(getStringValue()); + } + String val = null; + if (hasReferenceValue() || hasAttributeValue()) { + val = getValueAsReference(); + } + if (val == null) { + val = String.format("0x%08x", getValue()); + } + builder.append(val); + builder.append(""); + return builder.toString(); + } + + protected static StyleBagItem create(ResValueMap resValueMap) { + if (resValueMap == null) { + return null; + } + return new StyleBagItem(resValueMap); + } + + public static StyleBagItem create(ValueType valueType, int value) { + if (valueType == null || valueType == ValueType.STRING) { + return null; + } + return new StyleBagItem(valueType, value); + } + + protected static StyleBagItem copyOf(ResValueMap resValueMap) { + ValueType valueType = resValueMap.getValueType(); + if (valueType == ValueType.STRING) { + return new StyleBagItem(resValueMap.getDataAsPoolString()); + } else { + return new StyleBagItem(valueType, resValueMap.getData()); + } + } + + public static StyleBagItem integer(int n) { + return new StyleBagItem(ValueType.INT_DEC, n); + } + + public static StyleBagItem string(TableString str) { + if (str == null) { + return null; + } + return new StyleBagItem(str); + } + + public static StyleBagItem reference(int resourceId) { + return new StyleBagItem(ValueType.REFERENCE, resourceId); + } + public static StyleBagItem attribute(int resourceId) { + return new StyleBagItem(ValueType.ATTRIBUTE, resourceId); + } + public static StyleBagItem encoded(ValueDecoder.EncodeResult encodeResult) { + if (encodeResult == null) { + return null; + } + return create(encodeResult.valueType, encodeResult.value); + } + public static StyleBagItem color(String color) { + return encoded(ValueDecoder.encodeColor(color)); + } + public static StyleBagItem dimensionOrFraction(String str) { + return encoded(ValueDecoder.encodeDimensionOrFraction(str)); + } + public static StyleBagItem createFloat(float n) { + return new StyleBagItem(ValueType.FLOAT, Float.floatToIntBits(n)); + } + }