package com.canoo.webtest.ant; import java.text.ParsePosition; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.tools.ant.MagicNames; import org.apache.tools.ant.Project; import org.apache.tools.ant.PropertyHelper; import org.apache.tools.ant.property.ParseNextProperty; import org.apache.tools.ant.property.PropertyExpander; import com.canoo.webtest.engine.Context; import com.canoo.webtest.extension.ScriptStep; /** * Helper class for working with Ant and WebTest dynamic properties.<p> * * This property helper is registered at the start of a WebTest and used by ant to evaluate * properties when configuring tasks.<br/> * It is able to evaluate traditional Ant properties like ${my.property} as well as * WebTest dynamic properties like #{my.dynamic.property}.<br/> * It will notify build listeners implementing {@link IPropertyExpansionListener} of the property expansion. * @author Marc Guillemot */ public class WebtestPropertyHelper { private static final Logger LOG = Logger.getLogger(WebtestPropertyHelper.class); private static final PropertyExpander WEBTEST_PROPERTY_EXPANDER = new PropertyExpander() { /** * Parse the next property name. * @param value the String to parse. * @param pos the ParsePosition in use. * @param parseNextProperty parse next property * @return parsed String if any, else <code>null</code>. */ public String parsePropertyName(final String value, final ParsePosition pos, final ParseNextProperty parseNextProperty) { final int start = pos.getIndex(); if (value.length() - start >= 3 && '#' == value.charAt(start) && '{' == value.charAt(start + 1)) { pos.setIndex(start + 2); final StringBuilder sb = new StringBuilder(); for (int c = pos.getIndex(); c < value.length(); c = pos.getIndex()) { if (value.charAt(c) == '}') { pos.setIndex(c + 1); return "wt:" + sb.toString(); } Object o = parseNextProperty.parseNextProperty(value, pos); if (o != null) { sb.append(o); } else { // be aware that the parse position may now have changed; // update: c = pos.getIndex(); sb.append(value.charAt(c)); pos.setIndex(c + 1); } } } pos.setIndex(start); return null; } }; private static final PropertyExpander NESTED_PROPERTY_EXPANDER = new PropertyExpander() { /** * Parse the next property name. * @param value the String to parse. * @param pos the ParsePosition in use. * @param parseNextProperty parse next property * @return parsed String if any, else <code>null</code>. */ public String parsePropertyName(final String value, final ParsePosition pos, final ParseNextProperty parseNextProperty) { final int start = pos.getIndex(); if (value.length() - start >= 3 && '$' == value.charAt(start) && '{' == value.charAt(start + 1)) { pos.setIndex(start + 2); final StringBuilder sb = new StringBuilder(); for (int c = pos.getIndex(); c < value.length(); c = pos.getIndex()) { if (value.charAt(c) == '}') { pos.setIndex(c + 1); return sb.toString(); } Object o = parseNextProperty.parseNextProperty(value, pos); if (o != null) { sb.append(o); } else { // be aware that the parse position may now have changed; // update: c = pos.getIndex(); sb.append(value.charAt(c)); pos.setIndex(c + 1); } } } pos.setIndex(start); return null; } }; /** * Configures the special WebTest property helper for the current project * @param project the project which property helper should be wrapped */ public static void configureWebtestPropertyHelper(final Project project) { final PropertyHelper.PropertyEvaluator getProperty = new PropertyHelper.PropertyEvaluator() { @Override public Object evaluate(final String property, final PropertyHelper propertyHelper) { if (!property.startsWith("wt:")) { return null; } return getDynamicPropertyValue(property.substring(3)); } }; final PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper(project); propertyHelper.add(WEBTEST_PROPERTY_EXPANDER); propertyHelper.add(NESTED_PROPERTY_EXPANDER); propertyHelper.add(getProperty); } private static String getDynamicPropertyValue(final String propName) { if (propName.startsWith("script:")) { final Context context = WebtestTask.getThreadContext(); if (context == null || context.getRunner() == null) { return null; } final String expr = StringUtils.substringAfter(propName, "script:"); return ScriptStep.evalScriptExpression(context, expr, null); } else return (String) getDynamicProperties().get(propName); } /** * Gets the dynamic properties map for the current webtest * @return the map of properties */ static Map getDynamicProperties() { return WebtestTask.getThreadContext().getWebtest().getDynamicProperties(); } public static void cleanWebtestPropertyHelper(Project project) { final PropertyHelper propertyHelper = (PropertyHelper) project.getReferences().get(MagicNames.REFID_PROPERTY_HELPER); propertyHelper.add(WEBTEST_PROPERTY_EXPANDER); } }