Source code: com/further/jaudit/http/StatisticsBean.java
1 /*
2 * StatisticsBean.java
3 * Copyright (c) 2001, Kristopher Wehner
4 * Created on September 23, 2001, 7:48 PM
5 */
6
7 package com.further.jaudit.http;
8
9 import java.util.Date;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Set;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.TreeMap;
17
18 import java.io.IOException;
19
20 import java.text.NumberFormat;
21
22 import org.apache.log4j.Category;
23
24 import com.further.jaudit.SourceDirectory;
25 import com.further.jaudit.SourceFile;
26 import com.further.jaudit.AuditRecord;
27 import com.further.jaudit.SourceMetric;
28 import com.further.jaudit.SystemConfiguration;
29 import com.further.jaudit.ValueGettingVisitor;
30
31 /**
32 * The statistics bean is responsible for iterating the entire source base
33 * and compiling summary statistics about the source files. These stats include:
34 * <ul>
35 * <li>Number of files
36 * <li>Percent of files that have been audited
37 * <li>Percent of files that are compliant (passing audit)
38 * <li>Minimum audit age
39 * <li>Maximum audit age
40 * <li>For each metric, what percent of files have each value.
41 * </ul>
42 *
43 * @author Kris Wehner <kris@further.com>
44 * @version $Id: StatisticsBean.java,v 1.1.1.1 2001/10/11 16:42:10 krisw Exp $
45 * @since 1.0
46 */
47 public class StatisticsBean {
48 /**
49 * log4j audit debugging channel
50 */
51 private Category category =
52 Category.getInstance(StatisticsBean.class.getName());
53 /**
54 * The number of files in the system
55 */
56 private int fileCount = 0;
57 /**
58 * The number of files in the system that have been audited at least once
59 */
60 private int auditCount = 0;
61 /**
62 * The number of files in the system that passed their last registered audit
63 */
64 private int compliantCount = 0;
65 /**
66 * The set of audit dates for all the files in the system, for the last
67 * registered audit.
68 */
69 private Set auditDateSet = new HashSet();
70 /**
71 * The map of metrics. Each key in this map is a string metric abbreviation,
72 * and each value is another HashMap. The keys in the target map are the
73 * metric values and the values in the map are the count of files that have
74 * that value for the metric (as an Integer).
75 */
76 private Map metricMap = new TreeMap();
77 /**
78 * The root source directory being traversed. This is independent of the
79 * current directory in the DirectoryBean
80 */
81 private SourceDirectory rootDirectory;
82 /**
83 * A map of metric abbreviations to metric full names. This is built
84 * at construction time from the list of metrics in the system, and queried
85 * to build the statistics display page.
86 */
87 private Map fullNameMap = new HashMap();
88 /**
89 * Get the number of files in the system. This is the compiled count
90 * of all the individual source files in all the directories.
91 *
92 * @return The number of files in all the directories of the system.
93 */
94 public int getFileCount() {
95 return fileCount;
96 }
97
98 /**
99 * Get the number of files in the system that have been audited. This
100 * is the files that have ever had an audit registered, rather than
101 * the files that have a current audit.
102 *
103 * @return The percent of files in the system that have been audited, from 0.0 to 100.0
104 */
105 public String getAuditPercent() {
106 double percentage =
107 (((double)auditCount / (double)fileCount) * 100.0);
108 NumberFormat numberFormat = NumberFormat.getInstance();
109 numberFormat.setMaximumFractionDigits(2);
110 numberFormat.setMinimumFractionDigits(2);
111 numberFormat.setMinimumIntegerDigits(2);
112 numberFormat.setMaximumIntegerDigits(2);
113 return numberFormat.format(percentage);
114 }
115
116 /**
117 * Get the percentage of files in the system whose last audit was compliant.
118 * That is, the number of files that do not need to be modified to
119 * bring them up to compliance.
120 *
121 * @return The percent of files in the system that have been audited and are
122 * compliant from 0.0 to 100.0
123 */
124 public String getCompliantPercent() {
125 double percentage =
126 (((double)compliantCount / (double)auditCount)) * 100.0;
127 NumberFormat numberFormat = NumberFormat.getInstance();
128 numberFormat.setMaximumFractionDigits(2);
129 numberFormat.setMinimumFractionDigits(2);
130 numberFormat.setMinimumIntegerDigits(2);
131 numberFormat.setMaximumIntegerDigits(2);
132 return numberFormat.format(percentage);
133 }
134
135 /**
136 * Get the newest audit date from the entire set of audit dates for the
137 * files in the system.
138 *
139 * @return The newest audit date of any file in the system
140 */
141 public Date getNewestAuditDate() {
142 Date auditDate = null;
143 for (Iterator i = auditDateSet.iterator(); i.hasNext(); ) {
144 Date date = (Date)i.next();
145 if ((auditDate == null) || (auditDate.getTime() < date.getTime()))
146 auditDate = date;
147 }
148 return auditDate;
149 }
150
151
152 /**
153 * Get the oldest audit date from the entire set of audit dates for the
154 * files in the system.
155 *
156 * @return The oldest audit date of any file in the system
157 */
158 public Date getOldestAuditDate() {
159 Date auditDate = new Date();
160 for (Iterator i = auditDateSet.iterator(); i.hasNext(); ) {
161 Date date = (Date)i.next();
162 if (auditDate.getTime() > date.getTime())
163 auditDate = date;
164 }
165 return auditDate;
166 }
167
168 /**
169 * Get an iterator over the defined metrics in the system. The values
170 * returned by the iterator are string keys that can be used to access
171 * the values for that metric and what percentage of source files have
172 * that metric value
173 *
174 * @return An iterator over the metric identifiers
175 */
176 public Iterator getMetricIterator() {
177 return metricMap.keySet().iterator();
178 }
179
180 /**
181 * Get an iterator over the values of a particular metric. This iterator
182 * returns string values that can be used (along with the metric id) to
183 * retreive the percentage of files that have that given metric value
184 *
185 * @param id The id of the metric to obtain an iterator over
186 * @return An iterator over the values of the metric
187 */
188 public Iterator getValuesIterator(String id) {
189 Map valuesMap = (Map)metricMap.get(id);
190 return valuesMap.keySet().iterator();
191 }
192
193 /**
194 * Get the percentage of files that have the given metric value, for the
195 * specified metric id and metric value. This returns a floating point
196 * percentage between 0.0 and 100.0
197 *
198 * @param id The id of the metric to obtain statistics for
199 * @param value The metric value to retreive the percentage for
200 * @return The floating point percentage of files that have the given
201 * metric value
202 */
203 public String getFilePercentage(String id, String value) {
204 Map valuesMap = (Map)metricMap.get(id);
205 int valueCount = ((Integer)valuesMap.get(value)).intValue();
206 double percentage =
207 (((double)valueCount / (double)fileCount) * 100.0);
208 NumberFormat numberFormat = NumberFormat.getInstance();
209 numberFormat.setMaximumFractionDigits(2);
210 numberFormat.setMinimumFractionDigits(2);
211 numberFormat.setMinimumIntegerDigits(2);
212 numberFormat.setMaximumIntegerDigits(2);
213 return numberFormat.format(percentage);
214 }
215
216 /**
217 * The recursive call to traverse the source files in a given directory.
218 * This first calls itself on all subdirectories in the directory, then
219 * iterates over all the files in the directory compiling the statistics
220 * for those files.
221 *
222 * @param sourceDirectory The source directory to traverse
223 */
224 private void traverseDirectory(SourceDirectory sourceDirectory)
225 throws IOException {
226 for (Iterator i = sourceDirectory.getDirectoryIterator(); i.hasNext(); ) {
227 String directoryName = (String)i.next();
228 SourceDirectory directory =
229 sourceDirectory.getSourceDirectory(directoryName);
230 traverseDirectory(directory);
231 }
232 for (Iterator i = sourceDirectory.getFileIterator(); i.hasNext(); ) {
233 String fileName = (String)i.next();
234 SourceFile sourceFile =
235 sourceDirectory.getSourceFile(fileName);
236 buildStatistics(sourceFile);
237 }
238 }
239
240 /**
241 * Build the statistics for an individual source file. This method was
242 * refactored out for readabilitys sake from traverseDirectory, and is
243 * only called from there.
244 *
245 * @param sourceFile The source file to build the statistics for
246 */
247 private void buildStatistics(SourceFile sourceFile) {
248 // Compile the statistics
249 fileCount++;
250 if (sourceFile.passedLastAudit())
251 compliantCount++;
252 if (sourceFile.hasBeenAudited())
253 auditCount++;
254
255 if (!sourceFile.hasBeenAudited())
256 return;
257
258 // The only audit that is relevant is the latest one. The last
259 // audit record is extracted, then the internal metrics are individually
260 // examined to update the metric tables.
261 AuditRecord auditRecord =
262 getNewestAuditRecord(sourceFile.getAuditRecords());
263 category.debug("Last audit record for " + sourceFile.getFileName() + " is " + auditRecord.getAuditDate());
264 for (Iterator j = auditRecord.getMetrics().iterator(); j.hasNext(); ) {
265 SourceMetric metric = (SourceMetric)j.next();
266 ValueGettingVisitor visitor = new ValueGettingVisitor();
267 metric.accept(visitor);
268 String metricValue = visitor.getStringValue();
269 incrementMetricCount(metric.getMetricAbbreviation(), metricValue);
270 }
271 }
272
273 /**
274 * Retreive the newest audit record from the given list of audit
275 * records. This does a simple O(n) pass over the list of audit records,
276 * assuming they are stored unorderd.
277 *
278 * @param auditRecords The list of audit records, in no particular order
279 * @return The newest audit record in the list, or null if the list is
280 * empty
281 */
282 private AuditRecord getNewestAuditRecord(List auditRecords) {
283 if (auditRecords.size() > 0) {
284 AuditRecord auditRecord = (AuditRecord)auditRecords.get(0);
285 for (Iterator j = auditRecords.iterator(); j.hasNext(); ) {
286 AuditRecord record = (AuditRecord)j.next();
287 if (record.getAuditDate().after(auditRecord.getAuditDate()))
288 auditRecord = record;
289 }
290 return auditRecord;
291 }
292 return null;
293 }
294
295 /**
296 * Increment the count of a given metric value and metric id. This method
297 * updates the metricMap to increment the integer value at the value entry
298 * if it exists, and sets it to 1 if it does not.
299 *
300 * @param id The metric id for the metric to update
301 * @param value The metric value (as a string) to increment the count for
302 */
303 private void incrementMetricCount(String id, String value) {
304 if (!metricMap.containsKey(id))
305 metricMap.put(id,new TreeMap());
306 Map valueMap = (Map)metricMap.get(id);
307 if (!valueMap.containsKey(value))
308 valueMap.put(value, new Integer(0));
309 int valueCount = ((Integer)valueMap.get(value)).intValue();
310 valueCount++;
311 category.debug("Metric value for " + id + " is " + value + ", count is " + valueCount);
312 valueMap.put(value, new Integer(valueCount));
313 }
314
315 /**
316 * Get the full name of the given metric from the metric abbreviation.
317 * This is used for displaying the statistics summary in stats.jsp
318 *
319 * @param id The metric id for the given metric
320 * @return The full name of the metric
321 */
322 public String getFullName(String id) {
323 return (String)fullNameMap.get(id);
324 }
325
326 /**
327 * The compile statistics method causes the statistics bean to traverse the
328 * the entire source tree and build the statistics for the sourcebase. This
329 * is the one driver method that must be called before querying the statistics
330 * for display
331 */
332 public void compileStatistics()
333 throws IOException {
334 rootDirectory =
335 new SourceDirectory(SystemConfiguration.getRootSourceDirectory());
336 traverseDirectory(rootDirectory);
337 }
338
339 /**
340 * Create a new StatisticsBean. This is the required empty constructor
341 * to use the bean from a jsp. Use compileStatistics()
342 * in order to prepare the statistics.
343 */
344 public StatisticsBean() {
345 List metrics =
346 SystemConfiguration.createMetrics();
347 for (Iterator i = metrics.iterator(); i.hasNext(); ) {
348 SourceMetric metric = (SourceMetric)i.next();
349 fullNameMap.put(metric.getMetricAbbreviation(), metric.getMetricName());
350 }
351 }
352
353 }