All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.tornaia.geoip.GeoIPResidentImpl Maven / Gradle / Ivy

There is a newer version: 0.1.12
Show newest version
package com.github.tornaia.geoip;

import com.github.tornaia.geoip.internal.IpAddressMatcher;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.github.tornaia.geoip.GeoLiteConstants.IPV_CSV_GEONAME_ID;
import static com.github.tornaia.geoip.GeoLiteConstants.IPV_CSV_REGISTERED_COUNTRY_GEONAME_ID;
import static com.github.tornaia.geoip.GeoLiteConstants.LOCATIONS_CSV_COUNTRY_CODE_ISO;
import static com.github.tornaia.geoip.GeoLiteConstants.LOCATIONS_CSV_COUNTRY_NAME;
import static com.github.tornaia.geoip.GeoLiteConstants.LOCATIONS_CSV_GEO_ID;

public class GeoIPResidentImpl implements GeoIP {

    private static class MapHolder {

        private static final Map CIDR_NOTATIONS_TO_COUNTRY_ISO_CODE_MAP = getCidrNotationsToCountryIdoCodeMap();
        private static final Map COUNTRY_ISO_CODE_TO_COUNTRY_NAME_MAP = getCountryIsoCodeToCountryNameMapInternal();

        private static Map getCidrNotationsToCountryIdoCodeMap() {
            Map cidrNotationsToCountryIsoCodeMap = new HashMap<>();
            Map countryIdToCountryIsoCodeMap = getCountryIdToCountryIsoCodeMap();
            loadIpv4Csv(cidrNotationsToCountryIsoCodeMap, countryIdToCountryIsoCodeMap);
            loadIpv6Csv(cidrNotationsToCountryIsoCodeMap, countryIdToCountryIsoCodeMap);
            return cidrNotationsToCountryIsoCodeMap;
        }

        private static void loadIpv4Csv(Map cidrNotationsToCountryIsoCodeMap, Map countryIdToCountryIsoCodeMap) {
            loadIpvCsv(cidrNotationsToCountryIsoCodeMap, countryIdToCountryIsoCodeMap, "GeoLite2-Country-Blocks-IPv4.csv");
        }

        private static void loadIpv6Csv(Map cidrNotationsToCountryIsoCodeMap, Map countryIdToCountryIsoCodeMap) {
            loadIpvCsv(cidrNotationsToCountryIsoCodeMap, countryIdToCountryIsoCodeMap, "GeoLite2-Country-Blocks-IPv6.csv");
        }

        private static void loadIpvCsv(Map cidrNotationsToCountryIsoCodeMap, Map countryIdToCountryIsoCodeMap, String filename) {
            try (InputStream inputStream = MapHolder.class.getClassLoader().getResourceAsStream(filename)) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                Map ipMap = reader
                        .lines()
                        .skip(1)
                        .map(e -> e.split(","))
                        .filter(e -> !e[IPV_CSV_REGISTERED_COUNTRY_GEONAME_ID].isEmpty() || !e[IPV_CSV_GEONAME_ID].isEmpty())
                        .collect(Collectors.toMap(e -> new IpAddressMatcher(e[LOCATIONS_CSV_GEO_ID]), e -> mapCountryCodeToCountryIsoCode(countryIdToCountryIsoCodeMap, e)));
                cidrNotationsToCountryIsoCodeMap.putAll(ipMap);
            } catch (IOException e) {
                throw new GeoIPException("Failed to read csv: " + filename, e);
            }
        }

        private static Map getCountryIdToCountryIsoCodeMap() {
            try (InputStream inputStream = MapHolder.class.getClassLoader().getResourceAsStream("GeoLite2-Country-Locations-en.csv")) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                return reader
                        .lines()
                        .skip(1)
                        .map(e -> e.split(","))
                        .collect(Collectors.toMap(e -> e[LOCATIONS_CSV_GEO_ID], e -> e[LOCATIONS_CSV_COUNTRY_CODE_ISO]));
            } catch (IOException e) {
                throw new GeoIPException("Failed to read GeoLite2-Country-Locations-en.csv", e);
            }
        }

        private static Map getCountryIsoCodeToCountryNameMapInternal() {
            try (InputStream inputStream = MapHolder.class.getClassLoader().getResourceAsStream("GeoLite2-Country-Locations-en.csv")) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                return reader
                        .lines()
                        .skip(1)
                        .map(e -> e.split(","))
                        .filter(e -> e[LOCATIONS_CSV_COUNTRY_CODE_ISO] != null && !e[LOCATIONS_CSV_COUNTRY_CODE_ISO].isEmpty())
                        .filter(e -> e[LOCATIONS_CSV_COUNTRY_NAME] != null && !e[LOCATIONS_CSV_COUNTRY_NAME].isEmpty())
                        .collect(Collectors.toMap(e -> e[LOCATIONS_CSV_COUNTRY_CODE_ISO], e -> stripLeadingAndTrailingDoubleQuotes(e[LOCATIONS_CSV_COUNTRY_NAME]), (a, b) -> a));
            } catch (IOException e) {
                throw new GeoIPException("Failed to read GeoLite2-Country-Locations-en.csv", e);
            }
        }

        private static String mapCountryCodeToCountryIsoCode(Map countryIdToCountryIsoCodeMap, String[] splittedLine) {
            String registeredCountryGeonameId = splittedLine[IPV_CSV_REGISTERED_COUNTRY_GEONAME_ID];
            String geonameId = splittedLine[IPV_CSV_GEONAME_ID];
            String countryId = registeredCountryGeonameId.isEmpty() ? geonameId : registeredCountryGeonameId;
            return getCountryIsoCodeFromCountryId(countryIdToCountryIsoCodeMap, countryId);
        }

        private static String getCountryIsoCodeFromCountryId(Map countryIdToCountryIsoCodeMap, String countryId) {
            String countryIsoCode = countryIdToCountryIsoCodeMap.get(countryId);
            boolean countryIsoCodeWasNotFoundForCountryId = countryIsoCode == null;
            if (countryIsoCodeWasNotFoundForCountryId) {
                throw new GeoIPException("Country iso code was not found for countryId, countryId: " + countryId);
            }
            return countryIsoCode;
        }

        private static String stripLeadingAndTrailingDoubleQuotes(String maybeQuotedCountryName) {
            String countryName = maybeQuotedCountryName;

            if (countryName.startsWith("\"")) {
                countryName = countryName.substring(1);
            }

            if (countryName.endsWith("\"")) {
                countryName = countryName.substring(0, countryName.length() - 1);
            }

            return countryName;
        }


        private static Map getCidrNotationsToCountryIsoCodeMap() {
            return MapHolder.CIDR_NOTATIONS_TO_COUNTRY_ISO_CODE_MAP;
        }

        private static Map getCountryIsoCodeToCountryNameMap() {
            return MapHolder.COUNTRY_ISO_CODE_TO_COUNTRY_NAME_MAP;
        }
    }

    @Override
    public Optional getTwoLetterCountryCode(InetAddress inetAddress) {
        return getTwoLetterCountryCode(inetAddress.getHostAddress());
    }

    @Override
    public Optional getTwoLetterCountryCode(String ipAddress) {
        return MapHolder.getCidrNotationsToCountryIsoCodeMap()
                .entrySet()
                .parallelStream()
                .filter(e -> e.getKey().matches(ipAddress))
                .map(Map.Entry::getValue)
                .filter(e -> !e.isEmpty())
                .findFirst();
    }

    @Override
    public Optional getCountryName(InetAddress inetAddress) {
        return getCountryName(inetAddress.getHostAddress());
    }

    @Override
    public Optional getCountryName(String ipAddress) {
        Optional optionalTwoLetterCountryCode = getTwoLetterCountryCode(ipAddress);
        if (optionalTwoLetterCountryCode.isPresent()) {
            String twoLetterCountryCode = optionalTwoLetterCountryCode.get();
            Map countryIsoCodeToCountryNameMap = MapHolder.getCountryIsoCodeToCountryNameMap();
            String countryName = countryIsoCodeToCountryNameMap.get(twoLetterCountryCode);
            return Optional.ofNullable(countryName);
        }
        return Optional.empty();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy