package charts.builder.spreadsheet; import static com.google.common.base.Preconditions.checkNotNull; import static java.util.Arrays.asList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.text.StrSubstitutor; import charts.Chart; import charts.ChartType; import charts.Region; import charts.builder.ChartTypeBuilder; import charts.builder.DataSource; import charts.builder.DataSource.MissingDataException; import charts.jfree.AttributeMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; public abstract class AbstractBuilder implements ChartTypeBuilder { private static final SubstitutionKey TYPE = new SubstitutionKey("type", "the chart type label" + " e.g. Crown of Thorns Outbreak for chart type COTS_OUTBREAK", new SubstitutionKey.Val() { @Override public String value(Context ctx) { return ctx.type().getLabel(); } }); private static final SubstitutionKey REGION = new SubstitutionKey("region", "the region name" + " e.g. Wet Tropics", new SubstitutionKey.Val() { @Override public String value(Context ctx) { return ctx.region().getProperName(); } }); private final List<ChartType> types; public AbstractBuilder(ChartType type) { types = Lists.newArrayList(type); } public AbstractBuilder(List<ChartType> types) { this.types = types; } public boolean supports(ChartType type) { return types.contains(type); } protected abstract boolean canHandle(SpreadsheetDataSource datasource); protected abstract Chart build(Context context); protected Map<String, List<String>> getParameters(SpreadsheetDataSource datasource, ChartType type) { return Maps.newHashMap(); } protected Map<String, List<String>> getParameters(DataSource datasource, ChartType type) { return getParameters((SpreadsheetDataSource)datasource, type); } @Override public List<Chart> build(DataSource datasource, ChartType type, List<Region> regions, Map<String, String> parameters) { List<Chart> charts = Lists.newArrayList(); if((type == null) || types.contains(type)) { if (datasource instanceof SpreadsheetDataSource) { for(int i = 0; i<((SpreadsheetDataSource)datasource).sheets();i++) { SpreadsheetDataSource ds = ((SpreadsheetDataSource)datasource).toSheet(i); if(canHandle(ds)) { charts.addAll(build(ds, type, regions, parameters)); } } } } return charts; } private List<Chart> build(SpreadsheetDataSource datasource, ChartType type, List<Region> regions, Map<String, String> parameters) { checkNotNull(regions, "Regions can be empty, but not null."); Map<String, List<String>> m = Maps.newHashMap(); Map<String, List<String>> supportedParameters = getParameters(datasource, type); for(String key : supportedParameters.keySet()) { if ((parameters != null) && parameters.containsKey(key)) { m.put(key, asList(parameters.get(key))); } else { m.put(key, supportedParameters.get(key)); } } final List<ChartType> t = type == null ? types : asList(type); final List<Region> r = regions.isEmpty() ? asList(Region.values()) : regions; final List<Chart> charts = Lists.newLinkedList(); for (Object o : ChartPermutations.apply(t, r, m)) { ChartPermutation p = (ChartPermutation)o; final Chart chart = build(context(datasource, p.chartType(), p.region(), p.javaParams())); if (chart != null) { charts.add(chart); } } return charts; } protected boolean cellEquals(SpreadsheetDataSource datasource, String s, String cellreference) { try { return s.equals(StringUtils.strip(datasource.select(cellreference).asString())); } catch (MissingDataException e) { return false; } } protected ChartConfigurator configurator(Context ctx) { return new ChartConfigurator(defaults(ctx.type()), ctx.datasource(), new StrSubstitutor(substitutions(ctx))); } private Map<String, String> substitutions(Context ctx) { Map<String, String> m = Maps.newHashMap(); for(SubstitutionKey key : substitutionKeys()) { m.put(key.getName(), key.getValue(ctx)); } return m; } public AttributeMap defaults(ChartType type) { throw new ChartConfigurationNotSupported(this); } public Set<SubstitutionKey> substitutionKeys() { return ImmutableSet.of(TYPE, REGION); } ChartType type() { if(types.size() == 1) { return types.get(0); } else { throw new RuntimeException(String.format("this builder (%s) supports multiple types", this.getClass().getName())); } } protected Context context(SpreadsheetDataSource datasource, ChartType type, Region region, Map<String, String> parameters) { return new Context(datasource, type, region, parameters); } protected String formatNumber(String format, Number n) { return n!=null?String.format(format,n.doubleValue()):""; } }