com.opengamma.strata.basics.date.ImmutableHolidayCalendar.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.strata.basics.date.ImmutableHolidayCalendar.java

Source

/**
 * Copyright (C) 2014 - 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.DayOfWeek;
import java.time.LocalDate;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;

import org.joda.beans.Bean;
import org.joda.beans.BeanBuilder;
import org.joda.beans.BeanDefinition;
import org.joda.beans.ImmutableBean;
import org.joda.beans.ImmutableConstructor;
import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaProperty;
import org.joda.beans.Property;
import org.joda.beans.PropertyDefinition;
import org.joda.beans.impl.direct.DirectFieldsBeanBuilder;
import org.joda.beans.impl.direct.DirectMetaBean;
import org.joda.beans.impl.direct.DirectMetaProperty;
import org.joda.beans.impl.direct.DirectMetaPropertyMap;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.collect.ArgChecker;

/**
 * A holiday calendar implementation based on an immutable set of holiday dates and weekends.
 * <p>
 * A standard immutable implementation of {@link HolidayCalendar} that stores all
 * dates that are holidays, plus a list of weekend days.
 * <p>
 * Internally, the class uses a range to determine the range of known holiday dates.
 * Beyond the range of known holiday dates, weekend days are used to determine business days.
 * Dates may be queried from year zero to year 10,000.
 * <p>
 * Applications should refer to holidays using {@link HolidayCalendarId}.
 * The identifier must be {@linkplain HolidayCalendarId#resolve(ReferenceData) resolved}
 * to a {@link HolidayCalendar} before the holiday data methods can be accessed.
 * See {@link HolidayCalendarIds} for a standard set of identifiers available in {@link ReferenceData#standard()}.
 */
@BeanDefinition(builderScope = "private")
public final class ImmutableHolidayCalendar implements HolidayCalendar, ImmutableBean, Serializable {
    // optimized implementation of HolidayCalendar
    // uses an int array where each int represents a month
    // each bit within the int represents a date, where 0 is a holiday and 1 is a business day
    // (most logic involves finding business days, finding 1 is easier than finding 0
    // when using Integer.numberOfTrailingZeros and Integer.numberOfLeadingZeros)
    // benchmarking showed nextOrSame() and previousOrSame() do not need to be overridden
    // out-of-range and weekend-only (used in testing) are handled using exceptions to fast-path the common case

    /**
     * The identifier, such as 'GBLO'.
     */
    @PropertyDefinition(validate = "notNull", overrideGet = true)
    private final HolidayCalendarId id;
    /**
     * The set of holiday dates.
     * <p>
     * Each date in this set is not a business day.
     */
    @PropertyDefinition(validate = "notNull")
    private final ImmutableSortedSet<LocalDate> holidays;
    /**
     * The set of weekend days.
     * <p>
     * Each date that has a day-of-week matching one of these days is not a business day.
     */
    @PropertyDefinition(validate = "notNull")
    private final ImmutableSet<DayOfWeek> weekendDays;
    /**
     * The start year.
     * Used as the base year for the lookup table.
     */
    private final int startYear;
    /**
     * The lookup table, where each item represents a month from January of startYear onwards.
     * Bits 0 to 31 are used for each day-of-month, where 0 is a holiday and 1 is a business day.
     * Trailing bits are set to 0 so they act as holidays, avoiding month length logic.
     */
    private final int[] lookup;

    //-------------------------------------------------------------------------
    /**
     * Obtains an instance from a set of holiday dates and weekend days.
     * <p>
     * The holiday dates will be extracted into a set with duplicates ignored.
     * The minimum supported date for query is the start of the year of the earliest holiday.
     * The maximum supported date for query is the end of the year of the latest holiday.
     * <p>
     * The weekend days may both be the same.
     * 
     * @param id  the identifier
     * @param holidays  the set of holiday dates
     * @param firstWeekendDay  the first weekend day
     * @param secondWeekendDay  the second weekend day, may be same as first
     * @return the holiday calendar
     */
    public static ImmutableHolidayCalendar of(HolidayCalendarId id, Iterable<LocalDate> holidays,
            DayOfWeek firstWeekendDay, DayOfWeek secondWeekendDay) {
        ImmutableSet<DayOfWeek> weekendDays = Sets.immutableEnumSet(firstWeekendDay, secondWeekendDay);
        return new ImmutableHolidayCalendar(id, ImmutableSortedSet.copyOf(holidays), weekendDays);
    }

    /**
     * Obtains an instance from a set of holiday dates and weekend days.
     * <p>
     * The holiday dates will be extracted into a set with duplicates ignored.
     * The minimum supported date for query is the start of the year of the earliest holiday.
     * The maximum supported date for query is the end of the year of the latest holiday.
     * <p>
     * The weekend days may be empty, in which case the holiday dates should contain any weekends.
     * 
     * @param id  the identifier
     * @param holidays  the set of holiday dates
     * @param weekendDays  the days that define the weekend, if empty then weekends are treated as business days
     * @return the holiday calendar
     */
    public static ImmutableHolidayCalendar of(HolidayCalendarId id, Iterable<LocalDate> holidays,
            Iterable<DayOfWeek> weekendDays) {
        return new ImmutableHolidayCalendar(id, ImmutableSortedSet.copyOf(holidays),
                Sets.immutableEnumSet(weekendDays));
    }

    /**
     * Obtains a combined holiday calendar instance.
     * <p>
     * This combines the two input calendars.
     * It is intended for up-front occasional use rather than continuous use, as it is relatively slow.
     * 
     * @param cal1  the first calendar
     * @param cal2  the second calendar
     * @return the combined calendar
     */
    public static ImmutableHolidayCalendar combined(ImmutableHolidayCalendar cal1, ImmutableHolidayCalendar cal2) {
        // do not override combinedWith(), as this is too slow
        if (cal1 == cal2) {
            return ArgChecker.notNull(cal1, "cal1");
        }
        ImmutableSortedSet<LocalDate> newHolidays = ImmutableSortedSet
                .copyOf(Iterables.concat(cal1.holidays, cal2.holidays));
        ImmutableSet<DayOfWeek> newWeekends = ImmutableSet
                .copyOf(Iterables.concat(cal1.weekendDays, cal2.weekendDays));
        return new ImmutableHolidayCalendar(cal1.id.combinedWith(cal2.id), newHolidays, newWeekends);
    }

    //-------------------------------------------------------------------------
    /**
     * Creates an instance calculating the supported range.
     * 
     * @param name  the calendar name
     * @param holidays  the set of holidays, validated non-null
     * @param weekendDays  the set of weekend days, validated non-null
     */
    @ImmutableConstructor
    private ImmutableHolidayCalendar(HolidayCalendarId id, SortedSet<LocalDate> holidays,
            Set<DayOfWeek> weekendDays) {
        ArgChecker.notNull(id, "id");
        ArgChecker.notNull(holidays, "holidays");
        ArgChecker.notNull(weekendDays, "weekendDays");
        this.id = id;
        this.holidays = ImmutableSortedSet.copyOfSorted(holidays);
        this.weekendDays = Sets.immutableEnumSet(weekendDays);
        if (holidays.isEmpty()) {
            // special case where no holiday dates are specified
            this.startYear = 0;
            this.lookup = new int[0];
        } else {
            // normal case where holidays are specified
            this.startYear = holidays.first().getYear();
            int endYearExclusive = holidays.last().getYear() + 1;
            this.lookup = buildLookupArray(holidays, weekendDays, startYear, endYearExclusive);
        }
    }

    // create and populate the int[] lookup
    // use 1 for business days and 0 for holidays
    private static int[] buildLookupArray(SortedSet<LocalDate> holidays, Set<DayOfWeek> weekendDays, int startYear,
            int endYearExclusive) {
        // array that has one entry for each month
        int[] array = new int[(endYearExclusive - startYear) * 12];
        // loop through all months to handle end-of-month and weekends
        LocalDate firstOfMonth = LocalDate.of(startYear, 1, 1);
        for (int i = 0; i < array.length; i++) {
            int monthLen = firstOfMonth.lengthOfMonth();
            // set each valid day-of-month to be a business day
            // the bits for days beyond the end-of-month will be unset and thus treated as non-business days
            // the minus one part converts a single set bit into each lower bit being set
            array[i] = (1 << monthLen) - 1;
            // unset the bits associated with a weekend
            // can unset across whole month using repeating pattern of 7 bits
            // just need to find the offset between the weekend and the day-of-week of the 1st of the month
            for (DayOfWeek weekendDow : weekendDays) {
                int daysDiff = weekendDow.getValue() - firstOfMonth.getDayOfWeek().getValue();
                int offset = (daysDiff < 0 ? daysDiff + 7 : daysDiff);
                array[i] &= ~(0b10000001000000100000010000001 << offset);
            }
            firstOfMonth = firstOfMonth.plusMonths(1);
        }
        // unset the bit associated with each holiday date
        for (LocalDate date : holidays) {
            int index = (date.getYear() - startYear) * 12 + date.getMonthValue() - 1;
            array[index] &= ~(1 << (date.getDayOfMonth() - 1));
        }
        return array;
    }

    //-------------------------------------------------------------------------
    @Override
    public boolean isHoliday(LocalDate date) {
        try {
            // find data for month
            int index = (date.getYear() - startYear) * 12 + date.getMonthValue() - 1;
            // check if bit is 1 at zero-based day-of-month
            return (lookup[index] & (1 << (date.getDayOfMonth() - 1))) == 0;

        } catch (ArrayIndexOutOfBoundsException ex) {
            return isHolidayOutOfRange(date);
        }
    }

    // pulled out to aid hotspot inlining
    private boolean isHolidayOutOfRange(LocalDate date) {
        if (date.getYear() >= 0 && date.getYear() < 10000) {
            return weekendDays.contains(date.getDayOfWeek());
        }
        throw new IllegalArgumentException("Date is outside the accepted range (year 0000 to 10,000): " + date);
    }

    //-------------------------------------------------------------------------
    @Override
    public LocalDate shift(LocalDate date, int amount) {
        try {
            if (amount > 0) {
                // day-of-month: minus one for zero-based day-of-month, plus one to start from next day
                return shiftNext(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), amount);
            } else if (amount < 0) {
                // day-of-month: minus one to start from previous day
                return shiftPrev(date.getYear(), date.getMonthValue(), date.getDayOfMonth() - 1, amount);
            }
            return date;

        } catch (ArrayIndexOutOfBoundsException ex) {
            return shiftOutOfRange(date, amount);
        }
    }

    // pulled out to aid hotspot inlining
    private LocalDate shiftOutOfRange(LocalDate date, int amount) {
        if (date.getYear() >= 0 && date.getYear() < 10000) {
            return HolidayCalendar.super.shift(date, amount);
        }
        throw new IllegalArgumentException("Date is outside the accepted range (year 0000 to 10,000): " + date);
    }

    //-------------------------------------------------------------------------
    @Override
    public LocalDate next(LocalDate date) {
        try {
            // day-of-month: minus one for zero-based day-of-month, plus one to start from next day
            return shiftNext(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), 1);

        } catch (ArrayIndexOutOfBoundsException ex) {
            return HolidayCalendar.super.next(date);
        }
    }

    // shift to a later working day, following nextOrSame semantics
    // input day-of-month is zero-based
    private LocalDate shiftNext(int baseYear, int baseMonth, int baseDom0, int amount) {
        // find data for month
        int index = (baseYear - startYear) * 12 + baseMonth - 1;
        int monthData = lookup[index];
        // loop around amount, the number of days to shift by
        // use domOffset to keep track of day-of-month
        int domOffset = baseDom0;
        for (int amt = amount; amt > 0; amt--) {
            // shift to move the target day-of-month into bit-0, removing earlier days
            int shifted = monthData >> domOffset;
            // recurse to next month if no more business days in the month
            if (shifted == 0) {
                return baseMonth == 12 ? shiftNext(baseYear + 1, 1, 0, amt)
                        : shiftNext(baseYear, baseMonth + 1, 0, amt);
            }
            // find least significant bit, which is next business day
            // use JDK numberOfTrailingZeros() method which is mapped to a fast intrinsic
            domOffset += (Integer.numberOfTrailingZeros(shifted) + 1);
        }
        return LocalDate.of(baseYear, baseMonth, domOffset);
    }

    //-------------------------------------------------------------------------
    @Override
    public LocalDate previous(LocalDate date) {
        try {
            // day-of-month: minus one to start from previous day
            return shiftPrev(date.getYear(), date.getMonthValue(), date.getDayOfMonth() - 1, -1);

        } catch (ArrayIndexOutOfBoundsException ex) {
            return previousOutOfRange(date);
        }
    }

    // shift to an earlier working day, following previousOrSame semantics
    // input day-of-month is one-based and may be zero or negative
    private LocalDate shiftPrev(int baseYear, int baseMonth, int baseDom, int amount) {
        // find data for month
        int index = (baseYear - startYear) * 12 + baseMonth - 1;
        int monthData = lookup[index];
        // loop around amount, the number of days to shift by
        // use domOffset to keep track of day-of-month
        int domOffset = baseDom;
        for (int amt = amount; amt < 0; amt++) {
            // shift to move the target day-of-month into bit-31, removing later days
            int shifted = (monthData << (32 - domOffset));
            // recurse to previous month if no more business days in the month
            if (shifted == 0 || domOffset <= 0) {
                return baseMonth == 1 ? shiftPrev(baseYear - 1, 12, 31, amt)
                        : shiftPrev(baseYear, baseMonth - 1, 31, amt);
            }
            // find most significant bit, which is previous business day
            // use JDK numberOfLeadingZeros() method which is mapped to a fast intrinsic
            domOffset -= (Integer.numberOfLeadingZeros(shifted) + 1);
        }
        return LocalDate.of(baseYear, baseMonth, domOffset + 1);
    }

    // pulled out to aid hotspot inlining
    private LocalDate previousOutOfRange(LocalDate date) {
        if (date.getYear() >= 0 && date.getYear() < 10000) {
            return HolidayCalendar.super.previous(date);
        }
        throw new IllegalArgumentException("Date is outside the accepted range (year 0000 to 10,000): " + date);
    }

    //-------------------------------------------------------------------------
    @Override
    public LocalDate nextSameOrLastInMonth(LocalDate date) {
        try {
            // day-of-month: no alteration as method is one-based and same is valid
            return shiftNextSameLast(date);

        } catch (ArrayIndexOutOfBoundsException ex) {
            return HolidayCalendar.super.nextSameOrLastInMonth(date);
        }
    }

    // shift to a later working day, following nextOrSame semantics
    // falling back to the last business day-of-month to avoid crossing a month boundary
    // input day-of-month is one-based
    private LocalDate shiftNextSameLast(LocalDate baseDate) {
        int baseYear = baseDate.getYear();
        int baseMonth = baseDate.getMonthValue();
        int baseDom = baseDate.getDayOfMonth();
        // find data for month
        int index = (baseYear - startYear) * 12 + baseMonth - 1;
        int monthData = lookup[index];
        // shift to move the target day-of-month into bit-0, removing earlier days
        int shifted = monthData >> (baseDom - 1);
        // return last business day-of-month if no more business days in the month
        int dom;
        if (shifted == 0) {
            // need to find the most significant bit, which is the last business day
            // use JDK numberOfLeadingZeros() method which is mapped to a fast intrinsic
            int leading = Integer.numberOfLeadingZeros(monthData);
            dom = 32 - leading;
        } else {
            // find least significant bit, which is the next/same business day
            // use JDK numberOfTrailingZeros() method which is mapped to a fast intrinsic
            dom = baseDom + Integer.numberOfTrailingZeros(shifted);
        }
        // only one call to LocalDate to aid inlining
        return baseDate.withDayOfMonth(dom);
    }

    //-------------------------------------------------------------------------
    @Override
    public boolean isLastBusinessDayOfMonth(LocalDate date) {
        try {
            // find data for month
            int index = (date.getYear() - startYear) * 12 + date.getMonthValue() - 1;
            // shift right, leaving the input date as bit-0 and filling with 0 on the left
            // if the result is 1, which is all zeroes and a final 1 (...0001) then it is last business day of month
            return (lookup[index] >>> (date.getDayOfMonth() - 1)) == 1;

        } catch (ArrayIndexOutOfBoundsException ex) {
            return isLastBusinessDayOfMonthOutOfRange(date);
        }
    }

    // pulled out to aid hotspot inlining
    private boolean isLastBusinessDayOfMonthOutOfRange(LocalDate date) {
        if (date.getYear() >= 0 && date.getYear() < 10000) {
            return HolidayCalendar.super.isLastBusinessDayOfMonth(date);
        }
        throw new IllegalArgumentException("Date is outside the accepted range (year 0000 to 10,000): " + date);
    }

    //-------------------------------------------------------------------------
    @Override
    public LocalDate lastBusinessDayOfMonth(LocalDate date) {
        try {
            // find data for month
            int index = (date.getYear() - startYear) * 12 + date.getMonthValue() - 1;
            // need to find the most significant bit, which is the last business day
            // use JDK numberOfLeadingZeros() method which is mapped to a fast intrinsic
            int leading = Integer.numberOfLeadingZeros(lookup[index]);
            return date.withDayOfMonth(32 - leading);

        } catch (ArrayIndexOutOfBoundsException ex) {
            return lastBusinessDayOfMonthOutOfRange(date);
        }
    }

    // pulled out to aid hotspot inlining
    private LocalDate lastBusinessDayOfMonthOutOfRange(LocalDate date) {
        if (date.getYear() >= 0 && date.getYear() < 10000) {
            return HolidayCalendar.super.lastBusinessDayOfMonth(date);
        }
        throw new IllegalArgumentException("Date is outside the accepted range (year 0000 to 10,000): " + date);
    }

    //-------------------------------------------------------------------------
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj instanceof ImmutableHolidayCalendar) {
            return id.equals(((ImmutableHolidayCalendar) obj).id);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }

    //-------------------------------------------------------------------------
    /**
     * Returns the name of the calendar.
     * 
     * @return the descriptive string
     */
    @Override
    public String toString() {
        return "HolidayCalendar[" + getName() + ']';
    }

    //------------------------- AUTOGENERATED START -------------------------
    ///CLOVER:OFF
    /**
     * The meta-bean for {@code ImmutableHolidayCalendar}.
     * @return the meta-bean, not null
     */
    public static ImmutableHolidayCalendar.Meta meta() {
        return ImmutableHolidayCalendar.Meta.INSTANCE;
    }

    static {
        JodaBeanUtils.registerMetaBean(ImmutableHolidayCalendar.Meta.INSTANCE);
    }

    /**
     * The serialization version id.
     */
    private static final long serialVersionUID = 1L;

    @Override
    public ImmutableHolidayCalendar.Meta metaBean() {
        return ImmutableHolidayCalendar.Meta.INSTANCE;
    }

    @Override
    public <R> Property<R> property(String propertyName) {
        return metaBean().<R>metaProperty(propertyName).createProperty(this);
    }

    @Override
    public Set<String> propertyNames() {
        return metaBean().metaPropertyMap().keySet();
    }

    //-----------------------------------------------------------------------
    /**
     * Gets the identifier, such as 'GBLO'.
     * @return the value of the property, not null
     */
    @Override
    public HolidayCalendarId getId() {
        return id;
    }

    //-----------------------------------------------------------------------
    /**
     * Gets the set of holiday dates.
     * <p>
     * Each date in this set is not a business day.
     * @return the value of the property, not null
     */
    public ImmutableSortedSet<LocalDate> getHolidays() {
        return holidays;
    }

    //-----------------------------------------------------------------------
    /**
     * Gets the set of weekend days.
     * <p>
     * Each date that has a day-of-week matching one of these days is not a business day.
     * @return the value of the property, not null
     */
    public ImmutableSet<DayOfWeek> getWeekendDays() {
        return weekendDays;
    }

    //-----------------------------------------------------------------------
    /**
     * The meta-bean for {@code ImmutableHolidayCalendar}.
     */
    public static final class Meta extends DirectMetaBean {
        /**
         * The singleton instance of the meta-bean.
         */
        static final Meta INSTANCE = new Meta();

        /**
         * The meta-property for the {@code id} property.
         */
        private final MetaProperty<HolidayCalendarId> id = DirectMetaProperty.ofImmutable(this, "id",
                ImmutableHolidayCalendar.class, HolidayCalendarId.class);
        /**
         * The meta-property for the {@code holidays} property.
         */
        @SuppressWarnings({ "unchecked", "rawtypes" })
        private final MetaProperty<ImmutableSortedSet<LocalDate>> holidays = DirectMetaProperty.ofImmutable(this,
                "holidays", ImmutableHolidayCalendar.class, (Class) ImmutableSortedSet.class);
        /**
         * The meta-property for the {@code weekendDays} property.
         */
        @SuppressWarnings({ "unchecked", "rawtypes" })
        private final MetaProperty<ImmutableSet<DayOfWeek>> weekendDays = DirectMetaProperty.ofImmutable(this,
                "weekendDays", ImmutableHolidayCalendar.class, (Class) ImmutableSet.class);
        /**
         * The meta-properties.
         */
        private final Map<String, MetaProperty<?>> metaPropertyMap$ = new DirectMetaPropertyMap(this, null, "id",
                "holidays", "weekendDays");

        /**
         * Restricted constructor.
         */
        private Meta() {
        }

        @Override
        protected MetaProperty<?> metaPropertyGet(String propertyName) {
            switch (propertyName.hashCode()) {
            case 3355: // id
                return id;
            case -510663909: // holidays
                return holidays;
            case 563236190: // weekendDays
                return weekendDays;
            }
            return super.metaPropertyGet(propertyName);
        }

        @Override
        public BeanBuilder<? extends ImmutableHolidayCalendar> builder() {
            return new ImmutableHolidayCalendar.Builder();
        }

        @Override
        public Class<? extends ImmutableHolidayCalendar> beanType() {
            return ImmutableHolidayCalendar.class;
        }

        @Override
        public Map<String, MetaProperty<?>> metaPropertyMap() {
            return metaPropertyMap$;
        }

        //-----------------------------------------------------------------------
        /**
         * The meta-property for the {@code id} property.
         * @return the meta-property, not null
         */
        public MetaProperty<HolidayCalendarId> id() {
            return id;
        }

        /**
         * The meta-property for the {@code holidays} property.
         * @return the meta-property, not null
         */
        public MetaProperty<ImmutableSortedSet<LocalDate>> holidays() {
            return holidays;
        }

        /**
         * The meta-property for the {@code weekendDays} property.
         * @return the meta-property, not null
         */
        public MetaProperty<ImmutableSet<DayOfWeek>> weekendDays() {
            return weekendDays;
        }

        //-----------------------------------------------------------------------
        @Override
        protected Object propertyGet(Bean bean, String propertyName, boolean quiet) {
            switch (propertyName.hashCode()) {
            case 3355: // id
                return ((ImmutableHolidayCalendar) bean).getId();
            case -510663909: // holidays
                return ((ImmutableHolidayCalendar) bean).getHolidays();
            case 563236190: // weekendDays
                return ((ImmutableHolidayCalendar) bean).getWeekendDays();
            }
            return super.propertyGet(bean, propertyName, quiet);
        }

        @Override
        protected void propertySet(Bean bean, String propertyName, Object newValue, boolean quiet) {
            metaProperty(propertyName);
            if (quiet) {
                return;
            }
            throw new UnsupportedOperationException("Property cannot be written: " + propertyName);
        }

    }

    //-----------------------------------------------------------------------
    /**
     * The bean-builder for {@code ImmutableHolidayCalendar}.
     */
    private static final class Builder extends DirectFieldsBeanBuilder<ImmutableHolidayCalendar> {

        private HolidayCalendarId id;
        private SortedSet<LocalDate> holidays = ImmutableSortedSet.of();
        private Set<DayOfWeek> weekendDays = ImmutableSet.of();

        /**
         * Restricted constructor.
         */
        private Builder() {
        }

        //-----------------------------------------------------------------------
        @Override
        public Object get(String propertyName) {
            switch (propertyName.hashCode()) {
            case 3355: // id
                return id;
            case -510663909: // holidays
                return holidays;
            case 563236190: // weekendDays
                return weekendDays;
            default:
                throw new NoSuchElementException("Unknown property: " + propertyName);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        public Builder set(String propertyName, Object newValue) {
            switch (propertyName.hashCode()) {
            case 3355: // id
                this.id = (HolidayCalendarId) newValue;
                break;
            case -510663909: // holidays
                this.holidays = (SortedSet<LocalDate>) newValue;
                break;
            case 563236190: // weekendDays
                this.weekendDays = (Set<DayOfWeek>) newValue;
                break;
            default:
                throw new NoSuchElementException("Unknown property: " + propertyName);
            }
            return this;
        }

        @Override
        public Builder set(MetaProperty<?> property, Object value) {
            super.set(property, value);
            return this;
        }

        @Override
        public Builder setString(String propertyName, String value) {
            setString(meta().metaProperty(propertyName), value);
            return this;
        }

        @Override
        public Builder setString(MetaProperty<?> property, String value) {
            super.setString(property, value);
            return this;
        }

        @Override
        public Builder setAll(Map<String, ? extends Object> propertyValueMap) {
            super.setAll(propertyValueMap);
            return this;
        }

        @Override
        public ImmutableHolidayCalendar build() {
            return new ImmutableHolidayCalendar(id, holidays, weekendDays);
        }

        //-----------------------------------------------------------------------
        @Override
        public String toString() {
            StringBuilder buf = new StringBuilder(128);
            buf.append("ImmutableHolidayCalendar.Builder{");
            buf.append("id").append('=').append(JodaBeanUtils.toString(id)).append(',').append(' ');
            buf.append("holidays").append('=').append(JodaBeanUtils.toString(holidays)).append(',').append(' ');
            buf.append("weekendDays").append('=').append(JodaBeanUtils.toString(weekendDays));
            buf.append('}');
            return buf.toString();
        }

    }

    ///CLOVER:ON
    //-------------------------- AUTOGENERATED END --------------------------
}