PomSiteScanner.java

package network.ike.plugin.ws;

import network.ike.plugin.ws.ReleasePlan.GA;
import network.ike.plugin.ws.ReleasePlan.ReferenceKind;
import network.ike.plugin.ws.ReleasePlan.ReferenceSite;
import org.apache.maven.api.model.Dependency;
import org.apache.maven.api.model.Parent;
import org.apache.maven.api.model.Plugin;

import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Extracts the version-bearing reference sites from a single POM file.
 *
 * <p>Reads the POM via {@link PomModel} (Maven 4 model API,
 * location-tracking enabled) and walks the {@code <parent>} block,
 * the {@code <dependencies>} / {@code <dependencyManagement>}
 * sections, and the {@code <build><plugins>} /
 * {@code <build><pluginManagement>} sections.
 * For each site, records the target groupId:artifactId and the
 * literal text at the {@code <version>} element.
 *
 * <p>Property references are preserved verbatim.
 * A version of {@code ${ike-tooling.version}} is recorded as the text
 * {@code ${ike-tooling.version}} — no property resolution.
 * Property resolution is the plan-compute layer's job,
 * using the full reactor's declaration set.
 *
 * <p>Thread-safe: all methods are stateless.
 *
 * @see ReleasePlan
 * @see PomModel
 */
final class PomSiteScanner {

    private PomSiteScanner() {}

    /**
     * A scanned POM's declarations and version references.
     *
     * @param pomPath              absolute path to the scanned POM
     * @param selfGa               the artifact this POM produces —
     *                             groupId:artifactId, with groupId
     *                             falling back to the parent's when
     *                             inherited. {@code null} only if the
     *                             POM is malformed; well-formed POMs
     *                             always have an artifactId
     * @param propertyDeclarations {@code <properties>} entries in this
     *                             POM (name → value, verbatim — a
     *                             value of {@code ${other}} stays
     *                             unresolved)
     * @param sites                every parent, plugin, and dependency
     *                             version site in this POM
     */
    record PomSiteSurvey(
            Path pomPath,
            GA selfGa,
            Map<String, String> propertyDeclarations,
            List<ReferenceSite> sites) {

        PomSiteSurvey {
            propertyDeclarations = Map.copyOf(propertyDeclarations);
            sites = List.copyOf(sites);
        }
    }

    /**
     * Scan the POM at {@code pomPath} for version-bearing reference
     * sites and property declarations.
     *
     * @param pomPath absolute path to pom.xml
     * @return a survey of the POM's property declarations and
     *         reference sites
     * @throws IOException if the POM cannot be read or parsed
     */
    static PomSiteSurvey scan(Path pomPath) throws IOException {
        PomModel pom = PomModel.parse(pomPath);
        List<ReferenceSite> sites = new ArrayList<>();

        String selfGid = pom.groupId();
        String selfAid = pom.artifactId();
        GA selfGa = selfAid == null ? null : new GA(selfGid, selfAid);

        Parent parent = pom.parent();
        if (parent != null) {
            sites.add(new ReferenceSite(
                    pomPath,
                    ReferenceKind.PARENT,
                    new GA(parent.getGroupId(), parent.getArtifactId()),
                    parent.getVersion()));
        }

        for (Dependency dep : pom.allDependencies()) {
            sites.add(new ReferenceSite(
                    pomPath,
                    ReferenceKind.DEPENDENCY,
                    new GA(dep.getGroupId(), dep.getArtifactId()),
                    dep.getVersion()));
        }

        for (Plugin plugin : pom.allPlugins()) {
            String gid = plugin.getGroupId();
            if (gid == null) {
                gid = "org.apache.maven.plugins";
            }
            sites.add(new ReferenceSite(
                    pomPath,
                    ReferenceKind.PLUGIN,
                    new GA(gid, plugin.getArtifactId()),
                    plugin.getVersion()));
        }

        Map<String, String> props = pom.properties();
        return new PomSiteSurvey(
                pomPath,
                selfGa,
                props != null ? props : Map.of(),
                Collections.unmodifiableList(sites));
    }
}