001/*
002 *  $Source: $
003 *  $Name:  $
004 *  $Revision: $
005 *  $Date: $
006 *  $Locker:  $
007 */
008/*
009 *  CategorizedStackedBarChart -- A class to create a categorized stacked bar chart.
010 *
011 *  Written by Tom Gutwin - WebARTS Design.
012 *  Copyright (C) 2007-2010 WebARTS Design, North Vancouver Canada
013 *  http://www..webarts.bc.ca
014 *
015 *  This program is free software; you can redistribute it and/or modify
016 *  it under the terms of the GNU General Public License as published by
017 *  the Free Software Foundation; either version 2 of the License, or
018 *  (at your option) any later version.
019 *
020 *  This program is distributed in the hope that it will be useful,
021 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
022 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 *  GNU General Public License for more details.
024 *
025 *  You should have received a copy of the GNU General Public License
026 *  along with this program; if not, write to the Free Software
027 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
028 */
029package ca.bc.webarts.tools;
030
031import java.awt.*;
032import java.awt.Color;
033import java.awt.Dimension;
034import java.awt.Font;
035import java.awt.GradientPaint;
036import java.awt.geom.Rectangle2D;
037import java.io.File;
038import java.io.FileReader;
039import java.io.FileNotFoundException;
040import java.io.FileOutputStream;
041import java.io.IOException;
042import java.io.OutputStream;
043import java.io.BufferedOutputStream;
044import java.io.OutputStreamWriter;
045import java.awt.Rectangle;
046import java.util.Vector;
047import java.io.Writer;
048
049import javax.swing.JPanel;
050
051import org.jfree.chart.ChartFactory;
052import org.jfree.chart.ChartUtilities;
053import org.jfree.chart.ChartPanel;
054import org.jfree.chart.JFreeChart;
055import org.jfree.chart.LegendItem;
056import org.jfree.chart.LegendItemCollection;
057import org.jfree.chart.axis.SubCategoryAxis;
058import org.jfree.chart.plot.CategoryPlot;
059import org.jfree.chart.plot.Plot;
060import org.jfree.chart.plot.PlotOrientation;
061import org.jfree.chart.plot.CategoryMarker;
062import org.jfree.chart.plot.IntervalMarker;
063import org.jfree.chart.labels.*;
064import org.jfree.chart.renderer.category.GroupedStackedBarRenderer;
065//import org.jfree.data.io.CSV;
066import org.jfree.data.KeyToGroupMap;
067import org.jfree.data.category.CategoryDataset;
068import org.jfree.data.category.DefaultCategoryDataset;
069import org.jfree.chart.renderer.category.BarRenderer;
070import org.jfree.ui.ApplicationFrame;
071import org.jfree.ui.GradientPaintTransformType;
072import org.jfree.ui.RefineryUtilities;
073import org.jfree.ui.StandardGradientPaintTransformer;
074import org.jfree.ui.TextAnchor;
075
076import com.itextpdf.text.Document;
077import com.itextpdf.text.DocumentException;
078//import com.itextpdf.text.Font;
079import com.itextpdf.text.FontFactory;
080import com.itextpdf.text.Header;
081//import com.itextpdf.text.Rectangle;
082import com.itextpdf.text.Paragraph;
083import com.itextpdf.text.pdf.DefaultFontMapper;
084import com.itextpdf.text.pdf.FontMapper;
085import com.itextpdf.text.pdf.PdfContentByte;
086import com.itextpdf.text.pdf.PdfTemplate;
087import com.itextpdf.text.pdf.PdfWriter;
088
089
090import au.com.bytecode.opencsv.CSVReader;
091//import au.com.bytecode.opencsv.CSVWriter;
092import jxl.*;
093
094
095import ca.bc.webarts.widgets.Util;
096
097/**
098 *  A class that reads a csv file of data and creates a bar chart - Categoirzed, grouped Stacked Bars.
099 * It also saves the rendered chart as a png and svg file so you can use it elsewhere.<br />
100 * <a href="file:///C:/DOCUME~1/tgutwin/Desktop/BCHydroReliabilityAndGrowthSpend.svg">
101 *   <img src="file:///C:/DOCUME~1/tgutwin/Desktop/BCHydroReliabilityAndGrowthSpend.png" width="240" height="160"/>
102 * </a>
103 * <br /><br />
104 * The data in the csv has to be in the following format:<br />
105 * <pre>
106 * // Sample data for a Categorized Bar chart with sub-grouped stacked bars.
107 * // the first real line is to define the group titles
108 * Fiscal Year,Accounting Category,Project Driver Dollars
109 * F2005,SI Sustain,Reliability,12.20
110 * F2005,SI Sustain,Vegetation Management,25.5
111 * F2005,SI Sustain,Asset Replacement,34
112 * F2005,SI Sustain,Wires Maintenance,25.6
113 * F2005,Growth,System,8.4
114 * F2006,SI Sustain,Reliability,13.40
115 * F2006,SI Sustain,Vegetation Management,23.40
116 * F2006,SI Sustain,Asset Replacement,33.00
117 * F2006,SI Sustain,Wires Maintenance,26.90
118 * F2006,Growth,System,21.20
119 * F2007,SI Sustain,Reliability,14.51
120 * F2007,SI Sustain,Vegetation Management,22.20
121 * F2007,SI Sustain,Asset Replacement,38.10
122 * F2007,SI Sustain,Wires Maintenance,26.80
123 * F2007,Growth,System,31.70
124 * F2008,SI Sustain,Reliability,14.51
125 * F2008,SI Sustain,Vegetation Management,23.00
126 * F2008,SI Sustain,Asset Replacement,38.20
127 * F2008,SI Sustain,Wires Maintenance,31.30
128 * F2008,Growth,System,52.50
129 * </pre>
130 * Tom Gutwin
131 */
132public class CategorizedStackedBarChart extends ApplicationFrame
133{
134  public static final short FILETYPE_UNKNOWN=0;
135  public static final short FILETYPE_XLS=1;
136  public static final short FILETYPE_CSV=2;
137  public static final short FILETYPE_SQL=3;
138
139  private String categoryName = "Category";
140  private Vector categories = new Vector();
141  private String groupName = "Group";
142  private Vector groups = new Vector();
143  private String stackedDataName = "StackedData";
144  private Vector stackedGroup = new Vector();
145  private GroupedStackedBarRenderer groupedstackedbarrenderer
146         = new GroupedStackedBarRenderer();
147  private KeyToGroupMap keytogroupmap_ = new KeyToGroupMap();
148  private  JFreeChart jfreechart_ = null;
149  private int chartdX = 1200;
150  private int chartdY = 800;
151  /* The class value for the percent of category space to use for a margin around the category.
152     It is a decimal  double value representing the percentage to use (for example, 0.05 is five percent).*/
153  private double categoryMargin_ = 0.25;
154
155  private String chartTitle = "Categorized Stacked Bar Chart";
156  public Color[] chartColours = new Color[20];
157  public GradientPaint[] gradientpaint =  new GradientPaint[chartColours.length];
158  public Color chartBackgroundFromColor = new Color(200,205,255);
159  public Color chartBackgroundToColor = new Color(160,240,255);
160  public Font itemLabelFont = new Font("SansSerif",Font.PLAIN,7);
161  private boolean showLabels = true;
162  private boolean showToolTips = true;
163
164  /**
165   *  Constructor for the CategorizedStackedBarChart object. This constructor
166   * does not do anything exept instantiate so some of the methods can be used.
167   *
168   */
169  public CategorizedStackedBarChart()
170  {
171    super("");
172  }
173
174
175  /**
176   *  Constructor for the CategorizedStackedBarChart object. This constructor
177   * does not do anything exept instantiate so some of the methods can be used.
178   *
179   */
180  public CategorizedStackedBarChart(String chartTitle)
181  {
182    super(chartTitle);
183    this.chartTitle = chartTitle;
184    //init colours
185    initColours();
186  }
187
188
189  /**
190   *  Constructor for the CategorizedStackedBarChart object
191   *
192   * @param  chartTitle  is the title to place at the top of the chart, it also uses this for the exported filenames
193   * @param csvFilename is the name of the csv file to read the data from
194   */
195  public CategorizedStackedBarChart(String chartTitle, String filename)
196  {
197    super(chartTitle);
198    this.chartTitle = chartTitle;
199    //init colours
200    initColours();
201
202    JPanel jpanel = null;
203
204    switch (getFileType(filename))
205    {
206      case FILETYPE_XLS:
207        jpanel = createChartPanel(createDatasetFromXLS(filename));
208        break;
209
210      case FILETYPE_CSV:
211        jpanel = createChartPanel(createDatasetFromCSVFile(filename));
212        break;
213
214      case FILETYPE_SQL:
215        jpanel = createChartPanel(createDatasetFromSQL(filename));
216        break;
217
218      default:
219
220    }
221
222    if (jpanel!=null)
223    {
224      jpanel.setPreferredSize(new Dimension(chartdX, chartdY));
225      setContentPane(jpanel);
226    }
227  }
228
229
230  public static short getFileType(String filename)
231  {
232    short retVal = FILETYPE_UNKNOWN;
233    if (filename.length() >=3)
234    {
235      String ext = filename.substring(filename.length()-3).toLowerCase();
236      //System.out.println("Input file is type:"+ext);
237      if (ext.equals("xls")) { retVal = FILETYPE_XLS;}
238      else if (ext.equals("sql")) {retVal = FILETYPE_SQL;}
239      else if (ext.equals("csv")) { retVal = FILETYPE_CSV;}
240    }
241    return retVal;
242  }
243
244
245  private void initColours()
246  {
247    chartColours[0] = new Color(34, 34, 255);
248    chartColours[1] = new Color(34, 255, 34);
249    chartColours[2] = new Color(255, 34, 34);
250    chartColours[3] = new Color(255, 255, 34);
251    chartColours[4] = new Color(255, 100, 34);
252    chartColours[5] = new Color(0, 100, 150);
253    chartColours[6] = new Color(235, 235, 235);
254    chartColours[7] = new Color(255, 10, 255);
255    chartColours[8] = new Color(100, 40, 100);
256    chartColours[9] = new Color(70, 255, 255);
257    chartColours[10] = new Color(155, 10, 255);
258    chartColours[11] = new Color(25, 125, 0);
259    chartColours[12] = new Color(255, 34, 134);
260    chartColours[13] = new Color(180, 180, 255);
261    chartColours[14] = new Color(180, 60, 0);
262    chartColours[15] = new Color(134, 150, 150);
263    chartColours[16] = new Color(225, 225, 225);
264    chartColours[17] = new Color(0, 255, 255);
265    chartColours[18] = new Color(140, 140, 140);
266    chartColours[19] = new Color(50, 50, 50);
267    for (int i=0; i<chartColours.length && chartColours[i] != null; i++)
268      gradientpaint[i] = new GradientPaint(0.0F, 0.0F, chartColours[i],
269                              0.0F, 0.0F, chartColours[i].darker().darker().darker());
270  }
271
272
273  /**
274   *  The main program for the CategorizedStackedBarChart class
275   *
276   * @param  args  The command line arguments (1st is the csvFilename, the rest make up a Chart Title)
277   */
278  public static void main(String[] args)
279  {
280    if (args.length >0 && getFileType(args[0])!=FILETYPE_UNKNOWN)
281    {
282      CategorizedStackedBarChart chart = null;
283      String titleParms = "Categorized Stacked Bar Chart";
284      if (args.length >1 )
285      {
286        titleParms = "";
287        for (int i=1; i < args.length; i++)
288          titleParms += args[i] + " ";
289        titleParms = titleParms.trim();
290      }
291      chart = new CategorizedStackedBarChart(titleParms, args[0]);
292      chart.pack();
293      RefineryUtilities.centerFrameOnScreen(chart);
294      chart.setVisible(true);
295      chart.saveChartToSvg(new File(Util.spacesToCapsInString(titleParms)+".svg"));
296      chart.saveChartToPng(new File(Util.spacesToCapsInString(titleParms)+".png"));
297      chart.saveChartToPdf(new File(Util.spacesToCapsInString(titleParms)+".pdf"));
298    }
299    else
300    {
301      System.out.println("Don't be a dufus... give me a csv, sqlFilename or xls filename to use.");
302    }
303  }
304
305
306  public JFreeChart getChart() { return jfreechart_;}
307
308
309  /**
310   *  Description of the Method
311   *
312   * @return    Description of the Return Value
313   */
314  public JPanel createChartPanel(CategoryDataset ds)
315  {
316    jfreechart_ = createCategoryStackedBarChart(ds);
317    return new ChartPanel(jfreechart_);
318  }
319
320
321  /**
322   *  Queries a db and converts the results into a Dataset that can be used in
323   *  a Grouped Stacked Bar Chart.
324   *
325   * @return    The SQL results as a CategoryDataset
326   */
327  private CategoryDataset createDatasetFromSQL(String sqlFilename)
328  {
329    DefaultCategoryDataset cd = new DefaultCategoryDataset();
330
331    try
332    {
333    }
334    catch( Exception ex)
335    {
336    }
337    return cd;
338  }
339
340
341  /**
342   *  takes an xls file in the proper format into a Dataset useable by the charter.
343   *
344   * @return    Description of the Return Value
345   */
346  public CategoryDataset createDatasetFromXLS(String xlsFilename)
347  {
348    DefaultCategoryDataset cd = new DefaultCategoryDataset();
349
350    /*  These are to read data from an excel data file */
351    Workbook workbook = null;
352    Sheet configSheet = null;
353    Sheet dataSheet = null;
354
355    try
356    {
357      /* Get the Excel Config file init */
358      System.out.println("Opening Excel Workbook:"+xlsFilename );
359      File f = new File(xlsFilename);
360      if (f.exists() && f.canRead())
361      {
362        workbook = Workbook.getWorkbook(f);
363        dataSheet = workbook.getSheet(0);  // keep the data on the 1st sheet
364        String categoryName = "";
365        String groupName = "";
366        String stackName = "";
367        double value = 0.0;
368        boolean done = false;
369        boolean nextGroup = false;
370        String cellStr = "";
371        int valCol = 3;
372        keytogroupmap_ = null;
373
374        for (int row = 3; !done; row++)
375        {
376          nextGroup = false;
377          System.out.print((row-2)+") ");
378          try
379          {
380            // getCell is (Column, row)
381            cellStr = dataSheet.getCell(0,row+1).getContents().trim();
382            if (cellStr !=null && !cellStr.equals("")) nextGroup = true; //this is a total row so skip it
383            cellStr = dataSheet.getCell(0,row).getContents().trim();
384            if (cellStr !=null && !cellStr.equals("")) categoryName = cellStr;
385            //System.out.print(" (cellStr="+cellStr+") ");
386            System.out.print(categoryName+", ");
387            cellStr = dataSheet.getCell(1,row+1).getContents().trim();
388            if (cellStr !=null && !cellStr.equals("")) nextGroup = true; //this is a total row so skip it
389            cellStr = dataSheet.getCell(1,row).getContents().trim();
390            if (cellStr !=null && !cellStr.equals("")) groupName = cellStr;
391            //System.out.print(" (cellStr="+cellStr+") ");
392            System.out.print(groupName+", ");
393            cellStr = dataSheet.getCell(2,row).getContents().trim();
394            if (cellStr !=null && !cellStr.equals("")) stackName = cellStr;
395            //System.out.print(" (cellStr="+cellStr+") ");
396            System.out.print(stackName+", ");
397
398
399            if (!nextGroup)
400            {
401              // Find the last value column
402              valCol = 3;
403              try
404              {
405                while (dataSheet.getCell(valCol+1,row)!=null &&
406                        dataSheet.getCell(valCol+1,row).getContents()!=null &&
407                        !dataSheet.getCell(valCol+1,row).getContents().trim().equals(""))
408                  valCol++;
409              }
410              catch (java.lang.ArrayIndexOutOfBoundsException vEx)
411              {
412
413              }
414              value = ((NumberCell)dataSheet.getCell(valCol,row)).getValue();
415              System.out.print(value);
416
417              if (keytogroupmap_ == null) keytogroupmap_ = new KeyToGroupMap(groupName);
418              if (!categories.contains(categoryName)) categories.add(categoryName);
419              if (!groups.contains(groupName)) groups.add(groupName);
420              if (!stackedGroup.contains(stackName)) stackedGroup.add(stackName);
421              cd.addValue(value, stackName + " ("+groupName+")", categoryName);
422              keytogroupmap_.mapKeyToGroup(stackName + " ("+groupName+")", groupName);
423
424              try
425              {
426              if (dataSheet.getCell(valCol,row+1) == null ||
427                  dataSheet.getCell(valCol,row+1).getContents() == null ||
428                  dataSheet.getCell(valCol,row+1).getContents().equals("NA") ||
429                  dataSheet.getCell(valCol,row+1).getContents().equals("") )
430                done = true;
431              }
432              catch (java.lang.ArrayIndexOutOfBoundsException vEx)
433              {
434                System.out.println("\nWe Seem to be done at Row:"+row+" Col:"+valCol);
435                done = true;
436              }
437            }
438            System.out.println();
439          }
440          catch (java.lang.ArrayIndexOutOfBoundsException vEx)
441          {
442            nextGroup = true; done = true;
443          }
444
445        }
446        groupedstackedbarrenderer.setSeriesToGroupMap(keytogroupmap_);
447      }
448      else
449        System.out.println("What The F%#@, Cannot Read File "+xlsFilename);
450    }
451    catch (FileNotFoundException fnfEx)
452    {
453      System.out.println("File Not Found "+xlsFilename);
454      fnfEx.printStackTrace();
455    }
456    catch (IOException ioEx)
457    {
458     System.out.println("Could Not read input EXCEL File: "+xlsFilename);
459     ioEx.printStackTrace();
460    }
461    catch (jxl.read.biff.BiffException biffEx)
462    {
463     System.out.println("Could Not read input EXCEL File: "+xlsFilename);
464     biffEx.printStackTrace();
465    }
466    catch( Exception ex)
467    {
468     ex.printStackTrace();
469    }
470    return cd;
471  }
472
473
474  /**
475   *  Description of the Method
476   *
477   * @return    Description of the Return Value
478   */
479  public CategoryDataset createDatasetFromCSVFile(String csvFilename)
480  {
481    DefaultCategoryDataset cd = new DefaultCategoryDataset();
482
483    try
484    {
485      CSVReader reader = new CSVReader(new FileReader(csvFilename));
486      String [] nextLine;
487      int rows = 0;
488      while ((nextLine = reader.readNext()) != null)
489      {
490        if (nextLine.length>1 && !nextLine[0].startsWith("//"))
491        {
492          if (rows==1) //this is to setup an initial group
493          {
494            keytogroupmap_ = new KeyToGroupMap(nextLine[1]);
495          }
496          if (rows==0)
497          {
498            categoryName = nextLine[0];
499            groupName = nextLine[1];
500            stackedDataName = nextLine[2];
501          }
502          else
503          {
504            // nextLine[] is an array of values from the line
505            System.out.print(rows+") ");
506            for (int i=0;i< nextLine.length; i++){System.out.print(nextLine[i]+", ");}
507            System.out.println("");
508
509            if (!categories.contains(nextLine[0])) categories.add(nextLine[0]);
510            if (!groups.contains(nextLine[1])) groups.add(nextLine[1]);
511            if (!stackedGroup.contains(nextLine[2])) stackedGroup.add(nextLine[2]);
512            // add to dataset              Value       , Group      , Category
513            cd.addValue(Double.parseDouble(nextLine[3]), nextLine[2] + " ("+nextLine[1]+")", nextLine[0]);
514            //cd.addValue(Double.parseDouble(nextLine[3]), nextLine[2], nextLine[0]);
515            // Assigns which data gets into which categorygroup.
516            // One group represents One Stack on the barchart
517            //stackedGroup.add(nextLine[2]);
518            keytogroupmap_.mapKeyToGroup(nextLine[2] + " ("+nextLine[1]+")", nextLine[1]);
519            //keytogroupmap_.mapKeyToGroup(nextLine[2], nextLine[1]);
520          }
521          rows++;
522        }
523      }
524      groupedstackedbarrenderer.setSeriesToGroupMap(keytogroupmap_);
525    }
526    catch (FileNotFoundException fnfEx)
527    {
528      System.out.println("File Not Found "+csvFilename);
529      fnfEx.printStackTrace();
530    }
531    catch (IOException ioEx)
532    {
533      System.out.println("Cannot read from CSV file "+csvFilename);
534      ioEx.printStackTrace();
535    }
536
537    return cd;
538  }
539
540
541   /**
542   *  Create the dataset from delimited String.
543   *
544   * @return    Description of the Return Value
545   */
546  public CategoryDataset createDatasetFromCSV(String csv)
547  {
548    DefaultCategoryDataset cd = new DefaultCategoryDataset();
549
550    try
551    {
552      CSVReader reader = new CSVReader(new java.io.StringReader(csv));
553      String [] nextLine;
554      int rows = 0;
555      while ((nextLine = reader.readNext()) != null)
556      {
557        if (nextLine.length>1 && !nextLine[0].startsWith("//"))
558        {
559          if (rows==1) //this is to setup an initial group
560          {
561            keytogroupmap_ = new KeyToGroupMap(nextLine[1]);
562          }
563          if (rows==0)
564          {
565            categoryName = nextLine[0];
566            groupName = nextLine[1];
567            stackedDataName = nextLine[2];
568          }
569          else
570          {
571            // nextLine[] is an array of values from the line
572            System.out.print(rows+") ");
573            for (int i=0;i< nextLine.length; i++){System.out.print(nextLine[i]+", ");}
574            System.out.println("");
575
576            if (!categories.contains(nextLine[0])) categories.add(nextLine[0]);
577            if (!groups.contains(nextLine[1])) groups.add(nextLine[1]);
578            if (!stackedGroup.contains(nextLine[2])) stackedGroup.add(nextLine[2]);
579            // add to dataset              Value       , Group      , Category
580            cd.addValue(Double.parseDouble(nextLine[3]), nextLine[2] + " ("+nextLine[1]+")", nextLine[0]);
581            //cd.addValue(Double.parseDouble(nextLine[3]), nextLine[2], nextLine[0]);
582            // Assigns which data gets into which categorygroup.
583            // One group represents One Stack on the barchart
584            //stackedGroup.add(nextLine[2]);
585            keytogroupmap_.mapKeyToGroup(nextLine[2] + " ("+nextLine[1]+")", nextLine[1]);
586            //keytogroupmap_.mapKeyToGroup(nextLine[2], nextLine[1]);
587          }
588          rows++;
589        }
590      }
591      groupedstackedbarrenderer.setSeriesToGroupMap(keytogroupmap_);
592    }
593    catch (IOException ioEx)
594    {
595      System.out.println("Cannot read from CSV  "+csv);
596      ioEx.printStackTrace();
597    }
598
599    return cd;
600  }
601
602
603  /**
604   *  This method does all the work for getting the chart plot set up.
605   * It sets the labels, axis, colours, margins and whatnot. It uses the class chartTtile.
606   *
607   * @param  categorydataset  the dataset to use in the chart
608   * @return                  The category  grouped stacked bar chart.
609   */
610  public JFreeChart createCategoryStackedBarChart(CategoryDataset categorydataset)
611  {
612    return createCategoryStackedBarChart(this.chartTitle, categorydataset);
613  }
614
615
616  /**
617   *  This method does all the work for getting the chart plot set up. It sets the labels, axis, colours, margins and whatnot.
618   *
619   * @param  chartTitle  a new title to put at the top of the chart
620   * @param  categorydataset  the dataset to use in the chart
621   * @return                  The category  grouped stacked bar chart.
622   */
623  public JFreeChart createCategoryStackedBarChart(String chartTitle,
624                                                CategoryDataset categorydataset)
625  {
626    return createCategoryStackedBarChart(chartTitle, null, categorydataset);
627  }
628
629
630  /**
631   *  This method does all the work for getting the chart plot set up. It sets the labels, axis, colours, margins and whatnot.
632   *
633   * @param  chartTitle  a new title to put at the top of the chart
634   * @param  chartSubTitle  a new subtitle below the title
635   * @param  categorydataset  the dataset to use in the chart
636   * @return                  The category  grouped stacked bar chart.
637   */
638  public JFreeChart createCategoryStackedBarChart(String chartTitle, String subTitle,
639                                                CategoryDataset categorydataset)
640  {
641    jfreechart_ = ChartFactory.createStackedBarChart(chartTitle,
642                                              categoryName, stackedDataName,
643                                              categorydataset,
644                                              PlotOrientation.VERTICAL,
645                                              true, true, false);
646
647    if (subTitle !=null && !subTitle.equals(""))
648    {
649      org.jfree.chart.title.TextTitle textSubTitle = new org.jfree.chart.title.TextTitle(subTitle);
650      jfreechart_.addSubtitle(textSubTitle);
651    }
652
653    CategoryPlot categoryplot = (CategoryPlot) jfreechart_.getPlot();
654
655    // setup axis label
656    SubCategoryAxis subcategoryaxis
657         = new SubCategoryAxis(groupName+" / "+categoryName);
658
659    // set the margin between the Categories (% of space )
660    subcategoryaxis.setCategoryMargin(categoryMargin_);
661
662    // sub categorize the groups
663    for (int j=0; j<groups.size(); j++)
664    {
665      subcategoryaxis.addSubCategory((String)groups.get(j));
666      subcategoryaxis.setTickLabelFont((String)groups.get(j), itemLabelFont);
667    }
668
669    subcategoryaxis.setMaximumCategoryLabelWidthRatio(1/groups.size());
670    subcategoryaxis.setCategoryMargin(0.15);
671    subcategoryaxis.setMaximumCategoryLabelLines(3);
672
673    // Set the axis to the subcategorized axis already defined above
674    categoryplot.setDomainAxis(subcategoryaxis);
675
676    // Category series labels
677    groupedstackedbarrenderer.setItemLabelGenerator(new StandardCategoryItemLabelGenerator());
678    groupedstackedbarrenderer.setItemLabelsVisible(showLabels);
679    groupedstackedbarrenderer.setItemLabelFont(itemLabelFont);
680    //groupedstackedbarrenderer.setItemMargin(0.30);
681    ItemLabelPosition p = new ItemLabelPosition(
682        ItemLabelAnchor.CENTER, TextAnchor.CENTER, TextAnchor.CENTER, 0.0
683    );
684    groupedstackedbarrenderer.setPositiveItemLabelPosition(p);
685    categoryplot.setRenderer(groupedstackedbarrenderer);
686
687    // Assign the Floatover tooltips
688    StandardCategoryToolTipGenerator toolTipGen = new StandardCategoryToolTipGenerator();
689    groupedstackedbarrenderer.setToolTipGenerator(toolTipGen);
690    //groupedstackedbarrenderer.setDisplayToolTips(showToolTips);
691
692    // Group the entries in the Legend
693    categoryplot.setFixedLegendItems(createLegendItems());
694
695    // Assign gradient colours to the bars
696    BarRenderer barrenderer = (BarRenderer) categoryplot.getRenderer();
697    barrenderer.setDrawBarOutline(false);
698    barrenderer.setMaximumBarWidth(0.2);
699    // set the margin between the sub-groups with in each category (% of space )
700    barrenderer.setItemMargin(0.02);
701
702    // Set up the backFrame colour
703    jfreechart_.setBackgroundPaint(new GradientPaint(
704                                      0,
705                                      0,
706                                      chartBackgroundFromColor,
707                                      chartdX,
708                                      chartdY,
709                                      chartBackgroundToColor));
710
711    // Assign the colours to the stacked groups.
712    // each stacked group gets its own colour
713    for (int j = 0; j < groups.size(); j++)
714      for (int i=0; i<stackedGroup.size() && chartColours[i] != null; i++)
715        barrenderer.setSeriesPaint((j*stackedGroup.size()) + i, gradientpaint[i]);
716
717    return jfreechart_;
718  }
719
720
721  /** Gets the current jfreechart plot (null if not already initialized). **/
722  public CategoryPlot getPlot()
723  {
724    CategoryPlot categoryplot = null;
725    if (jfreechart_ !=null)
726    {
727      categoryplot = (CategoryPlot) jfreechart_.getPlot();
728    }
729    return categoryplot;
730  }
731
732
733  /**
734   * Adds a marker for the Domain category.
735   *
736   * @param catMarginPercent  The main category spacing in percent (where 0.10 is ten percent).
737   */
738  public void setDomainMarker(String categoryKey)
739  {
740    if (jfreechart_ !=null)
741    {
742      CategoryPlot categoryplot = this.getPlot();
743      if (categoryplot !=null)
744      {
745        /*
746        groupedstackedbarrenderer.getSeriesToGroupMap(keytogroupmap_);
747        final CategoryMarker marker = new CategoryMarker(categoryKey);
748        marker.setLabel("Projects Year");
749        marker.setLabelFont(new Font("SansSerif", Font.ITALIC, 11));
750        marker.setLabelAnchor(org.jfree.ui.RectangleAnchor.LEFT);
751        marker.setLabelTextAnchor(TextAnchor.CENTER_LEFT);
752        marker.setPaint(new Color(222, 222, 255, 128));
753        */
754      }
755    }
756  }
757
758
759  /**
760   * Changes the spacing between the categories.
761   *
762   * @param catMarginPercent  The main category spacing in percent (where 0.10 is ten percent).
763   */
764  public void setCategoryMargins(double catMarginPercent)
765  {
766    if (jfreechart_ !=null)
767    {
768      CategoryPlot categoryplot = this.getPlot();
769      if (categoryplot !=null)
770      {
771        BarRenderer barrenderer = (BarRenderer) categoryplot.getRenderer();
772        barrenderer.setItemMargin(catMarginPercent);
773
774        //SubCategoryAxis subcategoryaxis = (SubCategoryAxis) barrenderer.getDomainAxis(categoryplot,0);
775        //subcategoryaxis.setCategoryMargin(subCatMarginPercent);
776      }
777    }
778  }
779
780
781  /**
782   * Changes the spacing between the sub categories.
783   *
784   * @param subCatMarginPercent  The sub-category spacing in percent (where 0.10 is ten percent).
785   */
786  public void setSubCategoryMargins(double subCatMarginPercent )
787  {
788    if (jfreechart_ !=null)
789    {
790      CategoryPlot categoryplot = (CategoryPlot) jfreechart_.getPlot();
791      if (categoryplot !=null)
792      {
793        // setup axis label
794        SubCategoryAxis subcategoryaxis
795             = new SubCategoryAxis(groupName+" / "+categoryName);
796
797        // set the margin between the Categories (% of space )
798        subcategoryaxis.setCategoryMargin(categoryMargin_);
799
800        // sub categorize the groups
801        for (int j=0; j<groups.size(); j++)
802        {
803          subcategoryaxis.addSubCategory((String)groups.get(j));
804          subcategoryaxis.setTickLabelFont((String)groups.get(j), itemLabelFont);
805        }
806
807        subcategoryaxis.setMaximumCategoryLabelWidthRatio(1/groups.size());
808        subcategoryaxis.setCategoryMargin(subCatMarginPercent);
809        subcategoryaxis.setMaximumCategoryLabelLines(3);
810
811        // Set the axis to the subcategorized axis already defined above
812        categoryplot.setDomainAxis(subcategoryaxis);
813
814      }
815    }
816  }
817
818
819  /**
820   *  Adds/or changes the subtitle.
821   *
822   * @param subtitleParm -  The new subtitle
823   */
824  public void addSubtitle(String subtitleParm )
825  {
826    if (subtitleParm !=null && !subtitleParm.equals(""))
827      {
828        org.jfree.chart.title.TextTitle textSubTitle = new org.jfree.chart.title.TextTitle(subtitleParm);
829        jfreechart_.addSubtitle(textSubTitle);
830      }
831  }
832
833
834  /**
835   *  Sets up the words in the grouped legend.
836   *
837   * @return    Description of the Return Value
838   */
839  private LegendItemCollection createLegendItems()
840  {
841    LegendItemCollection legenditemcollection = new LegendItemCollection();
842    LegendItem legenditem = null;
843    try
844    {
845      for (int j=0; j<stackedGroup.size(); j++)
846      {
847        legenditem = new LegendItem((String)stackedGroup.get(j), "-", null, null,
848            Plot.DEFAULT_LEGEND_ITEM_BOX,
849            chartColours[j]);
850        legenditemcollection.add(legenditem);
851      }
852    }
853    catch (Exception ex)
854    {
855       System.out.println(" Error Setting Legend Colours ");
856    }
857    return legenditemcollection;
858  }
859
860
861  /** saves the passed chart to a svg file in the current models yearly dir.**/
862  public void saveChartToSvg(JFreeChart chart, File svgFile)
863  {
864    if (svgFile != null)
865    try
866    {
867      // Get a DOMImplementation and create an XML document
868      org.w3c.dom.DOMImplementation domImpl =
869          org.apache.batik.dom.GenericDOMImplementation.getDOMImplementation();
870      org.w3c.dom.Document document = domImpl.createDocument(null, "svg", null);
871
872      // Create an instance of the SVG Generator
873      org.apache.batik.svggen.SVGGraphics2D svgGenerator =
874          new org.apache.batik.svggen.SVGGraphics2D(document);
875
876      // draw the chart in the SVG generator
877      chart.draw(svgGenerator, new java.awt.Rectangle(chartdX, chartdY));
878
879      // Write svg file
880      OutputStream outputStream = new FileOutputStream(svgFile);
881      Writer out = new OutputStreamWriter(outputStream, "UTF-8");
882      svgGenerator.stream(out, true /* use css */);
883      outputStream.flush();
884      outputStream.close();
885    }
886    catch (IOException ioEx)
887    {
888      System.out.println("Error Saving "+svgFile.toString());
889    }
890
891  }
892
893
894  /** saves the passed chart to a svg file in the current models yearly dir.**/
895  public void saveChartToSvg(File svgFile)
896  {
897    saveChartToSvg(jfreechart_, svgFile);
898  }
899
900
901  /**
902   * Saves the passed chart to a png file in the specified file.
903   * NOTE: This method spawns a seperate thread for this process.
904   *
905   * @param savedFile is the resulting file to save into
906   **/
907  public void saveChartToPng(File savedFile)
908  {
909    if (savedFile != null)
910      saveChartToPng( jfreechart_, savedFile);
911  }
912
913
914  /**
915   * Saves the passed chart to a png file in the specified file.
916   * NOTE: This method spawns a seperate thread for this process.
917   *
918   * @param chart is the chart to render to a file
919   * @param savedFile is the resulting file to save into
920   **/
921  public void saveChartToPng(JFreeChart chart, File savedFile)
922  {
923    if (savedFile != null)
924    {
925      /*
926      // Run in separate thread.
927      Thread fileSaveThread =
928        new Thread()
929        {
930          public void run()
931          {
932      */
933            try
934            {
935              ChartUtilities.saveChartAsPNG(savedFile, chart, chartdX, chartdY);
936            }
937            catch (IOException ioEx)
938            {
939              System.out.println("Error Saving "+savedFile.toString());
940            }
941      /*
942          }
943        };
944      fileSaveThread.start();
945      */
946    }
947  }
948
949  /**
950   *  Writes a chart to an output stream in PDF format.
951   *
952   * @param  out              the output stream.
953   * @param  chart            the chart.
954   * @param  width            the chart width.
955   * @param  height           the chart height.
956   * @param  title            duh
957   * @exception  IOException  Description of the Exception
958   */
959  public static void exportChartAsPDF(OutputStream out,
960                                        JFreeChart chart,
961                                        int width,int height, String title)
962  {
963    exportChartAsPDF(out, chart, width, height, new DefaultFontMapper(),title);
964  }
965
966
967  /**
968   *  Writes a chart to an output stream in PDF format.
969   *
970   * @param  out              the output stream.
971   * @param  chart            the chart.
972   * @param  width            the chart width.
973   * @param  height           the chart height.
974   * @exception  IOException  Description of the Exception
975   */
976  public static void exportChartAsPDF(OutputStream out,
977                                        JFreeChart chart, int width,int height)
978  {
979    exportChartAsPDF(out, chart, width, height, new DefaultFontMapper(),"");
980  }
981
982
983  /**
984   *  Writes a chart to an output stream in PDF format.
985   *
986   * @param  out              the output stream.
987   * @param  chart            the chart.
988   * @param  width            the chart width.
989   * @param  height           the chart height.
990   * @param  mapper           Description of the Parameter
991   * @exception  IOException  Description of the Exception
992   */
993  public static void exportChartAsPDF(OutputStream out,
994      JFreeChart chart, int width,
995      int height,
996      FontMapper mapper)
997  {
998    exportChartAsPDF(out, chart, width, height, mapper,"");
999  }
1000
1001
1002  /**
1003   *  Writes a chart to an output stream in PDF format.
1004   *
1005   *
1006
1007   * @param  out              the output stream.
1008   * @param  chart            the chart.
1009   * @param  width            the chart width.
1010   * @param  height           the chart height.
1011   * @param  mapper           Description of the Parameter
1012   * @param  title            is the title to assign to the pdf
1013   * @exception  IOException  Description of the Exception
1014   */
1015  public static void exportChartAsPDF(final OutputStream out,
1016      final JFreeChart chart, final int width, final int height,
1017      final FontMapper mapper,
1018      final String title)
1019  {
1020    if (out != null)
1021    {
1022      /*
1023      // Run in separate thread.
1024      Thread fileSaveThread =
1025        new Thread()
1026        {
1027          public void run()
1028          {
1029      */
1030            try
1031            {
1032              com.itextpdf.text.Rectangle pagesize = new com.itextpdf.text.Rectangle(width, height);
1033              Document document = new Document(pagesize, 50, 50, 50, 50);
1034              PdfWriter writer = PdfWriter.getInstance(document, out);
1035              document.open();
1036
1037              PdfContentByte cb = writer.getDirectContent();
1038              PdfTemplate tp = cb.createTemplate(width, height);
1039              Graphics2D g2 = tp.createGraphics(width, height, mapper);
1040              Rectangle2D r2D = new Rectangle2D.Double(0, 0, width, height);
1041              chart.draw(g2, r2D);
1042              g2.dispose();
1043              cb.addTemplate(tp, 0, 0);
1044              document.close();
1045            }
1046            catch (DocumentException de)
1047            {
1048              System.out.println("Error Saving PDF Stream"+out.toString());
1049              System.err.println(de.getMessage());
1050            }
1051            catch (Exception ioEx)
1052            {
1053              System.out.println("Error Saving PDF Stream"+out.toString());
1054              System.err.println(ioEx.getMessage());
1055            }
1056      /*
1057          } // run
1058        }; // Thread
1059      fileSaveThread.start();
1060      */
1061    }
1062  }
1063
1064
1065  /** saves the passed chart to a pdf file in the current models yearly dir.
1066   * NOTE: This method spawns a seperate thread for this process.
1067   *
1068   **/
1069  public void saveChartToPdf(File pdfFile)
1070  {
1071    saveChartToPdf(jfreechart_, pdfFile);
1072  }
1073
1074
1075  /** saves the passed chart to a pdf file in the current models yearly dir.
1076   * NOTE: This method spawns a seperate thread for this process.
1077   *
1078   **/
1079  public void saveChartToPdf(String fileName)
1080  {
1081    File pdfFile = new File(fileName);
1082    saveChartToPdf(jfreechart_, pdfFile);
1083  }
1084
1085
1086  /** saves the passed chart to a pdf file in the current models yearly dir.
1087   * NOTE: This method spawns a seperate thread for this process.
1088   *
1089   **/
1090  public void saveChartToPdf(JFreeChart chart, String fileName)
1091  {
1092    File pdfFile = new File(fileName);
1093    saveChartToPdf(chart, pdfFile);
1094  }
1095
1096
1097  /** saves the passed chart to a pdf file in the current models yearly dir.
1098   * NOTE: This method spawns a seperate thread for this process.
1099   *
1100   **/
1101  public void saveChartToPdf(JFreeChart chart, File pdfFile)
1102  {
1103    //File pdfFile = new File(fileName);
1104
1105    if (pdfFile != null)
1106    {
1107      try
1108      {
1109        OutputStream out = new BufferedOutputStream(new FileOutputStream(pdfFile));
1110        exportChartAsPDF(out, chart, chartdX, chartdY,  new DefaultFontMapper());
1111        out.close();
1112      }
1113      catch (FileNotFoundException fnfEx)
1114      {
1115        System.out.println("Error Saving PDF "+pdfFile.getName());
1116        System.err.println(fnfEx.getMessage());
1117      }
1118      catch (IOException ioEx)
1119      {
1120        System.out.println("Error Saving PDF "+pdfFile.getName());
1121        System.err.println(ioEx.getMessage());
1122      }
1123    }
1124  }
1125}
1126