From ba9b788de0cfbd5daa1d9a4790a986df2c9b48c9 Mon Sep 17 00:00:00 2001 From: Yoshiyuki Oikawa Date: Fri, 19 May 2023 21:12:02 +0900 Subject: [PATCH] Imporove LanguageDictionary serialization --- .../grammar/AbstractLanguageDeclension.java | 28 +++---- .../com/force/i18n/grammar/Adjective.java | 12 ++- .../i18n/grammar/ArticledDeclension.java | 12 +-- .../force/i18n/grammar/GrammaticalTerm.java | 65 ++++++++++++---- .../i18n/grammar/LanguageDictionary.java | 63 ++++++++++----- .../java/com/force/i18n/grammar/Noun.java | 61 +++++---------- .../java/com/force/i18n/grammar/NounForm.java | 11 +-- .../i18n/grammar/impl/BengaliDeclension.java | 1 + .../grammar/impl/BulgarianDeclension.java | 14 ++-- .../i18n/grammar/impl/CatalanDeclension.java | 3 +- .../grammar/impl/ComplexGrammaticalForm.java | 30 +++++-- .../grammar/impl/DravidianDeclension.java | 3 + .../i18n/grammar/impl/FrenchDeclension.java | 32 ++++---- .../i18n/grammar/impl/GermanicDeclension.java | 69 +++++++++------- .../i18n/grammar/impl/HebrewDeclension.java | 29 ++++--- .../grammar/impl/HindiUrduDeclension.java | 8 +- .../grammar/impl/HungarianDeclension.java | 4 +- .../i18n/grammar/impl/ItalianDeclension.java | 10 +-- .../i18n/grammar/impl/KoreanDeclension.java | 78 +++++++++---------- .../i18n/grammar/impl/RomanianDeclension.java | 2 +- .../i18n/grammar/impl/SimpleDeclension.java | 27 +------ .../impl/UnsupportedLanguageDeclension.java | 8 -- .../grammar/parser/LabelSetLoaderConfig.java | 3 +- .../parser/LanguageDictionaryHandler.java | 6 +- .../LanguageDictionarySerializationTest.java | 64 ++++++++++++--- 25 files changed, 353 insertions(+), 290 deletions(-) diff --git a/src/main/java/com/force/i18n/grammar/AbstractLanguageDeclension.java b/src/main/java/com/force/i18n/grammar/AbstractLanguageDeclension.java index 8f4942d..bd13ce1 100644 --- a/src/main/java/com/force/i18n/grammar/AbstractLanguageDeclension.java +++ b/src/main/java/com/force/i18n/grammar/AbstractLanguageDeclension.java @@ -37,7 +37,7 @@ protected AbstractLanguageDeclension(HumanLanguage language) { @Override public final HumanLanguage getLanguage() { - return this.language; + return this.language; } @Override @@ -72,7 +72,7 @@ public Article createArticle(String name, LanguageArticle articleType) { @Override public boolean hasEndsWith() { - return false; + return false; } @Override @@ -95,7 +95,7 @@ public boolean hasPlural() { @Override public Set getAllowedNumbers() { - return hasPlural() ? LanguageNumber.PLURAL_SET : LanguageNumber.SINGULAR_SET; + return hasPlural() ? LanguageNumber.PLURAL_SET : LanguageNumber.SINGULAR_SET; } @@ -144,7 +144,7 @@ public EnumSet getRequiredGenders() { return null; // Default } - final static EnumSet NOMINATIVE_SET = EnumSet.of(LanguageCase.NOMINATIVE); + static final EnumSet NOMINATIVE_SET = EnumSet.of(LanguageCase.NOMINATIVE); @Override public EnumSet getRequiredCases() { return NOMINATIVE_SET; // Nominative is usually required @@ -155,13 +155,13 @@ public EnumSet getAllowedCases() { return getRequiredCases(); } - final static EnumSet CONSONANT_SET = EnumSet.of(LanguageStartsWith.CONSONANT); + static final EnumSet CONSONANT_SET = EnumSet.of(LanguageStartsWith.CONSONANT); @Override public EnumSet getRequiredStartsWith() { return CONSONANT_SET; // Only generally care about consonant. } - final static EnumSet POSSESSIVE_NONE_SET = EnumSet.of(LanguagePossessive.NONE); + static final EnumSet POSSESSIVE_NONE_SET = EnumSet.of(LanguagePossessive.NONE); @Override public EnumSet getRequiredPossessive() { return POSSESSIVE_NONE_SET; // Doesn't matter much. @@ -316,7 +316,7 @@ public NounForm getNounForm(NounForm nf) { @Override public int getMaxDistanceForModifiers() { - return isInflected() ? 0 : 5; + return isInflected() ? 0 : 5; } // Convenience method for retrieving an equivalent AdjectiveForm from this declension @@ -399,9 +399,8 @@ public AdjectiveForm getApproximateAdjectiveForm(LanguageStartsWith startsWith, if (baseForm == null && number != LanguageNumber.SINGULAR) { baseForm = getAdjectiveForm(startsWithToTry, genderToTry, LanguageNumber.SINGULAR, caseToTry, articleToTry, possessiveToTry); } - // OK, you asked for something that wasn't supported. + // OK, you asked for something that wasn't supported. simply fallback to the first one in the supported form list if (baseForm == null) { - assert false : "Programmer error, you asked for an illegal adjective form"; baseForm = getAdjectiveForms().iterator().next(); } return baseForm; @@ -453,7 +452,7 @@ public String formLowercaseNounForm(String s, NounForm form) { @Override public final LanguagePluralRules getPluralRules() { - return LanguageProviderFactory.get().getPluralRules(getLanguage()); + return LanguageProviderFactory.get().getPluralRules(getLanguage()); } /** @@ -462,7 +461,7 @@ public final LanguagePluralRules getPluralRules() { * Often times, complex declension have simple forms of one thing or another. * This is the "simple" form types for which there is only one possible declension */ - public static enum SimpleModifierForm implements AdjectiveForm, ArticleForm { + public enum SimpleModifierForm implements AdjectiveForm, ArticleForm { SINGULAR ; @Override public LanguageArticle getArticle() { return LanguageArticle.ZERO;} @@ -547,12 +546,13 @@ public LanguageStartsWith getStartsWith() { * Simple noun form with singular and plurals * @author stamm */ - public static enum PluralNounForm implements NounForm { + public enum PluralNounForm implements NounForm { SINGULAR(LanguageNumber.SINGULAR), PLURAL(LanguageNumber.PLURAL), ; private final LanguageNumber number; + private PluralNounForm(LanguageNumber number) { this.number = number; } @@ -609,10 +609,6 @@ public SimplePluralNounWithGender(LanguageDeclension declension, String name, St super(declension, name, pluralAlias, type, entityName, LanguageStartsWith.CONSONANT, gender, access, isStandardField, isCopiedFromDefault); } - @Override - public void makeSkinny() { - } - @Override public Map getAllDefinedValues() { // TODO: All the values, or just the interesting ones? The world may never know. diff --git a/src/main/java/com/force/i18n/grammar/Adjective.java b/src/main/java/com/force/i18n/grammar/Adjective.java index 2271d04..fc787f0 100644 --- a/src/main/java/com/force/i18n/grammar/Adjective.java +++ b/src/main/java/com/force/i18n/grammar/Adjective.java @@ -1,7 +1,7 @@ -/* +/* * Copyright (c) 2017, salesforce.com, inc. * All rights reserved. - * Licensed under the BSD 3-Clause license. + * 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 */ @@ -21,11 +21,9 @@ * @author yoikawa,stamm */ public abstract class Adjective extends NounModifier { - /** - * - */ - private static final long serialVersionUID = 1L; - private static final Logger logger = Logger.getLogger(Adjective.class.getName()); + private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(Adjective.class.getName()); + private final LanguagePosition position; /** diff --git a/src/main/java/com/force/i18n/grammar/ArticledDeclension.java b/src/main/java/com/force/i18n/grammar/ArticledDeclension.java index be6f342..ab6312d 100644 --- a/src/main/java/com/force/i18n/grammar/ArticledDeclension.java +++ b/src/main/java/com/force/i18n/grammar/ArticledDeclension.java @@ -21,11 +21,11 @@ * @author stamm */ public abstract class ArticledDeclension extends AbstractLanguageDeclension { - public ArticledDeclension(HumanLanguage language) { - super(language); - } + protected ArticledDeclension(HumanLanguage language) { + super(language); + } - @Override + @Override public final boolean hasArticle() { return true; } @@ -215,10 +215,6 @@ public Noun clone() { return noun; } - @Override - public void makeSkinny() { - } - @Override protected Object readResolve() { super.readResolve(); diff --git a/src/main/java/com/force/i18n/grammar/GrammaticalTerm.java b/src/main/java/com/force/i18n/grammar/GrammaticalTerm.java index 8de5a7a..d88253d 100644 --- a/src/main/java/com/force/i18n/grammar/GrammaticalTerm.java +++ b/src/main/java/com/force/i18n/grammar/GrammaticalTerm.java @@ -10,10 +10,12 @@ import static com.force.i18n.commons.util.settings.IniFileUtil.intern; import java.io.*; -import java.util.Objects; +import java.util.*; -import com.force.i18n.*; +import com.force.i18n.HumanLanguage; +import com.force.i18n.LanguageProviderFactory; import com.force.i18n.grammar.impl.LanguageDeclensionFactory; +import com.google.common.collect.ImmutableSortedMap; /** * Represents a grammatical term; generally one that is declined based on a noun form or other @@ -25,7 +27,7 @@ * @author stamm */ public abstract class GrammaticalTerm implements Serializable, Comparable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; private String name; // non-final. see readObject() private transient LanguageDeclension declension; @@ -34,11 +36,13 @@ public enum TermType { Noun('n'), Adjective('a'), Article('d'); - private final char id; - TermType(char id) { - this.id = id; - } - public char getCharId() {return this.id;} + + private final char id; + + TermType(char id) { + this.id = id; + } + public char getCharId() {return this.id;} } protected GrammaticalTerm(LanguageDeclension declension, String name) { @@ -84,12 +88,12 @@ public LanguageDeclension getDeclension() { } @Override - public int compareTo(GrammaticalTerm o) { - TermType thisType = getTermType(); - TermType oType = o.getTermType(); - int typeComp = thisType.compareTo(oType); - return typeComp == 0 ? getName().compareTo(o.getName()) : typeComp; - } + public int compareTo(GrammaticalTerm o) { + TermType thisType = getTermType(); + TermType oType = o.getTermType(); + int typeComp = thisType.compareTo(oType); + return typeComp == 0 ? getName().compareTo(o.getName()) : typeComp; + } @Override public boolean equals(Object o) { @@ -106,7 +110,7 @@ public int hashCode() { return Objects.hash(this.declension.getLanguage().ordinal(), getTermType(), this.name); } - public abstract void toJson(Appendable appendable) throws IOException; + public abstract void toJson(Appendable appendable) throws IOException; private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); @@ -119,4 +123,35 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE HumanLanguage ul = LanguageProviderFactory.get().getProvider().getLanguage((String)in.readObject()); this.declension = LanguageDeclensionFactory.get().getDeclension(ul); } + + /** + * Provides clients the capability of indicating when members of Noun's can be converted to space efficient data + * structures. + */ + public void makeSkinny() { + // the default implementation does nothing + } + + /** + * Utility method used to convert static {@link Map}'s concrete type to a {@link ImmutableSortedMap}. + * {@link ImmutableSortedMap} have a 8 byte overhead per element and are useful for reducing the per element + * overhead, that is traditionally high on most {@code Map} implementations. + * + * @param + * the type of the grammatical form for this term + * @param map + * the map to make skinny + * @return A {@link ImmutableSortedMap} created from a {@link Map} of {@link GrammaticalForm}'s (key) to + * {@link String}'s (value). + */ + protected Map makeSkinny(Map map) { + return ImmutableSortedMap.copyOf(map, new KeyComparator()); + } + + private static class KeyComparator implements Comparator, Serializable { + @Override + public int compare(T o1, T o2) { + return o1.getKey().compareTo(o2.getKey()); + } + } } diff --git a/src/main/java/com/force/i18n/grammar/LanguageDictionary.java b/src/main/java/com/force/i18n/grammar/LanguageDictionary.java index a4e245b..6671201 100644 --- a/src/main/java/com/force/i18n/grammar/LanguageDictionary.java +++ b/src/main/java/com/force/i18n/grammar/LanguageDictionary.java @@ -64,12 +64,12 @@ public final class LanguageDictionary implements Serializable { // instances; but it would have to be concurrent. // TODO: "TableEnumOrId" should be stored on each Noun, right? /** For UI support. keyed by TableEnumOrId to HashMap(name, NounType) */ - private final Multimap nounsByEntityType; + private transient Multimap nounsByEntityType; public LanguageDictionary(HumanLanguage language) { this.language = language; this.declension = LanguageDeclensionFactory.get().getDeclension(language); - this.nounsByEntityType = ArrayListMultimap.create(); + this.nounsByEntityType = HashMultimap.create(); } /** @@ -94,20 +94,29 @@ public LanguageDictionary(HumanLanguage language, LanguageDictionary from) { @Override public boolean equals(Object o) { - if (!(o instanceof LanguageDictionary)) return false; + if (this == o) return true; - LanguageDictionary l = (LanguageDictionary)o; + if (o instanceof LanguageDictionary) { + LanguageDictionary l = (LanguageDictionary)o; - return this.language == l.language && this.adjectiveMap.equals(l.adjectiveMap) && this.nounMap.equals(l.nounMap) - && this.nounsByEntityType.equals(l.nounsByEntityType); + return this.language == l.language + && this.nounMap.equals(l.nounMap) + && this.adjectiveMap.equals(l.adjectiveMap) + && this.articleMap.equals(l.articleMap) + && this.nounMapByPluralAlias.equals(l.nounMapByPluralAlias) + && this.nounsByEntityType.equals(l.nounsByEntityType); + } + return false; } @Override public int hashCode() { int hash = super.hashCode(); hash = 37 * hash + ((null != language) ? language.hashCode() : 0); - hash = 37 * hash + ((null != adjectiveMap) ? adjectiveMap.hashCode() : 0); hash = 37 * hash + ((null != nounMap) ? nounMap.hashCode() : 0); + hash = 37 * hash + ((null != adjectiveMap) ? adjectiveMap.hashCode() : 0); + hash = 37 * hash + ((null != articleMap) ? articleMap.hashCode() : 0); + hash = 37 * hash + ((null != nounMapByPluralAlias) ? nounMapByPluralAlias.hashCode() : 0); hash = 37 * hash + ((null != nounsByEntityType) ? nounsByEntityType.hashCode() : 0); return hash; } @@ -121,8 +130,13 @@ public LanguageDeclension getDeclension() { } public Noun createNoun(String name, String pluralAlias, NounType type, String entityName, LanguageStartsWith startsWith, LanguageGender gender, String access, boolean isStandardField, boolean isCopied) { + if (entityName != null && entityName.isEmpty()) entityName = null; Noun n = getDeclension().createNoun(name, pluralAlias, type, entityName, startsWith, gender, access, isStandardField, isCopied); - if (entityName != null) nounsByEntityType.put(intern(entityName.toLowerCase()), n); // Add to the noun map + + // Add to the noun map + if (entityName != null) { + nounsByEntityType.put(intern(entityName.toLowerCase()), n); + } return n; } @@ -338,12 +352,12 @@ public void put(String name, GrammaticalTerm term) { if (term instanceof Noun) { Noun noun = (Noun) term; assert name.equals(((Noun)term).getName()) : "Trying to put a noun into the map at the wrong name"; - nounMap.put(name.intern(), noun); - if (noun.getPluralAlias() != null) nounMapByPluralAlias.put(noun.getPluralAlias().toLowerCase().intern(), noun); + nounMap.put(intern(name), noun); + if (noun.getPluralAlias() != null) nounMapByPluralAlias.put(intern(noun.getPluralAlias().toLowerCase()), noun); } else if (term instanceof Article) { - articleMap.put(name.intern(), (Article)term); + articleMap.put(intern(name), (Article)term); } else { - adjectiveMap.put(name.intern(), (Adjective)term); + adjectiveMap.put(intern(name), (Adjective)term); } } @@ -380,10 +394,12 @@ public void validateAll() { for (Map.Entry e : adjectiveMap.entrySet()) { e.getValue().validate(e.getKey()); + e.getValue().makeSkinny(); } for (Map.Entry e : articleMap.entrySet()) { e.getValue().validate(e.getKey()); + e.getValue().makeSkinny(); } if (this.nounVersionOverrides != null) { @@ -401,14 +417,14 @@ public void validateAll() { */ public final SortedSet getNames(String tableEnum, boolean includeEntity) { Collection m = nounsByEntityType.get(tableEnum.toLowerCase()); - TreeSet list = new TreeSet(); + TreeSet list = new TreeSet<>(); for (Noun noun : m) { String s = noun.getName(); - // skip for the custom dummy name - if (s.equalsIgnoreCase(Renameable.ENTITY_NAME)) continue; + // skip for the custom dummy name + if (s.equalsIgnoreCase(Renameable.ENTITY_NAME)) continue; if (noun.getNounType() != NounType.ENTITY || includeEntity) list.add(s); - } + } return list; } @@ -631,9 +647,9 @@ private void writeJsonTerm(Appendable out, RenamingProvider renamingProvider, Gr } private void writeObject(ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - makeSkinny(); // ensure it's "skinny" + + out.defaultWriteObject(); out.writeObject(new TermMapSerializer<>(this.nounMap)); out.writeObject(new TermMapSerializer<>(this.nounMapByPluralAlias)); out.writeObject(new TermMapSerializer<>(this.adjectiveMap)); @@ -648,9 +664,15 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE this.nounMapByPluralAlias = ((TermMapSerializer)in.readObject()).getMap(); this.adjectiveMap = ((TermMapSerializer)in.readObject()).getMap(); this.articleMap = ((TermMapSerializer
)in.readObject()).getMap(); - this.isSkinny = true; this.declension = LanguageDeclensionFactory.get().getDeclension(this.language); + + this.nounsByEntityType = HashMultimap.create(); + for (Noun n : this.nounMap.values()) { + if (n.getEntityName() != null) this.nounsByEntityType.put(intern(n.getEntityName().toLowerCase()), n); + } + this.nounsByEntityType = ImmutableSetMultimap.copyOf(nounsByEntityType); + this.isSkinny = true; } Noun getNounOverride(Noun n) { @@ -707,6 +729,9 @@ public void makeSkinny() { adjectiveMap = ImmutableSortedMap.copyOf(adjectiveMap); articleMap = ImmutableSortedMap.copyOf(articleMap); + nounMapByPluralAlias = ImmutableSortedMap.copyOf(nounMapByPluralAlias); + nounsByEntityType = ImmutableSetMultimap.copyOf(nounsByEntityType); + isSkinny = true; // Prevent adding anything to this dictionary set. By assumption, the nouns are skinny. } diff --git a/src/main/java/com/force/i18n/grammar/Noun.java b/src/main/java/com/force/i18n/grammar/Noun.java index a53f217..3c1fae4 100644 --- a/src/main/java/com/force/i18n/grammar/Noun.java +++ b/src/main/java/com/force/i18n/grammar/Noun.java @@ -18,7 +18,6 @@ import java.util.stream.Collectors; import com.force.i18n.commons.text.TextUtil; -import com.google.common.collect.ImmutableSortedMap; /** * A linguistic noun, containing a set of strings associated with the various noun forms @@ -342,7 +341,7 @@ public String getCloseButNoCigarString(NounForm form) { // Now number if (s == null && getDeclension().hasPlural() && form.getNumber() != LanguageNumber.SINGULAR) { - LanguageNumber numberToTry = form.getNumber() == LanguageNumber.PLURAL ? LanguageNumber.SINGULAR : LanguageNumber.PLURAL; // If we're plural try singular, otherwise, like for dual, try plural + LanguageNumber numberToTry = form.getNumber() == LanguageNumber.PLURAL ? LanguageNumber.SINGULAR : LanguageNumber.PLURAL; // If we're plural try singular, otherwise, like for dual, try plural baseForm = getDeclension().getExactNounForm(numberToTry, form.getCase(), form.getPossessive(), form.getArticle()); s = getString(baseForm); } @@ -357,48 +356,24 @@ public String toString() { return "Noun-" + getDeclension().getLanguage().getLocale() + "-'" + getAllDefinedValues().get(getDeclension().getAllNounForms().iterator().next()) + "'"; } - /** - * Provides clients the capability of indicating when members of Noun's can be converted to space efficient data structures. - */ - public abstract void makeSkinny(); - @Override - public void toJson(Appendable appendable) throws IOException { - appendable.append("{\"t\":\"n\",\"l\":\""); - appendable.append(getName()); - appendable.append("\","); - if (getDeclension().hasGender() && getGender() != null) { - appendable.append("\"g\":\"").append(getGender().getDbValue()).append("\","); - } - if ((getDeclension().hasStartsWith() || getDeclension().hasEndsWith()) && getStartsWith() != null) { - appendable.append("\"s\":\"").append(getStartsWith().getDbValue()).append("\","); - } - if (getDeclension().hasClassifiers() && getClassifier() != null) { - appendable.append("\"c\":\"").append(getClassifier()).append("\","); - } - appendable.append("\"v\":{"); - Map allValues = getAllDefinedValues(); - appendable.append(new TreeMap<>(allValues).entrySet().stream().map(e->"\""+e.getKey().getKey()+"\":\""+TextUtil.escapeForJsonString(e.getValue())+"\"").collect(Collectors.joining(","))); - appendable.append("}}"); - } - - /** - * Utility method used to convert static {@link Map}'s concrete type to a {@link ImmutableSortedMap}. - * {@link ImmutableSortedMap} have a 8 byte overhead per element and are useful for reducing the per element - * overhead, that is traditionally high on most {@code Map} implementations. - * - * @param the type of the noun form for this noun - * @param map the map to make skinny - * @return A {@link ImmutableSortedMap} created from a {@link Map} of {@link NounForm}'s (key) to {@link String}'s - * (value). - */ - public Map makeSkinny(Map map) { - return ImmutableSortedMap.copyOf(map, new Comparator() { - @Override - public int compare(NounForm n1, NounForm n2) { - return n1.getKey().compareTo(n2.getKey()); - } - }); + public void toJson(Appendable appendable) throws IOException { + appendable.append("{\"t\":\"n\",\"l\":\""); + appendable.append(getName()); + appendable.append("\","); + if (getDeclension().hasGender() && getGender() != null) { + appendable.append("\"g\":\"").append(getGender().getDbValue()).append("\","); + } + if ((getDeclension().hasStartsWith() || getDeclension().hasEndsWith()) && getStartsWith() != null) { + appendable.append("\"s\":\"").append(getStartsWith().getDbValue()).append("\","); + } + if (getDeclension().hasClassifiers() && getClassifier() != null) { + appendable.append("\"c\":\"").append(getClassifier()).append("\","); + } + appendable.append("\"v\":{"); + Map allValues = getAllDefinedValues(); + appendable.append(new TreeMap<>(allValues).entrySet().stream().map(e->"\""+e.getKey().getKey()+"\":\""+TextUtil.escapeForJsonString(e.getValue())+"\"").collect(Collectors.joining(","))); + appendable.append("}}"); } protected Object readResolve() { diff --git a/src/main/java/com/force/i18n/grammar/NounForm.java b/src/main/java/com/force/i18n/grammar/NounForm.java index 36a781f..a6e378c 100644 --- a/src/main/java/com/force/i18n/grammar/NounForm.java +++ b/src/main/java/com/force/i18n/grammar/NounForm.java @@ -1,7 +1,7 @@ -/* +/* * Copyright (c) 2017, salesforce.com, inc. * All rights reserved. - * Licensed under the BSD 3-Clause license. + * 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 */ @@ -33,11 +33,4 @@ public interface NounForm extends GrammaticalForm { * @return the possessive associated with this noun form */ LanguagePossessive getPossessive(); - - /** - * @return a HTML compatible screen that can be used to represent this noun - * form uniquely when compared to all other noun forms. - */ - @Override - String getKey(); } diff --git a/src/main/java/com/force/i18n/grammar/impl/BengaliDeclension.java b/src/main/java/com/force/i18n/grammar/impl/BengaliDeclension.java index db00819..be9cf60 100644 --- a/src/main/java/com/force/i18n/grammar/impl/BengaliDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/BengaliDeclension.java @@ -163,6 +163,7 @@ private void writeObject(ObjectOutputStream out) throws IOException { private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); this.values = ComplexGrammaticalForm.deserializeFormMap(in, getDeclension(), TermType.Noun); + makeSkinny(); } } diff --git a/src/main/java/com/force/i18n/grammar/impl/BulgarianDeclension.java b/src/main/java/com/force/i18n/grammar/impl/BulgarianDeclension.java index 2499f4e..b4ef386 100644 --- a/src/main/java/com/force/i18n/grammar/impl/BulgarianDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/BulgarianDeclension.java @@ -65,8 +65,8 @@ */ class BulgarianDeclension extends AbstractLanguageDeclension { public BulgarianDeclension(HumanLanguage language) { - super(language); - } + super(language); + } private static final Logger logger = Logger.getLogger(BulgarianDeclension.class.getName()); @@ -90,6 +90,7 @@ public enum BulgarianModifierForm implements AdjectiveForm { private final LanguageGender gender; private final LanguageArticle article; private final LanguageCase caseType; + private BulgarianModifierForm(LanguageNumber number, LanguageGender gender) { this(number, gender, LanguageArticle.ZERO); } @@ -123,7 +124,7 @@ public void appendJsFormReplacement(Appendable a, String termFormVar, String gen } } - public static enum BulgarianNounForm implements NounForm { + public enum BulgarianNounForm implements NounForm { SINGULAR(LanguageNumber.SINGULAR, LanguageCase.NOMINATIVE), PLURAL(LanguageNumber.PLURAL, LanguageCase.NOMINATIVE), SINGULAR_DEF(LanguageNumber.SINGULAR, LanguageCase.NOMINATIVE, LanguageArticle.DEFINITE), @@ -134,6 +135,7 @@ public static enum BulgarianNounForm implements NounForm { private final LanguageNumber number; private final LanguageCase caseType; private final LanguageArticle article; + BulgarianNounForm(LanguageNumber number, LanguageCase caseType) { this(number, caseType, LanguageArticle.ZERO); } @@ -149,7 +151,7 @@ public static enum BulgarianNounForm implements NounForm { @Override public LanguageNumber getNumber() {return this.number;} @Override public String getKey() { - return getCase().getDbValue()+"-"+getNumber().getDbValue()+"-"+getArticle().getDbValue(); + return getCase().getDbValue() + "-" + getNumber().getDbValue() + "-" + getArticle().getDbValue(); } } @@ -258,10 +260,6 @@ public Noun clone() { return noun; } - @Override - public void makeSkinny() { - } - @Override protected Object readResolve() { super.readResolve(); diff --git a/src/main/java/com/force/i18n/grammar/impl/CatalanDeclension.java b/src/main/java/com/force/i18n/grammar/impl/CatalanDeclension.java index 41870cc..a776e67 100644 --- a/src/main/java/com/force/i18n/grammar/impl/CatalanDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/CatalanDeclension.java @@ -44,7 +44,7 @@ public CatalanDeclension(HumanLanguage language) { /** * Adjective for Catalan. It takes care of gender, plurality(number) and startwith. */ - public static enum CatalanModifierForm implements AdjectiveForm, ArticleForm { + public enum CatalanModifierForm implements AdjectiveForm, ArticleForm { SINGULAR_MASCULINE(LanguageNumber.SINGULAR, LanguageGender.MASCULINE, LanguageStartsWith.CONSONANT), SINGULAR_FEMININE(LanguageNumber.SINGULAR, LanguageGender.FEMININE, LanguageStartsWith.CONSONANT), PLURAL_MASCULINE(LanguageNumber.PLURAL, LanguageGender.MASCULINE, LanguageStartsWith.CONSONANT), @@ -59,6 +59,7 @@ public static enum CatalanModifierForm implements AdjectiveForm, ArticleForm { private final LanguageNumber number; private final LanguageGender gender; private final LanguageStartsWith startsWith; + private CatalanModifierForm(LanguageNumber number, LanguageGender gender, LanguageStartsWith startsWith) { this.number = number; this.gender = gender; diff --git a/src/main/java/com/force/i18n/grammar/impl/ComplexGrammaticalForm.java b/src/main/java/com/force/i18n/grammar/impl/ComplexGrammaticalForm.java index ccf5983..ebf18e0 100644 --- a/src/main/java/com/force/i18n/grammar/impl/ComplexGrammaticalForm.java +++ b/src/main/java/com/force/i18n/grammar/impl/ComplexGrammaticalForm.java @@ -257,9 +257,9 @@ static void serializeFormMap(ObjectOutputStre } @SuppressWarnings("unchecked") // Deserializing a map using trickery - static Map deserializeFormMap(ObjectInputStream in, LanguageDeclension declension, TermType termType) throws IOException, ClassNotFoundException { + static Map deserializeFormMap(ObjectInputStream in, LanguageDeclension declension, TermType termType) throws IOException, ClassNotFoundException { int size = in.readByte(); - Map result = new HashMap(size << 1); + Map result = new HashMap<>(size << 1); if (size == 0) { return result; } @@ -512,6 +512,12 @@ private void writeObject(ObjectOutputStream out) throws IOException { private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); this.values = ComplexGrammaticalForm.deserializeFormMap(in, getDeclension(), TermType.Adjective); + makeSkinny(); + } + + @Override + public void makeSkinny() { + values = makeSkinny(values); } } @@ -525,7 +531,7 @@ abstract static class ComplexNoun extends Noun { private transient Map values = new HashMap<>(); ComplexNoun(LanguageDeclension declension, String name, String pluralAlias, NounType type, String entityName, LanguageStartsWith startsWith, - LanguageGender gender, String access, boolean isStandardField, boolean isCopiedFromDefault) { + LanguageGender gender, String access, boolean isStandardField, boolean isCopiedFromDefault) { super(declension, name, pluralAlias, type, entityName, startsWith, gender, access, isStandardField, isCopiedFromDefault); } @@ -548,8 +554,9 @@ public void setString(String value, NounForm form) { @Override public Noun clone() { - @SuppressWarnings("unchecked") // Clone not generalized + @SuppressWarnings("unchecked") // Clone not generalized ComplexNoun noun = (ComplexNoun) super.clone(); + noun.values = new HashMap<>(noun.values); return noun; } @@ -557,12 +564,15 @@ public Noun clone() { // Reduce size of noun maps private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); + ComplexGrammaticalForm.serializeFormMap(out, values); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); + this.values = ComplexGrammaticalForm.deserializeFormMap(in, getDeclension(), TermType.Noun); + makeSkinny(); } @Override @@ -582,7 +592,7 @@ abstract static class ComplexArticledNoun extends Leg private transient Map values = new HashMap<>(); ComplexArticledNoun(ArticledDeclension declension, String name, String pluralAlias, NounType type, String entityName, LanguageStartsWith startsWith, - LanguageGender gender, String access, boolean isStandardField, boolean isCopiedFromDefault) { + LanguageGender gender, String access, boolean isStandardField, boolean isCopiedFromDefault) { super(declension, name, pluralAlias, type, entityName, startsWith, gender, access, isStandardField, isCopiedFromDefault); } @@ -606,21 +616,25 @@ public void setString(String value, NounForm form) { @Override public Noun clone() { - @SuppressWarnings("unchecked") // Clone not generalized - ComplexArticledNoun noun = (ComplexArticledNoun) super.clone(); - noun.values = new HashMap(noun.values); + @SuppressWarnings("unchecked") // Clone not generalized + ComplexArticledNoun noun = (ComplexArticledNoun) super.clone(); + + noun.values = new HashMap<>(noun.values); return noun; } // Reduce size of noun maps private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); + ComplexGrammaticalForm.serializeFormMap(out, values); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); + this.values = ComplexGrammaticalForm.deserializeFormMap(in, getDeclension(), TermType.Noun); + makeSkinny(); } @Override diff --git a/src/main/java/com/force/i18n/grammar/impl/DravidianDeclension.java b/src/main/java/com/force/i18n/grammar/impl/DravidianDeclension.java index 7eb432d..96db4f4 100644 --- a/src/main/java/com/force/i18n/grammar/impl/DravidianDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/DravidianDeclension.java @@ -149,12 +149,15 @@ public String getString(NounForm nid) { // Override read and write, or else you'll get mysterious exception private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); + ComplexGrammaticalForm.serializeFormMap(out, values); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); + this.values = ComplexGrammaticalForm.deserializeFormMap(in, getDeclension(), TermType.Noun); + makeSkinny(); } } diff --git a/src/main/java/com/force/i18n/grammar/impl/FrenchDeclension.java b/src/main/java/com/force/i18n/grammar/impl/FrenchDeclension.java index 02befc5..1efc113 100644 --- a/src/main/java/com/force/i18n/grammar/impl/FrenchDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/FrenchDeclension.java @@ -23,13 +23,13 @@ */ class FrenchDeclension extends RomanceDeclension { public FrenchDeclension(HumanLanguage language) { - super(language); - } + super(language); + } - /** + /** * Adjective form for languages that don't care about "starts with" */ - public static enum FrenchModifierForm implements AdjectiveForm, ArticleForm { + public enum FrenchModifierForm implements AdjectiveForm, ArticleForm { // TODO: are all of these necessary? SINGULAR_MASCULINE(LanguageNumber.SINGULAR, LanguageGender.MASCULINE, LanguageStartsWith.CONSONANT), SINGULAR_FEMININE(LanguageNumber.SINGULAR, LanguageGender.FEMININE, LanguageStartsWith.CONSONANT), @@ -56,15 +56,15 @@ private FrenchModifierForm(LanguageNumber number, LanguageGender gender, Languag @Override public LanguageGender getGender() {return this.gender;} @Override public LanguageStartsWith getStartsWith() { return startsWith; } @Override public LanguagePossessive getPossessive() { return LanguagePossessive.NONE; } - @Override - public String getKey() { - return getNumber().getDbValue() + "-" + getGender().getDbValue() + "-" + getStartsWith().getDbValue(); - } - @Override - public void appendJsFormReplacement(Appendable a, String termFormVar, String genderVar, String startsWithVar) - throws IOException { - a.append(termFormVar+".substr(0,2)+"+genderVar+"+'-'+"+startsWithVar); - } + @Override + public String getKey() { + return getNumber().getDbValue() + "-" + getGender().getDbValue() + "-" + getStartsWith().getDbValue(); + } + @Override + public void appendJsFormReplacement(Appendable a, String termFormVar, String genderVar, String startsWithVar) + throws IOException { + a.append(termFormVar+".substr(0,2)+"+genderVar+"+'-'+"+startsWithVar); + } } protected static class FrenchAdjective extends Adjective { @@ -241,10 +241,10 @@ public AdjectiveForm getAdjectiveForm(LanguageStartsWith startsWith, LanguageGen */ static class RomanshDeclension extends FrenchDeclension { public RomanshDeclension(HumanLanguage language) { - super(language); - } + super(language); + } - private static final EnumMap RM_INDEFINITE_ARTICLE = + private static final EnumMap RM_INDEFINITE_ARTICLE = new EnumMap<>(ImmutableMap.builder() .put(FrenchModifierForm.SINGULAR_FEMININE, "ina ") .put(FrenchModifierForm.SINGULAR_MASCULINE, "in ") diff --git a/src/main/java/com/force/i18n/grammar/impl/GermanicDeclension.java b/src/main/java/com/force/i18n/grammar/impl/GermanicDeclension.java index 6865fab..02abd96 100644 --- a/src/main/java/com/force/i18n/grammar/impl/GermanicDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/GermanicDeclension.java @@ -9,7 +9,7 @@ import static com.force.i18n.commons.util.settings.IniFileUtil.intern; -import java.io.IOException; +import java.io.*; import java.util.*; import java.util.logging.Logger; @@ -43,7 +43,8 @@ abstract class GermanicDeclension extends ArticledDeclension { private final ModifierFormMap articleFormMap; protected GermanicDeclension(HumanLanguage language) { - super(language); + super(language); + // Generate the different forms from subclass methods ImmutableList.Builder entityBuilder = ImmutableList.builder(); ImmutableList.Builder fieldBuilder = ImmutableList.builder(); @@ -172,7 +173,10 @@ public GermanicAdjectiveForm(LanguageDeclension declension, LanguageNumber numbe @Override public String getKey() { - return getGender().getDbValue() + "-" + getNumber().getDbValue() + "-" + getCase().getDbValue() + "-" + getArticle().getDbValue() ; + String ret = getGender().getDbValue() + "-" + getNumber().getDbValue() + "-" + getCase().getDbValue() + "-" + + getArticle().getDbValue(); + if (getDeclension().hasStartsWith()) ret += "-" + getStartsWith().getDbValue(); + return ret; } @Override @@ -273,7 +277,7 @@ protected String appendArticleToBase(String base, String article, NounForm form) } /** - * Represents an english adjective + * Represents an Germanic adjective */ public static class GermanicAdjective extends ComplexAdjective { private static final long serialVersionUID = 1L; @@ -295,12 +299,12 @@ public boolean validate(String name) { } /** - * Represents an english adjective + * Represents an Germanic adjective */ public static class GermanicArticle extends Article { private static final long serialVersionUID = 1L; - private Map values = new HashMap<>(); + private transient Map values = new HashMap<>(); GermanicArticle(ArticledDeclension declension, String name, LanguageArticle articleType) { super(declension, name, articleType); @@ -327,9 +331,22 @@ public boolean validate(String name) { return true; } - protected Object readResolve() { - this.values.replaceAll((k, v) -> intern(v)); - return this; + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + + ComplexGrammaticalForm.serializeFormMap(out, values); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + + this.values = ComplexGrammaticalForm.deserializeFormMap(in, getDeclension(), TermType.Article); + makeSkinny(); + } + + @Override + public void makeSkinny() { + values = makeSkinny(values); } } @@ -592,11 +609,11 @@ public EnumSet getRequiredGenders() { static class DutchDeclension extends GermanicDeclension { public DutchDeclension(HumanLanguage language) { - super(language); - assert language.getLocale().getLanguage().equals("nl") : "Initializing a language that isn't dutch"; - } + super(language); + assert language.getLocale().getLanguage().equals("nl") : "Initializing a language that isn't dutch"; + } - @Override + @Override protected final EnumSet getRequiredAdjectiveArticles() { return ZERO_AND_DEFARTICLES; } @@ -623,11 +640,11 @@ protected String getDefaultArticleString(ArticleForm form, LanguageArticle artic static class DanishDeclension extends GermanicDeclension { public DanishDeclension(HumanLanguage language) { - super(language); - assert language.getLocale().getLanguage().equals("da") : "Initializing a language that isn't danish"; - } + super(language); + assert language.getLocale().getLanguage().equals("da") : "Initializing a language that isn't danish"; + } - @Override + @Override protected EnumSet getRequiredAdjectiveArticles() { return ZERO_AND_DEFARTICLES; } @@ -651,10 +668,10 @@ protected String getDefaultArticleString(ArticleForm form, LanguageArticle artic static class NorwegianDeclension extends GermanicDeclension { public NorwegianDeclension(HumanLanguage language) { - super(language); - } + super(language); + } - @Override + @Override protected EnumSet getRequiredAdjectiveArticles() { return ZERO_AND_DEFARTICLES; } @@ -682,10 +699,10 @@ public boolean shouldInferNounDefArticleFromParticle() { static class IcelandicDeclension extends GermanicDeclension { public IcelandicDeclension(HumanLanguage language) { - super(language); - } + super(language); + } - @Override + @Override protected EnumSet getRequiredAdjectiveArticles() { return ZERO_AND_DEFARTICLES; } @@ -719,10 +736,10 @@ public boolean shouldInferNounDefArticleFromParticle() { static class LuxembourgishDeclension extends GermanicDeclension { public LuxembourgishDeclension(HumanLanguage language) { - super(language); - } + super(language); + } - private static final Map>> DEFINITE_ARTICLE = + private static final Map>> DEFINITE_ARTICLE = ImmutableMap.of( LanguageCase.NOMINATIVE, ImmutableMap.of(LanguageNumber.SINGULAR, ImmutableMap.of( diff --git a/src/main/java/com/force/i18n/grammar/impl/HebrewDeclension.java b/src/main/java/com/force/i18n/grammar/impl/HebrewDeclension.java index 8e9a2ad..2579886 100644 --- a/src/main/java/com/force/i18n/grammar/impl/HebrewDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/HebrewDeclension.java @@ -29,8 +29,8 @@ */ class HebrewDeclension extends SemiticDeclension { public HebrewDeclension(HumanLanguage language) { - super(language); - } + super(language); + } private static final Logger logger = Logger.getLogger(HebrewDeclension.class.getName()); @@ -40,8 +40,10 @@ public static enum HebrewNounForm implements NounForm { SINGULAR_DEF(LanguageNumber.SINGULAR, LanguageArticle.DEFINITE), PLURAL_DEF(LanguageNumber.PLURAL, LanguageArticle.DEFINITE), ; + private final LanguageNumber number; private final LanguageArticle article; + HebrewNounForm(LanguageNumber number, LanguageArticle article) { this.number = number; this.article = article; @@ -73,6 +75,7 @@ public static enum HebrewModifierForm implements AdjectiveForm { private final LanguageNumber number; private final LanguageGender gender; private final LanguageArticle article; + private HebrewModifierForm(LanguageNumber number, LanguageGender gender) { this(number, gender, LanguageArticle.ZERO); } @@ -88,15 +91,15 @@ private HebrewModifierForm(LanguageNumber number, LanguageGender gender, Languag @Override public LanguageGender getGender() {return this.gender;} @Override public LanguageStartsWith getStartsWith() { return LanguageStartsWith.CONSONANT; } @Override public LanguagePossessive getPossessive() { return LanguagePossessive.NONE; } - @Override - public String getKey() { - return getGender().getDbValue() + "-" + getArticle().getDbValue() + "-" + getNumber().getDbValue(); - } - @Override - public void appendJsFormReplacement(Appendable a, String termFormVar, String genderVar, String startsWithVar) - throws IOException { - a.append(genderVar+"+"+termFormVar+".substr(1)"); - } + @Override + public String getKey() { + return getGender().getDbValue() + "-" + getArticle().getDbValue() + "-" + getNumber().getDbValue(); + } + @Override + public void appendJsFormReplacement(Appendable a, String termFormVar, String genderVar, String startsWithVar) + throws IOException { + a.append(genderVar+"+"+termFormVar+".substr(1)"); + } } private static final String DEFAULT_DEFINITE_PREFIX = "\u05d4"; // ה @@ -113,10 +116,6 @@ public static final class HebrewNoun extends LegacyArticledNoun { super(declension, name, pluralAlias, type, entityName, LanguageStartsWith.CONSONANT, gender, access, isStandardField, isCopiedFromDefault); } - @Override - public void makeSkinny() { - } - @Override public Map getAllDefinedValues() { return enumMapFilterNulls(HebrewNounForm.SINGULAR, singular, HebrewNounForm.PLURAL, plural, HebrewNounForm.SINGULAR_DEF, singular_def, diff --git a/src/main/java/com/force/i18n/grammar/impl/HindiUrduDeclension.java b/src/main/java/com/force/i18n/grammar/impl/HindiUrduDeclension.java index 21272ce..b817d05 100644 --- a/src/main/java/com/force/i18n/grammar/impl/HindiUrduDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/HindiUrduDeclension.java @@ -37,7 +37,7 @@ class HindiUrduDeclension extends AbstractLanguageDeclension { private static EnumSet GENDER_TYPES = EnumSet.of(LanguageGender.FEMININE, LanguageGender.MASCULINE); HindiUrduDeclension(HumanLanguage language) { - super(language); + super(language); } /** @@ -76,7 +76,7 @@ public String getKey() { @Override public void appendJsFormReplacement(Appendable a, String termFormVar, String genderVar, String startsWithVar) throws IOException { - a.append(genderVar+"+"+termFormVar+".substr(1)"); + a.append(genderVar+"+"+termFormVar+".substr(1)"); } } @@ -116,10 +116,6 @@ public static final class HindiUrduNoun extends Noun { super(declension, name, pluralAlias, type, entityName, LanguageStartsWith.CONSONANT, gender, access, isStandardField, isCopiedFromDefault); } - @Override - public void makeSkinny() { - } - @Override public Map getAllDefinedValues() { return enumMapFilterNulls(HindiUrduNounForm.SINGULAR, singular, HindiUrduNounForm.PLURAL, plural, HindiUrduNounForm.SINGULAR_OBL, singular_obl, diff --git a/src/main/java/com/force/i18n/grammar/impl/HungarianDeclension.java b/src/main/java/com/force/i18n/grammar/impl/HungarianDeclension.java index 46b4f01..a8315f1 100644 --- a/src/main/java/com/force/i18n/grammar/impl/HungarianDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/HungarianDeclension.java @@ -70,7 +70,7 @@ public boolean hasPossessive() { public HungarianDeclension(HumanLanguage language) { - super(language); + super(language); // Generate the different forms from subclass methods ImmutableList.Builder entityBuilder = ImmutableList.builder(); ImmutableList.Builder fieldBuilder = ImmutableList.builder(); @@ -216,7 +216,7 @@ protected boolean validateGender(String name) { public static class HungarianArticle extends Article { private static final long serialVersionUID = 1L; - private Map values = new HashMap<>(); + private Map values = new EnumMap<>(HungarianArticleForm.class); HungarianArticle(ArticledDeclension declension, String name, LanguageArticle articleType) { super(declension, name, articleType); diff --git a/src/main/java/com/force/i18n/grammar/impl/ItalianDeclension.java b/src/main/java/com/force/i18n/grammar/impl/ItalianDeclension.java index 1e113c9..2d47df9 100644 --- a/src/main/java/com/force/i18n/grammar/impl/ItalianDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/ItalianDeclension.java @@ -23,13 +23,13 @@ */ class ItalianDeclension extends RomanceDeclension { public ItalianDeclension(HumanLanguage language) { - super(language); - } + super(language); + } - /** + /** * Adjective form for languages that don't care about "starts with" */ - public static enum ItalianModifierForm implements AdjectiveForm, ArticleForm { + public enum ItalianModifierForm implements AdjectiveForm, ArticleForm { // TODO: are all of these necessary? SINGULAR_MASCULINE(LanguageNumber.SINGULAR, LanguageGender.MASCULINE, LanguageStartsWith.CONSONANT), SINGULAR_FEMININE(LanguageNumber.SINGULAR, LanguageGender.FEMININE, LanguageStartsWith.CONSONANT), @@ -68,7 +68,7 @@ public String getKey() { @Override public void appendJsFormReplacement(Appendable a, String termFormVar, String genderVar, String startsWithVar) throws IOException { - a.append(termFormVar+".substr(0,2)+"+genderVar+"+'-'+"+startsWithVar); + a.append(termFormVar+".substr(0,2)+"+genderVar+"+'-'+"+startsWithVar); } } diff --git a/src/main/java/com/force/i18n/grammar/impl/KoreanDeclension.java b/src/main/java/com/force/i18n/grammar/impl/KoreanDeclension.java index 12c841e..8c91be1 100644 --- a/src/main/java/com/force/i18n/grammar/impl/KoreanDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/KoreanDeclension.java @@ -29,24 +29,25 @@ */ public class KoreanDeclension extends AbstractLanguageDeclension implements WithClassifiers { - private static final Logger logger = Logger.getLogger(KoreanDeclension.class.getName()); + private static final Logger logger = Logger.getLogger(KoreanDeclension.class.getName()); - public KoreanDeclension(HumanLanguage language) { - super(language); + public KoreanDeclension(HumanLanguage language) { + super(language); assert language.getLocale().getLanguage().equals("ko") : "Initializing a language that isn't korean"; - } + } /** * Korean particles differ based on whether the previous noun ended with a vowel or a consonant. * We use "adjective" for this to be simpler. */ - public static enum KoreanAdjectiveForm implements AdjectiveForm { + public enum KoreanAdjectiveForm implements AdjectiveForm { PREV_CONSONANT(LanguageStartsWith.CONSONANT), PREV_VOWEL(LanguageStartsWith.VOWEL), PREV_FLAP(LanguageStartsWith.SPECIAL), // ㄹ should be treated like a consonant, except with the instrumental case particle ; private final LanguageStartsWith prevEndsWith; + private KoreanAdjectiveForm(LanguageStartsWith prevEndsWith) { this.prevEndsWith = prevEndsWith; } @@ -55,26 +56,27 @@ private KoreanAdjectiveForm(LanguageStartsWith prevEndsWith) { @Override public LanguageGender getGender() { return LanguageGender.NEUTER; } @Override public LanguageNumber getNumber() { return LanguageNumber.SINGULAR; } @Override public LanguageStartsWith getStartsWith() {return this.prevEndsWith; } - @Override public LanguageArticle getArticle() { return LanguageArticle.ZERO; } - @Override public LanguagePossessive getPossessive() { return LanguagePossessive.NONE; } + @Override public LanguageArticle getArticle() { return LanguageArticle.ZERO; } + @Override public LanguagePossessive getPossessive() { return LanguagePossessive.NONE; } static KoreanAdjectiveForm getForm(ModifierForm form) { - switch (form.getStartsWith()) { - case VOWEL: return PREV_VOWEL; - case SPECIAL: return PREV_FLAP; - case CONSONANT: return PREV_CONSONANT; - } - return PREV_CONSONANT; + switch (form.getStartsWith()) { + case VOWEL: return PREV_VOWEL; + case SPECIAL: return PREV_FLAP; + case CONSONANT: return PREV_CONSONANT; + } + return PREV_CONSONANT; } - @Override - public String getKey() { - return prevEndsWith.getDbValue(); - } - @Override - public void appendJsFormReplacement(Appendable a, String termFormVar, String genderVar, String startsWithVar) - throws IOException { - a.append(startsWithVar); // The noun form is the startsWithVar. - } + @Override + public String getKey() { + return prevEndsWith.getDbValue(); + } + + @Override + public void appendJsFormReplacement(Appendable a, String termFormVar, String genderVar, String startsWithVar) + throws IOException { + a.append(startsWithVar); // The noun form is the startsWithVar. + } } /** @@ -94,8 +96,8 @@ public static class KoreanAdjective extends Adjective { @Override public Map getAllValues() { return enumMapFilterNulls(KoreanAdjectiveForm.PREV_VOWEL, prevEndsWithVowel, - KoreanAdjectiveForm.PREV_CONSONANT, prevEndsWithConsonant, - KoreanAdjectiveForm.PREV_FLAP, prevEndsWithSpecial); + KoreanAdjectiveForm.PREV_CONSONANT, prevEndsWithConsonant, + KoreanAdjectiveForm.PREV_FLAP, prevEndsWithSpecial); } @Override @@ -167,7 +169,7 @@ public String getDefaultString(boolean isPlural) { @Override public final String getString(NounForm form) { - return this.value; + return this.value; } @Override @@ -215,10 +217,6 @@ public Noun clone() { return noun; } - @Override - public void makeSkinny() { - } - @Override public void setClassifier(String classifier) { this.classifier = intern(classifier); @@ -255,19 +253,19 @@ protected Object readResolve() { @Override public Adjective createAdjective(String name, LanguageStartsWith startsWith, LanguagePosition position) { - // It isn't starts with, it's end with here. + // It isn't starts with, it's end with here. return new KoreanAdjective(this, name, position); } @Override public AdjectiveForm getAdjectiveForm(LanguageStartsWith startsWith, LanguageGender gender, LanguageNumber number, LanguageCase _case, LanguageArticle article, LanguagePossessive possessive) { - switch (startsWith) { - case VOWEL: return KoreanAdjectiveForm.PREV_VOWEL; - case SPECIAL: return KoreanAdjectiveForm.PREV_FLAP; - default: - } - return KoreanAdjectiveForm.PREV_CONSONANT; + switch (startsWith) { + case VOWEL: return KoreanAdjectiveForm.PREV_VOWEL; + case SPECIAL: return KoreanAdjectiveForm.PREV_FLAP; + default: + } + return KoreanAdjectiveForm.PREV_CONSONANT; } @Override @@ -286,9 +284,9 @@ public boolean hasStartsWith() { } @Override - public boolean hasEndsWith() { - return true; - } + public boolean hasEndsWith() { + return true; + } @Override public boolean hasPlural() { @@ -300,7 +298,7 @@ public boolean hasCapitalization() { return false; } - @Override + @Override public EnumSet getRequiredStartsWith() { return EnumSet.of(LanguageStartsWith.CONSONANT, LanguageStartsWith.VOWEL, LanguageStartsWith.SPECIAL); // Only generally care about consonant. } diff --git a/src/main/java/com/force/i18n/grammar/impl/RomanianDeclension.java b/src/main/java/com/force/i18n/grammar/impl/RomanianDeclension.java index c3b3eef..20567d3 100644 --- a/src/main/java/com/force/i18n/grammar/impl/RomanianDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/RomanianDeclension.java @@ -91,7 +91,7 @@ private RomanianNounForm(LanguageDeclension declension, LanguageNumber number, L @Override public String getKey() { - return getNumber().getDbValue() + "-" + getCase().getDbValue() + getArticle().getDbValue(); + return getNumber().getDbValue() + "-" + getCase().getDbValue() + "-" + getArticle().getDbValue(); } @Override diff --git a/src/main/java/com/force/i18n/grammar/impl/SimpleDeclension.java b/src/main/java/com/force/i18n/grammar/impl/SimpleDeclension.java index 01bed31..cec08f8 100644 --- a/src/main/java/com/force/i18n/grammar/impl/SimpleDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/SimpleDeclension.java @@ -10,30 +10,15 @@ import static com.force.i18n.commons.util.settings.IniFileUtil.intern; import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.logging.Logger; import com.force.i18n.HumanLanguage; import com.force.i18n.LanguageConstants; -import com.force.i18n.grammar.AbstractLanguageDeclension; -import com.force.i18n.grammar.Adjective; -import com.force.i18n.grammar.AdjectiveForm; -import com.force.i18n.grammar.LanguageArticle; -import com.force.i18n.grammar.LanguageCase; -import com.force.i18n.grammar.LanguageDeclension; -import com.force.i18n.grammar.LanguageGender; -import com.force.i18n.grammar.LanguageNumber; -import com.force.i18n.grammar.LanguagePosition; -import com.force.i18n.grammar.LanguagePossessive; -import com.force.i18n.grammar.LanguageStartsWith; -import com.force.i18n.grammar.Noun; +import com.force.i18n.grammar.*; import com.force.i18n.grammar.Noun.NounType; -import com.force.i18n.grammar.NounForm; import com.google.common.collect.ImmutableList; + /** * An implementation of declension of a language that doesn't use different forms for nouns. * @@ -49,7 +34,7 @@ public SimpleDeclension(HumanLanguage language) { } // Nice classes that can be reused for languages with little or no inflection - public static enum SimpleNounForm implements NounForm { + public enum SimpleNounForm implements NounForm { SINGULAR ; @@ -80,10 +65,6 @@ public static class SimpleNoun extends Noun { super(declension, name, pluralAlias, type, entityName, startsWith, LanguageGender.NEUTER, access, isStandardField, isCopiedFromDefault); } - @Override - public void makeSkinny() { - } - @Override public Map getAllDefinedValues() { // TODO: Should this return "all" of them, or just the real ones? diff --git a/src/main/java/com/force/i18n/grammar/impl/UnsupportedLanguageDeclension.java b/src/main/java/com/force/i18n/grammar/impl/UnsupportedLanguageDeclension.java index a9dc8e5..7f0b8d1 100644 --- a/src/main/java/com/force/i18n/grammar/impl/UnsupportedLanguageDeclension.java +++ b/src/main/java/com/force/i18n/grammar/impl/UnsupportedLanguageDeclension.java @@ -189,10 +189,6 @@ public static final class IrishNoun extends LegacyArticledNoun { super(declension, name, pluralAlias, type, entityName, startsWith, gender, access, isStandardField, isCopiedFromDefault); } - @Override - public void makeSkinny() { - } - @Override public Map getAllDefinedValues() { return enumMapFilterNulls(IrishNounForm.SINGULAR, singular, IrishNounForm.PLURAL, plural, IrishNounForm.SINGULAR_GEN, singular_gen, @@ -440,10 +436,6 @@ public static final class PersianNoun extends Noun { super(declension, name, pluralAlias, type, entityName, startsWith, gender, access, isStandardField, isCopiedFromDefault); } - @Override - public void makeSkinny() { - } - @Override public Map getAllDefinedValues() { return enumMapFilterNulls(PersianNounForm.SINGULAR, singular, PersianNounForm.PLURAL, plural, PersianNounForm.SINGULAR_ACC, singular_acc, diff --git a/src/main/java/com/force/i18n/grammar/parser/LabelSetLoaderConfig.java b/src/main/java/com/force/i18n/grammar/parser/LabelSetLoaderConfig.java index 2430657..57c1f18 100644 --- a/src/main/java/com/force/i18n/grammar/parser/LabelSetLoaderConfig.java +++ b/src/main/java/com/force/i18n/grammar/parser/LabelSetLoaderConfig.java @@ -141,7 +141,8 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("stats=").append(this.isCacheStatsEnabled) .append(", expire=").append(this.cacheExpireAfter) - .append(", size=").append(this.cacheMaxSize); + .append(", size=").append(this.cacheMaxSize) + .append(", dir=").append(this.cacheDir.toAbsolutePath()); return sb.toString(); } } diff --git a/src/main/java/com/force/i18n/grammar/parser/LanguageDictionaryHandler.java b/src/main/java/com/force/i18n/grammar/parser/LanguageDictionaryHandler.java index 4378dae..b183a53 100644 --- a/src/main/java/com/force/i18n/grammar/parser/LanguageDictionaryHandler.java +++ b/src/main/java/com/force/i18n/grammar/parser/LanguageDictionaryHandler.java @@ -7,6 +7,8 @@ package com.force.i18n.grammar.parser; +import static com.force.i18n.commons.util.settings.IniFileUtil.intern; + import java.net.MalformedURLException; import java.net.URL; import java.util.logging.Logger; @@ -363,7 +365,7 @@ class AdjectiveTag extends BaseTag { super(parent, atts); // always store as lower case - this.name = atts.getValue(NAME).toLowerCase().intern(); + this.name = intern(atts.getValue(NAME).toLowerCase()); LanguageStartsWith starts = LanguageStartsWith.fromDbValue(atts.getValue(ENDS) != null ? atts.getValue(ENDS) : atts.getValue(STARTS)); if (starts == null) starts = parser.getDictionary().getDeclension().getDefaultStartsWith(); LanguagePosition position = LanguagePosition.fromDbValue(atts.getValue(POSITION)); @@ -413,7 +415,7 @@ class ArticleTag extends BaseTag { super(parent, atts); // always store as lower case - this.name = atts.getValue(NAME).toLowerCase().intern(); + this.name = intern(atts.getValue(NAME).toLowerCase()); LanguageArticle articleType = LanguageArticle.fromLabelValue(atts.getValue(TYPE)); this.art = parser.getDictionary().getOrCreateArticle(name, articleType); // We're reparsing the same noun. Create it instead diff --git a/src/test/java/com/force/i18n/grammar/impl/LanguageDictionarySerializationTest.java b/src/test/java/com/force/i18n/grammar/impl/LanguageDictionarySerializationTest.java index cf652b0..448a387 100644 --- a/src/test/java/com/force/i18n/grammar/impl/LanguageDictionarySerializationTest.java +++ b/src/test/java/com/force/i18n/grammar/impl/LanguageDictionarySerializationTest.java @@ -1,21 +1,24 @@ -/* +/* * Copyright (c) 2017, salesforce.com, inc. * All rights reserved. - * Licensed under the BSD 3-Clause license. + * 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 java.io.*; -import java.util.IdentityHashMap; -import java.util.List; +import java.lang.reflect.Field; +import java.util.*; import java.util.logging.Logger; +import org.junit.Test; + import com.force.i18n.HumanLanguage; import com.force.i18n.LanguageProviderFactory; import com.force.i18n.grammar.*; import com.force.i18n.grammar.parser.BaseGrammaticalLabelTest; +import com.google.common.collect.Multimap; /** * Test various issues around serialization of Dictionaries and NounForms, along with invariant testing @@ -43,10 +46,10 @@ private void _testComplexGrammaticalFormInvariant(List list) throws Exception /** * Group of tests that verify that *all* noun forms, if they are Complex, * have the ordinal match the index into the list, or they are an enum. - * + * * These tests are broken up by language type to avoid test timeouts. * Standard languages are further divided since they were flapping. - * + * * Chances are you should run this again in the production environment, that's why there's * a test version so you don't have tests that run forever */ @@ -54,11 +57,11 @@ public void testDeclensionInvariants() throws Exception { declensionInvariantTester(LanguageProviderFactory.get().getAll()); } - + /** * Helper method for testDeclensionInvariant* test methods. - * - * @param langs - list of languages to test + * + * @param langs - list of languages to test * @throws Exception */ private void declensionInvariantTester(List langs) throws Exception { @@ -73,6 +76,43 @@ private void declensionInvariantTester(List langs) thro } } + @SuppressWarnings("unchecked") + private T getPrivateField(LanguageDictionary dict, String name) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + Field f = LanguageDictionary.class.getDeclaredField(name); + f.setAccessible(true); + + return (T) f.get(dict); + } + + // ensure noun is shared across internal maps + private void validateFields(LanguageDictionary dict) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final String SINGULAR = "account"; + final String PLUARL = "accounts"; + + Map nounMap = getPrivateField(dict, "nounMap"); + Multimap nounsByEntityType = getPrivateField(dict, "nounsByEntityType"); + + Noun expected = nounMap.get(SINGULAR); + + Noun actual = null; + for (Noun n : nounsByEntityType.get(SINGULAR)) { + if (SINGULAR.equals(n.getName())) { + actual = n; + break; + } + } + assertNotNull(actual); + assertSame(expected, actual); + + if (dict.getDeclension().hasPlural()) { + Map nounMapByPluralAlias = getPrivateField(dict, "nounMapByPluralAlias"); + actual = nounMapByPluralAlias.get(PLUARL); + assertNotNull(actual); + assertSame(expected, actual); + } + } + + @Test public void testSerializeDictionary() throws Exception { // TODO: This is slow, so only do the first ten for (HumanLanguage language : LanguageProviderFactory.get().getAll().subList(0, 10)) { @@ -94,14 +134,16 @@ public void testSerializeDictionary() throws Exception { start = System.nanoTime(); LanguageDictionary copy; - + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { copy = (LanguageDictionary)ois.readObject(); } logger.info("Read " + language + " dictionary in " + (System.nanoTime() - start)/1000000 + " msec"); + validateFields(copy); + // Make sure the noun forms are the same Noun copyNoun = copy.getNoun("account", false); - IdentityHashMap identityCopyValues = new IdentityHashMap(noun.getAllDefinedValues()); + IdentityHashMap identityCopyValues = new IdentityHashMap<>(noun.getAllDefinedValues()); assertEquals("Serialized nouns aren't the same", noun, copyNoun); assertSame("Serialized declensions are being duplicated", noun.getDeclension(), copyNoun.getDeclension());