Skip to content

Added feature to get vessel flag from MMSI.#49

Open
panoet wants to merge 1 commit into
tbsalling:masterfrom
panoet:vessel-flag-identification
Open

Added feature to get vessel flag from MMSI.#49
panoet wants to merge 1 commit into
tbsalling:masterfrom
panoet:vessel-flag-identification

Conversation

@panoet

@panoet panoet commented Oct 13, 2022

Copy link
Copy Markdown

No description provided.

@tbsalling tbsalling requested a review from Copilot October 16, 2025 08:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds a utility to derive a vessel’s flag from an MMSI by mapping MIDs to country names and ISO codes.

  • Introduces VesselFlag with a static MID→flag map and getters
  • Adds getFlagFromMmsi(String) to extract the MID from MMSI prefixes and look up the flag

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.


public class VesselFlag {

private final static HashMap<Integer, VesselFlag> FLAGS_MAP = new HashMap<>();

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API exposes a mutable internal map and returns a raw type. Return an unmodifiable Map<Integer, VesselFlag> to prevent external mutation and use generics for type safety. Example: change the field to private static final Map<Integer, VesselFlag> and return Collections.unmodifiableMap(FLAGS_MAP).

Copilot uses AI. Check for mistakes.
Comment on lines +302 to +304
public static HashMap getVesselFlags() {
return FLAGS_MAP;
}

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API exposes a mutable internal map and returns a raw type. Return an unmodifiable Map<Integer, VesselFlag> to prevent external mutation and use generics for type safety. Example: change the field to private static final Map<Integer, VesselFlag> and return Collections.unmodifiableMap(FLAGS_MAP).

Copilot uses AI. Check for mistakes.
Comment on lines +306 to +311
private String country, shortCode;

protected VesselFlag(String country, String shortCode) {
this.country = country;
this.shortCode = shortCode;
}

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Make VesselFlag immutable and non-extensible: declare fields final, make the constructor private, and consider making the class final. This prevents accidental mutation and clarifies usage as a value object.

Copilot uses AI. Check for mistakes.
Comment on lines +321 to +332
public static VesselFlag getFlagFromMmsi(String mmsi) throws NumberFormatException {
int mid;
if (mmsi.startsWith("111") || mmsi.startsWith("970")) {
mid = Integer.parseInt(mmsi.substring(3, 6));
} else if (mmsi.startsWith("00") || mmsi.startsWith("98") || mmsi.startsWith("99")) {
mid = Integer.parseInt(mmsi.substring(2, 5));
} else if (mmsi.startsWith("8") || mmsi.startsWith("0")) {
mid = Integer.parseInt(mmsi.substring(1, 4));
} else {
mid = Integer.parseInt(mmsi.substring(0, 3));
}

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Substring operations can throw StringIndexOutOfBoundsException for short inputs and NumberFormatException for non-digit inputs. Add input validation (length and numeric) and fail fast with an IllegalArgumentException and a clear message before substring/parse.

Suggested change
public static VesselFlag getFlagFromMmsi(String mmsi) throws NumberFormatException {
int mid;
if (mmsi.startsWith("111") || mmsi.startsWith("970")) {
mid = Integer.parseInt(mmsi.substring(3, 6));
} else if (mmsi.startsWith("00") || mmsi.startsWith("98") || mmsi.startsWith("99")) {
mid = Integer.parseInt(mmsi.substring(2, 5));
} else if (mmsi.startsWith("8") || mmsi.startsWith("0")) {
mid = Integer.parseInt(mmsi.substring(1, 4));
} else {
mid = Integer.parseInt(mmsi.substring(0, 3));
}
public static VesselFlag getFlagFromMmsi(String mmsi) {
if (mmsi == null) {
throw new IllegalArgumentException("MMSI must not be null");
}
// The largest substring index is 6 (mmsi.substring(3, 6)), so require at least 6 characters
if (mmsi.length() < 6) {
throw new IllegalArgumentException("MMSI must be at least 6 characters long: " + mmsi);
}
int mid;
String midStr;
if (mmsi.startsWith("111") || mmsi.startsWith("970")) {
midStr = mmsi.substring(3, 6);
} else if (mmsi.startsWith("00") || mmsi.startsWith("98") || mmsi.startsWith("99")) {
midStr = mmsi.substring(2, 5);
} else if (mmsi.startsWith("8") || mmsi.startsWith("0")) {
midStr = mmsi.substring(1, 4);
} else {
midStr = mmsi.substring(0, 3);
}
if (midStr.length() != 3 || !midStr.chars().allMatch(Character::isDigit)) {
throw new IllegalArgumentException("MMSI MID substring is not a 3-digit number: " + midStr + " (from MMSI: " + mmsi + ")");
}
mid = Integer.parseInt(midStr);

Copilot uses AI. Check for mistakes.
FLAGS_MAP.put(725, new VesselFlag("Chile", "CL"));
FLAGS_MAP.put(730, new VesselFlag("Colombia", "CO"));
FLAGS_MAP.put(735, new VesselFlag("Ecuador", "EC"));
FLAGS_MAP.put(740, new VesselFlag("UK", "UK"));

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ISO 3166-1 alpha-2 code 'UK' is invalid (United Kingdom is 'GB'). Replace the short code with 'GB', and for consistency with other entries use 'United Kingdom' as the country name.

Suggested change
FLAGS_MAP.put(740, new VesselFlag("UK", "UK"));
FLAGS_MAP.put(740, new VesselFlag("United Kingdom", "GB"));

Copilot uses AI. Check for mistakes.
public String getShortCode() {
return shortCode;
}

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add Javadoc describing the MMSI parsing rules (special prefixes like 111/970, 00/98/99, 8/0) and the expected MMSI format/length, plus what is returned when no MID match is found (null). This will make the intent and edge-case handling clearer to callers.

Suggested change
/**
* Returns the {@link VesselFlag} corresponding to the given MMSI (Maritime Mobile Service Identity).
* <p>
* MMSI is expected to be a 9-digit numeric string. The method extracts the Maritime Identification Digits (MID)
* using the following rules:
* <ul>
* <li>If the MMSI starts with "111" or "970", the MID is digits 4-6 (positions 3-5, zero-based).</li>
* <li>If the MMSI starts with "00", "98", or "99", the MID is digits 3-5 (positions 2-4).</li>
* <li>If the MMSI starts with "8" or "0", the MID is digits 2-4 (positions 1-3).</li>
* <li>Otherwise, the MID is the first three digits (positions 0-2).</li>
* </ul>
* <p>
* If the extracted MID does not match any known country, this method returns {@code null}.
* <p>
* @param mmsi the 9-digit MMSI string to parse
* @return the corresponding {@link VesselFlag}, or {@code null} if no MID match is found
* @throws NumberFormatException if the MMSI is not numeric or is too short to extract the MID
*/

Copilot uses AI. Check for mistakes.
FLAGS_MAP.put(271, new VesselFlag("Turkey", "TR"));
FLAGS_MAP.put(272, new VesselFlag("Ukraine", "UA"));
FLAGS_MAP.put(273, new VesselFlag("Russia", "RU"));
FLAGS_MAP.put(274, new VesselFlag("FYR Macedonia", "MK"));

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update country name to its current official name 'North Macedonia'.

Suggested change
FLAGS_MAP.put(274, new VesselFlag("FYR Macedonia", "MK"));
FLAGS_MAP.put(274, new VesselFlag("North Macedonia", "MK"));

Copilot uses AI. Check for mistakes.
FLAGS_MAP.put(666, new VesselFlag("Somalia", "SO"));
FLAGS_MAP.put(667, new VesselFlag("Sierra Leone", "SL"));
FLAGS_MAP.put(668, new VesselFlag("Sao Tome Principe", "ST"));
FLAGS_MAP.put(669, new VesselFlag("Swaziland", "SZ"));

Copilot AI Oct 16, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update country name to its current official name 'Eswatini'.

Suggested change
FLAGS_MAP.put(669, new VesselFlag("Swaziland", "SZ"));
FLAGS_MAP.put(669, new VesselFlag("Eswatini", "SZ"));

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants