/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.master.holiday.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.threeten.bp.LocalDate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.opengamma.core.holiday.Holiday;
import com.opengamma.core.holiday.HolidaySource;
import com.opengamma.core.holiday.HolidayType;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.VersionCorrection;
import com.opengamma.master.AbstractMasterSource;
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.service.ServiceContext;
import com.opengamma.service.ThreadLocalServiceContext;
import com.opengamma.service.VersionCorrectionProvider;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.PublicSPI;
import com.opengamma.util.money.Currency;
/**
* A {@code HolidaySource} implemented using an underlying {@code HolidayMaster}.
* <p>
* The {@link HolidaySource} interface provides holidays to the application via a narrow API.
* This class provides the source on top of a standard {@link HolidayMaster}.
*/
@PublicSPI
public class MasterHolidaySource
extends AbstractMasterSource<Holiday, HolidayDocument, HolidayMaster>
implements HolidaySource {
// an empty set of dates
private static final ImmutableSet<LocalDate> EMPTY = ImmutableSet.of();
private final boolean _cacheHolidayCalendars;
private final ConcurrentMap<HolidaySearchRequest, ImmutableSet<LocalDate>> _cachedHolidays = new ConcurrentHashMap<>();
/**
* Creates an instance with an underlying master.
*
* @param master the master, not null
*/
public MasterHolidaySource(final HolidayMaster master) {
this(master, false);
}
/**
* Creates an instance with an underlying master.
*
* @param master the master, not null
* @param cacheCalendars whether all calendars should be cached
*/
public MasterHolidaySource(final HolidayMaster master, final boolean cacheCalendars) {
super(master);
_cacheHolidayCalendars = cacheCalendars;
}
//-------------------------------------------------------------------------
@Override
public Collection<Holiday> get(HolidayType holidayType,
ExternalIdBundle regionOrExchangeIds) {
HolidaySearchRequest request = createNonCurrencySearchRequest(holidayType, regionOrExchangeIds);
return processDocuments(getMaster().search(request));
}
@Override
public Collection<Holiday> get(Currency currency) {
HolidaySearchRequest request = createCurrencySearchRequest(currency);
return processDocuments(getMaster().search(request));
}
private Collection<Holiday> processDocuments(HolidaySearchResult search) {
return ImmutableList.<Holiday>copyOf(search.getHolidays());
}
@Override
public boolean isHoliday(LocalDate dateToCheck, Currency currency) {
if (_cacheHolidayCalendars) {
ArgumentChecker.notNull(dateToCheck, "dateToCheck");
ArgumentChecker.notNull(currency, "currency");
if (isWeekend(dateToCheck)) {
return true;
}
}
HolidaySearchRequest request = createCurrencySearchRequest(currency);
return isHoliday(request, dateToCheck);
}
@Override
public boolean isHoliday(LocalDate dateToCheck, HolidayType holidayType, ExternalIdBundle regionOrExchangeIds) {
if (_cacheHolidayCalendars) {
ArgumentChecker.notNull(dateToCheck, "dateToCheck");
ArgumentChecker.notNull(holidayType, "holidayType");
ArgumentChecker.notNull(regionOrExchangeIds, "regionOrExchangeIds");
if (isWeekend(dateToCheck)) {
return true;
}
}
HolidaySearchRequest request = createNonCurrencySearchRequest(holidayType, regionOrExchangeIds);
return isHoliday(request, dateToCheck);
}
private VersionCorrection getVersionCorrection() {
ServiceContext serviceContext = ThreadLocalServiceContext.getInstance();
return serviceContext.get(VersionCorrectionProvider.class).getConfigVersionCorrection();
}
private HolidaySearchRequest createCurrencySearchRequest(Currency currency) {
return createdVersionCorrectedSearchRequest(new HolidaySearchRequest(currency));
}
private HolidaySearchRequest createNonCurrencySearchRequest(HolidayType holidayType,
ExternalIdBundle regionOrExchangeIds) {
return createdVersionCorrectedSearchRequest(new HolidaySearchRequest(holidayType, regionOrExchangeIds));
}
private HolidaySearchRequest createdVersionCorrectedSearchRequest(HolidaySearchRequest searchRequest) {
searchRequest.setVersionCorrection(getVersionCorrection());
return searchRequest;
}
@Override
public boolean isHoliday(final LocalDate dateToCheck, final HolidayType holidayType, final ExternalId regionOrExchangeId) {
if (_cacheHolidayCalendars) {
ArgumentChecker.notNull(dateToCheck, "dateToCheck");
ArgumentChecker.notNull(holidayType, "holidayType");
ArgumentChecker.notNull(regionOrExchangeId, "regionOrExchangeId");
if (isWeekend(dateToCheck)) {
return true;
}
}
HolidaySearchRequest request = new HolidaySearchRequest(holidayType, ExternalIdBundle.of(regionOrExchangeId));
return isHoliday(request, dateToCheck);
}
//-------------------------------------------------------------------------
/**
* Checks if the specified date is a holiday.
*
* @param request the request to search base on, not null
* @param dateToCheck the date to check, not null
* @return true if the date is a holiday
*/
protected boolean isHoliday(final HolidaySearchRequest request, final LocalDate dateToCheck) {
ArgumentChecker.notNull(request, "request");
ArgumentChecker.notNull(dateToCheck, "dateToCheck");
if (isWeekend(dateToCheck)) {
return true;
}
if (_cacheHolidayCalendars) {
ImmutableSet<LocalDate> cachedDates = _cachedHolidays.get(request);
if (cachedDates != null) {
return cachedDates.contains(dateToCheck);
}
// get all holidays and cache
HolidayDocument doc = getMaster().search(request).getFirstDocument();
HolidaySearchRequest cacheKey = request.clone();
if (doc == null) {
_cachedHolidays.put(cacheKey, EMPTY);
} else {
_cachedHolidays.put(cacheKey, ImmutableSet.copyOf(doc.getHoliday().getHolidayDates()));
}
return isHoliday(doc, dateToCheck);
}
// Not caching, search for this date only.
request.setDateToCheck(dateToCheck);
HolidayDocument doc = getMaster().search(request).getFirstDocument();
return isHoliday(doc, dateToCheck);
}
/**
* Checks if the specified date is a holiday.
*
* @param doc document retrieved from underlying holiday master, may be null
* @param dateToCheck the date to check, not null
* @return false if nothing was retrieved from underlying holiday master.
* Otherwise, true if and only if the date is a holiday based on the underlying holiday master
*/
protected boolean isHoliday(final HolidayDocument doc, final LocalDate dateToCheck) {
if (doc == null) {
return false;
}
return Collections.binarySearch(doc.getHoliday().getHolidayDates(), dateToCheck) >= 0;
}
/**
* Checks if the date is at the weekend, defined as a Saturday or Sunday.
*
* @param date the date to check, not null
* @return true if it is a weekend
*/
protected boolean isWeekend(LocalDate date) {
// avoids calling date.getDayOfWeek() twice
return date.getDayOfWeek().getValue() >= 6;
}
}