package io.spine.gradle.compiler.lookup.validate;

import com.google.common.annotations.VisibleForTesting;
import com.google.protobuf.DescriptorProtos.DescriptorProto;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import org.slf4j.Logger;

import java.util.Map;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Maps.newLinkedHashMap;
import static io.spine.gradle.compiler.util.UnknownOptions.getUnknownOptionValue;
import static io.spine.gradle.compiler.util.UnknownOptions.hasUnknownOption;
import static io.spine.option.OptionsProto.VALIDATION_OF_FIELD_NUMBER;
import static org.slf4j.LoggerFactory.getLogger;

/**
 * Finds Protobuf definitions of validation rules.
 *
 * @author Dmytro Grankin
 */
class ValidationRulesFinder {

    @VisibleForTesting
    static final String PROTO_TYPE_SEPARATOR = ".";

    private final FileDescriptorProto fileDescriptor;
    private final String packagePrefix;

    ValidationRulesFinder(FileDescriptorProto fileDescriptor) {
        this.fileDescriptor = checkNotNull(fileDescriptor);
        this.packagePrefix = fileDescriptor.getPackage() + PROTO_TYPE_SEPARATOR;
    }

    Map<String, String> findRules() {
        log().debug("Looking up for validation rules in {}.", fileDescriptor.getName());
        final Map<String, String> rules = newLinkedHashMap();
        for (DescriptorProto messageDescriptor : fileDescriptor.getMessageTypeList()) {
            scanMessage(rules, messageDescriptor);
        }
        log().debug("Found validation rules: {}.", rules.toString());
        return rules;
    }

    private void scanMessage(Map<String, String> rules, DescriptorProto messageDescriptor) {
        if (isValidationRule(messageDescriptor)) {
            final String ruleType = packagePrefix + messageDescriptor.getName();
            final String ruleTarget = getUnknownOptionValue(messageDescriptor,
                                                            VALIDATION_OF_FIELD_NUMBER);
            rules.put(ruleType, ruleTarget);
        }

        for (DescriptorProto nestedMessageDescriptor : messageDescriptor.getNestedTypeList()) {
            scanMessage(rules, nestedMessageDescriptor);
        }
    }

    private static boolean isValidationRule(DescriptorProto messageDescriptor) {
        return hasUnknownOption(messageDescriptor, VALIDATION_OF_FIELD_NUMBER);
    }

    private static Logger log() {
        return LogSingleton.INSTANCE.value;
    }

    private enum LogSingleton {
        INSTANCE;
        @SuppressWarnings("NonSerializableFieldInSerializableClass")
        private final Logger value = getLogger(ValidationRulesFinder.class);
    }
}

@@ -1181,24 +1181,199 @@
 TOR;%0A    %7D%0A%0A
+    /**%0A     * Finds Protobuf definitions of validation rules in the file descriptor.%0A     *%0A     * @return a map from a validation rule type name to the target field%0A     */%0A
     Map%3CStri
@@ -1394,24 +1394,24 @@
 ndRules() %7B%0A
-
         log(
@@ -1652,32 +1652,43 @@
      scanMessage
+Recursively
 (rules, messageD
@@ -1836,16 +1836,27 @@
 nMessage
+Recursively
 (Map%3CStr
@@ -1874,16 +1874,56 @@
 %3E rules,
+%0A                                       
  Descrip
@@ -2396,32 +2396,32 @@
 edTypeList()) %7B%0A
-
             scan
@@ -2427,16 +2427,27 @@
 nMessage
+Recursively
 (rules, 

Add Javadoc for `ValidationRulesFinder`