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

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


The following commit(s) were added to refs/heads/master by this push:
     new cf68f3dd [DOXIA-723] Expose document location in Sink (#194)
cf68f3dd is described below

commit cf68f3ddf8bdfafab64703dae4632cfed98e28e4
Author: Konrad Windszus <[email protected]>
AuthorDate: Thu Jan 25 14:23:51 2024 +0100

    [DOXIA-723] Expose document location in Sink (#194)
---
 .../maven/doxia/parser/AbstractXmlParser.java      | 27 ++++++++++++-
 .../java/org/apache/maven/doxia/parser/Parser.java |  4 +-
 .../maven/doxia/sink/impl/AbstractLocator.java     | 38 +++++++++++++++++
 .../apache/maven/doxia/sink/impl/AbstractSink.java | 46 +++++++++++++++++++++
 .../sink/impl/UniqueAnchorNamesValidator.java      |  3 +-
 .../maven/doxia/sink/impl/Xhtml5BaseSink.java      | 13 +++---
 .../apache/maven/doxia/module/apt/AptParser.java   | 23 ++++++++++-
 .../org/apache/maven/doxia/module/apt/AptSink.java |  7 +++-
 .../doxia/module/markdown/MarkdownParser.java      |  5 ++-
 .../maven/doxia/module/markdown/MarkdownSink.java  | 10 +++--
 .../org/apache/maven/doxia/sink/EmptyLocator.java  | 39 ++++++++++++++++++
 .../java/org/apache/maven/doxia/sink/Locator.java  | 47 ++++++++++++++++++++++
 .../java/org/apache/maven/doxia/sink/Sink.java     | 16 ++++++++
 13 files changed, 260 insertions(+), 18 deletions(-)

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 aa60cb52..92440b30 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
@@ -39,6 +39,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.maven.doxia.macro.MacroExecutionException;
 import org.apache.maven.doxia.markup.XmlMarkup;
 import org.apache.maven.doxia.sink.Sink;
+import org.apache.maven.doxia.sink.impl.AbstractLocator;
 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
 import org.apache.maven.doxia.util.HtmlTools;
 import org.apache.maven.doxia.util.XmlValidator;
@@ -135,7 +136,7 @@ public abstract class AbstractXmlParser extends 
AbstractParser implements XmlMar
             // Note: do it after input is set, otherwise values are reset
             initXmlParser(parser);
 
-            parseXml(parser, getWrappedSink(sink));
+            parseXml(parser, getWrappedSink(sink), reference);
         } catch (XmlPullParserException ex) {
             throw new ParseException("Error parsing the model", ex, 
ex.getLineNumber(), ex.getColumnNumber());
         } catch (MacroExecutionException ex) {
@@ -185,15 +186,37 @@ public abstract class AbstractXmlParser extends 
AbstractParser implements XmlMar
         return atts;
     }
 
+    private static final class XmlPullParserLocator extends AbstractLocator {
+
+        private final XmlPullParser parser;
+
+        XmlPullParserLocator(XmlPullParser parser, String reference) {
+            super(reference);
+            this.parser = parser;
+        }
+
+        @Override
+        public int getLineNumber() {
+            return parser.getLineNumber();
+        }
+
+        @Override
+        public int getColumnNumber() {
+            return parser.getColumnNumber() != -1 ? parser.getColumnNumber() + 
1 : -1;
+        }
+    }
     /**
      * Parse the model from the XmlPullParser into the given sink.
      *
      * @param parser A parser, not null.
      * @param sink the sink to receive the events.
+     * @param reference the reference (usually the file path of the parsed 
document)
      * @throws org.codehaus.plexus.util.xml.pull.XmlPullParserException if 
there's a problem parsing the model
      * @throws org.apache.maven.doxia.macro.MacroExecutionException if there's 
a problem executing a macro
      */
-    private void parseXml(XmlPullParser parser, Sink sink) throws 
XmlPullParserException, MacroExecutionException {
+    private void parseXml(XmlPullParser parser, Sink sink, String reference)
+            throws XmlPullParserException, MacroExecutionException {
+        sink.setDocumentLocator(new XmlPullParserLocator(parser, reference));
         int eventType = parser.getEventType();
 
         while (eventType != XmlPullParser.END_DOCUMENT) {
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 aecc63d9..4b271867 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
@@ -45,11 +45,13 @@ public interface Parser {
 
     /**
      * Parses the given source model and emits Doxia events into the given 
sink.
+     * Shortcut for {@link #parse(Reader, Sink, String)} with last argument 
being {@code null}.
      *
      * @param source not null reader that provides the source document.
      * You could use <code>newReader</code> methods from {@link 
org.codehaus.plexus.util.ReaderFactory}.
      * @param sink A sink that consumes the Doxia events.
      * @throws org.apache.maven.doxia.parser.ParseException if the model could 
not be parsed.
+     *
      */
     void parse(Reader source, Sink sink) throws ParseException;
 
@@ -59,7 +61,7 @@ public interface Parser {
      * @param source not null reader that provides the source document.
      * You could use <code>newReader</code> methods from {@link 
org.codehaus.plexus.util.ReaderFactory}.
      * @param sink A sink that consumes the Doxia events.
-     * @param reference the reference
+     * @param reference a string identifying the source (for file based 
documents the source file path)
      * @throws org.apache.maven.doxia.parser.ParseException if the model could 
not be parsed.
      */
     void parse(Reader source, Sink sink, String reference) throws 
ParseException;
diff --git 
a/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/AbstractLocator.java
 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/AbstractLocator.java
new file mode 100644
index 00000000..99f1884c
--- /dev/null
+++ 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/AbstractLocator.java
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.maven.doxia.sink.Locator;
+
+/**
+ * @since 2.0.0
+ */
+public abstract class AbstractLocator implements Locator {
+
+    private String reference;
+
+    protected AbstractLocator(String reference) {
+        this.reference = reference;
+    }
+
+    @Override
+    public String getReference() {
+        return reference;
+    }
+}
diff --git 
a/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/AbstractSink.java 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/AbstractSink.java
index 0839ad88..4adadfc8 100644
--- 
a/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/AbstractSink.java
+++ 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/AbstractSink.java
@@ -19,6 +19,8 @@
 package org.apache.maven.doxia.sink.impl;
 
 import org.apache.maven.doxia.markup.Markup;
+import org.apache.maven.doxia.sink.EmptyLocator;
+import org.apache.maven.doxia.sink.Locator;
 import org.apache.maven.doxia.sink.Sink;
 import org.apache.maven.doxia.sink.SinkEventAttributes;
 
@@ -33,6 +35,8 @@ import org.apache.maven.doxia.sink.SinkEventAttributes;
  */
 public abstract class AbstractSink implements Sink, Markup {
 
+    private Locator locator;
+
     // -- start default implementation for legacy doxia 1.0 methods which have 
overridden variants in Doxia 1.1 or 2.0,
     // all sink implementation derived from this must only override the Doxia 
> 1.0 variants
     @Override
@@ -420,4 +424,46 @@ public abstract class AbstractSink implements Sink, Markup 
{
     protected void init() {
         // nop
     }
+
+    @Override
+    public void setDocumentLocator(Locator locator) {
+        this.locator = locator;
+    }
+
+    @Override
+    public Locator getDocumentLocator() {
+        if (locator == null) {
+            return EmptyLocator.INSTANCE;
+        }
+        return locator;
+    }
+
+    protected String getLocationLogPrefix() {
+        return formatLocation(getDocumentLocator());
+    }
+
+    /**
+     * Creates a string with line/column information. Inspired by
+     * {@code o.a.m.model.building.ModelProblemUtils.formatLocation(...)}.
+     *
+     * @param locator The locator must not be {@code null}.
+     * @return The formatted location or an empty string if unknown, never 
{@code null}.
+     */
+    public static String formatLocation(Locator locator) {
+        StringBuilder buffer = new StringBuilder();
+
+        if (locator.getReference() != null) {
+            buffer.append(locator.getReference());
+        }
+        if (locator.getLineNumber() > 0) {
+            buffer.append(", line ").append(locator.getLineNumber());
+        }
+        if (locator.getColumnNumber() > 0) {
+            buffer.append(", column ").append(locator.getLineNumber());
+        }
+        if (buffer.length() > 0) {
+            buffer.append(": ");
+        }
+        return buffer.toString();
+    }
 }
diff --git 
a/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/UniqueAnchorNamesValidator.java
 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/UniqueAnchorNamesValidator.java
index b44ab474..eaf8f4a0 100644
--- 
a/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/UniqueAnchorNamesValidator.java
+++ 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/UniqueAnchorNamesValidator.java
@@ -46,7 +46,8 @@ public class UniqueAnchorNamesValidator extends SinkWrapper {
 
     private void enforceUniqueAnchor(String name) {
         if (!usedAnchorNames.add(name)) {
-            throw new IllegalStateException("Anchor name \"" + name + "\" used 
more than once");
+            throw new IllegalStateException(
+                    getLocationLogPrefix() + "Anchor name \"" + name + "\" 
used more than once");
         }
     }
 }
diff --git 
a/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/Xhtml5BaseSink.java 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/Xhtml5BaseSink.java
index 03693d3a..cdb7d7b8 100644
--- 
a/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/Xhtml5BaseSink.java
+++ 
b/doxia-core/src/main/java/org/apache/maven/doxia/sink/impl/Xhtml5BaseSink.java
@@ -927,7 +927,7 @@ public class Xhtml5BaseSink extends AbstractXmlSink 
implements HtmlMarkup {
         }
 
         if (this.tableContentWriterStack.isEmpty()) {
-            LOGGER.warn("No table content");
+            LOGGER.warn("{}No table content", getLocationLogPrefix());
             return;
         }
 
@@ -1182,7 +1182,7 @@ public class Xhtml5BaseSink extends AbstractXmlSink 
implements HtmlMarkup {
         if (!DoxiaUtils.isValidId(id)) {
             id = DoxiaUtils.encodeId(name);
 
-            LOGGER.debug("Modified invalid anchor name '{}' to '{}'", name, 
id);
+            LOGGER.debug("{}Modified invalid anchor name '{}' to '{}'", 
getLocationLogPrefix(), name, id);
         }
 
         MutableAttributeSet att = new SinkEventAttributeSet();
@@ -1448,7 +1448,8 @@ public class Xhtml5BaseSink extends AbstractXmlSink 
implements HtmlMarkup {
             }
 
             if (!originalComment.equals(comment)) {
-                LOGGER.warn("Modified invalid comment '{}' to '{}'", 
originalComment, comment);
+                LOGGER.warn(
+                        "{}Modified invalid comment '{}' to '{}'", 
getLocationLogPrefix(), originalComment, comment);
             }
 
             final StringBuilder buffer = new StringBuilder(comment.length() + 
7);
@@ -1505,7 +1506,7 @@ public class Xhtml5BaseSink extends AbstractXmlSink 
implements HtmlMarkup {
     @Override
     public void unknown(String name, Object[] requiredParams, 
SinkEventAttributes attributes) {
         if (requiredParams == null || !(requiredParams[0] instanceof Integer)) 
{
-            LOGGER.warn("No type information for unknown event '{}', 
ignoring!", name);
+            LOGGER.warn("{}No type information for unknown event '{}', 
ignoring!", getLocationLogPrefix(), name);
 
             return;
         }
@@ -1527,7 +1528,7 @@ public class Xhtml5BaseSink extends AbstractXmlSink 
implements HtmlMarkup {
         Tag tag = HtmlTools.getHtmlTag(name);
 
         if (tag == null) {
-            LOGGER.warn("No HTML tag found for unknown event '{}', ignoring!", 
name);
+            LOGGER.warn("[]No HTML tag found for unknown event '{}', 
ignoring!", getLocationLogPrefix(), name);
         } else {
             if (tagType == TAG_TYPE_SIMPLE) {
                 writeSimpleTag(tag, escapeAttributeValues(attributes));
@@ -1536,7 +1537,7 @@ public class Xhtml5BaseSink extends AbstractXmlSink 
implements HtmlMarkup {
             } else if (tagType == TAG_TYPE_END) {
                 writeEndTag(tag);
             } else {
-                LOGGER.warn("No type information for unknown event '{}', 
ignoring!", name);
+                LOGGER.warn("{}No type information for unknown event '{}', 
ignoring!", getLocationLogPrefix(), name);
             }
         }
     }
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 1df6a41c..52e8ec5a 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
@@ -38,6 +38,7 @@ import org.apache.maven.doxia.parser.AbstractTextParser;
 import org.apache.maven.doxia.parser.ParseException;
 import org.apache.maven.doxia.sink.Sink;
 import org.apache.maven.doxia.sink.SinkEventAttributes;
+import org.apache.maven.doxia.sink.impl.AbstractLocator;
 import org.apache.maven.doxia.sink.impl.SinkAdapter;
 import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
 import org.apache.maven.doxia.util.DoxiaUtils;
@@ -180,7 +181,26 @@ public class AptParser extends AbstractTextParser 
implements AptMarkup {
     /** {@inheritDoc} */
     @Override
     public void parse(Reader source, Sink sink) throws ParseException {
-        parse(source, sink, "");
+        parse(source, sink, null);
+    }
+
+    private static final class AptSourceLocator extends AbstractLocator {
+        private final AptSource aptSource;
+
+        AptSourceLocator(AptSource aptSource, String reference) {
+            super(reference);
+            this.aptSource = aptSource;
+        }
+
+        @Override
+        public int getLineNumber() {
+            return aptSource.getLineNumber() > -1 ? aptSource.getLineNumber() 
+ 1 : -1;
+        }
+
+        @Override
+        public int getColumnNumber() {
+            return -1;
+        }
     }
 
     /** {@inheritDoc} */
@@ -200,6 +220,7 @@ public class AptParser extends AbstractTextParser 
implements AptMarkup {
             this.source = new AptReaderSource(new StringReader(sourceContent), 
reference);
 
             this.sink = getWrappedSink(sink);
+            sink.setDocumentLocator(new AptSourceLocator(this.source, 
reference));
 
             blockFileName = null;
 
diff --git 
a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSink.java
 
b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSink.java
index 6a1854cb..fefaf18d 100644
--- 
a/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSink.java
+++ 
b/doxia-modules/doxia-module-apt/src/main/java/org/apache/maven/doxia/module/apt/AptSink.java
@@ -265,7 +265,10 @@ public class AptSink extends AbstractTextSink implements 
AptMarkup {
     @Override
     public void sectionTitle(int level, SinkEventAttributes attributes) {
         if (level > 5) {
-            LOGGER.warn("Replacing unsupported section title level {} in APT 
with level 5", level);
+            LOGGER.warn(
+                    "{}Replacing unsupported section title level {} in APT 
with level 5",
+                    getLocationLogPrefix(),
+                    level);
             level = 5;
         }
         if (level == 1) {
@@ -846,7 +849,7 @@ public class AptSink extends AbstractTextSink implements 
AptMarkup {
      * @see 
org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes)
      */
     public void unknown(String name, Object[] requiredParams, 
SinkEventAttributes attributes) {
-        LOGGER.warn("Unknown Sink event '{}', ignoring!", name);
+        LOGGER.warn("{}Unknown Sink event '{}', ignoring!", 
getLocationLogPrefix(), name);
     }
 
     /**
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 9c229ccf..8534b1a1 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
@@ -171,8 +171,11 @@ public class MarkdownParser extends AbstractTextParser 
implements TextMarkup {
             // Markdown to HTML (using flexmark-java library)
             String html = toHtml(source);
 
+            // TODO: add locator for the markdown source (not the intermediate 
HTML format)
+            // this requires writing a custom renderer not leveraging the 
XHTML parser
+
             // then HTML to Sink API
-            parser.parse(html, getWrappedSink(sink));
+            parser.parse(html, getWrappedSink(sink), "Intermediate HTML from " 
+ reference);
         } catch (IOException e) {
             throw new ParseException("Failed reading Markdown source 
document", e);
         }
diff --git 
a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java
 
b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java
index 71d45090..976b4100 100644
--- 
a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java
+++ 
b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java
@@ -303,7 +303,8 @@ public class MarkdownSink extends AbstractTextSink 
implements MarkdownMarkup {
         // markdown only supports decimal numbering
         if (numbering != NUMBERING_DECIMAL) {
             LOGGER.warn(
-                    "Markdown only supports numbered item with decimal style 
({}) but requested was style {}, falling back to decimal style",
+                    "{}Markdown only supports numbered item with decimal style 
({}) but requested was style {}, falling back to decimal style",
+                    getLocationLogPrefix(),
                     NUMBERING_DECIMAL,
                     numbering);
         }
@@ -379,7 +380,8 @@ public class MarkdownSink extends AbstractTextSink 
implements MarkdownMarkup {
 
     @Override
     public void definitionList(SinkEventAttributes attributes) {
-        LOGGER.warn("Definition list not natively supported in Markdown, 
rendering HTML instead");
+        LOGGER.warn(
+                "{}Definition list not natively supported in Markdown, 
rendering HTML instead", getLocationLogPrefix());
         writeUnescaped("<dl>" + EOL);
     }
 
@@ -757,7 +759,7 @@ public class MarkdownSink extends AbstractTextSink 
implements MarkdownMarkup {
         ElementContext currentContext = elementContextStack.element();
         if (currentContext == ElementContext.TABLE_CAPTION) {
             // table caption cannot even be emitted via XHTML in markdown as 
there is no suitable location
-            LOGGER.warn("Ignoring unsupported table caption in Markdown");
+            LOGGER.warn("{}Ignoring unsupported table caption in Markdown", 
getLocationLogPrefix());
         } else {
             String unifiedText = currentContext.escape(unifyEOLs(text));
             writeUnescaped(unifiedText);
@@ -785,7 +787,7 @@ public class MarkdownSink extends AbstractTextSink 
implements MarkdownMarkup {
      */
     @Override
     public void unknown(String name, Object[] requiredParams, 
SinkEventAttributes attributes) {
-        LOGGER.warn("Unknown Sink event '" + name + "', ignoring!");
+        LOGGER.warn("{}Unknown Sink event '" + name + "', ignoring!", 
getLocationLogPrefix());
     }
 
     /**
diff --git 
a/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/EmptyLocator.java 
b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/EmptyLocator.java
new file mode 100644
index 00000000..e3633e5d
--- /dev/null
+++ b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/EmptyLocator.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+public final class EmptyLocator implements Locator {
+
+    public static final Locator INSTANCE = new EmptyLocator();
+
+    @Override
+    public int getLineNumber() {
+        return -1;
+    }
+
+    @Override
+    public int getColumnNumber() {
+        return -1;
+    }
+
+    @Override
+    public String getReference() {
+        return null;
+    }
+}
diff --git 
a/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/Locator.java 
b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/Locator.java
new file mode 100644
index 00000000..4202a6cc
--- /dev/null
+++ b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/Locator.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+/**
+ * Interface for associating a {@link Sink} event with a document location.
+ * @since 2.0.0
+ */
+public interface Locator {
+    /**
+     * Returns the underlying source reference (for file based documents a 
relative file path, otherwise an arbitrary string or {@code null}).
+     * @return the source for the sink event (may be {@code null})
+     */
+    String getReference();
+
+    /**
+     * Returns the line number for the sink event (starting from 1).
+     * If it is not known returns {@code -1}.
+     *
+     * @return the line number for the sink event
+     */
+    int getLineNumber();
+
+    /**
+     * Returns the column number for the sink event (starting from 1).
+     * If it is not known returns {@code -1}.
+     *
+     * @return the column number for the sink event
+     */
+    int getColumnNumber();
+}
diff --git a/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/Sink.java 
b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/Sink.java
index 2739eec4..ec4d2833 100644
--- a/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/Sink.java
+++ b/doxia-sink-api/src/main/java/org/apache/maven/doxia/sink/Sink.java
@@ -1749,4 +1749,20 @@ public interface Sink extends AutoCloseable {
      * Closing a previously-closed Sink has no effect.
      */
     void close();
+
+    /**
+     * Sets the locator which exposes location information for a particular 
Sink event.
+     * @param locator the locator (never {@code null}).
+     * @since 2.0.0
+     */
+    default void setDocumentLocator(Locator locator) {}
+
+    /**
+     * Returns the locator which exposes location information for a particular 
Sink event.
+     * @return the locator (never {@code null}).
+     * @since 2.0.0
+     */
+    default Locator getDocumentLocator() {
+        return EmptyLocator.INSTANCE;
+    }
 }

Reply via email to