/ app / src / main / java / com / reandroid / identifiers / ResourceIdentifier.java
ResourceIdentifier.java
  1  /*
  2   *  Copyright (C) 2022 github.com/REAndroid
  3   *
  4   *  Licensed under the Apache License, Version 2.0 (the "License");
  5   *  you may not use this file except in compliance with the License.
  6   *  You may obtain a copy of the License at
  7   *
  8   *      http://www.apache.org/licenses/LICENSE-2.0
  9   *
 10   * Unless required by applicable law or agreed to in writing, software
 11   * distributed under the License is distributed on an "AS IS" BASIS,
 12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13   * See the License for the specific language governing permissions and
 14   * limitations under the License.
 15   */
 16  package com.reandroid.identifiers;
 17  
 18  import com.reandroid.arsc.chunk.PackageBlock;
 19  import com.reandroid.arsc.model.ResourceEntry;
 20  import com.reandroid.utils.HexUtil;
 21  import org.xmlpull.v1.XmlSerializer;
 22  
 23  import java.io.IOException;
 24  
 25  public class ResourceIdentifier extends Identifier{
 26      private Boolean mHasGoodName;
 27      public ResourceIdentifier(int id, String name){
 28          super(id, name);
 29      }
 30      public ResourceIdentifier(){
 31          this(0, null);
 32      }
 33  
 34  
 35      public void write(XmlSerializer serializer) throws IOException {
 36          serializer.text("\n  ");
 37          serializer.startTag(null, XML_TAG_PUBLIC);
 38          serializer.attribute(null, XML_ATTRIBUTE_ID, getHexId());
 39          serializer.attribute(null, TypeIdentifier.XML_ATTRIBUTE_TYPE, getTypeName());
 40          serializer.attribute(null, XML_ATTRIBUTE_NAME, getName());
 41          serializer.endTag(null, XML_TAG_PUBLIC);
 42      }
 43      public TypeIdentifier getTypeIdentifier() {
 44          return (TypeIdentifier) getParent();
 45      }
 46      public void setTypeIdentifier(TypeIdentifier typeIdentifier) {
 47          setParent(typeIdentifier);
 48      }
 49      public PackageIdentifier getPackageIdentifier(){
 50          TypeIdentifier typeIdentifier = getTypeIdentifier();
 51          if(typeIdentifier != null){
 52              return typeIdentifier.getPackageIdentifier();
 53          }
 54          return null;
 55      }
 56      public String getTypeName(){
 57          TypeIdentifier typeIdentifier = getTypeIdentifier();
 58          if(typeIdentifier != null){
 59              return typeIdentifier.getName();
 60          }
 61          return null;
 62      }
 63      public String getPackageName(){
 64          TypeIdentifier typeIdentifier = getTypeIdentifier();
 65          if(typeIdentifier != null){
 66              return typeIdentifier.getPackageName();
 67          }
 68          return null;
 69      }
 70      public int getTypeId(){
 71          TypeIdentifier typeIdentifier = getTypeIdentifier();
 72          if(typeIdentifier != null){
 73              return typeIdentifier.getId();
 74          }
 75          return 0;
 76      }
 77      public int getPackageId(){
 78          TypeIdentifier typeIdentifier = getTypeIdentifier();
 79          if(typeIdentifier != null){
 80              return typeIdentifier.getPackageId();
 81          }
 82          return 0;
 83      }
 84      public int getResourceId(){
 85          int resourceId = getPackageId() << 24;
 86          resourceId |= getTypeId() << 16;
 87          resourceId |= getId();
 88          return resourceId;
 89      }
 90      @Override
 91      public void setId(int id) {
 92          super.setId(id & 0xffff);
 93      }
 94      @Override
 95      public String getHexId(){
 96          return HexUtil.toHex8(getResourceId());
 97      }
 98      public String getResourceName(){
 99          return getResourceName(null);
100      }
101      public String getResourceName(PackageIdentifier context){
102          boolean appendPackage = context != getPackageIdentifier();
103          return getResourceName('@', appendPackage, true);
104      }
105      public String getResourceName(char prefix, boolean appendPackage, boolean appendType){
106          String packageName = appendPackage ? getPackageName() : null;
107          String typeName = appendType ? getTypeName() : null;
108          return buildResourceName(prefix, packageName, typeName, getName());
109      }
110      @Override
111      public void setName(String name) {
112          super.setName(name);
113          mHasGoodName = null;
114      }
115      @Override
116      long getUniqueId(){
117          return 0x00000000ffffffffL & this.getResourceId();
118      }
119      @Override
120      public void setTag(Object tag){
121          TypeIdentifier ti = getTypeIdentifier();
122          if(ti == null){
123              super.setTag(tag);
124              return;
125          }
126          Object exist = getTag();
127          if(exist != null){
128              ti.removeTag(exist);
129          }
130          ti.addTag(tag, this);
131          super.setTag(tag);
132      }
133      public String generateUniqueName(){
134          String type = getTypeName();
135          if(type == null){
136              type = "res";
137          }
138          return type + "_" + getHexId();
139      }
140      public boolean isGeneratedName(){
141          String name = getName();
142          if(name == null){
143              return false;
144          }
145          if(!name.contains("_0x")){
146              return false;
147          }
148          return generateUniqueName().equals(name);
149      }
150      public boolean renameSpecGenerated(){
151          setName(generateUniqueName());
152          return renameSpec();
153      }
154      public boolean renameBadSpec(){
155          if(hasGoodName()){
156              return renameDollarPrefix();
157          }
158          setName(generateUniqueName());
159          return renameSpec();
160      }
161      public boolean renameDollarPrefix(){
162          if(!Identifier.isAapt()) {
163              return false;
164          }
165          String name = getName();
166          if(name.charAt(0) != '$'){
167              return false;
168          }
169          name = name.substring(1);
170  
171          setName(name);
172          return renameSpec();
173      }
174      public boolean renameSpec(){
175          PackageBlock packageBlock = getPackageBlock();
176          if(packageBlock == null){
177              return false;
178          }
179          ResourceEntry resourceEntry = packageBlock.getResource(getResourceId());
180          if(resourceEntry == null){
181              return false;
182          }
183          String name = getName();
184          if(name == null){
185              return false;
186          }
187          if(name.equals(resourceEntry.getName())){
188              return false;
189          }
190          resourceEntry.setName(name);
191          return true;
192      }
193      private PackageBlock getPackageBlock(){
194          PackageIdentifier pi = getPackageIdentifier();
195          if(pi != null){
196              return pi.getPackageBlock();
197          }
198          return null;
199      }
200      public boolean hasGoodName(){
201          if(mHasGoodName == null){
202              mHasGoodName = isGoodName(getName());
203          }
204          return mHasGoodName;
205      }
206      @Override
207      public String toString(){
208          return getHexId() + " " + getResourceName();
209      }
210  
211      public static String buildResourceName(char prefix, String packageName, String type, String entry){
212          StringBuilder builder = new StringBuilder();
213          if(prefix != 0){
214              builder.append(prefix);
215          }
216          if(packageName != null){
217              builder.append(packageName);
218              builder.append(':');
219          }
220          if(type != null){
221              builder.append(type);
222              builder.append('/');
223          }
224          builder.append(entry);
225          return builder.toString();
226      }
227      public static boolean isGoodName(String name){
228          if(name == null){
229              return false;
230          }
231          int length = name.length();
232          if(length < NAME_LENGTH_MIN || length > NAME_LENGTH_MAX){
233              return false;
234          }
235          char[] chars = name.toCharArray();
236          if(!isGoodFirstChar(chars[0])){
237              return false;
238          }
239          length = chars.length;
240          for(int i = 1; i < length; i++){
241              if(!isGoodNameChar(chars[i])){
242                  return false;
243              }
244          }
245          return true;
246      }
247      private static boolean isGoodNameChar(char ch){
248          return isAtoZ(ch)
249                  || isDigits(ch)
250                  || ch == '_'
251                  || ch == '.'
252                  || ch == '$'
253                  || ch == '-';
254      }
255      private static boolean isGoodFirstChar(char ch){
256          return isAtoZ(ch) ||
257                  ch == '_'
258                  || ch == '$';
259      }
260      private static boolean isAtoZ(char ch){
261          if(ch >= 'A' && ch <= 'Z'){
262              return true;
263          }
264          return ch >= 'a' && ch <= 'z';
265      }
266      private static boolean isDigits(char ch){
267          return ch >= '0' && ch <= '9';
268      }
269  
270  
271      public static final int NAME_LENGTH_MIN = 1;
272      public static final int NAME_LENGTH_MAX = 100;
273  
274  }