feat: finish the bag apis

This commit is contained in:
Ax333l 2023-04-08 22:22:00 +02:00
parent 2eb4e7f96d
commit b110635b47
No known key found for this signature in database
GPG Key ID: D2B4D85271127D23
11 changed files with 990 additions and 586 deletions

View File

@ -196,21 +196,12 @@
} }
private TableEntry<?, ?> ensureTableEntry(boolean is_complex){ private TableEntry<?, ?> ensureTableEntry(boolean is_complex){
TableEntry<?, ?> tableEntry = getTableEntry(); 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); tableEntry = createTableEntry(is_complex);
setTableEntry(tableEntry); 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; return tableEntry;
} }

View File

@ -13,91 +13,164 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.array.ResValueMapArray;
import com.reandroid.arsc.value.ResTableMapEntry; import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ResValueMap; import com.reandroid.arsc.value.ResTableMapEntry;
import com.reandroid.arsc.value.ResValueMap;
import com.reandroid.arsc.value.bag.Bag;
public class ArrayBag { import java.util.AbstractList;
private final ArrayBagItem[] mBagItems; import java.util.RandomAccess;
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<allItems.length;i++){
builder.append("\n ");
builder.append(allItems[i].toString());
}
builder.append("\n</");
builder.append(type);
builder.append(">");
return builder.toString();
}
/** The result of this is not always 100% accurate, public class ArrayBag extends AbstractList<ArrayBagItem> implements Bag, RandomAccess {
* in addition to this use your methods to cross check like type-name == "array"**/ private final Entry entry;
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 static ArrayBag create(ResTableMapEntry mapEntry){ private ArrayBag(Entry entry) {
if(mapEntry==null){ this.entry = entry;
return null; }
}
ArrayBagItem[] bagItems=ArrayBagItem.create(mapEntry.listResValueMap()); private ResTableMapEntry getTableEntry() {
if(bagItems==null){ return (ResTableMapEntry) entry.getTableEntry();
return null; }
}
return new ArrayBag(bagItems); 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</");
builder.append(type);
builder.append(">");
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);
}
}

View File

@ -13,104 +13,83 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.decoder.ValueDecoder;
import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.item.StringItem;
import com.reandroid.arsc.item.TableString; import com.reandroid.arsc.item.TableString;
import com.reandroid.arsc.pool.TableStringPool; import com.reandroid.arsc.value.ValueType;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.bag.BagItem;
import com.reandroid.arsc.value.ResValueMap; import com.reandroid.arsc.value.ResValueMap;
import com.reandroid.arsc.value.ValueType;
import java.util.ArrayList; public class ArrayBagItem extends BagItem {
import java.util.List; private ArrayBagItem(ResValueMap valueMap) {
super(valueMap);
}
public class ArrayBagItem { private ArrayBagItem(StringItem str) {
private final ResValueMap mBagItem; super(str);
public ArrayBagItem(ResValueMap bagItem){ }
this.mBagItem=bagItem;
}
public ResValueMap getBagItem() {
return mBagItem;
}
public ValueType getValueType(){ private ArrayBagItem(ValueType valueType, int value) {
return getBagItem().getValueType(); super(valueType, value);
} }
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();
}
@Override @Override
public String toString() { public String toString() {
StringBuilder builder=new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("<item>"); builder.append("<item>");
if(hasStringValue()){ if (hasStringValue()) {
builder.append(getStringValue()); builder.append(getStringValue());
}else { } else {
builder.append(String.format("0x%08x", getValue())); builder.append(String.format("0x%08x", getValue()));
} }
builder.append("</item>"); builder.append("</item>");
return builder.toString(); return builder.toString();
} }
public static ArrayBagItem[] create(ResValueMap[] resValueMaps){
if(resValueMaps ==null){ protected static ArrayBagItem create(ResValueMap valueMap) {
return null; if (valueMap == null) {
} return null;
int len= resValueMaps.length; }
if(len==0){ return new ArrayBagItem(valueMap);
return null; }
}
List<ArrayBagItem> results=new ArrayList<>(); public static ArrayBagItem create(ValueType valueType, int value) {
for(int i=0;i<len;i++){ if (valueType == null || valueType == ValueType.STRING) {
ArrayBagItem item=create(resValueMaps[i]); return null;
if(item==null){ }
return null; return new ArrayBagItem(valueType, value);
} }
results.add(item);
} protected static ArrayBagItem copyOf(ResValueMap resValueMap) {
return results.toArray(new ArrayBagItem[0]); ValueType valueType = resValueMap.getValueType();
} if (valueType == ValueType.STRING) {
public static ArrayBagItem create(ResValueMap resValueMap){ return new ArrayBagItem(resValueMap.getDataAsPoolString());
if(resValueMap ==null){ } else {
return null; return new ArrayBagItem(valueType, resValueMap.getData());
} }
ArrayBagItem item=new ArrayBagItem(resValueMap); }
return item;
} 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);
}
}

View File

@ -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();
}
}

View File

@ -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");
}
}
}

View File

@ -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<K, V extends BagItem> extends AbstractMap<K, V> 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<K, V> {
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<Entry<K, V>> {
@Override
public Iterator<Entry<K, V>> iterator() {
return new Iterator<Entry<K, V>>() {
private final Iterator<ResValueMap> 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<K, V> 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<Entry<K, V>> 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<? extends K, ? extends V> m) {
LinkedHashSet<K> 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();
}
}

View File

@ -13,71 +13,110 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.ResValueMap;
import com.reandroid.arsc.value.ResTableMapEntry; import com.reandroid.arsc.value.ValueType;
import com.reandroid.arsc.value.bag.MapBag;
public class PluralsBag { import java.util.Arrays;
private final PluralsBagItem[] mBagItems; import java.util.HashSet;
private PluralsBag(PluralsBagItem[] bagItems){ import java.util.Set;
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();
}
@Override public class PluralsBag extends MapBag<PluralsQuantity, PluralsBagItem> {
public String toString() { private PluralsBag(com.reandroid.arsc.value.Entry entry) {
StringBuilder builder=new StringBuilder(); super(entry);
builder.append("<"); }
String type=getTypeName();
builder.append(type);
builder.append(" name=\"");
builder.append(getName());
builder.append("\">");
PluralsBagItem[] allItems = getBagItems();
for(int i=0;i<allItems.length;i++){
builder.append("\n ");
builder.append(allItems[i].toString());
}
builder.append("\n</");
builder.append(type);
builder.append(">");
return builder.toString();
}
/** The result of this is not always 100% accurate, @Override
* in addition to this use your methods to cross check like type-name == "plurals"**/ protected PluralsBagItem createBagItem(ResValueMap valueMap, boolean copied) {
public static boolean isPlurals(ResTableMapEntry mapEntry){ if (copied) {
if(mapEntry==null){ return PluralsBagItem.copyOf(valueMap);
return false; }
} return PluralsBagItem.create(valueMap);
return PluralsBagItem.create(mapEntry.listResValueMap()) != null; }
}
public static PluralsBag create(ResTableMapEntry mapEntry){ @Override
if(mapEntry==null){ protected ResValueMap newKey(PluralsQuantity key) {
return null; ResValueMap valueMap = new ResValueMap();
} valueMap.setParent(getMapArray());
PluralsBagItem[] bagItems=PluralsBagItem.create(mapEntry.listResValueMap()); valueMap.setNameHigh((short) 0x0100);
if(bagItems==null){ valueMap.setNameLow(key.getId());
return null; return valueMap;
} }
return new PluralsBag(bagItems);
} @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</");
builder.append(type);
builder.append(">");
return builder.toString();
}
private final static Set<ValueType> 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);
}
}

View File

@ -13,125 +13,84 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.item.StringItem;
import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.item.TableString;
import com.reandroid.arsc.item.TableString; import com.reandroid.arsc.value.ResValueMap;
import com.reandroid.arsc.pool.TableStringPool; import com.reandroid.arsc.value.ValueType;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.bag.BagItem;
import com.reandroid.arsc.value.ResValueMap;
import com.reandroid.arsc.value.ValueType;
import java.util.ArrayList; public class PluralsBagItem extends BagItem {
import java.util.HashSet; private PluralsBagItem(ResValueMap bagItem) {
import java.util.List; super(bagItem);
import java.util.Set; }
public class PluralsBagItem { private PluralsBagItem(StringItem str) {
private final ResValueMap mBagItem; super(str);
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();
}
@Override private PluralsBagItem(ValueType valueType, int data) {
public String toString() { super(valueType, data);
StringBuilder builder=new StringBuilder(); }
builder.append("<item quantity=\"");
builder.append(getQuantity());
builder.append("\">");
if(hasStringValue()){
builder.append(getStringValue());
}else {
builder.append(String.format("@0x%08x", getValue()));
}
builder.append("</item>");
return builder.toString();
}
public static PluralsBagItem[] create(ResValueMap[] resValueMaps){ public PluralsQuantity getQuantity() {
if(resValueMaps ==null){ if (mBagItem == null || mBagItem.getName() == 0) {
return null; return null;
} }
int len= resValueMaps.length; return PluralsQuantity.valueOf(mBagItem);
if(len==0){ }
return null;
} public String getQualityString() {
Set<PluralsQuantity> duplicates=new HashSet<>(); switch (getValueType()) {
List<PluralsBagItem> results=new ArrayList<>(); case STRING:
for(int i=0;i<len;i++){ return getStringValue();
ResValueMap resValueMap = resValueMaps[i]; case REFERENCE:
int high = (resValueMap.getName() >> 16) & 0xffff; // TODO: resolve string reference based on language
if(high != 0x0100){ default:
return null; throw new IllegalArgumentException("Not STR/REFERENCE ValueType=" + getValueType());
} }
PluralsBagItem item=create(resValueMap); }
if(item==null){
// If it reaches here type name is obfuscated @Override
return null; public String toString() {
} StringBuilder builder = new StringBuilder();
PluralsQuantity quantity=item.getQuantity(); builder.append("<item quantity=\"");
if(duplicates.contains(quantity)){ builder.append(getQuantity());
return null; builder.append("\">");
} if (hasStringValue()) {
duplicates.add(quantity); builder.append(getStringValue());
results.add(item); } else {
} builder.append(String.format("@0x%08x", getValue()));
return results.toArray(new PluralsBagItem[0]); }
} builder.append("</item>");
public static PluralsBagItem create(ResValueMap resValueMap){ return builder.toString();
PluralsBagItem item=new PluralsBagItem(resValueMap); }
if(item.getQuantity()==null){
return null; protected static PluralsBagItem create(ResValueMap resValueMap) {
} if (resValueMap == null) {
return item; 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);
}
}

View File

@ -16,7 +16,9 @@
package com.reandroid.arsc.value.plurals; package com.reandroid.arsc.value.plurals;
public enum PluralsQuantity { import com.reandroid.arsc.value.ResValueMap;
public enum PluralsQuantity {
OTHER((short) 0x0004), OTHER((short) 0x0004),
ZERO((short) 0x0005), ZERO((short) 0x0005),
ONE((short) 0x0006), ONE((short) 0x0006),
@ -44,6 +46,13 @@ public enum PluralsQuantity {
} }
return null; 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){ public static PluralsQuantity value(String name){
if(name==null){ if(name==null){
return null; return null;

View File

@ -13,110 +13,124 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.apk.xmlencoder.EncodeMaterials;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.value.ResTableMapEntry; import com.reandroid.arsc.value.ResValueMap;
import com.reandroid.arsc.value.bag.MapBag;
public class StyleBag { public class StyleBag extends MapBag<Integer, StyleBagItem> {
private final StyleBagItem[] mBagItems; private StyleBag(com.reandroid.arsc.value.Entry entry) {
private StyleBag(StyleBagItem[] bagItems){ super(entry);
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<allItems.length;i++){
builder.append("\n ");
builder.append(allItems[i].toString());
}
builder.append("\n</");
builder.append(type);
builder.append(">");
return builder.toString();
}
/** The result of this is not always 100% accurate, public String getParentResourceName() {
* in addition to this use your methods to cross check like type-name == "plurals"**/ int id = getParentId();
public static boolean isStyle(ResTableMapEntry mapEntry){ if (id == 0) {
if(mapEntry==null){ return null;
return false; }
} com.reandroid.arsc.value.Entry entry = getEntry();
return StyleBag.create(mapEntry) != null; if (entry == null) {
} return null;
}
return entry.buildResourceName(id, '@', true);
}
public static StyleBag create(ResTableMapEntry mapEntry){ public int getParentId() {
if(mapEntry==null){ return getTableEntry().getParentId();
return null; }
}
StyleBagItem[] bagItems=StyleBagItem.create(mapEntry.getValue().getChildes()); public int getResourceId() {
if(bagItems==null){ com.reandroid.arsc.value.Entry entry = getEntry();
return null; if (entry == null) {
} return 0;
return new StyleBag(bagItems); }
} 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</");
builder.append(type);
builder.append(">");
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);
}
}

View File

@ -13,117 +13,149 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.decoder.ValueDecoder;
import com.reandroid.arsc.value.ResValueMap; import com.reandroid.arsc.item.StringItem;
import com.reandroid.arsc.value.ValueType; 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; public class StyleBagItem extends BagItem {
import java.util.List; private StyleBagItem(ResValueMap bagItem) {
super(bagItem);
}
public class StyleBagItem { private StyleBagItem(ValueType valueType, int data) {
private final ResValueMap mBagItem; super(valueType, data);
public StyleBagItem(ResValueMap bagItem){ }
this.mBagItem=bagItem;
}
public ResValueMap getBagItem() {
return mBagItem;
}
public String getName(){ private StyleBagItem(StringItem str) {
Entry block=getBagItem().getEntry(); super(str);
if(block==null){ }
return null;
} public String getName() {
char prefix=0; if (mBagItem == null) {
return block.buildResourceName(getNameId(), prefix, false); return null;
} }
public int getNameId(){ Entry block = mBagItem.getEntry();
return getBagItem().getName(); if (block == null) {
} return null;
public boolean hasStringValue(){ }
return getValueType()== ValueType.STRING; char prefix = 0;
} return block.buildResourceName(mBagItem.getName(), prefix, false);
public boolean hasReferenceValue(){ }
return getValueType()==ValueType.REFERENCE;
} public int getNameId() {
public boolean hasAttributeValue(){ if (mBagItem == null) {
return getValueType()==ValueType.REFERENCE; return 0;
} }
public String getValueAsReference(){ return mBagItem.getName();
ValueType valueType=getValueType(); }
if(valueType!=ValueType.REFERENCE && valueType!=ValueType.ATTRIBUTE){
throw new IllegalArgumentException("Not REF ValueType="+valueType); public boolean hasAttributeValue() {
} return getValueType() == ValueType.ATTRIBUTE;
Entry entry =getBagItem().getEntry(); }
if(entry ==null){
return null; public String getValueAsReference() {
} ValueType valueType = getValueType();
char prefix='@'; if (valueType != ValueType.REFERENCE && valueType != ValueType.ATTRIBUTE) {
boolean includeType=true; throw new IllegalArgumentException("Not REF ValueType=" + valueType);
if(valueType==ValueType.ATTRIBUTE){ }
prefix='?'; Entry entry = getBagItem().getEntry();
includeType=false; if (entry == null) {
} return null;
int id=getValue(); }
return entry.buildResourceName(id, prefix, includeType); char prefix = '@';
} boolean includeType = true;
public String getStringValue(){ if (valueType == ValueType.ATTRIBUTE) {
return mBagItem.getValueAsString(); prefix = '?';
} includeType = false;
public ValueType getValueType(){ }
return getBagItem().getValueType(); int id = getValue();
} return entry.buildResourceName(id, prefix, includeType);
public int getValue(){ }
return getBagItem().getData();
} @Override
@Override public String toString() {
public String toString() { StringBuilder builder = new StringBuilder();
StringBuilder builder=new StringBuilder(); builder.append("<item name=\"");
builder.append("<item name=\""); String name = getName();
String name=getName(); if (name == null) {
if(name==null){ name = String.format("@0x%08x", getNameId());
name=String.format("@0x%08x", getNameId()); }
} builder.append(name);
builder.append(name); builder.append("\">");
builder.append("\">"); if (hasStringValue()) {
if(hasStringValue()){ builder.append(getStringValue());
builder.append(getStringValue()); }
} String val = null;
String val=null; if (hasReferenceValue() || hasAttributeValue()) {
if(hasReferenceValue()||hasAttributeValue()) { val = getValueAsReference();
val=getValueAsReference(); }
} if (val == null) {
if(val==null) { val = String.format("0x%08x", getValue());
val=String.format("0x%08x", getValue()); }
} builder.append(val);
builder.append(val); builder.append("</item>");
builder.append("</item>"); return builder.toString();
return builder.toString(); }
}
public static StyleBagItem[] create(ResValueMap[] resValueMaps){ protected static StyleBagItem create(ResValueMap resValueMap) {
if(resValueMaps ==null){ if (resValueMap == null) {
return null; return null;
} }
int len= resValueMaps.length; return new StyleBagItem(resValueMap);
if(len==0){ }
return null;
} public static StyleBagItem create(ValueType valueType, int value) {
List<StyleBagItem> results=new ArrayList<>(); if (valueType == null || valueType == ValueType.STRING) {
for(int i=0;i<len;i++){ return null;
StyleBagItem item=create(resValueMaps[i]); }
if(item==null){ return new StyleBagItem(valueType, value);
return null; }
}
results.add(item); protected static StyleBagItem copyOf(ResValueMap resValueMap) {
} ValueType valueType = resValueMap.getValueType();
return results.toArray(new StyleBagItem[0]); if (valueType == ValueType.STRING) {
} return new StyleBagItem(resValueMap.getDataAsPoolString());
public static StyleBagItem create(ResValueMap resValueMap){ } else {
if(resValueMap ==null){ return new StyleBagItem(valueType, resValueMap.getData());
return null; }
} }
return new StyleBagItem(resValueMap);
} 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));
}
}