Document required changes when migrating existing preprocessors to 3.x.
Update example preprocessor to use newReader().
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.*;
+import org.asciidoctor.ast.Block;
+import org.asciidoctor.ast.Cell;
+import org.asciidoctor.ast.Column;
+import org.asciidoctor.ast.ContentNode;
+import org.asciidoctor.ast.Cursor;
+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.log.LogRecord;
import java.util.HashMap;

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.*;
+import org.asciidoctor.ast.Block;
+import org.asciidoctor.ast.Cell;
+import org.asciidoctor.ast.Column;
+import org.asciidoctor.ast.ContentNode;
+import org.asciidoctor.ast.Cursor;
+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.log.LogRecord;
import java.util.List;

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.*;
+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.Cursor;
+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.extension.Processor;
import org.asciidoctor.extension.Reader;
-import org.asciidoctor.jruby.ast.impl.*;
-import org.asciidoctor.jruby.internal.*;
+import org.asciidoctor.jruby.ast.impl.ColumnImpl;
+import org.asciidoctor.jruby.ast.impl.ContentNodeImpl;
+import org.asciidoctor.jruby.ast.impl.CursorImpl;
+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.log.LogRecord;
-import org.jruby.*;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyHash;
+import org.jruby.RubySymbol;
import org.jruby.runtime.builtin.IRubyObject;
import java.util.ArrayList;

MODIFY
asciidoctorj-core/src/test/java/org/asciidoctor/extension/NextLineEmptyPreprocessor.java
asciidoctorj-core/src/test/java/org/asciidoctor/extension/NextLineEmptyPreprocessor.java
@@ -4,8 +4,8 @@ import org.asciidoctor.ast.Document;
import java.util.Map;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
public class NextLineEmptyPreprocessor extends Preprocessor {
@@ -17,11 +17,11 @@ public class NextLineEmptyPreprocessor extends Preprocessor {
public Reader process(Document document,
PreprocessorReader reader) {
- assertThat(reader.isNextLineEmpty(), is(false));
+ assertFalse(reader.isNextLineEmpty());
reader.advance();
- assertThat(reader.isNextLineEmpty(), is(true));
+ assertTrue(reader.isNextLineEmpty());
return reader;
}

MODIFY
asciidoctorj-documentation/src/test/java/org/asciidoctor/integrationguide/extension/CommentPreprocessor.java
asciidoctorj-documentation/src/test/java/org/asciidoctor/integrationguide/extension/CommentPreprocessor.java
@@ -31,8 +31,7 @@ public class CommentPreprocessor extends Preprocessor { // <1>
}
}
- reader.restoreLines(newLines); // <4>
- return reader;
+ return newReader(newLines); // <4>
}
}
//end::include[]

MODIFY
build.gradle
build.gradle
@@ -72,7 +72,7 @@ ext {
jrubyVersion = '9.3.4.0'
jsoupVersion = '1.14.3'
junitVersion = '4.13.2'
- assertjVersion = '3.22.0'
+ assertjVersion = '3.23.1'
nettyVersion = '4.1.58.Final'
saxonVersion = '9.9.0-2'
xmlMatchersVersion = '1.0-RC1'

MODIFY
docs/modules/extensions/pages/preprocessor.adoc
docs/modules/extensions/pages/preprocessor.adoc
@@ -33,7 +33,7 @@ include::example$org/asciidoctor/integrationguide/extension/CommentPreprocessor.
<2> The implementation gets the whole Asciidoctor source as an array of Strings where each entry corresponds to one line.
<3> Every odd occurrence of a comment start is replaced by opening an admonition block, every even occurrence is closing it.
The new content is collected in a new list.
-<4> The processed content is restored to the original `PreprocessorReader` so that it replaces the content that was already consumed at the beginning of the method.
+<4> A new Reader containing the processed content is returned so that it replaces the content that was already consumed at the beginning of the method.
There may be multiple Preprocessors registered and every Preprocessor will be called.
The order in which the Preprocessors are called is undefined so that all Preprocessors should be independent of each other.

ADD
docs/modules/guides/pages/extension-migration-guide-2x-to-30.adoc
+= Extension Migration: 1.6.x to 2.0.x
+:url-base-1-5: https://github.com/asciidoctor/asciidoctorj/blob/v1.5.8.1
+:url-base-1-6: https://github.com/asciidoctor/asciidoctorj/blob/v1.6.0
+
+Between 2.x and 3.0.x there are some changes that may affect existing extensions.
+This guide will illustrate the changes.
+
+== Preprocessor API
+
+In AsciidoctorJ 1.x and 2.x a Preprocessor could not return anything from its process() method.
+This deviated from the API offered by Asciidoctor itself which allows to replace the original Reader.
+Certain use cases were very hard or even impossible to achieve.
+
+Therefore the signature of the method `Preprocessor.process()` was changed so that it now may return a new Reader.
+This causes all existing Preprocessor implementation to fail compilation.
+The fix for this is very simple: It is sufficient to change the method to return `null` or the given reader.
+
+Let's assume that a Preprocessor originally looks like this:
+
+[source,java]
+----
+public class ChangeAttributeValuePreprocessor extends Preprocessor {
+
+ public ChangeAttributeValuePreprocessor(Map<String, Object> config) {
+ super(config);
+ }
+
+ @Override
+ public void process(Document document, PreprocessorReader reader) {
+ document.getAttributes().put("myattribute", "myvalue");
+ }
+}
+----
+
+To make this Preprocessor compile and run with AsciidoctorJ 3.x it has to be changed like this
+
+[source,java]
+----
+public class ChangeAttributeValuePreprocessor extends Preprocessor {
+
+ public ChangeAttributeValuePreprocessor(Map<String, Object> config) {
+ super(config);
+ }
+
+ @Override
+ public Reader process(Document document, PreprocessorReader reader) { // <1>
+ document.getAttributes().put("myattribute", "myvalue");
+ return reader; // <2>
+ }
+}
+----
+<1> Change the method signature to return a `Reader` instead of `void`.
+<2> Directly return the passed Reader.

Document required changes when migrating existing preprocessors to 3.x.
Update example preprocessor to use newReader().