Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package life.qbic.compass;

import java.util.List;
import life.qbic.compass.model.SignPostingResult;

/**
* Strategy interface for aggregating {@link SignPostingResult} instances produced by
* multiple {@link life.qbic.compass.spi.SignPostingValidator}s into a single result.
*
* <p>
* Aggregation strategies define how (and whether) multiple
* {@link life.qbic.compass.model.Level2LinksetView} instances are combined, selected,
* ignored, or rejected when more than one validator produces such a view.
* </p>
*
* <h2>Responsibilities</h2>
* <ul>
* <li>Inspect the list of {@link SignPostingResult}s returned by validators.</li>
* <li>Decide how to handle zero, one, or multiple Level&nbsp;2 Linkset Views.</li>
* <li>Return a single {@link SignPostingResult} that represents the aggregated outcome.</li>
* </ul>
*
* <h2>Non-responsibilities</h2>
* <ul>
* <li>Strategies must <em>not</em> execute validators.</li>
* <li>Strategies must <em>not</em> modify individual {@link SignPostingResult} instances.</li>
* <li>Strategies must <em>not</em> perform model or profile validation.</li>
* </ul>
*
* <h2>Error handling</h2>
* <p>
* Implementations may throw {@link AggregationStrategyException} if the aggregation
* policy cannot be satisfied (e.g. when multiple Linkset Views are present but the
* strategy requires exactly one).
* </p>
*
* <p>
* This interface is primarily intended for internal use by
* {@link life.qbic.compass.SignPostingProcessor}, but is exposed to allow advanced
* clients to supply custom aggregation behavior.
* </p>
*
* @since 1.0.0
*/
public interface LinkSetViewAggregationStrategy {

/**
* Aggregates the provided validation results into a single {@link SignPostingResult}.
*
* <p>
* The input list represents the results of all configured validators, in execution order.
* Implementations may inspect, select, merge, or ignore individual results according to
* their aggregation policy.
* </p>
*
* @param results the results produced by all executed validators
* @return a single aggregated {@link SignPostingResult}
* @throws AggregationStrategyException if aggregation fails according to the strategy rules
*/
SignPostingResult apply(List<SignPostingResult> results)
throws AggregationStrategyException;

/**
* Exception thrown when a {@link LinkSetViewAggregationStrategy} cannot successfully
* aggregate the provided results.
*
* <p>
* This is a runtime exception because aggregation failures indicate a configuration
* or policy violation rather than a recoverable validation error.
* </p>
*/
class AggregationStrategyException extends RuntimeException {

public AggregationStrategyException(String message) {
super(message);
}
}
}
95 changes: 95 additions & 0 deletions src/main/java/life/qbic/compass/LinkSetViewAggregations.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package life.qbic.compass;

import life.qbic.compass.SignPostingProcessor.LinkSetViewAggregationMode;
import life.qbic.compass.processing.FailOnMultipleLinkSetViewAggregation;
import life.qbic.compass.processing.MergeLinkSetViewAggregation;
import life.qbic.compass.processing.NoLinkSetViewAggregation;
import life.qbic.compass.processing.TakeFirstLinkSetViewAggregation;

/**
* Factory and registry for {@link LinkSetViewAggregationStrategy} implementations.
*
* <p>
* This class centralizes the mapping between {@link SignPostingProcessor.LinkSetViewAggregationMode}
* values and their concrete aggregation strategy implementations. It exists to:
* </p>
*
* <ul>
* <li>keep {@link SignPostingProcessor} free of conditional logic,</li>
* <li>encapsulate aggregation policy decisions in one place, and</li>
* <li>provide a stable extension point for future aggregation modes.</li>
* </ul>
*
* <h2>Design intent</h2>
* <p>
* Aggregation of {@code Level2LinksetView} instances is a <em>policy decision</em>, not a validation
* concern. Different clients may want:
* </p>
* <ul>
* <li>to ignore linkset views entirely,</li>
* <li>to accept only the first valid linkset view,</li>
* <li>to merge multiple linkset views into a single composite view, or</li>
* <li>to fail fast if more than one linkset view is produced.</li>
* </ul>
*
* <p>
* This factory ensures that the processor only needs to work with an enum
* ({@link SignPostingProcessor.LinkSetViewAggregationMode}), while the concrete
* strategy selection and lifecycle is handled here.
* </p>
*
* <h2>Implementation notes for maintainers</h2>
* <ul>
* <li>
* Strategies are held as <strong>singleton instances</strong>.
* They must therefore be stateless and thread-safe.
* </li>
* <li>
* If a future strategy requires configuration or state, this design will
* need to be revisited (e.g. per-processor instantiation instead of singletons).
* </li>
* <li>
* Adding a new aggregation mode requires:
* <ol>
* <li>adding a new enum constant to {@code LinkSetViewAggregationMode},</li>
* <li>implementing {@link LinkSetViewAggregationStrategy}, and</li>
* <li>registering it in this factory.</li>
* </ol>
* </li>
* </ul>
*
* <h2>Stability guarantees</h2>
* <p>
* This class is package-private and intended for internal use only. The set of
* available aggregation modes is part of the public API via the enum, but the
* concrete strategy classes and their internal behavior may evolve.
* </p>
*
* @since 1.0.0
* @author Sven Fillinger
*/
final class LinkSetViewAggregations {

private static final LinkSetViewAggregationStrategy NONE =
new NoLinkSetViewAggregation();
private static final LinkSetViewAggregationStrategy FIRST =
new TakeFirstLinkSetViewAggregation();
private static final LinkSetViewAggregationStrategy MERGE =
new MergeLinkSetViewAggregation();
private static final LinkSetViewAggregationStrategy FAIL =
new FailOnMultipleLinkSetViewAggregation();


private LinkSetViewAggregations() {
// utility class
}

static LinkSetViewAggregationStrategy forMode(LinkSetViewAggregationMode mode) {
return switch (mode) {
case LinkSetViewAggregationMode.NONE -> NONE;
case LinkSetViewAggregationMode.FIRST -> FIRST;
case LinkSetViewAggregationMode.MERGE -> MERGE;
case LinkSetViewAggregationMode.FAIL_ON_MULTIPLE -> FAIL;
};
}
}
Loading
Loading