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