diff options
| author | bjculkin <bjculkin@mix.wvu.edu> | 2017-04-04 16:02:16 -0400 |
|---|---|---|
| committer | bjculkin <bjculkin@mix.wvu.edu> | 2017-04-04 16:02:16 -0400 |
| commit | d6e49b472bcf023d86a7c236b57d6eef0ad9bb3f (patch) | |
| tree | a57822519fbdcbd58a4dfab26b44e092a88613bb /src | |
| parent | 3e20efa5625dd685e4989ff9f28833fd56cd962d (diff) | |
Implement bulk of inflector
Diffstat (limited to 'src')
| -rw-r--r-- | src/examples/java/bjc/inflexion/InflexionTester.java | 67 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/CategoricalNounInflection.java | 204 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/CompoundNounInflection.java | 170 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/DefaultNounInflection.java | 68 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/InflectionAffix.java | 56 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/InflectionAffixes.java | 74 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/InflectionException.java | 49 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/IrregularNounInflection.java | 197 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/Noun.java | 121 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/NounInflection.java | 113 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/Nouns.java | 183 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/Prepositions.java | 71 | ||||
| -rw-r--r-- | src/main/java/bjc/inflexion/v2/SimpleInflectionAffix.java | 103 |
13 files changed, 1476 insertions, 0 deletions
diff --git a/src/examples/java/bjc/inflexion/InflexionTester.java b/src/examples/java/bjc/inflexion/InflexionTester.java new file mode 100644 index 0000000..00ae3e2 --- /dev/null +++ b/src/examples/java/bjc/inflexion/InflexionTester.java @@ -0,0 +1,67 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion; + +import bjc.inflexion.v2.Noun; +import bjc.inflexion.v2.Nouns; +import bjc.inflexion.v2.Prepositions; + +import java.util.Scanner; + +/** + * Test inflecting words. + * + * @author EVE + * + */ +public class InflexionTester { + /** + * Main method. + * + * @param args + * Unused CLI args. + */ + public static void main(String[] args) { + Prepositions prepositionDB = new Prepositions(); + prepositionDB.loadFromStream(InflexionTester.class.getResourceAsStream("/prepositions.txt")); + + Nouns nounDB = new Nouns(); + nounDB.loadFromStream(InflexionTester.class.getResourceAsStream("/nouns.txt")); + + Scanner scn = new Scanner(System.in); + + System.out.print("Enter a noun to inflect (blank line to quit): "); + String ln = scn.nextLine().trim(); + + while(!ln.equals("")) { + System.out.println(); + + Noun noun = nounDB.getNoun(ln); + + if(noun == null) { + System.out.println("No inflection available for noun " + ln); + } else { + System.out.printf("Word: %s\nSingular: %s\nModern Plural: %s\tClassical Plural: %s\n\n", + ln, noun.singular(), noun.modernPlural(), noun.classicalPlural()); + } + + System.out.print("Enter a noun to inflect (blank line to quit: "); + ln = scn.nextLine().trim(); + } + + scn.close(); + } +}
\ No newline at end of file diff --git a/src/main/java/bjc/inflexion/v2/CategoricalNounInflection.java b/src/main/java/bjc/inflexion/v2/CategoricalNounInflection.java new file mode 100644 index 0000000..597b2a3 --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/CategoricalNounInflection.java @@ -0,0 +1,204 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +/** + * Implementation of {@link NounInflection} for nouns matched by a regular + * expression. + * + * @author EVE + * + */ +public class CategoricalNounInflection implements NounInflection { + private static final String TOSTRING_FMT = "CategoricalNounInflection [singular=%s, modernPlural=%s," + + " classicalPlural=%s]"; + + private InflectionAffix singular; + + private InflectionAffix modernPlural; + private InflectionAffix classicalPlural; + + private boolean preferClassical; + + /** + * Create a new categorical inflection. + * + * @param singlar + * The affix for the singular form. + * @param modrnPlural + * The affix for the modern plural. + * @param classiclPlural + * The affix for the classical plural. + * @param prefrClassical + * Whether or not the classical plural should be + * preferred. + */ + public CategoricalNounInflection(InflectionAffix singlar, InflectionAffix modrnPlural, + InflectionAffix classiclPlural, boolean prefrClassical) { + if(singlar == null) + throw new NullPointerException("Singular form must not be null"); + else if(modrnPlural == null && classiclPlural == null) + throw new NullPointerException("One of modern/classical plural forms must not be null"); + + singular = singlar; + modernPlural = modrnPlural; + classicalPlural = classiclPlural; + preferClassical = prefrClassical; + } + + @Override + public boolean matches(String noun) { + if(singular.hasAffix(noun)) + return true; + else if(modernPlural != null && modernPlural.hasAffix(noun)) + return true; + else if(classicalPlural != null && classicalPlural.hasAffix(noun)) + return true; + else + return false; + } + + @Override + public boolean isSingular(String noun) { + if(singular.hasAffix(noun)) + return true; + else if(matchesPlural(noun)) + return false; + else { + String msg = String.format("Noun '%s' doesn't belong to this inflection", noun, this); + + throw new InflectionException(msg); + } + } + + @Override + public boolean isPlural(String noun) { + if(singular.hasAffix(noun)) + return false; + else if(matchesPlural(noun)) + return true; + else { + String msg = String.format("Noun '%s' doesn't belong to this inflection", noun, this); + + throw new InflectionException(msg); + } + } + + @Override + public String singularize(String plural) { + if(singular.hasAffix(plural)) + return plural; + else if(modernPlural != null && modernPlural.hasAffix(plural)) + return singular.affix(modernPlural.deaffix(plural)); + else if(classicalPlural != null && classicalPlural.hasAffix(plural)) + return singular.affix(classicalPlural.deaffix(plural)); + else { + String msg = String.format("Noun '%s' doesn't belong to this inflection", plural, this); + + throw new InflectionException(msg); + } + } + + @Override + public String pluralize(String singlar) { + if(singular.hasAffix(singlar)) { + if(preferClassical) { + if(classicalPlural == null) { + return modernPlural.affix(singular.deaffix(singlar)); + } else { + return classicalPlural.affix(singular.deaffix(singlar)); + } + } else { + if(modernPlural == null) { + return classicalPlural.affix(singular.deaffix(singlar)); + } else { + return modernPlural.affix(singular.deaffix(singlar)); + } + } + } else if(matchesPlural(singlar)) { + return singlar; + } else { + String msg = String.format("Noun '%s' doesn't belong to this inflection", singlar, this); + + throw new InflectionException(msg); + } + } + + private boolean matchesPlural(String noun) { + boolean hasModernPlural = modernPlural != null && modernPlural.hasAffix(noun); + + return hasModernPlural || (classicalPlural != null && classicalPlural.hasAffix(noun)); + } + + @Override + public String toString() { + return String.format(TOSTRING_FMT, singular, modernPlural, classicalPlural); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + ((classicalPlural == null) ? 0 : classicalPlural.hashCode()); + result = prime * result + ((modernPlural == null) ? 0 : modernPlural.hashCode()); + + return result; + } + + @Override + public boolean equals(Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof CategoricalNounInflection)) return false; + + CategoricalNounInflection other = (CategoricalNounInflection) obj; + + if(classicalPlural == null) { + if(other.classicalPlural != null) return false; + } else if(!classicalPlural.equals(other.classicalPlural)) return false; + + if(modernPlural == null) { + if(other.modernPlural != null) return false; + } else if(!modernPlural.equals(other.modernPlural)) return false; + + return true; + } + + @Override + public String pluralizeModern(String singlar) { + if(modernPlural == null) return pluralizeClassical(singlar); + + String actSinglar = singlar; + if(isPlural(singlar)) { + actSinglar = singularize(singlar); + } + + return modernPlural.affix(singular.deaffix(actSinglar)); + } + + @Override + public String pluralizeClassical(String singlar) { + if(classicalPlural == null) return pluralizeModern(singlar); + + String actSinglar = singlar; + if(isPlural(singlar)) { + actSinglar = singularize(singlar); + } + + return classicalPlural.affix(singular.deaffix(actSinglar)); + } +}
\ No newline at end of file diff --git a/src/main/java/bjc/inflexion/v2/CompoundNounInflection.java b/src/main/java/bjc/inflexion/v2/CompoundNounInflection.java new file mode 100644 index 0000000..f82604e --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/CompoundNounInflection.java @@ -0,0 +1,170 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Implementation of {@link NounInflection} for words that don't inflect at the + * end. + * + * @author EVE + * + */ +public class CompoundNounInflection implements NounInflection { + /* + * Data stores for use. + */ + private Nouns nounDB; + private Prepositions prepositionDB; + + private Pattern compoundMatcher; + + private String singularPattern; + + private String modernPluralPattern; + private String classicalPluralPattern; + + private boolean preferClassical; + + /* + * Whether or not this inflection takes a preposition. + */ + private boolean hasPreposition; + + /** + * TODO fill in documentation. + * + * @param nounDB + * @param prepositionDB + * @param compoundMatcher + * @param singularPattern + * @param modernPluralPattern + * @param classicalPluralPattern + * @param preferClassical + * @param hasPreposition + */ + public CompoundNounInflection(Nouns nounDB, Prepositions prepositionDB, Pattern compoundMatcher, + String singularPattern, String modernPluralPattern, String classicalPluralPattern, + boolean preferClassical, boolean hasPreposition) { + this.nounDB = nounDB; + this.prepositionDB = prepositionDB; + this.compoundMatcher = compoundMatcher; + this.singularPattern = singularPattern; + this.modernPluralPattern = modernPluralPattern; + this.classicalPluralPattern = classicalPluralPattern; + this.preferClassical = preferClassical; + this.hasPreposition = hasPreposition; + } + + @Override + public boolean matches(String noun) { + Matcher matcher = compoundMatcher.matcher(noun); + + if(matcher.matches()) { + Noun actNoun = nounDB.getNoun(matcher.group("noun")); + + if(actNoun == null) return false; + + if(hasPreposition) { + return prepositionDB.isPreposition(matcher.group("preposition")); + } else + return true; + } else { + return false; + } + } + + @Override + public boolean isSingular(String noun) { + Matcher matcher = compoundMatcher.matcher(noun); + Noun actNoun = nounDB.getNoun(matcher.group("noun")); + + return actNoun.isSingular(); + } + + @Override + public boolean isPlural(String noun) { + Matcher matcher = compoundMatcher.matcher(noun); + Noun actNoun = nounDB.getNoun(matcher.group("noun")); + + return actNoun.isPlural(); + } + + @Override + public String singularize(String plural) { + Matcher matcher = compoundMatcher.matcher(plural); + Noun actNoun = nounDB.getNoun(matcher.group("noun")); + + if(hasPreposition) { + return String.format(singularPattern, actNoun.singular(), matcher.group("preposition")); + } else { + return String.format(singularPattern, actNoun.singular()); + } + } + + @Override + public String pluralize(String singular) { + Matcher matcher = compoundMatcher.matcher(singular); + Noun actNoun = getNoun(matcher); + + /* + * TODO adapt this to take preferClassical into account. + */ + if(hasPreposition) { + return String.format(modernPluralPattern, actNoun.plural(), matcher.group("preposition")); + } else { + return String.format(modernPluralPattern, actNoun.plural()); + } + } + + @Override + public String pluralizeModern(String singular) { + if(modernPluralPattern == null) return pluralizeClassical(singular); + + Matcher matcher = compoundMatcher.matcher(singular); + Noun actNoun = getNoun(matcher); + + if(hasPreposition) { + return String.format(modernPluralPattern, actNoun.modernPlural(), matcher.group("preposition")); + } else { + return String.format(modernPluralPattern, actNoun.modernPlural()); + } + } + + @Override + public String pluralizeClassical(String singular) { + if(modernPluralPattern == null) return pluralizeModern(singular); + + Matcher matcher = compoundMatcher.matcher(singular); + Noun actNoun = getNoun(matcher); + + if(hasPreposition) { + return String.format(classicalPluralPattern, actNoun.classicalPlural(), + matcher.group("preposition")); + } else { + return String.format(classicalPluralPattern, actNoun.classicalPlural()); + } + } + + private Noun getNoun(Matcher matcher) { + matcher.matches(); + + Noun actNoun = nounDB.getNoun(matcher.group("noun")); + return actNoun; + } +}
\ No newline at end of file diff --git a/src/main/java/bjc/inflexion/v2/DefaultNounInflection.java b/src/main/java/bjc/inflexion/v2/DefaultNounInflection.java new file mode 100644 index 0000000..7c5a467 --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/DefaultNounInflection.java @@ -0,0 +1,68 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +/** + * Default noun inflection for english nouns. + * + * @author EVE + * + */ +public class DefaultNounInflection implements NounInflection { + @Override + public boolean matches(String noun) { + return true; + } + + @Override + public boolean isSingular(String noun) { + return !noun.endsWith("s"); + } + + @Override + public boolean isPlural(String noun) { + return noun.endsWith("s"); + } + + @Override + public String singularize(String plural) { + if(plural.endsWith("ses")) { + return plural.substring(0, plural.length() - 3); + } else if(plural.endsWith("s")) { + return plural.substring(0, plural.length() - 1); + } else + return plural; + } + + @Override + public String pluralize(String singular) { + if(singular.endsWith("s")) { + return singular + "es"; + } + + return singular + "s"; + } + + @Override + public String pluralizeModern(String singular) { + return pluralize(singular); + } + + @Override + public String pluralizeClassical(String singular) { + return pluralize(singular); + } +}
\ No newline at end of file diff --git a/src/main/java/bjc/inflexion/v2/InflectionAffix.java b/src/main/java/bjc/inflexion/v2/InflectionAffix.java new file mode 100644 index 0000000..a9c583d --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/InflectionAffix.java @@ -0,0 +1,56 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +/** + * An affix attached to a word and used for inflection. + * + * @author EVE + * + */ +public interface InflectionAffix { + + /** + * Check if a word has this affix. + * + * @param word + * The word to check. + * + * @return Whether or not the word has the affix. + */ + boolean hasAffix(String word); + + /** + * Remove the affix from a word. + * + * @param word + * The word to remove the affix from. + * + * @return The word with the affix removed. + */ + String deaffix(String word); + + /** + * Apply this affix to a word. + * + * @param word + * The word to apply the affix to. + * + * @return The word with the affix applied. + */ + String affix(String word); + +}
\ No newline at end of file diff --git a/src/main/java/bjc/inflexion/v2/InflectionAffixes.java b/src/main/java/bjc/inflexion/v2/InflectionAffixes.java new file mode 100644 index 0000000..b47af00 --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/InflectionAffixes.java @@ -0,0 +1,74 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +import java.util.regex.Pattern; + +/** + * Utility methods for constructing inflection affixes. + * + * @author EVE + * + */ +public class InflectionAffixes { + /* + * Template for 'complete' affix patterns. + * + * Match the start of the word, followed by zero or more word + * characters, followed by the suffix, then the end of the string. + * + * The word is in a capturing group named 'stem'. + */ + private static final String COMPLETE_PATT_FMT = "^(?<stem>\\w*)%s$"; + + /* + * Template for 'incomplete' affix patterns. + * + * Match the start of the word, followed by one or more word characters, + * followed by the suffix, then the end of the string. + * + * The word is in a capturing group named 'stem'. + */ + private static final String INCOMPLETE_PATT_FMT = "^(?<stem>\\w+)%s$"; + + /** + * Create an affix that's a word by itself. + * + * @param suffix + * The suffix to use. + * + * @return A affix that represents the suffix. + */ + public static InflectionAffix complete(String suffix) { + Pattern patt = Pattern.compile(String.format(COMPLETE_PATT_FMT, suffix)); + + return new SimpleInflectionAffix("%s" + suffix, patt); + } + + /** + * Create an affix that's not a word by itself. + * + * @param suffix + * The suffix to use. + * + * @return An affix that represents the suffix. + */ + public static InflectionAffix incomplete(String suffix) { + Pattern patt = Pattern.compile(String.format(INCOMPLETE_PATT_FMT, suffix)); + + return new SimpleInflectionAffix("%s" + suffix, patt); + } +} diff --git a/src/main/java/bjc/inflexion/v2/InflectionException.java b/src/main/java/bjc/inflexion/v2/InflectionException.java new file mode 100644 index 0000000..5974018 --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/InflectionException.java @@ -0,0 +1,49 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +/** + * Exception thrown when something goes wrong with inflection. + * + * @author EVE + * + */ +public class InflectionException extends RuntimeException { + private static final long serialVersionUID = 5680541587449153748L; + + /** + * Create a new inflection exception with the given message and cause. + * + * @param message + * The message of the exception. + * + * @param cause + * The cause of the exception. + */ + public InflectionException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Create a new inflection exception with the given message. + * + * @param message + * The message of the exception. + */ + public InflectionException(String message) { + super(message); + } +} diff --git a/src/main/java/bjc/inflexion/v2/IrregularNounInflection.java b/src/main/java/bjc/inflexion/v2/IrregularNounInflection.java new file mode 100644 index 0000000..294c6a3 --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/IrregularNounInflection.java @@ -0,0 +1,197 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +/** + * Implementation of {@link NounInflection} for irregular nouns. + * + * @author EVE + * + */ +public class IrregularNounInflection implements NounInflection { + private static final String TOSTRING_FMT = "IrregularNounInflection [singular=%s, modernPlural=%s," + + " classicalPlural=%s, preferClassical=%s]"; + + private String singular; + + private String modernPlural; + private String classicalPlural; + + private boolean preferClassical; + + /** + * Create a new irregular noun inflection. + * + * @param singlar + * The singular form of the noun. + * + * @param modrnPlural + * The modern plural of the noun. + * + * @param classiclPlural + * The classical plural of the noun. + * + * @param prefrClassical + * Whether the classical form should be preferred if it + * is available. + */ + public IrregularNounInflection(String singlar, String modrnPlural, String classiclPlural, + boolean prefrClassical) { + if(singlar == null) throw new NullPointerException("Singular form must not be null"); + if(modrnPlural == null && classiclPlural == null) + throw new NullPointerException("One of modern/classical plural forms must not be null"); + + singular = singlar; + modernPlural = modrnPlural; + classicalPlural = classiclPlural; + preferClassical = prefrClassical; + } + + @Override + public boolean matches(String noun) { + if(noun.equalsIgnoreCase(singular)) + return true; + else if(noun.equalsIgnoreCase(modernPlural)) + return true; + else if(noun.equalsIgnoreCase(classicalPlural)) + return true; + else + return false; + } + + @Override + public boolean isSingular(String noun) { + if(noun.equalsIgnoreCase(singular)) + return true; + else if(matchesPlural(noun)) + return false; + else { + String msg = String.format("Noun '%s' doesn't belong to this inflection '%s'", noun, this); + + throw new InflectionException(msg); + } + } + + @Override + public boolean isPlural(String noun) { + if(noun.equalsIgnoreCase(singular)) + return false; + else if(matchesPlural(noun)) + return true; + else { + String msg = String.format("Noun '%s' doesn't belong to this inflection '%s'", noun, this); + + throw new InflectionException(msg); + } + } + + @Override + public String singularize(String plural) { + if(plural.equalsIgnoreCase(singular)) + return singular; + else if(matchesPlural(plural)) + return singular; + else { + String msg = String.format("Noun '%s' doesn't belong to this inflection '%s'", plural, this); + + throw new InflectionException(msg); + } + } + + @Override + public String pluralize(String singular) { + if(singular.equalsIgnoreCase(singular)) + return getPlural(); + else if(matchesPlural(singular)) + return getPlural(); + else { + String msg = String.format("Noun '%s' doesn't belong to this inflection '%s'", singular, this); + + throw new InflectionException(msg); + } + } + + private String getPlural() { + if(preferClassical) { + if(classicalPlural == null) + return modernPlural; + else + return classicalPlural; + } else if(modernPlural == null) { + return classicalPlural; + } else { + return modernPlural; + } + } + + private boolean matchesPlural(String noun) { + return noun.equalsIgnoreCase(modernPlural) || noun.equalsIgnoreCase(classicalPlural); + } + + @Override + public String toString() { + return String.format(TOSTRING_FMT, singular, modernPlural, classicalPlural, preferClassical); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + ((classicalPlural == null) ? 0 : classicalPlural.hashCode()); + result = prime * result + ((modernPlural == null) ? 0 : modernPlural.hashCode()); + result = prime * result + ((singular == null) ? 0 : singular.hashCode()); + + return result; + } + + @Override + public boolean equals(Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof IrregularNounInflection)) return false; + + IrregularNounInflection other = (IrregularNounInflection) obj; + + if(singular == null) { + if(other.singular != null) return false; + } else if(!singular.equals(other.singular)) return false; + + if(classicalPlural == null) { + if(other.classicalPlural != null) return false; + } else if(!classicalPlural.equals(other.classicalPlural)) return false; + + if(modernPlural == null) { + if(other.modernPlural != null) return false; + } else if(!modernPlural.equals(other.modernPlural)) return false; + + return true; + } + + @Override + public String pluralizeModern(String singular) { + if(modernPlural == null) return pluralizeClassical(singular); + + return modernPlural; + } + + @Override + public String pluralizeClassical(String singular) { + if(classicalPlural == null) return pluralizeModern(singular); + + return classicalPlural; + } +}
\ No newline at end of file diff --git a/src/main/java/bjc/inflexion/v2/Noun.java b/src/main/java/bjc/inflexion/v2/Noun.java new file mode 100644 index 0000000..bf74613 --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/Noun.java @@ -0,0 +1,121 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +/** + * A noun attached to an inflection. + * + * @author EVE + * + */ +public class Noun { + private static final String TOSTRING_FMT = "Noun [word=%s, inflection=%s]"; + + private final String word; + + private final NounInflection inflection; + + /** + * Create a new noun from a word and inflection. + * + * @param wrd + * The word for the noun. + * + * @param inflction + * The inflection for the word. + */ + public Noun(String wrd, NounInflection inflction) { + word = wrd; + inflection = inflction; + } + + /** + * Get the input noun. + * + * @return The noun, as input. + */ + public String getWord() { + return word; + } + + /** + * Get the inflection for this noun. + * + * @return The inflection for this noun. + */ + public NounInflection getInflection() { + return inflection; + } + + /** + * Check if this noun is singular. + * + * @return Whether or not the noun is singular. + */ + public boolean isSingular() { + return inflection.isSingular(word); + } + + /** + * Check if this noun is plural. + * + * @return Whether or not this noun is plural. + */ + public boolean isPlural() { + return inflection.isPlural(word); + } + + /** + * Get the singular form of this noun. + * + * @return The singular form of this noun. + */ + public String singular() { + return inflection.singularize(word); + } + + /** + * Get the plural form of this noun. + * + * @return The plural form of this noun. + */ + public String plural() { + return inflection.pluralize(word); + } + + @Override + public String toString() { + return String.format(TOSTRING_FMT, word, inflection); + } + + /** + * Get the modern plural form of this noun. + * + * @return The modern plural form of this noun. + */ + public String modernPlural() { + return inflection.pluralizeModern(word); + } + + /** + * Get the classical plural form of this noun. + * + * @return The classical plural form of this noun. + */ + public String classicalPlural() { + return inflection.pluralizeClassical(word); + } +}
\ No newline at end of file diff --git a/src/main/java/bjc/inflexion/v2/NounInflection.java b/src/main/java/bjc/inflexion/v2/NounInflection.java new file mode 100644 index 0000000..2a4049f --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/NounInflection.java @@ -0,0 +1,113 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +/** + * Interface for inflecting nouns. + * + * @author EVE + * + */ +public interface NounInflection { + /** + * Check if a noun matches this inflection. + * + * @param noun + * The noun to check on this inflection. + * + * @return Whether or not the noun belongs to the inflection. + */ + public boolean matches(String noun); + + /** + * Check if a noun for this inflection is singular or not. + * + * @param noun + * The noun to check for singularity. + * + * @return Whether or not the noun is singular. + * + * @throws InflectionException + * If the noun isn't part of this inflection. + */ + public boolean isSingular(String noun); + + /** + * Check if a noun for this inflection is plural or not. + * + * @param noun + * The noun to check for plurality. + * + * @return Whether or not the noun is plural. + * + * @throws InflectionException + * If the noun isn't part of this inflection. + */ + public boolean isPlural(String noun); + + /** + * Convert a singular noun to a plural noun. + * + * @param plural + * The plural noun to inflect to a singular form. + * + * @return The singular form of the noun. + * + * @throws InflectionException + * If the noun isn't part of the inflection. + */ + public String singularize(String plural); + + /** + * Convert a singular noun to a plural noun. + * + * @param singular + * The singular noun to inflect to a plural form. + * + * @return The plural form of the noun. + * + * @throws InflectionException + * If the noun isn't part of the inflection. + */ + public String pluralize(String singular); + + /** + * Convert a singular noun to a modern plural noun. + * + * @param singular + * The singular noun to inflect to a modern plural form. + * + * @return The modern plural form of the noun. + * + * @throws InflectionException + * If the noun isn't part of the inflection. + */ + public String pluralizeModern(String singular); + + /** + * Convert a singular noun to a classical plural noun. + * + * @param singular + * The singular noun to inflect to a classical plural + * form. + * + * @return The classical plural form of the noun. + * + * @throws InflectionException + * If the noun isn't part of the inflection. + */ + public String pluralizeClassical(String singular); +}
\ No newline at end of file diff --git a/src/main/java/bjc/inflexion/v2/Nouns.java b/src/main/java/bjc/inflexion/v2/Nouns.java new file mode 100644 index 0000000..6645d5c --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/Nouns.java @@ -0,0 +1,183 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.regex.Pattern; + +import static bjc.inflexion.v2.InflectionAffixes.*; + +/** + * @author EVE + * + */ +public class Nouns { + private static final DefaultNounInflection DEFAULT_INFLECTION = new DefaultNounInflection(); + + private Map<String, NounInflection> userIrregulars; + private List<NounInflection> userInflections; + + private Map<String, NounInflection> predefinedIrregulars; + private List<NounInflection> predefinedInflections; + + /** + * Create a new empty noun DB. + */ + public Nouns() { + userIrregulars = new HashMap<>(); + userInflections = new LinkedList<>(); + + predefinedIrregulars = new HashMap<>(); + predefinedInflections = new LinkedList<>(); + } + + /** + * Retrieve a noun with its inflection from the database of inflections. + * + * @param noun + * The noun to retrieve. + * + * @return The noun with its inflection. + * + * @throws InflectionException + * If the noun matched no inflection. + */ + public Noun getNoun(String noun) { + if(userIrregulars.containsKey(noun)) return new Noun(noun, userIrregulars.get(noun)); + for(NounInflection inflect : userInflections) { + if(inflect.matches(noun)) return new Noun(noun, inflect); + } + + if(predefinedIrregulars.containsKey(noun)) return new Noun(noun, predefinedIrregulars.get(noun)); + for(NounInflection inflect : predefinedInflections) { + if(inflect.matches(noun)) return new Noun(noun, inflect); + } + + return new Noun(noun, DEFAULT_INFLECTION); + } + + /** + * Load the contents of the stream into this DB. + * + * @param stream + * The stream to load from. + */ + public void loadFromStream(InputStream stream) { + try(Scanner scn = new Scanner(stream)) { + while(scn.hasNextLine()) { + String ln = scn.nextLine().trim(); + + /* + * Ignore comments and blank lines. + */ + if(ln.startsWith("#")) continue; + if(ln.equals("")) continue; + + handleLine(ln); + } + } + } + + private void handleLine(String ln) { + String[] parts = ln.split(Pattern.quote("=>")); + + if(parts.length != 2) { + String msg = String.format("Improperly formatted noun defn '%s'", ln); + + throw new InflectionException(msg); + } + + String singular = parts[0].trim(); + String plural = parts[1].trim(); + + String modernPlural = ""; + String classicalPlural = ""; + + if(plural.contains("|")) { + String[] plurals = plural.split(Pattern.quote("|")); + + if(plurals.length == 1) { + modernPlural = plurals[0].trim(); + } else { + modernPlural = plurals[0].trim(); + classicalPlural = plurals[1].trim(); + } + + if(modernPlural.equals("")) modernPlural = null; + if(classicalPlural.equals("")) classicalPlural = null; + } else { + modernPlural = plural; + classicalPlural = null; + } + + if(singular.startsWith("*")) { + handleCompletePlural(singular, modernPlural, classicalPlural); + } else if(singular.startsWith("-")) { + handleIncompletePlural(singular, modernPlural, classicalPlural); + } else { + handleIrregularPlural(singular, modernPlural, classicalPlural); + } + } + + private void handleIncompletePlural(String singular, String modernPlural, String classicalPlural) { + InflectionAffix singularAffix = incomplete(singular.substring(1)); + + InflectionAffix modernAffix = null; + InflectionAffix classicalAffix = null; + + if(modernPlural != null) modernAffix = incomplete(modernPlural.substring(1)); + if(classicalPlural != null) classicalAffix = incomplete(classicalPlural.substring(1)); + + CategoricalNounInflection inflection = new CategoricalNounInflection(singularAffix, modernAffix, + classicalAffix, false); + + predefinedInflections.add(inflection); + } + + private void handleCompletePlural(String singular, String modernPlural, String classicalPlural) { + InflectionAffix singularAffix = complete(singular.substring(1)); + + InflectionAffix modernAffix = null; + InflectionAffix classicalAffix = null; + + if(modernPlural != null) modernAffix = complete(modernPlural.substring(1)); + if(classicalPlural != null) classicalAffix = complete(classicalPlural.substring(1)); + + CategoricalNounInflection inflection = new CategoricalNounInflection(singularAffix, modernAffix, + classicalAffix, false); + + predefinedInflections.add(inflection); + } + + private void handleIrregularPlural(String singular, String modernPlural, String classicalPlural) { + IrregularNounInflection inflection = new IrregularNounInflection(singular, modernPlural, + classicalPlural, false); + + if(!predefinedIrregulars.containsKey(singular)) { + predefinedIrregulars.put(singular, inflection); + } + + if(modernPlural != null && !predefinedIrregulars.containsKey(modernPlural)) + predefinedIrregulars.put(modernPlural, inflection); + if(classicalPlural != null && !predefinedIrregulars.containsKey(classicalPlural)) + predefinedIrregulars.put(classicalPlural, inflection); + } +}
\ No newline at end of file diff --git a/src/main/java/bjc/inflexion/v2/Prepositions.java b/src/main/java/bjc/inflexion/v2/Prepositions.java new file mode 100644 index 0000000..042ab8a --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/Prepositions.java @@ -0,0 +1,71 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +import java.io.InputStream; +import java.util.HashSet; +import java.util.Scanner; +import java.util.Set; + +/** + * List of prepositions. + * + * @author EVE + * + */ +public class Prepositions { + private Set<String> prepositions; + + /** + * Create an empty preposition DB. + */ + public Prepositions() { + prepositions = new HashSet<>(); + } + + /** + * Check if a word is a preposition. + * + * @param word + * The word as a preposition. + * + * @return Whether or not the word is a preposition. + */ + public boolean isPreposition(String word) { + return prepositions.contains(word); + } + + /** + * Load the contents of the stream into this DB. + * + * @param stream + * The stream to load from. + */ + public void loadFromStream(InputStream stream) { + try(Scanner scn = new Scanner(stream)) { + while(scn.hasNextLine()) { + String ln = scn.nextLine().trim(); + + /* + * Ignore comments + */ + if(ln.startsWith("#")) continue; + + prepositions.add(ln); + } + } + } +}
\ No newline at end of file diff --git a/src/main/java/bjc/inflexion/v2/SimpleInflectionAffix.java b/src/main/java/bjc/inflexion/v2/SimpleInflectionAffix.java new file mode 100644 index 0000000..623c93c --- /dev/null +++ b/src/main/java/bjc/inflexion/v2/SimpleInflectionAffix.java @@ -0,0 +1,103 @@ +/** + * (C) Copyright 2017 Benjamin Culkin. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bjc.inflexion.v2; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Simple implementation of {@link InflectionAffix} + * + * @author EVE + * + */ +public class SimpleInflectionAffix implements InflectionAffix { + private static final String TOSTRING_FMT = "SimpleInflectionAffix [affixTemplate=%s, affixMatcher=%s]"; + + private String affixTemplate; + + private Pattern affixMatcher; + + /** + * Create a new inflection affix. + * + * @param affixTemplate + * The template for applying the affix, Should be a + * printf-style format string with a single string blank. + * + * @param affixMatcher + * The regular expression that matches the affix on + * strings. The 'stem' or word should be placed in a + * named capturing group named 'stem'. + */ + public SimpleInflectionAffix(String affixTemplate, Pattern affixMatcher) { + this.affixTemplate = affixTemplate; + this.affixMatcher = affixMatcher; + } + + @Override + public boolean hasAffix(String word) { + return affixMatcher.matcher(word).matches(); + } + + @Override + public String deaffix(String word) { + Matcher matcher = affixMatcher.matcher(word); + matcher.matches(); + + return matcher.group("stem"); + } + + @Override + public String affix(String word) { + return String.format(affixTemplate, word); + } + + @Override + public String toString() { + return String.format(TOSTRING_FMT, affixTemplate, affixMatcher); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + ((affixMatcher == null) ? 0 : affixMatcher.hashCode()); + result = prime * result + ((affixTemplate == null) ? 0 : affixTemplate.hashCode()); + + return result; + } + + @Override + public boolean equals(Object obj) { + if(this == obj) return true; + if(obj == null) return false; + if(!(obj instanceof SimpleInflectionAffix)) return false; + + SimpleInflectionAffix other = (SimpleInflectionAffix) obj; + + if(affixTemplate == null) { + if(other.affixTemplate != null) return false; + } else if(!affixTemplate.equals(other.affixTemplate)) return false; + + if(affixMatcher == null) { + if(other.affixMatcher != null) return false; + } else if(!affixMatcher.equals(other.affixMatcher)) return false; + + return true; + } +}
\ No newline at end of file |
