package hudson.plugins.testabilityexplorer.report;
import hudson.plugins.testabilityexplorer.report.costs.Statistic;
import hudson.plugins.testabilityexplorer.report.costs.CostSummary;
import hudson.plugins.testabilityexplorer.report.costs.ClassCost;
import hudson.plugins.testabilityexplorer.report.costs.MethodCost;
import hudson.plugins.testabilityexplorer.report.detail.ClassCostDetail;
import hudson.plugins.testabilityexplorer.report.detail.MethodCostDetail;
import hudson.plugins.testabilityexplorer.utils.TypeConverterUtil;
import java.util.Collection;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.regex.MatchResult;
import org.apache.commons.lang.StringUtils;
import hudson.model.AbstractBuild;
/**
* Helper class to return detail object for details in the report.
*
* @author reik.schatz
*/
public class CostDetailBuilder implements DetailBuilder<Statistic>
{
private static final Pattern LINE_PATTERN = Pattern.compile("\\:line\\.([\\d]*)[\\/]?\\Z", Pattern.DOTALL);
/**
* Returns a detail from the specified <code>statistics</code>. The detail will be looked up
* using the given <code>link</code> and <code>originalRequestUri</code>. The following details are supported right now:
* <p>
* <code>class.</code> returns the first {@link ClassCostDetail} matching what is behind .class
* </p>
* @param link the dynamic link part as sent in by Stapler
* @param statistics Collection
* @return Object or <code>null</code>
*/
public Object buildDetail(final String link, final String originalRequestUri, final AbstractBuild<?, ?> build, final Collection<Statistic> statistics)
{
Object dynamic = null;
if (!StringUtils.isBlank(link) && link.startsWith("class."))
{
String className = StringUtils.substringAfter(link, "class.");
if (className.contains(":"))
{
className = StringUtils.substringBefore(className, ":");
}
if (!StringUtils.isBlank(className))
{
for (Statistic statistic : statistics)
{
CostSummary summary = statistic.getCostSummary();
for (ClassCost classCost : summary.getCostStack())
{
if (className.equals(classCost.getName()))
{
ClassCostDetail classCostDetail = new ClassCostDetail();
classCostDetail.setOwner(build);
classCostDetail.setClassCost(classCost);
dynamic = classCostDetail;
MethodCost methodCost = lookupMethodCost(classCost, originalRequestUri);
if (methodCost != null)
{
MethodCostDetail methodCostDetail = new MethodCostDetail();
methodCostDetail.setMethodCost(methodCost);
methodCostDetail.setOwner(build);
dynamic = methodCostDetail;
}
break;
}
}
}
}
}
return dynamic;
}
/**
* Expects a <code>originalRequestUri</code> that ends with <code>/line.xx</code>, where xx is
* a line number. This method will return a MethodCost whose line number property matches the
* line number in the request URI. Will return <code>null</code> if no such MethodCost exists
* or the given <code>originalRequestUri</code> is invalid.
* @param classCost a ClassCost to search
* @param originalRequestUri request URI
* @return MethodCost or <code>null</code>
*/
MethodCost lookupMethodCost(final ClassCost classCost, final String originalRequestUri)
{
MethodCost cost = null;
if (!StringUtils.isBlank(originalRequestUri))
{
String lineNumber = findMatch(originalRequestUri);
if (lineNumber != null)
{
int line = TypeConverterUtil.toInt(lineNumber, -1);
for (MethodCost methodCost : classCost.getCostStack())
{
if (methodCost.getLine() == line)
{
cost = methodCost;
break;
}
}
}
}
return cost;
}
String findMatch(final String originalRequestUri)
{
String match = null;
for (Matcher m = LINE_PATTERN.matcher(originalRequestUri); m.find(); )
{
MatchResult matchResult = m.toMatchResult();
match = matchResult.group(1);
break;
}
return match;
}
}