/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.basics.value;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Set;
import org.joda.beans.Bean;
import org.joda.beans.BeanDefinition;
import org.joda.beans.ImmutableBean;
import org.joda.beans.ImmutableValidator;
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.opengamma.strata.basics.schedule.SchedulePeriod;
/**
* A single step in the variation of a value over time.
* <p>
* A financial value, such as the notional or interest rate, may vary over time.
* This class represents a single change in the value within {@link ValueSchedule}.
* <p>
* The date of the change is either specified explicitly, or in relative terms via an index.
* The adjustment to the value can also be specified absolutely, or in relative terms.
*/
@BeanDefinition
public final class ValueStep
implements ImmutableBean, Serializable {
/**
* The index of the schedule period boundary at which the change occurs.
* <p>
* This property is used to define the date that the step occurs in relative terms.
* The date is identified by specifying the zero-based index of the schedule period boundary.
* The change will occur at the start of the specified period.
* Thus an index of zero is the start of the first period or initial stub.
* The index must be one or greater, as a change is not permitted at the start of the first period.
* <p>
* For example, consider a 5 year swap from 2012-02-01 to 2017-02-01 with 6 month frequency.
* A zero-based index of '2' would refer to start of the 3rd period, which would be 2013-02-01.
*/
@PropertyDefinition(get = "optional")
private final Integer periodIndex;
/**
* The date of the schedule period boundary at which the change occurs.
* <p>
* This property is used to define the date that the step occurs in absolute terms.
* This must be one of the unadjusted dates in the schedule period schedule.
* This is an unadjusted date and calculation period business day adjustments will apply.
* <p>
* For example, consider a 5 year swap from 2012-02-01 to 2017-02-01 with 6 month frequency.
* The date '2013-02-01' is an unadjusted schedule period boundary, and so may be specified here.
*/
@PropertyDefinition(get = "optional")
private final LocalDate date;
/**
* The value representing the change that occurs.
* <p>
* The adjustment can be an absolute value, or various kinds of relative values.
*/
@PropertyDefinition(validate = "notNull")
private final ValueAdjustment value;
//-------------------------------------------------------------------------
/**
* Obtains an instance that applies at the specified schedule period index.
* <p>
* This factory is used to define the date that the step occurs in relative terms.
* The date is identified by specifying the zero-based index of the schedule period boundary.
* The change will occur at the start of the specified period.
* Thus an index of zero is the start of the first period or initial stub.
* The index must be one or greater, as a change is not permitted at the start of the first period.
* <p>
* For example, consider a 5 year swap from 2012-02-01 to 2017-02-01 with 6 month frequency.
* A zero-based index of '2' would refer to start of the 3rd period, which would be 2013-02-01.
* <p>
* The value may be absolute or relative, as per {@link ValueAdjustment}.
*
* @param periodIndex the index of the period of the value change
* @param value the adjustment to make to the value
* @return the varying step
*/
public static ValueStep of(int periodIndex, ValueAdjustment value) {
return new ValueStep(periodIndex, null, value);
}
/**
* Obtains an instance that applies at the specified date.
* <p>
* This factory obtains a step that causes the value to change at the specified date.
* <p>
* The value may be absolute or relative, as per {@link ValueAdjustment}.
*
* @param date the start date of the value change
* @param value the adjustment to make to the value
* @return the varying step
*/
public static ValueStep of(LocalDate date, ValueAdjustment value) {
return new ValueStep(null, date, value);
}
//-------------------------------------------------------------------------
/**
* Finds the index of this value step in the schedule.
*
* @param periods the list of schedule periods
* @return the index of the schedule period
*/
int findIndex(List<SchedulePeriod> periods) {
// either periodIndex or date is non-null, not both
if (periodIndex != null) {
// index based
if (periodIndex >= periods.size()) {
throw new IllegalArgumentException("ValueStep index is beyond last schedule period");
}
return periodIndex;
} else {
// date based, match one of the unadjusted period boundaries
for (int i = 0; i < periods.size(); i++) {
SchedulePeriod period = periods.get(i);
if (period.getUnadjustedStartDate().equals(date)) {
return i;
}
}
// try adjusted boundaries instead of unadjusted ones
for (int i = 0; i < periods.size(); i++) {
SchedulePeriod period = periods.get(i);
if (period.getStartDate().equals(date)) {
return i;
}
}
throw new IllegalArgumentException("ValueStep date does not match a period boundary: " + date);
}
}
//-------------------------------------------------------------------------
@ImmutableValidator
private void validate() {
if (periodIndex == null && date == null) {
throw new IllegalArgumentException("Either the 'periodIndex' or 'date' must be set");
}
if (periodIndex != null) {
if (date != null) {
throw new IllegalArgumentException("Either the 'periodIndex' or 'date' must be set, not both");
}
if (periodIndex < 1) {
throw new IllegalArgumentException("The 'periodIndex' must not be zero or negative");
}
}
}
//------------------------- AUTOGENERATED START -------------------------
///CLOVER:OFF
/**
* The meta-bean for {@code ValueStep}.
* @return the meta-bean, not null
*/
public static ValueStep.Meta meta() {
return ValueStep.Meta.INSTANCE;
}
static {
JodaBeanUtils.registerMetaBean(ValueStep.Meta.INSTANCE);
}
/**
* The serialization version id.
*/
private static final long serialVersionUID = 1L;
/**
* Returns a builder used to create an instance of the bean.
* @return the builder, not null
*/
public static ValueStep.Builder builder() {
return new ValueStep.Builder();
}
private ValueStep(
Integer periodIndex,
LocalDate date,
ValueAdjustment value) {
JodaBeanUtils.notNull(value, "value");
this.periodIndex = periodIndex;
this.date = date;
this.value = value;
validate();
}
@Override
public ValueStep.Meta metaBean() {
return ValueStep.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 index of the schedule period boundary at which the change occurs.
* <p>
* This property is used to define the date that the step occurs in relative terms.
* The date is identified by specifying the zero-based index of the schedule period boundary.
* The change will occur at the start of the specified period.
* Thus an index of zero is the start of the first period or initial stub.
* The index must be one or greater, as a change is not permitted at the start of the first period.
* <p>
* For example, consider a 5 year swap from 2012-02-01 to 2017-02-01 with 6 month frequency.
* A zero-based index of '2' would refer to start of the 3rd period, which would be 2013-02-01.
* @return the optional value of the property, not null
*/
public OptionalInt getPeriodIndex() {
return periodIndex != null ? OptionalInt.of(periodIndex) : OptionalInt.empty();
}
//-----------------------------------------------------------------------
/**
* Gets the date of the schedule period boundary at which the change occurs.
* <p>
* This property is used to define the date that the step occurs in absolute terms.
* This must be one of the unadjusted dates in the schedule period schedule.
* This is an unadjusted date and calculation period business day adjustments will apply.
* <p>
* For example, consider a 5 year swap from 2012-02-01 to 2017-02-01 with 6 month frequency.
* The date '2013-02-01' is an unadjusted schedule period boundary, and so may be specified here.
* @return the optional value of the property, not null
*/
public Optional<LocalDate> getDate() {
return Optional.ofNullable(date);
}
//-----------------------------------------------------------------------
/**
* Gets the value representing the change that occurs.
* <p>
* The adjustment can be an absolute value, or various kinds of relative values.
* @return the value of the property, not null
*/
public ValueAdjustment getValue() {
return value;
}
//-----------------------------------------------------------------------
/**
* Returns a builder that allows this bean to be mutated.
* @return the mutable builder, not null
*/
public Builder toBuilder() {
return new Builder(this);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj.getClass() == this.getClass()) {
ValueStep other = (ValueStep) obj;
return JodaBeanUtils.equal(periodIndex, other.periodIndex) &&
JodaBeanUtils.equal(date, other.date) &&
JodaBeanUtils.equal(value, other.value);
}
return false;
}
@Override
public int hashCode() {
int hash = getClass().hashCode();
hash = hash * 31 + JodaBeanUtils.hashCode(periodIndex);
hash = hash * 31 + JodaBeanUtils.hashCode(date);
hash = hash * 31 + JodaBeanUtils.hashCode(value);
return hash;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(128);
buf.append("ValueStep{");
buf.append("periodIndex").append('=').append(periodIndex).append(',').append(' ');
buf.append("date").append('=').append(date).append(',').append(' ');
buf.append("value").append('=').append(JodaBeanUtils.toString(value));
buf.append('}');
return buf.toString();
}
//-----------------------------------------------------------------------
/**
* The meta-bean for {@code ValueStep}.
*/
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 periodIndex} property.
*/
private final MetaProperty<Integer> periodIndex = DirectMetaProperty.ofImmutable(
this, "periodIndex", ValueStep.class, Integer.class);
/**
* The meta-property for the {@code date} property.
*/
private final MetaProperty<LocalDate> date = DirectMetaProperty.ofImmutable(
this, "date", ValueStep.class, LocalDate.class);
/**
* The meta-property for the {@code value} property.
*/
private final MetaProperty<ValueAdjustment> value = DirectMetaProperty.ofImmutable(
this, "value", ValueStep.class, ValueAdjustment.class);
/**
* The meta-properties.
*/
private final Map<String, MetaProperty<?>> metaPropertyMap$ = new DirectMetaPropertyMap(
this, null,
"periodIndex",
"date",
"value");
/**
* Restricted constructor.
*/
private Meta() {
}
@Override
protected MetaProperty<?> metaPropertyGet(String propertyName) {
switch (propertyName.hashCode()) {
case -980601967: // periodIndex
return periodIndex;
case 3076014: // date
return date;
case 111972721: // value
return value;
}
return super.metaPropertyGet(propertyName);
}
@Override
public ValueStep.Builder builder() {
return new ValueStep.Builder();
}
@Override
public Class<? extends ValueStep> beanType() {
return ValueStep.class;
}
@Override
public Map<String, MetaProperty<?>> metaPropertyMap() {
return metaPropertyMap$;
}
//-----------------------------------------------------------------------
/**
* The meta-property for the {@code periodIndex} property.
* @return the meta-property, not null
*/
public MetaProperty<Integer> periodIndex() {
return periodIndex;
}
/**
* The meta-property for the {@code date} property.
* @return the meta-property, not null
*/
public MetaProperty<LocalDate> date() {
return date;
}
/**
* The meta-property for the {@code value} property.
* @return the meta-property, not null
*/
public MetaProperty<ValueAdjustment> value() {
return value;
}
//-----------------------------------------------------------------------
@Override
protected Object propertyGet(Bean bean, String propertyName, boolean quiet) {
switch (propertyName.hashCode()) {
case -980601967: // periodIndex
return ((ValueStep) bean).periodIndex;
case 3076014: // date
return ((ValueStep) bean).date;
case 111972721: // value
return ((ValueStep) bean).getValue();
}
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 ValueStep}.
*/
public static final class Builder extends DirectFieldsBeanBuilder<ValueStep> {
private Integer periodIndex;
private LocalDate date;
private ValueAdjustment value;
/**
* Restricted constructor.
*/
private Builder() {
}
/**
* Restricted copy constructor.
* @param beanToCopy the bean to copy from, not null
*/
private Builder(ValueStep beanToCopy) {
this.periodIndex = beanToCopy.periodIndex;
this.date = beanToCopy.date;
this.value = beanToCopy.getValue();
}
//-----------------------------------------------------------------------
@Override
public Object get(String propertyName) {
switch (propertyName.hashCode()) {
case -980601967: // periodIndex
return periodIndex;
case 3076014: // date
return date;
case 111972721: // value
return value;
default:
throw new NoSuchElementException("Unknown property: " + propertyName);
}
}
@Override
public Builder set(String propertyName, Object newValue) {
switch (propertyName.hashCode()) {
case -980601967: // periodIndex
this.periodIndex = (Integer) newValue;
break;
case 3076014: // date
this.date = (LocalDate) newValue;
break;
case 111972721: // value
this.value = (ValueAdjustment) 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 ValueStep build() {
return new ValueStep(
periodIndex,
date,
value);
}
//-----------------------------------------------------------------------
/**
* Sets the index of the schedule period boundary at which the change occurs.
* <p>
* This property is used to define the date that the step occurs in relative terms.
* The date is identified by specifying the zero-based index of the schedule period boundary.
* The change will occur at the start of the specified period.
* Thus an index of zero is the start of the first period or initial stub.
* The index must be one or greater, as a change is not permitted at the start of the first period.
* <p>
* For example, consider a 5 year swap from 2012-02-01 to 2017-02-01 with 6 month frequency.
* A zero-based index of '2' would refer to start of the 3rd period, which would be 2013-02-01.
* @param periodIndex the new value
* @return this, for chaining, not null
*/
public Builder periodIndex(Integer periodIndex) {
this.periodIndex = periodIndex;
return this;
}
/**
* Sets the date of the schedule period boundary at which the change occurs.
* <p>
* This property is used to define the date that the step occurs in absolute terms.
* This must be one of the unadjusted dates in the schedule period schedule.
* This is an unadjusted date and calculation period business day adjustments will apply.
* <p>
* For example, consider a 5 year swap from 2012-02-01 to 2017-02-01 with 6 month frequency.
* The date '2013-02-01' is an unadjusted schedule period boundary, and so may be specified here.
* @param date the new value
* @return this, for chaining, not null
*/
public Builder date(LocalDate date) {
this.date = date;
return this;
}
/**
* Sets the value representing the change that occurs.
* <p>
* The adjustment can be an absolute value, or various kinds of relative values.
* @param value the new value, not null
* @return this, for chaining, not null
*/
public Builder value(ValueAdjustment value) {
JodaBeanUtils.notNull(value, "value");
this.value = value;
return this;
}
//-----------------------------------------------------------------------
@Override
public String toString() {
StringBuilder buf = new StringBuilder(128);
buf.append("ValueStep.Builder{");
buf.append("periodIndex").append('=').append(JodaBeanUtils.toString(periodIndex)).append(',').append(' ');
buf.append("date").append('=').append(JodaBeanUtils.toString(date)).append(',').append(' ');
buf.append("value").append('=').append(JodaBeanUtils.toString(value));
buf.append('}');
return buf.toString();
}
}
///CLOVER:ON
//-------------------------- AUTOGENERATED END --------------------------
}