/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.master.region.impl;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import au.com.bytecode.opencsv.CSVReader;
import com.google.common.base.Charsets;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.core.region.RegionClassification;
import com.opengamma.id.UniqueId;
import com.opengamma.master.region.ManageableRegion;
import com.opengamma.master.region.RegionDocument;
import com.opengamma.master.region.RegionMaster;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.i18n.Country;
import com.opengamma.util.money.Currency;
/**
* Loads a CSV formatted region file
* <p>
* This populates a region master.
*/
public class RegionFileReader {
/**
* Path to the default regions file
*/
private static final String REGIONS_RESOURCE = "/com/opengamma/region/regions.csv";
/**
* The name column header.
*/
private static final String NAME_COLUMN = "Name";
/**
* The full name column header.
*/
private static final String FORMAL_NAME_COLUMN = "Formal Name";
/**
* The type column header.
*/
private static final String CLASSIFICATION_COLUMN = "Type";
/**
* The sovereignty column header.
*/
private static final String SOVEREIGNITY_COLUMN = "Sovereignty";
/**
* The country code column header.
*/
private static final String ISO_COUNTRY_2_COLUMN = "ISO 3166-1 2 Letter Code";
/**
* The currency code column header.
*/
private static final String ISO_CURRENCY_3_COLUMN = "ISO 4217 Currency Code";
/**
* The sub regions column header
*/
private static final String SUB_REGIONS_COLUMN = "Sub Regions";
/**
* The region master to populate.
*/
private RegionMaster _regionMaster;
/**
* Creates a populated in-memory master and source.
* <p>
* The values can be extracted using the accessor methods.
*
* @return the region reader, not null
*/
public static RegionFileReader createPopulated() {
return createPopulated0(new InMemoryRegionMaster());
}
/**
* Populates a region master.
*
* @param regionMaster the region master to populate, not null
* @return the master, not null
*/
public static RegionMaster createPopulated(RegionMaster regionMaster) {
return createPopulated0(regionMaster).getRegionMaster();
}
/**
* Creates a populated file reader.
* <p>
* The values can be extracted using the accessor methods.
*
* @param regionMaster the region master to populate, not null
* @return the region reader, not null
*/
private static RegionFileReader createPopulated0(RegionMaster regionMaster) {
RegionFileReader fileReader = new RegionFileReader(regionMaster);
InputStream stream = regionMaster.getClass().getResourceAsStream(REGIONS_RESOURCE);
try {
fileReader.parse(stream);
} finally {
IOUtils.closeQuietly(stream);
}
UnLocodeRegionFileReader.populate(regionMaster);
return fileReader;
}
//-------------------------------------------------------------------------
/**
* Creates an instance with a master to populate.
*
* @param regionMaster the region master, not null
*/
public RegionFileReader(RegionMaster regionMaster) {
ArgumentChecker.notNull(regionMaster, "regionMaster");
_regionMaster = regionMaster;
}
//-------------------------------------------------------------------------
/**
* Gets the region master.
* @return the region master, not null
*/
public RegionMaster getRegionMaster() {
return _regionMaster;
}
/**
* Gets a {@code MasterRegionSource} for the master.
* @return the region source, not null
*/
public MasterRegionSource getRegionSource() {
return new MasterRegionSource(getRegionMaster());
}
//-------------------------------------------------------------------------
/**
* Parses the specified file to populate the master.
*
* @param in the input stream to read, not null
*/
public void parse(InputStream in) {
InputStreamReader reader = new InputStreamReader(new BufferedInputStream(in), Charsets.UTF_8);
try {
parse(reader);
} finally {
IOUtils.closeQuietly(reader);
}
}
/**
* Parses the specified file to populate the master.
*
* @param in the input reader to read, not closed, not null
*/
public void parse(Reader in) {
String name = null;
try {
Map<String, ManageableRegion> regions = new HashMap<String, ManageableRegion>();
Map<UniqueId, Set<String>> subRegions = new HashMap<UniqueId, Set<String>>();
// open CSV file
@SuppressWarnings("resource")
CSVReader reader = new CSVReader(in);
List<String> columns = Arrays.asList(reader.readNext());
// identify columns
final int nameColumnIdx = columns.indexOf(NAME_COLUMN);
final int formalNameColumnIdx = columns.indexOf(FORMAL_NAME_COLUMN);
final int classificationColumnIdx = columns.indexOf(CLASSIFICATION_COLUMN);
final int sovereignityColumnIdx = columns.indexOf(SOVEREIGNITY_COLUMN);
final int countryColumnIdx = columns.indexOf(ISO_COUNTRY_2_COLUMN);
final int currencyColumnIdx = columns.indexOf(ISO_CURRENCY_3_COLUMN);
final int subRegionsColumnIdx = columns.indexOf(SUB_REGIONS_COLUMN);
// parse
String[] row = null;
while ((row = reader.readNext()) != null) {
name = row[nameColumnIdx].trim(); // the primary key
String fullName = StringUtils.trimToNull(row[formalNameColumnIdx]);
if (fullName == null) {
fullName = name;
}
RegionClassification classification = RegionClassification.valueOf(row[classificationColumnIdx].trim());
String sovereignity = StringUtils.trimToNull(row[sovereignityColumnIdx]);
String countryISO = StringUtils.trimToNull(row[countryColumnIdx]);
String currencyISO = StringUtils.trimToNull(row[currencyColumnIdx]);
Set<String> rowSubRegions = new HashSet<String>(Arrays.asList(row[subRegionsColumnIdx].split(";")));
rowSubRegions = trim(rowSubRegions);
ManageableRegion region = new ManageableRegion();
region.setClassification(classification);
region.setName(name);
region.setFullName(fullName);
if (countryISO != null) {
region.setCountry(Country.of(countryISO));
region.addExternalId(ExternalSchemes.financialRegionId(countryISO)); // TODO: looks odd
}
if (currencyISO != null) {
region.setCurrency(Currency.of(currencyISO));
}
if (sovereignity != null) {
ManageableRegion parent = regions.get(sovereignity);
if (parent == null) {
throw new OpenGammaRuntimeException("Cannot find parent '" + sovereignity + "' for '" + name + "'");
}
region.getParentRegionIds().add(parent.getUniqueId());
}
for (Entry<UniqueId, Set<String>> entry : subRegions.entrySet()) {
if (entry.getValue().remove(name)) {
region.getParentRegionIds().add(entry.getKey());
}
}
// store
RegionDocument doc = getRegionMaster().add(new RegionDocument(region));
if (rowSubRegions.size() > 0) {
subRegions.put(doc.getUniqueId(), rowSubRegions);
}
regions.put(name, region);
}
for (Set<String> set : subRegions.values()) {
if (set.size() > 0) {
throw new OpenGammaRuntimeException("Cannot find children: " + set);
}
}
} catch (Exception ex) {
String detail = (name != null ? " while processing " + name : "");
throw new OpenGammaRuntimeException("Cannot open region data file (or file I/O problem)" + detail, ex);
}
}
/**
* Trim the contents of a set.
* @param subRegions the set to trim, not null
* @return the trimmed set, not null
*/
private Set<String> trim(Set<String> subRegions) {
Set<String> result = new HashSet<String>();
for (String subRegion : subRegions) {
String trimmed = subRegion.trim();
if (trimmed.isEmpty() == false) {
result.add(trimmed);
}
}
return result;
}
}