diff options
-rw-r--r-- | src/core/lombok/Singular.java | 38 | ||||
-rw-r--r-- | src/core/lombok/core/TypeLibrary.java | 9 | ||||
-rw-r--r-- | src/core/lombok/core/handlers/HandlerUtil.java | 13 | ||||
-rw-r--r-- | src/core/lombok/core/handlers/Singulars.java | 86 | ||||
-rw-r--r-- | src/core/lombok/core/handlers/singulars.txt | 54 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacNode.java | 48 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/JavacHandlerUtil.java | 21 | ||||
-rw-r--r-- | test/core/src/lombok/RunAllTests.java | 4 | ||||
-rw-r--r-- | test/core/src/lombok/core/RunCoreTests.java | 31 | ||||
-rw-r--r-- | test/core/src/lombok/core/TestSingulars.java | 52 |
10 files changed, 319 insertions, 37 deletions
diff --git a/src/core/lombok/Singular.java b/src/core/lombok/Singular.java new file mode 100644 index 00000000..7f22b008 --- /dev/null +++ b/src/core/lombok/Singular.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2013-2014 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * The singular annotation is used together with {@code @Builder} to create single element 'add' methods in the builder for collections. + * <p> + */ +@Target({FIELD, PARAMETER}) +@Retention(SOURCE) +public @interface Singular { + String value() default ""; +} diff --git a/src/core/lombok/core/TypeLibrary.java b/src/core/lombok/core/TypeLibrary.java index c0e9dc43..dc557c47 100644 --- a/src/core/lombok/core/TypeLibrary.java +++ b/src/core/lombok/core/TypeLibrary.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2015 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,6 +37,7 @@ import java.util.Map; public class TypeLibrary { private final Map<String, String> unqualifiedToQualifiedMap; private final String unqualified, qualified; + private boolean locked; public TypeLibrary() { unqualifiedToQualifiedMap = new HashMap<String, String>(); @@ -44,6 +45,10 @@ public class TypeLibrary { qualified = null; } + public void lock() { + this.locked = true; + } + private TypeLibrary(String fqnSingleton) { unqualifiedToQualifiedMap = null; qualified = fqnSingleton; @@ -53,6 +58,7 @@ public class TypeLibrary { } else { unqualified = fqnSingleton.substring(idx + 1); } + locked = true; } public static TypeLibrary createLibraryForSingleType(String fqnSingleton) { @@ -65,6 +71,7 @@ public class TypeLibrary { * @param fullyQualifiedTypeName the FQN type name, such as 'java.lang.String'. */ public void addType(String fullyQualifiedTypeName) { + if (locked) throw new IllegalStateException("locked"); fullyQualifiedTypeName = fullyQualifiedTypeName.replace("$", "."); int idx = fullyQualifiedTypeName.lastIndexOf('.'); if (idx == -1) throw new IllegalArgumentException( diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index 621d8760..87462921 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 The Project Lombok Authors. + * Copyright (C) 2013-2015 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -87,6 +87,9 @@ public class HandlerUtil { return true; } + public static String autoSingularize(String plural) { + return Singulars.autoSingularize(plural); + } public static void handleFlagUsage(LombokNode<?, ?, ?> node, ConfigurationKey<FlagUsageType> key, String featureName) { FlagUsageType fut = node.getAst().readConfiguration(key); @@ -303,7 +306,7 @@ public class HandlerUtil { return booleanPrefix + fName.substring(2); } - return buildName(isBoolean ? booleanPrefix : normalPrefix, fName); + return buildAccessorName(isBoolean ? booleanPrefix : normalPrefix, fName); } /** @@ -375,8 +378,8 @@ public class HandlerUtil { if (adhereToFluent && fluent) { names.add(baseName); } else { - names.add(buildName(normalPrefix, baseName)); - if (!normalPrefix.equals(booleanPrefix)) names.add(buildName(booleanPrefix, baseName)); + names.add(buildAccessorName(normalPrefix, baseName)); + if (!normalPrefix.equals(booleanPrefix)) names.add(buildAccessorName(booleanPrefix, baseName)); } } @@ -407,7 +410,7 @@ public class HandlerUtil { * @param suffix Something like {@code running}. * @return prefix + smartly title-cased suffix. For example, {@code setRunning}. */ - private static String buildName(String prefix, String suffix) { + public static String buildAccessorName(String prefix, String suffix) { if (suffix.length() == 0) return prefix; if (prefix.length() == 0) return suffix; diff --git a/src/core/lombok/core/handlers/Singulars.java b/src/core/lombok/core/handlers/Singulars.java new file mode 100644 index 00000000..87895790 --- /dev/null +++ b/src/core/lombok/core/handlers/Singulars.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.core.handlers; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +public class Singulars { + private static final List<String> SINGULAR_STORE; // intended to be immutable. + + static { + SINGULAR_STORE = new ArrayList<String>(); + + try { + InputStream in = Singulars.class.getResourceAsStream("singulars.txt"); + try { + BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8")); + for (String line = br.readLine(); line != null; line = br.readLine()) { + line = line.trim(); + if (line.startsWith("#") || line.isEmpty()) continue; + if (line.endsWith(" =")) { + SINGULAR_STORE.add(line.substring(0, line.length() - 2)); + SINGULAR_STORE.add(""); + continue; + } + + int idx = line.indexOf(" = "); + SINGULAR_STORE.add(line.substring(0, idx)); + SINGULAR_STORE.add(line.substring(idx + 3)); + } + } finally { + try { + in.close(); + } catch (Throwable ignore) {} + } + } catch (IOException e) { + SINGULAR_STORE.clear(); + } + } + + public static String autoSingularize(String in) { + final int inLen = in.length(); + for (int i = 0; i < SINGULAR_STORE.size(); i+= 2) { + final String lastPart = SINGULAR_STORE.get(i); + final boolean wholeWord = Character.isUpperCase(lastPart.charAt(0)); + final int endingOnly = lastPart.charAt(0) == '-' ? 1 : 0; + final int len = lastPart.length(); + if (inLen < len) continue; + if (!in.regionMatches(true, inLen - len + endingOnly, lastPart, endingOnly, len - endingOnly)) continue; + if (wholeWord && inLen != len && !Character.isUpperCase(in.charAt(inLen - len))) continue; + + String replacement = SINGULAR_STORE.get(i + 1); + if (replacement.equals("!")) return null; + + boolean capitalizeFirst = !replacement.isEmpty() && Character.isUpperCase(in.charAt(inLen - len + endingOnly)); + String pre = in.substring(0, inLen - len + endingOnly); + String post = capitalizeFirst ? Character.toUpperCase(replacement.charAt(0)) + replacement.substring(1) : replacement; + return pre + post; + } + + return null; + } +} diff --git a/src/core/lombok/core/handlers/singulars.txt b/src/core/lombok/core/handlers/singulars.txt new file mode 100644 index 00000000..33ef5629 --- /dev/null +++ b/src/core/lombok/core/handlers/singulars.txt @@ -0,0 +1,54 @@ +#Based on https://github.com/rails/rails/blob/efff6c1fd4b9e2e4c9f705a45879373cb34a5b0e/activesupport/lib/active_support/inflections.rb + +quizzes = quiz +matrices = matrix +indices = index +vertices = vertex +statuses = status +aliases = alias +alias = ! +species = ! +Axes = axis +-axes = axe +sexes = sex +Testes = testis +movies = movie +octopodes = octopus +buses = bus +Mice = mouse +Lice = louse +News = ! +# We could add more detail (axemen, boatsmen, boogymen, cavemen, gentlemen, etc, but (A) there's stuff like 'cerumen', and (B) the 'men' ending is common in singulars and other languages.) +# Therefore, the odds of a mistake are too high, so other than these 2 well known cases, so force the explicit singular. +Men = man +Women = woman +minutiae = minutia +shoes = shoe +synopses = synopsis +prognoses = prognosis +theses = thesis +diagnoses = diagnosis +bases = base +analyses = analysis +Crises = crisis +children = child +moves = move +zombies = zombie +-quies = quy +-us = ! +-is = ! +series = ! +-ies = y +-oes = o +hives = hive +-tives = tive +-sses = ss +-ches = ch +-xes = x +-shes = sh +-lves = lf +-rves = rf +-ves = fe +-ss = ! +-us = ! +-s = diff --git a/src/core/lombok/javac/JavacNode.java b/src/core/lombok/javac/JavacNode.java index 6eef36eb..727692ac 100644 --- a/src/core/lombok/javac/JavacNode.java +++ b/src/core/lombok/javac/JavacNode.java @@ -66,40 +66,40 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre public void traverse(JavacASTVisitor visitor) { switch (this.getKind()) { case COMPILATION_UNIT: - visitor.visitCompilationUnit(this, (JCCompilationUnit)get()); + visitor.visitCompilationUnit(this, (JCCompilationUnit) get()); ast.traverseChildren(visitor, this); - visitor.endVisitCompilationUnit(this, (JCCompilationUnit)get()); + visitor.endVisitCompilationUnit(this, (JCCompilationUnit) get()); break; case TYPE: - visitor.visitType(this, (JCClassDecl)get()); + visitor.visitType(this, (JCClassDecl) get()); ast.traverseChildren(visitor, this); - visitor.endVisitType(this, (JCClassDecl)get()); + visitor.endVisitType(this, (JCClassDecl) get()); break; case FIELD: - visitor.visitField(this, (JCVariableDecl)get()); + visitor.visitField(this, (JCVariableDecl) get()); ast.traverseChildren(visitor, this); - visitor.endVisitField(this, (JCVariableDecl)get()); + visitor.endVisitField(this, (JCVariableDecl) get()); break; case METHOD: - visitor.visitMethod(this, (JCMethodDecl)get()); + visitor.visitMethod(this, (JCMethodDecl) get()); ast.traverseChildren(visitor, this); - visitor.endVisitMethod(this, (JCMethodDecl)get()); + visitor.endVisitMethod(this, (JCMethodDecl) get()); break; case INITIALIZER: - visitor.visitInitializer(this, (JCBlock)get()); + visitor.visitInitializer(this, (JCBlock) get()); ast.traverseChildren(visitor, this); - visitor.endVisitInitializer(this, (JCBlock)get()); + visitor.endVisitInitializer(this, (JCBlock) get()); break; case ARGUMENT: JCMethodDecl parentMethod = (JCMethodDecl) up().get(); - visitor.visitMethodArgument(this, (JCVariableDecl)get(), parentMethod); + visitor.visitMethodArgument(this, (JCVariableDecl) get(), parentMethod); ast.traverseChildren(visitor, this); - visitor.endVisitMethodArgument(this, (JCVariableDecl)get(), parentMethod); + visitor.endVisitMethodArgument(this, (JCVariableDecl) get(), parentMethod); break; case LOCAL: - visitor.visitLocal(this, (JCVariableDecl)get()); + visitor.visitLocal(this, (JCVariableDecl) get()); ast.traverseChildren(visitor, this); - visitor.endVisitLocal(this, (JCVariableDecl)get()); + visitor.endVisitLocal(this, (JCVariableDecl) get()); break; case STATEMENT: visitor.visitStatement(this, get()); @@ -109,21 +109,21 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre case ANNOTATION: switch (up().getKind()) { case TYPE: - visitor.visitAnnotationOnType((JCClassDecl)up().get(), this, (JCAnnotation)get()); + visitor.visitAnnotationOnType((JCClassDecl) up().get(), this, (JCAnnotation) get()); break; case FIELD: - visitor.visitAnnotationOnField((JCVariableDecl)up().get(), this, (JCAnnotation)get()); + visitor.visitAnnotationOnField((JCVariableDecl) up().get(), this, (JCAnnotation) get()); break; case METHOD: - visitor.visitAnnotationOnMethod((JCMethodDecl)up().get(), this, (JCAnnotation)get()); + visitor.visitAnnotationOnMethod((JCMethodDecl) up().get(), this, (JCAnnotation) get()); break; case ARGUMENT: - JCVariableDecl argument = (JCVariableDecl)up().get(); - JCMethodDecl method = (JCMethodDecl)up().up().get(); - visitor.visitAnnotationOnMethodArgument(argument, method, this, (JCAnnotation)get()); + JCVariableDecl argument = (JCVariableDecl) up().get(); + JCMethodDecl method = (JCMethodDecl) up().up().get(); + visitor.visitAnnotationOnMethodArgument(argument, method, this, (JCAnnotation) get()); break; case LOCAL: - visitor.visitAnnotationOnLocal((JCVariableDecl)up().get(), this, (JCAnnotation)get()); + visitor.visitAnnotationOnLocal((JCVariableDecl) up().get(), this, (JCAnnotation) get()); break; default: throw new AssertionError("Annotion not expected as child of a " + up().getKind()); @@ -138,9 +138,9 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre @Override public String getName() { final Name n; - if (node instanceof JCClassDecl) n = ((JCClassDecl)node).name; - else if (node instanceof JCMethodDecl) n = ((JCMethodDecl)node).name; - else if (node instanceof JCVariableDecl) n = ((JCVariableDecl)node).name; + if (node instanceof JCClassDecl) n = ((JCClassDecl) node).name; + else if (node instanceof JCMethodDecl) n = ((JCMethodDecl) node).name; + else if (node instanceof JCVariableDecl) n = ((JCVariableDecl) node).name; else n = null; return n == null ? null : n.toString(); diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 6413e8ef..615a18d9 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2015 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,6 +41,7 @@ import lombok.Data; import lombok.Getter; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; +import lombok.core.LombokImmutableList; import lombok.core.AnnotationValues.AnnotationValue; import lombok.core.TypeResolver; import lombok.core.configuration.NullCheckExceptionType; @@ -445,9 +446,9 @@ public class JavacHandlerUtil { return HandlerUtil.shouldReturnThis0(accessors, field.getAst()); } - public static JCExpression cloneSelfType(JavacNode field) { - JavacNode typeNode = field; - JavacTreeMaker maker = field.getTreeMaker(); + public static JCExpression cloneSelfType(JavacNode childOfType) { + JavacNode typeNode = childOfType; + JavacTreeMaker maker = childOfType.getTreeMaker(); while (typeNode != null && typeNode.getKind() != Kind.TYPE) typeNode = typeNode.up(); if (typeNode != null && typeNode.get() instanceof JCClassDecl) { JCClassDecl type = (JCClassDecl) typeNode.get(); @@ -985,6 +986,17 @@ public class JavacHandlerUtil { return chainDots(node, -1, null, null, elems); } + public static JCExpression chainDots(JavacNode node, LombokImmutableList<String> elems) { + assert elems != null; + + JavacTreeMaker maker = node.getTreeMaker(); + JCExpression e = null; + for (String elem : elems) { + if (e == null) e = maker.Ident(node.toName(elem)); + else e = maker.Select(e, node.toName(elem)); + } + return e; + } /** * In javac, dotted access of any kind, from {@code java.lang.String} to {@code var.methodName} * is represented by a fold-left of {@code Select} nodes with the leftmost string represented by @@ -1013,7 +1025,6 @@ public class JavacHandlerUtil { return e; } - /** * In javac, dotted access of any kind, from {@code java.lang.String} to {@code var.methodName} diff --git a/test/core/src/lombok/RunAllTests.java b/test/core/src/lombok/RunAllTests.java index 9f56b45b..1ca76af5 100644 --- a/test/core/src/lombok/RunAllTests.java +++ b/test/core/src/lombok/RunAllTests.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 The Project Lombok Authors. + * Copyright (C) 2011-2015 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,6 +26,6 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses({lombok.transform.RunTransformTests.class, lombok.bytecode.RunBytecodeTests.class, lombok.core.configuration.RunConfigurationTests.class}) +@SuiteClasses({lombok.transform.RunTransformTests.class, lombok.bytecode.RunBytecodeTests.class, lombok.core.configuration.RunConfigurationTests.class, lombok.core.RunCoreTests.class}) public class RunAllTests { } diff --git a/test/core/src/lombok/core/RunCoreTests.java b/test/core/src/lombok/core/RunCoreTests.java new file mode 100644 index 00000000..8ac7cf81 --- /dev/null +++ b/test/core/src/lombok/core/RunCoreTests.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2015 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.core; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({TestSingulars.class}) +public class RunCoreTests { +} diff --git a/test/core/src/lombok/core/TestSingulars.java b/test/core/src/lombok/core/TestSingulars.java new file mode 100644 index 00000000..7e5e2617 --- /dev/null +++ b/test/core/src/lombok/core/TestSingulars.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.core; + +import static lombok.core.handlers.Singulars.autoSingularize; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class TestSingulars { + @Test + public void testSingulars() { + assertEquals("axis", autoSingularize("axes")); + assertEquals("adjective", autoSingularize("adjectives")); + assertEquals("bus", autoSingularize("buses")); + assertEquals("octopus", autoSingularize("octopodes")); + assertEquals(null, autoSingularize("octopi")); + assertEquals("elf", autoSingularize("elves")); + assertEquals("jack", autoSingularize("jacks")); + assertEquals("colloquy", autoSingularize("colloquies")); + assertEquals(null, autoSingularize("series")); + assertEquals("man", autoSingularize("men")); + assertEquals(null, autoSingularize("highwaymen")); + assertEquals("caveMan", autoSingularize("caveMen")); + assertEquals(null, autoSingularize("jackss")); + assertEquals(null, autoSingularize("virus")); + assertEquals("quiz", autoSingularize("quizzes")); + assertEquals("database", autoSingularize("databases")); + assertEquals("dataBase", autoSingularize("dataBases")); + assertEquals("Query", autoSingularize("Queries")); + assertEquals("Movie", autoSingularize("Movies")); + } +} |