Coverage Report - com.google.code.jetm.maven.TimingReportMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
TimingReportMojo
0%
0/103
0%
0/24
2.333
 
 1  
 package com.google.code.jetm.maven;
 2  
 
 3  
 import java.io.File;
 4  
 import java.io.FileNotFoundException;
 5  
 import java.io.FileReader;
 6  
 import java.text.DecimalFormat;
 7  
 import java.util.ArrayList;
 8  
 import java.util.Collection;
 9  
 import java.util.Collections;
 10  
 import java.util.HashMap;
 11  
 import java.util.LinkedHashMap;
 12  
 import java.util.LinkedList;
 13  
 import java.util.List;
 14  
 import java.util.Locale;
 15  
 import java.util.Map;
 16  
 import java.util.Map.Entry;
 17  
 
 18  
 import org.apache.commons.io.FileUtils;
 19  
 import org.apache.commons.io.IOUtils;
 20  
 import org.apache.commons.io.filefilter.TrueFileFilter;
 21  
 import org.apache.maven.doxia.sink.Sink;
 22  
 import org.apache.maven.doxia.siterenderer.Renderer;
 23  
 import org.apache.maven.project.MavenProject;
 24  
 import org.apache.maven.reporting.AbstractMavenReport;
 25  
 import org.apache.maven.reporting.MavenReportException;
 26  
 
 27  
 import com.google.code.jetm.maven.data.AggregateSummary;
 28  
 import com.google.code.jetm.maven.util.XmlIOFileFilter;
 29  
 import com.google.code.jetm.reporting.AggregateBinder;
 30  
 import com.google.code.jetm.reporting.xml.XmlAggregateBinder;
 31  
 
 32  
 import etm.core.aggregation.Aggregate;
 33  
 
 34  
 /**
 35  
  * A mojo used to create a report that displays the collective JETM timings that
 36  
  * were collected and rendered using an {@link XmlAggregateBinder}.
 37  
  * 
 38  
  * @author jrh3k5
 39  
  * @goal timing-report
 40  
  * @phase site
 41  
  */
 42  
 
 43  0
 public class TimingReportMojo extends AbstractMavenReport {
 44  
     /**
 45  
      * The directories containing the timing report XML files.
 46  
      * 
 47  
      * @parameter
 48  
      * @required
 49  
      */
 50  
     private File[] timings;
 51  
 
 52  
     /**
 53  
      * Directory where reports will go.
 54  
      * 
 55  
      * @parameter expression="${project.reporting.outputDirectory}"
 56  
      * @required
 57  
      * @readonly
 58  
      */
 59  
     private String outputDirectory;
 60  
 
 61  
     /**
 62  
      * @parameter default-value="${project}"
 63  
      * @required
 64  
      * @readonly
 65  
      */
 66  
     private MavenProject project;
 67  
 
 68  
     /**
 69  
      * @component
 70  
      * @required
 71  
      * @readonly
 72  
      */
 73  
     private Renderer siteRenderer;
 74  
 
 75  
     /**
 76  
      * {@inheritDoc}
 77  
      */
 78  
     public String getOutputName() {
 79  0
         return "jetm-timing-report";
 80  
     }
 81  
 
 82  
     /**
 83  
      * {@inheritDoc}
 84  
      */
 85  
     public String getName(Locale locale) {
 86  0
         return "JETM Timing Report";
 87  
     }
 88  
 
 89  
     /**
 90  
      * {@inheritDoc}
 91  
      */
 92  
     public String getDescription(Locale locale) {
 93  0
         return "A collective report of all JETM timings that were collected and rendered.";
 94  
     }
 95  
 
 96  
     /**
 97  
      * {@inheritDoc}
 98  
      */
 99  
     protected Renderer getSiteRenderer() {
 100  0
         return siteRenderer;
 101  
     }
 102  
 
 103  
     /**
 104  
      * {@inheritDoc}
 105  
      */
 106  
     protected String getOutputDirectory() {
 107  0
         return outputDirectory;
 108  
     }
 109  
 
 110  
     /**
 111  
      * {@inheritDoc}
 112  
      */
 113  
     protected MavenProject getProject() {
 114  0
         return project;
 115  
     }
 116  
 
 117  0
     private DecimalFormat decimalFormatter = new DecimalFormat("0.00");
 118  0
     private final AggregateBinder binder = new XmlAggregateBinder();
 119  0
     private final XmlIOFileFilter xmlFileFilter = new XmlIOFileFilter();
 120  
 
 121  
     /**
 122  
      * {@inheritDoc}
 123  
      */
 124  
     protected void executeReport(Locale locale) throws MavenReportException {
 125  0
         final Map<File, List<Aggregate>> aggregates = getAggregates();
 126  0
         final List<AggregateSummary> summaries = aggregate(aggregates);
 127  0
         Collections.sort(summaries);
 128  
 
 129  0
         final Sink sink = getSink();
 130  
         try {
 131  0
             sink.head();
 132  0
             sink.title();
 133  0
             sink.text(getName(locale));
 134  0
             sink.title_();
 135  0
             sink.head_();
 136  
 
 137  0
             sink.body();
 138  0
             sink.sectionTitle1();
 139  0
             sink.text(getName(locale));
 140  0
             sink.sectionTitle1_();
 141  
 
 142  0
             if (summaries.isEmpty()) {
 143  0
                 sink.text(" There are no JETM timings available for reporting.");
 144  
                 return;
 145  
             }
 146  
 
 147  0
             sink.sectionTitle2();
 148  0
             sink.text("Summary");
 149  0
             sink.sectionTitle2_();
 150  
 
 151  0
             sink.text("This is a summary, by measurement name, of the measurements taken.");
 152  
 
 153  0
             print(sink, summaries);
 154  
 
 155  0
             sink.sectionTitle2();
 156  0
             sink.text("File Breakdown");
 157  0
             sink.sectionTitle2_();
 158  
 
 159  0
             sink.text("This is a list of, per XML file, the measurements taken.");
 160  
 
 161  0
             for (Entry<File, List<Aggregate>> entry : aggregates.entrySet()) {
 162  0
                 sink.sectionTitle3();
 163  0
                 sink.text(entry.getKey().getName());
 164  0
                 sink.sectionTitle3_();
 165  
 
 166  0
                 print(sink, entry.getValue());
 167  
             }
 168  
         } finally {
 169  0
             sink.body_();
 170  
 
 171  0
             sink.flush();
 172  0
             sink.close();
 173  0
         }
 174  0
     }
 175  
 
 176  
     /**
 177  
      * Print a table containing information within a given set of aggregates.
 178  
      * 
 179  
      * @param sink
 180  
      *            The {@link Sink} used to render out the table.
 181  
      * @param aggregates
 182  
      *            A {@link Collection} of {@link Aggregate} objects representing
 183  
      *            the data to be written out.
 184  
      */
 185  
     private void print(Sink sink, Collection<? extends Aggregate> aggregates) {
 186  0
         sink.table();
 187  0
         tableHeaderCell(sink, "Name");
 188  0
         tableHeaderCell(sink, "Average (sec)");
 189  0
         tableHeaderCell(sink, "Measurements");
 190  0
         tableHeaderCell(sink, "Minimum (sec)");
 191  0
         tableHeaderCell(sink, "Maximum (sec)");
 192  0
         tableHeaderCell(sink, "Total");
 193  0
         for (Aggregate aggregate : aggregates) {
 194  0
             sink.tableRow();
 195  0
             tableCell(sink, aggregate.getName());
 196  0
             tableCell(sink, decimalFormatter.format((aggregate.getTotal() / aggregate
 197  
                     .getMeasurements()) / 1000.0));
 198  0
             tableCell(sink, Long.toString(aggregate.getMeasurements()));
 199  0
             tableCell(sink, decimalFormatter.format(aggregate.getMin() / 1000.0));
 200  0
             tableCell(sink, decimalFormatter.format(aggregate.getMax() / 1000.0));
 201  0
             tableCell(sink, decimalFormatter.format(aggregate.getTotal() / 1000.0));
 202  0
             sink.tableRow_();
 203  
         }
 204  0
         sink.table_();
 205  0
     }
 206  
 
 207  
     /**
 208  
      * Create a table header cell.
 209  
      * 
 210  
      * @param sink
 211  
      *            The {@link Sink} used to render out the header.
 212  
      * @param text
 213  
      *            The text to be printed within the table header.
 214  
      */
 215  
     private void tableHeaderCell(Sink sink, String text) {
 216  0
         sink.tableHeaderCell();
 217  0
         sink.text(text);
 218  0
         sink.tableHeaderCell_();
 219  0
     }
 220  
 
 221  
     /**
 222  
      * Create a table cell.
 223  
      * 
 224  
      * @param sink
 225  
      *            The {@link Sink} used to render out the table cell.
 226  
      * @param text
 227  
      *            The text to be written inside the cell.
 228  
      */
 229  
     private void tableCell(Sink sink, String text) {
 230  0
         sink.tableCell();
 231  0
         sink.text(text);
 232  0
         sink.tableCell_();
 233  0
     }
 234  
 
 235  
     /**
 236  
      * Get aggregates.
 237  
      * 
 238  
      * @return A {@link Map}. Its keys are the files that contain aggregate
 239  
      *         data; the values are {@link List}s of {@link Aggregate} objects
 240  
      *         representing the timings read within each file.
 241  
      *         <p />
 242  
      *         If a file contains no timing data, it will not be returned in
 243  
      *         this map.
 244  
      * @throws MavenReportException
 245  
      *             If any errors occur while reading the file.
 246  
      */
 247  
     private Map<File, List<Aggregate>> getAggregates() throws MavenReportException {
 248  0
         final Map<File, List<Aggregate>> aggregates = new LinkedHashMap<File, List<Aggregate>>();
 249  0
         for (File directory : timings) {
 250  0
             if (!directory.exists())
 251  0
                 continue;
 252  
 
 253  0
             for (File file : FileUtils.listFiles(directory, xmlFileFilter, TrueFileFilter.TRUE)) {
 254  0
                 final List<Aggregate> aggregateList = new LinkedList<Aggregate>();
 255  
                 FileReader reader;
 256  
                 try {
 257  0
                     reader = new FileReader(file);
 258  0
                 } catch (FileNotFoundException e) {
 259  0
                     throw new MavenReportException("File not found: " + file, e);
 260  0
                 }
 261  
                 try {
 262  0
                     final Collection<Aggregate> unbound = binder.unbind(reader);
 263  0
                     if (unbound.isEmpty())
 264  
                         continue;
 265  0
                     aggregateList.addAll(unbound);
 266  
                 } finally {
 267  0
                     IOUtils.closeQuietly(reader);
 268  0
                 }
 269  0
                 aggregates.put(file, aggregateList);
 270  0
             }
 271  
         }
 272  0
         return aggregates;
 273  
     }
 274  
 
 275  
     /**
 276  
      * Create aggregate summaries.
 277  
      * 
 278  
      * @param aggregates
 279  
      *            A {@link Map} containing the aggregate data to be summarized.
 280  
      * @return A {@link List} of {@link AggregateSummary} objects representing
 281  
      *         the entirety of aggregate data, summarized by name.
 282  
      * @see #getAggregates()
 283  
      */
 284  
     private List<AggregateSummary> aggregate(Map<File, List<Aggregate>> aggregates) {
 285  0
         if (aggregates.isEmpty())
 286  0
             return Collections.emptyList();
 287  
 
 288  0
         final Map<String, AggregateSummary> summaries = new HashMap<String, AggregateSummary>();
 289  0
         for (List<Aggregate> aggregateList : aggregates.values())
 290  0
             for (Aggregate aggregate : aggregateList) {
 291  0
                 final String name = aggregate.getName();
 292  0
                 if (!summaries.containsKey(name))
 293  0
                     summaries.put(name, new AggregateSummary(name));
 294  
 
 295  0
                 final AggregateSummary summary = summaries.get(name);
 296  0
                 summary.add(aggregate);
 297  0
             }
 298  
 
 299  0
         final List<AggregateSummary> summaryList = new ArrayList<AggregateSummary>(summaries.size());
 300  0
         for (AggregateSummary summary : summaries.values())
 301  0
             summaryList.add(summary);
 302  
 
 303  0
         return summaryList;
 304  
     }
 305  
 }