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