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: package org.hammurapi;
024:
025: import java.text.MessageFormat;
026: import java.util.Collection;
027: import java.util.Date;
028: import java.util.HashMap;
029: import java.util.Iterator;
030: import java.util.Map;
031:
032: import org.apache.tools.ant.Project;
033: import org.hammurapi.results.CompositeResults;
034: import org.hammurapi.results.ResultsFactory;
035: import org.hammurapi.results.ReviewResults;
036:
037: import com.pavelvlasov.jsel.CompilationUnit;
038: import com.pavelvlasov.jsel.Package;
039: import com.pavelvlasov.jsel.Repository;
040: import com.pavelvlasov.util.DispatcherAware;
041: import com.pavelvlasov.util.DispatchingVisitor;
042: import com.pavelvlasov.util.OrderedTarget;
043: import com.pavelvlasov.util.DispatchingVisitor.Stats;
044:
045: public class ResultsCollector implements DispatcherAware, OrderedTarget {
046: private final HammurapiTask task;
047: private InspectorSet inspectorSet;
048: private Collection listeners;
049: private Map packageResults = new HashMap();
050: private CompositeResults summary;
051: private long start;
052: private DispatchingVisitor dispatcher;
053: private WaiverSet waiverSet;
054:
055: ResultsCollector(HammurapiTask task, InspectorSet inspectorSet,
056: WaiverSet waiverSet, CompositeResults summary,
057: Collection listeners) {
058: this .inspectorSet = inspectorSet;
059: this .waiverSet = waiverSet;
060: this .task = task;
061: this .listeners = listeners;
062: this .summary = summary;
063: }
064:
065: /**
066: * @ant:ignore
067: * @param result
068: */
069: CompositeResults getPackageResult(String packageName) {
070: synchronized (packageResults) {
071: CompositeResults ret = (CompositeResults) packageResults
072: .get(packageName);
073: if (ret == null) {
074: ret = ResultsFactory.getInstance().newCompositeResults(
075: packageName);
076: packageResults.put(packageName, ret);
077: }
078: return ret;
079: }
080: }
081:
082: // Second to visit, second from the end to leave
083: private Integer order = new Integer(Integer.MIN_VALUE + 1);
084:
085: public Integer getOrder() {
086: return order;
087: }
088:
089: public boolean visit(CompilationUnit compilationUnit) {
090: ReviewResults result = task.isForce() ? null : ResultsFactory
091: .getInstance().findReviewResults(compilationUnit);
092: if (result != null) {
093: if (task.isForceOnWarnings() && result.hasWarnings()) {
094: result = null;
095: } else if (task.isForceOnWaivers()) {
096: // Checking if there are violations to be waived
097: Iterator it = result.getViolations().iterator();
098: while (it.hasNext()) {
099: Violation violation = (Violation) it.next();
100: if (waiverSet.requestWaiver(violation, true) != null) {
101: result = null;
102: break;
103: }
104: }
105: }
106: }
107:
108: if (result == null) {
109: ResultsFactory.getThreadResults().addMetric(null,
110: "Change ratio", 1);
111:
112: log(compilationUnit.getRelativeName() + " - reviewing");
113: result = ResultsFactory.getInstance().newReviewResults(
114: compilationUnit);
115: ResultsFactory.pushThreadResults(result);
116:
117: if (dispatcher != null) {
118: dispatcher.getThreadStats().reset();
119: }
120: return true;
121: }
122:
123: ResultsFactory.getThreadResults().addMetric(null,
124: "Change ratio", 0);
125:
126: log(compilationUnit.getRelativeName() + " - skipped");
127: synchronized (packageResults) {
128: getPackageResult(compilationUnit.getPackage().getName())
129: .add(result);
130: }
131:
132: return false;
133: }
134:
135: private static MessageFormat messageFormat = new MessageFormat(
136: "{0,date, yyyy/MM/dd HH:mm:ss} Progress: {1,number,integer}% ({2}/{3}). "
137: + "Elapsed time: {4} min. {5} sec. Remaining time: {6} min. {7} sec. ");
138:
139: public void leave(final CompilationUnit compilationUnit)
140: throws HammurapiException {
141: final ReviewResults results = (ReviewResults) ResultsFactory
142: .popThreadResults();
143:
144: Stats threadStats = dispatcher == null ? null : dispatcher
145: .getThreadStats();
146:
147: final long visits = threadStats == null ? 0 : threadStats
148: .getVisits();
149: final long invocations = threadStats == null ? 0 : threadStats
150: .getInvocations();
151:
152: final CompositeResults packageResult = getPackageResult(compilationUnit
153: .getPackage().getName());
154: ResultsFactory.getInstance().execute(new ResultsFactory.Task() {
155:
156: public void execute() throws HammurapiException {
157: results.commit();
158: Iterator it = listeners.iterator();
159: while (it.hasNext()) {
160: if (dispatcher != null) {
161: results.setCodeBase(visits);
162: results.setReviewsNumber(invocations);
163: }
164: ((Listener) it.next()).onReview(results);
165: }
166:
167: packageResult.add(results);
168: }
169: });
170:
171: ++counter;
172: double progress = (double) counter / (double) repoSize;
173: int percentage = (int) (100 * progress);
174: long now = System.currentTimeMillis();
175:
176: long elapsedSec = (now - start) / 1000;
177: long min = elapsedSec / 60;
178: long sec = elapsedSec % 60;
179:
180: long remainingSec = counter == 0 ? 0 : (long) (elapsedSec
181: * (1 - progress) / progress);
182: long rmin = remainingSec / 60;
183: long rsec = remainingSec % 60;
184:
185: task.log(messageFormat.format(
186: new Object[] { new Date(now), new Integer(percentage),
187: new Integer(counter), new Integer(repoSize),
188: new Long(min), new Long(sec), new Long(rmin),
189: new Long(rsec) }, new StringBuffer(), null)
190: .toString(), Project.MSG_INFO);
191: }
192:
193: public void visit(Package pkg) {
194: ResultsFactory
195: .pushThreadResults(getPackageResult(pkg.getName()));
196: }
197:
198: public void leave(Package pkg) throws HammurapiException {
199: final CompositeResults packageResult = (CompositeResults) ResultsFactory
200: .popThreadResults();
201: packageResult.commit();
202:
203: ResultsFactory.getInstance().execute(new ResultsFactory.Task() {
204:
205: public void execute() throws HammurapiException {
206: if (!task.skipIntactPackages || packageResult.isNew()) {
207: Iterator it = listeners.iterator();
208: while (it.hasNext()) {
209: ((Listener) it.next()).onPackage(packageResult);
210: }
211:
212: synchronized (summary) {
213: summary.add(packageResult);
214: }
215: }
216: }
217:
218: });
219: }
220:
221: private int counter;
222: private int repoSize;
223:
224: public void visit(Repository repository) {
225: start = System.currentTimeMillis();
226: //ResultsFactory.pushThreadResults(summary);
227:
228: repoSize = repository.size();
229: task.log("Review started at " + new Date(start) + ", "
230: + repoSize + " files to review", Project.MSG_INFO);
231: }
232:
233: public void leave(Repository repository) throws HammurapiException {
234: log(new Date() + " Completing results collection ...");
235:
236: ResultsFactory.getInstance().join();
237:
238: log(new Date() + " Building summary");
239:
240: if (!task.skipIntactPackages || summary.isNew()) {
241: Iterator it = listeners.iterator();
242: while (it.hasNext()) {
243: ((Listener) it.next()).onSummary(summary, inspectorSet);
244: }
245: }
246:
247: Iterator it = task.getReviewAcceptorEntries().iterator();
248: while (it.hasNext()) {
249: ((ReviewAcceptor) ((ReviewAcceptorEntry) it.next())
250: .getObject(null)).accept(summary);
251: }
252:
253: long finish = System.currentTimeMillis();
254:
255: long elapsedSec = (finish - start) / 1000;
256: long min = elapsedSec / 60;
257: long sec = elapsedSec % 60;
258:
259: log("Time: " + min + " min. " + sec + " sec.");
260: log(MessageFormat.format("Performance {0, number,###.000000}",
261: new Object[] { new Double((double) summary
262: .getCodeBase()
263: * 1000 / (finish - start)) }));
264:
265: Integer severityThreshold = task.getSeverityThreshold();
266: if (severityThreshold != null) {
267: final int sth = this .task.getSeverityThreshold().intValue();
268: new ReviewAcceptor() {
269: public void accept(CompositeResults summary)
270: throws HammurapiException {
271: Number severity = summary.getMaxSeverity();
272: if (severity != null && severity.intValue() <= sth) {
273: throw new HammurapiNonConsumableException(
274: "Severity threshold (" + sth
275: + ") infringed");
276: }
277: }
278: }.accept(summary);
279: }
280:
281: Double sigmaThreshold = task.getSigmaThreshold();
282: if (sigmaThreshold != null) {
283: final double cth = sigmaThreshold.doubleValue();
284: new ReviewAcceptor() {
285: public void accept(CompositeResults summary)
286: throws HammurapiException {
287: try {
288: if (Double.parseDouble(summary.getSigma()) < cth) {
289: throw new HammurapiNonConsumableException(
290: "Sigma is below threshold (" + cth
291: + ")");
292: }
293: } catch (NumberFormatException e) {
294: throw new HammurapiNonConsumableException(
295: "Sigma is not valid");
296: }
297: }
298: }.accept(summary);
299: }
300:
301: Integer dpmoThreshold = task.getDpmoThreshold();
302: if (dpmoThreshold != null) {
303: final int cth = dpmoThreshold.intValue();
304: new ReviewAcceptor() {
305: public void accept(CompositeResults summary)
306: throws HammurapiException {
307: try {
308: if (Integer.parseInt(summary.getDPMO()) > cth) {
309: throw new HammurapiNonConsumableException(
310: "DPMO is above threshold (" + cth
311: + ")");
312: }
313: } catch (NumberFormatException e) {
314: throw new HammurapiNonConsumableException(
315: "DPMO is not valid");
316: }
317: }
318: }.accept(summary);
319: }
320:
321: if (this .task.isFailOnWarnings()
322: && !summary.getWarnings().isEmpty()) {
323: throw new HammurapiNonConsumableException(
324: "There have been warnings during execution.");
325: }
326:
327: ResultsFactory.popThreadResults();
328: summary.commit();
329: }
330:
331: /**
332: * @param string
333: */
334: private void log(String message) {
335: task.log(message);
336: }
337:
338: public void setDispatcher(DispatchingVisitor dispatcher) {
339: this .dispatcher = dispatcher;
340: }
341:
342: /**
343: * @return Returns the summary.
344: */
345: CompositeResults getSummary() {
346: return summary;
347: }
348:
349: }
|