001/*
002 * JDrupes Builder
003 * Copyright (C) 2026 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.builder.core;
020
021import java.nio.file.Path;
022import java.util.List;
023import java.util.Properties;
024import org.apache.commons.cli.CommandLine;
025import org.jdrupes.builder.api.ConfigurationException;
026import org.jdrupes.builder.api.Project;
027import org.jdrupes.builder.api.RootProject;
028
029/// The Class LauncherBase.
030///
031@SuppressWarnings("PMD.UseUtilityClass")
032public class LauncherBase {
033
034    /// The ScopedValue for the build context.
035    @SuppressWarnings("PMD.FieldNamingConventions")
036    protected static final ScopedValue<
037            DefaultBuildContext> scopedBuildContext = ScopedValue.newInstance();
038
039    static {
040        ScopedValueContext.add(scopedBuildContext);
041    }
042
043    /// Initializes a new launcher base.
044    ///
045    public LauncherBase() {
046        // Make javadoc happy
047    }
048
049    /// Returns the context.
050    ///
051    /// @return the optional
052    ///
053    public static DefaultBuildContext context() {
054        if (!scopedBuildContext.isBound()) {
055            throw new ConfigurationException()
056                .cause(new IllegalStateException())
057                .message("BuildContext is not bound.");
058        }
059        return scopedBuildContext.get();
060    }
061
062    /// Creates and initializes the root project and the sub projects.
063    /// Adds the sub projects to the root project automatically. This
064    /// method should be used if the launcher detects the sub projects
065    /// e.g. by reflection and the root project does not add its sub
066    /// projects itself.
067    ///
068    /// @param buildRoot the build root
069    /// @param rootProject the root project
070    /// @param subprojects the sub projects
071    /// @param jdbldProps the builder properties
072    /// @param commandLine the command line
073    /// @return the root project
074    ///
075    protected static AbstractRootProject createProjects(
076            Path buildRoot, Class<? extends RootProject> rootProject,
077            List<Class<? extends Project>> subprojects,
078            Properties jdbldProps, CommandLine commandLine) {
079        try {
080            return ScopedValue
081                .where(scopedBuildContext,
082                    new DefaultBuildContext(buildRoot, jdbldProps, commandLine))
083                .call(() -> {
084                    var result = (AbstractRootProject) rootProject
085                        .getConstructor().newInstance();
086                    result.unlockProviders();
087                    subprojects.forEach(result::project);
088                    scopedBuildContext.get().buildProject().complete(result);
089                    return result;
090                });
091        } catch (SecurityException | NegativeArraySizeException
092                | IllegalArgumentException | ReflectiveOperationException e) {
093            throw new IllegalArgumentException(e);
094        }
095    }
096}