diff --git a/src/main/java/com/reandroid/lib/apk/ResourceIds.java b/src/main/java/com/reandroid/lib/apk/ResourceIds.java index 08a63ee..b39e6d0 100644 --- a/src/main/java/com/reandroid/lib/apk/ResourceIds.java +++ b/src/main/java/com/reandroid/lib/apk/ResourceIds.java @@ -19,6 +19,7 @@ import com.reandroid.lib.arsc.chunk.PackageBlock; import com.reandroid.lib.arsc.chunk.TableBlock; import com.reandroid.lib.arsc.group.EntryGroup; import com.reandroid.lib.arsc.pool.SpecStringPool; +import com.reandroid.lib.arsc.util.ResNameMap; import com.reandroid.lib.json.JSONArray; import com.reandroid.lib.json.JSONObject; import com.reandroid.xml.*; @@ -244,9 +245,23 @@ import java.util.*; public final byte id; public String name; public final Map typeMap; + private final ResNameMap mEntryNameMap; public Package(byte id){ this.id = id; this.typeMap = new HashMap<>(); + this.mEntryNameMap = new ResNameMap<>(); + } + public void loadEntryMap(){ + mEntryNameMap.clear(); + for(Type type:typeMap.values()){ + String typeName=type.getName(); + for(Type.Entry entry: type.entryMap.values()){ + mEntryNameMap.add(typeName, entry.getName(), entry.getResourceId()); + } + } + } + public Integer getResourceId(String typeName, String name){ + return mEntryNameMap.get(typeName, name); } public Type.Entry getEntry(String typeName, String name){ Type type=getType(typeName); diff --git a/src/main/java/com/reandroid/lib/apk/xmlencoder/EncodeMaterials.java b/src/main/java/com/reandroid/lib/apk/xmlencoder/EncodeMaterials.java index 2f3eb86..4f17dff 100644 --- a/src/main/java/com/reandroid/lib/apk/xmlencoder/EncodeMaterials.java +++ b/src/main/java/com/reandroid/lib/apk/xmlencoder/EncodeMaterials.java @@ -25,6 +25,7 @@ import com.reandroid.lib.arsc.group.EntryGroup; import com.reandroid.lib.arsc.item.SpecString; import com.reandroid.lib.arsc.util.FrameworkTable; + import com.reandroid.lib.arsc.util.ResNameMap; import com.reandroid.lib.arsc.value.EntryBlock; import com.reandroid.lib.common.Frameworks; import com.reandroid.lib.common.ResourceResolver; @@ -41,6 +42,7 @@ private APKLogger apkLogger; private boolean mForceCreateNamespaces = true; private Set mFrameworkPackageNames; + private final ResNameMap mLocalResNameMap = new ResNameMap<>(); public EncodeMaterials(){ } public SpecString getSpecString(String name){ @@ -48,29 +50,6 @@ .get(name) .get(0); } - public void addTableStringPool(Collection stringList){ - getCurrentPackage() - .getTableBlock() - .getTableStringPool() - .addStrings(stringList); - } - public int resolveAttributeNameReference(String refString){ - String packageName = null; - String type = "attr"; - String name = refString; - int i=refString.lastIndexOf(':'); - if(i>=0){ - packageName=refString.substring(0, i); - name=refString.substring(i+1); - } - if(EncodeUtil.isEmpty(packageName) - || packageName.equals(getCurrentPackageName()) - || !isFrameworkPackageName(packageName)){ - - return resolveLocalResourceId(type, name); - } - return resolveFrameworkResourceId(packageName, type, name); - } public EntryBlock getAttributeBlock(String refString){ String packageName = null; String type = "attr"; @@ -111,34 +90,11 @@ } return resolveFrameworkResourceId(packageName, type, name); } - - public EntryBlock resolveEntryReference(String refString){ - if("@null".equals(refString)){ - return null; - } - Matcher matcher = ValueDecoder.PATTERN_REFERENCE.matcher(refString); - if(!matcher.find()){ - return null; - } - String prefix=matcher.group(1); - String packageName = matcher.group(2); - if(packageName!=null && packageName.endsWith(":")){ - packageName=packageName.substring(0, packageName.length()-1); - } - String type = matcher.group(4); - String name = matcher.group(5); - if(EncodeUtil.isEmpty(packageName) - || packageName.equals(getCurrentPackageName()) - || !isFrameworkPackageName(packageName)){ - return getLocalEntryBlock(type, name); - } - return getFrameworkEntry(packageName, type, name); - } public int resolveLocalResourceId(String type, String name){ for(ResourceIds.Table.Package pkg:packageIdSet){ - ResourceIds.Table.Package.Type.Entry entry = pkg.getEntry(type, name); - if(entry!=null){ - return entry.getResourceId(); + Integer resId = pkg.getResourceId(type, name); + if(resId!=null){ + return resId; } } EntryGroup entryGroup=getLocalEntryGroup(type, name); @@ -149,15 +105,6 @@ "type="+type+ ", name="+name); } - public int resolveFrameworkResourceId(String type, String name){ - EntryBlock entryBlock = getFrameworkEntry(type, name); - if(entryBlock!=null){ - return entryBlock.getResourceId(); - } - throw new EncodeException("Framework entry not found: " + - "type="+type+ - ", name="+name); - } public int resolveFrameworkResourceId(String packageName, String type, String name){ EntryBlock entryBlock = getFrameworkEntry(packageName, type, name); if(entryBlock!=null){ @@ -196,6 +143,22 @@ return null; } public EntryBlock getLocalEntryBlock(String type, String name){ + EntryBlock entryBlock=mLocalResNameMap.get(type, name); + if(entryBlock!=null){ + return entryBlock; + } + loadLocalEntryBlockMap(type); + entryBlock=mLocalResNameMap.get(type, name); + if(entryBlock!=null){ + return entryBlock; + } + entryBlock= searchLocalEntryBlock(type, name); + if(entryBlock!=null){ + mLocalResNameMap.add(type, name, entryBlock); + } + return entryBlock; + } + private EntryBlock searchLocalEntryBlock(String type, String name){ for(EntryGroup entryGroup : currentPackage.listEntryGroup()){ if(type.equals(entryGroup.getTypeName()) && name.equals(entryGroup.getSpecName())){ @@ -205,7 +168,7 @@ SpecTypePair specTypePair=currentPackage.searchByTypeName(type); if(specTypePair!=null){ for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){ - for(EntryBlock entryBlock:typeBlock.listEntries()){ + for(EntryBlock entryBlock:typeBlock.listEntries(true)){ if(name.equals(entryBlock.getName())){ return entryBlock; } @@ -220,7 +183,7 @@ specTypePair=packageBlock.searchByTypeName(type); if(specTypePair!=null){ for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){ - for(EntryBlock entryBlock:typeBlock.listEntries()){ + for(EntryBlock entryBlock:typeBlock.listEntries(true)){ if(name.equals(entryBlock.getName())){ return entryBlock; } @@ -231,6 +194,20 @@ } return null; } + private void loadLocalEntryBlockMap(String type){ + ResNameMap localMap = mLocalResNameMap; + for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){ + SpecTypePair specTypePair=packageBlock.searchByTypeName(type); + if(specTypePair!=null){ + for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){ + for(EntryBlock entryBlock:typeBlock.listEntries(true)){ + localMap.add(entryBlock.getTypeName(), + entryBlock.getName(), entryBlock); + } + } + } + } + } public EntryBlock getFrameworkEntry(String type, String name){ for(FrameworkTable table:frameworkTables){ EntryBlock entryBlock = table.searchEntryBlock(type, name); @@ -282,16 +259,12 @@ } return null; } - public EncodeMaterials setForceCreateNamespaces(boolean force) { this.mForceCreateNamespaces = force; return this; } - public EncodeMaterials addPackageIds(Collection packageIdList) { - this.packageIdSet.addAll(packageIdList); - return this; - } public EncodeMaterials addPackageIds(ResourceIds.Table.Package packageIds) { + packageIds.loadEntryMap(); this.packageIdSet.add(packageIds); return this; } diff --git a/src/main/java/com/reandroid/lib/apk/xmlencoder/XMLFileEncoder.java b/src/main/java/com/reandroid/lib/apk/xmlencoder/XMLFileEncoder.java index bbf38aa..f1f557a 100644 --- a/src/main/java/com/reandroid/lib/apk/xmlencoder/XMLFileEncoder.java +++ b/src/main/java/com/reandroid/lib/apk/xmlencoder/XMLFileEncoder.java @@ -99,16 +99,23 @@ public class XMLFileEncoder { int count=element.getAttributeCount(); for(int i=0;i> mNameGroupMap; - private final Object mMapLock=new Object(); + private ResNameMap mNameGroupMap; public FrameworkTable(){ super(); } @@ -57,55 +56,40 @@ public class FrameworkTable extends TableBlock { * Call this if you plan to search entries frequently */ public void loadResourceNameMap(){ - synchronized (mMapLock){ - if(mNameGroupMap !=null){ - return; - } - Map> typeMap=new HashMap<>(); + ResNameMap resNameMap = mNameGroupMap; + if(resNameMap == null){ + resNameMap = new ResNameMap<>(); for(PackageBlock packageBlock:listPackages()){ for(EntryGroup group:packageBlock.listEntryGroup()){ - String type=group.getTypeName(); - Map groupMap=typeMap.get(type); - if(groupMap==null){ - groupMap=new HashMap<>(); - typeMap.put(type, groupMap); - } - groupMap.put(group.getSpecName(), group); + resNameMap.add(group.getTypeName(), + group.getSpecName(), + group); } } - mNameGroupMap = typeMap; + mNameGroupMap = resNameMap; } } /** * Clears resource name map from memory */ public void clearResourceNameMap(){ - synchronized (mMapLock){ - if(mNameGroupMap!=null){ - mNameGroupMap.clear(); - mNameGroupMap =null; - } + if(mNameGroupMap!=null){ + mNameGroupMap.clear(); + mNameGroupMap =null; } } private boolean hasResourceGroupMap(){ - synchronized (mMapLock){ - return mNameGroupMap!=null; - } + return mNameGroupMap!=null; } private EntryBlock searchEntryBlockFromMap(String typeName, String entryName){ - synchronized (mMapLock){ - if(mNameGroupMap ==null){ - return null; - } - Map groupMap = mNameGroupMap.get(typeName); - if(groupMap!=null){ - EntryGroup group=groupMap.get(entryName); - if(group!=null){ - return group.pickOne(); - } - } + if(mNameGroupMap ==null){ return null; } + EntryGroup entryGroup = mNameGroupMap.get(typeName, entryName); + if(entryGroup!=null){ + return entryGroup.pickOne(); + } + return null; } public EntryBlock searchEntryBlock(String typeName, String entryName){ if(hasResourceGroupMap()){ diff --git a/src/main/java/com/reandroid/lib/arsc/util/HexBytesWriter.java b/src/main/java/com/reandroid/lib/arsc/util/HexBytesWriter.java index 3b713bb..2511d46 100644 --- a/src/main/java/com/reandroid/lib/arsc/util/HexBytesWriter.java +++ b/src/main/java/com/reandroid/lib/arsc/util/HexBytesWriter.java @@ -1,3 +1,18 @@ + /* + * 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.lib.arsc.util; import java.io.IOException; diff --git a/src/main/java/com/reandroid/lib/arsc/util/ResNameMap.java b/src/main/java/com/reandroid/lib/arsc/util/ResNameMap.java new file mode 100644 index 0000000..33378ab --- /dev/null +++ b/src/main/java/com/reandroid/lib/arsc/util/ResNameMap.java @@ -0,0 +1,65 @@ + /* + * 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.lib.arsc.util; + +import java.util.HashMap; +import java.util.Map; + +public class ResNameMap { + private final Object mLock = new Object(); + private final Map> mainMap; + public ResNameMap(){ + this.mainMap = new HashMap<>(); + } + + public VALUE get(String type, String name){ + synchronized (mLock){ + if(type==null || name==null){ + return null; + } + Map valueMap = mainMap.get(type); + if(valueMap!=null){ + return valueMap.get(name); + } + return null; + } + } + public void add(String type, String name, VALUE value){ + synchronized (mLock){ + if(type==null || name==null || value==null){ + return; + } + Map valueMap = mainMap.get(type); + if(valueMap==null){ + valueMap=new HashMap<>(); + mainMap.put(type, valueMap); + } + valueMap.put(name, value); + } + } + public void clear(){ + synchronized (mLock){ + for(Map valueMap:mainMap.values()){ + valueMap.clear(); + } + mainMap.clear(); + } + } + + private static final String TYPE_ATTR = "attr"; + private static final String TYPE_ID = "id"; + +}