LoadFromRelativeFile.java
package org.jbehave.core.io;
import java.io.File;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.FileUtils;
/**
* <p>Loads story resources from relative file paths that are traversal to a given location.</p>
* <code>
* StoryLoader loader = new LoadFromRelativeFile(codeLocationFromClass(YourStory.class));
* </code>
* <p>By default, it uses traversal directory 'target/test-classes' with source dir in 'src/test/java'.</p>
*
* <p>Other traversal locations can be specified via the varargs constructor:</p>
* <code>
* StoryLoader loader = new LoadFromRelativeFile(codeLocationFromClass(YourStory.class),
* mavenModuleTestStoryFilePath("src/test/java"), intellijProjectTestStoryFilePath("src/test/java"));
* </code>
*
* <p>Convenience methods : {@link LoadFromRelativeFile#mavenModuleStoryFilePath},
* {@link LoadFromRelativeFile#mavenModuleTestStoryFilePath}, {@link LoadFromRelativeFile#intellijProjectStoryFilePath},
* {@link LoadFromRelativeFile#intellijProjectTestStoryFilePath}</p>
*
* @see CodeLocations#codeLocationFromClass(Class)
*
*/
public class LoadFromRelativeFile implements ResourceLoader, StoryLoader {
private final Charset charset;
private final StoryFilePath[] traversals;
private final URL location;
public LoadFromRelativeFile(URL location) {
this(location, StandardCharsets.UTF_8);
}
public LoadFromRelativeFile(URL location, Charset charset) {
this(location, charset, mavenModuleStoryFilePath("src/test/java"));
}
public LoadFromRelativeFile(URL location, StoryFilePath... traversals) {
this(location, StandardCharsets.UTF_8, traversals);
}
public LoadFromRelativeFile(URL location, Charset charset, StoryFilePath... traversals) {
this.charset = charset;
this.traversals = traversals;
this.location = location;
}
@Override
public String loadResourceAsText(String resourcePath) {
List<String> traversalPaths = new ArrayList<>();
String locationPath = normalise(new File(CodeLocations.getPathFromURL(location)).getAbsolutePath());
for (StoryFilePath traversal : traversals) {
String filePath = locationPath.replace(traversal.toRemove, traversal.relativePath) + "/" + resourcePath;
File file = new File(filePath);
if (file.exists()) {
return loadContent(filePath);
} else {
traversalPaths.add(filePath);
}
}
throw new StoryResourceNotFound(resourcePath, traversalPaths);
}
@Override
public String loadStoryAsText(String storyPath) {
List<String> traversalPaths = new ArrayList<>();
String locationPath = new File(CodeLocations.getPathFromURL(location)).getAbsolutePath();
for (StoryFilePath traversal : traversals) {
String filePath = locationPath.replace(traversal.toRemove, traversal.relativePath) + "/" + storyPath;
File file = new File(filePath);
if (file.exists()) {
return loadContent(filePath);
} else {
traversalPaths.add(filePath);
}
}
throw new StoryResourceNotFound(storyPath, traversalPaths);
}
protected String loadContent(String path) {
try {
return FileUtils.readFileToString(new File(path), charset);
} catch (Exception e) {
throw new InvalidStoryResource(path, e);
}
}
private static String normalise(String path) {
return path.replace('\\', '/');
}
/**
* For use the the varargs constructor of {@link LoadFromRelativeFile}, to
* allow a range of possibilities for locating Story file paths
*/
public static class StoryFilePath {
private final String toRemove;
private final String relativePath;
public StoryFilePath(String toRemove, String relativePath) {
this.toRemove = normalise(toRemove);
this.relativePath = normalise(relativePath);
}
}
/**
* Maven by default, has its PRODUCTION classes in target/classes. This
* story file path is relative to that.
*
* @param relativePath
* the path to the stories' base-dir inside the module
* @return the resulting StoryFilePath
*/
public static StoryFilePath mavenModuleStoryFilePath(String relativePath) {
return new StoryFilePath("target/classes", relativePath);
}
/**
* Maven by default, has its TEST classes in target/test-classes. This story
* file path is relative to that.
*
* @param relativePath
* the path to the stories' base-dir inside the module
* @return the resulting StoryFilePath
*/
public static StoryFilePath mavenModuleTestStoryFilePath(String relativePath) {
return new StoryFilePath("target/test-classes", relativePath);
}
/**
* Intellij by default, has its PRODUCTION classes in classes/production.
* This story file path is relative to that.
*
* @param relativePath
* the path to the stories' base-dir inside the module
* @return the resulting StoryFilePath
*/
public static StoryFilePath intellijProjectStoryFilePath(String relativePath) {
return new StoryFilePath("classes/production", relativePath);
}
/**
* Intellij by default, has its TEST classes in classes/test. This story
* file path is relative to that.
*
* @param relativePath
* the path to the stories' base-dir inside the module
* @return the resulting StoryFilePath
*/
public static StoryFilePath intellijProjectTestStoryFilePath(String relativePath) {
return new StoryFilePath("classes/test", relativePath);
}
}