Skip to content

Commit 2dfd255

Browse files
committed
Added checks for mapcode syntax checking
1 parent 14f9a90 commit 2dfd255

File tree

3 files changed

+164
-4
lines changed

3 files changed

+164
-4
lines changed

src/main/java/com/mapcode/Mapcode.java

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import javax.annotation.Nonnull;
2020
import java.util.Arrays;
21+
import java.util.regex.Matcher;
22+
import java.util.regex.Pattern;
2123

2224
/**
2325
* This class defines a single mapcode encoding result, including the mapcode itself and the
@@ -31,7 +33,14 @@ public final class Mapcode {
3133

3234
public Mapcode(
3335
@Nonnull final String mapcode,
34-
@Nonnull final Territory territory) {
36+
@Nonnull final Territory territory) throws IllegalArgumentException {
37+
38+
// Check mapcode format.
39+
if (!isValidMapcodeFormat(mapcode)) {
40+
throw new IllegalArgumentException(mapcode + " is not a correctly formatted mapcode; " +
41+
"the regular expression for the mapcode syntax is: " + REGEX_MAPCODE_FORMAT);
42+
}
43+
3544
this.mapcodePrecision2 = mapcode;
3645
if (mapcode.contains("-")) {
3746
this.mapcodePrecision0 = mapcode.substring(0, mapcode.length() - 3);
@@ -129,9 +138,65 @@ public Territory getTerritory() {
129138
return territory;
130139
}
131140

132-
@Override
133-
public int hashCode() {
134-
return Arrays.deepHashCode(new Object[]{mapcodePrecision0, territory});
141+
/**
142+
* This enum describes the types of mapcodes available.
143+
*/
144+
public enum MapcodeFormatType {
145+
MAPCODE_TYPE_INVALID,
146+
MAPCODE_TYPE_PRECISION_0,
147+
MAPCODE_TYPE_PRECISION_1,
148+
MAPCODE_TYPE_PRECISION_2,
149+
}
150+
151+
/**
152+
* These patterns and regular expressions are used for checking mapcode format strings.
153+
* They've been made pulkic to allow others to use the correct regular expressions as well.
154+
*/
155+
@Nonnull public static final String REGEX_MAPCODE_FORMAT =
156+
"^[a-zA-Z0-9]{2,5}?[.][a-zA-Z0-9]{2,5}?([-][a-zA-Z0-9]{1,2}?)?$";
157+
@Nonnull public static final String REGEX_MAPCODE_PRECISION = "[-][a-zA-Z0-9]{1,2}?$";
158+
159+
@Nonnull public static final Pattern PATTERN_MAPCODE_FORMAT = Pattern.compile(REGEX_MAPCODE_FORMAT);
160+
@Nonnull public static final Pattern PATTERN_MAPCODE_PRECISION = Pattern.compile(REGEX_MAPCODE_PRECISION);
161+
162+
/**
163+
* This method return the mapcode type, given a mapcode string. If the mapcode string has an invalid
164+
* format, {@link MapcodeFormatType#MAPCODE_TYPE_INVALID} is returned. If another value is returned,
165+
* the precision of the mapcode is given.
166+
*
167+
* Note that this method only checks the syntactic validity of the mapcode, the string format. It does not
168+
* check if the mapcode is really a valid mapcode representing a position on Earth.
169+
*
170+
* @param mapcode Mapcode string.
171+
* @return Type of mapcode format, or {@link MapcodeFormatType#MAPCODE_TYPE_INVALID} if not valid.
172+
*/
173+
@Nonnull
174+
public static MapcodeFormatType getMapcodeFormatType(@Nonnull final String mapcode) {
175+
final Matcher matcherMapcodeFormat = PATTERN_MAPCODE_FORMAT.matcher(mapcode);
176+
if (!matcherMapcodeFormat.matches()) {
177+
return MapcodeFormatType.MAPCODE_TYPE_INVALID;
178+
}
179+
final Matcher matcherMapcodePrecision = PATTERN_MAPCODE_PRECISION.matcher(mapcode);
180+
if (!matcherMapcodePrecision.find()) {
181+
return MapcodeFormatType.MAPCODE_TYPE_PRECISION_0;
182+
}
183+
final int length = matcherMapcodePrecision.group().length();
184+
assert (2 <= length) && (length <= 3);
185+
if (length == 2) {
186+
return MapcodeFormatType.MAPCODE_TYPE_PRECISION_1;
187+
}
188+
return MapcodeFormatType.MAPCODE_TYPE_PRECISION_2;
189+
}
190+
191+
/**
192+
* This method provides a shortcut to checking if a mapcode string is formatted properly or not at all.
193+
*
194+
* @param mapcode Mapcode string.
195+
* @return True if the mapcode format, the syntax, is correct. This does not mean the mapcode is actually a valid
196+
* mapcode representing a location on Earth.
197+
*/
198+
public static boolean isValidMapcodeFormat(@Nonnull final String mapcode) {
199+
return getMapcodeFormatType(mapcode) != MapcodeFormatType.MAPCODE_TYPE_INVALID;
135200
}
136201

137202
/**
@@ -186,6 +251,11 @@ public String toString() {
186251
return asInternationalISO();
187252
}
188253

254+
@Override
255+
public int hashCode() {
256+
return Arrays.deepHashCode(new Object[]{mapcodePrecision0, territory});
257+
}
258+
189259
@Override
190260
public boolean equals(final Object obj) {
191261
if (this == obj) {

src/site/apt/ReleaseNotes.apt.vm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ Release Notes (Version ${project.version})
99
In any case, never depend on them for your own non-<<<SNAPSHOT>>> releases.
1010
#end
1111

12+
* 1.40.2
13+
14+
* Added <<<getMapcodeFormatType>>> and <<<isValidMapcodeFormat>>> to check validity of mapcode strings. Added
15+
unit tests for these methods as well.
16+
17+
* Constructor of <<<Mapcode>>> now checks for validity of mapcode string.
18+
19+
[]
20+
1221
* 1.40.1
1322

1423
* Deprecated names <<<getMapcodeHighPrecision>>> and <<<getMapcodeMediumPrecision>>>.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (C) 2014 Stichting Mapcode Foundation (http://www.mapcode.com)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mapcode;
18+
19+
import org.junit.Test;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
23+
import static org.junit.Assert.assertEquals;
24+
import static org.junit.Assert.assertFalse;
25+
import static org.junit.Assert.assertTrue;
26+
27+
public class MapcodeTest {
28+
private static final Logger LOG = LoggerFactory.getLogger(MapcodeTest.class);
29+
30+
@Test
31+
public void checkValidMapcodeFormats() {
32+
LOG.info("checkValidMapcodeFormats");
33+
34+
assertTrue(Mapcode.isValidMapcodeFormat("A1.B1"));
35+
assertTrue(Mapcode.isValidMapcodeFormat("a1.B1"));
36+
assertTrue(Mapcode.isValidMapcodeFormat("00.01"));
37+
assertTrue(Mapcode.isValidMapcodeFormat("AAA.01"));
38+
assertTrue(Mapcode.isValidMapcodeFormat("AAA.BBB"));
39+
assertTrue(Mapcode.isValidMapcodeFormat("AAAA.BBB"));
40+
assertTrue(Mapcode.isValidMapcodeFormat("AAAA.BBBB"));
41+
assertTrue(Mapcode.isValidMapcodeFormat("AAAAA.BBBB"));
42+
assertTrue(Mapcode.isValidMapcodeFormat("AAAAA.BBBBB"));
43+
assertTrue(Mapcode.isValidMapcodeFormat("AA.AA-0"));
44+
assertTrue(Mapcode.isValidMapcodeFormat("AA.AA-01"));
45+
}
46+
47+
@Test
48+
public void checkInvalidMapcodeFormats() {
49+
LOG.info("checkInvalidMapcodeFormats");
50+
51+
assertFalse(Mapcode.isValidMapcodeFormat("A"));
52+
assertFalse(Mapcode.isValidMapcodeFormat("AB"));
53+
assertFalse(Mapcode.isValidMapcodeFormat("AB."));
54+
assertFalse(Mapcode.isValidMapcodeFormat(".A"));
55+
assertFalse(Mapcode.isValidMapcodeFormat(".AB"));
56+
assertFalse(Mapcode.isValidMapcodeFormat("A.B"));
57+
assertFalse(Mapcode.isValidMapcodeFormat("a.B"));
58+
assertFalse(Mapcode.isValidMapcodeFormat("0.1"));
59+
assertFalse(Mapcode.isValidMapcodeFormat("0.1"));
60+
assertFalse(Mapcode.isValidMapcodeFormat("00.1"));
61+
assertFalse(Mapcode.isValidMapcodeFormat("0.01"));
62+
assertFalse(Mapcode.isValidMapcodeFormat("00.01."));
63+
assertFalse(Mapcode.isValidMapcodeFormat("00.01.0"));
64+
assertFalse(Mapcode.isValidMapcodeFormat("00.01.00"));
65+
assertFalse(Mapcode.isValidMapcodeFormat("00.01-"));
66+
assertFalse(Mapcode.isValidMapcodeFormat("00.01-"));
67+
assertFalse(Mapcode.isValidMapcodeFormat("AAAAAA.BBBBB"));
68+
assertFalse(Mapcode.isValidMapcodeFormat("AAAAA.BBBBBB"));
69+
assertFalse(Mapcode.isValidMapcodeFormat("AA.AA-012"));
70+
}
71+
72+
@Test
73+
public void checkMapcodeFormatType() {
74+
LOG.info("checkMapcodeFormatType");
75+
76+
assertEquals(Mapcode.MapcodeFormatType.MAPCODE_TYPE_INVALID, Mapcode.getMapcodeFormatType("ABC"));
77+
assertEquals(Mapcode.MapcodeFormatType.MAPCODE_TYPE_PRECISION_0, Mapcode.getMapcodeFormatType("AA.BB"));
78+
assertEquals(Mapcode.MapcodeFormatType.MAPCODE_TYPE_PRECISION_1, Mapcode.getMapcodeFormatType("AA.BB-1"));
79+
assertEquals(Mapcode.MapcodeFormatType.MAPCODE_TYPE_PRECISION_2, Mapcode.getMapcodeFormatType("AA.BB-12"));
80+
}
81+
}

0 commit comments

Comments
 (0)