1 package org.robolectric.res; 2 3 import java.io.File; 4 import java.util.regex.Matcher; 5 import java.util.regex.Pattern; 6 import javax.annotation.Nonnull; 7 8 @SuppressWarnings("NewApi") 9 public class ResName { 10 public static final String ID_TYPE = "id"; 11 12 private static final Pattern FQN_PATTERN = Pattern.compile("^([^:]*):([^/]+)/(.+)$"); 13 private static final int NAMESPACE = 1; 14 private static final int TYPE = 2; 15 private static final int NAME = 3; 16 17 public final @Nonnull String packageName; 18 public final @Nonnull String type; 19 public final @Nonnull String name; 20 21 public final int hashCode; 22 ResName(@onnull String packageName, @Nonnull String type, @Nonnull String name)23 public ResName(@Nonnull String packageName, @Nonnull String type, @Nonnull String name) { 24 this.packageName = packageName; 25 this.type = type.trim(); 26 this.name = name.trim(); 27 28 hashCode = computeHashCode(); 29 } 30 ResName(@onnull String fullyQualifiedName)31 public ResName(@Nonnull String fullyQualifiedName) { 32 Matcher matcher = FQN_PATTERN.matcher(fullyQualifiedName.trim()); 33 if (!matcher.find()) { 34 throw new IllegalStateException("\"" + fullyQualifiedName + "\" is not fully qualified"); 35 } 36 packageName = matcher.group(NAMESPACE); 37 type = matcher.group(TYPE).trim(); 38 name = matcher.group(NAME).trim(); 39 40 hashCode = computeHashCode(); 41 if (packageName.equals("xmlns")) 42 throw new IllegalStateException("\"" + fullyQualifiedName + "\" unexpected"); 43 } 44 45 /** Returns the fully qualified resource name if null if the resource could not be qualified. */ qualifyResourceName( @onnull String possiblyQualifiedResourceName, String defaultPackageName, String defaultType)46 public static String qualifyResourceName( 47 @Nonnull String possiblyQualifiedResourceName, 48 String defaultPackageName, 49 String defaultType) { 50 ResName resName = qualifyResName(possiblyQualifiedResourceName, defaultPackageName, defaultType); 51 return resName != null ? resName.getFullyQualifiedName() : null; 52 } 53 qualifyResName(@onnull String possiblyQualifiedResourceName, ResName defaults)54 public static ResName qualifyResName(@Nonnull String possiblyQualifiedResourceName, ResName defaults) { 55 return qualifyResName(possiblyQualifiedResourceName, defaults.packageName, defaults.type); 56 } 57 qualifyResName(@onnull String possiblyQualifiedResourceName, String defaultPackageName, String defaultType)58 public static ResName qualifyResName(@Nonnull String possiblyQualifiedResourceName, String defaultPackageName, String defaultType) { 59 int indexOfColon = possiblyQualifiedResourceName.indexOf(':'); 60 int indexOfSlash = possiblyQualifiedResourceName.indexOf('/'); 61 String type = null; 62 String packageName = null; 63 String name = possiblyQualifiedResourceName; 64 if (indexOfColon > indexOfSlash) { 65 if (indexOfSlash > 0) { 66 type = possiblyQualifiedResourceName.substring(0, indexOfSlash); 67 } 68 packageName = possiblyQualifiedResourceName.substring(indexOfSlash + 1, indexOfColon); 69 name = possiblyQualifiedResourceName.substring(indexOfColon + 1); 70 } else if (indexOfSlash > indexOfColon) { 71 if (indexOfColon > 0) { 72 packageName = possiblyQualifiedResourceName.substring(0, indexOfColon); 73 } 74 type = possiblyQualifiedResourceName.substring(indexOfColon + 1, indexOfSlash); 75 name = possiblyQualifiedResourceName.substring(indexOfSlash + 1); 76 } 77 78 if ((type == null && defaultType == null) || (packageName == null && defaultPackageName == null)) { 79 return null; 80 } 81 82 if (packageName == null) { 83 packageName = defaultPackageName; 84 } else if ("*android".equals(packageName)) { 85 packageName = "android"; 86 } 87 88 return new ResName(packageName, type == null ? defaultType : type, name); 89 } 90 qualifyResName(String possiblyQualifiedResourceName, String contextPackageName)91 public static String qualifyResName(String possiblyQualifiedResourceName, String contextPackageName) { 92 if (possiblyQualifiedResourceName == null) { 93 return null; 94 } 95 96 if (AttributeResource.isNull(possiblyQualifiedResourceName)) { 97 return null; 98 } 99 100 // Was not able to fully qualify the resource name 101 String fullyQualifiedResourceName = qualifyResourceName(possiblyQualifiedResourceName, contextPackageName, null); 102 if (fullyQualifiedResourceName == null) { 103 return null; 104 } 105 106 return fullyQualifiedResourceName.replaceAll("[@+]", ""); 107 } 108 qualifyFromFilePath(@onnull final String packageName, @Nonnull final String filePath)109 public static ResName qualifyFromFilePath(@Nonnull final String packageName, @Nonnull final String filePath) { 110 final File file = new File(filePath); 111 final String type = file.getParentFile().getName().split("-", 0)[0]; 112 final String name = Fs.baseNameFor(file.toPath()); 113 114 return new ResName(packageName, type, name); 115 } 116 117 @Override equals(Object o)118 public boolean equals(Object o) { 119 if (this == o) return true; 120 if (!(o instanceof ResName)) return false; 121 122 ResName resName = (ResName) o; 123 124 if (hashCode() != resName.hashCode()) return false; 125 126 if (!packageName.equals(resName.packageName)) return false; 127 if (!type.equals(resName.type)) return false; 128 if (!name.equals(resName.name)) return false; 129 130 return true; 131 } 132 133 @Override hashCode()134 public int hashCode() { 135 return hashCode; 136 } 137 138 @Override toString()139 public String toString() { 140 return "ResName{" + getFullyQualifiedName() + "}"; 141 } 142 getFullyQualifiedName()143 public String getFullyQualifiedName() { 144 return packageName + ":" + type + "/" + name; 145 } 146 getNamespaceUri()147 public String getNamespaceUri() { 148 return "http://schemas.android.com/apk/res/" + packageName; 149 } 150 withPackageName(String packageName)151 public ResName withPackageName(String packageName) { 152 if (packageName.equals(this.packageName)) return this; 153 return new ResName(packageName, type, name); 154 } 155 mustBe(String expectedType)156 public void mustBe(String expectedType) { 157 if (!type.equals(expectedType)) { 158 throw new RuntimeException( 159 "expected " + getFullyQualifiedName() + " to be a " + expectedType + ", is a " + type); 160 } 161 } 162 computeHashCode()163 private int computeHashCode() { 164 int result = packageName.hashCode(); 165 result = 31 * result + type.hashCode(); 166 result = 31 * result + name.hashCode(); 167 return result; 168 } 169 } 170