001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.tools.ant.taskdefs.optional.junit;
019:
020: import java.io.File;
021: import java.io.FileInputStream;
022: import java.io.FileNotFoundException;
023: import java.io.IOException;
024: import java.io.InputStream;
025:
026: import java.net.URL;
027:
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Vector;
031:
032: import javax.xml.parsers.DocumentBuilder;
033: import javax.xml.parsers.DocumentBuilderFactory;
034:
035: import org.apache.tools.ant.BuildException;
036: import org.apache.tools.ant.Project;
037: import org.apache.tools.ant.Task;
038: import org.apache.tools.ant.taskdefs.XSLTProcess;
039: import org.apache.tools.ant.taskdefs.Delete;
040: import org.apache.tools.ant.taskdefs.TempFile;
041: import org.apache.tools.ant.util.JAXPUtils;
042: import org.apache.tools.ant.util.FileUtils;
043: import org.apache.tools.ant.types.EnumeratedAttribute;
044: import org.apache.tools.ant.types.Resource;
045: import org.apache.tools.ant.types.resources.URLResource;
046: import org.apache.tools.ant.types.resources.FileResource;
047:
048: import org.w3c.dom.Document;
049:
050: /**
051: * Transform a JUnit xml report.
052: * The default transformation generates an html report in either framed or non-framed
053: * style. The non-framed style is convenient to have a concise report via mail, the
054: * framed report is much more convenient if you want to browse into different
055: * packages or testcases since it is a Javadoc like report.
056: *
057: */
058: public class AggregateTransformer {
059: /**
060: * name of the frames format.
061: */
062: public static final String FRAMES = "frames";
063:
064: /**
065: * name of the no frames format.
066: */
067: public static final String NOFRAMES = "noframes";
068:
069: /**
070: * defines acceptable formats.
071: */
072: public static class Format extends EnumeratedAttribute {
073: /**
074: * list authorized values.
075: * @return authorized values.
076: */
077: public String[] getValues() {
078: return new String[] { FRAMES, NOFRAMES };
079: }
080: }
081:
082: // CheckStyle:VisibilityModifier OFF - bc
083: /** Task */
084: protected Task task;
085:
086: /** the xml document to process */
087: protected Document document;
088:
089: /** the style directory. XSLs should be read from here if necessary */
090: protected File styleDir;
091:
092: /** the destination directory, this is the root from where html should be generated */
093: protected File toDir;
094:
095: /**
096: * The params that will be sent to the XSL transformation
097: *
098: * @since Ant 1.7
099: */
100: private List params;
101:
102: /**
103: * Instance of a utility class to use for file operations.
104: *
105: * @since Ant 1.7
106: */
107: private static final FileUtils FILE_UTILS = FileUtils
108: .getFileUtils();
109:
110: /**
111: * Used to ensure the uniqueness of a property
112: */
113: private static int counter = 0;
114:
115: /** the format to use for the report. Must be <tt>FRAMES</tt> or <tt>NOFRAMES</tt> */
116: protected String format = FRAMES;
117:
118: /** XML Parser factory */
119: private static DocumentBuilderFactory privateDBFactory;
120:
121: /** XML Parser factory accessible to subclasses */
122: protected static DocumentBuilderFactory dbfactory;
123:
124: static {
125: privateDBFactory = DocumentBuilderFactory.newInstance();
126: dbfactory = privateDBFactory;
127: }
128:
129: // CheckStyle:VisibilityModifier ON
130:
131: /**
132: * constructor creating the transformer from the junitreport task.
133: * @param task task delegating to this class
134: */
135: public AggregateTransformer(Task task) {
136: this .task = task;
137: params = new Vector();
138: }
139:
140: /**
141: * Get the Document Builder Factory
142: *
143: * @return the DocumentBuilderFactory instance in use
144: */
145: protected static DocumentBuilderFactory getDocumentBuilderFactory() {
146: return privateDBFactory;
147: }
148:
149: /**
150: * sets the format.
151: * @param format Must be <tt>FRAMES</tt> or <tt>NOFRAMES</tt>
152: */
153: public void setFormat(Format format) {
154: this .format = format.getValue();
155: }
156:
157: /**
158: * sets the input document.
159: * @param doc input dom tree
160: */
161: public void setXmlDocument(Document doc) {
162: this .document = doc;
163: }
164:
165: /**
166: * Set the xml file to be processed. This is a helper if you want
167: * to set the file directly. Much more for testing purposes.
168: * @param xmlfile xml file to be processed
169: * @throws BuildException if the document cannot be parsed.
170: */
171: protected void setXmlfile(File xmlfile) throws BuildException {
172: try {
173: DocumentBuilder builder = privateDBFactory
174: .newDocumentBuilder();
175: InputStream in = new FileInputStream(xmlfile);
176: try {
177: Document doc = builder.parse(in);
178: setXmlDocument(doc);
179: } finally {
180: in.close();
181: }
182: } catch (Exception e) {
183: throw new BuildException("Error while parsing document: "
184: + xmlfile, e);
185: }
186: }
187:
188: /**
189: * set the style directory. It is optional and will override the
190: * default xsl used.
191: * @param styledir the directory containing the xsl files if the user
192: * would like to override with its own style.
193: */
194: public void setStyledir(File styledir) {
195: this .styleDir = styledir;
196: }
197:
198: /** set the destination directory.
199: * @param todir the destination directory
200: */
201: public void setTodir(File todir) {
202: this .toDir = todir;
203: }
204:
205: /** set the extension of the output files
206: * @param ext extension.
207: */
208: public void setExtension(String ext) {
209: task.log("extension is not used anymore", Project.MSG_WARN);
210: }
211:
212: /**
213: * Create an instance of an XSL parameter for configuration by Ant.
214: *
215: * @return an instance of the Param class to be configured.
216: * @since Ant 1.7
217: */
218: public XSLTProcess.Param createParam() {
219: XSLTProcess.Param p = new XSLTProcess.Param();
220: params.add(p);
221: return p;
222: }
223:
224: /**
225: * transformation
226: * @throws BuildException exception if something goes wrong with the transformation.
227: */
228: public void transform() throws BuildException {
229: checkOptions();
230: Project project = task.getProject();
231:
232: TempFile tempFileTask = new TempFile();
233: tempFileTask.bindToOwner(task);
234:
235: XSLTProcess xsltTask = new XSLTProcess();
236: xsltTask.bindToOwner(task);
237:
238: xsltTask.setXslResource(getStylesheet());
239:
240: // acrobatic cast.
241: xsltTask.setIn(((XMLResultAggregator) task)
242: .getDestinationFile());
243: File outputFile = null;
244: if (format.equals(FRAMES)) {
245: String tempFileProperty = getClass().getName()
246: + String.valueOf(counter++);
247: File tmp = FILE_UTILS.resolveFile(project.getBaseDir(),
248: project.getProperty("java.io.tmpdir"));
249: tempFileTask.setDestDir(tmp);
250: tempFileTask.setProperty(tempFileProperty);
251: tempFileTask.execute();
252: outputFile = new File(project.getProperty(tempFileProperty));
253: } else {
254: outputFile = new File(toDir, "junit-noframes.html");
255: }
256: xsltTask.setOut(outputFile);
257: for (Iterator i = params.iterator(); i.hasNext();) {
258: XSLTProcess.Param param = (XSLTProcess.Param) i.next();
259: XSLTProcess.Param newParam = xsltTask.createParam();
260: newParam.setProject(task.getProject());
261: newParam.setName(param.getName());
262: newParam.setExpression(param.getExpression());
263: }
264: XSLTProcess.Param paramx = xsltTask.createParam();
265: paramx.setProject(task.getProject());
266: paramx.setName("output.dir");
267: paramx.setExpression(toDir.getAbsolutePath());
268: final long t0 = System.currentTimeMillis();
269: try {
270: xsltTask.execute();
271: } catch (Exception e) {
272: throw new BuildException(
273: "Errors while applying transformations: "
274: + e.getMessage(), e);
275: }
276: final long dt = System.currentTimeMillis() - t0;
277: task.log("Transform time: " + dt + "ms");
278: if (format.equals(FRAMES)) {
279: Delete delete = new Delete();
280: delete.bindToOwner(task);
281: delete.setFile(outputFile);
282: delete.execute();
283: }
284: }
285:
286: /**
287: * access the stylesheet to be used as a resource.
288: * @return stylesheet as a resource
289: */
290: protected Resource getStylesheet() {
291: String xslname = "junit-frames.xsl";
292: if (NOFRAMES.equals(format)) {
293: xslname = "junit-noframes.xsl";
294: }
295: if (styleDir == null) {
296: // If style dir is not specified we have to retrieve
297: // the stylesheet from the classloader
298: URLResource stylesheet = new URLResource();
299: URL stylesheetURL = getClass().getClassLoader()
300: .getResource(
301: "org/apache/tools/ant/taskdefs/optional/junit/xsl/"
302: + xslname);
303: stylesheet.setURL(stylesheetURL);
304: return stylesheet;
305: }
306: // If we are here, then the style dir is here and we
307: // should read the stylesheet from the filesystem
308: FileResource stylesheet = new FileResource();
309: File stylesheetFile = new File(styleDir, xslname);
310: stylesheet.setFile(stylesheetFile);
311: return stylesheet;
312: }
313:
314: /** check for invalid options
315: * @throws BuildException if something goes wrong.
316: */
317: protected void checkOptions() throws BuildException {
318: // set the destination directory relative from the project if needed.
319: if (toDir == null) {
320: toDir = task.getProject().resolveFile(".");
321: } else if (!toDir.isAbsolute()) {
322: toDir = task.getProject().resolveFile(toDir.getPath());
323: }
324: }
325:
326: /**
327: * Get the systemid of the appropriate stylesheet based on its
328: * name and styledir. If no styledir is defined it will load
329: * it as a java resource in the xsl child package, otherwise it
330: * will get it from the given directory.
331: * @return system ID of the stylesheet.
332: * @throws IOException thrown if the requested stylesheet does
333: * not exist.
334: */
335: protected String getStylesheetSystemId() throws IOException {
336: String xslname = "junit-frames.xsl";
337: if (NOFRAMES.equals(format)) {
338: xslname = "junit-noframes.xsl";
339: }
340: if (styleDir == null) {
341: URL url = getClass().getResource("xsl/" + xslname);
342: if (url == null) {
343: throw new FileNotFoundException(
344: "Could not find jar resource " + xslname);
345: }
346: return url.toExternalForm();
347: }
348: File file = new File(styleDir, xslname);
349: if (!file.exists()) {
350: throw new FileNotFoundException("Could not find file '"
351: + file + "'");
352: }
353: return JAXPUtils.getSystemId(file);
354: }
355:
356: }
|