CommentRedactor.java
package org.egothor.methodatlas.api;
import java.nio.file.Path;
/**
* Language-specific service that blanks every comment in a source text before
* it is sent to an AI provider (data minimisation).
*
* <p>
* Implementations are discovered at runtime via {@link java.util.ServiceLoader}
* from {@code META-INF/services/org.egothor.methodatlas.api.CommentRedactor}.
* </p>
*
* <h2>Redaction contract</h2>
* <ul>
* <li>Every comment is replaced by U+0020 space characters.</li>
* <li>Every line terminator inside a comment is preserved unchanged.</li>
* <li>The result has exactly the same number of {@code char}s as the input,
* so every offset, line and column is identical before and after.</li>
* <li>The implementation must be string-aware: comment markers inside string
* or character literals are not comments and are left untouched.</li>
* <li>The given {@code String} is transformed; the file is not re-read.</li>
* <li>If the implementation cannot guarantee the above for a given input it
* throws {@link CommentRedactionException}; it must never return a
* partly redacted text.</li>
* </ul>
*
* <p>
* A language without a registered redactor cannot be scanned with
* {@code -redact-comments}: the run is aborted (fail-closed).
* </p>
*
* <h2>Lifecycle</h2>
* <p>
* {@link #configure(TestDiscoveryConfig)} is called once after loading, before
* {@link #supports(Path)} or {@link #redact(Path, String)}.
* </p>
*
* @see CommentRedactionException
* @see SourcePatcher
* @since 4.0.0
*/
public interface CommentRedactor {
/**
* Returns the unique identifier of this redactor; two redactors with the
* same identifier cause an {@link IllegalStateException} at load time.
*
* @return non-null, non-empty identifier, unique across loaded redactors
*/
String pluginId();
/**
* Receives the runtime configuration; the default implementation is a
* no-op.
*
* @param config runtime configuration; never {@code null}
*/
default void configure(TestDiscoveryConfig config) {
// no-op by default
}
/**
* Returns {@code true} if this redactor handles the given source file.
*
* @param sourceFile path to the candidate source file; never {@code null}
* @return {@code true} if this redactor accepts the file
*/
boolean supports(Path sourceFile);
/**
* Returns {@code source} with every comment replaced by spaces, preserving
* newlines and the exact character count.
*
* @param sourceFile path of the file the text came from; used only to
* select dialect, never read; never {@code null}
* @param source full source text; never {@code null}
* @return redacted text of the same length as {@code source}; never
* {@code null}
* @throws CommentRedactionException if redaction cannot be guaranteed
*/
String redact(Path sourceFile, String source) throws CommentRedactionException;
}