package org.jbehave.core.io;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* Loads story resources from relative file paths that are
* traversal to a given location.
*
* StoryLoader loader = new
* LoadFromRelativeFile(codeLocationFromClass(YourStory.class));
*
* By default, it uses traversal directory
* 'target/test-classes' with source dir in 'src/test/java'.
*
* Other traversal locations can be specified via the varargs constructor:
*
* StoryLoader loader = new
* LoadFromRelativeFile(codeLocationFromClass(YourStory.class),
* mavenModuleTestStoryFilePath("src/test/java"),
* intellijProjectTestStoryFilePath("src/test/java"));
*
* Convenience methods : {@link LoadFromRelativeFile#mavenModuleStoryFilePath},
* {@link LoadFromRelativeFile#mavenModuleTestStoryFilePath}
* {@link LoadFromRelativeFile#intellijProjectStoryFilePath}
* {@link LoadFromRelativeFile#intellijProjectTestStoryFilePath}
*
* @see {@link CodeLocations#codeLocationFromClass(Class)}
*
*/
public class LoadFromRelativeFile implements ResourceLoader, StoryLoader {
private final StoryFilePath[] traversals;
private final URL location;
public LoadFromRelativeFile(URL location) {
this(location, mavenModuleStoryFilePath("src/test/java"));
}
public LoadFromRelativeFile(URL location, StoryFilePath... traversals) {
this.traversals = traversals;
this.location = location;
}
public String loadResourceAsText(String resourcePath) {
List<String> traversalPaths = new ArrayList<String>();
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);
}
public String loadStoryAsText(String storyPath) {
List<String> traversalPaths = new ArrayList<String>();
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 IOUtils.toString(new FileInputStream(new File(path)), true);
} 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);
}
}