001: /*
002: * @(#)CoverageReportTask.java
003: *
004: * Copyright (C) 2002-2003 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a
009: * copy of this software and associated documentation files (the "Software"),
010: * to deal in the Software without restriction, including without limitation
011: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
012: * and/or sell copies of the Software, and to permit persons to whom the
013: * Software is furnished to do so, subject to the following conditions:
014: *
015: * The above copyright notice and this permission notice shall be included in
016: * all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
021: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
024: * DEALINGS IN THE SOFTWARE.
025: */
026:
027: package net.sourceforge.groboutils.codecoverage.v2.ant;
028:
029: import java.io.File;
030: import java.io.FileOutputStream;
031: import java.io.IOException;
032: import java.io.OutputStreamWriter;
033: import java.util.Enumeration;
034: import java.util.Vector;
035:
036: import net.sourceforge.groboutils.codecoverage.v2.IAnalysisModule;
037: import net.sourceforge.groboutils.codecoverage.v2.IChannelLogReader;
038: import net.sourceforge.groboutils.codecoverage.v2.datastore.AnalysisModuleSet;
039: import net.sourceforge.groboutils.codecoverage.v2.datastore.DirMetaDataReader;
040: import net.sourceforge.groboutils.codecoverage.v2.datastore.IMetaDataReader;
041: import net.sourceforge.groboutils.codecoverage.v2.logger.DirectoryChannelLogReader;
042: import net.sourceforge.groboutils.codecoverage.v2.report.AnalysisModuleData;
043: import net.sourceforge.groboutils.codecoverage.v2.report.IReportGenerator;
044: import net.sourceforge.groboutils.codecoverage.v2.report.OutputXml;
045: import net.sourceforge.groboutils.codecoverage.v2.report.XmlCombinedReportGenerator;
046: import net.sourceforge.groboutils.codecoverage.v2.report.XmlReportGenerator;
047:
048: import org.apache.tools.ant.BuildException;
049: import org.apache.tools.ant.Project;
050: import org.apache.tools.ant.Task;
051: import org.w3c.dom.Document;
052: import org.w3c.dom.Element;
053:
054: /**
055: * Ant task that creates an XML coverage report.
056: *
057: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
058: * @version $Date: 2004/04/17 08:24:39 $
059: * @since December 18, 2002
060: * @deprecated Use GroboReportTask instead of this one.
061: */
062: public class CoverageReportTask extends Task {
063: private File datadir = null;
064: private File logdir = null;
065: private File outdir = null;
066: private String prefix = "CoverageReport-";
067: private String postfix = ".xml";
068: private boolean failonerror = false;
069: private Vector singleReports = new Vector();
070: private Vector comboReports = new Vector();
071:
072: public void setDataDir(File f) {
073: this .datadir = f;
074: }
075:
076: public void setLogDir(File f) {
077: this .logdir = f;
078: }
079:
080: public void setOutDir(File f) {
081: this .outdir = f;
082: }
083:
084: public void setReportFilePrefix(String s) {
085: this .prefix = s;
086: }
087:
088: public void setReportFileExtension(String s) {
089: this .postfix = s;
090: }
091:
092: public void setFailOnError(boolean val) {
093: this .failonerror = val;
094: }
095:
096: public SimpleHtmlReportStyle createSimpleStyle() {
097: SimpleHtmlReportStyle style = new SimpleHtmlReportStyle();
098: this .singleReports.addElement(style);
099: return style;
100: }
101:
102: public SourceHtmlReportStyle createSourceStyle() {
103: SourceHtmlReportStyle style = new SourceHtmlReportStyle();
104: this .comboReports.addElement(style);
105: return style;
106: }
107:
108: public void execute() throws BuildException {
109: log(
110: "Use of the CoverageReport task is deprecated; please "
111: + "use the 'grobo-report' task instead. You may need to "
112: + "change the taskdef to reference the new resource file, located "
113: + "at [ant-grobocoverage.properties], instead of "
114: + "[net/sourceforge/groboutils/codecoverage/grobocoverage.properties"
115: + "].", Project.MSG_WARN);
116:
117: // check specifications
118: if (this .datadir == null) {
119: throw new BuildException(
120: "Did not specify attribute 'datadir'.");
121: }
122: if (this .logdir == null) {
123: throw new BuildException(
124: "Did not specify attribute 'logdir'.");
125: }
126: if (this .outdir == null) {
127: throw new BuildException(
128: "Did not specify attribute 'outdir'.");
129: }
130: if (this .prefix == null || this .postfix == null) {
131: throw new BuildException(
132: "Output report name format is null.");
133: }
134:
135: try {
136: setupDirectories();
137: } catch (IOException ioe) {
138: throw new BuildException(
139: "Error setting up the directories.", ioe);
140: }
141:
142: IMetaDataReader mdr = createMetaDataReader();
143: IReportGenerator rg = new XmlReportGenerator();
144: boolean errors = false;
145: try {
146: Vector reports = new Vector();
147: AnalysisModuleSet ams = mdr.getAnalysisModuleSet();
148: IAnalysisModule amL[] = ams.getAnalysisModules();
149: for (int i = 0; i < amL.length; ++i) {
150: try {
151: IChannelLogReader clr = createChannelLogReader(
152: amL[i], ams);
153: AnalysisModuleData amd = new AnalysisModuleData(
154: amL[i], mdr, clr);
155: Element rootEl = createReport(amL[i], amd, rg);
156: if (rootEl == null) {
157: errors = true;
158: } else {
159: Document doc = rootEl.getOwnerDocument();
160: reports.addElement(doc);
161: processStyles(doc, amL[i].getMeasureName());
162: }
163: } catch (IllegalArgumentException iae) {
164: iae.printStackTrace();
165: log(iae.getMessage(), Project.MSG_WARN);
166: errors = true;
167: }
168: }
169: finishStyles(reports);
170: } catch (IOException e) {
171: throw new BuildException(
172: "I/O Exception while creating a report.", e,
173: getLocation());
174: } finally {
175: try {
176: mdr.close();
177: } catch (IOException e) {
178: throw new BuildException(
179: "I/O Exception while closing meta-data reader.",
180: e, getLocation());
181: }
182: }
183:
184: if (errors && this .failonerror) {
185: throw new BuildException(
186: "No coverage logs were generated, or the logs "
187: + "are not located under '" + this .logdir
188: + "'.", getLocation());
189: }
190: }
191:
192: /**
193: * setup the directories in the logdir - ensure we have adequate
194: * setup protections. This saves some head banging in figuring out
195: * why bad exceptions are thrown.
196: */
197: private void setupDirectories() throws IOException, BuildException {
198: if (!this .datadir.exists() || !this .datadir.isDirectory()) {
199: throw new BuildException("Data directory setting ("
200: + this .datadir
201: + ") does not exist or is not a directory.");
202: }
203: if (!this .logdir.exists()) {
204: this .logdir.mkdirs();
205: }
206: if (!this .logdir.isDirectory()) {
207: throw new BuildException("Log directory setting ("
208: + this .logdir + ") is not a directory.");
209: }
210: String modules[] = this .datadir.list();
211: if (modules == null || modules.length <= 0) {
212: throw new BuildException(
213: "There are no module data directories in "
214: + this .datadir + ".");
215: }
216:
217: String indicies[] = this .logdir.list();
218: if (indicies == null) {
219: indicies = new String[0];
220: }
221: int count = modules.length;
222:
223: for (int i = 0; i <= count; ++i) {
224: String dirname = Integer.toString(i);
225: boolean found = false;
226: for (int j = 0; j < indicies.length; ++j) {
227: if (indicies[j].equals(dirname)) {
228: found = true;
229: break;
230: }
231: }
232: if (!found) {
233: File f = new File(this .logdir, dirname);
234: f.mkdirs();
235: }
236: }
237: }
238:
239: private Element createReport(IAnalysisModule am,
240: AnalysisModuleData amd, IReportGenerator rg)
241: throws IOException, BuildException {
242: log("Creating coverage report for module "
243: + am.getMeasureName(), Project.MSG_INFO);
244: File outfile = createReportFile(am);
245: FileOutputStream fos = new FileOutputStream(outfile);
246: Element ret = null;
247: try {
248: ret = rg.createReport(am, amd);
249: OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8");
250: (new OutputXml()).write(ret, osw, null);
251: } catch (IllegalArgumentException iae) {
252: // throw new BuildException( iae );
253:
254: // this is a hack: really, the underlying code should be made
255: // more robust.
256: iae.printStackTrace();
257: log(iae.getMessage(), Project.MSG_WARN);
258: ret = null;
259: } finally {
260: fos.close();
261: }
262:
263: return ret;
264: }
265:
266: private File createReportFile(IAnalysisModule am) {
267: File f = new File(this .outdir, this .prefix
268: + am.getMeasureName() + this .postfix);
269: log("Creating report file '" + f + "' for measure '"
270: + am.getMeasureName() + "'.", Project.MSG_VERBOSE);
271: return f;
272: }
273:
274: private IChannelLogReader createChannelLogReader(
275: IAnalysisModule am, AnalysisModuleSet ams)
276: throws IOException {
277: short mi = ams.getAnalysisModuleIndex(am);
278: IChannelLogReader clr = new DirectoryChannelLogReader(
279: this .logdir, mi);
280: return clr;
281: }
282:
283: private IMetaDataReader createMetaDataReader()
284: throws BuildException {
285: try {
286: return new DirMetaDataReader(this .datadir);
287: } catch (IOException e) {
288: throw new BuildException(
289: "I/O error creating meta-data reader.", e,
290: getLocation());
291: }
292: }
293:
294: private void processStyles(Document doc, String moduleName)
295: throws BuildException, IOException {
296: Enumeration e = singleReports.elements();
297: while (e.hasMoreElements()) {
298: IReportStyle rs = (IReportStyle) e.nextElement();
299: rs.generateReport(getProject(), doc, moduleName);
300: }
301: }
302:
303: private void finishStyles(Vector reports) throws BuildException,
304: IOException {
305: Enumeration e = singleReports.elements();
306: while (e.hasMoreElements()) {
307: IReportStyle rs = (IReportStyle) e.nextElement();
308: rs.reportComplete(getProject(), new Vector());
309: }
310: e = null;
311:
312: // Create the uber document
313: Document docs[] = new Document[reports.size()];
314: reports.copyInto(docs);
315: XmlCombinedReportGenerator gen = new XmlCombinedReportGenerator();
316: log("Creating combined coverage report", Project.MSG_INFO);
317: File outfile = new File(this .outdir, this .prefix + "all"
318: + this .postfix);
319: log("Creating report file '" + outfile + "' for all measures.",
320: Project.MSG_VERBOSE);
321: FileOutputStream fos = new FileOutputStream(outfile);
322: Element ret = null;
323: try {
324: ret = gen.createReport(docs);
325: OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8");
326: (new OutputXml()).write(ret, osw, null);
327: } catch (IllegalArgumentException iae) {
328: // throw new BuildException( iae );
329:
330: // this is a hack: really, the underlying code should be made
331: // more robust.
332: iae.printStackTrace();
333: log(iae.getMessage(), Project.MSG_WARN);
334: ret = null;
335: } finally {
336: fos.close();
337: }
338: docs = null;
339: reports.removeAllElements();
340: reports = null;
341:
342: // process all the combination report styles
343: if (ret != null) {
344: Document doc = ret.getOwnerDocument();
345: e = comboReports.elements();
346: while (e.hasMoreElements()) {
347: IReportStyle rs = (IReportStyle) e.nextElement();
348: rs.generateReport(getProject(), doc, "all");
349: // rs.reportComplete( getProject(), new Vector() );
350: }
351: }
352: e = comboReports.elements();
353: while (e.hasMoreElements()) {
354: IReportStyle rs = (IReportStyle) e.nextElement();
355: rs.reportComplete(getProject(), new Vector());
356: }
357: }
358: }
|