package cucumber.api; import cucumber.deps.com.thoughtworks.xstream.converters.SingleValueConverter; import cucumber.runtime.ParameterInfo; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Locale; /** * <p> * Allows transformation of a step definition argument to a custom type, giving you full control * over how that type is instantiated. * </p> * <p> * Consider the following Gherkin step: * </p> * <pre>Given today's date is "10/03/1985"</pre> * <p> * As an example, let's assume we want Cucumber to transform the substring "10/03/1985" into an instance of * <code>org.joda.time.LocalDate</code> class: * </p> * <pre> * @Given("today's date is \"(.*)\"") * public void todays_date_is(LocalDate d) { * } * </pre> * <p> * If the parameter's class has a constructor with a single <code>String</code> or <code>Object</code> argument, then * Cucumber will instantiate it without any further ado. However, in this case that might not give you what you * want. Depending on your Locale, the date may be Oct 3 or March 10! * * </p> * <p> * This is when you can use a custom transformer. You'll also have to do that if your parameter class doesn't * have a constructor with a single <code>String</code> or <code>Object</code> argument. For the JODA Time * example: * </p> * * <pre> * @Given("today's date is \"(.*)\"") * public void todays_date_is(@Transform(JodaTimeConverter.class) LocalDate d) { * } * </pre> * <p> * And then a <code>JodaTimeConverter</code> class: * </p> * <pre>{@code * public static class JodaTimeConverter extends Transformer<LocalDate> { * private static DateTimeFormatter FORMATTER = DateTimeFormat.forStyle("S-"); * * @Override * public LocalDate transform(String value) { * return FORMATTER.withLocale(getLocale()).parseLocalDate(value); * } * } * }</pre> * <p> * An alternative to annotating parameters with {@link Transform} is to annotate your class with * {@link cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter}: * </p> * <pre> * @XStreamConverter(MyConverter.class) * public class MyClass { * } * </pre> * <p> * This will also enable a {@link DataTable} to be transformed to * a <code>List<MyClass;></code> * </p> * * @param <T> the type to be instantiated * @see Transform */ public abstract class Transformer<T> implements SingleValueConverter { private final Type type; private Locale locale; public Transformer() { ParameterizedType ptype = (ParameterizedType) getClass().getGenericSuperclass(); this.type = ptype.getActualTypeArguments()[0]; } @Override public String toString(Object o) { return o.toString(); } @Override public final Object fromString(String s) { return transform(s); } @Override public boolean canConvert(Class type) { return type.equals(this.type); } public abstract T transform(String value); public void setParameterInfoAndLocale(ParameterInfo parameterInfo, Locale locale) { this.locale = locale; } /** * @return the current locale */ protected Locale getLocale() { return locale; } }