diff --git a/jbake-core/src/main/java/org/jbake/app/Asset.java b/jbake-core/src/main/java/org/jbake/app/Asset.java index 320ab4baa..5d4f7c7e1 100644 --- a/jbake-core/src/main/java/org/jbake/app/Asset.java +++ b/jbake-core/src/main/java/org/jbake/app/Asset.java @@ -2,6 +2,8 @@ import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.io.FileUtils; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,50 +19,61 @@ * Deals with assets (static files such as css, js or image files). * * @author Jonathan Bullock jonbullock@gmail.com - * */ public class Asset { private static final Logger LOGGER = LoggerFactory.getLogger(Asset.class); + private final List errors = new LinkedList<>(); + private JBakeConfiguration config; - private final File source; - private final File destination; - private CompositeConfiguration config; - private final List errors = new LinkedList(); - private final boolean ignoreHidden; + /** + * @param source Source file for the asset + * @param destination Destination (target) directory for asset file + * @param config Project configuration + * @deprecated Use {@link #Asset(JBakeConfiguration)} instead. + * Compatibility constructor. + * Creates an instance of Asset. + */ + @Deprecated + public Asset(File source, File destination, CompositeConfiguration config) { + this.config = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(source, destination, config); + } - /** - * Creates an instance of Asset. - * - * @param source Source file for the asset - * @param destination Destination (target) directory for asset file - * @param config Project configuration - */ - public Asset(File source, File destination, CompositeConfiguration config) { - this.source = source; - this.destination = destination; - this.config = config; - this.ignoreHidden = config.getBoolean(ConfigUtil.Keys.ASSET_IGNORE_HIDDEN, false); - } + /** + * Creates an instance of Asset. + * + * @param config The project configuration. @see{{@link JBakeConfiguration}} + */ + public Asset(JBakeConfiguration config) { + this.config = config; + } + + /** + * Copy all files from assets folder to destination folder + * read from configuration + */ + public void copy() { + copy(config.getAssetFolder()); + } - /** - * Copy all files from supplied path. - * - * @param path The starting path - */ - public void copy(File path) { - FileFilter filter = new FileFilter() { + /** + * Copy all files from supplied path. + * + * @param path The starting path + */ + public void copy(File path) { + FileFilter filter = new FileFilter() { @Override public boolean accept(File file) { - return (!ignoreHidden || !file.isHidden()) && (file.isFile() || FileUtil.directoryOnlyIfNotIgnored(file)); + return (!config.getAssetIgnoreHidden() || !file.isHidden()) && (file.isFile() || FileUtil.directoryOnlyIfNotIgnored(file)); } }; - copy(path, destination, filter); + copy(path, config.getDestinationFolder(), filter); } private void copy(File sourceFolder, File targetFolder, final FileFilter filter) { - - final File[] assets = sourceFolder.listFiles(filter); + + final File[] assets = sourceFolder.listFiles(filter); if (assets != null) { Arrays.sort(assets); for (File asset : assets) { @@ -79,14 +92,14 @@ private void copy(File sourceFolder, File targetFolder, final FileFilter filter) } } } - - public void copyAssetsFromContent(File path){ - copy(path, destination, FileUtil.getNotContentFileFilter()); + + public void copyAssetsFromContent(File path) { + copy(path, config.getDestinationFolder(), FileUtil.getNotContentFileFilter()); } - - public List getErrors() { - return new ArrayList(errors); - } + + public List getErrors() { + return new ArrayList<>(errors); + } } diff --git a/jbake-core/src/main/java/org/jbake/app/ConfigUtil.java b/jbake-core/src/main/java/org/jbake/app/ConfigUtil.java index 8ae065bcc..e69de29bb 100644 --- a/jbake-core/src/main/java/org/jbake/app/ConfigUtil.java +++ b/jbake-core/src/main/java/org/jbake/app/ConfigUtil.java @@ -1,227 +0,0 @@ -package org.jbake.app; - -import org.apache.commons.configuration.CompositeConfiguration; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.commons.configuration.PropertiesConfiguration; -import org.apache.commons.configuration.SystemConfiguration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; - -/** - * Provides Configuration related functions. - * - * @author Jonathan Bullock jonbullock@gmail.com - */ -public class ConfigUtil { - /** - * Set of config keys used by JBake - * @author ndx - * - */ - public interface Keys { - /** - * Output filename for archive file, is only used when {@link #RENDER_ARCHIVE} is true - */ - String ARCHIVE_FILE = "archive.file"; - /** - * Asciidoctor attributes to be set when processing input - */ - String ASCIIDOCTOR_ATTRIBUTES = "asciidoctor.attributes"; - /** - * Flag indicating if JBake properties should be made available to Asciidoctor - */ - String ASCIIDOCTOR_ATTRIBUTES_EXPORT = "asciidoctor.attributes.export"; - /** - * Prefix to be used when exporting JBake properties to Asciidoctor - */ - String ASCIIDOCTOR_ATTRIBUTES_EXPORT_PREFIX = "asciidoctor.attributes.export.prefix"; - /** - * Asciidoctor options to be set when processing input - */ - String ASCIIDOCTOR_OPTION = "asciidoctor.option"; - /** - * Folder where assets are stored, they are copied directly in output folder and not processed - */ - String ASSET_FOLDER = "asset.folder"; - /** - * Flag indicating if content matching prefix below should be given extension-less URI's - */ - String URI_NO_EXTENSION = "uri.noExtension"; - /** - * URI prefix for content that should be given extension-less output URI's - */ - String URI_NO_EXTENSION_PREFIX = "uri.noExtension.prefix"; - /** - * Flag indicating if hidden asset resources should be ignored - */ - String ASSET_IGNORE_HIDDEN = "asset.ignore"; - /** - * Timestamp that records when JBake build was made - */ - String BUILD_TIMESTAMP = "build.timestamp"; - /** - * Folder where content (that's to say files to be transformed) resides in - */ - String CONTENT_FOLDER = "content.folder"; - /** - * How date is formated - */ - String DATE_FORMAT = "date.format"; - /** - * Folder to store database files in - */ - String DB_PATH = "db.path"; - /** - * Flag to identify if database is kept in memory (memory) or persisted to disk (local) - */ - String DB_STORE = "db.store"; - /** - * Default status to use (in order to avoid putting it in all files) - */ - String DEFAULT_STATUS = "default.status"; - /** - * Default type to use (in order to avoid putting it in all files) - */ - String DEFAULT_TYPE = "default.type"; - /** - * Folder where rendered files are output - */ - String DESTINATION_FOLDER = "destination.folder"; - /** - * Suffix used to identify draft files - */ - String DRAFT_SUFFIX = "draft.suffix"; - /** - * Output filename for feed file, is only used when {@link #RENDER_FEED} is true - */ - String FEED_FILE = "feed.file"; - /** - * Output filename for index, is only used when {@link #RENDER_INDEX} is true - */ - String INDEX_FILE = "index.file"; - /** - * File extension to be used for all output files - */ - String OUTPUT_EXTENSION = "output.extension"; - /** - * Flag indicating if archive file should be generated - */ - String RENDER_ARCHIVE = "render.archive"; - /** - * Encoding used when rendering files - */ - String RENDER_ENCODING = "render.encoding"; - /** - * Flag indicating if feed file should be generated - */ - String RENDER_FEED = "render.feed"; - /** - * Flag indicating if index file should be generated - */ - String RENDER_INDEX = "render.index"; - /** - * Flag indicating if sitemap file should be generated - */ - String RENDER_SITEMAP = "render.sitemap"; - /** - * Flag indicating if tag files should be generated - */ - String RENDER_TAGS = "render.tags"; - /** - * Flag indicating if tag index file should be generated - */ - String RENDER_TAGS_INDEX = "render.tagsindex"; - /** - * String used to separate the header from the body. - */ - String HEADER_SEPARATOR = "header.separator"; - /** - * Port used when running Jetty server - */ - String SERVER_PORT = "server.port"; - /** - * Sitemap template file name. Used only when {@link #RENDER_SITEMAP} is set to true - */ - String SITEMAP_FILE = "sitemap.file"; - /** - * Tags output path, used only when {@link #RENDER_TAGS} is true - */ - String TAG_PATH = "tag.path"; - /** - * Should the tag value be sanitized? - */ - String TAG_SANITIZE = "tag.sanitize"; - /** - * Encoding to be used for template files - */ - String TEMPLATE_ENCODING = "template.encoding"; - /** - * Folder where template files are looked for - */ - String TEMPLATE_FOLDER = "template.folder"; - /** - * Locale used for Thymeleaf template rendering - */ - String THYMELEAF_LOCALE = "thymeleaf.locale"; - /** - * Version of JBake - */ - String VERSION = "version"; - /** - * Flag indicating if there should be pagination when rendering index - */ - String PAGINATE_INDEX = "index.paginate"; - /** - * How many posts per page on index - */ - String POSTS_PER_PAGE = "index.posts_per_page"; - /** - * The configured base URI for the hosted content - */ - String SITE_HOST = "site.host"; - } - - private final static Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class); - private final static String LEGACY_CONFIG_FILE = "custom.properties"; - private final static String CONFIG_FILE = "jbake.properties"; - private final static String DEFAULT_CONFIG_FILE = "default.properties"; - private static boolean LEGACY_CONFIG_FILE_EXISTS = false; - private static boolean LEGACY_CONFIG_FILE_WARNING_SHOWN = false; - - public static CompositeConfiguration load(File source) throws ConfigurationException { - return load(source, false); - } - - public static CompositeConfiguration load(File source, boolean isRunServer) throws ConfigurationException { - CompositeConfiguration config = new CompositeConfiguration(); - config.setListDelimiter(','); - File customConfigFile = new File(source, LEGACY_CONFIG_FILE); - if (customConfigFile.exists()) { - LEGACY_CONFIG_FILE_EXISTS = true; - config.addConfiguration(new PropertiesConfiguration(customConfigFile)); - } - customConfigFile = new File(source, CONFIG_FILE); - if (customConfigFile.exists()) { - config.addConfiguration(new PropertiesConfiguration(customConfigFile)); - } - config.addConfiguration(new PropertiesConfiguration(DEFAULT_CONFIG_FILE)); - config.addConfiguration(new SystemConfiguration()); - if (isRunServer) { - String port = config.getString(Keys.SERVER_PORT); - config.setProperty(Keys.SITE_HOST, "http://localhost:"+port); - } - return config; - } - public static void displayLegacyConfigFileWarningIfRequired() { - if (LEGACY_CONFIG_FILE_EXISTS) { - if (!LEGACY_CONFIG_FILE_WARNING_SHOWN) { - LOGGER.warn("You have defined a part of your JBake configuration in " + LEGACY_CONFIG_FILE); - LOGGER.warn("Usage of this file is being deprecated, please rename this file to: " + CONFIG_FILE + " to remove this warning"); - LEGACY_CONFIG_FILE_WARNING_SHOWN = true; - } - } - } - -} diff --git a/jbake-core/src/main/java/org/jbake/app/ContentStore.java b/jbake-core/src/main/java/org/jbake/app/ContentStore.java index 220d37603..f24ba540d 100644 --- a/jbake-core/src/main/java/org/jbake/app/ContentStore.java +++ b/jbake-core/src/main/java/org/jbake/app/ContentStore.java @@ -40,6 +40,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -51,12 +52,45 @@ */ public class ContentStore { + private static final String STATEMENT_GET_PUBLISHED_POST_BY_TYPE_AND_TAG = "select * from %s where status='published' and ? in tags order by date desc"; + private static final String STATEMENT_GET_DOCUMENT_STATUS_BY_DOCTYPE_AND_URI = "select sha1,rendered from %s where sourceuri=?"; + private static final String STATEMENT_GET_PUBLISHED_COUNT = "select count(*) as count from %s where status='published'"; + private static final String STATEMENT_MARK_CONTENT_AS_RENDERD = "update %s set rendered=true where rendered=false and cached=true"; + private static final String STATEMENT_DELETE_DOCTYPE_BY_SOURCEURI = "delete from %s where sourceuri=?"; + private static final String STATEMENT_GET_UNDRENDERED_CONTENT = "select * from %s where rendered=false order by date desc"; + private static final String STATEMENT_GET_SIGNATURE_FOR_TEMPLATES = "select sha1 from Signatures where key='templates'"; + private static final String STATEMENT_GET_TAGS_FROM_PUBLISHED_POSTS = "select tags from post where status='published'"; + private static final String STATEMENT_GET_ALL_CONTENT_BY_DOCTYPE = "select * from %s order by date desc"; + private static final String STATEMENT_GET_PUBLISHED_CONTENT_BY_DOCTYPE = "select * from %s where status='published' order by date desc"; + private static final String STATEMENT_GET_PUBLISHED_POSTS_BY_TAG = "select * from post where status='published' and ? in tags order by date desc"; + private static final String STATEMENT_GET_TAGS_BY_DOCTYPE = "select tags from %s where status='published'"; + private static final String STATEMENT_INSERT_TEMPLATES_SIGNATURE = "insert into Signatures(key,sha1) values('templates',?)"; + private static final String STATEMENT_DELETE_ALL = "delete from %s"; + private static final String STATEMENT_UPDATE_TEMPLATE_SIGNATURE = "update Signatures set sha1=? where key='templates'"; + private final Logger logger = LoggerFactory.getLogger(ContentStore.class); + private final String type; + private final String name; + private ODatabaseDocumentTx db; + private long start = -1; private long limit = -1; public ContentStore(final String type, String name) { + this.type = type; + this.name = name; + } + + public ODatabaseDocumentTx getDb() { + return db; + } + + public void setDb(ODatabaseDocumentTx db) { + this.db = db; + } + + public void startup() { startupIfEnginesAreMissing(); OPartitionedDatabasePool pool = new OPartitionedDatabasePoolFactory().get(type + ":" + name, "admin", "admin"); pool.setAutoCreate(true); @@ -105,8 +139,10 @@ public final void updateSchema() { } public void close() { - activateOnCurrentThread(); - db.close(); + if (db != null) { + activateOnCurrentThread(); + db.close(); + } DBUtil.closeDataStore(); } @@ -143,11 +179,13 @@ public long getDocumentCount(String docType) { } public long getPublishedCount(String docType) { - return (Long) query("select count(*) as count from " + docType + " where status='published'").get(0).get("count"); + String statement = String.format(STATEMENT_GET_PUBLISHED_COUNT, docType); + return (Long) query(statement).get(0).get("count"); } public DocumentList getDocumentStatus(String docType, String uri) { - return query("select sha1,rendered from " + docType + " where sourceuri=?", uri); + String statement = String.format(STATEMENT_GET_DOCUMENT_STATUS_BY_DOCTYPE_AND_URI, docType); + return query(statement, uri); } public DocumentList getPublishedPosts() { @@ -159,13 +197,15 @@ public DocumentList getPublishedPosts(boolean applyPaging) { } public DocumentList getPublishedPostsByTag(String tag) { - return query("select * from post where status='published' and ? in tags order by date desc", tag); + return query(STATEMENT_GET_PUBLISHED_POSTS_BY_TAG, tag); } public DocumentList getPublishedDocumentsByTag(String tag) { final DocumentList documents = new DocumentList(); + for (final String docType : DocumentTypes.getDocumentTypes()) { - DocumentList documentsByTag = query("select * from " + docType + " where status='published' and ? in tags order by date desc", tag); + String statement = String.format(STATEMENT_GET_PUBLISHED_POST_BY_TYPE_AND_TAG, docType); + DocumentList documentsByTag = query(statement, tag); documents.addAll(documentsByTag); } return documents; @@ -179,8 +219,8 @@ public DocumentList getPublishedContent(String docType) { return getPublishedContent(docType, false); } - public DocumentList getPublishedContent(String docType, boolean applyPaging) { - String query = "select * from " + docType + " where status='published' order by date desc"; + private DocumentList getPublishedContent(String docType, boolean applyPaging) { + String query = String.format(STATEMENT_GET_PUBLISHED_CONTENT_BY_DOCTYPE, docType); if (applyPaging && hasStartAndLimitBoundary()) { query += " SKIP " + start + " LIMIT " + limit; } @@ -192,7 +232,7 @@ public DocumentList getAllContent(String docType) { } public DocumentList getAllContent(String docType, boolean applyPaging) { - String query = "select * from " + docType + " order by date desc"; + String query = String.format(STATEMENT_GET_ALL_CONTENT_BY_DOCTYPE, docType); if (applyPaging && hasStartAndLimitBoundary()) { query += " SKIP " + start + " LIMIT " + limit; } @@ -203,36 +243,40 @@ private boolean hasStartAndLimitBoundary() { return (start >= 0) && (limit > -1); } - public DocumentList getAllTagsFromPublishedPosts() { - return query("select tags from post where status='published'"); + private DocumentList getAllTagsFromPublishedPosts() { + return query(STATEMENT_GET_TAGS_FROM_PUBLISHED_POSTS); } - public DocumentList getSignaturesForTemplates() { - return query("select sha1 from Signatures where key='templates'"); + private DocumentList getSignaturesForTemplates() { + return query(STATEMENT_GET_SIGNATURE_FOR_TEMPLATES); } public DocumentList getUnrenderedContent(String docType) { - return query("select * from " + docType + " where rendered=false order by date desc"); + String statement = String.format(STATEMENT_GET_UNDRENDERED_CONTENT, docType); + return query(statement); } public void deleteContent(String docType, String uri) { - executeCommand("delete from " + docType + " where sourceuri=?", uri); + String statement = String.format(STATEMENT_DELETE_DOCTYPE_BY_SOURCEURI, docType); + executeCommand(statement, uri); } public void markContentAsRendered(String docType) { - executeCommand("update " + docType + " set rendered=true where rendered=false and cached=true"); + String statement = String.format(STATEMENT_MARK_CONTENT_AS_RENDERD, docType); + executeCommand(statement); } - public void updateSignatures(String currentTemplatesSignature) { - executeCommand("update Signatures set sha1=? where key='templates'", currentTemplatesSignature); + private void updateSignatures(String currentTemplatesSignature) { + executeCommand(STATEMENT_UPDATE_TEMPLATE_SIGNATURE, currentTemplatesSignature); } public void deleteAllByDocType(String docType) { - executeCommand("delete from " + docType); + String statement = String.format(STATEMENT_DELETE_ALL, docType); + executeCommand(statement); } - public void insertSignature(String currentTemplatesSignature) { - executeCommand("insert into Signatures(key,sha1) values('templates',?)", currentTemplatesSignature); + private void insertTemplatesSignature(String currentTemplatesSignature) { + executeCommand(STATEMENT_INSERT_TEMPLATES_SIGNATURE, currentTemplatesSignature); } private DocumentList query(String sql) { @@ -265,7 +309,8 @@ public Set getTags() { public Set getAllTags() { Set result = new HashSet<>(); for (String docType : DocumentTypes.getDocumentTypes()) { - DocumentList docs = query("select tags from " + docType + " where status='published'"); + String statement = String.format(STATEMENT_GET_TAGS_BY_DOCTYPE, docType); + DocumentList docs = query(statement); for (Map document : docs) { String[] tags = DBUtil.toStringArray(document.get(Crawler.Attributes.TAGS)); Collections.addAll(result, tags); @@ -274,20 +319,20 @@ public Set getAllTags() { return result; } - private void createDocType(final OSchema schema, final String doctype) { - logger.debug("Create document class '{}'", doctype); + private void createDocType(final OSchema schema, final String docType) { + logger.debug("Create document class '{}'", docType); - OClass page = schema.createClass(doctype); + OClass page = schema.createClass(docType); page.createProperty(String.valueOf(DocumentAttributes.SHA1), OType.STRING).setNotNull(true); - page.createIndex(doctype + "sha1Index", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.SHA1.toString()); + page.createIndex(docType + "sha1Index", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.SHA1.toString()); page.createProperty(String.valueOf(DocumentAttributes.SOURCE_URI), OType.STRING).setNotNull(true); - page.createIndex(doctype + "sourceUriIndex", OClass.INDEX_TYPE.UNIQUE, DocumentAttributes.SOURCE_URI.toString()); + page.createIndex(docType + "sourceUriIndex", OClass.INDEX_TYPE.UNIQUE, DocumentAttributes.SOURCE_URI.toString()); page.createProperty(String.valueOf(DocumentAttributes.CACHED), OType.BOOLEAN).setNotNull(true); - page.createIndex(doctype + "cachedIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.CACHED.toString()); + page.createIndex(docType + "cachedIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.CACHED.toString()); page.createProperty(String.valueOf(DocumentAttributes.RENDERED), OType.BOOLEAN).setNotNull(true); - page.createIndex(doctype + "renderedIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.RENDERED.toString()); + page.createIndex(docType + "renderedIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.RENDERED.toString()); page.createProperty(String.valueOf(DocumentAttributes.STATUS), OType.STRING).setNotNull(true); - page.createIndex(doctype + "statusIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.STATUS.toString()); + page.createIndex(docType + "statusIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.STATUS.toString()); } private void createSignatureType(OSchema schema) { @@ -295,4 +340,56 @@ private void createSignatureType(OSchema schema) { signatures.createProperty(String.valueOf(DocumentAttributes.SHA1), OType.STRING).setNotNull(true); signatures.createIndex("sha1Idx", OClass.INDEX_TYPE.UNIQUE, DocumentAttributes.SHA1.toString()); } + + public void updateAndClearCacheIfNeeded(boolean needed, File templateFolder) { + + boolean clearCache = needed; + + if (!needed) { + clearCache = updateTemplateSignatureIfChanged(templateFolder); + } + + if (clearCache) { + deleteAllDocumentTypes(); + this.updateSchema(); + } + } + + private boolean updateTemplateSignatureIfChanged(File templateFolder) { + boolean templateSignatureChanged = false; + + DocumentList docs = this.getSignaturesForTemplates(); + String currentTemplatesSignature; + try { + currentTemplatesSignature = FileUtil.sha1(templateFolder); + } catch (Exception e) { + currentTemplatesSignature = ""; + } + if (!docs.isEmpty()) { + String sha1 = (String) docs.get(0).get(String.valueOf(DocumentAttributes.SHA1)); + if (!sha1.equals(currentTemplatesSignature)) { + this.updateSignatures(currentTemplatesSignature); + templateSignatureChanged = true; + } + } else { + // first computation of templates signature + this.insertTemplatesSignature(currentTemplatesSignature); + templateSignatureChanged = true; + } + return templateSignatureChanged; + } + + private void deleteAllDocumentTypes() { + for (String docType : DocumentTypes.getDocumentTypes()) { + try { + this.deleteAllByDocType(docType); + } catch (Exception e) { + // maybe a non existing document type + } + } + } + + public boolean isActive() { + return db.isActiveOnCurrentThread(); + } } diff --git a/jbake-core/src/main/java/org/jbake/app/Crawler.java b/jbake-core/src/main/java/org/jbake/app/Crawler.java index d80a326ea..3904d7248 100644 --- a/jbake-core/src/main/java/org/jbake/app/Crawler.java +++ b/jbake-core/src/main/java/org/jbake/app/Crawler.java @@ -1,14 +1,11 @@ package org.jbake.app; -import java.io.File; -import java.util.Arrays; -import java.util.Date; -import java.util.Map; - +import com.orientechnologies.orient.core.record.impl.ODocument; import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.io.FilenameUtils; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.Crawler.Attributes.Status; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; import org.jbake.model.DocumentAttributes; import org.jbake.model.DocumentStatus; import org.jbake.model.DocumentTypes; @@ -16,7 +13,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.orientechnologies.orient.core.record.impl.ODocument; +import java.io.File; +import java.util.Arrays; +import java.util.Date; +import java.util.Map; /** * Crawls a file system looking for content. @@ -24,54 +24,51 @@ * @author Jonathan Bullock jonbullock@gmail.com */ public class Crawler { - public interface Attributes { - /** - * Possible values of the {@link Attributes#STATUS} property - * - * @author ndx - */ - interface Status { - String PUBLISHED_DATE = "published-date"; - String PUBLISHED = "published"; - String DRAFT = "draft"; - } - - String DATE = "date"; - String STATUS = "status"; - String TYPE = "type"; - String TITLE = "title"; - String URI = "uri"; - String FILE = "file"; - String TAGS = "tags"; - String TAG = "tag"; - String ROOTPATH = "rootpath"; - String ID = "id"; - String NO_EXTENSION_URI = "noExtensionUri"; - String ALLTAGS = "alltags"; - String PUBLISHED_DATE = "published_date"; - String BODY = "body"; - String DB = "db"; - } private static final Logger LOGGER = LoggerFactory.getLogger(Crawler.class); - - private CompositeConfiguration config; - private Parser parser; + public static final String URI_SEPARATOR_CHAR = "/"; private final ContentStore db; - private String contentPath; + private JBakeConfiguration config; + private Parser parser; /** - * Creates new instance of Crawler. - * * @param db Database instance for content * @param source Base directory where content directory is located * @param config Project configuration + * @deprecated Use {@link #Crawler(ContentStore, JBakeConfiguration)} instead. + *

+ * Creates new instance of Crawler. */ + @Deprecated public Crawler(ContentStore db, File source, CompositeConfiguration config) { + this.db = db; + this.config = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(source, config); + this.parser = new Parser(this.config); + } + + /** + * Creates new instance of Crawler. + * + * @param db Database instance for content + * @param config Project configuration + */ + public Crawler(ContentStore db, JBakeConfiguration config) { this.db = db; this.config = config; - this.contentPath = FilenameUtils.concat(source.getAbsolutePath(), config.getString(ConfigUtil.Keys.CONTENT_FOLDER)); - this.parser = new Parser(config, contentPath); + this.parser = new Parser(config); + } + + public void crawl() { + crawl(config.getContentFolder()); + + LOGGER.info("Content detected:"); + for (String docType : DocumentTypes.getDocumentTypes()) { + long count = db.getDocumentCount(docType); + if (count > 0) { + LOGGER.info("Parsed {} files of type: {}", count, docType); + } + } + } /** @@ -79,7 +76,7 @@ public Crawler(ContentStore db, File source, CompositeConfiguration config) { * * @param path Folder to start from */ - public void crawl(File path) { + private void crawl(File path) { File[] contents = path.listFiles(FileUtil.getFileFilter()); if (contents != null) { Arrays.sort(contents); @@ -93,14 +90,13 @@ public void crawl(File path) { DocumentStatus status = DocumentStatus.NEW; for (String docType : DocumentTypes.getDocumentTypes()) { status = findDocumentStatus(docType, uri, sha1); - switch (status) { - case UPDATED: - sb.append(" : modified "); - db.deleteContent(docType, uri); - break; - case IDENTICAL: - sb.append(" : same "); - process = false; + if (status == DocumentStatus.UPDATED) { + sb.append(" : modified "); + db.deleteContent(docType, uri); + + } else if (status == DocumentStatus.IDENTICAL) { + sb.append(" : same "); + process = false; } if (!process) { break; @@ -126,17 +122,19 @@ private String buildHash(final File sourceFile) { try { sha1 = FileUtil.sha1(sourceFile); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error("unable to build sha1 hash for source file '{}'", sourceFile); sha1 = ""; } return sha1; } private String buildURI(final File sourceFile) { - String uri = FileUtil.asPath(sourceFile.getPath()) - .replace(FileUtil.asPath(contentPath), "") - // On windows we have to replace the backslash - .replace(File.separator, "/"); + String uri = FileUtil.asPath(sourceFile).replace(FileUtil.asPath(config.getContentFolder()), ""); + + // On windows we have to replace the backslash + if (!File.separator.equals(URI_SEPARATOR_CHAR)) { + uri = uri.replace(File.separator, URI_SEPARATOR_CHAR); + } if (useNoExtensionUri(uri)) { // convert URI from xxx.html to xxx/index.html @@ -146,27 +144,29 @@ private String buildURI(final File sourceFile) { } // strip off leading / to enable generating non-root based sites - if (uri.startsWith("/")) { + if (uri.startsWith(URI_SEPARATOR_CHAR)) { uri = uri.substring(1, uri.length()); } + return uri; } private String createUri(String uri) { - return uri.substring(0, uri.lastIndexOf('.')) + config.getString(Keys.OUTPUT_EXTENSION); + return uri.substring(0, uri.lastIndexOf('.')) + config.getOutputExtension(); } private String createNoExtensionUri(String uri) { - return "/" + return URI_SEPARATOR_CHAR + FilenameUtils.getPath(uri) + FilenameUtils.getBaseName(uri) - + "/index" - + config.getString(Keys.OUTPUT_EXTENSION); + + URI_SEPARATOR_CHAR + + "index" + + config.getOutputExtension(); } private boolean useNoExtensionUri(String uri) { - boolean noExtensionUri = config.getBoolean(Keys.URI_NO_EXTENSION); - String noExtensionUriPrefix = config.getString(Keys.URI_NO_EXTENSION_PREFIX); + boolean noExtensionUri = config.getUriWithoutExtension(); + String noExtensionUriPrefix = config.getPrefixForUriWithoutExtension(); return noExtensionUri && (noExtensionUriPrefix != null) @@ -198,13 +198,13 @@ private void crawlSourceFile(final File sourceFile, final String sha1, final Str } } - if (config.getBoolean(Keys.URI_NO_EXTENSION)) { + if (config.getUriWithoutExtension()) { fileContents.put(Attributes.NO_EXTENSION_URI, uri.replace("/index.html", "/")); } - - + + // Prevent image source url's from breaking - HtmlUtil.fixImageSourceUrls(fileContents,config); + HtmlUtil.fixImageSourceUrls(fileContents, config); ODocument doc = new ODocument(documentType); doc.fromMap(fileContents); @@ -216,22 +216,22 @@ private void crawlSourceFile(final File sourceFile, final String sha1, final Str } } - public String getPathToRoot(File sourceFile) { - File rootPath = new File(contentPath); - File parentPath = sourceFile.getParentFile(); - int parentCount = 0; - while (!parentPath.equals(rootPath)) { - parentPath = parentPath.getParentFile(); - parentCount++; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < parentCount; i++) { - sb.append("../"); - } - if (config.getBoolean(Keys.URI_NO_EXTENSION)) { - sb.append("../"); + private String getPathToRoot(File sourceFile) { + File rootPath = config.getContentFolder(); + File parentPath = sourceFile.getParentFile(); + int parentCount = 0; + while (!parentPath.equals(rootPath)) { + parentPath = parentPath.getParentFile(); + parentCount++; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < parentCount; i++) { + sb.append("../"); + } + if (config.getUriWithoutExtension()) { + sb.append("../"); } - return sb.toString(); + return sb.toString(); } private DocumentStatus findDocumentStatus(String docType, String uri, String sha1) { @@ -248,4 +248,41 @@ private DocumentStatus findDocumentStatus(String docType, String uri, String sha return DocumentStatus.NEW; } } + + public abstract static class Attributes { + + public static final String DATE = "date"; + public static final String STATUS = "status"; + public static final String TYPE = "type"; + public static final String TITLE = "title"; + public static final String URI = "uri"; + public static final String FILE = "file"; + public static final String TAGS = "tags"; + public static final String TAG = "tag"; + public static final String ROOTPATH = "rootpath"; + public static final String ID = "id"; + public static final String NO_EXTENSION_URI = "noExtensionUri"; + public static final String ALLTAGS = "alltags"; + public static final String PUBLISHED_DATE = "published_date"; + public static final String BODY = "body"; + public static final String DB = "db"; + + /** + * Possible values of the {@link Attributes#STATUS} property + * + * @author ndx + */ + public abstract static class Status { + public static final String PUBLISHED_DATE = "published-date"; + public static final String PUBLISHED = "published"; + public static final String DRAFT = "draft"; + + private Status() { + } + } + + private Attributes() { + } + + } } diff --git a/jbake-core/src/main/java/org/jbake/app/DBUtil.java b/jbake-core/src/main/java/org/jbake/app/DBUtil.java index 0446bcc96..efb0593bf 100644 --- a/jbake-core/src/main/java/org/jbake/app/DBUtil.java +++ b/jbake-core/src/main/java/org/jbake/app/DBUtil.java @@ -2,6 +2,7 @@ import com.orientechnologies.orient.core.db.record.OTrackedList; import com.orientechnologies.orient.core.record.impl.ODocument; +import org.jbake.app.configuration.JBakeConfiguration; import java.util.HashMap; import java.util.Iterator; @@ -9,24 +10,34 @@ public class DBUtil { private static ContentStore contentStore; - + + @Deprecated public static ContentStore createDataStore(final String type, String name) { if (contentStore == null) { contentStore = new ContentStore(type, name); } return contentStore; } - - public static void closeDataStore() { - contentStore = null; - } - + + @Deprecated public static void updateSchema(final ContentStore db) { db.updateSchema(); } + public static ContentStore createDataStore(JBakeConfiguration configuration) { + if (contentStore == null) { + contentStore = new ContentStore(configuration.getDatabaseStore(), configuration.getDatabasePath()); + } + + return contentStore; + } + + public static void closeDataStore() { + contentStore = null; + } + public static Map documentToModel(ODocument doc) { - Map result = new HashMap(); + Map result = new HashMap<>(); Iterator> fieldIterator = doc.iterator(); while (fieldIterator.hasNext()) { Map.Entry entry = fieldIterator.next(); diff --git a/jbake-core/src/main/java/org/jbake/app/FileUtil.java b/jbake-core/src/main/java/org/jbake/app/FileUtil.java index 495eb45ee..416c83ecb 100644 --- a/jbake-core/src/main/java/org/jbake/app/FileUtil.java +++ b/jbake-core/src/main/java/org/jbake/app/FileUtil.java @@ -1,7 +1,5 @@ package org.jbake.app; -import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.parser.Engines; import java.io.File; @@ -31,16 +29,16 @@ public static FileFilter getFileFilter() { @Override public boolean accept(File pathname) { //Accept if input is a non-hidden file with registered extension - //or if a non-hidden and not-ignored directory + //or if a non-hidden and not-ignored directory return !pathname.isHidden() && (pathname.isFile() && Engines.getRecognizedExtensions().contains(fileExt(pathname))) || (directoryOnlyIfNotIgnored(pathname)); } }; } - + /** * Gets the list of files that are not content files based on their extension. - * + * * @return FileFilter object */ public static FileFilter getNotContentFileFilter() { @@ -49,15 +47,15 @@ public static FileFilter getNotContentFileFilter() { @Override public boolean accept(File pathname) { //Accept if input is a non-hidden file with NOT-registered extension - //or if a non-hidden and not-ignored directory + //or if a non-hidden and not-ignored directory return !pathname.isHidden() && (pathname.isFile() //extension should not be from registered content extensions - && !Engines.getRecognizedExtensions().contains(fileExt(pathname))) + && !Engines.getRecognizedExtensions().contains(fileExt(pathname))) || (directoryOnlyIfNotIgnored(pathname)); } }; } - + /** * Ignores directory (and children) if it contains a file named ".jbakeignore". * @param file {@link File} @@ -65,16 +63,16 @@ public boolean accept(File pathname) { */ public static boolean directoryOnlyIfNotIgnored(File file){ boolean accept = false; - + FilenameFilter ignoreFile = new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.equalsIgnoreCase(".jbakeignore"); } - }; - + }; + accept = file.isDirectory() && (file.listFiles(ignoreFile).length == 0); - + return accept; } @@ -157,15 +155,6 @@ private static void updateDigest(final MessageDigest digest, final File sourceFi } } - public static String findExtension(CompositeConfiguration config, String docType) { - String extension = config.getString("template."+docType+".extension"); - if (extension != null) { - return extension; - } else { - return config.getString(Keys.OUTPUT_EXTENSION); - } - } - /** * platform independent file.getPath() * diff --git a/jbake-core/src/main/java/org/jbake/app/Oven.java b/jbake-core/src/main/java/org/jbake/app/Oven.java index c62de212d..9eb9a6781 100644 --- a/jbake-core/src/main/java/org/jbake/app/Oven.java +++ b/jbake-core/src/main/java/org/jbake/app/Oven.java @@ -1,10 +1,10 @@ package org.jbake.app; import org.apache.commons.configuration.CompositeConfiguration; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.commons.io.FilenameUtils; -import org.jbake.app.ConfigUtil.Keys; -import org.jbake.model.DocumentAttributes; +import org.jbake.app.configuration.DefaultJBakeConfiguration; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; +import org.jbake.app.configuration.JBakeConfigurationInspector; import org.jbake.model.DocumentTypes; import org.jbake.render.RenderingTool; import org.jbake.template.ModelExtractors; @@ -14,9 +14,11 @@ import org.slf4j.LoggerFactory; import java.io.File; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.ServiceLoader; /** * All the baking happens in the Oven! @@ -25,158 +27,133 @@ */ public class Oven { - private final static Logger LOGGER = LoggerFactory.getLogger(Oven.class); + private static final Logger LOGGER = LoggerFactory.getLogger(Oven.class); - private final static Pattern TEMPLATE_DOC_PATTERN = Pattern.compile("(?:template\\.)([a-zA-Z0-9-_]+)(?:\\.file)"); - - private CompositeConfiguration config; - private File source; - private File destination; - private File templatesPath; - private File contentsPath; - private File assetsPath; - private boolean isClearCache; - private List errors = new LinkedList(); - private int renderedCount = 0; + private Utensils utensils; + private List errors = new LinkedList<>(); + private int renderedCount = 0; /** + * @param source Project source directory + * @param destination The destination folder + * @param isClearCache Should the cache be cleaned + * @throws Exception if configuration is not loaded correctly + * @deprecated Use {@link #Oven(JBakeConfiguration)} instead * Delegate c'tor to prevent API break for the moment. - * - * @param source Project source directory - * @param destination The destination folder - * @param isClearCache Should the cache be cleaned - * @throws ConfigurationException if configuration is not loaded correctly */ - public Oven(final File source, final File destination, final boolean isClearCache) throws ConfigurationException { - this(source, destination, ConfigUtil.load(source), isClearCache); + @Deprecated + public Oven(final File source, final File destination, final boolean isClearCache) throws Exception { + this(new JBakeConfigurationFactory().createDefaultJbakeConfiguration(source, destination, isClearCache)); } /** + * @param source Project source directory + * @param destination The destination folder + * @param config Project configuration + * @param isClearCache Should the cache be cleaned + * @throws Exception if configuration is not loaded correctly + * @deprecated Use {@link #Oven(JBakeConfiguration)} instead * Creates a new instance of the Oven with references to the source and destination folders. - * - * @param source Project source directory - * @param destination The destination folder - * @param config Project configuration - * @param isClearCache Should the cache be cleaned */ - public Oven(final File source, final File destination, final CompositeConfiguration config, final boolean isClearCache) { - this.source = source; - this.destination = destination; - this.config = config; - this.isClearCache = isClearCache; + @Deprecated + public Oven(final File source, final File destination, final CompositeConfiguration config, final boolean isClearCache) throws Exception { + this(new JBakeConfigurationFactory().createDefaultJbakeConfiguration(source, destination, config, isClearCache)); } - public CompositeConfiguration getConfig() { - return config; + /** + * Create an Oven instance by a {@link JBakeConfiguration} + *

+ * It creates default {@link Utensils} needed to bake sites. + * + * @param config The project configuration. see {@link JBakeConfiguration} + */ + public Oven(JBakeConfiguration config) { + this.utensils = UtensilsFactory.createDefaultUtensils(config); } - // TODO: do we want to use this. Else, config could be final - public void setConfig(final CompositeConfiguration config) { - this.config = config; + /** + * Create an Oven instance with given {@link Utensils} + * + * @param utensils All Utensils necessary to bake + */ + public Oven(Utensils utensils) { + checkConfiguration(utensils.getConfiguration()); + this.utensils = utensils; } - private void ensureSource() { - if (!FileUtil.isExistingFolder(source)) { - throw new JBakeException("Error: Source folder must exist: " + source.getAbsolutePath()); - } - if (!source.canRead()) { - throw new JBakeException("Error: Source folder is not readable: " + source.getAbsolutePath()); - } + @Deprecated + public CompositeConfiguration getConfig() { + return ((DefaultJBakeConfiguration) utensils.getConfiguration()).getCompositeConfiguration(); } - private void ensureDestination() { - if (null == destination) { - destination = new File(source, config.getString(Keys.DESTINATION_FOLDER)); - } - if (!destination.exists()) { - destination.mkdirs(); - } - if (!destination.canWrite()) { - throw new JBakeException("Error: Destination folder is not writable: " + destination.getAbsolutePath()); - } + // TODO: do we want to use this. Else, config could be final + @Deprecated + public void setConfig(final CompositeConfiguration config) { + ((DefaultJBakeConfiguration) utensils.getConfiguration()).setCompositeConfiguration(config); } - private File setupPathFromConfig(String key) { - return new File(FilenameUtils.concat(source.getAbsolutePath(), config.getString(key))); + /** + * Checks source path contains required sub-folders (i.e. templates) and setups up variables for them. + * + * @deprecated There is no need for this method anymore. Validation is now part of the instantiation. + * Can be removed with 3.0.0. + */ + @Deprecated + public void setupPaths() { + /* nothing to do here */ } /** * Checks source path contains required sub-folders (i.e. templates) and setups up variables for them. + * Creates destination folder if it does not exist. * * @throws JBakeException If template or contents folder don't exist */ - public void setupPaths() { - ensureSource(); - templatesPath = setupRequiredFolderFromConfig(Keys.TEMPLATE_FOLDER); - contentsPath = setupRequiredFolderFromConfig(Keys.CONTENT_FOLDER); - assetsPath = setupPathFromConfig(Keys.ASSET_FOLDER); - if (!assetsPath.exists()) { - LOGGER.warn("No asset folder was found!"); - } - ensureDestination(); - } - - private File setupRequiredFolderFromConfig(final String key) { - final File path = setupPathFromConfig(key); - if (!FileUtil.isExistingFolder(path)) { - throw new JBakeException("Error: Required folder cannot be found! Expected to find [" + key + "] at: " + path.getAbsolutePath()); - } - return path; - } - - /** - * All the good stuff happens in here... - * - */ - public void bake() { - final ContentStore db = DBUtil.createDataStore(config.getString(Keys.DB_STORE), config.getString(Keys.DB_PATH)); + private void checkConfiguration(JBakeConfiguration configuration) { + JBakeConfigurationInspector inspector = new JBakeConfigurationInspector(configuration); + inspector.inspect(); + } + + /** + * All the good stuff happens in here... + */ + public void bake() { + + ContentStore contentStore = utensils.getContentStore(); + JBakeConfiguration config = utensils.getConfiguration(); + Crawler crawler = utensils.getCrawler(); + Asset asset = utensils.getAsset(); + + try { + + final long start = new Date().getTime(); + LOGGER.info("Baking has started..."); + contentStore.startup(); updateDocTypesFromConfiguration(); - DBUtil.updateSchema(db); - try { - final long start = new Date().getTime(); - LOGGER.info("Baking has started..."); - clearCacheIfNeeded(db); - - // process source content - Crawler crawler = new Crawler(db, source, config); - crawler.crawl(contentsPath); - LOGGER.info("Content detected:"); - for (String docType : DocumentTypes.getDocumentTypes()) { - long count = db.getDocumentCount(docType); - if (count > 0) { - LOGGER.info("Parsed {} files of type: {}", count, docType); - } - } - - Renderer renderer = new Renderer(db, destination, templatesPath, config); - - for(RenderingTool tool : ServiceLoader.load(RenderingTool.class)) { - try { - renderedCount += tool.render(renderer, db, destination, templatesPath, config); - } catch(RenderingException e) { - errors.add(e); - } - } - - // mark docs as rendered - for (String docType : DocumentTypes.getDocumentTypes()) { - db.markContentAsRendered(docType); - } - // copy assets - Asset asset = new Asset(source, destination, config); - asset.copy(assetsPath); - asset.copyAssetsFromContent(contentsPath); - errors.addAll(asset.getErrors()); - - LOGGER.info("Baking finished!"); - long end = new Date().getTime(); - LOGGER.info("Baked {} items in {}ms", renderedCount, end - start); - if (errors.size() > 0) { - LOGGER.error("Failed to bake {} item(s)!", errors.size()); - } + contentStore.updateSchema(); + contentStore.updateAndClearCacheIfNeeded(config.getClearCache(), config.getTemplateFolder()); + + // process source content + crawler.crawl(); + + // render content + renderContent(); + + // copy assets + asset.copy(); + asset.copyAssetsFromContent(config.getContentFolder()); + + errors.addAll(asset.getErrors()); + + LOGGER.info("Baking finished!"); + long end = new Date().getTime(); + LOGGER.info("Baked {} items in {}ms", renderedCount, end - start); + if (!errors.isEmpty()) { + LOGGER.error("Failed to bake {} item(s)!", errors.size()); + } } finally { - db.close(); - db.shutdown(); + contentStore.close(); + contentStore.shutdown(); } } @@ -186,16 +163,13 @@ public void bake() { */ private void updateDocTypesFromConfiguration() { resetDocumentTypesAndExtractors(); + JBakeConfiguration config = utensils.getConfiguration(); + ModelExtractorsDocumentTypeListener listener = new ModelExtractorsDocumentTypeListener(); DocumentTypes.addListener(listener); - Iterator keyIterator = config.getKeys(); - while (keyIterator.hasNext()) { - String key = keyIterator.next(); - Matcher matcher = TEMPLATE_DOC_PATTERN.matcher(key); - if (matcher.find()) { - DocumentTypes.addDocumentType(matcher.group(1)); - } + for (String docType : config.getDocumentTypes()) { + DocumentTypes.addDocumentType(docType); } } @@ -204,42 +178,29 @@ private void resetDocumentTypesAndExtractors() { ModelExtractors.getInstance().reset(); } - private void clearCacheIfNeeded(final ContentStore db) { - boolean needed = isClearCache; - if (!needed) { - DocumentList docs = db.getSignaturesForTemplates(); - String currentTemplatesSignature; + /** + * Load {@link RenderingTool} instances and delegate rendering of documents to them + */ + private void renderContent() { + JBakeConfiguration config = utensils.getConfiguration(); + Renderer renderer = utensils.getRenderer(); + ContentStore contentStore = utensils.getContentStore(); + + for (RenderingTool tool : ServiceLoader.load(RenderingTool.class)) { try { - currentTemplatesSignature = FileUtil.sha1(templatesPath); - } catch (Exception e) { - currentTemplatesSignature = ""; + renderedCount += tool.render(renderer, contentStore, config); + } catch (RenderingException e) { + errors.add(e); } - if (!docs.isEmpty()) { - String sha1 = (String) docs.get(0).get(String.valueOf(DocumentAttributes.SHA1)); - needed = !sha1.equals(currentTemplatesSignature); - if (needed) { - db.updateSignatures(currentTemplatesSignature); - } - } else { - // first computation of templates signature - db.insertSignature(currentTemplatesSignature); - needed = true; - } - } - if (needed) { - for (String docType : DocumentTypes.getDocumentTypes()) { - try { - db.deleteAllByDocType(docType); - } catch (Exception e) { - // maybe a non existing document type - } - } - DBUtil.updateSchema(db); } } - public List getErrors() { - return new ArrayList(errors); - } + public List getErrors() { + return new ArrayList<>(errors); + } + + public Utensils getUtensils() { + return utensils; + } } diff --git a/jbake-core/src/main/java/org/jbake/app/Parser.java b/jbake-core/src/main/java/org/jbake/app/Parser.java index d7570c678..21d884cba 100644 --- a/jbake-core/src/main/java/org/jbake/app/Parser.java +++ b/jbake-core/src/main/java/org/jbake/app/Parser.java @@ -1,6 +1,6 @@ package org.jbake.app; -import org.apache.commons.configuration.Configuration; +import org.jbake.app.configuration.JBakeConfiguration; import org.jbake.parser.Engines; import org.jbake.parser.ParserEngine; import org.slf4j.Logger; @@ -17,25 +17,22 @@ public class Parser { private static final Logger LOGGER = LoggerFactory.getLogger(Parser.class); - private Configuration config; - private String contentPath; + private JBakeConfiguration config; /** * Creates a new instance of Parser. * - * @param config Project configuration - * @param contentPath Content location + * @param config Project configuration */ - public Parser(Configuration config, String contentPath) { + public Parser(JBakeConfiguration config) { this.config = config; - this.contentPath = contentPath; } /** * Process the file by parsing the contents. * * @param file File input for parsing - * @return The contents of the file as a @{@link Map} + * @return The contents of the file */ public Map processFile(File file) { ParserEngine engine = Engines.get(FileUtil.fileExt(file)); @@ -44,6 +41,6 @@ public Map processFile(File file) { return null; } - return engine.parse(config, file, contentPath); + return engine.parse(config, file); } } diff --git a/jbake-core/src/main/java/org/jbake/app/Renderer.java b/jbake-core/src/main/java/org/jbake/app/Renderer.java index 6cdf802a2..e39c3c144 100644 --- a/jbake-core/src/main/java/org/jbake/app/Renderer.java +++ b/jbake-core/src/main/java/org/jbake/app/Renderer.java @@ -1,14 +1,20 @@ package org.jbake.app; import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.Crawler.Attributes; +import org.jbake.app.configuration.DefaultJBakeConfiguration; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; import org.jbake.template.DelegatingTemplateEngine; import org.jbake.util.PagingHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -20,189 +26,110 @@ * @author Jonathan Bullock jonbullock@gmail.com */ public class Renderer { - private static final String MASTERINDEX_TEMPLATE_NAME = "masterindex"; private static final String SITEMAP_TEMPLATE_NAME = "sitemap"; private static final String FEED_TEMPLATE_NAME = "feed"; private static final String ARCHIVE_TEMPLATE_NAME = "archive"; - private interface RenderingConfig { - - File getPath(); - - String getName(); - - String getTemplate(); - - Map getModel(); - } - - private static abstract class AbstractRenderingConfig implements RenderingConfig { - - protected final File path; - protected final String name; - protected final String template; - - public AbstractRenderingConfig(File path, String name, String template) { - super(); - this.path = path; - this.name = name; - this.template = template; - } - - @Override - public File getPath() { - return path; - } - - @Override - public String getName() { - return name; - } - - @Override - public String getTemplate() { - return template; - } - - } - - public class ModelRenderingConfig extends AbstractRenderingConfig { - private final Map model; - - public ModelRenderingConfig(String fileName, Map model, String templateType) { - super(new File(destination.getPath() + File.separator + fileName), fileName, findTemplateName(templateType)); - this.model = model; - } - - public ModelRenderingConfig(File path, String name, Map model, String template) { - super(path, name, template); - this.model = model; - } - - @Override - public Map getModel() { - return model; - } - } - - class DefaultRenderingConfig extends AbstractRenderingConfig { - - private final Object content; - - private DefaultRenderingConfig(File path, String allInOneName) { - super(path, allInOneName, findTemplateName(allInOneName)); - this.content = buildSimpleModel(allInOneName); - } - - public DefaultRenderingConfig(String filename, String allInOneName) { - super(new File(destination.getPath() + File.separator + filename), allInOneName, findTemplateName(allInOneName)); - this.content = buildSimpleModel(allInOneName); - } - - /** - * Constructor added due to known use of a allInOneName which is used for name, template and content - * - * @param allInOneName - */ - public DefaultRenderingConfig(String allInOneName) { - this(new File(destination.getPath() + File.separator + allInOneName + config.getString(Keys.OUTPUT_EXTENSION)), - allInOneName); - } - - @Override - public Map getModel() { - Map model = new HashMap(); - model.put("renderer", renderingEngine); - model.put("content", content); - - if ( config.containsKey(Keys.PAGINATE_INDEX) && config.getBoolean(Keys.PAGINATE_INDEX) ) { - model.put("numberOfPages", 0); - model.put("currentPageNumber", 0); - model.put("previousFileName", ""); - model.put("nextFileName", ""); - } - - return model; - } - - } - - private final static Logger LOGGER = LoggerFactory.getLogger(Renderer.class); - - // TODO: should all content be made available to all templates via this class?? - - private final File destination; - private final CompositeConfiguration config; + private final Logger LOGGER = LoggerFactory.getLogger(Renderer.class); + private final JBakeConfiguration config; private final DelegatingTemplateEngine renderingEngine; private final ContentStore db; /** - * Creates a new instance of Renderer with supplied references to folders. - * * @param db The database holding the content * @param destination The destination folder * @param templatesPath The templates folder * @param config Project configuration + * @deprecated Use {@link #Renderer(ContentStore, JBakeConfiguration)} instead. + * Creates a new instance of Renderer with supplied references to folders. */ + @Deprecated public Renderer(ContentStore db, File destination, File templatesPath, CompositeConfiguration config) { - this.destination = destination; + this(db, new JBakeConfigurationFactory().createDefaultJbakeConfiguration(templatesPath.getParentFile(), config)); + DefaultJBakeConfiguration configuration = ((DefaultJBakeConfiguration) this.config); + configuration.setDestinationFolder(destination); + configuration.setTemplateFolder(templatesPath); + } + + // TOqDO: should all content be made available to all templates via this class?? + + /** + * @param db The database holding the content + * @param destination The destination folder + * @param templatesPath The templates folder + * @param config Project configuration + * @param renderingEngine The instance of DelegatingTemplateEngine to use + * @deprecated Use {@link #Renderer(ContentStore, JBakeConfiguration, DelegatingTemplateEngine)} instead. + * Creates a new instance of Renderer with supplied references to folders and the instance of DelegatingTemplateEngine to use. + */ + @Deprecated + public Renderer(ContentStore db, File destination, File templatesPath, CompositeConfiguration config, DelegatingTemplateEngine renderingEngine) { + this(db, new JBakeConfigurationFactory().createDefaultJbakeConfiguration(templatesPath.getParentFile(), config), renderingEngine); + DefaultJBakeConfiguration configuration = ((DefaultJBakeConfiguration) this.config); + configuration.setDestinationFolder(destination); + configuration.setTemplateFolder(templatesPath); + } + + /** + * Creates a new instance of Renderer with supplied references to folders. + * + * @param db The database holding the content + * @param config Project configuration + */ + public Renderer(ContentStore db, JBakeConfiguration config) { this.config = config; - this.renderingEngine = new DelegatingTemplateEngine(config, db, destination, templatesPath); + this.renderingEngine = new DelegatingTemplateEngine(db, config); this.db = db; } - + /** * Creates a new instance of Renderer with supplied references to folders and the instance of DelegatingTemplateEngine to use. * - * @param db The database holding the content - * @param destination The destination folder - * @param templatesPath The templates folder - * @param config Project configuration - * @param renderingEngine The instance of DelegatingTemplateEngine to use + * @param db The database holding the content + * @param config The application specific configuration + * @param renderingEngine The instance of DelegatingTemplateEngine to use */ - public Renderer(ContentStore db, File destination, File templatesPath, CompositeConfiguration config, DelegatingTemplateEngine renderingEngine) { - this.destination = destination; + public Renderer(ContentStore db, JBakeConfiguration config, DelegatingTemplateEngine renderingEngine) { this.config = config; this.renderingEngine = renderingEngine; this.db = db; } private String findTemplateName(String docType) { - String templateKey = "template." + docType + ".file"; - String returned = config.getString(templateKey); - return returned; + return config.getTemplateFileByDocType(docType).getName(); } /** * Render the supplied content to a file. * - * @param content The content to renderDocument - * @throws Exception if IOException or SecurityException are raised + * @param content The content to renderDocument + * @throws Exception if IOException or SecurityException are raised */ public void render(Map content) throws Exception { String docType = (String) content.get(Crawler.Attributes.TYPE); - String outputFilename = destination.getPath() + File.separatorChar + ((String)content.get(Attributes.URI)).replace("/",File.separator); + String outputFilename = config.getDestinationFolder().getPath() + File.separatorChar + content.get(Attributes.URI); if (outputFilename.lastIndexOf('.') > outputFilename.lastIndexOf(File.separatorChar)) { outputFilename = outputFilename.substring(0, outputFilename.lastIndexOf('.')); } // delete existing versions if they exist in case status has changed either way - File draftFile = new File(outputFilename + config.getString(Keys.DRAFT_SUFFIX) + FileUtil.findExtension(config, docType)); + String outputExtension = config.getOutputExtensionByDocType(docType); + File draftFile = new File(outputFilename, config.getDraftSuffix() + outputExtension); if (draftFile.exists()) { draftFile.delete(); } - File publishedFile = new File(outputFilename + FileUtil.findExtension(config, docType)); + File publishedFile = new File(outputFilename + outputExtension); if (publishedFile.exists()) { publishedFile.delete(); } if (content.get(Crawler.Attributes.STATUS).equals(Crawler.Attributes.Status.DRAFT)) { - outputFilename = outputFilename + config.getString(Keys.DRAFT_SUFFIX); + outputFilename = outputFilename + config.getDraftSuffix(); } - File outputFile = new File(outputFilename + FileUtil.findExtension(config, docType)); + File outputFile = new File(outputFilename + outputExtension); Map model = new HashMap(); model.put("content", content); model.put("renderer", renderingEngine); @@ -224,7 +151,7 @@ private Writer createWriter(File file) throws IOException { file.createNewFile(); } - return new OutputStreamWriter(new FileOutputStream(file), config.getString(ConfigUtil.Keys.RENDER_ENCODING)); + return new OutputStreamWriter(new FileOutputStream(file), config.getRenderEncoding()); } private void render(RenderingConfig renderConfig) throws Exception { @@ -253,7 +180,7 @@ public void renderIndex(String indexFile) throws Exception { public void renderIndexPaging(String indexFile) throws Exception { long totalPosts = db.getPublishedCount("post"); - int postsPerPage = config.getInt(Keys.POSTS_PER_PAGE, 5); + int postsPerPage = config.getPostsPerPage(); if (totalPosts == 0) { //paging makes no sense. render single index file instead @@ -264,7 +191,7 @@ public void renderIndexPaging(String indexFile) throws Exception { Map model = new HashMap(); model.put("renderer", renderingEngine); model.put("numberOfPages", pagingHelper.getNumberOfPages()); - + try { db.setLimit(postsPerPage); for (int pageStart = 0, page = 1; pageStart < totalPosts; pageStart += postsPerPage, page++) { @@ -276,11 +203,11 @@ public void renderIndexPaging(String indexFile) throws Exception { model.put("previousFileName", previous); String nextFileName = pagingHelper.getNextFileName(page); model.put("nextFileName", nextFileName); - + Map contentModel = buildSimpleModel(MASTERINDEX_TEMPLATE_NAME); - + if(page > 1){ - contentModel.put(Attributes.ROOTPATH, "../"); + contentModel.put(Attributes.ROOTPATH, "../"); } model.put("content", contentModel); @@ -299,9 +226,8 @@ public void renderIndexPaging(String indexFile) throws Exception { /** * Render an XML sitemap file using the supplied content. * - * @param sitemapFile configuration for site map - * @throws Exception if can't create correct default rendering config - * + * @param sitemapFile configuration for site map + * @throws Exception if can't create correct default rendering config * @see About Sitemaps * @see Sitemap protocol */ @@ -312,8 +238,8 @@ public void renderSitemap(String sitemapFile) throws Exception { /** * Render an XML feed file using the supplied content. * - * @param feedFile The name of the output file - * @throws Exception if default rendering configuration is not loaded correctly + * @param feedFile The name of the output file + * @throws Exception if default rendering configuration is not loaded correctly */ public void renderFeed(String feedFile) throws Exception { render(new DefaultRenderingConfig(feedFile, FEED_TEMPLATE_NAME)); @@ -322,8 +248,8 @@ public void renderFeed(String feedFile) throws Exception { /** * Render an archive file using the supplied content. * - * @param archiveFile The name of the output file - * @throws Exception if default rendering configuration is not loaded correctly + * @param archiveFile The name of the output file + * @throws Exception if default rendering configuration is not loaded correctly */ public void renderArchive(String archiveFile) throws Exception { render(new DefaultRenderingConfig(archiveFile, ARCHIVE_TEMPLATE_NAME)); @@ -337,57 +263,57 @@ public void renderArchive(String archiveFile) throws Exception { * @throws Exception if cannot render tags correctly */ public int renderTags(String tagPath) throws Exception { - int renderedCount = 0; - final List errors = new LinkedList(); - - for (String tag : db.getAllTags()) { - try { - Map model = new HashMap(); - model.put("renderer", renderingEngine); - model.put(Attributes.TAG, tag); - Map map = buildSimpleModel(Attributes.TAG); - map.put(Attributes.ROOTPATH, "../"); - model.put("content", map); - - File path = new File(destination.getPath() + File.separator + tagPath + File.separator + tag + config.getString(Keys.OUTPUT_EXTENSION)); - render(new ModelRenderingConfig(path, Attributes.TAG, model, findTemplateName(Attributes.TAG))); - - renderedCount++; - } catch (Exception e) { - errors.add(e); - } - } - - if (config.getBoolean(Keys.RENDER_TAGS_INDEX)) { - try{ - // Add an index file at root folder of tags. - // This will prevent directory listing and also provide an option to - // display all tags page. - Map model = new HashMap(); - model.put("renderer", renderingEngine); - Map map = buildSimpleModel(Attributes.TAGS); - map.put(Attributes.ROOTPATH, "../"); - model.put("content", map); - - File path = new File(destination.getPath() + File.separator + tagPath + File.separator + "index" + config.getString(Keys.OUTPUT_EXTENSION)); - render(new ModelRenderingConfig(path, "tagindex", model, findTemplateName("tagsindex"))); - renderedCount++; - } catch(Exception e){ - errors.add(e); - } - } - - if (!errors.isEmpty()) { - StringBuilder sb = new StringBuilder(); - sb.append("Failed to render tags. Cause(s):"); - for (Throwable error : errors) { - sb.append("\n").append(error.getMessage()); - } - throw new Exception(sb.toString(), errors.get(0)); - } else { - return renderedCount; - } - } + int renderedCount = 0; + final List errors = new LinkedList<>(); + + for (String tag : db.getAllTags()) { + try { + Map model = new HashMap<>(); + model.put("renderer", renderingEngine); + model.put(Attributes.TAG, tag); + Map map = buildSimpleModel(Attributes.TAG); + map.put(Attributes.ROOTPATH, "../"); + model.put("content", map); + + File path = new File(config.getDestinationFolder() + File.separator + tagPath + File.separator + tag + config.getOutputExtension()); + render(new ModelRenderingConfig(path, Attributes.TAG, model, findTemplateName(Attributes.TAG))); + + renderedCount++; + } catch (Exception e) { + errors.add(e); + } + } + + if (config.getRenderTagsIndex()) { + try { + // Add an index file at root folder of tags. + // This will prevent directory listing and also provide an option to + // display all tags page. + Map model = new HashMap(); + model.put("renderer", renderingEngine); + Map map = buildSimpleModel(Attributes.TAGS); + map.put(Attributes.ROOTPATH, "../"); + model.put("content", map); + + File path = new File(config.getDestinationFolder() + File.separator + tagPath + File.separator + "index" + config.getOutputExtension()); + render(new ModelRenderingConfig(path, "tagindex", model, findTemplateName("tagsindex"))); + renderedCount++; + } catch (Exception e) { + errors.add(e); + } + } + + if (!errors.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("Failed to render tags. Cause(s):"); + for (Throwable error : errors) { + sb.append("\n").append(error.getMessage()); + } + throw new Exception(sb.toString(), errors.get(0)); + } else { + return renderedCount; + } + } /** * Builds simple map of values, which are exposed when rendering index/archive/sitemap/feed/tags. @@ -402,4 +328,106 @@ private Map buildSimpleModel(String type) { // add any more keys here that need to have a default value to prevent need to perform null check in templates return content; } + + private interface RenderingConfig { + + File getPath(); + + String getName(); + + String getTemplate(); + + Map getModel(); + } + + private static abstract class AbstractRenderingConfig implements RenderingConfig { + + protected final File path; + protected final String name; + protected final String template; + + public AbstractRenderingConfig(File path, String name, String template) { + super(); + this.path = path; + this.name = name; + this.template = template; + } + + @Override + public File getPath() { + return path; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getTemplate() { + return template; + } + + } + + public class ModelRenderingConfig extends AbstractRenderingConfig { + private final Map model; + + public ModelRenderingConfig(String fileName, Map model, String templateType) { + super(new File(config.getDestinationFolder(), fileName), fileName, findTemplateName(templateType)); + this.model = model; + } + + public ModelRenderingConfig(File path, String name, Map model, String template) { + super(path, name, template); + this.model = model; + } + + @Override + public Map getModel() { + return model; + } + } + + class DefaultRenderingConfig extends AbstractRenderingConfig { + + private final Object content; + + private DefaultRenderingConfig(File path, String allInOneName) { + super(path, allInOneName, findTemplateName(allInOneName)); + this.content = buildSimpleModel(allInOneName); + } + + public DefaultRenderingConfig(String filename, String allInOneName) { + super(new File(config.getDestinationFolder(), File.separator + filename), allInOneName, findTemplateName(allInOneName)); + this.content = buildSimpleModel(allInOneName); + } + + /** + * Constructor added due to known use of a allInOneName which is used for name, template and content + * + * @param allInOneName + */ + public DefaultRenderingConfig(String allInOneName) { + this(new File(config.getDestinationFolder().getPath() + File.separator + allInOneName + config.getOutputExtension()), + allInOneName); + } + + @Override + public Map getModel() { + Map model = new HashMap(); + model.put("renderer", renderingEngine); + model.put("content", content); + + if (config.getPaginateIndex()) { + model.put("numberOfPages", 0); + model.put("currentPageNumber", 0); + model.put("previousFileName", ""); + model.put("nextFileName", ""); + } + + return model; + } + + } } diff --git a/jbake-core/src/main/java/org/jbake/app/Utensils.java b/jbake-core/src/main/java/org/jbake/app/Utensils.java new file mode 100644 index 000000000..648278cfe --- /dev/null +++ b/jbake-core/src/main/java/org/jbake/app/Utensils.java @@ -0,0 +1,55 @@ +package org.jbake.app; + +import org.jbake.app.configuration.JBakeConfiguration; + +/** + * A helper class to wrap all the utensils that are needed to bake. + */ +public class Utensils { + private JBakeConfiguration configuration; + private ContentStore contentStore; + private Crawler crawler; + private Renderer renderer; + private Asset asset; + + public JBakeConfiguration getConfiguration() { + return configuration; + } + + public void setConfiguration(JBakeConfiguration configuration) { + this.configuration = configuration; + } + + public ContentStore getContentStore() { + return contentStore; + } + + public void setContentStore(ContentStore contentStore) { + this.contentStore = contentStore; + } + + public Crawler getCrawler() { + return crawler; + } + + public void setCrawler(Crawler crawler) { + this.crawler = crawler; + } + + public Renderer getRenderer() { + return renderer; + } + + public void setRenderer(Renderer renderer) { + this.renderer = renderer; + } + + public Asset getAsset() { + return asset; + } + + public void setAsset(Asset asset) { + this.asset = asset; + } +} + diff --git a/jbake-core/src/main/java/org/jbake/app/UtensilsFactory.java b/jbake-core/src/main/java/org/jbake/app/UtensilsFactory.java new file mode 100644 index 000000000..5e2038193 --- /dev/null +++ b/jbake-core/src/main/java/org/jbake/app/UtensilsFactory.java @@ -0,0 +1,31 @@ +package org.jbake.app; + +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationInspector; + +/** + * A factory to create a {@link Utensils} object + */ +public class UtensilsFactory { + + /** + * Create default {@link Utensils} by a given {@link JBakeConfiguration} + * @param config a {@link JBakeConfiguration} + * @return a default {@link Utensils} instance + */ + public static Utensils createDefaultUtensils(JBakeConfiguration config) { + + JBakeConfigurationInspector inspector = new JBakeConfigurationInspector(config); + inspector.inspect(); + + Utensils utensils = new Utensils(); + utensils.setConfiguration(config); + ContentStore contentStore = DBUtil.createDataStore(config); + utensils.setContentStore(contentStore); + utensils.setCrawler(new Crawler(contentStore, config)); + utensils.setRenderer(new Renderer(contentStore, config)); + utensils.setAsset(new Asset(config)); + + return utensils; + } +} diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/ConfigUtil.java b/jbake-core/src/main/java/org/jbake/app/configuration/ConfigUtil.java new file mode 100644 index 000000000..9768d72d2 --- /dev/null +++ b/jbake-core/src/main/java/org/jbake/app/configuration/ConfigUtil.java @@ -0,0 +1,59 @@ +package org.jbake.app.configuration; + +import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.commons.configuration.SystemConfiguration; +import org.jbake.app.JBakeException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +/** + * Provides Configuration related functions. + * + * @author Jonathan Bullock jonbullock@gmail.com + */ +public class ConfigUtil { + + private static final Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class); + private static final String LEGACY_CONFIG_FILE = "custom.properties"; + private static final String CONFIG_FILE = "jbake.properties"; + private static final String DEFAULT_CONFIG_FILE = "default.properties"; + + private CompositeConfiguration load(File source) throws ConfigurationException { + + if (!source.exists()) { + throw new JBakeException("The given source folder '" + source.getAbsolutePath() + "' does not exist."); + } + if (!source.isDirectory()) { + throw new JBakeException("The given source folder is not a directory."); + } + + CompositeConfiguration config = new CompositeConfiguration(); + config.setListDelimiter(','); + File customConfigFile = new File(source, LEGACY_CONFIG_FILE); + if (customConfigFile.exists()) { + displayLegacyConfigFileWarningIfRequired(); + config.addConfiguration(new PropertiesConfiguration(customConfigFile)); + } + customConfigFile = new File(source, CONFIG_FILE); + if (customConfigFile.exists()) { + config.addConfiguration(new PropertiesConfiguration(customConfigFile)); + } + config.addConfiguration(new PropertiesConfiguration(DEFAULT_CONFIG_FILE)); + config.addConfiguration(new SystemConfiguration()); + return config; + } + + private void displayLegacyConfigFileWarningIfRequired() { + LOGGER.warn("You have defined a part of your JBake configuration in {}", LEGACY_CONFIG_FILE); + LOGGER.warn("Usage of this file is being deprecated, please rename this file to: {} to remove this warning", CONFIG_FILE); + } + + public JBakeConfiguration loadConfig(File source) throws ConfigurationException { + CompositeConfiguration configuration = load(source); + return new DefaultJBakeConfiguration(source, configuration); + } +} diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/DefaultJBakeConfiguration.java b/jbake-core/src/main/java/org/jbake/app/configuration/DefaultJBakeConfiguration.java new file mode 100644 index 000000000..804d5052f --- /dev/null +++ b/jbake-core/src/main/java/org/jbake/app/configuration/DefaultJBakeConfiguration.java @@ -0,0 +1,534 @@ +package org.jbake.app.configuration; + +import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * The default implementation of a {@link JBakeConfiguration} + */ +public class DefaultJBakeConfiguration implements JBakeConfiguration { + + + private static final String SOURCE_FOLDER_KEY = "sourceFolder"; + private static final String DESTINATION_FOLDER_KEY = "destinationFolder"; + private static final String ASSET_FOLDER_KEY = "assetFolder"; + private static final String TEMPLATE_FOLDER_KEY = "templateFolder"; + private static final String CONTENT_FOLDER_KEY = "contentFolder"; + private static final Pattern TEMPLATE_DOC_PATTERN = Pattern.compile("(?:template\\.)([a-zA-Z0-9-_]+)(?:\\.file)"); + private static final String DOCTYPE_FILE_POSTFIX = ".file"; + private static final String DOCTYPE_EXTENSION_POSTFIX = ".extension"; + private static final String DOCTYPE_TEMPLATE_PREFIX = "template."; + private Logger logger = LoggerFactory.getLogger(DefaultJBakeConfiguration.class); + private CompositeConfiguration compositeConfiguration; + + /** + * Some deprecated implementations just need access to the configuration without access to the source folder + * + * @param configuration The project configuration + * @deprecated use {@link #DefaultJBakeConfiguration(File, CompositeConfiguration)} instead + */ + @Deprecated + public DefaultJBakeConfiguration(CompositeConfiguration configuration) { + this.compositeConfiguration = configuration; + } + + public DefaultJBakeConfiguration(File sourceFolder, CompositeConfiguration configuration) { + this.compositeConfiguration = configuration; + setSourceFolder(sourceFolder); + setupDefaultDestination(); + setupPathsRelativeToSourceFile(); + } + + @Override + public Object get(String key) { + return compositeConfiguration.getProperty(key); + } + + @Override + public String getArchiveFileName() { + return getAsString(JBakeProperty.ARCHIVE_FILE); + } + + private boolean getAsBoolean(String key) { + return compositeConfiguration.getBoolean(key, false); + } + + private File getAsFolder(String key) { + return (File) get(key); + } + + private int getAsInt(String key, int defaultValue) { + return compositeConfiguration.getInt(key, defaultValue); + } + + private List getAsList(String key) { + return Arrays.asList(compositeConfiguration.getStringArray(key)); + } + + private String getAsString(String key) { + return compositeConfiguration.getString(key); + } + + private String getAsString(String key, String defaultValue) { + return compositeConfiguration.getString(key, defaultValue); + } + + @Override + public List getAsciidoctorAttributes() { + return getAsList(JBakeProperty.ASCIIDOCTOR_ATTRIBUTES); + } + + public Object getAsciidoctorOption(String optionKey) { + Configuration subConfig = compositeConfiguration.subset(JBakeProperty.ASCIIDOCTOR_OPTION); + Object value = subConfig.getProperty(optionKey); + + if (value == null) { + logger.warn("Cannot find asciidoctor option '{}.{}'", JBakeProperty.ASCIIDOCTOR_OPTION, optionKey); + return ""; + } + return value; + } + + @Override + public List getAsciidoctorOptionKeys() { + List options = new ArrayList<>(); + Configuration subConfig = compositeConfiguration.subset(JBakeProperty.ASCIIDOCTOR_OPTION); + + Iterator iterator = subConfig.getKeys(); + while (iterator.hasNext()) { + String key = iterator.next(); + options.add(key); + } + + return options; + } + + @Override + public File getAssetFolder() { + return getAsFolder(ASSET_FOLDER_KEY); + } + + public void setAssetFolder(File assetFolder) { + if (assetFolder != null) { + setProperty(ASSET_FOLDER_KEY, assetFolder); + setProperty(JBakeProperty.ASSET_FOLDER, assetFolder.getName()); + } + } + + @Override + public String getAssetFolderName() { + return getAsString(JBakeProperty.ASSET_FOLDER); + } + + @Override + public boolean getAssetIgnoreHidden() { + return getAsBoolean(JBakeProperty.ASSET_IGNORE_HIDDEN); + } + + public void setAssetIgnoreHidden(boolean assetIgnoreHidden) { + setProperty(JBakeProperty.ASSET_IGNORE_HIDDEN, assetIgnoreHidden); + } + + @Override + public String getAttributesExportPrefixForAsciidoctor() { + return getAsString(JBakeProperty.ASCIIDOCTOR_ATTRIBUTES_EXPORT_PREFIX, ""); + } + + @Override + public String getBuildTimeStamp() { + return getAsString(JBakeProperty.BUILD_TIMESTAMP); + } + + @Override + public boolean getClearCache() { + return getAsBoolean(JBakeProperty.CLEAR_CACHE); + } + + public void setClearCache(boolean clearCache) { + setProperty(JBakeProperty.CLEAR_CACHE, clearCache); + } + + public CompositeConfiguration getCompositeConfiguration() { + return compositeConfiguration; + } + + public void setCompositeConfiguration(CompositeConfiguration configuration) { + this.compositeConfiguration = configuration; + } + + @Override + public File getContentFolder() { + return getAsFolder(CONTENT_FOLDER_KEY); + } + + public void setContentFolder(File contentFolder) { + if (contentFolder != null) { + setProperty(CONTENT_FOLDER_KEY, contentFolder); + setProperty(JBakeProperty.CONTENT_FOLDER, contentFolder.getName()); + } + } + + @Override + public String getContentFolderName() { + return getAsString(JBakeProperty.CONTENT_FOLDER); + } + + @Override + public String getDatabasePath() { + return getAsString(JBakeProperty.DB_PATH); + } + + public void setDatabasePath(String path) { + setProperty(JBakeProperty.DB_PATH, path); + } + + @Override + public String getDatabaseStore() { + return getAsString(JBakeProperty.DB_STORE); + } + + public void setDatabaseStore(String storeType) { + setProperty(JBakeProperty.DB_STORE, storeType); + } + + @Override + public String getDateFormat() { + return getAsString(JBakeProperty.DATE_FORMAT); + } + + @Override + public String getDefaultStatus() { + return getAsString(JBakeProperty.DEFAULT_STATUS); + } + + public void setDefaultStatus(String status) { + setProperty(JBakeProperty.DEFAULT_STATUS, status); + } + + @Override + public String getDefaultType() { + String type = getAsString(JBakeProperty.DEFAULT_TYPE); + if (type.isEmpty()) { + return null; + } + return type; + } + + public void setDefaultType(String type) { + setProperty(JBakeProperty.DEFAULT_TYPE, type); + } + + @Override + public File getDestinationFolder() { + return getAsFolder(DESTINATION_FOLDER_KEY); + } + + public void setDestinationFolder(File destinationFolder) { + if (destinationFolder != null) { + setProperty(DESTINATION_FOLDER_KEY, destinationFolder); + setProperty(JBakeProperty.DESTINATION_FOLDER, destinationFolder.getName()); + } + } + + @Override + public List getDocumentTypes() { + List docTypes = new ArrayList<>(); + Iterator keyIterator = compositeConfiguration.getKeys(); + while (keyIterator.hasNext()) { + String key = keyIterator.next(); + Matcher matcher = TEMPLATE_DOC_PATTERN.matcher(key); + if (matcher.find()) { + docTypes.add(matcher.group(1)); + } + } + + return docTypes; + } + + @Override + public String getDraftSuffix() { + return getAsString(JBakeProperty.DRAFT_SUFFIX, ""); + } + + @Override + public String getExampleProjectByType(String templateType) { + return getAsString("example.project." + templateType); + } + + @Override + public boolean getExportAsciidoctorAttributes() { + return getAsBoolean(JBakeProperty.ASCIIDOCTOR_ATTRIBUTES_EXPORT); + } + + @Override + public String getFeedFileName() { + return getAsString(JBakeProperty.FEED_FILE); + } + + @Override + public String getIndexFileName() { + return getAsString(JBakeProperty.INDEX_FILE); + } + + @Override + public Iterator getKeys() { + return compositeConfiguration.getKeys(); + } + + @Override + public List getMarkdownExtensions() { + return getAsList(JBakeProperty.MARKDOWN_EXTENSIONS); + } + + public void setMarkdownExtensions(String... extensions) { + setProperty(JBakeProperty.MARKDOWN_EXTENSIONS, StringUtils.join(extensions, ",")); + } + + @Override + public String getOutputExtension() { + return getAsString(JBakeProperty.OUTPUT_EXTENSION); + } + + public void setOutputExtension(String outputExtension) { + setProperty(JBakeProperty.OUTPUT_EXTENSION, outputExtension); + } + + @Override + public String getOutputExtensionByDocType(String docType) { + String templateExtensionKey = DOCTYPE_TEMPLATE_PREFIX + docType + DOCTYPE_EXTENSION_POSTFIX; + String defaultOutputExtension = getOutputExtension(); + return getAsString(templateExtensionKey, defaultOutputExtension); + } + + @Override + public boolean getPaginateIndex() { + return getAsBoolean(JBakeProperty.PAGINATE_INDEX); + } + + public void setPaginateIndex(boolean paginateIndex) { + setProperty(JBakeProperty.PAGINATE_INDEX, paginateIndex); + } + + @Override + public int getPostsPerPage() { + return getAsInt(JBakeProperty.POSTS_PER_PAGE, 5); + } + + public void setPostsPerPage(int postsPerPage) { + setProperty(JBakeProperty.POSTS_PER_PAGE, postsPerPage); + } + + @Override + public String getPrefixForUriWithoutExtension() { + return getAsString(JBakeProperty.URI_NO_EXTENSION_PREFIX); + } + + public void setPrefixForUriWithoutExtension(String prefix) { + setProperty(JBakeProperty.URI_NO_EXTENSION_PREFIX, prefix); + } + + @Override + public boolean getRenderArchive() { + return getAsBoolean(JBakeProperty.RENDER_ARCHIVE); + } + + @Override + public String getRenderEncoding() { + return getAsString(JBakeProperty.RENDER_ENCODING); + } + + @Override + public boolean getRenderFeed() { + return getAsBoolean(JBakeProperty.RENDER_FEED); + } + + @Override + public boolean getRenderIndex() { + return getAsBoolean(JBakeProperty.RENDER_INDEX); + } + + @Override + public boolean getRenderSiteMap() { + return getAsBoolean(JBakeProperty.RENDER_SITEMAP); + } + + @Override + public boolean getRenderTags() { + return getAsBoolean(JBakeProperty.RENDER_TAGS); + } + + @Override + public boolean getRenderTagsIndex() { + return compositeConfiguration.getBoolean(JBakeProperty.RENDER_TAGS_INDEX, false); + } + + public void setRenderTagsIndex(boolean enable) { + compositeConfiguration.setProperty(JBakeProperty.RENDER_TAGS_INDEX, enable); + } + + @Override + public boolean getSanitizeTag() { + return getAsBoolean(JBakeProperty.TAG_SANITIZE); + } + + @Override + public int getServerPort() { + return getAsInt(JBakeProperty.SERVER_PORT, 8080); + } + + public void setServerPort(int port) { + setProperty(JBakeProperty.SERVER_PORT, port); + } + + @Override + public String getSiteHost() { + return getAsString(JBakeProperty.SITE_HOST, "http://www.jbake.org"); + } + + public void setSiteHost(String siteHost) { + setProperty(JBakeProperty.SITE_HOST, siteHost); + } + + @Override + public String getSiteMapFileName() { + return getAsString(JBakeProperty.SITEMAP_FILE); + } + + @Override + public File getSourceFolder() { + return getAsFolder(SOURCE_FOLDER_KEY); + } + + public void setSourceFolder(File sourceFolder) { + setProperty(SOURCE_FOLDER_KEY, sourceFolder); + setupPathsRelativeToSourceFile(); + } + + @Override + public String getTagPathName() { + return getAsString(JBakeProperty.TAG_PATH); + } + + @Override + public String getTemplateEncoding() { + return getAsString(JBakeProperty.TEMPLATE_ENCODING); + } + + @Override + public File getTemplateFileByDocType(String docType) { + String templateKey = DOCTYPE_TEMPLATE_PREFIX + docType + DOCTYPE_FILE_POSTFIX; + String templateFileName = getAsString(templateKey); + if (templateFileName != null) { + return new File(getTemplateFolder(), templateFileName); + } + logger.warn("Cannot find configuration key '{}' for document type '{}'", templateKey, docType); + return null; + } + + @Override + public File getTemplateFolder() { + return getAsFolder(TEMPLATE_FOLDER_KEY); + } + + public void setTemplateFolder(File templateFolder) { + if (templateFolder != null) { + setProperty(TEMPLATE_FOLDER_KEY, templateFolder); + setProperty(JBakeProperty.TEMPLATE_FOLDER, templateFolder.getName()); + } + } + + @Override + public String getTemplateFolderName() { + return getAsString(JBakeProperty.TEMPLATE_FOLDER); + } + + @Override + public String getThymeleafLocale() { + return getAsString(JBakeProperty.THYMELEAF_LOCALE); + } + + @Override + public boolean getUriWithoutExtension() { + return getAsBoolean(JBakeProperty.URI_NO_EXTENSION); + } + + public void setUriWithoutExtension(boolean withoutExtension) { + setProperty(JBakeProperty.URI_NO_EXTENSION, withoutExtension); + } + + @Override + public String getVersion() { + return getAsString(JBakeProperty.VERSION); + } + + public void setDestinationFolderName(String folderName) { + setProperty(JBakeProperty.DESTINATION_FOLDER, folderName); + setupDefaultDestination(); + } + + public void setExampleProject(String type, String fileName) { + String projectKey = "example.project." + type; + setProperty(projectKey, fileName); + } + + @Override + public void setProperty(String key, Object value) { + compositeConfiguration.setProperty(key, value); + } + + public void setTemplateExtensionForDocType(String docType, String extension) { + String templateExtensionKey = DOCTYPE_TEMPLATE_PREFIX + docType + DOCTYPE_EXTENSION_POSTFIX; + setProperty(templateExtensionKey, extension); + } + + public void setTemplateFileNameForDocType(String docType, String fileName) { + String templateKey = DOCTYPE_TEMPLATE_PREFIX + docType + DOCTYPE_FILE_POSTFIX; + setProperty(templateKey, fileName); + } + + private void setupDefaultAssetFolder() { + String assetFolder = getAsString(JBakeProperty.ASSET_FOLDER); + setAssetFolder(new File(getSourceFolder(), assetFolder)); + } + + private void setupDefaultContentFolder() { + setContentFolder(new File(getSourceFolder(), getContentFolderName())); + } + + private void setupDefaultDestination() { + String destinationPath = getAsString(JBakeProperty.DESTINATION_FOLDER); + setDestinationFolder(new File(getSourceFolder(), destinationPath)); + } + + private void setupDefaultTemplateFolder() { + String destinationPath = getAsString(JBakeProperty.TEMPLATE_FOLDER); + setTemplateFolder(new File(getSourceFolder(), destinationPath)); + } + + private void setupPathsRelativeToSourceFile() { + setupDefaultAssetFolder(); + setupDefaultTemplateFolder(); + setupDefaultContentFolder(); + } + + @Override + public String getHeaderSeparator() { + return getAsString(JBakeProperty.HEADER_SEPARATOR); + } + + public void setHeaderSeparator(String headerSeparator) { + setProperty(JBakeProperty.HEADER_SEPARATOR, headerSeparator); + } + +} diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfiguration.java b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfiguration.java new file mode 100644 index 000000000..a44de370f --- /dev/null +++ b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfiguration.java @@ -0,0 +1,298 @@ +package org.jbake.app.configuration; + +import java.io.File; +import java.util.Iterator; +import java.util.List; + +/** + * JBakeConfiguration gives you access to the project configuration. Typically located in a file called jbake.properties. + * + * Use one of {@link JBakeConfigurationFactory} methods to create an instance. + */ +public interface JBakeConfiguration { + + /** + * Get property value by a given key from the configuration + * + * @param key a key for the property like site.host + * @return the value of the property + */ + Object get(String key); + + /** + * @return Output filename for archive file, is only used when {@link #getRenderArchive()} is true + */ + String getArchiveFileName(); + + /** + * @return attributes to be set when processing input + */ + List getAsciidoctorAttributes(); + + /** + * Get an asciidoctor option by it's key + * + * @param optionKey an option key + * @return the value of the option key + */ + Object getAsciidoctorOption(String optionKey); + + /** + * Get a list of asciidoctor options + * + * @return list of asciidoctor options + */ + List getAsciidoctorOptionKeys(); + + /** + * @return the folder where assets are stored, they are copied directly in output folder and not processed + */ + File getAssetFolder(); + + /** + * @return name of folder for assets + */ + String getAssetFolderName(); + + /** + * @return Flag indicating if hidden asset resources should be ignored + */ + boolean getAssetIgnoreHidden(); + + /** + * @return Prefix to be used when exporting JBake properties to Asciidoctor + */ + String getAttributesExportPrefixForAsciidoctor(); + + /** + * @return Timestamp that records when JBake build was made + */ + String getBuildTimeStamp(); + + /** + * @return Flag indicating to flash the database cache + */ + boolean getClearCache(); + + /** + * @return the content folder + */ + File getContentFolder(); + + /** + * @return name of Folder where content (that's to say files to be transformed) resides in + */ + String getContentFolderName(); + + /** + * @return Folder to store database files in + */ + String getDatabasePath(); + + /** + * @return name to identify if database is kept in memory (memory) or persisted to disk (plocal) + */ + String getDatabaseStore(); + + /** + * @return How date is formated + */ + String getDateFormat(); + + /** + * @return Default status to use (in order to avoid putting it in all files) + */ + String getDefaultStatus(); + + /** + * @return Default type to use (in order to avoid putting it in all files) + */ + String getDefaultType(); + + /** + * @return The destination folder to render and copy files to + */ + File getDestinationFolder(); + + void setDestinationFolder(File destination); + + List getDocumentTypes(); + + /** + * @return Suffix used to identify draft files + */ + String getDraftSuffix(); + + /** + * Get name for example project name by given template type + * + * @param templateType a template type + * @return example project name + */ + String getExampleProjectByType(String templateType); + + /** + * @return Flag indicating if JBake properties should be made available to Asciidoctor + */ + boolean getExportAsciidoctorAttributes(); + + /** + * @return Output filename for feed file, is only used when {@link #getRenderFeed()} der} is true + */ + String getFeedFileName(); + + + /** + * @return String used to separate the header from the body + */ + String getHeaderSeparator(); + + /** + * @return Output filename for index, is only used when {@link #getRenderIndex()} is true + */ + String getIndexFileName(); + + /** + * Get an iterator of available configuration keys + * + * @return an iterator of configuration keys + */ + Iterator getKeys(); + + /** + * A list of markdown extensions + *

+ * markdown.extension=HARDWRAPS,AUTOLINKS,FENCED_CODE_BLOCKS,DEFINITIONS + * + * @return list of markdown extensions as string + */ + List getMarkdownExtensions(); + + /** + * @return file extension to be used for all output files + */ + String getOutputExtension(); + + String getOutputExtensionByDocType(String docType); + + /** + * @return Flag indicating if there should be pagination when rendering index + */ + boolean getPaginateIndex(); + + /** + * @return How many posts per page on index + */ + int getPostsPerPage(); + + /** + * @return URI prefix for content that should be given extension-less output URI's + */ + String getPrefixForUriWithoutExtension(); + + /** + * @return Flag indicating if archive file should be generated + */ + boolean getRenderArchive(); + + /** + * @return Encoding used when rendering files + */ + String getRenderEncoding(); + + /** + * @return Flag indicating if feed file should be generated + */ + boolean getRenderFeed(); + + /** + * @return Flag indicating if index file should be generated + */ + boolean getRenderIndex(); + + /** + * @return Flag indicating if sitemap file should be generated + */ + boolean getRenderSiteMap(); + + /** + * @return Flag indicating if tag files should be generated + */ + boolean getRenderTags(); + + /** + * @return Flag indicating if tag index file should be generated + */ + boolean getRenderTagsIndex(); + + /** + * @return Flag indicating if the tag value should be sanitized + */ + boolean getSanitizeTag(); + + /** + * @return Port used when running Jetty server + */ + int getServerPort(); + + /** + * @return the host url of the site e.g. http://jbake.org + */ + String getSiteHost(); + + /** + * @return Sitemap template file name. Used only when {@link #getRenderSiteMap()} is set to true + */ + String getSiteMapFileName(); + + /** + * @return the source folder of the project + */ + File getSourceFolder(); + + /** + * @return Tags output path, used only when {@link #getRenderTags()} is true + */ + String getTagPathName(); + + /** + * @return Encoding to be used for template files + */ + String getTemplateEncoding(); + + File getTemplateFileByDocType(String masterindex); + + /** + * @return the template folder + */ + File getTemplateFolder(); + + /** + * @return name of folder where template files are looked for + */ + String getTemplateFolderName(); + + /** + * @return Locale used for Thymeleaf template rendering + */ + String getThymeleafLocale(); + + /** + * @return Flag indicating if content matching prefix below should be given extension-less URI's + */ + boolean getUriWithoutExtension(); + + /** + * @return Version of JBake + */ + String getVersion(); + + /** + * Set a property value for the given key + * + * @param key the key for the property + * @param value the value of the property + */ + void setProperty(String key, Object value); +} + diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationFactory.java b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationFactory.java new file mode 100644 index 000000000..f9bbd4756 --- /dev/null +++ b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationFactory.java @@ -0,0 +1,108 @@ +package org.jbake.app.configuration; + +import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.commons.configuration.ConfigurationException; + +import java.io.File; + +/** + * A {@link JBakeConfiguration} factory + */ +public class JBakeConfigurationFactory { + + private ConfigUtil configUtil; + + public JBakeConfigurationFactory() { + this.configUtil = new ConfigUtil(); + } + + /** + * Creates a {@link DefaultJBakeConfiguration} + * @param sourceFolder The source folder of the project + * @param destination The destination folder to render and copy files to + * @param isClearCache Whether to clear database cache or not + * @return A configuration by given parameters + * @throws ConfigurationException if loading the configuration fails + */ + public DefaultJBakeConfiguration createDefaultJbakeConfiguration(File sourceFolder, File destination, boolean isClearCache) throws ConfigurationException { + + DefaultJBakeConfiguration configuration = (DefaultJBakeConfiguration) getConfigUtil().loadConfig(sourceFolder); + configuration.setDestinationFolder(destination); + configuration.setClearCache(isClearCache); + + return configuration; + } + + /** + * Creates a {@link DefaultJBakeConfiguration} + * + * This is a compatibility factory method + * + * @param sourceFolder The source folder of the project + * @param destination The destination folder to render and copy files to + * @param compositeConfiguration A given {@link CompositeConfiguration} + * @param isClearCache Whether to clear database cache or not + * @return A configuration by given parameters + */ + public DefaultJBakeConfiguration createDefaultJbakeConfiguration(File sourceFolder, File destination, CompositeConfiguration compositeConfiguration, boolean isClearCache) { + DefaultJBakeConfiguration configuration = new DefaultJBakeConfiguration(sourceFolder, compositeConfiguration); + configuration.setDestinationFolder(destination); + configuration.setClearCache(isClearCache); + return configuration; + } + + /** + * Creates a {@link DefaultJBakeConfiguration} + * + * This is a compatibility factory method + * + * @param sourceFolder The source folder of the project + * @param destination The destination folder to render and copy files to + * @param compositeConfiguration A given {@link CompositeConfiguration} + * @return A configuration by given parameters + */ + public DefaultJBakeConfiguration createDefaultJbakeConfiguration(File sourceFolder, File destination, CompositeConfiguration compositeConfiguration) { + DefaultJBakeConfiguration configuration = new DefaultJBakeConfiguration(sourceFolder, compositeConfiguration); + configuration.setDestinationFolder(destination); + return configuration; + } + + /** + * Creates a {@link DefaultJBakeConfiguration} + * + * + * @param sourceFolder The source folder of the project + * @param config A {@link CompositeConfiguration} + * @return A configuration by given parameters + */ + public DefaultJBakeConfiguration createDefaultJbakeConfiguration(File sourceFolder, CompositeConfiguration config) { + return new DefaultJBakeConfiguration(sourceFolder,config); + } + + /** + * Creates a {@link DefaultJBakeConfiguration} with value site.host replaced + * by http://localhost:[server.port]. + * The server.port is read from the project properties file. + * + * @param sourceFolder The source folder of the project + * @param destinationFolder The destination folder to render and copy files to + * @param isClearCache Whether to clear database cache or not + * @return A configuration by given parameters + * @throws ConfigurationException if loading the configuration fails + */ + public DefaultJBakeConfiguration createJettyJbakeConfiguration(File sourceFolder, File destinationFolder, boolean isClearCache) throws ConfigurationException { + DefaultJBakeConfiguration configuration = (DefaultJBakeConfiguration) getConfigUtil().loadConfig(sourceFolder); + configuration.setDestinationFolder(destinationFolder); + configuration.setClearCache(isClearCache); + configuration.setSiteHost("http://localhost:"+configuration.getServerPort()); + return configuration; + } + + public ConfigUtil getConfigUtil() { + return configUtil; + } + + public void setConfigUtil(ConfigUtil configUtil) { + this.configUtil = configUtil; + } +} diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationInspector.java b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationInspector.java new file mode 100644 index 000000000..d8c81882f --- /dev/null +++ b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationInspector.java @@ -0,0 +1,72 @@ +package org.jbake.app.configuration; + +import org.jbake.app.FileUtil; +import org.jbake.app.JBakeException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class JBakeConfigurationInspector { + + private static final Logger LOGGER = LoggerFactory.getLogger(JBakeConfigurationInspector.class); + + private final JBakeConfiguration configuration; + + public JBakeConfigurationInspector(JBakeConfiguration configuration) { + this.configuration = configuration; + } + + public void inspect() throws JBakeException { + ensureSource(); + ensureTemplateFolder(); + ensureContentFolder(); + ensureDestination(); + checkAssetFolder(); + } + + private void ensureSource() throws JBakeException { + File source = configuration.getSourceFolder(); + if (!FileUtil.isExistingFolder(source)) { + throw new JBakeException("Error: Source folder must exist: " + source.getAbsolutePath()); + } + if (!configuration.getSourceFolder().canRead()) { + throw new JBakeException("Error: Source folder is not readable: " + source.getAbsolutePath()); + } + } + + private void ensureTemplateFolder() { + File path = configuration.getTemplateFolder(); + checkRequiredFolderExists(configuration.getTemplateFolderName(), path); + } + + private void ensureContentFolder() { + File path = configuration.getContentFolder(); + checkRequiredFolderExists(configuration.getContentFolderName(), path); + } + + private void ensureDestination() { + File destination = configuration.getDestinationFolder(); + if (!destination.exists()) { + destination.mkdirs(); + } + if (!destination.canWrite()) { + throw new JBakeException("Error: Destination folder is not writable: " + destination.getAbsolutePath()); + } + } + + private void checkAssetFolder() { + File path = configuration.getAssetFolder(); + if (!path.exists()) { + LOGGER.warn("No asset folder '{}' was found!", path.getAbsolutePath()); + } + } + + private void checkRequiredFolderExists(String folderName, File path) { + if (!FileUtil.isExistingFolder(path)) { + throw new JBakeException("Error: Required folder cannot be found! Expected to find [" + folderName + "] at: " + path.getAbsolutePath()); + } + } + + +} diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/JBakeProperty.java b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeProperty.java new file mode 100644 index 000000000..052cad701 --- /dev/null +++ b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeProperty.java @@ -0,0 +1,50 @@ +package org.jbake.app.configuration; + +public class JBakeProperty { + + public static final String ARCHIVE_FILE = "archive.file"; + public static final String ASCIIDOCTOR_ATTRIBUTES = "asciidoctor.attributes"; + public static final String ASCIIDOCTOR_ATTRIBUTES_EXPORT = "asciidoctor.attributes.export"; + public static final String ASCIIDOCTOR_OPTION = "asciidoctor.option"; + public static final String ASCIIDOCTOR_ATTRIBUTES_EXPORT_PREFIX = "asciidoctor.attributes.export.prefix"; + public static final String ASSET_FOLDER = "asset.folder"; + public static final String ASSET_IGNORE_HIDDEN = "asset.ignore"; + public static final String BUILD_TIMESTAMP = "build.timestamp"; + public static final String CLEAR_CACHE = "db.clear.cache"; + public static final String CONTENT_FOLDER = "content.folder"; + public static final String DATE_FORMAT = "date.format"; + public static final String DB_STORE = "db.store"; + public static final String DB_PATH = "db.path"; + public static final String DEFAULT_STATUS = "default.status"; + public static final String DEFAULT_TYPE = "default.type"; + public static final String DESTINATION_FOLDER = "destination.folder"; + public static final String DRAFT_SUFFIX = "draft.suffix"; + public static final String FEED_FILE = "feed.file"; + public static final String HEADER_SEPARATOR = "header.separator"; + public static final String INDEX_FILE = "index.file"; + public static final String MARKDOWN_EXTENSIONS = "markdown.extensions"; + public static final String OUTPUT_EXTENSION = "output.extension"; + public static final String PAGINATE_INDEX = "index.paginate"; + public static final String POSTS_PER_PAGE = "index.posts_per_page"; + public static final String RENDER_ARCHIVE = "render.archive"; + public static final String RENDER_FEED = "render.feed"; + public static final String RENDER_INDEX = "render.index"; + public static final String RENDER_SITEMAP = "render.sitemap"; + public static final String RENDER_TAGS = "render.tags"; + public static final String RENDER_TAGS_INDEX = "render.tagsindex"; + public static final String RENDER_ENCODING = "render.encoding"; + public static final String SERVER_PORT = "server.port"; + public static final String SITE_HOST = "site.host"; + public static final String SITEMAP_FILE = "sitemap.file"; + public static final String TAG_SANITIZE = "tag.sanitize"; + public static final String TAG_PATH = "tag.path"; + public static final String TEMPLATE_FOLDER = "template.folder"; + public static final String TEMPLATE_ENCODING = "template.encoding"; + public static final String THYMELEAF_LOCALE = "thymeleaf.locale"; + public static final String URI_NO_EXTENSION = "uri.noExtension"; + public static final String URI_NO_EXTENSION_PREFIX = "uri.noExtension.prefix"; + public static final String VERSION = "version"; + + private JBakeProperty() {} + +} diff --git a/jbake-core/src/main/java/org/jbake/launcher/BakeWatcher.java b/jbake-core/src/main/java/org/jbake/launcher/BakeWatcher.java index a55017321..b6e2df1e8 100644 --- a/jbake-core/src/main/java/org/jbake/launcher/BakeWatcher.java +++ b/jbake-core/src/main/java/org/jbake/launcher/BakeWatcher.java @@ -6,7 +6,10 @@ import org.apache.commons.vfs2.FileSystemManager; import org.apache.commons.vfs2.VFS; import org.apache.commons.vfs2.impl.DefaultFileMonitor; -import org.jbake.app.ConfigUtil; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Delegate responsible for watching the file system for changes. @@ -15,29 +18,43 @@ */ public class BakeWatcher { + private Logger logger = LoggerFactory.getLogger(BakeWatcher.class); + /** * Starts watching the file system for changes to trigger a bake. * - * @param res Commandline options + * @deprecated use {@link BakeWatcher#start(JBakeConfiguration)} instead + * + * @param res Commandline options * @param config Configuration settings */ + @Deprecated public void start(final LaunchOptions res, CompositeConfiguration config) { + JBakeConfiguration configuration = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(res.getSource(), config); + start(configuration); + } + + /** + * Starts watching the file system for changes to trigger a bake. + * + * @param config JBakeConfiguration settings + */ + public void start(JBakeConfiguration config) { try { FileSystemManager fsMan = VFS.getManager(); - FileObject listenPath = fsMan.resolveFile(res.getSource(), config.getString( ConfigUtil.Keys.CONTENT_FOLDER)); - FileObject templateListenPath = fsMan.resolveFile(res.getSource(), config.getString( ConfigUtil.Keys.TEMPLATE_FOLDER)); - FileObject assetPath = fsMan.resolveFile(res.getSource(), config.getString( ConfigUtil.Keys.ASSET_FOLDER)); - + FileObject listenPath = fsMan.resolveFile(config.getContentFolder().toURI()); + FileObject templateListenPath = fsMan.resolveFile(config.getTemplateFolder().toURI()); + FileObject assetPath = fsMan.resolveFile(config.getAssetFolder().toURI()); - System.out.println("Watching for (content, template, asset) changes in [" + res.getSource() + "]"); - DefaultFileMonitor monitor = new DefaultFileMonitor(new CustomFSChangeListener(res, config)); + logger.info("Watching for (content, template, asset) changes in [{}]", config.getSourceFolder().getPath()); + DefaultFileMonitor monitor = new DefaultFileMonitor(new CustomFSChangeListener(config)); monitor.setRecursive(true); monitor.addFile(listenPath); monitor.addFile(templateListenPath); monitor.addFile(assetPath); monitor.start(); } catch (FileSystemException e) { - e.printStackTrace(); + logger.error("Problems watching filesystem changes", e); } } } diff --git a/jbake-core/src/main/java/org/jbake/launcher/Baker.java b/jbake-core/src/main/java/org/jbake/launcher/Baker.java index 6641a70bf..a1dcb373e 100644 --- a/jbake-core/src/main/java/org/jbake/launcher/Baker.java +++ b/jbake-core/src/main/java/org/jbake/launcher/Baker.java @@ -3,6 +3,8 @@ import org.apache.commons.configuration.CompositeConfiguration; import org.jbake.app.JBakeException; import org.jbake.app.Oven; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; import java.text.MessageFormat; import java.util.List; @@ -14,9 +16,19 @@ */ public class Baker { + /** + * @param options The given cli options + * @param config The project configuration + * @deprecated use {@link Baker#bake(JBakeConfiguration)} instead + */ + @Deprecated public void bake(final LaunchOptions options, final CompositeConfiguration config) { - final Oven oven = new Oven(options.getSource(), options.getDestination(), config, options.isClearCache()); - oven.setupPaths(); + JBakeConfiguration configuration = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(options.getSource(), options.getDestination(), config, options.isClearCache()); + bake(configuration); + } + + public void bake(final JBakeConfiguration config) { + final Oven oven = new Oven(config); oven.bake(); final List errors = oven.getErrors(); diff --git a/jbake-core/src/main/java/org/jbake/launcher/CustomFSChangeListener.java b/jbake-core/src/main/java/org/jbake/launcher/CustomFSChangeListener.java index 9ee1b2099..9e69f544c 100644 --- a/jbake-core/src/main/java/org/jbake/launcher/CustomFSChangeListener.java +++ b/jbake-core/src/main/java/org/jbake/launcher/CustomFSChangeListener.java @@ -1,9 +1,9 @@ package org.jbake.launcher; -import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.vfs2.FileChangeEvent; import org.apache.commons.vfs2.FileListener; import org.jbake.app.Oven; +import org.jbake.app.configuration.JBakeConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,11 +11,9 @@ public class CustomFSChangeListener implements FileListener { private final static Logger LOGGER = LoggerFactory.getLogger(CustomFSChangeListener.class); - private LaunchOptions options; - private CompositeConfiguration config; + private JBakeConfiguration config; - public CustomFSChangeListener(LaunchOptions res, CompositeConfiguration config) { - this.options = res; + public CustomFSChangeListener(JBakeConfiguration config) { this.config = config; } @@ -38,8 +36,7 @@ public void fileChanged(FileChangeEvent event) throws Exception { } private void exec() { - final Oven oven = new Oven(options.getSource(), options.getDestination(), config, options.isClearCache()); - oven.setupPaths(); + final Oven oven = new Oven(config); oven.bake(); } diff --git a/jbake-core/src/main/java/org/jbake/launcher/Init.java b/jbake-core/src/main/java/org/jbake/launcher/Init.java index de0baf92b..51edd839d 100644 --- a/jbake-core/src/main/java/org/jbake/launcher/Init.java +++ b/jbake-core/src/main/java/org/jbake/launcher/Init.java @@ -1,8 +1,9 @@ package org.jbake.launcher; import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ZipUtil; +import org.jbake.app.configuration.DefaultJBakeConfiguration; +import org.jbake.app.configuration.JBakeConfiguration; import java.io.File; import java.io.FileInputStream; @@ -15,9 +16,18 @@ */ public class Init { - private CompositeConfiguration config; - + private JBakeConfiguration config; + + /** + * @param config The project configuration + * @deprecated use {@link Init#Init(JBakeConfiguration)} instead + */ + @Deprecated public Init(CompositeConfiguration config) { + this(new DefaultJBakeConfiguration(config)); + } + + public Init(JBakeConfiguration config) { this.config = config; } @@ -39,13 +49,13 @@ public void run(File outputFolder, File templateLocationFolder, String templateT if (contents != null) { for (File content : contents) { if (content.isDirectory()) { - if (content.getName().equalsIgnoreCase(config.getString(Keys.TEMPLATE_FOLDER))) { + if (content.getName().equalsIgnoreCase(config.getTemplateFolderName())) { safe = false; } - if (content.getName().equalsIgnoreCase(config.getString(Keys.CONTENT_FOLDER))) { + if (content.getName().equalsIgnoreCase(config.getContentFolderName())) { safe = false; } - if (content.getName().equalsIgnoreCase(config.getString(Keys.ASSET_FOLDER))) { + if (content.getName().equalsIgnoreCase(config.getAssetFolderName())) { safe = false; } } @@ -56,8 +66,8 @@ public void run(File outputFolder, File templateLocationFolder, String templateT throw new Exception(String.format("Output folder '%s' already contains structure!", outputFolder.getAbsolutePath())); } - if (config.getString("example.project."+templateType) != null) { - File templateFile = new File(templateLocationFolder, config.getString("example.project."+templateType)); + if (config.getExampleProjectByType(templateType) != null) { + File templateFile = new File(templateLocationFolder, config.getExampleProjectByType(templateType)); if (!templateFile.exists()) { throw new Exception("Cannot find example project file: " + templateFile.getPath()); } diff --git a/jbake-core/src/main/java/org/jbake/launcher/JettyServer.java b/jbake-core/src/main/java/org/jbake/launcher/JettyServer.java index e82280cce..7e24af0f4 100644 --- a/jbake-core/src/main/java/org/jbake/launcher/JettyServer.java +++ b/jbake-core/src/main/java/org/jbake/launcher/JettyServer.java @@ -16,7 +16,7 @@ * @author Jonathan Bullock jonbullock@gmail.com */ public class JettyServer { - private final static Logger LOGGER = LoggerFactory.getLogger(JettyServer.class); + private static final Logger LOGGER = LoggerFactory.getLogger(JettyServer.class); /** * Run Jetty web server serving out supplied path on supplied port @@ -48,7 +48,7 @@ public void run(String path, String port) { server.start(); server.join(); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error("unable to start server", e); } } } diff --git a/jbake-core/src/main/java/org/jbake/launcher/LaunchOptions.java b/jbake-core/src/main/java/org/jbake/launcher/LaunchOptions.java index 4e3ebf2d7..d30bd5865 100644 --- a/jbake-core/src/main/java/org/jbake/launcher/LaunchOptions.java +++ b/jbake-core/src/main/java/org/jbake/launcher/LaunchOptions.java @@ -1,83 +1,84 @@ package org.jbake.launcher; -import java.io.File; - import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; +import java.io.File; + public class LaunchOptions { - @Argument(index = 0, usage = "source folder of site content (with templates and assets), if not supplied will default to current directory", metaVar = "") - private String source; - - @Argument(index = 1, usage = "destination folder for output, if not supplied will default to a folder called \"output\" in the current directory", metaVar = "") - private String destination; - - @Option(name = "-b", aliases = {"--bake"}, usage="performs a bake") - private boolean bake; - - @Option(name = "-i", aliases = {"--init"}, usage="initialises required folder structure with default templates (defaults to current directory if is not supplied)") - private boolean init; - - @Option(name = "-t", aliases = {"--template"}, usage="use specified template engine for default templates (uses Freemarker if is not supplied) ", depends = ("-i")) + @Argument(index = 0, usage = "source folder of site content (with templates and assets), if not supplied will default to current directory", metaVar = "") + private String source; + + @Argument(index = 1, usage = "destination folder for output, if not supplied will default to a folder called \"output\" in the current directory", metaVar = "") + private String destination; + + @Option(name = "-b", aliases = {"--bake"}, usage = "performs a bake") + private boolean bake; + + @Option(name = "-i", aliases = {"--init"}, usage = "initialises required folder structure with default templates (defaults to current directory if is not supplied)") + private boolean init; + + @Option(name = "-t", aliases = {"--template"}, usage = "use specified template engine for default templates (uses Freemarker if is not supplied) ", depends = ("-i")) private String template; - @Option(name = "-s", aliases = {"--server"}, usage="runs HTTP server to serve out baked site, if no is supplied will default to a folder called \"output\" in the current directory") - private boolean runServer; - - @Option(name = "-h", aliases = {"--help"}, usage="prints this message") - private boolean helpNeeded; + @Option(name = "-s", aliases = {"--server"}, usage = "runs HTTP server to serve out baked site, if no is supplied will default to a folder called \"output\" in the current directory") + private boolean runServer; + + @Option(name = "-h", aliases = {"--help"}, usage = "prints this message") + private boolean helpNeeded; - @Option(name = "--reset", usage="clears the local cache, enforcing rendering from scratch") + @Option(name = "--reset", usage = "clears the local cache, enforcing rendering from scratch") private boolean clearCache; public String getTemplate() { - if (template != null) { - return template; - } else { - return "freemarker"; - } - } - public File getSource() { - if (source != null) { - return new File(source); - } else { - return new File("."); - } - } - - public String getSourceValue() { - return source; - } - - public File getDestination() { - if (destination != null) { - return new File(destination); - } else { - return null; - } - } - - public String getDestinationValue() { - return destination; - } - - public boolean isHelpNeeded() { - return helpNeeded || !(isBake() || isRunServer() || isInit() || source != null || destination != null); - } - - public boolean isRunServer() { - return runServer; - } - - public boolean isInit() { - return init; - } + if (template != null) { + return template; + } else { + return "freemarker"; + } + } + + public File getSource() { + if (source != null) { + return new File(source); + } else { + return new File(System.getProperty("user.dir")); + } + } + + public String getSourceValue() { + return source; + } + + public File getDestination() { + if (destination != null) { + return new File(destination); + } else { + return new File(getSource(), "output"); + } + } + + public String getDestinationValue() { + return destination; + } + + public boolean isHelpNeeded() { + return helpNeeded || !(isBake() || isRunServer() || isInit() || source != null || destination != null); + } + + public boolean isRunServer() { + return runServer; + } + + public boolean isInit() { + return init; + } public boolean isClearCache() { return clearCache; } public boolean isBake() { - return bake || (source != null && destination != null); - } + return bake || (source != null && destination != null); + } } diff --git a/jbake-core/src/main/java/org/jbake/launcher/Main.java b/jbake-core/src/main/java/org/jbake/launcher/Main.java index 001d2c76f..6834649b9 100644 --- a/jbake-core/src/main/java/org/jbake/launcher/Main.java +++ b/jbake-core/src/main/java/org/jbake/launcher/Main.java @@ -1,180 +1,183 @@ package org.jbake.launcher; -import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.configuration.ConfigurationException; -import org.jbake.app.ConfigUtil; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.FileUtil; import org.jbake.app.JBakeException; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import org.slf4j.bridge.SLF4JBridgeHandler; import java.io.File; import java.io.StringWriter; -import java.text.MessageFormat; -import java.util.List; /** * Launcher for JBake. - * - * @author Jonathan Bullock jonbullock@gmail.com * + * @author Jonathan Bullock jonbullock@gmail.com */ public class Main { - private final String USAGE_PREFIX = "Usage: jbake"; - private final String ALT_USAGE_PREFIX = " or jbake"; - - /** - * Runs the app with the given arguments. - * - * @param args Application arguments - */ - public static void main(final String[] args) { - try { - new Main().run(args); - } catch (final JBakeException e) { - System.err.println(e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } catch (final Throwable e) { - System.err.println("An unexpected error occurred: " + e.getMessage()); - System.exit(2); - } - } - - private Baker baker; - private JettyServer jettyServer; - private BakeWatcher watcher; - - /** - * Default constructor. - */ - public Main() { - this(new Baker(), new JettyServer(), new BakeWatcher()); - } - - /** - * Optional constructor to externalize dependencies. - * - * @param baker A {@link Baker} instance - * @param jetty A {@link JettyServer} instance - * @param watcher A {@link BakeWatcher} instance - */ - protected Main(Baker baker, JettyServer jetty, BakeWatcher watcher) { - this.baker = baker; - this.jettyServer = jetty; - this.watcher = watcher; - } - - protected void run(String[] args) { - SLF4JBridgeHandler.removeHandlersForRootLogger(); - SLF4JBridgeHandler.install(); - LaunchOptions res = parseArguments( args ); - - final CompositeConfiguration config; - try { - config = ConfigUtil.load( res.getSource(), res.isRunServer() ); - } catch( final ConfigurationException e ) { - throw new JBakeException( "Configuration error: " + e.getMessage(), e ); - } - run(res, config); - } - - protected void run(LaunchOptions res, CompositeConfiguration config) { - System.out.println("JBake " + config.getString(Keys.VERSION) + " (" + config.getString(Keys.BUILD_TIMESTAMP) + ") [http://jbake.org]"); - System.out.println(); - - if (res.isHelpNeeded()) { - printUsage(res); - // Help was requested, so we are done here - return; - } - - if (res.isBake()) { - ConfigUtil.displayLegacyConfigFileWarningIfRequired(); - baker.bake(res, config); - } - - if (res.isInit()) { - initStructure(config, res.getTemplate(), res.getSourceValue()); - } - - if (res.isRunServer()) { - ConfigUtil.displayLegacyConfigFileWarningIfRequired(); - watcher.start(res, config); - // TODO: short term fix until bake, server, init commands no longer share underlying values (such as source/dest) - if (res.isBake()) { - // bake and server commands have been run together - if (res.getDestination() != null) { - // use the destination provided via the commandline - runServer( res.getDestination().getPath(), config.getString( Keys.SERVER_PORT )); - } else if (!res.getSource().getPath().equals(".")) { - // use the source folder provided via the commandline - runServer( res.getSource().getPath(), config.getString( Keys.SERVER_PORT )); - } else { - // use the default DESTINATION_FOLDER value - runServer(config.getString( Keys.DESTINATION_FOLDER ), config.getString(Keys.SERVER_PORT)); - } - } else { - // server command run on it's own - if (!res.getSource().getPath().equals(".")) { - runServer( res.getSource().getPath(), config.getString( Keys.SERVER_PORT )); - } else { - // use the default destination folder - runServer( config.getString( Keys.DESTINATION_FOLDER ), config.getString( Keys.SERVER_PORT ) ); - } - } - } - - } - private LaunchOptions parseArguments(String[] args) { - LaunchOptions res = new LaunchOptions(); - CmdLineParser parser = new CmdLineParser(res); - - try { - parser.parseArgument(args); - } catch (final CmdLineException e) { - printUsage(res); - throw new JBakeException("Invalid commandline arguments: " + e.getMessage(), e); - } - - return res; - } - - private void printUsage(Object options) { - CmdLineParser parser = new CmdLineParser(options); - StringWriter sw = new StringWriter(); - sw.append(USAGE_PREFIX + "\n"); - sw.append(ALT_USAGE_PREFIX + " \n"); - sw.append(ALT_USAGE_PREFIX + " [OPTION]... [...]\n\n"); - sw.append("Options:"); - System.out.println(sw.toString()); - parser.getProperties().withUsageWidth(100); - parser.printUsage(System.out); - } - - private void runServer(String path, String port) { - jettyServer.run(path, port); - } - - private void initStructure(CompositeConfiguration config, String type, String source) { + private final String USAGE_PREFIX = "Usage: jbake"; + private final String ALT_USAGE_PREFIX = " or jbake"; + private Baker baker; + private JettyServer jettyServer; + private BakeWatcher watcher; + private JBakeConfigurationFactory configurationFactory; + /** + * Default constructor. + */ + public Main() { + this(new Baker(), new JettyServer(), new BakeWatcher()); + } + + /** + * Optional constructor to externalize dependencies. + * + * @param baker A {@link Baker} instance + * @param jetty A {@link JettyServer} instance + * @param watcher A {@link BakeWatcher} instance + */ + protected Main(Baker baker, JettyServer jetty, BakeWatcher watcher) { + this.baker = baker; + this.jettyServer = jetty; + this.watcher = watcher; + this.configurationFactory = new JBakeConfigurationFactory(); + } + + /** + * Runs the app with the given arguments. + * + * @param args Application arguments + */ + public static void main(final String[] args) { + try { + new Main().run(args); + } catch (final JBakeException e) { + System.err.println(e.getMessage()); + e.printStackTrace(System.err); + System.exit(1); + } catch (final Throwable e) { + System.err.println("An unexpected error occurred: " + e.getMessage()); + System.exit(2); + } + } + + protected void run(String[] args) { + SLF4JBridgeHandler.removeHandlersForRootLogger(); + SLF4JBridgeHandler.install(); + LaunchOptions res = parseArguments(args); + + final JBakeConfiguration config; + try { + if (res.isRunServer()) { + config = getJBakeConfigurationFactory().createJettyJbakeConfiguration(res.getSource(), res.getDestination(), res.isClearCache()); + } else { + config = getJBakeConfigurationFactory().createDefaultJbakeConfiguration(res.getSource(), res.getDestination(), res.isClearCache()); + } + } catch (final ConfigurationException e) { + throw new JBakeException("Configuration error: " + e.getMessage(), e); + } + run(res, config); + } + + protected void run(LaunchOptions res, JBakeConfiguration config) { + System.out.println("JBake " + config.getVersion() + " (" + config.getBuildTimeStamp() + ") [http://jbake.org]"); + System.out.println(); + + if (res.isHelpNeeded()) { + printUsage(res); + // Help was requested, so we are done here + return; + } + + if (res.isBake()) { + baker.bake(config); + } + + if (res.isInit()) { + initStructure(res.getTemplate(), config); + } + + if (res.isRunServer()) { + watcher.start(config); + // TODO: short term fix until bake, server, init commands no longer share underlying values (such as source/dest) + if (res.isBake()) { + // bake and server commands have been run together + if (res.getDestination() != null) { + // use the destination provided via the commandline + runServer(res.getDestination(), config.getServerPort()); + } else if (!res.getSource().getPath().equals(".")) { + // use the source folder provided via the commandline + runServer(res.getSource(), config.getServerPort()); + } else { + // use the default DESTINATION_FOLDER value + runServer(config.getDestinationFolder(), config.getServerPort()); + } + } else { + // use the default destination folder + runServer(config.getDestinationFolder(), config.getServerPort()); + } + } + + } + + private LaunchOptions parseArguments(String[] args) { + LaunchOptions res = new LaunchOptions(); + CmdLineParser parser = new CmdLineParser(res); + + try { + parser.parseArgument(args); + } catch (final CmdLineException e) { + printUsage(res); + throw new JBakeException("Invalid commandline arguments: " + e.getMessage(), e); + } + + return res; + } + + private void printUsage(Object options) { + CmdLineParser parser = new CmdLineParser(options); + StringWriter sw = new StringWriter(); + sw.append(USAGE_PREFIX + "\n"); + sw.append(ALT_USAGE_PREFIX + " \n"); + sw.append(ALT_USAGE_PREFIX + " [OPTION]... [...]\n\n"); + sw.append("Options:"); + System.out.println(sw.toString()); + parser.getProperties().withUsageWidth(100); + parser.printUsage(System.out); + } + + private void runServer(File path, int port) { + jettyServer.run(path.getPath(), String.valueOf(port)); + } + + private void initStructure(String type, JBakeConfiguration config) { Init init = new Init(config); - try { + try { File templateFolder = FileUtil.getRunningLocation(); File outputFolder; - if(source != null){ - outputFolder = new File(source); - } else{ + if (config.getSourceFolder() != null) { + outputFolder = config.getSourceFolder(); + } else { outputFolder = new File("."); } init.run(outputFolder, templateFolder, type); - System.out.println("Base folder structure successfully created."); - } catch (final Exception e) { - final String msg = "Failed to initialise structure: " + e.getMessage(); - throw new JBakeException(msg, e); - } - } + System.out.println("Base folder structure successfully created."); + } catch (final Exception e) { + final String msg = "Failed to initialise structure: " + e.getMessage(); + throw new JBakeException(msg, e); + } + } + + public JBakeConfigurationFactory getJBakeConfigurationFactory() { + return configurationFactory; + } + + public void setJBakeConfigurationFactory(JBakeConfigurationFactory factory) { + configurationFactory = factory; + } + } diff --git a/jbake-core/src/main/java/org/jbake/model/DocumentStatus.java b/jbake-core/src/main/java/org/jbake/model/DocumentStatus.java index bec29010e..86d182b9f 100644 --- a/jbake-core/src/main/java/org/jbake/model/DocumentStatus.java +++ b/jbake-core/src/main/java/org/jbake/model/DocumentStatus.java @@ -9,6 +9,5 @@ public enum DocumentStatus { NEW, UPDATED, - REMOVED, IDENTICAL } diff --git a/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java b/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java index 081db009c..847a3098a 100644 --- a/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java +++ b/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java @@ -1,25 +1,23 @@ package org.jbake.parser; -import org.apache.commons.configuration.Configuration; import org.asciidoctor.Asciidoctor; import org.asciidoctor.AttributesBuilder; import org.asciidoctor.Options; import org.asciidoctor.ast.DocumentHeader; -import org.jbake.app.ConfigUtil.Keys; +import org.jbake.app.configuration.JBakeConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; -import static org.apache.commons.lang.BooleanUtils.toBooleanObject; -import static org.apache.commons.lang.math.NumberUtils.isNumber; -import static org.apache.commons.lang.math.NumberUtils.toInt; import static org.asciidoctor.AttributesBuilder.attributes; import static org.asciidoctor.OptionsBuilder.options; import static org.asciidoctor.SafeMode.UNSAFE; @@ -30,7 +28,9 @@ * @author Cédric Champeau */ public class AsciidoctorEngine extends MarkupEngine { - private final static Logger LOGGER = LoggerFactory.getLogger(AsciidoctorEngine.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AsciidoctorEngine.class); + public static final String JBAKE_PREFIX = "jbake-"; + public static final String REVDATE_KEY = "revdate"; private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); @@ -42,17 +42,17 @@ public class AsciidoctorEngine extends MarkupEngine { public AsciidoctorEngine() { Class engineClass = Asciidoctor.class; - assert engineClass!=null; + assert engineClass != null; } private Asciidoctor getEngine(Options options) { try { lock.readLock().lock(); - if (engine==null) { + if (engine == null) { lock.readLock().unlock(); try { lock.writeLock().lock(); - if (engine==null) { + if (engine == null) { LOGGER.info("Initializing Asciidoctor engine..."); if (options.map().containsKey(OPT_GEM_PATH)) { engine = Asciidoctor.Factory.create(String.valueOf(options.map().get(OPT_GEM_PATH))); @@ -89,33 +89,33 @@ public void processHeader(final ParserContext context) { DocumentHeader header = asciidoctor.readDocumentHeader(context.getFile()); Map contents = context.getContents(); if (header.getDocumentTitle() != null) { - contents.put("title", header.getDocumentTitle().getCombined()); + contents.put("title", header.getDocumentTitle().getCombined()); } Map attributes = header.getAttributes(); - for (String key : attributes.keySet()) { - if (key.startsWith("jbake-")) { - Object val = attributes.get(key); - if (val!=null) { - String pKey = key.substring(6); - contents.put(pKey, val); - } + for (Map.Entry attribute : attributes.entrySet()) { + String key = attribute.getKey(); + Object value = attribute.getValue(); + + if (hasJBakePrefix(key)) { + String pKey = key.substring(6); + contents.put(pKey, value); } - if (key.equals("revdate")) { - if (attributes.get(key) != null && attributes.get(key) instanceof String) { - - DateFormat df = new SimpleDateFormat(context.getConfig().getString(Keys.DATE_FORMAT)); - Date date = null; - try { - date = df.parse((String)attributes.get(key)); - contents.put("date", date); - } catch (ParseException e) { - e.printStackTrace(); - } + if (hasRevdate(key) && canCastToString(value)) { + + String dateFormat = context.getConfig().getDateFormat(); + DateFormat df = new SimpleDateFormat(dateFormat); + try { + Date date = df.parse((String) value); + contents.put("date", date); + } catch (ParseException e) { + LOGGER.error("Unable to parse revdate. Expected {}", dateFormat, e); } } if (key.equals("jbake-tags")) { - if (attributes.get(key) != null && attributes.get(key) instanceof String) { - contents.put("tags", ((String) attributes.get(key)).split(",")); + if (canCastToString(value)) { + contents.put("tags", ((String) value).split(",")); + } else { + LOGGER.error("Wrong value of 'jbake-tags'. Expected a String got '{}'", getValueClassName(value)); } } else { contents.put(key, attributes.get(key)); @@ -123,6 +123,23 @@ public void processHeader(final ParserContext context) { } } + private boolean canCastToString(Object value) { + return value != null && value instanceof String; + } + + private String getValueClassName(Object value) { + return (value == null) ? "null" : value.getClass().getCanonicalName(); + } + + private boolean hasRevdate(String key) { + return key.equals(REVDATE_KEY); + } + + private boolean hasJBakePrefix(String key) { + return key.startsWith(JBAKE_PREFIX); + } + + // TODO: write tests with options and attributes @Override public void processBody(ParserContext context) { StringBuilder body = new StringBuilder(context.getBody().length()); @@ -142,42 +159,49 @@ private void processAsciiDoc(ParserContext context) { } private Options getAsciiDocOptionsAndAttributes(ParserContext context) { - Configuration config = context.getConfig(); - final AttributesBuilder attributes = attributes(config.getStringArray(Keys.ASCIIDOCTOR_ATTRIBUTES)); - if (config.getBoolean(Keys.ASCIIDOCTOR_ATTRIBUTES_EXPORT, false)) { - final String prefix = config.getString( Keys.ASCIIDOCTOR_ATTRIBUTES_EXPORT_PREFIX, ""); - for (final Iterator it = config.getKeys(); it.hasNext();) { + JBakeConfiguration config = context.getConfig(); + List asciidoctorAttributes = config.getAsciidoctorAttributes(); + final AttributesBuilder attributes = attributes(asciidoctorAttributes.toArray(new String[asciidoctorAttributes.size()])); + if (config.getExportAsciidoctorAttributes()) { + final String prefix = config.getAttributesExportPrefixForAsciidoctor(); + + for (final Iterator it = config.getKeys(); it.hasNext(); ) { final String key = it.next(); if (!key.startsWith("asciidoctor")) { - attributes.attribute(prefix + key.replace(".", "_"), config.getProperty(key)); + attributes.attribute(prefix + key.replace(".", "_"), config.get(key)); } } } - final Configuration optionsSubset = config.subset(Keys.ASCIIDOCTOR_OPTION); + + final List optionsSubset = config.getAsciidoctorOptionKeys(); final Options options = options().attributes(attributes.get()).get(); - for (final Iterator iterator = optionsSubset.getKeys(); iterator.hasNext();) { - final String name = iterator.next(); - if (name.equals(Options.TEMPLATE_DIRS)) { - options.setTemplateDirs(optionsSubset.getString(name)); + for (final String optionKey : optionsSubset) { + + Object optionValue = config.getAsciidoctorOption(optionKey); + if (optionKey.equals(Options.TEMPLATE_DIRS)) { + List dirs = getAsList(optionValue); + if (!dirs.isEmpty()) { + options.setTemplateDirs(String.valueOf(dirs)); + } } else { - options.setOption(name, guessTypeByContent(optionsSubset.getString(name))); + options.setOption(optionKey, optionValue); } + } options.setBaseDir(context.getFile().getParentFile().getAbsolutePath()); options.setSafe(UNSAFE); return options; } - /** - * Guess the type by content it has. - * @param value - * @return boolean,integer of string as fallback - */ - private static Object guessTypeByContent(String value){ - if (toBooleanObject(value)!=null) - return toBooleanObject(value); - if(isNumber(value)) - return toInt(value); - return value; + private List getAsList(Object asciidoctorOption) { + List values = new ArrayList<>(); + + if (asciidoctorOption instanceof List) { + values.addAll((List) asciidoctorOption); + } else if (asciidoctorOption instanceof String) { + values.add(String.valueOf(asciidoctorOption)); + } + return values; } + } diff --git a/jbake-core/src/main/java/org/jbake/parser/Engines.java b/jbake-core/src/main/java/org/jbake/parser/Engines.java index dc9c2329a..c0558377c 100644 --- a/jbake-core/src/main/java/org/jbake/parser/Engines.java +++ b/jbake-core/src/main/java/org/jbake/parser/Engines.java @@ -4,8 +4,8 @@ import org.slf4j.LoggerFactory; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.URL; -import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -40,17 +40,17 @@ * */ public class Engines { - private final static Logger LOGGER = LoggerFactory.getLogger(Engines.class); + private static final Logger LOGGER = LoggerFactory.getLogger(Engines.class); + private static final Engines INSTANCE; + + private final Map parsers; - private final static Engines INSTANCE; static { INSTANCE = new Engines(); loadEngines(); } - private final Map parsers; - public static ParserEngine get(String fileExtension) { return INSTANCE.getEngine(fileExtension); } @@ -64,7 +64,7 @@ public static Set getRecognizedExtensions() { } private Engines() { - parsers = new HashMap(); + parsers = new HashMap<>(); } private void registerEngine(String fileExtension, ParserEngine markupEngine) { @@ -89,17 +89,13 @@ private static ParserEngine tryLoadEngine(String engineClassName) { try { @SuppressWarnings("unchecked") Class engineClass = (Class) Class.forName(engineClassName, false, Engines.class.getClassLoader()); - return engineClass.newInstance(); - } catch (ClassNotFoundException e) { - return new ErrorEngine(engineClassName); - } catch (InstantiationException e) { - return new ErrorEngine(engineClassName); - } catch (IllegalAccessException e) { - return new ErrorEngine(engineClassName); - } catch (NoClassDefFoundError e) { - // a dependency of the engine may not be found on classpath + return engineClass.getDeclaredConstructor().newInstance(); + } catch (ClassNotFoundException | NoClassDefFoundError | IllegalAccessException | InstantiationException e) { return new ErrorEngine(engineClassName); + } catch (NoSuchMethodException | InvocationTargetException e) { + LOGGER.error("unable to instantiate ParserEngine {}", engineClassName); } + return null; } /** @@ -121,7 +117,7 @@ private static void loadEngines() { } } } catch (IOException e) { - e.printStackTrace(); + LOGGER.error("Error loading Engines", e); } } diff --git a/jbake-core/src/main/java/org/jbake/parser/ErrorEngine.java b/jbake-core/src/main/java/org/jbake/parser/ErrorEngine.java index 976eaf4b0..4b01a3b13 100644 --- a/jbake-core/src/main/java/org/jbake/parser/ErrorEngine.java +++ b/jbake-core/src/main/java/org/jbake/parser/ErrorEngine.java @@ -1,10 +1,10 @@ package org.jbake.parser; +import org.jbake.app.Crawler.Attributes; + import java.util.Date; import java.util.Map; -import org.jbake.app.Crawler.Attributes; - /** * An internal rendering engine used to notify the user that the markup format he used requires an engine that couldn't * be loaded. diff --git a/jbake-core/src/main/java/org/jbake/parser/MarkdownEngine.java b/jbake-core/src/main/java/org/jbake/parser/MarkdownEngine.java index faac9e515..bda4f1660 100644 --- a/jbake-core/src/main/java/org/jbake/parser/MarkdownEngine.java +++ b/jbake-core/src/main/java/org/jbake/parser/MarkdownEngine.java @@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.Field; +import java.util.List; /** * Renders documents in the Markdown format. @@ -22,12 +23,11 @@ public class MarkdownEngine extends MarkupEngine { @Override public void processBody(final ParserContext context) { - String[] mdExts = context.getConfig().getStringArray("markdown.extensions"); + List mdExts = context.getConfig().getMarkdownExtensions(); int extensions = Extensions.NONE; - if (mdExts.length > 0) { - for (int index = 0; index < mdExts.length; index++) { - String ext = mdExts[index]; + if (mdExts.size() > 0) { + for (String ext : mdExts) { if (ext.startsWith("-")) { ext = ext.substring(1); extensions=removeExtension(extensions, extensionFor(ext)); diff --git a/jbake-core/src/main/java/org/jbake/parser/MarkupEngine.java b/jbake-core/src/main/java/org/jbake/parser/MarkupEngine.java index bc55baae6..9528ecef1 100644 --- a/jbake-core/src/main/java/org/jbake/parser/MarkupEngine.java +++ b/jbake-core/src/main/java/org/jbake/parser/MarkupEngine.java @@ -1,5 +1,15 @@ package org.jbake.parser; +import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.io.IOUtils; +import org.jbake.app.Crawler; +import org.jbake.app.configuration.DefaultJBakeConfiguration; +import org.jbake.app.configuration.JBakeConfiguration; +import org.json.simple.JSONValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -7,20 +17,11 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.io.IOUtils; -import org.jbake.app.ConfigUtil.Keys; -import org.jbake.app.Crawler; -import org.json.simple.JSONValue; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Base class for markup engine wrappers. A markup engine is responsible for rendering * markup in a source file and exporting the result into the {@link ParserContext#getContents() contents} map. @@ -31,6 +32,9 @@ */ public abstract class MarkupEngine implements ParserEngine { private static final Logger LOGGER = LoggerFactory.getLogger(MarkupEngine.class); + private static final String UTF_8_BOM = "\uFEFF"; + + private JBakeConfiguration configuration; /** * Tests if this markup engine can process the document. @@ -60,70 +64,57 @@ public void processHeader(final ParserContext context) { public void processBody(final ParserContext context) { } + @Override + public Map parse(Configuration config, File file, String contentPath) { + return parse(new DefaultJBakeConfiguration((CompositeConfiguration) config), file); + } + /** * Parse given file to extract as much infos as possible * * @param file file to process * @return a map containing all infos. Returning null indicates an error, even if an exception would be better. */ - public Map parse(Configuration config, File file, String contentPath) { - - Map content = new HashMap(); - List fileContents = null; + public Map parse(JBakeConfiguration config, File file) { + this.configuration = config; + Map documentModel = new HashMap<>(); + List fileContents; try (InputStream is = new FileInputStream(file)) { - fileContents = IOUtils.readLines(is, config.getString(Keys.RENDER_ENCODING)); + fileContents = IOUtils.readLines(is, config.getRenderEncoding()); } catch (IOException e) { LOGGER.error("Error while opening file {}", file, e); return null; } - boolean hasHeader = hasHeader(config, fileContents); + boolean hasHeader = hasHeader(fileContents); ParserContext context = new ParserContext( file, fileContents, config, - contentPath, hasHeader, - content + documentModel ); if (hasHeader) { // read header from file - processHeader(config, fileContents, content); + processHeader(fileContents, documentModel); } // then read engine specific headers processHeader(context); - if (content.get(Crawler.Attributes.DATE) == null) { - content.put(Crawler.Attributes.DATE, new Date(file.lastModified())); - } - - if (config.getString(Keys.DEFAULT_STATUS) != null) { - // default status has been set - if (content.get(Crawler.Attributes.STATUS) == null) { - // file hasn't got status so use default - content.put(Crawler.Attributes.STATUS, config.getString(Keys.DEFAULT_STATUS)); - } - } - - if (config.getString(Keys.DEFAULT_TYPE) != null) { - // default type has been set - if (content.get(Crawler.Attributes.TYPE) == null) { - // file hasn't got type so use default - content.put(Crawler.Attributes.TYPE, config.getString(Keys.DEFAULT_TYPE)); - } - } + setModelDefaultsIfNotSetInHeader(config, file, documentModel); + sanitizeTags(config, documentModel); - if (content.get(Crawler.Attributes.TYPE) == null || content.get(Crawler.Attributes.STATUS) == null) { + if (documentModel.get(Crawler.Attributes.TYPE) == null || documentModel.get(Crawler.Attributes.STATUS) == null) { // output error LOGGER.warn("Parsing skipped (missing type or status value in header meta data) for file {}!", file); return null; } // generate default body - processBody(config, fileContents, content); + processBody(fileContents, documentModel); // eventually process body using specific engine if (validate(context)) { @@ -132,21 +123,40 @@ public Map parse(Configuration config, File file, String content LOGGER.error("Incomplete source file ({}) for markup engine: {}", file, getClass().getSimpleName()); return null; } + // TODO: post parsing plugins to hook in here? + return documentModel; + } + + private void sanitizeTags(JBakeConfiguration config, Map content) { if (content.get(Crawler.Attributes.TAGS) != null) { String[] tags = (String[]) content.get(Crawler.Attributes.TAGS); for (int i = 0; i < tags.length; i++) { - tags[i] = tags[i].trim(); - if (config.getBoolean(Keys.TAG_SANITIZE)) { + tags[i] = sanitizeValue(tags[i]); + if (config.getSanitizeTag()) { tags[i] = tags[i].replace(" ", "-"); } } content.put(Crawler.Attributes.TAGS, tags); } + } - // TODO: post parsing plugins to hook in here? + private void setModelDefaultsIfNotSetInHeader(JBakeConfiguration config, File file, Map content) { + if (content.get(Crawler.Attributes.DATE) == null) { + content.put(Crawler.Attributes.DATE, new Date(file.lastModified())); + } - return content; + // default status has been set + if (config.getDefaultStatus() != null && content.get(Crawler.Attributes.STATUS) == null) { + // file hasn't got status so use default + content.put(Crawler.Attributes.STATUS, config.getDefaultStatus()); + } + + // default type has been set + if (config.getDefaultType() != null && content.get(Crawler.Attributes.TYPE) == null) { + // file hasn't got type so use default + content.put(Crawler.Attributes.TYPE, config.getDefaultType()); + } } /** @@ -155,85 +165,84 @@ public Map parse(Configuration config, File file, String content * @param contents Contents of file * @return true if header exists, false if not */ - private boolean hasHeader(Configuration config, List contents) { - boolean headerValid = false; - boolean headerSeparatorFound = false; + private boolean hasHeader(List contents) { + boolean headerValid = true; boolean statusFound = false; boolean typeFound = false; - List header = new ArrayList(); + if ( ! hasHeaderSeparatorInContent( contents ) ) { + return false; + } for (String line : contents) { - if (!line.isEmpty()) { - header.add(line); - } - if (line.contains("=")) { - if (line.startsWith("type=")) { - typeFound = true; - } - if (line.startsWith("status=")) { - statusFound = true; - } - } - if (line.equals(config.getString(Keys.HEADER_SEPARATOR))) { - headerSeparatorFound = true; - header.remove(line); + if (hasHeaderSeparator(line)) { + LOGGER.debug("Header separator found"); break; } - } + if ( isTypeProperty(line) ) { + LOGGER.debug("Type property found"); + typeFound = true; + } - if (headerSeparatorFound) { - headerValid = true; - for (String headerLine : header) { - if (!headerLine.contains("=")) { - headerValid = false; - break; - } + if ( isStatusProperty(line) ) { + LOGGER.debug("Status property found"); + statusFound = true; + } + if ( !line.isEmpty() && !line.contains("=") ) { + LOGGER.error("Property found without assignment [{}]", line); + headerValid = false; } } - return headerValid && statusFound && typeFound; } + private boolean hasHeaderSeparatorInContent(List contents) { + return contents.indexOf(configuration.getHeaderSeparator()) != -1; + } + + private boolean hasHeaderSeparator(String line) { + return line.equals(configuration.getHeaderSeparator()); + } + + private boolean isStatusProperty(String line) { + return line.startsWith("status="); + } + + private boolean isTypeProperty(String line) { + return line.startsWith("type="); + } + /** * Process the header of the file. - * - * @param config - * @param contents Contents of file + * @param contents Contents of file * @param content */ - private void processHeader(Configuration config, List contents, final Map content) { + private void processHeader(List contents, final Map content) { for (String line : contents) { - if (line.equals(config.getString(Keys.HEADER_SEPARATOR))) { + + if (hasHeaderSeparator(line)) { break; } - if (line.isEmpty()) { - continue; - } + processLine(line, content); + } + } - String[] parts = line.split("=", 2); - if (parts.length != 2) { - continue; - } + private void processLine(String line, Map content) { + String[] parts = line.split("=", 2); + if (!line.isEmpty() && parts.length == 2) { - String utf8BOM = "\uFEFF"; - String key; - if (parts[0].contains(utf8BOM)) { - key = parts[0].trim().replace(utf8BOM, ""); - } else { - key = parts[0].trim(); - } - String value = parts[1].trim(); + + String key = sanitizeKey(parts[0]); + String value = sanitizeValue(parts[1]); if (key.equalsIgnoreCase(Crawler.Attributes.DATE)) { - DateFormat df = new SimpleDateFormat(config.getString(Keys.DATE_FORMAT)); - Date date = null; + DateFormat df = new SimpleDateFormat(configuration.getDateFormat()); try { - date = df.parse(value); + Date date = df.parse(value); content.put(key, date); } catch (ParseException e) { - e.printStackTrace(); + LOGGER.error("unable to parse date {}", value); } } else if (key.equalsIgnoreCase(Crawler.Attributes.TAGS)) { content.put(key, getTags(value)); @@ -245,10 +254,24 @@ private void processHeader(Configuration config, List contents, final Ma } } + private String sanitizeValue(String part) { + return part.trim(); + } + + private String sanitizeKey(String part) { + String key; + if (part.contains(UTF_8_BOM)) { + key = part.trim().replace(UTF_8_BOM, ""); + } else { + key = part.trim(); + } + return key; + } + private String[] getTags(String tagsPart) { String[] tags = tagsPart.split(","); for (int i = 0; i < tags.length; i++) - tags[i] = tags[i].trim(); + tags[i] = sanitizeValue(tags[i]); return tags; } @@ -262,14 +285,14 @@ private boolean isJson(String part) { * @param contents Contents of file * @param content */ - private void processBody(Configuration config, List contents, final Map content) { + private void processBody(List contents, final Map content) { StringBuilder body = new StringBuilder(); boolean inBody = false; for (String line : contents) { if (inBody) { body.append(line).append("\n"); } - if (line.equals(config.getString(Keys.HEADER_SEPARATOR))) { + if (line.equals(configuration.getHeaderSeparator())) { inBody = true; } } @@ -282,4 +305,4 @@ private void processBody(Configuration config, List contents, final Map< content.put(Crawler.Attributes.BODY, body.toString()); } -} +} \ No newline at end of file diff --git a/jbake-core/src/main/java/org/jbake/parser/ParserContext.java b/jbake-core/src/main/java/org/jbake/parser/ParserContext.java index a28bb36f3..0f50222e7 100644 --- a/jbake-core/src/main/java/org/jbake/parser/ParserContext.java +++ b/jbake-core/src/main/java/org/jbake/parser/ParserContext.java @@ -1,31 +1,28 @@ package org.jbake.parser; +import org.jbake.app.Crawler; +import org.jbake.app.configuration.JBakeConfiguration; + import java.io.File; import java.util.List; import java.util.Map; -import org.apache.commons.configuration.Configuration; -import org.jbake.app.Crawler; - public class ParserContext { private final File file; private final List fileLines; - private final Configuration config; - private final String contentPath; + private final JBakeConfiguration config; private final boolean hasHeader; private final Map contents; public ParserContext( File file, List fileLines, - Configuration config, - String contentPath, + JBakeConfiguration config, boolean hasHeader, Map contents) { this.file = file; this.fileLines = fileLines; this.config = config; - this.contentPath = contentPath; this.hasHeader = hasHeader; this.contents = contents; } @@ -38,14 +35,10 @@ public List getFileLines() { return fileLines; } - public Configuration getConfig() { + public JBakeConfiguration getConfig() { return config; } - public String getContentPath() { - return contentPath; - } - public Map getContents() { return contents; } diff --git a/jbake-core/src/main/java/org/jbake/parser/ParserEngine.java b/jbake-core/src/main/java/org/jbake/parser/ParserEngine.java index e5f500f9b..c6ea7c2fc 100644 --- a/jbake-core/src/main/java/org/jbake/parser/ParserEngine.java +++ b/jbake-core/src/main/java/org/jbake/parser/ParserEngine.java @@ -1,12 +1,30 @@ package org.jbake.parser; +import org.apache.commons.configuration.Configuration; +import org.jbake.app.configuration.JBakeConfiguration; + import java.io.File; import java.util.Map; -import org.apache.commons.configuration.Configuration; - public interface ParserEngine { + /** + * Parse a given file and transform to a model representation used by {@link MarkdownEngine} implementations + * to render the file content. + * @param config The project configuration + * @param file The file to be parsed + * @return A model representation of the given file + */ + Map parse(JBakeConfiguration config, File file); + + /** + * @param config The project configuration + * @param file The file to be parsed + * @param contentPath unknown + * @return A model representation of the given file + * @deprecated use {@link #parse(JBakeConfiguration, File)} instead + */ + @Deprecated Map parse(Configuration config, File file, String contentPath); } diff --git a/jbake-core/src/main/java/org/jbake/render/ArchiveRenderer.java b/jbake-core/src/main/java/org/jbake/render/ArchiveRenderer.java index 8ebb25a52..ba6eebcb3 100644 --- a/jbake-core/src/main/java/org/jbake/render/ArchiveRenderer.java +++ b/jbake-core/src/main/java/org/jbake/render/ArchiveRenderer.java @@ -1,28 +1,36 @@ package org.jbake.render; -import java.io.File; - import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ContentStore; import org.jbake.app.Renderer; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; import org.jbake.template.RenderingException; +import java.io.File; + public class ArchiveRenderer implements RenderingTool { - @Override - public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException { - if (config.getBoolean(Keys.RENDER_ARCHIVE)) { - try { - renderer.renderArchive(config.getString(Keys.ARCHIVE_FILE)); - return 1; - } catch (Exception e) { - throw new RenderingException(e); - } - } else { - return 0; - } - } + @Override + public int render(Renderer renderer, ContentStore db, JBakeConfiguration config) throws RenderingException { + if (config.getRenderArchive()) { + try { + renderer.renderArchive(config.getArchiveFileName()); + return 1; + } catch (Exception e) { + throw new RenderingException(e); + } + } else { + return 0; + } + } + + @Override + public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException { + + JBakeConfiguration configuration = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(templatesPath.getParentFile(), config); + return render(renderer, db, configuration); + } } \ No newline at end of file diff --git a/jbake-core/src/main/java/org/jbake/render/DocumentsRenderer.java b/jbake-core/src/main/java/org/jbake/render/DocumentsRenderer.java index 8706b3376..59fe93e94 100644 --- a/jbake-core/src/main/java/org/jbake/render/DocumentsRenderer.java +++ b/jbake-core/src/main/java/org/jbake/render/DocumentsRenderer.java @@ -1,84 +1,92 @@ package org.jbake.render; -import java.io.File; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - import org.apache.commons.configuration.CompositeConfiguration; import org.jbake.app.ContentStore; import org.jbake.app.Crawler.Attributes; import org.jbake.app.DocumentList; import org.jbake.app.Renderer; +import org.jbake.app.configuration.JBakeConfiguration; import org.jbake.model.DocumentTypes; import org.jbake.template.RenderingException; +import java.io.File; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + public class DocumentsRenderer implements RenderingTool { - @Override - public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, - CompositeConfiguration config) throws RenderingException { - int renderedCount = 0; - final List errors = new LinkedList(); - for (String docType : DocumentTypes.getDocumentTypes()) { - DocumentList documentList = db.getUnrenderedContent(docType); - - if(documentList == null) continue; - - int index = 0; - - Map nextDocument = null; - - while (index < documentList.size()) { - try { - Map document = documentList.get(index); - document.put("nextContent", null); - document.put("previousContent", null); - - if (index > 0) { - document.put("nextContent", getContentForNav(nextDocument)); - } - - if (index < documentList.size() - 1) { - Map tempNext = documentList.get(index + 1); - document.put("previousContent", getContentForNav(tempNext)); - } - - nextDocument = document; - - renderer.render(document); - renderedCount++; - - } catch (Exception e) { - errors.add(e.getMessage()); - } - - index++; - } - } - if (!errors.isEmpty()) { - StringBuilder sb = new StringBuilder(); - sb.append("Failed to render documents. Cause(s):"); - for (String error : errors) { - sb.append("\n").append(error); - } - throw new RenderingException(sb.toString()); - } else { - return renderedCount; - } - } - - /** - * Creates a simple content model to use in individual post navigations. - * @param document - * @return - */ - private Map getContentForNav(Map document) { - Map navDocument = new HashMap(); - navDocument.put(Attributes.NO_EXTENSION_URI, document.get(Attributes.NO_EXTENSION_URI)); - navDocument.put(Attributes.URI, document.get(Attributes.URI)); - navDocument.put(Attributes.TITLE, document.get(Attributes.TITLE)); - return navDocument; - } + @Override + public int render(Renderer renderer, ContentStore db, JBakeConfiguration config) throws RenderingException { + int renderedCount = 0; + final List errors = new LinkedList<>(); + for (String docType : DocumentTypes.getDocumentTypes()) { + DocumentList documentList = db.getUnrenderedContent(docType); + + if (documentList == null) continue; + + int index = 0; + + Map nextDocument = null; + + while (index < documentList.size()) { + try { + Map document = documentList.get(index); + document.put("nextContent", null); + document.put("previousContent", null); + + if (index > 0) { + document.put("nextContent", getContentForNav(nextDocument)); + } + + if (index < documentList.size() - 1) { + Map tempNext = documentList.get(index + 1); + document.put("previousContent", getContentForNav(tempNext)); + } + + nextDocument = document; + + renderer.render(document); + renderedCount++; + + } catch (Exception e) { + errors.add(e.getMessage()); + } + + index++; + } + + db.markContentAsRendered(docType); + } + if (!errors.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("Failed to render documents. Cause(s):"); + for (String error : errors) { + sb.append("\n").append(error); + } + throw new RenderingException(sb.toString()); + } else { + return renderedCount; + } + } + + /** + * Creates a simple content model to use in individual post navigations. + * + * @param document + * @return + */ + private Map getContentForNav(Map document) { + Map navDocument = new HashMap<>(); + navDocument.put(Attributes.NO_EXTENSION_URI, document.get(Attributes.NO_EXTENSION_URI)); + navDocument.put(Attributes.URI, document.get(Attributes.URI)); + navDocument.put(Attributes.TITLE, document.get(Attributes.TITLE)); + return navDocument; + } + + @Override + public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException { + return render(renderer, db, null); + } } \ No newline at end of file diff --git a/jbake-core/src/main/java/org/jbake/render/FeedRenderer.java b/jbake-core/src/main/java/org/jbake/render/FeedRenderer.java index 0d6ef55e6..da956df33 100644 --- a/jbake-core/src/main/java/org/jbake/render/FeedRenderer.java +++ b/jbake-core/src/main/java/org/jbake/render/FeedRenderer.java @@ -1,28 +1,36 @@ package org.jbake.render; -import java.io.File; - import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ContentStore; import org.jbake.app.Renderer; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; import org.jbake.template.RenderingException; +import java.io.File; + public class FeedRenderer implements RenderingTool { - @Override - public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException { - if (config.getBoolean(Keys.RENDER_FEED)) { - try { - renderer.renderFeed(config.getString(Keys.FEED_FILE)); - return 1; - } catch (Exception e) { - throw new RenderingException(e); - } - } else { - return 0; - } - } + @Override + public int render(Renderer renderer, ContentStore db, JBakeConfiguration config) throws RenderingException { + if (config.getRenderFeed()) { + try { + //TODO: refactor this. the renderer has a reference to the configuration + renderer.renderFeed(config.getFeedFileName()); + return 1; + } catch (Exception e) { + throw new RenderingException(e); + } + } else { + return 0; + } + } + + @Override + public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException { + JBakeConfiguration configuration = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(templatesPath.getParentFile(), config); + return render(renderer, db, configuration); + } } \ No newline at end of file diff --git a/jbake-core/src/main/java/org/jbake/render/IndexRenderer.java b/jbake-core/src/main/java/org/jbake/render/IndexRenderer.java index 14aeab438..bbdb7e4ae 100644 --- a/jbake-core/src/main/java/org/jbake/render/IndexRenderer.java +++ b/jbake-core/src/main/java/org/jbake/render/IndexRenderer.java @@ -1,9 +1,10 @@ package org.jbake.render; import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ContentStore; import org.jbake.app.Renderer; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; import org.jbake.template.RenderingException; import java.io.File; @@ -11,13 +12,16 @@ public class IndexRenderer implements RenderingTool { @Override - public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException { - if (config.getBoolean(Keys.RENDER_INDEX)) { + public int render(Renderer renderer, ContentStore db, JBakeConfiguration config) throws RenderingException { + if (config.getRenderIndex()) { try { - if (shouldPaginateIndex(config)) { - renderer.renderIndexPaging(config.getString(Keys.INDEX_FILE)); + String fileName = config.getIndexFileName(); + + //TODO: refactor this. the renderer has a reference to the configuration + if (config.getPaginateIndex()) { + renderer.renderIndexPaging(fileName); } else { - renderer.renderIndex(config.getString(Keys.INDEX_FILE)); + renderer.renderIndex(fileName); } return 1; } catch (Exception e) { @@ -28,8 +32,9 @@ public int render(Renderer renderer, ContentStore db, File destination, File tem } } - private boolean shouldPaginateIndex(CompositeConfiguration config) { - return config.containsKey(Keys.PAGINATE_INDEX) && config.getBoolean(Keys.PAGINATE_INDEX); + @Override + public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException { + JBakeConfiguration configuration = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(templatesPath.getParentFile(), config); + return render(renderer, db, configuration); } - } \ No newline at end of file diff --git a/jbake-core/src/main/java/org/jbake/render/RenderingTool.java b/jbake-core/src/main/java/org/jbake/render/RenderingTool.java index 4db43b10f..013eaaf2e 100644 --- a/jbake-core/src/main/java/org/jbake/render/RenderingTool.java +++ b/jbake-core/src/main/java/org/jbake/render/RenderingTool.java @@ -1,14 +1,20 @@ package org.jbake.render; -import java.io.File; - import org.apache.commons.configuration.CompositeConfiguration; import org.jbake.app.ContentStore; import org.jbake.app.Renderer; +import org.jbake.app.configuration.JBakeConfiguration; import org.jbake.template.RenderingException; +import java.io.File; + public interface RenderingTool { + + int render(Renderer renderer, ContentStore db, JBakeConfiguration config) throws RenderingException; + + @Deprecated + //TODO: remove at 3.0.0 int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException; } \ No newline at end of file diff --git a/jbake-core/src/main/java/org/jbake/render/SitemapRenderer.java b/jbake-core/src/main/java/org/jbake/render/SitemapRenderer.java index 3542206a0..71bb5756b 100644 --- a/jbake-core/src/main/java/org/jbake/render/SitemapRenderer.java +++ b/jbake-core/src/main/java/org/jbake/render/SitemapRenderer.java @@ -1,28 +1,36 @@ package org.jbake.render; -import java.io.File; - import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ContentStore; import org.jbake.app.Renderer; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; import org.jbake.template.RenderingException; +import java.io.File; + public class SitemapRenderer implements RenderingTool { - @Override - public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException { - if (config.getBoolean(Keys.RENDER_SITEMAP)) { - try { - renderer.renderSitemap(config.getString(Keys.SITEMAP_FILE)); - return 1; - } catch (Exception e) { - throw new RenderingException(e); - } - } else { - return 0; - } - } + @Override + public int render(Renderer renderer, ContentStore db, JBakeConfiguration config) throws RenderingException { + if (config.getRenderSiteMap()) { + try { + //TODO: refactor this. the renderer has a reference to the configuration + renderer.renderSitemap(config.getSiteMapFileName()); + return 1; + } catch (Exception e) { + throw new RenderingException(e); + } + } else { + return 0; + } + } + + @Override + public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException { + JBakeConfiguration configuration = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(templatesPath.getParentFile(), config); + return render(renderer, db, configuration); + } } \ No newline at end of file diff --git a/jbake-core/src/main/java/org/jbake/render/TagsRenderer.java b/jbake-core/src/main/java/org/jbake/render/TagsRenderer.java index aeaa54406..2bb1f55df 100644 --- a/jbake-core/src/main/java/org/jbake/render/TagsRenderer.java +++ b/jbake-core/src/main/java/org/jbake/render/TagsRenderer.java @@ -1,27 +1,35 @@ package org.jbake.render; -import java.io.File; - import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ContentStore; import org.jbake.app.Renderer; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; import org.jbake.template.RenderingException; +import java.io.File; + public class TagsRenderer implements RenderingTool { - @Override - public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException { - if (config.getBoolean(Keys.RENDER_TAGS)) { - try { - return renderer.renderTags(config.getString(Keys.TAG_PATH)); - } catch (Exception e) { - throw new RenderingException(e); - } - } else { - return 0; - } - } + @Override + public int render(Renderer renderer, ContentStore db, JBakeConfiguration config) throws RenderingException { + if (config.getRenderTags()) { + try { + //TODO: refactor this. the renderer has a reference to the configuration + return renderer.renderTags(config.getTagPathName()); + } catch (Exception e) { + throw new RenderingException(e); + } + } else { + return 0; + } + } + + @Override + public int render(Renderer renderer, ContentStore db, File destination, File templatesPath, CompositeConfiguration config) throws RenderingException { + JBakeConfiguration configuration = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(templatesPath.getParentFile(), config); + return render(renderer, db, configuration); + } } \ No newline at end of file diff --git a/jbake-core/src/main/java/org/jbake/template/AbstractTemplateEngine.java b/jbake-core/src/main/java/org/jbake/template/AbstractTemplateEngine.java index a94d0d74c..aaa630032 100644 --- a/jbake-core/src/main/java/org/jbake/template/AbstractTemplateEngine.java +++ b/jbake-core/src/main/java/org/jbake/template/AbstractTemplateEngine.java @@ -1,7 +1,10 @@ package org.jbake.template; +import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.configuration.Configuration; import org.jbake.app.ContentStore; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; import java.io.File; import java.io.Writer; @@ -27,17 +30,18 @@ public abstract class AbstractTemplateEngine { protected static ModelExtractors extractors = ModelExtractors.getInstance(); - protected final Configuration config; + protected final JBakeConfiguration config; protected final ContentStore db; - protected final File destination; - protected final File templatesPath; + @Deprecated protected AbstractTemplateEngine(final Configuration config, final ContentStore db, final File destination, final File templatesPath) { + this(new JBakeConfigurationFactory().createDefaultJbakeConfiguration(templatesPath.getParentFile(),destination, (CompositeConfiguration) config),db); + } + + protected AbstractTemplateEngine(final JBakeConfiguration config, final ContentStore db) { this.config = config; this.db = db; - this.destination = destination; - this.templatesPath = templatesPath; } - public abstract void renderDocument(Map model, String templateName, Writer writer) throws RenderingException; + public abstract void renderDocument(Map model, String templateName, Writer writer) throws RenderingException; } diff --git a/jbake-core/src/main/java/org/jbake/template/DelegatingTemplateEngine.java b/jbake-core/src/main/java/org/jbake/template/DelegatingTemplateEngine.java index e9d04ee8d..4458e018b 100644 --- a/jbake-core/src/main/java/org/jbake/template/DelegatingTemplateEngine.java +++ b/jbake-core/src/main/java/org/jbake/template/DelegatingTemplateEngine.java @@ -1,9 +1,10 @@ package org.jbake.template; import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ContentStore; import org.jbake.app.FileUtil; +import org.jbake.app.configuration.JBakeConfiguration; +import org.jbake.app.configuration.JBakeProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,34 +21,54 @@ * @author Cédric Champeau */ public class DelegatingTemplateEngine extends AbstractTemplateEngine { - private final static Logger LOGGER = LoggerFactory.getLogger(DelegatingTemplateEngine.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DelegatingTemplateEngine.class); private final TemplateEngines renderers; + /** + * @deprecated Use {@link #DelegatingTemplateEngine(ContentStore, JBakeConfiguration)} instead. + */ + @Deprecated public DelegatingTemplateEngine(final CompositeConfiguration config, final ContentStore db, final File destination, final File templatesPath) { super(config, db, destination, templatesPath); - this.renderers = new TemplateEngines(config, db, destination, templatesPath); + this.renderers = new TemplateEngines(this.config, db); + } + + public DelegatingTemplateEngine(final ContentStore db, final JBakeConfiguration config) { + super(config, db); + this.renderers = new TemplateEngines(config, db); } @Override public void renderDocument(final Map model, String templateName, final Writer writer) throws RenderingException { - model.put("version", config.getString(Keys.VERSION)); - Map configModel = new HashMap(); + model.put("version", config.getVersion()); + + // TODO: create config model from configuration + Map configModel = new HashMap<>(); Iterator configKeys = config.getKeys(); while (configKeys.hasNext()) { String key = configKeys.next(); + Object valueObject; + + if ( key.equals(JBakeProperty.PAGINATE_INDEX) ){ + valueObject = config.getPaginateIndex(); + } + else { + valueObject = config.get(key); + } //replace "." in key so you can use dot notation in templates - configModel.put(key.replace(".", "_"), config.getProperty(key)); + configModel.put(key.replace(".", "_"), valueObject); } model.put("config", configModel); // if default template exists we will use it - File templateFile = new File(templatesPath, templateName); + File templateFolder = config.getTemplateFolder(); + File templateFile = new File(templateFolder, templateName); if (!templateFile.exists()) { LOGGER.info("Default template: {} was not found, searching for others...", templateName); // if default template does not exist then check if any alternative engine templates exist String templateNameWithoutExt = templateName.substring(0, templateName.length() - 4); for (String extension : renderers.getRecognizedExtensions()) { - templateFile = new File(templatesPath, templateNameWithoutExt + "." + extension); + templateFile = new File(templateFolder, templateNameWithoutExt + "." + extension); if (templateFile.exists()) { LOGGER.info("Found alternative template file: {} using this instead", templateFile.getName()); templateName = templateFile.getName(); diff --git a/jbake-core/src/main/java/org/jbake/template/FreemarkerTemplateEngine.java b/jbake-core/src/main/java/org/jbake/template/FreemarkerTemplateEngine.java index a6d352bf9..cde2ef1df 100644 --- a/jbake-core/src/main/java/org/jbake/template/FreemarkerTemplateEngine.java +++ b/jbake-core/src/main/java/org/jbake/template/FreemarkerTemplateEngine.java @@ -3,11 +3,22 @@ import freemarker.ext.beans.BeansWrapper; import freemarker.ext.beans.BeansWrapperBuilder; -import freemarker.template.*; +import freemarker.template.Configuration; +import freemarker.template.ObjectWrapper; +import freemarker.template.SimpleCollection; +import freemarker.template.SimpleDate; +import freemarker.template.SimpleHash; +import freemarker.template.SimpleSequence; +import freemarker.template.Template; +import freemarker.template.TemplateDateModel; +import freemarker.template.TemplateException; +import freemarker.template.TemplateHashModel; +import freemarker.template.TemplateModel; +import freemarker.template.TemplateModelException; import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ContentStore; import org.jbake.app.Crawler; +import org.jbake.app.configuration.JBakeConfiguration; import java.io.File; import java.io.IOException; @@ -25,16 +36,22 @@ public class FreemarkerTemplateEngine extends AbstractTemplateEngine { private Configuration templateCfg; + @Deprecated public FreemarkerTemplateEngine(final CompositeConfiguration config, final ContentStore db, final File destination, final File templatesPath) { super(config, db, destination, templatesPath); - createTemplateConfiguration(config, templatesPath); + createTemplateConfiguration(); } - private void createTemplateConfiguration(final CompositeConfiguration config, final File templatesPath) { + public FreemarkerTemplateEngine(final JBakeConfiguration config, final ContentStore db) { + super(config, db); + createTemplateConfiguration(); + } + + private void createTemplateConfiguration() { templateCfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); - templateCfg.setDefaultEncoding(config.getString(Keys.RENDER_ENCODING)); + templateCfg.setDefaultEncoding(config.getRenderEncoding()); try { - templateCfg.setDirectoryForTemplateLoading(templatesPath); + templateCfg.setDirectoryForTemplateLoading(config.getTemplateFolder()); } catch (IOException e) { e.printStackTrace(); } @@ -69,7 +86,7 @@ public LazyLoadingModel(ObjectWrapper wrapper, Map eagerModel, f @Override public TemplateModel get(final String key) throws TemplateModelException { try { - + // GIT Issue#357: Accessing db in freemarker template throws exception // When content store is accessed with key "db" then wrap the ContentStore with BeansWrapper and return to template. // All methods on db are then accessible in template. Eg: ${db.getPublishedPostsByTag(tagName).size()} @@ -77,9 +94,9 @@ public TemplateModel get(final String key) throws TemplateModelException { BeansWrapperBuilder bwb = new BeansWrapperBuilder(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); BeansWrapper bw = bwb.build(); return bw.wrap(db); - } - - + } + + return extractors.extractAndTransform(db, key, eagerModel.toMap(), new TemplateEngineAdapter() { @Override diff --git a/jbake-core/src/main/java/org/jbake/template/GroovyMarkupTemplateEngine.java b/jbake-core/src/main/java/org/jbake/template/GroovyMarkupTemplateEngine.java index f3c359e6e..cb89bea90 100644 --- a/jbake-core/src/main/java/org/jbake/template/GroovyMarkupTemplateEngine.java +++ b/jbake-core/src/main/java/org/jbake/template/GroovyMarkupTemplateEngine.java @@ -7,6 +7,7 @@ import groovy.text.markup.TemplateConfiguration; import org.apache.commons.configuration.CompositeConfiguration; import org.jbake.app.ContentStore; +import org.jbake.app.configuration.JBakeConfiguration; import java.io.File; import java.io.Writer; @@ -15,7 +16,7 @@ /** * Renders documents using the GroovyMarkupTemplateEngine. - * + *

* The file extension to activate this Engine is .tpl * * @see Groovy MarkupTemplateEngine Documentation @@ -24,12 +25,22 @@ public class GroovyMarkupTemplateEngine extends AbstractTemplateEngine { private TemplateConfiguration templateConfiguration; private MarkupTemplateEngine templateEngine; + /** + * @deprecated Use {@link #GroovyMarkupTemplateEngine(JBakeConfiguration, ContentStore)} instead + */ + @Deprecated public GroovyMarkupTemplateEngine(final CompositeConfiguration config, final ContentStore db, final File destination, final File templatesPath) { super(config, db, destination, templatesPath); setupTemplateConfiguration(); initializeTemplateEngine(); } + public GroovyMarkupTemplateEngine(final JBakeConfiguration config, final ContentStore db) { + super(config, db); + setupTemplateConfiguration(); + initializeTemplateEngine(); + } + private void setupTemplateConfiguration() { templateConfiguration = new TemplateConfiguration(); templateConfiguration.setUseDoubleQuotes(true); @@ -39,7 +50,7 @@ private void setupTemplateConfiguration() { } private void initializeTemplateEngine() { - templateEngine = new MarkupTemplateEngine(MarkupTemplateEngine.class.getClassLoader(),templatesPath,templateConfiguration); + templateEngine = new MarkupTemplateEngine(MarkupTemplateEngine.class.getClassLoader(), config.getTemplateFolder(), templateConfiguration); } @Override @@ -58,14 +69,14 @@ private Map wrap(final Map model) { return new HashMap(model) { @Override public Object get(final Object property) { - if (property instanceof String || property instanceof GString) { - String key = property.toString(); - try { - put(key, extractors.extractAndTransform(db, key, model, new TemplateEngineAdapter.NoopAdapter())); - } catch (NoModelExtractorException e) { - // should never happen, as we iterate over existing extractors - } - } + if (property instanceof String || property instanceof GString) { + String key = property.toString(); + try { + put(key, extractors.extractAndTransform(db, key, model, new TemplateEngineAdapter.NoopAdapter())); + } catch (NoModelExtractorException e) { + // should never happen, as we iterate over existing extractors + } + } return super.get(property); } diff --git a/jbake-core/src/main/java/org/jbake/template/GroovyTemplateEngine.java b/jbake-core/src/main/java/org/jbake/template/GroovyTemplateEngine.java index ad41dbb7e..3799dc89d 100644 --- a/jbake-core/src/main/java/org/jbake/template/GroovyTemplateEngine.java +++ b/jbake-core/src/main/java/org/jbake/template/GroovyTemplateEngine.java @@ -7,14 +7,13 @@ import groovy.text.Template; import groovy.text.TemplateEngine; import groovy.text.XmlTemplateEngine; - import org.apache.commons.configuration.CompositeConfiguration; import org.codehaus.groovy.runtime.MethodClosure; -import org.jbake.app.ConfigUtil.Keys; +import org.jbake.app.ContentStore; +import org.jbake.app.configuration.JBakeConfiguration; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; - import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -23,7 +22,6 @@ import java.io.Writer; import java.util.HashMap; import java.util.Map; -import org.jbake.app.ContentStore; /** * Renders documents using a Groovy template engine. Depending on the file extension of the template, the template @@ -34,12 +32,20 @@ */ public class GroovyTemplateEngine extends AbstractTemplateEngine { - private final Map cachedTemplates = new HashMap(); + private final Map cachedTemplates = new HashMap<>(); + /** + * @deprecated Use {@link #GroovyTemplateEngine(JBakeConfiguration, ContentStore)} instead + */ + @Deprecated public GroovyTemplateEngine(final CompositeConfiguration config, final ContentStore db, final File destination, final File templatesPath) { super(config, db, destination, templatesPath); } + public GroovyTemplateEngine(final JBakeConfiguration config, final ContentStore db) { + super(config, db); + } + @Override public void renderDocument(final Map model, final String templateName, final Writer writer) throws RenderingException { try { @@ -53,17 +59,17 @@ public void renderDocument(final Map model, final String templat private Template findTemplate(final String templateName) throws SAXException, ParserConfigurationException, ClassNotFoundException, IOException { TemplateEngine ste = templateName.endsWith(".gxml") ? new XmlTemplateEngine() : new SimpleTemplateEngine(); - File sourceTemplate = new File(templatesPath, templateName); + File sourceTemplate = new File(config.getTemplateFolder(), templateName); Template template = cachedTemplates.get(templateName); if (template == null) { - template = ste.createTemplate(new InputStreamReader(new BufferedInputStream(new FileInputStream(sourceTemplate)), config.getString(Keys.TEMPLATE_ENCODING))); + template = ste.createTemplate(new InputStreamReader(new BufferedInputStream(new FileInputStream(sourceTemplate)), config.getTemplateEncoding())); cachedTemplates.put(templateName, template); } return template; } private Map wrap(final Map model) { - return new HashMap(model) { + return new HashMap(model) { @Override public Object get(final Object property) { if (property instanceof String || property instanceof GString) { @@ -71,11 +77,11 @@ public Object get(final Object property) { if ("include".equals(key)) { return new MethodClosure(GroovyTemplateEngine.this, "doInclude").curry(this); } - try { - return extractors.extractAndTransform(db, key, model, new TemplateEngineAdapter.NoopAdapter()); - } catch(NoModelExtractorException e) { - // fallback to parent model - } + try { + return extractors.extractAndTransform(db, key, model, new TemplateEngineAdapter.NoopAdapter()); + } catch (NoModelExtractorException e) { + // fallback to parent model + } } return super.get(property); diff --git a/jbake-core/src/main/java/org/jbake/template/JadeTemplateEngine.java b/jbake-core/src/main/java/org/jbake/template/JadeTemplateEngine.java index 1f8c27b2a..83171b015 100644 --- a/jbake-core/src/main/java/org/jbake/template/JadeTemplateEngine.java +++ b/jbake-core/src/main/java/org/jbake/template/JadeTemplateEngine.java @@ -13,8 +13,8 @@ import de.neuland.jade4j.template.TemplateLoader; import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.lang.StringEscapeUtils; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ContentStore; +import org.jbake.app.configuration.JBakeConfiguration; import java.io.File; import java.io.IOException; @@ -37,10 +37,15 @@ public class JadeTemplateEngine extends AbstractTemplateEngine { private JadeConfiguration jadeConfiguration = new JadeConfiguration(); + @Deprecated public JadeTemplateEngine(final CompositeConfiguration config, final ContentStore db, final File destination, final File templatesPath) { super(config, db, destination, templatesPath); + } + + public JadeTemplateEngine(final JBakeConfiguration config, final ContentStore db) { + super(config, db); - TemplateLoader loader = new FileTemplateLoader(templatesPath.getAbsolutePath() + File.separatorChar, config.getString(Keys.TEMPLATE_ENCODING)); + TemplateLoader loader = new FileTemplateLoader(config.getTemplateFolder().getPath() + File.separatorChar, config.getTemplateEncoding()); jadeConfiguration.setTemplateLoader(loader); jadeConfiguration.setMode(Jade4J.Mode.XHTML); jadeConfiguration.setPrettyPrint(true); diff --git a/jbake-core/src/main/java/org/jbake/template/ModelExtractor.java b/jbake-core/src/main/java/org/jbake/template/ModelExtractor.java index a9af5cac5..4d485a1bf 100644 --- a/jbake-core/src/main/java/org/jbake/template/ModelExtractor.java +++ b/jbake-core/src/main/java/org/jbake/template/ModelExtractor.java @@ -1,17 +1,17 @@ package org.jbake.template; -import java.util.Map; - import org.jbake.app.ContentStore; +import java.util.Map; + /** * * @author ndx * - * T the type of data returned by this model extractor + * @param the type of data returned by this model extractor */ -public interface ModelExtractor { +public interface ModelExtractor { T get(ContentStore db, Map model, String key); diff --git a/jbake-core/src/main/java/org/jbake/template/TemplateEngines.java b/jbake-core/src/main/java/org/jbake/template/TemplateEngines.java index 9479db181..4de13df2f 100644 --- a/jbake-core/src/main/java/org/jbake/template/TemplateEngines.java +++ b/jbake-core/src/main/java/org/jbake/template/TemplateEngines.java @@ -1,15 +1,12 @@ package org.jbake.template; -import org.apache.commons.configuration.CompositeConfiguration; -import org.apache.commons.configuration.Configuration; import org.jbake.app.ContentStore; +import org.jbake.app.configuration.JBakeConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.Collections; import java.util.Enumeration; @@ -40,28 +37,29 @@ * @author Cédric Champeau */ public class TemplateEngines { - private final static Logger LOGGER = LoggerFactory.getLogger(TemplateEngines.class); - private final Map templateEngines; + private static final Logger LOGGER = LoggerFactory.getLogger(TemplateEngines.class); + + private final Map engines; public Set getRecognizedExtensions() { - return Collections.unmodifiableSet(templateEngines.keySet()); + return Collections.unmodifiableSet(engines.keySet()); } - public TemplateEngines(final Configuration config, final ContentStore db, final File destination, final File templatesPath) { - templateEngines = new HashMap(); - loadEngines(config, db, destination, templatesPath); + public TemplateEngines(final JBakeConfiguration config, final ContentStore db) { + engines = new HashMap<>(); + loadEngines(config, db); } private void registerEngine(String fileExtension, AbstractTemplateEngine templateEngine) { - AbstractTemplateEngine old = templateEngines.put(fileExtension, templateEngine); + AbstractTemplateEngine old = engines.put(fileExtension, templateEngine); if (old != null) { LOGGER.warn("Registered a template engine for extension [.{}] but another one was already defined: {}", fileExtension, old); } } public AbstractTemplateEngine getEngine(String fileExtension) { - return templateEngines.get(fileExtension); + return engines.get(fileExtension); } /** @@ -71,28 +69,16 @@ public AbstractTemplateEngine getEngine(String fileExtension) { * * @param config the configuration * @param db database instance - * @param destination target directory - * @param templatesPath path to template directory * @param engineClassName engine class, used both as a hint to find it and to create the engine itself. @return null if the engine is not available, an instance of the engine otherwise */ - private static AbstractTemplateEngine tryLoadEngine(final Configuration config, final ContentStore db, final File destination, final File templatesPath, String engineClassName) { + private static AbstractTemplateEngine tryLoadEngine(final JBakeConfiguration config, final ContentStore db, String engineClassName) { try { @SuppressWarnings("unchecked") Class engineClass = (Class) Class.forName(engineClassName, false, TemplateEngines.class.getClassLoader()); - Constructor ctor = engineClass.getConstructor(CompositeConfiguration.class, ContentStore.class, File.class, File.class); - return ctor.newInstance(config, db, destination, templatesPath); - } catch (ClassNotFoundException e) { - return null; - } catch (InstantiationException e) { - return null; - } catch (IllegalAccessException e) { - return null; - } catch (NoClassDefFoundError e) { - // a dependency of the engine may not be found on classpath - return null; - } catch (NoSuchMethodException e) { - return null; - } catch (InvocationTargetException e) { + Constructor ctor = engineClass.getConstructor(JBakeConfiguration.class, ContentStore.class); + return ctor.newInstance(config, db); + } catch (Exception e) { + LOGGER.error("unable to load engine", e); return null; } } @@ -101,7 +87,7 @@ private static AbstractTemplateEngine tryLoadEngine(final Configuration config, * This method is used internally to load markup engines. Markup engines are found using descriptor files on * classpath, so adding an engine is as easy as adding a jar on classpath with the descriptor file included. */ - private void loadEngines(final Configuration config, final ContentStore db, final File destination, final File templatesPath) { + private void loadEngines(final JBakeConfiguration config, final ContentStore db) { try { ClassLoader cl = TemplateEngines.class.getClassLoader(); Enumeration resources = cl.getResources("META-INF/org.jbake.parser.TemplateEngines.properties"); @@ -112,16 +98,16 @@ private void loadEngines(final Configuration config, final ContentStore db, fina for (Map.Entry entry : props.entrySet()) { String className = (String) entry.getKey(); String[] extensions = ((String) entry.getValue()).split(","); - registerEngine(config, db, destination, templatesPath, className, extensions); + registerEngine(config, db, className, extensions); } } } catch (IOException e) { - e.printStackTrace(); + LOGGER.error("Error loading engines", e); } } - private void registerEngine(final Configuration config, final ContentStore db, final File destination, final File templatesPath, String className, String... extensions) { - AbstractTemplateEngine engine = tryLoadEngine(config, db, destination, templatesPath, className); + private void registerEngine(final JBakeConfiguration config, final ContentStore db, String className, String... extensions) { + AbstractTemplateEngine engine = tryLoadEngine(config, db, className); if (engine != null) { for (String extension : extensions) { registerEngine(extension, engine); diff --git a/jbake-core/src/main/java/org/jbake/template/ThymeleafTemplateEngine.java b/jbake-core/src/main/java/org/jbake/template/ThymeleafTemplateEngine.java index 65731ba0b..6caebc87b 100644 --- a/jbake-core/src/main/java/org/jbake/template/ThymeleafTemplateEngine.java +++ b/jbake-core/src/main/java/org/jbake/template/ThymeleafTemplateEngine.java @@ -2,10 +2,10 @@ import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.lang.LocaleUtils; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ContentStore; import org.jbake.app.Crawler; import org.jbake.app.Crawler.Attributes; +import org.jbake.app.configuration.JBakeConfiguration; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; import org.thymeleaf.context.LazyContextVariable; @@ -42,18 +42,26 @@ public class ThymeleafTemplateEngine extends AbstractTemplateEngine { private String templateMode; + /** + * @deprecated Use {@link #ThymeleafTemplateEngine(JBakeConfiguration, ContentStore)} instead + */ + @Deprecated public ThymeleafTemplateEngine(final CompositeConfiguration config, final ContentStore db, final File destination, final File templatesPath) { super(config, db, destination, templatesPath); } + public ThymeleafTemplateEngine(final JBakeConfiguration config, final ContentStore db) { + super(config, db); + } + private void initializeTemplateEngine(String mode) { if (mode.equals(templateMode)) { return; } templateMode = mode; FileTemplateResolver templateResolver = new FileTemplateResolver(); - templateResolver.setPrefix(templatesPath.getAbsolutePath() + File.separatorChar); - templateResolver.setCharacterEncoding(config.getString(Keys.TEMPLATE_ENCODING)); + templateResolver.setPrefix(config.getTemplateFolder().getAbsolutePath() + File.separatorChar); + templateResolver.setCharacterEncoding(config.getTemplateEncoding()); templateResolver.setTemplateMode(mode); templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver); @@ -61,7 +69,7 @@ private void initializeTemplateEngine(String mode) { @Override public void renderDocument(final Map model, final String templateName, final Writer writer) throws RenderingException { - String localeString = config.getString(Keys.THYMELEAF_LOCALE); + String localeString = config.getThymeleafLocale(); Locale locale = localeString != null ? LocaleUtils.toLocale(localeString) : Locale.getDefault(); Context context = new Context(locale, wrap(model)); lock.lock(); diff --git a/jbake-core/src/main/java/org/jbake/template/model/PublishedPostsExtractor.java b/jbake-core/src/main/java/org/jbake/template/model/PublishedPostsExtractor.java index 343c00604..741526901 100644 --- a/jbake-core/src/main/java/org/jbake/template/model/PublishedPostsExtractor.java +++ b/jbake-core/src/main/java/org/jbake/template/model/PublishedPostsExtractor.java @@ -10,11 +10,11 @@ public class PublishedPostsExtractor implements ModelExtractor { @Override public DocumentList get(ContentStore db, Map model, String key) { - if (model.containsKey("numberOfPages")) { - return db.getPublishedPosts(true); - } else { - return db.getPublishedPosts(); - } + if (model.containsKey("numberOfPages")) { + return db.getPublishedPosts(true); + } else { + return db.getPublishedPosts(); + } } } \ No newline at end of file diff --git a/jbake-core/src/main/java/org/jbake/template/model/TaggedDocumentsExtractor.java b/jbake-core/src/main/java/org/jbake/template/model/TaggedDocumentsExtractor.java index c298ddc68..3fbaff373 100644 --- a/jbake-core/src/main/java/org/jbake/template/model/TaggedDocumentsExtractor.java +++ b/jbake-core/src/main/java/org/jbake/template/model/TaggedDocumentsExtractor.java @@ -1,12 +1,12 @@ package org.jbake.template.model; -import java.util.Map; - import org.jbake.app.ContentStore; import org.jbake.app.Crawler; import org.jbake.app.DocumentList; import org.jbake.template.ModelExtractor; +import java.util.Map; + public class TaggedDocumentsExtractor implements ModelExtractor { @Override diff --git a/jbake-core/src/main/java/org/jbake/template/model/TagsExtractor.java b/jbake-core/src/main/java/org/jbake/template/model/TagsExtractor.java index c7a189172..04aabb16d 100644 --- a/jbake-core/src/main/java/org/jbake/template/model/TagsExtractor.java +++ b/jbake-core/src/main/java/org/jbake/template/model/TagsExtractor.java @@ -1,35 +1,39 @@ package org.jbake.template.model; -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ContentStore; +import org.jbake.app.Crawler; import org.jbake.app.DocumentList; import org.jbake.template.ModelExtractor; +import java.util.HashMap; +import java.util.Map; + +import static org.jbake.app.configuration.JBakeProperty.OUTPUT_EXTENSION; +import static org.jbake.app.configuration.JBakeProperty.TAG_PATH; + + public class TagsExtractor implements ModelExtractor { - @Override - public DocumentList get(ContentStore db, Map model, String key) { - DocumentList dl = new DocumentList(); - Map config = (Map) model.get("config"); - - String tagPath = config.get(Keys.TAG_PATH.replace(".", "_")).toString(); - - for (String tag : db.getAllTags()){ - Map newTag = new HashMap<>(); - newTag.put("name", tag); - - String uri = tagPath + File.separator + tag + config.get(Keys.OUTPUT_EXTENSION.replace(".", "_")).toString(); - - newTag.put("uri", uri); - newTag.put("tagged_posts", db.getPublishedPostsByTag(tag)); - newTag.put("tagged_documents", db.getPublishedDocumentsByTag(tag)); - dl.push(newTag); - } - return dl; - } + @Override + public DocumentList get(ContentStore db, Map model, String key) { + DocumentList dl = new DocumentList(); + Map config = (Map) model.get("config"); + + String tagPath = config.get(TAG_PATH.replace(".", "_")).toString(); + + for (String tag : db.getAllTags()) { + Map newTag = new HashMap<>(); + String tagName = tag; + newTag.put("name", tagName); + + String uri = tagPath + Crawler.URI_SEPARATOR_CHAR + tag + config.get(OUTPUT_EXTENSION.replace(".", "_")).toString(); + + newTag.put("uri", uri); + newTag.put("tagged_posts", db.getPublishedPostsByTag(tagName)); + newTag.put("tagged_documents", db.getPublishedDocumentsByTag(tagName)); + dl.push(newTag); + } + return dl; + } } diff --git a/jbake-core/src/main/java/org/jbake/util/HtmlUtil.java b/jbake-core/src/main/java/org/jbake/util/HtmlUtil.java index 82d9ba30e..b627e2f28 100644 --- a/jbake-core/src/main/java/org/jbake/util/HtmlUtil.java +++ b/jbake-core/src/main/java/org/jbake/util/HtmlUtil.java @@ -1,15 +1,14 @@ package org.jbake.util; -import java.util.Map; - -import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.Crawler.Attributes; +import org.jbake.app.configuration.JBakeConfiguration; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import java.util.Map; + /** * * @author Manik Magar @@ -24,13 +23,15 @@ public class HtmlUtil { * If image path starts with "./", i.e. relative to the source file, then it first replace that with output file directory and the add site host. * * @param fileContents Map representing file contents - * @param config Configuration object + * @param configuration Configuration object */ - public static void fixImageSourceUrls(Map fileContents, CompositeConfiguration config){ + + //TODO: use JBakeConfiguration + public static void fixImageSourceUrls(Map fileContents, JBakeConfiguration configuration){ String htmlContent = fileContents.get(Attributes.BODY).toString(); - String siteHost = config.getString(Keys.SITE_HOST); + String siteHost = (String) configuration.get("site.host"); String uri = fileContents.get(Attributes.URI).toString(); diff --git a/jbake-core/src/main/resources/default.properties b/jbake-core/src/main/resources/default.properties index 01a45d96f..62d00a16e 100644 --- a/jbake-core/src/main/resources/default.properties +++ b/jbake-core/src/main/resources/default.properties @@ -26,6 +26,8 @@ template.page.file=page.ftl content.folder=content # folder that contains all asset files asset.folder=assets +# Flag indicating if hidden asset resources should be ignored +asset.ignore=false # render index file? render.index=true # filename to use for index file @@ -73,15 +75,19 @@ example.project.thymeleaf=example_project_thymeleaf.zip # zip file containing example project structure using jade templates example.project.jade=example_project_jade.zip # default asciidoctor options +asciidoctor.option= +# default asciidoctor attributes asciidoctor.attributes=source-highlighter=prettify # should JBake config options be exported to Asciidoctor engine? -#asciidoctor.attributes.export=false +asciidoctor.attributes.export=true # prefix that should be used when JBake config options are exported -#asciidoctor.attributes.export.prefix= +asciidoctor.attributes.export.prefix= # default date format used in content files date.format=yyyy-MM-dd # default status -#default.status=published +default.status=draft +# default type +default.type= # comma delimited default markdown extensions; for available extensions: # http://www.decodified.com/pegdown/api/org/pegdown/Extensions.html markdown.extensions=HARDWRAPS,AUTOLINKS,FENCED_CODE_BLOCKS,DEFINITIONS @@ -91,9 +97,19 @@ markdown.maxParsingTimeInMillis=2000 db.store=memory # database path db.path=cache +# clear cache +db.clear.cache=false # enable extension-less URI option? uri.noExtension=false # Set to a prefix path (starting with a slash) for which to generate extension-less URI's (i.e. a folder with index.html in) uri.noExtension.prefix= +# default thymeleafe locale +thymeleaf.locale=en_US +# paginate index +index.paginate=false +# number of post per page for pagination +index.posts_per_page=3 +# site host +site.host=http://www.jbake.org # String used to separate the header from the body header.separator=~~~~~~ diff --git a/jbake-core/src/test/java/org/jbake/TestUtils.java b/jbake-core/src/test/java/org/jbake/TestUtils.java index 22188094e..22bd86a7a 100644 --- a/jbake-core/src/test/java/org/jbake/TestUtils.java +++ b/jbake-core/src/test/java/org/jbake/TestUtils.java @@ -1,28 +1,32 @@ package org.jbake; +import org.apache.commons.vfs2.util.Os; + import java.io.File; import java.io.FilenameFilter; import java.io.IOException; -import org.apache.commons.vfs2.util.Os; - public class TestUtils { - - /** - * Hides the assets on Windows that start with a dot (e.g. .test.txt but not test.txt) so File.isHidden() returns true for those files. - */ - public static void hideAssets(File assets) throws IOException, InterruptedException { - if ( Os.isFamily(Os.OS_FAMILY_WINDOWS) ) { - final File[] hiddenFiles = assets.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.startsWith("."); - } - }); - for (File file : hiddenFiles) { - final Process process = Runtime.getRuntime().exec(new String[] {"attrib" , "+h", file.getAbsolutePath()}); - process.waitFor(); - } - } - } + + /** + * Hides the assets on Windows that start with a dot (e.g. .test.txt but not test.txt) so File.isHidden() returns true for those files. + */ + public static void hideAssets(File assets) throws IOException, InterruptedException { + if (isWindows()) { + final File[] hiddenFiles = assets.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.startsWith("."); + } + }); + for (File file : hiddenFiles) { + final Process process = Runtime.getRuntime().exec(new String[]{"attrib", "+h", file.getAbsolutePath()}); + process.waitFor(); + } + } + } + + public static boolean isWindows() { + return Os.isFamily(Os.OS_FAMILY_WINDOWS); + } } diff --git a/jbake-core/src/test/java/org/jbake/app/AssetTest.java b/jbake-core/src/test/java/org/jbake/app/AssetTest.java index cbd93cfaf..f11cbccc1 100644 --- a/jbake-core/src/test/java/org/jbake/app/AssetTest.java +++ b/jbake-core/src/test/java/org/jbake/app/AssetTest.java @@ -1,32 +1,29 @@ package org.jbake.app; -import java.io.File; -import java.io.FileFilter; -import java.io.FilenameFilter; -import java.io.IOException; -import java.net.URL; -import java.util.Locale; - -import org.apache.commons.vfs2.util.Os; -import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.FileFilterUtils; import org.jbake.TestUtils; -import org.jbake.app.ConfigUtil.Keys; +import org.jbake.app.configuration.ConfigUtil; +import org.jbake.app.configuration.DefaultJBakeConfiguration; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import java.io.File; +import java.io.FileFilter; +import java.net.URL; + public class AssetTest { - private CompositeConfiguration config; + private DefaultJBakeConfiguration config; @Before public void setup() throws Exception { - config = ConfigUtil.load(new File(this.getClass().getResource("/fixture").getFile())); - Assert.assertEquals(".html", config.getString(Keys.OUTPUT_EXTENSION)); + config = (DefaultJBakeConfiguration) new ConfigUtil().loadConfig(new File(this.getClass().getResource("/fixture").getFile())); + config.setDestinationFolder(folder.getRoot()); + Assert.assertEquals(".html", config.getOutputExtension()); } @Rule @@ -34,10 +31,8 @@ public void setup() throws Exception { @Test public void copy() throws Exception { - URL assetsUrl = this.getClass().getResource("/fixture/assets"); - File assets = new File(assetsUrl.getFile()); - Asset asset = new Asset(assets.getParentFile(), folder.getRoot(), config); - asset.copy(assets); + Asset asset = new Asset(config); + asset.copy(); File cssFile = new File(folder.getRoot().getPath() + File.separatorChar + "css" + File.separatorChar + "bootstrap.min.css"); Assert.assertTrue("File " + cssFile.getAbsolutePath() + " does not exist", cssFile.exists()); @@ -51,11 +46,9 @@ public void copy() throws Exception { @Test public void copyCustomFolder() throws Exception { - config.setProperty(ConfigUtil.Keys.ASSET_FOLDER, "media"); - URL assetsUrl = this.getClass().getResource("/fixture/media"); - File assets = new File(assetsUrl.getFile()); - Asset asset = new Asset(assets.getParentFile(), folder.getRoot(), config); - asset.copy(assets); + config.setAssetFolder(new File(config.getSourceFolder(),"/media")); + Asset asset = new Asset(config); + asset.copy(); File favFile = new File(folder.getRoot().getPath() + File.separatorChar + "favicon.ico"); Assert.assertTrue("File " + favFile.getAbsolutePath() + " does not exist", favFile.exists()); @@ -66,11 +59,11 @@ public void copyCustomFolder() throws Exception { @Test public void copyIgnore() throws Exception { File assetFolder = folder.newFolder("ignoredAssets"); - FileUtils.copyDirectory(new File(this.getClass().getResource("/fixture/ignorables").getFile()), assetFolder); - config.setProperty(Keys.ASSET_FOLDER, "ignorables"); - config.setProperty(Keys.ASSET_IGNORE_HIDDEN, "true"); + FileUtils.copyDirectory(new File(this.getClass().getResource("/fixture/ignorables").getFile()), assetFolder); + config.setAssetFolder(assetFolder); + config.setAssetIgnoreHidden(true); TestUtils.hideAssets(assetFolder); - Asset asset = new Asset(assetFolder.getParentFile(), folder.getRoot(), config); + Asset asset = new Asset(config); asset.copy(assetFolder); File testFile = new File(folder.getRoot(), "test.txt"); @@ -81,7 +74,7 @@ public void copyIgnore() throws Exception { Assert.assertTrue("Errors during asset copying", asset.getErrors().isEmpty()); } - + /** * Primary intention is to extend test cases to increase coverage. @@ -90,13 +83,15 @@ public void copyIgnore() throws Exception { */ @Test public void testWriteProtected() throws Exception { - URL assetsUrl = this.getClass().getResource("/fixture/assets"); - File assets = new File(assetsUrl.getFile()); + File assets = new File(config.getSourceFolder(),"assets"); final File cssFile = new File(folder.newFolder("css"), "bootstrap.min.css"); FileUtils.touch(cssFile); cssFile.setReadOnly(); - Asset asset = new Asset(assets.getParentFile(), folder.getRoot(), config); - asset.copy(assets); + + config.setAssetFolder(assets); + config.setDestinationFolder(folder.getRoot()); + Asset asset = new Asset(config); + asset.copy(); Assert.assertFalse("At least one error during copy expected", asset.getErrors().isEmpty()); } @@ -108,18 +103,16 @@ public void testWriteProtected() throws Exception { */ @Test public void testUnlistable() throws Exception { - config.setProperty(Keys.ASSET_FOLDER, "non-existent"); - URL assetsUrl = this.getClass().getResource("/fixture"); - File assets = new File(assetsUrl.getFile() + File.separatorChar + "non-existent"); - Asset asset = new Asset(assets.getParentFile(), folder.getRoot(), config); - asset.copy(assets); + config.setAssetFolder(new File(config.getSourceFolder(), "non-exsitent")); + Asset asset = new Asset(config); + asset.copy(); } - + @Test public void testJBakeIgnoredFolder(){ URL assetsUrl = this.getClass().getResource("/fixture/assets"); File assets = new File(assetsUrl.getFile()); - Asset asset = new Asset(assets.getParentFile(), folder.getRoot(), config); + Asset asset = new Asset(config); asset.copy(assets); File cssFile = new File(folder.getRoot().getPath() + File.separatorChar + "css" + File.separatorChar + "bootstrap.min.css"); @@ -133,32 +126,32 @@ public void testJBakeIgnoredFolder(){ Assert.assertTrue("Errors during asset copying", asset.getErrors().isEmpty()); } - - + + @Test public void testCopyAssetsFromContent(){ URL contentUrl = this.getClass().getResource("/fixture/content"); File contents = new File(contentUrl.getFile()); - Asset asset = new Asset(contents.getParentFile(), folder.getRoot(), config); + Asset asset = new Asset(config); asset.copyAssetsFromContent(contents); - + int totalFiles = countFiles(folder.getRoot()); int expected = 3; - + Assert.assertTrue(String.format("Number of files copied must be %d but are %d", expected, totalFiles), totalFiles == expected); - + File pngFile = new File(folder.getRoot().getPath() + File.separatorChar + "blog" + File.separatorChar + "2012/images/custom-image.png"); Assert.assertTrue("File " + pngFile.getAbsolutePath() + " does not exist", pngFile.exists()); - + File jpgFile = new File(folder.getRoot().getPath() + File.separatorChar + "blog" + File.separatorChar + "2013/images/custom-image.jpg"); Assert.assertTrue("File " + jpgFile.getAbsolutePath() + " does not exist", jpgFile.exists()); - + File jsonFile = new File(folder.getRoot().getPath() + File.separatorChar + "blog" + File.separatorChar + "2012/sample.json"); Assert.assertTrue("File " + jsonFile.getAbsolutePath() + " does not exist", jsonFile.exists()); - + Assert.assertTrue("Errors during asset copying", asset.getErrors().isEmpty()); } - + private Integer countFiles(File path){ int total = 0; FileFilter filesOnly = FileFilterUtils.fileFileFilter(); diff --git a/jbake-core/src/test/java/org/jbake/app/ConfigUtilTest.java b/jbake-core/src/test/java/org/jbake/app/ConfigUtilTest.java deleted file mode 100644 index 47ee8ddc0..000000000 --- a/jbake-core/src/test/java/org/jbake/app/ConfigUtilTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.jbake.app; - -import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; -import org.junit.Test; - -import java.io.File; - -import static org.junit.Assert.assertEquals; - -public class ConfigUtilTest { - - @Test - public void load() throws Exception { - CompositeConfiguration config = ConfigUtil.load(new File(this.getClass().getResource("/fixture").getFile())); - - // check default.properties values exist - assertEquals("output", config.getString(Keys.DESTINATION_FOLDER)); - - // check custom.properties values exist - assertEquals("testing123", config.getString("test.property")); - - assertEquals("http://www.jbake.org", config.getString(Keys.SITE_HOST)); - - } -} diff --git a/jbake-core/src/test/java/org/jbake/app/ContentStoreIntegrationTest.java b/jbake-core/src/test/java/org/jbake/app/ContentStoreIntegrationTest.java index e5459ad55..0ba49a52f 100644 --- a/jbake-core/src/test/java/org/jbake/app/ContentStoreIntegrationTest.java +++ b/jbake-core/src/test/java/org/jbake/app/ContentStoreIntegrationTest.java @@ -1,17 +1,44 @@ package org.jbake.app; +import org.jbake.TestUtils; +import org.jbake.app.configuration.ConfigUtil; +import org.jbake.app.configuration.DefaultJBakeConfiguration; import org.junit.After; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.net.URL; public abstract class ContentStoreIntegrationTest { protected static ContentStore db; + protected static DefaultJBakeConfiguration config; + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + protected static File sourceFolder; @BeforeClass - public static void setUpClass() { - db = DBUtil.createDataStore("memory", "documents" + System.currentTimeMillis()); + public static void setUpClass() throws Exception { + + URL sourceUrl = TestUtils.class.getResource("/fixture"); + + sourceFolder = new File(sourceUrl.getFile()); + Assert.assertTrue("Cannot find sample data structure!", sourceFolder.exists()); + + config = (DefaultJBakeConfiguration) new ConfigUtil().loadConfig(sourceFolder); + config.setSourceFolder(sourceFolder); + + Assert.assertEquals(".html", config.getOutputExtension()); + config.setDatabaseStore("memory"); + config.setDatabasePath("documents" + System.currentTimeMillis()); + + db = DBUtil.createDataStore(config); } @AfterClass @@ -22,11 +49,11 @@ public static void cleanUpClass() { @Before public void setUp() { - db.updateSchema(); + db.startup(); } @After - public void tearDown() throws Exception { + public void tearDown() { db.drop(); } } diff --git a/jbake-core/src/test/java/org/jbake/app/CrawlerTest.java b/jbake-core/src/test/java/org/jbake/app/CrawlerTest.java index 3db67fe82..4e38e3cc9 100644 --- a/jbake-core/src/test/java/org/jbake/app/CrawlerTest.java +++ b/jbake-core/src/test/java/org/jbake/app/CrawlerTest.java @@ -1,44 +1,22 @@ package org.jbake.app; -import org.apache.commons.configuration.CompositeConfiguration; -import org.apache.commons.configuration.MapConfiguration; import org.apache.commons.io.FilenameUtils; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; -import org.jbake.app.ConfigUtil.Keys; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; -import java.io.File; -import java.net.URL; -import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; public class CrawlerTest extends ContentStoreIntegrationTest { - private CompositeConfiguration config; - private File sourceFolder; - @Before - public void setup() throws Exception { - URL sourceUrl = this.getClass().getResource("/fixture"); - - sourceFolder = new File(sourceUrl.getFile()); - if (!sourceFolder.exists()) { - throw new Exception("Cannot find sample data structure!"); - } - - config = ConfigUtil.load(sourceFolder); - Assert.assertEquals(".html", config.getString(Keys.OUTPUT_EXTENSION)); - } - @Test public void crawl() { - Crawler crawler = new Crawler(db, sourceFolder, config); - crawler.crawl(new File(sourceFolder.getPath() + File.separator + config.getString(Keys.CONTENT_FOLDER))); + Crawler crawler = new Crawler(db, config); + crawler.crawl(); Assert.assertEquals(4, db.getDocumentCount("post")); Assert.assertEquals(3, db.getDocumentCount("page")); @@ -50,7 +28,7 @@ public void crawl() { for (Map content : results) { assertThat(content) .containsKey(Crawler.Attributes.ROOTPATH) - .containsValue("../../"); + .containsValue("../../../"); } DocumentList allPosts = db.getAllContent("post"); @@ -70,16 +48,12 @@ public void crawl() { @Test public void renderWithPrettyUrls() throws Exception { - Map testProperties = new HashMap<>(); - testProperties.put(Keys.URI_NO_EXTENSION, true); - testProperties.put(Keys.URI_NO_EXTENSION_PREFIX, "/blog"); - CompositeConfiguration config = new CompositeConfiguration(); - config.addConfiguration(new MapConfiguration(testProperties)); - config.addConfiguration(ConfigUtil.load(sourceFolder)); + config.setUriWithoutExtension(true); + config.setPrefixForUriWithoutExtension("/blog"); - Crawler crawler = new Crawler(db, sourceFolder, config); - crawler.crawl(new File(sourceFolder.getPath() + File.separator + config.getString(Keys.CONTENT_FOLDER))); + Crawler crawler = new Crawler(db, config); + crawler.crawl(); Assert.assertEquals(4, db.getDocumentCount("post")); Assert.assertEquals(3, db.getDocumentCount("page")); diff --git a/jbake-core/src/test/java/org/jbake/app/FileUtilTest.java b/jbake-core/src/test/java/org/jbake/app/FileUtilTest.java index 826f7118e..c91bba49f 100644 --- a/jbake-core/src/test/java/org/jbake/app/FileUtilTest.java +++ b/jbake-core/src/test/java/org/jbake/app/FileUtilTest.java @@ -1,13 +1,10 @@ package org.jbake.app; -import static org.assertj.core.api.Assertions.*; - -import org.jbake.TestUtils; import org.junit.Test; import java.io.File; -import java.io.IOException; -import java.net.URL; + +import static org.junit.Assert.assertEquals; /** * Created by frank on 28.03.16. @@ -18,36 +15,6 @@ public class FileUtilTest { public void testGetRunningLocation() throws Exception { File path = FileUtil.getRunningLocation(); - assertThat(new File("build/classes").getAbsolutePath()).isEqualTo(path.getPath()); - } - - @Test - public void testFileIgnore() throws IOException, InterruptedException { - URL sourceUrl = this.getClass().getResource("/fixture"); - File contentFolder = new File(sourceUrl.getFile(), "content"); - - //Without filter, make sure ignorable file is selected - File files1[] = contentFolder.listFiles(); - assertThat(files1).contains(new File(contentFolder, ".ignorablefile.html")); - - TestUtils.hideAssets(contentFolder); - - //When using filter, ignorable file should not be selected - File files2[] = contentFolder.listFiles(FileUtil.getFileFilter()); - assertThat(files2).doesNotContain(new File(contentFolder, ".ignorablefile.html")); - } - - @Test - public void testFolderIgnore(){ - URL sourceUrl = this.getClass().getResource("/fixture"); - File contentFolder = new File(sourceUrl.getFile()); - - //Without filter, make sure ignorable folder is selected - File files1[] = contentFolder.listFiles(); - assertThat(files1).contains(new File(contentFolder, "ignorablefolder")); - - //When using filter, ignorable folder should not be selected - File files2[] = contentFolder.listFiles(FileUtil.getFileFilter()); - assertThat(files2).doesNotContain(new File(contentFolder, "ignorablefolder")); + assertEquals(new File("build/classes").getAbsolutePath(), path.getPath()); } -} \ No newline at end of file +} diff --git a/jbake-core/src/test/java/org/jbake/app/InitTest.java b/jbake-core/src/test/java/org/jbake/app/InitTest.java index 51ca5e11e..19657394c 100644 --- a/jbake-core/src/test/java/org/jbake/app/InitTest.java +++ b/jbake-core/src/test/java/org/jbake/app/InitTest.java @@ -1,7 +1,7 @@ package org.jbake.app; -import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; +import org.jbake.app.configuration.ConfigUtil; +import org.jbake.app.configuration.DefaultJBakeConfiguration; import org.jbake.launcher.Init; import org.junit.Before; import org.junit.Rule; @@ -17,60 +17,60 @@ public class InitTest { - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - public CompositeConfiguration config; - private File rootPath; - - @Before - public void setup() throws Exception { - URL sourceUrl = this.getClass().getResource("/fixture"); - rootPath = new File(sourceUrl.getFile()); + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + public DefaultJBakeConfiguration config; + private File rootPath; + + @Before + public void setup() throws Exception { + URL sourceUrl = this.getClass().getResource("/fixture"); + rootPath = new File(sourceUrl.getFile()); if (!rootPath.exists()) { throw new Exception("Cannot find base path for test!"); } - config = ConfigUtil.load(rootPath); - // override base template config option - config.setProperty("example.project.freemarker", "test.zip"); - } - - @Test - public void initOK() throws Exception { - Init init = new Init(config); - File initPath = folder.newFolder("init"); - init.run(initPath, rootPath, "freemarker"); - File testFile = new File(initPath, "testfile.txt"); - assertThat(testFile).exists(); - } - - @Test - public void initFailDestinationContainsContent() throws IOException { - Init init = new Init(config); - File initPath = folder.newFolder("init"); - File contentFolder = new File(initPath.getPath() + File.separatorChar + config.getString(Keys.CONTENT_FOLDER)); - contentFolder.mkdir(); - try { - init.run(initPath, rootPath, "freemarker"); - fail("Shouldn't be able to initialise folder with content folder within it!"); - } catch (Exception e) { - e.printStackTrace(); - } - File testFile = new File(initPath, "testfile.txt"); - assertThat(testFile).doesNotExist(); - } - - @Test - public void initFailInvalidTemplateType() throws IOException { - Init init = new Init(config); - File initPath = folder.newFolder("init"); - try { - init.run(initPath, rootPath, "invalid"); - fail("Shouldn't be able to initialise folder with invalid template type"); - } catch (Exception e) { - e.printStackTrace(); - } - File testFile = new File(initPath, "testfile.txt"); - assertThat(testFile).doesNotExist(); - } + config = (DefaultJBakeConfiguration) new ConfigUtil().loadConfig(rootPath); + // override base template config option + config.setExampleProject("freemarker", "test.zip"); + } + + @Test + public void initOK() throws Exception { + Init init = new Init(config); + File initPath = folder.newFolder("init"); + init.run(initPath, rootPath, "freemarker"); + File testFile = new File(initPath, "testfile.txt"); + assertThat(testFile).exists(); + } + + @Test + public void initFailDestinationContainsContent() throws IOException{ + Init init = new Init(config); + File initPath = folder.newFolder("init"); + File contentFolder = new File(initPath.getPath(), config.getContentFolderName()); + contentFolder.mkdir(); + try { + init.run(initPath, rootPath, "freemarker"); + fail("Shouldn't be able to initialise folder with content folder within it!"); + } catch (Exception e) { + e.printStackTrace(); + } + File testFile = new File(initPath, "testfile.txt"); + assertThat(testFile).doesNotExist(); + } + + @Test + public void initFailInvalidTemplateType() throws IOException{ + Init init = new Init(config); + File initPath = folder.newFolder("init"); + try { + init.run(initPath, rootPath, "invalid"); + fail("Shouldn't be able to initialise folder with invalid template type"); + } catch (Exception e) { + e.printStackTrace(); + } + File testFile = new File(initPath, "testfile.txt"); + assertThat(testFile).doesNotExist(); + } } diff --git a/jbake-core/src/test/java/org/jbake/app/LoggingTest.java b/jbake-core/src/test/java/org/jbake/app/LoggingTest.java new file mode 100644 index 000000000..799493212 --- /dev/null +++ b/jbake-core/src/test/java/org/jbake/app/LoggingTest.java @@ -0,0 +1,42 @@ +package org.jbake.app; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.Appender; +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.slf4j.LoggerFactory; + +@RunWith(MockitoJUnitRunner.class) +public abstract class LoggingTest { + + @Mock + protected Appender mockAppender; + + @Captor + protected ArgumentCaptor captorLoggingEvent; + + protected Logger root; + + @Before + public void setup() { + root = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + + root.addAppender(mockAppender); + root.setLevel(Level.INFO); + } + + @After + public void teardown() { + root.detachAppender(mockAppender); + } + + +} diff --git a/jbake-core/src/test/java/org/jbake/app/MdParserTest.java b/jbake-core/src/test/java/org/jbake/app/MdParserTest.java index 22df97cce..36889af02 100644 --- a/jbake-core/src/test/java/org/jbake/app/MdParserTest.java +++ b/jbake-core/src/test/java/org/jbake/app/MdParserTest.java @@ -1,7 +1,8 @@ package org.jbake.app; -import org.apache.commons.configuration.CompositeConfiguration; +import org.jbake.app.configuration.ConfigUtil; +import org.jbake.app.configuration.DefaultJBakeConfiguration; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -26,9 +27,7 @@ public class MdParserTest { @Rule public TemporaryFolder folder = new TemporaryFolder(); - public CompositeConfiguration config; - - private File configFile; + public DefaultJBakeConfiguration config; private File validMdFileBasic; @@ -74,13 +73,11 @@ public class MdParserTest { private String invalidHeader = "title=Title\n~~~~~~"; - private String extensions = "markdown.extensions"; - @Before public void createSampleFile() throws Exception { - configFile = new File(this.getClass().getResource(".").getFile()); - config = ConfigUtil.load(configFile); + File configFile = new File(this.getClass().getResource("/fixture").getFile()); + config = (DefaultJBakeConfiguration) new ConfigUtil().loadConfig(configFile); validMdFileBasic = folder.newFile("validBasic.md"); PrintWriter out = new PrintWriter(validMdFileBasic); @@ -235,35 +232,34 @@ public void createSampleFile() throws Exception { @Test public void parseValidMarkdownFileBasic() throws Exception { - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(validMdFileBasic); Assert.assertNotNull(map); Assert.assertEquals("draft", map.get("status")); Assert.assertEquals("post", map.get("type")); - assertThat((String)map.get("body")).contains("

This is a test

"); + Assert.assertEquals("

This is a test

\n", map.get("body")); } @Test public void parseInvalidMarkdownFileBasic() { - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(invalidMdFileBasic); Assert.assertNull(map); } @Test public void parseValidMdFileHardWraps() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "HARDWRAPS"); + config.setMarkdownExtensions("HARDWRAPS"); // Test with HARDWRAPS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileHardWraps); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

First line
\nSecond line

\n"); // Test without HARDWRAPS - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileHardWraps); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

First line Second line

"); @@ -271,11 +267,10 @@ public void parseValidMdFileHardWraps() throws Exception { @Test public void parseWithInvalidExtension() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "HARDWRAPS,UNDEFINED_EXTENSION"); + config.setMarkdownExtensions("HARDWRAPS,UNDEFINED_EXTENSION"); // Test with HARDWRAPS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileHardWraps); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

First line
\nSecond line

\n"); @@ -283,11 +278,10 @@ public void parseWithInvalidExtension() throws Exception { @Test public void parseValidMdFileAbbreviations() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "ABBREVIATIONS"); + config.setMarkdownExtensions("ABBREVIATIONS"); // Test with ABBREVIATIONS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileAbbreviations); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -295,8 +289,8 @@ public void parseValidMdFileAbbreviations() throws Exception { ); // Test without ABBREVIATIONS - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileAbbreviations); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

*[HTML]: Hyper Text Markup Language HTML

"); @@ -304,11 +298,11 @@ public void parseValidMdFileAbbreviations() throws Exception { @Test public void parseValidMdFileAutolinks() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "AUTOLINKS"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("AUTOLINKS"); // Test with AUTOLINKS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileAutolinks); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -316,8 +310,8 @@ public void parseValidMdFileAutolinks() throws Exception { ); // Test without AUTOLINKS - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileAutolinks); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

http://github.com

"); @@ -325,11 +319,11 @@ public void parseValidMdFileAutolinks() throws Exception { @Test public void parseValidMdFileDefinitions() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "DEFINITIONS"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("DEFINITIONS"); // Test with DEFINITIONS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileDefinitions); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -337,8 +331,8 @@ public void parseValidMdFileDefinitions() throws Exception { ); // Test without DEFNITIONS - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileDefinitions); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

Apple : Pomaceous fruit

"); @@ -346,11 +340,11 @@ public void parseValidMdFileDefinitions() throws Exception { @Test public void parseValidMdFileFencedCodeBlocks() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "FENCED_CODE_BLOCKS"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("FENCED_CODE_BLOCKS"); // Test with FENCED_CODE_BLOCKS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileFencedCodeBlocks); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -358,8 +352,8 @@ public void parseValidMdFileFencedCodeBlocks() throws Exception { ); // Test without FENCED_CODE_BLOCKS - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileFencedCodeBlocks); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -369,18 +363,18 @@ public void parseValidMdFileFencedCodeBlocks() throws Exception { @Test public void parseValidMdFileQuotes() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "QUOTES"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("QUOTES"); // Test with QUOTES - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileQuotes); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

“quotes”

"); // Test without QUOTES - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileQuotes); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

"quotes"

"); @@ -388,18 +382,18 @@ public void parseValidMdFileQuotes() throws Exception { @Test public void parseValidMdFileSmarts() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "SMARTS"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("SMARTS"); // Test with SMARTS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileSmarts); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

"); // Test without SMARTS - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileSmarts); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

...

"); @@ -407,18 +401,18 @@ public void parseValidMdFileSmarts() throws Exception { @Test public void parseValidMdFileSmartypants() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "SMARTYPANTS"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("SMARTYPANTS"); // Test with SMARTYPANTS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileSmartypants); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

“…”

"); // Test without SMARTYPANTS - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileSmartypants); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

"..."

"); @@ -426,18 +420,18 @@ public void parseValidMdFileSmartypants() throws Exception { @Test public void parseValidMdFileSuppressAllHTML() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "SUPPRESS_ALL_HTML"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("SUPPRESS_ALL_HTML"); // Test with SUPPRESS_ALL_HTML - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileSuppressAllHTML); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains(""); // Test without SUPPRESS_ALL_HTML - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileSuppressAllHTML); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("
!
!"); @@ -445,18 +439,18 @@ public void parseValidMdFileSuppressAllHTML() throws Exception { @Test public void parseValidMdFileSuppressHTMLBlocks() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "SUPPRESS_HTML_BLOCKS"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("SUPPRESS_HTML_BLOCKS"); // Test with SUPPRESS_HTML_BLOCKS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileSuppressHTMLBlocks); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains(""); // Test without SUPPRESS_HTML_BLOCKS - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileSuppressHTMLBlocks); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("
!
!"); @@ -464,18 +458,18 @@ public void parseValidMdFileSuppressHTMLBlocks() throws Exception { @Test public void parseValidMdFileSuppressInlineHTML() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "SUPPRESS_INLINE_HTML"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("SUPPRESS_INLINE_HTML"); // Test with SUPPRESS_INLINE_HTML - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileSuppressInlineHTML); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

This is the first paragraph. with inline html

"); // Test without SUPPRESS_INLINE_HTML - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileSuppressInlineHTML); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

This is the first paragraph. with inline html

"); @@ -483,11 +477,11 @@ public void parseValidMdFileSuppressInlineHTML() throws Exception { @Test public void parseValidMdFileTables() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "TABLES"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("TABLES"); // Test with TABLES - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileTables); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -503,8 +497,8 @@ public void parseValidMdFileTables() throws Exception { ); // Test without TABLES - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileTables); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -514,11 +508,11 @@ public void parseValidMdFileTables() throws Exception { @Test public void parseValidMdFileWikilinks() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "WIKILINKS"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("WIKILINKS"); // Test with WIKILINKS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileWikilinks); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -526,8 +520,8 @@ public void parseValidMdFileWikilinks() throws Exception { ); // Test without WIKILINKS - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileWikilinks); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

[[Wiki-style links]]

"); @@ -535,18 +529,18 @@ public void parseValidMdFileWikilinks() throws Exception { @Test public void parseValidMdFileAtxheaderspace() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "ATXHEADERSPACE"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("ATXHEADERSPACE"); // Test with ATXHEADERSPACE - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileAtxheaderspace); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

#Test

"); // Test without ATXHEADERSPACE - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileAtxheaderspace); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

Test

"); @@ -554,11 +548,11 @@ public void parseValidMdFileAtxheaderspace() throws Exception { @Test public void parseValidMdFileForcelistitempara() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "FORCELISTITEMPARA"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("FORCELISTITEMPARA"); // Test with FORCELISTITEMPARA - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileForcelistitempara); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -570,8 +564,8 @@ public void parseValidMdFileForcelistitempara() throws Exception { ""); // Test without FORCELISTITEMPARA - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileForcelistitempara); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -585,11 +579,11 @@ public void parseValidMdFileForcelistitempara() throws Exception { @Test public void parseValidMdFileRelaxedhrules() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "RELAXEDHRULES"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("RELAXEDHRULES"); // Test with RELAXEDHRULES - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdFileRelaxedhrules); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -607,8 +601,8 @@ public void parseValidMdFileRelaxedhrules() throws Exception { ); // Test without RELAXEDHRULES - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdFileRelaxedhrules); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -624,11 +618,11 @@ public void parseValidMdFileRelaxedhrules() throws Exception { @Test public void parseValidMdFileTasklistitems() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "TASKLISTITEMS"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("TASKLISTITEMS"); // Test with TASKLISTITEMS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdTasklistitems); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -640,8 +634,8 @@ public void parseValidMdFileTasklistitems() throws Exception { ); // Test without TASKLISTITEMS - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdTasklistitems); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -654,11 +648,11 @@ public void parseValidMdFileTasklistitems() throws Exception { @Test public void parseValidMdFileExtanchorlinks() throws Exception { - config.clearProperty(extensions); - config.setProperty(extensions, "EXTANCHORLINKS"); + config.setMarkdownExtensions(""); + config.setMarkdownExtensions("EXTANCHORLINKS"); // Test with EXTANCHORLINKS - Parser parser = new Parser(config, configFile.getPath()); + Parser parser = new Parser(config); Map map = parser.processFile(mdExtanchorlinks); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains( @@ -666,8 +660,8 @@ public void parseValidMdFileExtanchorlinks() throws Exception { ); // Test without EXTANCHORLINKS - config.clearProperty(extensions); - parser = new Parser(config, configFile.getPath()); + config.setMarkdownExtensions(""); + parser = new Parser(config); map = parser.processFile(mdExtanchorlinks); Assert.assertNotNull(map); assertThat(map.get("body").toString()).contains("

header & some formatting ~~chars~~

"); diff --git a/jbake-core/src/test/java/org/jbake/app/OvenTest.java b/jbake-core/src/test/java/org/jbake/app/OvenTest.java index f324d5cb4..5ee32cd70 100644 --- a/jbake-core/src/test/java/org/jbake/app/OvenTest.java +++ b/jbake-core/src/test/java/org/jbake/app/OvenTest.java @@ -1,67 +1,122 @@ package org.jbake.app; -import org.apache.commons.configuration.CompositeConfiguration; -import org.apache.commons.configuration.Configuration; -import org.jbake.app.ConfigUtil.Keys; +import org.jbake.app.configuration.ConfigUtil; +import org.jbake.app.configuration.DefaultJBakeConfiguration; import org.jbake.model.DocumentTypes; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import java.io.File; -import java.net.URL; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; public class OvenTest { @Rule public TemporaryFolder folder = new TemporaryFolder(); - private CompositeConfiguration config; - private File rootPath; - private File outputPath; + + private DefaultJBakeConfiguration configuration; + private File sourceFolder; + private ContentStore contentStore; @Before - public void setup() throws Exception { + public void setUp() throws Exception { // reset values to known state otherwise previous test case runs can affect the success of this test case DocumentTypes.resetDocumentTypes(); + sourceFolder = new File(this.getClass().getResource("/fixture").getPath()); + configuration = (DefaultJBakeConfiguration) new ConfigUtil().loadConfig(sourceFolder); + configuration.setDestinationFolder(folder.newFolder("output")); + configuration.setTemplateFolder(new File(sourceFolder,"freemarkerTemplates")); + } - URL sourceUrl = this.getClass().getResource("/fixture"); - rootPath = new File(sourceUrl.getFile()); - if (!rootPath.exists()) { - throw new Exception("Cannot find base path for test!"); + @After + public void tearDown() { + if (contentStore!=null && contentStore.isActive()){ + contentStore.close(); + contentStore.shutdown(); } - outputPath = folder.newFolder("destination"); - config = ConfigUtil.load(rootPath); - config.setProperty(Keys.TEMPLATE_FOLDER, "freemarkerTemplates"); } @Test - public void bakeWithRelativePaths() { - Oven oven = new Oven(rootPath, outputPath, config, true); - oven.setupPaths(); + public void bakeWithAbsolutePaths() { + configuration.setTemplateFolder( new File(sourceFolder, "freemarkerTemplates") ); + configuration.setContentFolder( new File(sourceFolder, "content") ); + configuration.setAssetFolder( new File(sourceFolder, "assets") ); + + final Oven oven = new Oven(configuration); oven.bake(); - assertThat("There shouldn't be any errors: " + oven.getErrors(), oven.getErrors().isEmpty()); + assertThat(oven.getErrors()).isEmpty(); + } + + @Test(expected = JBakeException.class) + public void shouldThrowExceptionIfSourceFolderDoesNotExist() { + configuration.setSourceFolder(new File(folder.getRoot(),"none")); + new Oven(configuration); } @Test - public void bakeWithAbsolutePaths() { - makeAbsolute(config, rootPath, Keys.TEMPLATE_FOLDER); - makeAbsolute(config, rootPath, Keys.CONTENT_FOLDER); - makeAbsolute(config, rootPath, Keys.ASSET_FOLDER); + public void shouldInstantiateNeededUtensils() throws Exception { - Oven oven = new Oven(rootPath, outputPath, config, true); - oven.setupPaths(); - oven.bake(); + configuration.setTemplateFolder( folder.newFolder("template") ); + configuration.setContentFolder( folder.newFolder("content") ); + configuration.setAssetFolder( folder.newFolder("assets") ); + + Oven oven = new Oven(configuration); + + assertThat(oven.getUtensils().getContentStore()).isNotNull(); + assertThat(oven.getUtensils().getCrawler()).isNotNull(); + assertThat(oven.getUtensils().getRenderer()).isNotNull(); + assertThat(oven.getUtensils().getAsset()).isNotNull(); + assertThat(oven.getUtensils().getConfiguration()).isEqualTo(configuration); - assertThat("There shouldn't be any errors: " + oven.getErrors(), oven.getErrors().isEmpty()); } - private void makeAbsolute(Configuration configuration, File source, String key) { - final File folder = new File(source, configuration.getString(key)); - configuration.setProperty(key, folder.getAbsolutePath()); + @Test(expected = JBakeException.class) + public void shouldInspectConfigurationDuringInstantiationFromUtils() { + configuration.setSourceFolder(new File(folder.getRoot(),"none")); + + Utensils utensils = new Utensils(); + utensils.setConfiguration(configuration); + + new Oven(utensils); } + @Test + public void shouldCrawlRenderAndCopyAssets() throws Exception { + configuration.setTemplateFolder( folder.newFolder("template") ); + configuration.setContentFolder( folder.newFolder("content") ); + configuration.setAssetFolder( folder.newFolder("assets") ); + + contentStore = spy(new ContentStore("memory", "documents"+ System.currentTimeMillis())); + + Crawler crawler = mock(Crawler.class); + Renderer renderer = mock(Renderer.class); + Asset asset = mock(Asset.class); + + Utensils utensils = new Utensils(); + utensils.setConfiguration(configuration); + utensils.setContentStore(contentStore); + utensils.setRenderer(renderer); + utensils.setCrawler(crawler); + utensils.setAsset(asset); + + Oven oven = new Oven(utensils); + + oven.bake(); + + verify(contentStore, times(1)).startup(); + verify(renderer,atLeastOnce()).renderIndex(anyString()); + verify(crawler,times(1)).crawl(); + verify(asset,times(1)).copy(); + } } diff --git a/jbake-core/src/test/java/org/jbake/app/PaginationTest.java b/jbake-core/src/test/java/org/jbake/app/PaginationTest.java index 48bcaf052..039a7ce2a 100644 --- a/jbake-core/src/test/java/org/jbake/app/PaginationTest.java +++ b/jbake-core/src/test/java/org/jbake/app/PaginationTest.java @@ -23,16 +23,14 @@ */ package org.jbake.app; -import org.apache.commons.configuration.CompositeConfiguration; import org.jbake.FakeDocumentBuilder; +import org.jbake.model.DocumentTypes; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import java.io.File; import java.util.Calendar; import java.util.Date; -import java.util.Iterator; import java.util.Locale; import static org.assertj.core.api.Assertions.assertThat; @@ -42,20 +40,18 @@ */ public class PaginationTest extends ContentStoreIntegrationTest { - @Before - public void setup() throws Exception { - CompositeConfiguration config = ConfigUtil.load(new File(this.getClass().getResource("/fixture").getFile())); - Iterator keys = config.getKeys(); - while (keys.hasNext()) { - String key = keys.next(); - if (key.startsWith("template") && key.endsWith(".file")) { - String old = (String) config.getProperty(key); - config.setProperty(key, old.substring(0, old.length() - 4) + ".ftl"); + public void setUpOwn() { + for (String docType : DocumentTypes.getDocumentTypes()) { + String fileBaseName = docType; + if (docType.equals("masterindex")) { + fileBaseName = "index"; } + config.setTemplateFileNameForDocType(docType, fileBaseName + ".ftl"); } - config.setProperty(ConfigUtil.Keys.PAGINATE_INDEX, true); - config.setProperty(ConfigUtil.Keys.POSTS_PER_PAGE, 1); + + config.setPaginateIndex(true); + config.setPostsPerPage(1); } @Test diff --git a/jbake-core/src/test/java/org/jbake/app/ParserTest.java b/jbake-core/src/test/java/org/jbake/app/ParserTest.java index f44394179..1ce8799e1 100644 --- a/jbake-core/src/test/java/org/jbake/app/ParserTest.java +++ b/jbake-core/src/test/java/org/jbake/app/ParserTest.java @@ -1,7 +1,7 @@ package org.jbake.app; -import org.apache.commons.configuration.CompositeConfiguration; -import org.apache.commons.configuration.ConfigurationException; +import org.jbake.app.configuration.ConfigUtil; +import org.jbake.app.configuration.DefaultJBakeConfiguration; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -21,7 +21,7 @@ public class ParserTest { @Rule public TemporaryFolder folder = new TemporaryFolder(); - public CompositeConfiguration config; + public DefaultJBakeConfiguration config; public Parser parser; private File rootPath; @@ -43,9 +43,9 @@ public class ParserTest { @Before public void createSampleFile() throws Exception { - rootPath = new File(this.getClass().getResource(".").getFile()); - config = ConfigUtil.load(rootPath); - parser = new Parser(config, rootPath.getPath()); + rootPath = new File(this.getClass().getResource("/fixture").getFile()); + config = (DefaultJBakeConfiguration) new ConfigUtil().loadConfig(rootPath); + parser = new Parser(config); validHTMLFile = folder.newFile("valid.html"); PrintWriter out = new PrintWriter(validHTMLFile); @@ -233,11 +233,10 @@ public void parseValidAsciiDocFileWithExampleHeaderInContent() { } @Test - public void parseValidAsciiDocFileWithoutJBakeMetaDataUsingDefaultTypeAndStatus() throws ConfigurationException { - - config.addProperty(ConfigUtil.Keys.DEFAULT_STATUS, "published"); - config.addProperty(ConfigUtil.Keys.DEFAULT_TYPE, "page"); - + public void parseValidAsciiDocFileWithoutJBakeMetaDataUsingDefaultTypeAndStatus() { + config.setDefaultStatus("published"); + config.setDefaultType("page"); + Parser parser = new Parser(config); Map map = parser.processFile(validAsciiDocFileWithoutJBakeMetaData); Assert.assertNotNull(map); Assert.assertEquals("published", map.get("status")); @@ -248,7 +247,7 @@ public void parseValidAsciiDocFileWithoutJBakeMetaDataUsingDefaultTypeAndStatus( @Test public void parseMarkdownFileWithCustomHeaderSeparator() { - config.setProperty(ConfigUtil.Keys.HEADER_SEPARATOR, customHeaderSeparator); + config.setHeaderSeparator(customHeaderSeparator); Map map = parser.processFile(validMarkdownFileWithCustomHeader); Assert.assertNotNull(map); diff --git a/jbake-core/src/test/java/org/jbake/app/configuration/ConfigUtilTest.java b/jbake-core/src/test/java/org/jbake/app/configuration/ConfigUtilTest.java new file mode 100644 index 000000000..c815f2998 --- /dev/null +++ b/jbake-core/src/test/java/org/jbake/app/configuration/ConfigUtilTest.java @@ -0,0 +1,337 @@ +package org.jbake.app.configuration; + +import ch.qos.logback.classic.spi.LoggingEvent; +import org.jbake.app.JBakeException; +import org.jbake.app.LoggingTest; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.lang.reflect.Field; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + + +public class ConfigUtilTest extends LoggingTest { + + + @Rule + public TemporaryFolder sourceFolder = new TemporaryFolder(); + + private ConfigUtil util; + + @Before + public void setUp() { + util = new ConfigUtil(); + } + + @Test + public void shouldLoadSiteHost() throws Exception { + JBakeConfiguration config = util.loadConfig(getTestResourcesAsSourceFolder()); + assertThat(config.getSiteHost()).isEqualTo("http://www.jbake.org"); + } + + @Test + public void shouldLoadADefaultConfiguration() throws Exception { + JBakeConfiguration config = util.loadConfig(getTestResourcesAsSourceFolder()); + assertDefaultPropertiesPresent(config); + } + + @Test + public void shouldLoadACustomConfiguration() throws Exception { + File customConfigFile = sourceFolder.newFile("jbake.properties"); + + BufferedWriter writer = new BufferedWriter(new FileWriter(customConfigFile)); + writer.append("test.property=12345"); + writer.close(); + + JBakeConfiguration configuration = util.loadConfig(sourceFolder.getRoot()); + + assertThat(configuration.get("test.property")).isEqualTo("12345"); + assertDefaultPropertiesPresent(configuration); + } + + @Test + public void shouldThrowAnExceptionIfSourcefolderDoesNotExist() throws Exception { + File nonExistentSourceFolder = mock(File.class); + when(nonExistentSourceFolder.getAbsolutePath()).thenReturn("/tmp/nonexistent"); + when(nonExistentSourceFolder.exists()).thenReturn(false); + + try { + util.loadConfig(nonExistentSourceFolder); + fail("Exception should be thrown, as source folder does not exist"); + } catch (JBakeException e) { + + assertThat(e.getMessage()).isEqualTo("The given source folder '/tmp/nonexistent' does not exist."); + } + } + + @Test + public void shouldAddSourcefolderToConfiguration() throws Exception { + + File sourceFolder = getTestResourcesAsSourceFolder(); + JBakeConfiguration config = util.loadConfig(sourceFolder); + + assertThat(config.getSourceFolder()).isEqualTo(sourceFolder); + + } + + @Test + public void shouldThrowAnExceptionIfSourcefolderIsNotADirectory() throws Exception { + + File sourceFolder = mock(File.class); + when(sourceFolder.exists()).thenReturn(true); + when(sourceFolder.isDirectory()).thenReturn(false); + + try { + util.loadConfig(sourceFolder); + fail("Exception should be thrown if given source folder is not a directory."); + } + catch ( JBakeException e ) { + assertThat( e.getMessage() ).isEqualTo("The given source folder is not a directory."); + } + + } + + @Test + public void shouldReturnDestinationFolderFromConfiguration() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + File expectedDestinationFolder = new File(sourceFolder,"output"); + JBakeConfiguration config = util.loadConfig(sourceFolder); + + assertThat(config.getDestinationFolder()).isEqualTo(expectedDestinationFolder); + } + + @Test + public void shouldReturnAssetFolderFromConfiguration() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + File expectedDestinationFolder = new File(sourceFolder,"assets"); + JBakeConfiguration config = util.loadConfig(sourceFolder); + + assertThat(config.getAssetFolder()).isEqualTo(expectedDestinationFolder); + } + + @Test + public void shouldReturnTemplateFolderFromConfiguration() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + File expectedDestinationFolder = new File(sourceFolder,"templates"); + JBakeConfiguration config = util.loadConfig(sourceFolder); + + assertThat(config.getTemplateFolder()).isEqualTo(expectedDestinationFolder); + } + + @Test + public void shouldReturnContentFolderFromConfiguration() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + File expectedDestinationFolder = new File(sourceFolder,"content"); + JBakeConfiguration config = util.loadConfig(sourceFolder); + + assertThat(config.getContentFolder()).isEqualTo(expectedDestinationFolder); + } + + @Test + public void shouldGetTemplateFileDoctype() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + File expectedTemplateFile = new File(sourceFolder, "templates/index.ftl"); + JBakeConfiguration config = util.loadConfig(sourceFolder); + + File templateFile = config.getTemplateFileByDocType("masterindex"); + + assertThat(templateFile).isEqualTo( expectedTemplateFile ); + } + + @Test + public void shouldLogWarningIfDocumentTypeNotFound() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + JBakeConfiguration config = util.loadConfig(sourceFolder); + + config.getTemplateFileByDocType("none"); + + verify(mockAppender, times(1)).doAppend(captorLoggingEvent.capture()); + + LoggingEvent loggingEvent = captorLoggingEvent.getValue(); + + assertThat(loggingEvent.getMessage()).isEqualTo("Cannot find configuration key '{}' for document type '{}'"); + + } + + @Test + public void shouldGetTemplateOutputExtension() throws Exception { + + String docType = "masterindex"; + File sourceFolder = getTestResourcesAsSourceFolder(); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(sourceFolder); + config.setTemplateExtensionForDocType(docType, ".xhtml"); + + String extension = config.getOutputExtensionByDocType(docType); + + assertThat(extension).isEqualTo(".xhtml"); + } + + @Test + public void shouldGetMarkdownExtensionsAsList() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(sourceFolder); + + List markdownExtensions = config.getMarkdownExtensions(); + + assertThat(markdownExtensions).containsExactly("HARDWRAPS", "AUTOLINKS", "FENCED_CODE_BLOCKS", "DEFINITIONS"); + } + + @Test + public void shouldReturnConfiguredDocTypes() throws Exception { + + File sourceFolder = getTestResourcesAsSourceFolder(); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(sourceFolder); + + List docTypes = config.getDocumentTypes(); + + assertThat(docTypes).containsExactly("allcontent", "masterindex","feed", "archive", "tag", "tagsindex", "sitemap", "post", "page"); + + } + + @Test + public void shouldReturnAListOfAsciidoctorOptionsKeys() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(sourceFolder); + config.setProperty("asciidoctor.option.requires", "asciidoctor-diagram"); + config.setProperty("asciidoctor.option.template_dirs", "src/template1,src/template2"); + + List options = config.getAsciidoctorOptionKeys(); + + assertThat(options).contains("requires","template_dirs"); + } + + @Test + public void shouldReturnAnAsciidoctorOption() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(sourceFolder); + config.setProperty("asciidoctor.option.requires", "asciidoctor-diagram"); + config.setProperty("asciidoctor.option.template_dirs", "src/template1,src/template2"); + + Object option = config.getAsciidoctorOption("requires"); + + assertThat(String.valueOf(option)).contains("asciidoctor-diagram"); + } + + @Test + public void shouldReturnAnAsciidoctorOptionWithAListValue() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(sourceFolder); + config.setProperty("asciidoctor.option.requires", "asciidoctor-diagram"); + config.setProperty("asciidoctor.option.template_dirs", "src/template1,src/template2"); + + Object option = config.getAsciidoctorOption("template_dirs"); + + assertTrue(option instanceof List); + assertThat((List)option).contains("src/template1","src/template2"); + } + + @Test + public void shouldReturnEmptyStringIfOptionNotAvailable() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(sourceFolder); + + Object option = config.getAsciidoctorOption("template_dirs"); + + assertThat(String.valueOf(option)).isEmpty(); + } + + @Test + public void shouldLogAWarningIfAsciidocOptionCouldNotBeFound() throws Exception { + File sourceFolder = getTestResourcesAsSourceFolder(); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(sourceFolder); + + config.getAsciidoctorOption("template_dirs"); + + verify(mockAppender, times(1)).doAppend(captorLoggingEvent.capture()); + + LoggingEvent loggingEvent = captorLoggingEvent.getValue(); + + assertThat(loggingEvent.getMessage()).isEqualTo("Cannot find asciidoctor option '{}.{}'"); + } + + @Test + public void shouldHandleNonExistingFiles() throws Exception { + + File source = getTestResourcesAsSourceFolder(); + File expectedTemplateFolder = new File(source,"templates"); + File expectedAssetFolder = new File(source,"assets"); + File expectedContentFolder = new File(source,"content"); + File expectedDestinationFolder = new File(source,"output"); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(source); + + config.setTemplateFolder(null); + config.setAssetFolder(null); + config.setContentFolder(null); + config.setDestinationFolder(null); + + File templateFolder = config.getTemplateFolder(); + File assetFolder = config.getAssetFolder(); + File contentFolder = config.getContentFolder(); + File destinationFolder = config.getDestinationFolder(); + + assertThat(templateFolder).isEqualTo(expectedTemplateFolder); + assertThat(assetFolder).isEqualTo(expectedAssetFolder); + assertThat(contentFolder).isEqualTo(expectedContentFolder); + assertThat(destinationFolder).isEqualTo(expectedDestinationFolder); + } + + @Test + public void shouldHandleCustomTemplateFolder() throws Exception { + File source = getTestResourcesAsSourceFolder(); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(source); + + config.setTemplateFolder(sourceFolder.newFolder("my_custom_templates")); + + assertThat(config.getTemplateFolderName()).isEqualTo("my_custom_templates"); + } + + @Test + public void shouldHandleCustomContentFolder() throws Exception { + File source = getTestResourcesAsSourceFolder(); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(source); + + config.setContentFolder(sourceFolder.newFolder("my_custom_content")); + + assertThat(config.getContentFolderName()).isEqualTo("my_custom_content"); + } + + @Test + public void shouldHandleCustomAssetFolder() throws Exception { + File source = getTestResourcesAsSourceFolder(); + DefaultJBakeConfiguration config = (DefaultJBakeConfiguration) util.loadConfig(source); + + config.setAssetFolder(sourceFolder.newFolder("my_custom_asset")); + + assertThat(config.getAssetFolderName()).isEqualTo("my_custom_asset"); + } + + private void assertDefaultPropertiesPresent(JBakeConfiguration config) throws IllegalAccessException { + for(Field field : JBakeConfiguration.class.getFields() ) { + + if (field.isAccessible()) { + String key = (String) field.get(""); + System.out.println("Key: " + key); + assertThat(config.get(key)).isNotNull(); + } + } + } + + //TODO: move to test util. use in all tests... + private File getTestResourcesAsSourceFolder() { + return new File(this.getClass().getResource("/fixture").getFile()); + } + +} diff --git a/jbake-core/src/test/java/org/jbake/app/configuration/JBakeConfigurationFactoryTest.java b/jbake-core/src/test/java/org/jbake/app/configuration/JBakeConfigurationFactoryTest.java new file mode 100644 index 000000000..0c7ba405d --- /dev/null +++ b/jbake-core/src/test/java/org/jbake/app/configuration/JBakeConfigurationFactoryTest.java @@ -0,0 +1,54 @@ +package org.jbake.app.configuration; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JBakeConfigurationFactoryTest { + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + @Test + public void shouldReturnDefaultConfigurationWithDefaultFolders() throws Exception { + File sourceFolder = folder.getRoot(); + File destinationFolder = folder.newFolder("output"); + File templateFolder = new File(folder.getRoot(), "templates"); + File assetFolder = new File(folder.getRoot(), "assets"); + + JBakeConfiguration configuration = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(sourceFolder, destinationFolder, true); + + assertThat(configuration.getSourceFolder()).isEqualTo(sourceFolder); + assertThat(configuration.getDestinationFolder()).isEqualTo(destinationFolder); + assertThat(configuration.getTemplateFolder()).isEqualTo(templateFolder); + assertThat(configuration.getAssetFolder()).isEqualTo(assetFolder); + assertThat(configuration.getClearCache()).isEqualTo(true); + } + + @Test + public void shouldReturnADefaultConfigurationWithSitehost() throws Exception { + File sourceFolder = folder.getRoot(); + File destinationFolder = folder.newFolder("output"); + String siteHost = "http://www.jbake.org"; + + JBakeConfiguration configuration = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(sourceFolder, destinationFolder, true); + + assertThat(configuration.getSiteHost()).isEqualTo(siteHost); + } + + @Test + public void shouldReturnAJettyConfiguration() throws Exception { + File sourceFolder = folder.getRoot(); + File destinationFolder = folder.newFolder("output"); + String siteHost = "http://localhost:8820"; + + JBakeConfiguration configuration = new JBakeConfigurationFactory().createJettyJbakeConfiguration(sourceFolder, destinationFolder, true); + + assertThat(configuration.getSiteHost()).isEqualTo(siteHost); + } + +} \ No newline at end of file diff --git a/jbake-core/src/test/java/org/jbake/app/configuration/JBakeConfigurationInspectorTest.java b/jbake-core/src/test/java/org/jbake/app/configuration/JBakeConfigurationInspectorTest.java new file mode 100644 index 000000000..e7c98f37c --- /dev/null +++ b/jbake-core/src/test/java/org/jbake/app/configuration/JBakeConfigurationInspectorTest.java @@ -0,0 +1,195 @@ +package org.jbake.app.configuration; + +import ch.qos.logback.classic.spi.LoggingEvent; +import org.jbake.app.JBakeException; +import org.jbake.app.LoggingTest; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class JBakeConfigurationInspectorTest extends LoggingTest { + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + @Test + public void shouldThrowExceptionIfSourceFolderDoesNotExist() throws Exception { + File nonExistentFile = new File(folder.getRoot(), "nofolder"); + JBakeConfiguration configuration = mock(JBakeConfiguration.class); + when(configuration.getSourceFolder()).thenReturn(nonExistentFile); + + JBakeConfigurationInspector inspector = new JBakeConfigurationInspector(configuration); + + try { + inspector.inspect(); + fail("should throw a JBakeException"); + } catch (JBakeException e) { + assertThat(e.getMessage()).isEqualTo("Error: Source folder must exist: " + nonExistentFile.getAbsolutePath()); + } + } + + @Test + public void shouldThrowExceptionIfSourceFolderIsNotReadable() throws Exception { + File nonReadableFile = mock(File.class); + when(nonReadableFile.exists()).thenReturn(true); + when(nonReadableFile.isDirectory()).thenReturn(true); + when(nonReadableFile.canRead()).thenReturn(false); + + JBakeConfiguration configuration = mock(JBakeConfiguration.class); + when(configuration.getSourceFolder()).thenReturn(nonReadableFile); + + JBakeConfigurationInspector inspector = new JBakeConfigurationInspector(configuration); + + try { + inspector.inspect(); + fail("should throw a JBakeException"); + } catch (JBakeException e) { + assertThat(e.getMessage()).isEqualTo("Error: Source folder is not readable: " + nonReadableFile.getAbsolutePath()); + } + } + + @Test + public void shouldThrowExceptionIfTemplateFolderDoesNotExist() throws Exception { + String templateFolderName = "template"; + File expectedFolder = new File(folder.getRoot(), templateFolderName); + JBakeConfiguration configuration = mock(JBakeConfiguration.class); + when(configuration.getSourceFolder()).thenReturn(folder.getRoot()); + when(configuration.getTemplateFolder()).thenReturn(expectedFolder); + when(configuration.getTemplateFolderName()).thenReturn(templateFolderName); + + JBakeConfigurationInspector inspector = new JBakeConfigurationInspector(configuration); + + try { + inspector.inspect(); + fail("should throw a JBakeException"); + } catch (JBakeException e) { + assertThat(e.getMessage()).isEqualTo("Error: Required folder cannot be found! Expected to find [" + templateFolderName + "] at: " + expectedFolder.getAbsolutePath()); + } + + } + + @Test + public void shouldThrowExceptionIfContentFolderDoesNotExist() throws Exception { + String contentFolderName = "content"; + String templateFolderName = "template"; + File templateFolder = folder.newFolder(templateFolderName); + File contentFolder = new File(folder.getRoot(), contentFolderName); + + JBakeConfiguration configuration = mock(JBakeConfiguration.class); + when(configuration.getSourceFolder()).thenReturn(folder.getRoot()); + when(configuration.getTemplateFolder()).thenReturn(templateFolder); + when(configuration.getTemplateFolderName()).thenReturn(templateFolderName); + when(configuration.getContentFolder()).thenReturn(contentFolder); + when(configuration.getContentFolderName()).thenReturn(contentFolderName); + + JBakeConfigurationInspector inspector = new JBakeConfigurationInspector(configuration); + + try { + inspector.inspect(); + fail("should throw a JBakeException"); + } catch (JBakeException e) { + assertThat(e.getMessage()).isEqualTo("Error: Required folder cannot be found! Expected to find [" + contentFolderName + "] at: " + contentFolder.getAbsolutePath()); + } + } + + @Test + public void shouldCreateDestinationFolderIfNotExists() throws Exception { + String contentFolderName = "content"; + String templateFolderName = "template"; + String destinationFolderName = "output"; + + File templateFolder = folder.newFolder(templateFolderName); + File contentFolder = folder.newFolder(contentFolderName); + File destinationFolder = new File(folder.getRoot(), destinationFolderName); + + JBakeConfiguration configuration = mock(JBakeConfiguration.class); + when(configuration.getSourceFolder()).thenReturn(folder.getRoot()); + when(configuration.getTemplateFolder()).thenReturn(templateFolder); + when(configuration.getTemplateFolderName()).thenReturn(templateFolderName); + when(configuration.getContentFolder()).thenReturn(contentFolder); + when(configuration.getContentFolderName()).thenReturn(contentFolderName); + when(configuration.getDestinationFolder()).thenReturn(destinationFolder); + when(configuration.getAssetFolder()).thenReturn(destinationFolder); + + JBakeConfigurationInspector inspector = new JBakeConfigurationInspector(configuration); + + inspector.inspect(); + + assertThat(destinationFolder).exists(); + } + + @Test + public void shouldThrowExceptionIfDestinationFolderNotWritable() throws Exception { + String contentFolderName = "content"; + String templateFolderName = "template"; + + File templateFolder = folder.newFolder(templateFolderName); + File contentFolder = folder.newFolder(contentFolderName); + File destinationFolder = mock(File.class); + when(destinationFolder.exists()).thenReturn(true); + + JBakeConfiguration configuration = mock(JBakeConfiguration.class); + when(configuration.getSourceFolder()).thenReturn(folder.getRoot()); + when(configuration.getTemplateFolder()).thenReturn(templateFolder); + when(configuration.getTemplateFolderName()).thenReturn(templateFolderName); + when(configuration.getContentFolder()).thenReturn(contentFolder); + when(configuration.getContentFolderName()).thenReturn(contentFolderName); + when(configuration.getDestinationFolder()).thenReturn(destinationFolder); + + JBakeConfigurationInspector inspector = new JBakeConfigurationInspector(configuration); + + try { + inspector.inspect(); + fail("should throw JBakeException"); + } catch (JBakeException e) { + assertThat(e.getMessage()).contains("Error: Destination folder is not writable:"); + } + + } + + @Test + public void shouldLogWarningIfAssetFolderDoesNotExist() throws Exception { + String contentFolderName = "content"; + String templateFolderName = "template"; + String destinationFolderName = "output"; + String assetFolderName = "assets"; + + File templateFolder = folder.newFolder(templateFolderName); + File contentFolder = folder.newFolder(contentFolderName); + File destinationFolder = folder.newFolder(destinationFolderName); + File assetFolder = new File(folder.getRoot(), assetFolderName); + + JBakeConfiguration configuration = mock(JBakeConfiguration.class); + when(configuration.getSourceFolder()).thenReturn(folder.getRoot()); + when(configuration.getTemplateFolder()).thenReturn(templateFolder); + when(configuration.getTemplateFolderName()).thenReturn(templateFolderName); + when(configuration.getContentFolder()).thenReturn(contentFolder); + when(configuration.getContentFolderName()).thenReturn(contentFolderName); + when(configuration.getDestinationFolder()).thenReturn(destinationFolder); + when(configuration.getAssetFolder()).thenReturn(assetFolder); + + + JBakeConfigurationInspector inspector = new JBakeConfigurationInspector(configuration); + + inspector.inspect(); + + verify(mockAppender, times(1)).doAppend(captorLoggingEvent.capture()); + + LoggingEvent loggingEvent = captorLoggingEvent.getValue(); + + assertThat(loggingEvent.getMessage()).isEqualTo("No asset folder '{}' was found!"); + assertThat(loggingEvent.getFormattedMessage()).isEqualTo("No asset folder '" + assetFolder.getAbsolutePath() + "' was found!"); + + } + + +} \ No newline at end of file diff --git a/jbake-core/src/test/java/org/jbake/app/template/AbstractTemplateEngineRenderingTest.java b/jbake-core/src/test/java/org/jbake/app/template/AbstractTemplateEngineRenderingTest.java index 92d41315a..b85581a38 100644 --- a/jbake-core/src/test/java/org/jbake/app/template/AbstractTemplateEngineRenderingTest.java +++ b/jbake-core/src/test/java/org/jbake/app/template/AbstractTemplateEngineRenderingTest.java @@ -23,13 +23,9 @@ */ package org.jbake.app.template; -import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.io.FileUtils; -import org.jbake.app.ConfigUtil; -import org.jbake.app.ContentStore; import org.jbake.app.ContentStoreIntegrationTest; import org.jbake.app.Crawler; -import org.jbake.app.DBUtil; import org.jbake.app.Parser; import org.jbake.app.Renderer; import org.jbake.model.DocumentTypes; @@ -38,16 +34,12 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.TemporaryFolder; import java.io.File; -import java.net.URL; import java.nio.charset.Charset; import java.util.Arrays; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -62,12 +54,9 @@ public abstract class AbstractTemplateEngineRenderingTest extends ContentStoreIn protected final String templateDir; protected final String templateExtension; protected final Map> outputStrings = new HashMap<>(); - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - protected File sourceFolder; + protected File destinationFolder; protected File templateFolder; - protected CompositeConfiguration config; protected Renderer renderer; protected Locale currentLocale; private Parser parser; @@ -85,35 +74,30 @@ public void setup() throws Exception { ModelExtractorsDocumentTypeListener listener = new ModelExtractorsDocumentTypeListener(); DocumentTypes.addListener(listener); - URL sourceUrl = this.getClass().getResource("/fixture"); - - sourceFolder = new File(sourceUrl.getFile()); - if (!sourceFolder.exists()) { - throw new Exception("Cannot find sample data structure!"); - } - - destinationFolder = folder.getRoot(); - templateFolder = new File(sourceFolder, templateDir); if (!templateFolder.exists()) { throw new Exception("Cannot find template folder!"); } - config = ConfigUtil.load(new File(this.getClass().getResource("/fixture").getFile())); - Iterator keys = config.getKeys(); - while (keys.hasNext()) { - String key = keys.next(); - if (key.startsWith("template") && key.endsWith(".file")) { - String old = (String) config.getProperty(key); - config.setProperty(key, old.substring(0, old.length() - 4) + "." + templateExtension); + destinationFolder = folder.getRoot(); + config.setDestinationFolder(destinationFolder); + config.setTemplateFolder(templateFolder); + + for (String docType : DocumentTypes.getDocumentTypes()) { + File templateFile = config.getTemplateFileByDocType(docType); + + if (templateFile != null) { + String fileName = templateFile.getName(); + String fileBaseName = fileName.substring(0, fileName.lastIndexOf(".")); + config.setTemplateFileNameForDocType(docType, fileBaseName + "." + templateExtension); } } - Assert.assertEquals(".html", config.getString(ConfigUtil.Keys.OUTPUT_EXTENSION)); + Assert.assertEquals(".html", config.getOutputExtension()); - Crawler crawler = new Crawler(db, sourceFolder, config); - crawler.crawl(new File(sourceFolder.getPath() + File.separator + "content")); - parser = new Parser(config, sourceFolder.getPath()); - renderer = new Renderer(db, destinationFolder, templateFolder, config); + Crawler crawler = new Crawler(db, config); + crawler.crawl(); + parser = new Parser(config); + renderer = new Renderer(db, config); setupExpectedOutputStrings(); } @@ -151,6 +135,10 @@ private void setupExpectedOutputStrings() { "First Post")); + outputStrings.put("tags-index", Arrays.asList("

Tags

", + "

blog", + "3

")); + outputStrings.put("sitemap", Arrays.asList("blog/2013/second-post.html", "blog/2012/first-post.html", "papers/published-paper.html")); @@ -258,10 +246,24 @@ public void renderTags() throws Exception { } } + @Test + public void renderTagsIndex() throws Exception { + config.setRenderTagsIndex(true); + + renderer.renderTags("tags"); + File outputFile = new File(destinationFolder + File.separator + "tags" + File.separator + "index.html"); + Assert.assertTrue(outputFile.exists()); + String output = FileUtils.readFileToString(outputFile, Charset.defaultCharset()); + for (String string : getOutputStrings("tags-index")) { + assertThat(output).contains(string); + } + + } + @Test public void renderSitemap() throws Exception { DocumentTypes.addDocumentType("paper"); - DBUtil.updateSchema(db); + db.updateSchema(); renderer.renderSitemap("sitemap.xml"); File outputFile = new File(destinationFolder, "sitemap.xml"); @@ -283,8 +285,8 @@ protected List getOutputStrings(String type) { @Test public void checkDbTemplateModelIsPopulated() throws Exception { - config.setProperty(ConfigUtil.Keys.PAGINATE_INDEX, true); - config.setProperty(ConfigUtil.Keys.POSTS_PER_PAGE, 1); + config.setPaginateIndex(true); + config.setPostsPerPage(1); outputStrings.put("dbSpan", Arrays.asList("3")); diff --git a/jbake-core/src/test/java/org/jbake/app/template/FreemarkerTemplateEngineRenderingTest.java b/jbake-core/src/test/java/org/jbake/app/template/FreemarkerTemplateEngineRenderingTest.java index a5d7d27d8..908dafad3 100644 --- a/jbake-core/src/test/java/org/jbake/app/template/FreemarkerTemplateEngineRenderingTest.java +++ b/jbake-core/src/test/java/org/jbake/app/template/FreemarkerTemplateEngineRenderingTest.java @@ -24,7 +24,6 @@ package org.jbake.app.template; import org.apache.commons.io.FileUtils; -import org.jbake.app.ConfigUtil.Keys; import org.junit.Test; import java.io.File; @@ -46,8 +45,8 @@ public FreemarkerTemplateEngineRenderingTest() { @Test public void renderPaginatedIndex() throws Exception { - config.setProperty(Keys.PAGINATE_INDEX, true); - config.setProperty(Keys.POSTS_PER_PAGE, 1); + config.setPaginateIndex(true); + config.setPostsPerPage(1); outputStrings.put("index", Arrays.asList( "\">Previous", @@ -67,8 +66,8 @@ public void renderPaginatedIndex() throws Exception { @Test public void shouldFallbackToRenderSingleIndexIfNoPostArePresent() throws Exception { - config.setProperty(Keys.PAGINATE_INDEX, true); - config.setProperty(Keys.POSTS_PER_PAGE, 1); + config.setPaginateIndex(true); + config.setPostsPerPage(1); db.deleteAllByDocType("post"); diff --git a/jbake-core/src/test/java/org/jbake/app/template/GroovyMarkupTemplateEngineRenderingTest.java b/jbake-core/src/test/java/org/jbake/app/template/GroovyMarkupTemplateEngineRenderingTest.java index 782839cb9..787fc08ab 100644 --- a/jbake-core/src/test/java/org/jbake/app/template/GroovyMarkupTemplateEngineRenderingTest.java +++ b/jbake-core/src/test/java/org/jbake/app/template/GroovyMarkupTemplateEngineRenderingTest.java @@ -2,7 +2,6 @@ import org.apache.commons.io.FileUtils; import org.jbake.app.Crawler; -import org.jbake.app.DBUtil; import org.jbake.app.Parser; import org.jbake.app.Renderer; import org.jbake.model.DocumentTypes; @@ -55,14 +54,14 @@ public GroovyMarkupTemplateEngineRenderingTest() { @Test public void renderCustomTypePaper() throws Exception { // setup - config.setProperty("template.paper.file", "paper." + templateExtension); + config.setTemplateFileNameForDocType("paper", "paper." + templateExtension); DocumentTypes.addDocumentType("paper"); - DBUtil.updateSchema(db); + db.updateSchema(); - Crawler crawler = new Crawler(db, sourceFolder, config); - crawler.crawl(new File(sourceFolder.getPath() + File.separator + "content")); - Parser parser = new Parser(config, sourceFolder.getPath()); - Renderer renderer = new Renderer(db, destinationFolder, templateFolder, config); + Crawler crawler = new Crawler(db, config); + crawler.crawl(); + Parser parser = new Parser(config); + Renderer renderer = new Renderer(db, config); String filename = "published-paper.html"; File sampleFile = new File(sourceFolder.getPath() + File.separator + "content" + File.separator + "papers" + File.separator + filename); diff --git a/jbake-core/src/test/java/org/jbake/app/template/GroovyTemplateEngineRenderingTest.java b/jbake-core/src/test/java/org/jbake/app/template/GroovyTemplateEngineRenderingTest.java index 37d4958a0..769415bfc 100644 --- a/jbake-core/src/test/java/org/jbake/app/template/GroovyTemplateEngineRenderingTest.java +++ b/jbake-core/src/test/java/org/jbake/app/template/GroovyTemplateEngineRenderingTest.java @@ -23,8 +23,6 @@ */ package org.jbake.app.template; -import java.util.Arrays; - /** * * @author jdlee diff --git a/jbake-core/src/test/java/org/jbake/app/template/JadeTemplateEngineRenderingTest.java b/jbake-core/src/test/java/org/jbake/app/template/JadeTemplateEngineRenderingTest.java index 64504ea8f..4c0cb946f 100644 --- a/jbake-core/src/test/java/org/jbake/app/template/JadeTemplateEngineRenderingTest.java +++ b/jbake-core/src/test/java/org/jbake/app/template/JadeTemplateEngineRenderingTest.java @@ -1,7 +1,5 @@ package org.jbake.app.template; -import java.util.Arrays; - public class JadeTemplateEngineRenderingTest extends AbstractTemplateEngineRenderingTest { public JadeTemplateEngineRenderingTest() { diff --git a/jbake-core/src/test/java/org/jbake/app/template/ThymeleafTemplateEngineRenderingTest.java b/jbake-core/src/test/java/org/jbake/app/template/ThymeleafTemplateEngineRenderingTest.java index 5573fd85b..6a11d1bc1 100644 --- a/jbake-core/src/test/java/org/jbake/app/template/ThymeleafTemplateEngineRenderingTest.java +++ b/jbake-core/src/test/java/org/jbake/app/template/ThymeleafTemplateEngineRenderingTest.java @@ -23,8 +23,6 @@ */ package org.jbake.app.template; -import java.util.Arrays; - /** * * @author jdlee diff --git a/jbake-core/src/test/java/org/jbake/launcher/LaunchOptionsTest.java b/jbake-core/src/test/java/org/jbake/launcher/LaunchOptionsTest.java index a6699ea54..78d8060f4 100644 --- a/jbake-core/src/test/java/org/jbake/launcher/LaunchOptionsTest.java +++ b/jbake-core/src/test/java/org/jbake/launcher/LaunchOptionsTest.java @@ -106,8 +106,8 @@ public void bakeNoArgs() throws Exception { assertThat(res.isRunServer()).isFalse(); assertThat(res.isInit()).isFalse(); assertThat(res.isBake()).isFalse(); - assertThat(res.getSource().getPath()).isEqualTo("."); - assertThat(res.getDestination()).isNull(); + assertThat(res.getSource().getPath()).isEqualTo(System.getProperty("user.dir")); + assertThat(res.getDestination().getPath()).isEqualTo(System.getProperty("user.dir") + File.separator + "output"); } @Test diff --git a/jbake-core/src/test/java/org/jbake/launcher/MainTest.java b/jbake-core/src/test/java/org/jbake/launcher/MainTest.java index 1a448e888..12e320f2a 100644 --- a/jbake-core/src/test/java/org/jbake/launcher/MainTest.java +++ b/jbake-core/src/test/java/org/jbake/launcher/MainTest.java @@ -1,8 +1,14 @@ package org.jbake.launcher; -import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.commons.configuration.ConfigurationException; +import org.jbake.app.configuration.ConfigUtil; +import org.jbake.app.configuration.DefaultJBakeConfiguration; +import org.jbake.app.configuration.JBakeConfigurationFactory; +import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; @@ -10,113 +16,133 @@ import org.mockito.junit.MockitoJUnitRunner; import java.io.File; -import java.util.HashMap; -import java.util.Map; +import java.io.IOException; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith( MockitoJUnitRunner.class ) public class MainTest { private Main main; + @Rule public TemporaryFolder folder = new TemporaryFolder(); @Mock private Baker mockBaker; @Mock private JettyServer mockJetty; @Mock private BakeWatcher mockWatcher; + @Mock private ConfigUtil configUtil; + @Mock private JBakeConfigurationFactory factory; + + private String workingdir; @Before - public void setup() { + public void setUp() { this.main = new Main(mockBaker, mockJetty, mockWatcher); + workingdir = System.getProperty("user.dir"); + factory.setConfigUtil(configUtil); + main.setJBakeConfigurationFactory(factory); + } + + @After + public void tearDown() { + System.setProperty("user.dir", workingdir); } @Test - public void launchJetty() { + public void launchJetty() throws Exception { + File currentWorkingdir = folder.newFolder("src", "jbake"); + File expectedOutput = new File(currentWorkingdir,"output"); + mockJettyConfiguration(currentWorkingdir,expectedOutput); + String[] args = {"-s"}; main.run(args); - verify(mockJetty).run("output","8820"); + verify(mockJetty).run(expectedOutput.getPath(),"8820"); } @Test - public void launchBakeAndJetty() { + public void launchBakeAndJetty() throws Exception { + File sourceFolder = folder.newFolder("src", "jbake"); + File expectedOutput = new File(sourceFolder, "output"); + mockJettyConfiguration(sourceFolder, expectedOutput); + String[] args = {"-b", "-s"}; main.run(args); - verify(mockJetty).run("output","8820"); + verify(mockJetty).run(expectedOutput.getPath(),"8820"); } @Test - public void launchBakeAndJettyWithCustomDirForJetty() { - String path = "src" + File.separator + "jbake"; - String[] args = {"-b", "-s", path}; + public void launchBakeAndJettyWithCustomDirForJetty() throws ConfigurationException, IOException { + mockValidSourceFolder("src/jbake", true); + String expectedRunPath = "src" + File.separator + "jbake" + File.separator + "output"; + + String[] args = {"-b", "-s", "src/jbake"}; main.run(args); - verify(mockJetty).run(path,"8820"); + verify(mockJetty).run(expectedRunPath,"8820"); } @Test - public void launchJettyWithCustomSourceDir() { - String path = "src" + File.separator + "jbake"; - String[] args = {path, "-s"}; - main.run(args); + public void launchJettyWithCustomServerSourceDir() throws Exception { + File sourceFolder = mockValidSourceFolder("build/jbake", true); - verify(mockJetty).run(path,"8820"); - } - - @Test - public void launchJettyWithCustomServerSourceDir() { - String path = "build" + File.separator + "jbake"; - String[] args = {"-s", path}; + String[] args = {sourceFolder.getPath(), "-s"}; main.run(args); - verify(mockJetty).run(path,"8820"); + verify(mockJetty).run(sourceFolder.getPath(),"8820"); } - // Documentation states these two commands will define the custom output, but the LaunchOptions file isn't setup for that. - // I have written this test to define the existing functionality of the code and not that defined in docs. + // ATTENTION + // There ist no extra argument for -s option. you can call jbake -s /customsource or jbake /customsource -s @Test - public void launchJettyWithCustomDestinationDir() { - String path = "build" + File.separator + "jbake"; - String[] args = {"-s", path}; + public void launchJettyWithCustomDestinationDir() throws Exception { + File sourceFolder = mockValidSourceFolder("src/jbake", true); + + String[] args = {"-s", sourceFolder.getPath()}; main.run(args); - verify(mockJetty).run(path,"8820"); + verify(mockJetty).run(sourceFolder.getPath(),"8820"); } @Test - public void launchJettyWithCustomSrcAndDestDir() { - String path = "build" + File.separator + "jbake"; - String[] args = {"jbake", path, "-s"}; + public void launchJettyWithCustomSrcAndDestDir() throws Exception { + File sourceFolder = mockValidSourceFolder("src/jbake", true); + + final File exampleOutput = folder.newFolder("build","jbake"); + + String[] args = {sourceFolder.getPath(), exampleOutput.getPath(), "-s"}; main.run(args); - verify(mockJetty).run(path,"8820"); + verify(mockJetty).run(exampleOutput.getPath(),"8820"); } @Test - public void launchJettyWithCustomDestViaConfig() throws CmdLineException { - final String path = "build" + File.separator + "jbake"; + public void launchJettyWithCustomDestViaConfig() throws Exception { String[] args = {"-s"}; - Map properties = new HashMap(){{ - put("destination.folder", path); - }}; - main.run(stubOptions(args), stubConfig(properties)); + final File exampleOutput = folder.newFolder("build","jbake"); + DefaultJBakeConfiguration configuration = stubConfig(); + configuration.setDestinationFolder(exampleOutput); - verify(mockJetty).run(path,"8820"); + main.run(stubOptions(args), configuration); + + verify(mockJetty).run(exampleOutput.getPath(),"8820"); } @Test - public void launchJettyWithCmdlineOverridingProperties() throws CmdLineException { - String buildPath = "build" + File.separator + "jbake"; - String sourcePath = "src" + File.separator + "jbake"; - final String targetPath = "target" + File.separator + "jbake"; - - String[] args = {sourcePath, buildPath, "-s"}; - Map properties = new HashMap(){{ - put("destination.folder", targetPath); - }}; - main.run(stubOptions(args), stubConfig(properties)); - - verify(mockJetty).run(buildPath,"8820"); + public void launchJettyWithCmdlineOverridingProperties() throws Exception { + File sourceFolder = mockValidSourceFolder("src/jbake", true); + final File expectedOutput = folder.newFolder("build","jbake"); + final File configTarget = folder.newFolder("target","jbake"); + + String[] args = {sourceFolder.getPath(), expectedOutput.getPath(), "-s"}; + DefaultJBakeConfiguration configuration = stubConfig(); + configuration.setDestinationFolder(configTarget); + main.run(stubOptions(args), configuration); + + verify(mockJetty).run(expectedOutput.getPath(),"8820"); } private LaunchOptions stubOptions(String[] args) throws CmdLineException { @@ -126,12 +152,35 @@ private LaunchOptions stubOptions(String[] args) throws CmdLineException { return res; } - private CompositeConfiguration stubConfig(Map properties) { - CompositeConfiguration config = new CompositeConfiguration(); - config.addProperty("server.port", "8820"); - for (Map.Entry pair : properties.entrySet()) { - config.addProperty(pair.getKey(), pair.getValue()); + private DefaultJBakeConfiguration stubConfig() throws ConfigurationException { + File sourceFolder = new File(this.getClass().getResource("/fixture").getFile()); + DefaultJBakeConfiguration configuration = (DefaultJBakeConfiguration) new ConfigUtil().loadConfig( sourceFolder ); + configuration.setServerPort(8820); + return configuration; + } + + private File mockValidSourceFolder(String sourcePath, boolean withJetty) throws IOException, ConfigurationException { + File mockedSourceFolder = folder.newFolder(sourcePath.split("/")); + if ( withJetty ) { + mockJettyConfiguration(mockedSourceFolder, mockedSourceFolder); + } + else { + mockDefaultJbakeConfiguration(mockedSourceFolder); } - return config; + return mockedSourceFolder; + } + + private void mockDefaultJbakeConfiguration(File sourceFolder) throws ConfigurationException { + DefaultJBakeConfiguration configuration = new JBakeConfigurationFactory().createJettyJbakeConfiguration(sourceFolder,null,false); + System.setProperty("user.dir", sourceFolder.getPath()); + + when(factory.createJettyJbakeConfiguration(any(File.class),any(File.class),anyBoolean())).thenReturn( configuration ); + } + + private void mockJettyConfiguration(File sourceFolder, File destinationFolder) throws ConfigurationException { + DefaultJBakeConfiguration configuration = new JBakeConfigurationFactory().createJettyJbakeConfiguration(sourceFolder,destinationFolder,false); + System.setProperty("user.dir", sourceFolder.getPath()); + + when(factory.createJettyJbakeConfiguration(any(File.class),any(File.class),anyBoolean())).thenReturn( configuration ); } } diff --git a/jbake-core/src/test/java/org/jbake/render/ArchiveRendererTest.java b/jbake-core/src/test/java/org/jbake/render/ArchiveRendererTest.java index b5443e813..7d49a304e 100644 --- a/jbake-core/src/test/java/org/jbake/render/ArchiveRendererTest.java +++ b/jbake-core/src/test/java/org/jbake/render/ArchiveRendererTest.java @@ -1,16 +1,20 @@ package org.jbake.render; -import org.apache.commons.configuration.CompositeConfiguration; import org.jbake.app.ContentStore; import org.jbake.app.Renderer; -import org.jbake.render.support.MockCompositeConfiguration; +import org.jbake.app.configuration.DefaultJBakeConfiguration; +import org.jbake.app.configuration.JBakeConfiguration; import org.jbake.template.RenderingException; import org.junit.Test; -import java.io.File; - import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class ArchiveRendererTest { @@ -18,12 +22,13 @@ public class ArchiveRendererTest { public void returnsZeroWhenConfigDoesNotRenderArchives() throws RenderingException { ArchiveRenderer renderer = new ArchiveRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(false); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderArchive()).thenReturn(false); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + int renderResponse = renderer.render(mockRenderer, contentStore, configuration); assertThat(renderResponse).isEqualTo(0); } @@ -32,12 +37,13 @@ public void returnsZeroWhenConfigDoesNotRenderArchives() throws RenderingExcepti public void doesNotRenderWhenConfigDoesNotRenderArchives() throws Exception { ArchiveRenderer renderer = new ArchiveRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(false); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderArchive()).thenReturn(false); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); verify(mockRenderer, never()).renderArchive(anyString()); } @@ -46,43 +52,48 @@ public void doesNotRenderWhenConfigDoesNotRenderArchives() throws Exception { public void returnsOneWhenConfigRendersArchives() throws RenderingException { ArchiveRenderer renderer = new ArchiveRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderArchive()).thenReturn(true); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + int renderResponse = renderer.render(mockRenderer, contentStore, configuration); assertThat(renderResponse).isEqualTo(1); } @Test - public void doesRenderWhenConfigDoesNotRenderArchives() throws Exception { + public void doesRenderWhenConfigDoesRenderArchives() throws Exception { ArchiveRenderer renderer = new ArchiveRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderArchive()).thenReturn(true); + when(configuration.getArchiveFileName()).thenReturn("mockarchive.html"); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); - verify(mockRenderer, times(1)).renderArchive("random string"); + verify(mockRenderer, times(1)).renderArchive(anyString()); } @Test(expected = RenderingException.class) public void propogatesRenderingException() throws Exception { ArchiveRenderer renderer = new ArchiveRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderArchive()).thenReturn(true); + when(configuration.getArchiveFileName()).thenReturn("mockarchive.html"); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); doThrow(new Exception()).when(mockRenderer).renderArchive(anyString()); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); verify(mockRenderer, never()).renderArchive("random string"); } diff --git a/jbake-core/src/test/java/org/jbake/render/DocumentsRendererTest.java b/jbake-core/src/test/java/org/jbake/render/DocumentsRendererTest.java index 2ba8e5c9d..cc57bab39 100644 --- a/jbake-core/src/test/java/org/jbake/render/DocumentsRendererTest.java +++ b/jbake-core/src/test/java/org/jbake/render/DocumentsRendererTest.java @@ -1,10 +1,10 @@ package org.jbake.render; -import org.apache.commons.configuration.CompositeConfiguration; import org.jbake.app.ContentStore; import org.jbake.app.Crawler.Attributes; import org.jbake.app.DocumentList; import org.jbake.app.Renderer; +import org.jbake.app.configuration.JBakeConfiguration; import org.jbake.model.DocumentTypes; import org.jbake.template.RenderingException; import org.junit.Before; @@ -16,14 +16,17 @@ import org.mockito.Captor; import org.mockito.MockitoAnnotations; -import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class DocumentsRendererTest { @@ -32,36 +35,31 @@ public class DocumentsRendererTest { private DocumentsRenderer documentsRenderer; private ContentStore db; private Renderer renderer; - private CompositeConfiguration configuration; + private JBakeConfiguration configuration; private DocumentList emptyDocumentList; - private File destinationFile; - private File templatePath; - + @Captor private ArgumentCaptor> argument; @Before - public void setUp() throws Exception { - - MockitoAnnotations.initMocks(this); - + public void setUp() { + + MockitoAnnotations.initMocks(this); + documentsRenderer = new DocumentsRenderer(); db = mock(ContentStore.class); renderer = mock(Renderer.class); - configuration = mock(CompositeConfiguration.class); + configuration = mock(JBakeConfiguration.class); emptyDocumentList = new DocumentList(); - - destinationFile = new File("fakefile"); - templatePath = new File("fakepath"); } @Test public void shouldReturnZeroIfNothingHasRendered() throws Exception { when(db.getUnrenderedContent(anyString())).thenReturn(emptyDocumentList); - - int renderResponse = documentsRenderer.render(renderer,db,destinationFile,templatePath,configuration); + + int renderResponse = documentsRenderer.render(renderer, db, configuration); assertThat(renderResponse).isEqualTo(0); } @@ -82,7 +80,7 @@ public void shouldReturnCountOfProcessedDocuments() throws Exception { when(db.getUnrenderedContent("customType")).thenReturn(documentList); // when: - int renderResponse = documentsRenderer.render(renderer,db,destinationFile,templatePath,configuration); + int renderResponse = documentsRenderer.render(renderer, db, configuration); // then: assertThat(renderResponse).isEqualTo(2); @@ -93,7 +91,7 @@ public void shouldThrowAnExceptionWithCollectedErrorMessages() throws Exception String fakeExceptionMessage = "fake exception"; // expect exception.expect(RenderingException.class); - exception.expectMessage(fakeExceptionMessage+"\n"+fakeExceptionMessage); + exception.expectMessage(fakeExceptionMessage + "\n" + fakeExceptionMessage); // given DocumentTypes.addDocumentType("customType"); @@ -110,73 +108,73 @@ public void shouldThrowAnExceptionWithCollectedErrorMessages() throws Exception when(db.getUnrenderedContent("customType")).thenReturn(documentList); // when - int renderResponse = documentsRenderer.render(renderer,db,destinationFile,templatePath,configuration); + int renderResponse = documentsRenderer.render(renderer, db, configuration); // then assertThat(renderResponse).isEqualTo(2); } - + @Test - public void shouldContainPostNavigation() throws Exception{ - DocumentTypes.addDocumentType("customType"); - - String first = "First Document"; - String second = "Second Document"; - String third = "Third Document"; - String fourth = "Fourth Document"; - - DocumentList documents = new DocumentList(); - documents.add(simpleDocument(fourth)); - documents.add(simpleDocument(third)); - documents.add(simpleDocument(second)); - documents.add(simpleDocument(first)); - - when(db.getUnrenderedContent("customType")).thenReturn(documents); - - int renderResponse = documentsRenderer.render(renderer,db,destinationFile,templatePath,configuration); - - Map fourthDoc = simpleDocument(fourth); - fourthDoc.put("previousContent", simpleDocument(third)); - fourthDoc.put("nextContent", null); - - Map thirdDoc = simpleDocument(third); - thirdDoc.put("nextContent", simpleDocument(fourth)); - thirdDoc.put("previousContent", simpleDocument(second)); - - Map secondDoc = simpleDocument(second); - secondDoc.put("nextContent", simpleDocument(third)); - secondDoc.put("previousContent", simpleDocument(first)); - - Map firstDoc = simpleDocument(first); - firstDoc.put("nextContent", simpleDocument(second)); - firstDoc.put("previousContent", null); - - verify(renderer,times(4)).render(argument.capture()); - - List> maps = argument.getAllValues(); - - assertThat(maps).contains(fourthDoc); - - assertThat(maps).contains(thirdDoc); - - assertThat(maps).contains(secondDoc); - - assertThat(maps).contains(firstDoc); - - assertThat(renderResponse).isEqualTo(4); + public void shouldContainPostNavigation() throws Exception { + DocumentTypes.addDocumentType("customType"); + + String first = "First Document"; + String second = "Second Document"; + String third = "Third Document"; + String fourth = "Fourth Document"; + + DocumentList documents = new DocumentList(); + documents.add(simpleDocument(fourth)); + documents.add(simpleDocument(third)); + documents.add(simpleDocument(second)); + documents.add(simpleDocument(first)); + + when(db.getUnrenderedContent("customType")).thenReturn(documents); + + int renderResponse = documentsRenderer.render(renderer, db, configuration); + + Map fourthDoc = simpleDocument(fourth); + fourthDoc.put("previousContent", simpleDocument(third)); + fourthDoc.put("nextContent", null); + + Map thirdDoc = simpleDocument(third); + thirdDoc.put("nextContent", simpleDocument(fourth)); + thirdDoc.put("previousContent", simpleDocument(second)); + + Map secondDoc = simpleDocument(second); + secondDoc.put("nextContent", simpleDocument(third)); + secondDoc.put("previousContent", simpleDocument(first)); + + Map firstDoc = simpleDocument(first); + firstDoc.put("nextContent", simpleDocument(second)); + firstDoc.put("previousContent", null); + + verify(renderer, times(4)).render(argument.capture()); + + List> maps = argument.getAllValues(); + + assertThat(maps).contains(fourthDoc); + + assertThat(maps).contains(thirdDoc); + + assertThat(maps).contains(secondDoc); + + assertThat(maps).contains(firstDoc); + + assertThat(renderResponse).isEqualTo(4); } private HashMap emptyDocument() { - return new HashMap(); + return new HashMap<>(); } - + private Map simpleDocument(String title) { - Map simpleDoc = new HashMap(); - String uri = title.replace(" ", "_"); - simpleDoc.put(Attributes.NO_EXTENSION_URI, uri); - simpleDoc.put(Attributes.URI, uri); - simpleDoc.put(Attributes.TITLE, title);; - return simpleDoc; + Map simpleDoc = new HashMap<>(); + String uri = title.replace(" ", "_"); + simpleDoc.put(Attributes.NO_EXTENSION_URI, uri); + simpleDoc.put(Attributes.URI, uri); + simpleDoc.put(Attributes.TITLE, title); + return simpleDoc; } - + } \ No newline at end of file diff --git a/jbake-core/src/test/java/org/jbake/render/FeedRendererTest.java b/jbake-core/src/test/java/org/jbake/render/FeedRendererTest.java index 12da9d11b..cb81f1d7d 100644 --- a/jbake-core/src/test/java/org/jbake/render/FeedRendererTest.java +++ b/jbake-core/src/test/java/org/jbake/render/FeedRendererTest.java @@ -1,16 +1,20 @@ package org.jbake.render; -import org.apache.commons.configuration.CompositeConfiguration; import org.jbake.app.ContentStore; import org.jbake.app.Renderer; -import org.jbake.render.support.MockCompositeConfiguration; +import org.jbake.app.configuration.DefaultJBakeConfiguration; +import org.jbake.app.configuration.JBakeConfiguration; import org.jbake.template.RenderingException; import org.junit.Test; -import java.io.File; - import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class FeedRendererTest { @@ -18,12 +22,13 @@ public class FeedRendererTest { public void returnsZeroWhenConfigDoesNotRenderFeeds() throws RenderingException { FeedRenderer renderer = new FeedRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(false); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderFeed()).thenReturn(false); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + int renderResponse = renderer.render(mockRenderer, contentStore, configuration); assertThat(renderResponse).isEqualTo(0); } @@ -32,12 +37,13 @@ public void returnsZeroWhenConfigDoesNotRenderFeeds() throws RenderingException public void doesNotRenderWhenConfigDoesNotRenderFeeds() throws Exception { FeedRenderer renderer = new FeedRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(false); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderFeed()).thenReturn(false); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); verify(mockRenderer, never()).renderFeed(anyString()); } @@ -46,43 +52,47 @@ public void doesNotRenderWhenConfigDoesNotRenderFeeds() throws Exception { public void returnsOneWhenConfigRendersFeeds() throws RenderingException { FeedRenderer renderer = new FeedRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderFeed()).thenReturn(true); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + int renderResponse = renderer.render(mockRenderer, contentStore, configuration); assertThat(renderResponse).isEqualTo(1); } @Test - public void doesRenderWhenConfigDoesNotRenderFeeds() throws Exception { + public void doesRenderWhenConfigDoesRenderFeeds() throws Exception { FeedRenderer renderer = new FeedRenderer(); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderFeed()).thenReturn(true); + when(configuration.getFeedFileName()).thenReturn("mockfeedfile.xml"); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); - verify(mockRenderer, times(1)).renderFeed("random string"); + verify(mockRenderer, times(1)).renderFeed(anyString()); } @Test(expected = RenderingException.class) public void propogatesRenderingException() throws Exception { FeedRenderer renderer = new FeedRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderFeed()).thenReturn(true); + when(configuration.getFeedFileName()).thenReturn("mockfeedfile.xml"); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); doThrow(new Exception()).when(mockRenderer).renderFeed(anyString()); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); verify(mockRenderer, never()).renderFeed("random string"); } diff --git a/jbake-core/src/test/java/org/jbake/render/IndexRendererTest.java b/jbake-core/src/test/java/org/jbake/render/IndexRendererTest.java index b3f98b6f0..40d6f1d8e 100644 --- a/jbake-core/src/test/java/org/jbake/render/IndexRendererTest.java +++ b/jbake-core/src/test/java/org/jbake/render/IndexRendererTest.java @@ -1,22 +1,20 @@ package org.jbake.render; -import org.apache.commons.configuration.CompositeConfiguration; import org.jbake.app.ContentStore; import org.jbake.app.Renderer; -import org.jbake.render.support.MockCompositeConfiguration; +import org.jbake.app.configuration.DefaultJBakeConfiguration; +import org.jbake.app.configuration.JBakeConfiguration; import org.jbake.template.RenderingException; import org.junit.Test; -import java.io.File; - import static org.assertj.core.api.Assertions.assertThat; -import static org.jbake.app.ConfigUtil.Keys.PAGINATE_INDEX; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class IndexRendererTest { @@ -24,12 +22,13 @@ public class IndexRendererTest { public void returnsZeroWhenConfigDoesNotRenderIndices() throws RenderingException { IndexRenderer renderer = new IndexRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(false); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderIndex()).thenReturn(false); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + int renderResponse = renderer.render(mockRenderer, contentStore, configuration); assertThat(renderResponse).isEqualTo(0); } @@ -38,12 +37,13 @@ public void returnsZeroWhenConfigDoesNotRenderIndices() throws RenderingExceptio public void doesNotRenderWhenConfigDoesNotRenderIndices() throws Exception { IndexRenderer renderer = new IndexRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(false); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderIndex()).thenReturn(false); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); verify(mockRenderer, never()).renderIndex(anyString()); } @@ -52,47 +52,35 @@ public void doesNotRenderWhenConfigDoesNotRenderIndices() throws Exception { public void returnsOneWhenConfigRendersIndices() throws RenderingException { IndexRenderer renderer = new IndexRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderIndex()).thenReturn(true); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + int renderResponse = renderer.render(mockRenderer, contentStore, configuration); assertThat(renderResponse).isEqualTo(1); } - @Test - public void doesRenderWhenConfigDoesNotRenderIndices() throws Exception { - IndexRenderer renderer = new IndexRenderer(); - - MockCompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); - compositeConfiguration.setProperty(PAGINATE_INDEX, false); - ContentStore contentStore = mock(ContentStore.class); - Renderer mockRenderer = mock(Renderer.class); - - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); - - verify(mockRenderer, times(1)).renderIndex("random string"); - } @Test(expected = RenderingException.class) public void propagatesRenderingException() throws Exception { IndexRenderer renderer = new IndexRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); - compositeConfiguration.setProperty(PAGINATE_INDEX, false); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderIndex()).thenReturn(true); + when(configuration.getIndexFileName()).thenReturn("mockindex.html"); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); doThrow(new Exception()).when(mockRenderer).renderIndex(anyString()); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); - verify(mockRenderer, never()).renderIndex("random string"); + verify(mockRenderer, never()).renderIndex(anyString()); } @@ -103,16 +91,36 @@ public void propagatesRenderingException() throws Exception { public void shouldFallbackToStandardIndexRenderingIfPropertyIsMissing() throws Exception { IndexRenderer renderer = new IndexRenderer(); - MockCompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderIndex()).thenReturn(true); + when(configuration.getIndexFileName()).thenReturn("mockindex.html"); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); - verify(mockRenderer, times(1)).renderIndex("random string"); + verify(mockRenderer, times(1)).renderIndex(anyString()); } + @Test + public void shouldRenderPaginatedIndex() throws Exception { + + IndexRenderer renderer = new IndexRenderer(); + + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderIndex()).thenReturn(true); + when(configuration.getPaginateIndex()).thenReturn(true); + when(configuration.getIndexFileName()).thenReturn("mockindex.html"); + + ContentStore contentStore = mock(ContentStore.class); + Renderer mockRenderer = mock(Renderer.class); + + renderer.render(mockRenderer, contentStore, configuration); + + verify(mockRenderer, times(1)).renderIndexPaging(anyString()); + + } } diff --git a/jbake-core/src/test/java/org/jbake/render/RendererTest.java b/jbake-core/src/test/java/org/jbake/render/RendererTest.java index 951f167d3..ea3e57ee7 100644 --- a/jbake-core/src/test/java/org/jbake/render/RendererTest.java +++ b/jbake-core/src/test/java/org/jbake/render/RendererTest.java @@ -1,12 +1,13 @@ package org.jbake.render; -import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil; -import org.jbake.app.ConfigUtil.Keys; +import org.jbake.TestUtils; import org.jbake.app.ContentStore; import org.jbake.app.Crawler; import org.jbake.app.Renderer; +import org.jbake.app.configuration.ConfigUtil; +import org.jbake.app.configuration.DefaultJBakeConfiguration; import org.jbake.template.DelegatingTemplateEngine; +import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -22,50 +23,56 @@ import static org.assertj.core.api.Assertions.assertThat; -@RunWith( MockitoJUnitRunner.class ) +@RunWith(MockitoJUnitRunner.class) public class RendererTest { - @Rule + @Rule public TemporaryFolder folder = new TemporaryFolder(); - private CompositeConfiguration config; - private File rootPath; - private File outputPath; - - @Mock private ContentStore db; - @Mock private DelegatingTemplateEngine renderingEngine; - - @Before + private DefaultJBakeConfiguration config; + private File outputPath; + + @Mock + private ContentStore db; + + @Mock + private DelegatingTemplateEngine renderingEngine; + + @Before public void setup() throws Exception { - URL sourceUrl = this.getClass().getResource("/"); - rootPath = new File(sourceUrl.getFile()); - if (!rootPath.exists()) { + URL sourceUrl = this.getClass().getResource("/fixture"); + File sourcePath = new File(sourceUrl.getFile()); + if (!sourcePath.exists()) { throw new Exception("Cannot find base path for test!"); } outputPath = folder.newFolder("output"); - config = ConfigUtil.load(rootPath); - } - - /** - * See issue #300 - * - * @throws Exception - */ - @Test + config = (DefaultJBakeConfiguration) new ConfigUtil().loadConfig(sourcePath); + config.setDestinationFolder(outputPath); + } + + /** + * See issue #300 + * + * @throws Exception + */ + @Test public void testRenderFileWorksWhenPathHasDotInButFileDoesNot() throws Exception { - String FOLDER = "real.path"; - final String FILENAME = "about"; - config.setProperty(Keys.OUTPUT_EXTENSION, ""); - Renderer renderer = new Renderer(db, outputPath, folder.newFolder("templates"), config, renderingEngine); - - Map content = new HashMap(); - content.put(Crawler.Attributes.TYPE, "page"); - content.put(Crawler.Attributes.URI, "/" + FOLDER + "/" + FILENAME); - content.put(Crawler.Attributes.STATUS, "published"); - - renderer.render(content); - - File outputFile = new File(outputPath.getAbsolutePath() + File.separatorChar + FOLDER + File.separatorChar + FILENAME); - assertThat(outputFile).isFile(); - } + Assume.assumeFalse("Ignore running on Windows", TestUtils.isWindows()); + String FOLDER = "real.path"; + + final String FILENAME = "about"; + config.setOutputExtension(""); + config.setTemplateFolder(folder.newFolder("templates")); + Renderer renderer = new Renderer(db, config, renderingEngine); + + Map content = new HashMap<>(); + content.put(Crawler.Attributes.TYPE, "page"); + content.put(Crawler.Attributes.URI, "/" + FOLDER + "/" + FILENAME); + content.put(Crawler.Attributes.STATUS, "published"); + + renderer.render(content); + + File outputFile = new File(outputPath.getAbsolutePath() + File.separatorChar + FOLDER + File.separatorChar + FILENAME); + assertThat(outputFile).isFile(); + } } diff --git a/jbake-core/src/test/java/org/jbake/render/SitemapRendererTest.java b/jbake-core/src/test/java/org/jbake/render/SitemapRendererTest.java index e2c68f3d4..ecbccc379 100644 --- a/jbake-core/src/test/java/org/jbake/render/SitemapRendererTest.java +++ b/jbake-core/src/test/java/org/jbake/render/SitemapRendererTest.java @@ -1,16 +1,20 @@ package org.jbake.render; -import org.apache.commons.configuration.CompositeConfiguration; import org.jbake.app.ContentStore; import org.jbake.app.Renderer; -import org.jbake.render.support.MockCompositeConfiguration; +import org.jbake.app.configuration.DefaultJBakeConfiguration; +import org.jbake.app.configuration.JBakeConfiguration; import org.jbake.template.RenderingException; import org.junit.Test; -import java.io.File; - import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class SitemapRendererTest { @@ -18,12 +22,13 @@ public class SitemapRendererTest { public void returnsZeroWhenConfigDoesNotRenderSitemaps() throws RenderingException { SitemapRenderer renderer = new SitemapRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(false); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderSiteMap()).thenReturn(false); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + int renderResponse = renderer.render(mockRenderer, contentStore, configuration); assertThat(renderResponse).isEqualTo(0); } @@ -32,12 +37,13 @@ public void returnsZeroWhenConfigDoesNotRenderSitemaps() throws RenderingExcepti public void doesNotRenderWhenConfigDoesNotRenderSitemaps() throws Exception { SitemapRenderer renderer = new SitemapRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(false); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderSiteMap()).thenReturn(false); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); verify(mockRenderer, never()).renderSitemap(anyString()); } @@ -46,45 +52,50 @@ public void doesNotRenderWhenConfigDoesNotRenderSitemaps() throws Exception { public void returnsOneWhenConfigRendersSitemaps() throws RenderingException { SitemapRenderer renderer = new SitemapRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderSiteMap()).thenReturn(true); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + int renderResponse = renderer.render(mockRenderer, contentStore, configuration); assertThat(renderResponse).isEqualTo(1); } @Test - public void doesRenderWhenConfigDoesNotRenderSitemaps() throws Exception { + public void doesRenderWhenConfigDoesRenderSitemaps() throws Exception { SitemapRenderer renderer = new SitemapRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderSiteMap()).thenReturn(true); + when(configuration.getSiteMapFileName()).thenReturn("mocksitemap.html"); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); - verify(mockRenderer, times(1)).renderSitemap("random string"); + verify(mockRenderer, times(1)).renderSitemap(anyString()); } @Test(expected = RenderingException.class) public void propogatesRenderingException() throws Exception { SitemapRenderer renderer = new SitemapRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderSiteMap()).thenReturn(true); + when(configuration.getSiteMapFileName()).thenReturn("mocksitemap.html"); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); doThrow(new Exception()).when(mockRenderer).renderSitemap(anyString()); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); - verify(mockRenderer, never()).renderSitemap("random string"); + verify(mockRenderer, never()).renderSitemap(anyString()); } } diff --git a/jbake-core/src/test/java/org/jbake/render/TagsRendererTest.java b/jbake-core/src/test/java/org/jbake/render/TagsRendererTest.java index 995cffd0d..479ff67d6 100644 --- a/jbake-core/src/test/java/org/jbake/render/TagsRendererTest.java +++ b/jbake-core/src/test/java/org/jbake/render/TagsRendererTest.java @@ -1,39 +1,38 @@ package org.jbake.render; -import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ContentStore; import org.jbake.app.Renderer; -import org.jbake.render.support.MockCompositeConfiguration; +import org.jbake.app.configuration.DefaultJBakeConfiguration; +import org.jbake.app.configuration.JBakeConfiguration; import org.jbake.template.RenderingException; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import java.io.File; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class TagsRendererTest { - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - @Test public void returnsZeroWhenConfigDoesNotRenderTags() throws RenderingException { TagsRenderer renderer = new TagsRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(false); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderTags()).thenReturn(false); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + int renderResponse = renderer.render(mockRenderer, contentStore, configuration); assertThat(renderResponse).isEqualTo(0); } @@ -42,12 +41,13 @@ public void returnsZeroWhenConfigDoesNotRenderTags() throws RenderingException { public void doesNotRenderWhenConfigDoesNotRenderTags() throws Exception { TagsRenderer renderer = new TagsRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(false); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderTags()).thenReturn(false); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); verify(mockRenderer, never()).renderTags(anyString()); } @@ -56,82 +56,59 @@ public void doesNotRenderWhenConfigDoesNotRenderTags() throws Exception { public void returnsOneWhenConfigRendersIndices() throws Exception { TagsRenderer renderer = new TagsRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderTags()).thenReturn(true); + when(configuration.getTagPathName()).thenReturn("mocktagpath"); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); Set tags = new HashSet(Arrays.asList("tag1", "tags2")); when(contentStore.getTags()).thenReturn(tags); - when(mockRenderer.renderTags("random string")).thenReturn(1); + when(mockRenderer.renderTags(anyString())).thenReturn(1); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + int renderResponse = renderer.render(mockRenderer, contentStore, configuration); assertThat(renderResponse).isEqualTo(1); } @Test - public void doesRenderWhenConfigDoesNotRenderIndices() throws Exception { + public void doesRenderWhenConfigDoesRenderIndices() throws Exception { TagsRenderer renderer = new TagsRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderTags()).thenReturn(true); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); Set tags = new HashSet(Arrays.asList("tag1", "tags2")); when(contentStore.getTags()).thenReturn(tags); + when(configuration.getTagPathName()).thenReturn("mockTagfile.html"); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); - verify(mockRenderer, times(1)).renderTags("random string"); + verify(mockRenderer, times(1)).renderTags(anyString()); } @Test(expected = RenderingException.class) public void propogatesRenderingException() throws Exception { TagsRenderer renderer = new TagsRenderer(); - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); + JBakeConfiguration configuration = mock(DefaultJBakeConfiguration.class); + when(configuration.getRenderTags()).thenReturn(true); + when(configuration.getTagPathName()).thenReturn("mocktagpath/tag"); + ContentStore contentStore = mock(ContentStore.class); Renderer mockRenderer = mock(Renderer.class); doThrow(new Exception()).when(mockRenderer).renderTags(anyString()); - int renderResponse = renderer.render(mockRenderer, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); + renderer.render(mockRenderer, contentStore, configuration); verify(mockRenderer, never()).renderTags(anyString()); } - - @Test - public void shouldGenerateTagsHome() throws Exception { - TagsRenderer renderer = new TagsRenderer(); - - CompositeConfiguration compositeConfiguration = new MockCompositeConfiguration().withDefaultBoolean(true); - compositeConfiguration.setProperty(Keys.TAG_PATH, "tags"); - compositeConfiguration.setProperty(Keys.OUTPUT_EXTENSION, ".html"); - compositeConfiguration.setProperty(Keys.RENDER_ENCODING, "UTF-8"); - - ContentStore contentStore = mock(ContentStore.class); - - File destination = folder.newFolder(); - - //Let it create the files in temporary folder - Renderer rendere = new Renderer(contentStore, destination, new File("."), compositeConfiguration); - - Set tags = new HashSet(Arrays.asList("tag1", "tags2")); - when(contentStore.getAllTags()).thenReturn(tags); - - int renderResponse = renderer.render(rendere, contentStore, - new File("fake"), new File("fake"), compositeConfiguration); - - assertThat(renderResponse).isEqualTo(3); - - //Verify that index.html is created as tags home. - assertThat(new File(destination, "tags/index.html").exists()).isEqualTo(true); - } - } diff --git a/jbake-core/src/test/java/org/jbake/template/ModelExtractorsTest.java b/jbake-core/src/test/java/org/jbake/template/ModelExtractorsTest.java index 59e185c69..9e09a1374 100644 --- a/jbake-core/src/test/java/org/jbake/template/ModelExtractorsTest.java +++ b/jbake-core/src/test/java/org/jbake/template/ModelExtractorsTest.java @@ -7,7 +7,6 @@ import org.junit.rules.ExpectedException; import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.CoreMatchers.is; public class ModelExtractorsTest { @@ -38,6 +37,7 @@ public void shouldLoadExtractorsOnInstantiation() { "db", "tag_posts", "tags", + "tagged_documents", }; for (String aKey : expectedKeys) { diff --git a/jbake-core/src/test/java/org/jbake/util/HtmlUtilTest.java b/jbake-core/src/test/java/org/jbake/util/HtmlUtilTest.java index 68b87e3e3..7661e301e 100644 --- a/jbake-core/src/test/java/org/jbake/util/HtmlUtilTest.java +++ b/jbake-core/src/test/java/org/jbake/util/HtmlUtilTest.java @@ -1,27 +1,26 @@ package org.jbake.util; -import static org.assertj.core.api.Assertions.*; +import org.jbake.app.Crawler.Attributes; +import org.jbake.app.configuration.ConfigUtil; +import org.jbake.app.configuration.DefaultJBakeConfiguration; +import org.junit.Before; +import org.junit.Test; import java.io.File; import java.util.HashMap; import java.util.Map; -import org.apache.commons.configuration.CompositeConfiguration; -import org.jbake.app.ConfigUtil; -import org.jbake.app.Crawler.Attributes; -import org.jbake.util.HtmlUtil; -import org.junit.Before; -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; public class HtmlUtilTest { - private CompositeConfiguration config; + private DefaultJBakeConfiguration config; @Before public void setUp() throws Exception{ - config = ConfigUtil.load(new File(this.getClass().getResource("/fixture").getFile())); + config = (DefaultJBakeConfiguration) new ConfigUtil().loadConfig(new File(this.getClass().getResource("/fixture").getFile())); } @Test diff --git a/jbake-core/src/test/resources/fixture/freemarkerTemplates/index.ftl b/jbake-core/src/test/resources/fixture/freemarkerTemplates/index.ftl index 0a0d0931f..986219394 100644 --- a/jbake-core/src/test/resources/fixture/freemarkerTemplates/index.ftl +++ b/jbake-core/src/test/resources/fixture/freemarkerTemplates/index.ftl @@ -15,7 +15,7 @@ ${db.getPublishedPages().size()}
-<#if (config.index_paginate!false) > +<#if config.index_paginate > <#if (previousFileName??) > Previous - <#if (nextFileName??) > Next ${currentPageNumber} of ${numberOfPages}
diff --git a/jbake-core/src/test/resources/fixture/freemarkerTemplates/tags-index.ftl b/jbake-core/src/test/resources/fixture/freemarkerTemplates/tags-index.ftl index d3f82f482..9e8d42a5f 100644 --- a/jbake-core/src/test/resources/fixture/freemarkerTemplates/tags-index.ftl +++ b/jbake-core/src/test/resources/fixture/freemarkerTemplates/tags-index.ftl @@ -8,7 +8,7 @@

Tags

<#list tags as tag> -

${tag.name} [${tag.tagged_posts?size}]

+

${tag.name} ${tag.tagged_posts?size}

diff --git a/jbake-core/src/test/resources/fixture/groovyMarkupTemplates/tags-index.tpl b/jbake-core/src/test/resources/fixture/groovyMarkupTemplates/tags-index.tpl index 418332290..3c97814e9 100644 --- a/jbake-core/src/test/resources/fixture/groovyMarkupTemplates/tags-index.tpl +++ b/jbake-core/src/test/resources/fixture/groovyMarkupTemplates/tags-index.tpl @@ -9,7 +9,8 @@ layout 'layout/main.tpl', tags.each {tag -> h2 { - a(href:"${tag.uri}","${tag.name}") + a(href:"${content.rootpath}${tag.uri}","${tag.name}") + yield "${tag.tagged_posts.size()}" } } } diff --git a/jbake-core/src/test/resources/fixture/groovyTemplates/tags-index.gsp b/jbake-core/src/test/resources/fixture/groovyTemplates/tags-index.gsp index f53217c05..b50736cae 100644 --- a/jbake-core/src/test/resources/fixture/groovyTemplates/tags-index.gsp +++ b/jbake-core/src/test/resources/fixture/groovyTemplates/tags-index.gsp @@ -15,7 +15,7 @@ <%tags.each {tag ->%> -

${tag.name}

+

${tag.name} ${tag.tagged_posts.size()}

<%}%> diff --git a/jbake-core/src/test/resources/fixture/jadeTemplates/tags-index.jade b/jbake-core/src/test/resources/fixture/jadeTemplates/tags-index.jade index 0f8abb1cd..3e923342e 100644 --- a/jbake-core/src/test/resources/fixture/jadeTemplates/tags-index.jade +++ b/jbake-core/src/test/resources/fixture/jadeTemplates/tags-index.jade @@ -5,6 +5,7 @@ block content .span12 h1 Tags each tag, index in tags - h4 - a(href='#{tag.uri}') #{tag.title} + h2 + a(href='#{content.rootpath}#{tag.uri}') #{tag.name} + | #{tag.tagged_posts.size()} hr diff --git a/jbake-core/src/test/resources/fixture/thymeleafTemplates/tags-index.thyme b/jbake-core/src/test/resources/fixture/thymeleafTemplates/tags-index.thyme index fae8d4671..28337ae19 100644 --- a/jbake-core/src/test/resources/fixture/thymeleafTemplates/tags-index.thyme +++ b/jbake-core/src/test/resources/fixture/thymeleafTemplates/tags-index.thyme @@ -10,7 +10,7 @@

Tags

-

Post title

+

Tag[[${tag.tagged_posts.size()}]]

@@ -19,4 +19,4 @@
- \ No newline at end of file +