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 1e8681353..ae2538ecc 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.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.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,6 +52,11 @@ public abstract class AbstractParser implements Parser { @Inject private MacroManager macroManager; + @Inject + private Collection automaticallyRegisteredSinkWrapperFactories; + + private final Collection manuallyRegisteredSinkWrapperFactories = new LinkedList<>(); + /** * Emit Doxia comment events when parsing comments? */ @@ -171,7 +180,25 @@ public void parse(Reader source, Sink sink) throws ParseException { } /** - * Set secondParsing 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 secondParsing to true, if this represents a secondary parsing of the same source. * * @param second true for second parsing */ @@ -189,6 +216,22 @@ protected boolean isSecondParsing() { return secondParsing; } + @Override + public void addSinkWrapperFactory(SinkWrapperFactory factory) { + manuallyRegisteredSinkWrapperFactories.add(factory); + } + + @Override + public Collection getSinkWrapperFactories() { + PriorityQueue 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 b4cbbfaa6..d0d143d65 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 void parse(Reader source, Sink sink, String reference) throws ParseExcept // 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 000000000..4112ad31d --- /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 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 e09953ed8..ea80bb8e6 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 true (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 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 000000000..6d793e5f6 --- /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 000000000..c6082c17e --- /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 f35d030a6..6de18e69e 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.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 @@ * @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 RandomAccessSink(SinkFactory sinkFactory, OutputStream stream) throws IOE * @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 RandomAccessSink(SinkFactory sinkFactory, OutputStream stream, String enc * @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 RandomAccessSink(SinkFactory sinkFactory, File outputDirectory, String ou */ 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 Sink addSinkHook() { } 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 void close() { 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 void flush() { } 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 000000000..c3c8149bd --- /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 { + + @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 5e1ffbd4f..1db75f501 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 @@ 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 final void testDocument() throws IOException, ParseException { } } - 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 it, String... names) { + public static void assertSinkEquals(Iterator it, String... names) { StringBuilder expected = new StringBuilder(); StringBuilder actual = new StringBuilder(); @@ -122,7 +175,7 @@ protected static void assertSinkEquals(Iterator it, String... Assertions.assertEquals(expected.toString(), actual.toString()); } - protected static void assertSinkStartsWith(Iterator it, String... names) { + public static void assertSinkStartsWith(Iterator 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 36990f7ab..1df6a41c9 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 void parse(Reader source, Sink sink, String reference) throws ParseExcept 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 c1dd98cd2..559d3f2a3 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 void parse(Reader source, Sink sink, String reference) throws ParseExcept // 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 610f4dc40..9c229ccf5 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 void parse(Reader source, Sink sink, String reference) throws ParseExcept 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); }