From 3480fa00e8b8718e135a1f6aba5f7baeacc554ed Mon Sep 17 00:00:00 2001 From: NatanHugo2004 Date: Mon, 29 Jun 2026 13:05:02 -0300 Subject: [PATCH 1/9] refactor: ciclomatic complexity --- .../src/main/java/org/jbake/app/Crawler.java | 8 +- .../main/java/org/jbake/launcher/Init.java | 57 ++++++----- .../org/jbake/parser/AsciidoctorEngine.java | 94 ++++++++++++------- 3 files changed, 94 insertions(+), 65 deletions(-) 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 0c3d8b9d6..3195dfbd9 100644 --- a/jbake-core/src/main/java/org/jbake/app/Crawler.java +++ b/jbake-core/src/main/java/org/jbake/app/Crawler.java @@ -140,7 +140,6 @@ private void crawlDataFiles(File path) { sb.append("Processing [").append(sourceFile.getPath()).append("]... "); String sha1 = buildHash(sourceFile); String uri = buildDataFileURI(sourceFile); - boolean process = true; DocumentStatus status = DocumentStatus.NEW; String docType = config.getDataFileDocType(); status = findDocumentStatus(uri, sha1); @@ -149,17 +148,12 @@ private void crawlDataFiles(File path) { db.deleteContent(uri); } else if (status == DocumentStatus.IDENTICAL) { sb.append(" : same "); - process = false; - } - if (!process) { break; } if (DocumentStatus.NEW == status) { sb.append(" : new "); } - if (process) { // new or updated - crawlDataFile(sourceFile, sha1, uri, docType); - } + crawlDataFile(sourceFile, sha1, uri, docType); logger.info("{}", sb); } if (sourceFile.isDirectory()) { 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 ae1dd1bca..4d7fc9c13 100644 --- a/jbake-core/src/main/java/org/jbake/launcher/Init.java +++ b/jbake-core/src/main/java/org/jbake/launcher/Init.java @@ -43,37 +43,42 @@ public void run(File outputFolder, File templateLocationFolder, String templateT if (!outputFolder.canWrite()) { throw new Exception("Output folder is not writeable!"); } + if (hasExistingStructure(outputFolder)) { + throw new Exception(String.format("Output folder '%s' already contains structure!", outputFolder.getAbsolutePath())); + } + String exampleProject = config.getExampleProjectByType(templateType); + if (exampleProject == null) { + throw new Exception("Cannot locate example project type: " + templateType); + } + File templateFile = new File(templateLocationFolder, exampleProject); + if (!templateFile.exists()) { + throw new Exception("Cannot find example project file: " + templateFile.getPath()); + } + ZipUtil.extract(new FileInputStream(templateFile), outputFolder); + } + /** + * Checks if the output folder already contains JBake standard folders. + */ + private boolean hasExistingStructure(File outputFolder) { File[] contents = outputFolder.listFiles(); - boolean safe = true; - if (contents != null) { - for (File content : contents) { - if (content.isDirectory()) { - if (content.getName().equalsIgnoreCase(config.getTemplateFolderName())) { - safe = false; - } - if (content.getName().equalsIgnoreCase(config.getContentFolderName())) { - safe = false; - } - if (content.getName().equalsIgnoreCase(config.getAssetFolderName())) { - safe = false; - } - } - } + if (contents == null) { + return false; } - if (!safe) { - throw new Exception(String.format("Output folder '%s' already contains structure!", - outputFolder.getAbsolutePath())); - } - 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()); + for (File content : contents) { + if (content.isDirectory() && isJBakeFolder(content.getName())) { + return true; } - ZipUtil.extract(new FileInputStream(templateFile), outputFolder); - } else { - throw new Exception("Cannot locate example project type: " + templateType); } + return false; + } + /** + * Compares a folder name against configured JBake folder names. + */ + private boolean isJBakeFolder(String name) { + return name.equalsIgnoreCase(config.getTemplateFolderName()) + || name.equalsIgnoreCase(config.getContentFolderName()) + || name.equalsIgnoreCase(config.getAssetFolderName()); } } 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 a5b106895..e4ec4659c 100644 --- a/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java +++ b/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java @@ -38,6 +38,9 @@ public class AsciidoctorEngine extends MarkupEngine { /* comma separated gem names */ private static final String OPT_REQUIRES = "requires"; + /** + * Constructs a new {@code AsciidoctorEngine}. + */ public AsciidoctorEngine() { Class engineClass = Asciidoctor.class; assert engineClass != null; @@ -91,37 +94,64 @@ public void processHeader(final ParserContext context) { } Map attributes = header.getAttributes(); for (Map.Entry attribute : attributes.entrySet()) { - String key = attribute.getKey(); - Object value = attribute.getValue(); - - if (hasJBakePrefix(key)) { - String pKey = key.substring(6); - if(canCastToString(value)) { - storeHeaderValue(pKey, (String) value, documentModel); - } else { - documentModel.put(pKey, value); - } - } - if (hasRevdate(key) && canCastToString(value)) { + processAttribute(attribute.getKey(), attribute.getValue(), context, documentModel); + } + } - String dateFormat = context.getConfig().getDateFormat(); - DateFormat df = new SimpleDateFormat(dateFormat); - try { - Date date = df.parse((String) value); - context.setDate(date); - } catch (ParseException e) { - LOGGER.error("Unable to parse revdate. Expected {}", dateFormat, e); - } - } - if (key.equals("jbake-tags")) { - if (canCastToString(value)) { - context.setTags(((String) value).split(",")); - } else { - LOGGER.error("Wrong value of 'jbake-tags'. Expected a String got '{}'", getValueClassName(value)); - } - } else { - documentModel.put(key, attributes.get(key)); - } + /** + * Evaluates attribute keys and directs them to their corresponding processing logic. + */ + private void processAttribute(String key, Object value, ParserContext context, DocumentModel documentModel) { + if (hasJbakePrefix(key)) { + processJbakeAttribute(key, value, documentModel); + } + + if (hasRevdate(key) && canCastToString(value)) { + processRevdate((String) value, context); + } + + if ("jbake-tags".equals(key)) { + processTags(value, context); + } else { + // Directly saves the attribute if it does not require any special handling + documentModel.put(key, value); + } + } + + /** + * Extracts and stores internal configuration properties specific to JBake. + */ + private void processJbakeAttribute(String key, Object value, DocumentModel documentModel) { + String pKey = key.substring(6); + if (canCastToString(value)) { + storeHeaderValue(pKey, (String) value, documentModel); + } else { + documentModel.put(pKey, value); + } + } + + /** + * Parses and assigns the document revision date based on the expected system format. + */ + private void processRevdate(String value, ParserContext context) { + String dateFormat = context.getConfig().getDateFormat(); + DateFormat df = new SimpleDateFormat(dateFormat); + try { + Date date = df.parse(value); + context.setDate(date); + } catch (ParseException e) { + LOGGER.error("Unable to parse revdate. Expected {}", dateFormat, e); + } + } + + /** + * Validates the data type and extracts the tag collection associated with the document. + */ + private void processTags(Object value, ParserContext context) { + if (canCastToString(value)) { + context.setTags(((String) value).split(",")); + } else { + LOGGER.error("Wrong value of 'jbake-tags'. Expected a String got '{}'", getValueClassName(value)); } } @@ -137,7 +167,7 @@ private boolean hasRevdate(String key) { return key.equals(REVDATE_KEY); } - private boolean hasJBakePrefix(String key) { + private boolean hasJbakePrefix(String key) { return key.startsWith(JBAKE_PREFIX); } @@ -180,7 +210,7 @@ private Options getAsciiDocOptionsAndAttributes(ParserContext context) { for (final String optionKey : optionsSubset) { Object optionValue = config.getAsciidoctorOption(optionKey); - if (optionKey.equals(Options.TEMPLATE_DIRS)) { + if (Options.TEMPLATE_DIRS.equals(optionKey)) { List dirs = getAsList(optionValue); if (!dirs.isEmpty()) { options.setTemplateDirs(String.valueOf(dirs)); From 91a853acf289b1c83b3094d4c46b3bbf99d948ea Mon Sep 17 00:00:00 2001 From: NatanHugo2004 Date: Mon, 29 Jun 2026 16:22:07 -0300 Subject: [PATCH 2/9] refactor: ciclomatic complexity --- .../src/main/java/org/jbake/parser/AsciidoctorEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e4ec4659c..d9645d6c8 100644 --- a/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java +++ b/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java @@ -210,7 +210,7 @@ private Options getAsciiDocOptionsAndAttributes(ParserContext context) { for (final String optionKey : optionsSubset) { Object optionValue = config.getAsciidoctorOption(optionKey); - if (Options.TEMPLATE_DIRS.equals(optionKey)) { + if (optionKey.equals(Options.TEMPLATE_DIRS)) { List dirs = getAsList(optionValue); if (!dirs.isEmpty()) { options.setTemplateDirs(String.valueOf(dirs)); From 8c29216993ea1114b233a250e5cfe3b6215080c4 Mon Sep 17 00:00:00 2001 From: NatanHugo2004 Date: Mon, 29 Jun 2026 16:23:54 -0300 Subject: [PATCH 3/9] refactor: ciclomatic complexity --- .../java/org/jbake/parser/AsciidoctorEngine.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) 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 d9645d6c8..13663f850 100644 --- a/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java +++ b/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java @@ -97,10 +97,7 @@ public void processHeader(final ParserContext context) { processAttribute(attribute.getKey(), attribute.getValue(), context, documentModel); } } - - /** - * Evaluates attribute keys and directs them to their corresponding processing logic. - */ + private void processAttribute(String key, Object value, ParserContext context, DocumentModel documentModel) { if (hasJbakePrefix(key)) { processJbakeAttribute(key, value, documentModel); @@ -113,14 +110,10 @@ private void processAttribute(String key, Object value, ParserContext context, D if ("jbake-tags".equals(key)) { processTags(value, context); } else { - // Directly saves the attribute if it does not require any special handling documentModel.put(key, value); } } - /** - * Extracts and stores internal configuration properties specific to JBake. - */ private void processJbakeAttribute(String key, Object value, DocumentModel documentModel) { String pKey = key.substring(6); if (canCastToString(value)) { @@ -130,9 +123,6 @@ private void processJbakeAttribute(String key, Object value, DocumentModel docum } } - /** - * Parses and assigns the document revision date based on the expected system format. - */ private void processRevdate(String value, ParserContext context) { String dateFormat = context.getConfig().getDateFormat(); DateFormat df = new SimpleDateFormat(dateFormat); From 4b1056f701ef9e95640f6ec7cc62823c1bc40f2f Mon Sep 17 00:00:00 2001 From: NatanHugo2004 Date: Mon, 29 Jun 2026 16:26:16 -0300 Subject: [PATCH 4/9] refactor: ciclomatic complexity --- jbake-core/src/main/java/org/jbake/launcher/Init.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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 4d7fc9c13..ec6aa1514 100644 --- a/jbake-core/src/main/java/org/jbake/launcher/Init.java +++ b/jbake-core/src/main/java/org/jbake/launcher/Init.java @@ -57,9 +57,7 @@ public void run(File outputFolder, File templateLocationFolder, String templateT ZipUtil.extract(new FileInputStream(templateFile), outputFolder); } - /** - * Checks if the output folder already contains JBake standard folders. - */ + private boolean hasExistingStructure(File outputFolder) { File[] contents = outputFolder.listFiles(); if (contents == null) { @@ -73,9 +71,7 @@ private boolean hasExistingStructure(File outputFolder) { } return false; } - /** - * Compares a folder name against configured JBake folder names. - */ + private boolean isJBakeFolder(String name) { return name.equalsIgnoreCase(config.getTemplateFolderName()) || name.equalsIgnoreCase(config.getContentFolderName()) From 9a74558f3ab411ba4326a33405a3a8878353aeb8 Mon Sep 17 00:00:00 2001 From: NatanHugo2004 Date: Mon, 29 Jun 2026 16:29:17 -0300 Subject: [PATCH 5/9] refactor: ciclomatic complexity --- .../src/main/java/org/jbake/parser/AsciidoctorEngine.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 13663f850..449d1bc71 100644 --- a/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java +++ b/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java @@ -97,7 +97,7 @@ public void processHeader(final ParserContext context) { processAttribute(attribute.getKey(), attribute.getValue(), context, documentModel); } } - + private void processAttribute(String key, Object value, ParserContext context, DocumentModel documentModel) { if (hasJbakePrefix(key)) { processJbakeAttribute(key, value, documentModel); @@ -134,9 +134,7 @@ private void processRevdate(String value, ParserContext context) { } } - /** - * Validates the data type and extracts the tag collection associated with the document. - */ + private void processTags(Object value, ParserContext context) { if (canCastToString(value)) { context.setTags(((String) value).split(",")); From 399f30b8d5c952ca4d4d7928ede106b40deee9f3 Mon Sep 17 00:00:00 2001 From: NatanHugo2004 Date: Sat, 4 Jul 2026 22:08:58 -0300 Subject: [PATCH 6/9] refactor: class extract --- .../main/java/org/jbake/app/ContentStore.java | 117 ++--------------- .../src/main/java/org/jbake/app/DBUtil.java | 2 +- .../src/main/java/org/jbake/app/Oven.java | 2 +- .../java/org/jbake/app/SchemaManager.java | 118 ++++++++++++++++++ .../java/org/jbake/app/ContentStoreTest.java | 2 +- .../test/java/org/jbake/app/CrawlerTest.java | 2 +- .../AbstractTemplateEngineRenderingTest.java | 6 +- ...FreemarkerTemplateEngineRenderingTest.java | 2 +- 8 files changed, 137 insertions(+), 114 deletions(-) create mode 100644 jbake-core/src/main/java/org/jbake/app/SchemaManager.java 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 352e97ae5..5fbb7066f 100644 --- a/jbake-core/src/main/java/org/jbake/app/ContentStore.java +++ b/jbake-core/src/main/java/org/jbake/app/ContentStore.java @@ -29,16 +29,12 @@ import com.orientechnologies.orient.core.db.ODatabaseType; import com.orientechnologies.orient.core.db.OrientDB; import com.orientechnologies.orient.core.db.OrientDBConfig; -import com.orientechnologies.orient.core.metadata.schema.OClass; -import com.orientechnologies.orient.core.metadata.schema.OSchema; import com.orientechnologies.orient.core.metadata.schema.OType; import com.orientechnologies.orient.core.record.OElement; import com.orientechnologies.orient.core.sql.executor.OResultSet; import org.jbake.model.DocumentModel; import org.jbake.model.DocumentTypes; -import org.jbake.model.ModelAttributes; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + import java.io.File; import java.util.Collections; @@ -46,28 +42,21 @@ import java.util.Set; /** - * @author jdlee - */ + * @author jdlee */ public class ContentStore { - + private SchemaManager schemaManager; private static final String STATEMENT_GET_PUBLISHED_POST_BY_TYPE_AND_TAG = "select * from Documents where status='published' and type='%s' and ? in tags order by date desc"; private static final String STATEMENT_GET_DOCUMENT_STATUS_BY_DOCTYPE_AND_URI = "select sha1,rendered from Documents where sourceuri=?"; private static final String STATEMENT_GET_PUBLISHED_COUNT = "select count(*) as count from Documents where status='published' and type='%s'"; private static final String STATEMENT_MARK_CONTENT_AS_RENDERD = "update Documents set rendered=true where rendered=false and type='%s' and sourceuri='%s' and cached=true"; private static final String STATEMENT_DELETE_DOCTYPE_BY_SOURCEURI = "delete from Documents where sourceuri=?"; private static final String STATEMENT_GET_UNDRENDERED_CONTENT = "select * from Documents 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 Documents where status='published' and type='post'"; private static final String STATEMENT_GET_ALL_CONTENT_BY_DOCTYPE = "select * from Documents where type='%s' order by date desc"; private static final String STATEMENT_GET_PUBLISHED_CONTENT_BY_DOCTYPE = "select * from Documents where status='published' and type='%s' order by date desc"; private static final String STATEMENT_GET_PUBLISHED_POSTS_BY_TAG = "select * from Documents where status='published' and type='post' and ? in tags order by date desc"; private static final String STATEMENT_GET_TAGS_BY_DOCTYPE = "select tags from Documents where status='published' and type='%s'"; - private static final String STATEMENT_INSERT_TEMPLATES_SIGNATURE = "insert into Signatures(key,sha1) values('templates',?)"; - private static final String STATEMENT_DELETE_ALL = "delete from Documents where type='%s'"; - private static final String STATEMENT_UPDATE_TEMPLATE_SIGNATURE = "update Signatures set sha1=? where key='templates'"; private static final String STATEMENT_GET_DOCUMENT_COUNT_BY_TYPE = "select count(*) as count from Documents where type='%s'"; - - private final Logger logger = LoggerFactory.getLogger(ContentStore.class); private final String type; private final String name; @@ -95,10 +84,9 @@ public void startup() { orient.createIfNotExists(name, ODatabaseType.valueOf(type.toUpperCase())); db = orient.open(name, "admin", "admin"); - + schemaManager = new SchemaManager(db); activateOnCurrentThread(); - - updateSchema(); + schemaManager.updateSchema(); } public long getStart() { @@ -122,18 +110,6 @@ public void resetPagination() { this.limit = -1; } - public final void updateSchema() { - - OSchema schema = db.getMetadata().getSchema(); - - if (!schema.existsClass(Schema.DOCUMENTS)) { - createDocType(schema); - } - if (!schema.existsClass(Schema.SIGNATURES)) { - createSignatureType(schema); - } - } - public void close() { if (db != null) { activateOnCurrentThread(); @@ -258,10 +234,6 @@ private DocumentList getAllTagsFromPublishedPosts() { return query(STATEMENT_GET_TAGS_FROM_PUBLISHED_POSTS); } - private DocumentList getSignaturesForTemplates() { - return query(STATEMENT_GET_SIGNATURE_FOR_TEMPLATES); - } - public DocumentList getUnrenderedContent() { return query(STATEMENT_GET_UNDRENDERED_CONTENT); } @@ -275,19 +247,6 @@ public void markContentAsRendered(DocumentModel document) { executeCommand(statement); } - private void updateSignatures(String currentTemplatesSignature) { - executeCommand(STATEMENT_UPDATE_TEMPLATE_SIGNATURE, currentTemplatesSignature); - } - - public void deleteAllByDocType(String docType) { - String statement = String.format(STATEMENT_DELETE_ALL, docType); - executeCommand(statement); - } - - private void insertTemplatesSignature(String currentTemplatesSignature) { - executeCommand(STATEMENT_INSERT_TEMPLATES_SIGNATURE, currentTemplatesSignature); - } - private DocumentList query(String sql) { activateOnCurrentThread(); OResultSet results = db.query(sql); @@ -315,6 +274,10 @@ public Set getTags() { return result; } + public SchemaManager getSchemaManager() { + return schemaManager; + } + public Set getAllTags() { Set result = new HashSet<>(); for (String docType : DocumentTypes.getDocumentTypes()) { @@ -328,78 +291,20 @@ public Set getAllTags() { return result; } - private void createDocType(final OSchema schema) { - logger.debug("Create document class"); - - OClass page = schema.createClass(Schema.DOCUMENTS); - page.createProperty(ModelAttributes.SHA1, OType.STRING).setNotNull(true); - page.createIndex(Schema.DOCUMENTS + "sha1Index", OClass.INDEX_TYPE.NOTUNIQUE, ModelAttributes.SHA1); - page.createProperty(ModelAttributes.SOURCE_URI, OType.STRING).setNotNull(true); - page.createIndex(Schema.DOCUMENTS + "sourceUriIndex", OClass.INDEX_TYPE.UNIQUE, ModelAttributes.SOURCE_URI); - page.createProperty(ModelAttributes.CACHED, OType.BOOLEAN).setNotNull(true); - page.createIndex(Schema.DOCUMENTS + "cachedIndex", OClass.INDEX_TYPE.NOTUNIQUE, ModelAttributes.CACHED); - page.createProperty(ModelAttributes.RENDERED, OType.BOOLEAN).setNotNull(true); - page.createIndex(Schema.DOCUMENTS + "renderedIndex", OClass.INDEX_TYPE.NOTUNIQUE, ModelAttributes.RENDERED); - page.createProperty(ModelAttributes.STATUS, OType.STRING).setNotNull(true); - page.createIndex(Schema.DOCUMENTS + "statusIndex", OClass.INDEX_TYPE.NOTUNIQUE, ModelAttributes.STATUS); - page.createProperty(ModelAttributes.TYPE, OType.STRING).setNotNull(true); - page.createIndex(Schema.DOCUMENTS + "typeIndex", OClass.INDEX_TYPE.NOTUNIQUE, ModelAttributes.TYPE); - - } - - private void createSignatureType(OSchema schema) { - OClass signatures = schema.createClass(Schema.SIGNATURES); - signatures.createProperty(ModelAttributes.SHA1, OType.STRING).setNotNull(true); - signatures.createIndex("sha1Idx", OClass.INDEX_TYPE.UNIQUE, ModelAttributes.SHA1); - } - public void updateAndClearCacheIfNeeded(boolean needed, File templateFolder) { boolean clearCache = needed; if (!needed) { - clearCache = updateTemplateSignatureIfChanged(templateFolder); + clearCache = this.schemaManager.updateTemplateSignatureIfChanged(templateFolder); } if (clearCache) { - deleteAllDocumentTypes(); - this.updateSchema(); + schemaManager.deleteAllDocumentTypes(); schemaManager.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 = docs.get(0).getSha1(); - 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/DBUtil.java b/jbake-core/src/main/java/org/jbake/app/DBUtil.java index 33568b450..b1c271ea5 100644 --- a/jbake-core/src/main/java/org/jbake/app/DBUtil.java +++ b/jbake-core/src/main/java/org/jbake/app/DBUtil.java @@ -21,7 +21,7 @@ public static ContentStore createDataStore(final String type, String name) { @Deprecated public static void updateSchema(final ContentStore db) { - db.updateSchema(); + db.getSchemaManager().updateSchema(); } public static ContentStore createDataStore(JBakeConfiguration configuration) { 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 ead7ce2cf..1146d5f43 100644 --- a/jbake-core/src/main/java/org/jbake/app/Oven.java +++ b/jbake-core/src/main/java/org/jbake/app/Oven.java @@ -159,7 +159,7 @@ public void bake() { LOGGER.info("Baking has started..."); contentStore.startup(); updateDocTypesFromConfiguration(); - contentStore.updateSchema(); + contentStore.getSchemaManager().updateSchema(); contentStore.updateAndClearCacheIfNeeded(config.getClearCache(), config.getTemplateFolder()); // process source content diff --git a/jbake-core/src/main/java/org/jbake/app/SchemaManager.java b/jbake-core/src/main/java/org/jbake/app/SchemaManager.java new file mode 100644 index 000000000..fc370bffc --- /dev/null +++ b/jbake-core/src/main/java/org/jbake/app/SchemaManager.java @@ -0,0 +1,118 @@ +package org.jbake.app; + +import com.orientechnologies.orient.core.db.ODatabaseSession; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OSchema; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.sql.executor.OResultSet; +import org.jbake.model.DocumentModel; +import org.jbake.model.DocumentTypes; +import org.jbake.model.ModelAttributes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class SchemaManager { + private static final Logger LOGGER = LoggerFactory.getLogger(SchemaManager.class); + private static final String STATEMENT_GET_SIGNATURE_FOR_TEMPLATES = "select sha1 from Signatures where key='templates'"; + private static final String STATEMENT_UPDATE_TEMPLATE_SIGNATURE = "update Signatures set sha1=? where key='templates'"; + private static final String STATEMENT_INSERT_TEMPLATES_SIGNATURE = "insert into Signatures(key,sha1) values('templates',?)"; + private static final String STATEMENT_DELETE_ALL = "delete from Documents where type='%s'"; + + private final ODatabaseSession db; + + public SchemaManager(ODatabaseSession db) { + this.db = db; + } + + public void updateSchema() { + OSchema schema = db.getMetadata().getSchema(); + + if (!schema.existsClass("Documents")) { + createDocuments(schema); + } + + if (!schema.existsClass("Signatures")) { + createSignatures(schema); + } + } + + private void createDocuments(OSchema schema) { + LOGGER.debug("Create document class"); + + OClass page = schema.createClass("Documents"); + page.createProperty(ModelAttributes.SHA1, OType.STRING).setNotNull(true); + page.createIndex("Documentssha1Index", OClass.INDEX_TYPE.NOTUNIQUE, ModelAttributes.SHA1); + + page.createProperty(ModelAttributes.SOURCE_URI, OType.STRING).setNotNull(true); + page.createIndex("DocumentssourceUriIndex", OClass.INDEX_TYPE.UNIQUE, ModelAttributes.SOURCE_URI); + + page.createProperty(ModelAttributes.CACHED, OType.BOOLEAN).setNotNull(true); + page.createIndex("DocumentscachedIndex", OClass.INDEX_TYPE.NOTUNIQUE, ModelAttributes.CACHED); + + page.createProperty(ModelAttributes.RENDERED, OType.BOOLEAN).setNotNull(true); + page.createIndex("DocumentsrenderedIndex", OClass.INDEX_TYPE.NOTUNIQUE, ModelAttributes.RENDERED); + + page.createProperty(ModelAttributes.STATUS, OType.STRING).setNotNull(true); + page.createIndex("DocumentsstatusIndex", OClass.INDEX_TYPE.NOTUNIQUE, ModelAttributes.STATUS); + + page.createProperty(ModelAttributes.TYPE, OType.STRING).setNotNull(true); + page.createIndex("DocumentstypeIndex", OClass.INDEX_TYPE.NOTUNIQUE, ModelAttributes.TYPE); + } + + private void createSignatures(OSchema schema) { + OClass signatures = schema.createClass("Signatures"); + signatures.createProperty(ModelAttributes.SHA1, OType.STRING).setNotNull(true); + signatures.createIndex("sha1Idx", OClass.INDEX_TYPE.UNIQUE, ModelAttributes.SHA1); + } + private DocumentList getSignaturesForTemplates() { + OResultSet results = db.query(STATEMENT_GET_SIGNATURE_FOR_TEMPLATES); + return DocumentList.wrap(results); + } + public boolean updateTemplateSignatureIfChanged(File templateFolder) { + boolean templateSignatureChanged = false; + DocumentList docs = getSignaturesForTemplates(); + String currentTemplatesSignature; + try { + currentTemplatesSignature = FileUtil.sha1(templateFolder); + } catch (Exception e) { + currentTemplatesSignature = ""; + } + if (!docs.isEmpty()) { + String sha1 = docs.get(0).getSha1(); + + if (!sha1.equals(currentTemplatesSignature)) { + updateSignatures(currentTemplatesSignature); + templateSignatureChanged = true; + } + } else { + insertTemplatesSignature(currentTemplatesSignature); + templateSignatureChanged = true; + } + + return templateSignatureChanged; + } + + private void updateSignatures(String sha1) { + db.command(STATEMENT_UPDATE_TEMPLATE_SIGNATURE, sha1); + } + + private void insertTemplatesSignature(String sha1) { + db.command(STATEMENT_INSERT_TEMPLATES_SIGNATURE, sha1); + } + public void deleteAllByDocType(String docType) { + String statement = String.format(STATEMENT_DELETE_ALL, docType); + db.command(statement); + } + public void deleteAllDocumentTypes() { + for (String docType : DocumentTypes.getDocumentTypes()) { + try { + deleteAllByDocType(docType); + } catch (Exception e) { + // maybe a non existing document type + } + } + } + +} diff --git a/jbake-core/src/test/java/org/jbake/app/ContentStoreTest.java b/jbake-core/src/test/java/org/jbake/app/ContentStoreTest.java index 567ced0c0..c927a4397 100644 --- a/jbake-core/src/test/java/org/jbake/app/ContentStoreTest.java +++ b/jbake-core/src/test/java/org/jbake/app/ContentStoreTest.java @@ -122,7 +122,7 @@ public void testStoreTypeWithSpecialCharacters() { long documentCount4 = db.getDocumentCount(typeWithHyphen); assertEquals(1, documentCount4); - db.deleteAllByDocType(typeWithHyphen); + db.getSchemaManager().deleteAllByDocType(typeWithHyphen); } } 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 326f001a0..7fb8f38b9 100644 --- a/jbake-core/src/test/java/org/jbake/app/CrawlerTest.java +++ b/jbake-core/src/test/java/org/jbake/app/CrawlerTest.java @@ -60,7 +60,7 @@ public void crawlDataFiles() { Crawler crawler = new Crawler(db, config); // manually register data doctype DocumentTypes.addDocumentType(config.getDataFileDocType()); - db.updateSchema(); + db.getSchemaManager().updateSchema(); crawler.crawlDataFiles(); Assert.assertEquals(2, db.getDocumentCount("data")); 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 69ee1cab4..eb73d995e 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 @@ -95,7 +95,7 @@ public void setup() throws Exception { config.setTemplateFileNameForDocType("paper", "paper." + templateExtension); DocumentTypes.addDocumentType("paper"); - db.updateSchema(); + db.getSchemaManager().updateSchema(); Assert.assertEquals(".html", config.getOutputExtension()); @@ -268,7 +268,7 @@ public void renderTagsIndex() throws Exception { @Test public void renderSitemap() throws Exception { DocumentTypes.addDocumentType("paper"); - db.updateSchema(); + db.getSchemaManager().updateSchema(); renderer.renderSitemap("sitemap.xml"); File outputFile = new File(destinationFolder, "sitemap.xml"); @@ -295,7 +295,7 @@ public void checkDbTemplateModelIsPopulated() throws Exception { outputStrings.put("dbSpan", Arrays.asList("3")); - db.deleteAllByDocType("post"); + db.getSchemaManager().deleteAllByDocType("post"); renderer.renderIndexPaging("index.html"); 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 de138b8c7..b39d91a02 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 @@ -71,7 +71,7 @@ public void shouldFallbackToRenderSingleIndexIfNoPostArePresent() throws Excepti config.setPaginateIndex(true); config.setPostsPerPage(1); - db.deleteAllByDocType("post"); + db.getSchemaManager().deleteAllByDocType("post"); renderer.renderIndexPaging("index.html"); From d7362ac7b0258e155e0a2eea92074eb098451a90 Mon Sep 17 00:00:00 2001 From: NatanHugo2004 Date: Sat, 4 Jul 2026 22:59:20 -0300 Subject: [PATCH 7/9] refactor: replace nested conditional with guard clauses: --- .../src/main/java/org/jbake/app/Crawler.java | 61 +++++++++---------- 1 file changed, 29 insertions(+), 32 deletions(-) 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 3195dfbd9..51922897d 100644 --- a/jbake-core/src/main/java/org/jbake/app/Crawler.java +++ b/jbake-core/src/main/java/org/jbake/app/Crawler.java @@ -125,41 +125,38 @@ private void crawlFile(File sourceFile) { } } - /** - * Crawl all files and folders looking for data files. - * - * @param path Folder to start from - */ private void crawlDataFiles(File path) { File[] contents = path.listFiles(FileUtil.getDataFileFilter()); - if (contents != null) { - Arrays.sort(contents); - for (File sourceFile : contents) { - if (sourceFile.isFile()) { - StringBuilder sb = new StringBuilder(); - sb.append("Processing [").append(sourceFile.getPath()).append("]... "); - String sha1 = buildHash(sourceFile); - String uri = buildDataFileURI(sourceFile); - DocumentStatus status = DocumentStatus.NEW; - String docType = config.getDataFileDocType(); - status = findDocumentStatus(uri, sha1); - if (status == DocumentStatus.UPDATED) { - sb.append(" : modified "); - db.deleteContent(uri); - } else if (status == DocumentStatus.IDENTICAL) { - sb.append(" : same "); - break; - } - if (DocumentStatus.NEW == status) { - sb.append(" : new "); - } - crawlDataFile(sourceFile, sha1, uri, docType); - logger.info("{}", sb); - } - if (sourceFile.isDirectory()) { - crawlDataFiles(sourceFile); - } + if (contents == null) { + return; + } + Arrays.sort(contents); + for (File sourceFile : contents) { + if (sourceFile.isDirectory()) { + crawlDataFiles(sourceFile); + continue; + } + if (!sourceFile.isFile()) { + continue; + } + StringBuilder sb = new StringBuilder(); + sb.append("Processing [").append(sourceFile.getPath()).append("]... "); + String sha1 = buildHash(sourceFile); + String uri = buildDataFileURI(sourceFile); + String docType = config.getDataFileDocType(); + DocumentStatus status = findDocumentStatus(uri, sha1); + if (status == DocumentStatus.UPDATED) { + sb.append(" : modified "); + db.deleteContent(uri); + } else if (status == DocumentStatus.IDENTICAL) { + sb.append(" : same "); + break; + } + if (status == DocumentStatus.NEW) { + sb.append(" : new "); } + crawlDataFile(sourceFile, sha1, uri, docType); + logger.info("{}", sb); } } From 803b3c5151a73ebd895a4783751ccac0bf64f4a3 Mon Sep 17 00:00:00 2001 From: NatanHugo2004 Date: Sat, 4 Jul 2026 23:22:59 -0300 Subject: [PATCH 8/9] refactor: extract method --- .../org/jbake/parser/AsciidoctorEngine.java | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) 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 449d1bc71..ef27d1280 100644 --- a/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java +++ b/jbake-core/src/main/java/org/jbake/parser/AsciidoctorEngine.java @@ -54,23 +54,7 @@ private Asciidoctor getEngine(Options options) { try { lock.writeLock().lock(); if (engine == null) { - LOGGER.info("Initializing Asciidoctor engine..."); - if (options.map().containsKey(OPT_GEM_PATH)) { - engine = AsciidoctorJRuby.Factory.create(String.valueOf(options.map().get(OPT_GEM_PATH))); - } else { - engine = Asciidoctor.Factory.create(); - } - - if (options.map().containsKey(OPT_REQUIRES)) { - String[] requires = String.valueOf(options.map().get(OPT_REQUIRES)).split(","); - if (requires.length != 0) { - for (String require : requires) { - engine.requireLibrary(require); - } - } - } - - LOGGER.info("Asciidoctor engine initialized."); + initializeEngine(options); } } finally { lock.readLock().lock(); @@ -83,6 +67,28 @@ private Asciidoctor getEngine(Options options) { return engine; } + private void initializeEngine(Options options) { + LOGGER.info("Initializing Asciidoctor engine..."); + + if (options.map().containsKey(OPT_GEM_PATH)) { + engine = AsciidoctorJRuby.Factory.create( + String.valueOf(options.map().get(OPT_GEM_PATH))); + } else { + engine = Asciidoctor.Factory.create(); + } + + if (options.map().containsKey(OPT_REQUIRES)) { + String[] requires = String.valueOf(options.map().get(OPT_REQUIRES)).split(","); + if (requires.length != 0) { + for (String require : requires) { + engine.requireLibrary(require); + } + } + } + + LOGGER.info("Asciidoctor engine initialized."); + } + @Override public void processHeader(final ParserContext context) { Options options = getAsciiDocOptionsAndAttributes(context); From 5dd12ec6950f54548cd165a39d9c446950a812a2 Mon Sep 17 00:00:00 2001 From: NatanHugo2004 Date: Sat, 4 Jul 2026 23:47:36 -0300 Subject: [PATCH 9/9] refactor: substitute algorithm --- jbake-core/src/main/java/org/jbake/app/Renderer.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) 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 5508fd327..b04eb8ce2 100644 --- a/jbake-core/src/main/java/org/jbake/app/Renderer.java +++ b/jbake-core/src/main/java/org/jbake/app/Renderer.java @@ -185,7 +185,6 @@ public void renderIndexPaging(String indexFile) throws Exception { int postsPerPage = config.getPostsPerPage(); if (totalPosts == 0) { - //paging makes no sense. render single index file instead renderIndex(indexFile); } else { PagingHelper pagingHelper = new PagingHelper(totalPosts, postsPerPage); @@ -196,26 +195,23 @@ public void renderIndexPaging(String indexFile) throws Exception { try { db.setLimit(postsPerPage); - for (int pageStart = 0, page = 1; pageStart < totalPosts; pageStart += postsPerPage, page++) { + for (int page = 1; page <= pagingHelper.getNumberOfPages(); page++) { + int pageStart = (page - 1) * postsPerPage; String fileName = indexFile; - db.setStart(pageStart); model.setCurrentPageNuber(page); String previous = pagingHelper.getPreviousFileName(page); model.setPreviousFilename(previous); String nextFileName = pagingHelper.getNextFileName(page); model.setNextFileName(nextFileName); - DocumentModel contentModel = buildSimpleModel(MASTERINDEX_TEMPLATE_NAME); - if (page > 1) { contentModel.setRootPath("../"); } model.setContent(contentModel); - - // Add page number to file name fileName = pagingHelper.getCurrentFileName(page, fileName); - ModelRenderingConfig renderConfig = new ModelRenderingConfig(fileName, model, MASTERINDEX_TEMPLATE_NAME); + ModelRenderingConfig renderConfig = + new ModelRenderingConfig(fileName, model, MASTERINDEX_TEMPLATE_NAME); render(renderConfig); } db.resetPagination();