From 31c6ccb90e6dda190008b4f3354a55e92c38a00c Mon Sep 17 00:00:00 2001 From: REAndroid Date: Wed, 12 Apr 2023 16:23:39 +0200 Subject: [PATCH] create reference id resolver --- .../com/reandroid/arsc/chunk/TableBlock.java | 25 +++- .../reandroid/common/ReferenceResolver.java | 129 ++++++++++++++++++ 2 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/reandroid/common/ReferenceResolver.java diff --git a/src/main/java/com/reandroid/arsc/chunk/TableBlock.java b/src/main/java/com/reandroid/arsc/chunk/TableBlock.java index e40f89d..9e30e84 100755 --- a/src/main/java/com/reandroid/arsc/chunk/TableBlock.java +++ b/src/main/java/com/reandroid/arsc/chunk/TableBlock.java @@ -24,15 +24,16 @@ import com.reandroid.arsc.header.InfoHeader; import com.reandroid.arsc.header.TableHeader; import com.reandroid.arsc.io.BlockReader; 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.Frameworks; +import com.reandroid.common.ReferenceResolver; import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONArray; import com.reandroid.json.JSONObject; import java.io.*; import java.util.*; +import java.util.function.Predicate; public class TableBlock extends Chunk implements MainChunk, JSONConvert, EntryStore { @@ -40,6 +41,7 @@ import java.util.*; private final PackageArray mPackageArray; private final List mFrameWorks=new ArrayList<>(); private ApkFile mApkFile; + private ReferenceResolver referenceResolver; public TableBlock() { super(new TableHeader(), 2); TableHeader header = getHeaderBlock(); @@ -48,6 +50,25 @@ import java.util.*; addChild(mTableStringPool); addChild(mPackageArray); } + public List resolveReference(int referenceId){ + return resolveReference(referenceId, null); + } + public List 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 resolveReference(int referenceId, Predicate filter){ + ReferenceResolver resolver = this.referenceResolver; + if(resolver == null){ + resolver = new ReferenceResolver(this); + this.referenceResolver = resolver; + } + return resolver.resolveAll(referenceId, filter); + } public void destroy(){ getPackageArray().destroy(); getStringPool().destroy(); diff --git a/src/main/java/com/reandroid/common/ReferenceResolver.java b/src/main/java/com/reandroid/common/ReferenceResolver.java new file mode 100644 index 0000000..40c3716 --- /dev/null +++ b/src/main/java/com/reandroid/common/ReferenceResolver.java @@ -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 results; + private final Set 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 filter){ + resolveReference(referenceId, filter); + List results = new ArrayList<>(this.results); + reset(); + if(results.size() > 0){ + return results.get(0); + } + return null; + } + + public List resolveWithConfig(int referenceId, ResConfig resConfig){ + return resolveAll(referenceId, new ConfigFilter(resConfig)); + } + public List resolveAll(int referenceId){ + return resolveAll(referenceId, (Predicate)null); + } + public synchronized List resolveAll(int referenceId, Predicate filter){ + resolveReference(referenceId, filter); + List results = new ArrayList<>(this.results); + reset(); + return results; + } + private void resolveReference(int referenceId, Predicate filter){ + if(referenceId == 0 || isFinished() || this.resolvedIds.contains(referenceId)){ + return; + } + this.resolvedIds.add(referenceId); + List entryList = listNonNullEntries(referenceId); + List 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 filter, Entry entry){ + if(filter == null || filter.test(entry)){ + this.results.add(entry); + } + } + private List listNonNullEntries(int resourceId){ + List results = new ArrayList<>(); + EntryGroup entryGroup = this.entryStore.getEntryGroup(resourceId); + if(entryGroup==null){ + return results; + } + Iterator itr = entryGroup.iterator(true); + while (itr.hasNext()){ + results.add(itr.next()); + } + return results; + } + + public static class ConfigFilter implements Predicate{ + 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); + } + } +}