Statistics.java revision 579d7739c53a2707ad711a2d2cae46d7d782f061
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;
20
21import java.util.Collection;
22import java.util.HashMap;
23import java.util.TreeMap;
24
25/**
26 * Statistics about the contents of a file.
27 */
28public final class Statistics {
29    /** {@code non-null;} data about each type of item */
30    private final HashMap<String, Data> dataMap;
31
32    /**
33     * Constructs an instance.
34     */
35    public Statistics() {
36        dataMap = new HashMap<String, Data>(50);
37    }
38
39    /**
40     * Adds the given item to the statistics.
41     *
42     * @param item {@code non-null;} the item to add
43     */
44    public void add(Item item) {
45        String typeName = item.typeName();
46        Data data = dataMap.get(typeName);
47
48        if (data == null) {
49            dataMap.put(typeName, new Data(item, typeName));
50        } else {
51            data.add(item);
52        }
53    }
54
55    /**
56     * Adds the given list of items to the statistics.
57     *
58     * @param list {@code non-null;} the list of items to add
59     */
60    public void addAll(Section list) {
61        Collection<? extends Item> items = list.items();
62        for (Item item : items) {
63            add(item);
64        }
65    }
66
67    /**
68     * Writes the statistics as an annotation.
69     *
70     * @param out {@code non-null;} where to write to
71     */
72    public final void writeAnnotation(AnnotatedOutput out) {
73        if (dataMap.size() == 0) {
74            return;
75        }
76
77        out.annotate(0, "\nstatistics:\n");
78
79        TreeMap<String, Data> sortedData = new TreeMap<String, Data>();
80
81        for (Data data : dataMap.values()) {
82            sortedData.put(data.name, data);
83        }
84
85        for (Data data : sortedData.values()) {
86            data.writeAnnotation(out);
87        }
88    }
89
90    public String toHuman() {
91        StringBuilder sb = new StringBuilder();
92
93        sb.append("Statistics:\n");
94
95        TreeMap<String, Data> sortedData = new TreeMap<String, Data>();
96
97        for (Data data : dataMap.values()) {
98            sortedData.put(data.name, data);
99        }
100
101        for (Data data : sortedData.values()) {
102            sb.append(data.toHuman());
103        }
104
105        return sb.toString();
106    }
107
108    /**
109     * Statistical data about a particular class.
110     */
111    private static class Data {
112        /** {@code non-null;} name to use as a label */
113        private final String name;
114
115        /** {@code >= 0;} number of instances */
116        private int count;
117
118        /** {@code >= 0;} total size of instances in bytes */
119        private int totalSize;
120
121        /** {@code >= 0;} largest size of any individual item */
122        private int largestSize;
123
124        /** {@code >= 0;} smallest size of any individual item */
125        private int smallestSize;
126
127        /**
128         * Constructs an instance for the given item.
129         *
130         * @param item {@code non-null;} item in question
131         * @param name {@code non-null;} type name to use
132         */
133        public Data(Item item, String name) {
134            int size = item.writeSize();
135
136            this.name = name;
137            this.count = 1;
138            this.totalSize = size;
139            this.largestSize = size;
140            this.smallestSize = size;
141        }
142
143        /**
144         * Incorporates a new item. This assumes the type name matches.
145         *
146         * @param item {@code non-null;} item to incorporate
147         */
148        public void add(Item item) {
149            int size = item.writeSize();
150
151            count++;
152            totalSize += size;
153
154            if (size > largestSize) {
155                largestSize = size;
156            }
157
158            if (size < smallestSize) {
159                smallestSize = size;
160            }
161        }
162
163        /**
164         * Writes this instance as an annotation.
165         *
166         * @param out {@code non-null;} where to write to
167         */
168        public void writeAnnotation(AnnotatedOutput out) {
169            out.annotate(toHuman());
170        }
171
172        /**
173         * Generates a human-readable string for this data item.
174         *
175         * @return string for human consumption.
176         */
177        public String toHuman() {
178            StringBuilder sb = new StringBuilder();
179
180            sb.append("  " + name + ": " +
181                         count + " item" + (count == 1 ? "" : "s") + "; " +
182                         totalSize + " bytes total\n");
183
184            if (smallestSize == largestSize) {
185                sb.append("    " + smallestSize + " bytes/item\n");
186            } else {
187                int average = totalSize / count;
188                sb.append("    " + smallestSize + ".." + largestSize +
189                             " bytes/item; average " + average + "\n");
190            }
191
192            return sb.toString();
193        }
194    }
195}
196