LoadFromRelativeFile.java

  1. package org.jbehave.core.io;

  2. import java.io.File;
  3. import java.net.URL;
  4. import java.nio.charset.Charset;
  5. import java.nio.charset.StandardCharsets;
  6. import java.util.ArrayList;
  7. import java.util.List;

  8. import org.apache.commons.io.FileUtils;

  9. /**
  10.  * <p>Loads story resources from relative file paths that are traversal to a given location.</p>
  11.  * <code>
  12.  * StoryLoader loader = new LoadFromRelativeFile(codeLocationFromClass(YourStory.class));
  13.  * </code>
  14.  * <p>By default, it uses traversal directory 'target/test-classes' with source dir in 'src/test/java'.</p>
  15.  *
  16.  * <p>Other traversal locations can be specified via the varargs constructor:</p>
  17.  * <code>
  18.  * StoryLoader loader = new LoadFromRelativeFile(codeLocationFromClass(YourStory.class),
  19.  *         mavenModuleTestStoryFilePath("src/test/java"), intellijProjectTestStoryFilePath("src/test/java"));
  20.  * </code>
  21.  *
  22.  * <p>Convenience methods : {@link LoadFromRelativeFile#mavenModuleStoryFilePath},
  23.  * {@link LoadFromRelativeFile#mavenModuleTestStoryFilePath}, {@link LoadFromRelativeFile#intellijProjectStoryFilePath},
  24.  * {@link LoadFromRelativeFile#intellijProjectTestStoryFilePath}</p>
  25.  *
  26.  * @see CodeLocations#codeLocationFromClass(Class)
  27.  *
  28.  */
  29. public class LoadFromRelativeFile implements ResourceLoader, StoryLoader {

  30.     private final Charset charset;
  31.     private final StoryFilePath[] traversals;
  32.     private final URL location;

  33.     public LoadFromRelativeFile(URL location) {
  34.         this(location, StandardCharsets.UTF_8);
  35.     }

  36.     public LoadFromRelativeFile(URL location, Charset charset) {
  37.         this(location, charset, mavenModuleStoryFilePath("src/test/java"));
  38.     }

  39.     public LoadFromRelativeFile(URL location, StoryFilePath... traversals) {
  40.         this(location, StandardCharsets.UTF_8, traversals);
  41.     }

  42.     public LoadFromRelativeFile(URL location, Charset charset, StoryFilePath... traversals) {
  43.         this.charset = charset;
  44.         this.traversals = traversals;
  45.         this.location = location;
  46.     }

  47.     @Override
  48.     public String loadResourceAsText(String resourcePath) {
  49.         List<String> traversalPaths = new ArrayList<>();
  50.         String locationPath = normalise(new File(CodeLocations.getPathFromURL(location)).getAbsolutePath());
  51.         for (StoryFilePath traversal : traversals) {
  52.             String filePath = locationPath.replace(traversal.toRemove, traversal.relativePath) + "/" + resourcePath;
  53.             File file = new File(filePath);
  54.             if (file.exists()) {
  55.                 return loadContent(filePath);
  56.             } else {
  57.                 traversalPaths.add(filePath);
  58.             }
  59.         }
  60.         throw new StoryResourceNotFound(resourcePath, traversalPaths);
  61.     }

  62.     @Override
  63.     public String loadStoryAsText(String storyPath) {
  64.         List<String> traversalPaths = new ArrayList<>();
  65.         String locationPath = new File(CodeLocations.getPathFromURL(location)).getAbsolutePath();
  66.         for (StoryFilePath traversal : traversals) {
  67.             String filePath = locationPath.replace(traversal.toRemove, traversal.relativePath) + "/" + storyPath;
  68.             File file = new File(filePath);
  69.             if (file.exists()) {
  70.                 return loadContent(filePath);
  71.             } else {
  72.                 traversalPaths.add(filePath);
  73.             }
  74.         }
  75.         throw new StoryResourceNotFound(storyPath, traversalPaths);
  76.     }

  77.     protected String loadContent(String path) {
  78.         try {
  79.             return FileUtils.readFileToString(new File(path), charset);
  80.         } catch (Exception e) {
  81.             throw new InvalidStoryResource(path, e);
  82.         }
  83.     }

  84.     private static String normalise(String path) {
  85.         return path.replace('\\', '/');
  86.     }

  87.     /**
  88.      * For use the the varargs constructor of {@link LoadFromRelativeFile}, to
  89.      * allow a range of possibilities for locating Story file paths
  90.      */
  91.     public static class StoryFilePath {
  92.         private final String toRemove;
  93.         private final String relativePath;

  94.         public StoryFilePath(String toRemove, String relativePath) {
  95.             this.toRemove = normalise(toRemove);
  96.             this.relativePath = normalise(relativePath);
  97.         }

  98.     }

  99.     /**
  100.      * Maven by default, has its PRODUCTION classes in target/classes. This
  101.      * story file path is relative to that.
  102.      *
  103.      * @param relativePath
  104.      *            the path to the stories' base-dir inside the module
  105.      * @return the resulting StoryFilePath
  106.      */
  107.     public static StoryFilePath mavenModuleStoryFilePath(String relativePath) {
  108.         return new StoryFilePath("target/classes", relativePath);
  109.     }

  110.     /**
  111.      * Maven by default, has its TEST classes in target/test-classes. This story
  112.      * file path is relative to that.
  113.      *
  114.      * @param relativePath
  115.      *            the path to the stories' base-dir inside the module
  116.      * @return the resulting StoryFilePath
  117.      */
  118.     public static StoryFilePath mavenModuleTestStoryFilePath(String relativePath) {
  119.         return new StoryFilePath("target/test-classes", relativePath);
  120.     }

  121.     /**
  122.      * Intellij by default, has its PRODUCTION classes in classes/production.
  123.      * This story file path is relative to that.
  124.      *
  125.      * @param relativePath
  126.      *            the path to the stories' base-dir inside the module
  127.      * @return the resulting StoryFilePath
  128.      */
  129.     public static StoryFilePath intellijProjectStoryFilePath(String relativePath) {
  130.         return new StoryFilePath("classes/production", relativePath);
  131.     }

  132.     /**
  133.      * Intellij by default, has its TEST classes in classes/test. This story
  134.      * file path is relative to that.
  135.      *
  136.      * @param relativePath
  137.      *            the path to the stories' base-dir inside the module
  138.      * @return the resulting StoryFilePath
  139.      */
  140.     public static StoryFilePath intellijProjectTestStoryFilePath(String relativePath) {
  141.         return new StoryFilePath("classes/test", relativePath);
  142.     }

  143. }