This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
The following commit(s) were added to refs/heads/master by this push:
new c55346b Add support for reading resources from YAML front matter
c55346b is described below
commit c55346bbb381e875bb3e01dcf35e3f663fef0f90
Author: Robert Munteanu <[email protected]>
AuthorDate: Fri Jul 27 00:09:05 2018 +0200
Add support for reading resources from YAML front matter
---
mdresourceprovider/pom.xml | 6 ++
.../sling/mdresource/impl/MarkdownResource.java | 103 ++++++++++++++++++---
.../impl/MarkdownResourceProviderTest.java | 2 +
.../src/test/resources/md-test/index.md | 7 ++
4 files changed, 105 insertions(+), 13 deletions(-)
diff --git a/mdresourceprovider/pom.xml b/mdresourceprovider/pom.xml
index 2109f48..91e353a 100644
--- a/mdresourceprovider/pom.xml
+++ b/mdresourceprovider/pom.xml
@@ -64,6 +64,12 @@
<version>0.32.18</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>com.vladsch.flexmark</groupId>
+ <artifactId>flexmark-ext-yaml-front-matter</artifactId>
+ <version>0.32.18</version>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>junit</groupId>
diff --git
a/mdresourceprovider/src/main/java/org/apache/sling/mdresource/impl/MarkdownResource.java
b/mdresourceprovider/src/main/java/org/apache/sling/mdresource/impl/MarkdownResource.java
index 0a57b5d..8201b30 100644
---
a/mdresourceprovider/src/main/java/org/apache/sling/mdresource/impl/MarkdownResource.java
+++
b/mdresourceprovider/src/main/java/org/apache/sling/mdresource/impl/MarkdownResource.java
@@ -18,11 +18,15 @@
*/
package org.apache.sling.mdresource.impl;
+import static java.util.Collections.singleton;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.apache.sling.api.resource.AbstractResource;
@@ -33,7 +37,8 @@ import org.apache.sling.api.wrappers.ValueMapDecorator;
import com.vladsch.flexmark.ast.Heading;
import com.vladsch.flexmark.ast.Node;
-import com.vladsch.flexmark.ast.util.TextCollectingVisitor;
+import
com.vladsch.flexmark.ext.yaml.front.matter.AbstractYamlFrontMatterVisitor;
+import com.vladsch.flexmark.ext.yaml.front.matter.YamlFrontMatterExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
@@ -44,6 +49,11 @@ public class MarkdownResource extends AbstractResource {
private final File backingFile;
private ValueMap valueMap;
private ResourceMetadata metadata;
+ private final List<SpecialHandler> handlers = new ArrayList<>();
+ {
+ handlers.add(new HeadingHandler());
+ handlers.add(new YamlFrontMatterHandler());
+ }
public MarkdownResource(ResourceResolver resourceResolver, String path,
File backingFile) {
this.resolver = resourceResolver;
@@ -104,28 +114,42 @@ public class MarkdownResource extends AbstractResource {
return null;
}
- Parser parser = Parser.builder().build();
+ Parser parser = Parser.builder()
+
.extensions(singleton(YamlFrontMatterExtension.create()))
+ .build();
HtmlRenderer htmlRenderer = HtmlRenderer.builder().build();
- TextCollectingVisitor visitor = new TextCollectingVisitor();
Map<String, Object> props = new HashMap<>();
- // TODO - do we need the primary type?
- // TODO - allow reading properties from the file, overriding default
for resource type
props.put("sling:resourceType", "sling/markdown/file");
try {
try ( BufferedReader r =
Files.newBufferedReader(backingFile.toPath())) {
- Node node = parser.parseReader(r);
- Node maybeTitle = node.getFirstChild();
- // TODO - what to do if no title is found?
- if ( maybeTitle instanceof Heading ) {
- props.put("jcr:title",
visitor.collectAndGetText(maybeTitle));
- node = maybeTitle.getNext();
+ Node document = parser.parseReader(r);
+ Node currentNode = document.getFirstChild();
+ // consume special nodes at the beginning of the file
+ // while at least one special node (as defined by the list of
handlers) finds
+ // something to handle, parsing continues
+ //
+ // this restriction is mostly for simplicity, as it's easy to
skip the first
+ // special nodes and pass off the rest to the HTML renderer
+ // in the future, we can consider allowing these special nodes
anywhere
+ while ( currentNode != null ) {
+ boolean handled = false;
+ for ( SpecialHandler handler : handlers ) {
+ handled = handler.consume(currentNode, props);
+ if ( handled ) {
+ currentNode = currentNode.getNext();
+ break;
+ }
+ }
+
+ if ( !handled )
+ break;
}
- // TODO parse excluding the title
- props.put("jcr:description", htmlRenderer.render(node));
+ if ( currentNode != null)
+ props.put("jcr:description",
htmlRenderer.render(currentNode));
}
} catch (IOException e) {
// TODO - handle errors someplace else?
@@ -149,4 +173,57 @@ public class MarkdownResource extends AbstractResource {
return getClass().getSimpleName() + ", path: " + path;
}
+
+ /**
+ * Interface for declaring handlers for 'special' nodes
+ *
+ * <p>A 'special' node is processed by a separate handler and will not
+ * be included in the parsed HTML body.</p>
+ *
+ */
+ private interface SpecialHandler {
+ boolean consume(Node node, Map<String, Object> properties);
+ }
+
+ /**
+ * Handler that populates a resource's properties based on a YAML front
matter entry
+ *
+ */
+ private static final class YamlFrontMatterHandler implements
SpecialHandler {
+ @Override
+ public boolean consume(Node n, Map<String, Object> p) {
+ AbstractYamlFrontMatterVisitor vis = new
AbstractYamlFrontMatterVisitor();
+ vis.visit(n);
+ if ( vis.getData().isEmpty() )
+ return false;
+
+ for ( Map.Entry<String, List<String>> entry :
vis.getData().entrySet() ) {
+ if ( entry.getValue().size() == 1)
+ p.put(entry.getKey(),
entry.getValue().get(0));
+ else
+ p.put(entry.getKey(),
entry.getValue().toArray(new String[0]));
+ }
+
+
+ return true;
+ }
+ }
+
+ /**
+ * Handler that populates a resource's jcr:title property based on a
first-level heading
+ *
+ */
+ private static final class HeadingHandler implements SpecialHandler {
+ @Override
+ public boolean consume(Node n, Map<String, Object> p) {
+ if ( n instanceof Heading ) {
+ Heading h = (Heading) n;
+ if ( h.getLevel() == 1 ) {
+ p.put("jcr:title", h.getText());
+ return true;
+ }
+ }
+ return false;
+ }
+ }
}
diff --git
a/mdresourceprovider/src/test/java/org/apache/sling/mdresource/impl/MarkdownResourceProviderTest.java
b/mdresourceprovider/src/test/java/org/apache/sling/mdresource/impl/MarkdownResourceProviderTest.java
index 49e4476..543d3e7 100644
---
a/mdresourceprovider/src/test/java/org/apache/sling/mdresource/impl/MarkdownResourceProviderTest.java
+++
b/mdresourceprovider/src/test/java/org/apache/sling/mdresource/impl/MarkdownResourceProviderTest.java
@@ -60,6 +60,8 @@ public class MarkdownResourceProviderTest {
equalTo("<p>This is an example of a simple markdown file</p>\n"));
assertThat("valueMap[sling:resourceType]",
resource.getValueMap().get("sling:resourceType", String.class),
equalTo("sling/markdown/file"));
+ assertThat("valueMap[author]", resource.getValueMap().get("author",
String.class), equalTo("John Doe"));
+ assertThat("valueMap[keywords]",
resource.getValueMap().get("keywords", String[].class), equalTo(new String[]
{"news", "simple"}));
ValueMap adapted = resource.adaptTo(ValueMap.class);
assertThat("adapted ValueMap", adapted, notNullValue());
diff --git a/mdresourceprovider/src/test/resources/md-test/index.md
b/mdresourceprovider/src/test/resources/md-test/index.md
index 8d58cc2..fb452b2 100644
--- a/mdresourceprovider/src/test/resources/md-test/index.md
+++ b/mdresourceprovider/src/test/resources/md-test/index.md
@@ -1,3 +1,10 @@
+---
+author: John Doe
+keywords:
+ - news
+ - simple
+---
+
# Simple markdown file
This is an example of a simple markdown file
\ No newline at end of file