aboutsummaryrefslogtreecommitdiff
path: root/attribute_stats/src/Analyzer.java
diff options
context:
space:
mode:
Diffstat (limited to 'attribute_stats/src/Analyzer.java')
-rw-r--r--attribute_stats/src/Analyzer.java663
1 files changed, 0 insertions, 663 deletions
diff --git a/attribute_stats/src/Analyzer.java b/attribute_stats/src/Analyzer.java
deleted file mode 100644
index 8da53ea38..000000000
--- a/attribute_stats/src/Analyzer.java
+++ /dev/null
@@ -1,663 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-/**
- * Gathers statistics about attribute usage in layout files. This is how the "topAttrs"
- * attributes listed in ADT's extra-view-metadata.xml (which drives the common attributes
- * listed in the top of the context menu) is determined by running this script on a body
- * of sample layout code.
- * <p>
- * This program takes one or more directory paths, and then it searches all of them recursively
- * for layout files that are not in folders containing the string "test", and computes and
- * prints frequency statistics.
- */
-public class Analyzer {
- /** Number of attributes to print for each view */
- public static final int ATTRIBUTE_COUNT = 6;
- /** Separate out any attributes that constitute less than N percent of the total */
- public static final int THRESHOLD = 10; // percent
-
- private List<File> mDirectories;
- private File mCurrentFile;
- private boolean mListAdvanced;
-
- /** Map from view id to map from attribute to frequency count */
- private Map<String, Map<String, Usage>> mFrequencies =
- new HashMap<String, Map<String, Usage>>(100);
-
- private Map<String, Map<String, Usage>> mLayoutAttributeFrequencies =
- new HashMap<String, Map<String, Usage>>(100);
-
- private Map<String, String> mTopAttributes = new HashMap<String, String>(100);
- private Map<String, String> mTopLayoutAttributes = new HashMap<String, String>(100);
-
- private int mFileVisitCount;
- private int mLayoutFileCount;
- private File mXmlMetadataFile;
-
- private Analyzer(List<File> directories, File xmlMetadataFile, boolean listAdvanced) {
- mDirectories = directories;
- mXmlMetadataFile = xmlMetadataFile;
- mListAdvanced = listAdvanced;
- }
-
- public static void main(String[] args) {
- if (args.length < 1) {
- System.err.println("Usage: " + Analyzer.class.getSimpleName()
- + " <directory1> [directory2 [directory3 ...]]\n");
- System.err.println("Recursively scans for layouts in the given directory and");
- System.err.println("computes statistics about attribute frequencies.");
- System.exit(-1);
- }
-
- File metadataFile = null;
- List<File> directories = new ArrayList<File>();
- boolean listAdvanced = false;
- for (int i = 0, n = args.length; i < n; i++) {
- String arg = args[i];
-
- if (arg.equals("--list")) {
- // List ALL encountered attributes
- listAdvanced = true;
- continue;
- }
-
- // The -metadata flag takes a pointer to an ADT extra-view-metadata.xml file
- // and attempts to insert topAttrs attributes into it (and saves it as same
- // file +.mod as an extension). This isn't listed on the usage flag because
- // it's pretty brittle and requires some manual fixups to the file afterwards.
- if (arg.equals("--metadata")) {
- i++;
- File file = new File(args[i]);
- if (!file.exists()) {
- System.err.println(file.getName() + " does not exist");
- System.exit(-5);
- }
- if (!file.isFile() || !file.getName().endsWith(".xml")) {
- System.err.println(file.getName() + " must be an XML file");
- System.exit(-4);
- }
- metadataFile = file;
- continue;
- }
- File directory = new File(arg);
- if (!directory.exists()) {
- System.err.println(directory.getName() + " does not exist");
- System.exit(-2);
- }
-
- if (!directory.isDirectory()) {
- System.err.println(directory.getName() + " is not a directory");
- System.exit(-3);
- }
-
- directories.add(directory);
- }
-
- new Analyzer(directories, metadataFile, listAdvanced).analyze();
- }
-
- private void analyze() {
- for (File directory : mDirectories) {
- scanDirectory(directory);
- }
-
- if (mListAdvanced) {
- listAdvanced();
- }
-
- printStatistics();
-
- if (mXmlMetadataFile != null) {
- printMergedMetadata();
- }
- }
-
- private void scanDirectory(File directory) {
- File[] files = directory.listFiles();
- if (files == null) {
- return;
- }
-
- for (File file : files) {
- mFileVisitCount++;
- if (mFileVisitCount % 50000 == 0) {
- System.out.println("Analyzed " + mFileVisitCount + " files...");
- }
-
- if (file.isFile()) {
- scanFile(file);
- } else if (file.isDirectory()) {
- // Skip stuff related to tests
- if (file.getName().contains("test")) {
- continue;
- }
-
- // Recurse over subdirectories
- scanDirectory(file);
- }
- }
- }
-
- private void scanFile(File file) {
- if (file.getName().endsWith(".xml")) {
- File parent = file.getParentFile();
- if (parent.getName().startsWith("layout")) {
- analyzeLayout(file);
- }
- }
-
- }
-
- private void analyzeLayout(File file) {
- mCurrentFile = file;
- mLayoutFileCount++;
- Document document = null;
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- InputSource is = new InputSource(new StringReader(readFile(file)));
- try {
- factory.setNamespaceAware(true);
- factory.setValidating(false);
- DocumentBuilder builder = factory.newDocumentBuilder();
- document = builder.parse(is);
-
- analyzeDocument(document);
-
- } catch (ParserConfigurationException e) {
- // pass -- ignore files we can't parse
- } catch (SAXException e) {
- // pass -- ignore files we can't parse
- } catch (IOException e) {
- // pass -- ignore files we can't parse
- }
- }
-
-
- private void analyzeDocument(Document document) {
- analyzeElement(document.getDocumentElement());
- }
-
- private void analyzeElement(Element element) {
- if (element.getTagName().equals("item")) {
- // Resource files shouldn't be in the layout/ folder but I came across
- // some cases
- System.out.println("Warning: found <item> tag in a layout file in "
- + mCurrentFile.getPath());
- return;
- }
-
- countAttributes(element);
- countLayoutAttributes(element);
-
- // Recurse over children
- NodeList childNodes = element.getChildNodes();
- for (int i = 0, n = childNodes.getLength(); i < n; i++) {
- Node child = childNodes.item(i);
- if (child.getNodeType() == Node.ELEMENT_NODE) {
- analyzeElement((Element) child);
- }
- }
- }
-
- private void countAttributes(Element element) {
- String tag = element.getTagName();
- Map<String, Usage> attributeMap = mFrequencies.get(tag);
- if (attributeMap == null) {
- attributeMap = new HashMap<String, Usage>(70);
- mFrequencies.put(tag, attributeMap);
- }
-
- NamedNodeMap attributes = element.getAttributes();
- for (int i = 0, n = attributes.getLength(); i < n; i++) {
- Node attribute = attributes.item(i);
- String name = attribute.getNodeName();
-
- if (name.startsWith("android:layout_")) {
- // Skip layout attributes; they are a function of the parent layout that this
- // view is embedded within, not the view itself.
- // TODO: Consider whether we should incorporate this info or make statistics
- // about that as well?
- continue;
- }
-
- if (name.equals("android:id")) {
- // Skip ids: they are (mostly) unrelated to the view type and the tool
- // already offers id editing prominently
- continue;
- }
-
- if (name.startsWith("xmlns:")) {
- // Unrelated to frequency counts
- continue;
- }
-
- Usage usage = attributeMap.get(name);
- if (usage == null) {
- usage = new Usage(name);
- } else {
- usage.incrementCount();
- }
- attributeMap.put(name, usage);
- }
- }
-
- private void countLayoutAttributes(Element element) {
- String parentTag = element.getParentNode().getNodeName();
- Map<String, Usage> attributeMap = mLayoutAttributeFrequencies.get(parentTag);
- if (attributeMap == null) {
- attributeMap = new HashMap<String, Usage>(70);
- mLayoutAttributeFrequencies.put(parentTag, attributeMap);
- }
-
- NamedNodeMap attributes = element.getAttributes();
- for (int i = 0, n = attributes.getLength(); i < n; i++) {
- Node attribute = attributes.item(i);
- String name = attribute.getNodeName();
-
- if (!name.startsWith("android:layout_")) {
- continue;
- }
-
- // Skip layout_width and layout_height; they are mandatory in all but GridLayout so not
- // very interesting
- if (name.equals("android:layout_width") || name.equals("android:layout_height")) {
- continue;
- }
-
- Usage usage = attributeMap.get(name);
- if (usage == null) {
- usage = new Usage(name);
- } else {
- usage.incrementCount();
- }
- attributeMap.put(name, usage);
- }
- }
-
- // Copied from AdtUtils
- private static String readFile(File file) {
- try {
- return readFile(new FileReader(file));
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
-
- return null;
- }
-
- private static String readFile(Reader inputStream) {
- BufferedReader reader = null;
- try {
- reader = new BufferedReader(inputStream);
- StringBuilder sb = new StringBuilder(2000);
- while (true) {
- int c = reader.read();
- if (c == -1) {
- return sb.toString();
- } else {
- sb.append((char)c);
- }
- }
- } catch (IOException e) {
- // pass -- ignore files we can't read
- } finally {
- try {
- if (reader != null) {
- reader.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- return null;
- }
-
- private void printStatistics() {
- System.out.println("Analyzed " + mLayoutFileCount
- + " layouts (in a directory trees containing " + mFileVisitCount + " files)");
- System.out.println("Top " + ATTRIBUTE_COUNT
- + " for each view (excluding layout_ attributes) :");
- System.out.println("\n");
- System.out.println(" Rank Count Share Attribute");
- System.out.println("=========================================================");
- List<String> views = new ArrayList<String>(mFrequencies.keySet());
- Collections.sort(views);
- for (String view : views) {
- String top = processUageMap(view, mFrequencies.get(view));
- if (top != null) {
- mTopAttributes.put(view, top);
- }
- }
-
- System.out.println("\n\n\nTop " + ATTRIBUTE_COUNT + " layout attributes (excluding "
- + "mandatory layout_width and layout_height):");
- System.out.println("\n");
- System.out.println(" Rank Count Share Attribute");
- System.out.println("=========================================================");
- views = new ArrayList<String>(mLayoutAttributeFrequencies.keySet());
- Collections.sort(views);
- for (String view : views) {
- String top = processUageMap(view, mLayoutAttributeFrequencies.get(view));
- if (top != null) {
- mTopLayoutAttributes.put(view, top);
- }
- }
- }
-
- private static String processUageMap(String view, Map<String, Usage> map) {
- if (map == null) {
- return null;
- }
-
- if (view.indexOf('.') != -1 && !view.startsWith("android.")) {
- // Skip custom views
- return null;
- }
-
- List<Usage> values = new ArrayList<Usage>(map.values());
- if (values.size() == 0) {
- return null;
- }
-
- Collections.sort(values);
- int totalCount = 0;
- for (Usage usage : values) {
- totalCount += usage.count;
- }
-
- System.out.println("\n<" + view + ">:");
- if (view.equals("#document")) {
- System.out.println("(Set on root tag, probably intended for included context)");
- }
-
- int place = 1;
- int count = 0;
- int prevCount = -1;
- float prevPercentage = 0f;
- StringBuilder sb = new StringBuilder();
- for (Usage usage : values) {
- if (count++ >= ATTRIBUTE_COUNT && usage.count < prevCount) {
- break;
- }
-
- float percentage = 100 * usage.count/(float)totalCount;
- if (percentage < THRESHOLD && prevPercentage >= THRESHOLD) {
- System.out.println(" -----Less than 10%-------------------------------------");
- }
- System.out.printf(" %1d. %5d %5.1f%% %s\n", place, usage.count,
- percentage, usage.attribute);
-
- prevPercentage = percentage;
- if (prevCount != usage.count) {
- prevCount = usage.count;
- place++;
- }
-
- if (percentage >= THRESHOLD /*&& usage.count > 1*/) { // 1:Ignore when not enough data?
- if (sb.length() > 0) {
- sb.append(',');
- }
- String name = usage.attribute;
- if (name.startsWith("android:")) {
- name = name.substring("android:".length());
- }
- sb.append(name);
- }
- }
-
- return sb.length() > 0 ? sb.toString() : null;
- }
-
- private void printMergedMetadata() {
- assert mXmlMetadataFile != null;
- String metadata = readFile(mXmlMetadataFile);
- if (metadata == null || metadata.length() == 0) {
- System.err.println("Invalid metadata file");
- System.exit(-6);
- }
-
- System.err.flush();
- System.out.println("\n\nUpdating layout metadata file...");
- System.out.flush();
-
- StringBuilder sb = new StringBuilder((int) (2 * mXmlMetadataFile.length()));
- String[] lines = metadata.split("\n");
- for (int i = 0; i < lines.length; i++) {
- String line = lines[i];
- sb.append(line).append('\n');
- int classIndex = line.indexOf("class=\"");
- if (classIndex != -1) {
- int start = classIndex + "class=\"".length();
- int end = line.indexOf('"', start + 1);
- if (end != -1) {
- String view = line.substring(start, end);
- if (view.startsWith("android.widget.")) {
- view = view.substring("android.widget.".length());
- } else if (view.startsWith("android.view.")) {
- view = view.substring("android.view.".length());
- } else if (view.startsWith("android.webkit.")) {
- view = view.substring("android.webkit.".length());
- }
- String top = mTopAttributes.get(view);
- if (top == null) {
- System.err.println("Warning: No frequency data for view " + view);
- } else {
- sb.append(line.substring(0, classIndex)); // Indentation
-
- sb.append("topAttrs=\"");
- sb.append(top);
- sb.append("\"\n");
- }
-
- top = mTopLayoutAttributes.get(view);
- if (top != null) {
- // It's a layout attribute
- sb.append(line.substring(0, classIndex)); // Indentation
-
- sb.append("topLayoutAttrs=\"");
- sb.append(top);
- sb.append("\"\n");
- }
- }
- }
- }
-
- System.out.println("\nTop attributes:");
- System.out.println("--------------------------");
- List<String> views = new ArrayList<String>(mTopAttributes.keySet());
- Collections.sort(views);
- for (String view : views) {
- String top = mTopAttributes.get(view);
- System.out.println(view + ": " + top);
- }
-
- System.out.println("\nTop layout attributes:");
- System.out.println("--------------------------");
- views = new ArrayList<String>(mTopLayoutAttributes.keySet());
- Collections.sort(views);
- for (String view : views) {
- String top = mTopLayoutAttributes.get(view);
- System.out.println(view + ": " + top);
- }
-
- System.out.println("\nModified XML metadata file:\n");
- String newContent = sb.toString();
- File output = new File(mXmlMetadataFile.getParentFile(), mXmlMetadataFile.getName() + ".mod");
- if (output.exists()) {
- output.delete();
- }
- try {
- BufferedWriter writer = new BufferedWriter(new FileWriter(output));
- writer.write(newContent);
- writer.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- System.out.println("Done - wrote " + output.getPath());
- }
-
- //private File mPublicFile = new File(location, "data/res/values/public.xml");
- private File mPublicFile = new File("/Volumes/AndroidWork/git/frameworks/base/core/res/res/values/public.xml");
-
- private void listAdvanced() {
- Set<String> keys = new HashSet<String>(1000);
-
- // Merged usages across view types
- Map<String, Usage> mergedUsages = new HashMap<String, Usage>(100);
-
- for (Entry<String,Map<String,Usage>> entry : mFrequencies.entrySet()) {
- String view = entry.getKey();
- if (view.indexOf('.') != -1 && !view.startsWith("android.")) {
- // Skip custom views etc
- continue;
- }
- Map<String, Usage> map = entry.getValue();
- for (Usage usage : map.values()) {
-// if (usage.count == 1) {
-// System.out.println("Only found *one* usage of " + usage.attribute);
-// }
-// if (usage.count < 4) {
-// System.out.println("Only found " + usage.count + " usage of " + usage.attribute);
-// }
-
- String attribute = usage.attribute;
- int index = attribute.indexOf(':');
- if (index == -1 || attribute.startsWith("android:")) {
- Usage merged = mergedUsages.get(attribute);
- if (merged == null) {
- merged = new Usage(attribute);
- merged.count = usage.count;
- mergedUsages.put(attribute, merged);
- } else {
- merged.count += usage.count;
- }
- }
- }
- }
-
- for (Usage usage : mergedUsages.values()) {
- String attribute = usage.attribute;
- if (usage.count < 4) {
- System.out.println("Only found " + usage.count + " usage of " + usage.attribute);
- continue;
- }
- int index = attribute.indexOf(':');
- if (index != -1) {
- attribute = attribute.substring(index + 1); // +1: skip ':'
- }
- keys.add(attribute);
- }
-
- List<String> sorted = new ArrayList<String>(keys);
- Collections.sort(sorted);
- System.out.println("\nEncountered Attributes");
- System.out.println("-----------------------------");
- for (String attribute : sorted) {
- System.out.println(attribute);
- }
-
- System.out.println();
- }
-
- private static class Usage implements Comparable<Usage> {
- public String attribute;
- public int count;
-
-
- public Usage(String attribute) {
- super();
- this.attribute = attribute;
-
- count = 1;
- }
-
- public void incrementCount() {
- count++;
- }
-
- @Override
- public int compareTo(Usage o) {
- // Sort by decreasing frequency, then sort alphabetically
- int frequencyDelta = o.count - count;
- if (frequencyDelta != 0) {
- return frequencyDelta;
- } else {
- return attribute.compareTo(o.attribute);
- }
- }
-
- @Override
- public String toString() {
- return attribute + ": " + count;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((attribute == null) ? 0 : attribute.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- Usage other = (Usage) obj;
- if (attribute == null) {
- if (other.attribute != null)
- return false;
- } else if (!attribute.equals(other.attribute))
- return false;
- return true;
- }
- }
-}