Package ar.com.fdvs.dj.core.layout

Source Code of ar.com.fdvs.dj.core.layout.AbstractLayoutManager

/*
* Dynamic Jasper: A library for creating reports dynamically by specifying
* columns, groups, styles, etc. at runtime. It also saves a lot of development
* time in many cases! (http://sourceforge.net/projects/dynamicjasper)
*
* Copyright (C) 2008  FDV Solutions (http://www.fdvsolutions.com)
*
* This library 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 library 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 library; if not, write to the Free Software
*
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*
*
*/

package ar.com.fdvs.dj.core.layout;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import net.sf.jasperreports.charts.design.JRDesignBarPlot;
import net.sf.jasperreports.engine.JRBand;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRGroup;
import net.sf.jasperreports.engine.JRStyle;
import net.sf.jasperreports.engine.JRTextField;
import net.sf.jasperreports.engine.base.JRBaseChartPlot;
import net.sf.jasperreports.engine.base.JRBaseVariable;
import net.sf.jasperreports.engine.design.JRDesignBand;
import net.sf.jasperreports.engine.design.JRDesignChart;
import net.sf.jasperreports.engine.design.JRDesignElement;
import net.sf.jasperreports.engine.design.JRDesignExpression;
import net.sf.jasperreports.engine.design.JRDesignGroup;
import net.sf.jasperreports.engine.design.JRDesignImage;
import net.sf.jasperreports.engine.design.JRDesignRectangle;
import net.sf.jasperreports.engine.design.JRDesignStyle;
import net.sf.jasperreports.engine.design.JRDesignTextElement;
import net.sf.jasperreports.engine.design.JRDesignTextField;
import net.sf.jasperreports.engine.design.JRDesignVariable;
import net.sf.jasperreports.engine.design.JasperDesign;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import ar.com.fdvs.dj.domain.CustomExpression;
import ar.com.fdvs.dj.domain.DJChart;
import ar.com.fdvs.dj.domain.DJChartOptions;
import ar.com.fdvs.dj.domain.DynamicReport;
import ar.com.fdvs.dj.domain.DynamicReportOptions;
import ar.com.fdvs.dj.domain.Style;
import ar.com.fdvs.dj.domain.builders.DataSetFactory;
import ar.com.fdvs.dj.domain.entities.ColumnsGroup;
import ar.com.fdvs.dj.domain.entities.columns.AbstractColumn;
import ar.com.fdvs.dj.domain.entities.columns.BarCodeColumn;
import ar.com.fdvs.dj.domain.entities.columns.ImageColumn;
import ar.com.fdvs.dj.domain.entities.columns.PropertyColumn;
import ar.com.fdvs.dj.domain.entities.conditionalStyle.ConditionalStyle;
import ar.com.fdvs.dj.util.ExpressionUtils;

/**
* Abstract Class used as base for the different Layout Managers.</br>
* </br>
* A Layout Manager is always invoked after the entities registration stage.</br>
* A subclass should be created whenever we want to give the users the chance to </br>
* easily apply global layout changes to their reports. Example: Ignore groups </br>
* and styles for an Excel optimized report.
*/
public abstract class AbstractLayoutManager implements LayoutManager {

  static final Log log = LogFactory.getLog(AbstractLayoutManager.class);
  protected static final String EXPRESSION_TRUE_WHEN_ODD = "new java.lang.Boolean(((Number)$V{REPORT_COUNT}).doubleValue() % 2 == 0)";

  JasperDesign design;
  private DynamicReport report;

  protected abstract void transformDetailBandTextField(AbstractColumn column, JRDesignTextField textField);

  private HashMap reportStyles = new HashMap();

  public HashMap getReportStyles() {
    return reportStyles;
  }

  public void setReportStyles(HashMap reportStyles) {
    this.reportStyles = reportStyles;
  }

  public void applyLayout(JasperDesign design, DynamicReport report) throws LayoutException {
    log.debug("Applying Layout...");
    try {
      setDesign(design);
      setReport(report);
      ensureDJStyles();
      startLayout();
      transformDetailBand();
      setWhenNoDataBand();
      endLayout();
      registerRemainingStyles();
    } catch (RuntimeException e) {
      throw new LayoutException(e.getMessage(),e);
    }
  }


  /**
   * Creates the graphic element to be shown when the datasource is empty
   */
  protected void setWhenNoDataBand() {
    log.debug("setting up WHEN NO DATA band");
    String whenNoDataText = getReport().getWhenNoDataText();
    Style style = getReport().getWhenNoDataStyle();
    if (whenNoDataText == null || "".equals(whenNoDataText))
      return;
    JRDesignBand band = new JRDesignBand();
    getDesign().setNoData(band);
   
    JRDesignTextField text = new JRDesignTextField();
    JRDesignExpression expression = ExpressionUtils.createStringExpression("\""+whenNoDataText+"\"");
    text.setExpression(expression);
   
    if (style == null){
      style = getReport().getOptions().getDefaultDetailStyle();
    }
   
    if (getReport().isWhenNoDataShowTitle())
      copyBandElements(band, getDesign().getPageHeader());
    if (getReport().isWhenNoDataShowColumnHeader())
      copyBandElements(band, getDesign().getColumnHeader());
   
    int offset = findVerticalOffset(band);
    text.setY(offset);
    applyStyleToElement(style, text);
    text.setWidth(getReport().getOptions().getPrintableWidth());
    text.setHeight(50);
    band.addElement(text);
    log.debug("OK setting up WHEN NO DATA band");
   
  }

  /**
   * @param destBand
   * @param sourceBand
   */
  private void copyBandElements(JRDesignBand destBand, JRBand sourceBand) {
    int offset = findVerticalOffset(destBand);
    for (Iterator iterator = sourceBand.getChildren().iterator(); iterator.hasNext();) {
      JRDesignElement element = (JRDesignElement) iterator.next();
      JRDesignElement dest = null;
      try {
        dest = (JRDesignElement) element.getClass().newInstance();
        BeanUtils.copyProperties(dest, element);
        dest.setY(dest.getY() + offset);
      } catch (Exception e) {
        e.printStackTrace();
      }
      destBand.addElement((JRDesignElement) dest);
    }
  }

  protected void startLayout() {
    setColumnsFinalWidth();
  }

  protected void endLayout() {
    layoutCharts();
    setBandsFinalHeight();
  }

  protected void registerRemainingStyles() {
    //TODO: troll all elements in the JRDesing and for elements that has styles with null name
    //or not registered, register them in the design

  }

  /**
   * Sets a default style for every element that doesn't have one
   * @throws JRException
   */
  protected void ensureDJStyles()  {

    //first of all, register all parent styles if any
    for (Iterator iterator = getReport().getStyles().values().iterator(); iterator.hasNext();) {
      Style style = (Style) iterator.next();
      addStyleToDesign(style);
    }

    Style defaultDetailStyle = getReport().getOptions().getDefaultDetailStyle();

      Style defaultHeaderStyle = getReport().getOptions().getDefaultHeaderStyle();
  //    Style defaultFooterStyle = getReport().getOptions().getDefaultFooterStyle();
      for (Iterator iter = report.getColumns().iterator(); iter.hasNext();) {
        AbstractColumn column = (AbstractColumn) iter.next();
        if (column.getStyle() == null)
          column.setStyle(defaultDetailStyle);
        if (column.getHeaderStyle() == null)
          column.setHeaderStyle(defaultHeaderStyle);
  //      if (column.getFooterStyle() == null) column.setFooterStyle(defaulyFooterStyle);

//        addStyleToDesign(column.getStyle().transform());
//        addStyleToDesign(column.getHeaderStyle().transform());
      }

  }

  /**
   * @param baseStyle
   * @throws JRException
   */
  public void addStyleToDesign(Style style)  {
    JRDesignStyle jrstyle = style.transform();
    try {
      if (jrstyle.getName() == null) {
        String name = "dj_style_" + (getReportStyles().values().size() + 1);
        jrstyle.setName(name);
        style.setName(name);
        getReportStyles().put(name, jrstyle);
        design.addStyle(jrstyle);
      }

      JRStyle old = (JRStyle) design.getStylesMap().get(jrstyle.getName());
      if (old != null && style.isOverridesExistingStyle()){
        log.info("Overriding style with name \""+ style.getName() +"\"");

        design.removeStyle(style.getName());
        design.addStyle(jrstyle);
      } else if (old == null){
        log.info("Registering new style with name \""+ style.getName() +"\"");
        design.addStyle(jrstyle);
      } else {
        if (style.getName() != null)
          log.info("Using existing style for style with name \""+ style.getName() +"\"");
      }
    } catch (JRException e) {
      log.info("Duplicated style (it's ok): " + e.getMessage());
    }
  }

  /**
   * For each column, puts the elements in the detail band
   */
  protected void transformDetailBand() {
    log.debug("transforming Detail Band...");
    JRDesignBand detail = (JRDesignBand) design.getDetail();
    detail.setHeight(report.getOptions().getDetailHeight().intValue());

    if (getReport().getOptions().isPrintBackgroundOnOddRows()){
      decorateOddRows(detail);
    }

    for (Iterator iter = getVisibleColumns().iterator(); iter.hasNext();) {

      AbstractColumn column = (AbstractColumn)iter.next();

      /**
       * Barcode column
       */
      if (column instanceof BarCodeColumn) {
        BarCodeColumn barcodeColumn = (BarCodeColumn)column;
        JRDesignImage image = new JRDesignImage(new JRDesignStyle().getDefaultStyleProvider());
        JRDesignExpression imageExp = new JRDesignExpression();
//        imageExp.setText("ar.com.fdvs.dj.core.BarcodeHelper.getBarcodeImage("+barcodeColumn.getBarcodeType() + ", "+ column.getTextForExpression()+ ", "+ barcodeColumn.isShowText() + ", " + barcodeColumn.isCheckSum() + ", " + barcodeColumn.getApplicationIdentifier() + ","+ column.getWidth() +", "+ report.getOptions().getDetailHeight().intValue() + " )" );

        //Do not pass column height and width mecause barbecue
        //generates the image with wierd dimensions. Pass 0 in both cases
        String applicationIdentifier = barcodeColumn.getApplicationIdentifier();
        if (applicationIdentifier != null && !"".equals(applicationIdentifier.trim()) ){
          applicationIdentifier = "$F{" + applicationIdentifier + "}";
        } else {
          applicationIdentifier = "\"\"";
        }
        imageExp.setText("ar.com.fdvs.dj.core.BarcodeHelper.getBarcodeImage("+barcodeColumn.getBarcodeType() + ", "+ column.getTextForExpression()+ ", "+ barcodeColumn.isShowText() + ", " + barcodeColumn.isCheckSum() + ", " + applicationIdentifier + ",0,0 )" );


        imageExp.setValueClass(java.awt.Image.class);
        image.setExpression(imageExp);
        image.setHeight(getReport().getOptions().getDetailHeight().intValue());
        image.setWidth(column.getWidth().intValue());
        image.setX(column.getPosX().intValue());
        image.setScaleImage(barcodeColumn.getScaleMode().getValue());

        image.setOnErrorType(JRDesignImage.ON_ERROR_TYPE_ICON); //FIXME should we provide control of this to the user?

        applyStyleToElement(column.getStyle(), image);

        detail.addElement(image);
      }
      /**
       * Image columns
       */
      else if (column instanceof ImageColumn) {
        ImageColumn imageColumn = (ImageColumn)column;
        JRDesignImage image = new JRDesignImage(new JRDesignStyle().getDefaultStyleProvider());
        JRDesignExpression imageExp = new JRDesignExpression();
        imageExp.setText(column.getTextForExpression());

        imageExp.setValueClassName(imageColumn.getColumnProperty().getValueClassName());
        image.setExpression(imageExp);
        image.setHeight(getReport().getOptions().getDetailHeight().intValue());
        image.setWidth(column.getWidth().intValue());
        image.setX(column.getPosX().intValue());
        image.setScaleImage(imageColumn.getScaleMode().getValue());

        applyStyleToElement(column.getStyle(), image);

        detail.addElement(image);
      }
      /**
       * Regular Column
       */
      else {
        if (column.getConditionalStyles() != null && !column.getConditionalStyles().isEmpty() ){
          for (Iterator iterator = column.getConditionalStyles().iterator(); iterator.hasNext();) {
            ConditionalStyle condition = (ConditionalStyle) iterator.next();
            JRDesignTextField textField = generateTextFieldFromColumn(column, getReport().getOptions().getDetailHeight().intValue(), null);
            transformDetailBandTextField(column, textField);
            applyStyleToElement(condition.getStyle(), textField);
            textField.setPrintWhenExpression(getExpressionForConditionalStyle(condition.getName(), column.getTextForExpression()));
            detail.addElement(textField);
          }
        } else {
          JRDesignTextField textField = generateTextFieldFromColumn(column, getReport().getOptions().getDetailHeight().intValue(), null);
          transformDetailBandTextField(column, textField);
          if (textField.getExpression() != null)
            detail.addElement(textField);
        }
      }

        }
  }


  /**
   * Places a square as DetailBand background for odd rows.
   * @param JRDesignBand detail
   */
  private void decorateOddRows(JRDesignBand detail) {
    JRDesignExpression expression = new JRDesignExpression();
    expression.setValueClass(Boolean.class);
    expression.setText(EXPRESSION_TRUE_WHEN_ODD);

    JRDesignRectangle rectangle = new JRDesignRectangle();
    rectangle.setPrintWhenExpression(expression);
    DynamicReportOptions options = getReport().getOptions();
    rectangle.setHeight(options.getDetailHeight().intValue());
    rectangle.setWidth(options.getColumnWidth());
    rectangle.setStretchType(JRDesignRectangle.STRETCH_TYPE_RELATIVE_TO_TALLEST_OBJECT);
    Style oddRowBackgroundStyle = options.getOddRowBackgroundStyle();

    addStyleToDesign(oddRowBackgroundStyle);//register this style in the jasperDesign

    JRDesignStyle style = oddRowBackgroundStyle.transform();
    style.setForecolor(oddRowBackgroundStyle.getBackgroundColor());

    applyStyleToElement(oddRowBackgroundStyle, rectangle);
//    rectangle.setStyle(style);
    rectangle.setBackcolor(oddRowBackgroundStyle.getBackgroundColor());
    rectangle.setForecolor(oddRowBackgroundStyle.getBorderColor());
    rectangle.setPen(oddRowBackgroundStyle.getBorder().getValue());
    rectangle.setMode(JRDesignElement.MODE_OPAQUE);
    detail.addElement(rectangle);
  }

  /**
   * Creates and returns the expression used to apply a conditional style.
   * @param String paramName
   * @param String textForExpression
   * @return JRExpression
   */
  private JRExpression getExpressionForConditionalStyle(String paramName, String textForExpression) {
     String text = "(("+CustomExpression.class.getName()+")$P{"+paramName+"})."+CustomExpression.EVAL_METHOD_NAME+"("+textForExpression+")";
     JRDesignExpression expression = new JRDesignExpression();
     expression.setValueClass(Boolean.class);
     expression.setText(text);
     return expression;
  }

  protected final void generateHeaderBand(JRDesignBand band) {
    log.debug("Generating header band...");
    band.setHeight(report.getOptions().getHeaderHeight().intValue());

    for (Iterator iter = getVisibleColumns().iterator(); iter.hasNext();) {

      AbstractColumn col = (AbstractColumn) iter.next();
      if (col.getTitle() == null)
        continue;

      JRDesignExpression expression = new JRDesignExpression();
      JRDesignTextField textField = new JRDesignTextField();
      expression.setText("\""+ col.getTitle() + "\"");

      expression.setValueClass(String.class);

      textField.setKey("header_"+col.getTitle());
      textField.setExpression(expression);

      textField.setX(col.getPosX().intValue());
      textField.setY(col.getPosY().intValue());
      textField.setHeight(band.getHeight());
      textField.setWidth(col.getWidth().intValue());

      textField.setPrintWhenDetailOverflows(true);
      textField.setBlankWhenNull(true);

      Style headerStyle = col.getHeaderStyle();
      if (headerStyle == null)
        headerStyle = report.getOptions().getDefaultHeaderStyle();

      applyStyleToElement(headerStyle, textField);

      band.addElement(textField);
    }
  }

  public void applyStyleToElement(Style style, JRDesignElement designElemen) {
    if (style == null){
      log.warn("NULL style passed to object");
      return;
    }
    boolean existsInDesign = style.getName() != null
    && design.getStylesMap().get(style.getName()) != null;
//    && !style.isOverridesExistingStyle();

    JRStyle jrstyle = null;
    if (existsInDesign && !style.isOverridesExistingStyle()){
      jrstyle = (JRStyle) design.getStylesMap().get(style.getName());
    } else {
      addStyleToDesign(style); //Order maters. This line fist
      jrstyle = style.transform();
    }

    designElemen.setStyle(jrstyle);
    if (designElemen instanceof JRDesignTextElement ) {
      JRDesignTextElement textField = (JRDesignTextElement) designElemen;
      if (style.getStreching() != null)
        textField.setStretchType(style.getStreching().getValue());
      textField.setPositionType(JRTextField.POSITION_TYPE_FLOAT);

    }
    if (designElemen instanceof JRDesignTextField ) {
      JRDesignTextField textField = (JRDesignTextField) designElemen;
      textField.setStretchWithOverflow(style.isStretchWithOverflow());

      if (textField.isBlankWhenNull() == false && style.isBlankWhenNull())
        textField.setBlankWhenNull(true);
    }
  }


  /**
   * Sets the columns width by reading some report options like the
   * printableArea and useFullPageWidth.
   * columns with fixedWidth property set in TRUE will not be modified
   */
  private void setColumnsFinalWidth() {
    log.debug("Setting columns final width...");
    float factor = 1;
    int printableArea = report.getOptions().getColumnWidth();

    //Create a list with only the visible columns.
    List visibleColums = getVisibleColumns();

    log.debug("printableArea = " + printableArea );

    if (report.getOptions().isUseFullPageWidth()) {
      int columnsWidth = 0;
      int notRezisableWidth = 0;

      //Store in a variable the total with of all visible columns
      for (Iterator iterator =  visibleColums.iterator(); iterator.hasNext();) {
        AbstractColumn col = (AbstractColumn) iterator.next();
        columnsWidth += col.getWidth().intValue();
        if (col.getFixedWidth().booleanValue())
          notRezisableWidth += col.getWidth().intValue();
      }

      log.debug("columnsWidth = "+ columnsWidth);
      log.debug("notRezisableWidth = "+ notRezisableWidth);

      factor = (float) (printableArea-notRezisableWidth) / (float) (columnsWidth-notRezisableWidth);
      log.debug("factor = "+ factor);
      int acu = 0;
      int colFinalWidth = 0;

      //Select the non-resizable columns
      Collection resizableColumns = CollectionUtils.select( visibleColums,new Predicate() {
        public boolean evaluate(Object arg0) {
          return !((AbstractColumn)arg0).getFixedWidth().booleanValue();
        }

      }) ;

      //Finally, set the new width to the resizable columns
      for (Iterator iter = resizableColumns.iterator(); iter.hasNext();) {
        AbstractColumn col = (AbstractColumn) iter.next();

        if (!iter.hasNext()) {
          col.setWidth(new Integer(printableArea - notRezisableWidth - acu));
        } else {
          colFinalWidth = (new Float(col.getWidth().intValue() * factor)).intValue();
          acu += colFinalWidth;
          col.setWidth(new Integer(colFinalWidth));
        }
      }
    }

    // If the columns width changed, the X position must be setted again.
    int posx = 0;
    for (Iterator iterator =  visibleColums.iterator(); iterator.hasNext();) {
      AbstractColumn col = (AbstractColumn) iterator.next();
      col.setPosX(new Integer(posx));
      posx += col.getWidth().intValue();
    }
  }

  /**
   * @return
   */
  protected List getVisibleColumns() {
    List visibleColums = new ArrayList(report.getColumns());
    return visibleColums;
  }

  /**
   * Sets the necessary height for all bands in the report, to hold their children
   */
  protected void setBandsFinalHeight() {
    log.debug("Setting bands final height...");

    setBandFinalHeight((JRDesignBand) design.getPageHeader());
    setBandFinalHeight((JRDesignBand) design.getPageFooter());
    setBandFinalHeight((JRDesignBand) design.getColumnHeader());
    setBandFinalHeight((JRDesignBand) design.getColumnFooter());
    setBandFinalHeight((JRDesignBand) design.getSummary());
    setBandFinalHeight((JRDesignBand) design.getBackground());
    setBandFinalHeight((JRDesignBand) design.getDetail());
    setBandFinalHeight((JRDesignBand) design.getLastPageFooter());
    setBandFinalHeight((JRDesignBand) design.getTitle());
    setBandFinalHeight((JRDesignBand) design.getPageFooter());
    setBandFinalHeight((JRDesignBand) design.getNoData());

    for (Iterator iter = design.getGroupsList().iterator(); iter.hasNext();) {
      JRGroup jrgroup = (JRGroup) iter.next();
      setBandFinalHeight((JRDesignBand) jrgroup.getGroupHeader());
      setBandFinalHeight((JRDesignBand) jrgroup.getGroupFooter());
    }
  }

  /**
   * Sets the band's height to hold all its children
   * @param band Band to be resized
   */
  protected void setBandFinalHeight(JRDesignBand band) {
    if (band != null) {
      int finalHeight = findVerticalOffset(band);
      band.setHeight(finalHeight);
    }
  }

  /**
   * Finds "Y" corrdinate value in with more elements could be added in the band
   * @param band
   * @return
   */
  protected int findVerticalOffset(JRDesignBand band) {
    int finalHeight = 0;
    if (band != null) {
      for (Iterator iter = band.getChildren().iterator(); iter.hasNext();) {
        JRDesignElement element = (JRDesignElement) iter.next();
        int currentHeight = element.getY() + element.getHeight();
        if (currentHeight > finalHeight) finalHeight = currentHeight;
      }
      return finalHeight;
    }
    return finalHeight;
  }

  /**
   * Creates a JasperReport DesignTextField from a DynamicJasper AbstractColumn.
   * @param AbstractColumn col
   * @param int height
   * @param ColumnsGroup group
   * @return JRDesignTextField
   */
  protected final JRDesignTextField generateTextFieldFromColumn(AbstractColumn col, int height, ColumnsGroup group) {
    JRDesignTextField textField = new JRDesignTextField();
    JRDesignExpression exp = new JRDesignExpression();

    if (col.getPattern() != null && "".equals(col.getPattern().trim())) {
      textField.setPattern(col.getPattern());
        }

    exp.setText(col.getTextForExpression());
    exp.setValueClassName(col.getValueClassNameForExpression());
    textField.setExpression(exp);
    textField.setWidth(col.getWidth().intValue());
    textField.setX(col.getPosX().intValue());
    textField.setY(col.getPosY().intValue());
    textField.setHeight(height);

    textField.setBlankWhenNull(col.getBlankWhenNull());

    textField.setPattern(col.getPattern());

    textField.setPrintRepeatedValues(col.getPrintRepeatedValues().booleanValue());

        textField.setPrintWhenDetailOverflows(true);

        Style columnStyle = col.getStyle();
        if (columnStyle == null)
          columnStyle = report.getOptions().getDefaultDetailStyle();

    applyStyleToElement(columnStyle, textField);

        if (group != null) {
          int index = getReport().getColumnsGroups().indexOf(group);
            JRDesignGroup previousGroup = (JRDesignGroup) getDesign().getGroupsList().get(index);
            textField.setPrintWhenGroupChanges(previousGroup);
        }
        return textField;
  }

  /*
   * Takes all the report's charts and inserts them in their corresponding bands
   */
  protected void layoutCharts() {
    for (Iterator iter = getReport().getCharts().iterator(); iter.hasNext();) {
      DJChart djChart = (DJChart) iter.next();
      JRDesignChart chart = createChart(djChart);
      JRDesignBand band = getPositionBand(djChart);
     
      //If it is a HEADER chart, then Y remains 0. If FOOTER chart, need yOffset
      int yOffset = 0;
      if (djChart.getOptions().getPosition() == DJChartOptions.POSITION_FOOTER)
        yOffset = findVerticalOffset(band);     
      chart.setY(yOffset); //The chart will be located at the very end of the band so far
     
      band.addElement(chart);
    }
  }

  /**
   * Calculates and returns the band where the band is to be inserted
   * @param djChart
   * @return
   */
  private JRDesignBand getPositionBand(DJChart djChart) {
    JRDesignGroup jgroup = getGroupFromColumnsGroup(djChart.getColumnsGroup());
    JRDesignGroup parentGroup = getParent(jgroup);

    JRDesignBand band = null;
    switch (djChart.getOptions().getPosition()) {
    case DJChartOptions.POSITION_HEADER:
      band = (JRDesignBand) ((parentGroup.equals(jgroup)) ? getDesign().getSummary(): parentGroup.getGroupHeader());
      break;
    case DJChartOptions.POSITION_FOOTER:
      band = (JRDesignBand) ((parentGroup.equals(jgroup)) ? getDesign().getSummary(): parentGroup.getGroupFooter());
    }
    return band;
  }

  private JRDesignChart createChart(DJChart djChart){

      JRDesignGroup jrGroup = getGroupFromColumnsGroup(djChart.getColumnsGroup());

      JRDesignChart chart = new JRDesignChart(new JRDesignStyle().getDefaultStyleProvider(), djChart.getType());
      chart.setDataset(DataSetFactory.getDataset(djChart.getType(), jrGroup, getParent(jrGroup), registerChartVariable(djChart)));
      interpeterOptions(djChart, chart);
     
      chart.setEvaluationTime(JRExpression.EVALUATION_TIME_GROUP);
      chart.setEvaluationGroup(jrGroup);
      return chart;
  }

  private void interpeterOptions(DJChart djChart, JRDesignChart chart) {
    DJChartOptions options = djChart.getOptions();

    //size
    if (options.isCentered()) chart.setWidth(getReport().getOptions().getPrintableWidth());
    else chart.setWidth(options.getWidth());
    chart.setHeight(options.getHeight());

    //position
    chart.setX(options.getX());
    chart.setPadding(10);
    chart.setY(options.getY());
    arrangeBand(djChart, chart);

    //options
    chart.setShowLegend(options.isShowLegend());
    chart.setBackcolor(options.getBackColor());
    chart.setBorder(options.getBorder());

    //colors
    if (options.getColors() != null){
      int i = 1;
      for (Iterator iter = options.getColors().iterator(); iter.hasNext();i++) {
        Color color = (Color) iter.next();
        chart.getPlot().getSeriesColors().add(new JRBaseChartPlot.JRBaseSeriesColor(i, color));
      }
    }
    //Chart-dependant options
    if (djChart.getType() == DJChart.BAR_CHART) ((JRDesignBarPlot) chart.getPlot()).setShowTickLabels(options.isShowLabels());
  }

  //TODO: 5's must be replaced by a constant or a configurable number
  private void arrangeBand(DJChart djChart, JRDesignChart chart) {
    int index = getReport().getColumnsGroups().indexOf(djChart.getColumnsGroup());

    if (djChart.getOptions().getPosition() == DJChartOptions.POSITION_HEADER){
      JRDesignBand band = (JRDesignBand) getParent(((JRDesignGroup)getDesign().getGroupsList().get(index))).getGroupHeader();

      for (int i = 0; i < band.getElements().length; i++) {
        JRDesignElement element = (JRDesignElement) band.getElements()[i];
        element.setY(element.getY() + chart.getY() + chart.getHeight() + 5);
      }     
    }
    else {
      JRDesignBand band = (JRDesignBand) getParent(((JRDesignGroup)getDesign().getGroupsList().get(index))).getGroupFooter();
      int max = 0;
      for (int i = 0; i < band.getElements().length; i++) {
        JRDesignElement element = (JRDesignElement) band.getElements()[i];
        if ( (element.getHeight() + element.getY()) > max)
          max = element.getHeight() + element.getY();
      }
      chart.setY(max +5 );
    }
  }

  /**
   * Creates and registers a variable to be used by the Chart
   * @param chart Chart that needs a variable to be generated
   * @return the generated variable
   */
  private JRDesignVariable registerChartVariable(DJChart chart) {

    JRDesignGroup group = getGroupFromColumnsGroup(chart.getColumnsGroup());

    Class clazz = null;
    try { clazz = Class.forName(chart.getColumn().getValueClassNameForExpression());
    } catch (ClassNotFoundException e) { /*Should never happen*/ }

    JRDesignExpression expression = new JRDesignExpression();
//    expression.setText("$F{" + chart.getColumn().getTitle().toLowerCase() + "}");
    expression.setText("$F{" + ((PropertyColumn) chart.getColumn()).getColumnProperty().getProperty()  + "}");
    expression.setValueClass(clazz);

    JRDesignVariable var = new JRDesignVariable();
    var.setValueClass(clazz);
    var.setExpression(expression);
    var.setCalculation(chart.getOperation());
    var.setResetGroup(group);
    var.setResetType(JRBaseVariable.RESET_TYPE_GROUP);
    int chartIndex = getReport().getCharts().indexOf(chart); //use the index as part of the name just because I may want 2
    //different types of chart from the very same column (with the same operation also) making the variables name to be duplicated
    var.setName("CHART_[" + chartIndex + "+]_" + group.getName() + "_" + chart.getColumn().getTitle() + "_" + chart.getOperation());
//    JRDesignExpression initExp = new JRDesignExpression();
//    initExp.setText("new Float(0)");
//    initExp.setValueClass(clazz);
//    var.setInitialValueExpression(initExp);

    try {
      getDesign().addVariable(var);
    } catch (JRException e) {
      throw new LayoutException(e.getMessage());
    }
    return var;
  }





  //TODO: Maybe a helper could calculate the following
  /**
   * Finds the parent group of the given one and returns it
   * @param group Group for which the parent is needed
   * @return The parent group of the given one. If the given one is the first one, it returns the same group
   */
  private JRDesignGroup getParent(JRDesignGroup group){
    int index = getDesign().getGroupsList().indexOf(group);
    JRDesignGroup parentGroup = (index > 0) ? (JRDesignGroup) getDesign().getGroupsList().get(index-1): group;
    return parentGroup;
  }

  protected JRDesignGroup getGroupFromColumnsGroup(ColumnsGroup group){
    int index = getReport().getColumnsGroups().indexOf(group);
    return (JRDesignGroup) getDesign().getGroupsList().get(index);
  }

  //TODO: Maybe a helper could calculate this (up to here)





  protected JasperDesign getDesign() {
    return design;
  }

  protected void setDesign(JasperDesign design) {
    this.design = design;
  }

  protected DynamicReport getReport() {
    return report;
  }

  protected void setReport(DynamicReport report) {
    this.report = report;
  }

}
TOP

Related Classes of ar.com.fdvs.dj.core.layout.AbstractLayoutManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.