/* * Copyright 2014-present Facebook, Inc. * * 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.tencent.tinker.build.aapt; import com.google.common.base.Joiner; import com.tencent.tinker.build.aapt.RDotTxtEntry.IdType; import com.tencent.tinker.build.aapt.RDotTxtEntry.RType; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; public class AaptResourceCollector { private final Map<RType, Map<String, Set<ResourceDirectory>>> rTypeResourceDirectoryMap; //private final Map<RType, List<ResourceDirectory>> rTypeIncreaseResourceDirectoryListMap; // private final Map<RType, Map<ResourceDirectory,ResourceDirectory>> rTypeIncreaseResourceDirectoryMap; private final Map<RType, ResourceIdEnumerator> rTypeEnumeratorMap; private final Map<RDotTxtEntry, RDotTxtEntry> originalResourceMap; private final Map<RType, Set<RDotTxtEntry>> rTypeResourceMap; private final Map<RType, Set<RDotTxtEntry>> rTypeIncreaseResourceMap; private final Map<String, Set<String>> duplicateResourceMap; private final Map<RType, HashMap<String, String>> sanitizeTypeMap; private final Set<String> ignoreIdSet; private int currentTypeId; public AaptResourceCollector() { this.rTypeResourceDirectoryMap = new HashMap<RType, Map<String, Set<ResourceDirectory>>>(); // this.rTypeIncreaseResourceDirectoryListMap = new HashMap<RType, List<ResourceDirectory>>(); // this.rTypeIncreaseResourceDirectoryMap = new HashMap<RType, Map<ResourceDirectory,ResourceDirectory>>(); this.rTypeEnumeratorMap = new HashMap<RType, ResourceIdEnumerator>(); this.rTypeResourceMap = new HashMap<RType, Set<RDotTxtEntry>>(); this.rTypeIncreaseResourceMap = new HashMap<RType, Set<RDotTxtEntry>>(); this.duplicateResourceMap = new HashMap<String, Set<String>>(); this.sanitizeTypeMap = new HashMap<RType, HashMap<String, String>>(); this.originalResourceMap = new HashMap<RDotTxtEntry, RDotTxtEntry>(); this.ignoreIdSet = new HashSet<String>(); //attr type must 1 this.currentTypeId = 2; } public AaptResourceCollector(Map<RType, Set<RDotTxtEntry>> rTypeResourceMap) { this(); if (rTypeResourceMap != null) { Iterator<Entry<RType, Set<RDotTxtEntry>>> iterator = rTypeResourceMap.entrySet().iterator(); while (iterator.hasNext()) { Entry<RType, Set<RDotTxtEntry>> entry = iterator.next(); RType rType = entry.getKey(); Set<RDotTxtEntry> set = entry.getValue(); // this.rTypeResourceMap.put(rType, new HashSet<RDotTxtEntry>(set)); for (RDotTxtEntry rDotTxtEntry : set) { originalResourceMap.put(rDotTxtEntry, rDotTxtEntry); ResourceIdEnumerator resourceIdEnumerator = null; if (!rDotTxtEntry.idType.equals(IdType.INT_ARRAY)) { int resourceId = Integer.decode(rDotTxtEntry.idValue.trim()).intValue(); int typeId = ((resourceId & 0x00FF0000) / 0x00010000); if (typeId >= currentTypeId) { currentTypeId = typeId + 1; } if (this.rTypeEnumeratorMap.containsKey(rType)) { resourceIdEnumerator = this.rTypeEnumeratorMap.get(rType); if (resourceIdEnumerator.currentId < resourceId) { resourceIdEnumerator.currentId = resourceId; } } else { resourceIdEnumerator = new ResourceIdEnumerator(); resourceIdEnumerator.currentId = resourceId; this.rTypeEnumeratorMap.put(rType, resourceIdEnumerator); } } } } } } public void addIntResourceIfNotPresent(RType rType, String name) { //, ResourceDirectory resourceDirectory) { if (!rTypeEnumeratorMap.containsKey(rType)) { if (rType.equals(RType.ATTR)) { rTypeEnumeratorMap.put(rType, new ResourceIdEnumerator(1)); } else { rTypeEnumeratorMap.put(rType, new ResourceIdEnumerator(currentTypeId++)); } } RDotTxtEntry entry = new FakeRDotTxtEntry(IdType.INT, rType, name); Set<RDotTxtEntry> resourceSet = null; if (this.rTypeResourceMap.containsKey(rType)) { resourceSet = this.rTypeResourceMap.get(rType); } else { resourceSet = new HashSet<RDotTxtEntry>(); this.rTypeResourceMap.put(rType, resourceSet); } if (!resourceSet.contains(entry)) { String idValue = String.format("0x%08x", rTypeEnumeratorMap.get(rType).next()); addResource(rType, IdType.INT, name, idValue); //, resourceDirectory); } } public void addIntArrayResourceIfNotPresent(RType rType, String name, int numValues) { // Robolectric expects the array to be populated with the right number // of values, irrespective // of what the values are. String idValue = String.format("{ %s }", Joiner.on(",").join(Collections.nCopies(numValues, "0x7f000000"))); addResource(rType, IdType.INT_ARRAY, name, idValue); } /** * add resource * * @param rType * @param idType * @param name * @param idValue */ public void addResource(RType rType, IdType idType, String name, String idValue) { Set<RDotTxtEntry> resourceSet = null; if (this.rTypeResourceMap.containsKey(rType)) { resourceSet = this.rTypeResourceMap.get(rType); } else { resourceSet = new HashSet<RDotTxtEntry>(); this.rTypeResourceMap.put(rType, resourceSet); } RDotTxtEntry rDotTxtEntry = new RDotTxtEntry(idType, rType, name, idValue); boolean increaseResource = false; if (!resourceSet.contains(rDotTxtEntry)) { if (this.originalResourceMap.containsKey(rDotTxtEntry)) { this.rTypeEnumeratorMap.get(rType).previous(); rDotTxtEntry = this.originalResourceMap.get(rDotTxtEntry); } else { increaseResource = true; } resourceSet.add(rDotTxtEntry); } Set<RDotTxtEntry> increaseResourceSet = null; //new r dot txt entry if (this.rTypeIncreaseResourceMap.containsKey(rType)) { increaseResourceSet = this.rTypeIncreaseResourceMap.get(rType); } else { increaseResourceSet = new HashSet<RDotTxtEntry>(); this.rTypeIncreaseResourceMap.put(rType, increaseResourceSet); } if (increaseResource) { increaseResourceSet.add(rDotTxtEntry); //addResourceDirectory(rType, name, resourceDirectory); } } //private void addResourceDirectory(RType rType,String name, ResourceDirectory resourceDirectory){ //if(resourceDirectory!=null){ //Map<ResourceDirectory, ResourceDirectory> resourceDirectoryMap=null; //List<ResourceDirectory> resourceDirectoryList=null; //if(this.rTypeIncreaseResourceDirectoryMap.containsKey(rType)){ //resourceDirectoryMap=this.rTypeIncreaseResourceDirectoryMap.get(rType); //resourceDirectoryList=this.rTypeIncreaseResourceDirectoryListMap.get(rType); //}else{ //resourceDirectoryMap=new HashMap<ResourceDirectory, ResourceDirectory>(); //this.rTypeIncreaseResourceDirectoryMap.put(rType, resourceDirectoryMap); //resourceDirectoryList=new ArrayList<ResourceDirectory>(); //this.rTypeIncreaseResourceDirectoryListMap.put(rType, resourceDirectoryList); //} //ResourceDirectory existResourceDirectory=null; //if(resourceDirectoryMap.containsKey(resourceDirectory)){ //existResourceDirectory=resourceDirectoryMap.get(resourceDirectory); //}else{ //existResourceDirectory=resourceDirectory; //resourceDirectoryMap.put(resourceDirectory, resourceDirectory); //resourceDirectoryList.add(existResourceDirectory); //} //existResourceDirectory.resourceEntrySet.add(new ResourceEntry(name,null)); //} //} /** * is contain resource * * @param rType * @param idType * @param name * @return boolean */ public boolean isContainResource(RType rType, IdType idType, String name) { boolean result = false; if (this.rTypeResourceMap.containsKey(rType)) { Set<RDotTxtEntry> resourceSet = this.rTypeResourceMap.get(rType); if (resourceSet.contains(new RDotTxtEntry(idType, rType, name, "0x7f000000"))) { result = true; } } return result; } /** * add r type resource name * * @param rType * @param resourceName * @param resourceDirectory */ void addRTypeResourceName(RType rType, String resourceName, String resourceValue, ResourceDirectory resourceDirectory) { Map<String, Set<ResourceDirectory>> directoryResourceDirectoryMap = null; if (this.rTypeResourceDirectoryMap.containsKey(rType)) { directoryResourceDirectoryMap = this.rTypeResourceDirectoryMap.get(rType); } else { directoryResourceDirectoryMap = new HashMap<String, Set<ResourceDirectory>>(); this.rTypeResourceDirectoryMap.put(rType, directoryResourceDirectoryMap); } Set<ResourceDirectory> resourceDirectorySet = null; if (directoryResourceDirectoryMap.containsKey(resourceDirectory.directoryName)) { resourceDirectorySet = directoryResourceDirectoryMap.get(resourceDirectory.directoryName); } else { resourceDirectorySet = new HashSet<ResourceDirectory>(); directoryResourceDirectoryMap.put(resourceDirectory.directoryName, resourceDirectorySet); } boolean find = false; ResourceDirectory newResourceDirectory = new ResourceDirectory(resourceDirectory.directoryName, resourceDirectory.resourceFullFilename); if (!resourceDirectorySet.contains(newResourceDirectory)) { resourceDirectorySet.add(newResourceDirectory); } for (ResourceDirectory oldResourceDirectory : resourceDirectorySet) { if (oldResourceDirectory.resourceEntrySet.contains(new ResourceEntry(resourceName, resourceValue))) { find = true; String resourceKey = rType + "/" + resourceDirectory.directoryName + "/" + resourceName; Set<String> fullFilenameSet = null; if (!this.duplicateResourceMap.containsKey(resourceKey)) { fullFilenameSet = new HashSet<String>(); fullFilenameSet.add(oldResourceDirectory.resourceFullFilename); this.duplicateResourceMap.put(resourceKey, fullFilenameSet); } else { fullFilenameSet = this.duplicateResourceMap.get(resourceKey); } fullFilenameSet.add(resourceDirectory.resourceFullFilename); } } if (!find) { for (ResourceDirectory oldResourceDirectory : resourceDirectorySet) { if (oldResourceDirectory.equals(newResourceDirectory)) { if (!oldResourceDirectory.resourceEntrySet.contains(new ResourceEntry(resourceName, resourceValue))) { oldResourceDirectory.resourceEntrySet.add(new ResourceEntry(resourceName, resourceValue)); } } } } } void putSanitizeName(RType rType, String sanitizeName, String rawName) { HashMap<String, String> sanitizeNameMap; if (!sanitizeTypeMap.containsKey(rType)) { sanitizeNameMap = new HashMap<>(); sanitizeTypeMap.put(rType, sanitizeNameMap); } else { sanitizeNameMap = sanitizeTypeMap.get(rType); } if (!sanitizeNameMap.containsKey(sanitizeName)) { sanitizeNameMap.put(sanitizeName, rawName); } } /** * get raw name * * @param sanitizeName * @return String */ public String getRawName(RType rType, String sanitizeName) { if (!sanitizeTypeMap.containsKey(rType)) { return null; } return this.sanitizeTypeMap.get(rType).get(sanitizeName); } /** * get r type resource map * * @return Map<RType, Set<RDotTxtEntry>> */ public Map<RType, Set<RDotTxtEntry>> getRTypeResourceMap() { return this.rTypeResourceMap; } /** * @return the duplicateResourceMap */ public Map<String, Set<String>> getDuplicateResourceMap() { return duplicateResourceMap; } /** * @return the rTypeIncreaseResourceMap */ public Map<RType, Set<RDotTxtEntry>> getRTypeIncreaseResourceMap() { return rTypeIncreaseResourceMap; } /** * @return the rTypeResourceDirectoryMap */ public Map<RType, Map<String, Set<ResourceDirectory>>> getRTypeResourceDirectoryMap() { return rTypeResourceDirectoryMap; } ///** // * @return the rTypeIncreaseResourceDirectoryListMap // */ //public Map<RType, List<ResourceDirectory>> getRTypeIncreaseResourceDirectoryListMap() { //return rTypeIncreaseResourceDirectoryListMap; //} void addIgnoreId(String name) { ignoreIdSet.add(name); } /** * @return the ignoreIdSet */ public Set<String> getIgnoreIdSet() { return ignoreIdSet; } private static class ResourceIdEnumerator { private int currentId = 0; ResourceIdEnumerator() { } ResourceIdEnumerator(int typeId) { this.currentId = 0x7f000000 + 0x10000 * typeId + -1; } int previous() { return --currentId; } int next() { return ++currentId; } } }