/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.rendering.internal.macro.chart.source;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.jfree.chart.axis.Axis;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickMarkPosition;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.xwiki.chart.axis.AxisType;
import org.xwiki.chart.plot.PlotType;
import org.xwiki.rendering.macro.MacroExecutionException;
/**
* A configurator object for the axes.
*
* This super class provides basic parameter validation.
*
* @version $Id: 55363825ab755a236151a92e1d714116144dbbce $
* @since 4.2M1
*/
public class AxisConfigurator extends AbstractConfigurator
{
/**
* The name of the domain axis parameter.
*/
public static final String DOMAIN_AXIS_PARAM = "domain_axis_type";
/**
* The name of the range axis parameter.
*/
public static final String RANGE_AXIS_PARAM = "range_axis_type";
/**
* The name of the domain axis date format parameter. Used only if the axis type is date.
*/
public static final String DOMAIN_AXIS_DATE_FORMAT_PARAM = "domain_axis_date_format";
/**
* The name of the range axis date format parameter. Used only if the axis type is date.
*/
public static final String RANGE_AXIS_DATE_FORMAT_PARAM = "range_axis_date_format";
/**
* The lower limit on the domain axis.
*/
public static final String DOMAIN_AXIS_LOWER_PARAM = "domain_axis_lower";
/**
* The upper limit on the domain axis.
*/
public static final String DOMAIN_AXIS_UPPER_PARAM = "domain_axis_upper";
/**
* The lower limit on the range axis.
*/
public static final String RANGE_AXIS_LOWER_PARAM = "range_axis_lower";
/**
* The upper limit on the range axis.
*/
public static final String RANGE_AXIS_UPPER_PARAM = "range_axis_upper";
/**
* The configured axis types.
*/
private AxisType[] axisTypes = {null, null, null};
/**
* The configured axis date formats.
*/
private String[] axisDateFormat = {null, null, null};
/**
* The locale configuration.
*/
private final LocaleConfiguration localeConfiguration;
/**
* The lower limits.
*/
private String[] axisLowerLimit = {null, null, null};
/**
* The upper limits.
*/
private String[] axisUpperLimit = {null, null, null};
/**
* @param localeConfiguration The locale configuration.
*/
public AxisConfigurator(LocaleConfiguration localeConfiguration)
{
this.localeConfiguration = localeConfiguration;
}
@Override
public boolean setParameter(String key, String value) throws MacroExecutionException
{
boolean claimed = true;
if (DOMAIN_AXIS_PARAM.equals(key)) {
AxisType type = AxisType.forName(value);
if (type == null) {
invalidParameterValue(DOMAIN_AXIS_PARAM, value);
}
setAxisType(0, type);
} else if (DOMAIN_AXIS_DATE_FORMAT_PARAM.equals(key)) {
setAxisDateFormat(0, value);
} else if (RANGE_AXIS_PARAM.equals(key)) {
AxisType type = AxisType.forName(value);
if (type == null) {
invalidParameterValue(RANGE_AXIS_PARAM, value);
}
setAxisType(1, type);
} else if (RANGE_AXIS_DATE_FORMAT_PARAM.equals(key)) {
setAxisDateFormat(0, value);
} else if (!claimLimitParameters(key, value)) {
claimed = false;
}
return claimed;
}
/**
* Claime limit parameters.
*
* @param key the parameter name.
* @param value the parameter value.
* @return {@code true} if the parameter was claimed.
*/
private boolean claimLimitParameters(String key, String value)
{
boolean claimed = true;
if (DOMAIN_AXIS_LOWER_PARAM.equals(key)) {
axisLowerLimit[0] = value;
} else if (DOMAIN_AXIS_UPPER_PARAM.equals(key)) {
axisUpperLimit[0] = value;
} else if (RANGE_AXIS_LOWER_PARAM.equals(key)) {
axisLowerLimit[1] = value;
} else if (RANGE_AXIS_UPPER_PARAM.equals(key)) {
axisUpperLimit[1] = value;
} else {
claimed = false;
}
return claimed;
}
/**
* Set the axis type for the axis at the given index.
*
* @param index The index.
* @param axisType the axis type.
*/
private void setAxisType(int index, AxisType axisType)
{
axisTypes[index] = axisType;
}
/**
* Set the date format for the axis at the given index.
*
* @param index The index.
* @param dateFormatString the date format.
* @throws MacroExecutionException if the date format string is invalid.
*/
private void setAxisDateFormat(int index, String dateFormatString) throws MacroExecutionException
{
axisDateFormat[index] = dateFormatString;
}
/**
* Set the axes in the chart model.
*
* @param plotType The target plot type.
* @param chartModel The target chart model.
* @throws MacroExecutionException if the axes are incorrectly configured.
*/
public void setAxes(PlotType plotType, SimpleChartModel chartModel) throws MacroExecutionException
{
AxisType[] defaultAxisTypes = plotType.getDefaultAxisTypes();
for (int i = 0; i < axisTypes.length; i++) {
AxisType type = axisTypes[i];
if (i >= defaultAxisTypes.length) {
if (type != null) {
throw new MacroExecutionException("To many axes for plot type.");
}
continue;
}
if (type == null) {
type = defaultAxisTypes[i];
}
Axis axis;
switch (type) {
case NUMBER:
NumberAxis numberAxis = new NumberAxis();
axis = numberAxis;
setNumberLimits(numberAxis, i);
break;
case CATEGORY:
axis = new CategoryAxis();
break;
case DATE:
DateAxis dateAxis = new DateAxis();
axis = dateAxis;
dateAxis.setTickMarkPosition(DateTickMarkPosition.END);
if (axisDateFormat[i] != null) {
try {
DateFormat dateFormat = new SimpleDateFormat(axisDateFormat[i],
localeConfiguration.getLocale());
dateAxis.setDateFormatOverride(dateFormat);
} catch (IllegalArgumentException e) {
throw new MacroExecutionException(String.format("Invalid date format [%s].",
axisDateFormat[i]));
}
}
setDateLimits(dateAxis, i);
break;
default:
throw new MacroExecutionException(String.format("Unsupported axis type [%s]", type.getName()));
}
chartModel.setAxis(i, axis);
}
}
/**
* Set the limits of a number axis.
*
* @param axis The axis.
* @param index The index of the axis
* @throws MacroExecutionException if the parameters could not be parsed as numbers.
*/
private void setNumberLimits(ValueAxis axis, int index) throws MacroExecutionException
{
try {
if (axisLowerLimit[index] != null) {
Number number = NumberUtils.createNumber(StringUtils.trim(axisLowerLimit[index]));
axis.setLowerBound(number.doubleValue());
}
if (axisUpperLimit[index] != null) {
Number number = NumberUtils.createNumber(StringUtils.trim(axisUpperLimit[index]));
axis.setUpperBound(number.doubleValue());
}
} catch (NumberFormatException e) {
throw new MacroExecutionException("Invalid number in axis bound.", e);
}
}
/**
* Set the limits of a date axis.
*
* @param axis The axis.
* @param index The index of the axis.
* @throws MacroExecutionException if the parameters could not be parsed as dates.
*/
private void setDateLimits(DateAxis axis, int index) throws MacroExecutionException
{
try {
if (axisLowerLimit[index] != null) {
Date date = localeConfiguration.getDateFormat().parse(StringUtils.trim(axisLowerLimit[index]));
axis.setMinimumDate(date);
}
if (axisUpperLimit[index] != null) {
Date date = localeConfiguration.getDateFormat().parse(StringUtils.trim(axisUpperLimit[index]));
axis.setMaximumDate(date);
}
} catch (ParseException e) {
throw new MacroExecutionException("Invalid date in axis bound.", e);
}
}
}