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

org.jreleaser.model.internal.common.FileSet Maven / Gradle / Ivy

The newest version!
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * Copyright 2020-2024 The JReleaser authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jreleaser.model.internal.common;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.jreleaser.bundle.RB;
import org.jreleaser.logging.JReleaserLogger;
import org.jreleaser.model.Active;
import org.jreleaser.model.internal.JReleaserContext;

import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.FileVisitResult.SKIP_SUBTREE;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;
import static java.util.stream.Collectors.toSet;
import static org.jreleaser.model.internal.util.Artifacts.resolveForFileSet;
import static org.jreleaser.util.CollectionUtils.setOf;

/**
 * @author Andres Almiray
 * @since 0.8.0
 */
public final class FileSet extends AbstractArtifact implements Domain, ExtraProperties {
    private static final long serialVersionUID = 4945222704477480182L;

    private final Set includes = new LinkedHashSet<>();
    private final Set excludes = new LinkedHashSet<>();

    private String input;
    private String output;
    private Boolean failOnMissingInput;

    @JsonIgnore
    private final org.jreleaser.model.api.common.FileSet immutable = new org.jreleaser.model.api.common.FileSet() {
        private static final long serialVersionUID = 6362560616892633246L;

        @Override
        public Active getActive() {
            return FileSet.this.getActive();
        }

        @Override
        public boolean isEnabled() {
            return FileSet.this.isEnabled();
        }

        @Override
        public boolean isSelected() {
            return FileSet.this.isSelected();
        }

        @Override
        public Set getIncludes() {
            return unmodifiableSet(includes);
        }

        @Override
        public Set getExcludes() {
            return unmodifiableSet(excludes);
        }

        @Override
        public String getInput() {
            return input;
        }

        @Override
        public String getOutput() {
            return output;
        }

        @Override
        public String getPlatform() {
            return FileSet.this.getPlatform();
        }

        @Override
        public boolean isFailOnMissingInput() {
            return FileSet.this.isFailOnMissingInput();
        }

        @Override
        public Map asMap(boolean full) {
            return unmodifiableMap(FileSet.this.asMap(full));
        }

        @Override
        public String getPrefix() {
            return FileSet.this.prefix();
        }

        @Override
        public Map getExtraProperties() {
            return unmodifiableMap(FileSet.this.getExtraProperties());
        }
    };

    public org.jreleaser.model.api.common.FileSet asImmutable() {
        return immutable;
    }

    @Override
    public void merge(FileSet source) {
        super.merge(source);
        this.input = merge(this.input, source.input);
        this.output = merge(this.output, source.output);
        this.failOnMissingInput = merge(this.failOnMissingInput, source.failOnMissingInput);
        setIncludes(merge(this.includes, source.includes));
        setExcludes(merge(this.excludes, source.excludes));
    }

    public Set getIncludes() {
        return includes;
    }

    public void setIncludes(Set includes) {
        this.includes.clear();
        this.includes.addAll(includes);
    }

    public Set getExcludes() {
        return excludes;
    }

    public void setExcludes(Set excludes) {
        this.excludes.clear();
        this.excludes.addAll(excludes);
    }

    public String getInput() {
        return input;
    }

    public void setInput(String input) {
        this.input = input;
    }

    public String getOutput() {
        return output;
    }

    public void setOutput(String output) {
        this.output = output;
    }

    public boolean isFailOnMissingInput() {
        return null == failOnMissingInput || failOnMissingInput;
    }

    public void setFailOnMissingInput(Boolean failOnMissingInput) {
        this.failOnMissingInput = failOnMissingInput;
    }

    public boolean isFailOnMissingInputSet() {
        return null != failOnMissingInput;
    }

    @Override
    public Map asMap(boolean full) {
        Map props = new LinkedHashMap<>();
        props.put("enabled", isEnabled());
        props.put("active", getActive());
        props.put("input", input);
        props.put("output", output);
        props.put("platform", getPlatform());
        props.put("includes", includes);
        props.put("excludes", excludes);
        props.put("failOnMissingInput", failOnMissingInput);
        props.put("extraProperties", getExtraProperties());
        return props;
    }

    public String getResolvedInput(JReleaserContext context) {
        return resolveForFileSet(input, context, this);
    }

    public String getResolvedOutput(JReleaserContext context) {
        return resolveForFileSet(output, context, this);
    }

    public Set getResolvedIncludes(JReleaserContext context) {
        return includes.stream()
            .map(s -> resolveForFileSet(s, context, this))
            .collect(toSet());
    }

    public Set getResolvedExcludes(JReleaserContext context) {
        return excludes.stream()
            .map(s -> resolveForFileSet(s, context, this))
            .collect(toSet());
    }

    public Set getResolvedPaths(JReleaserContext context) throws IOException {
        if (!isActiveAndSelected()) return emptySet();

        Path basedir = context.getBasedir().resolve(getResolvedInput(context)).normalize().toAbsolutePath();

        Set resolvedIncludes = getResolvedIncludes(context);
        if (resolvedIncludes.isEmpty()) {
            resolvedIncludes = setOf("**/*");
        }
        resolvedIncludes = resolvedIncludes.stream()
            .map(s -> GLOB_PREFIX + s)
            .collect(toSet());

        Set resolvedExcludes = getResolvedExcludes(context);
        resolvedExcludes = resolvedExcludes.stream()
            .map(s -> GLOB_PREFIX + s)
            .collect(toSet());

        if (!java.nio.file.Files.exists(basedir)) {
            if (isFailOnMissingInput()) {
                throw new IOException(RB.$("ERROR_artifacts_glob_missing_input",
                    context.getBasedir().relativize(basedir)));
            } else {
                context.getLogger().debug(RB.$("ERROR_artifacts_glob_missing_input",
                    context.getBasedir().relativize(basedir)));
            }
            return emptySet();
        }

        GlobResolver resolver = new GlobResolver(context.getLogger(), basedir, resolvedIncludes, resolvedExcludes);

        java.nio.file.Files.walkFileTree(basedir, resolver);
        if (resolver.failed) {
            throw new IOException(RB.$("ERROR_artifacts_glob_resolution"));
        }

        return resolver.paths;
    }

    private static final class GlobResolver extends SimpleFileVisitor {
        private final JReleaserLogger logger;
        private final Set includes = new LinkedHashSet<>();
        private final Set excludes = new LinkedHashSet<>();
        private final Path basedir;
        private final Set paths = new LinkedHashSet<>();
        private boolean failed;

        private GlobResolver(JReleaserLogger logger, Path basedir, Set includes, Set excludes) {
            this.logger = logger;
            this.basedir = basedir;

            FileSystem fileSystem = FileSystems.getDefault();
            for (String s : includes) {
                this.includes.add(new ExtPathMatcher(fileSystem.getPathMatcher(s), s.contains("**")));
            }
            for (String s : excludes) {
                this.excludes.add(new ExtPathMatcher(fileSystem.getPathMatcher(s), s.contains("**")));
            }
        }

        private void match(Path path) {
            if (includes.stream().anyMatch(matcher -> matches(path, matcher)) &&
                excludes.stream().noneMatch(matcher -> matches(path, matcher))) {
                paths.add(basedir.relativize(path));
            }
        }

        private boolean matches(Path path, ExtPathMatcher matcher) {
            if (matcher.recursive) {
                return matcher.matcher.matches(path);
            } else {
                return basedir.normalize().equals(path.normalize().getParent()) &&
                    matcher.matcher.matches(path.getFileName());
            }
        }

        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
            if (basedir.normalize().equals(dir.normalize())) return CONTINUE;

            if (includes.stream().anyMatch(matcher -> matcher.recursive)) {
                return CONTINUE;
            } else {
                return SKIP_SUBTREE;
            }
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            match(file);
            return CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path file, IOException e) throws IOException {
            failed = true;
            logger.error(RB.$("ERROR_artifacts_unexpected_error_path"),
                basedir.toAbsolutePath().relativize(file.toAbsolutePath()), e);
            return CONTINUE;
        }
    }

    private static final class ExtPathMatcher {
        private final PathMatcher matcher;
        private final boolean recursive;

        private ExtPathMatcher(PathMatcher matcher, boolean recursive) {
            this.matcher = matcher;
            this.recursive = recursive;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy