com.opengamma.integration.coppclark.CoppClarkHolidayFileReader.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.integration.coppclark.CoppClarkHolidayFileReader.java

Source

/**
 * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.integration.coppclark;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.threeten.bp.LocalDate;
import org.threeten.bp.format.DateTimeFormatter;

import au.com.bytecode.opencsv.CSVReader;

import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.core.holiday.HolidaySource;
import com.opengamma.core.holiday.HolidayType;
import com.opengamma.core.holiday.impl.NonVersionedRedisHolidaySource;
import com.opengamma.core.holiday.impl.SimpleHoliday;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalScheme;
import com.opengamma.master.holiday.HolidayDocument;
import com.opengamma.master.holiday.HolidayMaster;
import com.opengamma.master.holiday.HolidaySearchRequest;
import com.opengamma.master.holiday.HolidaySearchResult;
import com.opengamma.master.holiday.ManageableHoliday;
import com.opengamma.master.holiday.impl.InMemoryHolidayMaster;
import com.opengamma.master.holiday.impl.MasterHolidaySource;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;

/**
 * Reads the holiday data from the Copp-Clark data source.
 * <p>
 * This will merge the input with the data already in the database.
 */
public class CoppClarkHolidayFileReader {

    /**
     * The Copp Clark scheme.
     */
    private static final ExternalScheme COPP_CLARK_SCHEME = ExternalScheme.of("COPP_CLARK");
    /**
     * The date format.
     */
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");
    /**
     * An empty list of dates.
     */
    private static final List<LocalDate> EMPTY_DATE_LIST = Collections.emptyList();
    /**
     * The file location of the resource.
     */
    private static final String HOLIDAY_RESOURCE_PACKAGE = "/com/coppclark/holiday/";
    /**
     * The file location of the index file.
     */
    private static final String HOLIDAY_INDEX_RESOURCE = HOLIDAY_RESOURCE_PACKAGE + "Index.txt";
    /**
     * The Euro.
     */
    private static final Currency EUR = Currency.EUR;
    /**
     * The map of Euro capitals to currencies.
     */
    private static final Map<String, Currency> EURO_TO_OLD = new HashMap<String, Currency>();
    static {
        EURO_TO_OLD.put("AT", Currency.of("ATS"));
        EURO_TO_OLD.put("BE", Currency.of("BEF"));
        EURO_TO_OLD.put("NL", Currency.of("NLG"));
        EURO_TO_OLD.put("DE", Currency.of("DEM"));
        EURO_TO_OLD.put("FI", Currency.of("FIM"));
        EURO_TO_OLD.put("FR", Currency.of("FRF"));
        EURO_TO_OLD.put("IE", Currency.of("IEP"));
        EURO_TO_OLD.put("IT", Currency.of("ITL"));
        EURO_TO_OLD.put("LU", Currency.of("LUF"));
        EURO_TO_OLD.put("MC", Currency.of("MCF"));
        EURO_TO_OLD.put("PT", Currency.of("PTE"));
        EURO_TO_OLD.put("SM", Currency.of("SML"));
        EURO_TO_OLD.put("ES", Currency.of("ESP"));
        EURO_TO_OLD.put("VA", Currency.of("VAL"));
        EURO_TO_OLD.put("GR", Currency.of("GRD"));
        EURO_TO_OLD.put("SI", Currency.of("SIT"));
        EURO_TO_OLD.put("CY", Currency.of("CYP"));
        EURO_TO_OLD.put("MT", Currency.of("MTL"));
        EURO_TO_OLD.put("SK", Currency.of("SKK"));
        EURO_TO_OLD.put("EE", Currency.of("EEK"));
    }

    /**
     * The streams to load the currency data.
     */
    private final List<InputStream> _currencyStreams = new ArrayList<InputStream>();
    /**
     * The streams to load the financial centers data.
     */
    private final List<InputStream> _financialCentresStreams = new ArrayList<InputStream>();
    /**
     * The streams to load the exchange settlement data.
     */
    private final List<InputStream> _exchangeSettlementStreams = new ArrayList<InputStream>();
    /**
     * The streams to load the exchange trading data.
     */
    private final List<InputStream> _exchangeTradingStreams = new ArrayList<InputStream>();
    /**
     * The holiday master to populate.
     */
    private HolidayMaster _holidayMaster;

    /**
     * Creates a populated holiday source around the specified master.
     * 
     * @param holidayMaster  the holiday master to populate, not null
     * @return the holiday source, not null
     */
    public static HolidaySource createPopulated(HolidayMaster holidayMaster) {
        CoppClarkHolidayFileReader fileReader = createPopulated0(holidayMaster);
        return fileReader.getHolidaySource();
    }

    public static NonVersionedRedisHolidaySource createPopulated(
            NonVersionedRedisHolidaySource redisHolidaySource) {
        ArgumentChecker.notNull(redisHolidaySource, "redisHolidaySource");
        final InMemoryHolidayMaster inMemoryHolidayMaster = new InMemoryHolidayMaster();
        createPopulated(inMemoryHolidayMaster);

        HolidaySearchResult holidaySearchResult = inMemoryHolidayMaster.search(new HolidaySearchRequest());
        List<ManageableHoliday> holidays = holidaySearchResult.getHolidays();

        for (ManageableHoliday manageableHoliday : holidays) {
            SimpleHoliday simpleHoliday = new SimpleHoliday();
            simpleHoliday.setCurrency(manageableHoliday.getCurrency());
            simpleHoliday.setExchangeExternalId(manageableHoliday.getExchangeExternalId());
            simpleHoliday.setHolidayDates(manageableHoliday.getHolidayDates());
            simpleHoliday.setRegionExternalId(manageableHoliday.getRegionExternalId());
            simpleHoliday.setType(manageableHoliday.getType());
            redisHolidaySource.addHoliday(simpleHoliday);
        }
        return redisHolidaySource;
    }

    /**
     * Creates a populated file reader.
     * <p>
     * The values can be extracted using the methods.
     * 
     * @param holidayMaster  the holiday master to populate, not null
     * @return the holiday reader, not null
     */
    private static CoppClarkHolidayFileReader createPopulated0(HolidayMaster holidayMaster) {
        CoppClarkHolidayFileReader fileReader = new CoppClarkHolidayFileReader(holidayMaster);
        InputStream indexStream = fileReader.getClass().getResourceAsStream(HOLIDAY_INDEX_RESOURCE);
        if (indexStream == null) {
            throw new IllegalArgumentException("Unable to find holiday index resource: " + HOLIDAY_INDEX_RESOURCE);
        }
        try {
            List<String> suffixes = IOUtils.readLines(indexStream, "UTF-8");
            for (String suffix : suffixes) {
                if (StringUtils.isNotEmpty(suffix)) {
                    String prefix = "";
                    if (suffix.startsWith("U")) {
                        prefix = "Update_";
                        suffix = suffix.substring(1);
                    }
                    prefix = HOLIDAY_RESOURCE_PACKAGE + prefix;
                    suffix += ".csv";
                    InputStream currencyStream = fileReader.getClass()
                            .getResourceAsStream(prefix + "Currencies_" + suffix);
                    if (currencyStream == null) {
                        throw new IllegalArgumentException(
                                "Unable to find holiday data resource: " + prefix + "Currencies_" + suffix);
                    }
                    fileReader.getCurrencyStreams().add(currencyStream);

                    InputStream financialCentersStream = fileReader.getClass()
                            .getResourceAsStream(prefix + "FinancialCentres_" + suffix);
                    if (financialCentersStream == null) {
                        throw new IllegalArgumentException(
                                "Unable to find holiday data resource: " + prefix + "FinancialCentres_" + suffix);
                    }
                    fileReader.getFinancialCentresStreams().add(financialCentersStream);

                    InputStream exchangeSettlementStream = fileReader.getClass()
                            .getResourceAsStream(prefix + "ExchangeSettlement_" + suffix);
                    if (exchangeSettlementStream == null) {
                        throw new IllegalArgumentException(
                                "Unable to find holiday data resource: " + prefix + "ExchangeSettlement_" + suffix);
                    }
                    fileReader.getExchangeSettlementStreams().add(exchangeSettlementStream);

                    InputStream exchangeTradingStream = fileReader.getClass()
                            .getResourceAsStream(prefix + "ExchangeTrading_" + suffix);
                    if (exchangeTradingStream == null) {
                        throw new IllegalArgumentException(
                                "Unable to find holiday data resource: " + prefix + "ExchangeTrading_" + suffix);
                    }
                    fileReader.getExchangeTradingStreams().add(exchangeTradingStream);
                }
            }
            try {
                fileReader.read();
                return fileReader;
            } finally {
                fileReader.close();
            }

        } catch (IOException ex) {
            throw new OpenGammaRuntimeException("Unable to read holiday file", ex);
        } finally {
            IOUtils.closeQuietly(indexStream);
        }
    }

    //-------------------------------------------------------------------------
    /**
     * Creates an instance with a master to populate.
     * 
     * @param holidayMaster  the holiday master, not null
     */
    public CoppClarkHolidayFileReader(HolidayMaster holidayMaster) {
        ArgumentChecker.notNull(holidayMaster, "holidayMaster");
        _holidayMaster = holidayMaster;
    }

    //-------------------------------------------------------------------------
    /**
     * Gets the holiday master.
     * 
     * @return the holiday master, not null
     */
    public HolidayMaster getHolidayMaster() {
        return _holidayMaster;
    }

    /**
     * Gets the holiday source.
     * 
     * @return the holiday source, not null
     */
    public MasterHolidaySource getHolidaySource() {
        return new MasterHolidaySource(getHolidayMaster());
    }

    /**
     * Gets the currency streams.
     * 
     * @return the list of streams
     */
    public List<InputStream> getCurrencyStreams() {
        return _currencyStreams;
    }

    /**
     * Gets the financial centres streams.
     * 
     * @return the list of streams
     */
    public List<InputStream> getFinancialCentresStreams() {
        return _financialCentresStreams;
    }

    /**
     * Gets the exchange settlement streams.
     * 
     * @return the list of streams
     */
    public List<InputStream> getExchangeSettlementStreams() {
        return _exchangeSettlementStreams;
    }

    /**
     * Gets the exchange trading streams.
     * 
     * @return the list of streams
     */
    public List<InputStream> getExchangeTradingStreams() {
        return _exchangeTradingStreams;
    }

    //-------------------------------------------------------------------------
    /**
     * Reads the streams to create the master.
     */
    public void read() {
        try {
            parseCurrencyFile();
            parseFinancialCentersFile();
            parseExchangeSettlementFile();
            parseExchangeTradingFile();
        } catch (IOException ioe) {
            throw new OpenGammaRuntimeException("Problem parsing holiday data files", ioe);
        }
    }

    /**
     * Closes the streams.
     */
    public void close() {
        for (InputStream stream : getCurrencyStreams()) {
            IOUtils.closeQuietly(stream);
        }
        for (InputStream stream : getFinancialCentresStreams()) {
            IOUtils.closeQuietly(stream);
        }
        for (InputStream stream : getExchangeSettlementStreams()) {
            IOUtils.closeQuietly(stream);
        }
        for (InputStream stream : getExchangeTradingStreams()) {
            IOUtils.closeQuietly(stream);
        }
    }

    //-------------------------------------------------------------------------
    private void parseCurrencyFile() throws IOException {
        System.out.println("Parse currencies");
        Map<String, HolidayDocument> combinedMap = new HashMap<String, HolidayDocument>(512);

        for (InputStream stream : getCurrencyStreams()) {
            Map<String, HolidayDocument> fileMap = new HashMap<String, HolidayDocument>(512);
            @SuppressWarnings("resource")
            CSVReader reader = new CSVReader(new InputStreamReader(new BufferedInputStream(stream)));

            // header
            String[] row = reader.readNext();
            final int ccIdx = ArrayUtils.indexOf(row, "CenterID");
            final int isoCountryIdx = ArrayUtils.indexOf(row, "ISOCountryCode");
            final int isoCurrencyIdx = ArrayUtils.indexOf(row, "ISOCurrencyCode");
            final int eventDateIdx = ArrayUtils.indexOf(row, "EventDate");

            // data
            while ((row = reader.readNext()) != null) {
                String ccId = row[ccIdx].trim();
                String countryISO = row[isoCountryIdx].trim();
                String currencyISO = row[isoCurrencyIdx].trim();
                Currency currency = Currency.of(currencyISO); // validates format
                String eventDateStr = row[eventDateIdx];
                LocalDate eventDate = LocalDate.parse(eventDateStr, DATE_FORMAT);
                HolidayDocument doc = fileMap.get(ccId);
                if (doc == null) {
                    currency = fixEuro(currency, countryISO);
                    doc = new HolidayDocument(new ManageableHoliday(currency, EMPTY_DATE_LIST));
                    doc.setProviderId(ExternalId.of(COPP_CLARK_SCHEME, ccId));
                    fileMap.put(ccId, doc);
                }
                doc.getHoliday().getHolidayDates().add(eventDate);
            }
            merge(combinedMap, fileMap);
        }
        mergeWithDatabase(combinedMap);
    }

    private Currency fixEuro(Currency currency, String countryISO) {
        // historic data has lots mapped to EUR, which isn't very useful
        if (countryISO.equals("EU") || currency.equals(EUR) == false) {
            return currency;
        }
        Currency newCurrency = EURO_TO_OLD.get(countryISO);
        if (newCurrency == null) {
            throw new OpenGammaRuntimeException(
                    "EUR currency found for " + countryISO + " without mapping to true currency");
        }
        return newCurrency;
    }

    private void parseFinancialCentersFile() throws IOException {
        System.out.println("Parse financial centres");
        Map<String, HolidayDocument> combinedMap = new HashMap<String, HolidayDocument>(512);

        for (InputStream stream : getFinancialCentresStreams()) {
            Map<String, HolidayDocument> fileMap = new HashMap<String, HolidayDocument>(512);
            @SuppressWarnings("resource")
            CSVReader reader = new CSVReader(new InputStreamReader(new BufferedInputStream(stream)));

            // header
            String[] row = reader.readNext();
            final int ccIdx = ArrayUtils.indexOf(row, "CenterID");
            final int isoCountryIdx = ArrayUtils.indexOf(row, "ISOCountryCode");
            final int unlocodeIdx = ArrayUtils.indexOf(row, "UN/LOCODE");
            final int eventDateIdx = ArrayUtils.indexOf(row, "EventDate");

            // data
            while ((row = reader.readNext()) != null) {
                String ccId = row[ccIdx].trim();
                String countryISO = row[isoCountryIdx].trim();
                String unlocodePart = row[unlocodeIdx].trim();
                ExternalId regionId = ExternalSchemes.coppClarkRegionId(countryISO + unlocodePart);
                String eventDateStr = row[eventDateIdx];
                LocalDate eventDate = LocalDate.parse(eventDateStr, DATE_FORMAT);
                HolidayDocument doc = fileMap.get(ccId);
                if (doc == null) {
                    doc = new HolidayDocument(new ManageableHoliday(HolidayType.BANK, regionId, EMPTY_DATE_LIST));
                    doc.setProviderId(ExternalId.of(COPP_CLARK_SCHEME, ccId));
                    fileMap.put(ccId, doc);
                }
                doc.getHoliday().getHolidayDates().add(eventDate);
            }
            merge(combinedMap, fileMap);
        }
        mergeWithDatabase(combinedMap);
    }

    private void parseExchangeSettlementFile() throws IOException {
        System.out.println("Parse exchange settlements");
        Map<String, HolidayDocument> combinedMap = new HashMap<String, HolidayDocument>(512);

        for (InputStream stream : getExchangeSettlementStreams()) {
            Map<String, HolidayDocument> fileMap = new HashMap<String, HolidayDocument>(512);
            @SuppressWarnings("resource")
            CSVReader reader = new CSVReader(new InputStreamReader(new BufferedInputStream(stream)));

            // header
            String[] row = reader.readNext();
            final int ccIdx = ArrayUtils.indexOf(row, "CenterID");
            final int isoMICCodeIdx = ArrayUtils.indexOf(row, "ISO MIC Code");
            final int eventDateIdx = ArrayUtils.indexOf(row, "EventDate");

            // data
            while ((row = reader.readNext()) != null) {
                String ccId = row[ccIdx].trim();
                String isoMICCode = row[isoMICCodeIdx].trim();
                ExternalId micId = ExternalSchemes.isoMicExchangeId(isoMICCode);
                String eventDateStr = row[eventDateIdx];
                LocalDate eventDate = LocalDate.parse(eventDateStr, DATE_FORMAT);
                HolidayDocument doc = fileMap.get(ccId);
                if (doc == null) {
                    doc = new HolidayDocument(
                            new ManageableHoliday(HolidayType.SETTLEMENT, micId, EMPTY_DATE_LIST));
                    doc.setProviderId(ExternalId.of(COPP_CLARK_SCHEME, ccId));
                    fileMap.put(ccId, doc);
                }
                doc.getHoliday().getHolidayDates().add(eventDate);
            }
            merge(combinedMap, fileMap);
        }
        mergeWithDatabase(combinedMap);
    }

    private void parseExchangeTradingFile() throws IOException {
        System.out.println("Parse exchange trading");
        Map<String, HolidayDocument> combinedMap = new HashMap<String, HolidayDocument>(512);

        for (InputStream stream : getExchangeTradingStreams()) {
            Map<String, HolidayDocument> fileMap = new HashMap<String, HolidayDocument>(512);
            @SuppressWarnings("resource")
            CSVReader reader = new CSVReader(new InputStreamReader(new BufferedInputStream(stream)));

            // header
            String[] row = reader.readNext();
            final int ccIdx = ArrayUtils.indexOf(row, "CenterID");
            final int isoMICCodeIdx = ArrayUtils.indexOf(row, "ISO MIC Code");
            final int eventDateIdx = ArrayUtils.indexOf(row, "EventDate");

            // data
            while ((row = reader.readNext()) != null) {
                String ccId = row[ccIdx].trim();
                String isoMICCode = row[isoMICCodeIdx].trim();
                ExternalId micId = ExternalSchemes.isoMicExchangeId(isoMICCode);
                String eventDateStr = row[eventDateIdx];
                LocalDate eventDate = LocalDate.parse(eventDateStr, DATE_FORMAT);
                HolidayDocument doc = fileMap.get(ccId);
                if (doc == null) {
                    doc = new HolidayDocument(new ManageableHoliday(HolidayType.TRADING, micId, EMPTY_DATE_LIST));
                    doc.setProviderId(ExternalId.of(COPP_CLARK_SCHEME, ccId));
                    fileMap.put(ccId, doc);
                }
                doc.getHoliday().getHolidayDates().add(eventDate);
            }
            merge(combinedMap, fileMap);
        }
        mergeWithDatabase(combinedMap);
    }

    //-------------------------------------------------------------------------
    private void merge(Map<String, HolidayDocument> combinedMap, Map<String, HolidayDocument> newMap) {
        for (String id : newMap.keySet()) {
            HolidayDocument newDoc = newMap.get(id);
            Collections.sort(newDoc.getHoliday().getHolidayDates());
            HolidayDocument combinedDoc = combinedMap.get(id);
            if (combinedDoc == null) {
                combinedMap.put(id, newDoc);
            } else {
                mergeDates(combinedDoc, newDoc);
                combinedMap.put(id, newDoc);
            }
        }
    }

    private void mergeWithDatabase(Map<String, HolidayDocument> map) {
        Set<String> set = new HashSet<String>();
        for (HolidayDocument doc : map.values()) {
            if (set.add(doc.getName()) == false) {
                System.out.println("Duplicate name: " + doc.getName());
                System.exit(0);
            }
        }
        for (HolidayDocument doc : map.values()) {
            Collections.sort(doc.getHoliday().getHolidayDates());
            HolidaySearchRequest search = new HolidaySearchRequest(doc.getHoliday().getType());
            search.setProviderId(doc.getProviderId());
            HolidaySearchResult result = _holidayMaster.search(search);
            if (result.getDocuments().size() == 0) {
                // add new data
                _holidayMaster.add(doc);
            } else if (result.getDocuments().size() == 1) {
                // update existing data
                HolidayDocument existing = result.getFirstDocument();
                doc.setUniqueId(existing.getUniqueId());
                doc.getHoliday().setUniqueId(existing.getUniqueId());
                mergeDates(existing, doc);
                // only update if changed
                doc.setVersionFromInstant(null);
                doc.setVersionToInstant(null);
                doc.setCorrectionFromInstant(null);
                doc.setCorrectionToInstant(null);
                existing.setVersionFromInstant(null);
                existing.setVersionToInstant(null);
                existing.setCorrectionFromInstant(null);
                existing.setCorrectionToInstant(null);
                if (doc.equals(existing) == false) { // only update if changed
                    _holidayMaster.update(doc);
                }
            } else {
                throw new IllegalStateException("Multiple rows in database for Copp Clark ID: "
                        + doc.getProviderId().getValue() + " " + doc.getHoliday().getType());
            }
        }
    }

    private void mergeDates(HolidayDocument existingDoc, HolidayDocument newDoc) {
        if (newDoc.getHoliday().getHolidayDates().size() == 0) {
            return;
        }

        // merge dates
        SortedSet<LocalDate> existingDates = new TreeSet<LocalDate>(existingDoc.getHoliday().getHolidayDates());
        SortedSet<LocalDate> newDates = new TreeSet<LocalDate>(newDoc.getHoliday().getHolidayDates());
        List<LocalDate> result = new ArrayList<LocalDate>(newDates);
        result.addAll(0, existingDates.headSet(newDates.first()));
        result.addAll(existingDates.tailSet(newDates.last().plusYears(1).withDayOfYear(1))); // file is based on whole years

        // store into new document
        newDoc.getHoliday().getHolidayDates().clear();
        newDoc.getHoliday().getHolidayDates().addAll(result);
    }

}