View Javadoc

1   /*
2    * DynamicJasper: A library for creating reports dynamically by specifying
3    * columns, groups, styles, etc. at runtime. It also saves a lot of development
4    * time in many cases! (http://sourceforge.net/projects/dynamicjasper)
5    *
6    * Copyright (C) 2008  FDV Solutions (http://www.fdvsolutions.com)
7    *
8    * This library is free software; you can redistribute it and/or
9    * modify it under the terms of the GNU Lesser General Public
10   *
11   * License as published by the Free Software Foundation; either
12   *
13   * version 2.1 of the License, or (at your option) any later version.
14   *
15   * This library is distributed in the hope that it will be useful,
16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
17   *
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19   *
20   * Lesser General Public License for more details.
21   *
22   * You should have received a copy of the GNU Lesser General Public
23   * License along with this library; if not, write to the Free Software
24   *
25   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26   *
27   *
28   */
29  
30  package ar.com.fdvs.dj.core.layout;
31  
32  import ar.com.fdvs.dj.core.DJException;
33  import ar.com.fdvs.dj.domain.*;
34  import ar.com.fdvs.dj.domain.builders.DataSetFactory;
35  import ar.com.fdvs.dj.domain.constants.Transparency;
36  import ar.com.fdvs.dj.domain.entities.DJColSpan;
37  import ar.com.fdvs.dj.domain.entities.DJGroup;
38  import ar.com.fdvs.dj.domain.entities.columns.*;
39  import ar.com.fdvs.dj.domain.entities.conditionalStyle.ConditionalStyle;
40  import ar.com.fdvs.dj.util.ExpressionUtils;
41  import ar.com.fdvs.dj.util.HyperLinkUtil;
42  import ar.com.fdvs.dj.util.LayoutUtils;
43  import ar.com.fdvs.dj.util.Utils;
44  import net.sf.jasperreports.charts.design.JRDesignBarPlot;
45  import net.sf.jasperreports.engine.*;
46  import net.sf.jasperreports.engine.base.JRBaseChartPlot;
47  import net.sf.jasperreports.engine.base.JRBaseVariable;
48  import net.sf.jasperreports.engine.design.*;
49  import net.sf.jasperreports.engine.type.*;
50  import net.sf.jasperreports.engine.util.JRExpressionUtil;
51  import org.apache.commons.collections.CollectionUtils;
52  import org.apache.commons.collections.MultiHashMap;
53  import org.apache.commons.collections.MultiMap;
54  import org.apache.commons.collections.Predicate;
55  import org.apache.commons.logging.Log;
56  import org.apache.commons.logging.LogFactory;
57  
58  import java.awt.*;
59  import java.util.*;
60  import java.util.List;
61  
62  /**
63   * Abstract Class used as base for the different Layout Managers.</br>
64   * </br>
65   * A Layout Manager is always invoked after the entities registration stage.</br>
66   * A subclass should be created whenever we want to give the users the chance to </br>
67   * easily apply global layout changes to their reports. Example: Ignore groups </br>
68   * and styles for an Excel optimized report.
69   */
70  public abstract class AbstractLayoutManager implements LayoutManager {
71  
72  	static final Log log = LogFactory.getLog(AbstractLayoutManager.class);
73  	protected static final String EXPRESSION_TRUE_WHEN_ODD = "new java.lang.Boolean(((Number)$V{REPORT_COUNT}).doubleValue() % 2 == 0)";
74  	protected static final String EXPRESSION_TRUE_WHEN_EVEN = "new java.lang.Boolean(((Number)$V{REPORT_COUNT}).doubleValue() % 2 != 0)";
75  
76  	JasperDesign design;
77  	private DynamicReport report;
78  
79  	protected abstract void transformDetailBandTextField(AbstractColumn column, JRDesignTextField textField);
80  
81  	private HashMap reportStyles = new HashMap();
82  
83  	/**
84  	 * Holds the original groups binded to a column.
85  	 * Needed for later reference
86  	 * List<JRDesignGroup>
87  	 */
88  	protected List realGroups = new ArrayList();
89  
90  	public HashMap getReportStyles() {
91  		return reportStyles;
92  	}
93  
94  	public void setReportStyles(HashMap reportStyles) {
95  		this.reportStyles = reportStyles;
96  	}
97  
98  	public void applyLayout(JasperDesign design, DynamicReport report) throws LayoutException {
99  		log.debug("Applying Layout...");
100 		try {
101 			setDesign(design);
102 			setReport(report);
103 			ensureDJStyles();
104 			startLayout();
105 			transformDetailBand();
106 			endLayout();
107 			setWhenNoDataBand();
108 			setBandsFinalHeight();
109 			registerRemainingStyles();
110 		} catch (RuntimeException e) {
111 			throw new LayoutException(e.getMessage(),e);
112 		}
113 	}
114 
115 
116 	/**
117 	 * Creates the graphic element to be shown when the datasource is empty
118 	 */
119 	protected void setWhenNoDataBand() {
120 		log.debug("setting up WHEN NO DATA band");
121 		String whenNoDataText = getReport().getWhenNoDataText();
122 		Style style = getReport().getWhenNoDataStyle();
123 		if (whenNoDataText == null || "".equals(whenNoDataText))
124 			return;
125 		JRDesignBand band = new JRDesignBand();
126 		getDesign().setNoData(band);
127 		
128 		JRDesignTextField text = new JRDesignTextField();
129 		JRDesignExpression expression = ExpressionUtils.createStringExpression("\""+whenNoDataText+"\"");
130 		text.setExpression(expression);
131 
132 		if (style == null){
133 			style = getReport().getOptions().getDefaultDetailStyle();
134 		}
135 
136 		if (getReport().isWhenNoDataShowTitle()){
137 			LayoutUtils.copyBandElements(band, getDesign().getTitle());
138 			LayoutUtils.copyBandElements(band, getDesign().getPageHeader());
139 		}
140 		if (getReport().isWhenNoDataShowColumnHeader())
141 			LayoutUtils.copyBandElements(band, getDesign().getColumnHeader());
142 
143 		int offset = LayoutUtils.findVerticalOffset(band);
144 		text.setY(offset);
145 		applyStyleToElement(style, text);
146 		text.setWidth(getReport().getOptions().getPrintableWidth());
147 		text.setHeight(50);
148 		band.addElement(text);
149 		log.debug("OK setting up WHEN NO DATA band");
150 
151 	}
152 
153 	protected void startLayout() {
154 		setColumnsFinalWidth();
155 		realGroups.addAll(getDesign().getGroupsList()); //Hold the original groups
156 	}
157 
158 	protected void endLayout() {
159 		layoutCharts();
160 		setBandsFinalHeight();
161 	}
162 
163 	protected void registerRemainingStyles() {
164 		//TODO: troll all elements in the JRDesing and for elements that has styles with null name
165 		//or not registered, register them in the design
166 	}
167 
168 	/**
169 	 * Sets a default style for every element that doesn't have one
170 	 * @throws JRException
171 	 */
172 	protected void ensureDJStyles()  {
173 		//first of all, register all parent styles if any
174 		for (Iterator iterator = getReport().getStyles().values().iterator(); iterator.hasNext();) {
175 			Style style = (Style) iterator.next();
176 			addStyleToDesign(style);
177 		}
178 
179 		Style defaultDetailStyle = getReport().getOptions().getDefaultDetailStyle();
180 
181 			Style defaultHeaderStyle = getReport().getOptions().getDefaultHeaderStyle();
182 			for (Iterator iter = report.getColumns().iterator(); iter.hasNext();) {
183 				AbstractColumn column = (AbstractColumn) iter.next();
184 				if (column.getStyle() == null)
185 					column.setStyle(defaultDetailStyle);
186 				if (column.getHeaderStyle() == null)
187 					column.setHeaderStyle(defaultHeaderStyle);
188 			}
189 	}
190 
191 	/**
192 	 * @param style
193 	 * @throws JRException
194 	 */
195 	public void addStyleToDesign(Style style)  {
196 		JRDesignStyle jrstyle = style.transform();
197 		try {
198 			if (jrstyle.getName() == null) {
199 				String name = createUniqueStyleName();
200 				jrstyle.setName(name);
201 				style.setName(name);
202 				getReportStyles().put(name, jrstyle);
203 				design.addStyle(jrstyle);
204 			}
205 
206 			JRStyle old = (JRStyle) design.getStylesMap().get(jrstyle.getName());
207 			if (old != null && style.isOverridesExistingStyle()){
208 				log.debug("Overriding style with name \""+ style.getName() +"\"");
209 
210 				design.removeStyle(style.getName());
211 				design.addStyle(jrstyle);
212 			} else if (old == null){
213 				log.debug("Registering new style with name \""+ style.getName() +"\"");
214 				design.addStyle(jrstyle);
215 			} else {
216 				if (style.getName() != null)
217 					log.debug("Using existing style for style with name \""+ style.getName() +"\"");
218 			}
219 		} catch (JRException e) {
220 			log.debug("Duplicated style (it's ok): " + e.getMessage());
221 		}
222 	}
223 
224 	protected String createUniqueStyleName() {
225 		synchronized (this) {
226 			int counter = getReportStyles().values().size() + 1;
227 			String tryName = "dj_style_" + counter + "_"; //FIX for issue 3002761 @SF tracker
228 			while (design.getStylesMap().get(tryName) != null){
229 				counter++;
230 				tryName = "dj_style_" + counter;
231 			}
232 			return tryName;
233 		}
234 	}
235 
236 	/**
237 	 * For each column, puts the elements in the detail band
238 	 */
239 	protected void transformDetailBand() {
240 		log.debug("transforming Detail Band...");
241 
242         JRDesignSection detailSection = (JRDesignSection) design.getDetailSection();
243 
244         //TODO: With this new way, we can use template content as it comes, and add a new band for DJ on top or bellow it.
245         JRDesignBand detail = null;
246         if (detailSection.getBandsList().isEmpty()){
247             detail = new JRDesignBand();
248             detailSection.getBandsList().add(detail);
249         } else {
250             detail = (JRDesignBand) detailSection.getBandsList().iterator().next();
251         }
252 
253 		detail.setHeight(report.getOptions().getDetailHeight().intValue());
254 
255 		for (Iterator iter = getVisibleColumns().iterator(); iter.hasNext();) {
256 
257 			AbstractColumn column = (AbstractColumn)iter.next();
258 
259 			/**
260 			 * Barcode column
261 			 */
262 			if (column instanceof BarCodeColumn) {
263 				BarCodeColumn barcodeColumn = (BarCodeColumn)column;
264 				JRDesignImage image = new JRDesignImage(new JRDesignStyle().getDefaultStyleProvider());
265 				JRDesignExpression imageExp = new JRDesignExpression();
266 //				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() + " )" );
267 
268 				//Do not pass column height and width mecause barbecue
269 				//generates the image with wierd dimensions. Pass 0 in both cases
270 				String applicationIdentifier = barcodeColumn.getApplicationIdentifier();
271 				if (applicationIdentifier != null && !"".equals(applicationIdentifier.trim()) ){
272 					applicationIdentifier = "$F{" + applicationIdentifier + "}";
273 				} else {
274 					applicationIdentifier = "\"\"";
275 				}
276 				imageExp.setText("ar.com.fdvs.dj.core.BarcodeHelper.getBarcodeImage("+barcodeColumn.getBarcodeType() + ", "+ column.getTextForExpression()+ ", "+ barcodeColumn.isShowText() + ", " + barcodeColumn.isCheckSum() + ", " + applicationIdentifier + ",0,0 )" );
277 
278 
279 				imageExp.setValueClass(java.awt.Image.class);
280 				image.setExpression(imageExp);
281 				image.setHeight(getReport().getOptions().getDetailHeight().intValue());
282 				image.setWidth(column.getWidth().intValue());
283 				image.setX(column.getPosX().intValue());
284 				image.setScaleImage(ScaleImageEnum.getByValue(barcodeColumn.getScaleMode().getValue()));
285 
286 				image.setOnErrorType(OnErrorTypeEnum.ICON ); //FIXME should we provide control of this to the user?
287 
288 				if (column.getLink() != null) {
289 					String name = "column_" + getReport().getColumns().indexOf(column);
290 					HyperLinkUtil.applyHyperLinkToElement((DynamicJasperDesign) getDesign(), column.getLink(),image,name);
291 				}
292 
293 				applyStyleToElement(column.getStyle(), image);
294 
295 				detail.addElement(image);
296 			}
297 			/**
298 			 * Image columns
299 			 */
300 			else if (column instanceof ImageColumn) {
301 				ImageColumn imageColumn = (ImageColumn)column;
302 				JRDesignImage image = new JRDesignImage(new JRDesignStyle().getDefaultStyleProvider());
303 				JRDesignExpression imageExp = new JRDesignExpression();
304 				imageExp.setText(column.getTextForExpression());
305 
306 				imageExp.setValueClassName(imageColumn.getValueClassNameForExpression());
307 				image.setExpression(imageExp);
308 				image.setHeight(getReport().getOptions().getDetailHeight().intValue());
309 				image.setWidth(column.getWidth().intValue());
310 				image.setX(column.getPosX().intValue());
311 				image.setScaleImage(ScaleImageEnum.getByValue(imageColumn.getScaleMode().getValue()));
312 
313 				applyStyleToElement(column.getStyle(), image);
314 
315 				if (column.getLink() != null) {
316 					String name = "column_" + getReport().getColumns().indexOf(column);
317 					HyperLinkUtil.applyHyperLinkToElement((DynamicJasperDesign) getDesign(),column.getLink(), image,name);
318 				}
319 
320 				detail.addElement(image);
321 			}
322 			/**
323 			 * Regular Column
324 			 */
325 			else {
326 				if (getReport().getOptions().isShowDetailBand()){
327 					JRDesignTextField textField = generateTextFieldFromColumn(column, getReport().getOptions().getDetailHeight().intValue(), null);
328 
329 					if (column.getLink() != null) {
330 						String name = getDesign().getName() + "_column_" + getReport().getColumns().indexOf(column);
331 						HyperLinkUtil.applyHyperLinkToElement((DynamicJasperDesign) getDesign(),column.getLink(),textField,name);
332 					}
333 
334 					transformDetailBandTextField(column, textField);
335 
336 					if (textField.getExpression() != null)
337 						detail.addElement(textField);
338 				}
339 
340 			}
341 
342         }
343 	}
344 
345 
346 //	/**
347 //	 * Creates and returns the expression used to apply a conditional style.
348 //	 * @param String paramName
349 //	 * @param String textForExpression
350 //	 * @return JRExpression
351 //	 */
352 	/*
353 	 * MOVED INSIDE ExpressionUtils
354 	protected JRDesignExpression getExpressionForConditionalStyle(ConditionalStyle condition, AbstractColumn column) {
355 		//String text = "(("+CustomExpression.class.getName()+")$P{"+paramName+"})."+CustomExpression.EVAL_METHOD_NAME+"("+textForExpression+")";
356 		String columExpression = column.getTextForExpression();
357 		//condition.getCondition().setFieldToEvaluate(exprParams)
358 
359 		// PeS17 patch, 2008-11-29: put all fields to fields map, including "invisible" i.e. only registered ones
360 
361 		String fieldsMap = "(("+DJDefaultScriptlet.class.getName() + ")$P{REPORT_SCRIPTLET}).getCurrentFiels()";
362 		String parametersMap = "(("+DJDefaultScriptlet.class.getName() + ")$P{REPORT_SCRIPTLET}).getCurrentParams()";
363 		String variablesMap = "(("+DJDefaultScriptlet.class.getName() + ")$P{REPORT_SCRIPTLET}).getCurrentVariables()";
364 
365 		String evalMethodParams =  fieldsMap +", " + variablesMap + ", " + parametersMap + ", " + columExpression;
366 
367 		String text = "(("+ConditionStyleExpression.class.getName()+")$P{"+condition.getName()+"})."+CustomExpression.EVAL_METHOD_NAME+"("+evalMethodParams+")";
368 		JRDesignExpression expression = new JRDesignExpression();
369 		expression.setValueClass(Boolean.class);
370 		expression.setText(text);
371 		return expression;
372 	}
373 	 */
374 
375 	protected void generateHeaderBand(JRDesignBand band) {
376         log.debug("Adding column names in header band.");
377         band.setHeight(report.getOptions().getHeaderHeight());
378 
379         for (AbstractColumn col : getVisibleColumns()) {
380 
381             if (col.getTitle() == null)
382                 continue;
383 
384             Style headerStyle = col.getHeaderStyle();
385             if (headerStyle == null)
386                 headerStyle = report.getOptions().getDefaultHeaderStyle();
387 
388             this.generateColspanHeader(col,band);
389 
390             JRDesignExpression expression   = new JRDesignExpression();
391             JRDesignTextField textField     = new JRDesignTextField();
392             expression.setText("\"" + col.getTitle() + "\"");
393 
394             //sets header markup (if any)
395             if (col.getHeaderMarkup() != null)
396                 textField.setMarkup(col.getHeaderMarkup().toLowerCase());
397 
398             expression.setValueClass(String.class);
399 
400             textField.setKey("header_" + col.getTitle());
401             textField.setExpression(expression);
402 
403             if (col.hasParentCol()) {
404                 textField.setY(col.getPosY() + band.getHeight() / 2);
405                 textField.setHeight(band.getHeight() / 2);
406 
407             } else {
408                 textField.setY(col.getPosY());
409                 textField.setHeight(band.getHeight());
410             }
411 
412             textField.setX(col.getPosX().intValue());
413             textField.setWidth(col.getWidth().intValue());
414 
415             textField.setPrintWhenDetailOverflows(true);
416             textField.setBlankWhenNull(true);
417 
418             applyStyleToElement(headerStyle, textField);
419             band.addElement(textField);
420         }
421 	}
422 
423     private void generateColspanHeader(AbstractColumn col,JRDesignBand band) {
424 
425         DJColSpan colSpan = col.getColSpan();
426         if (colSpan != null && colSpan.isFirstColum(col)) {
427             //Set colspan
428             JRDesignTextField spanTitle             = new JRDesignTextField();
429             JRDesignExpression colspanExpression    = new JRDesignExpression();
430             colspanExpression.setValueClassName(String.class.getName());
431             colspanExpression.setText("\"" + col.getColSpan().getTitle() + "\"");
432 
433             spanTitle.setExpression(colspanExpression);
434             spanTitle.setKey("colspan-header" + col.getTitle());
435 
436             spanTitle.setX(col.getPosX().intValue());
437             spanTitle.setY(col.getPosY());
438             spanTitle.setHeight(band.getHeight() / 2);
439             spanTitle.setWidth(colSpan.getWidth());
440 
441             Style spanStyle = colSpan.getColspanHeaderStyle();
442 
443             if (spanStyle == null) {
444                 spanStyle = report.getOptions().getDefaultHeaderStyle();
445             }
446 
447             applyStyleToElement(spanStyle, spanTitle);
448             band.addElement(spanTitle);
449         }
450     }
451 
452     /**
453 	 * Given a dj-Style, it is applied to the jasper element.
454 	 * If the style is being used by the first time, it is registered in the jasper-design,
455 	 * if it is the second time, the one created before is used  (cached one)
456 	 *
457 	 *
458 	 * @param style
459 	 * @param designElemen
460 	 */
461 	public void applyStyleToElement(Style style, JRDesignElement designElemen) {
462 		if (style == null){
463 //			log.warn("NULL style passed to object");
464 			JRDesignStyle style_ = new JRDesignStyle();
465 			style_.setName( createUniqueStyleName());
466 			designElemen.setStyle(style_);
467 			try {
468 				getDesign().addStyle(style_);
469 			} catch (JRException e) {
470 				//duplicated style, its ok
471 			}
472 //			return null;
473 			return;
474 		}
475 		boolean existsInDesign = style.getName() != null
476 								&& design.getStylesMap().get(style.getName()) != null;
477 						//		&& !style.isOverridesExistingStyle();
478 
479 		JRDesignStyle jrstyle = null;
480 		//Let's allways add a new JR style
481   		if (existsInDesign && !style.isOverridesExistingStyle()){
482 			jrstyle = (JRDesignStyle) design.getStylesMap().get(style.getName());
483 		} else {
484 			addStyleToDesign(style); //Order maters. This line fist
485 			jrstyle = style.transform();
486 		}
487 
488 		designElemen.setStyle(jrstyle);
489 		if (designElemen instanceof JRDesignTextElement ) {
490 			JRDesignTextElement textField = (JRDesignTextElement) designElemen;
491 			if (style.getStreching() != null)
492 				textField.setStretchType(StretchTypeEnum.getByValue( style.getStreching().getValue() ));
493 			textField.setPositionType(PositionTypeEnum.FLOAT);
494 
495 		}
496 		if (designElemen instanceof JRDesignTextField ) {
497 			JRDesignTextField textField = (JRDesignTextField) designElemen;
498 			textField.setStretchWithOverflow(style.isStretchWithOverflow());
499 
500 			if (!textField.isBlankWhenNull() && style.isBlankWhenNull()) //TODO Re check if this condition is ok
501 				textField.setBlankWhenNull(true);
502 		}
503 		
504 		if (designElemen instanceof JRDesignGraphicElement) {
505 			JRDesignGraphicElement graphicElement = (JRDesignGraphicElement) designElemen;
506 			graphicElement.setStretchType(StretchTypeEnum.getByValue(style.getStreching().getValue()));
507 			graphicElement.setPositionType(PositionTypeEnum.FLOAT);
508 		}
509     }
510 
511 
512 	/**
513 	 * Sets the columns width by reading some report options like the
514 	 * printableArea and useFullPageWidth.
515 	 * columns with fixedWidth property set in TRUE will not be modified
516 	 */
517 	protected void setColumnsFinalWidth() {
518 		log.debug("Setting columns final width.");
519 		float factor = 1;
520 		int printableArea = report.getOptions().getColumnWidth();
521 
522 		//Create a list with only the visible columns.
523 		List visibleColums = getVisibleColumns();
524 
525 		
526 
527 		if (report.getOptions().isUseFullPageWidth()) {
528 			int columnsWidth = 0;
529 			int notRezisableWidth = 0;
530 
531 			//Store in a variable the total with of all visible columns
532 			for (Iterator iterator =  visibleColums.iterator(); iterator.hasNext();) {
533 				AbstractColumn col = (AbstractColumn) iterator.next();
534 				columnsWidth += col.getWidth().intValue();
535 				if (col.getFixedWidth().booleanValue())
536 					notRezisableWidth += col.getWidth().intValue();
537 			}
538 
539 
540 			factor = (float) (printableArea-notRezisableWidth) / (float) (columnsWidth-notRezisableWidth);
541 
542 			log.debug("printableArea = " + printableArea 
543 					+ ", columnsWidth = "+ columnsWidth 
544 					+ ", columnsWidth = "+ columnsWidth 
545 					+ ", notRezisableWidth = "+ notRezisableWidth 
546 					+ ", factor = "+ factor);
547 
548 			int acumulated = 0;
549 			int colFinalWidth = 0;
550 
551 			//Select the non-resizable columns
552 			Collection resizableColumns = CollectionUtils.select( visibleColums,new Predicate() {
553 				public boolean evaluate(Object arg0) {
554 					return !((AbstractColumn)arg0).getFixedWidth().booleanValue();
555 				}
556 
557 			}) ;
558 
559 			//Finally, set the new width to the resizable columns
560 			for (Iterator iter = resizableColumns.iterator(); iter.hasNext();) {
561 				AbstractColumn col = (AbstractColumn) iter.next();
562 
563 				if (!iter.hasNext()) {
564 					col.setWidth(new Integer(printableArea - notRezisableWidth - acumulated));
565 				} else {
566 					colFinalWidth = (new Float(col.getWidth().intValue() * factor)).intValue();
567 					acumulated += colFinalWidth;
568 					col.setWidth(new Integer(colFinalWidth));
569 				}
570 			}
571 		}
572 
573 		// If the columns width changed, the X position must be setted again.
574 		int posx = 0;
575 		for (Iterator iterator =  visibleColums.iterator(); iterator.hasNext();) {
576 			AbstractColumn col = (AbstractColumn) iterator.next();
577 			col.setPosX(new Integer(posx));
578 			posx += col.getWidth().intValue();
579 		}
580 	}
581 
582     /**
583      * @return A list of visible columns
584      */
585 	protected List<AbstractColumn> getVisibleColumns() {
586         return new ArrayList<AbstractColumn>(report.getColumns());
587 	}
588 
589 	/**
590 	 * Sets the necessary height for all bands in the report, to hold their children
591 	 */
592 	protected void setBandsFinalHeight() {
593 		log.debug("Setting bands final height...");
594 		
595 		List<JRBand> bands = new ArrayList<JRBand>();
596 		
597 		Utils.addNotNull(bands, (JRDesignBand) design.getPageHeader());
598 		Utils.addNotNull(bands, (JRDesignBand) design.getPageFooter());
599 		Utils.addNotNull(bands, (JRDesignBand) design.getColumnHeader());
600 		Utils.addNotNull(bands, (JRDesignBand) design.getColumnFooter());
601 		Utils.addNotNull(bands, (JRDesignBand) design.getSummary());
602 		Utils.addNotNull(bands, (JRDesignBand) design.getBackground());
603 		bands.addAll(((JRDesignSection) design.getDetailSection()).getBandsList());
604 		Utils.addNotNull(bands, (JRDesignBand) design.getLastPageFooter());
605 		Utils.addNotNull(bands, (JRDesignBand) design.getTitle());
606 		Utils.addNotNull(bands, (JRDesignBand) design.getPageFooter());
607 		Utils.addNotNull(bands, (JRDesignBand) design.getNoData());
608 
609 		for (Iterator iter = design.getGroupsList().iterator(); iter.hasNext();) {
610 			JRGroup jrgroup = (JRGroup) iter.next();
611 			DJGroup djGroup = (DJGroup) getReferencesMap().get(jrgroup.getName());
612 			JRDesignSection headerSection = (JRDesignSection) jrgroup.getGroupHeaderSection();
613 			JRDesignSection footerSection = (JRDesignSection) jrgroup.getGroupFooterSection();
614 			if (djGroup != null){
615 				for (JRBand headerBand : (List<JRBand>)headerSection.getBandsList()) {
616 					setBandFinalHeight((JRDesignBand) headerBand,djGroup.getHeaderHeight(), djGroup.isFitHeaderHeightToContent());
617 					
618 				}
619 				for (JRBand footerBand : (List<JRBand>)footerSection.getBandsList()) {
620 					setBandFinalHeight((JRDesignBand) footerBand,djGroup.getFooterHeight(), djGroup.isFitFooterHeightToContent());
621 					
622 				}
623 			} else {
624 				bands.addAll(headerSection.getBandsList());
625 				bands.addAll(footerSection.getBandsList());
626 			}
627 		}
628 		
629 		for (JRBand jrDesignBand : bands) {
630 			setBandFinalHeight((JRDesignBand)jrDesignBand);
631 		}
632 	}
633 
634 	/**
635 	 * Removes empty space when "fitToContent" is true and real height of object is
636 	 * taller than current bands height, otherwise, it is not modified
637 	 * @param band
638 	 * @param currHeigth
639 	 * @param fitToContent
640 	 */
641 	private void setBandFinalHeight(JRDesignBand band, int currHeigth, boolean fitToContent) {
642 		if (band != null) {
643 			int finalHeight = LayoutUtils.findVerticalOffset(band);
644 			if (finalHeight < currHeigth && !fitToContent){
645 				//nothing
646 			} else {
647 				band.setHeight(finalHeight);
648 			}
649 		}
650 		
651 	}
652 
653 	/**
654 	 * Sets the band's height to hold all its children
655 	 * @param band Band to be resized
656 	 */
657 	protected void setBandFinalHeight(JRDesignBand band) {
658 		if (band != null) {
659 			int finalHeight = LayoutUtils.findVerticalOffset(band);
660 			band.setHeight(finalHeight);
661 		}
662 	}
663 
664 	/**
665 	 * Creates a JasperReport DesignTextField from a DynamicJasper AbstractColumn.
666 	 * @param col
667      * @param height
668      * @param group
669      * @return JRDesignTextField
670 	 */
671 	protected JRDesignTextField generateTextFieldFromColumn(AbstractColumn col, int height, DJGroup group) {
672 		JRDesignTextField textField = new JRDesignTextField();
673 		JRDesignExpression exp = new JRDesignExpression();
674 
675 		if (col.getPattern() != null && "".equals(col.getPattern().trim())) {
676 			textField.setPattern(col.getPattern());
677         }
678 		
679 		if (col.getTruncateSuffix() != null){
680 			textField.getPropertiesMap().setProperty(JRTextElement.PROPERTY_TRUNCATE_SUFFIX, col.getTruncateSuffix());
681 		}
682 
683 		List columnsGroups = getReport().getColumnsGroups();
684 		if (col instanceof PercentageColumn) {
685 			PercentageColumn pcol = (PercentageColumn) col;
686 			
687 			if (group==null) { //we are in the detail band
688 				DJGroup innerMostGroup = (DJGroup) columnsGroups.get(columnsGroups.size()-1);
689 				exp.setText(pcol.getTextForExpression(innerMostGroup));
690 			} else {
691 				exp.setText(pcol.getTextForExpression(group));
692 			}
693 
694 			textField.setEvaluationTime(EvaluationTimeEnum.AUTO);
695 		} else {
696 			exp.setText(col.getTextForExpression());
697 			
698 		}
699 		
700 		exp.setValueClassName(col.getValueClassNameForExpression());
701 		textField.setExpression(exp);
702 		textField.setWidth(col.getWidth().intValue());
703 		textField.setX(col.getPosX().intValue());
704 		textField.setY(col.getPosY().intValue());
705 		textField.setHeight(height);
706 
707 		textField.setBlankWhenNull(col.getBlankWhenNull());
708 
709 		textField.setPattern(col.getPattern());
710 
711         if (col.getMarkup() != null)
712             textField.setMarkup(col.getMarkup().toLowerCase());
713 
714         textField.setPrintRepeatedValues(col.getPrintRepeatedValues().booleanValue());
715 
716         textField.setPrintWhenDetailOverflows(true);
717 
718         Style columnStyle = col.getStyle();
719         if (columnStyle == null)
720         	columnStyle = report.getOptions().getDefaultDetailStyle();
721 
722         applyStyleToElement(columnStyle, textField);
723         JRDesignStyle jrstyle = (JRDesignStyle) textField.getStyle();
724         
725         if (group != null) {
726         	int index = columnsGroups.indexOf(group);
727 //            JRDesignGroup previousGroup = (JRDesignGroup) getDesign().getGroupsList().get(index);
728             JRDesignGroup previousGroup = getJRGroupFromDJGroup(group);
729             textField.setPrintWhenGroupChanges(previousGroup);
730 
731             /**
732              * Since a group column can share the style with non group columns, if oddRow coloring is enabled,
733              * we modified this shared style to have a colored background on odd rows. We don't want that for group
734              * columns, that's why we create our own style from the existing one, and remove proper odd-row conditional
735              * style if present
736              */
737             JRDesignStyle groupStyle = Utils.cloneStyle(jrstyle);
738 
739 			groupStyle.setName(groupStyle.getFontName() +"_for_group_"+index + "_");
740 			textField.setStyle(groupStyle);
741 			try {
742 				design.addStyle(groupStyle);
743 			} catch (JRException e) { /**e.printStackTrace(); //Already there, nothing to do **/}
744 
745         } else {
746         	
747         	JRDesignStyle alternateStyle = Utils.cloneStyle(jrstyle);
748 
749 			alternateStyle.setName(alternateStyle.getFontName() +"_for_column_"+col.getName() + "_");
750 			alternateStyle.getConditionalStyleList().clear();
751 			textField.setStyle(alternateStyle);
752 			try {
753 				design.addStyle(alternateStyle);
754 			} catch (JRException e) { /**e.printStackTrace(); //Already there, nothing to do **/}
755         	
756         	
757         	setUpConditionStyles(alternateStyle, col );
758         	/*
759         	if (getReport().getOptions().isPrintBackgroundOnOddRows() &&
760         			(jrstyle.getConditionalStyles() == null || jrstyle.getConditionalStyles().length == 0)) {
761 	        	// No group column so this is a detail text field
762 	    		JRDesignExpression expression = new JRDesignExpression();
763 	    		expression.setValueClass(Boolean.class);
764 	    		expression.setText(EXPRESSION_TRUE_WHEN_ODD);
765 
766 	    		Style oddRowBackgroundStyle = getReport().getOptions().getOddRowBackgroundStyle();
767 
768 	    		JRDesignConditionalStyle condStyle = new JRDesignConditionalStyle();
769 	    		condStyle.setBackcolor(oddRowBackgroundStyle.getBackgroundColor());
770 	    		condStyle.setMode(JRDesignElement.MODE_OPAQUE);
771 
772 	    		condStyle.setConditionExpression(expression);
773 	    		jrstyle.addConditionalStyle(condStyle);
774         	}*/
775         }
776         return textField;
777 	}
778 
779 	/**
780 	 * set up properly the final JRStyle of the column element (for detail band) upon condition style and odd-background
781 	 * @param jrstyle
782 	 * @param column
783 	 */
784 	private void setUpConditionStyles(JRDesignStyle jrstyle, AbstractColumn column) {
785 				
786 		if (getReport().getOptions().isPrintBackgroundOnOddRows() && Utils.isEmpty(column.getConditionalStyles())){
787     		JRDesignExpression expression = new JRDesignExpression();
788     		expression.setValueClass(Boolean.class);
789     		expression.setText(EXPRESSION_TRUE_WHEN_ODD);
790 
791     		Style oddRowBackgroundStyle = getReport().getOptions().getOddRowBackgroundStyle();
792 
793     		JRDesignConditionalStyle condStyle = new JRDesignConditionalStyle();
794     		condStyle.setBackcolor(oddRowBackgroundStyle.getBackgroundColor());
795     		condStyle.setMode(ModeEnum.OPAQUE );
796 
797     		condStyle.setConditionExpression(expression);
798     		jrstyle.addConditionalStyle(condStyle);
799     		
800     		return;
801 		}
802 			
803 		if (Utils.isEmpty(column.getConditionalStyles()))
804 			return;
805 		
806 		for (Iterator iterator = column.getConditionalStyles().iterator(); iterator.hasNext();) {
807 			ConditionalStyle condition = (ConditionalStyle) iterator.next();
808 			
809 			if (getReport().getOptions().isPrintBackgroundOnOddRows() 
810 					&& Transparency.TRANSPARENT == condition.getStyle().getTransparency() ){ //condition style + odd row (only if conditional style's background is transparent)
811 				
812 				JRDesignExpression expressionForConditionalStyle = ExpressionUtils.getExpressionForConditionalStyle(condition, column.getTextForExpression());
813 				String expStr = JRExpressionUtil.getExpressionText(expressionForConditionalStyle);
814 				
815 				//ODD
816 				JRDesignExpression expressionOdd = new JRDesignExpression();
817 				expressionOdd.setValueClass(Boolean.class);
818 				expressionOdd.setText("new java.lang.Boolean(" +EXPRESSION_TRUE_WHEN_ODD+".booleanValue() && ((java.lang.Boolean)" + expStr + ").booleanValue() )");
819 
820 				Style oddRowBackgroundStyle = getReport().getOptions().getOddRowBackgroundStyle();
821 
822 				JRDesignConditionalStyle condStyleOdd = makeConditionalStyle( condition.getStyle());			
823 //				Utils.copyProperties(condStyleOdd, condition.getStyle().transform());
824 				condStyleOdd.setBackcolor(oddRowBackgroundStyle.getBackgroundColor());
825 				condStyleOdd.setMode( ModeEnum.OPAQUE );
826 				condStyleOdd.setConditionExpression(expressionOdd);
827 				jrstyle.addConditionalStyle(condStyleOdd);	
828 				
829 				//EVEN
830 				JRDesignExpression expressionEven = new JRDesignExpression();
831 				expressionEven.setValueClass(Boolean.class);
832 				expressionEven.setText("new java.lang.Boolean(" +EXPRESSION_TRUE_WHEN_EVEN+".booleanValue() && ((java.lang.Boolean)" + expStr + ").booleanValue() )");
833 
834 				JRDesignConditionalStyle condStyleEven = makeConditionalStyle( condition.getStyle());			
835 				condStyleEven.setConditionExpression(expressionEven);
836 				jrstyle.addConditionalStyle(condStyleEven);				
837 							
838 			} else { //No odd row, just the conditional style
839 				JRDesignExpression expression = ExpressionUtils.getExpressionForConditionalStyle(condition, column.getTextForExpression());
840 				JRDesignConditionalStyle condStyle = makeConditionalStyle( condition.getStyle());
841 				condStyle.setConditionExpression(expression);
842 				jrstyle.addConditionalStyle(condStyle);						
843 			}		
844 		}
845 		
846 		//The last condition is the basic one
847 		//ODD
848 		if (getReport().getOptions().isPrintBackgroundOnOddRows() ){
849 			
850 			JRDesignExpression expressionOdd = new JRDesignExpression();
851 			expressionOdd.setValueClass(Boolean.class);
852 			expressionOdd.setText(EXPRESSION_TRUE_WHEN_ODD);
853 	
854 			Style oddRowBackgroundStyle = getReport().getOptions().getOddRowBackgroundStyle();
855 	
856 			JRDesignConditionalStyle condStyleOdd = new JRDesignConditionalStyle();
857 			condStyleOdd.setBackcolor(oddRowBackgroundStyle.getBackgroundColor());
858 			condStyleOdd.setMode( ModeEnum.OPAQUE );
859 			condStyleOdd.setConditionExpression(expressionOdd);
860 			
861 			jrstyle.addConditionalStyle(condStyleOdd);	
862 			
863 			//EVEN
864 			JRDesignExpression expressionEven = new JRDesignExpression();
865 			expressionEven.setValueClass(Boolean.class);
866 			expressionEven.setText(EXPRESSION_TRUE_WHEN_EVEN);
867 	
868 			JRDesignConditionalStyle condStyleEven = new JRDesignConditionalStyle();
869 			condStyleEven.setBackcolor(jrstyle.getBackcolor());
870 			condStyleEven.setMode(  jrstyle.getModeValue() );
871 			condStyleEven.setConditionExpression(expressionEven);
872 			
873 			jrstyle.addConditionalStyle(condStyleEven);		
874 		}
875 	}
876 
877 	
878 	protected JRDesignConditionalStyle makeConditionalStyle( Style style )	{
879 		JRDesignConditionalStyle condStyle = style.transformAsConditinalStyle();
880 		return condStyle;
881 	}
882 	
883 	/*
884 	 * Takes all the report's charts and inserts them in their corresponding bands
885 	 */
886 	protected void layoutCharts() {
887 		//Pre-sort charts by group column
888 		MultiMap mmap = new MultiHashMap();
889 		for (Iterator iter = getReport().getCharts().iterator(); iter.hasNext();) {
890 			DJChart djChart = (DJChart) iter.next();
891 			mmap.put(djChart.getColumnsGroup(), djChart);
892 		}
893 
894 		for (Iterator iterator = mmap.keySet().iterator(); iterator.hasNext();) {
895 			Object key =  iterator.next();
896 			Collection charts = (Collection) mmap.get(key);
897 			ArrayList l = new ArrayList(charts);
898 			//Reverse iteration of the charts to meet insertion order
899 			for (int i = l.size(); i > 0; i--) {
900 				DJChart djChart = (DJChart) l.get(i-1);
901 				JRDesignChart chart = createChart(djChart);
902 
903 				//Charts has their own band, so they are added in the band at Y=0
904 				JRDesignBand band = createGroupForChartAndGetBand(djChart);
905 				band.addElement(chart);
906 			}
907 		}
908 		
909 		//Pre-sort charts by group column
910 		mmap = new MultiHashMap();
911 		for (Iterator iter = getReport().getNewCharts().iterator(); iter.hasNext();) {
912 			ar.com.fdvs.dj.domain.chart.DJChart djChart = (ar.com.fdvs.dj.domain.chart.DJChart) iter.next();
913 			mmap.put(djChart.getDataset().getColumnsGroup(), djChart);
914 		}
915 
916 		for (Iterator iterator = mmap.keySet().iterator(); iterator.hasNext();) {
917 			Object key =  iterator.next();
918 			Collection charts = (Collection) mmap.get(key);
919 			ArrayList l = new ArrayList(charts);
920 			//Reverse iteration of the charts to meet insertion order
921 			for (int i = l.size(); i > 0; i--) {
922 				ar.com.fdvs.dj.domain.chart.DJChart djChart = (ar.com.fdvs.dj.domain.chart.DJChart) l.get(i-1);
923 				String name = "chart_" + (i-1);
924 				JRDesignChart chart = createChart(djChart, name);
925 
926 				if (djChart.getLink() != null)
927 					HyperLinkUtil.applyHyperLinkToElement((DynamicJasperDesign) getDesign(), djChart.getLink(), chart, name + "_hyperlink");
928 				
929 				//Charts has their own band, so they are added in the band at Y=0
930 				JRDesignBand band = createGroupForChartAndGetBand(djChart);
931 				band.addElement(chart);
932 			}
933 		}
934 	}
935 
936 	protected JRDesignBand createGroupForChartAndGetBand(DJChart djChart) {
937 		JRDesignGroup jrGroup = getJRGroupFromDJGroup(djChart.getColumnsGroup());
938 		JRDesignGroup parentGroup = getParent(jrGroup);
939 		JRDesignGroup jrGroupChart = null;
940 		try {
941 //			jrGroupChart = (JRDesignGroup) BeanUtils.cloneBean(parentGroup);
942 			jrGroupChart = new JRDesignGroup(); //FIXME nuevo 3.5.2			
943 			jrGroupChart.setExpression(parentGroup.getExpression());
944 			((JRDesignSection)jrGroupChart.getGroupFooterSection()).addBand(new JRDesignBand());
945 			((JRDesignSection)jrGroupChart.getGroupHeaderSection()).addBand(new JRDesignBand());
946 			jrGroupChart.setName(jrGroupChart.getName()+"_Chart" + getReport().getCharts().indexOf(djChart));
947 		} catch (Exception e) {
948 			throw new DJException("Problem creating band for chart: " + e.getMessage(),e);
949 		}
950 
951 		//Charts should be added in its own band (to ensure page break, etc)
952 		//To achieve that, we create a group and insert it right before to the criteria group.
953 		//I need to find parent group of the criteria group, clone and insert after.
954 		//The only precaution is that if parent == child (only one group in the report) the we insert before
955 		if (jrGroup.equals(parentGroup)){
956 			jrGroupChart.setExpression(ExpressionUtils.createStringExpression("\"dummy_for_chart\""));
957 			getDesign().getGroupsList().add( getDesign().getGroupsList().indexOf(jrGroup) , jrGroupChart);
958 		} else {
959 			int index = getDesign().getGroupsList().indexOf(parentGroup);
960 			getDesign().getGroupsList().add(index, jrGroupChart);
961 		}
962 
963 		JRDesignBand band = null;
964 		switch (djChart.getOptions().getPosition()) {
965 		case DJChartOptions.POSITION_HEADER:			
966 			band = (JRDesignBand) ((JRDesignSection)jrGroupChart.getGroupHeaderSection()).getBandsList().get(0);
967 			break;
968 		case DJChartOptions.POSITION_FOOTER:
969 			band = (JRDesignBand) ((JRDesignSection)jrGroupChart.getGroupFooterSection()).getBandsList().get(0);
970 		}
971 		return band;
972 	}
973 
974 	/**
975 	 * Creates the JRDesignChart from the DJChart. To do so it also creates needed variables and data-set
976 	 * @param djChart
977 	 * @return
978 	 */
979 	protected JRDesignChart createChart(DJChart djChart){
980 			JRDesignGroup jrGroupChart = getJRGroupFromDJGroup(djChart.getColumnsGroup());
981 
982 			JRDesignChart chart = new JRDesignChart(new JRDesignStyle().getDefaultStyleProvider(), djChart.getType());
983 			JRDesignGroup parentGroup = getParent(jrGroupChart);
984 			List chartVariables = registerChartVariable(djChart);
985 			JRDesignChartDataset chartDataset = DataSetFactory.getDataset(djChart, jrGroupChart, parentGroup, chartVariables);
986 			chart.setDataset(chartDataset);
987 			interpeterOptions(djChart, chart);
988 
989 			chart.setEvaluationTime( EvaluationTimeEnum.GROUP );
990 			chart.setEvaluationGroup(jrGroupChart);
991 			return chart;
992 	}
993 
994 	protected void interpeterOptions(DJChart djChart, JRDesignChart chart) {
995 		DJChartOptions options = djChart.getOptions();
996 
997 		//size
998 		if (options.isCentered())
999 			chart.setWidth(getReport().getOptions().getPrintableWidth());
1000 		else
1001 			chart.setWidth(options.getWidth());
1002 
1003 		chart.setHeight(options.getHeight());
1004 
1005 		//position
1006 		chart.setX(options.getX());
1007 		//FIXME no more padding
1008 		//chart.setPadding(10);
1009 		chart.setY(options.getY());
1010 
1011 		//options
1012 		chart.setShowLegend(options.isShowLegend());
1013 		chart.setBackcolor(options.getBackColor());
1014 
1015         //FIXME no more border, maybe setLineBox(...) or so
1016         //chart.setBorder(options.getBorder());
1017 
1018 		//colors
1019 		if (options.getColors() != null){
1020 			int i = 1;
1021 			for (Iterator iter = options.getColors().iterator(); iter.hasNext();i++) {
1022 				Color color = (Color) iter.next();
1023 				chart.getPlot().getSeriesColors().add(new JRBaseChartPlot.JRBaseSeriesColor(i, color));
1024 			}
1025 		}
1026 		//Chart-dependent options
1027 		if (djChart.getType() == DJChart.BAR_CHART)
1028 			((JRDesignBarPlot) chart.getPlot()).setShowTickLabels(options.isShowLabels());
1029 	}
1030 
1031 
1032 	/**
1033 	 * Creates and registers a variable to be used by the Chart
1034 	 * @param chart Chart that needs a variable to be generated
1035 	 * @return the generated variables
1036 	 */
1037 	protected List registerChartVariable(DJChart chart) {
1038 		//FIXME aca hay que iterar por cada columna. Cambiar DJChart para que tome muchas
1039 		JRDesignGroup group = getJRGroupFromDJGroup(chart.getColumnsGroup());
1040 		List vars = new ArrayList();
1041 
1042 		int serieNum = 0;
1043 		for (Iterator iterator = chart.getColumns().iterator(); iterator.hasNext();) {
1044 			AbstractColumn col = (AbstractColumn) iterator.next();
1045 
1046 			Class clazz = null;
1047 
1048 			JRDesignExpression expression = new JRDesignExpression();
1049             if (col instanceof ExpressionColumn) {
1050                 try { clazz = Class.forName(((ExpressionColumn) col).getExpression().getClassName());
1051                 } catch (ClassNotFoundException e) {
1052                     throw new DJException("Exeption creating chart variable: " + e.getMessage(),e);
1053                 }
1054 
1055                 ExpressionColumn expCol = (ExpressionColumn) col;
1056                 expression.setText(expCol.getTextForExpression());
1057                 expression.setValueClassName(expCol.getExpression().getClassName());
1058             }
1059             else
1060             {
1061                 try { clazz = Class.forName(((PropertyColumn) col).getColumnProperty().getValueClassName());
1062                 } catch (ClassNotFoundException e) {
1063                     throw new DJException("Exeption creating chart variable: " + e.getMessage(),e);
1064                 }
1065 
1066                 expression.setText("$F{" + ((PropertyColumn) col).getColumnProperty().getProperty()  + "}");
1067                 expression.setValueClass(clazz);
1068             }
1069 //			expression.setText("$F{" + ((PropertyColumn) col).getColumnProperty().getProperty()  + "}");
1070 //			expression.setValueClass(clazz);
1071 
1072 			JRDesignVariable var = new JRDesignVariable();
1073 			var.setValueClass(clazz);
1074 			var.setExpression(expression);
1075 			var.setCalculation(CalculationEnum.getByValue(chart.getOperation()));
1076 			var.setResetGroup(group);
1077 			var.setResetType( ResetTypeEnum.GROUP );
1078 
1079 			//use the index as part of the name just because I may want 2
1080 			//different types of chart from the very same column (with the same operation also) making the variables name to be duplicated
1081 			int chartIndex = getReport().getCharts().indexOf(chart);
1082 			var.setName("CHART_[" + chartIndex +"_s" +serieNum + "+]_" + group.getName() + "_" + col.getTitle() + "_" + chart.getOperation());
1083 
1084 			try {
1085 				getDesign().addVariable(var);
1086 				vars.add(var);
1087 			} catch (JRException e) {
1088 				throw new LayoutException(e.getMessage(),e);
1089 			}
1090 			serieNum++;
1091 		}
1092 		return vars;
1093 	}
1094 
1095 	protected JRDesignGroup getChartColumnsGroup(ar.com.fdvs.dj.domain.chart.DJChart djChart) {
1096 		PropertyColumn columnsGroup = djChart.getDataset().getColumnsGroup();
1097 		for (Iterator iterator = getReport().getColumnsGroups().iterator(); iterator.hasNext();) {
1098 			DJGroup djGroup = (DJGroup) iterator.next();			
1099 			if (djGroup.getColumnToGroupBy() == columnsGroup)
1100 				return getJRGroupFromDJGroup(djGroup);		
1101 		}
1102 		return null;
1103 	}
1104 	
1105 	protected JRDesignBand createGroupForChartAndGetBand(ar.com.fdvs.dj.domain.chart.DJChart djChart) {
1106 		JRDesignGroup jrGroup = getChartColumnsGroup(djChart);
1107 		JRDesignGroup parentGroup = getParent(jrGroup);
1108 		JRDesignGroup jrGroupChart = null;
1109 		try {
1110 			jrGroupChart = new JRDesignGroup(); //FIXME nuevo 3.5.2			
1111 			jrGroupChart.setExpression(parentGroup.getExpression());
1112 			((JRDesignSection)jrGroupChart.getGroupFooterSection()).addBand(new JRDesignBand());
1113 			((JRDesignSection)jrGroupChart.getGroupHeaderSection()).addBand(new JRDesignBand());
1114 			jrGroupChart.setName(jrGroupChart.getName()+"_Chart" + getReport().getCharts().indexOf(djChart));			
1115 		} catch (Exception e) {
1116 			throw new DJException("Problem creating band for chart: " + e.getMessage(),e);
1117 		}
1118 
1119 		//Charts should be added in its own band (to ensure page break, etc)
1120 		//To achieve that, we create a group and insert it right before to the criteria group.
1121 		//I need to find parent group of the criteria group, clone and insert after.
1122 		//The only precaution is that if parent == child (only one group in the report) the we insert before
1123 		if (jrGroup.equals(parentGroup)){
1124 			jrGroupChart.setExpression(ExpressionUtils.createStringExpression("\"dummy_for_chart\""));
1125 			getDesign().getGroupsList().add( getDesign().getGroupsList().indexOf(jrGroup) , jrGroupChart);
1126 		} else {
1127 			int index = getDesign().getGroupsList().indexOf(parentGroup);
1128 			getDesign().getGroupsList().add(index, jrGroupChart);
1129 		}
1130 
1131 		JRDesignBand band = null;
1132 		switch (djChart.getOptions().getPosition()) {
1133 		case DJChartOptions.POSITION_HEADER:
1134 			band = (JRDesignBand) ((JRDesignSection)jrGroupChart.getGroupHeaderSection()).getBandsList().get(0);
1135 			break;
1136 		case DJChartOptions.POSITION_FOOTER:
1137 			band = (JRDesignBand)  ((JRDesignSection)jrGroupChart.getGroupFooterSection()).getBandsList().get(0);
1138 		}
1139 		return band;
1140 	}
1141 
1142 	/**
1143 	 * Creates the JRDesignChart from the DJChart. To do so it also creates needed variables and data-set
1144 	 * @param djChart
1145 	 * @return
1146 	 */
1147 	protected JRDesignChart createChart(ar.com.fdvs.dj.domain.chart.DJChart djChart, String name){
1148 			JRDesignGroup jrGroupChart = getChartColumnsGroup(djChart);
1149 			JRDesignGroup parentGroup = getParent(jrGroupChart);
1150 			Map chartVariables = registerChartVariable(djChart);
1151 			return djChart.transform((DynamicJasperDesign) getDesign(), name, jrGroupChart, parentGroup, chartVariables, getReport().getOptions().getPrintableWidth());
1152 	}
1153 
1154 	/**
1155 	 * Creates and registers a variable to be used by the Chart
1156 	 * @param chart Chart that needs a variable to be generated
1157 	 * @return the generated variables
1158 	 */
1159 	protected Map registerChartVariable(ar.com.fdvs.dj.domain.chart.DJChart chart) {
1160 		//FIXME aca hay que iterar por cada columna. Cambiar DJChart para que tome muchas
1161 		JRDesignGroup group = getChartColumnsGroup(chart);
1162 		Map vars = new HashMap();
1163 
1164 		int serieNum = 0;
1165 		for (Iterator iterator = chart.getDataset().getColumns().iterator(); iterator.hasNext();) {
1166 			AbstractColumn col = (AbstractColumn) iterator.next();
1167 
1168 
1169 			Class clazz = null;
1170 //			try { clazz = Class.forName(col.getValueClassNameForExpression());
1171 //			} catch (ClassNotFoundException e) {
1172 //				throw new DJException("Exeption creating chart variable: " + e.getMessage(),e);
1173 //			}
1174 
1175 			JRDesignExpression expression = new JRDesignExpression();
1176 			//FIXME Only PropertyColumn allowed?
1177             if (col instanceof ExpressionColumn) {
1178                 try { clazz = Class.forName(((ExpressionColumn) col).getExpression().getClassName());
1179                 } catch (ClassNotFoundException e) {
1180                     throw new DJException("Exeption creating chart variable: " + e.getMessage(),e);
1181                 }
1182 
1183                 ExpressionColumn expCol = (ExpressionColumn) col;
1184                 expression.setText(expCol.getTextForExpression());
1185                 expression.setValueClassName(expCol.getExpression().getClassName());
1186             }
1187             else {
1188                 try { clazz = Class.forName(((PropertyColumn) col).getColumnProperty().getValueClassName());
1189                 } catch (ClassNotFoundException e) {
1190                     throw new DJException("Exeption creating chart variable: " + e.getMessage(),e);
1191                 }
1192 
1193                 expression.setText("$F{" + ((PropertyColumn) col).getColumnProperty().getProperty()  + "}");
1194                 expression.setValueClass(clazz);
1195             }
1196 
1197 			JRDesignVariable var = new JRDesignVariable();
1198 			var.setValueClass(clazz);
1199 			var.setExpression(expression);
1200 			var.setCalculation(CalculationEnum.getByValue(chart.getOperation()));
1201 			var.setResetGroup(group);
1202 			var.setResetType( ResetTypeEnum.GROUP );
1203 
1204 			//use the index as part of the name just because I may want 2
1205 			//different types of chart from the very same column (with the same operation also) making the variables name to be duplicated
1206 			int chartIndex = getReport().getNewCharts().indexOf(chart);
1207 			var.setName("CHART_[" + chartIndex +"_s" +serieNum + "+]_" + group.getName() + "_" + col.getTitle() + "_" + chart.getOperation());
1208 
1209 			try {
1210 				getDesign().addVariable(var);
1211 				vars.put(col, var);
1212 			} catch (JRException e) {
1213 				throw new LayoutException(e.getMessage(),e);
1214 			}
1215 			serieNum++;
1216 		}
1217 		return vars;
1218 	}
1219 
1220 	/**
1221 	 * Finds the parent group of the given one and returns it
1222 	 * @param group Group for which the parent is needed
1223 	 * @return The parent group of the given one. If the given one is the first one, it returns the same group
1224 	 */
1225 	protected JRDesignGroup getParent(JRDesignGroup group){
1226 		int index = realGroups.indexOf(group);
1227 		JRDesignGroup parentGroup = (index > 0) ? (JRDesignGroup) realGroups.get(index-1): group;
1228 		return parentGroup;
1229 	}
1230 
1231 	/***
1232 	 * Finds JRDesignGroup associated to a DJGroup
1233 	 * @param group
1234 	 * @return
1235 	 */
1236 	protected JRDesignGroup getJRGroupFromDJGroup(DJGroup group){
1237 		int index = getReport().getColumnsGroups().indexOf(group);
1238 		return (JRDesignGroup) realGroups.get(index);
1239 	}
1240 
1241 	
1242 	protected DJGroup getDJGroup(AbstractColumn col) {
1243 		Iterator it = getReport().getColumnsGroups().iterator();
1244 		while (it.hasNext()) {
1245 			DJGroup group = (DJGroup) it.next();
1246 			if (group.getColumnToGroupBy().equals(col))
1247 				return group;
1248 		}
1249 		return null;
1250 	}		
1251 	
1252 	
1253 	/**
1254 	 * Returns true if at least one group is configured to show the column name in its header
1255 	 * @return
1256 	 */
1257 	protected boolean existsGroupWithColumnNames() {
1258 		Iterator it = getReport().getColumnsGroups().iterator();
1259 		while (it.hasNext()) {
1260 			DJGroup group = (DJGroup) it.next();
1261 			if (group.getLayout().isShowColumnName())
1262 				return true;
1263 		}
1264 		return false;
1265 	}	
1266 
1267 	protected JasperDesign getDesign() {
1268 		return design;
1269 	}
1270 
1271 	protected void setDesign(JasperDesign design) {
1272 		this.design = design;
1273 	}
1274 
1275 	protected DynamicReport getReport() {
1276 		return report;
1277 	}
1278 
1279 	protected void setReport(DynamicReport report) {
1280 		this.report = report;
1281 	}
1282 
1283 }