Package com.opengamma.financial.analytics.timeseries

Source Code of com.opengamma.financial.analytics.timeseries.DateConstraint

/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.timeseries;

import org.apache.commons.lang.ObjectUtils;
import org.threeten.bp.LocalDate;
import org.threeten.bp.Period;

import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.time.DateUtils;
import com.opengamma.util.tuple.Pair;

/**
* Utility class for building date constraints for the time series fetching functions.
* <p>
* Date constraint strings are crude expressions that are evaluated at execution time. This allows the valuation time to be referred to symbolically.
* <dl>
* <dt><em>YYYY</em>-</em>MM</em>-<em>DD</em></dt>
* <dd>The date literal</dd>
* <dt>Now</dt>
* <dd>The valuation date</dd>
* <dt>Null</dt>
* <dd>Will return <code>null</code> from the {@link #getLocalDate} function</dd>
* <dt>PreviousWeekDay(<em>expr</em>)</dt>
* <dd>The previous weekday to the evaluated date constraint expression</dd>
* <dt>PreviousWeekDay</dt>
* <dd>The previous weekday to the valuation date. This is equivalent to <code>PreviousWeekDay(NOW)</code></dd>
* <dt>NextWeekDay(<em>expr</em>)</dt>
* <dd>The next weekday to the evaluated date constraint expression</dd>
* <dt><em>expr</em>[+|-]<em>period</em></dt>
* <dd>The evaluated date constraint expression plus or minus the given period, for example <code>PreviousWeekDay-P7D</code></dd>
* <dt>-<em>period</em></dt>
* <dd>The valuation date minus the given period, for example <code>-P1D</code> for the previous day. This is equivalent to <code>NOW-<em>period</em></code></dd>
* </dl>
*/
public abstract class DateConstraint {

  private static final String NOW_STRING = "Now";

  private static final String NULL_STRING = "Null";

  private static final String PREVIOUS_WEEK_DAY_STRING = "PreviousWeekDay";

  private static final String NEXT_WEEK_DAY_STRING = "NextWeekDay";

  /**
   * Constant for the "null" date constraint. Some of the time series APIs use null to mean the earliest available date.
   */
  public static final DateConstraint NULL = new NullDateConstraint();
  /**
   * Date constraint referring to the current valuation time.
   */
  public static final DateConstraint VALUATION_TIME = new ValuationTime();

  /* package */DateConstraint() {
  }

  public static DateConstraint of(final LocalDate date) {
    ArgumentChecker.notNull(date, "date");
    return new LiteralDateConstraint(date);
  }

  /**
   * Returns a date constraint that corresponds to the weekday before this one.
   *
   * @return the new date constraint
   */
  public DateConstraint previousWeekDay() {
    return new WeekDayDateConstraint(this, -1);
  }

  /**
   * Returns a date constraint that corresponds to the weekday after this one.
   *
   * @return the new date constraint
   */
  public DateConstraint nextWeekDay() {
    return new WeekDayDateConstraint(this, 1);
  }

  /**
   * Returns a date constraint that corresponds to this one plus the given period.
   *
   * @param period the period to add, not null
   * @return the new date constraint
   */
  public DateConstraint plus(final Period period) {
    return new PlusMinusPeriodDateConstraint(this, true, period);
  }

  /**
   * Returns a date constraint that corresponds to this one minus the given period.
   *
   * @param period the period to subtract, not null
   * @return the new date constraint
   */
  public DateConstraint minus(final Period period) {
    return new PlusMinusPeriodDateConstraint(this, false, period);
  }

  /**
   * Returns a date constraint that corresponds to this one minus the given period.
   *
   * @param period the period to subtract, not null
   * @return the new date constraint
   */
  public DateConstraint minus(final String period) {
    return minus(Period.parse(period));
  }

  /**
   * Approximates the period difference between two constraints, that is the period that must be added to this contraint to get the same value as the other one.
   *
   * @param other the other constraint, not null
   * @return the difference as a period, not null
   * @throws IllegalArgumentException if the constraints are not sufficiently compatible
   */
  public Period periodUntil(final DateConstraint other) {
    if (equals(other)) {
      return Period.ZERO;
    } else if (other instanceof PlusMinusPeriodDateConstraint) {
      return ((PlusMinusPeriodDateConstraint) other).periodUntil(this).negated();
    } else {
      throw new IllegalArgumentException(other + " - " + this);
    }
  }

  @Override
  public abstract String toString();

  private static final class NullDateConstraint extends DateConstraint {

    @Override
    public DateConstraint previousWeekDay() {
      throw new UnsupportedOperationException("Can't take previous week day from NULL");
    }

    @Override
    public DateConstraint nextWeekDay() {
      throw new UnsupportedOperationException("Can't take next week day from NULL");
    }

    @Override
    public DateConstraint plus(final Period period) {
      throw new UnsupportedOperationException("Can't add a period to NULL");
    }

    @Override
    public DateConstraint minus(final Period period) {
      throw new UnsupportedOperationException("Can't subtract a period from NULL");
    }

    @Override
    public String toString() {
      return NULL_STRING;
    }

    @Override
    public int hashCode() {
      return 0;
    }

    @Override
    public boolean equals(final Object o) {
      return (o instanceof NullDateConstraint);
    }

  }

  private static final class LiteralDateConstraint extends DateConstraint {

    private final LocalDate _value;

    public LiteralDateConstraint(final LocalDate value) {
      _value = value;
    }

    @Override
    public DateConstraint previousWeekDay() {
      return new LiteralDateConstraint(DateUtils.previousWeekDay(_value));
    }

    @Override
    public DateConstraint nextWeekDay() {
      return new LiteralDateConstraint(DateUtils.nextWeekDay(_value));
    }

    @Override
    public DateConstraint plus(final Period period) {
      return new LiteralDateConstraint(_value.plus(period));
    }

    @Override
    public DateConstraint minus(final Period period) {
      return new LiteralDateConstraint(_value.minus(period));
    }

    @Override
    public Period periodUntil(final DateConstraint other) {
      if (other instanceof LiteralDateConstraint) {
        return _value.periodUntil(((LiteralDateConstraint) other)._value);
      } else {
        return super.periodUntil(other);
      }
    }

    @Override
    public String toString() {
      return _value.toString();
    }

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

    @Override
    public boolean equals(final Object o) {
      if (!(o instanceof LiteralDateConstraint)) {
        return false;
      }
      final LiteralDateConstraint other = (LiteralDateConstraint) o;
      return _value.equals(other._value);
    }

  }

  private static final class PlusMinusPeriodDateConstraint extends DateConstraint {

    private final DateConstraint _underlying;
    private final boolean _plus;
    private final Period _period;

    public PlusMinusPeriodDateConstraint(final DateConstraint underlying, final boolean plus, final Period period) {
      _underlying = underlying;
      _plus = plus;
      _period = period;
    }

    @Override
    public DateConstraint plus(final Period period) {
      final Period newPeriod;
      if (_plus) {
        newPeriod = _period.plus(period);
      } else {
        newPeriod = _period.minus(period);
      }
      if (newPeriod.isZero()) {
        if (_underlying != null) {
          return _underlying;
        } else {
          return VALUATION_TIME;
        }
      } else {
        return new PlusMinusPeriodDateConstraint(_underlying, _plus, newPeriod);
      }
    }

    @Override
    public DateConstraint minus(final Period period) {
      final Period newPeriod;
      if (_plus) {
        newPeriod = _period.minus(period);
      } else {
        newPeriod = _period.plus(period);
      }
      if (newPeriod.isZero()) {
        if (_underlying != null) {
          return _underlying;
        } else {
          return VALUATION_TIME;
        }
      } else {
        return new PlusMinusPeriodDateConstraint(_underlying, _plus, newPeriod);
      }
    }

    @Override
    public Period periodUntil(final DateConstraint o) {
      if (o instanceof PlusMinusPeriodDateConstraint) {
        final PlusMinusPeriodDateConstraint other = (PlusMinusPeriodDateConstraint) o;
        if (ObjectUtils.equals(_underlying, other._underlying)) {
          final Period a = _plus ? _period : _period.negated();
          final Period b = other._plus ? other._period : other._period.negated();
          return b.minus(a);
        }
      } else if (o.equals((_underlying == null) ? DateConstraint.VALUATION_TIME : _underlying)) {
        if (_plus) {
          return _period.negated();
        } else {
          return _period;
        }
      }
      throw new IllegalArgumentException();
    }

    @Override
    public String toString() {
      final StringBuilder sb = new StringBuilder();
      if (_underlying != null) {
        sb.append(_underlying);
      }
      sb.append(_plus ? '+' : '-');
      sb.append(_period);
      return sb.toString();
    }

    @Override
    public int hashCode() {
      return ObjectUtils.hashCode(_underlying) + (_plus ? 1 : 0) + ObjectUtils.hashCode(_period);
    }

    @Override
    public boolean equals(final Object o) {
      if (!(o instanceof PlusMinusPeriodDateConstraint)) {
        return false;
      }
      final PlusMinusPeriodDateConstraint other = (PlusMinusPeriodDateConstraint) o;
      return ObjectUtils.equals(_underlying, other._underlying)
          && (_plus == other._plus)
          && ObjectUtils.equals(_period, other._period);
    }

  }

  private static final class WeekDayDateConstraint extends DateConstraint {

    private final DateConstraint _underlying;
    private final int _adjust;

    public WeekDayDateConstraint(final DateConstraint underlying, final int adjust) {
      _underlying = underlying;
      _adjust = adjust;
    }

    @Override
    public DateConstraint previousWeekDay() {
      if (_adjust == 1) {
        if (_underlying != null) {
          return _underlying;
        } else {
          return VALUATION_TIME;
        }
      } else {
        return new WeekDayDateConstraint(_underlying, _adjust - 1);
      }
    }

    @Override
    public DateConstraint nextWeekDay() {
      if (_adjust == -1) {
        if (_underlying != null) {
          return _underlying;
        } else {
          return VALUATION_TIME;
        }
      } else {
        return new WeekDayDateConstraint(_underlying, _adjust + 1);
      }
    }

    private String expr(final int adjust, final String str) {
      if (adjust == 1) {
        if (_underlying != null) {
          return str + "(" + _underlying + ")";
        } else {
          return str;
        }
      } else {
        return str + "(" + expr(adjust - 1, str) + ")";
      }
    }

    @Override
    public String toString() {
      if (_adjust < 0) {
        return expr(-_adjust, PREVIOUS_WEEK_DAY_STRING);
      } else {
        return expr(_adjust, NEXT_WEEK_DAY_STRING);
      }
    }

    @Override
    public int hashCode() {
      return ObjectUtils.hashCode(_underlying) + _adjust;
    }

    @Override
    public boolean equals(final Object o) {
      if (!(o instanceof WeekDayDateConstraint)) {
        return false;
      }
      final WeekDayDateConstraint other = (WeekDayDateConstraint) o;
      return ObjectUtils.equals(_underlying, other._underlying)
          && (_adjust == other._adjust);
    }

  }

  private static final class ValuationTime extends DateConstraint {

    @Override
    public DateConstraint previousWeekDay() {
      return new WeekDayDateConstraint(null, -1);
    }

    @Override
    public DateConstraint nextWeekDay() {
      return new WeekDayDateConstraint(null, 1);
    }

    @Override
    public DateConstraint plus(final Period period) {
      return new PlusMinusPeriodDateConstraint(null, true, period);
    }

    @Override
    public DateConstraint minus(final Period period) {
      return new PlusMinusPeriodDateConstraint(null, false, period);
    }

    @Override
    public String toString() {
      return NOW_STRING;
    }

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

    @Override
    public boolean equals(final Object o) {
      return (o instanceof ValuationTime);
    }

  }

  private static Pair<String, String> parseBrackets(final String str) {
    if (str.length() == 0) {
      return Pair.of(null, null);
    } else if (str.charAt(0) == '(') {
      int index = 1;
      int count = 1;
      do {
        switch (str.charAt(index++)) {
          case '(':
            count++;
            break;
          case ')':
            count--;
            break;
        }
      } while (count > 0);
      final String bracketExpr = str.substring(1, index - 1);
      if (index == str.length()) {
        return Pair.of(bracketExpr, null);
      } else {
        return Pair.of(bracketExpr, str.substring(index));
      }
    } else {
      return Pair.of(null, str);
    }
  }

  private static DateConstraint parseRight(final DateConstraint left, final String str) {
    if (str.charAt(0) == '-') {
      return left.minus(Period.parse(str.substring(1)));
    } else if (str.charAt(0) == '+') {
      return left.plus(Period.parse(str.substring(1)));
    } else {
      throw new IllegalArgumentException("Can't parse tail expression " + str + " of " + left);
    }
  }

  private static LocalDate evaluateRight(final LocalDate left, final String str) {
    if (str.charAt(0) == '-') {
      return left.minus(Period.parse(str.substring(1)));
    } else if (str.charAt(0) == '+') {
      return left.plus(Period.parse(str.substring(1)));
    } else {
      throw new IllegalArgumentException("Can't parse tail expression " + str + " of " + left);
    }
  }

  /**
   * Basic parsing of a date constraint string to a {@link DateConstraint} object.
   * <p>
   * This is not a full parser for the syntax described above. For example, expressions such as <code>-P7D-P7D</code> will not be recognized. Such expressions will not however be constructed using the
   * classes above (it would produce <code>-P14D</code>).
   *
   * @param str the string to parse, not null
   * @return the parsed constraint or null if the empty string is given
   */
  public static DateConstraint parse(final String str) {
    if (str.length() == 0) {
      return null;
    }
    try {
      if (str.startsWith(NOW_STRING)) {
        if (str.length() == NOW_STRING.length()) {
          return VALUATION_TIME;
        } else {
          return parseRight(VALUATION_TIME, str.substring(NOW_STRING.length()));
        }
      } else if (str.startsWith(NULL_STRING)) {
        return NULL;
      } else if (str.charAt(0) == '-') {
        return VALUATION_TIME.minus(Period.parse(str.substring(1)));
      } else if (str.charAt(0) == '+') {
        return VALUATION_TIME.plus(Period.parse(str.substring(1)));
      } else if (str.startsWith(PREVIOUS_WEEK_DAY_STRING)) {
        final Pair<String, String> brackets = parseBrackets(str.substring(PREVIOUS_WEEK_DAY_STRING.length()));
        final DateConstraint left;
        if (brackets.getFirst() != null) {
          left = parse(brackets.getFirst()).previousWeekDay();
        } else {
          left = VALUATION_TIME.previousWeekDay();
        }
        if (brackets.getSecond() != null) {
          return parseRight(left, brackets.getSecond());
        } else {
          return left;
        }
      } else if (str.startsWith(NEXT_WEEK_DAY_STRING)) {
        final Pair<String, String> brackets = parseBrackets(str.substring(NEXT_WEEK_DAY_STRING.length()));
        final DateConstraint left;
        if (brackets.getFirst() != null) {
          left = parse(brackets.getFirst()).nextWeekDay();
        } else {
          left = VALUATION_TIME.nextWeekDay();
        }
        if (brackets.getSecond() != null) {
          return parseRight(left, brackets.getSecond());
        } else {
          return left;
        }
      } else {
        return new LiteralDateConstraint(LocalDate.parse(str));
      }
    } catch (final Exception e) {
      throw new OpenGammaRuntimeException("Unable to parse date constraint '" + str + "'", e);
    }
  }

  /**
   * Evaluates a date constraint expression with respect to the information in the execution context such as the valuation time.
   * <p>
   * This is more efficient than parsing and evaluating the {@link DateConstraint} object structures as two separate steps.
   *
   * @param context the execution context, not null
   * @param str the string to parse and evaluate
   * @return the evaluated local date, possibly null
   */
  public static LocalDate evaluate(final FunctionExecutionContext context, final String str) {
    if (str.length() == 0) {
      return null;
    }
    if (str.startsWith(NOW_STRING)) {
      if (str.length() == NOW_STRING.length()) {
        return valuationTime(context);
      } else {
        return evaluateRight(valuationTime(context), str.substring(NOW_STRING.length()));
      }
    } else if (str.startsWith(NULL_STRING)) {
      return null;
    } else if (str.charAt(0) == '-') {
      return valuationTime(context).minus(Period.parse(str.substring(1)));
    } else if (str.charAt(0) == '+') {
      return valuationTime(context).plus(Period.parse(str.substring(1)));
    } else if (str.startsWith(PREVIOUS_WEEK_DAY_STRING)) {
      final Pair<String, String> brackets = parseBrackets(str.substring(PREVIOUS_WEEK_DAY_STRING.length()));
      final LocalDate left;
      if (brackets.getFirst() != null) {
        left = DateUtils.previousWeekDay(evaluate(context, brackets.getFirst()));
      } else {
        left = DateUtils.previousWeekDay(valuationTime(context));
      }
      if (brackets.getSecond() != null) {
        return evaluateRight(left, brackets.getSecond());
      } else {
        return left;
      }
    } else if (str.startsWith(NEXT_WEEK_DAY_STRING)) {
      final Pair<String, String> brackets = parseBrackets(str.substring(NEXT_WEEK_DAY_STRING.length()));
      final LocalDate left;
      if (brackets.getFirst() != null) {
        left = DateUtils.nextWeekDay(evaluate(context, brackets.getFirst()));
      } else {
        left = DateUtils.nextWeekDay(valuationTime(context));
      }
      if (brackets.getSecond() != null) {
        return evaluateRight(left, brackets.getSecond());
      } else {
        return left;
      }
    } else {
      return LocalDate.parse(str);
    }
  }

  private static LocalDate valuationTime(final FunctionExecutionContext context) {
    return LocalDate.now(context.getValuationClock());
  }

}
TOP

Related Classes of com.opengamma.financial.analytics.timeseries.DateConstraint

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.