All Downloads are FREE. Search and download functionalities are using the official Maven repository.

name.remal.gradle_plugins.idea_settings.IdeaSettings Maven / Gradle / Ivy

The newest version!
package name.remal.gradle_plugins.idea_settings;

import static java.lang.Boolean.parseBoolean;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static name.remal.gradle_plugins.toolkit.ObjectUtils.doNotInline;
import static name.remal.gradle_plugins.toolkit.PathUtils.normalizePath;

import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.annotation.Nullable;
import javax.inject.Inject;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.val;
import name.remal.gradle_plugins.idea_settings.internal.IdeaSettingsAction;
import name.remal.gradle_plugins.idea_settings.internal.SpecificIdeaXmlFileInitializer;
import name.remal.gradle_plugins.idea_settings.internal.SpecificIdeaXmlFileProcessor;
import name.remal.gradle_plugins.idea_settings.internal.XsltFileIdeaXmlFileInitializer;
import name.remal.gradle_plugins.idea_settings.internal.XsltFileIdeaXmlFilePostProcessor;
import name.remal.gradle_plugins.idea_settings.internal.XsltFileIdeaXmlFileProcessor;
import org.gradle.api.Action;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.XmlProvider;
import org.w3c.dom.Document;

@Getter
@Setter
public class IdeaSettings {

    public static final String IDEA_SETTINGS_EXTENSION_NAME = doNotInline("ideaSettings");


    private boolean enabled = true;

    private boolean explicitlyEnabled = false;


    private final Set requiredPlugins = new TreeSet<>();

    public void setRequiredPlugins(Iterable requiredPlugins) {
        setStringsCollectionFromIterable(this.requiredPlugins, requiredPlugins);
    }


    private final Set entryPoints = new TreeSet<>();

    {
        entryPoints.addAll(asList(
            "com.google.auto.service.AutoService",
            "name.remal.gradle_plugins.api.AutoService"
        ));
    }

    public void setEntryPoints(Iterable entryPoints) {
        setStringsCollectionFromIterable(this.entryPoints, entryPoints);
    }


    private final IdeaNullabilitySettingsSettings nullability;

    public void nullability(Action action) {
        action.execute(nullability);
    }


    @Nullable
    private Path externalAnnotationsRootDir;

    public void setExternalAnnotationsRootDir(@Nullable Object dir) {
        this.externalAnnotationsRootDir = dir != null ? normalizePath(project.file(dir).toPath()) : null;
    }


    private final IdeaRunOnSaveSettings runOnSave;

    public void runOnSave(Action action) {
        action.execute(runOnSave);
    }


    private final IdeaCheckstyleSettings checkstyle;

    public void checkstyle(Action action) {
        action.execute(checkstyle);
    }


    private final IdeaRunConfigurationsSettings runConfigurations;

    public void runConfigurations(Action action) {
        action.execute(runConfigurations);
    }


    private final IdeaDatabaseSettings database;

    public void database(Action action) {
        action.execute(database);
    }


    private final List> ideaDirProcessors = new ArrayList<>();

    {
        streamServices(IdeaDirProcessor.class)
            .forEach(this::addIdeaDirProcessor);
    }

    public void addIdeaDirProcessor(Action processor) {
        ideaDirProcessors.add(processor);
    }


    private final Map> xmlFileInitializers = new LinkedHashMap<>();

    {
        streamServices(SpecificIdeaXmlFileInitializer.class)
            .forEach(initializer -> setXmlFileInitializer(initializer.getRelativeFilePath(), initializer));
    }

    public void setXmlFileInitializer(String relativeFilePath, Supplier initializer) {
        relativeFilePath = canonizeIdeaSettingsRelativeFilePath(relativeFilePath);
        val prevInitializer = xmlFileInitializers.putIfAbsent(relativeFilePath, initializer);
        if (prevInitializer != null) {
            throw new IllegalArgumentException("Initializer for IDEA file has already been added: " + relativeFilePath);
        }
    }

    /**
     * @param xsltTemplateFile Resolves a file path relative to the project directory of this project. See
     *     {@link Project#file(Object)}.
     */
    public void setXmlFileInitializerXslt(
        String relativeFilePath,
        Object xsltTemplateFile,
        Map templateParams
    ) {
        val xsltTemplateUri = project.file(xsltTemplateFile).toURI();
        setXmlFileInitializer(relativeFilePath, new XsltFileIdeaXmlFileInitializer(xsltTemplateUri, templateParams));
    }

    /**
     * See {@link #setXmlFileInitializerXslt(String, Object, Map)}.
     */
    public void setXmlFileInitializerXslt(
        String relativeFilePath,
        Object xsltTemplateFile
    ) {
        setXmlFileInitializerXslt(relativeFilePath, xsltTemplateFile, emptyMap());
    }

    private final Map>> xmlFilesProcessors = new LinkedHashMap<>();

    {
        streamServices(SpecificIdeaXmlFileProcessor.class)
            .forEach(processor -> processXmlFile(processor.getRelativeFilePath(), processor));
    }

    public void processXmlFile(String relativeFilePath, Action action) {
        relativeFilePath = canonizeIdeaSettingsRelativeFilePath(relativeFilePath);
        xmlFilesProcessors.computeIfAbsent(relativeFilePath, __ -> new ArrayList<>())
            .add(action);
    }

    /**
     * @param xsltTemplateFile Resolves a file path relative to the project directory of this project. See
     *     {@link Project#file(Object)}.
     */
    public void processXmlFileWithXslt(
        String relativeFilePath,
        Object xsltTemplateFile,
        Map xsltTemplateParams
    ) {
        val xsltTemplateUri = project.file(xsltTemplateFile).toURI();
        processXmlFile(relativeFilePath, new XsltFileIdeaXmlFileProcessor(xsltTemplateUri, xsltTemplateParams));
    }

    /**
     * See {@link #processXmlFileWithXslt(String, Object, Map)}.
     */
    public void processXmlFileWithXslt(
        String relativeFilePath,
        Object xsltTemplateFile
    ) {
        processXmlFileWithXslt(relativeFilePath, xsltTemplateFile, emptyMap());
    }


    private final List> xmlFilesPostProcessors = new ArrayList<>();

    {
        streamServices(IdeaXmlFilePostProcessor.class)
            .forEach(this::addXmlFilesPostProcessor);
    }

    public void addXmlFilesPostProcessor(Action postProcessor) {
        this.xmlFilesPostProcessors.add(postProcessor);
    }

    /**
     * @param xsltTemplateFile Resolves a file path relative to the project directory of this project. See
     *     {@link Project#file(Object)}.
     */
    public void addXmlFilesXsltPostProcessor(
        Object xsltTemplateFile,
        Map templateParams
    ) {
        val xsltTemplateUri = project.file(xsltTemplateFile).toURI();
        addXmlFilesPostProcessor(new XsltFileIdeaXmlFilePostProcessor(xsltTemplateUri, templateParams));
    }

    /**
     * See {@link #addXmlFilesXsltPostProcessor(Object, Map)}.
     */
    public void addXmlFilesXsltPostProcessor(
        Object xsltTemplateFile
    ) {
        addXmlFilesXsltPostProcessor(xsltTemplateFile, emptyMap());
    }


    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    private final Project project;

    @Inject
    public IdeaSettings(Project project) {
        this.project = project;
        this.explicitlyEnabled = parseBoolean(String.valueOf(project.findProperty(
            IDEA_SETTINGS_EXTENSION_NAME + ".explicitlyEnabled"
        )));
        this.nullability = project.getObjects().newInstance(IdeaNullabilitySettingsSettings.class);
        this.runOnSave = project.getObjects().newInstance(IdeaRunOnSaveSettings.class);
        this.checkstyle = project.getObjects().newInstance(IdeaCheckstyleSettings.class, project);
        this.runConfigurations = project.getObjects().newInstance(IdeaRunConfigurationsSettings.class, project);
        this.database = project.getObjects().newInstance(IdeaDatabaseSettings.class);
    }


    static String canonizeIdeaSettingsRelativeFilePath(String relativeFilePath) {
        relativeFilePath = relativeFilePath.replace(File.separatorChar, '/');

        if (relativeFilePath.startsWith("/")) {
            throw new GradleException("Not a relative IDEA file path: " + relativeFilePath);
        } else if (relativeFilePath.contains("/../")) {
            throw new GradleException("Not a normalized IDEA file path (contains '/../'): " + relativeFilePath);
        }

        return relativeFilePath;
    }

    static void setStringsCollectionFromIterable(
        Collection collection,
        Iterable iterable
    ) {
        collection.clear();
        StreamSupport.stream(iterable.spliterator(), false)
            .filter(Objects::nonNull)
            .map(Object::toString)
            .forEach(collection::add);
    }


    private static  Stream streamServices(Class serviceType) {
        return StreamSupport.stream(
                ServiceLoader.load(serviceType, serviceType.getClassLoader()).spliterator(),
                false
            )
            .sorted();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy