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

com.force.i18n.grammar.impl.SlavicDeclension Maven / Gradle / Ivy

There is a newer version: 1.2.30
Show newest version
/*
 * Copyright (c) 2017, salesforce.com, inc.
 * All rights reserved.
 * Licensed under the BSD 3-Clause license.
 * For full license text, see LICENSE.txt file in the repo root  or https://opensource.org/licenses/BSD-3-Clause
 */

package com.force.i18n.grammar.impl;

import static com.force.i18n.grammar.LanguageCase.*;
import static com.force.i18n.grammar.LanguageGender.*;
import static com.force.i18n.grammar.LanguageNumber.SINGULAR;

import java.util.*;
import java.util.logging.Logger;

import com.force.i18n.HumanLanguage;
import com.force.i18n.grammar.*;
import com.force.i18n.grammar.Noun.NounType;
import com.force.i18n.grammar.impl.ComplexGrammaticalForm.*;
import com.google.common.collect.*;

/**
 * Superclass of slavic languages; i.e. there are more than 7 noun forms, and no articles
 * In this case, we have a "generic" and non-enum based NounForm and AdjectiveForm
 * @author stamm
 */
abstract class SlavicDeclension extends AbstractLanguageDeclension {
    private static final Logger logger = Logger.getLogger(SlavicDeclension.class.getName());

    private final List nounForms;
    private final Multimap nounFormsByCase;
    private final NounFormMap nounFormMap;
    private final List adjectiveForms;
    private final ModifierFormMap adjectiveFormMap;

    protected SlavicDeclension(HumanLanguage language) {
    	super(language);
        // Generate the different forms from subclass methods
        ImmutableList.Builder nounBuilder = ImmutableList.builder();
        ImmutableMultimap.Builder byCaseBuilder = ImmutableMultimap.builder();
        int ordinal = 0;
        for (LanguageNumber number : getAllowedNumbers()) {
            for (LanguageCase caseType : getRequiredCases()) {
                SlavicNounForm form = new SlavicNounForm(this, number, caseType, ordinal++);
                byCaseBuilder.put(caseType, form);
                nounBuilder.add(form);
            }
        }
        this.nounForms = nounBuilder.build();
        this.nounFormsByCase = byCaseBuilder.build();
        this.nounFormMap = new NounFormMap(this.nounForms);

        ImmutableList.Builder adjBuilder = ImmutableList.builder();
        int adjOrdinal = 0;
        for (LanguageNumber number : getAllowedNumbers()) {
            for (LanguageGender gender : (hasGender() ? getRequiredGenders() : ImmutableSet.of(getDefaultGender()))) {
                if (gender == LanguageGender.ANIMATE_MASCULINE) continue; //we'll handle it separately, since it only needs a few cases
                for (LanguageCase caseType : getRequiredCases()) {
                    adjBuilder.add(new SlavicAdjectiveForm(this, number, gender, caseType, adjOrdinal++));
                }
            }
        }

        if (getMasculineAnimateForms() != null) {
            //add animate masculine forms, if any
            for (Map.Entry> entry : getMasculineAnimateForms().entrySet()) {
                for (LanguageCase caseType : entry.getValue()) {
                    adjBuilder.add(new SlavicAdjectiveForm(this, entry.getKey(), LanguageGender.ANIMATE_MASCULINE, caseType, adjOrdinal++));
                }
            }
        }

        this.adjectiveForms = adjBuilder.build();
        this.adjectiveFormMap = new ModifierFormMap(this.adjectiveForms);
    }

    static class SlavicNounForm extends ComplexNounForm {
        private static final long serialVersionUID = 1L;

        private final LanguageCase caseType;
        private final LanguageNumber number;

        public SlavicNounForm(LanguageDeclension declension, LanguageNumber number, LanguageCase caseType, int ordinal) {
            super(declension, ordinal);
            this.number = number;
            this.caseType = caseType;
        }

        @Override public LanguageArticle getArticle() { return LanguageArticle.ZERO; }
        @Override public LanguageCase getCase() {  return this.caseType; }
        @Override public LanguageNumber getNumber() {  return this.number; }
        @Override public LanguagePossessive getPossessive() { return LanguagePossessive.NONE;}

        @Override
        public String getKey() {
            return getNumber().getDbValue() + "-" + getCase().getDbValue();
        }

        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), this.caseType, this.number);
        }

        @Override
        public boolean equals(Object other) {
            if (this == other) return true;
            if (other instanceof SlavicNounForm) {
                SlavicNounForm o = this.getClass().cast(other);
                return super.equals(other) && this.caseType == o.caseType && this.number == o.number;
            }
            return false;
        }

        @Override
        public String toString() {
            return "SlavicNF:" + getKey();
        }
    }

    static class SlavicAdjectiveForm extends ComplexAdjectiveForm {
        private static final long serialVersionUID = 1L;

        private final LanguageNumber number;
        private final LanguageCase caseType;
        private final LanguageGender gender;

        public SlavicAdjectiveForm(LanguageDeclension declension, LanguageNumber number, LanguageGender gender,LanguageCase caseType, int ordinal) {
            super(declension, ordinal);
            this.number = number;
            this.gender = gender;
            this.caseType = caseType;
        }

        @Override public LanguageArticle getArticle() { return LanguageArticle.ZERO; }
        @Override public LanguageCase getCase() {  return this.caseType; }
        @Override public LanguageNumber getNumber() {  return this.number; }
        @Override public LanguageStartsWith getStartsWith() {  return LanguageStartsWith.CONSONANT; }
        @Override public LanguageGender getGender() {  return this.gender; }
        @Override public LanguagePossessive getPossessive() { return LanguagePossessive.NONE; }

        @Override
        public String getKey() {
            return getGender().getDbValue() + "-" + getNumber().getDbValue() + "-" + getCase().getDbValue();
        }

        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), this.caseType, this.number, this.gender);
        }

        @Override
        public boolean equals(Object other) {
            if (this == other) return true;
            if (other instanceof SlavicAdjectiveForm) {
                SlavicAdjectiveForm o = this.getClass().cast(other);
                return super.equals(other) && this.caseType == o.caseType && this.number == o.number
                        && this.gender == o.gender;
            }
            return false;
        }
    }

    /**
     * Represents an Slavic noun.
     * See SlavicNounForm for more info
     */
    public static class SlavicNoun extends ComplexNoun {
        private static final long serialVersionUID = 1L;

        SlavicNoun(LanguageDeclension declension, String name, String pluralAlias, NounType type, String entityName, LanguageGender gender, String access, boolean isStandardField, boolean isCopiedFromDefault) {
            super(declension, name, pluralAlias, type, entityName, LanguageStartsWith.CONSONANT, gender, access, isStandardField, isCopiedFromDefault);
        }

        @Override
        protected Class getFormClass() {
            return SlavicNounForm.class;
        }

        @Override
        protected boolean validateValues(String name, LanguageCase _case) {
            return defaultValidate(name, getDeclension().getFieldForms());
        }

        @Override
        protected boolean validateGender(String name) {
            if (getDeclension().hasGender() && !getDeclension().getRequiredGenders().contains(getGender())) {
                logger.info(VALIDATION_WARNING_HEADER + name + " invalid gender");
                setGender(getDeclension().getDefaultGender());
            }
            return true;
        }
    }

    /**
     * Represents an english adjective
     */
    public static class SlavicAdjective extends ComplexAdjective {
        private static final long serialVersionUID = 1L;

        SlavicAdjective(LanguageDeclension declension, String name, LanguagePosition position) {
            super(declension, name, position);
        }

        @Override
        protected Class getFormClass() {
            return SlavicAdjectiveForm.class;
        }
   }

    @Override
    public Adjective createAdjective(String name, LanguageStartsWith startsWith, LanguagePosition position) {
        return new SlavicAdjective(this, name, position);
    }

    @Override
    public Noun createNoun(String name, String pluralAlias, NounType type, String entityName, LanguageStartsWith startsWith,
            LanguageGender gender, String access, boolean isStandardField, boolean isCopied) {
        return new SlavicNoun(this, name, pluralAlias, type, entityName, gender, access, isStandardField, isCopied);
    }

    @Override
    public List< ? extends AdjectiveForm> getAdjectiveForms() {
        return this.adjectiveForms;
    }

    @Override
    public List< ? extends NounForm> getAllNounForms() {
        return this.nounForms;
    }

    @Override
    public Collection< ? extends NounForm> getEntityForms() {
        return this.nounForms;
    }
    @Override
    public Collection< ? extends NounForm> getFieldForms() {
        return this.nounFormsByCase.get(NOMINATIVE);
    }

    @Override
    public Collection< ? extends NounForm> getOtherForms() {
        assert nounForms.get(0).getCase() == NOMINATIVE && nounForms.get(0).getNumber() == SINGULAR : "Invalid case map";
        return Collections.singleton(nounForms.get(0));
    }

    @Override
    public AdjectiveForm getAdjectiveForm(LanguageStartsWith startsWith, LanguageGender gender, LanguageNumber number,
            LanguageCase _case, LanguageArticle article, LanguagePossessive possessive) {
        if (article != LanguageArticle.ZERO) return null;
        return this.adjectiveFormMap.getForm(startsWith, gender, number, _case);
    }

    @Override
    public NounForm getExactNounForm(LanguageNumber number, LanguageCase _case, LanguagePossessive possessive,
            LanguageArticle article) {
        if (article != LanguageArticle.ZERO || possessive != LanguagePossessive.NONE) return null;
        return this.nounFormMap.getForm(number, _case);
    }

    @Override
    public boolean hasArticle() {
        return false;
    }

    @Override
    public boolean hasGender() {
        return true;
    }

    @Override
    public EnumSet getRequiredGenders() {
        return EnumSet.of(NEUTER, FEMININE, MASCULINE);
    }

    @Override
    public boolean hasStartsWith() {
        return false;
    }

    /**
     * A few slavic adjective forms use the gender masculine animate.
     * @return map of number to list of cases that use that gender
     */
    public Map> getMasculineAnimateForms() {
        return null;
    }

    static final EnumSet WEST_SLAVIC_CASES = EnumSet.of(NOMINATIVE, ACCUSATIVE, DATIVE, GENITIVE, INSTRUMENTAL, LOCATIVE, VOCATIVE);
    static final EnumSet WEST_SLAVIC_CASES_NO_VOC = EnumSet.of(NOMINATIVE, ACCUSATIVE, DATIVE, GENITIVE, INSTRUMENTAL, LOCATIVE);
    static final EnumSet WEST_SLAVIC_GENDERS = EnumSet.of(NEUTER, MASCULINE, FEMININE, ANIMATE_MASCULINE);

    static class CzechDeclension extends SlavicDeclension {
        public CzechDeclension(HumanLanguage language) {
            super(language);
        }

        @Override
        public EnumSet getRequiredCases() {
            return WEST_SLAVIC_CASES;
        }

        @Override
        public Map> getMasculineAnimateForms() {
           return ImmutableMap.of(
                   LanguageNumber.PLURAL, EnumSet.of(NOMINATIVE, VOCATIVE),
                   LanguageNumber.SINGULAR, EnumSet.of(ACCUSATIVE));
        }

        @Override
        public EnumSet getRequiredGenders() {
            return WEST_SLAVIC_GENDERS;
        }
    }

    static class PolishDeclension extends SlavicDeclension {
        public PolishDeclension(HumanLanguage language) {
            super(language);
        }

        @Override
        public EnumSet getRequiredCases() {
            return WEST_SLAVIC_CASES;
        }

        @Override
        public Map> getMasculineAnimateForms() {
           return ImmutableMap.of(
                   LanguageNumber.PLURAL, EnumSet.of(NOMINATIVE, ACCUSATIVE),
                   LanguageNumber.SINGULAR, EnumSet.of(ACCUSATIVE));
        }

        @Override
        public EnumSet getRequiredGenders() {
            return WEST_SLAVIC_GENDERS;
        }
    }

    static class RussianDeclension extends SlavicDeclension {
        public RussianDeclension(HumanLanguage language) {
            super(language);
        }

        @Override
        public EnumSet getRequiredCases() {
            return EnumSet.of(NOMINATIVE, ACCUSATIVE, DATIVE, GENITIVE, INSTRUMENTAL, PREPOSITIONAL);
        }

        @Override
        public boolean shouldLowercaseEntityInCompoundNouns() {
            return true;
        }

        @Override
        public Map> getMasculineAnimateForms() {
           return ImmutableMap.of(LanguageNumber.PLURAL, EnumSet.of(ACCUSATIVE),
                                  LanguageNumber.SINGULAR, EnumSet.of(ACCUSATIVE));
        }

        @Override
        public EnumSet getRequiredGenders() {
            return WEST_SLAVIC_GENDERS;
        }
    }

    static class UkrainianDeclension extends SlavicDeclension {
        public UkrainianDeclension(HumanLanguage language) {
            super(language);
        }

        @Override
        public EnumSet getRequiredCases() {
            return WEST_SLAVIC_CASES;
        }

        @Override
        public Map> getMasculineAnimateForms() {
           return ImmutableMap.of(
                   LanguageNumber.PLURAL, EnumSet.of(ACCUSATIVE),
                   LanguageNumber.SINGULAR, EnumSet.of(ACCUSATIVE));
        }

        @Override
        public EnumSet getRequiredGenders() {
            return WEST_SLAVIC_GENDERS;
        }
    }


    static class SlovakianDeclension extends SlavicDeclension {
        public SlovakianDeclension(HumanLanguage language) {
            super(language);
        }

        @Override
        public EnumSet getRequiredCases() {
            return WEST_SLAVIC_CASES_NO_VOC;
        }

        @Override
        public Map> getMasculineAnimateForms() {
           return ImmutableMap.of(
                   LanguageNumber.PLURAL, EnumSet.of(NOMINATIVE, ACCUSATIVE),
                   LanguageNumber.SINGULAR, EnumSet.of(ACCUSATIVE));
        }

        @Override
        public EnumSet getRequiredGenders() {
            return WEST_SLAVIC_GENDERS;
        }
    }

    static class SlovenianDeclension extends SlavicDeclension {
        public SlovenianDeclension(HumanLanguage language) {
            super(language);
        }

        @Override
        public EnumSet getRequiredCases() {
            return WEST_SLAVIC_CASES;
        }

        @Override
        public Map> getMasculineAnimateForms() {
           return ImmutableMap.of(LanguageNumber.SINGULAR, EnumSet.of(ACCUSATIVE));
        }

        @Override
        public EnumSet getRequiredGenders() {
            return WEST_SLAVIC_GENDERS;
        }

        @Override
        public Set getAllowedNumbers() {
            return LanguageNumber.DUAL_SET;  // Duals are really required.
        }
    }

    /**
     * SerboCroatian-like languages. (serbian, Croation, Bosnian, Montenegran
     * @author stamm
     */
    static class VariantSerboCroatianDeclension extends SlavicDeclension {
        public VariantSerboCroatianDeclension(HumanLanguage language) {
        	super(language);
        }

        @Override
        public EnumSet getRequiredCases() {
            return WEST_SLAVIC_CASES_NO_VOC;
        }
    }


    /**
     * Georgian is not Slavic (it's Kartvelian, close enough to isolate), but shares enough in common
     * with Slavic languages through Sprachbunding to be close enough.
     * @author stamm
     *
     */
    static class GeorgianDeclension extends SlavicDeclension {
        public GeorgianDeclension(HumanLanguage language) {
            super(language);
        }

        @Override
        public EnumSet getRequiredCases() {
            return EnumSet.of(LanguageCase.NOMINATIVE, LanguageCase.ERGATIVE, LanguageCase.DATIVE,
                    LanguageCase.GENITIVE, LanguageCase.INSTRUMENTAL, LanguageCase.ADVERBIAL);
        }

        @Override
        public boolean hasGender() {
            return false;
        }

        @Override
        public EnumSet getRequiredGenders() {
            return null;  // No gender in georgian.
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy