StoryReporterBuilder.java
package org.jbehave.core.reporters;
import static java.util.Arrays.asList;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import org.jbehave.core.configuration.Configuration;
import org.jbehave.core.configuration.Keywords;
import org.jbehave.core.i18n.LocalizedKeywords;
import org.jbehave.core.io.CodeLocations;
import org.jbehave.core.io.StoryLocation;
import org.jbehave.core.reporters.FilePrintStreamFactory.FileConfiguration;
import org.jbehave.core.reporters.FilePrintStreamFactory.FilePathResolver;
/**
* <p>
* A <a href="http://en.wikipedia.org/wiki/Builder_pattern">Builder</a> for
* {@link StoryReporter}s. It builds a {@link DelegatingStoryReporter} with
* delegates for a number of formats - mostly file-based ones except
* {@Format.CONSOLE}. It requires a
* {@link FilePrintStreamFactory} and provides default delegate instances for
* each format.
* </p>
* <p>
* To build a reporter for a single story path with default and given formats:
*
* <pre>
* Class<MyStory> storyClass = MyStory.class;
* StoryPathResolver resolver = new UnderscoredCamelCaseResolver();
* String storyPath = resolver.resolve(storyClass);
* StoryReporter reporter = new StoryReporterBuilder()
* .withCodeLocation(CodeLocations.codeLocationFromClass(storyClass))
* .withDefaultFormats().withFormats(TXT, HTML, XML).build(storyPath);
* </pre>
*
* </p>
* <p>
* The builder is configured to build with the {@link Format#STATS} as default
* format. To change the default formats the user can override the method:
*
* <pre>
* new StoryReporterBuilder() {
* protected StoryReporterBuilder withDefaultFormats() {
* return withFormats(STATS);
* }
* }
* </pre>
*
* </p>
* <p>
* The builder configures the file-based reporters to output to the default file
* directory {@link FileConfiguration#RELATIVE_DIRECTORY} as relative to the code
* location. In some case, e.g. with Ant class loader, the code source location
* from class may not be properly set. In this case, we may specify it from a
* file:
*
* <pre>
* new StoryReporterBuilder()
* .withCodeLocation(
* CodeLocations.codeLocationFromFile(new File("target/classes")))
* .withDefaultFormats().withFormats(TXT, HTML, XML).build(storyPath);
* </pre>
*
* </p>
* <p>
* By default, the reporters will output minimal failure information, the single
* line describing the failure cause and the outcomes if failures occur. To
* configure the failure trace to be reported as well:
*
* <pre>
* new StoryReporterBuilder().withFailureTrace(true)
* </pre>
*
* </p>
* <p>
* If failure trace is reported, it is with the full stack trace. In some cases,
* it's useful to have it compressed, eliminating unnecessary lines that are not
* very informative:
*
* <pre>
* new StoryReporterBuilder().withFailureTraceCompression(true)
* </pre>
*
* </p>
*
* <p>
* To specify the use of keywords for a given locale:
*
* <pre>
* new StoryReporterBuilder().withKeywords(new LocalisedKeywords(Locale.IT)
* </pre>
*
* </p>
*
* <p>
* The builder provides default instances for all reporters, using the default
* output patterns. To change the reporter for a specific instance, e.g. to
* report format <b>TXT</b> to <b>.text</b> files and to inject other
* non-default parameters, such as the custom output patterns:
*
* <pre>
* new StoryReporterBuilder() {
* public StoryReporter reporterFor(String storyPath, Format format) {
* FilePrintStreamFactory factory = new FilePrintStreamFactory(new StoryLocation(storyPath, codeLocation));
* switch (format) {
* case TXT:
* factory.useConfiguration(new FileConfiguration("text"));
* Properties customPatterns = new Properties();
* customPatterns.setProperty("successful", "{0}(YEAH!!!)\n");
* return new TxtOutput(factory.createPrintStream(), customPatterns, keywords);
* default:
* return super.reporterFor(format);
* }
* }
* </pre>
*
* </p>
*/
public class StoryReporterBuilder {
private List<Format> formats = new ArrayList<>();
protected String relativeDirectory;
protected FilePathResolver pathResolver;
protected URL codeLocation;
protected Properties viewResources;
protected boolean reportFailureTrace = false;
protected boolean compressFailureTrace = false;
protected Keywords keywords;
protected SGRCodes codes;
protected SurefireReporter surefireReporter;
protected boolean multiThreading;
protected Configuration configuration;
private FileConfiguration defaultFileConfiguration = new FileConfiguration();
public StoryReporterBuilder() {
}
public StoryReporterBuilder(Configuration configuration) {
this.configuration = configuration;
}
public File outputDirectory() {
return filePrintStreamFactory("").outputDirectory();
}
public String relativeDirectory() {
if (relativeDirectory == null) {
relativeDirectory = defaultFileConfiguration.getRelativeDirectory();
}
return relativeDirectory;
}
public FilePathResolver pathResolver() {
if (pathResolver == null) {
pathResolver = defaultFileConfiguration.getPathResolver();
}
return pathResolver;
}
public URL codeLocation() {
if (codeLocation == null) {
codeLocation = CodeLocations.codeLocationFromPath("target/classes");
}
return codeLocation;
}
public List<org.jbehave.core.reporters.Format> formats() {
return formats;
}
public List<String> formatNames(boolean toLowerCase) {
Locale locale = Locale.getDefault();
if (keywords instanceof LocalizedKeywords) {
locale = ((LocalizedKeywords) keywords).getLocale();
}
List<String> names = new ArrayList<>();
for (org.jbehave.core.reporters.Format format : formats) {
String name = format.name();
if (toLowerCase) {
name = name.toLowerCase(locale);
}
names.add(name);
}
return names;
}
public Keywords keywords() {
if (keywords == null) {
if (configuration != null) {
keywords = configuration.keywords();
}
keywords = new LocalizedKeywords();
}
return keywords;
}
public SGRCodes codes() {
if (codes == null) {
return new SGRCodes();
}
return codes;
}
public boolean multiThreading() {
return multiThreading;
}
public boolean reportFailureTrace() {
return reportFailureTrace;
}
public boolean compressFailureTrace() {
return compressFailureTrace;
}
public Properties viewResources() {
if (viewResources == null) {
if (configuration != null) {
viewResources = configuration.viewGenerator()
.defaultViewProperties();
} else {
viewResources = new FreemarkerViewGenerator()
.defaultViewProperties();
}
}
return viewResources;
}
public StoryReporterBuilder withRelativeDirectory(String relativeDirectory) {
this.relativeDirectory = relativeDirectory;
return this;
}
public StoryReporterBuilder withPathResolver(FilePathResolver pathResolver) {
this.pathResolver = pathResolver;
return this;
}
public StoryReporterBuilder withCodeLocation(URL codeLocation) {
this.codeLocation = codeLocation;
return this;
}
public SurefireReporter surefireReporter() {
return surefireReporter;
}
public boolean hasSurefireReporter() {
return surefireReporter != null;
}
public StoryReporterBuilder withSurefireReporter(SurefireReporter surefireReporter) {
this.surefireReporter = surefireReporter;
return this;
}
public StoryReporterBuilder withDefaultFormats() {
return withFormats(Format.STATS);
}
public StoryReporterBuilder withFormats(Format... formats) {
this.formats.addAll(asList(formats));
return this;
}
public StoryReporterBuilder withReporters(StoryReporter... reporters) {
for (StoryReporter reporter : reporters) {
this.formats.add(new ProvidedFormat(reporter));
}
return this;
}
public StoryReporterBuilder withFailureTrace(boolean reportFailureTrace) {
this.reportFailureTrace = reportFailureTrace;
return this;
}
public StoryReporterBuilder withFailureTraceCompression(
boolean compressFailureTrace) {
this.compressFailureTrace = compressFailureTrace;
return this;
}
public StoryReporterBuilder withKeywords(Keywords keywords) {
this.keywords = keywords;
return this;
}
public StoryReporterBuilder withCodes(SGRCodes codes) {
this.codes = codes;
return this;
}
public StoryReporterBuilder withMultiThreading(boolean multiThreading) {
this.multiThreading = multiThreading;
return this;
}
public StoryReporterBuilder withViewResources(Properties resources) {
this.viewResources = resources;
return this;
}
public StoryReporter build(String storyPath) {
Map<org.jbehave.core.reporters.Format, StoryReporter> delegates = new HashMap<>();
for (org.jbehave.core.reporters.Format format : formats) {
delegates.put(format, reporterFor(storyPath, format));
}
return new ConcurrentStoryReporter(new NullStoryReporter(), new ArrayList<>(delegates.values()),
multiThreading());
}
public Map<String, StoryReporter> build(List<String> storyPaths) {
Map<String, StoryReporter> reporters = new HashMap<>();
for (String storyPath : storyPaths) {
reporters.put(storyPath, build(storyPath));
}
reporters.put("*", build("*"));
return reporters;
}
public StoryReporter reporterFor(String storyPath, Format format) {
FilePrintStreamFactory factory = filePrintStreamFactory(storyPath);
return format.createStoryReporter(factory, this);
}
protected FilePrintStreamFactory filePrintStreamFactory(String storyPath) {
return new FilePrintStreamFactory(new StoryLocation(codeLocation(),
storyPath), fileConfiguration(""));
}
public FileConfiguration fileConfiguration(String extension) {
return new FileConfiguration(relativeDirectory(), extension,
pathResolver());
}
/**
* A Format that wraps a StoryReporter instance provided.
*/
public static class ProvidedFormat extends
org.jbehave.core.reporters.Format {
private final StoryReporter reporter;
public ProvidedFormat(StoryReporter reporter) {
super(reporter.getClass().getSimpleName());
this.reporter = reporter;
}
@Override
public StoryReporter createStoryReporter(
FilePrintStreamFactory factory,
StoryReporterBuilder storyReporterBuilder) {
return reporter;
}
}
}