From 2c5b86fac2f5299feebcb987cba03e6d562d315d Mon Sep 17 00:00:00 2001 From: redcatbear Date: Fri, 19 Jun 2026 16:24:02 +0200 Subject: [PATCH 1/9] #536: Extracted interface `ImporterFactory` from abstract base class that is now named`AbstractImporterFactory`. --- .../api/importer/AbstractImporterFactory.java | 37 +++ .../api/importer/ImporterFactory.java | 33 +-- .../RegexMatchingImporterFactory.java | 6 +- doc/images/uml/class/cl_importer_overview.svg | 210 +----------------- doc/plugin_developer_guide.md | 4 +- doc/spec/design.md | 2 + .../specobject/SpecobjectImporterFactory.java | 2 +- .../importer/tag/TagImporterFactory.java | 2 +- .../class/cl_importer_overview.plantuml | 11 +- .../importer/ImporterFactoryTestBaseTest.java | 2 +- 10 files changed, 63 insertions(+), 246 deletions(-) create mode 100644 api/src/main/java/org/itsallcode/openfasttrace/api/importer/AbstractImporterFactory.java diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/importer/AbstractImporterFactory.java b/api/src/main/java/org/itsallcode/openfasttrace/api/importer/AbstractImporterFactory.java new file mode 100644 index 000000000..b2e2a7450 --- /dev/null +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/importer/AbstractImporterFactory.java @@ -0,0 +1,37 @@ +package org.itsallcode.openfasttrace.api.importer; + +import java.util.Objects; + +/** + * Base class for {@link ImporterFactory} implementations that share context handling. + */ +public abstract class AbstractImporterFactory implements ImporterFactory +{ + private ImporterContext context; + + /** + * Create a new {@link AbstractImporterFactory}. + */ + protected AbstractImporterFactory() + { + // empty by intention + } + + @Override + public void init(final ImporterContext context) + { + this.context = context; + } + + /** + * Get the {@link ImporterContext} set by the {@link #init(ImporterContext)} + * method. + * + * @return the {@link ImporterContext}. + */ + @Override + public ImporterContext getContext() + { + return Objects.requireNonNull(this.context, "Context was not initialized"); + } +} diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/importer/ImporterFactory.java b/api/src/main/java/org/itsallcode/openfasttrace/api/importer/ImporterFactory.java index 7740bba8c..d92644176 100644 --- a/api/src/main/java/org/itsallcode/openfasttrace/api/importer/ImporterFactory.java +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/importer/ImporterFactory.java @@ -1,24 +1,13 @@ package org.itsallcode.openfasttrace.api.importer; -import java.util.Objects; - import org.itsallcode.openfasttrace.api.core.serviceloader.Initializable; import org.itsallcode.openfasttrace.api.importer.input.InputFile; /** - * Super class for factories producing {@link Importer}s. + * Interface for factories producing {@link Importer}s. */ -public abstract class ImporterFactory implements Initializable +public interface ImporterFactory extends Initializable { - private ImporterContext context; - - /** - * Create a new {@link ImporterFactory}. - */ - protected ImporterFactory() - { - // empty by intention - } /** * Get the priority of this {@link ImporterFactory}. @@ -30,7 +19,7 @@ protected ImporterFactory() * * @return priority of this importer factory */ - public abstract int getPriority(); + int getPriority(); /** * Returns {@code true} if this {@link ImporterFactory} supports @@ -40,7 +29,7 @@ protected ImporterFactory() * the file to check. * @return {@code true} if the given file is supported for importing. */ - public abstract boolean supportsFile(final InputFile file); + boolean supportsFile(final InputFile file); /** * Create an importer that is able to read the given file. @@ -52,14 +41,7 @@ protected ImporterFactory() * fragments * @return an importer instance */ - public abstract Importer createImporter(final InputFile file, - final ImportEventListener listener); - - @Override - public void init(final ImporterContext context) - { - this.context = context; - } + Importer createImporter(final InputFile file, final ImportEventListener listener); /** * Get the {@link ImporterContext} set by the {@link #init(ImporterContext)} @@ -67,8 +49,5 @@ public void init(final ImporterContext context) * * @return the {@link ImporterContext}. */ - public ImporterContext getContext() - { - return Objects.requireNonNull(this.context, "Context was not initialized"); - } + ImporterContext getContext(); } diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/importer/RegexMatchingImporterFactory.java b/api/src/main/java/org/itsallcode/openfasttrace/api/importer/RegexMatchingImporterFactory.java index 6ff79364e..60bcc3a3a 100644 --- a/api/src/main/java/org/itsallcode/openfasttrace/api/importer/RegexMatchingImporterFactory.java +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/importer/RegexMatchingImporterFactory.java @@ -11,10 +11,10 @@ import org.itsallcode.openfasttrace.api.importer.input.InputFile; /** - * Base class for {@link ImporterFactory}s that can import files matching a list - * of regexp patterns. + * Base class for {@link ImporterFactory} implementations that can import files + * matching a list of regexp patterns. */ -public abstract class RegexMatchingImporterFactory extends ImporterFactory +public abstract class RegexMatchingImporterFactory extends AbstractImporterFactory { private static final Logger LOG = Logger .getLogger(RegexMatchingImporterFactory.class.getName()); diff --git a/doc/images/uml/class/cl_importer_overview.svg b/doc/images/uml/class/cl_importer_overview.svg index 193dcf5d7..a46184ac1 100644 --- a/doc/images/uml/class/cl_importer_overview.svg +++ b/doc/images/uml/class/cl_importer_overview.svg @@ -1,209 +1 @@ -java.utilorg.itsallcode.openfasttraceimporterzipcore.serviceloaderServiceLoaderImporterFactoryLoaderMultiFileImporter«interface»ImporterrunImport() : void«abstract»ImporterFactorysupportsFile(inputFile: InputFile) : booleancreateImporter(inputFile: InputFile, listener: ImportEventListener) : ImportergetContext() : ImporterContextImporterServiceImporterService(context: ImporterContext) : ImporterServicecreateImporter() : MultiFileImporter«abstract»RegExMatchingImporterFactoryImporterContextgetImporterService() : ImporterServicesetImporterService(importerService: ImporterService) : voidZipFileImporterZipFileImporterFactoryInitializingServiceLoader«interface»Initializableinit(context: ImporterContext) : void«create»contains«create»delegatespasses«initialize»getavailablefactoriesfind factory responsiblefor input format«create»«use» \ No newline at end of file +javautilorgitsallcodeopenfasttraceimporterzipcoreserviceloaderServiceLoaderImporterFactoryLoaderMultiFileImporter«interface»ImporterrunImport() : void«interface»ImporterFactorysupportsFile(inputFile: InputFile) : booleancreateImporter(inputFile: InputFile, listener: ImportEventListener) : ImportergetContext() : ImporterContext«abstract»AbstractImporterFactorygetContext() : ImporterContextImporterServiceImporterService(context: ImporterContext) : ImporterServicecreateImporter() : MultiFileImporter«abstract»RegExMatchingImporterFactoryImporterContextgetImporterService() : ImporterServicesetImporterService(importerService: ImporterService) : voidZipFileImporterZipFileImporterFactoryInitializingServiceLoader«interface»Initializableinit(context: ImporterContext) : void«create»contains«create»delegatespasses«initialize»getavailablefactoriesfind factory responsiblefor input format«create»«use» \ No newline at end of file diff --git a/doc/plugin_developer_guide.md b/doc/plugin_developer_guide.md index 5cd6f2f4c..8e91817d9 100644 --- a/doc/plugin_developer_guide.md +++ b/doc/plugin_developer_guide.md @@ -13,11 +13,13 @@ This guide describes how to develop [plugins](plugins.md) for OpenFastTrace (OFT ``` -2. Create a new class (e.g. `com.example.oft.import.MyImporter`) implementing one of the following interfaces: +2. Create a new class (e.g. `com.example.oft.import.MyImporter`) using one of the following plugin APIs: * [`org.itsallcode.openfasttrace.api.report.ReporterFactory`](https://github.com/itsallcode/openfasttrace/blob/main/api/src/main/java/org/itsallcode/openfasttrace/api/report/ReporterFactory.java): Generate tracing report * [`org.itsallcode.openfasttrace.api.importer.ImporterFactory`](https://github.com/itsallcode/openfasttrace/blob/main/api/src/main/java/org/itsallcode/openfasttrace/api/importer/ImporterFactory.java): Import requirements from a new file format * [`org.itsallcode.openfasttrace.api.exporter.ExporterFactory`](https://github.com/itsallcode/openfasttrace/blob/main/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/ExporterFactory.java): Export requirements in a new file format + Importer plugins will usually extend [`org.itsallcode.openfasttrace.api.importer.AbstractImporterFactory`](https://github.com/itsallcode/openfasttrace/blob/main/api/src/main/java/org/itsallcode/openfasttrace/api/importer/AbstractImporterFactory.java) instead of implementing `ImporterFactory` directly so they inherit the standard context handling. + 3. Create a file in `src/main/resources/$INTERFACE_FQN`, using the fully qualified class name of the interface as file name. 4. Add the fully qualified class name of your new plugin class to the new file, e.g. `com.example.oft.import.MyImporter` diff --git a/doc/spec/design.md b/doc/spec/design.md index 7a88a4af4..e90e03b04 100644 --- a/doc/spec/design.md +++ b/doc/spec/design.md @@ -217,6 +217,8 @@ return A factory for importers decides which importer to use. When multiple importers support the same file, the one with the lowest priority value (highest precedence) is chosen. Usually, importers are selected based on file extension, but some importers may peek into the file content to determine compatibility. +`ImporterFactory` is the plugin interface discovered by the service loader. Importer implementations in OFT usually extend `AbstractImporterFactory` to reuse the default context handling. + The default priorities for standard importers are: 1. Markdown Importer: 1000 2. reStructuredText Importer: 2000 diff --git a/importer/specobject/src/main/java/org/itsallcode/openfasttrace/importer/specobject/SpecobjectImporterFactory.java b/importer/specobject/src/main/java/org/itsallcode/openfasttrace/importer/specobject/SpecobjectImporterFactory.java index 8e7761e5d..aa2874924 100644 --- a/importer/specobject/src/main/java/org/itsallcode/openfasttrace/importer/specobject/SpecobjectImporterFactory.java +++ b/importer/specobject/src/main/java/org/itsallcode/openfasttrace/importer/specobject/SpecobjectImporterFactory.java @@ -12,7 +12,7 @@ /** * An {@link ImporterFactory} for ReqM2/SpecObject XML files. */ -public class SpecobjectImporterFactory extends ImporterFactory +public class SpecobjectImporterFactory extends AbstractImporterFactory { private static final Logger LOG = Logger.getLogger(SpecobjectImporterFactory.class.getName()); private static final int PEEK_CHARS = 4096; diff --git a/importer/tag/src/main/java/org/itsallcode/openfasttrace/importer/tag/TagImporterFactory.java b/importer/tag/src/main/java/org/itsallcode/openfasttrace/importer/tag/TagImporterFactory.java index 7daf0be2d..f5ac03696 100644 --- a/importer/tag/src/main/java/org/itsallcode/openfasttrace/importer/tag/TagImporterFactory.java +++ b/importer/tag/src/main/java/org/itsallcode/openfasttrace/importer/tag/TagImporterFactory.java @@ -11,7 +11,7 @@ * {@link ImporterFactory} for tags in source code files. */ // [impl->dsn~import.full-coverage-tag~1] -public class TagImporterFactory extends ImporterFactory +public class TagImporterFactory extends AbstractImporterFactory { private static final String DEFAULT_FILE_REGEX = "(?i).*\\.java"; private static final List SUPPORTED_DEFAULT_EXTENSIONS = Arrays.asList( // diff --git a/model/diagrams/class/cl_importer_overview.plantuml b/model/diagrams/class/cl_importer_overview.plantuml index 2434a0980..bd7246d25 100644 --- a/model/diagrams/class/cl_importer_overview.plantuml +++ b/model/diagrams/class/cl_importer_overview.plantuml @@ -20,9 +20,13 @@ package org.itsallcode.openfasttrace { + runImport() : void } - class ImporterFactory <> { + interface ImporterFactory <> { + supportsFile(inputFile: InputFile) : boolean + createImporter(inputFile: InputFile, listener: ImportEventListener) : Importer + + getContext() : ImporterContext + } + + class AbstractImporterFactory <> { * getContext() : ImporterContext } @@ -49,7 +53,8 @@ package org.itsallcode.openfasttrace { ZipFileImporterFactory -left-> ZipFileImporter : <> } - RegExMatchingImporterFactory -up-|> ImporterFactory + AbstractImporterFactory .up.|> ImporterFactory + RegExMatchingImporterFactory -up-|> AbstractImporterFactory ZipFileImporterFactory -up-|> RegExMatchingImporterFactory ImporterContext -down-> ImporterService : contains ImporterService -up-> MultiFileImporter : <> @@ -77,4 +82,4 @@ package org.itsallcode.openfasttrace { ServiceLoader -down-> ZipFileImporterFactory : <> InitializingServiceLoader -up-> ServiceLoader : <> -@enduml \ No newline at end of file +@enduml diff --git a/testutil/src/test/java/org/itsallcode/openfasttrace/testutil/importer/ImporterFactoryTestBaseTest.java b/testutil/src/test/java/org/itsallcode/openfasttrace/testutil/importer/ImporterFactoryTestBaseTest.java index 67470c659..f8473d8d5 100644 --- a/testutil/src/test/java/org/itsallcode/openfasttrace/testutil/importer/ImporterFactoryTestBaseTest.java +++ b/testutil/src/test/java/org/itsallcode/openfasttrace/testutil/importer/ImporterFactoryTestBaseTest.java @@ -43,7 +43,7 @@ protected List getUnsupportedFilenames() } } - private static class DummyImporterFactory extends ImporterFactory + private static class DummyImporterFactory extends AbstractImporterFactory { @Override public int getPriority() { From 07deab3b9d47ef7842449aebec75162124ab1485 Mon Sep 17 00:00:00 2001 From: redcatbear Date: Fri, 19 Jun 2026 16:26:40 +0200 Subject: [PATCH 2/9] #536: Removed broken link. --- .../openfasttrace/api/importer/ImporterFactory.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/importer/ImporterFactory.java b/api/src/main/java/org/itsallcode/openfasttrace/api/importer/ImporterFactory.java index d92644176..6fc1a7528 100644 --- a/api/src/main/java/org/itsallcode/openfasttrace/api/importer/ImporterFactory.java +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/importer/ImporterFactory.java @@ -8,7 +8,6 @@ */ public interface ImporterFactory extends Initializable { - /** * Get the priority of this {@link ImporterFactory}. *

@@ -44,8 +43,7 @@ public interface ImporterFactory extends Initializable Importer createImporter(final InputFile file, final ImportEventListener listener); /** - * Get the {@link ImporterContext} set by the {@link #init(ImporterContext)} - * method. + * Get the {@link ImporterContext}. * * @return the {@link ImporterContext}. */ From 1e89ce46080ce4609a6fad389120e1b057bd916d Mon Sep 17 00:00:00 2001 From: redcatbear Date: Fri, 19 Jun 2026 16:31:57 +0200 Subject: [PATCH 3/9] #536: Renamed `RegexMatchingImporterFactory` to `AbstractRegexMatchingImporterFactory`. --- ...ory.java => AbstractRegexMatchingImporterFactory.java} | 8 ++++---- .../importer/markdown/MarkdownImporterFactory.java | 2 +- .../restructuredtext/RestructuredTextImporterFactory.java | 2 +- .../importer/zip/ZipFileImporterFactory.java | 2 +- model/diagrams/class/cl_importer_overview.plantuml | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) rename api/src/main/java/org/itsallcode/openfasttrace/api/importer/{RegexMatchingImporterFactory.java => AbstractRegexMatchingImporterFactory.java} (88%) diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/importer/RegexMatchingImporterFactory.java b/api/src/main/java/org/itsallcode/openfasttrace/api/importer/AbstractRegexMatchingImporterFactory.java similarity index 88% rename from api/src/main/java/org/itsallcode/openfasttrace/api/importer/RegexMatchingImporterFactory.java rename to api/src/main/java/org/itsallcode/openfasttrace/api/importer/AbstractRegexMatchingImporterFactory.java index 60bcc3a3a..bb72df9d5 100644 --- a/api/src/main/java/org/itsallcode/openfasttrace/api/importer/RegexMatchingImporterFactory.java +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/importer/AbstractRegexMatchingImporterFactory.java @@ -14,10 +14,10 @@ * Base class for {@link ImporterFactory} implementations that can import files * matching a list of regexp patterns. */ -public abstract class RegexMatchingImporterFactory extends AbstractImporterFactory +public abstract class AbstractRegexMatchingImporterFactory extends AbstractImporterFactory { private static final Logger LOG = Logger - .getLogger(RegexMatchingImporterFactory.class.getName()); + .getLogger(AbstractRegexMatchingImporterFactory.class.getName()); private final Set supportedFilenamePatterns; @@ -27,7 +27,7 @@ public abstract class RegexMatchingImporterFactory extends AbstractImporterFacto * @param supportedFilenamePatterns * the filename patterns supported by the importer. */ - protected RegexMatchingImporterFactory(final String... supportedFilenamePatterns) + protected AbstractRegexMatchingImporterFactory(final String... supportedFilenamePatterns) { this(asList(supportedFilenamePatterns)); } @@ -38,7 +38,7 @@ protected RegexMatchingImporterFactory(final String... supportedFilenamePatterns * @param supportedFilenamePatterns * the filename patterns supported by the importer. */ - protected RegexMatchingImporterFactory(final Collection supportedFilenamePatterns) + protected AbstractRegexMatchingImporterFactory(final Collection supportedFilenamePatterns) { this.supportedFilenamePatterns = supportedFilenamePatterns.stream() // .map(Pattern::compile) // diff --git a/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MarkdownImporterFactory.java b/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MarkdownImporterFactory.java index bf58311f5..f16d0b3b0 100644 --- a/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MarkdownImporterFactory.java +++ b/importer/markdown/src/main/java/org/itsallcode/openfasttrace/importer/markdown/MarkdownImporterFactory.java @@ -6,7 +6,7 @@ /** * {@link ImporterFactory} for Markdown files */ -public class MarkdownImporterFactory extends RegexMatchingImporterFactory +public class MarkdownImporterFactory extends AbstractRegexMatchingImporterFactory { /** Creates a new instance. */ public MarkdownImporterFactory() diff --git a/importer/restructuredtext/src/main/java/org/itsallcode/openfasttrace/importer/restructuredtext/RestructuredTextImporterFactory.java b/importer/restructuredtext/src/main/java/org/itsallcode/openfasttrace/importer/restructuredtext/RestructuredTextImporterFactory.java index c5d33d152..83604687b 100644 --- a/importer/restructuredtext/src/main/java/org/itsallcode/openfasttrace/importer/restructuredtext/RestructuredTextImporterFactory.java +++ b/importer/restructuredtext/src/main/java/org/itsallcode/openfasttrace/importer/restructuredtext/RestructuredTextImporterFactory.java @@ -6,7 +6,7 @@ /** * {@link ImporterFactory} for reStructuredText files */ -public class RestructuredTextImporterFactory extends RegexMatchingImporterFactory +public class RestructuredTextImporterFactory extends AbstractRegexMatchingImporterFactory { /** Creates a new instance. */ public RestructuredTextImporterFactory() diff --git a/importer/zip/src/main/java/org/itsallcode/openfasttrace/importer/zip/ZipFileImporterFactory.java b/importer/zip/src/main/java/org/itsallcode/openfasttrace/importer/zip/ZipFileImporterFactory.java index f94d6b9e7..b2ee61208 100644 --- a/importer/zip/src/main/java/org/itsallcode/openfasttrace/importer/zip/ZipFileImporterFactory.java +++ b/importer/zip/src/main/java/org/itsallcode/openfasttrace/importer/zip/ZipFileImporterFactory.java @@ -10,7 +10,7 @@ * {@link ImporterFactory} for importing {@link ZipEntry}s of a {@link ZipFile} * using a {@link ZipFileImporter}. */ -public class ZipFileImporterFactory extends RegexMatchingImporterFactory +public class ZipFileImporterFactory extends AbstractRegexMatchingImporterFactory { /** Creates a new instance. */ public ZipFileImporterFactory() diff --git a/model/diagrams/class/cl_importer_overview.plantuml b/model/diagrams/class/cl_importer_overview.plantuml index bd7246d25..6e6caa330 100644 --- a/model/diagrams/class/cl_importer_overview.plantuml +++ b/model/diagrams/class/cl_importer_overview.plantuml @@ -35,7 +35,7 @@ package org.itsallcode.openfasttrace { + createImporter() : MultiFileImporter } - class RegExMatchingImporterFactory <> { + class AbstractRegExMatchingImporterFactory <> { } class ImporterContext { @@ -54,8 +54,8 @@ package org.itsallcode.openfasttrace { } AbstractImporterFactory .up.|> ImporterFactory - RegExMatchingImporterFactory -up-|> AbstractImporterFactory - ZipFileImporterFactory -up-|> RegExMatchingImporterFactory + AbstractRegExMatchingImporterFactory -up-|> AbstractImporterFactory + ZipFileImporterFactory -up-|> AbstractRegExMatchingImporterFactory ImporterContext -down-> ImporterService : contains ImporterService -up-> MultiFileImporter : <> ZipFileImporter -up-> MultiFileImporter : delegates From 4dc5c6d9bb69c7ab309b8fc84dd55b5021e247e5 Mon Sep 17 00:00:00 2001 From: redcatbear Date: Fri, 19 Jun 2026 16:46:34 +0200 Subject: [PATCH 4/9] #536: Renamed `ReporterFactory` to `AbstractReporterFactory`. --- .../api/report/AbstractReporterFactory.java | 37 +++++++++++++++++++ .../api/report/ReporterContext.java | 5 ++- .../api/report/ReporterFactory.java | 34 +++-------------- .../api/report/TestReporterFactory.java | 2 +- doc/plugin_developer_guide.md | 2 + doc/spec/design.md | 3 ++ .../report/aspec/ASpecReporterFactory.java | 3 +- .../report/html/HtmlReporterFactory.java | 3 +- .../plaintext/PlaintextReporterFactory.java | 3 +- 9 files changed, 58 insertions(+), 34 deletions(-) create mode 100644 api/src/main/java/org/itsallcode/openfasttrace/api/report/AbstractReporterFactory.java diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/report/AbstractReporterFactory.java b/api/src/main/java/org/itsallcode/openfasttrace/api/report/AbstractReporterFactory.java new file mode 100644 index 000000000..1571163e9 --- /dev/null +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/report/AbstractReporterFactory.java @@ -0,0 +1,37 @@ +package org.itsallcode.openfasttrace.api.report; + +import java.util.Objects; + +/** + * Base class for {@link ReporterFactory} implementations that share context handling. + */ +public abstract class AbstractReporterFactory implements ReporterFactory +{ + private ReporterContext context; + + /** + * Create a new {@link AbstractReporterFactory}. + */ + protected AbstractReporterFactory() + { + // empty by intention + } + + @Override + public void init(final ReporterContext context) + { + this.context = context; + } + + /** + * Get the {@link ReporterContext} set by the {@link #init(ReporterContext)} + * method. + * + * @return the {@link ReporterContext}. + */ + @Override + public ReporterContext getContext() + { + return Objects.requireNonNull(this.context, "Context was not initialized"); + } +} diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/report/ReporterContext.java b/api/src/main/java/org/itsallcode/openfasttrace/api/report/ReporterContext.java index d540d1e5b..4b5c48d1d 100644 --- a/api/src/main/java/org/itsallcode/openfasttrace/api/report/ReporterContext.java +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/report/ReporterContext.java @@ -3,8 +3,9 @@ import org.itsallcode.openfasttrace.api.ReportSettings; /** - * Common context shared by all {@link ReporterFactory}s. This allows reporters - * to access common infrastructure, e.g. the {@link ReportSettings}. + * Common context shared by all {@link ReporterFactory} implementations. This + * allows reporters to access common infrastructure, e.g. the + * {@link ReportSettings}. */ public class ReporterContext { diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/report/ReporterFactory.java b/api/src/main/java/org/itsallcode/openfasttrace/api/report/ReporterFactory.java index e768ad07a..83dbe5ba9 100644 --- a/api/src/main/java/org/itsallcode/openfasttrace/api/report/ReporterFactory.java +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/report/ReporterFactory.java @@ -1,25 +1,13 @@ package org.itsallcode.openfasttrace.api.report; -import java.util.Objects; - import org.itsallcode.openfasttrace.api.core.Trace; import org.itsallcode.openfasttrace.api.core.serviceloader.Initializable; /** - * Super class for factories producing {@link Reportable}s. + * Interface for factories producing {@link Reportable}s. */ -public abstract class ReporterFactory implements Initializable +public interface ReporterFactory extends Initializable { - private ReporterContext context; - - /** - * Creates a new {@link ReporterFactory}. - */ - protected ReporterFactory() - { - // empty by intention - } - /** * Check if this {@link ReporterFactory} supports creating * {@link Reportable}s for the given format. @@ -29,7 +17,7 @@ protected ReporterFactory() * @return {@code true} if this {@link ReporterFactory} supports the given * format. */ - public abstract boolean supportsFormat(final String format); + boolean supportsFormat(final String format); /** * Create a new {@link Reportable}. @@ -38,22 +26,12 @@ protected ReporterFactory() * the trace that will be reported. * @return the new {@link Reportable}. */ - public abstract Reportable createImporter(final Trace trace); - - @Override - public void init(final ReporterContext context) - { - this.context = context; - } + Reportable createImporter(final Trace trace); /** - * Get the {@link ReporterContext} set by the {@link #init(ReporterContext)} - * method. + * Get the {@link ReporterContext}. * * @return the {@link ReporterContext}. */ - public ReporterContext getContext() - { - return Objects.requireNonNull(this.context, "Context was not initialized"); - } + ReporterContext getContext(); } diff --git a/api/src/test/java/org/itsallcode/openfasttrace/api/report/TestReporterFactory.java b/api/src/test/java/org/itsallcode/openfasttrace/api/report/TestReporterFactory.java index bdbb571f7..2a9f17ee1 100644 --- a/api/src/test/java/org/itsallcode/openfasttrace/api/report/TestReporterFactory.java +++ b/api/src/test/java/org/itsallcode/openfasttrace/api/report/TestReporterFactory.java @@ -26,7 +26,7 @@ void testInitSetsContext() assertThat(reporterFactory.getContext(), sameInstance(context)); } - private static class TestingReporterFactory extends ReporterFactory + private static class TestingReporterFactory extends AbstractReporterFactory { @Override public boolean supportsFormat(String format) diff --git a/doc/plugin_developer_guide.md b/doc/plugin_developer_guide.md index 8e91817d9..245d98141 100644 --- a/doc/plugin_developer_guide.md +++ b/doc/plugin_developer_guide.md @@ -18,6 +18,8 @@ This guide describes how to develop [plugins](plugins.md) for OpenFastTrace (OFT * [`org.itsallcode.openfasttrace.api.importer.ImporterFactory`](https://github.com/itsallcode/openfasttrace/blob/main/api/src/main/java/org/itsallcode/openfasttrace/api/importer/ImporterFactory.java): Import requirements from a new file format * [`org.itsallcode.openfasttrace.api.exporter.ExporterFactory`](https://github.com/itsallcode/openfasttrace/blob/main/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/ExporterFactory.java): Export requirements in a new file format + Reporter plugins will usually extend [`org.itsallcode.openfasttrace.api.report.AbstractReporterFactory`](https://github.com/itsallcode/openfasttrace/blob/main/api/src/main/java/org/itsallcode/openfasttrace/api/report/AbstractReporterFactory.java) instead of implementing `ReporterFactory` directly so they inherit the standard context handling. + Importer plugins will usually extend [`org.itsallcode.openfasttrace.api.importer.AbstractImporterFactory`](https://github.com/itsallcode/openfasttrace/blob/main/api/src/main/java/org/itsallcode/openfasttrace/api/importer/AbstractImporterFactory.java) instead of implementing `ImporterFactory` directly so they inherit the standard context handling. 3. Create a file in `src/main/resources/$INTERFACE_FQN`, using the fully qualified class name of the interface as file name. diff --git a/doc/spec/design.md b/doc/spec/design.md index e90e03b04..aa3de6b6d 100644 --- a/doc/spec/design.md +++ b/doc/spec/design.md @@ -167,6 +167,9 @@ The Plugin loader supports loading factories for the following plugin types: * Exporters: `org.itsallcode.openfasttrace.api.exporter.ExporterFactory` * Reports: `org.itsallcode.openfasttrace.api.report.ReporterFactory` +Reporter implementations usually extend `org.itsallcode.openfasttrace.api.report.AbstractReporterFactory` +to reuse standard reporter context handling while keeping `ReporterFactory` as the service type. + Covers: * [`req~plugins.types~1`](system_requirements.md#supported-plugin-types) diff --git a/reporter/aspec/src/main/java/org/itsallcode/openfasttrace/report/aspec/ASpecReporterFactory.java b/reporter/aspec/src/main/java/org/itsallcode/openfasttrace/report/aspec/ASpecReporterFactory.java index eef12a262..0805c300e 100644 --- a/reporter/aspec/src/main/java/org/itsallcode/openfasttrace/report/aspec/ASpecReporterFactory.java +++ b/reporter/aspec/src/main/java/org/itsallcode/openfasttrace/report/aspec/ASpecReporterFactory.java @@ -2,13 +2,14 @@ import org.itsallcode.openfasttrace.api.core.Trace; import org.itsallcode.openfasttrace.api.report.Reportable; +import org.itsallcode.openfasttrace.api.report.AbstractReporterFactory; import org.itsallcode.openfasttrace.api.report.ReporterFactory; /** * A {@link ReporterFactory} for SpecObject reports. This supports the * specobject format. */ -public class ASpecReporterFactory extends ReporterFactory +public class ASpecReporterFactory extends AbstractReporterFactory { private static final String ASPEC_REPORT_FORMAT = "aspec"; diff --git a/reporter/html/src/main/java/org/itsallcode/openfasttrace/report/html/HtmlReporterFactory.java b/reporter/html/src/main/java/org/itsallcode/openfasttrace/report/html/HtmlReporterFactory.java index f69100804..c480cfc21 100644 --- a/reporter/html/src/main/java/org/itsallcode/openfasttrace/report/html/HtmlReporterFactory.java +++ b/reporter/html/src/main/java/org/itsallcode/openfasttrace/report/html/HtmlReporterFactory.java @@ -2,13 +2,14 @@ import org.itsallcode.openfasttrace.api.core.Trace; import org.itsallcode.openfasttrace.api.report.Reportable; +import org.itsallcode.openfasttrace.api.report.AbstractReporterFactory; import org.itsallcode.openfasttrace.api.report.ReporterFactory; /** * A {@link ReporterFactory} for HTML reports. This supports the * html format. */ -public class HtmlReporterFactory extends ReporterFactory +public class HtmlReporterFactory extends AbstractReporterFactory { private static final String HTML_REPORT_FORMAT = "html"; diff --git a/reporter/plaintext/src/main/java/org/itsallcode/openfasttrace/report/plaintext/PlaintextReporterFactory.java b/reporter/plaintext/src/main/java/org/itsallcode/openfasttrace/report/plaintext/PlaintextReporterFactory.java index 272e296b2..718092c09 100644 --- a/reporter/plaintext/src/main/java/org/itsallcode/openfasttrace/report/plaintext/PlaintextReporterFactory.java +++ b/reporter/plaintext/src/main/java/org/itsallcode/openfasttrace/report/plaintext/PlaintextReporterFactory.java @@ -2,13 +2,14 @@ import org.itsallcode.openfasttrace.api.core.Trace; import org.itsallcode.openfasttrace.api.report.Reportable; +import org.itsallcode.openfasttrace.api.report.AbstractReporterFactory; import org.itsallcode.openfasttrace.api.report.ReporterFactory; /** * A {@link ReporterFactory} for plain text reports. This supports the * plain format. */ -public class PlaintextReporterFactory extends ReporterFactory +public class PlaintextReporterFactory extends AbstractReporterFactory { private static final String PLAIN_REPORT_FORMAT = "plain"; From bc5ea64c214db3a98ca84e0b1b91621a4935cc5b Mon Sep 17 00:00:00 2001 From: redcatbear Date: Fri, 19 Jun 2026 16:51:34 +0200 Subject: [PATCH 5/9] #536: Renamed `ExporterFactory` to `AbstractExporterFactory`. --- .../api/exporter/AbstractExporterFactory.java | 104 ++++++++++++++++++ .../api/exporter/ExporterContext.java | 4 +- .../api/exporter/ExporterFactory.java | 92 ++-------------- .../api/exporter/TestExporterFactory.java | 49 +++++++++ doc/plugin_developer_guide.md | 2 + doc/spec/design.md | 3 + .../specobject/SpecobjectExporterFactory.java | 5 +- 7 files changed, 174 insertions(+), 85 deletions(-) create mode 100644 api/src/main/java/org/itsallcode/openfasttrace/api/exporter/AbstractExporterFactory.java create mode 100644 api/src/test/java/org/itsallcode/openfasttrace/api/exporter/TestExporterFactory.java diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/AbstractExporterFactory.java b/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/AbstractExporterFactory.java new file mode 100644 index 000000000..bd5f6b0bc --- /dev/null +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/AbstractExporterFactory.java @@ -0,0 +1,104 @@ +package org.itsallcode.openfasttrace.api.exporter; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.logging.Logger; +import java.util.stream.Stream; + +import org.itsallcode.openfasttrace.api.core.Newline; +import org.itsallcode.openfasttrace.api.core.SpecificationItem; + +/** + * Base class for {@link ExporterFactory} implementations that share exporter logic. + */ +public abstract class AbstractExporterFactory implements ExporterFactory +{ + private static final Logger LOG = Logger.getLogger(AbstractExporterFactory.class.getName()); + + private final String supportedOutputFormat; + + private ExporterContext context; + + /** + * Create a new {@link AbstractExporterFactory}. + * + * @param supportedOutputFormat + * the format of the supported output format, e.g. + * {@code "html"}. + */ + protected AbstractExporterFactory(final String supportedOutputFormat) + { + this.supportedOutputFormat = supportedOutputFormat; + } + + @Override + public void init(final ExporterContext context) + { + this.context = context; + } + + /** + * Get the {@link ExporterContext} set by the {@link #init(ExporterContext)} + * method. + * + * @return the {@link ExporterContext}. + */ + @Override + public ExporterContext getContext() + { + return this.context; + } + + @Override + public boolean supportsFormat(final String format) + { + return this.supportedOutputFormat.equals(format); + } + + @Override + public Exporter createExporter(final Path file, final String format, final Charset charset, + final Newline newline, final Stream itemStream) + { + if (!supportsFormat(format)) + { + throw new ExporterException("Output format '" + format + "' not supported for export"); + } + final Writer writer = createWriter(file, charset); + return createExporter(writer, itemStream, newline); + } + + private static Writer createWriter(final Path file, final Charset charset) + { + if (file == null) + { + LOG.finest(() -> "Creating exporter for STDOUT using charset " + charset); + return new OutputStreamWriter(getStdOutStream(), charset); + } + LOG.finest(() -> "Creating exporter for file " + file + " using charset " + charset); + return createFileWriter(file, charset); + } + + // Using System.out by intention + @SuppressWarnings("squid:S106") + private static PrintStream getStdOutStream() + { + return System.out; + } + + private static Writer createFileWriter(final Path file, final Charset charset) + { + try + { + return Files.newBufferedWriter(file, charset); + } + catch (final IOException e) + { + throw new ExporterException("Error creating writer for file " + file, e); + } + } +} diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/ExporterContext.java b/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/ExporterContext.java index b7df7ddec..731702dea 100644 --- a/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/ExporterContext.java +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/ExporterContext.java @@ -3,8 +3,8 @@ import org.itsallcode.openfasttrace.api.core.serviceloader.Initializable; /** - * Context for {@link ExporterFactory}. Currently only used to satisfy - * {@link Initializable} interface. + * Context for {@link ExporterFactory} implementations. Currently only used to + * satisfy {@link Initializable} interface. */ // Class is empty by intention @SuppressWarnings("squid:S2094") diff --git a/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/ExporterFactory.java b/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/ExporterFactory.java index a51dcd904..d38544ce3 100644 --- a/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/ExporterFactory.java +++ b/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/ExporterFactory.java @@ -1,9 +1,8 @@ package org.itsallcode.openfasttrace.api.exporter; -import java.io.*; + +import java.io.Writer; import java.nio.charset.Charset; -import java.nio.file.Files; import java.nio.file.Path; -import java.util.logging.Logger; import java.util.stream.Stream; import org.itsallcode.openfasttrace.api.core.Newline; @@ -11,44 +10,16 @@ import org.itsallcode.openfasttrace.api.core.serviceloader.Initializable; /** - * Super class for factories producing {@link Exporter}s. + * Interface for factories producing {@link Exporter}s. */ -public abstract class ExporterFactory implements Initializable +public interface ExporterFactory extends Initializable { - private static final Logger LOG = Logger.getLogger(ExporterFactory.class.getName()); - - private final String supportedOutputFormat; - - private ExporterContext context; - - /** - * Creates a new {@link ExporterFactory}. - * - * @param supportedOutputFormat - * the format of the supported output format, e.g. - * {@code "html"}. - */ - protected ExporterFactory(final String supportedOutputFormat) - { - this.supportedOutputFormat = supportedOutputFormat; - } - - @Override - public void init(final ExporterContext context) - { - this.context = context; - } - /** - * Get the {@link ExporterContext} set by the {@link #init(ExporterContext)} - * method. + * Get the {@link ExporterContext}. * * @return the {@link ExporterContext}. */ - public ExporterContext getContext() - { - return this.context; - } + ExporterContext getContext(); /** * Returns {@code true} if this {@link ExporterFactory} supports @@ -58,10 +29,7 @@ public ExporterContext getContext() * the output type to check. * @return {@code true} if the given type is supported for exporting. */ - public boolean supportsFormat(final String format) - { - return this.supportedOutputFormat.equals(format); - } + boolean supportsFormat(final String format); /** * Create an exporter that is able to export the given output format. @@ -78,46 +46,8 @@ public boolean supportsFormat(final String format) * the items to export * @return an exporter instance */ - public Exporter createExporter(final Path file, final String format, final Charset charset, - final Newline newline, final Stream itemStream) - { - if (!supportsFormat(format)) - { - throw new ExporterException("Output format '" + format + "' not supported for export"); - } - final Writer writer = createWriter(file, charset); - return createExporter(writer, itemStream, newline); - } - - private static Writer createWriter(final Path file, final Charset charset) - { - if (file == null) - { - LOG.finest(() -> "Creating exporter for STDOUT using charset " + charset); - return new OutputStreamWriter(getStdOutStream(), charset); - } - LOG.finest(() -> "Creating exporter for file " + file + " using charset " + charset); - return createFileWriter(file, charset); - } - - // Using System.out by intention - @SuppressWarnings("squid:S106") - private static PrintStream getStdOutStream() - { - return System.out; - } - - private static Writer createFileWriter(final Path file, final Charset charset) - { - try - { - return Files.newBufferedWriter(file, charset); - } - catch (final IOException e) - { - throw new ExporterException("Error creating writer for file " + file, e); - } - } + Exporter createExporter(final Path file, final String format, final Charset charset, + final Newline newline, final Stream itemStream); /** * Create an exporter that is able to write to the given file. @@ -130,6 +60,6 @@ private static Writer createFileWriter(final Path file, final Charset charset) * newline format * @return an {@link Exporter} instance */ - protected abstract Exporter createExporter(final Writer writer, - Stream linkedSpecItemStream, final Newline newline); + Exporter createExporter(final Writer writer, Stream linkedSpecItemStream, + final Newline newline); } diff --git a/api/src/test/java/org/itsallcode/openfasttrace/api/exporter/TestExporterFactory.java b/api/src/test/java/org/itsallcode/openfasttrace/api/exporter/TestExporterFactory.java new file mode 100644 index 000000000..0983d6185 --- /dev/null +++ b/api/src/test/java/org/itsallcode/openfasttrace/api/exporter/TestExporterFactory.java @@ -0,0 +1,49 @@ +package org.itsallcode.openfasttrace.api.exporter; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.sameInstance; + +import java.io.Writer; +import java.util.stream.Stream; + +import org.itsallcode.openfasttrace.api.core.Newline; +import org.itsallcode.openfasttrace.api.core.SpecificationItem; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class TestExporterFactory +{ + private ExporterFactory exporterFactory; + private ExporterContext context; + + @BeforeEach + void setUp() + { + context = new ExporterContext(); + exporterFactory = new TestingExporterFactory(); + } + + @Test + void testInitSetsContext() + { + exporterFactory.init(context); + assertThat(exporterFactory.getContext(), sameInstance(context)); + } + + private static class TestingExporterFactory extends AbstractExporterFactory + { + TestingExporterFactory() + { + super("test"); + } + + @Override + public Exporter createExporter(final Writer writer, + final Stream linkedSpecItemStream, final Newline newline) + { + return () -> { + // empty by intention + }; + } + } +} diff --git a/doc/plugin_developer_guide.md b/doc/plugin_developer_guide.md index 245d98141..3faddabf0 100644 --- a/doc/plugin_developer_guide.md +++ b/doc/plugin_developer_guide.md @@ -22,6 +22,8 @@ This guide describes how to develop [plugins](plugins.md) for OpenFastTrace (OFT Importer plugins will usually extend [`org.itsallcode.openfasttrace.api.importer.AbstractImporterFactory`](https://github.com/itsallcode/openfasttrace/blob/main/api/src/main/java/org/itsallcode/openfasttrace/api/importer/AbstractImporterFactory.java) instead of implementing `ImporterFactory` directly so they inherit the standard context handling. + Exporter plugins will usually extend [`org.itsallcode.openfasttrace.api.exporter.AbstractExporterFactory`](https://github.com/itsallcode/openfasttrace/blob/main/api/src/main/java/org/itsallcode/openfasttrace/api/exporter/AbstractExporterFactory.java) instead of implementing `ExporterFactory` directly so they inherit the standard exporter and context handling. + 3. Create a file in `src/main/resources/$INTERFACE_FQN`, using the fully qualified class name of the interface as file name. 4. Add the fully qualified class name of your new plugin class to the new file, e.g. `com.example.oft.import.MyImporter` diff --git a/doc/spec/design.md b/doc/spec/design.md index aa3de6b6d..237ab4e57 100644 --- a/doc/spec/design.md +++ b/doc/spec/design.md @@ -170,6 +170,9 @@ The Plugin loader supports loading factories for the following plugin types: Reporter implementations usually extend `org.itsallcode.openfasttrace.api.report.AbstractReporterFactory` to reuse standard reporter context handling while keeping `ReporterFactory` as the service type. +Exporter implementations usually extend `org.itsallcode.openfasttrace.api.exporter.AbstractExporterFactory` +to reuse standard exporter and context handling while keeping `ExporterFactory` as the service type. + Covers: * [`req~plugins.types~1`](system_requirements.md#supported-plugin-types) diff --git a/exporter/specobject/src/main/java/org/itsallcode/openfasttrace/exporter/specobject/SpecobjectExporterFactory.java b/exporter/specobject/src/main/java/org/itsallcode/openfasttrace/exporter/specobject/SpecobjectExporterFactory.java index 5245f4f10..319062315 100644 --- a/exporter/specobject/src/main/java/org/itsallcode/openfasttrace/exporter/specobject/SpecobjectExporterFactory.java +++ b/exporter/specobject/src/main/java/org/itsallcode/openfasttrace/exporter/specobject/SpecobjectExporterFactory.java @@ -9,6 +9,7 @@ import org.itsallcode.openfasttrace.api.core.Newline; import org.itsallcode.openfasttrace.api.core.SpecificationItem; +import org.itsallcode.openfasttrace.api.exporter.AbstractExporterFactory; import org.itsallcode.openfasttrace.api.exporter.Exporter; import org.itsallcode.openfasttrace.api.exporter.ExporterException; import org.itsallcode.openfasttrace.api.exporter.ExporterFactory; @@ -18,7 +19,7 @@ * {@link ExporterFactory} for creating {@link Exporter}s that support writing * specobject output files. */ -public class SpecobjectExporterFactory extends ExporterFactory +public class SpecobjectExporterFactory extends AbstractExporterFactory { private static final String SUPPORTED_FORMAT = "specobject"; private final XMLOutputFactory xmlOutputFactory; @@ -31,7 +32,7 @@ public SpecobjectExporterFactory() } @Override - protected Exporter createExporter(final Writer writer, + public Exporter createExporter(final Writer writer, final Stream itemStream, final Newline newline) { final XMLStreamWriter xmlWriter = createXmlWriter(writer); From 9e382f71813cea6b78985dbee9714f95b36232a7 Mon Sep 17 00:00:00 2001 From: redcatbear Date: Fri, 19 Jun 2026 16:57:39 +0200 Subject: [PATCH 6/9] #536: Renamed `StreamWriterDelegate` to `AbstractStreamWriterDelegate`. --- ...mWriterDelegate.java => AbstractStreamWriterDelegate.java} | 4 ++-- .../openfasttrace/testutil/xml/IndentingXMLStreamWriter.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename testutil/src/main/java/org/itsallcode/openfasttrace/testutil/xml/{StreamWriterDelegate.java => AbstractStreamWriterDelegate.java} (97%) diff --git a/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/xml/StreamWriterDelegate.java b/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/xml/AbstractStreamWriterDelegate.java similarity index 97% rename from testutil/src/main/java/org/itsallcode/openfasttrace/testutil/xml/StreamWriterDelegate.java rename to testutil/src/main/java/org/itsallcode/openfasttrace/testutil/xml/AbstractStreamWriterDelegate.java index edb878c12..cfa2ddfa4 100644 --- a/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/xml/StreamWriterDelegate.java +++ b/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/xml/AbstractStreamWriterDelegate.java @@ -8,7 +8,7 @@ * This interface allows intercepting calls to a stream writer, pre-processing * them and then handing the rest of the work of to a given delegate. */ -public abstract class StreamWriterDelegate implements XMLStreamWriter +public abstract class AbstractStreamWriterDelegate implements XMLStreamWriter { /** Stream writer to delegate to. */ protected XMLStreamWriter out; @@ -19,7 +19,7 @@ public abstract class StreamWriterDelegate implements XMLStreamWriter * @param out * stream writer to delegate to */ - protected StreamWriterDelegate(final XMLStreamWriter out) + protected AbstractStreamWriterDelegate(final XMLStreamWriter out) { this.out = out; } diff --git a/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/xml/IndentingXMLStreamWriter.java b/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/xml/IndentingXMLStreamWriter.java index 4142075f2..423a2c451 100644 --- a/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/xml/IndentingXMLStreamWriter.java +++ b/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/xml/IndentingXMLStreamWriter.java @@ -6,7 +6,7 @@ /** * A stream writer for XML that adds indentation. */ -public class IndentingXMLStreamWriter extends StreamWriterDelegate +public class IndentingXMLStreamWriter extends AbstractStreamWriterDelegate { private static final int WROTE_MARKUP = 1; private static final int WROTE_DATA = 2; From a4fcef465fc7e1777ce49dc5e0e795265ef24e82 Mon Sep 17 00:00:00 2001 From: redcatbaer Date: Fri, 19 Jun 2026 19:37:32 +0200 Subject: [PATCH 7/9] 536: Renamed `ImporterFactoryTestBase` to `AbstractImporterFactoryTestBase`. --- .../importer/markdown/TestMarkdownImporterFactory.java | 4 ++-- .../TestRestructuredTextImporterFactory.java | 4 ++-- .../specobject/TestSpecobjectImporterFactory.java | 4 ++-- .../importer/tag/TestTagImporterFactory.java | 4 ++-- .../importer/zip/TestZipFileImporterFactory.java | 4 ++-- ...TestBase.java => AbstractImporterFactoryTestBase.java} | 8 ++++---- .../testutil/importer/ImporterFactoryTestBaseTest.java | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) rename testutil/src/main/java/org/itsallcode/openfasttrace/testutil/importer/{ImporterFactoryTestBase.java => AbstractImporterFactoryTestBase.java} (93%) diff --git a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporterFactory.java b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporterFactory.java index 8e86a3724..a237678f4 100644 --- a/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporterFactory.java +++ b/importer/markdown/src/test/java/org/itsallcode/openfasttrace/importer/markdown/TestMarkdownImporterFactory.java @@ -4,12 +4,12 @@ import java.util.List; -import org.itsallcode.openfasttrace.testutil.importer.ImporterFactoryTestBase; +import org.itsallcode.openfasttrace.testutil.importer.AbstractImporterFactoryTestBase; /** * Tests for {@link MarkdownImporterFactory} */ -class TestMarkdownImporterFactory extends ImporterFactoryTestBase +class TestMarkdownImporterFactory extends AbstractImporterFactoryTestBase { @Override protected MarkdownImporterFactory createFactory() diff --git a/importer/restructuredtext/src/test/java/org/itsallcode/openfasttrace/importer/restructuredtext/TestRestructuredTextImporterFactory.java b/importer/restructuredtext/src/test/java/org/itsallcode/openfasttrace/importer/restructuredtext/TestRestructuredTextImporterFactory.java index accf2fafd..64d0a62cb 100644 --- a/importer/restructuredtext/src/test/java/org/itsallcode/openfasttrace/importer/restructuredtext/TestRestructuredTextImporterFactory.java +++ b/importer/restructuredtext/src/test/java/org/itsallcode/openfasttrace/importer/restructuredtext/TestRestructuredTextImporterFactory.java @@ -1,6 +1,6 @@ package org.itsallcode.openfasttrace.importer.restructuredtext; -import org.itsallcode.openfasttrace.testutil.importer.ImporterFactoryTestBase; +import org.itsallcode.openfasttrace.testutil.importer.AbstractImporterFactoryTestBase; import java.util.List; @@ -9,7 +9,7 @@ /** * Tests for {@link RestructuredTextImporterFactory} */ -class TestRestructuredTextImporterFactory extends ImporterFactoryTestBase +class TestRestructuredTextImporterFactory extends AbstractImporterFactoryTestBase { @Override protected RestructuredTextImporterFactory createFactory() diff --git a/importer/specobject/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImporterFactory.java b/importer/specobject/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImporterFactory.java index fccb70bcc..198855548 100644 --- a/importer/specobject/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImporterFactory.java +++ b/importer/specobject/src/test/java/org/itsallcode/openfasttrace/importer/specobject/TestSpecobjectImporterFactory.java @@ -12,7 +12,7 @@ import org.itsallcode.openfasttrace.api.importer.input.InputFile; import org.itsallcode.openfasttrace.api.importer.input.RealFileInput; -import org.itsallcode.openfasttrace.testutil.importer.ImporterFactoryTestBase; +import org.itsallcode.openfasttrace.testutil.importer.AbstractImporterFactoryTestBase; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.mockito.Mock; @@ -21,7 +21,7 @@ * Tests for {@link SpecobjectImporterFactory} */ class TestSpecobjectImporterFactory - extends ImporterFactoryTestBase + extends AbstractImporterFactoryTestBase { @Override protected SpecobjectImporterFactory createFactory() diff --git a/importer/tag/src/test/java/org/itsallcode/openfasttrace/importer/tag/TestTagImporterFactory.java b/importer/tag/src/test/java/org/itsallcode/openfasttrace/importer/tag/TestTagImporterFactory.java index 50a71b4b5..d9c44b08c 100644 --- a/importer/tag/src/test/java/org/itsallcode/openfasttrace/importer/tag/TestTagImporterFactory.java +++ b/importer/tag/src/test/java/org/itsallcode/openfasttrace/importer/tag/TestTagImporterFactory.java @@ -5,13 +5,13 @@ import java.util.List; import org.itsallcode.openfasttrace.api.importer.ImporterContext; -import org.itsallcode.openfasttrace.testutil.importer.ImporterFactoryTestBase; +import org.itsallcode.openfasttrace.testutil.importer.AbstractImporterFactoryTestBase; /** * Tests for {@link TagImporterFactory} */ // [utest->dsn~import.full-coverage-tag~1] -class TestTagImporterFactory extends ImporterFactoryTestBase +class TestTagImporterFactory extends AbstractImporterFactoryTestBase { @Override protected TagImporterFactory createFactory() diff --git a/importer/zip/src/test/java/org/itsallcode/openfasttrace/importer/zip/TestZipFileImporterFactory.java b/importer/zip/src/test/java/org/itsallcode/openfasttrace/importer/zip/TestZipFileImporterFactory.java index c91f18313..257132486 100644 --- a/importer/zip/src/test/java/org/itsallcode/openfasttrace/importer/zip/TestZipFileImporterFactory.java +++ b/importer/zip/src/test/java/org/itsallcode/openfasttrace/importer/zip/TestZipFileImporterFactory.java @@ -9,12 +9,12 @@ import java.util.List; import org.itsallcode.openfasttrace.api.importer.ImporterService; -import org.itsallcode.openfasttrace.testutil.importer.ImporterFactoryTestBase; +import org.itsallcode.openfasttrace.testutil.importer.AbstractImporterFactoryTestBase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; -class TestZipFileImporterFactory extends ImporterFactoryTestBase +class TestZipFileImporterFactory extends AbstractImporterFactoryTestBase { @Mock private ImporterService importerServiceMock; diff --git a/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/importer/ImporterFactoryTestBase.java b/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/importer/AbstractImporterFactoryTestBase.java similarity index 93% rename from testutil/src/main/java/org/itsallcode/openfasttrace/testutil/importer/ImporterFactoryTestBase.java rename to testutil/src/main/java/org/itsallcode/openfasttrace/testutil/importer/AbstractImporterFactoryTestBase.java index c6400c422..43d89999b 100644 --- a/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/importer/ImporterFactoryTestBase.java +++ b/testutil/src/main/java/org/itsallcode/openfasttrace/testutil/importer/AbstractImporterFactoryTestBase.java @@ -4,7 +4,6 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.sameInstance; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.lenient; import java.nio.file.Path; import java.nio.file.Paths; @@ -16,6 +15,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; /** @@ -25,7 +25,7 @@ * type of the factory under test */ @ExtendWith(MockitoExtension.class) -public abstract class ImporterFactoryTestBase +public abstract class AbstractImporterFactoryTestBase { /** * Mock of the importer context used in tests. @@ -36,7 +36,7 @@ public abstract class ImporterFactoryTestBase /** * Create a new instance of the test base. */ - protected ImporterFactoryTestBase() + protected AbstractImporterFactoryTestBase() { // Default constructor to fix compiler warning "missing-explicit-ctor" } @@ -44,7 +44,7 @@ protected ImporterFactoryTestBase() @BeforeEach void initMocks() { - lenient().when(this.contextMock.getImportSettings()).thenReturn(ImportSettings.createDefault()); + Mockito.lenient().when(this.contextMock.getImportSettings()).thenReturn(ImportSettings.createDefault()); } @Test diff --git a/testutil/src/test/java/org/itsallcode/openfasttrace/testutil/importer/ImporterFactoryTestBaseTest.java b/testutil/src/test/java/org/itsallcode/openfasttrace/testutil/importer/ImporterFactoryTestBaseTest.java index f8473d8d5..64e916cf6 100644 --- a/testutil/src/test/java/org/itsallcode/openfasttrace/testutil/importer/ImporterFactoryTestBaseTest.java +++ b/testutil/src/test/java/org/itsallcode/openfasttrace/testutil/importer/ImporterFactoryTestBaseTest.java @@ -17,7 +17,7 @@ void testConstructor() assertThat(new DummyImplementation(), notNullValue()); } - private static class DummyImplementation extends ImporterFactoryTestBase + private static class DummyImplementation extends AbstractImporterFactoryTestBase { @Override protected DummyImporterFactory createFactory() From bd27ea93002a02606962dfada8a66d75849226c2 Mon Sep 17 00:00:00 2001 From: redcatbaer Date: Fri, 19 Jun 2026 19:50:57 +0200 Subject: [PATCH 8/9] 536: Added test for `AbstractImporterFactory`. --- .../importer/TestAbstractImporterFactory.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 api/src/test/java/org/itsallcode/openfasttrace/api/importer/TestAbstractImporterFactory.java diff --git a/api/src/test/java/org/itsallcode/openfasttrace/api/importer/TestAbstractImporterFactory.java b/api/src/test/java/org/itsallcode/openfasttrace/api/importer/TestAbstractImporterFactory.java new file mode 100644 index 000000000..cb469c01e --- /dev/null +++ b/api/src/test/java/org/itsallcode/openfasttrace/api/importer/TestAbstractImporterFactory.java @@ -0,0 +1,60 @@ +package org.itsallcode.openfasttrace.api.importer; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.itsallcode.openfasttrace.api.importer.input.InputFile; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +/** + * Unit test for {@link AbstractImporterFactory}. + */ +class TestAbstractImporterFactory +{ + private ImporterFactory importerFactory; + private ImporterContext context; + + @BeforeEach + void setUp() + { + context = Mockito.mock(ImporterContext.class); + importerFactory = new TestingImporterFactory(); + } + + @Test + void testInitSetsContext() + { + importerFactory.init(context); + assertThat(importerFactory.getContext(), sameInstance(context)); + } + + @Test + void testGetContextThrowsExceptionWhenNotInitialized() + { + assertThrows(NullPointerException.class, () -> importerFactory.getContext()); + } + + private static class TestingImporterFactory extends AbstractImporterFactory + { + @Override + public boolean supportsFile(final InputFile file) + { + return false; + } + + @Override + public Importer createImporter(final InputFile file, final ImportEventListener listener) + { + return null; + } + + @Override + public int getPriority() + { + return 0; + } + } +} From b2f6d4c33f824bc5f6568c7c3925bc2c3a585b6e Mon Sep 17 00:00:00 2001 From: redcatbaer Date: Fri, 19 Jun 2026 19:55:53 +0200 Subject: [PATCH 9/9] 536: Added fix for EqualsVerifier. --- api/src/main/java/module-info.java | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java index b16994285..4d7015cbf 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -13,6 +13,7 @@ exports org.itsallcode.openfasttrace.api.exporter; exports org.itsallcode.openfasttrace.api.report; // Workaround: Required for the EqualsVerifier + opens org.itsallcode.openfasttrace.api; opens org.itsallcode.openfasttrace.api.core; requires java.logging;