/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.basics.schedule; import java.io.Serializable; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.temporal.TemporalAdjusters; import java.util.Locale; import com.google.common.collect.ImmutableMap; import com.opengamma.strata.collect.ArgChecker; import com.opengamma.strata.collect.named.NamedLookup; /** * Standard roll convention implementations. * <p> * See {@link RollConventions} for the description of each. */ final class DayRollConventions implements NamedLookup<RollConvention> { // lookup of conventions static final ImmutableMap<String, RollConvention> MAP; static { ImmutableMap.Builder<String, RollConvention> mapBuilder = ImmutableMap.builder(); for (RollConvention roll : Dom.CONVENTIONS) { mapBuilder.put(roll.getName(), roll); mapBuilder.put(roll.getName().toUpperCase(Locale.ENGLISH), roll); } for (RollConvention roll : Dow.CONVENTIONS) { mapBuilder.put(roll.getName(), roll); mapBuilder.put(roll.getName().toUpperCase(Locale.ENGLISH), roll); } MAP = mapBuilder.build(); } /** * Restricted constructor. */ private DayRollConventions() { } //------------------------------------------------------------------------- @Override public ImmutableMap<String, RollConvention> lookupAll() { return MAP; } //------------------------------------------------------------------------- /** * Implementation of the day-of-month roll convention. */ static final class Dom implements RollConvention, Serializable { // singleton, so no equals/hashCode // Serialization version private static final long serialVersionUID = 1L; // cache of conventions private static final RollConvention[] CONVENTIONS = new RollConvention[30]; static { for (int i = 0; i < 30; i++) { CONVENTIONS[i] = new Dom(i + 1); } } // day-of-month private final int day; // unique name private final String name; // obtains instance static RollConvention of(int day) { if (day == 31) { return RollConventions.EOM; } else if (day < 1 || day > 30) { throw new IllegalArgumentException("Invalid day-of-month: " + day); } return CONVENTIONS[day - 1]; } // create private Dom(int day) { this.day = day; this.name = "Day" + day; } private Object readResolve() { return Dom.of(day); } @Override public int getDayOfMonth() { return day; } @Override public LocalDate adjust(LocalDate date) { ArgChecker.notNull(date, "date"); if (day >= 29 && date.getMonthValue() == 2) { return date.withDayOfMonth(date.lengthOfMonth()); } return date.withDayOfMonth(day); } @Override public boolean matches(LocalDate date) { ArgChecker.notNull(date, "date"); return date.getDayOfMonth() == day || (date.getMonthValue() == 2 && day >= date.lengthOfMonth() && date.getDayOfMonth() == date.lengthOfMonth()); } @Override public String getName() { return name; } @Override public String toString() { return name; } } //------------------------------------------------------------------------- /** * Implementation of the day-of-week roll convention. */ static final class Dow implements RollConvention, Serializable { // singleton, so no equals/hashCode // Serialization version private static final long serialVersionUID = 1L; // convention names private static final String NAMES = "DayMonDayTueDayWedDayThuDayFriDaySatDaySun"; // cache of conventions private static final RollConvention[] CONVENTIONS = new RollConvention[7]; static { for (int i = 0; i < 7; i++) { DayOfWeek dow = DayOfWeek.of(i + 1); String name = NAMES.substring(i * 6, (i + 1) * 6); CONVENTIONS[i] = new Dow(dow, name); } } // day-of-week private final DayOfWeek day; // unique name private final String name; // obtains instance static RollConvention of(DayOfWeek dayOfWeek) { ArgChecker.notNull(dayOfWeek, "dayOfWeek"); return CONVENTIONS[dayOfWeek.getValue() - 1]; } private Object readResolve() { return Dow.of(day); } // create private Dow(DayOfWeek dayOfWeek, String name) { this.day = dayOfWeek; this.name = name; } @Override public LocalDate adjust(LocalDate date) { ArgChecker.notNull(date, "date"); return date.with(TemporalAdjusters.nextOrSame(day)); } @Override public boolean matches(LocalDate date) { ArgChecker.notNull(date, "date"); return date.getDayOfWeek() == day; } @Override public LocalDate next(LocalDate date, Frequency periodicFrequency) { ArgChecker.notNull(date, "date"); ArgChecker.notNull(periodicFrequency, "periodicFrequency"); LocalDate calculated = date.plus(periodicFrequency); return calculated.with(TemporalAdjusters.nextOrSame(day)); } @Override public LocalDate previous(LocalDate date, Frequency periodicFrequency) { ArgChecker.notNull(date, "date"); ArgChecker.notNull(periodicFrequency, "periodicFrequency"); LocalDate calculated = date.minus(periodicFrequency); return calculated.with(TemporalAdjusters.previousOrSame(day)); } @Override public String getName() { return name; } @Override public String toString() { return name; } } }