Config.java revision 29a8359eaef1ee9f40c967d3c4b5c1117c8c2a43
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 the targetSdkVersion in your app's manifest. 28 * 29 * Not yet supported as of Robolectric 2.0. 30 */ 31 int emulateSdk() default -1; 32 33 /** 34 * The Android manifest file to load; Robolectric will look relative to the current directory. 35 * Resources and assets will be loaded relative to the manifest. 36 * 37 * If not specified, Robolectric defaults to {@code AndroidManifest.xml}. 38 * 39 * If your project has no manifest or resources, use {@link Config#NONE}. 40 */ 41 String manifest() default DEFAULT; 42 43 /** 44 * Qualifiers for the resource resolution, such as "fr-normal-port-hdpi". 45 * 46 * @see <a href="http://developer.android.com/guide/topics/resources/providing-resources.html">Providing Resources</a> in the Android Developer docs for more information. 47 */ 48 String qualifiers() default ""; 49 50 /** 51 * The Android SDK level to report in Build.VERSION.SDK_INT. 52 * 53 * @see <a href="http://en.wikipedia.org/wiki/Android_version_history">Android Version History</a>. 54 */ 55 int reportSdk() default -1; 56 57 /** 58 * A list of shadow classes to enable, in addition to those that are already present. 59 */ 60 Class<?>[] shadows() default {}; 61 62 public class Implementation implements Config { 63 private final int emulateSdk; 64 private final String manifest; 65 private final String qualifiers; 66 private final int reportSdk; 67 private final Class<?>[] shadows; 68 69 public static Config fromProperties(Properties configProperties) { 70 if (configProperties == null || configProperties.size() == 0) return null; 71 return new Implementation( 72 Integer.parseInt(configProperties.getProperty("emulateSdk", "-1")), 73 configProperties.getProperty("manifest", DEFAULT), 74 configProperties.getProperty("qualifiers", ""), 75 Integer.parseInt(configProperties.getProperty("reportSdk", "-1")), 76 parseClasses(configProperties.getProperty("shadows", "")) 77 ); 78 } 79 80 private static Class<?>[] parseClasses(String classList) { 81 if (classList.length() == 0) return new Class[0]; 82 String[] classNames = classList.split("[, ]+"); 83 Class[] classes = new Class[classNames.length]; 84 for (int i = 0; i < classNames.length; i++) { 85 try { 86 classes[i] = Implementation.class.getClassLoader().loadClass(classNames[i]); 87 } catch (ClassNotFoundException e) { 88 throw new RuntimeException(e); 89 } 90 } 91 return classes; 92 } 93 94 public Implementation(int emulateSdk, String manifest, String qualifiers, int reportSdk, Class<?>[] shadows) { 95 this.emulateSdk = emulateSdk; 96 this.manifest = manifest; 97 this.qualifiers = qualifiers; 98 this.reportSdk = reportSdk; 99 this.shadows = shadows; 100 } 101 102 public Implementation(Config baseConfig, Config overlayConfig) { 103 this.emulateSdk = pick(baseConfig.emulateSdk(), overlayConfig.emulateSdk(), -1); 104 this.manifest = pick(baseConfig.manifest(), overlayConfig.manifest(), DEFAULT); 105 this.qualifiers = pick(baseConfig.qualifiers(), overlayConfig.qualifiers(), ""); 106 this.reportSdk = pick(baseConfig.reportSdk(), overlayConfig.reportSdk(), -1); 107 ArrayList<Class<?>> shadows = new ArrayList<Class<?>>(); 108 shadows.addAll(Arrays.asList(baseConfig.shadows())); 109 shadows.addAll(Arrays.asList(overlayConfig.shadows())); 110 this.shadows = shadows.toArray(new Class[shadows.size()]); 111 } 112 113 private <T> T pick(T baseValue, T overlayValue, T nullValue) { 114 return overlayValue.equals(nullValue) ? baseValue : overlayValue; 115 } 116 117 @Override public int emulateSdk() { 118 return emulateSdk; 119 } 120 121 @Override public String manifest() { 122 return manifest; 123 } 124 125 @Override public String qualifiers() { 126 return qualifiers; 127 } 128 129 @Override public int reportSdk() { 130 return reportSdk; 131 } 132 133 @Override public Class<?>[] shadows() { 134 return shadows; 135 } 136 137 @NotNull @Override public Class<? extends Annotation> annotationType() { 138 return Config.class; 139 } 140 141 @Override 142 public boolean equals(Object o) { 143 if (this == o) return true; 144 if (o == null || getClass() != o.getClass()) return false; 145 146 Implementation other = (Implementation) o; 147 148 if (emulateSdk != other.emulateSdk) return false; 149 if (reportSdk != other.reportSdk) return false; 150 if (!qualifiers.equals(other.qualifiers)) return false; 151 if (!Arrays.equals(shadows, other.shadows)) return false; 152 153 return true; 154 } 155 156 @Override 157 public int hashCode() { 158 int result = emulateSdk; 159 result = 31 * result + qualifiers.hashCode(); 160 result = 31 * result + reportSdk; 161 result = 31 * result + Arrays.hashCode(shadows); 162 return result; 163 } 164 } 165} 166