1. /*
  2. * Copyright 2001-2002,2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs.optional.metamata;
  18. import java.io.BufferedReader;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.io.OutputStream;
  23. import java.util.Date;
  24. import java.util.Enumeration;
  25. import java.util.Hashtable;
  26. import java.util.Vector;
  27. import javax.xml.parsers.DocumentBuilder;
  28. import javax.xml.parsers.DocumentBuilderFactory;
  29. import org.apache.tools.ant.BuildException;
  30. import org.apache.tools.ant.Project;
  31. import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
  32. import org.apache.tools.ant.taskdefs.LogOutputStream;
  33. import org.apache.tools.ant.taskdefs.StreamPumper;
  34. import org.apache.tools.ant.util.DOMElementWriter;
  35. import org.apache.tools.ant.util.DateUtils;
  36. import org.w3c.dom.Document;
  37. import org.w3c.dom.Element;
  38. /**
  39. * This is a very bad stream handler for the MAudit task.
  40. * All report to stdout that does not match a specific report pattern is dumped
  41. * to the Ant output as warn level. The report that match the pattern is stored
  42. * in a map with the key being the filepath that caused the error report.
  43. * <p>
  44. * The limitation with the choosen implementation is clear:
  45. * <ul>
  46. * <li>it does not handle multiline report( message that has \n ). the part until
  47. * the \n will be stored and the other part (which will not match the pattern)
  48. * will go to Ant output in Warn level.
  49. * <li>it does not report error that goes to stderr.
  50. * </ul>
  51. *
  52. */
  53. class MAuditStreamHandler implements ExecuteStreamHandler {
  54. /** parent task */
  55. private MAudit task;
  56. /** reader for stdout */
  57. private BufferedReader br;
  58. /**
  59. * this is where the XML output will go, should mostly be a file
  60. * the caller is responsible for flushing and closing this stream
  61. */
  62. private OutputStream xmlOut = null;
  63. /** error stream, might be useful to spit out error messages */
  64. private OutputStream errStream;
  65. /** thread pumping out error stream */
  66. private Thread errThread;
  67. /**
  68. * the multimap. The key in the map is the filepath that caused the audit
  69. * error and the value is a vector of MAudit.Violation entries.
  70. */
  71. private Hashtable auditedFiles = new Hashtable();
  72. /** program start timestamp for reporting purpose */
  73. private Date program_start;
  74. MAuditStreamHandler(MAudit task, OutputStream xmlOut) {
  75. this.task = task;
  76. this.xmlOut = xmlOut;
  77. }
  78. /** Ignore. */
  79. public void setProcessInputStream(OutputStream os) {
  80. }
  81. /** Ignore. */
  82. public void setProcessErrorStream(InputStream is) {
  83. errStream = new LogOutputStream(task, Project.MSG_ERR);
  84. errThread = createPump(is, errStream);
  85. }
  86. /** Set the inputstream */
  87. public void setProcessOutputStream(InputStream is) throws IOException {
  88. br = new BufferedReader(new InputStreamReader(is));
  89. }
  90. /** Invokes parseOutput. This will block until the end :-(*/
  91. public void start() throws IOException {
  92. program_start = new Date();
  93. errThread.start();
  94. parseOutput(br);
  95. }
  96. /**
  97. * Pretty dangerous business here. It serializes what was extracted from
  98. * the MAudit output and write it to the output.
  99. */
  100. public void stop() {
  101. // make sure to flush err stream
  102. try {
  103. errThread.join();
  104. } catch (InterruptedException e) {
  105. }
  106. try {
  107. errStream.flush();
  108. } catch (IOException e) {
  109. }
  110. // serialize the content as XML, move this to another method
  111. // this is the only code that could be needed to be overriden
  112. Document doc = getDocumentBuilder().newDocument();
  113. Element rootElement = doc.createElement("classes");
  114. Enumeration keys = auditedFiles.keys();
  115. Hashtable filemapping = task.getFileMapping();
  116. final Date now = new Date();
  117. rootElement.setAttribute("snapshot_created",
  118. DateUtils.format(now, DateUtils.ISO8601_DATETIME_PATTERN));
  119. rootElement.setAttribute("elapsed_time",
  120. String.valueOf(now.getTime() - program_start.getTime()));
  121. rootElement.setAttribute("program_start",
  122. DateUtils.format(now, DateUtils.ISO8601_DATETIME_PATTERN));
  123. rootElement.setAttribute("audited",
  124. String.valueOf(filemapping.size()));
  125. rootElement.setAttribute("reported",
  126. String.valueOf(auditedFiles.size()));
  127. int errors = 0;
  128. while (keys.hasMoreElements()) {
  129. String filepath = (String) keys.nextElement();
  130. Vector v = (Vector) auditedFiles.get(filepath);
  131. String fullclassname = (String) filemapping.get(filepath);
  132. if (fullclassname == null) {
  133. task.getProject().log("Could not find class mapping for "
  134. + filepath, Project.MSG_WARN);
  135. continue;
  136. }
  137. int pos = fullclassname.lastIndexOf('.');
  138. String pkg = (pos == -1) ? "" : fullclassname.substring(0, pos);
  139. String clazzname = (pos == -1) ? fullclassname : fullclassname.substring(pos + 1);
  140. Element clazz = doc.createElement("class");
  141. clazz.setAttribute("package", pkg);
  142. clazz.setAttribute("name", clazzname);
  143. final int violationCount = v.size();
  144. clazz.setAttribute("violations", String.valueOf(violationCount));
  145. errors += violationCount;
  146. for (int i = 0; i < violationCount; i++) {
  147. MAuditParser.Violation violation = (MAuditParser.Violation) v.elementAt(i);
  148. Element error = doc.createElement("violation");
  149. error.setAttribute("line", violation.line);
  150. error.setAttribute("message", violation.error);
  151. clazz.appendChild(error);
  152. }
  153. rootElement.appendChild(clazz);
  154. }
  155. rootElement.setAttribute("violations", String.valueOf(errors));
  156. // now write it to the outputstream, not very nice code
  157. DOMElementWriter domWriter = new DOMElementWriter();
  158. try {
  159. domWriter.write(rootElement, xmlOut);
  160. } catch (IOException e) {
  161. throw new BuildException(e);
  162. }
  163. }
  164. protected static DocumentBuilder getDocumentBuilder() {
  165. try {
  166. return DocumentBuilderFactory.newInstance().newDocumentBuilder();
  167. } catch (Exception exc) {
  168. throw new ExceptionInInitializerError(exc);
  169. }
  170. }
  171. /**
  172. * Creates a stream pumper to copy the given input stream to the given output stream.
  173. */
  174. protected Thread createPump(InputStream is, OutputStream os) {
  175. final Thread result = new Thread(new StreamPumper(is, os));
  176. result.setDaemon(true);
  177. return result;
  178. }
  179. /** read each line and process it */
  180. protected void parseOutput(BufferedReader br) throws IOException {
  181. String line = null;
  182. final MAuditParser parser = new MAuditParser();
  183. while ((line = br.readLine()) != null) {
  184. final MAuditParser.Violation violation = parser.parseLine(line);
  185. if (violation != null) {
  186. addViolation(violation.file, violation);
  187. } else {
  188. // this doesn't match..report it as info, it could be
  189. // either the copyright, summary or a multiline message (damn !)
  190. task.log(line, Project.MSG_INFO);
  191. }
  192. }
  193. }
  194. /** add a violation entry for the file */
  195. private void addViolation(String file, MAuditParser.Violation entry) {
  196. Vector violations = (Vector) auditedFiles.get(file);
  197. // if there is no decl for this file yet, create it.
  198. if (violations == null) {
  199. violations = new Vector();
  200. auditedFiles.put(file, violations);
  201. }
  202. violations.addElement(entry);
  203. }
  204. }