001: /*
002: * ============================================================================
003: * GNU Lesser General Public License
004: * ============================================================================
005: *
006: * JasperReports - Free Java report-generating library.
007: * Copyright (C) 2001-2006 JasperSoft Corporation http://www.jaspersoft.com
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
022: *
023: * JasperSoft Corporation
024: * 303 Second Street, Suite 450 North
025: * San Francisco, CA 94107
026: * http://www.jaspersoft.com
027: */
028: package net.sf.jasperreports.engine.fill;
029:
030: import java.util.ArrayList;
031: import java.util.Collection;
032: import java.util.Collections;
033: import java.util.HashMap;
034: import java.util.HashSet;
035: import java.util.Iterator;
036: import java.util.List;
037: import java.util.Map;
038: import java.util.Set;
039:
040: import net.sf.jasperreports.engine.JRConditionalStyle;
041: import net.sf.jasperreports.engine.JRElement;
042: import net.sf.jasperreports.engine.JRElementGroup;
043: import net.sf.jasperreports.engine.JRException;
044: import net.sf.jasperreports.engine.JRFrame;
045: import net.sf.jasperreports.engine.JRPrintElement;
046: import net.sf.jasperreports.engine.JRPrintElementContainer;
047: import net.sf.jasperreports.engine.JRReportFont;
048: import net.sf.jasperreports.engine.JRStyle;
049: import net.sf.jasperreports.engine.base.JRBaseStyle;
050: import net.sf.jasperreports.engine.util.JRStyleResolver;
051:
052: /**
053: * Abstract implementation of an element container filler.
054: * <p>
055: * This is the base for band, frame and crosstab cell fillers.
056: *
057: * @author Lucian Chirita (lucianc@users.sourceforge.net)
058: * @version $Id: JRFillElementContainer.java 1765 2007-06-21 15:50:27Z lucianc $
059: */
060: public abstract class JRFillElementContainer extends JRFillElementGroup {
061: protected JRBaseFiller filler;
062:
063: private JRFillElement[] ySortedElements = null;
064: private JRFillElement[] stretchElements = null;
065: private JRFillElement[] bandBottomElements = null;
066: private JRFillElement[] removableElements = null;
067:
068: private boolean willOverflow = false;
069: protected boolean isOverflow = false;
070:
071: private int stretchHeight = 0;
072: private int firstY = 0;
073: protected JRFillElement firstYElement = null;
074:
075: protected final JRFillExpressionEvaluator expressionEvaluator;
076:
077: protected JRFillElement[] deepElements;
078:
079: /**
080: *
081: */
082: protected Set stylesToEvaluate = new HashSet();
083: protected Map evaluatedStyles = new HashMap();
084:
085: protected boolean hasPrintWhenOverflowElement;
086:
087: protected JRFillElementContainer(JRBaseFiller filler,
088: JRElementGroup container, JRFillObjectFactory factory) {
089: super (container, factory);
090:
091: expressionEvaluator = factory.getExpressionEvaluator();
092: initDeepElements();
093:
094: this .filler = filler;
095: }
096:
097: protected JRFillElementContainer(JRFillElementContainer container,
098: JRFillCloneFactory factory) {
099: super (container, factory);
100:
101: expressionEvaluator = container.expressionEvaluator;
102: initDeepElements();
103:
104: this .filler = container.filler;
105: }
106:
107: private void initDeepElements() {
108: if (elements == null) {
109: deepElements = new JRFillElement[0];
110: } else {
111: List deepElementsList = new ArrayList(elements.length);
112: collectDeepElements(elements, deepElementsList);
113: deepElements = new JRFillElement[deepElementsList.size()];
114: deepElementsList.toArray(deepElements);
115: }
116: }
117:
118: private static void collectDeepElements(JRElement[] elements,
119: List deepElementsList) {
120: for (int i = 0; i < elements.length; i++) {
121: JRElement element = elements[i];
122: deepElementsList.add(element);
123:
124: if (element instanceof JRFillFrame) {
125: JRFrame frame = (JRFrame) element;
126: collectDeepElements(frame.getElements(),
127: deepElementsList);
128: }
129: }
130: }
131:
132: protected final void initElements() {
133: hasPrintWhenOverflowElement = false;
134:
135: if (elements != null && elements.length > 0) {
136: List sortedElemsList = new ArrayList();
137: List stretchElemsList = new ArrayList();
138: List bandBottomElemsList = new ArrayList();
139: List removableElemsList = new ArrayList();
140: for (int i = 0; i < elements.length; i++) {
141: JRFillElement element = elements[i];
142: sortedElemsList.add(element);
143:
144: if (element.getPositionType() == JRElement.POSITION_TYPE_FIX_RELATIVE_TO_BOTTOM) {
145: bandBottomElemsList.add(element);
146: }
147:
148: if (element.getStretchType() != JRElement.STRETCH_TYPE_NO_STRETCH) {
149: stretchElemsList.add(element);
150: }
151:
152: if (element.isRemoveLineWhenBlank()) {
153: removableElemsList.add(element);
154: }
155:
156: if (element.isPrintWhenDetailOverflows()) {
157: hasPrintWhenOverflowElement = true;
158: }
159: }
160:
161: /* */
162: Collections.sort(sortedElemsList, new JRYComparator());
163: ySortedElements = new JRFillElement[elements.length];
164: sortedElemsList.toArray(ySortedElements);
165:
166: /* */
167: stretchElements = new JRFillElement[stretchElemsList.size()];
168: stretchElemsList.toArray(stretchElements);
169:
170: /* */
171: bandBottomElements = new JRFillElement[bandBottomElemsList
172: .size()];
173: bandBottomElemsList.toArray(bandBottomElements);
174:
175: /* */
176: removableElements = new JRFillElement[removableElemsList
177: .size()];
178: removableElemsList.toArray(removableElements);
179: }
180:
181: /* */
182: setDependentElements();
183:
184: setElementsBandBottomY();
185: }
186:
187: protected final void setElementsBandBottomY() {
188: if (elements != null && elements.length > 0) {
189: for (int i = 0; i < elements.length; i++) {
190: elements[i].setBandBottomY(getContainerHeight()
191: - elements[i].getY() - elements[i].getHeight());
192: }
193: }
194: }
195:
196: /**
197: *
198: */
199: private void setDependentElements() {
200: if (ySortedElements != null && ySortedElements.length > 0) {
201: for (int i = 0; i < ySortedElements.length - 1; i++) {
202: JRFillElement iElem = ySortedElements[i];
203: boolean isBreakElem = iElem instanceof JRFillBreak;
204:
205: for (int j = i + 1; j < ySortedElements.length; j++) {
206: JRFillElement jElem = ySortedElements[j];
207:
208: int left = Math.min(iElem.getX(), jElem.getX());
209: int right = Math.max(iElem.getX()
210: + iElem.getWidth(), jElem.getX()
211: + jElem.getWidth());
212:
213: if (((isBreakElem && jElem.getPositionType() == JRElement.POSITION_TYPE_FIX_RELATIVE_TO_TOP) || jElem
214: .getPositionType() == JRElement.POSITION_TYPE_FLOAT)
215: && iElem.getY() + iElem.getHeight() <= jElem
216: .getY()
217: && iElem.getWidth() + jElem.getWidth() > right
218: - left // FIXME band bottom elements should not have dependent elements
219: ) {
220: iElem.addDependantElement(jElem);
221: }
222: }
223:
224: /*
225: if (iElem.getParent().getElementGroup() != null) //parent might be null
226: {
227: iElem.setGroupElements(
228: iElem.getParent().getElementGroup().getElements()
229: );
230: }
231: */
232: }
233: }
234: }
235:
236: /**
237: *
238: */
239: protected void evaluate(byte evaluation) throws JRException {
240: //evaluatePrintWhenExpression(evaluation);
241:
242: //if (
243: // (isPrintWhenExpressionNull() ||
244: // (!isPrintWhenExpressionNull() &&
245: // isPrintWhenTrue()))
246: // )
247: //{
248: JRElement[] allElements = getElements();
249: if (allElements != null && allElements.length > 0) {
250: for (int i = 0; i < allElements.length; i++) {
251: JRFillElement element = (JRFillElement) allElements[i];
252: element.setCurrentEvaluation(evaluation);
253: element.evaluate(evaluation);
254: }
255: }
256: //}
257: }
258:
259: /**
260: *
261: */
262: protected void resetElements() {
263: if (ySortedElements != null && ySortedElements.length > 0) {
264: for (int i = 0; i < ySortedElements.length; i++) {
265: JRFillElement element = ySortedElements[i];
266:
267: element.reset();
268:
269: if (!isOverflow) {
270: element.setAlreadyPrinted(false);
271: }
272: }
273: }
274: }
275:
276: /**
277: *
278: */
279: protected boolean willOverflow() {
280: return willOverflow;
281: }
282:
283: protected void initFill() {
284: isOverflow = willOverflow;
285: firstY = 0;
286: firstYElement = null;
287: }
288:
289: /**
290: *
291: */
292: protected void prepareElements(int availableStretchHeight,
293: boolean isOverflowAllowed) throws JRException {
294: boolean tmpWillOverflow = false;
295:
296: int maxBandStretch = 0;
297: int bandStretch = 0;
298:
299: firstY = isOverflow ? getContainerHeight() : 0;
300: firstYElement = null;
301: boolean isFirstYFound = false;
302:
303: if (ySortedElements != null && ySortedElements.length > 0) {
304: for (int i = 0; i < ySortedElements.length; i++) {
305: JRFillElement element = ySortedElements[i];
306:
307: tmpWillOverflow = element
308: .prepare(availableStretchHeight
309: + getElementFirstY(element), isOverflow)
310: || tmpWillOverflow;
311:
312: element.moveDependantElements();
313:
314: if (element.isToPrint()) {
315: if (isOverflow) {
316: if (element.isReprinted()) {
317: firstY = 0;
318: } else if (!isFirstYFound) {
319: firstY = element.getY();
320: }
321: isFirstYFound = true;
322: }
323:
324: firstYElement = element;
325:
326: bandStretch = element.getRelativeY()
327: + element.getStretchHeight()
328: - getContainerHeight()
329: + element.getBandBottomY();
330: if (bandStretch > maxBandStretch) {
331: maxBandStretch = bandStretch;
332: }
333: }
334: }
335: }
336:
337: if (maxBandStretch > availableStretchHeight + firstY) {
338: tmpWillOverflow = true;
339: }
340:
341: if (tmpWillOverflow) {
342: stretchHeight = getContainerHeight()
343: + availableStretchHeight;
344: } else {
345: stretchHeight = getContainerHeight() + maxBandStretch;
346: }
347:
348: willOverflow = tmpWillOverflow && isOverflowAllowed;
349: }
350:
351: private int getElementFirstY(JRFillElement element) {
352: int elemFirstY;
353: if (!isOverflow || hasPrintWhenOverflowElement) {
354: elemFirstY = 0;
355: } else if (element.getY() >= firstY) {
356: elemFirstY = firstY;
357: } else {
358: elemFirstY = element.getY();
359: }
360: return elemFirstY;
361: }
362:
363: protected void setStretchHeight(int stretchHeight) {
364: if (stretchHeight > this .stretchHeight) {
365: this .stretchHeight = stretchHeight;
366: }
367: }
368:
369: /**
370: *
371: */
372: protected void stretchElements() {
373: if (stretchElements != null && stretchElements.length > 0) {
374: for (int i = 0; i < stretchElements.length; i++) {
375: JRFillElement element = stretchElements[i];
376:
377: element.stretchElement(stretchHeight
378: - getContainerHeight());
379:
380: element.moveDependantElements();
381: }
382: }
383:
384: if (ySortedElements != null && ySortedElements.length > 0) {
385: for (int i = 0; i < ySortedElements.length; i++) {
386: JRFillElement element = ySortedElements[i];
387:
388: element.stretchHeightFinal();
389: }
390: }
391: }
392:
393: protected int getStretchHeight() {
394: return stretchHeight;
395: }
396:
397: /**
398: *
399: */
400: protected void moveBandBottomElements() {
401: //if (!willOverflow)
402: //{
403: if (bandBottomElements != null && bandBottomElements.length > 0) {
404: for (int i = 0; i < bandBottomElements.length; i++) {
405: JRFillElement element = bandBottomElements[i];
406:
407: element.setRelativeY(element.getY() + stretchHeight
408: - getContainerHeight());
409:
410: // band bottom elements do not print if there will be an overflow
411: element
412: .setToPrint(element.isToPrint()
413: && !willOverflow);
414: }
415: }
416: //}
417: }
418:
419: /**
420: *
421: */
422: protected void removeBlankElements() {
423: JRElement[] remElems = removableElements;
424: if (remElems != null && remElems.length > 0) {
425: JRElement[] elems = ySortedElements;
426:
427: for (int i = 0; i < remElems.length; i++) {
428: JRFillElement iElem = (JRFillElement) remElems[i];
429:
430: int blankHeight;
431: if (iElem.isToPrint()) {
432: blankHeight = iElem.getHeight()
433: - iElem.getStretchHeight();
434: } else {
435: blankHeight = iElem.getHeight();//FIXME subreports that strech and then don't print, will not remove all space
436: }
437:
438: if (blankHeight > 0
439: && iElem.getRelativeY()
440: + iElem.getStretchHeight() <= stretchHeight
441: && iElem.getRelativeY() >= firstY) {
442: int blankY = iElem.getRelativeY()
443: + iElem.getHeight() - blankHeight;
444: boolean isToRemove = true;
445:
446: for (int j = 0; j < elems.length; j++) {
447: JRFillElement jElem = (JRFillElement) elems[j];
448:
449: if (iElem != jElem && jElem.isToPrint()) {
450: int top = Math.min(blankY, jElem
451: .getRelativeY());
452: int bottom = Math.max(blankY + blankHeight,
453: jElem.getRelativeY()
454: + jElem.getStretchHeight());
455:
456: if (blankHeight + jElem.getStretchHeight() > bottom
457: - top) {
458: isToRemove = false;
459: break;
460: }
461: }
462: }
463:
464: if (isToRemove) {
465: for (int j = 0; j < elems.length; j++) {
466: JRFillElement jElem = (JRFillElement) elems[j];
467:
468: if (jElem.getRelativeY() >= blankY
469: + blankHeight) {
470: jElem.setRelativeY(jElem.getRelativeY()
471: - blankHeight);
472: }
473: }
474:
475: stretchHeight = stretchHeight - blankHeight;
476: }
477: }
478: }
479: }
480: }
481:
482: /**
483: *
484: */
485: protected void fillElements(JRPrintElementContainer printContainer)
486: throws JRException {
487: //int maxStretch = 0;
488: //int stretch = 0;
489: JRElement[] allElements = getElements();
490: if (allElements != null && allElements.length > 0) {
491: for (int i = 0; i < allElements.length; i++) {
492: JRFillElement element = (JRFillElement) allElements[i];
493:
494: element.setRelativeY(element.getRelativeY() - firstY);
495:
496: if (element.getRelativeY() + element.getStretchHeight() > stretchHeight) {
497: element.setToPrint(false);
498: }
499:
500: element.setAlreadyPrinted(element.isToPrint()
501: || element.isAlreadyPrinted());
502:
503: if (element.isToPrint()) {
504: JRPrintElement printElement = element.fill();
505: //printElement.setY(printElement.getY() - firstY);
506:
507: if (printElement != null) {
508: //FIXME not all elements affect height
509: //stretch = printElement.getY() + firstY + printElement.getHeight() - element.getY() - element.getHeight();
510: //if (stretch > maxStretch)
511: //{
512: // maxStretch = stretch;
513: //}
514: printContainer.addElement(printElement);
515:
516: if (element instanceof JRFillSubreport) {
517: JRFillSubreport subreport = (JRFillSubreport) element;
518:
519: List fonts = subreport.subreportFiller
520: .getJasperPrint().getFontsList();
521: if (fonts != null) {
522: for (int j = 0; j < fonts.size(); j++) {
523: filler
524: .getJasperPrint()
525: .addFont(
526: (JRReportFont) fonts
527: .get(j),
528: true);
529: }
530: }
531:
532: List styles = subreport.subreportFiller
533: .getJasperPrint().getStylesList();
534: if (styles != null) {
535: for (int j = 0; j < styles.size(); j++) {
536: filler.getJasperPrint().addStyle(
537: (JRStyle) styles.get(j),
538: true);
539: }
540: }
541:
542: Collection printElements = subreport
543: .getPrintElements();
544: addSubElements(printContainer, element,
545: printElements);
546: } else if (element instanceof JRFillCrosstab) {
547: List printElements = ((JRFillCrosstab) element)
548: .getPrintElements();
549: addSubElements(printContainer, element,
550: printElements);
551: }
552: }
553: }
554: }
555: }
556:
557: //printBand.setHeight(getHeight() + maxStretch - firstY);
558: printContainer.setHeight(stretchHeight - firstY);
559: }
560:
561: protected void addSubElements(
562: JRPrintElementContainer printContainer,
563: JRFillElement element, Collection printElements) {
564: JRPrintElement printElement;
565: if (printElements != null && printElements.size() > 0) {
566: for (Iterator it = printElements.iterator(); it.hasNext();) {
567: printElement = (JRPrintElement) it.next();
568: printElement.setX(element.getX() + printElement.getX());
569: printElement.setY(element.getRelativeY()
570: + printElement.getY());
571: printContainer.addElement(printElement);
572: }
573: }
574: }
575:
576: /**
577: *
578: */
579: protected void rewind() throws JRException {
580: if (ySortedElements != null && ySortedElements.length > 0) {
581: for (int i = 0; i < ySortedElements.length; i++) {
582: JRFillElement element = ySortedElements[i];
583:
584: element.rewind();
585:
586: element.setAlreadyPrinted(false);
587: }
588: }
589:
590: willOverflow = false;
591: }
592:
593: protected int getFirstY() {
594: return firstY;
595: }
596:
597: /**
598: * Returns the height of the element container.
599: *
600: * @return the height of the element container
601: */
602: protected abstract int getContainerHeight();
603:
604: /**
605: * Find all styles containing conditional styles which are referenced by elements in this band.
606: */
607: protected void initConditionalStyles() {
608: filler
609: .addDefaultStyleListener(new JRBaseFiller.DefaultStyleListener() {
610: public void defaultStyleSet(JRStyle style) {
611: collectConditionalStyle(style);
612: }
613: });
614:
615: for (int i = 0; i < deepElements.length; i++) {
616: JRStyle style = deepElements[i].initStyle;
617: collectConditionalStyle(style);
618: }
619:
620: if (deepElements.length > 0) {
621: for (int i = 0; i < deepElements.length; i++) {
622: deepElements[i].setConditionalStylesContainer(this );
623: }
624: }
625: }
626:
627: protected void collectConditionalStyle(JRStyle style) {
628: if (style != null)// && style.getConditionalStyles() != null)
629: {
630: stylesToEvaluate.add(style);
631: }
632: }
633:
634: protected void evaluateConditionalStyles(byte evaluation)
635: throws JRException {
636: for (Iterator it = stylesToEvaluate.iterator(); it.hasNext();) {
637: evaluateConditionalStyle((JRStyle) it.next(), evaluation);
638: }
639: }
640:
641: protected JRStyle evaluateConditionalStyle(JRStyle initialStyle,
642: byte evaluation) throws JRException {
643: JRStyle consolidatedStyle = initialStyle;
644:
645: StringBuffer code = new StringBuffer();
646: List condStylesToApply = new ArrayList();
647:
648: boolean anyTrue = buildConsolidatedStyle(initialStyle,
649: evaluation, code, condStylesToApply);
650:
651: if (anyTrue) {
652: String consolidatedStyleName = initialStyle.getName()
653: + code.toString();
654: consolidatedStyle = (JRStyle) filler.getJasperPrint()
655: .getStylesMap().get(consolidatedStyleName);
656: if (consolidatedStyle == null) {
657: consolidatedStyle = new JRBaseStyle(
658: consolidatedStyleName);
659: for (int j = condStylesToApply.size() - 1; j >= 0; j--) {
660: JRStyleResolver.appendStyle(consolidatedStyle,
661: (JRStyle) condStylesToApply.get(j));
662: }
663:
664: filler.getJasperPrint().addStyle(consolidatedStyle,
665: true);
666: }
667: }
668:
669: evaluatedStyles.put(initialStyle, consolidatedStyle);
670:
671: return consolidatedStyle;
672: }
673:
674: protected boolean buildConsolidatedStyle(JRStyle style,
675: byte evaluation, StringBuffer code, List condStylesToApply)
676: throws JRException {
677: boolean anyTrue = false;
678:
679: JRConditionalStyle[] conditionalStyles = style
680: .getConditionalStyles();
681: if (conditionalStyles != null && conditionalStyles.length > 0) {
682: for (int j = 0; j < conditionalStyles.length; j++) {
683: JRConditionalStyle conditionalStyle = conditionalStyles[j];
684: Boolean expressionValue = (Boolean) expressionEvaluator
685: .evaluate(conditionalStyle
686: .getConditionExpression(), evaluation);
687:
688: boolean condition;
689: if (expressionValue == null) {
690: condition = false;
691: } else {
692: condition = expressionValue.booleanValue();
693: }
694:
695: code.append(condition ? '1' : '0');
696: anyTrue = anyTrue | condition;
697:
698: if (condition)
699: condStylesToApply.add(conditionalStyle);
700: }
701: }
702:
703: condStylesToApply.add(style);
704:
705: if (style.getStyle() != null)
706: anyTrue = anyTrue
707: | buildConsolidatedStyle(style.getStyle(),
708: evaluation, code, condStylesToApply);
709:
710: return anyTrue;
711: }
712:
713: public JRStyle getEvaluatedConditionalStyle(JRStyle parentStyle) {
714: return (JRStyle) evaluatedStyles.get(parentStyle);
715: }
716: }
|