Search in sources :

Example 6 with ResourceType

use of com.android.resources.ResourceType in project bazel by bazelbuild.

the class RClassGenerator method write.

/** Builds the bytecode and writes out the R.class file, and R$inner.class files. */
public void write() throws IOException {
    Iterable<String> folders = PACKAGE_SPLITTER.split(packageName);
    Path packageDir = outFolder;
    for (String folder : folders) {
        packageDir = packageDir.resolve(folder);
    }
    // At least create the outFolder that was requested. However, if there are no symbols, don't
    // create the R.class and inner class files (no need to have an empty class).
    Files.createDirectories(packageDir);
    if (initializers.isEmpty()) {
        return;
    }
    Path rClassFile = packageDir.resolve(SdkConstants.FN_COMPILED_RESOURCE_CLASS);
    String packageWithSlashes = packageName.replaceAll("\\.", "/");
    String rClassName = packageWithSlashes.isEmpty() ? "R" : (packageWithSlashes + "/R");
    ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
    classWriter.visit(JAVA_VERSION, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER, rClassName, null, /* signature */
    SUPER_CLASS, null);
    classWriter.visitSource(SdkConstants.FN_RESOURCE_CLASS, null);
    writeConstructor(classWriter);
    // Build the R.class w/ the inner classes, then later build the individual R$inner.class.
    for (ResourceType resourceType : initializers.keySet()) {
        String innerClassName = rClassName + "$" + resourceType;
        classWriter.visitInnerClass(innerClassName, rClassName, resourceType.toString(), Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC);
    }
    classWriter.visitEnd();
    Files.write(rClassFile, classWriter.toByteArray());
    // Now generate the R$inner.class files.
    for (Map.Entry<ResourceType, List<FieldInitializer>> entry : initializers.entrySet()) {
        writeInnerClass(entry.getValue(), packageDir, rClassName, entry.getKey().toString());
    }
}
Also used : Path(java.nio.file.Path) ResourceType(com.android.resources.ResourceType) ArrayList(java.util.ArrayList) List(java.util.List) Map(java.util.Map) EnumMap(java.util.EnumMap) ClassWriter(org.objectweb.asm.ClassWriter)

Example 7 with ResourceType

use of com.android.resources.ResourceType in project bazel by bazelbuild.

the class ResourceUsageAnalyzer method removeUnused.

/**
   * Remove resources (already identified by {@link #shrink(Path)}).
   *
   * <p>This task will copy all remaining used resources over from the full resource directory to a
   * new reduced resource directory and removes unused values from all value xml files.
   *
   * @param destination directory to copy resources into; if null, delete resources in place
   * @throws IOException
   * @throws ParserConfigurationException
   * @throws SAXException
   */
private void removeUnused(Path destination) throws IOException, ParserConfigurationException, SAXException {
    // should always call analyze() first
    assert unused != null;
    // *4: account for some resource folder repetition
    int resourceCount = unused.size() * 4;
    Set<File> skip = Sets.newHashSetWithExpectedSize(resourceCount);
    Set<File> rewrite = Sets.newHashSetWithExpectedSize(resourceCount);
    Set<Resource> deleted = Sets.newHashSetWithExpectedSize(resourceCount);
    for (Resource resource : unused) {
        if (resource.declarations != null) {
            for (File file : resource.declarations) {
                String folder = file.getParentFile().getName();
                ResourceFolderType folderType = ResourceFolderType.getFolderType(folder);
                if (folderType != null && folderType != ResourceFolderType.VALUES) {
                    List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(folderType);
                    ResourceType type = types.get(0);
                    assert type != ResourceType.ID : folderType;
                    Resource fileResource = model.getResource(type, LintUtils.getBaseName(file.getName()));
                    // considered uses and would otherwise cause deletion of the file.
                    if (fileResource == null || fileResource.equals(resource)) {
                        logger.fine("Deleted unused file " + file + " for resource " + resource);
                        assert skip != null;
                        skip.add(file);
                        deleted.add(resource);
                    }
                } else {
                    // Can't delete values immediately; there can be many resources
                    // in this file, so we have to process them all
                    rewrite.add(file);
                }
            }
        } else {
            // Not declared anywhere; mark as deleted. Covers the case of inline resources.
            // https://developer.android.com/guide/topics/resources/complex-xml-resources.html
            deleted.add(resource);
        }
    }
    // Special case the base values.xml folder
    File values = new File(mergedResourceDir.toFile(), FD_RES_VALUES + File.separatorChar + "values.xml");
    if (values.exists()) {
        rewrite.add(values);
    }
    Map<File, String> rewritten = Maps.newHashMapWithExpectedSize(rewrite.size());
    rewriteXml(rewrite, rewritten, deleted);
    // TODO(apell): The graph traversal does not mark IDs as reachable or not, so they cannot be
    // accurately removed from public.xml, but the definitions may be deleted if they occur in
    // other files. IDs should be added to values.xml so that there are no declarations in
    // public.xml without definitions.
    File publicXml = new File(mergedResourceDir.toFile(), FD_RES_VALUES + File.separatorChar + "public.xml");
    createStubIds(values, rewritten, publicXml);
    trimPublicResources(publicXml, deleted, rewritten);
    filteredCopy(mergedResourceDir.toFile(), destination, skip, rewritten);
}
Also used : ResourceFolderType(com.android.resources.ResourceFolderType) Resource(com.android.tools.lint.checks.ResourceUsageModel.Resource) ResourceType(com.android.resources.ResourceType) File(java.io.File)

Example 8 with ResourceType

use of com.android.resources.ResourceType in project bazel by bazelbuild.

the class ResourceUsageAnalyzer method keepPossiblyReferencedResources.

private void keepPossiblyReferencedResources() {
    if ((!foundGetIdentifier && !foundWebContent) || strings == null) {
        // to worry about string references to resources
        return;
    }
    if (!model.isSafeMode()) {
        // explicitly mark them as kept if necessary instead
        return;
    }
    List<String> sortedStrings = new ArrayList<String>(strings);
    Collections.sort(sortedStrings);
    logger.fine("android.content.res.Resources#getIdentifier present: " + foundGetIdentifier);
    logger.fine("Web content present: " + foundWebContent);
    logger.fine("Referenced Strings:");
    for (String string : sortedStrings) {
        string = string.trim().replace("\n", "\\n");
        if (string.length() > 40) {
            string = string.substring(0, 37) + "...";
        } else if (string.isEmpty()) {
            continue;
        }
        logger.fine("  " + string);
    }
    int shortest = Integer.MAX_VALUE;
    Set<String> names = Sets.newHashSetWithExpectedSize(50);
    for (Resource resource : model.getResources()) {
        String name = resource.name;
        names.add(name);
        int length = name.length();
        if (length < shortest) {
            shortest = length;
        }
    }
    for (String string : strings) {
        if (string.length() < shortest) {
            continue;
        }
        //  (4) If foundWebContent is true, look for android_res/ URL strings as well
        if (foundWebContent) {
            Resource resource = model.getResourceFromFilePath(string);
            if (resource != null) {
                ResourceUsageModel.markReachable(resource);
                continue;
            } else {
                int start = 0;
                int slash = string.lastIndexOf('/');
                if (slash != -1) {
                    start = slash + 1;
                }
                int dot = string.indexOf('.', start);
                String name = string.substring(start, dot != -1 ? dot : string.length());
                if (names.contains(name)) {
                    for (Map<String, Resource> map : model.getResourceMaps()) {
                        resource = map.get(name);
                        if (resource != null) {
                            logger.fine(String.format("Marking %s used because it matches string pool constant %s", resource, string));
                        }
                        ResourceUsageModel.markReachable(resource);
                    }
                }
            }
        }
        // Look for normal getIdentifier resource URLs
        int n = string.length();
        boolean justName = true;
        boolean formatting = false;
        boolean haveSlash = false;
        for (int i = 0; i < n; i++) {
            char c = string.charAt(i);
            if (c == '/') {
                haveSlash = true;
                justName = false;
            } else if (c == '.' || c == ':' || c == '%') {
                justName = false;
                if (c == '%') {
                    formatting = true;
                }
            } else if (!Character.isJavaIdentifierPart(c)) {
                // the {@link #referencedString} method
                assert false : string;
                break;
            }
        }
        String name;
        if (justName) {
            // Check name (below)
            name = string;
            // getResources().getIdentifier("ic_video_codec_" + codecName, "drawable", ...)
            for (Resource resource : model.getResources()) {
                if (resource.name.startsWith(name)) {
                    logger.fine(String.format("Marking %s used because its prefix matches string pool constant %s", resource, string));
                    ResourceUsageModel.markReachable(resource);
                }
            }
        } else if (!haveSlash) {
            if (formatting) {
                //   int res = getContext().getResources().getIdentifier(name, "drawable", ...)
                try {
                    Pattern pattern = Pattern.compile(convertFormatStringToRegexp(string));
                    for (Resource resource : model.getResources()) {
                        if (pattern.matcher(resource.name).matches()) {
                            logger.fine(String.format("Marking %s used because it format-string matches string pool constant %s", resource, string));
                            ResourceUsageModel.markReachable(resource);
                        }
                    }
                } catch (PatternSyntaxException ignored) {
                // Might not have been a formatting string after all!
                }
            }
            //noinspection UnnecessaryContinue
            continue;
        } else {
            // Try to pick out the resource name pieces; if we can find the
            // resource type unambiguously; if not, just match on names
            int slash = string.indexOf('/');
            // checked with haveSlash above
            assert slash != -1;
            name = string.substring(slash + 1);
            if (name.isEmpty() || !names.contains(name)) {
                continue;
            }
            // See if have a known specific resource type
            if (slash > 0) {
                int colon = string.indexOf(':');
                String typeName = string.substring(colon != -1 ? colon + 1 : 0, slash);
                ResourceType type = ResourceType.getEnum(typeName);
                if (type == null) {
                    continue;
                }
                Resource resource = model.getResource(type, name);
                if (resource != null) {
                    logger.fine(String.format("Marking %s used because it matches string pool constant %s", resource, string));
                }
                ResourceUsageModel.markReachable(resource);
                continue;
            }
        // fall through and check the name
        }
        if (names.contains(name)) {
            for (Map<String, Resource> map : model.getResourceMaps()) {
                Resource resource = map.get(name);
                if (resource != null) {
                    logger.fine(String.format("Marking %s used because it matches string pool constant %s", resource, string));
                }
                ResourceUsageModel.markReachable(resource);
            }
        } else if (Character.isDigit(name.charAt(0))) {
            // "android.resource://com.android.alarmclock/2130837524".
            try {
                int id = Integer.parseInt(name);
                if (id != 0) {
                    ResourceUsageModel.markReachable(model.getResource(id));
                }
            } catch (NumberFormatException e) {
            // pass
            }
        }
    }
}
Also used : Pattern(java.util.regex.Pattern) ArrayList(java.util.ArrayList) Resource(com.android.tools.lint.checks.ResourceUsageModel.Resource) ResourceType(com.android.resources.ResourceType) PatternSyntaxException(java.util.regex.PatternSyntaxException)

Example 9 with ResourceType

use of com.android.resources.ResourceType in project bazel by bazelbuild.

the class ResourceUsageAnalyzer method parseResourceTxtFile.

private void parseResourceTxtFile(Path rTxt, Set<String> resourcePackages) throws IOException {
    BufferedReader reader = java.nio.file.Files.newBufferedReader(rTxt, UTF_8);
    String line;
    while ((line = reader.readLine()) != null) {
        String[] tokens = line.split(" ");
        ResourceType type = ResourceType.getEnum(tokens[1]);
        for (String resourcePackage : resourcePackages) {
            String owner = resourcePackage.replace('.', '/') + "/R$" + type.getName();
            Pair<ResourceType, Map<String, String>> pair = resourceObfuscation.get(owner);
            if (pair == null) {
                Map<String, String> nameMap = Maps.newHashMap();
                pair = Pair.of(type, nameMap);
            }
            resourceObfuscation.put(owner, pair);
        }
        if (type == ResourceType.STYLEABLE) {
            if (tokens[0].equals("int[]")) {
                model.addResource(ResourceType.DECLARE_STYLEABLE, tokens[2], null);
            } else {
            // TODO(jongerrish): Implement stripping of styleables.
            }
        } else {
            model.addResource(type, tokens[2], tokens[3]);
        }
    }
}
Also used : BufferedReader(java.io.BufferedReader) ResourceType(com.android.resources.ResourceType) Map(java.util.Map) NamedNodeMap(org.w3c.dom.NamedNodeMap)

Example 10 with ResourceType

use of com.android.resources.ResourceType in project bazel by bazelbuild.

the class ResourceUsageAnalyzer method trimPublicResources.

/**
   * Remove public definitions of unused resources.
   */
private void trimPublicResources(File publicXml, Set<Resource> deleted, Map<File, String> rewritten) throws IOException, ParserConfigurationException, SAXException {
    if (publicXml.exists()) {
        String xml = rewritten.get(publicXml);
        if (xml == null) {
            xml = Files.toString(publicXml, UTF_8);
        }
        Document document = XmlUtils.parseDocument(xml, true);
        Element root = document.getDocumentElement();
        if (root != null && TAG_RESOURCES.equals(root.getTagName())) {
            NodeList children = root.getChildNodes();
            for (int i = children.getLength() - 1; i >= 0; i--) {
                Node child = children.item(i);
                if (child.getNodeType() == Node.ELEMENT_NODE) {
                    Element resourceElement = (Element) child;
                    ResourceType type = ResourceType.getEnum(resourceElement.getAttribute(ATTR_TYPE));
                    String name = resourceElement.getAttribute(ATTR_NAME);
                    if (type != null && name != null) {
                        Resource resource = model.getResource(type, name);
                        if (resource != null && deleted.contains(resource)) {
                            root.removeChild(child);
                        }
                    }
                }
            }
        }
        String formatted = XmlPrettyPrinter.prettyPrint(document, xml.endsWith("\n"));
        rewritten.put(publicXml, formatted);
    }
}
Also used : Element(org.w3c.dom.Element) NodeList(org.w3c.dom.NodeList) Node(org.w3c.dom.Node) Resource(com.android.tools.lint.checks.ResourceUsageModel.Resource) ResourceType(com.android.resources.ResourceType) Document(org.w3c.dom.Document)

Aggregations

ResourceType (com.android.resources.ResourceType)137 ResourceValue (com.android.ide.common.rendering.api.ResourceValue)31 NotNull (org.jetbrains.annotations.NotNull)16 Field (java.lang.reflect.Field)13 StyleResourceValue (com.android.ide.common.rendering.api.StyleResourceValue)12 BridgeContext (com.android.layoutlib.bridge.android.BridgeContext)11 VirtualFile (com.intellij.openapi.vfs.VirtualFile)10 Map (java.util.Map)10 Nullable (org.jetbrains.annotations.Nullable)10 Nullable (com.android.annotations.Nullable)8 ResourceFolderType (com.android.resources.ResourceFolderType)8 File (java.io.File)8 EnumMap (java.util.EnumMap)7 AndroidFacet (org.jetbrains.android.facet.AndroidFacet)7 Context (android.content.Context)6 View (android.view.View)6 AbsListView (android.widget.AbsListView)6 AdapterView (android.widget.AdapterView)6 ExpandableListView (android.widget.ExpandableListView)6 FrameLayout (android.widget.FrameLayout)6