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 }