Config.java revision 5277f871bca2be31c657ace35ae305285b2e0c55
1package org.robolectric.annotation; 2 3import org.jetbrains.annotations.NotNull; 4 5import java.lang.annotation.Annotation; 6import java.lang.annotation.Documented; 7import java.lang.annotation.ElementType; 8import java.lang.annotation.Retention; 9import java.lang.annotation.RetentionPolicy; 10import java.lang.annotation.Target; 11import java.util.ArrayList; 12import java.util.Arrays; 13import java.util.Properties; 14 15/** 16 * Indicate that robolectric should look for values that is specific by those qualifiers 17 */ 18@Documented 19@Retention(RetentionPolicy.RUNTIME) 20@Target({ElementType.TYPE, ElementType.METHOD}) 21public @interface Config { 22 @SuppressWarnings("UnusedDeclaration") 23 public static final String NONE = "--none"; 24 public static final String DEFAULT = "--default"; 25 26 /** 27 * The Android SDK level to emulate. If not specified, Robolectric defaults to API 16. 28 */ 29 int emulateSdk() default -1; 30 31 /** 32 * The Android manifest file to load; Robolectric will look relative to the current directory. 33 * Resources and assets will be loaded relative to the manifest. 34 * 35 * If not specified, Robolectric defaults to {@code AndroidManifest.xml}. 36 * 37 * If your project has no manifest or resources, use {@link Config#NONE}. 38 */ 39 String manifest() default DEFAULT; 40 41 /** 42 * Qualifiers for the resource resolution, such as "fr-normal-port-hdpi". 43 * 44 * @see <a href="http://developer.android.com/guide/topics/resources/providing-resources.html">Providing Resources</a> in the Android Developer docs for more information. 45 */ 46 String qualifiers() default ""; 47 48 /** 49 * The Directory from which to load resources. This should be relative from the directory containing the AndroidManifest. 50 * 51 * If not specified, Robolectric defaults to {@code res}. 52 */ 53 String resourceDir() default "res"; 54 55 /** 56 * The Android SDK level to report in Build.VERSION.SDK_INT. 57 * 58 * @see <a href="http://en.wikipedia.org/wiki/Android_version_history">Android Version History</a>. 59 */ 60 int reportSdk() default -1; 61 62 /** 63 * A list of shadow classes to enable, in addition to those that are already present. 64 */ 65 Class<?>[] shadows() default {}; 66 67 public class Implementation implements Config { 68 private final int emulateSdk; 69 private final String manifest; 70 private final String qualifiers; 71 private final String resourceDir; 72 private final int reportSdk; 73 private final Class<?>[] shadows; 74 75 public static Config fromProperties(Properties configProperties) { 76 if (configProperties == null || configProperties.size() == 0) return null; 77 return new Implementation( 78 Integer.parseInt(configProperties.getProperty("emulateSdk", "-1")), 79 configProperties.getProperty("manifest", DEFAULT), 80 configProperties.getProperty("qualifiers", ""), 81 configProperties.getProperty("resourceDir", "res"), 82 Integer.parseInt(configProperties.getProperty("reportSdk", "-1")), 83 parseClasses(configProperties.getProperty("shadows", "")) 84 ); 85 } 86 87 private static Class<?>[] parseClasses(String classList) { 88 if (classList.length() == 0) return new Class[0]; 89 String[] classNames = classList.split("[, ]+"); 90 Class[] classes = new Class[classNames.length]; 91 for (int i = 0; i < classNames.length; i++) { 92 try { 93 classes[i] = Implementation.class.getClassLoader().loadClass(classNames[i]); 94 } catch (ClassNotFoundException e) { 95 throw new RuntimeException(e); 96 } 97 } 98 return classes; 99 } 100 101 public Implementation(int emulateSdk, String manifest, String qualifiers, String resourceDir, int reportSdk, Class<?>[] shadows) { 102 this.emulateSdk = emulateSdk; 103 this.manifest = manifest; 104 this.qualifiers = qualifiers; 105 this.resourceDir = resourceDir; 106 this.reportSdk = reportSdk; 107 this.shadows = shadows; 108 } 109 110 public Implementation(Config baseConfig, Config overlayConfig) { 111 this.emulateSdk = pick(baseConfig.emulateSdk(), overlayConfig.emulateSdk(), -1); 112 this.manifest = pick(baseConfig.manifest(), overlayConfig.manifest(), DEFAULT); 113 this.qualifiers = pick(baseConfig.qualifiers(), overlayConfig.qualifiers(), ""); 114 this.resourceDir = pick(baseConfig.resourceDir(), overlayConfig.resourceDir(), "res"); 115 this.reportSdk = pick(baseConfig.reportSdk(), overlayConfig.reportSdk(), -1); 116 ArrayList<Class<?>> shadows = new ArrayList<Class<?>>(); 117 shadows.addAll(Arrays.asList(baseConfig.shadows())); 118 shadows.addAll(Arrays.asList(overlayConfig.shadows())); 119 this.shadows = shadows.toArray(new Class[shadows.size()]); 120 } 121 122 private <T> T pick(T baseValue, T overlayValue, T nullValue) { 123 return overlayValue.equals(nullValue) ? baseValue : overlayValue; 124 } 125 126 @Override public int emulateSdk() { 127 return emulateSdk; 128 } 129 130 @Override public String manifest() { 131 return manifest; 132 } 133 134 @Override public String qualifiers() { 135 return qualifiers; 136 } 137 138 @Override 139 public String resourceDir() { 140 return resourceDir; 141 } 142 143 @Override public int reportSdk() { 144 return reportSdk; 145 } 146 147 @Override public Class<?>[] shadows() { 148 return shadows; 149 } 150 151 @NotNull @Override public Class<? extends Annotation> annotationType() { 152 return Config.class; 153 } 154 155 @Override 156 public boolean equals(Object o) { 157 if (this == o) return true; 158 if (o == null || getClass() != o.getClass()) return false; 159 160 Implementation other = (Implementation) o; 161 162 if (emulateSdk != other.emulateSdk) return false; 163 if (reportSdk != other.reportSdk) return false; 164 if (!qualifiers.equals(other.qualifiers)) return false; 165 if (!Arrays.equals(shadows, other.shadows)) return false; 166 167 return true; 168 } 169 170 @Override 171 public int hashCode() { 172 int result = emulateSdk; 173 result = 31 * result + qualifiers.hashCode(); 174 result = 31 * result + reportSdk; 175 result = 31 * result + Arrays.hashCode(shadows); 176 return result; 177 } 178 } 179} 180