package cucumber.contrib.formatter.renderer;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author <a href="http://twitter.com/aloyer">@aloyer</a>
*/
public class ChartDescriptorParser {
private static final Pattern TYPE = singleLine("type", "([a-zA-Z]+)");
private static final Pattern LEGEND = singleLine("legend", "(true|false)");
public ChartDescriptor parse(String input) {
ChartDescriptor descriptor = handleType(input);
handleLegend(input, descriptor);
return descriptor;
}
private void handleLegend(String input, ChartDescriptor descriptor) {
Boolean bool = bool(input, LEGEND);
if (bool != null)
descriptor.setLegendVisible(bool);
}
private ChartDescriptor handleType(String input) {
String value = first(input, TYPE);
if (value == null) {
throw new IllegalArgumentException("No type defined");
}
ChartType chartType = ChartType.fromString(value);
switch (chartType) {
case Pie: {
ChartPieDescriptor descriptor = new ChartPieDescriptor();
handlePieSpecifics(input, descriptor);
return descriptor;
}
case XY: {
ChartXYDescriptor descriptor = new ChartXYDescriptor();
handleXYSpecifics(input, descriptor);
return descriptor;
}
default:
throw new UnsupportedOperationException("Chart type " + chartType + " not yet supported");
}
}
private void handlePieSpecifics(String input, ChartPieDescriptor descriptor) {
double[] values = commaSeparatedDoubles(input, singleLine("data", "\\[(.*)\\]"));
if (values != null)
descriptor.setValues(values);
String radius = first(input, singleLine("radius", "(.+)"));
if (radius != null)
descriptor.setRadius(Double.parseDouble(radius));
String innerRadius = first(input, singleLine("inner-radius", "(.+)"));
if (innerRadius != null)
descriptor.setInnerRadius(Double.parseDouble(innerRadius));
String gap = first(input, singleLine("gap", "(.+)"));
if (gap != null)
descriptor.setGap(Double.parseDouble(gap));
}
private void handleXYSpecifics(String input, ChartXYDescriptor descriptor) {
double[] xs = commaSeparatedDoubles(input, singleLine("x", "\\[(.*)\\]"));
if (xs != null)
descriptor.setXs(xs);
List<String> blocks = allMultiline(input, "(y:[^\n]*(?:\n\\s*\\-[^\n]*)*)");
for(String block : blocks) {
double[] ys = commaSeparatedDoubles(block, singleLine("y", "\\[(.*)\\]"));
descriptor.addYs(ys);
}
}
private static double[] commaSeparatedDoubles(String input, Pattern pattern) {
String raw = first(input, pattern);
if (raw == null)
return null;
String[] valuesAsString = raw.split(",");
double[] values = new double[valuesAsString.length];
int pos = 0;
for (String valueAsString : valuesAsString) {
values[pos++] = Double.parseDouble(valueAsString);
}
return trimIfRequired(values, pos);
}
private static double[] trimIfRequired(double[] values, int length) {
if (values.length > length) {
double[] n = new double[length];
System.arraycopy(values, 0, n, 0, length);
return n;
}
return values;
}
private static Boolean bool(String input, Pattern pattern) {
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
return Boolean.parseBoolean(matcher.group(1));
}
return null;
}
private static String first(String input, Pattern pattern) {
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
return matcher.group(1);
}
return null;
}
private List<String> allMultiline(String input, String pattern) {
Matcher matcher = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE|Pattern.MULTILINE).matcher(input);
List<String> found = new ArrayList<String>();
while(matcher.find()) {
found.add(matcher.group(1));
}
return found;
}
private static Pattern singleLine(String key, String valuePattern) {
return Pattern.compile("^\\s*\\Q" + key + "\\E\\s*:\\s*" + valuePattern + "\\s*$",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
}
}