• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.res;
2 
3 import com.google.common.collect.BiMap;
4 import com.google.common.collect.HashBiMap;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import javax.annotation.Nonnull;
8 import org.robolectric.res.android.ResTable_config;
9 import org.robolectric.res.builder.XmlBlock;
10 
11 /**
12  * A {@link ResourceTable} for a single package, e.g: "android" / ox01
13  */
14 public class PackageResourceTable implements ResourceTable {
15 
16   private final ResBunch resources = new ResBunch();
17   private final BiMap<Integer, ResName> resourceTable = HashBiMap.create();
18 
19   private final ResourceIdGenerator androidResourceIdGenerator = new ResourceIdGenerator(0x01);
20   private final String packageName;
21   private int packageIdentifier;
22 
23 
PackageResourceTable(String packageName)24   public PackageResourceTable(String packageName) {
25     this.packageName = packageName;
26   }
27 
28   @Override
getPackageName()29   public String getPackageName() {
30     return packageName;
31   }
32 
getPackageIdentifier()33   int getPackageIdentifier() {
34     return packageIdentifier;
35   }
36 
37   @Override
getResourceId(ResName resName)38   public Integer getResourceId(ResName resName) {
39     Integer id = resourceTable.inverse().get(resName);
40     if (id == null && resName != null && resName.name.contains(".")) {
41       // try again with underscores (in case we're looking in the compile-time resources, where
42       // we haven't read XML declarations and only know what the R.class tells us).
43       id =
44           resourceTable
45               .inverse()
46               .get(new ResName(resName.packageName, resName.type, underscorize(resName.name)));
47     }
48     return id != null ? id : 0;
49   }
50 
51   @Override
getResName(int resourceId)52   public ResName getResName(int resourceId) {
53     return resourceTable.get(resourceId);
54   }
55 
56   @Override
getValue(@onnull ResName resName, ResTable_config config)57   public TypedResource getValue(@Nonnull ResName resName, ResTable_config config) {
58     return resources.get(resName, config);
59   }
60 
61   @Override
getValue(int resId, ResTable_config config)62   public TypedResource getValue(int resId, ResTable_config config) {
63     return resources.get(getResName(resId), config);
64   }
65 
getXml(ResName resName, ResTable_config config)66   @Override public XmlBlock getXml(ResName resName, ResTable_config config) {
67     FileTypedResource fileTypedResource = getFileResource(resName, config);
68     if (fileTypedResource == null || !fileTypedResource.isXml()) {
69       return null;
70     } else {
71       return XmlBlock.create(fileTypedResource.getFsFile(), resName.packageName);
72     }
73   }
74 
getRawValue(ResName resName, ResTable_config config)75   @Override public InputStream getRawValue(ResName resName, ResTable_config config) {
76     FileTypedResource fileTypedResource = getFileResource(resName, config);
77     if (fileTypedResource == null) {
78       return null;
79     } else {
80       FsFile file = fileTypedResource.getFsFile();
81       try {
82         return file == null ? null : file.getInputStream();
83       } catch (IOException e) {
84         throw new RuntimeException(e);
85       }
86     }
87   }
88 
getFileResource(ResName resName, ResTable_config config)89   private FileTypedResource getFileResource(ResName resName, ResTable_config config) {
90     TypedResource typedResource = resources.get(resName, config);
91     if (!(typedResource instanceof FileTypedResource)) {
92       return null;
93     } else {
94       return (FileTypedResource) typedResource;
95     }
96   }
97 
98   @Override
getRawValue(int resId, ResTable_config config)99   public InputStream getRawValue(int resId, ResTable_config config) {
100     return getRawValue(getResName(resId), config);
101   }
102 
103   @Override
receive(Visitor visitor)104   public void receive(Visitor visitor) {
105     resources.receive(visitor);
106   }
107 
addResource(int resId, String type, String name)108   void addResource(int resId, String type, String name) {
109       if (ResourceIds.isFrameworkResource(resId)) {
110         androidResourceIdGenerator.record(resId, type, name);
111       }
112       ResName resName = new ResName(packageName, type, name);
113       int resIdPackageIdentifier = ResourceIds.getPackageIdentifier(resId);
114       if (getPackageIdentifier() == 0) {
115         this.packageIdentifier = resIdPackageIdentifier;
116       } else if (getPackageIdentifier() != resIdPackageIdentifier) {
117         throw new IllegalArgumentException("Incompatible package for " + packageName + ":" + type + "/" + name + " with resId " + resIdPackageIdentifier + " to ResourceIndex with packageIdentifier " + getPackageIdentifier());
118       }
119 
120       ResName existingEntry = resourceTable.put(resId, resName);
121       if (existingEntry != null && !existingEntry.equals(resName)) {
122         throw new IllegalArgumentException("ResId " + Integer.toHexString(resId) + " mapped to both " + resName + " and " + existingEntry);
123       }
124   }
125 
addResource(String type, String name, TypedResource value)126   void addResource(String type, String name, TypedResource value) {
127     ResName resName = new ResName(packageName, type, name);
128 
129     // compound style names were previously registered with underscores (TextAppearance_Small)
130     // because they came from R.style; re-register with dots.
131     ResName resNameWithUnderscores = new ResName(packageName, type, underscorize(name));
132     Integer oldId = resourceTable.inverse().get(resNameWithUnderscores);
133     if (oldId != null) {
134       resourceTable.forcePut(oldId, resName);
135     }
136 
137     Integer id = resourceTable.inverse().get(resName);
138     if (id == null && isAndroidPackage(resName)) {
139       id = androidResourceIdGenerator.generate(type, name);
140       ResName existing = resourceTable.put(id, resName);
141       if (existing != null) {
142         throw new IllegalStateException(resName + " assigned ID to already existing " + existing);
143       }
144     }
145     resources.put(resName, value);
146   }
147 
isAndroidPackage(ResName resName)148   private boolean isAndroidPackage(ResName resName) {
149     return "android".equals(resName.packageName);
150   }
151 
underscorize(String s)152   private static String underscorize(String s) {
153     return s == null ? null : s.replace('.', '_');
154   }
155 }
156