1818
1919import javax .annotation .Nonnull ;
2020import 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 ) {
0 commit comments