This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch feature/sink-wrapper
in repository https://gitbox.apache.org/repos/asf/maven-doxia.git

commit 39b177c60d0dbf2b26c2591d8c5c34abc40d0ba4
Author: Konrad Windszus <[email protected]>
AuthorDate: Sat Dec 23 19:47:07 2023 +0100

    [DOXIA-711] Introduce SinkWrapper and according SinkWrapperFactory
    
    Add a BufferingSinkProxyWrapper which buffers all calls on the
    underlying sink until it is flushed.
---
 .../apache/maven/doxia/parser/AbstractParser.java  |  47 +-
 .../maven/doxia/parser/AbstractXmlParser.java      |   2 +-
 .../doxia/parser/BufferingSinkProxyFactory.java    | 112 +++
 .../java/org/apache/maven/doxia/parser/Parser.java |  15 +
 .../org/apache/maven/doxia/parser/SinkWrapper.java | 529 +++++++++++
 .../maven/doxia/parser/SinkWrapperFactory.java     |  43 +
 .../maven/doxia/sink/impl/RandomAccessSink.java    | 965 +--------------------
 .../sink/impl/SinkWrapperFactoryComparator.java    |  33 +
 .../maven/doxia/parser/AbstractParserTest.java     |  61 +-
 .../apache/maven/doxia/module/apt/AptParser.java   |   2 +-
 .../apache/maven/doxia/module/fml/FmlParser.java   |   2 +-
 .../doxia/module/markdown/MarkdownParser.java      |   2 +-
 12 files changed, 850 insertions(+), 963 deletions(-)

diff --git 
a/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractParser.java 
b/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractParser.java
index 1e868135..0c28241a 100644
--- a/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractParser.java
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractParser.java
@@ -25,6 +25,9 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
 import java.io.StringReader;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.PriorityQueue;
 import java.util.Properties;
 
 import org.apache.maven.doxia.macro.Macro;
@@ -33,6 +36,7 @@ import org.apache.maven.doxia.macro.MacroRequest;
 import org.apache.maven.doxia.macro.manager.MacroManager;
 import org.apache.maven.doxia.macro.manager.MacroNotFoundException;
 import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.impl.SinkWrapperFactoryComparator;
 
 /**
  * An abstract base class that defines some convenience methods for parsers.
@@ -48,11 +52,18 @@ public abstract class AbstractParser implements Parser {
     @Inject
     private MacroManager macroManager;
 
+    @Inject
+    private Collection<SinkWrapperFactory> 
automaticallyRegisteredSinkWrapperFactories;
+
+    private final Collection<SinkWrapperFactory> 
manuallyRegisteredSinkWrapperFactories = new LinkedList<>();
+
     /**
      * Emit Doxia comment events when parsing comments?
      */
     private boolean emitComments = true;
 
+    private boolean emitAnchors = false;
+
     private static final String DOXIA_VERSION;
 
     static {
@@ -171,7 +182,25 @@ public abstract class AbstractParser implements Parser {
     }
 
     /**
-     * Set <code>secondParsing</code> to true, if we need a second parsing.
+     * Retrieves the sink pipeline built from all registered {@link 
SinkWrapperFactory} objects.
+     * For secondary parsers (i.e. ones with {@link #isSecondParsing()} 
returning {@code true} just the given original sink is returned.
+     * @param sink
+     * @return the Sink pipeline to be used
+     */
+    protected Sink getWrappedSink(Sink sink) {
+        // no wrapping for secondary parsing
+        if (secondParsing) {
+            return sink;
+        }
+        Sink currentSink = sink;
+        for (SinkWrapperFactory factory : getSinkWrapperFactories()) {
+            currentSink = factory.createWrapper(currentSink);
+        }
+        return currentSink;
+    }
+
+    /**
+     * Set <code>secondParsing</code> to true, if this represents a secondary 
parsing of the same source.
      *
      * @param second true for second parsing
      */
@@ -189,6 +218,22 @@ public abstract class AbstractParser implements Parser {
         return secondParsing;
     }
 
+    @Override
+    public void addSinkWrapperFactory(SinkWrapperFactory factory) {
+        manuallyRegisteredSinkWrapperFactories.add(factory);
+    }
+
+    @Override
+    public Collection<SinkWrapperFactory> getSinkWrapperFactories() {
+        PriorityQueue<SinkWrapperFactory> effectiveSinkWrapperFactories =
+                new PriorityQueue<>(new SinkWrapperFactoryComparator());
+        if (automaticallyRegisteredSinkWrapperFactories != null) {
+            
effectiveSinkWrapperFactories.addAll(automaticallyRegisteredSinkWrapperFactories);
+        }
+        
effectiveSinkWrapperFactories.addAll(manuallyRegisteredSinkWrapperFactories);
+        return effectiveSinkWrapperFactories;
+    }
+
     /**
      * Gets the current {@link MacroManager}.
      *
diff --git 
a/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractXmlParser.java 
b/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractXmlParser.java
index b4cbbfaa..d0d143d6 100644
--- 
a/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractXmlParser.java
+++ 
b/doxia-core/src/main/java/org/apache/maven/doxia/parser/AbstractXmlParser.java
@@ -131,7 +131,7 @@ public abstract class AbstractXmlParser extends 
AbstractParser implements XmlMar
             // Note: do it after input is set, otherwise values are reset
             initXmlParser(parser);
 
-            parseXml(parser, sink);
+            parseXml(parser, getWrappedSink(sink));
         } catch (XmlPullParserException ex) {
             throw new ParseException("Error parsing the model", ex, 
ex.getLineNumber(), ex.getColumnNumber());
         } catch (MacroExecutionException ex) {
diff --git 
a/doxia-core/src/main/java/org/apache/maven/doxia/parser/BufferingSinkProxyFactory.java
 
b/doxia-core/src/main/java/org/apache/maven/doxia/parser/BufferingSinkProxyFactory.java
new file mode 100644
index 00000000..4112ad31
--- /dev/null
+++ 
b/doxia-core/src/main/java/org/apache/maven/doxia/parser/BufferingSinkProxyFactory.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.doxia.parser;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.LinkedList;
+import java.util.Queue;
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * Buffers all method calls on the wrapped Sink until the proxy sink's method 
{@link Sink#flush()} is called.
+ */
+public class BufferingSinkProxyFactory implements SinkWrapperFactory {
+
+    private static final class MethodWithArguments {
+        private final Method method;
+        private final Object[] args;
+
+        MethodWithArguments(Method method, Object[] args) {
+            super();
+            this.method = method;
+            this.args = args;
+        }
+
+        void invoke(Object object) {
+            try {
+                method.invoke(object, args);
+            } catch (IllegalAccessException | IllegalArgumentException | 
InvocationTargetException e) {
+                throw new IllegalStateException("Could not call buffered 
method " + method, e);
+            }
+        }
+    }
+
+    public interface BufferingSink extends Sink {
+        // just a marker interface
+        Sink getBufferedSink();
+    }
+
+    private static final class BufferingSinkProxy implements InvocationHandler 
{
+        private final Queue<MethodWithArguments> bufferedInvocations;
+        private final Sink delegate;
+        private static final Method FLUSH_METHOD;
+        private static final Method GET_BUFFERED_SINK_METHOD;
+
+        static {
+            try {
+                FLUSH_METHOD = Sink.class.getMethod("flush");
+                GET_BUFFERED_SINK_METHOD = 
BufferingSink.class.getMethod("getBufferedSink");
+            } catch (NoSuchMethodException | SecurityException e) {
+                throw new IllegalStateException("Could not find flush method 
in Sink!", e);
+            }
+        }
+
+        BufferingSinkProxy(Sink delegate) {
+            bufferedInvocations = new LinkedList<>();
+            this.delegate = delegate;
+        }
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
+            if (method.equals(FLUSH_METHOD)) {
+                bufferedInvocations.forEach(i -> i.invoke(delegate));
+                bufferedInvocations.clear();
+            } else if (method.equals(GET_BUFFERED_SINK_METHOD)) {
+                return delegate;
+            } else {
+                bufferedInvocations.add(new MethodWithArguments(method, args));
+            }
+            return null;
+        }
+    }
+
+    @Override
+    public Sink createWrapper(Sink delegate) {
+        BufferingSinkProxy proxy = new BufferingSinkProxy(delegate);
+        return (Sink) Proxy.newProxyInstance(
+                delegate.getClass().getClassLoader(), new Class<?>[] 
{BufferingSink.class}, proxy);
+    }
+
+    public static BufferingSink castAsBufferingSink(Sink sink) {
+        if (sink instanceof BufferingSink) {
+            return (BufferingSink) sink;
+        } else {
+            throw new IllegalArgumentException("The given sink is no 
BufferingSink but a " + sink.getClass());
+        }
+    }
+
+    @Override
+    public int getPriority() {
+        return 0;
+    }
+}
diff --git a/doxia-core/src/main/java/org/apache/maven/doxia/parser/Parser.java 
b/doxia-core/src/main/java/org/apache/maven/doxia/parser/Parser.java
index e09953ed..ea80bb8e 100644
--- a/doxia-core/src/main/java/org/apache/maven/doxia/parser/Parser.java
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/Parser.java
@@ -19,6 +19,7 @@
 package org.apache.maven.doxia.parser;
 
 import java.io.Reader;
+import java.util.Collection;
 
 import org.apache.maven.doxia.sink.Sink;
 
@@ -83,4 +84,18 @@ public interface Parser {
      * @return <code>true</code> (default value) if comment Doxia events are 
emitted
      */
     boolean isEmitComments();
+
+    /**
+     * Registers a given {@link SinkWrapperFactory} with the parser used in 
subsequent calls of {@code parse(...)}
+     * @param factory the factory to create the sink wrapper
+     * @since 2.0.0
+     */
+    void addSinkWrapperFactory(SinkWrapperFactory factory);
+
+    /**
+     *
+     * @return all sink wrapper factories in the correct order (both 
registered automatically and manually)
+     * @since 2.0.0
+     */
+    Collection<SinkWrapperFactory> getSinkWrapperFactories();
 }
diff --git 
a/doxia-core/src/main/java/org/apache/maven/doxia/parser/SinkWrapper.java 
b/doxia-core/src/main/java/org/apache/maven/doxia/parser/SinkWrapper.java
new file mode 100644
index 00000000..6d793e5f
--- /dev/null
+++ b/doxia-core/src/main/java/org/apache/maven/doxia/parser/SinkWrapper.java
@@ -0,0 +1,529 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.doxia.parser;
+
+import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.sink.impl.AbstractSink;
+
+/**
+ * By default a {@link SinkWrapper} just delegates each method to the wrapped 
sink's method.
+ * For certain sink methods a derived wrapper may modify the sink before/after 
or instead of calling the delegate's method.
+ * Sink wrappers can either be registered manually via {@link 
Parser#registerSinkWrapperFactory(int, SinkWrapperFactory)} or
+ * are automatically registered if provided as JSR330 component.
+ * In addition Sink wrappers can be used programmatically without an according 
factory.
+ * @since 2.0.0
+ */
+public class SinkWrapper extends AbstractSink {
+
+    private Sink delegate;
+
+    public SinkWrapper(Sink delegate) {
+        super();
+        this.delegate = delegate;
+    }
+
+    public Sink getWrappedSink() {
+        return delegate;
+    }
+
+    public void setWrappedSink(Sink sink) {
+        delegate = sink;
+    }
+
+    @Override
+    public void head(SinkEventAttributes attributes) {
+        delegate.head(attributes);
+    }
+
+    @Override
+    public void head_() {
+        delegate.head_();
+    }
+
+    @Override
+    public void title(SinkEventAttributes attributes) {
+        delegate.title(attributes);
+    }
+
+    @Override
+    public void title_() {
+        delegate.title_();
+    }
+
+    @Override
+    public void author(SinkEventAttributes attributes) {
+        delegate.author(attributes);
+    }
+
+    @Override
+    public void author_() {
+        delegate.author_();
+    }
+
+    @Override
+    public void date(SinkEventAttributes attributes) {
+        delegate.date(attributes);
+    }
+
+    @Override
+    public void date_() {
+        delegate.date_();
+    }
+
+    @Override
+    public void body(SinkEventAttributes attributes) {
+        delegate.body(attributes);
+    }
+
+    @Override
+    public void body_() {
+        delegate.body_();
+    }
+
+    @Override
+    public void article(SinkEventAttributes attributes) {
+        delegate.article(attributes);
+    }
+
+    @Override
+    public void article_() {
+        delegate.article_();
+    }
+
+    @Override
+    public void navigation(SinkEventAttributes attributes) {
+        delegate.navigation(attributes);
+    }
+
+    @Override
+    public void navigation_() {
+        delegate.navigation_();
+    }
+
+    @Override
+    public void sidebar(SinkEventAttributes attributes) {
+        delegate.sidebar(attributes);
+    }
+
+    @Override
+    public void sidebar_() {
+        delegate.sidebar_();
+    }
+
+    @Override
+    public void section(int level, SinkEventAttributes attributes) {
+        delegate.section(level, attributes);
+    }
+
+    @Override
+    public void section_(int level) {
+        delegate.section_(level);
+    }
+
+    @Override
+    public void sectionTitle(int level, SinkEventAttributes attributes) {
+        delegate.sectionTitle(level, attributes);
+    }
+
+    @Override
+    public void sectionTitle_(int level) {
+        delegate.sectionTitle_(level);
+    }
+
+    @Override
+    public void header(SinkEventAttributes attributes) {
+        delegate.header(attributes);
+    }
+
+    @Override
+    public void header_() {
+        delegate.header_();
+    }
+
+    @Override
+    public void content(SinkEventAttributes attributes) {
+        delegate.content(attributes);
+    }
+
+    @Override
+    public void content_() {
+        delegate.content_();
+    }
+
+    @Override
+    public void footer(SinkEventAttributes attributes) {
+        delegate.footer(attributes);
+    }
+
+    @Override
+    public void footer_() {
+        delegate.footer_();
+    }
+
+    @Override
+    public void list(SinkEventAttributes attributes) {
+        delegate.list(attributes);
+    }
+
+    @Override
+    public void list_() {
+        delegate.list_();
+    }
+
+    @Override
+    public void listItem(SinkEventAttributes attributes) {
+        delegate.listItem(attributes);
+    }
+
+    @Override
+    public void listItem_() {
+        delegate.listItem_();
+    }
+
+    @Override
+    public void numberedList(int numbering, SinkEventAttributes attributes) {
+        delegate.numberedList(numbering, attributes);
+    }
+
+    @Override
+    public void numberedList_() {
+        delegate.numberedList_();
+    }
+
+    @Override
+    public void numberedListItem(SinkEventAttributes attributes) {
+        delegate.numberedListItem(attributes);
+    }
+
+    @Override
+    public void numberedListItem_() {
+        delegate.numberedListItem_();
+    }
+
+    @Override
+    public void definitionList(SinkEventAttributes attributes) {
+        delegate.definitionList(attributes);
+    }
+
+    @Override
+    public void definitionList_() {
+        delegate.definitionList_();
+    }
+
+    @Override
+    public void definitionListItem(SinkEventAttributes attributes) {
+        delegate.definitionListItem(attributes);
+    }
+
+    @Override
+    public void definitionListItem_() {
+        delegate.definitionListItem_();
+    }
+
+    @Override
+    public void definition(SinkEventAttributes attributes) {
+        delegate.definition(attributes);
+    }
+
+    @Override
+    public void definition_() {
+        delegate.definition_();
+    }
+
+    @Override
+    public void definedTerm(SinkEventAttributes attributes) {
+        delegate.definedTerm(attributes);
+    }
+
+    @Override
+    public void definedTerm_() {
+        delegate.definedTerm_();
+    }
+
+    @Override
+    public void figure(SinkEventAttributes attributes) {
+        delegate.figure(attributes);
+    }
+
+    @Override
+    public void figure_() {
+        delegate.figure_();
+    }
+
+    @Override
+    public void figureCaption(SinkEventAttributes attributes) {
+        delegate.figureCaption(attributes);
+    }
+
+    @Override
+    public void figureCaption_() {
+        delegate.figureCaption_();
+    }
+
+    @Override
+    public void figureGraphics(String src, SinkEventAttributes attributes) {
+        delegate.figureGraphics(src, attributes);
+    }
+
+    @Override
+    public void table(SinkEventAttributes attributes) {
+        delegate.table(attributes);
+    }
+
+    @Override
+    public void table_() {
+        delegate.table_();
+    }
+
+    @Override
+    public void tableRows(int[] justification, boolean grid) {
+        delegate.tableRows(justification, grid);
+    }
+
+    @Override
+    public void tableRows_() {
+        delegate.tableRows_();
+    }
+
+    @Override
+    public void tableRow(SinkEventAttributes attributes) {
+        delegate.tableRow(attributes);
+    }
+
+    @Override
+    public void tableRow_() {
+        delegate.tableRow_();
+    }
+
+    @Override
+    public void tableCell(SinkEventAttributes attributes) {
+        delegate.tableCell(attributes);
+    }
+
+    @Override
+    public void tableCell_() {
+        delegate.tableCell_();
+    }
+
+    @Override
+    public void tableHeaderCell(SinkEventAttributes attributes) {
+        delegate.tableHeaderCell(attributes);
+    }
+
+    @Override
+    public void tableHeaderCell_() {
+        delegate.tableHeaderCell_();
+    }
+
+    @Override
+    public void tableCaption(SinkEventAttributes attributes) {
+        delegate.tableCaption(attributes);
+    }
+
+    @Override
+    public void tableCaption_() {
+        delegate.tableCaption_();
+    }
+
+    @Override
+    public void paragraph(SinkEventAttributes attributes) {
+        delegate.paragraph(attributes);
+    }
+
+    @Override
+    public void paragraph_() {
+        delegate.paragraph_();
+    }
+
+    @Override
+    public void data(String value, SinkEventAttributes attributes) {
+        delegate.data(value, attributes);
+    }
+
+    @Override
+    public void data_() {
+        delegate.data_();
+    }
+
+    @Override
+    public void time(String datetime, SinkEventAttributes attributes) {
+        delegate.time(datetime, attributes);
+    }
+
+    @Override
+    public void time_() {
+        delegate.time_();
+    }
+
+    @Override
+    public void address(SinkEventAttributes attributes) {
+        delegate.address(attributes);
+    }
+
+    @Override
+    public void address_() {
+        delegate.address_();
+    }
+
+    @Override
+    public void blockquote(SinkEventAttributes attributes) {
+        delegate.blockquote(attributes);
+    }
+
+    @Override
+    public void blockquote_() {
+        delegate.blockquote_();
+    }
+
+    @Override
+    public void division(SinkEventAttributes attributes) {
+        delegate.division(attributes);
+    }
+
+    @Override
+    public void division_() {
+        delegate.division_();
+    }
+
+    @Override
+    public void verbatim(SinkEventAttributes attributes) {
+        delegate.verbatim(attributes);
+    }
+
+    @Override
+    public void verbatim_() {
+        delegate.verbatim_();
+    }
+
+    @Override
+    public void horizontalRule(SinkEventAttributes attributes) {
+        delegate.horizontalRule(attributes);
+    }
+
+    @Override
+    public void pageBreak() {
+        delegate.pageBreak();
+    }
+
+    @Override
+    public void anchor(String name, SinkEventAttributes attributes) {
+        delegate.anchor(name, attributes);
+    }
+
+    @Override
+    public void anchor_() {
+        delegate.anchor_();
+    }
+
+    @Override
+    public void link(String name, SinkEventAttributes attributes) {
+        delegate.link(name, attributes);
+    }
+
+    @Override
+    public void link_() {
+        delegate.link_();
+    }
+
+    @Override
+    public void inline(SinkEventAttributes attributes) {
+        delegate.inline(attributes);
+    }
+
+    @Override
+    public void inline_() {
+        delegate.inline_();
+    }
+
+    @Override
+    public void italic() {
+        delegate.italic();
+    }
+
+    @Override
+    public void italic_() {
+        delegate.italic_();
+    }
+
+    @Override
+    public void bold() {
+        delegate.bold();
+    }
+
+    @Override
+    public void bold_() {
+        delegate.bold_();
+    }
+
+    @Override
+    public void monospaced() {
+        delegate.monospaced();
+    }
+
+    @Override
+    public void monospaced_() {
+        delegate.monospaced_();
+    }
+
+    @Override
+    public void lineBreak(SinkEventAttributes attributes) {
+        delegate.lineBreak(attributes);
+    }
+
+    @Override
+    public void lineBreakOpportunity(SinkEventAttributes attributes) {
+        delegate.lineBreakOpportunity(attributes);
+    }
+
+    @Override
+    public void nonBreakingSpace() {
+        delegate.nonBreakingSpace();
+    }
+
+    @Override
+    public void text(String text, SinkEventAttributes attributes) {
+        delegate.text(text, attributes);
+    }
+
+    @Override
+    public void rawText(String text) {
+        delegate.rawText(text);
+    }
+
+    @Override
+    public void comment(String comment) {
+        delegate.comment(comment);
+    }
+
+    @Override
+    public void unknown(String name, Object[] requiredParams, 
SinkEventAttributes attributes) {
+        delegate.unknown(name, requiredParams, attributes);
+    }
+
+    @Override
+    public void flush() {
+        delegate.flush();
+    }
+
+    @Override
+    public void close() {
+        delegate.close();
+    }
+}
diff --git 
a/doxia-core/src/main/java/org/apache/maven/doxia/parser/SinkWrapperFactory.java
 
b/doxia-core/src/main/java/org/apache/maven/doxia/parser/SinkWrapperFactory.java
new file mode 100644
index 00000000..c6082c17
--- /dev/null
+++ 
b/doxia-core/src/main/java/org/apache/maven/doxia/parser/SinkWrapperFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.doxia.parser;
+
+import org.apache.maven.doxia.sink.Sink;
+
+/**
+ * A factory for a sink wrapping another sink.
+ * The delegate may either be the original sink or another wrapper.
+ * @since 2.0.0
+ */
+public interface SinkWrapperFactory {
+
+    /**
+     * By default all wrappers just delegate each method to the wrapped sink's 
method.
+     * For certain Sink methods the wrapper may modify the sink before/after 
or instead of calling the delegate's method.
+     * @param sink the delegate
+     * @return a new sink wrapping the given one
+     */
+    Sink createWrapper(Sink sink);
+
+    /**
+     * Determines the order of sink wrappers. The wrapper factory with the 
highest priority is called first.
+     * @return the priority of this factory
+     */
+    int getPriority();
+}
diff --git 
a/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/RandomAccessSink.java
 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/RandomAccessSink.java
index f35d030a..6de18e69 100644
--- 
a/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/RandomAccessSink.java
+++ 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/RandomAccessSink.java
@@ -26,8 +26,8 @@ import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.maven.doxia.parser.SinkWrapper;
 import org.apache.maven.doxia.sink.Sink;
-import org.apache.maven.doxia.sink.SinkEventAttributes;
 import org.apache.maven.doxia.sink.SinkFactory;
 
 /**
@@ -39,7 +39,7 @@ import org.apache.maven.doxia.sink.SinkFactory;
  * @author Robert Scholte
  * @since 1.3
  */
-public class RandomAccessSink implements Sink {
+public class RandomAccessSink extends SinkWrapper {
     private SinkFactory sinkFactory;
 
     private String encoding;
@@ -62,10 +62,10 @@ public class RandomAccessSink implements Sink {
      * @throws java.io.IOException if any.
      */
     public RandomAccessSink(SinkFactory sinkFactory, OutputStream stream) 
throws IOException {
+        super(sinkFactory.createSink(stream));
         this.sinkFactory = sinkFactory;
         this.coreOutputStream = stream;
-        this.currentSink = sinkFactory.createSink(stream);
-        this.coreSink = this.currentSink;
+        this.coreSink = getWrappedSink();
     }
 
     /**
@@ -77,11 +77,11 @@ public class RandomAccessSink implements Sink {
      * @throws java.io.IOException if any.
      */
     public RandomAccessSink(SinkFactory sinkFactory, OutputStream stream, 
String encoding) throws IOException {
+        super(sinkFactory.createSink(stream, encoding));
         this.sinkFactory = sinkFactory;
         this.coreOutputStream = stream;
         this.encoding = encoding;
-        this.currentSink = sinkFactory.createSink(stream, encoding);
-        this.coreSink = this.currentSink;
+        this.coreSink = getWrappedSink();
     }
 
     /**
@@ -93,10 +93,12 @@ public class RandomAccessSink implements Sink {
      * @throws java.io.IOException if any.
      */
     public RandomAccessSink(SinkFactory sinkFactory, File outputDirectory, 
String outputName) throws IOException {
+        super(null);
         this.sinkFactory = sinkFactory;
         this.coreOutputStream = new FileOutputStream(new File(outputDirectory, 
outputName));
         this.currentSink = sinkFactory.createSink(coreOutputStream);
         this.coreSink = this.currentSink;
+        setWrappedSink(currentSink);
     }
 
     /**
@@ -110,29 +112,13 @@ public class RandomAccessSink implements Sink {
      */
     public RandomAccessSink(SinkFactory sinkFactory, File outputDirectory, 
String outputName, String encoding)
             throws IOException {
+        super(null);
         this.sinkFactory = sinkFactory;
         this.coreOutputStream = new FileOutputStream(new File(outputDirectory, 
outputName));
         this.encoding = encoding;
         this.currentSink = sinkFactory.createSink(coreOutputStream, encoding);
         this.coreSink = this.currentSink;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void address() {
-        currentSink.address();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void address(SinkEventAttributes attributes) {
-        currentSink.address(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void address_() {
-        currentSink.address_();
+        setWrappedSink(currentSink);
     }
 
     /**
@@ -159,114 +145,13 @@ public class RandomAccessSink implements Sink {
             }
             sinks.add(subSink);
             sinks.add(currentSink);
+            setWrappedSink(currentSink);
         } catch (IOException e) {
             // IOException can only be caused by our own ByteArrayOutputStream
         }
         return subSink;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public void anchor(String name) {
-        currentSink.anchor(name);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void anchor(String name, SinkEventAttributes attributes) {
-        currentSink.anchor(name, attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void anchor_() {
-        currentSink.anchor_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void article() {
-        currentSink.article();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void article(SinkEventAttributes attributes) {
-        currentSink.article(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void article_() {
-        currentSink.article_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void author() {
-        currentSink.author();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void author(SinkEventAttributes attributes) {
-        currentSink.author(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void author_() {
-        currentSink.author_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void blockquote() {
-        currentSink.blockquote();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void blockquote(SinkEventAttributes attributes) {
-        currentSink.blockquote(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void blockquote_() {
-        currentSink.blockquote_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void body() {
-        currentSink.body();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void body(SinkEventAttributes attributes) {
-        currentSink.body(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void body_() {
-        currentSink.body_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void bold() {
-        currentSink.bold();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void bold_() {
-        currentSink.bold_();
-    }
-
     /**
      * Close all sinks
      */
@@ -278,204 +163,6 @@ public class RandomAccessSink implements Sink {
         coreSink.close();
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public void comment(String comment) {
-        currentSink.comment(comment);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void content() {
-        currentSink.content();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void content(SinkEventAttributes attributes) {
-        currentSink.content(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void content_() {
-        currentSink.content_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void data(String value) {
-        currentSink.data(value);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void data(String value, SinkEventAttributes attributes) {
-        currentSink.data(value, attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void data_() {
-        currentSink.data_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void date() {
-        currentSink.date();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void date(SinkEventAttributes attributes) {
-        currentSink.date(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void date_() {
-        currentSink.date_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definedTerm() {
-        currentSink.definedTerm();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definedTerm(SinkEventAttributes attributes) {
-        currentSink.definedTerm(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definedTerm_() {
-        currentSink.definedTerm_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definition() {
-        currentSink.definition();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definition(SinkEventAttributes attributes) {
-        currentSink.definition(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definitionList() {
-        currentSink.definitionList();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definitionList(SinkEventAttributes attributes) {
-        currentSink.definitionList(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definitionListItem() {
-        currentSink.definitionListItem();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definitionListItem(SinkEventAttributes attributes) {
-        currentSink.definitionListItem(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definitionListItem_() {
-        currentSink.definitionListItem_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definitionList_() {
-        currentSink.definitionList_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void definition_() {
-        currentSink.definition_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void division() {
-        currentSink.division();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void division(SinkEventAttributes attributes) {
-        currentSink.division(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void division_() {
-        currentSink.division_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void figure() {
-        currentSink.figure();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void figure(SinkEventAttributes attributes) {
-        currentSink.figure(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void figureCaption() {
-        currentSink.figureCaption();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void figureCaption(SinkEventAttributes attributes) {
-        currentSink.figureCaption(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void figureCaption_() {
-        currentSink.figureCaption_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void figureGraphics(String name) {
-        currentSink.figureGraphics(name);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void figureGraphics(String src, SinkEventAttributes attributes) {
-        currentSink.figureGraphics(src, attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void figure_() {
-        currentSink.figure_();
-    }
-
     /**
      * Flush all sinks
      */
@@ -495,634 +182,4 @@ public class RandomAccessSink implements Sink {
         }
         coreSink.flush();
     }
-
-    /** {@inheritDoc} */
-    @Override
-    public void footer() {
-        currentSink.footer();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void footer(SinkEventAttributes attributes) {
-        currentSink.footer(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void footer_() {
-        currentSink.footer_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void head() {
-        currentSink.head();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void head(SinkEventAttributes attributes) {
-        currentSink.head(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void head_() {
-        currentSink.head_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void header() {
-        currentSink.header();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void header(SinkEventAttributes attributes) {
-        currentSink.header(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void header_() {
-        currentSink.header_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void horizontalRule() {
-        currentSink.horizontalRule();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void horizontalRule(SinkEventAttributes attributes) {
-        currentSink.horizontalRule(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void inline() {
-        currentSink.inline();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void inline(SinkEventAttributes attributes) {
-        currentSink.inline(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void inline_() {
-        currentSink.inline_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void italic() {
-        currentSink.italic();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void italic_() {
-        currentSink.italic_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void lineBreak() {
-        currentSink.lineBreak();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void lineBreak(SinkEventAttributes attributes) {
-        currentSink.lineBreak(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void lineBreakOpportunity() {
-        currentSink.lineBreakOpportunity();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void lineBreakOpportunity(SinkEventAttributes attributes) {
-        currentSink.lineBreakOpportunity(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void link(String name) {
-        currentSink.link(name);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void link(String name, SinkEventAttributes attributes) {
-        currentSink.link(name, attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void link_() {
-        currentSink.link_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void list() {
-        currentSink.list();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void list(SinkEventAttributes attributes) {
-        currentSink.list(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void listItem() {
-        currentSink.listItem();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void listItem(SinkEventAttributes attributes) {
-        currentSink.listItem(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void listItem_() {
-        currentSink.listItem_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void list_() {
-        currentSink.list_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void monospaced() {
-        currentSink.monospaced();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void monospaced_() {
-        currentSink.monospaced_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void navigation() {
-        currentSink.navigation();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void navigation(SinkEventAttributes attributes) {
-        currentSink.navigation(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void navigation_() {
-        currentSink.navigation_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void nonBreakingSpace() {
-        currentSink.nonBreakingSpace();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void numberedList(int numbering) {
-        currentSink.numberedList(numbering);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void numberedList(int numbering, SinkEventAttributes attributes) {
-        currentSink.numberedList(numbering, attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void numberedListItem() {
-        currentSink.numberedListItem();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void numberedListItem(SinkEventAttributes attributes) {
-        currentSink.numberedListItem(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void numberedListItem_() {
-        currentSink.numberedListItem_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void numberedList_() {
-        currentSink.numberedList_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void pageBreak() {
-        currentSink.pageBreak();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void paragraph() {
-        currentSink.paragraph();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void paragraph(SinkEventAttributes attributes) {
-        currentSink.paragraph(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void paragraph_() {
-        currentSink.paragraph_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void rawText(String text) {
-        currentSink.rawText(text);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section(int level, SinkEventAttributes attributes) {
-        currentSink.section(level, attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section1() {
-        currentSink.section1();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section1_() {
-        currentSink.section1_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section2() {
-        currentSink.section2();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section2_() {
-        currentSink.section2_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section3() {
-        currentSink.section3();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section3_() {
-        currentSink.section3_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section4() {
-        currentSink.section4();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section4_() {
-        currentSink.section4_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section5() {
-        currentSink.section5();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section5_() {
-        currentSink.section5_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle() {
-        currentSink.sectionTitle();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle(int level, SinkEventAttributes attributes) {
-        currentSink.sectionTitle(level, attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle1() {
-        currentSink.sectionTitle1();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle1_() {
-        currentSink.sectionTitle1_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle2() {
-        currentSink.sectionTitle2();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle2_() {
-        currentSink.sectionTitle2_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle3() {
-        currentSink.sectionTitle3();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle3_() {
-        currentSink.sectionTitle3_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle4() {
-        currentSink.sectionTitle4();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle4_() {
-        currentSink.sectionTitle4_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle5() {
-        currentSink.sectionTitle5();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle5_() {
-        currentSink.sectionTitle5_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle_() {
-        currentSink.sectionTitle_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sectionTitle_(int level) {
-        currentSink.sectionTitle_(level);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void section_(int level) {
-        currentSink.section_(level);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sidebar() {
-        currentSink.sidebar();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sidebar(SinkEventAttributes attributes) {
-        currentSink.sidebar(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sidebar_() {
-        currentSink.sidebar_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void table() {
-        currentSink.table();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void table(SinkEventAttributes attributes) {
-        currentSink.table(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableCaption() {
-        currentSink.tableCaption();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableCaption(SinkEventAttributes attributes) {
-        currentSink.tableCaption(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableCaption_() {
-        currentSink.tableCaption_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableCell() {
-        currentSink.tableCell();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableCell(SinkEventAttributes attributes) {
-        currentSink.tableCell(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableCell_() {
-        currentSink.tableCell_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableHeaderCell() {
-        currentSink.tableHeaderCell();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableHeaderCell(SinkEventAttributes attributes) {
-        currentSink.tableHeaderCell(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableHeaderCell_() {
-        currentSink.tableHeaderCell_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableRow() {
-        currentSink.tableRow();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableRow(SinkEventAttributes attributes) {
-        currentSink.tableRow(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableRow_() {
-        currentSink.tableRow_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableRows() {
-        currentSink.tableRows();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableRows(int[] justification, boolean grid) {
-        currentSink.tableRows(justification, grid);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void tableRows_() {
-        currentSink.tableRows_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void table_() {
-        currentSink.table_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void text(String text) {
-        currentSink.text(text);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void text(String text, SinkEventAttributes attributes) {
-        currentSink.text(text, attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void time(String datetime) {
-        currentSink.time(datetime);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void time(String datetime, SinkEventAttributes attributes) {
-        currentSink.time(datetime, attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void time_() {
-        currentSink.time_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void title() {
-        currentSink.title();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void title(SinkEventAttributes attributes) {
-        currentSink.title(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void title_() {
-        currentSink.title_();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void unknown(String name, Object[] requiredParams, 
SinkEventAttributes attributes) {
-        currentSink.unknown(name, requiredParams, attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void verbatim() {
-        currentSink.verbatim();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void verbatim(SinkEventAttributes attributes) {
-        currentSink.verbatim(attributes);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void verbatim_() {
-        currentSink.verbatim_();
-    }
 }
diff --git 
a/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/SinkWrapperFactoryComparator.java
 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/SinkWrapperFactoryComparator.java
new file mode 100644
index 00000000..c3c8149b
--- /dev/null
+++ 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/SinkWrapperFactoryComparator.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.doxia.sink.impl;
+
+import java.util.Comparator;
+
+import org.apache.maven.doxia.parser.SinkWrapperFactory;
+
+/** Sorts the given {@link SinkWrapperFactory}s so that the one with the 
highest rank comes first (i.e. order by descending ranking) */
+public class SinkWrapperFactoryComparator implements 
Comparator<SinkWrapperFactory> {
+
+    @Override
+    public int compare(SinkWrapperFactory o1, SinkWrapperFactory o2) {
+
+        return Integer.compare(o2.getPriority(), o1.getPriority());
+    }
+}
diff --git 
a/doxia-core/src/test/java/org/apache/maven/doxia/parser/AbstractParserTest.java
 
b/doxia-core/src/test/java/org/apache/maven/doxia/parser/AbstractParserTest.java
index 5e1ffbd4..1db75f50 100644
--- 
a/doxia-core/src/test/java/org/apache/maven/doxia/parser/AbstractParserTest.java
+++ 
b/doxia-core/src/test/java/org/apache/maven/doxia/parser/AbstractParserTest.java
@@ -20,11 +20,13 @@ package org.apache.maven.doxia.parser;
 
 import java.io.IOException;
 import java.io.Reader;
+import java.io.StringWriter;
 import java.io.Writer;
 import java.util.Iterator;
 
 import org.apache.maven.doxia.AbstractModuleTest;
 import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.SinkEventAttributes;
 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
 import org.apache.maven.doxia.sink.impl.SinkEventElement;
 import org.apache.maven.doxia.sink.impl.TextSink;
@@ -96,18 +98,69 @@ public abstract class AbstractParserTest extends 
AbstractModuleTest {
         }
     }
 
-    protected static void assertSinkEquals(SinkEventElement element, String 
name, Object... args) {
+    @Test
+    public final void testSinkWrapper() throws ParseException, IOException {
+        Parser parser = createParser();
+
+        parser.addSinkWrapperFactory(new SinkWrapperFactory() {
+
+            @Override
+            public Sink createWrapper(Sink sink) {
+                return new SinkWrapper(sink) {
+
+                    @Override
+                    public void text(String text, SinkEventAttributes 
attributes) {
+                        super.text("beforeWrapper1" + text + "afterWrapper1", 
attributes);
+                    }
+                };
+            }
+
+            @Override
+            public int getPriority() {
+                return 0;
+            }
+        });
+
+        parser.addSinkWrapperFactory(new SinkWrapperFactory() {
+
+            @Override
+            public Sink createWrapper(Sink sink) {
+                return new SinkWrapper(sink) {
+                    @Override
+                    public void text(String text, SinkEventAttributes 
attributes) {
+                        super.text("beforeWrapper2" + text + "afterWrapper2", 
attributes);
+                    }
+                };
+            }
+
+            @Override
+            public int getPriority() {
+                return 1;
+            }
+        });
+        try (StringWriter writer = new StringWriter();
+                Reader reader = getTestReader("test", outputExtension())) {
+            Sink sink = new TextSink(writer);
+            parser.parse(reader, sink);
+
+            // assert order of sink wrappers
+            
assertTrue(writer.toString().contains("beforeWrapper2beforeWrapper1"));
+            
assertTrue(writer.toString().contains("afterWrapper1afterWrapper2"));
+        }
+    }
+
+    public static void assertSinkEquals(SinkEventElement element, String name, 
Object... args) {
         Assertions.assertEquals(name, element.getName(), "Name of element 
doesn't match");
         Assertions.assertArrayEquals(args, element.getArgs(), "Arguments don't 
match");
     }
 
-    protected static void assertSinkAttributeEquals(SinkEventElement element, 
String name, String attr, String value) {
+    public static void assertSinkAttributeEquals(SinkEventElement element, 
String name, String attr, String value) {
         Assertions.assertEquals(name, element.getName());
         SinkEventAttributeSet atts = (SinkEventAttributeSet) 
element.getArgs()[0];
         Assertions.assertEquals(value, atts.getAttribute(attr));
     }
 
-    protected static void assertSinkEquals(Iterator<SinkEventElement> it, 
String... names) {
+    public static void assertSinkEquals(Iterator<SinkEventElement> it, 
String... names) {
         StringBuilder expected = new StringBuilder();
         StringBuilder actual = new StringBuilder();
 
@@ -122,7 +175,7 @@ public abstract class AbstractParserTest extends 
AbstractModuleTest {
         Assertions.assertEquals(expected.toString(), actual.toString());
     }
 
-    protected static void assertSinkStartsWith(Iterator<SinkEventElement> it, 
String... names) {
+    public static void assertSinkStartsWith(Iterator<SinkEventElement> it, 
String... names) {
         StringBuilder expected = new StringBuilder();
         StringBuilder actual = new StringBuilder();
 
diff --git 
a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java
 
b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java
index 36990f7a..1df6a41c 100644
--- 
a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java
+++ 
b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptParser.java
@@ -199,7 +199,7 @@ public class AptParser extends AbstractTextParser 
implements AptMarkup {
         try {
             this.source = new AptReaderSource(new StringReader(sourceContent), 
reference);
 
-            this.sink = sink;
+            this.sink = getWrappedSink(sink);
 
             blockFileName = null;
 
diff --git 
a/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlParser.java
 
b/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlParser.java
index c1dd98cd..559d3f2a 100644
--- 
a/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlParser.java
+++ 
b/doxia-modules/doxia-module-fml/src/main/java/org/apache/maven/doxia/module/fml/FmlParser.java
@@ -105,7 +105,7 @@ public class FmlParser extends AbstractXmlParser implements 
FmlMarkup {
             // this populates faqs
             super.parse(tmp, sink, reference);
 
-            writeFaqs(sink);
+            writeFaqs(getWrappedSink(sink));
         } finally {
             this.faqs = null;
             this.sourceContent = null;
diff --git 
a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownParser.java
 
b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownParser.java
index 610f4dc4..9c229ccf 100644
--- 
a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownParser.java
+++ 
b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownParser.java
@@ -172,7 +172,7 @@ public class MarkdownParser extends AbstractTextParser 
implements TextMarkup {
             String html = toHtml(source);
 
             // then HTML to Sink API
-            parser.parse(html, sink);
+            parser.parse(html, getWrappedSink(sink));
         } catch (IOException e) {
             throw new ParseException("Failed reading Markdown source 
document", e);
         }

Reply via email to