Change API of Preprocessor to return a Reader.
Add a Processor method to create a new Reader from a list of source lines.
MODIFY
asciidoctorj-api/src/main/java/org/asciidoctor/extension/BaseProcessor.java
asciidoctorj-api/src/main/java/org/asciidoctor/extension/BaseProcessor.java
package org.asciidoctor.extension;
import org.asciidoctor.Options;
-import org.asciidoctor.ast.Block;
-import org.asciidoctor.ast.Cell;
-import org.asciidoctor.ast.Column;
-import org.asciidoctor.ast.ContentNode;
-import org.asciidoctor.ast.DescriptionList;
-import org.asciidoctor.ast.Document;
-import org.asciidoctor.ast.ListItem;
-import org.asciidoctor.ast.PhraseNode;
-import org.asciidoctor.ast.Row;
-import org.asciidoctor.ast.Section;
-import org.asciidoctor.ast.StructuralNode;
-import org.asciidoctor.ast.Table;
+import org.asciidoctor.ast.*;
import org.asciidoctor.log.LogRecord;
import java.util.HashMap;
@@ -23,7 +12,7 @@ import java.util.ServiceLoader;
public class BaseProcessor implements Processor {
- private static ProcessorFactory processorFactory;
+ private static final ProcessorFactory processorFactory;
static {
Iterator<ProcessorFactory> iterator = ServiceLoader.load(ProcessorFactory.class).iterator();
@@ -33,7 +22,7 @@ public class BaseProcessor implements Processor {
processorFactory = iterator.next();
}
- private Processor delegate;
+ private final Processor delegate;
public BaseProcessor() {
this(new HashMap<>());
@@ -154,6 +143,41 @@ public class BaseProcessor implements Processor {
delegate.parseContent(parent, lines);
}
+ @Override
+ public Cursor newCursor(String file) {
+ return delegate.newCursor(file);
+ }
+
+ @Override
+ public Cursor newCursor(String file, String dir) {
+ return delegate.newCursor(file, dir);
+ }
+
+ @Override
+ public Cursor newCursor(String file, String dir, int lineno) {
+ return delegate.newCursor(file, dir, lineno);
+ }
+
+ @Override
+ public Reader newReader(List<String> source, Cursor cursor, Map<Object, Object> options) {
+ return delegate.newReader(source, cursor, options);
+ }
+
+ @Override
+ public Reader newReader(List<String> source, Cursor cursor) {
+ return delegate.newReader(source, cursor);
+ }
+
+ @Override
+ public Reader newReader(List<String> source, Map<Object, Object> options) {
+ return delegate.newReader(source, options);
+ }
+
+ @Override
+ public Reader newReader(List<String> source) {
+ return delegate.newReader(source);
+ }
+
// REMIND: Could be default method but Groovy (with compileStatic does not resolve default methods)
@Override

MODIFY
asciidoctorj-api/src/main/java/org/asciidoctor/extension/Preprocessor.java
asciidoctorj-api/src/main/java/org/asciidoctor/extension/Preprocessor.java
@@ -15,6 +15,6 @@ public abstract class Preprocessor extends BaseProcessor {
super(config);
}
- public abstract void process(Document document, PreprocessorReader reader);
+ public abstract Reader process(Document document, PreprocessorReader reader);
}

MODIFY
asciidoctorj-api/src/main/java/org/asciidoctor/extension/Processor.java
asciidoctorj-api/src/main/java/org/asciidoctor/extension/Processor.java
package org.asciidoctor.extension;
-import org.asciidoctor.ast.Block;
-import org.asciidoctor.ast.Cell;
-import org.asciidoctor.ast.Column;
-import org.asciidoctor.ast.ContentNode;
-import org.asciidoctor.ast.Document;
-import org.asciidoctor.ast.ListItem;
-import org.asciidoctor.ast.PhraseNode;
-import org.asciidoctor.ast.Row;
-import org.asciidoctor.ast.Section;
-import org.asciidoctor.ast.StructuralNode;
-import org.asciidoctor.ast.Table;
+import org.asciidoctor.ast.*;
import org.asciidoctor.log.LogRecord;
import java.util.List;
@@ -144,6 +134,20 @@ public interface Processor {
*/
void parseContent(StructuralNode parent, List<String> lines);
+ Cursor newCursor(String file);
+
+ Cursor newCursor(String file, String dir);
+
+ Cursor newCursor(String file, String dir, int lineno);
+
+ Reader newReader(List<String> source, Cursor cursor, Map<Object, Object> options);
+
+ Reader newReader(List<String> source, Cursor cursor);
+
+ Reader newReader(List<String> source, Map<Object, Object> options);
+
+ Reader newReader(List<String> source);
+
Map<String, Object> getConfig();
void log(LogRecord logRecord);

MODIFY
asciidoctorj-core/src/main/java/org/asciidoctor/jruby/extension/internal/JRubyProcessor.java
asciidoctorj-core/src/main/java/org/asciidoctor/jruby/extension/internal/JRubyProcessor.java
package org.asciidoctor.jruby.extension.internal;
import org.asciidoctor.Options;
-import org.asciidoctor.ast.Block;
-import org.asciidoctor.ast.Cell;
-import org.asciidoctor.ast.Column;
-import org.asciidoctor.ast.ContentModel;
-import org.asciidoctor.ast.ContentNode;
-import org.asciidoctor.ast.Document;
-import org.asciidoctor.ast.ListItem;
-import org.asciidoctor.ast.PhraseNode;
-import org.asciidoctor.ast.Row;
-import org.asciidoctor.ast.Section;
-import org.asciidoctor.ast.StructuralNode;
-import org.asciidoctor.ast.Table;
+import org.asciidoctor.ast.*;
import org.asciidoctor.extension.Processor;
import org.asciidoctor.extension.Reader;
-import org.asciidoctor.jruby.ast.impl.ColumnImpl;
-import org.asciidoctor.jruby.ast.impl.ContentNodeImpl;
-import org.asciidoctor.jruby.ast.impl.DescriptionListImpl;
-import org.asciidoctor.jruby.ast.impl.DocumentImpl;
-import org.asciidoctor.jruby.ast.impl.ListImpl;
-import org.asciidoctor.jruby.ast.impl.NodeConverter;
-import org.asciidoctor.jruby.ast.impl.RowImpl;
-import org.asciidoctor.jruby.ast.impl.StructuralNodeImpl;
-import org.asciidoctor.jruby.internal.JRubyAsciidoctor;
-import org.asciidoctor.jruby.internal.JRubyRuntimeContext;
-import org.asciidoctor.jruby.internal.RubyHashUtil;
-import org.asciidoctor.jruby.internal.RubyObjectWrapper;
-import org.asciidoctor.jruby.internal.RubyUtils;
+import org.asciidoctor.jruby.ast.impl.*;
+import org.asciidoctor.jruby.internal.*;
import org.asciidoctor.log.LogRecord;
-import org.jruby.Ruby;
-import org.jruby.RubyArray;
-import org.jruby.RubyFixnum;
-import org.jruby.RubyHash;
-import org.jruby.RubySymbol;
+import org.jruby.*;
import org.jruby.runtime.builtin.IRubyObject;
import java.util.ArrayList;
@@ -376,6 +350,60 @@ public class JRubyProcessor implements Processor {
}
}
+ public Cursor newCursor(String file) {
+ return newCursor(file, null);
+ }
+
+ public Cursor newCursor(String file, String dir) {
+ return newCursor(file, dir, 1);
+ }
+
+ public Cursor newCursor(String file, String dir, int lineNo) {
+ Ruby runtime = JRubyRuntimeContext.get(asciidoctor);
+ RubyClass klazz = runtime.getModule("Asciidoctor").getClass("Reader").getClass("Cursor");
+ IRubyObject rubyCursor = klazz.newInstance(runtime.getCurrentContext(), new IRubyObject[]{
+ runtime.newString(file),
+ dir == null ? runtime.getNil() : runtime.newString(dir),
+ runtime.newFixnum(lineNo),
+ }, org.jruby.runtime.Block.NULL_BLOCK);
+ return new CursorImpl(rubyCursor);
+ }
+
+ @Override
+ public Reader newReader(List<String> source, Cursor cursor) {
+ return newReader(source, cursor, null);
+ }
+
+ @Override
+ public Reader newReader(List<String> source, Map<Object, Object> options) {
+ return newReader(source, null, options);
+ }
+
+ @Override
+ public Reader newReader(List<String> source) {
+ return newReader(source, null, null);
+ }
+
+ @Override
+ public Reader newReader(List<String> source, Cursor cursor, Map<Object, Object> options) {
+ Ruby runtime = JRubyRuntimeContext.get(asciidoctor);
+ RubyClass klazz = runtime.getModule("Asciidoctor").getClass("Reader");
+
+ RubyHash convertMapToRubyHashWithSymbols = RubyHashUtil.convertMapToRubyHashWithSymbolsIfNecessary(runtime, options);
+
+ RubyArray data = runtime.newArray();
+ for (String line : source) {
+ data.add(runtime.newString(line));
+ }
+
+ IRubyObject rubyReader = klazz.newInstance(runtime.getCurrentContext(), new IRubyObject[]{
+ data,
+ cursor == null ? runtime.getNil() : ((RubyObjectWrapper) cursor).getRubyObject(),
+ convertMapToRubyHashWithSymbols
+ }, org.jruby.runtime.Block.NULL_BLOCK);
+ return new ReaderImpl(rubyReader);
+ }
+
private class Parser extends RubyObjectWrapper {
private final Reader reader;

MODIFY
asciidoctorj-core/src/main/java/org/asciidoctor/jruby/extension/processorproxies/PreprocessorProxy.java
asciidoctorj-core/src/main/java/org/asciidoctor/jruby/extension/processorproxies/PreprocessorProxy.java
@@ -2,11 +2,13 @@ package org.asciidoctor.jruby.extension.processorproxies;
import org.asciidoctor.ast.Document;
import org.asciidoctor.extension.Preprocessor;
+import org.asciidoctor.extension.Reader;
import org.asciidoctor.jruby.ast.impl.NodeConverter;
import org.asciidoctor.jruby.extension.internal.PreprocessorReaderImpl;
import org.asciidoctor.jruby.internal.JRubyAsciidoctor;
import org.asciidoctor.jruby.internal.RubyHashMapDecorator;
import org.asciidoctor.jruby.internal.RubyHashUtil;
+import org.asciidoctor.jruby.internal.RubyObjectWrapper;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyHash;
@@ -91,10 +93,14 @@ public class PreprocessorProxy extends AbstractProcessorProxy<Preprocessor> {
@JRubyMethod(name = "process", required = 2)
public IRubyObject process(ThreadContext context, IRubyObject document, IRubyObject preprocessorReader) {
- getProcessor().process(
+ PreprocessorReaderImpl readerArg = new PreprocessorReaderImpl(preprocessorReader);
+ Reader reader = getProcessor().process(
(Document) NodeConverter.createASTNode(document),
- new PreprocessorReaderImpl(preprocessorReader));
+ readerArg);
+ if (reader == null || reader == readerArg) {
return getRuntime().getNil();
}
+ return ((RubyObjectWrapper) reader).getRubyObject();
+ }
}

MODIFY
asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/RubyHashUtil.java
asciidoctorj-core/src/main/java/org/asciidoctor/jruby/internal/RubyHashUtil.java
@@ -24,6 +24,10 @@ public class RubyHashUtil {
RubyHash rubyHash = new RubyHash(rubyRuntime);
+ if (options == null) {
+ return rubyHash;
+ }
+
Set<Entry<Object, Object>> optionsSet = options.entrySet();
for (Entry<Object, Object> entry : optionsSet) {

MODIFY
asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenAPreprocessorIsRegistered.groovy
asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenAPreprocessorIsRegistered.groovy
@@ -32,7 +32,7 @@ $secondLine"""
asciidoctor.javaExtensionRegistry().preprocessor(new Preprocessor() {
@Override
- void process(Document doc, PreprocessorReader reader) {
+ Reader process(Document doc, PreprocessorReader reader) {
preprocessorCalled.set(true)
assert reader.peekLine() == firstLine
@@ -46,6 +46,7 @@ $secondLine"""
reader.advance()
assert reader.peekLine() == null
+ reader
}
})
@@ -63,7 +64,7 @@ $secondLine"""
asciidoctor.javaExtensionRegistry().preprocessor(new Preprocessor() {
@Override
- void process(Document doc, PreprocessorReader reader) {
+ Reader process(Document doc, PreprocessorReader reader) {
preprocessorCalled.set(true)
assert reader.peekLines(ONE) == [firstLine]
@@ -78,6 +79,7 @@ $secondLine"""
assert reader.peekLines(ONE) == []
assert reader.peekLines(TWO) == []
+ reader
}
})
@@ -95,12 +97,13 @@ $secondLine"""
asciidoctor.javaExtensionRegistry().preprocessor(new Preprocessor() {
@Override
- void process(Document doc, PreprocessorReader reader) {
+ Reader process(Document doc, PreprocessorReader reader) {
preprocessorCalled.set(true)
assert reader.readLine() == firstLine
assert reader.readLine() == secondLine
assert reader.readLine() == null
+ reader
}
})
@@ -120,7 +123,7 @@ $secondLine"""
asciidoctor.javaExtensionRegistry().preprocessor(new Preprocessor() {
@Override
- void process(Document doc, PreprocessorReader reader) {
+ Reader process(Document doc, PreprocessorReader reader) {
preprocessorCalled.set(true)
assert reader.peekLine() == firstLine
@@ -131,6 +134,7 @@ $secondLine"""
assert reader.readLine() == anotherLine
assert reader.peekLine() == firstLine
assert reader.readLine() == firstLine
+ reader
}
})
@@ -151,7 +155,7 @@ $secondLine"""
asciidoctor.javaExtensionRegistry().preprocessor(new Preprocessor() {
@Override
- void process(Document doc, PreprocessorReader reader) {
+ Reader process(Document doc, PreprocessorReader reader) {
preprocessorCalled.set(true)
assert reader.peekLine() == firstLine
@@ -164,6 +168,7 @@ $secondLine"""
assert reader.readLine() == anotherSecondLine
assert reader.peekLine() == firstLine
assert reader.readLine() == firstLine
+ reader
}
})

MODIFY
asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenAPreprocessorSetsTheSourceMapOption.groovy
asciidoctorj-core/src/test/groovy/org/asciidoctor/extension/WhenAPreprocessorSetsTheSourceMapOption.groovy
@@ -18,8 +18,9 @@ class WhenAPreprocessorSetsTheSourceMapOption extends Specification {
static class SourceMapOptionPreprocessor extends Preprocessor {
@Override
- void process(Document document, PreprocessorReader reader) {
+ Reader process(Document document, PreprocessorReader reader) {
document.sourcemap = true
+ reader
}
}

MODIFY
asciidoctorj-core/src/test/java/org/asciidoctor/extension/ChangeAttributeValuePreprocessor.java
asciidoctorj-core/src/test/java/org/asciidoctor/extension/ChangeAttributeValuePreprocessor.java
@@ -11,10 +11,11 @@ public class ChangeAttributeValuePreprocessor extends Preprocessor {
}
@Override
- public void process(Document document,
+ public Reader process(Document document,
PreprocessorReader reader) {
document.getAttributes().put("content", "Alex");
+ return reader;
}
}
\ No newline at end of file

MODIFY
asciidoctorj-core/src/test/java/org/asciidoctor/extension/FrontMatterPreprocessor.java
asciidoctorj-core/src/test/java/org/asciidoctor/extension/FrontMatterPreprocessor.java
@@ -8,10 +8,10 @@ import java.util.List;
public class FrontMatterPreprocessor extends Preprocessor {
@Override
- public void process(Document document, PreprocessorReader reader) {
+ public Reader process(Document document, PreprocessorReader reader) {
List<String> lines = reader.getLines();
if (lines.isEmpty()) {
- return;
+ return reader;
}
List<String> frontMatter = new ArrayList<>();
if ("---".equals(lines.get(0).trim())) {
@@ -28,5 +28,6 @@ public class FrontMatterPreprocessor extends Preprocessor {
}
}
}
+ return reader;
}
}

ADD
asciidoctorj-core/src/test/java/org/asciidoctor/extension/HardBreakPreprocessor.java
+package org.asciidoctor.extension;
+
+import org.asciidoctor.ast.Document;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class HardBreakPreprocessor extends Preprocessor {
+
+ @Override
+ public Reader process(Document document, PreprocessorReader reader) {
+ List<String> lines = reader.getLines();
+ return newReader(lines.stream()
+ .map(line -> line.isEmpty() || line.startsWith("=") ? line : line + " +")
+ .collect(Collectors.toList()));
+ }
+}

MODIFY
asciidoctorj-core/src/test/java/org/asciidoctor/extension/HasMoreLinesPreprocessor.java
asciidoctorj-core/src/test/java/org/asciidoctor/extension/HasMoreLinesPreprocessor.java
@@ -4,8 +4,8 @@ import org.asciidoctor.ast.Document;
import java.util.Map;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
public class HasMoreLinesPreprocessor extends Preprocessor {
@@ -14,10 +14,11 @@ public class HasMoreLinesPreprocessor extends Preprocessor {
}
@Override
- public void process(Document document,
+ public Reader process(Document document,
PreprocessorReader reader) {
- assertThat(reader.hasMoreLines(), is(true));
+ assertThat(reader.hasMoreLines()).isTrue();
+ return reader;
}
}

MODIFY
asciidoctorj-core/src/test/java/org/asciidoctor/extension/NextLineEmptyPreprocessor.java
asciidoctorj-core/src/test/java/org/asciidoctor/extension/NextLineEmptyPreprocessor.java
@@ -14,7 +14,7 @@ public class NextLineEmptyPreprocessor extends Preprocessor {
}
@Override
- public void process(Document document,
+ public Reader process(Document document,
PreprocessorReader reader) {
assertThat(reader.isNextLineEmpty(), is(false));
@@ -22,6 +22,8 @@ public class NextLineEmptyPreprocessor extends Preprocessor {
reader.advance();
assertThat(reader.isNextLineEmpty(), is(true));
+
+ return reader;
}
}

MODIFY
asciidoctorj-core/src/test/java/org/asciidoctor/extension/NumberLinesPreprocessor.java
asciidoctorj-core/src/test/java/org/asciidoctor/extension/NumberLinesPreprocessor.java
@@ -2,8 +2,8 @@ package org.asciidoctor.extension;
import org.asciidoctor.ast.Document;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
public class NumberLinesPreprocessor extends Preprocessor {
@@ -11,7 +11,8 @@ public class NumberLinesPreprocessor extends Preprocessor {
}
@Override
- public void process(Document document, PreprocessorReader reader) {
- assertThat(reader.getLineNumber(), is(1));
+ public Reader process(Document document, PreprocessorReader reader) {
+ assertThat(reader.getLineNumber()).isEqualTo(1);
+ return reader;
}
}

RENAME
asciidoctorj-core/src/test/java/org/asciidoctor/extension/FrontMatterPreprocessorTest.java
asciidoctorj-core/src/test/java/org/asciidoctor/extension/PreprocessorTest.java
@@ -15,7 +15,7 @@ import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(Arquillian.class)
-public class FrontMatterPreprocessorTest {
+public class PreprocessorTest {
@ArquillianResource
private Asciidoctor asciidoctor;
@@ -63,4 +63,19 @@ public class FrontMatterPreprocessorTest {
assertThat(body.getElementsByTag("p").first().text())
.startsWith("This is a sample page");
}
+
+ @Test
+ public void shouldUseNewReader() {
+ // Given: the HardBreakPreprocessor
+ asciidoctor.javaExtensionRegistry().preprocessor(HardBreakPreprocessor.class);
+
+ // And a document
+ String document = "Ruby is red.\n" +
+ "Java is beige.";
+
+ // When the document is converted
+ String html = asciidoctor.convert(document, Options.builder().toFile(false).build());
+ org.jsoup.nodes.Document htmlDoc = Jsoup.parse(html);
+ assertThat(htmlDoc.getElementsByTag("br")).hasSize(2);
+ }
}

MODIFY
asciidoctorj-documentation/src/test/java/org/asciidoctor/integrationguide/extension/CommentPreprocessor.java
asciidoctorj-documentation/src/test/java/org/asciidoctor/integrationguide/extension/CommentPreprocessor.java
@@ -4,6 +4,7 @@ package org.asciidoctor.integrationguide.extension;
import org.asciidoctor.ast.Document;
import org.asciidoctor.extension.Preprocessor;
import org.asciidoctor.extension.PreprocessorReader;
+import org.asciidoctor.extension.Reader;
import java.util.ArrayList;
import java.util.List;
@@ -11,7 +12,7 @@ import java.util.List;
public class CommentPreprocessor extends Preprocessor { // <1>
@Override
- public void process(Document document, PreprocessorReader reader) {
+ public Reader process(Document document, PreprocessorReader reader) {
List<String> lines = reader.readLines(); // <2>
List<String> newLines = new ArrayList<String>();
@@ -31,6 +32,7 @@ public class CommentPreprocessor extends Preprocessor { // <1>
}
reader.restoreLines(newLines); // <4>
+ return reader;
}
}
//end::include[]

Change API of Preprocessor to return a Reader.
Add a Processor method to create a new Reader from a list of source lines.