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

io.helidon.build.maven.enforcer.inclusivenaming.InclusiveNamingRule Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2023 Oracle and/or its affiliates.
 *
 * 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
 *
 *     http://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 io.helidon.build.maven.enforcer.inclusivenaming;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import io.helidon.build.common.logging.Log;
import io.helidon.build.maven.enforcer.EnforcerException;
import io.helidon.build.maven.enforcer.FileMatcher;
import io.helidon.build.maven.enforcer.FileRequest;
import io.helidon.build.maven.enforcer.FoundFiles;
import io.helidon.build.maven.enforcer.RuleFailure;

/**
 * Rule for Inclusive Naming checking.
 */
public class InclusiveNamingRule {
    private static final String INCLUSIVE_NAMING_XML = "inclusive-names.xml";
    private static final Set ALLOWED_TIERS = Set.of("0");
    private static final Set DEFAULT_INCLUDES = Set.of(".java", ".xml", ".adoc",
                                                               ".txt", ".md", ".html",
                                                               ".css", ".properties", ".yaml",
                                                               ".sh", ".sql", ".conf",
                                                               ".json", ".kt", ".groovy",
                                                               ".mustache", ".yml", ".graphql",
                                                               ".proto", "Dockerfile.native", "Dockerfile.jlink",
                                                               ".gradle", ".MF");
    private final List excludes;
    private final List includes;
    private final List terms;

    private InclusiveNamingRule(Builder builder) {
        this.excludes = builder.excludes();
        this.includes = builder.includes();
        this.terms = builder.terms();
    }

    /**
     * Fluent API builder for typos rule.
     *
     * @return a new builder
     */
    public static Builder builder() {
        return new Builder();
    }

    /**
     * Check the files for typos.
     *
     * @param filesToCheck files to check
     * @return list of identified failures
     */
    public List check(FoundFiles filesToCheck) {
        List failures = new LinkedList<>();

        AtomicInteger count = new AtomicInteger();

        filesToCheck.fileRequests()
                .stream()
                .filter(this::include)
                .forEach(fr -> {
                    count.incrementAndGet();
                    processFile(fr, failures);
                });

        if (failures.size() == 0) {
            Log.info("Inclusive naming processed " + count.get() + " files.");
        } else {
            Log.info("Inclusive naming processed " + count.get() + " files, found " + failures.size() + " errors");
        }
        return failures;
    }

    private void processFile(FileRequest fr, List errors) {
        try (BufferedReader bufferedReader = Files.newBufferedReader(fr.path(), StandardCharsets.UTF_8)) {
            String line;
            int lineNum = 0;
            List found = new LinkedList<>();
            while ((line = bufferedReader.readLine()) != null) {
                // we want to start from 1
                lineNum++;
                for (XmlData term : terms) {
                    if (line.toLowerCase().contains(term.getTerm())) {
                        found.add(term);
                    }
                }
                if (!found.isEmpty()) {
                    errors.add(RuleFailure.create(fr, lineNum, "inclusive naming found: " + String.join(", ", found.toString())));
                }
                found.clear();
            }

        } catch (IOException e) {
            throw new EnforcerException("Failed to process file for inclusive naming: " + fr.relativePath());
        }
    }

    private boolean include(FileRequest fr) {
        for (FileMatcher exclude : excludes) {
            if (exclude.matches(fr)) {
                return false;
            }
        }

        for (FileMatcher include : includes) {
            if (include.matches(fr)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Fluent API builder for {@link io.helidon.build.maven.enforcer.inclusivenaming.InclusiveNamingRule}.
     */
    public static class Builder {
        private InlusiveNamingConfig inclusiveNamingConfig;

        private Builder() {
        }

        /**
         * Update builder from maven configuration.
         *
         * @param inclusiveNamingConfig maven config
         * @return updated builder
         */
        public Builder config(InlusiveNamingConfig inclusiveNamingConfig) {
            this.inclusiveNamingConfig = inclusiveNamingConfig;
            return this;
        }

        /**
         * Build a new instance of {@link io.helidon.build.maven.enforcer.inclusivenaming.InclusiveNamingRule}.
         *
         * @return new rule
         */
        public InclusiveNamingRule build() {
            return new InclusiveNamingRule(this);
        }

        private List readTerms(Supplier supplier){
            List terms = new ArrayList<>();
            try (InputStream is = supplier.get()) {
                XmlInclusiveNaming xml = xmlInclusiveNaming(is);
                for (XmlData data : xml.getData()) {
                    String tier = data.getTier();
                    String term = data.getTerm();
                    if (!ALLOWED_TIERS.contains(tier)) {
                        boolean exclude = false;
                        for (Pattern pattern : inclusiveNamingConfig.excludeTermsRegExps()) {
                            if (pattern.matcher(term).matches()) {
                                exclude = true;
                                break;
                            }
                        }
                        if (!exclude) {
                            terms.add(data);
                        }
                    }
                }
            } catch (IOException | JAXBException e) {
                throw new EnforcerException("Failed to read inclusive naming file", e);
            }
            terms.addAll(inclusiveNamingConfig.additionalTerms());
            return terms;
        }

        private XmlInclusiveNaming xmlInclusiveNaming(InputStream is) throws JAXBException {
            JAXBContext contextObj = JAXBContext.newInstance(XmlInclusiveNaming.class);
            Unmarshaller unmarshaller = contextObj.createUnmarshaller();
            return (XmlInclusiveNaming) unmarshaller.unmarshal(is);
        }

        List terms() {
            if (inclusiveNamingConfig.inclusiveNamingFile().isPresent()) {
                return readTerms(() -> {
                    try {
                        return new FileInputStream(inclusiveNamingConfig.inclusiveNamingFile().get());
                    } catch (FileNotFoundException e) {
                        throw new EnforcerException(inclusiveNamingConfig.inclusiveNamingFile().get() + " was not found");
                    }
                });
            } else {
                return readTerms(() -> InclusiveNamingRule.class.getResourceAsStream(INCLUSIVE_NAMING_XML));
            }
        }

        List excludes() {
            return inclusiveNamingConfig.excludes()
                    .stream()
                    .map(FileMatcher::create)
                    .flatMap(List::stream)
                    .collect(Collectors.toList());
        }

        List includes() {
            Set includes = new HashSet<>(DEFAULT_INCLUDES);
            includes.addAll(inclusiveNamingConfig.includes());
            return includes.stream()
                    .map(FileMatcher::create)
                    .flatMap(List::stream)
                    .collect(Collectors.toList());
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy