<commit_before>/*
 * Copyright 2018, TeamDev. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.tools.compiler.validation;

import com.google.protobuf.DescriptorProtos.DescriptorProto;
import io.spine.code.properties.PropertiesWriter;
import io.spine.code.proto.FileSet;
import io.spine.code.proto.MessageType;
import io.spine.logging.Logging;
import io.spine.option.Options;
import io.spine.type.TypeName;
import io.spine.validate.rule.ValidationRules;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Maps.newHashMap;
import static io.spine.option.OptionsProto.validationOf;
import static io.spine.util.Exceptions.newIllegalArgumentException;

/**
 * Finds Protobuf definitions of validation rules and creates a {@code .properties} file,
 * which contains entries like:
 *
 * <p>{@code foo.bar.ValidationRule=alpha.beta.TargetMessage.name_of_field_for_rule}.
 *
 * <p>If a validation rule has more than one target, the entry will look like:
 *
 * <p>{@code foo.bar.ValidationRule=foo.bar.MessageOne.field_name,foo.bar.MessageTwo.field_name}.
 */
public final class ValidationRulesWriter implements Logging {

    private final FileSet files;
    private final File targetDir;

    private ValidationRulesWriter(File descriptorSetFile, File targetDir) {
        this.files = FileSet.parseSkipStandard(descriptorSetFile.getPath());
        this.targetDir = targetDir;
    }

    public static void processDescriptorSetFile(File descriptorSetFile, File targetDir) {
        ValidationRulesWriter writer =
                new ValidationRulesWriter(descriptorSetFile, targetDir);
        writer.findRulesAndWriteProperties();
    }

    private void findRulesAndWriteProperties() {
        _debug("Validation rules lookup started.");
        List<MessageType> declarations = files.findMessageTypes(new IsValidationRule());
        writeProperties(declarations);
        _debug("Validation rules lookup complete.");
    }

    private void writeProperties(List<MessageType> ruleDeclarations) {
        Map<String, String> propsMap = newHashMap();
        for (MessageType declaration : ruleDeclarations) {
            TypeName typeName = declaration.name();
            String ruleTargets =
                    Options.option(declaration.descriptor(), validationOf)
                           .orElseThrow(() -> newIllegalArgumentException(declaration.name()
                                                                                     .value())
                           );
            propsMap.put(typeName.value(), ruleTargets);
        }

        String fileName = ValidationRules.fileName();
        log().debug("Writing the validation rules description to {}/{}.",
                    targetDir, fileName);
        PropertiesWriter writer = new PropertiesWriter(targetDir.getAbsolutePath(), fileName);
        writer.write(propsMap);
    }

    private static class IsValidationRule implements Predicate<DescriptorProto> {

        @Override
        public boolean test(@Nullable DescriptorProto input) {
            checkNotNull(input);<commit_msg>Log number of validation rule declarations<commit_after>/*
 * Copyright 2018, TeamDev. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.tools.compiler.validation;

import com.google.protobuf.DescriptorProtos.DescriptorProto;
import io.spine.code.properties.PropertiesWriter;
import io.spine.code.proto.FileSet;
import io.spine.code.proto.MessageType;
import io.spine.logging.Logging;
import io.spine.option.Options;
import io.spine.type.TypeName;
import io.spine.validate.rule.ValidationRules;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Maps.newHashMap;
import static io.spine.option.OptionsProto.validationOf;
import static io.spine.util.Exceptions.newIllegalArgumentException;

/**
 * Finds Protobuf definitions of validation rules and creates a {@code .properties} file,
 * which contains entries like:
 *
 * <p>{@code foo.bar.ValidationRule=alpha.beta.TargetMessage.name_of_field_for_rule}.
 *
 * <p>If a validation rule has more than one target, the entry will look like:
 *
 * <p>{@code foo.bar.ValidationRule=foo.bar.MessageOne.field_name,foo.bar.MessageTwo.field_name}.
 */
public final class ValidationRulesWriter implements Logging {

    private final FileSet files;
    private final File targetDir;

    private ValidationRulesWriter(File descriptorSetFile, File targetDir) {
        this.files = FileSet.parseSkipStandard(descriptorSetFile.getPath());
        this.targetDir = targetDir;
    }

    public static void processDescriptorSetFile(File descriptorSetFile, File targetDir) {
        ValidationRulesWriter writer =
                new ValidationRulesWriter(descriptorSetFile, targetDir);
        writer.findRulesAndWriteProperties();
    }

    private void findRulesAndWriteProperties() {
        _debug("Validation rules lookup started.");
        List<MessageType> declarations = files.findMessageTypes(new IsValidationRule());
        _debug("Found declarations: {}", declarations.size());
        writeProperties(declarations);
        _debug("Validation rules written to directory: {}", targetDir);
    }

    private void writeProperties(List<MessageType> ruleDeclarations) {
        Map<String, String> propsMap = newHashMap();
        for (MessageType declaration : ruleDeclarations) {
            TypeName typeName = declaration.name();
            String ruleTargets =
                    Options.option(declaration.descriptor(), validationOf)
                           .orElseThrow(() -> newIllegalArgumentException(declaration.name()
                                                                                     .value())
                           );
            propsMap.put(typeName.value(), ruleTargets);
        }

        String fileName = ValidationRules.fileName();
        log().debug("Writing the validation rules description to {}/{}.",
                    targetDir, fileName);
        PropertiesWriter writer = new PropertiesWriter(targetDir.getAbsolutePath(), fileName);
        writer.write(propsMap);
    }

    private static class IsValidationRule implements Predicate<DescriptorProto> {

        @Override
        public boolean test(@Nullable DescriptorProto input) {
            checkNotNull(input);
{'fraction_non_alphanumeric': 0.08388752770971881, 'fraction_numerical': 0.00093338000233345, 'mean_word_length': 3.4391506991196272, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7492820', 'n_tokens_mistral': 2229, 'n_tokens_neox': 2097, 'n_words': 659}