text: package er.plot;
  
  import java.lang.reflect.Method;
  import java.util.Enumeration;
  
  import org.jfree.chart.ChartFactory;
  import org.jfree.chart.JFreeChart;
  import org.jfree.chart.plot.PlotOrientation;
  import org.jfree.data.category.CategoryDataset;
  import org.jfree.data.category.DefaultCategoryDataset;
  import org.jfree.data.general.Dataset;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.webobjects.appserver.WOContext;
  import com.webobjects.foundation.NSArray;
  import com.webobjects.foundation.NSForwardException;
  import com.webobjects.foundation.NSKeyValueCodingAdditions;
  
  /**
   * Display a category chart - a chart where you have 2 or 3 dimensions. The most important binding is <code>items</code> which
   * should contain an array of objects from which the values <code>xNameKey</code>, <code>yNameKey</code>, <code>categoryKey</code> and <code>
   * valueKey</code> are retrieved. For example, you might have an array of line items, 
   * with a valueKey <code>amount</code>, an <code>xNameKey</code> with <code>invoice.datePurchased</code>, <code>yNameKey</code>, <code>categoryKey</code>and a nameKey <code>product.name</code>. 
   * 
   * @binding name the name of the chart
   * @binding chartType the type of the chart (possible values depend on the concrete subclass)
   * @binding imageType the type of the image to show: <code>png</code> (default) or <code>jpeg</code>
   * @binding width the width of the chart (400 pixel if not specified)
   * @binding height the height of the chart (400 pixel if not specified)
   * @binding dataset Dataset to use. If this is given, then items, nameKey, valueKey and categoryKey are not considered.
   * @binding items array of values to display the chart for
   * @binding nameKey the key for the name (must return Comparable)
   * @binding valueKey the key for the value (must return Number)
   * @binding categoryKey the key for the categories (optional, must return Comparable)
   * @binding xName the name for the x axis (String)
   * @binding yName the name for the y axis (String)
   * @binding showLegends true, if legends should be shown
   * @binding showToolTips true, if tool tips should be shown
   * @binding showUrls true, if urls should be shown
   * @binding orientation either "horizontal" (default) or "vertical"
   * @binding chart Chart to use instead of the created one. If this binding is set-able, then it will be set to the actually used chart
   * @binding configuration NSDictionary that will be applied to the chart via key-value-coding prior to rendering. Contains 
   *      entries like <code>antiAlias=true</code> or <code>categoryPlot.dataAreaRatio = 0.8</code>.
   * @author ak
   */
  
  public class ERPCategoryChart extends ERPChart {
  	/**
  	 * Do I need to update serialVersionUID?
  	 * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the 
  	 * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
  	 */
  	private static final long serialVersionUID = 1L;
  
  	private static final Logger log = LoggerFactory.getLogger(ERPCategoryChart.class);
  
      public static final NSArray<String> SUPPORTED_TYPES = new NSArray<>(new String[]{
              "BarChart", "StackedBarChart", "BarChart3D", "StackedBarChart3D", "AreaChart", 
              "StackedAreaChart", "LineChart", "WaterfallChart"
      });
      
      protected String _categoryKey;
      protected String _yName;
      protected String _xName;
      protected PlotOrientation _orientation;
      
      public ERPCategoryChart(WOContext context) {
          super(context);
      }
      
      @Override
  	public void reset() {
          super.reset();
          _xName = null;
          _yName = null;
          _categoryKey = null;
          _orientation = null;
      }
      
      public String categoryKey() {
          if(_categoryKey == null) {
              _categoryKey = stringValueForBinding("categoryKey", null);
          }
          return _categoryKey;
      }
      
      public String xName() {
          if(_xName == null) {
              _xName = stringValueForBinding("xName", "xName");
          }
          return _xName;
      }
      
      public String yName() {
          if(_yName == null) {
              _yName = stringValueForBinding("yName", "yName");
          }
          return _yName;
      }
      
      public PlotOrientation orientation() {
          if(_orientation == null) {
              _orientation = ("horizontal".equals(stringValueForBinding("orientation", "vertical")) ? 
                      PlotOrientation.HORIZONTAL : PlotOrientation.VERTICAL);
          }
          return _orientation;
      }
      
      @Override
  	protected NSArray<String> supportedTypes() {
          return SUPPORTED_TYPES;
      }
      
      @Override
  	protected JFreeChart createChart() {
          JFreeChart chart = null;
          String name = stringValueForBinding("name", "");
          Class<ChartFactory> clazz = ChartFactory.class;
          try {
              Method method = clazz.getDeclaredMethod("create" + chartType(), new Class[] {
                  String.class, String.class, String.class, CategoryDataset.class, PlotOrientation.class, 
                  boolean.class, boolean.class, boolean.class
              });
              chart = (JFreeChart) method.invoke(clazz, new Object[] {name, xName(), yName(), dataset(), orientation(), 
                      (showLegends() ? Boolean.TRUE : Boolean.FALSE),
                      (showToolTips() ? Boolean.TRUE : Boolean.FALSE),
                      (showUrls() ? Boolean.TRUE : Boolean.FALSE )
              });
          } catch(Throwable t) {
              log.error("Could not create chart.", t);
              throw  NSForwardException._runtimeExceptionForThrowable(t);
          }
          return chart;
      }
      
      @Override
  	protected Dataset createDataset() {
          DefaultCategoryDataset dataset = new DefaultCategoryDataset();
          
          for(Enumeration<?> items = items().objectEnumerator(); items.hasMoreElements(); ) {
              Object item = items.nextElement();
              Comparable<?> name = (Comparable<?>)NSKeyValueCodingAdditions.Utility.valueForKeyPath(item, nameKey());
              Number value = (Number)NSKeyValueCodingAdditions.Utility.valueForKeyPath(item, valueKey());
              Comparable<?> category = null;
              if(categoryKey() != null) {
                  category = (Comparable<?>)NSKeyValueCodingAdditions.Utility.valueForKeyPath(item, categoryKey());
              }
              dataset.setValue(value, name, category);
          }
          return dataset;
      }
  }
  