create reference id resolver

This commit is contained in:
REAndroid 2023-04-12 16:23:39 +02:00
parent e53f294258
commit 31c6ccb90e
2 changed files with 152 additions and 2 deletions

View File

@ -24,15 +24,16 @@ import com.reandroid.arsc.header.InfoHeader;
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.pool.TableStringPool; import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.value.StagedAliasEntry; import com.reandroid.arsc.value.*;
import com.reandroid.common.EntryStore; import com.reandroid.common.EntryStore;
import com.reandroid.common.Frameworks; import com.reandroid.common.ReferenceResolver;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONArray; import com.reandroid.json.JSONArray;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import java.util.function.Predicate;
public class TableBlock extends Chunk<TableHeader> public class TableBlock extends Chunk<TableHeader>
implements MainChunk, JSONConvert<JSONObject>, EntryStore { implements MainChunk, JSONConvert<JSONObject>, EntryStore {
@ -40,6 +41,7 @@ import java.util.*;
private final PackageArray mPackageArray; private final PackageArray mPackageArray;
private final List<TableBlock> mFrameWorks=new ArrayList<>(); private final List<TableBlock> mFrameWorks=new ArrayList<>();
private ApkFile mApkFile; private ApkFile mApkFile;
private ReferenceResolver referenceResolver;
public TableBlock() { public TableBlock() {
super(new TableHeader(), 2); super(new TableHeader(), 2);
TableHeader header = getHeaderBlock(); TableHeader header = getHeaderBlock();
@ -48,6 +50,25 @@ import java.util.*;
addChild(mTableStringPool); addChild(mTableStringPool);
addChild(mPackageArray); addChild(mPackageArray);
} }
public List<Entry> resolveReference(int referenceId){
return resolveReference(referenceId, null);
}
public List<Entry> resolveReferenceWithConfig(int referenceId, ResConfig resConfig){
ReferenceResolver resolver = this.referenceResolver;
if(resolver == null){
resolver = new ReferenceResolver(this);
this.referenceResolver = resolver;
}
return resolver.resolveWithConfig(referenceId, resConfig);
}
public List<Entry> resolveReference(int referenceId, Predicate<Entry> filter){
ReferenceResolver resolver = this.referenceResolver;
if(resolver == null){
resolver = new ReferenceResolver(this);
this.referenceResolver = resolver;
}
return resolver.resolveAll(referenceId, filter);
}
public void destroy(){ public void destroy(){
getPackageArray().destroy(); getPackageArray().destroy();
getStringPool().destroy(); getStringPool().destroy();

View File

@ -0,0 +1,129 @@
/*
* 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.common;
import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ResConfig;
import com.reandroid.arsc.value.ResValue;
import com.reandroid.arsc.value.ValueType;
import java.util.*;
import java.util.function.Predicate;
public class ReferenceResolver {
private final EntryStore entryStore;
private final List<Entry> results;
private final Set<Integer> resolvedIds;
private int limit;
public ReferenceResolver(EntryStore entryStore){
this.entryStore = entryStore;
this.results = new ArrayList<>();
this.resolvedIds = new HashSet<>();
this.limit = -1;
}
public Entry resolve(int referenceId){
return resolve(referenceId, null);
}
public synchronized Entry resolve(int referenceId, Predicate<Entry> filter){
resolveReference(referenceId, filter);
List<Entry> results = new ArrayList<>(this.results);
reset();
if(results.size() > 0){
return results.get(0);
}
return null;
}
public List<Entry> resolveWithConfig(int referenceId, ResConfig resConfig){
return resolveAll(referenceId, new ConfigFilter(resConfig));
}
public List<Entry> resolveAll(int referenceId){
return resolveAll(referenceId, (Predicate<Entry>)null);
}
public synchronized List<Entry> resolveAll(int referenceId, Predicate<Entry> filter){
resolveReference(referenceId, filter);
List<Entry> results = new ArrayList<>(this.results);
reset();
return results;
}
private void resolveReference(int referenceId, Predicate<Entry> filter){
if(referenceId == 0 || isFinished() || this.resolvedIds.contains(referenceId)){
return;
}
this.resolvedIds.add(referenceId);
List<Entry> entryList = listNonNullEntries(referenceId);
List<Entry> results = this.results;
for(Entry entry:entryList){
if(isFinished()){
return;
}
if(results.contains(entry)){
continue;
}
if(entry.isComplex()){
addResult(filter, entry);
continue;
}
ResValue resValue = entry.getResValue();
if(resValue.getValueType() != ValueType.REFERENCE){
addResult(filter, entry);
continue;
}
resolveReference(resValue.getData(), filter);
}
}
private void reset(){
this.results.clear();
this.resolvedIds.clear();
this.limit = -1;
}
private boolean isFinished(){
return this.limit >= this.results.size();
}
private void addResult(Predicate<Entry> filter, Entry entry){
if(filter == null || filter.test(entry)){
this.results.add(entry);
}
}
private List<Entry> listNonNullEntries(int resourceId){
List<Entry> results = new ArrayList<>();
EntryGroup entryGroup = this.entryStore.getEntryGroup(resourceId);
if(entryGroup==null){
return results;
}
Iterator<Entry> itr = entryGroup.iterator(true);
while (itr.hasNext()){
results.add(itr.next());
}
return results;
}
public static class ConfigFilter implements Predicate<Entry>{
private final ResConfig config;
public ConfigFilter(ResConfig config){
this.config = config;
}
@Override
public boolean test(Entry entry) {
ResConfig resConfig = entry.getResConfig();
if(resConfig == null){
return false;
}
return resConfig.isEqualOrMoreSpecificThan(this.config);
}
}
}