<commit_before>/*
 * Copyright 2017 Lime - HighTech Solutions s.r.o.
 *
 * 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.getlime.security.powerauth.lib.webflow.authentication.service;

import io.getlime.security.powerauth.app.webflow.i18n.I18NService;
import io.getlime.security.powerauth.lib.nextstep.model.entity.OperationFormData;
import io.getlime.security.powerauth.lib.nextstep.model.entity.attribute.*;
import io.getlime.security.powerauth.lib.nextstep.model.entity.enumeration.ValueFormatType;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.stereotype.Service;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Service which localizes and translates form data messages.
 *
 * @author <NAME>, <EMAIL>
 */
@Service
public class MessageTranslationService {

    private final I18NService i18NService;
    private final ValueFormatterService valueFormatterService;

    private static final String CHOSEN_BANK_ACCOUNT_NUMBER_INPUT = "operation.bankAccountChoice";
    private static final String MISSING_KEY_MESSAGE = "MISSING_LOCALIZATION_FOR_FIELD";
    private static final String MISSING_VALUE_MESSAGE = "MISSING_VALUE_FOR_FIELD";

    /**
     * Service constructor
     * @param i18NService I18N service.
     * @param valueFormatterService Value formatter service.
     */
    public MessageTranslationService(I18NService i18NService, ValueFormatterService valueFormatterService) {
        this.i18NService = i18NService;
        this.valueFormatterService = valueFormatterService;
    }

    /**
     * Translate form data strings.
     *
     * @param formData Form data.
     */
    public void translateFormData(OperationFormData formData) {
        if (formData==null) {
            return;
        }

        // Localize labels for form fields
        localizeFormFieldLabels(formData);

        // Create id -> value map for translation using the {id} notation
        Map<String, String> idValueMap = createIdValueMap(formData);

        // Localize and translate title using the {id} notation
        OperationFormMessageAttribute title = formData.getTitle();
        localizeAndTranslateFormAttributeValue(title, idValueMap);

        // Localize and translate greeting using the {id} notation
        OperationFormMessageAttribute greeting = formData.getGreeting();
        localizeAndTranslateFormAttributeValue(greeting, idValueMap);

        // Localize and translate summary using the {id} notation
        OperationFormMessageAttribute summary = formData.getSummary();
        localizeAndTranslateFormAttributeValue(summary, idValueMap);

        // Localize and translate banners using the {id} notation
        for (OperationFormBanner banner: formData.getBanners()) {
            localizeAndTranslateFormAttributeValue(banner, idValueMap);
        }

        // Format form field attributes
        formatFormFieldAttributes(formData.getParameters());
    }

    /**
     * Localize value of operation form attribute.
     * @param attribute Operation form attribute.
     * @param idValueMap Id -> value map for translation using the {id} notation.
     */
    private void localizeAndTranslateFormAttributeValue(OperationFormMessageAttribute attribute, Map<String, String> idValueMap) {
        String message = localize(attribute.getId());
        String translatedMessage = translateMessage(message, idValueMap);
        attribute.setMessage(translatedMessage);
    }

    /**
     * Format form field attributes.
     * @param attributes Form field attributes
     */
    private void formatFormFieldAttributes(List<OperationFormFieldAttribute> attributes) {
        if (attributes == null) {
            return;
        }
        for (OperationFormFieldAttribute attribute : attributes) {
            // Formatting of attributes with specified format
            if (attribute instanceof OperationFormFieldAttributeFormatted) {
                OperationFormFieldAttributeFormatted formattedAttribute = (OperationFormFieldAttributeFormatted) attribute;
                String formattedValue;
                if (formattedAttribute.getValueFormatType() == ValueFormatType.LOCALIZED_TEXT) {
                    formattedValue = localize(valueFormatterService.getValue(attribute));
                } else {
                    formattedValue = valueFormatterService.format(formattedAttribute, LocaleContextHolder.getLocale());
                }
                formattedAttribute.setFormattedValue(formattedValue);
                continue;
            }
            // Localization of banners
            if (attribute.getType() == OperationFormFieldAttribute.Type.BANNER) {
                OperationBannerFieldAttribute banner = (OperationBannerFieldAttribute) attribute;
                String localizedMessage = localize(banner.getId());
                banner.setMessage(localizedMessage);
            }
        }
    }

    /**
     * Localize form field attributes.
     * @param formData Operation form data.
     */
    private void localizeFormFieldLabels(OperationFormData formData) {
        for (OperationFormFieldAttribute attribute: formData.getParameters()) {
            // Banners do not have labels, skip localization
            if (attribute.getType() == OperationFormFieldAttribute.Type.BANNER) {
                continue;
            }
            String localizedValue = localize(attribute.getId());
            attribute.setLabel(localizedValue);
        }
    }

    /**
     * Create ID-Value map for operation form fields.
     * @param formData Form data
     * @return ID-Value map.<commit_msg>Fix #410: Do not localize the label for HEADING attribute<commit_after>/*
 * Copyright 2017 Lime - HighTech Solutions s.r.o.
 *
 * 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.getlime.security.powerauth.lib.webflow.authentication.service;

import io.getlime.security.powerauth.app.webflow.i18n.I18NService;
import io.getlime.security.powerauth.lib.nextstep.model.entity.OperationFormData;
import io.getlime.security.powerauth.lib.nextstep.model.entity.attribute.*;
import io.getlime.security.powerauth.lib.nextstep.model.entity.enumeration.ValueFormatType;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.stereotype.Service;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Service which localizes and translates form data messages.
 *
 * @author <NAME>, <EMAIL>
 */
@Service
public class MessageTranslationService {

    private final I18NService i18NService;
    private final ValueFormatterService valueFormatterService;

    private static final String CHOSEN_BANK_ACCOUNT_NUMBER_INPUT = "operation.bankAccountChoice";
    private static final String MISSING_KEY_MESSAGE = "MISSING_LOCALIZATION_FOR_FIELD";
    private static final String MISSING_VALUE_MESSAGE = "MISSING_VALUE_FOR_FIELD";

    /**
     * Service constructor
     * @param i18NService I18N service.
     * @param valueFormatterService Value formatter service.
     */
    public MessageTranslationService(I18NService i18NService, ValueFormatterService valueFormatterService) {
        this.i18NService = i18NService;
        this.valueFormatterService = valueFormatterService;
    }

    /**
     * Translate form data strings.
     *
     * @param formData Form data.
     */
    public void translateFormData(OperationFormData formData) {
        if (formData==null) {
            return;
        }

        // Localize labels for form fields
        localizeFormFieldLabels(formData);

        // Create id -> value map for translation using the {id} notation
        Map<String, String> idValueMap = createIdValueMap(formData);

        // Localize and translate title using the {id} notation
        OperationFormMessageAttribute title = formData.getTitle();
        localizeAndTranslateFormAttributeValue(title, idValueMap);

        // Localize and translate greeting using the {id} notation
        OperationFormMessageAttribute greeting = formData.getGreeting();
        localizeAndTranslateFormAttributeValue(greeting, idValueMap);

        // Localize and translate summary using the {id} notation
        OperationFormMessageAttribute summary = formData.getSummary();
        localizeAndTranslateFormAttributeValue(summary, idValueMap);

        // Localize and translate banners using the {id} notation
        for (OperationFormBanner banner: formData.getBanners()) {
            localizeAndTranslateFormAttributeValue(banner, idValueMap);
        }

        // Format form field attributes
        formatFormFieldAttributes(formData.getParameters());
    }

    /**
     * Localize value of operation form attribute.
     * @param attribute Operation form attribute.
     * @param idValueMap Id -> value map for translation using the {id} notation.
     */
    private void localizeAndTranslateFormAttributeValue(OperationFormMessageAttribute attribute, Map<String, String> idValueMap) {
        String message = localize(attribute.getId());
        String translatedMessage = translateMessage(message, idValueMap);
        attribute.setMessage(translatedMessage);
    }

    /**
     * Format form field attributes.
     * @param attributes Form field attributes
     */
    private void formatFormFieldAttributes(List<OperationFormFieldAttribute> attributes) {
        if (attributes == null) {
            return;
        }
        for (OperationFormFieldAttribute attribute : attributes) {
            // Formatting of attributes with specified format
            if (attribute instanceof OperationFormFieldAttributeFormatted) {
                OperationFormFieldAttributeFormatted formattedAttribute = (OperationFormFieldAttributeFormatted) attribute;
                String formattedValue;
                if (formattedAttribute.getValueFormatType() == ValueFormatType.LOCALIZED_TEXT) {
                    formattedValue = localize(valueFormatterService.getValue(attribute));
                } else {
                    formattedValue = valueFormatterService.format(formattedAttribute, LocaleContextHolder.getLocale());
                }
                formattedAttribute.setFormattedValue(formattedValue);
                continue;
            }
            // Localization of banners
            if (attribute.getType() == OperationFormFieldAttribute.Type.BANNER) {
                OperationBannerFieldAttribute banner = (OperationBannerFieldAttribute) attribute;
                String localizedMessage = localize(banner.getId());
                banner.setMessage(localizedMessage);
            }
        }
    }

    /**
     * Localize the form field labels.
     * @param formData Operation form data.
     */
    private void localizeFormFieldLabels(OperationFormData formData) {
        for (OperationFormFieldAttribute attribute: formData.getParameters()) {
            // Banners do not have labels, skip localization
            if (attribute.getType() == OperationFormFieldAttribute.Type.BANNER) {
                continue;
            }
            // Headings do not have labels, skip localization
            if (attribute.getType() == OperationFormFieldAttribute.Type.HEADING) {
                continue;
            }
            String localizedValue = localize(attribute.getId());
            attribute.setLabel(localizedValue);
        }
    }

    /**
     * Create ID-Value map for operation form fields.
     * @param formData Form data
     * @return ID-Value map.
{'fraction_non_alphanumeric': 0.07684057304834138, 'fraction_numerical': 0.004826476672029419, 'mean_word_length': 3.1037409619616474, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4338800', 'n_tokens_mistral': 3276, 'n_tokens_neox': 3173, 'n_words': 852}