package org.marketcetera.client.instruments;
import org.marketcetera.util.misc.ClassVersion;
import org.marketcetera.util.log.I18NBoundMessage1P;
import org.marketcetera.trade.Instrument;
import org.marketcetera.trade.Option;
import org.marketcetera.client.OrderValidationException;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.GregorianCalendar;
import java.util.Calendar;
/* $License$ */
/**
* Validates option instruments.
* <p>
* The option expiry date is validated by {@link #validateExpiry(String)}.
*
* @author anshul@marketcetera.com
* @version $Id: OptionValidationHandler.java 16154 2012-07-14 16:34:05Z colin $
* @since 2.0.0
*/
@ClassVersion("$Id: OptionValidationHandler.java 16154 2012-07-14 16:34:05Z colin $")
public class OptionValidationHandler extends InstrumentValidationHandler<Option> {
/**
* Creates an instance.
*/
public OptionValidationHandler() {
super(Option.class);
}
@Override
public void validate(Instrument instrument) throws OrderValidationException {
Option option = (Option) instrument;
validateExpiry(option.getExpiry());
}
/**
* Validates expiry field of an instrument.
* <p>
* Verifies that the expiry field has either of the following formats
* <ul>
* <li>YYYYMM</li>
* <li>YYYYMMDD</li>
* <li>YYYYMMwN</li>
* </ul>
* where:
* <ul>
* YYYY: represents the year
* <br/>
* MM: represents the month: 01-12
* <br/>
* DD: represents the day of the month: 01-31
* <br/>
* wN: represents the week number: w1-w5
* </ul>
*
* @param inExpiry the expiry field of the option
*
* @throws OrderValidationException if the expiry field failed validation.
*/
public static void validateExpiry(String inExpiry) throws OrderValidationException {
Matcher m = EXPIRY_PATTERN.matcher(inExpiry);
if(!m.matches()) {
throw new OrderValidationException(new I18NBoundMessage1P(
Messages.INVALID_OPTION_EXPIRY_FORMAT,inExpiry));
}
}
/**
* Validates that the date matches the expected pattern per
* {@link #validateExpiry(String)} and is a correct date for the given
* month, year and day/week combination.
* <p>
* Note that this method is only available for convenience for UI validation.
* This method is not invoked for validation when the client is sending
* orders.
*
* @param inExpiry the expiry field of the option
*
* @throws OrderValidationException if the expiry field failed validation.
*/
public static void validateExpiryDate(String inExpiry) throws OrderValidationException {
Matcher m = EXPIRY_PATTERN.matcher(inExpiry);
if(!m.matches()) {
throw new OrderValidationException(new I18NBoundMessage1P(
Messages.INVALID_OPTION_EXPIRY_FORMAT,inExpiry));
}
if (m.groupCount() > 2) {
String last = m.group(3);
if (last != null && (!last.isEmpty())) {
try {
int year = Integer.parseInt(m.group(1));
int month = Calendar.JANUARY + Integer.parseInt(m.group(2)) - 1;
if(last.charAt(0) == 'w'){ //$NON-NLS-1$
GregorianCalendar cal = new GregorianCalendar(year, month, 1);
cal.setFirstDayOfWeek(Calendar.SUNDAY);
//It's unclear how the current week number in the month
//should be computed. Based on the week number in year
//computation as specified by ISO 8601, assume
//that the first week of the month needs to have atleast
//4 days.
cal.setMinimalDaysInFirstWeek(4);
int week = Integer.parseInt(last.substring(1));
if(week > cal.getActualMaximum(Calendar.WEEK_OF_MONTH)) {
throw new OrderValidationException(new I18NBoundMessage1P(
Messages.INVALID_OPTION_EXPIRY_WEEK, inExpiry));
}
} else {
int day = Integer.parseInt(last);
GregorianCalendar cal = new GregorianCalendar();
cal.clear();
cal.setLenient(false);
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month);
cal.set(Calendar.DAY_OF_MONTH, day);
try {
cal.getTime();
} catch (Exception e) {
throw new OrderValidationException(new I18NBoundMessage1P(
Messages.INVALID_OPTION_EXPIRY_DAY, inExpiry));
}
}
} catch (NumberFormatException e) {
throw new OrderValidationException(new I18NBoundMessage1P(
Messages.INVALID_OPTION_EXPIRY_FORMAT,inExpiry));
}
}
}
}
private static final Pattern EXPIRY_PATTERN = Pattern.compile(
"^(\\d{4})((?:0[1-9])|(?:1[012]))((?:(?:0[1-9])|(?:[12]\\d)|(?:3[01]))|(?:w[1-5]))?$"); //$NON-NLS-1$
}