/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.basics.date;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.opengamma.strata.collect.named.NamedLookup;
/**
* Day count convention for 'Bus/252'.
* <p>
* This day count is based on a holiday calendar, which is stored in the day count
* and referenced in the name.
*/
final class Business252DayCount implements NamedLookup<DayCount> {
/**
* The singleton instance of the lookup.
*/
public static final Business252DayCount INSTANCE = new Business252DayCount();
/**
* The cache of day count by name.
*/
private static final ConcurrentMap<String, DayCount> BY_NAME = new ConcurrentHashMap<>();
/**
* The cache of day count by calendar.
*/
private static final ConcurrentMap<String, DayCount> BY_CALENDAR = new ConcurrentHashMap<>();
/**
* Restricted constructor.
*/
private Business252DayCount() {
}
// obtains the day count
DayCount of(HolidayCalendar calendar) {
return BY_CALENDAR.computeIfAbsent(calendar.getName(), this::createByCalendarName);
}
private DayCount createByCalendarName(String calendarName) {
return lookup("Bus/252 " + calendarName);
}
//-------------------------------------------------------------------------
@Override
public DayCount lookup(String name) {
DayCount value = BY_NAME.get(name);
if (value == null) {
if (name.regionMatches(true, 0, "Bus/252 ", 0, 8)) {
HolidayCalendar cal = HolidayCalendars.of(name.substring(8)); // load from standard calendars
String correctName = "Bus/252 " + cal.getName();
DayCount created = new Bus252(correctName, cal);
value = BY_NAME.computeIfAbsent(correctName, k -> created);
BY_NAME.putIfAbsent(correctName.toUpperCase(Locale.ENGLISH), created);
}
}
return value;
}
@Override
public Map<String, DayCount> lookupAll() {
return BY_NAME;
}
//-------------------------------------------------------------------------
/**
* Implementation of the day-of-month roll convention.
*/
static final class Bus252 implements DayCount, Serializable {
private static final long serialVersionUID = 1L;
private final String name;
private final transient HolidayCalendar calendar;
Bus252(String name, HolidayCalendar calendar) {
this.name = name;
this.calendar = calendar;
}
// resolve instance
private Object readResolve() {
return DayCount.of(name);
}
@Override
public double yearFraction(LocalDate firstDate, LocalDate secondDate, ScheduleInfo scheduleInfo) {
if (secondDate.isBefore(firstDate)) {
throw new IllegalArgumentException("Dates must be in time-line order");
}
return calendar.daysBetween(firstDate, secondDate) / 252d;
}
@Override
public int days(LocalDate firstDate, LocalDate secondDate) {
if (secondDate.isBefore(firstDate)) {
throw new IllegalArgumentException("Dates must be in time-line order");
}
return calendar.daysBetween(firstDate, secondDate);
}
//-------------------------------------------------------------------------
@Override
public String getName() {
return name;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Bus252) {
return ((Bus252) obj).name.equals(name);
}
return false;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public String toString() {
return name;
}
}
}