15
15
import org .openstreetmap .atlas .geography .atlas .items .LocationItem ;
16
16
import org .openstreetmap .atlas .geography .atlas .items .Relation ;
17
17
import org .openstreetmap .atlas .geography .atlas .walker .OsmWayWalker ;
18
+ import org .openstreetmap .atlas .tags .AmenityTag ;
19
+ import org .openstreetmap .atlas .tags .BrandTag ;
18
20
import org .openstreetmap .atlas .tags .ISOCountryTag ;
21
+ import org .openstreetmap .atlas .tags .LeisureTag ;
22
+ import org .openstreetmap .atlas .tags .ShopTag ;
19
23
import org .openstreetmap .atlas .tags .annotations .validation .Validators ;
20
24
import org .openstreetmap .atlas .tags .names .NameTag ;
21
25
import org .openstreetmap .atlas .utilities .configuration .Configuration ;
@@ -40,7 +44,7 @@ public class MixedCaseNameCheck extends BaseCheck<String>
40
44
private static final List <String > LANGUAGE_NAME_TAGS_DEFAULT = Arrays .asList ("name:en" );
41
45
private static final List <String > LOWER_CASE_PREPOSITIONS_DEFAULT = Arrays .asList ("and" , "from" ,
42
46
"to" , "of" , "by" , "upon" , "on" , "off" , "at" , "as" , "into" , "like" , "near" , "onto" ,
43
- "per" , "till" , "up" , "via" , "with" , "for" , "in" );
47
+ "per" , "till" , "up" , "via" , "with" , "for" , "in" , "de" , "la" , "del" , "du" , "van" , "der" );
44
48
private static final List <String > LOWER_CASE_ARTICLES_DEFAULT = Arrays .asList ("a" , "an" , "the" );
45
49
private static final String SPLIT_CHARACTERS_DEFAULT = " -/&@–" ;
46
50
private static final List <String > NAME_AFFIXES_DEFAULT = Arrays .asList ("Mc" , "Mac" , "Mck" ,
@@ -69,6 +73,8 @@ public class MixedCaseNameCheck extends BaseCheck<String>
69
73
private final Pattern mixedCaseUnitsPattern ;
70
74
private final Pattern mixedCaseApostrophePattern ;
71
75
private final Pattern nonFirstCapitalPattern ;
76
+ private final Pattern iCasePattern ;
77
+ private final Pattern dashAndDashPattern ;
72
78
73
79
/**
74
80
* The default constructor that must be supplied. The Atlas Checks framework will generate the
@@ -106,6 +112,8 @@ public MixedCaseNameCheck(final Configuration configuration)
106
112
.compile ("([^\\ p{Ll}]+'\\ p{Ll})|([^\\ p{Ll}]+\\ p{Ll}')" );
107
113
this .nonFirstCapitalPattern = Pattern .compile (String .format (
108
114
"(\\ p{L}.*(?<!'|%1$s)(\\ p{Lu}))|(\\ p{L}.*(?<=')\\ p{Lu}(?!.))" , this .nameAffixes ));
115
+ this .iCasePattern = Pattern .compile ("^i[A-Z]" );
116
+ this .dashAndDashPattern = Pattern .compile ("^n$|^n'$|^'n$|^'n'$" );
109
117
}
110
118
111
119
/**
@@ -127,6 +135,10 @@ public boolean validCheckForObject(final AtlasObject object)
127
135
.contains (object .tag (ISOCountryTag .KEY ).toUpperCase ())
128
136
// And have a name tag
129
137
&& Validators .hasValuesFor (object , NameTag .class )
138
+ // Excluding names with following tags to reduce number of legit
139
+ // MixedCaseNames
140
+ && !Validators .hasValuesFor (object , BrandTag .class , ShopTag .class ,
141
+ AmenityTag .class , LeisureTag .class )
130
142
// And if an Edge, is a main edge
131
143
&& (!(object instanceof Edge ) || ((Edge ) object ).isMainEdge ())
132
144
// Or it must have a specific language name tag from languageNameTags
@@ -191,6 +203,44 @@ protected List<String> getFallbackInstructions()
191
203
return FALLBACK_INSTRUCTIONS ;
192
204
}
193
205
206
+ /**
207
+ * Tests if {@link String} matches one of the patterns.
208
+ *
209
+ * @param word
210
+ * {@link String} to test
211
+ * @return true if {@code word} matches the {@code dashAndDashPattern} or {@code iCasePattern}.
212
+ */
213
+ private boolean isAlowedPatternHandling (final String word )
214
+ {
215
+ return this .isMixedCaseUnit (word ) || this .isICase (word ) || this .isDashAndDashCase (word );
216
+ }
217
+
218
+ /**
219
+ * Tests if {@link String} is "n" or "'n"
220
+ *
221
+ * @param word
222
+ * {@link String} to test
223
+ * @return true if {@code word} matches the {@code dashAndDashPattern}.
224
+ */
225
+ private boolean isDashAndDashCase (final String word )
226
+ {
227
+ // return true for following cases: Rock-n-Roll, Rock n' Roll
228
+ return this .dashAndDashPattern .matcher (word ).find ();
229
+ }
230
+
231
+ /**
232
+ * Tests if {@link String} starts with lower "i" character followed by capital letter.
233
+ *
234
+ * @param word
235
+ * {@link String} to test
236
+ * @return true if {@code word} matches the {@code iCasePattern}.
237
+ */
238
+ private boolean isICase (final String word )
239
+ {
240
+ // return true for following cases: iExample, iCode
241
+ return this .iCasePattern .matcher (word ).find ();
242
+ }
243
+
194
244
/**
195
245
* Tests each word in a string for proper use of case in a name.
196
246
*
@@ -210,7 +260,7 @@ private boolean isMixedCase(final String value)
210
260
for (final String word : wordArray )
211
261
{
212
262
// Check if the word is intentionally mixed case
213
- if (!this .isMixedCaseUnit (word ))
263
+ if (!this .isAlowedPatternHandling (word ))
214
264
{
215
265
final Matcher firstLetterMatcher = this .anyLetterPattern .matcher (word );
216
266
// If the word is not in the list of prepositions, and the
0 commit comments