Skip to content

Commit 988a6b0

Browse files
Improve singular method - next try
1 parent f475c2c commit 988a6b0

File tree

7 files changed

+225
-37
lines changed

7 files changed

+225
-37
lines changed

modello-core/src/main/java/org/codehaus/modello/ModelloParameterConstants.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,11 @@ public class ModelloParameterConstants {
8787
*/
8888
public static final String LICENSE_TEXT = "modello.license.text";
8989

90+
/**
91+
* Additional plural to singular exceptions
92+
* @since 2.5.0
93+
*/
94+
public static final String PLURAL_EXCEPTIONS = "modello.plural.exceptions";
95+
9096
private ModelloParameterConstants() {}
9197
}

modello-core/src/main/java/org/codehaus/modello/plugin/AbstractModelloGenerator.java

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.HashMap;
3636
import java.util.List;
3737
import java.util.Map;
38+
import java.util.Optional;
3839

3940
import org.codehaus.modello.ModelloException;
4041
import org.codehaus.modello.ModelloParameterConstants;
@@ -58,12 +59,59 @@
5859
public abstract class AbstractModelloGenerator implements ModelloGenerator {
5960
private final Logger logger = LoggerFactory.getLogger(getClass());
6061

61-
private static final Map<String, String> PLURAL_EXCEPTION = new HashMap<>();
62+
private static final Map<String, String> PLURAL_EXCEPTIONS = new HashMap<>();
6263

6364
static {
64-
PLURAL_EXCEPTION.put("children", "child");
65-
PLURAL_EXCEPTION.put("licenses", "license");
66-
PLURAL_EXCEPTION.put("series", "series");
65+
// Irregular names
66+
PLURAL_EXCEPTIONS.put("children", "child");
67+
PLURAL_EXCEPTIONS.put("feet", "foot");
68+
PLURAL_EXCEPTIONS.put("geese", "goose");
69+
PLURAL_EXCEPTIONS.put("indices", "index");
70+
PLURAL_EXCEPTIONS.put("men", "man");
71+
PLURAL_EXCEPTIONS.put("mice", "mouse");
72+
PLURAL_EXCEPTIONS.put("people", "person");
73+
PLURAL_EXCEPTIONS.put("teeth", "tooth");
74+
PLURAL_EXCEPTIONS.put("women", "woman");
75+
76+
// Invariant names
77+
PLURAL_EXCEPTIONS.put("aircraft", "aircraft");
78+
PLURAL_EXCEPTIONS.put("bison", "bison");
79+
PLURAL_EXCEPTIONS.put("deer", "deer");
80+
PLURAL_EXCEPTIONS.put("elk", "elk");
81+
PLURAL_EXCEPTIONS.put("fish", "fish");
82+
PLURAL_EXCEPTIONS.put("series", "series");
83+
PLURAL_EXCEPTIONS.put("sheep", "sheep");
84+
PLURAL_EXCEPTIONS.put("species", "species");
85+
86+
// Special "oes" exceptions
87+
PLURAL_EXCEPTIONS.put("buffaloes", "buffalo");
88+
PLURAL_EXCEPTIONS.put("cargoes", "cargo");
89+
PLURAL_EXCEPTIONS.put("echoes", "echo");
90+
PLURAL_EXCEPTIONS.put("goes", "go");
91+
PLURAL_EXCEPTIONS.put("haloes", "halo");
92+
PLURAL_EXCEPTIONS.put("heroes", "hero");
93+
PLURAL_EXCEPTIONS.put("mosquitoes", "mosquito");
94+
PLURAL_EXCEPTIONS.put("noes", "no");
95+
PLURAL_EXCEPTIONS.put("potatoes", "potato");
96+
PLURAL_EXCEPTIONS.put("tomatoes", "tomato");
97+
PLURAL_EXCEPTIONS.put("torpedoes", "torpedo");
98+
PLURAL_EXCEPTIONS.put("vetoes", "veto");
99+
PLURAL_EXCEPTIONS.put("volcanoes", "volcano");
100+
101+
// Special "ses" exceptions
102+
PLURAL_EXCEPTIONS.put("horses", "horse");
103+
PLURAL_EXCEPTIONS.put("licenses", "license");
104+
PLURAL_EXCEPTIONS.put("phases", "phase");
105+
106+
// Special "zzes" exceptions
107+
PLURAL_EXCEPTIONS.put("fezzes", "fez");
108+
PLURAL_EXCEPTIONS.put("whizzes", "whiz");
109+
110+
// Special "ies" exceptions
111+
PLURAL_EXCEPTIONS.put("movies", "movie");
112+
113+
// Special "ves" exceptions
114+
PLURAL_EXCEPTIONS.put("relatives", "relative");
67115
}
68116

69117
private Model model;
@@ -85,6 +133,7 @@ protected Logger getLogger() {
85133
return logger;
86134
}
87135

136+
@SuppressWarnings("uncheked")
88137
protected void initialize(Model model, Map<String, Object> parameters) throws ModelloException {
89138
this.model = model;
90139

@@ -100,6 +149,9 @@ protected void initialize(Model model, Map<String, Object> parameters) throws Mo
100149
encoding = (String) parameters.get(ModelloParameterConstants.ENCODING);
101150

102151
licenseText = (List<String>) parameters.get(ModelloParameterConstants.LICENSE_TEXT);
152+
153+
Optional.ofNullable(parameters.get(ModelloParameterConstants.PLURAL_EXCEPTIONS))
154+
.ifPresent(o -> PLURAL_EXCEPTIONS.putAll((Map<String, String>) o));
103155
}
104156

105157
protected Model getModel() {
@@ -204,20 +256,43 @@ protected String capitalise(String str) {
204256
}
205257

206258
public static String singular(String name) {
207-
if (StringUtils.isEmpty(name)) {
208-
return name;
259+
if (name == null || name.isEmpty()) return name;
260+
261+
String lower = name.toLowerCase();
262+
263+
if (PLURAL_EXCEPTIONS.containsKey(lower)) {
264+
return PLURAL_EXCEPTIONS.get(lower);
209265
}
210266

211-
if (PLURAL_EXCEPTION.containsKey(name)) {
212-
return PLURAL_EXCEPTION.get(name);
213-
} else if (name.endsWith("ies")) {
267+
// Suffix-based rules
268+
if (lower.endsWith("ies") && name.length() > 3) {
214269
return name.substring(0, name.length() - 3) + "y";
215-
} else if (name.endsWith("zzes")) {
216-
return name.substring(0, name.length() - 3);
217-
} else if (name.endsWith("ches") || name.endsWith("xes") || name.endsWith("ses") || name.endsWith("oes")
218-
|| name.endsWith("shes")) {
270+
}
271+
if (lower.endsWith("aves") || lower.endsWith("lves") || lower.endsWith("rves")) {
272+
return name.substring(0, name.length() - 3) + "f";
273+
}
274+
if (lower.endsWith("ves") && !lower.endsWith("fves")) {
275+
return name.substring(0, name.length() - 3) + "fe";
276+
}
277+
if (lower.endsWith("zzes")) {
278+
return name.substring(0, name.length() - 2);
279+
}
280+
if (lower.endsWith("sses")) {
219281
return name.substring(0, name.length() - 2);
220-
} else if (name.endsWith("s") && (name.length() != 1)) {
282+
}
283+
if (lower.endsWith("ses")) {
284+
return name.substring(0, name.length() - 2);
285+
}
286+
if (lower.endsWith("ches") || lower.endsWith("shes")) {
287+
return name.substring(0, name.length() - 2);
288+
}
289+
if (lower.endsWith("xes")) {
290+
return name.substring(0, name.length() - 2);
291+
}
292+
if (lower.endsWith("oes")) {
293+
return name.substring(0, name.length() - 1);
294+
}
295+
if (lower.endsWith("s") && name.length() > 1) {
221296
return name.substring(0, name.length() - 1);
222297
}
223298

modello-core/src/test/java/org/codehaus/modello/plugin/AbstractModelloGeneratorTest.java

Lines changed: 98 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,104 @@
77

88
class AbstractModelloGeneratorTest {
99

10-
@CsvSource( {
11-
",",
12-
"'',''",
13-
"s,s",
14-
"aliases, alias",
15-
"babies, baby",
16-
"fezzes, fez",
17-
"foxes, fox",
18-
"ids, id",
19-
"licenses, license",
20-
"lunches, lunch",
21-
"potatoes, potato",
22-
"repositories, repository",
23-
"roles, role",
24-
"rushes, rush",
25-
"series, series"
10+
@CsvSource({
11+
",",
12+
"'',''",
13+
"s,s",
14+
15+
// Known exceptions
16+
"men, man",
17+
"women, woman",
18+
"children,child",
19+
"mice, mouse",
20+
"people, person",
21+
"teeth, tooth",
22+
"feet, foot",
23+
"geese, goose",
24+
"series, series",
25+
"species, species",
26+
"sheep, sheep",
27+
"fish, fish",
28+
"deer, deer",
29+
"aircraft, aircraft",
30+
"heroes, hero",
31+
"potatoes, potato",
32+
"tomatoes, tomato",
33+
"echoes, echo",
34+
"vetoes, veto",
35+
"torpedoes, torpedo",
36+
"cargoes, cargo",
37+
"haloes, halo",
38+
"mosquitoes, mosquito",
39+
"buffaloes, buffalo",
40+
"bison, bison",
41+
"elk, elk",
42+
43+
// Regular plural forms with suffixes
44+
"voes, voe",
45+
"hoes, hoe",
46+
"canoes, canoe",
47+
"toes, toe",
48+
"foes, foe",
49+
"oboes, oboe",
50+
"noes, no",
51+
"boxes, box",
52+
"wishes, wish",
53+
"dishes, dish",
54+
"brushes, brush",
55+
"classes, class",
56+
"buzzes, buzz",
57+
"cars, car",
58+
"dogs, dog",
59+
"cats, cat",
60+
"horses, horse",
61+
"fezzes, fez",
62+
"whizzes, whiz",
63+
"foxes, fox",
64+
65+
// Some test cases with different rules
66+
"wolves, wolf",
67+
"knives, knife",
68+
"leaves, leaf",
69+
"wives, wife",
70+
"lives, life",
71+
"babies, baby",
72+
"parties, party",
73+
"cities, city",
74+
"buses, bus",
75+
"boxes, box",
76+
"churches, church",
77+
"matches, match",
78+
"watches, watch",
79+
"riches, rich",
80+
"dresses, dress",
81+
"crosses, cross",
82+
"lunches, lunch",
83+
"relatives, relative",
84+
85+
// More edge cases
86+
"heroes, hero",
87+
"vetoes, veto",
88+
"torpedoes, torpedo",
89+
"tomatoes, tomato",
90+
"potatoes, potato",
91+
"echoes, echo",
92+
"mosquitoes, mosquito",
93+
"buffaloes, buffalo",
94+
"volcanoes, volcano",
95+
"goes, go",
96+
"indices, index",
97+
"phases, phase",
98+
"kisses, kiss",
99+
"movies, movie",
100+
"shoes, shoe",
101+
102+
// other examples
103+
"aliases, alias",
104+
"ids, id",
105+
"licenses, license",
106+
"repositories, repository",
107+
"roles, role",
26108
})
27109
@ParameterizedTest
28110
public void testSingular(String plural, String singular) {

modello-maven-plugin/src/it/clone/pom.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@
5656
<models>
5757
<model>src/main/mdo/thing.mdo</model>
5858
</models>
59+
<pluralExceptions>
60+
<someStringSets>ownSingularStringSet</someStringSets>
61+
</pluralExceptions>
5962
</configuration>
6063
<executions>
6164
<execution>

modello-maven-plugin/src/it/clone/src/main/mdo/thing.mdo

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ under the License.
104104
</association>
105105
</field>
106106
<field>
107-
<name>someStringList</name>
107+
<name>someStringLists</name>
108108
<version>1.0.0</version>
109109
<type>List</type>
110110
<association>
@@ -113,7 +113,7 @@ under the License.
113113
</association>
114114
</field>
115115
<field>
116-
<name>someStringSet</name>
116+
<name>someStringSets</name>
117117
<version>1.0.0</version>
118118
<type>Set</type>
119119
<association>

modello-maven-plugin/src/it/clone/src/test/java/test/CloneTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public void testClone()
3737
orig.setSomeDate( new Date() );
3838
orig.setSomeDom( new Xpp3Dom( "test" ) );
3939
orig.addSomeStringList( "string" );
40-
orig.addSomeStringSet( "string" );
40+
orig.addOwnSingularStringSet( "string" );
4141
orig.setDeepThingy( new Thingy() );
4242
orig.addDeepThingyList( new Thingy() );
4343
orig.addDeepThingySet( new Thingy() );
@@ -67,10 +67,10 @@ public void testClone()
6767
assertEquals( orig.getSomeDom(), copy.getSomeDom() );
6868
assertNotSame( orig.getSomeDom(), copy.getSomeDom() );
6969

70-
assertEquals( orig.getSomeStringList(), copy.getSomeStringList() );
71-
assertNotSame( orig.getSomeStringList(), copy.getSomeStringList() );
72-
assertEquals( orig.getSomeStringSet(), copy.getSomeStringSet() );
73-
assertNotSame( orig.getSomeStringSet(), copy.getSomeStringSet() );
70+
assertEquals( orig.getSomeStringLists(), copy.getSomeStringLists() );
71+
assertNotSame( orig.getSomeStringLists(), copy.getSomeStringLists() );
72+
assertEquals( orig.getSomeStringSets(), copy.getSomeStringSets() );
73+
assertNotSame( orig.getSomeStringSets(), copy.getSomeStringSets() );
7474

7575
assertNotSame( orig.getDeepThingy(), copy.getDeepThingy() );
7676
assertNotSame( orig.getDeepThingyList(), copy.getDeepThingyList() );

modello-maven-plugin/src/main/java/org/codehaus/modello/maven/AbstractModelloGeneratorMojo.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,19 @@ public abstract class AbstractModelloGeneratorMojo extends AbstractMojo {
115115
@Parameter
116116
private File licenseFile;
117117

118+
/**
119+
* Additional exceptions to the singularization rules, changing plural noun to singular.
120+
* <p>
121+
* As a kay we provide plural noun and as value we provide singular noun, eg:
122+
* <pre>
123+
* <kisses>kiss</kisses>
124+
* </pre>
125+
*
126+
* @since 2.5.0
127+
*/
128+
@Parameter
129+
private Map<String, String> pluralExceptions;
130+
118131
/**
119132
* @since 1.0.1
120133
*/
@@ -180,6 +193,8 @@ public void execute() throws MojoExecutionException {
180193

181194
parameters.put(ModelloParameterConstants.PACKAGE_WITH_VERSION, Boolean.toString(packageWithVersion));
182195

196+
parameters.put(ModelloParameterConstants.PLURAL_EXCEPTIONS, keysToLower(pluralExceptions));
197+
183198
if (!packagedVersions.isEmpty()) {
184199
parameters.put(ModelloParameterConstants.ALL_VERSIONS, StringUtils.join(packagedVersions.iterator(), ","));
185200
}
@@ -224,6 +239,13 @@ public void execute() throws MojoExecutionException {
224239
}
225240
}
226241

242+
private Object keysToLower(Map<String, String> maps) {
243+
if (maps == null) {
244+
return null;
245+
}
246+
return maps.entrySet().stream().collect(Collectors.toMap(e -> e.getKey().toLowerCase(), Map.Entry::getValue));
247+
}
248+
227249
/**
228250
* Performs execute on a single specified model.
229251
*/

0 commit comments

Comments
 (0)