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

com.ibm.icu.impl.personname.FieldModifierImpl Maven / Gradle / Ivy

Go to download

International Component for Unicode for Java (ICU4J) is a mature, widely used Java library providing Unicode and Globalization support

There is a newer version: 76.1
Show newest version
// © 2022 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package com.ibm.icu.impl.personname;

import java.util.Locale;

import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.text.BreakIterator;
import com.ibm.icu.text.CaseMap;
import com.ibm.icu.text.PersonName;
import com.ibm.icu.text.SimpleFormatter;

/**
 * Parent class for classes that implement field-modifier behavior.
 */
abstract class FieldModifierImpl {
    public abstract String modifyField(String fieldValue);

    public static FieldModifierImpl forName(PersonName.FieldModifier modifierID, PersonNameFormatterImpl formatterImpl) {
        switch (modifierID) {
            case INFORMAL:
                return NOOP_MODIFIER;
            case PREFIX:
                return NULL_MODIFIER;
            case CORE:
                return NOOP_MODIFIER;
            case ALL_CAPS:
                return new AllCapsModifier(formatterImpl.getLocale());
            case INITIAL_CAP:
                return new InitialCapModifier(formatterImpl.getLocale());
            case INITIAL:
                return new InitialModifier(formatterImpl.getLocale(), formatterImpl.getInitialPattern(), formatterImpl.getInitialSequencePattern());
            case RETAIN:
                // "retain" is handled by InitialMofidier and PersonNamePattern.NameFieldImpl
                return NOOP_MODIFIER;
            case MONOGRAM:
                return MONOGRAM_MODIFIER;
            case GENITIVE:
                // no built-in implementation for deriving genitive from nominative; PersonName object must supply
                return NOOP_MODIFIER;
            case VOCATIVE:
                // no built-in implementation for deriving vocative from nominative; PersonName object must supply
                return NOOP_MODIFIER;
            default:
                throw new IllegalArgumentException("Invalid modifier ID " + modifierID);
        }
    }

    /**
     * A field modifier that just returns the field value unmodified.  This is used to implement the default
     * behavior of the "informal" and "core" modifiers ("real" informal or core variants have to be supplied or
     * calculated by the PersonName object).
     */
    private static final FieldModifierImpl NOOP_MODIFIER = new FieldModifierImpl() {
        @Override
        public String modifyField(String fieldValue) {
            return fieldValue;
        }
    };

    /**
     * A field modifier that just returns the empty string.  This is used to implement the default behavior of the
     * "prefix" modifier ("real" prefix variants have to be supplied to calculated by the PersonName object).
     */
    private static final FieldModifierImpl NULL_MODIFIER = new FieldModifierImpl() {
        @Override
        public String modifyField(String fieldValue) {
            return "";
        }
    };

    /**
     * A field modifier that returns the field value converted to ALL CAPS.  This is the default behavior
     * for the "allCaps" modifier.
     */
    private static class AllCapsModifier extends FieldModifierImpl {
        private final Locale locale;

        public AllCapsModifier(Locale locale) {
            this.locale = locale;
        }

        @Override
        public String modifyField(String fieldValue) {
            return UCharacter.toUpperCase(locale, fieldValue);
        }
    }

    /**
     * A field modifier that returns the field value with the first letter of each word capitalized.  This is
     * the default behavior of the "initialCap" modifier.
     */
    private static class InitialCapModifier extends FieldModifierImpl {
        private final Locale locale;
        private static final CaseMap.Title TO_TITLE_WHOLE_STRING_NO_LOWERCASE = CaseMap.toTitle().wholeString().noLowercase();

        public InitialCapModifier(Locale locale) {
            this.locale = locale;
        }

        @Override
        public String modifyField(String fieldValue) {
            return TO_TITLE_WHOLE_STRING_NO_LOWERCASE.apply(locale, null, fieldValue);
        }
    }

    /**
     * A field modifier that returns the field value converted into one or more initials.  This is the first grapheme
     * cluster of each word in the field value, modified using the initialPattern/initial resource value from the
     * locale data, and strung together using the initialPattern/initialSequence resource value from the locale data.
     * (In English, these patterns put periods after each initial and connect them with spaces.)
     * This is default behavior of the "initial" modifier.
     */
    static class InitialModifier extends FieldModifierImpl {
        private final Locale locale;
        private final SimpleFormatter initialFormatter;
        private final SimpleFormatter initialSequenceFormatter;
        private boolean retainPunctuation;

        public InitialModifier(Locale locale, String initialPattern, String initialSequencePattern) {
            this.locale = locale;
            this.initialFormatter = SimpleFormatter.compile(initialPattern);
            this.initialSequenceFormatter = SimpleFormatter.compile(initialSequencePattern);
            this.retainPunctuation = false;
        }

        public void setRetainPunctuation(boolean retain) {
            this.retainPunctuation = retain;
        }

        @Override
        public String modifyField(String fieldValue) {
            String separator = "";
            String result = null;
            BreakIterator bi = BreakIterator.getWordInstance(locale);
            bi.setText(fieldValue);
            int wordStart = bi.first();
            for (int wordEnd = bi.next(); wordEnd != BreakIterator.DONE; wordStart = wordEnd, wordEnd = bi.next()) {
                String word = fieldValue.substring(wordStart, wordEnd);
                if (Character.isLetter(word.charAt(0))) {
                    String curInitial = getFirstGrapheme(word);
                    if (result == null) {
                        result = initialFormatter.format(curInitial);
                    } else if (retainPunctuation) {
                        result = result + separator + initialFormatter.format(curInitial);
                        separator = "";
                    } else {
                        result = initialSequenceFormatter.format(result, initialFormatter.format(curInitial));
                    }
                } else if (Character.isWhitespace(word.charAt(0))) {
                    // coalesce a sequence of whitespace characters down to a single space
                    separator = separator + word.charAt(0);
                } else {
                    separator = separator + word;
                }
            }
            return result;
        }
    }

    /**
     * A field modifier that simply returns the first grapheme cluster in the field value.
     * This is the default implementation of the "monogram" modifier.
     */
    private static final FieldModifierImpl MONOGRAM_MODIFIER = new FieldModifierImpl() {
        @Override
        public String modifyField(String fieldValue) {
            return getFirstGrapheme(fieldValue);
        }
    };

    /**
     * A utility function that just returns the first grapheme cluster in the string.
     */
    private static String getFirstGrapheme(String s) {
        // early out if the string is empty to avoid StringIndexOutOfBoundsException
        if (s.isEmpty()) {
            return "";
        }

        // (currently, no locale overrides the grapheme-break rules, so we just use "root" instead of passing in the locale)
        BreakIterator bi = BreakIterator.getCharacterInstance(Locale.ROOT);
        bi.setText(s);
        return s.substring(0, bi.next());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy