
com.reandroid.arsc.pool.StringPool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ARSCLib Show documentation
Show all versions of ARSCLib Show documentation
Android binary resources read/write library
The newest version!
/*
* 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.pool;
import com.reandroid.arsc.array.IntegerOffsetArray;
import com.reandroid.arsc.array.OffsetArray;
import com.reandroid.arsc.array.StringArray;
import com.reandroid.arsc.array.StyleArray;
import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.chunk.Chunk;
import com.reandroid.arsc.header.StringPoolHeader;
import com.reandroid.arsc.io.BlockLoad;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*;
import com.reandroid.common.BytesOutputStream;
import com.reandroid.json.JSONArray;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject;
import com.reandroid.utils.CompareUtil;
import com.reandroid.utils.NumbersUtil;
import com.reandroid.utils.StringsUtil;
import com.reandroid.utils.collection.CollectionUtil;
import com.reandroid.utils.collection.ComputeIterator;
import com.reandroid.utils.collection.IterableIterator;
import com.reandroid.utils.collection.MultiMap;
import com.reandroid.xml.StyleDocument;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
public abstract class StringPool extends Chunk
implements BlockLoad, Iterable, JSONConvert {
private final Object mLock = new Object();
private final StringArray mArrayStrings;
private final StyleArray mArrayStyles;
private final MultiMap poolMap;
private boolean stringLinkLocked;
StringPool(boolean is_utf8, boolean stringLinkLocked){
super(new StringPoolHeader(), 4);
IntegerOffsetArray offsetStrings = new IntegerOffsetArray();
IntegerOffsetArray offsetStyles = new IntegerOffsetArray();
StringPoolHeader header = getHeaderBlock();
this.mArrayStrings = newInstance(
offsetStrings,
header.getCountStrings(),
header.getStartStrings(),
is_utf8);
this.mArrayStyles = new StyleArray(
offsetStyles,
header.getCountStyles(),
header.getStartStyles());
addChild(offsetStrings);
addChild(offsetStyles);
addChild(mArrayStrings);
addChild(mArrayStyles);
setUtf8(is_utf8, false);
header.getFlagUtf8().setBlockLoad(this);
this.stringLinkLocked = stringLinkLocked;
this.poolMap = new MultiMap<>();
this.poolMap.setFavouriteObjectsSorter((item1, item2) -> {
int i = item1.compareTo(item2);
if(i == 0) {
i = CompareUtil.compareUnsigned(item1.getIndex(), item2.getIndex());
}
return i;
});
}
StringPool(boolean is_utf8){
this(is_utf8, true);
}
@Override
public Iterator iterator(){
return mArrayStrings.iterator();
}
public final T get(int index){
return mArrayStrings.get(index);
}
public int size(){
return mArrayStrings.getCount();
}
public boolean isEmpty(){
return size() == 0;
}
public void clear(){
getStyleArray().clear();
getStringsArray().clear();
poolMap.clear();
}
/**
* Use clear()
* **/
@Deprecated
public void destroy(){
clear();
}
public void sort(){
ensureStringLinkUnlockedInternal();
getStringsArray().sort();
}
public boolean isStringLinkLocked(){
return stringLinkLocked;
}
public void ensureStringLinkUnlockedInternal(){
synchronized (mLock){
if(!stringLinkLocked){
return;
}
stringLinkLocked = false;
linkStrings();
reloadPoolMap();
}
}
void linkStrings(){
linkStyleStrings();
}
private void linkStyleStrings(){
StyleArray styleArray = getStyleArray();
int size = styleArray.size();
for(int i = 0; i < size; i++){
StyleItem styleItem = styleArray.get(i);
styleItem.linkStringsInternal();
}
}
public void linkStylesInternal(){
StyleArray styleArray = getStyleArray();
StringArray stringsArray = getStringsArray();
int size = NumbersUtil.min(stringsArray.size(), styleArray.size());
for(int i = 0; i < size; i++){
StyleItem styleItem = styleArray.get(i);
StringItem stringItem = stringsArray.get(i);
stringItem.linkStyleItemInternal(styleItem);
}
}
public void removeString(T item){
getStringsArray().remove(item);
}
public Iterator getStrings() {
return ComputeIterator.of(iterator(), T::getXml);
}
public void addStrings(Collection stringList){
if(stringList == null || stringList.size() == 0){
return;
}
for(String str : stringList) {
createNewString(str);
}
}
private void reloadPoolMap() {
if(poolMap.size() == 0) {
poolMap.clear();
poolMap.setInitialSize(size());
poolMap.putAll(StringItem::getXml, iterator());
}
}
public void compressDuplicates(){
ensureStringLinkUnlockedInternal();
poolMap.findDuplicates(CompareUtil.getComparableComparator(), list -> {
T first = list.get(0);
for(int i = 1; i < list.size(); i++) {
T item = list.get(i);
first.transferReferences(item);
}
});
}
public boolean removeUnusedStrings(){
return getStringsArray().removeIf(getUnusedStringsFilter());
}
public List listUnusedStrings(){
return getStringsArray().subListIf(getUnusedStringsFilter());
}
private Predicate getUnusedStringsFilter() {
return item -> !item.hasReference();
}
public StyleArray getStyleArray(){
return mArrayStyles;
}
public StringArray getStringsArray(){
return mArrayStrings;
}
public T removeReference(ReferenceItem ref){
if(ref == null){
return null;
}
T item = get(ref.get());
if(item != null){
item.removeReference(ref);
return item;
}
return null;
}
public boolean contains(String str){
return poolMap.containsKey(str);
}
public void onStringChanged(String old, T stringItem) {
if(!stringLinkLocked){
poolMap.updateKey(old, stringItem.getXml(), stringItem);
}
}
public void onStringRemoved(T stringItem) {
if(!stringLinkLocked) {
poolMap.remove(stringItem.getXml(), stringItem);
}
}
public final T getLast(){
return mArrayStrings.getLast();
}
public Iterator getUsers(Class parentClass, String value) {
return new IterableIterator(getAll(value)) {
@Override
public Iterator iterator(T element) {
return element.getUsers(parentClass);
}
};
}
public final Iterator getAll(String str){
ensureStringLinkUnlockedInternal();
return poolMap.getAll(str);
}
public final T get(String str, Predicate super T> predicate){
ensureStringLinkUnlockedInternal();
return poolMap.get(str, predicate);
}
public final T getString(String str){
return CollectionUtil.getFirst(getAll(str));
}
public T getOrCreate(String str){
ensureStringLinkUnlockedInternal();
if(str == null){
str = StringsUtil.EMPTY;
}
String key = str;
T item = get(str, stringItem -> key.equals(stringItem.getXml()));
if(item == null) {
item = createNewString(str);
poolMap.put(str, item);
}
return item;
}
public T getOrCreate(StyleDocument styleDocument) {
ensureStringLinkUnlockedInternal();
String xml = styleDocument.getXml();
T item = get(xml, StringItem::hasStyle);
if(item != null) {
return item;
}
item = createNewString();
item.set(styleDocument);
return item;
}
public T getOrCreate(JSONObject jsonObject) {
ensureStringLinkUnlockedInternal();
JSONObject style = jsonObject.optJSONObject(StringItem.NAME_style);
if(style != null) {
T item = createNewString();
item.set(jsonObject);
StyleDocument styleDocument = item.getStyleDocument();
if(styleDocument != null) {
T exist = getString(styleDocument.getXml());
if(exist != null) {
if(item.hasReference()) {
item.set((String) null);
item.setNull(true);
}
return exist;
}
return item;
}
}
return getOrCreate(jsonObject.getString(StringItem.NAME_string));
}
public T createNewString(String str){
T item = createNewString();
item.set(str);
return item;
}
public T createNewString() {
return mArrayStrings.createNext();
}
@Deprecated
public final int countStrings(){
return mArrayStrings.size();
}
public final int countStyles(){
return mArrayStyles.size();
}
public boolean isUtf8(){
return getHeaderBlock().isUtf8();
}
public void setUtf8(boolean is_utf8){
setUtf8(is_utf8, true);
}
private void setUtf8(boolean is_utf8, boolean updateAll){
StringPoolHeader header = getHeaderBlock();
if(is_utf8 == header.isUtf8()){
return;
}
ByteItem flagUtf8 = header.getFlagUtf8();
if(is_utf8){
flagUtf8.set((byte) 0x01);
}else {
flagUtf8.set((byte) 0x00);
}
if(!updateAll){
return;
}
mArrayStrings.setUtf8(is_utf8);
}
public void setFlagSorted(boolean sorted){
getHeaderBlock().setSorted(sorted);
}
abstract StringArray newInstance(OffsetArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8);
@Override
protected void onChunkRefreshed() {
mArrayStrings.refreshCountAndStart();
mArrayStyles.refreshCountAndStart();
}
@Override
public void onChunkLoaded() {
linkStylesInternal();
reloadPoolMap();
}
@Override
public void onBlockLoaded(BlockReader reader, Block sender) throws IOException {
StringPoolHeader header = getHeaderBlock();
if(sender == header.getFlagUtf8()){
mArrayStrings.setUtf8(header.isUtf8());
}
}
@Override
public byte[] getBytes(){
BytesOutputStream outputStream = new BytesOutputStream(
getHeaderBlock().getChunkSize());
try {
writeBytes(outputStream);
outputStream.close();
} catch (IOException ignored) {
}
return outputStream.toByteArray();
}
@Override
public JSONArray toJson() {
return getStringsArray().toJson();
}
//Only for styled strings
@Override
public void fromJson(JSONArray json) {
if(json == null){
return;
}
getStringsArray().fromJson(json);
refresh();
}
boolean containsInternal(T item) {
return poolMap.containsValue(item.getXml(),
stringItem -> stringItem.compareTo(item) == 0);
}
public void merge(StringPool stringPool){
if(stringPool == null || stringPool == this || stringPool.size() == 0){
return;
}
ensureStringLinkUnlockedInternal();
for (T stringItem : stringPool) {
if(!containsInternal(stringItem)) {
createNewString().merge(stringItem);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy