FilePrintStreamFactory.java

  1. package org.jbehave.core.reporters;

  2. import java.io.File;
  3. import java.io.FileNotFoundException;
  4. import java.io.FileOutputStream;
  5. import java.io.PrintStream;

  6. import org.apache.commons.lang3.StringUtils;
  7. import org.apache.commons.lang3.builder.ToStringBuilder;
  8. import org.apache.commons.lang3.builder.ToStringStyle;
  9. import org.jbehave.core.io.CodeLocations;
  10. import org.jbehave.core.io.StoryLocation;

  11. /**
  12.  * Creates {@link PrintStream} instances that write to a file identified by the
  13.  * {@link StoryLocation}. {@link FileConfiguration} specifies directory and the
  14.  * extension, providing useful default values.
  15.  */
  16. public class FilePrintStreamFactory implements PrintStreamFactory {

  17.     private final StoryLocation storyLocation;
  18.     private FileConfiguration configuration;
  19.     private File outputFile;

  20.     public FilePrintStreamFactory(StoryLocation storyLocation) {
  21.         this(storyLocation, new FileConfiguration());
  22.     }

  23.     public FilePrintStreamFactory(StoryLocation storyLocation, FileConfiguration configuration) {
  24.         this.storyLocation = storyLocation;
  25.         this.configuration = configuration;
  26.     }

  27.     @Override
  28.     public PrintStream createPrintStream() {
  29.         try {
  30.             outputFile = outputFile();
  31.             outputFile.getParentFile().mkdirs();
  32.             return new FilePrintStream(outputFile, false);
  33.         } catch (Exception e) {
  34.             throw new PrintStreamCreationFailed(outputFile, e);
  35.         }
  36.     }

  37.     public File getOutputFile() {
  38.         return outputFile;
  39.     }

  40.     public void useConfiguration(FileConfiguration configuration) {
  41.         this.configuration = configuration;
  42.         this.outputFile = outputFile();
  43.     }

  44.     public FileConfiguration configuration() {
  45.         return configuration;
  46.     }

  47.     protected File outputFile() {
  48.         return new File(outputDirectory(), outputName());
  49.     }

  50.     /**
  51.      * Return the file output directory, using the configured
  52.      * {@link FilePathResolver}
  53.      *
  54.      * @return The File representing the output directory
  55.      */
  56.     protected File outputDirectory() {
  57.         return new File(configuration.getPathResolver().resolveDirectory(storyLocation,
  58.                 configuration.getRelativeDirectory()));
  59.     }

  60.     /**
  61.      * Return the file output name, using the configured
  62.      * {@link FilePathResolver}
  63.      *
  64.      * @return The file output name
  65.      */
  66.     protected String outputName() {
  67.         return configuration.getPathResolver().resolveName(storyLocation, configuration.getExtension());
  68.     }

  69.     public static interface FilePathResolver {

  70.         String resolveDirectory(StoryLocation storyLocation, String relativeDirectory);

  71.         String resolveName(StoryLocation storyLocation, String extension);

  72.     }

  73.     /**
  74.      * Resolves directory from code location parent file.
  75.      */
  76.     public abstract static class AbstractPathResolver implements FilePathResolver {

  77.         @Override
  78.         public String resolveDirectory(StoryLocation storyLocation, String relativeDirectory) {
  79.             File parent = new File(CodeLocations.getPathFromURL(storyLocation.getCodeLocation())).getParentFile();
  80.             return parent.getPath().replace('\\', '/') + "/" + relativeDirectory;
  81.         }

  82.     }

  83.     /**
  84.      * Resolves story location path to java packaged name, replacing '/' with '.'
  85.      */
  86.     public static class ResolveToPackagedName extends AbstractPathResolver {

  87.         @Override
  88.         public String resolveName(StoryLocation storyLocation, String extension) {
  89.             String name = storyLocation.getPath().replaceAll(":?/", ".");
  90.             if (name.startsWith(".")) {
  91.                 name = name.substring(1);
  92.             }
  93.             return StringUtils.substringBeforeLast(name, ".") + "." + extension;
  94.         }

  95.     }

  96.     /**
  97.      * Resolves story location path to simple name, considering portion after last '/'.
  98.      */
  99.     public static class ResolveToSimpleName extends AbstractPathResolver {

  100.         @Override
  101.         public String resolveName(StoryLocation storyLocation, String extension) {
  102.             String name = storyLocation.getPath();
  103.             if (StringUtils.contains(name, '/')) {
  104.                 name = StringUtils.substringAfterLast(name, "/");
  105.             }
  106.             return StringUtils.substringBeforeLast(name, ".") + "." + extension;
  107.         }

  108.     }

  109.     public static class FilePrintStream extends PrintStream {

  110.         private final File outputFile;
  111.         private final boolean append;

  112.         public FilePrintStream(File outputFile, boolean append) throws FileNotFoundException {
  113.             super(new FileOutputStream(outputFile, append));
  114.             this.outputFile = outputFile;
  115.             this.append = append;
  116.         }

  117.         @Override
  118.         public String toString() {
  119.             return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append(outputFile).append(append)
  120.                     .toString();
  121.         }

  122.     }

  123.     /**
  124.      * Configuration class for file print streams. Allows specification the
  125.      * relative directory (relative to code location) and file extension.
  126.      * Provides as defaults {@link #RELATIVE_DIRECTORY} and {@link #EXTENSION}.
  127.      */
  128.     public static class FileConfiguration {
  129.         public static final String RELATIVE_DIRECTORY = "jbehave";
  130.         public static final String EXTENSION = "html";

  131.         private final String relativeDirectory;
  132.         private final String extension;
  133.         private final FilePathResolver pathResolver;

  134.         public FileConfiguration() {
  135.             this(EXTENSION);
  136.         }

  137.         public FileConfiguration(String extension) {
  138.             this(RELATIVE_DIRECTORY, extension, new ResolveToPackagedName());
  139.         }

  140.         public FileConfiguration(String relativeDirectory, String extension, FilePathResolver pathResolver) {
  141.             this.relativeDirectory = relativeDirectory;
  142.             this.extension = extension;
  143.             this.pathResolver = pathResolver;
  144.         }

  145.         public String getRelativeDirectory() {
  146.             return relativeDirectory;
  147.         }

  148.         public String getExtension() {
  149.             return extension;
  150.         }

  151.         public FilePathResolver getPathResolver() {
  152.             return pathResolver;
  153.         }

  154.         @Override
  155.         public String toString() {
  156.             return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
  157.         }

  158.     }

  159.     @SuppressWarnings("serial")
  160.     public class PrintStreamCreationFailed extends RuntimeException {
  161.         public PrintStreamCreationFailed(File file, Exception cause) {
  162.             super("Failed to create print stream for file " + file, cause);
  163.         }
  164.     }
  165. }