<commit_before>package eu.modernmt.lang;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class LanguageIndex {

    public static class Builder {

        private final Set<LanguagePair> languages = new HashSet<>();
        private final Map<LanguageKey, List<LanguageEntry>> index = new HashMap<>();
        private final Map<String, List<LanguageRule>> rules = new HashMap<>();

        public Builder add(LanguagePair pair) {
            LanguageKey key = LanguageKey.fromLanguage(pair);
            LanguageEntry entry = LanguageEntry.fromLanguage(pair);

            languages.add(pair);
            index.computeIfAbsent(key, k -> new ArrayList<>()).add(entry);

            return this;
        }

        public Builder addWildcardRule(Language lang, Language to) {
            return addRule(lang, null, to);
        }

        public Builder addRule(Language lang, Language from, Language to) {
            if (lang.getRegion() != null)
                throw new IllegalArgumentException("Language region not supported for rule: " + lang);

            rules.computeIfAbsent(lang.getLanguage(), k -> new ArrayList<>()).add(LanguageRule.make(from, to));

            return this;
        }

        public LanguageIndex build() {
            return new LanguageIndex(languages, index, rules);
        }

    }

    private final Set<LanguagePair> languages;
    private final Map<LanguageKey, List<LanguageEntry>> index;
    private final Map<String, List<LanguageRule>> rules;
    private final Set<Language> rulesSkipList;
    private final ConcurrentHashMap<LanguagePair, CacheEntry> mappingCache = new ConcurrentHashMap<>();

    private LanguageIndex(Set<LanguagePair> languages, Map<LanguageKey, List<LanguageEntry>> index, Map<String, List<LanguageRule>> rules) {
        this.languages = Collections.unmodifiableSet(languages);
        this.index = index;
        this.rules = rules;

        this.rulesSkipList = new HashSet<>();
        for (LanguagePair pair : languages) {
            if (pair.source.getRegion() != null)
                this.rulesSkipList.add(pair.source);
            if (pair.target.getRegion() != null)
                this.rulesSkipList.add(pair.target);
        }
    }

    public Set<LanguagePair> getLanguages() {
        return languages;
    }

    public int size() {
        return languages.size();
    }

    public LanguagePair asSingleLanguagePair() {
        return languages.size() == 1 ? languages.iterator().next() : null;
    }

    public LanguagePair mapIgnoringDirection(LanguagePair pair, boolean mask) {
        CacheEntry cached = mappingCache.get(pair);
        if (cached != null)
            return mask ? cached.masked : cached.normalized;
        cached = mappingCache.get(pair.reversed());
        if (cached != null)
            return (mask ? cached.masked : cached.normalized).reversed();

        LanguagePair mapped = map(pair, mask);

        if (mapped == null) {
            mapped = map(pair.reversed(), mask);
            if (mapped != null)
                mapped = mapped.reversed();
        }
<commit_msg>Added empy constructor to LanguageIndex to enable sub-classing<commit_after>package eu.modernmt.lang;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class LanguageIndex {

    public static class Builder {

        private final Set<LanguagePair> languages = new HashSet<>();
        private final Map<LanguageKey, List<LanguageEntry>> index = new HashMap<>();
        private final Map<String, List<LanguageRule>> rules = new HashMap<>();

        public Builder add(LanguagePair pair) {
            LanguageKey key = LanguageKey.fromLanguage(pair);
            LanguageEntry entry = LanguageEntry.fromLanguage(pair);

            languages.add(pair);
            index.computeIfAbsent(key, k -> new ArrayList<>()).add(entry);

            return this;
        }

        public Builder addWildcardRule(Language lang, Language to) {
            return addRule(lang, null, to);
        }

        public Builder addRule(Language lang, Language from, Language to) {
            if (lang.getRegion() != null)
                throw new IllegalArgumentException("Language region not supported for rule: " + lang);

            rules.computeIfAbsent(lang.getLanguage(), k -> new ArrayList<>()).add(LanguageRule.make(from, to));

            return this;
        }

        public LanguageIndex build() {
            return new LanguageIndex(languages, index, rules);
        }

    }

    private final Set<LanguagePair> languages;
    private final Map<LanguageKey, List<LanguageEntry>> index;
    private final Map<String, List<LanguageRule>> rules;
    private final Set<Language> rulesSkipList;
    private final ConcurrentHashMap<LanguagePair, CacheEntry> mappingCache;

    protected LanguageIndex() {
        this.languages = null;
        this.index = null;
        this.rules = null;
        this.rulesSkipList = null;
        this.mappingCache = null;
    }

    private LanguageIndex(Set<LanguagePair> languages, Map<LanguageKey, List<LanguageEntry>> index, Map<String, List<LanguageRule>> rules) {
        this.languages = Collections.unmodifiableSet(languages);
        this.index = index;
        this.rules = rules;

        this.rulesSkipList = new HashSet<>();
        for (LanguagePair pair : languages) {
            if (pair.source.getRegion() != null)
                this.rulesSkipList.add(pair.source);
            if (pair.target.getRegion() != null)
                this.rulesSkipList.add(pair.target);
        }

        this.mappingCache = new ConcurrentHashMap<>();
    }

    public Set<LanguagePair> getLanguages() {
        return languages;
    }

    public int size() {
        return languages.size();
    }

    public LanguagePair asSingleLanguagePair() {
        return languages.size() == 1 ? languages.iterator().next() : null;
    }

    public LanguagePair mapIgnoringDirection(LanguagePair pair, boolean mask) {
        CacheEntry cached = mappingCache.get(pair);
        if (cached != null)
            return mask ? cached.masked : cached.normalized;
        cached = mappingCache.get(pair.reversed());
        if (cached != null)
            return (mask ? cached.masked : cached.normalized).reversed();

        LanguagePair mapped = map(pair, mask);

        if (mapped == null) {
            mapped = map(pair.reversed(), mask);
            if (mapped != null)
                mapped = mapped.reversed();
        }

{'fraction_non_alphanumeric': 0.10759299108515216, 'fraction_numerical': 0.0003074085459575776, 'mean_word_length': 2.542188350571584, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '3434506', 'n_tokens_mistral': 1690, 'n_tokens_neox': 1637, 'n_words': 436}