full implementation of SPARSE type entries

This commit is contained in:
REAndroid 2023-04-27 19:12:39 +02:00
parent c239585091
commit 21569db1f3
29 changed files with 1008 additions and 946 deletions

View File

@ -15,7 +15,6 @@
*/ */
package com.reandroid.arsc.array; package com.reandroid.arsc.array;
import com.reandroid.arsc.item.IntegerArray;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.Entry;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
@ -26,9 +25,32 @@ import java.util.Iterator;
public class EntryArray extends OffsetBlockArray<Entry> implements JSONConvert<JSONArray> { public class EntryArray extends OffsetBlockArray<Entry> implements JSONConvert<JSONArray> {
public EntryArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart){ public EntryArray(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart){
super(offsets, itemCount, itemStart); super(offsets, itemCount, itemStart);
} }
public int getHighestEntryId(){
if(isSparse()){
return ((SparseOffsetsArray) getOffsetArray()).getHighestId();
}
return childesCount();
}
public int getEntryId(int index){
OffsetArray offsetArray = getOffsetArray();
if(offsetArray instanceof SparseOffsetsArray){
return ((SparseOffsetsArray) offsetArray).getIdx(index);
}
return index;
}
public int getEntryIndex(int entryId){
OffsetArray offsetArray = getOffsetArray();
if(offsetArray instanceof SparseOffsetsArray){
return ((SparseOffsetsArray) offsetArray).indexOf(entryId);
}
return entryId;
}
public boolean isSparse(){
return super.getOffsetArray() instanceof SparseOffsetsArray;
}
public void destroy(){ public void destroy(){
for(Entry entry:listItems()){ for(Entry entry:listItems()){
if(entry!=null){ if(entry!=null){
@ -47,26 +69,41 @@ public class EntryArray extends OffsetBlockArray<Entry> implements JSONConvert<J
public boolean isEmpty(){ public boolean isEmpty(){
return !iterator(true).hasNext(); return !iterator(true).hasNext();
} }
public void setEntry(short entryId, Entry entry){
setItem(0xffff & entryId, entry);
}
public Entry getOrCreate(short entryId){ public Entry getOrCreate(short entryId){
int id = 0xffff & entryId; int id = 0xffff & entryId;
Entry entry =get(id); Entry entry = getEntry(id);
if(entry !=null){ if(entry != null){
return entry; return entry;
} }
int count=id+1; boolean sparse = isSparse();
int count;
if(sparse){
count = childesCount() + 1;
}else {
count = id + 1;
}
ensureSize(count); ensureSize(count);
if(!sparse){
refreshCount();
return super.get(id);
}
SparseOffsetsArray offsetsArray = (SparseOffsetsArray) getOffsetArray();
offsetsArray.ensureArraySize(count);
int index = count - 1;
offsetsArray.setIdx(index, id);
refreshCount(); refreshCount();
return get(id);
}
public Entry get(short entryId){
int index = 0xffff & entryId;
return super.get(index); return super.get(index);
} }
public Entry get(short entryId){
return getEntry(entryId);
}
public Entry getEntry(short entryId){ public Entry getEntry(short entryId){
return get(0xffff & entryId); return getEntry(0xffff & entryId);
}
public Entry getEntry(int entryId){
int index = getEntryIndex(entryId);
return super.get(index);
} }
@Override @Override
public Entry newInstance() { public Entry newInstance() {
@ -98,7 +135,7 @@ public class EntryArray extends OffsetBlockArray<Entry> implements JSONConvert<J
JSONArray jsonArray=new JSONArray(); JSONArray jsonArray=new JSONArray();
int index=0; int index=0;
String name_id = Entry.NAME_id; String name_id = Entry.NAME_id;
for(Entry entry :listItems()){ for(Entry entry : listItems(true)){
JSONObject childObject = entry.toJson(); JSONObject childObject = entry.toJson();
if(childObject==null){ if(childObject==null){
continue; continue;
@ -112,33 +149,74 @@ public class EntryArray extends OffsetBlockArray<Entry> implements JSONConvert<J
@Override @Override
public void fromJson(JSONArray json) { public void fromJson(JSONArray json) {
clearChildes(); clearChildes();
if(isSparse()){
fromJsonSparse(json);
}else {
fromJsonNonSparse(json);
}
refreshCountAndStart();
}
private void fromJsonNonSparse(JSONArray json){
int length=json.length(); int length=json.length();
ensureSize(length); ensureSize(length);
String name_id = Entry.NAME_id; String name_id = Entry.NAME_id;
for(int i=0;i<length;i++){ for(int i=0;i<length;i++){
JSONObject jsonObject= json.getJSONObject(i); JSONObject jsonObject = json.optJSONObject(i);
if(jsonObject==null){ if(jsonObject==null){
continue; continue;
} }
int id = jsonObject.getInt(name_id); int id = jsonObject.getInt(name_id);
ensureSize(id+1); ensureSize(id + 1);
Entry entry =get(id); Entry entry = super.get(id);
entry.fromJson(jsonObject); entry.fromJson(jsonObject);
} }
}
private void fromJsonSparse(JSONArray json){
SparseOffsetsArray offsetsArray = (SparseOffsetsArray) getOffsetArray();
offsetsArray.setSize(0);
int length = json.length();
ensureSize(length);
offsetsArray.setSize(length);
String name_id = Entry.NAME_id;
for(int i=0;i<length;i++){
JSONObject jsonObject = json.optJSONObject(i);
if(jsonObject==null){
offsetsArray.setIdx(i , OffsetArray.NO_ENTRY);
continue;
}
int id = jsonObject.getInt(name_id);
Entry entry = super.get(i);
offsetsArray.setIdx(i, id);
entry.fromJson(jsonObject);
}
}
public void merge(EntryArray entryArray){
if(entryArray ==null|| entryArray == this|| entryArray.isEmpty()){
return;
}
if(isSparse()){
mergeSparse(entryArray);
}else {
mergeNonSparse(entryArray);
}
refreshCountAndStart(); refreshCountAndStart();
} }
public void merge(EntryArray entryArray){ private void mergeSparse(EntryArray entryArray){
if(entryArray ==null|| entryArray ==this|| entryArray.isEmpty()){ Iterator<Entry> itr = entryArray.iterator(true);
return; while (itr.hasNext()){
Entry comingBlock = itr.next();
Entry existingBlock = getOrCreate((short) comingBlock.getId());
existingBlock.merge(comingBlock);
} }
}
private void mergeNonSparse(EntryArray entryArray){
ensureSize(entryArray.childesCount()); ensureSize(entryArray.childesCount());
Iterator<Entry> itr = entryArray.iterator(true); Iterator<Entry> itr = entryArray.iterator(true);
while (itr.hasNext()){ while (itr.hasNext()){
Entry comingBlock = itr.next(); Entry comingBlock = itr.next();
Entry existingBlock = get(comingBlock.getIndex()); Entry existingBlock = super.get(comingBlock.getIndex());
existingBlock.merge(comingBlock); existingBlock.merge(comingBlock);
} }
refreshCountAndStart();
} }
@Override @Override
public String toString(){ public String toString(){

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.arsc.array;
import com.reandroid.arsc.item.IntegerArray;
public class OffsetArray extends IntegerArray {
public OffsetArray(){
super();
}
public int getOffset(int i){
return super.getAt(i);
}
public void setOffset(int index, int value){
super.put(index, value);
}
public int[] getOffsets(){
int length = size();
int[] result = new int[length];
for(int i=0;i<length;i++){
result[i] = getOffset(i);
}
return result;
}
public static final int NO_ENTRY = 0xFFFFFFFF;
}

View File

@ -1,18 +1,18 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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.array; package com.reandroid.arsc.array;
@ -22,7 +22,6 @@ import com.reandroid.arsc.base.BlockCounter;
import com.reandroid.arsc.io.BlockLoad; import com.reandroid.arsc.io.BlockLoad;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.ByteArray; import com.reandroid.arsc.item.ByteArray;
import com.reandroid.arsc.item.IntegerArray;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
@ -30,12 +29,12 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
public abstract class OffsetBlockArray<T extends Block> extends BlockArray<T> implements BlockLoad { public abstract class OffsetBlockArray<T extends Block> extends BlockArray<T> implements BlockLoad {
private final IntegerArray mOffsets; private final OffsetArray mOffsets;
private final IntegerItem mItemStart; private final IntegerItem mItemStart;
private final IntegerItem mItemCount; private final IntegerItem mItemCount;
private final ByteArray mEnd4Block; private final ByteArray mEnd4Block;
private byte mEnd4Type; private byte mEnd4Type;
public OffsetBlockArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart){ public OffsetBlockArray(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart){
super(); super();
this.mOffsets=offsets; this.mOffsets=offsets;
this.mItemCount=itemCount; this.mItemCount=itemCount;
@ -43,6 +42,9 @@ public abstract class OffsetBlockArray<T extends Block> extends BlockArray<T> im
this.mEnd4Block=new ByteArray(); this.mEnd4Block=new ByteArray();
mItemCount.setBlockLoad(this); mItemCount.setBlockLoad(this);
} }
OffsetArray getOffsetArray(){
return mOffsets;
}
void setEndBytes(byte b){ void setEndBytes(byte b){
this.mEnd4Type=b; this.mEnd4Type=b;
this.mEnd4Block.fill(b); this.mEnd4Block.fill(b);
@ -91,7 +93,8 @@ public abstract class OffsetBlockArray<T extends Block> extends BlockArray<T> im
@Override @Override
protected void onRefreshed() { protected void onRefreshed() {
int count=childesCount(); int count=childesCount();
mOffsets.setSize(count); OffsetArray offsetArray = this.mOffsets;
offsetArray.setSize(count);
T[] childes=getChildes(); T[] childes=getChildes();
int sum=0; int sum=0;
if(childes!=null){ if(childes!=null){
@ -105,7 +108,7 @@ public abstract class OffsetBlockArray<T extends Block> extends BlockArray<T> im
offset=sum; offset=sum;
sum+=item.countBytes(); sum+=item.countBytes();
} }
mOffsets.put(i, offset); offsetArray.setOffset(i, offset);
} }
} }
refreshCount(); refreshCount();
@ -167,7 +170,7 @@ public abstract class OffsetBlockArray<T extends Block> extends BlockArray<T> im
if(childes==null||childes.length==0){ if(childes==null||childes.length==0){
return; return;
} }
int[] offsetArray=mOffsets.toArray(); int[] offsetArray=mOffsets.getOffsets();
int max=childes.length; int max=childes.length;
int start=mItemStart.get(); int start=mItemStart.get();
reader.seek(start); reader.seek(start);

View File

@ -1,18 +1,18 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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.array; package com.reandroid.arsc.array;
import com.reandroid.arsc.chunk.xml.ResXmlDocument; import com.reandroid.arsc.chunk.xml.ResXmlDocument;
@ -24,8 +24,8 @@ import com.reandroid.arsc.item.ResXmlString;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class ResXmlStringArray extends StringArray<ResXmlString> { public class ResXmlStringArray extends StringArray<ResXmlString> {
public ResXmlStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { public ResXmlStringArray(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
super(offsets, itemCount, itemStart, is_utf8); super(offsets, itemCount, itemStart, is_utf8);
} }
@Override @Override

View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.arsc.array;
public class SparseOffsetsArray extends OffsetArray{
public SparseOffsetsArray(){
super();
}
public int getHighestId(){
int result = NO_ENTRY;
int size = size();
for(int i=0; i<size;i++){
int id = getIdx(i);
if(id > result){
result = id;
}
}
if(result == NO_ENTRY){
result = 0;
}
return result;
}
public int indexOf(int idx){
int size = super.size();
for(int i=0; i<size; i++){
if(idx == getIdx(i)){
return i;
}
}
return NO_ENTRY;
}
public int getIdx(int i){
int value = super.getAt(i);
if(value != NO_ENTRY) {
value = value & 0xffff;
}
return value;
}
public void setIdx(int index, int idx){
int value;
if(idx == NO_ENTRY){
value = idx;
}else {
int offset = getAt(index) & 0xffff0000;
idx = idx & 0xffff;
value = offset | idx;
}
super.put(index, value);
}
@Override
public int getOffset(int i){
int value = super.getAt(i);
if(value == NO_ENTRY){
return value;
}
value = (value >>> 16) & 0xffff;
return value * 4;
}
@Override
public void setOffset(int index, int offset){
int value;
if(offset == NO_ENTRY){
value = 0;
}else {
int idx = getAt(0);
idx = idx & 0xffff;
offset = offset & 0xffff;
offset = offset / 4;
offset = offset << 16;
value = offset | idx;
}
super.put(index, value);
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.arsc.array;
import com.reandroid.arsc.base.BlockArray;
import com.reandroid.arsc.chunk.SparseTypeBlock;
public class SparseTypeBlockArray extends BlockArray<SparseTypeBlock> {
@Override
public SparseTypeBlock[] newInstance(int len) {
return new SparseTypeBlock[len];
}
@Override
public SparseTypeBlock newInstance() {
return new SparseTypeBlock();
}
@Override
protected void onRefreshed() {
}
public void merge(SparseTypeBlockArray sparse){
if(sparse == null || sparse == this){
return;
}
addAll(sparse.getChildes());
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.arsc.array;
import com.reandroid.arsc.base.BlockArray;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.value.SparseTypeEntry;
import java.io.IOException;
public class SparseTypeEntryArray extends BlockArray<SparseTypeEntry> {
@Override
public SparseTypeEntry[] newInstance(int len) {
return new SparseTypeEntry[len];
}
@Override
public SparseTypeEntry newInstance() {
return new SparseTypeEntry();
}
@Override
protected void onRefreshed() {
}
@Override
public void onReadBytes(BlockReader reader) throws IOException {
int count = reader.available() / 4;
setChildesCount(count);
super.onReadBytes(reader);
}
}

View File

@ -1,26 +1,25 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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.array; package com.reandroid.arsc.array;
import com.reandroid.arsc.item.IntegerArray;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.SpecString; import com.reandroid.arsc.item.SpecString;
public class SpecStringArray extends StringArray<SpecString> { public class SpecStringArray extends StringArray<SpecString> {
public SpecStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { public SpecStringArray(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
super(offsets, itemCount, itemStart, is_utf8); super(offsets, itemCount, itemStart, is_utf8);
} }
@Override @Override

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -90,11 +90,11 @@ public class SpecTypePairArray extends BlockArray<SpecTypePair>
return pair.getTypeBlockArray().getOrCreate(resConfig); return pair.getTypeBlockArray().getOrCreate(resConfig);
} }
public SpecTypePair getOrCreate(byte typeId){ public SpecTypePair getOrCreate(byte typeId){
SpecTypePair pair=getPair(typeId); SpecTypePair pair = getPair(typeId);
if(pair!=null){ if(pair!=null){
return pair; return pair;
} }
pair=createNext(); pair = createNext();
pair.setTypeId(typeId); pair.setTypeId(typeId);
return pair; return pair;
} }
@ -115,25 +115,9 @@ public class SpecTypePairArray extends BlockArray<SpecTypePair>
} }
return null; return null;
} }
public byte getTypeId(){
SpecTypePair[] items=getChildes();
if(items==null){
return 0;
}
int max=items.length;
for(int i=0;i<max;i++){
SpecTypePair pair=items[i];
if(pair!=null){
return pair.getTypeId();
}
}
return 0;
}
@Override @Override
public SpecTypePair newInstance() { public SpecTypePair newInstance() {
SpecTypePair pair=new SpecTypePair(); return new SpecTypePair();
pair.setTypeId(getTypeId());
return pair;
} }
@Override @Override
public SpecTypePair[] newInstance(int len) { public SpecTypePair[] newInstance(int len) {
@ -147,7 +131,7 @@ public class SpecTypePairArray extends BlockArray<SpecTypePair>
protected void onPreRefreshRefresh(){ protected void onPreRefreshRefresh(){
validateEntryCounts(); validateEntryCounts();
} }
// For android API < 26, it is required to have equal entry count on all SpecTypePair
private void validateEntryCounts(){ private void validateEntryCounts(){
Map<Byte, Integer> entryCountMap=mapHighestEntryCount(); Map<Byte, Integer> entryCountMap=mapHighestEntryCount();
for(Map.Entry<Byte, Integer> entry:entryCountMap.entrySet()){ for(Map.Entry<Byte, Integer> entry:entryCountMap.entrySet()){

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -15,7 +15,6 @@
*/ */
package com.reandroid.arsc.array; package com.reandroid.arsc.array;
import com.reandroid.arsc.item.IntegerArray;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.StringItem; import com.reandroid.arsc.item.StringItem;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
@ -29,7 +28,7 @@ import java.util.List;
public abstract class StringArray<T extends StringItem> extends OffsetBlockArray<T> implements JSONConvert<JSONArray> { public abstract class StringArray<T extends StringItem> extends OffsetBlockArray<T> implements JSONConvert<JSONArray> {
private boolean mUtf8; private boolean mUtf8;
public StringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { public StringArray(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
super(offsets, itemCount, itemStart); super(offsets, itemCount, itemStart);
this.mUtf8=is_utf8; this.mUtf8=is_utf8;
setEndBytes((byte)0x00); setEndBytes((byte)0x00);

View File

@ -1,23 +1,22 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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.array; package com.reandroid.arsc.array;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.ByteArray; import com.reandroid.arsc.item.ByteArray;
import com.reandroid.arsc.item.IntegerArray;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.StyleItem; import com.reandroid.arsc.item.StyleItem;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
@ -26,7 +25,7 @@ import com.reandroid.json.JSONArray;
import java.io.IOException; import java.io.IOException;
public class StyleArray extends OffsetBlockArray<StyleItem> implements JSONConvert<JSONArray> { public class StyleArray extends OffsetBlockArray<StyleItem> implements JSONConvert<JSONArray> {
public StyleArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart) { public StyleArray(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart) {
super(offsets, itemCount, itemStart); super(offsets, itemCount, itemStart);
setEndBytes(END_BYTE); setEndBytes(END_BYTE);
} }

View File

@ -1,26 +1,25 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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.array; package com.reandroid.arsc.array;
import com.reandroid.arsc.item.IntegerArray;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.TableString; import com.reandroid.arsc.item.TableString;
public class TableStringArray extends StringArray<TableString> { public class TableStringArray extends StringArray<TableString> {
public TableStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { public TableStringArray(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
super(offsets, itemCount, itemStart, is_utf8); super(offsets, itemCount, itemStart, is_utf8);
} }
@Override @Override

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -95,14 +95,17 @@ public class TypeBlockArray extends BlockArray<TypeBlock>
return typeBlock.getEntry(entryId); return typeBlock.getEntry(entryId);
} }
public TypeBlock getOrCreate(ResConfig resConfig){ public TypeBlock getOrCreate(ResConfig resConfig){
TypeBlock typeBlock=getTypeBlock(resConfig); return getOrCreate(resConfig, false);
if(typeBlock!=null){ }
public TypeBlock getOrCreate(ResConfig resConfig, boolean sparse){
TypeBlock typeBlock = getTypeBlock(resConfig, sparse);
if(typeBlock != null){
return typeBlock; return typeBlock;
} }
byte id=getTypeId(); byte id = getTypeId();
typeBlock=createNext(); typeBlock = createNext(sparse);
typeBlock.setTypeId(id); typeBlock.setTypeId(id);
ResConfig config=typeBlock.getResConfig(); ResConfig config = typeBlock.getResConfig();
config.copyFrom(resConfig); config.copyFrom(resConfig);
return typeBlock; return typeBlock;
} }
@ -131,18 +134,24 @@ public class TypeBlockArray extends BlockArray<TypeBlock>
return null; return null;
} }
public TypeBlock getTypeBlock(ResConfig config){ public TypeBlock getTypeBlock(ResConfig config){
return getTypeBlock(config, false);
}
public TypeBlock getTypeBlock(ResConfig config, boolean sparse){
if(config==null){ if(config==null){
return null; return null;
} }
TypeBlock[] items=getChildes(); TypeBlock[] items = getChildes();
if(items==null){ if(items == null){
return null; return null;
} }
int max=items.length; int length = items.length;
for(int i=0;i<max;i++){ for(int i = 0; i < length; i++){
TypeBlock block=items[i]; TypeBlock typeBlock = items[i];
if(config.equals(block.getResConfig())){ if(typeBlock == null || sparse != typeBlock.isSparse()){
return block; continue;
}
if(config.equals(typeBlock.getResConfig())){
return typeBlock;
} }
} }
return null; return null;
@ -204,12 +213,9 @@ public class TypeBlockArray extends BlockArray<TypeBlock>
}; };
} }
private SpecBlock getSpecBlock(){ private SpecBlock getSpecBlock(){
Block parent=getParent(); SpecTypePair parent = getParent(SpecTypePair.class);
while(parent!=null){ if(parent != null){
if(parent instanceof SpecTypePair){ return parent.getSpecBlock();
return ((SpecTypePair) parent).getSpecBlock();
}
parent=parent.getParent();
} }
return null; return null;
} }
@ -223,8 +229,8 @@ public class TypeBlockArray extends BlockArray<TypeBlock>
} }
@Override @Override
public TypeBlock newInstance() { public TypeBlock newInstance() {
byte id=getTypeId(); byte id = getTypeId();
TypeBlock typeBlock=new TypeBlock(); TypeBlock typeBlock = new TypeBlock(false);
typeBlock.setTypeId(id); typeBlock.setTypeId(id);
return typeBlock; return typeBlock;
} }
@ -232,6 +238,13 @@ public class TypeBlockArray extends BlockArray<TypeBlock>
public TypeBlock[] newInstance(int len) { public TypeBlock[] newInstance(int len) {
return new TypeBlock[len]; return new TypeBlock[len];
} }
public TypeBlock createNext(boolean sparse){
byte id = getTypeId();
TypeBlock typeBlock = new TypeBlock(sparse);
typeBlock.setTypeId(id);
add(typeBlock);
return typeBlock;
}
@Override @Override
protected void onRefreshed() { protected void onRefreshed() {
@ -265,16 +278,18 @@ public class TypeBlockArray extends BlockArray<TypeBlock>
public int getHighestEntryCount(){ public int getHighestEntryCount(){
int result=0; int result=0;
for(TypeBlock typeBlock:getChildes()){ for(TypeBlock typeBlock:getChildes()){
int count=typeBlock.getEntryArray().childesCount(); int high = typeBlock.getEntryArray().getHighestEntryId();
if(count>result){ if(high > result){
result=count; result = high;
} }
} }
return result; return result;
} }
public void setEntryCount(int count){ public void setEntryCount(int count){
for(TypeBlock typeBlock:getChildes()){ for(TypeBlock typeBlock:getChildes()){
typeBlock.setEntryCount(count); if(!typeBlock.isSparse()){
typeBlock.setEntryCount(count);
}
} }
} }
public TypeString getTypeString(){ public TypeString getTypeString(){
@ -305,21 +320,22 @@ public class TypeBlockArray extends BlockArray<TypeBlock>
if(json == null){ if(json == null){
return; return;
} }
int length= json.length(); int length = json.length();
ensureSize(length); for(int i = 0; i < length; i++){
for (int i=0;i<length;i++){ JSONObject jsonObject = json.getJSONObject(i);
JSONObject jsonObject=json.getJSONObject(i); TypeBlock typeBlock = createNext(
TypeBlock typeBlock=get(i); jsonObject.optBoolean(TypeBlock.NAME_is_sparse, false));
typeBlock.fromJson(jsonObject); typeBlock.fromJson(jsonObject);
} }
} }
public void merge(TypeBlockArray typeBlockArray){ public void merge(TypeBlockArray typeBlockArray){
if(typeBlockArray==null||typeBlockArray==this){ if(typeBlockArray == null || typeBlockArray == this){
return; return;
} }
for(TypeBlock typeBlock:typeBlockArray.listItems()){ for(TypeBlock typeBlock:typeBlockArray.listItems()){
TypeBlock block=getOrCreate(typeBlock.getResConfig()); TypeBlock exist = getOrCreate(
block.merge(typeBlock); typeBlock.getResConfig(), typeBlock.isSparse());
exist.merge(typeBlock);
} }
} }
/** /**

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -15,13 +15,12 @@
*/ */
package com.reandroid.arsc.array; package com.reandroid.arsc.array;
import com.reandroid.arsc.item.IntegerArray;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.TypeString; import com.reandroid.arsc.item.TypeString;
public class TypeStringArray extends StringArray<TypeString> { public class TypeStringArray extends StringArray<TypeString> {
private int lastCreateIndex; private int lastCreateIndex;
public TypeStringArray(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { public TypeStringArray(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
super(offsets, itemCount, itemStart, is_utf8); super(offsets, itemCount, itemStart, is_utf8);
} }
@Override @Override

View File

@ -83,10 +83,13 @@ public abstract class BlockArray<T extends Block> extends BlockContainer<T> impl
return results; return results;
} }
public Collection<T> listItems(){ public Collection<T> listItems(){
return listItems(false);
}
public Collection<T> listItems(boolean skipNullBlocks){
return new AbstractCollection<T>() { return new AbstractCollection<T>() {
@Override @Override
public Iterator<T> iterator(){ public Iterator<T> iterator(){
return BlockArray.this.iterator(); return BlockArray.this.iterator(skipNullBlocks);
} }
@Override @Override
public boolean contains(Object o){ public boolean contains(Object o){
@ -277,12 +280,15 @@ public abstract class BlockArray<T extends Block> extends BlockContainer<T> impl
} }
int len=items.length; int len=items.length;
for(int i=0;i<len;i++){ for(int i=0;i<len;i++){
if(block==items[i]){ if(isEqual(items[i], block)){
return true; return true;
} }
} }
return false; return false;
} }
protected boolean isEqual(T item, Object obj){
return obj == item;
}
public void remove(Collection<T> blockList){ public void remove(Collection<T> blockList){
T[] items=elementData; T[] items=elementData;
if(items==null || items.length==0){ if(items==null || items.length==0){

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.arsc.chunk;
import com.reandroid.arsc.array.SparseTypeEntryArray;
import com.reandroid.arsc.header.TypeHeader;
import com.reandroid.arsc.value.SparseTypeEntry;
import java.util.Collection;
public class SparseTypeBlock extends Chunk<TypeHeader>{
private final SparseTypeEntryArray entryArray;
public SparseTypeBlock() {
super(new TypeHeader(), 1);
entryArray = new SparseTypeEntryArray();
addChild(entryArray);
getHeaderBlock().setSparse(true);
}
public Collection<SparseTypeEntry> listEntries(){
return getEntryArray().listItems();
}
public SparseTypeEntryArray getEntryArray() {
return entryArray;
}
@Override
protected void onChunkRefreshed() {
getHeaderBlock().setSparse(true);
}
}

View File

@ -1,21 +1,23 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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.chunk; package com.reandroid.arsc.chunk;
import com.reandroid.arsc.array.EntryArray; import com.reandroid.arsc.array.EntryArray;
import com.reandroid.arsc.array.OffsetArray;
import com.reandroid.arsc.array.SparseOffsetsArray;
import com.reandroid.arsc.base.Block; import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.container.SpecTypePair; import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.header.TypeHeader; import com.reandroid.arsc.header.TypeHeader;
@ -39,11 +41,16 @@ public class TypeBlock extends Chunk<TypeHeader>
private final EntryArray mEntryArray; private final EntryArray mEntryArray;
private TypeString mTypeString; private TypeString mTypeString;
public TypeBlock() { public TypeBlock(boolean sparse) {
super(new TypeHeader(), 2); super(new TypeHeader(sparse), 2);
TypeHeader header = getHeaderBlock(); TypeHeader header = getHeaderBlock();
IntegerArray entryOffsets = new IntegerArray(); OffsetArray entryOffsets;
if(sparse){
entryOffsets = new SparseOffsetsArray();
}else {
entryOffsets = new OffsetArray();
}
this.mEntryArray = new EntryArray(entryOffsets, this.mEntryArray = new EntryArray(entryOffsets,
header.getCount(), header.getEntriesStart()); header.getCount(), header.getEntriesStart());
@ -52,6 +59,9 @@ public class TypeBlock extends Chunk<TypeHeader>
addChild(entryOffsets); addChild(entryOffsets);
addChild(mEntryArray); addChild(mEntryArray);
} }
public boolean isSparse(){
return getHeaderBlock().isSparse();
}
public void destroy(){ public void destroy(){
getEntryArray().destroy(); getEntryArray().destroy();
setId(0); setId(0);
@ -227,9 +237,9 @@ public class TypeBlock extends Chunk<TypeHeader>
super.onPreRefreshRefresh(); super.onPreRefreshRefresh();
} }
/* /*
* method Block.addBytes is inefficient for large size byte array * method Block.addBytes is inefficient for large size byte array
* so let's override here because this block is the largest * so let's override here because this block is the largest
*/ */
@Override @Override
public byte[] getBytes(){ public byte[] getBytes(){
ByteArrayOutputStream os=new ByteArrayOutputStream(); ByteArrayOutputStream os=new ByteArrayOutputStream();
@ -242,7 +252,10 @@ public class TypeBlock extends Chunk<TypeHeader>
} }
@Override @Override
public JSONObject toJson() { public JSONObject toJson() {
JSONObject jsonObject=new JSONObject(); JSONObject jsonObject = new JSONObject();
if(isSparse()){
jsonObject.put(NAME_is_sparse, true);
}
jsonObject.put(NAME_id, getId()); jsonObject.put(NAME_id, getId());
jsonObject.put(NAME_name, getTypeName()); jsonObject.put(NAME_name, getTypeName());
jsonObject.put(NAME_config, getResConfig().toJson()); jsonObject.put(NAME_config, getResConfig().toJson());
@ -274,12 +287,16 @@ public class TypeBlock extends Chunk<TypeHeader>
} }
@Override @Override
public int compareTo(TypeBlock typeBlock) { public int compareTo(TypeBlock typeBlock) {
int id1= getId(); int id1 = getId();
int id2=typeBlock.getId(); int id2 = typeBlock.getId();
if(id1!=id2){ if(id1 != id2){
return Integer.compare(id1, id2); return Integer.compare(id1, id2);
} }
return getResConfig().compareTo(typeBlock.getResConfig()); String q1 = (isSparse() ? "1" : "0")
+ getResConfig().getQualifiers();
String q2 = (typeBlock.isSparse() ? "1" : "0")
+ typeBlock.getResConfig().getQualifiers();
return q1.compareTo(q2);
} }
/** /**
* It is allowed to have duplicate entry name therefore it is not recommend to use this. * It is allowed to have duplicate entry name therefore it is not recommend to use this.
@ -311,4 +328,5 @@ public class TypeBlock extends Chunk<TypeHeader>
public static final String NAME_config = "config"; public static final String NAME_config = "config";
public static final String NAME_id = "id"; public static final String NAME_id = "id";
public static final String NAME_entries = "entries"; public static final String NAME_entries = "entries";
public static final String NAME_is_sparse = "is_sparse";
} }

View File

@ -117,13 +117,10 @@ public class PackageBody extends FixedBlockContainer {
TypeHeader typeHeader = reader.readTypeHeader(); TypeHeader typeHeader = reader.readTypeHeader();
SpecTypePair specTypePair = mSpecTypePairArray.getOrCreate(typeHeader.getId().get()); SpecTypePair specTypePair = mSpecTypePairArray.getOrCreate(typeHeader.getId().get());
if(typeHeader.isSparse()){ if(typeHeader.isSparse()){
SparseTypeBlock sparseTypeBlock = specTypePair String junk = "";
.getSparseTypeBlockArray().createNext();
sparseTypeBlock.readBytes(reader);
}else {
TypeBlock typeBlock = specTypePair.getTypeBlockArray().createNext();
typeBlock.readBytes(reader);
} }
TypeBlock typeBlock = specTypePair.getTypeBlockArray().createNext(typeHeader.isSparse());
typeBlock.readBytes(reader);
} }
private void readLibraryBlock(BlockReader reader) throws IOException{ private void readLibraryBlock(BlockReader reader) throws IOException{
LibraryBlock libraryBlock=new LibraryBlock(); LibraryBlock libraryBlock=new LibraryBlock();

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -16,7 +16,6 @@
package com.reandroid.arsc.container; package com.reandroid.arsc.container;
import com.reandroid.arsc.array.EntryArray; import com.reandroid.arsc.array.EntryArray;
import com.reandroid.arsc.array.SparseTypeBlockArray;
import com.reandroid.arsc.chunk.*; import com.reandroid.arsc.chunk.*;
import com.reandroid.arsc.array.TypeBlockArray; import com.reandroid.arsc.array.TypeBlockArray;
import com.reandroid.arsc.base.Block; import com.reandroid.arsc.base.Block;
@ -39,31 +38,23 @@ public class SpecTypePair extends BlockContainer<Block>
private final Block[] mChildes; private final Block[] mChildes;
private final SpecBlock mSpecBlock; private final SpecBlock mSpecBlock;
private final TypeBlockArray mTypeBlockArray; private final TypeBlockArray mTypeBlockArray;
private final SparseTypeBlockArray sparseTypeBlockArray;
public SpecTypePair(SpecBlock specBlock, TypeBlockArray typeBlockArray){ public SpecTypePair(SpecBlock specBlock, TypeBlockArray typeBlockArray){
this.mSpecBlock = specBlock; this.mSpecBlock = specBlock;
this.mTypeBlockArray = typeBlockArray; this.mTypeBlockArray = typeBlockArray;
this.sparseTypeBlockArray = new SparseTypeBlockArray();
this.mChildes=new Block[]{specBlock, typeBlockArray, sparseTypeBlockArray}; this.mChildes = new Block[]{specBlock, typeBlockArray};
mSpecBlock.setIndex(0);
mTypeBlockArray.setIndex(1); specBlock.setIndex(0);
sparseTypeBlockArray.setIndex(2); typeBlockArray.setIndex(1);
mSpecBlock.setParent(this);
mTypeBlockArray.setParent(this); specBlock.setParent(this);
sparseTypeBlockArray.setParent(this); typeBlockArray.setParent(this);
} }
public SpecTypePair(){ public SpecTypePair(){
this(new SpecBlock(), new TypeBlockArray()); this(new SpecBlock(), new TypeBlockArray());
} }
public SparseTypeBlockArray getSparseTypeBlockArray() {
return sparseTypeBlockArray;
}
public Collection<SparseTypeBlock> listSparseTypeBlock(){
return sparseTypeBlockArray.listItems();
}
public Map<Integer, EntryGroup> createEntryGroups(){ public Map<Integer, EntryGroup> createEntryGroups(){
Map<Integer, EntryGroup> map = new HashMap<>(); Map<Integer, EntryGroup> map = new HashMap<>();
for(TypeBlock typeBlock:listTypeBlocks()){ for(TypeBlock typeBlock:listTypeBlocks()){
@ -233,12 +224,8 @@ public class SpecTypePair extends BlockContainer<Block>
} }
private void readTypeBlock(BlockReader reader) throws IOException { private void readTypeBlock(BlockReader reader) throws IOException {
TypeHeader typeHeader = reader.readTypeHeader(); TypeHeader typeHeader = reader.readTypeHeader();
if(typeHeader.isSparse()){ TypeBlock typeBlock = mTypeBlockArray.createNext(typeHeader.isSparse());
SparseTypeBlock sparseTypeBlock = sparseTypeBlockArray.createNext(); typeBlock.readBytes(reader);
sparseTypeBlock.readBytes(reader);
return;
}
mTypeBlockArray.readBytes(reader);
} }
private void readUnexpectedNonSpecBlock(BlockReader reader, HeaderBlock headerBlock) throws IOException{ private void readUnexpectedNonSpecBlock(BlockReader reader, HeaderBlock headerBlock) throws IOException{
throw new IOException("Unexpected block: "+headerBlock.toString()+", Should be: "+ChunkType.SPEC); throw new IOException("Unexpected block: "+headerBlock.toString()+", Should be: "+ChunkType.SPEC);
@ -281,7 +268,6 @@ public class SpecTypePair extends BlockContainer<Block>
} }
getSpecBlock().merge(typePair.getSpecBlock()); getSpecBlock().merge(typePair.getSpecBlock());
getTypeBlockArray().merge(typePair.getTypeBlockArray()); getTypeBlockArray().merge(typePair.getTypeBlockArray());
getSparseTypeBlockArray().merge(typePair.getSparseTypeBlockArray());
} }
@Override @Override
public int compareTo(SpecTypePair specTypePair) { public int compareTo(SpecTypePair specTypePair) {
@ -304,4 +290,5 @@ public class SpecTypePair extends BlockContainer<Block>
} }
public static final String NAME_types = "types"; public static final String NAME_types = "types";
public static final String NAME_sparse_types = "sparse_types";
} }

View File

@ -27,7 +27,7 @@ public class TypeHeader extends HeaderBlock{
private final IntegerItem count; private final IntegerItem count;
private final IntegerItem entriesStart; private final IntegerItem entriesStart;
private final ResConfig config; private final ResConfig config;
public TypeHeader() { public TypeHeader(boolean sparse) {
super(ChunkType.TYPE.ID); super(ChunkType.TYPE.ID);
this.id = new ByteItem(); this.id = new ByteItem();
this.flags = new ByteItem(); this.flags = new ByteItem();
@ -42,6 +42,7 @@ public class TypeHeader extends HeaderBlock{
addChild(count); addChild(count);
addChild(entriesStart); addChild(entriesStart);
addChild(config); addChild(config);
setSparse(sparse);
} }
public boolean isSparse(){ public boolean isSparse(){
return (getFlags().get() & FLAG_SPARSE) == FLAG_SPARSE; return (getFlags().get() & FLAG_SPARSE) == FLAG_SPARSE;

View File

@ -69,7 +69,7 @@ import java.io.*;
return specHeader; return specHeader;
} }
public TypeHeader readTypeHeader() throws IOException{ public TypeHeader readTypeHeader() throws IOException{
TypeHeader typeHeader = new TypeHeader(); TypeHeader typeHeader = new TypeHeader(false);
if(available() < typeHeader.getMinimumSize()){ if(available() < typeHeader.getMinimumSize()){
return null; return null;
} }

View File

@ -113,6 +113,14 @@ public class IntegerArray extends BlockItem {
(bts[i+2] & 0xff) << 16 | (bts[i+2] & 0xff) << 16 |
(bts[i+3] & 0xff) << 24; (bts[i+3] & 0xff) << 24;
} }
public int getAt(int index){
int i=index*4;
byte[] bts = getBytesInternal();
return bts[i] & 0xff |
(bts[i+1] & 0xff) << 8 |
(bts[i+2] & 0xff) << 16 |
(bts[i+3] & 0xff) << 24;
}
public final int size(){ public final int size(){
return getBytesLength()/4; return getBytesLength()/4;
} }

View File

@ -1,24 +1,21 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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.pool; package com.reandroid.arsc.pool;
import com.reandroid.arsc.array.ResXmlIDArray; import com.reandroid.arsc.array.*;
import com.reandroid.arsc.array.StringArray;
import com.reandroid.arsc.array.ResXmlStringArray;
import com.reandroid.arsc.array.StyleArray;
import com.reandroid.arsc.chunk.xml.ResXmlDocument; import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import com.reandroid.arsc.chunk.xml.ResXmlIDMap; import com.reandroid.arsc.chunk.xml.ResXmlIDMap;
import com.reandroid.arsc.group.StringGroup; import com.reandroid.arsc.group.StringGroup;
@ -26,7 +23,7 @@ import com.reandroid.arsc.item.*;
import java.util.Objects; import java.util.Objects;
public class ResXmlStringPool extends StringPool<ResXmlString> { public class ResXmlStringPool extends StringPool<ResXmlString> {
public ResXmlStringPool(boolean is_utf8) { public ResXmlStringPool(boolean is_utf8) {
super(is_utf8); super(is_utf8);
} }
@ -55,7 +52,7 @@ import java.util.Objects;
} }
} }
@Override @Override
StringArray<ResXmlString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { StringArray<ResXmlString> newInstance(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
return new ResXmlStringArray(offsets, itemCount, itemStart, is_utf8); return new ResXmlStringArray(offsets, itemCount, itemStart, is_utf8);
} }
public ResXmlString getOrCreate(String str){ public ResXmlString getOrCreate(String str){

View File

@ -1,25 +1,24 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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.pool; package com.reandroid.arsc.pool;
import com.reandroid.arsc.array.OffsetArray;
import com.reandroid.arsc.array.SpecStringArray; import com.reandroid.arsc.array.SpecStringArray;
import com.reandroid.arsc.array.StringArray; import com.reandroid.arsc.array.StringArray;
import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.item.IntegerArray;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.SpecString; import com.reandroid.arsc.item.SpecString;
@ -29,7 +28,7 @@ public class SpecStringPool extends StringPool<SpecString> {
} }
@Override @Override
StringArray<SpecString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { StringArray<SpecString> newInstance(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
return new SpecStringArray(offsets, itemCount, itemStart, is_utf8); return new SpecStringArray(offsets, itemCount, itemStart, is_utf8);
} }
public PackageBlock getPackageBlock(){ public PackageBlock getPackageBlock(){

View File

@ -1,37 +1,38 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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.pool; package com.reandroid.arsc.pool;
import com.reandroid.arsc.array.StringArray; import com.reandroid.arsc.array.OffsetArray;
import com.reandroid.arsc.array.StyleArray; import com.reandroid.arsc.array.StringArray;
import com.reandroid.arsc.base.Block; import com.reandroid.arsc.array.StyleArray;
import com.reandroid.arsc.chunk.Chunk; import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.group.StringGroup; import com.reandroid.arsc.chunk.Chunk;
import com.reandroid.arsc.header.StringPoolHeader; import com.reandroid.arsc.group.StringGroup;
import com.reandroid.arsc.io.BlockLoad; import com.reandroid.arsc.header.StringPoolHeader;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockLoad;
import com.reandroid.arsc.item.*; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.json.JSONArray; import com.reandroid.arsc.item.*;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONArray;
import com.reandroid.json.JSONConvert;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
public abstract class StringPool<T extends StringItem> extends Chunk<StringPoolHeader> implements BlockLoad, JSONConvert<JSONArray>, Comparator<String> { public abstract class StringPool<T extends StringItem> extends Chunk<StringPoolHeader> implements BlockLoad, JSONConvert<JSONArray>, Comparator<String> {
private final StringArray<T> mArrayStrings; private final StringArray<T> mArrayStrings;
private final StyleArray mArrayStyles; private final StyleArray mArrayStyles;
@ -40,8 +41,8 @@ package com.reandroid.arsc.pool;
StringPool(boolean is_utf8){ StringPool(boolean is_utf8){
super(new StringPoolHeader(), 4); super(new StringPoolHeader(), 4);
IntegerArray offsetStrings = new IntegerArray(); OffsetArray offsetStrings = new OffsetArray();
IntegerArray offsetStyles = new IntegerArray(); OffsetArray offsetStyles = new OffsetArray();
StringPoolHeader header = getHeaderBlock(); StringPoolHeader header = getHeaderBlock();
@ -278,7 +279,7 @@ package com.reandroid.arsc.pool;
getHeaderBlock().setSorted(sorted); getHeaderBlock().setSorted(sorted);
} }
abstract StringArray<T> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8); abstract StringArray<T> newInstance(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8);
@Override @Override
protected void onChunkRefreshed() { protected void onChunkRefreshed() {
mArrayStrings.refreshCountAndStart(); mArrayStrings.refreshCountAndStart();

View File

@ -1,20 +1,21 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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.pool; package com.reandroid.arsc.pool;
import com.reandroid.arsc.array.OffsetArray;
import com.reandroid.arsc.array.StringArray; import com.reandroid.arsc.array.StringArray;
import com.reandroid.arsc.array.TableStringArray; import com.reandroid.arsc.array.TableStringArray;
import com.reandroid.arsc.chunk.ChunkType; import com.reandroid.arsc.chunk.ChunkType;
@ -29,13 +30,13 @@ import com.reandroid.arsc.item.TableString;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
public class TableStringPool extends StringPool<TableString> { public class TableStringPool extends StringPool<TableString> {
public TableStringPool(boolean is_utf8) { public TableStringPool(boolean is_utf8) {
super(is_utf8); super(is_utf8);
} }
@Override @Override
StringArray<TableString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { StringArray<TableString> newInstance(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
return new TableStringArray(offsets, itemCount, itemStart, is_utf8); return new TableStringArray(offsets, itemCount, itemStart, is_utf8);
} }
public void merge(TableStringPool stringPool){ public void merge(TableStringPool stringPool){
@ -58,10 +59,10 @@ import java.io.InputStream;
refreshUniqueIdMap(); refreshUniqueIdMap();
} }
/** /**
* Loads string pool only from table block (resources.arsc) without * Loads string pool only from table block (resources.arsc) without
* loading other chunks * loading other chunks
*/ */
public static TableStringPool readFromTable(InputStream inputStream) throws IOException { public static TableStringPool readFromTable(InputStream inputStream) throws IOException {
TableHeader tableHeader = new TableHeader(); TableHeader tableHeader = new TableHeader();
tableHeader.readBytes(inputStream); tableHeader.readBytes(inputStream);

View File

@ -1,20 +1,21 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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.pool; package com.reandroid.arsc.pool;
import com.reandroid.arsc.array.OffsetArray;
import com.reandroid.arsc.array.StringArray; import com.reandroid.arsc.array.StringArray;
import com.reandroid.arsc.array.TypeStringArray; import com.reandroid.arsc.array.TypeStringArray;
import com.reandroid.arsc.chunk.TypeBlock; import com.reandroid.arsc.chunk.TypeBlock;
@ -80,7 +81,7 @@ public class TypeStringPool extends StringPool<TypeString> {
return group.get(0); return group.get(0);
} }
@Override @Override
StringArray<TypeString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) { StringArray<TypeString> newInstance(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
return new TypeStringArray(offsets, itemCount, itemStart, is_utf8); return new TypeStringArray(offsets, itemCount, itemStart, is_utf8);
} }
} }

View File

@ -1,459 +1,465 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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; package com.reandroid.arsc.value;
import com.reandroid.arsc.array.ResValueMapArray; import com.reandroid.arsc.array.EntryArray;
import com.reandroid.arsc.base.Block; import com.reandroid.arsc.array.ResValueMapArray;
import com.reandroid.arsc.base.BlockCounter; import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.base.BlockCounter;
import com.reandroid.arsc.chunk.SpecBlock; import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.SpecBlock;
import com.reandroid.arsc.chunk.TypeBlock; import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.container.SpecTypePair; import com.reandroid.arsc.chunk.TypeBlock;
import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.item.*; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.json.JSONConvert; import com.reandroid.arsc.item.*;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
public class Entry extends Block implements JSONConvert<JSONObject> { public class Entry extends Block implements JSONConvert<JSONObject> {
private TableEntry<?, ?> mTableEntry; private TableEntry<?, ?> mTableEntry;
public Entry(){ public Entry(){
super(); super();
} }
public ResValue getResValue(){ public ResValue getResValue(){
TableEntry<?, ?> tableEntry = getTableEntry(); TableEntry<?, ?> tableEntry = getTableEntry();
if(tableEntry instanceof ResTableEntry){ if(tableEntry instanceof ResTableEntry){
return ((ResTableEntry)tableEntry).getValue(); return ((ResTableEntry)tableEntry).getValue();
} }
return null; return null;
} }
public ResValueMapArray getResValueMapArray(){ public ResValueMapArray getResValueMapArray(){
TableEntry<?, ?> tableEntry = getTableEntry(); TableEntry<?, ?> tableEntry = getTableEntry();
if(tableEntry instanceof ResTableMapEntry){ if(tableEntry instanceof ResTableMapEntry){
return ((ResTableMapEntry)tableEntry).getValue(); return ((ResTableMapEntry)tableEntry).getValue();
} }
return null; return null;
} }
public SpecFlag getSpecFlag(){ public SpecFlag getSpecFlag(){
SpecBlock specBlock = getSpecBlock(); SpecBlock specBlock = getSpecBlock();
if(specBlock == null){ if(specBlock == null){
return null; return null;
} }
return specBlock.getSpecFlag(getId()); return specBlock.getSpecFlag(getId());
} }
public void ensureComplex(boolean isComplex){ public void ensureComplex(boolean isComplex){
ensureTableEntry(isComplex); ensureTableEntry(isComplex);
} }
public int getId(){ public int getId(){
return getIndex(); int id = getIndex();
} EntryArray entryArray = getParentInstance(EntryArray.class);
public String getName(){ if(entryArray != null){
SpecString specString = getSpecString(); id = entryArray.getEntryId(id);
if(specString!=null){ }
return specString.get(); return id;
} }
return null; public String getName(){
} SpecString specString = getSpecString();
public String getTypeName(){ if(specString!=null){
TypeBlock typeBlock = getTypeBlock(); return specString.get();
if(typeBlock!=null){ }
return typeBlock.getTypeName(); return null;
} }
return null; public String getTypeName(){
} TypeBlock typeBlock = getTypeBlock();
public int getResourceId(){ if(typeBlock!=null){
PackageBlock packageBlock = getPackageBlock(); return typeBlock.getTypeName();
if(packageBlock==null){ }
return 0; return null;
} }
TypeBlock typeBlock = getTypeBlock(); public int getResourceId(){
if(typeBlock==null){ PackageBlock packageBlock = getPackageBlock();
return 0; if(packageBlock==null){
} return 0;
return (packageBlock.getId()<<24) }
| (typeBlock.getId() << 16) TypeBlock typeBlock = getTypeBlock();
| getId(); if(typeBlock==null){
} return 0;
public int getSpecReference(){ }
TableEntry<?, ?> tableEntry = getTableEntry(); return (packageBlock.getId()<<24)
if(tableEntry == null){ | (typeBlock.getId() << 16)
return 0; | getId();
} }
return tableEntry.getHeader().getKey(); public int getSpecReference(){
} TableEntry<?, ?> tableEntry = getTableEntry();
public TypeString getTypeString(){ if(tableEntry == null){
TypeBlock typeBlock = getTypeBlock(); return 0;
if(typeBlock!=null){ }
return typeBlock.getTypeString(); return tableEntry.getHeader().getKey();
} }
return null; public TypeString getTypeString(){
} TypeBlock typeBlock = getTypeBlock();
public boolean isDefault(){ if(typeBlock!=null){
ResConfig resConfig = getResConfig(); return typeBlock.getTypeString();
if(resConfig!=null){ }
return resConfig.isDefault(); return null;
} }
return false; public boolean isDefault(){
} ResConfig resConfig = getResConfig();
public void setSpecReference(StringItem specReference){ if(resConfig!=null){
TableEntry<?, ?> tableEntry = getTableEntry(); return resConfig.isDefault();
if(tableEntry == null){ }
return; return false;
} }
tableEntry.getHeader().setKey(specReference); public void setSpecReference(StringItem specReference){
} TableEntry<?, ?> tableEntry = getTableEntry();
public void setSpecReference(int ref){ if(tableEntry == null){
TableEntry<?, ?> tableEntry = getTableEntry(); return;
if(tableEntry == null){ }
return; tableEntry.getHeader().setKey(specReference);
} }
tableEntry.getHeader().setKey(ref); public void setSpecReference(int ref){
} TableEntry<?, ?> tableEntry = getTableEntry();
private Entry searchEntry(int resourceId){ if(tableEntry == null){
if(resourceId==getResourceId()){ return;
return this; }
} tableEntry.getHeader().setKey(ref);
PackageBlock packageBlock= getPackageBlock(); }
if(packageBlock==null){ private Entry searchEntry(int resourceId){
return null; if(resourceId==getResourceId()){
} return this;
TableBlock tableBlock = packageBlock.getTableBlock(); }
if(tableBlock==null){ PackageBlock packageBlock= getPackageBlock();
return null; if(packageBlock==null){
} return null;
EntryGroup entryGroup = tableBlock.search(resourceId); }
if(entryGroup!=null){ TableBlock tableBlock = packageBlock.getTableBlock();
return entryGroup.pickOne(); if(tableBlock==null){
} return null;
return null; }
} EntryGroup entryGroup = tableBlock.search(resourceId);
public ResValue setValueAsRaw(ValueType valueType, int data){ if(entryGroup!=null){
TableEntry<?, ?> tableEntry = ensureTableEntry(false); return entryGroup.pickOne();
ResValue resValue = (ResValue) tableEntry.getValue(); }
resValue.setTypeAndData(valueType, data); return null;
return resValue; }
} public ResValue setValueAsRaw(ValueType valueType, int data){
public ResValue setValueAsBoolean(boolean val){ TableEntry<?, ?> tableEntry = ensureTableEntry(false);
int data = val?0xffffffff:0; ResValue resValue = (ResValue) tableEntry.getValue();
return setValueAsRaw(ValueType.INT_BOOLEAN, data); resValue.setTypeAndData(valueType, data);
} return resValue;
public ResValue setValueAsReference(int resourceId){ }
return setValueAsRaw(ValueType.REFERENCE, resourceId); public ResValue setValueAsBoolean(boolean val){
} int data = val?0xffffffff:0;
public ResValue setValueAsString(String str){ return setValueAsRaw(ValueType.INT_BOOLEAN, data);
TableEntry<?, ?> tableEntry = ensureTableEntry(false); }
ResValue resValue = (ResValue) tableEntry.getValue(); public ResValue setValueAsReference(int resourceId){
resValue.setValueAsString(str); return setValueAsRaw(ValueType.REFERENCE, resourceId);
return resValue; }
} public ResValue setValueAsString(String str){
public SpecString getSpecString(){ TableEntry<?, ?> tableEntry = ensureTableEntry(false);
TableEntry<?, ?> tableEntry = getTableEntry(); ResValue resValue = (ResValue) tableEntry.getValue();
if(tableEntry == null){ resValue.setValueAsString(str);
return null; return resValue;
} }
PackageBlock packageBlock = getPackageBlock(); public SpecString getSpecString(){
if(packageBlock == null){ TableEntry<?, ?> tableEntry = getTableEntry();
return null; if(tableEntry == null){
} return null;
return packageBlock.getSpecStringPool() }
.get(tableEntry.getHeader().getKey()); PackageBlock packageBlock = getPackageBlock();
} if(packageBlock == null){
public ResConfig getResConfig(){ return null;
TypeBlock typeBlock = getTypeBlock(); }
if(typeBlock!=null){ return packageBlock.getSpecStringPool()
return typeBlock.getResConfig(); .get(tableEntry.getHeader().getKey());
} }
return null; public ResConfig getResConfig(){
} TypeBlock typeBlock = getTypeBlock();
public SpecBlock getSpecBlock(){ if(typeBlock!=null){
TypeBlock typeBlock = getTypeBlock(); return typeBlock.getResConfig();
if(typeBlock == null){ }
return null; return null;
} }
SpecTypePair specTypePair = typeBlock.getParentSpecTypePair(); public SpecBlock getSpecBlock(){
if(specTypePair==null){ TypeBlock typeBlock = getTypeBlock();
return null; if(typeBlock == null){
} return null;
return specTypePair.getSpecBlock(); }
} SpecTypePair specTypePair = typeBlock.getParentSpecTypePair();
public TypeBlock getTypeBlock(){ if(specTypePair==null){
return getParent(TypeBlock.class); return null;
} }
private String getPackageName(){ return specTypePair.getSpecBlock();
PackageBlock packageBlock = getPackageBlock(); }
if(packageBlock!=null){ public TypeBlock getTypeBlock(){
return packageBlock.getName(); return getParent(TypeBlock.class);
} }
return null; private String getPackageName(){
} PackageBlock packageBlock = getPackageBlock();
public PackageBlock getPackageBlock(){ if(packageBlock!=null){
return getParent(PackageBlock.class); return packageBlock.getName();
} }
private TableEntry<?, ?> ensureTableEntry(boolean is_complex){ return null;
TableEntry<?, ?> tableEntry = getTableEntry(); }
public PackageBlock getPackageBlock(){
return getParent(PackageBlock.class);
}
private TableEntry<?, ?> ensureTableEntry(boolean is_complex){
TableEntry<?, ?> tableEntry = getTableEntry();
boolean is_correct_type = (is_complex && tableEntry instanceof ResTableMapEntry) || (!is_complex && tableEntry instanceof ResTableEntry); boolean is_correct_type = (is_complex && tableEntry instanceof ResTableMapEntry) || (!is_complex && tableEntry instanceof ResTableEntry);
if (tableEntry == null || !is_correct_type) { if (tableEntry == null || !is_correct_type) {
tableEntry = createTableEntry(is_complex); tableEntry = createTableEntry(is_complex);
setTableEntry(tableEntry); setTableEntry(tableEntry);
} }
return tableEntry; return tableEntry;
} }
public TableEntry<?, ?> getTableEntry(){ public TableEntry<?, ?> getTableEntry(){
return mTableEntry; return mTableEntry;
} }
public ValueHeader getHeader(){ public ValueHeader getHeader(){
TableEntry<?, ?> tableEntry = getTableEntry(); TableEntry<?, ?> tableEntry = getTableEntry();
if(tableEntry!=null){ if(tableEntry!=null){
return tableEntry.getHeader(); return tableEntry.getHeader();
} }
return null; return null;
} }
@Override @Override
public boolean isNull(){ public boolean isNull(){
return getTableEntry()==null; return getTableEntry()==null;
} }
@Override @Override
public void setNull(boolean is_null){ public void setNull(boolean is_null){
if(is_null){ if(is_null){
setTableEntry(null); setTableEntry(null);
} }
} }
@Override @Override
public byte[] getBytes() { public byte[] getBytes() {
if(isNull()){ if(isNull()){
return null; return null;
} }
return getTableEntry().getBytes(); return getTableEntry().getBytes();
} }
@Override @Override
public int countBytes() { public int countBytes() {
if(isNull()){ if(isNull()){
return 0; return 0;
} }
return getTableEntry().countBytes(); return getTableEntry().countBytes();
} }
@Override @Override
public void onCountUpTo(BlockCounter counter) { public void onCountUpTo(BlockCounter counter) {
if(counter.FOUND){ if(counter.FOUND){
return; return;
} }
if(counter.END==this){ if(counter.END==this){
counter.FOUND=true; counter.FOUND=true;
return; return;
} }
if(isNull()){ if(isNull()){
return; return;
} }
counter.addCount(getTableEntry().countBytes()); counter.addCount(getTableEntry().countBytes());
} }
@Override @Override
protected int onWriteBytes(OutputStream stream) throws IOException { protected int onWriteBytes(OutputStream stream) throws IOException {
if(isNull()){ if(isNull()){
return 0; return 0;
} }
return getTableEntry().writeBytes(stream); return getTableEntry().writeBytes(stream);
} }
@Override @Override
public void onReadBytes(BlockReader reader) throws IOException { public void onReadBytes(BlockReader reader) throws IOException {
TableEntry<?, ?> tableEntry = createTableEntry(reader); TableEntry<?, ?> tableEntry = createTableEntry(reader);
setTableEntry(tableEntry); setTableEntry(tableEntry);
tableEntry.readBytes(reader); tableEntry.readBytes(reader);
} }
public boolean isComplex(){ public boolean isComplex(){
return getTableEntry() instanceof ResTableMapEntry; return getTableEntry() instanceof ResTableMapEntry;
} }
public void setTableEntry(TableEntry<?, ?> tableEntry){ public void setTableEntry(TableEntry<?, ?> tableEntry){
if(tableEntry==this.mTableEntry){ if(tableEntry==this.mTableEntry){
return; return;
} }
onTableEntryRemoved(); onTableEntryRemoved();
if(tableEntry==null){ if(tableEntry==null){
return; return;
} }
tableEntry.setIndex(0); tableEntry.setIndex(0);
tableEntry.setParent(this); tableEntry.setParent(this);
this.mTableEntry = tableEntry; this.mTableEntry = tableEntry;
onTableEntryAdded(); onTableEntryAdded();
} }
private void onTableEntryAdded(){ private void onTableEntryAdded(){
PackageBlock packageBlock = getPackageBlock(); PackageBlock packageBlock = getPackageBlock();
if(packageBlock!=null){ if(packageBlock!=null){
packageBlock.onEntryAdded(this); packageBlock.onEntryAdded(this);
} }
} }
private void onTableEntryRemoved(){ private void onTableEntryRemoved(){
TableEntry<?, ?> exist = this.mTableEntry; TableEntry<?, ?> exist = this.mTableEntry;
if(exist == null){ if(exist == null){
return; return;
} }
PackageBlock packageBlock = getPackageBlock(); PackageBlock packageBlock = getPackageBlock();
if(packageBlock!=null){ if(packageBlock!=null){
packageBlock.removeEntryGroup(this); packageBlock.removeEntryGroup(this);
} }
exist.onRemoved(); exist.onRemoved();
exist.setIndex(-1); exist.setIndex(-1);
exist.setParent(null); exist.setParent(null);
this.mTableEntry = null; this.mTableEntry = null;
} }
private TableEntry<?, ?> createTableEntry(BlockReader reader) throws IOException { private TableEntry<?, ?> createTableEntry(BlockReader reader) throws IOException {
int startPosition = reader.getPosition(); int startPosition = reader.getPosition();
reader.offset(2); reader.offset(2);
boolean is_complex = (0x0001 & reader.readShort()) == 0x0001; boolean is_complex = (0x0001 & reader.readShort()) == 0x0001;
reader.seek(startPosition); reader.seek(startPosition);
return createTableEntry(is_complex); return createTableEntry(is_complex);
} }
private TableEntry<?, ?> createTableEntry(boolean is_complex) { private TableEntry<?, ?> createTableEntry(boolean is_complex) {
if(is_complex){ if(is_complex){
return new ResTableMapEntry(); return new ResTableMapEntry();
}else { }else {
return new ResTableEntry(); return new ResTableEntry();
} }
} }
@Override @Override
public JSONObject toJson() { public JSONObject toJson() {
if(isNull()){ if(isNull()){
return null; return null;
} }
return getTableEntry().toJson(); return getTableEntry().toJson();
} }
@Override @Override
public void fromJson(JSONObject json) { public void fromJson(JSONObject json) {
if(json==null){ if(json==null){
setNull(true); setNull(true);
return; return;
} }
boolean is_complex = json.optBoolean(ValueHeader.NAME_is_complex, false); boolean is_complex = json.optBoolean(ValueHeader.NAME_is_complex, false);
TableEntry<?, ?> entry = createTableEntry(is_complex); TableEntry<?, ?> entry = createTableEntry(is_complex);
setTableEntry(entry); setTableEntry(entry);
entry.fromJson(json); entry.fromJson(json);
} }
public void merge(Entry entry){ public void merge(Entry entry){
if(!shouldMerge(entry)){ if(!shouldMerge(entry)){
return; return;
} }
TableEntry<?, ?> tableEntry = entry.getTableEntry(); TableEntry<?, ?> tableEntry = entry.getTableEntry();
TableEntry<?, ?> existEntry = ensureTableEntry(tableEntry instanceof ResTableMapEntry); TableEntry<?, ?> existEntry = ensureTableEntry(tableEntry instanceof ResTableMapEntry);
existEntry.merge(tableEntry); existEntry.merge(tableEntry);
} }
private boolean shouldMerge(Entry coming){ private boolean shouldMerge(Entry coming){
if(coming == null || coming == this || coming.isNull()){ if(coming == null || coming == this || coming.isNull()){
return false; return false;
} }
if(this.isNull()){ if(this.isNull()){
return true; return true;
} }
return getTableEntry().shouldMerge(coming.getTableEntry()); return getTableEntry().shouldMerge(coming.getTableEntry());
} }
public String buildResourceName(int resourceId, char prefix, boolean includeType){ public String buildResourceName(int resourceId, char prefix, boolean includeType){
if(resourceId==0){ if(resourceId==0){
return null; return null;
} }
Entry entry=searchEntry(resourceId); Entry entry=searchEntry(resourceId);
return buildResourceName(entry, prefix, includeType); return buildResourceName(entry, prefix, includeType);
} }
public String buildResourceName(Entry entry, char prefix, boolean includeType){ public String buildResourceName(Entry entry, char prefix, boolean includeType){
if(entry==null){ if(entry==null){
return null; return null;
} }
String pkgName=entry.getPackageName(); String pkgName=entry.getPackageName();
if(getResourceId()==entry.getResourceId()){ if(getResourceId()==entry.getResourceId()){
pkgName=null; pkgName=null;
}else if(pkgName!=null){ }else if(pkgName!=null){
if(pkgName.equals(this.getPackageName())){ if(pkgName.equals(this.getPackageName())){
pkgName=null; pkgName=null;
} }
} }
String type=null; String type=null;
if(includeType){ if(includeType){
type=entry.getTypeName(); type=entry.getTypeName();
} }
String name=entry.getName(); String name=entry.getName();
return buildResourceName(prefix, pkgName, type, name); return buildResourceName(prefix, pkgName, type, name);
} }
public String getResourceName(){ public String getResourceName(){
return buildResourceName('@',null, getTypeName(), getName()); return buildResourceName('@',null, getTypeName(), getName());
} }
public String getResourceName(char prefix){ public String getResourceName(char prefix){
return getResourceName(prefix, false, true); return getResourceName(prefix, false, true);
} }
public String getResourceName(char prefix, boolean includePackage, boolean includeType){ public String getResourceName(char prefix, boolean includePackage, boolean includeType){
String pkg=includePackage?getPackageName():null; String pkg=includePackage?getPackageName():null;
String type=includeType?getTypeName():null; String type=includeType?getTypeName():null;
return buildResourceName(prefix,pkg, type, getName()); return buildResourceName(prefix,pkg, type, getName());
} }
@Override @Override
public String toString(){ public String toString(){
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append(String.format("0x%08x", getResourceId())); builder.append(String.format("0x%08x", getResourceId()));
builder.append(' '); builder.append(' ');
ResConfig resConfig = getResConfig(); ResConfig resConfig = getResConfig();
if(resConfig!=null){ if(resConfig!=null){
builder.append(resConfig); builder.append(resConfig);
builder.append(' '); builder.append(' ');
} }
SpecFlag specFlag = getSpecFlag(); SpecFlag specFlag = getSpecFlag();
if(specFlag!=null){ if(specFlag!=null){
builder.append(specFlag); builder.append(specFlag);
builder.append(' '); builder.append(' ');
} }
if(isNull()){ if(isNull()){
builder.append("NULL"); builder.append("NULL");
return builder.toString(); return builder.toString();
} }
builder.append('@'); builder.append('@');
builder.append(getTypeName()); builder.append(getTypeName());
builder.append('/'); builder.append('/');
builder.append(getName()); builder.append(getName());
return builder.toString(); return builder.toString();
} }
public static String buildResourceName(char prefix, String packageName, String type, String name){ public static String buildResourceName(char prefix, String packageName, String type, String name){
if(name==null){ if(name==null){
return null; return null;
} }
StringBuilder builder=new StringBuilder(); StringBuilder builder=new StringBuilder();
if(prefix!=0){ if(prefix!=0){
builder.append(prefix); builder.append(prefix);
} }
if(packageName!=null){ if(packageName!=null){
builder.append(packageName); builder.append(packageName);
builder.append(':'); builder.append(':');
} }
if(type!=null){ if(type!=null){
builder.append(type); builder.append(type);
builder.append('/'); builder.append('/');
} }
builder.append(name); builder.append(name);
return builder.toString(); return builder.toString();
} }
public static final String NAME_id = "id"; public static final String NAME_id = "id";
} }

View File

@ -1,40 +0,0 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.arsc.value;
import com.reandroid.arsc.item.BlockItem;
public class SparseTypeEntry extends BlockItem {
public SparseTypeEntry() {
super(4);
}
public int getIdx(){
return getShort(getBytesInternal(), 0) & 0xffff;
}
public void setIdx(int idx){
putShort(getBytesInternal(), 0, (short) idx);
}
public int getOffset(){
return getShort(getBytesInternal(), 2) & 0xffff;
}
public void setOffset(int offset){
putShort(getBytesInternal(), 2, (short) offset);
}
@Override
public String toString(){
return "idx=" + getIdx() + ", offset=" + getOffset();
}
}