Skip to content

Commit

Permalink
Merge 883e110 into c3ca239
Browse files Browse the repository at this point in the history
  • Loading branch information
jmcgarr committed Jan 18, 2019
2 parents c3ca239 + 883e110 commit 653a7a7
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 30 deletions.
89 changes: 73 additions & 16 deletions jbake-core/src/main/java/org/jbake/app/Asset.java
Expand Up @@ -71,35 +71,92 @@ public boolean accept(File file) {
copy(path, config.getDestinationFolder(), filter);
}

private void copy(File sourceFolder, File targetFolder, final FileFilter filter) {
/**
* Copy one asset file at a time.
*
* @param asset The asset file to copy
*/
public void copySingleFile(File asset) {
try {
String targetPath = config.getDestinationFolder().getCanonicalPath() + File.separatorChar + assetSubPath(asset);
LOGGER.info("Copying single asset file to [" + targetPath + "]");
copyFile(asset, new File(targetPath));
} catch (IOException io) {
LOGGER.error("Failed to copy the asset file.", io);
}
}

/**
* Determine if a given file is an asset file.
* @param path to the file to validate.
* @return true if the path provided points to a file in the asset folder.
*/
public boolean isAssetFile(File path) {
boolean isAsset = false;

try {
if(FileUtil.directoryOnlyIfNotIgnored(path.getParentFile())) {
if (FileUtil.isFileInDirectory(path, config.getAssetFolder())) {
isAsset = true;
} else if (FileUtil.isFileInDirectory(path, config.getContentFolder())
&& FileUtil.getNotContentFileFilter().accept(path)) {
isAsset = true;
}
}
} catch (IOException ioe) {
LOGGER.error("Unable to determine the path to asset file {}", path.getPath(), ioe);
}
return isAsset;
}

/**
* Responsible for copying any asset files that exist within the content directory.
*
* @param path of the content directory
*/
public void copyAssetsFromContent(File path) {
copy(path, config.getDestinationFolder(), FileUtil.getNotContentFileFilter());
}

/**
* Accessor method to the collection of errors generated during the bake
*
* @return a list of errors.
*/
public List<Throwable> getErrors() {
return new ArrayList<>(errors);
}

private String assetSubPath(File asset) throws IOException {
// First, strip asset folder from file path
String targetFolder = asset.getCanonicalPath().replace(config.getAssetFolder().getCanonicalPath() + File.separatorChar, "");
// And just to be sure, let's also remove the content folder, as some assets are copied from here.
targetFolder = targetFolder.replace(config.getContentFolder().getCanonicalPath() + File.separatorChar, "");
return targetFolder;
}

private void copy(File sourceFolder, File targetFolder, final FileFilter filter) {
final File[] assets = sourceFolder.listFiles(filter);
if (assets != null) {
Arrays.sort(assets);
for (File asset : assets) {
final File target = new File(targetFolder, asset.getName());
if (asset.isFile()) {
try {
FileUtils.copyFile(asset, target);
LOGGER.info("Copying [{}]... done!", asset.getPath());
} catch (IOException e) {
LOGGER.error("Copying [{}]... failed!", asset.getPath(), e);
errors.add(e);
}
copyFile(asset, target);
} else if (asset.isDirectory()) {
copy(asset, target, filter);
}
}
}
}

public void copyAssetsFromContent(File path) {
copy(path, config.getDestinationFolder(), FileUtil.getNotContentFileFilter());
}


public List<Throwable> getErrors() {
return new ArrayList<>(errors);
private void copyFile(File asset, File targetFolder) {
try {
FileUtils.copyFile(asset, targetFolder);
LOGGER.info("Copying [{}]... done!", asset.getPath());
} catch (IOException e) {
LOGGER.error("Copying [{}]... failed!", asset.getPath(), e);
errors.add(e);
}
}

}
15 changes: 15 additions & 0 deletions jbake-core/src/main/java/org/jbake/app/FileUtil.java
Expand Up @@ -229,4 +229,19 @@ static public String getUriPathToDestinationRoot(JBakeConfiguration config, File
static public String getUriPathToContentRoot(JBakeConfiguration config, File sourceFile) {
return getPathToRoot(config, config.getContentFolder(), sourceFile);
}

/**
* Utility method to determine if a given file is located somewhere in the directory provided.
*
* @param file used to check if it is located in the provided directory.
* @param directory to validate whether or not the provided file resides.
* @return true if the file is somewhere in the provided directory, false if it is not.
* @throws IOException if the canonical path for either of the input directories can't be determined.
*/
public static boolean isFileInDirectory(File file, File directory) throws IOException {
return (file.exists()
&& !file.isHidden()
&& directory.isDirectory()
&& file.getCanonicalPath().startsWith(directory.getCanonicalPath()));
}
}
17 changes: 16 additions & 1 deletion jbake-core/src/main/java/org/jbake/app/Oven.java
Expand Up @@ -114,6 +114,22 @@ private void checkConfiguration(JBakeConfiguration configuration) {
inspector.inspect();
}

/**
* Responsible for incremental baking, typically a single file at a time.
*
* @param fileToBake The file to bake
*/
public void bake(File fileToBake) {
Asset asset = utensils.getAsset();
if(asset.isAssetFile(fileToBake)) {
LOGGER.info("Baking a change to an asset [" + fileToBake.getPath() + "]");
asset.copySingleFile(fileToBake);
} else {
LOGGER.info("Playing it safe and running a full bake...");
bake();
}
}

/**
* All the good stuff happens in here...
*/
Expand Down Expand Up @@ -195,7 +211,6 @@ private void renderContent() {
}
}


public List<Throwable> getErrors() {
return new ArrayList<>(errors);
}
Expand Down
Expand Up @@ -2,11 +2,14 @@

import org.apache.commons.vfs2.FileChangeEvent;
import org.apache.commons.vfs2.FileListener;
import org.apache.commons.vfs2.FileObject;
import org.jbake.app.Oven;
import org.jbake.app.configuration.JBakeConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

public class CustomFSChangeListener implements FileListener {

private final static Logger LOGGER = LoggerFactory.getLogger(CustomFSChangeListener.class);
Expand All @@ -20,24 +23,23 @@ public CustomFSChangeListener(JBakeConfiguration config) {
@Override
public void fileCreated(FileChangeEvent event) throws Exception {
LOGGER.info("File created event detected: {}", event.getFile().getURL());
exec();
exec(event.getFile());
}

@Override
public void fileDeleted(FileChangeEvent event) throws Exception {
LOGGER.info("File deleted event detected: {}", event.getFile().getURL());
exec();
exec(event.getFile());
}

@Override
public void fileChanged(FileChangeEvent event) throws Exception {
LOGGER.info("File changed event detected: {}", event.getFile().getURL());
exec();
exec(event.getFile());
}

private void exec() {
private void exec(FileObject file) {
final Oven oven = new Oven(config);
oven.bake();
oven.bake(new File(file.getName().getPath()));
}

}
48 changes: 42 additions & 6 deletions jbake-core/src/test/java/org/jbake/app/AssetTest.java
Expand Up @@ -20,16 +20,18 @@ public class AssetTest {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
private DefaultJBakeConfiguration config;
private File fixtureDir;

@Before
public void setup() throws Exception {
config = (DefaultJBakeConfiguration) new ConfigUtil().loadConfig(TestUtils.getTestResourcesAsSourceFolder());
fixtureDir = new File(this.getClass().getResource("/fixture").getFile());
config = (DefaultJBakeConfiguration) new ConfigUtil().loadConfig(fixtureDir);
config.setDestinationFolder(folder.getRoot());
Assert.assertEquals(".html", config.getOutputExtension());
}

@Test
public void copy() throws Exception {
public void testCopy() throws Exception {
Asset asset = new Asset(config);
asset.copy();

Expand All @@ -44,8 +46,30 @@ public void copy() throws Exception {
}

@Test
public void copyCustomFolder() throws Exception {
config.setAssetFolder(new File(config.getSourceFolder(), "/media"));
public void testCopySingleFile() throws Exception {
Asset asset = new Asset(config);
String cssSubPath = File.separatorChar + "css" + File.separatorChar + "bootstrap.min.css";
String contentImgPath = File.separatorChar + "blog" + File.separatorChar + "2013" + File.separatorChar
+ "images" + File.separatorChar + "custom-image.jpg";

// Copy single Asset File
File expected = new File(folder.getRoot().getPath() + cssSubPath);
Assert.assertFalse("cssFile should not exist before running the test; avoids false positives", expected.exists());
File cssFile = new File(fixtureDir.getPath() + File.separatorChar + "assets" + cssSubPath);
asset.copySingleFile(cssFile);
Assert.assertTrue("Css asset file did not copy", expected.exists());

// Copy single Content file
expected = new File(folder.getRoot().getPath() + contentImgPath);
Assert.assertFalse("content image file should not exist before running the test", expected.exists());
File imgFile = new File(fixtureDir.getPath() + File.separatorChar + "content" + contentImgPath);
asset.copySingleFile(imgFile);
Assert.assertTrue("Content img file did not copy", expected.exists());
}

@Test
public void testCopyCustomFolder() throws Exception {
config.setAssetFolder(new File(config.getSourceFolder(),"/media"));
Asset asset = new Asset(config);
asset.copy();

Expand All @@ -56,7 +80,7 @@ public void copyCustomFolder() throws Exception {
}

@Test
public void copyIgnore() throws Exception {
public void testCopyIgnore() throws Exception {
File assetFolder = folder.newFolder("ignoredAssets");
FileUtils.copyDirectory(new File(this.getClass().getResource("/fixture/ignorables").getFile()), assetFolder);
config.setAssetFolder(assetFolder);
Expand Down Expand Up @@ -150,7 +174,19 @@ public void testCopyAssetsFromContent() {
Assert.assertTrue("Errors during asset copying", asset.getErrors().isEmpty());
}

private Integer countFiles(File path) {
@Test
public void testIsFileAsset() {
File cssAsset = new File(config.getAssetFolder().getAbsolutePath() + File.separatorChar + "css" + File.separatorChar + "bootstrap.min.css");
Assert.assertTrue(cssAsset.exists());
File contentFile = new File(config.getContentFolder().getAbsolutePath() + File.separatorChar + "about.html");
Assert.assertTrue(contentFile.exists());
Asset asset = new Asset(config);

Assert.assertTrue(asset.isAssetFile(cssAsset));
Assert.assertFalse(asset.isAssetFile(contentFile));
}

private Integer countFiles(File path){
int total = 0;
FileFilter filesOnly = FileFilterUtils.fileFileFilter();
FileFilter dirsOnly = FileFilterUtils.directoryFileFilter();
Expand Down
16 changes: 15 additions & 1 deletion jbake-core/src/test/java/org/jbake/app/FileUtilTest.java
Expand Up @@ -7,8 +7,10 @@

import java.io.File;

import static org.junit.Assert.assertTrue;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

/**
* Created by frank on 28.03.16.
Expand All @@ -23,6 +25,19 @@ public void testGetRunningLocation() throws Exception {
}

@Test
public void testIsFileInDirectory() throws Exception {
File fixtureDir = new File(this.getClass().getResource("/fixture").getFile());
File jbakeFile = new File(fixtureDir.getCanonicalPath() + File.separatorChar + "jbake.properties");
assertTrue("jbake.properties expected to be in /fixture directory", FileUtil.isFileInDirectory(jbakeFile, fixtureDir));

File contentFile = new File(fixtureDir.getCanonicalPath() + File.separatorChar + "content" + File.separatorChar + "projects.html");
assertTrue("projects.html expected to be nested in the /fixture directory", FileUtil.isFileInDirectory(contentFile, fixtureDir));

File contentDir = contentFile.getParentFile();
assertFalse("jbake.properties file should not be in the /fixture/content directory", FileUtil.isFileInDirectory(jbakeFile, contentDir));
}

@Test
public void testGetContentRoothPath() throws Exception {

File source = TestUtils.getTestResourcesAsSourceFolder();
Expand All @@ -38,5 +53,4 @@ public void testGetContentRoothPath() throws Exception {
path = FileUtil.getUriPathToContentRoot(config, new File(config.getContentFolder(), "/blog/level2/index.html"));
assertThat(path).isEqualTo("../../");
}

}

0 comments on commit 653a7a7

Please sign in to comment.