001: /*
002: * Hammurapi
003: * Automated Java code review system.
004: * Copyright (C) 2004 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.org
021: * e-Mail: support@hammurapi.biz
022: */
023:
024: package org.hammurapi.results.simple;
025:
026: import java.io.Serializable;
027: import java.text.MessageFormat;
028: import java.util.ArrayList;
029: import java.util.Collection;
030: import java.util.Collections;
031: import java.util.Date;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Map;
036: import java.util.Set;
037: import java.util.TreeMap;
038: import java.util.TreeSet;
039:
040: import org.hammurapi.HammurapiException;
041: import org.hammurapi.Violation;
042: import org.hammurapi.Waiver;
043: import org.hammurapi.WaiverSet;
044: import org.hammurapi.results.AggregatedResults;
045: import org.hammurapi.results.Annotation;
046: import org.hammurapi.results.BasicResults;
047: import org.hammurapi.results.InspectorSummary;
048:
049: import com.pavelvlasov.metrics.Metric;
050: import com.pavelvlasov.metrics.SimpleMetric;
051: import com.pavelvlasov.metrics.Metric.Measurement;
052: import com.pavelvlasov.review.SourceMarker;
053:
054: /**
055: *
056: * @author Pavel Vlasov
057: * @version $Revision: 1.10 $
058: */
059: public class SimpleAggregatedResults implements AggregatedResults,
060: Serializable {
061: /**
062: * Comment for <code>serialVersionUID</code>
063: */
064: private static final long serialVersionUID = -3469850853510240052L;
065: protected long codeBase = 0;
066: protected long reviews = 0;
067: protected double violationLevel = 0;
068: private int violationsNumber = 0;
069: private int waivedViolationsNumber = 0;
070: private Map metrics = new HashMap();
071: private Set warnings = new TreeSet();
072:
073: public SimpleAggregatedResults(WaiverSet waiverSet) {
074: getContext().waiverSet = waiverSet;
075: }
076:
077: /**
078: * Map( severity (Integer) -> Map(string -> InspectorSummary))
079: *
080: */
081: protected Map severitySummary = new TreeMap();
082:
083: /**
084: * Add violation to severity summary
085: * @param violation
086: */
087: public Waiver addViolation(Violation violation)
088: throws HammurapiException {
089: Context context = getContext();
090: Waiver ret = context.waiverSet == null ? null
091: : context.waiverSet.requestWaiver(violation, false);
092: if (ret == null) {
093: Integer severity = violation.getDescriptor().getSeverity();
094: Integer s = severity;
095: Map rsm = (Map) severitySummary.get(s);
096: if (rsm == null) {
097: rsm = new TreeMap();
098: severitySummary.put(s, rsm);
099: }
100:
101: SimpleInspectorSummary rsEntry = (SimpleInspectorSummary) rsm
102: .get(violation.getDescriptor().getName());
103: if (rsEntry == null) {
104: rsEntry = new SimpleInspectorSummary(violation
105: .getDescriptor());
106: rsm.put(violation.getDescriptor().getName(), rsEntry);
107:
108: }
109: rsEntry.addLocation(violation.getSource());
110:
111: /**
112: * Violations with severity >=5 considered as hints.
113: */
114: if (severity.intValue() > 0 && severity.intValue() < 5) {
115: violationLevel += Math.pow(10, 1 - severity
116: .doubleValue());
117: }
118: } else {
119: waivedViolationsNumber++;
120: }
121:
122: return ret;
123: }
124:
125: public Map getSeveritySummary() {
126: return Collections.unmodifiableMap(severitySummary);
127: }
128:
129: public Number getMaxSeverity() {
130: Integer ret = null;
131: Iterator it = severitySummary.keySet().iterator();
132: while (it.hasNext()) {
133: Integer severity = (Integer) it.next();
134: if (ret == null || severity.intValue() < ret.intValue()) {
135: ret = severity;
136: }
137: }
138: return ret;
139: }
140:
141: public Collection getWarnings() {
142: return Collections.unmodifiableCollection(warnings);
143: }
144:
145: public void addWarning(Violation warning) {
146: warnings.add(warning);
147: }
148:
149: public void addMetric(SourceMarker source, String name, double value) {
150: class SourceAwareMetric extends SimpleMetric {
151: SourceAwareMetric(String name) {
152: super (name);
153: }
154:
155: SourceAwareMetric(String name, boolean keepMeasurements) {
156: super (name, keepMeasurements);
157: }
158:
159: void add(final SourceMarker source, final double value,
160: final long time) {
161: addMeasurement(new Measurement() {
162: public double getValue() {
163: return value;
164: }
165:
166: public long getTime() {
167: return time;
168: }
169: });
170: }
171: }
172:
173: SourceAwareMetric metric = (SourceAwareMetric) metrics
174: .get(name);
175: if (metric == null) {
176: metric = new SourceAwareMetric(name);
177: metrics.put(name, metric);
178: }
179: metric.add(source, value, 0);
180: }
181:
182: public double getViolationLevel() {
183: return violationLevel;
184: }
185:
186: public long getReviewsNumber() {
187: return reviews;
188: }
189:
190: public long getCodeBase() {
191: return codeBase;
192: }
193:
194: public int getViolationsNumber() {
195: return violationsNumber;
196: }
197:
198: // public double getPerfectionAffinityIndex() {
199: // return 1-violationLevel/reviews;
200: // }
201: //
202: public Map getMetrics() {
203: return Collections.unmodifiableMap(metrics);
204: }
205:
206: // public boolean isIncomplete() {
207: // return isIncomplete;
208: // }
209:
210: public void aggregate(AggregatedResults agregee) {
211: codeBase += agregee.getCodeBase();
212: reviews += agregee.getReviewsNumber();
213: violationsNumber += agregee.getViolationsNumber();
214: waivedViolationsNumber += agregee.getWaivedViolationsNumber();
215: violationLevel += agregee.getViolationLevel();
216: warnings.addAll(agregee.getWarnings());
217:
218: Iterator it = agregee.getSeveritySummary().entrySet()
219: .iterator();
220: while (it.hasNext()) {
221: Map.Entry entry = (Map.Entry) it.next();
222: Map ss = (Map) severitySummary.get(entry.getKey());
223: if (ss == null) {
224: ss = new TreeMap();
225: severitySummary.put(entry.getKey(), ss);
226: }
227:
228: Iterator rsit = ((Map) entry.getValue()).values()
229: .iterator();
230: while (rsit.hasNext()) {
231: InspectorSummary rse = (InspectorSummary) rsit.next();
232: SimpleInspectorSummary masterRSE = (SimpleInspectorSummary) ss
233: .get(rse.getName());
234: if (masterRSE == null) {
235: masterRSE = new SimpleInspectorSummary(rse);
236: ss.put(rse.getName(), masterRSE);
237: } else {
238: masterRSE.addLocations(rse.getLocations());
239: }
240: }
241: }
242:
243: it = agregee.getMetrics().entrySet().iterator();
244: while (it.hasNext()) {
245: Map.Entry entry = (Map.Entry) it.next();
246: Metric metric = (Metric) metrics.get(entry.getKey());
247: if (metric == null) {
248: metric = new SimpleMetric((String) entry.getKey());
249: metrics.put(entry.getKey(), metric);
250: }
251: metric.add((SimpleMetric) entry.getValue());
252: }
253: }
254:
255: /**
256: * Sets number of reviews.
257: * @param reviews The reviews to set.
258: */
259: public void setReviewsNumber(long reviews) {
260: this .reviews = reviews;
261: }
262:
263: public void setCodeBase(long codeBase) {
264: this .codeBase = codeBase;
265: }
266:
267: private static class Context {
268: private List annotations = new ArrayList();
269: private WaiverSet waiverSet;
270: }
271:
272: private static ThreadLocal contextMap = new ThreadLocal() {
273: protected Object initialValue() {
274: return new HashMap();
275: }
276: };
277:
278: private static int counter;
279:
280: private Integer id;
281:
282: {
283: synchronized (this .getClass()) {
284: id = new Integer(counter++);
285: }
286: }
287:
288: private Context getContext() {
289: Map map = (Map) contextMap.get();
290: Context ret = (Context) map.get(id);
291: if (ret == null) {
292: ret = new Context();
293: map.put(id, ret);
294: }
295: return ret;
296: }
297:
298: private Date date = new Date();
299:
300: public void addAnnotation(Annotation annotation) {
301: getContext().annotations.add(annotation);
302: }
303:
304: public Collection getAnnotations() {
305: return Collections
306: .unmodifiableCollection(getContext().annotations);
307: }
308:
309: public Date getDate() {
310: return date = new Date();
311: }
312:
313: public int getWaivedViolationsNumber() {
314: return waivedViolationsNumber;
315: }
316:
317: public String getDPMO() {
318: if (reviews == 0) {
319: return "Not available, no reviews";
320: } else {
321: return String
322: .valueOf((int) (1000000 * violationLevel / reviews))
323: + (warnings.isEmpty() ? ""
324: : " (not accurate because of warnings)");
325: }
326: }
327:
328: public String getSigma() {
329: double p = 1.0 - violationLevel / reviews;
330: if (reviews == 0) {
331: return "No results";
332: } else if (p <= 0) {
333: return "Full incompliance";
334: } else if (p >= 1) {
335: return "Full compliance";
336: } else {
337: return MessageFormat.format("{0,number,#.###}",
338: new Object[] { new Double(normsinv(p) + 1.5) })
339: + (warnings.isEmpty() ? ""
340: : " (not accurate because of warnings)");
341: }
342: }
343:
344: public static double normsinv(double probability) {
345: // Coefficients in rational approximations
346: double[] a = { -3.969683028665376e+01, 2.209460984245205e+02,
347: -2.759285104469687e+02, 1.383577518672690e+02,
348: -3.066479806614716e+01, 2.506628277459239e+00 };
349:
350: double[] b = { -5.447609879822406e+01, 1.615858368580409e+02,
351: -1.556989798598866e+02, 6.680131188771972e+01,
352: -1.328068155288572e+01 };
353:
354: double[] c = { -7.784894002430293e-03, -3.223964580411365e-01,
355: -2.400758277161838e+00, -2.549732539343734e+00,
356: 4.374664141464968e+00, 2.938163982698783e+00 };
357:
358: double[] d = { 7.784695709041462e-03, 3.224671290700398e-01,
359: 2.445134137142996e+00, 3.754408661907416e+00 };
360:
361: // Define break-points.
362: double plow = 0.02425;
363: double phigh = 1 - plow;
364:
365: // Rational approximation for lower region:
366: if (probability < plow) {
367: double q = Math.sqrt(-2 * Math.log(probability));
368: return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4])
369: * q + c[5])
370: / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
371: }
372:
373: // Rational approximation for upper region:
374: if (phigh < probability) {
375: double q = Math.sqrt(-2 * Math.log(1 - probability));
376: return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4])
377: * q + c[5])
378: / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
379: }
380:
381: // Rational approximation for central region:
382: double q = probability - 0.5;
383: double r = q * q;
384: return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4])
385: * r + a[5])
386: * q
387: / (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4])
388: * r + 1);
389: }
390:
391: public boolean hasWarnings() {
392: return !warnings.isEmpty();
393: }
394:
395: public WaiverSet getWaiverSet() {
396: return getContext().waiverSet;
397: }
398:
399: public void commit() {
400: // Does nothing because Simple results are not persistent.
401: }
402:
403: public boolean isNew() {
404: return true;
405: }
406:
407: public BasicResults getBaseLine() {
408: return null;
409: }
410: }
|