Coverage Report - org.jbehave.core.reporters.FreemarkerViewGenerator
 
Classes in this File Line Coverage Branch Coverage Complexity
FreemarkerViewGenerator
100%
98/98
100%
20/20
1.88
FreemarkerViewGenerator$1
100%
6/6
100%
8/8
1.88
FreemarkerViewGenerator$Report
100%
12/12
N/A
1.88
FreemarkerViewGenerator$ReportCreationFailed
100%
2/2
N/A
1.88
FreemarkerViewGenerator$ViewGenerationFailedForTemplate
100%
2/2
N/A
1.88
 
 1  
 package org.jbehave.core.reporters;
 2  
 
 3  
 import static java.util.Arrays.asList;
 4  
 
 5  
 import java.io.File;
 6  
 import java.io.FileInputStream;
 7  
 import java.io.FileReader;
 8  
 import java.io.FileWriter;
 9  
 import java.io.FilenameFilter;
 10  
 import java.io.IOException;
 11  
 import java.io.Writer;
 12  
 import java.util.ArrayList;
 13  
 import java.util.Date;
 14  
 import java.util.HashMap;
 15  
 import java.util.List;
 16  
 import java.util.Map;
 17  
 import java.util.Properties;
 18  
 import java.util.SortedMap;
 19  
 import java.util.TreeMap;
 20  
 
 21  
 import org.apache.commons.io.FilenameUtils;
 22  
 import org.apache.commons.io.IOUtils;
 23  
 
 24  
 import freemarker.template.Configuration;
 25  
 import freemarker.template.ObjectWrapper;
 26  
 import freemarker.template.Template;
 27  
 import freemarker.template.TemplateException;
 28  
 
 29  
 /**
 30  
  * <p>
 31  
  * Freemarker-based {@link ViewGenerator}, using the file outputs of the
 32  
  * reporters for the given formats. The FTL templates for the index and single
 33  
  * views are injectable the {@link #generateView(File, List, Properties)} but
 34  
  * defaults are provided. To override, specify the path the new template under
 35  
  * keys "index", "decorated" and "nonDecorated".
 36  
  * </p>
 37  
  * <p>
 38  
  * The view generator provides the following resources:
 39  
  * 
 40  
  * <pre>
 41  
  * resources.setProperty(&quot;index&quot;, &quot;ftl/jbehave-reports-index.ftl&quot;);
 42  
  * resources.setProperty(&quot;decorated&quot;, &quot;ftl/jbehave-report-decorated.ftl&quot;);
 43  
  * resources.setProperty(&quot;nonDecorated&quot;, &quot;ftl/jbehave-report-non-decorated.ftl&quot;);
 44  
  * resources.setProperty(&quot;decorateNonHtml&quot;, &quot;true&quot;);
 45  
  * resources.setProperty(&quot;defaultFormats&quot;, &quot;stats&quot;);
 46  
  * resources.setProperty(&quot;viewDirectory&quot;, &quot;view&quot;);
 47  
  * </pre>
 48  
  * 
 49  
  * </p>
 50  
  * 
 51  
  * @author Mauro Talevi
 52  
  */
 53  
 public class FreemarkerViewGenerator implements ViewGenerator {
 54  
 
 55  
     private final Configuration configuration;
 56  
     private Properties viewProperties;
 57  395
     private List<Report> reports = new ArrayList<Report>();
 58  
 
 59  395
     public FreemarkerViewGenerator() {
 60  395
         this.configuration = configure();
 61  395
     }
 62  
 
 63  
     public static Properties defaultViewProperties() {
 64  233
         Properties properties = new Properties();
 65  233
         properties.setProperty("index", "ftl/jbehave-reports-index.ftl");
 66  233
         properties.setProperty("decorated", "ftl/jbehave-report-decorated.ftl");
 67  233
         properties.setProperty("nonDecorated", "ftl/jbehave-report-non-decorated.ftl");
 68  233
         properties.setProperty("decorateNonHtml", "true");
 69  233
         properties.setProperty("defaultFormats", "stats");
 70  233
         properties.setProperty("viewDirectory", "view");
 71  233
         return properties;
 72  
     }
 73  
 
 74  
     private Properties mergeWithDefault(Properties properties) {
 75  20
         Properties merged = defaultViewProperties();
 76  20
         merged.putAll(properties);
 77  20
         return merged;
 78  
     }
 79  
 
 80  
     public void generateView(File outputDirectory, List<String> formats, Properties viewProperties) {
 81  20
         this.viewProperties = mergeWithDefault(viewProperties);
 82  20
         createIndex(outputDirectory, formats);
 83  19
     }
 84  
 
 85  
     public int countStories() {
 86  17
         return reports.size();
 87  
     }
 88  
 
 89  
     public int countScenarios() {
 90  17
         return count("scenarios", reports);
 91  
     }
 92  
 
 93  
     public int countFailedScenarios() {
 94  17
         return count("scenariosFailed", reports);
 95  
     }
 96  
 
 97  
     int count(String event, List<Report> reports) {
 98  36
         int count = 0;
 99  36
         for (Report report : reports) {
 100  36
             Properties stats = report.asProperties("stats");
 101  36
             if (stats.containsKey(event)) {
 102  1
                 count = count + Integer.parseInt((String) stats.get(event));
 103  
             }
 104  36
         }
 105  36
         return count;
 106  
     }
 107  
 
 108  
     private void createIndex(File outputDirectory, List<String> formats) {
 109  20
         String outputName = templateResource("viewDirectory") + "/index.html";
 110  20
         String index = templateResource("index");
 111  20
         List<String> mergedFormats = mergeWithDefaults(formats);
 112  20
         reports = toReports(indexedReportFiles(outputDirectory, outputName, mergedFormats));
 113  20
         Map<String, Object> dataModel = newDataModel();
 114  20
         dataModel.put("reports", reports);
 115  20
         dataModel.put("date", new Date());
 116  20
         write(outputDirectory, outputName, index, dataModel);
 117  19
     }
 118  
 
 119  
     private List<String> mergeWithDefaults(List<String> formats) {
 120  20
         List<String> merged = new ArrayList<String>();
 121  20
         merged.addAll(asList(templateResource("defaultFormats").split(",")));
 122  20
         merged.addAll(formats);
 123  20
         return merged;
 124  
     }
 125  
 
 126  
     SortedMap<String, List<File>> indexedReportFiles(File outputDirectory, final String outputName,
 127  
             final List<String> formats) {
 128  23
         SortedMap<String, List<File>> reports = new TreeMap<String, List<File>>();
 129  23
         if (outputDirectory == null || !outputDirectory.exists()) {
 130  2
             return reports;
 131  
         }
 132  21
         String[] fileNames = outputDirectory.list(new FilenameFilter() {
 133  
             public boolean accept(File dir, String name) {
 134  155
                 return !name.equals(outputName) && hasFormats(name, formats);
 135  
             }
 136  
 
 137  
             private boolean hasFormats(String name, List<String> formats) {
 138  154
                 for (String format : formats) {
 139  191
                     if (name.endsWith(format)) {
 140  25
                         return true;
 141  
                     }
 142  
                 }
 143  129
                 return false;
 144  
             }
 145  
         });
 146  46
         for (String fileName : fileNames) {
 147  25
             String name = FilenameUtils.getBaseName(fileName);
 148  25
             List<File> filesByName = reports.get(name);
 149  25
             if (filesByName == null) {
 150  21
                 filesByName = new ArrayList<File>();
 151  21
                 reports.put(name, filesByName);
 152  
             }
 153  25
             filesByName.add(new File(outputDirectory, fileName));
 154  
         }
 155  21
         return reports;
 156  
     }
 157  
 
 158  
     List<Report> toReports(Map<String, List<File>> reportFiles) {
 159  
         try {
 160  21
             String decoratedTemplate = templateResource("decorated");
 161  20
             String nonDecoratedTemplate = templateResource("nonDecorated");
 162  20
             String viewDirectory = templateResource("viewDirectory");
 163  20
             boolean decorateNonHtml = Boolean.valueOf(templateResource("decorateNonHtml"));
 164  20
             List<Report> reports = new ArrayList<Report>();
 165  20
             for (String name : reportFiles.keySet()) {
 166  19
                 Map<String, File> filesByFormat = new HashMap<String, File>();
 167  19
                 for (File file : reportFiles.get(name)) {
 168  21
                     String fileName = file.getName();
 169  21
                     String format = FilenameUtils.getExtension(fileName);
 170  21
                     Map<String, Object> dataModel = newDataModel();
 171  21
                     dataModel.put("name", name);
 172  21
                     dataModel.put("body", IOUtils.toString(new FileReader(file)));
 173  21
                     dataModel.put("format", format);
 174  21
                     File outputDirectory = file.getParentFile();
 175  21
                     String outputName = viewDirectory + "/" + fileName;
 176  21
                     String template = decoratedTemplate;
 177  21
                     if (!format.equals("html")) {
 178  19
                         if (decorateNonHtml) {
 179  18
                             outputName = outputName + ".html";
 180  
                         } else {
 181  1
                             template = nonDecoratedTemplate;
 182  
                         }
 183  
                     }
 184  21
                     File written = write(outputDirectory, outputName, template, dataModel);
 185  21
                     filesByFormat.put(format, written);
 186  21
                 }
 187  19
                 reports.add(new Report(name, filesByFormat));
 188  19
             }
 189  20
             return reports;
 190  1
         } catch (Exception e) {
 191  1
             throw new ReportCreationFailed(reportFiles, e);
 192  
         }
 193  
     }
 194  
 
 195  
     private File write(File outputDirectory, String outputName, String resource, Map<String, Object> dataModel) {
 196  
         try {
 197  41
             File file = new File(outputDirectory, outputName);
 198  41
             file.getParentFile().mkdirs();
 199  41
             Writer writer = new FileWriter(file);
 200  41
             process(resource, dataModel, writer);
 201  40
             return file;
 202  1
         } catch (Exception e) {
 203  1
             throw new ViewGenerationFailedForTemplate(resource, e);
 204  
         }
 205  
     }
 206  
 
 207  
     private Configuration configure() {
 208  395
         Configuration configuration = new Configuration();
 209  395
         configuration.setClassForTemplateLoading(FreemarkerViewGenerator.class, "/");
 210  395
         configuration.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
 211  395
         return configuration;
 212  
     }
 213  
 
 214  
     private void process(String resource, Map<String, Object> dataModel, Writer writer) throws TemplateException,
 215  
             IOException {
 216  41
         Template template = configuration.getTemplate(resource);
 217  40
         template.process(dataModel, writer);
 218  40
     }
 219  
 
 220  
     private String templateResource(String format) {
 221  141
         return viewProperties.getProperty(format);
 222  
     }
 223  
 
 224  
     private Map<String, Object> newDataModel() {
 225  41
         return new HashMap<String, Object>();
 226  
     }
 227  
 
 228  
     @SuppressWarnings("serial")
 229  
     public static class ReportCreationFailed extends RuntimeException {
 230  
 
 231  
         public ReportCreationFailed(Map<String, List<File>> reportFiles, Exception cause) {
 232  1
             super("Report creation failed from file " + reportFiles, cause);
 233  1
         }
 234  
     }
 235  
 
 236  
     @SuppressWarnings("serial")
 237  
     public static class ViewGenerationFailedForTemplate extends RuntimeException {
 238  
 
 239  
         public ViewGenerationFailedForTemplate(String resource, Exception cause) {
 240  1
             super(resource, cause);
 241  1
         }
 242  
 
 243  
     }
 244  
 
 245  
     public static class Report {
 246  
 
 247  
         private final String name;
 248  
         private final Map<String, File> filesByFormat;
 249  
 
 250  20
         public Report(String name, Map<String, File> filesByFormat) {
 251  20
             this.name = name;
 252  20
             this.filesByFormat = filesByFormat;
 253  20
         }
 254  
 
 255  
         public String getName() {
 256  19
             return name;
 257  
         }
 258  
 
 259  
         public Map<String, File> getFilesByFormat() {
 260  19
             return filesByFormat;
 261  
         }
 262  
 
 263  
         public Properties asProperties(String format) {
 264  54
             Properties p = new Properties();
 265  54
             File stats = filesByFormat.get(format);
 266  
             try {
 267  54
                 p.load(new FileInputStream(stats));
 268  3
             } catch (Exception e) {
 269  
                 // return empty map
 270  51
             }
 271  54
             return p;
 272  
         }
 273  
 
 274  
     }
 275  
 }