/** * Looks up a (possibly cached) {@link ResourceVisibilityLookup} for the given {@link * AndroidLibrary} * * @param library the library * @return the corresponding {@link ResourceVisibilityLookup} */ @NonNull public ResourceVisibilityLookup get(@NonNull AndroidLibrary library) { ResourceVisibilityLookup visibility = mInstances.get(library); if (visibility == null) { visibility = new LibraryResourceVisibility(library); if (visibility.isEmpty()) { visibility = NONE; } List<? extends AndroidLibrary> dependsOn = library.getLibraryDependencies(); if (!dependsOn.isEmpty()) { List<ResourceVisibilityLookup> list = Lists.newArrayListWithExpectedSize(dependsOn.size() + 1); list.add(visibility); for (AndroidLibrary d : dependsOn) { ResourceVisibilityLookup v = get(d); if (!v.isEmpty()) { list.add(v); } } if (list.size() > 1) { visibility = new MultipleLibraryResourceVisibility(list); } } mInstances.put(library, visibility); } return visibility; }
/** * Returns a map from name to resource types for all resources known to this library. This is * used to make sure that when the {@link #isPrivate(ResourceType, String)} query method is * called, it can tell the difference between a resource implicitly private by not being * declared as public and a resource unknown to this library (e.g. defined by a different * library or the user's own project resources.) * * @return a map from name to resource type for all resources in this library */ @Nullable private Multimap<String, ResourceType> computeAllMap() { // getSymbolFile() is not defined in AndroidLibrary, only in the subclass LibraryBundle File symbolFile = new File(mLibrary.getPublicResources().getParentFile(), FN_RESOURCE_TEXT); if (!symbolFile.exists()) { return null; } try { List<String> lines = Files.readLines(symbolFile, Charsets.UTF_8); Multimap<String, ResourceType> result = ArrayListMultimap.create(lines.size(), 2); ResourceType previousType = null; String previousTypeString = ""; int lineIndex = 1; final int count = lines.size(); for (; lineIndex <= count; lineIndex++) { String line = lines.get(lineIndex - 1); if (line.startsWith("int ")) { // not int[] definitions for styleables // format is "int <type> <class> <name> <value>" int typeStart = 4; int typeEnd = line.indexOf(' ', typeStart); // Items are sorted by type, so we can avoid looping over types in // ResourceType.getEnum() for each line by sharing type in each section String typeString = line.substring(typeStart, typeEnd); ResourceType type; if (typeString.equals(previousTypeString)) { type = previousType; } else { type = ResourceType.getEnum(typeString); previousTypeString = typeString; previousType = type; } if (type == null) { // some newly introduced type continue; } int nameStart = typeEnd + 1; int nameEnd = line.indexOf(' ', nameStart); String name = line.substring(nameStart, nameEnd); result.put(name, type); } } return result; } catch (IOException ignore) { } return null; }
/** * Returns a map from name to applicable resource types where the presence of the type+name * combination means that the corresponding resource is explicitly public. * * <p>If the result is null, there is no {@code public.txt} definition for this library, so all * resources should be taken to be public. * * @return a map from name to resource type for public resources in this library */ @Nullable private Multimap<String, ResourceType> computeVisibilityMap() { File publicResources = mLibrary.getPublicResources(); if (!publicResources.exists()) { return null; } try { List<String> lines = Files.readLines(publicResources, Charsets.UTF_8); Multimap<String, ResourceType> result = ArrayListMultimap.create(lines.size(), 2); for (String line : lines) { // These files are written by code in MergedResourceWriter#postWriteAction // Format for each line: <type><space><name>\n // Therefore, we don't expect/allow variations in the format (we don't // worry about extra spaces needing to be trimmed etc) int index = line.indexOf(' '); if (index == -1 || line.isEmpty()) { continue; } String typeString = line.substring(0, index); ResourceType type = ResourceType.getEnum(typeString); if (type == null) { // This could in theory happen if in the future a new ResourceType is // introduced, and a newer version of the Gradle build system writes the // name of this type into the public.txt file, and an older version of // the IDE then attempts to read it. Just skip these symbols. continue; } String name = line.substring(index + 1); result.put(name, type); } return result; } catch (IOException ignore) { } return null; }