CustomISO3166Source.java

/*
 * Copyright 2018 Global Crop Diversity Trust
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.genesys.server.service.worker;

import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.opencsv.CSVParserBuilder;
import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import com.opencsv.exceptions.CsvValidationException;

/**
 * @author Maxym Borodenko
 */
@Component
public class CustomISO3166Source {

	private static final String CUSTOM_CSV_RESOURCE = "/iso3166/custom.csv";

	public static final String[] CUSTOM_COUNTRIES_HEADERS = { "NAME", "CODE3", "URL" };
	public static final int COL_NAME = 0;
	public static final int COL_CODE3 = 1;
	public static final int COL_URL = 2;

	public static final Logger LOG = LoggerFactory.getLogger(CustomISO3166Source.class);

	public List<Custom3166Entry> fetchCountryData() throws IOException {
		var parser = new CSVParserBuilder()
			.withSeparator(',')
			.withQuoteChar('"')
			.withEscapeChar('\\')
			.withStrictQuotes(false)
			.withIgnoreLeadingWhiteSpace(true)
			.withIgnoreQuotations(false)
			.build();

		try (CSVReader reader = new CSVReaderBuilder(new FileReader(getClass().getResource(CUSTOM_CSV_RESOURCE).getPath())).withCSVParser(parser).build()) {
			// Ensure headers match known format
			final String[] headers = reader.readNext();
			LOG.warn("{} headers: {}", CUSTOM_CSV_RESOURCE, ArrayUtils.toString(headers, "<null>"));

			if (CUSTOM_COUNTRIES_HEADERS.length != headers.length || !Arrays.equals(CUSTOM_COUNTRIES_HEADERS, headers)) {
				throw new ExtraCountryUpdateException(CUSTOM_CSV_RESOURCE + " headers mismatch: " + ArrayUtils.toString(headers, "<null>"));
			}

			final List<Custom3166Entry> dataToImport = new ArrayList<>();

			String[] line;
			while ((line = reader.readNext()) != null) {
				for (int i = 0; i < line.length; i++) {
					if (line[i].equals("null") || StringUtils.isBlank(line[i])) {
						line[i] = null;
					}
				}
				dataToImport.add(new Custom3166Entry(line[COL_NAME], line[COL_CODE3], line[COL_URL]));
			}

			LOG.info("Done reading {}", CUSTOM_CSV_RESOURCE);
			return dataToImport;
		} catch (ExtraCountryUpdateException | CsvValidationException e) {
			throw new IOException(e);
		}
	}

	/**
	 * The Class Custom3166Entry.
	 */
	public static class Custom3166Entry {
		private final String name;
		private final String code3;
		private final String url;

		/**
		 * Instantiates a new country info.
		 *
		 * @param code3 the code 3
		 * @param name the name
		 * @param url the url
		 */
		public Custom3166Entry(final String name, final String code3, final String url) {
			this.name = name;
			this.code3 = code3;
			this.url = url;
		}

		/**
		 * Gets the code 3.
		 *
		 * @return the code 3
		 */
		public String getCode3() {
			return code3;
		}

		/**
		 * Gets the name.
		 *
		 * @return the name
		 */
		public String getName() {
			return name;
		}

		/**
		 * Gets the URL.
		 *
		 * @return the url
		 */
		public String getUrl() {
			return url;
		}

		@Override
		public String toString() {
			final StringBuilder sb = new StringBuilder();
			sb.append("name=").append(name).append(" code3=").append(code3).append(" URL=").append(url);
			return sb.toString();
		}
	}

	public static class ExtraCountryUpdateException extends Exception {
		private static final long serialVersionUID = -747223163381736058L;

		public ExtraCountryUpdateException(final String message) {
			super(message);
		}
	}

}