performance: link pool strings when only needed #36

This commit is contained in:
REAndroid 2023-04-28 19:06:52 +02:00
parent c80e0943d3
commit b6bbac2dcf
22 changed files with 695 additions and 530 deletions

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,6 +16,8 @@
package com.reandroid.arsc.array; package com.reandroid.arsc.array;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.Entry;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONArray; import com.reandroid.json.JSONArray;
@ -28,6 +30,20 @@ public class EntryArray extends OffsetBlockArray<Entry> implements JSONConvert<J
public EntryArray(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart){ public EntryArray(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart){
super(offsets, itemCount, itemStart); super(offsets, itemCount, itemStart);
} }
public void linkTableStringsInternal(TableStringPool tableStringPool){
Iterator<Entry> itr = iterator(true);
while (itr.hasNext()){
Entry entry = itr.next();
entry.linkTableStringsInternal(tableStringPool);
}
}
public void linkSpecStringsInternal(SpecStringPool specStringPool){
Iterator<Entry> itr = iterator(true);
while (itr.hasNext()){
Entry entry = itr.next();
entry.linkSpecStringsInternal(specStringPool);
}
}
public int getHighestEntryId(){ public int getHighestEntryId(){
if(isSparse()){ if(isSparse()){
return ((SparseOffsetsArray) getOffsetArray()).getHighestId(); return ((SparseOffsetsArray) getOffsetArray()).getHighestId();

View File

@ -27,6 +27,7 @@ import com.reandroid.arsc.header.PackageHeader;
import com.reandroid.arsc.list.OverlayableList; import com.reandroid.arsc.list.OverlayableList;
import com.reandroid.arsc.list.StagedAliasList; import com.reandroid.arsc.list.StagedAliasList;
import com.reandroid.arsc.pool.SpecStringPool; import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.pool.TypeStringPool; import com.reandroid.arsc.pool.TypeStringPool;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.LibraryInfo; import com.reandroid.arsc.value.LibraryInfo;
@ -68,6 +69,16 @@ public class PackageBlock extends Chunk<PackageHeader>
addChild(mSpecStringPool); addChild(mSpecStringPool);
addChild(mBody); addChild(mBody);
} }
public void linkTableStringsInternal(TableStringPool tableStringPool){
for(SpecTypePair specTypePair : listAllSpecTypePair()){
specTypePair.linkTableStringsInternal(tableStringPool);
}
}
public void linkSpecStringsInternal(SpecStringPool specStringPool){
for(SpecTypePair specTypePair : listAllSpecTypePair()){
specTypePair.linkSpecStringsInternal(specStringPool);
}
}
public void destroy(){ public void destroy(){
getEntriesGroupMap().clear(); getEntriesGroupMap().clear();
getPackageBody().destroy(); getPackageBody().destroy();
@ -221,6 +232,7 @@ public class PackageBlock extends Chunk<PackageHeader>
if(!this.entryGroupMapLocked){ if(!this.entryGroupMapLocked){
return; return;
} }
entryGroupMapLocked = false;
Map<Integer, EntryGroup> map = this.mEntriesGroup; Map<Integer, EntryGroup> map = this.mEntriesGroup;
map.clear(); map.clear();
createEntryGroupMap(map); createEntryGroupMap(map);

View File

@ -53,6 +53,11 @@ public class TableBlock extends Chunk<TableHeader>
addChild(mPackageArray); addChild(mPackageArray);
} }
public void linkTableStringsInternal(TableStringPool tableStringPool){
for(PackageBlock packageBlock : listPackages()){
packageBlock.linkTableStringsInternal(tableStringPool);
}
}
public List<Entry> resolveReference(int referenceId){ public List<Entry> resolveReference(int referenceId){
return resolveReference(referenceId, null); return resolveReference(referenceId, null);
} }

View File

@ -24,6 +24,8 @@ import com.reandroid.arsc.header.TypeHeader;
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.*; import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.pool.TypeStringPool; import com.reandroid.arsc.pool.TypeStringPool;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ResConfig; import com.reandroid.arsc.value.ResConfig;
@ -59,6 +61,14 @@ public class TypeBlock extends Chunk<TypeHeader>
addChild(entryOffsets); addChild(entryOffsets);
addChild(mEntryArray); addChild(mEntryArray);
} }
public void linkTableStringsInternal(TableStringPool tableStringPool){
EntryArray entryArray = getEntryArray();
entryArray.linkTableStringsInternal(tableStringPool);
}
public void linkSpecStringsInternal(SpecStringPool specStringPool){
EntryArray entryArray = getEntryArray();
entryArray.linkSpecStringsInternal(specStringPool);
}
public boolean isSparse(){ public boolean isSparse(){
return getHeaderBlock().isSparse(); return getHeaderBlock().isSparse();
} }

View File

@ -1,489 +1,490 @@
/* /*
* 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.xml; package com.reandroid.arsc.chunk.xml;
import com.reandroid.arsc.decoder.ValueDecoder; import com.reandroid.arsc.decoder.ValueDecoder;
import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*; import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.ResXmlStringPool; import com.reandroid.arsc.pool.ResXmlStringPool;
import com.reandroid.arsc.pool.StringPool; import com.reandroid.arsc.pool.StringPool;
import com.reandroid.arsc.value.AttributeValue; import com.reandroid.arsc.value.AttributeValue;
import com.reandroid.arsc.value.ValueItem; import com.reandroid.arsc.value.ValueItem;
import com.reandroid.arsc.value.ValueType; import com.reandroid.arsc.value.ValueType;
import com.reandroid.common.EntryStore; import com.reandroid.common.EntryStore;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
import com.reandroid.xml.XMLAttribute; import com.reandroid.xml.XMLAttribute;
import com.reandroid.xml.XMLException; import com.reandroid.xml.XMLException;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
public class ResXmlAttribute extends ValueItem implements AttributeValue, Comparable<ResXmlAttribute>{ public class ResXmlAttribute extends ValueItem implements AttributeValue, Comparable<ResXmlAttribute>{
private ReferenceItem mNSReference; private ReferenceItem mNSReference;
private ReferenceItem mNameReference; private ReferenceItem mNameReference;
private ReferenceItem mNameIdReference; private ReferenceItem mNameIdReference;
private ReferenceItem mValueStringReference; private ReferenceItem mValueStringReference;
public ResXmlAttribute(int attributeUnitSize) { public ResXmlAttribute(int attributeUnitSize) {
super(attributeUnitSize, OFFSET_SIZE); super(attributeUnitSize, OFFSET_SIZE);
byte[] bts = getBytesInternal(); byte[] bts = getBytesInternal();
putInteger(bts, OFFSET_NS, -1); putInteger(bts, OFFSET_NS, -1);
putInteger(bts, OFFSET_NAME, -1); putInteger(bts, OFFSET_NAME, -1);
putInteger(bts, OFFSET_STRING, -1); putInteger(bts, OFFSET_STRING, -1);
} }
public ResXmlAttribute() { public ResXmlAttribute() {
this(20); this(20);
} }
public String getUri(){ public String getUri(){
return getString(getNamespaceReference()); return getString(getNamespaceReference());
} }
public String getFullName(){ public String getFullName(){
String name=getName(); String name=getName();
if(name==null){ if(name==null){
return null; return null;
} }
String prefix=getNamePrefix(); String prefix=getNamePrefix();
if(prefix==null){ if(prefix==null){
return name; return name;
} }
return prefix+":"+name; return prefix+":"+name;
} }
public String getName(){ public String getName(){
return getString(getNameReference()); return getString(getNameReference());
} }
public String getNamePrefix(){ public String getNamePrefix(){
ResXmlElement xmlElement=getParentResXmlElement(); ResXmlElement xmlElement=getParentResXmlElement();
if(xmlElement==null){ if(xmlElement==null){
return null; return null;
} }
ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference()); ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference());
if(startNamespace==null){ if(startNamespace==null){
return null; return null;
} }
return startNamespace.getPrefix(); return startNamespace.getPrefix();
} }
// WARN! Careful this is not real value // WARN! Careful this is not real value
public String getValueString(){ public String getValueString(){
return getString(getValueStringReference()); return getString(getValueStringReference());
} }
@Override @Override
public int getNameResourceID(){ public int getNameResourceID(){
ResXmlID xmlID = getResXmlID(); ResXmlID xmlID = getResXmlID();
if(xmlID != null){ if(xmlID != null){
return xmlID.get(); return xmlID.get();
} }
return 0; return 0;
} }
@Override @Override
public void setNameResourceID(int resourceId){ public void setNameResourceID(int resourceId){
ResXmlIDMap xmlIDMap=getResXmlIDMap(); ResXmlIDMap xmlIDMap=getResXmlIDMap();
if(xmlIDMap==null){ if(xmlIDMap==null){
return; return;
} }
ResXmlID xmlID = xmlIDMap.getOrCreate(resourceId); ResXmlID xmlID = xmlIDMap.getOrCreate(resourceId);
setNameReference(xmlID.getIndex()); setNameReference(xmlID.getIndex());
} }
public void setName(String name, int resourceId){ public void setName(String name, int resourceId){
if(Objects.equals(name, getName()) && resourceId==getNameResourceID()){ if(Objects.equals(name, getName()) && resourceId==getNameResourceID()){
return; return;
} }
unlink(mNameReference); unlink(mNameReference);
unLinkNameId(getResXmlID()); unLinkNameId(getResXmlID());
ResXmlString xmlString = getOrCreateAttributeName(name, resourceId); ResXmlString xmlString = getOrCreateAttributeName(name, resourceId);
if(xmlString==null){ if(xmlString==null){
return; return;
} }
setNameReference(xmlString.getIndex()); setNameReference(xmlString.getIndex());
mNameReference = link(OFFSET_NAME); mNameReference = link(OFFSET_NAME);
linkNameId(); linkNameId();
} }
private void linkStartNameSpace(){ private void linkStartNameSpace(){
ResXmlElement xmlElement=getParentResXmlElement(); ResXmlElement xmlElement=getParentResXmlElement();
if(xmlElement==null){ if(xmlElement==null){
return; return;
} }
ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference()); ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference());
if(startNamespace==null){ if(startNamespace==null){
return; return;
} }
startNamespace.addAttributeReference(this); startNamespace.addAttributeReference(this);
} }
private void unLinkStartNameSpace(){ private void unLinkStartNameSpace(){
ResXmlElement xmlElement=getParentResXmlElement(); ResXmlElement xmlElement=getParentResXmlElement();
if(xmlElement==null){ if(xmlElement==null){
return; return;
} }
ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference()); ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference());
if(startNamespace==null){ if(startNamespace==null){
return; return;
} }
startNamespace.removeAttributeReference(this); startNamespace.removeAttributeReference(this);
} }
private ResXmlString getOrCreateAttributeName(String name, int resourceId){ private ResXmlString getOrCreateAttributeName(String name, int resourceId){
ResXmlStringPool stringPool = getStringPool(); ResXmlStringPool stringPool = getStringPool();
if(stringPool==null){ if(stringPool==null){
return null; return null;
} }
return stringPool.getOrCreateAttribute(resourceId, name); return stringPool.getOrCreateAttribute(resourceId, name);
} }
public ResXmlElement getParentResXmlElement(){ public ResXmlElement getParentResXmlElement(){
return getParent(ResXmlElement.class); return getParent(ResXmlElement.class);
} }
public int getAttributesUnitSize(){ public int getAttributesUnitSize(){
return OFFSET_SIZE + super.getSize(); return OFFSET_SIZE + super.getSize();
} }
public void setAttributesUnitSize(int size){ public void setAttributesUnitSize(int size){
int eight = size - OFFSET_SIZE; int eight = size - OFFSET_SIZE;
super.setSize(eight); super.setSize(eight);
} }
private String getString(int ref){ private String getString(int ref){
if(ref<0){ if(ref<0){
return null; return null;
} }
StringPool<?> stringPool = getStringPool(); StringPool<?> stringPool = getStringPool();
if(stringPool == null){ if(stringPool == null){
return null; return null;
} }
StringItem stringItem = stringPool.get(ref); StringItem stringItem = stringPool.get(ref);
if(stringItem == null){ if(stringItem == null){
return null; return null;
} }
return stringItem.getHtml(); return stringItem.getHtml();
} }
private ResXmlID getResXmlID(){ private ResXmlID getResXmlID(){
ResXmlIDMap xmlIDMap = getResXmlIDMap(); ResXmlIDMap xmlIDMap = getResXmlIDMap();
if(xmlIDMap == null){ if(xmlIDMap == null){
return null; return null;
} }
return xmlIDMap.getResXmlIDArray().get(getNameReference()); return xmlIDMap.getResXmlIDArray().get(getNameReference());
} }
private ResXmlIDMap getResXmlIDMap(){ private ResXmlIDMap getResXmlIDMap(){
ResXmlElement xmlElement=getParentResXmlElement(); ResXmlElement xmlElement=getParentResXmlElement();
if(xmlElement!=null){ if(xmlElement!=null){
return xmlElement.getResXmlIDMap(); return xmlElement.getResXmlIDMap();
} }
return null; return null;
} }
int getNamespaceReference(){ int getNamespaceReference(){
return getInteger(getBytesInternal(), OFFSET_NS); return getInteger(getBytesInternal(), OFFSET_NS);
} }
public void setNamespaceReference(int ref){ public void setNamespaceReference(int ref){
if(ref == getNamespaceReference()){ if(ref == getNamespaceReference()){
return; return;
} }
unlink(mNSReference); unlink(mNSReference);
putInteger(getBytesInternal(), OFFSET_NS, ref); putInteger(getBytesInternal(), OFFSET_NS, ref);
mNSReference = link(OFFSET_NS); mNSReference = link(OFFSET_NS);
linkStartNameSpace(); linkStartNameSpace();
} }
int getNameReference(){ int getNameReference(){
return getInteger(getBytesInternal(), OFFSET_NAME); return getInteger(getBytesInternal(), OFFSET_NAME);
} }
void setNameReference(int ref){ void setNameReference(int ref){
if(ref == getNameReference()){ if(ref == getNameReference()){
return; return;
} }
unLinkNameId(getResXmlID()); unLinkNameId(getResXmlID());
unlink(mNameReference); unlink(mNameReference);
putInteger(getBytesInternal(), OFFSET_NAME, ref); putInteger(getBytesInternal(), OFFSET_NAME, ref);
mNameReference = link(OFFSET_NAME); mNameReference = link(OFFSET_NAME);
linkNameId(); linkNameId();
} }
int getValueStringReference(){ int getValueStringReference(){
return getInteger(getBytesInternal(), OFFSET_STRING); return getInteger(getBytesInternal(), OFFSET_STRING);
} }
void setValueStringReference(int ref){ void setValueStringReference(int ref){
if(ref == getValueStringReference() && mValueStringReference!=null){ if(ref == getValueStringReference() && mValueStringReference!=null){
return; return;
} }
StringPool<?> stringPool = getStringPool(); StringPool<?> stringPool = getStringPool();
if(stringPool == null){ if(stringPool == null){
return; return;
} }
StringItem stringItem = stringPool.get(ref); StringItem stringItem = stringPool.get(ref);
unlink(mValueStringReference); unlink(mValueStringReference);
if(stringItem!=null){ if(stringItem!=null){
ref = stringItem.getIndex(); ref = stringItem.getIndex();
} }
putInteger(getBytesInternal(), OFFSET_STRING, ref); putInteger(getBytesInternal(), OFFSET_STRING, ref);
ReferenceItem referenceItem = null; ReferenceItem referenceItem = null;
if(stringItem!=null){ if(stringItem!=null){
referenceItem = new ReferenceBlock<>(this, OFFSET_STRING); referenceItem = new ReferenceBlock<>(this, OFFSET_STRING);
stringItem.addReference(referenceItem); stringItem.addReference(referenceItem);
} }
mValueStringReference = referenceItem; mValueStringReference = referenceItem;
} }
@Override @Override
public void onReadBytes(BlockReader reader) throws IOException { public void onReadBytes(BlockReader reader) throws IOException {
super.onReadBytes(reader); super.onReadBytes(reader);
linkAll(); super.onDataLoaded();
linkStartNameSpace(); linkAll();
} linkStartNameSpace();
@Override }
public void onRemoved(){ @Override
super.onRemoved(); public void onRemoved(){
unLinkStartNameSpace(); super.onRemoved();
unlinkAll(); unLinkStartNameSpace();
} unlinkAll();
@Override }
protected void onUnlinkDataString(ReferenceItem referenceItem){ @Override
unlink(referenceItem); protected void onUnlinkDataString(ReferenceItem referenceItem){
} unlink(referenceItem);
@Override }
protected void onDataChanged(){ @Override
if(getValueType()==ValueType.STRING){ protected void onDataChanged(){
setValueStringReference(getData()); if(getValueType()==ValueType.STRING){
}else { setValueStringReference(getData());
setValueStringReference(-1); }else {
} setValueStringReference(-1);
} }
@Override }
public ResXmlDocument getParentChunk() { @Override
ResXmlElement element = getParentResXmlElement(); public ResXmlDocument getParentChunk() {
if(element!=null){ ResXmlElement element = getParentResXmlElement();
return element.getParentDocument(); if(element!=null){
} return element.getParentDocument();
return null; }
} return null;
}
private void linkNameId(){ private void linkNameId(){
ResXmlID xmlID = getResXmlID(); ResXmlID xmlID = getResXmlID();
if(xmlID==null){ if(xmlID==null){
return; return;
} }
unLinkNameId(xmlID); unLinkNameId(xmlID);
ReferenceItem referenceItem = new ReferenceBlock<>(this, OFFSET_NAME); ReferenceItem referenceItem = new ReferenceBlock<>(this, OFFSET_NAME);
xmlID.addReference(referenceItem); xmlID.addReference(referenceItem);
mNameIdReference = referenceItem; mNameIdReference = referenceItem;
} }
private void unLinkNameId(ResXmlID xmlID){ private void unLinkNameId(ResXmlID xmlID){
ReferenceItem referenceItem = mNameIdReference; ReferenceItem referenceItem = mNameIdReference;
if(referenceItem==null || xmlID == null){ if(referenceItem==null || xmlID == null){
return; return;
} }
xmlID.removeReference(referenceItem); xmlID.removeReference(referenceItem);
mNameIdReference = null; mNameIdReference = null;
if(xmlID.hasReference()){ if(xmlID.hasReference()){
return; return;
} }
ResXmlIDMap xmlIDMap = getResXmlIDMap(); ResXmlIDMap xmlIDMap = getResXmlIDMap();
if(xmlIDMap == null){ if(xmlIDMap == null){
return; return;
} }
xmlIDMap.removeSafely(xmlID); xmlIDMap.removeSafely(xmlID);
} }
private void linkAll(){ private void linkAll(){
unlink(mNSReference); unlink(mNSReference);
mNSReference = link(OFFSET_NS); mNSReference = link(OFFSET_NS);
unlink(mNameReference); unlink(mNameReference);
mNameReference = link(OFFSET_NAME); mNameReference = link(OFFSET_NAME);
unlink(mValueStringReference); unlink(mValueStringReference);
mValueStringReference = link(OFFSET_STRING); mValueStringReference = link(OFFSET_STRING);
linkNameId(); linkNameId();
} }
private void unlinkAll(){ private void unlinkAll(){
unlink(mNSReference); unlink(mNSReference);
unlink(mNameReference); unlink(mNameReference);
unlink(mValueStringReference); unlink(mValueStringReference);
mNSReference = null; mNSReference = null;
mNameReference = null; mNameReference = null;
mValueStringReference = null; mValueStringReference = null;
unLinkNameId(getResXmlID()); unLinkNameId(getResXmlID());
} }
private ReferenceItem link(int offset){ private ReferenceItem link(int offset){
if(offset<0){ if(offset<0){
return null; return null;
} }
StringPool<?> stringPool = getStringPool(); StringPool<?> stringPool = getStringPool();
if(stringPool == null){ if(stringPool == null){
return null; return null;
} }
int ref = getInteger(getBytesInternal(), offset); int ref = getInteger(getBytesInternal(), offset);
StringItem stringItem = stringPool.get(ref); StringItem stringItem = stringPool.get(ref);
if(stringItem == null){ if(stringItem == null){
return null; return null;
} }
ReferenceItem referenceItem = new ReferenceBlock<>(this, offset); ReferenceItem referenceItem = new ReferenceBlock<>(this, offset);
stringItem.addReference(referenceItem); stringItem.addReference(referenceItem);
return referenceItem; return referenceItem;
} }
private void unlink(ReferenceItem reference){ private void unlink(ReferenceItem reference){
if(reference == null){ if(reference == null){
return; return;
} }
ResXmlStringPool stringPool = getStringPool(); ResXmlStringPool stringPool = getStringPool();
if(stringPool==null){ if(stringPool==null){
return; return;
} }
stringPool.removeReference(reference); stringPool.removeReference(reference);
} }
@Override @Override
public ResXmlStringPool getStringPool(){ public ResXmlStringPool getStringPool(){
StringPool<?> stringPool = super.getStringPool(); StringPool<?> stringPool = super.getStringPool();
if(stringPool instanceof ResXmlStringPool){ if(stringPool instanceof ResXmlStringPool){
return (ResXmlStringPool) stringPool; return (ResXmlStringPool) stringPool;
} }
return null; return null;
} }
@Override @Override
public int compareTo(ResXmlAttribute other) { public int compareTo(ResXmlAttribute other) {
int id1=getNameResourceID(); int id1=getNameResourceID();
int id2=other.getNameResourceID(); int id2=other.getNameResourceID();
if(id1==0 && id2!=0){ if(id1==0 && id2!=0){
return 1; return 1;
} }
if(id2==0 && id1!=0){ if(id2==0 && id1!=0){
return -1; return -1;
} }
if(id1!=0){ if(id1!=0){
return Integer.compare(id1, id2); return Integer.compare(id1, id2);
} }
String name1=getName(); String name1=getName();
if(name1==null){ if(name1==null){
name1=""; name1="";
} }
String name2=other.getName(); String name2=other.getName();
if(name2==null){ if(name2==null){
name2=""; name2="";
} }
return name1.compareTo(name2); return name1.compareTo(name2);
} }
@Override @Override
public JSONObject toJson() { public JSONObject toJson() {
JSONObject jsonObject= new JSONObject(); JSONObject jsonObject= new JSONObject();
jsonObject.put(NAME_name, getName()); jsonObject.put(NAME_name, getName());
jsonObject.put(NAME_id, getNameResourceID()); jsonObject.put(NAME_id, getNameResourceID());
jsonObject.put(NAME_namespace_uri, getUri()); jsonObject.put(NAME_namespace_uri, getUri());
ValueType valueType=getValueType(); ValueType valueType=getValueType();
jsonObject.put(NAME_value_type, valueType.name()); jsonObject.put(NAME_value_type, valueType.name());
if(valueType==ValueType.STRING){ if(valueType==ValueType.STRING){
jsonObject.put(NAME_data, getValueAsString()); jsonObject.put(NAME_data, getValueAsString());
}else if(valueType==ValueType.INT_BOOLEAN){ }else if(valueType==ValueType.INT_BOOLEAN){
jsonObject.put(NAME_data, getValueAsBoolean()); jsonObject.put(NAME_data, getValueAsBoolean());
}else { }else {
jsonObject.put(NAME_data, getData()); jsonObject.put(NAME_data, getData());
} }
return jsonObject; return jsonObject;
} }
@Override @Override
public void fromJson(JSONObject json) { public void fromJson(JSONObject json) {
String name = json.optString(NAME_name, ""); String name = json.optString(NAME_name, "");
int id = json.optInt(NAME_id, 0); int id = json.optInt(NAME_id, 0);
setName(name, id); setName(name, id);
String uri= json.optString(NAME_namespace_uri, null); String uri= json.optString(NAME_namespace_uri, null);
if(uri!=null){ if(uri!=null){
ResXmlStartNamespace ns = getParentResXmlElement().getStartNamespaceByUri(uri); ResXmlStartNamespace ns = getParentResXmlElement().getStartNamespaceByUri(uri);
if(ns==null){ if(ns==null){
ns = getParentResXmlElement().getRootResXmlElement() ns = getParentResXmlElement().getRootResXmlElement()
.getOrCreateNamespace(uri, ""); .getOrCreateNamespace(uri, "");
} }
setNamespaceReference(ns.getUriReference()); setNamespaceReference(ns.getUriReference());
} }
ValueType valueType=ValueType.fromName(json.getString(NAME_value_type)); ValueType valueType=ValueType.fromName(json.getString(NAME_value_type));
if(valueType==ValueType.STRING){ if(valueType==ValueType.STRING){
setValueAsString(json.optString(NAME_data, "")); setValueAsString(json.optString(NAME_data, ""));
}else if(valueType==ValueType.INT_BOOLEAN){ }else if(valueType==ValueType.INT_BOOLEAN){
setValueAsBoolean(json.getBoolean(NAME_data)); setValueAsBoolean(json.getBoolean(NAME_data));
}else { }else {
setValueType(valueType); setValueType(valueType);
setData(json.getInt(NAME_data)); setData(json.getInt(NAME_data));
} }
} }
public XMLAttribute decodeToXml(EntryStore entryStore, int currentPackageId) throws XMLException { public XMLAttribute decodeToXml(EntryStore entryStore, int currentPackageId) throws XMLException {
int resourceId=getNameResourceID(); int resourceId=getNameResourceID();
String name; String name;
if(resourceId==0){ if(resourceId==0){
name=getName(); name=getName();
}else { }else {
EntryGroup group = entryStore.getEntryGroup(resourceId); EntryGroup group = entryStore.getEntryGroup(resourceId);
if(group==null){ if(group==null){
//Lets ignore such error until XML encoder implemented //Lets ignore such error until XML encoder implemented
//throw new XMLException("Failed to decode attribute name: " //throw new XMLException("Failed to decode attribute name: "
//+ String.format("@0x%08x", resourceId)); //+ String.format("@0x%08x", resourceId));
name=String.format("@0x%08x", resourceId); name=String.format("@0x%08x", resourceId);
}else { }else {
name=group.getSpecName(); name=group.getSpecName();
} }
} }
String prefix = getNamePrefix(); String prefix = getNamePrefix();
if(prefix!=null){ if(prefix!=null){
name=prefix+":"+name; name=prefix+":"+name;
} }
ValueType valueType = getValueType(); ValueType valueType = getValueType();
int raw = getData(); int raw = getData();
String value = ValueDecoder.decode(entryStore, currentPackageId, (AttributeValue) this); String value = ValueDecoder.decode(entryStore, currentPackageId, (AttributeValue) this);
XMLAttribute attribute = new XMLAttribute(name, value); XMLAttribute attribute = new XMLAttribute(name, value);
attribute.setNameId(resourceId); attribute.setNameId(resourceId);
if(valueType==ValueType.REFERENCE||valueType==ValueType.ATTRIBUTE){ if(valueType==ValueType.REFERENCE||valueType==ValueType.ATTRIBUTE){
attribute.setValueId(raw); attribute.setValueId(raw);
} }
return attribute; return attribute;
} }
@Override @Override
public String toString(){ public String toString(){
String fullName = getFullName(); String fullName = getFullName();
if(fullName!=null ){ if(fullName!=null ){
int id=getNameResourceID(); int id=getNameResourceID();
if(id!=0){ if(id!=0){
fullName=fullName+"(@"+String.format("0x%08x",id)+")"; fullName=fullName+"(@"+String.format("0x%08x",id)+")";
} }
String valStr; String valStr;
ValueType valueType=getValueType(); ValueType valueType=getValueType();
if(valueType==ValueType.STRING){ if(valueType==ValueType.STRING){
valStr=getValueAsString(); valStr=getValueAsString();
}else if (valueType==ValueType.INT_BOOLEAN){ }else if (valueType==ValueType.INT_BOOLEAN){
valStr = String.valueOf(getValueAsBoolean()); valStr = String.valueOf(getValueAsBoolean());
}else if (valueType==ValueType.INT_DEC){ }else if (valueType==ValueType.INT_DEC){
valStr = String.valueOf(getData()); valStr = String.valueOf(getData());
}else { }else {
valStr = "["+valueType+"] " + String.format("0x%08x",getData()); valStr = "["+valueType+"] " + String.format("0x%08x",getData());
} }
if(valStr!=null){ if(valStr!=null){
return fullName+"=\""+valStr+"\""; return fullName+"=\""+valStr+"\"";
} }
return fullName+"["+valueType+"]=\""+ getData()+"\""; return fullName+"["+valueType+"]=\""+ getData()+"\"";
} }
StringBuilder builder= new StringBuilder(); StringBuilder builder= new StringBuilder();
builder.append(getClass().getSimpleName()); builder.append(getClass().getSimpleName());
builder.append(": "); builder.append(": ");
builder.append(getIndex()); builder.append(getIndex());
builder.append("{NamespaceReference=").append(getNamespaceReference()); builder.append("{NamespaceReference=").append(getNamespaceReference());
builder.append(", NameReference=").append(getNameReference()); builder.append(", NameReference=").append(getNameReference());
builder.append(", ValueStringReference=").append(getValueStringReference()); builder.append(", ValueStringReference=").append(getValueStringReference());
builder.append(", ValueSize=").append(getSize()); builder.append(", ValueSize=").append(getSize());
builder.append(", ValueTypeByte=").append(getType() & 0xff); builder.append(", ValueTypeByte=").append(getType() & 0xff);
builder.append(", Data=").append(getData()); builder.append(", Data=").append(getData());
builder.append("}"); builder.append("}");
return builder.toString(); return builder.toString();
} }
public static final String NAME_id = "id"; public static final String NAME_id = "id";
public static final String NAME_name = "name"; public static final String NAME_name = "name";
public static final String NAME_namespace_uri = "namespace_uri"; public static final String NAME_namespace_uri = "namespace_uri";
private static final int OFFSET_NS = 0; private static final int OFFSET_NS = 0;
private static final int OFFSET_NAME = 4; private static final int OFFSET_NAME = 4;
private static final int OFFSET_STRING = 8; private static final int OFFSET_STRING = 8;
private static final int OFFSET_SIZE = 12; private static final int OFFSET_SIZE = 12;
} }

View File

@ -25,6 +25,8 @@ import com.reandroid.arsc.header.HeaderBlock;
import com.reandroid.arsc.header.TypeHeader; import com.reandroid.arsc.header.TypeHeader;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.TypeString; import com.reandroid.arsc.item.TypeString;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ResConfig; import com.reandroid.arsc.value.ResConfig;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
@ -55,6 +57,16 @@ public class SpecTypePair extends BlockContainer<Block>
this(new SpecBlock(), new TypeBlockArray()); this(new SpecBlock(), new TypeBlockArray());
} }
public void linkTableStringsInternal(TableStringPool tableStringPool){
for(TypeBlock typeBlock:listTypeBlocks()){
typeBlock.linkTableStringsInternal(tableStringPool);
}
}
public void linkSpecStringsInternal(SpecStringPool specStringPool){
for(TypeBlock typeBlock:listTypeBlocks()){
typeBlock.linkSpecStringsInternal(specStringPool);
}
}
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()){

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.item; package com.reandroid.arsc.item;
public class ResXmlString extends StringItem { public class ResXmlString extends StringItem {
public ResXmlString(boolean utf8) { public ResXmlString(boolean utf8) {
super(utf8); super(utf8);
@ -24,4 +23,7 @@ public class ResXmlString extends StringItem {
this(utf8); this(utf8);
set(value); set(value);
} }
@Override
void ensureStringLinkUnlocked(){
}
} }

View File

@ -49,11 +49,19 @@ public class StringItem extends BlockItem implements JSONConvert<JSONObject> {
mReferencedList.clear(); mReferencedList.clear();
} }
public boolean hasReference(){ public boolean hasReference(){
ensureStringLinkUnlocked();
return mReferencedList.size()>0; return mReferencedList.size()>0;
} }
public Collection<ReferenceItem> getReferencedList(){ public Collection<ReferenceItem> getReferencedList(){
ensureStringLinkUnlocked();
return mReferencedList; return mReferencedList;
} }
void ensureStringLinkUnlocked(){
StringPool<?> stringPool = getParentInstance(StringPool.class);
if(stringPool != null){
stringPool.ensureStringLinkUnlockedInternal();
}
}
public void addReference(ReferenceItem ref){ public void addReference(ReferenceItem ref){
if(ref!=null){ if(ref!=null){
mReferencedList.add(ref); mReferencedList.add(ref);
@ -265,11 +273,11 @@ public class StringItem extends BlockItem implements JSONConvert<JSONObject> {
} }
@Override @Override
public String toString(){ public String toString(){
String str=getHtml(); String str = getHtml();
if(str==null){ if(str == null){
return "NULL"; return "NULL";
} }
return "USED BY="+getReferencedList().size()+"{"+str+"}"; return "USED BY=" + mReferencedList.size() + "{" + str + "}";
} }
private static int[] decodeUtf8StringByteLength(byte[] lengthBytes) { private static int[] decodeUtf8StringByteLength(byte[] lengthBytes) {

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

View File

@ -1,24 +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.item; package com.reandroid.arsc.item;
import com.reandroid.arsc.pool.TypeStringPool; import com.reandroid.arsc.pool.TypeStringPool;
public class TypeString extends StringItem { public class TypeString extends StringItem {
public TypeString(boolean utf8) { public TypeString(boolean utf8) {
super(utf8); super(utf8);
} }
@ -36,6 +36,9 @@ package com.reandroid.arsc.item;
return null; return null;
} }
@Override @Override
void ensureStringLinkUnlocked(){
}
@Override
public String toString(){ public String toString(){
return String.format("0x%02x", getId())+':'+get(); return String.format("0x%02x", getId())+':'+get();
} }

View File

@ -25,7 +25,7 @@ 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, false);
} }
@Override @Override
public ResXmlString removeReference(ReferenceItem referenceItem){ public ResXmlString removeReference(ReferenceItem referenceItem){

View File

@ -34,4 +34,12 @@ public class SpecStringPool extends StringPool<SpecString> {
public PackageBlock getPackageBlock(){ public PackageBlock getPackageBlock(){
return getParent(PackageBlock.class); return getParent(PackageBlock.class);
} }
@Override
void linkStrings(){
PackageBlock packageBlock = getPackageBlock();
if(packageBlock != null){
packageBlock.linkSpecStringsInternal(this);
}
}
} }

View File

@ -33,12 +33,14 @@ 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 Object mLock = new Object();
private final StringArray<T> mArrayStrings; private final StringArray<T> mArrayStrings;
private final StyleArray mArrayStyles; private final StyleArray mArrayStyles;
private final Map<String, StringGroup<T>> mUniqueMap; private final Map<String, StringGroup<T>> mUniqueMap;
private boolean stringLinkLocked;
StringPool(boolean is_utf8){ StringPool(boolean is_utf8, boolean stringLinkLocked){
super(new StringPoolHeader(), 4); super(new StringPoolHeader(), 4);
OffsetArray offsetStrings = new OffsetArray(); OffsetArray offsetStrings = new OffsetArray();
@ -68,7 +70,30 @@ public abstract class StringPool<T extends StringItem> extends Chunk<StringPoolH
header.getFlagUtf8().setBlockLoad(this); header.getFlagUtf8().setBlockLoad(this);
mUniqueMap = new HashMap<>(); mUniqueMap = new HashMap<>();
this.stringLinkLocked = stringLinkLocked;
} }
StringPool(boolean is_utf8){
this(is_utf8, true);
}
public boolean isStringLinkLocked(){
return stringLinkLocked;
}
public void ensureStringLinkUnlockedInternal(){
if(!stringLinkLocked){
return;
}
synchronized (mLock){
if(!stringLinkLocked){
return;
}
stringLinkLocked = false;
linkStrings();
}
}
void linkStrings(){
}
public void removeString(T item){ public void removeString(T item){
getStringsArray().remove(item); getStringsArray().remove(item);
} }

View File

@ -19,11 +19,11 @@ 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;
import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.UnknownChunk; import com.reandroid.arsc.chunk.UnknownChunk;
import com.reandroid.arsc.header.HeaderBlock; import com.reandroid.arsc.header.HeaderBlock;
import com.reandroid.arsc.header.TableHeader; import com.reandroid.arsc.header.TableHeader;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
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;
@ -35,6 +35,13 @@ public class TableStringPool extends StringPool<TableString> {
super(is_utf8); super(is_utf8);
} }
@Override
void linkStrings(){
TableBlock tableBlock = getParentInstance(TableBlock.class);
if(tableBlock != null){
tableBlock.linkTableStringsInternal(this);
}
}
@Override @Override
StringArray<TableString> newInstance(OffsetArray 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);

View File

@ -27,7 +27,7 @@ import com.reandroid.arsc.item.TypeString;
public class TypeStringPool extends StringPool<TypeString> { public class TypeStringPool extends StringPool<TypeString> {
private final IntegerItem mTypeIdOffset; private final IntegerItem mTypeIdOffset;
public TypeStringPool(boolean is_utf8, IntegerItem typeIdOffset) { public TypeStringPool(boolean is_utf8, IntegerItem typeIdOffset) {
super(is_utf8); super(is_utf8, false);
this.mTypeIdOffset = typeIdOffset; this.mTypeIdOffset = typeIdOffset;
} }
public int idOf(String typeName){ public int idOf(String typeName){

View File

@ -27,6 +27,8 @@ import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*; import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
@ -41,6 +43,15 @@ public class Entry extends Block implements JSONConvert<JSONObject> {
super(); super();
} }
public void linkTableStringsInternal(TableStringPool tableStringPool){
TableEntry<?, ?> tableEntry = getTableEntry();
tableEntry.linkTableStringsInternal(tableStringPool);
}
public void linkSpecStringsInternal(SpecStringPool specStringPool){
TableEntry<?, ?> tableEntry = getTableEntry();
ValueHeader header = tableEntry.getHeader();
header.linkSpecStringsInternal(specStringPool);
}
public ResValue getResValue(){ public ResValue getResValue(){
TableEntry<?, ?> tableEntry = getTableEntry(); TableEntry<?, ?> tableEntry = getTableEntry();
if(tableEntry instanceof ResTableEntry){ if(tableEntry instanceof ResTableEntry){

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,6 +15,7 @@
*/ */
package com.reandroid.arsc.value; package com.reandroid.arsc.value;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
public class ResTableEntry extends TableEntry<EntryHeader, ResValue> { public class ResTableEntry extends TableEntry<EntryHeader, ResValue> {
@ -22,6 +23,10 @@ public class ResTableEntry extends TableEntry<EntryHeader, ResValue> {
super(new EntryHeader(), new ResValue()); super(new EntryHeader(), new ResValue());
} }
@Override
void linkTableStringsInternal(TableStringPool tableStringPool){
getValue().linkTableStrings(tableStringPool);
}
@Override @Override
void onRemoved(){ void onRemoved(){
getHeader().onRemoved(); getHeader().onRemoved();

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,6 +16,7 @@
package com.reandroid.arsc.value; package com.reandroid.arsc.value;
import com.reandroid.arsc.array.ResValueMapArray; import com.reandroid.arsc.array.ResValueMapArray;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
public class ResTableMapEntry extends TableEntry<EntryHeaderMap, ResValueMapArray> { public class ResTableMapEntry extends TableEntry<EntryHeaderMap, ResValueMapArray> {
@ -42,6 +43,12 @@ public class ResTableMapEntry extends TableEntry<EntryHeaderMap, ResValueMapArra
getValue().setChildesCount(valuesCount); getValue().setChildesCount(valuesCount);
} }
@Override @Override
void linkTableStringsInternal(TableStringPool tableStringPool){
for(ResValueMap resValueMap : listResValueMap()){
resValueMap.linkTableStrings(tableStringPool);
}
}
@Override
void onHeaderLoaded(ValueHeader valueHeader){ void onHeaderLoaded(ValueHeader valueHeader){
getValue().setChildesCount(getValuesCount()); getValue().setChildesCount(getValuesCount());
} }

View File

@ -1,23 +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.value; package com.reandroid.arsc.value;
import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.PackageBlock;
public class ResValue extends ValueItem { public class ResValue extends ValueItem {
public ResValue() { public ResValue() {
super(8, OFFSET_SIZE); super(8, OFFSET_SIZE);
} }

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");
@ -18,6 +18,7 @@ package com.reandroid.arsc.value;
import com.reandroid.arsc.base.Block; import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.base.BlockCounter; import com.reandroid.arsc.base.BlockCounter;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
@ -96,6 +97,7 @@ public abstract class TableEntry<HEADER extends ValueHeader, VALUE extends Block
} }
abstract void onRemoved(); abstract void onRemoved();
abstract boolean shouldMerge(TableEntry<?, ?> tableEntry); abstract boolean shouldMerge(TableEntry<?, ?> tableEntry);
abstract void linkTableStringsInternal(TableStringPool tableStringPool);
public abstract void merge(TableEntry<?, ?> tableEntry); public abstract void merge(TableEntry<?, ?> tableEntry);
@Override @Override

View File

@ -19,6 +19,7 @@ import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.chunk.ParentChunk; import com.reandroid.arsc.chunk.ParentChunk;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*; import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.StringPool; import com.reandroid.arsc.pool.StringPool;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
@ -32,6 +33,21 @@ public class ValueHeader extends BlockItem implements JSONConvert<JSONObject> {
writeSize(); writeSize();
putInteger(getBytesInternal(), OFFSET_SPEC_REFERENCE, -1); putInteger(getBytesInternal(), OFFSET_SPEC_REFERENCE, -1);
} }
void linkSpecStringsInternal(SpecStringPool specStringPool){
int key = getKey();
SpecString specString = specStringPool.get(key);
if(specString == null){
mStringReference = null;
return;
}
if(mStringReference != null){
specString.removeReference(mStringReference);
}
ReferenceItem stringReference = new ReferenceBlock<>(this, OFFSET_SPEC_REFERENCE);
mStringReference = stringReference;
specString.addReference(stringReference);
}
public void onRemoved(){ public void onRemoved(){
unLinkStringReference(); unLinkStringReference();
} }
@ -116,7 +132,11 @@ public class ValueHeader extends BlockItem implements JSONConvert<JSONObject> {
} }
private void linkStringReference(){ private void linkStringReference(){
linkStringReference(getNameString()); StringPool<?> specStringPool = getSpecStringPool();
if(specStringPool == null || specStringPool.isStringLinkLocked()){
return;
}
linkStringReference(specStringPool.get(getKey()));
} }
private void linkStringReference(StringItem stringItem){ private void linkStringReference(StringItem stringItem){
unLinkStringReference(); unLinkStringReference();
@ -167,7 +187,6 @@ public class ValueHeader extends BlockItem implements JSONConvert<JSONObject> {
int size = reader.readUnsignedShort(); int size = reader.readUnsignedShort();
setBytesLength(size, false); setBytesLength(size, false);
reader.readFully(getBytesInternal()); reader.readFully(getBytesInternal());
linkStringReference();
} }
private void setName(String name){ private void setName(String name){
if(name==null){ if(name==null){

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");
@ -23,6 +23,7 @@ import com.reandroid.arsc.item.ReferenceBlock;
import com.reandroid.arsc.item.ReferenceItem; import com.reandroid.arsc.item.ReferenceItem;
import com.reandroid.arsc.item.StringItem; import com.reandroid.arsc.item.StringItem;
import com.reandroid.arsc.pool.StringPool; import com.reandroid.arsc.pool.StringPool;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
@ -39,6 +40,10 @@ import java.util.Objects;
writeSize(); writeSize();
} }
void linkTableStrings(TableStringPool tableStringPool){
linkStringReference(tableStringPool);
}
public void onRemoved(){ public void onRemoved(){
unLinkStringReference(); unLinkStringReference();
} }
@ -78,7 +83,7 @@ import java.util.Objects;
int size = countBytes() - offset; int size = countBytes() - offset;
putShort(getBytesInternal(), offset + OFFSET_SIZE, (short) size); putShort(getBytesInternal(), offset + OFFSET_SIZE, (short) size);
} }
private void onDataLoaded(){ protected void onDataLoaded(){
if(getValueType() == ValueType.STRING){ if(getValueType() == ValueType.STRING){
linkStringReference(); linkStringReference();
}else { }else {
@ -136,8 +141,16 @@ import java.util.Objects;
} }
} }
private void linkStringReference(){ private void linkStringReference(){
StringItem tableString = getDataAsPoolString(); StringPool<?> stringPool = getStringPool();
if(tableString==null){ if(stringPool == null || stringPool.isStringLinkLocked()){
return;
}
linkStringReference(stringPool);
}
private void linkStringReference(StringPool<?> stringPool){
StringItem tableString = stringPool.get(getData());
if(tableString == null){
unLinkStringReference();
return; return;
} }
ReferenceItem stringReference = mStringReference; ReferenceItem stringReference = mStringReference;
@ -181,7 +194,6 @@ import java.util.Objects;
setBytesLength(this.sizeOffset + 8, false); setBytesLength(this.sizeOffset + 8, false);
writeSize(); writeSize();
} }
onDataLoaded();
} }
private int initializeBytes(BlockReader reader) throws IOException { private int initializeBytes(BlockReader reader) throws IOException {
int position = reader.getPosition(); int position = reader.getPosition();