Statistics.java revision fe107fb6e3f308ac5174ebdc5a794ee880c741d9
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.dex.file;
18
19import com.android.dx.util.AnnotatedOutput;
20import java.util.Collection;
21import java.util.HashMap;
22import java.util.TreeMap;
23
24/**
25 * Statistics about the contents of a file.
26 */
27public final class Statistics {
28    /** {@code non-null;} data about each type of item */
29    private final HashMap<String, Data> dataMap;
30
31    /**
32     * Constructs an instance.
33     */
34    public Statistics() {
35        dataMap = new HashMap<String, Data>(50);
36    }
37
38    /**
39     * Adds the given item to the statistics.
40     *
41     * @param item {@code non-null;} the item to add
42     */
43    public void add(Item item) {
44        String typeName = item.typeName();
45        Data data = dataMap.get(typeName);
46
47        if (data == null) {
48            dataMap.put(typeName, new Data(item, typeName));
49        } else {
50            data.add(item);
51        }
52    }
53
54    /**
55     * Adds the given list of items to the statistics.
56     *
57     * @param list {@code non-null;} the list of items to add
58     */
59    public void addAll(Section list) {
60        Collection<? extends Item> items = list.items();
61        for (Item item : items) {
62            add(item);
63        }
64    }
65
66    /**
67     * Writes the statistics as an annotation.
68     *
69     * @param out {@code non-null;} where to write to
70     */
71    public final void writeAnnotation(AnnotatedOutput out) {
72        if (dataMap.size() == 0) {
73            return;
74        }
75
76        out.annotate(0, "\nstatistics:\n");
77
78        TreeMap<String, Data> sortedData = new TreeMap<String, Data>();
79
80        for (Data data : dataMap.values()) {
81            sortedData.put(data.name, data);
82        }
83
84        for (Data data : sortedData.values()) {
85            data.writeAnnotation(out);
86        }
87    }
88
89    public String toHuman() {
90        StringBuilder sb = new StringBuilder();
91
92        sb.append("Statistics:\n");
93
94        TreeMap<String, Data> sortedData = new TreeMap<String, Data>();
95
96        for (Data data : dataMap.values()) {
97            sortedData.put(data.name, data);
98        }
99
100        for (Data data : sortedData.values()) {
101            sb.append(data.toHuman());
102        }
103
104        return sb.toString();
105    }
106
107    /**
108     * Statistical data about a particular class.
109     */
110    private static class Data {
111        /** {@code non-null;} name to use as a label */
112        private final String name;
113
114        /** {@code >= 0;} number of instances */
115        private int count;
116
117        /** {@code >= 0;} total size of instances in bytes */
118        private int totalSize;
119
120        /** {@code >= 0;} largest size of any individual item */
121        private int largestSize;
122
123        /** {@code >= 0;} smallest size of any individual item */
124        private int smallestSize;
125
126        /**
127         * Constructs an instance for the given item.
128         *
129         * @param item {@code non-null;} item in question
130         * @param name {@code non-null;} type name to use
131         */
132        public Data(Item item, String name) {
133            int size = item.writeSize();
134
135            this.name = name;
136            this.count = 1;
137            this.totalSize = size;
138            this.largestSize = size;
139            this.smallestSize = size;
140        }
141
142        /**
143         * Incorporates a new item. This assumes the type name matches.
144         *
145         * @param item {@code non-null;} item to incorporate
146         */
147        public void add(Item item) {
148            int size = item.writeSize();
149
150            count++;
151            totalSize += size;
152
153            if (size > largestSize) {
154                largestSize = size;
155            }
156
157            if (size < smallestSize) {
158                smallestSize = size;
159            }
160        }
161
162        /**
163         * Writes this instance as an annotation.
164         *
165         * @param out {@code non-null;} where to write to
166         */
167        public void writeAnnotation(AnnotatedOutput out) {
168            out.annotate(toHuman());
169        }
170
171        /**
172         * Generates a human-readable string for this data item.
173         *
174         * @return string for human consumption.
175         */
176        public String toHuman() {
177            StringBuilder sb = new StringBuilder();
178
179            sb.append("  " + name + ": " +
180                         count + " item" + (count == 1 ? "" : "s") + "; " +
181                         totalSize + " bytes total\n");
182
183            if (smallestSize == largestSize) {
184                sb.append("    " + smallestSize + " bytes/item\n");
185            } else {
186                int average = totalSize / count;
187                sb.append("    " + smallestSize + ".." + largestSize +
188                             " bytes/item; average " + average + "\n");
189            }
190
191            return sb.toString();
192        }
193    }
194}
195