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:
029: /*
030: * Contributors:
031: * Greg Hilton
032: */
033:
034: package net.sf.jasperreports.engine.export;
035:
036: import java.io.File;
037: import java.io.FileOutputStream;
038: import java.io.IOException;
039: import java.io.OutputStream;
040: import java.util.ArrayList;
041: import java.util.HashMap;
042: import java.util.List;
043: import java.util.Map;
044:
045: import net.sf.jasperreports.engine.JRAbstractExporter;
046: import net.sf.jasperreports.engine.JRAlignment;
047: import net.sf.jasperreports.engine.JRException;
048: import net.sf.jasperreports.engine.JRExporterParameter;
049: import net.sf.jasperreports.engine.JRFont;
050: import net.sf.jasperreports.engine.JRPrintElement;
051: import net.sf.jasperreports.engine.JRPrintEllipse;
052: import net.sf.jasperreports.engine.JRPrintFrame;
053: import net.sf.jasperreports.engine.JRPrintHyperlink;
054: import net.sf.jasperreports.engine.JRPrintImage;
055: import net.sf.jasperreports.engine.JRPrintLine;
056: import net.sf.jasperreports.engine.JRPrintPage;
057: import net.sf.jasperreports.engine.JRPrintRectangle;
058: import net.sf.jasperreports.engine.JRPrintText;
059: import net.sf.jasperreports.engine.JRTextElement;
060: import net.sf.jasperreports.engine.JasperPrint;
061: import net.sf.jasperreports.engine.base.JRBasePrintText;
062: import net.sf.jasperreports.engine.util.JRStyledText;
063: import net.sf.jasperreports.engine.util.JRStyledTextParser;
064:
065: import org.xml.sax.SAXException;
066:
067: /**
068: * @author Teodor Danciu (teodord@users.sourceforge.net)
069: * @version $Id: JRXlsAbstractExporter.java 1824 2007-08-23 14:19:12Z teodord $
070: */
071: public abstract class JRXlsAbstractExporter extends JRAbstractExporter {
072:
073: protected static class TextAlignHolder {
074: public final short horizontalAlignment;
075: public final short verticalAlignment;
076: public final short rotation;
077:
078: public TextAlignHolder(short horizontalAlignment,
079: short verticalAlignment, short rotation) {
080: this .horizontalAlignment = horizontalAlignment;
081: this .verticalAlignment = verticalAlignment;
082: this .rotation = rotation;
083: }
084: }
085:
086: /**
087: *
088: */
089: protected List loadedFonts = new ArrayList();
090:
091: /**
092: *
093: */
094: protected boolean isOnePagePerSheet;
095: protected boolean isRemoveEmptySpace;
096: protected boolean isWhitePageBackground;
097: protected boolean isAutoDetectCellType = true;
098: protected boolean isDetectCellType;
099: protected boolean isFontSizeFixEnabled;
100: protected boolean isIgnoreGraphics;
101: protected boolean isCollapseRowSpan;
102: protected boolean isIgnoreCellBorder;
103:
104: protected int maxRowsPerSheet;
105:
106: protected JRHyperlinkProducerFactory hyperlinkProducerFactory;
107:
108: protected String[] sheetNames = null;
109:
110: /**
111: *
112: */
113: protected JRStyledTextParser styledTextParser = new JRStyledTextParser();
114:
115: protected JRExportProgressMonitor progressMonitor = null;
116:
117: protected int reportIndex = 0;
118:
119: protected Map fontMap = null;
120:
121: /**
122: *
123: */
124: protected JRFont defaultFont = null;
125:
126: /**
127: * used for counting the total number of sheets
128: */
129: protected int sheetIndex = 0;
130:
131: /**
132: * used when indexing the identical sheet generated names with ordering numbers;
133: * contains sheet names as keys and the number of occurences of each sheet name as values
134: */
135: protected Map sheetNamesMap = null;
136: protected String currentSheetName = null;
137:
138: /**
139: *
140: */
141: protected JRFont getDefaultFont() {
142: return defaultFont;
143: }
144:
145: /**
146: *
147: */
148: protected JRHyperlinkProducer getCustomHandler(JRPrintHyperlink link) {
149: return hyperlinkProducerFactory == null ? null
150: : hyperlinkProducerFactory.getHandler(link
151: .getLinkType());
152: }
153:
154: /**
155: *
156: */
157: public void exportReport() throws JRException {
158: progressMonitor = (JRExportProgressMonitor) parameters
159: .get(JRExporterParameter.PROGRESS_MONITOR);
160:
161: /* */
162: setOffset();
163:
164: try {
165: /* */
166: setExportContext();
167:
168: /* */
169: setInput();
170:
171: /* */
172: if (!isModeBatch) {
173: setPageRange();
174: }
175:
176: setParameters();
177:
178: OutputStream os = (OutputStream) parameters
179: .get(JRExporterParameter.OUTPUT_STREAM);
180: if (os != null) {
181: exportReportToStream(os);
182: } else {
183: File destFile = (File) parameters
184: .get(JRExporterParameter.OUTPUT_FILE);
185: if (destFile == null) {
186: String fileName = (String) parameters
187: .get(JRExporterParameter.OUTPUT_FILE_NAME);
188: if (fileName != null) {
189: destFile = new File(fileName);
190: } else {
191: throw new JRException(
192: "No output specified for the exporter.");
193: }
194: }
195:
196: try {
197: os = new FileOutputStream(destFile);
198: exportReportToStream(os);
199: os.flush();
200: } catch (IOException e) {
201: throw new JRException(
202: "Error trying to export to file : "
203: + destFile, e);
204: } finally {
205: if (os != null) {
206: try {
207: os.close();
208: } catch (IOException e) {
209: }
210: }
211: }
212: }
213: } finally {
214: resetExportContext();
215: }
216: }
217:
218: protected void setParameters() {
219: isOnePagePerSheet = getBooleanParameter(
220: JRXlsAbstractExporterParameter.IS_ONE_PAGE_PER_SHEET,
221: JRXlsAbstractExporterParameter.PROPERTY_ONE_PAGE_PER_SHEET,
222: false);
223:
224: isRemoveEmptySpace = getBooleanParameter(
225: JRXlsAbstractExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS,
226: JRXlsAbstractExporterParameter.PROPERTY_REMOVE_EMPTY_SPACE_BETWEEN_ROWS,
227: false);
228:
229: isWhitePageBackground = getBooleanParameter(
230: JRXlsAbstractExporterParameter.IS_WHITE_PAGE_BACKGROUND,
231: JRXlsAbstractExporterParameter.PROPERTY_WHITE_PAGE_BACKGROUND,
232: false);
233: setBackground();
234:
235: Boolean isAutoDetectCellTypeParameter = (Boolean) parameters
236: .get(JRXlsAbstractExporterParameter.IS_AUTO_DETECT_CELL_TYPE);
237: if (isAutoDetectCellTypeParameter != null) {
238: isAutoDetectCellType = isAutoDetectCellTypeParameter
239: .booleanValue();
240: }
241:
242: isWhitePageBackground = getBooleanParameter(
243: JRXlsAbstractExporterParameter.IS_DETECT_CELL_TYPE,
244: JRXlsAbstractExporterParameter.PROPERTY_DETECT_CELL_TYPE,
245: false);
246:
247: isFontSizeFixEnabled = getBooleanParameter(
248: JRXlsAbstractExporterParameter.IS_FONT_SIZE_FIX_ENABLED,
249: JRXlsAbstractExporterParameter.PROPERTY_FONT_SIZE_FIX_ENABLED,
250: false);
251:
252: isIgnoreGraphics = getBooleanParameter(
253: JRXlsAbstractExporterParameter.IS_IGNORE_GRAPHICS,
254: JRXlsAbstractExporterParameter.PROPERTY_IGNORE_GRAPHICS,
255: false);
256:
257: isCollapseRowSpan = getBooleanParameter(
258: JRXlsAbstractExporterParameter.IS_COLLAPSE_ROW_SPAN,
259: JRXlsAbstractExporterParameter.PROPERTY_COLLAPSE_ROW_SPAN,
260: false);
261:
262: isIgnoreCellBorder = getBooleanParameter(
263: JRXlsAbstractExporterParameter.IS_IGNORE_CELL_BORDER,
264: JRXlsAbstractExporterParameter.PROPERTY_IGNORE_CELL_BORDER,
265: false);
266:
267: sheetNames = (String[]) parameters
268: .get(JRXlsAbstractExporterParameter.SHEET_NAMES);
269:
270: fontMap = (Map) parameters.get(JRExporterParameter.FONT_MAP);
271:
272: hyperlinkProducerFactory = (JRHyperlinkProducerFactory) parameters
273: .get(JRExporterParameter.HYPERLINK_PRODUCER_FACTORY);
274:
275: maxRowsPerSheet = getIntegerParameter(
276: JRXlsAbstractExporterParameter.MAXIMUM_ROWS_PER_SHEET,
277: JRXlsAbstractExporterParameter.PROPERTY_MAXIMUM_ROWS_PER_SHEET,
278: 0);
279: }
280:
281: protected abstract void setBackground();
282:
283: protected void exportReportToStream(OutputStream os)
284: throws JRException {
285: openWorkbook(os);
286: sheetNamesMap = new HashMap();
287: sheetNamesMap.put("Page", new Integer(0)); // in order to skip first sheet name that would have no index
288:
289: for (reportIndex = 0; reportIndex < jasperPrintList.size(); reportIndex++) {
290: jasperPrint = (JasperPrint) jasperPrintList
291: .get(reportIndex);
292: defaultFont = new JRBasePrintText(jasperPrint
293: .getDefaultStyleProvider());
294:
295: List pages = jasperPrint.getPages();
296: if (pages != null && pages.size() > 0) {
297: if (isModeBatch) {
298: startPageIndex = 0;
299: endPageIndex = pages.size() - 1;
300: }
301:
302: if (isOnePagePerSheet) {
303: for (int pageIndex = startPageIndex; pageIndex <= endPageIndex; pageIndex++) {
304: if (Thread.currentThread().isInterrupted()) {
305: throw new JRException(
306: "Current thread interrupted.");
307: }
308:
309: JRPrintPage page = (JRPrintPage) pages
310: .get(pageIndex);
311:
312: if (sheetNames != null
313: && sheetIndex < sheetNames.length) {
314: createSheet(getSheetName(sheetNames[sheetIndex]));
315: } else {
316: createSheet(getSheetName("Page"));
317: }
318:
319: // we need to count all sheets generated for all exported documents
320: sheetIndex++;
321:
322: /* */
323: exportPage(page, /*xCuts*/null, /*startRow*/0);
324: }
325: } else {
326: // Create the sheet before looping.
327: if (sheetNames != null
328: && sheetIndex < sheetNames.length) {
329: createSheet(getSheetName(sheetNames[sheetIndex]));
330: } else {
331: createSheet(getSheetName(jasperPrint.getName()));
332: }
333:
334: // we need to count all sheets generated for all exported documents
335: sheetIndex++;
336:
337: /*
338: * Make a pass and calculate the X cuts for all pages on this sheet.
339: * The Y cuts can be calculated as each page is exported.
340: */
341: List xCuts = JRGridLayout.calculateXCuts(
342: getNature(), pages, startPageIndex,
343: endPageIndex, jasperPrint.getPageWidth(),
344: globalOffsetX);
345: setColumnWidths(xCuts);
346:
347: int startRow = 0;
348:
349: for (int pageIndex = startPageIndex; pageIndex <= endPageIndex; pageIndex++) {
350: if (Thread.currentThread().isInterrupted()) {
351: throw new JRException(
352: "Current thread interrupted.");
353: }
354: JRPrintPage page = (JRPrintPage) pages
355: .get(pageIndex);
356: startRow = exportPage(page, xCuts, startRow);
357: }
358: }
359: }
360: }
361:
362: closeWorkbook(os);
363: }
364:
365: /**
366: *
367: * @return the number of rows added.
368: */
369: protected int exportPage(JRPrintPage page, List xCuts, int startRow)
370: throws JRException {
371: JRGridLayout layout = new JRGridLayout(getNature(), page
372: .getElements(), jasperPrint.getPageWidth(), jasperPrint
373: .getPageHeight(), globalOffsetX, globalOffsetY, xCuts);
374:
375: JRExporterGridCell grid[][] = layout.getGrid();
376:
377: if (xCuts == null) {
378: xCuts = layout.getXCuts();
379: setColumnWidths(xCuts);
380: }
381:
382: int skippedRows = 0;
383: int rowIndex = startRow;
384: for (int y = 0; y < grid.length; y++) {
385: rowIndex = y - skippedRows + startRow;
386:
387: //if number of rows is too large a new sheet is created and populated with remaining rows
388: if (maxRowsPerSheet > 0 && rowIndex >= maxRowsPerSheet) {
389: createSheet(getSheetName(currentSheetName));
390: startRow = 0;
391: rowIndex = 0;
392: skippedRows = y;
393: setColumnWidths(xCuts);
394: }
395:
396: if (layout.isRowNotEmpty(y)
397: || ((!isRemoveEmptySpace || layout.isRowSpanned(y)) && !isCollapseRowSpan)) {
398: JRExporterGridCell[] gridRow = grid[y];
399:
400: int emptyCellColSpan = 0;
401: int emptyCellWidth = 0;
402:
403: setRowHeight(rowIndex, isCollapseRowSpan ? JRGridLayout
404: .getMaxRowHeight(gridRow) : JRGridLayout
405: .getRowHeight(gridRow));
406:
407: for (int x = 0; x < gridRow.length; x++) {
408: setCell(x, rowIndex);
409:
410: JRExporterGridCell gridCell = gridRow[x];
411: if (gridCell.getWrapper() != null) {
412: if (emptyCellColSpan > 0) {
413: if (emptyCellColSpan > 1) {
414: //sbuffer.append(" colspan=" + emptyCellColSpan);
415: //sheet.addMergedRegion(new Region(y, (short)(x - emptyCellColSpan - 1), y, (short)(x - 1)));
416: }
417: emptyCellColSpan = 0;
418: emptyCellWidth = 0;
419: }
420:
421: JRPrintElement element = gridCell.getWrapper()
422: .getElement();
423:
424: if (element instanceof JRPrintLine) {
425: exportLine((JRPrintLine) element, gridCell,
426: x, rowIndex);
427: } else if (element instanceof JRPrintRectangle) {
428: exportRectangle(element, gridCell, x,
429: rowIndex);
430: } else if (element instanceof JRPrintEllipse) {
431: exportRectangle(element, gridCell, x,
432: rowIndex);
433: } else if (element instanceof JRPrintImage) {
434: exportImage((JRPrintImage) element,
435: gridCell, x, rowIndex);
436: } else if (element instanceof JRPrintText) {
437: exportText((JRPrintText) element, gridCell,
438: x, rowIndex);
439: } else if (element instanceof JRPrintFrame) {
440: exportFrame((JRPrintFrame) element,
441: gridCell, x, y);//FIXME rowIndex?
442: }
443:
444: x += gridCell.getColSpan() - 1;
445: } else {
446: emptyCellColSpan++;
447: emptyCellWidth += gridCell.getWidth();
448: addBlankCell(gridCell, x, rowIndex);
449: }
450: }
451:
452: if (emptyCellColSpan > 0) {
453: if (emptyCellColSpan > 1) {
454: //sbuffer.append(" colspan=" + emptyCellColSpan);
455: //sheet.addMergedRegion(new Region(y, (short)x, y, (short)(x + emptyCellColSpan - 1)));
456: }
457: }
458: } else {
459: skippedRows++;
460: // setRowHeight(y, 0);
461: //
462: // for(int x = 0; x < grid[y].length; x++)
463: // {
464: // addBlankCell(x, y);
465: // setCell(x, y);
466: // }
467: }
468: }
469:
470: if (progressMonitor != null) {
471: progressMonitor.afterPageExport();
472: }
473:
474: // Return the number of rows added
475: return rowIndex;
476: }
477:
478: protected void setColumnWidths(List xCuts) {
479: int width = 0;
480: for (int i = 1; i < xCuts.size(); i++) {
481: width = ((Integer) xCuts.get(i)).intValue()
482: - ((Integer) xCuts.get(i - 1)).intValue();
483: setColumnWidth((short) (i - 1), (short) (width * 43));
484: }
485: }
486:
487: /**
488: *
489: */
490: protected JRStyledText getStyledText(JRPrintText textElement) {
491: JRStyledText styledText = null;
492:
493: String text = textElement.getText();
494: if (text != null) {
495: if (textElement.isStyledText()) {
496: try {
497: styledText = styledTextParser.parse(null, text);
498: } catch (SAXException e) {
499: //ignore if invalid styled text and treat like normal text
500: }
501: }
502:
503: if (styledText == null) {
504: styledText = new JRStyledText();
505: styledText.append(text);
506: styledText.addRun(new JRStyledText.Run(null, 0, text
507: .length()));
508: }
509: }
510:
511: return styledText;
512: }
513:
514: protected static TextAlignHolder getTextAlignHolder(
515: JRPrintText textElement) {
516: short horizontalAlignment;
517: short verticalAlignment;
518: short rotation = textElement.getRotation();
519:
520: switch (textElement.getRotation()) {
521: case JRTextElement.ROTATION_LEFT: {
522: switch (textElement.getHorizontalAlignment()) {
523: case JRAlignment.HORIZONTAL_ALIGN_LEFT: {
524: verticalAlignment = JRAlignment.VERTICAL_ALIGN_BOTTOM;
525: break;
526: }
527: case JRAlignment.HORIZONTAL_ALIGN_CENTER: {
528: verticalAlignment = JRAlignment.VERTICAL_ALIGN_MIDDLE;
529: break;
530: }
531: case JRAlignment.HORIZONTAL_ALIGN_RIGHT: {
532: verticalAlignment = JRAlignment.VERTICAL_ALIGN_TOP;
533: break;
534: }
535: case JRAlignment.HORIZONTAL_ALIGN_JUSTIFIED: {
536: verticalAlignment = JRAlignment.VERTICAL_ALIGN_JUSTIFIED;
537: break;
538: }
539: default: {
540: verticalAlignment = JRAlignment.VERTICAL_ALIGN_BOTTOM;
541: }
542: }
543:
544: switch (textElement.getVerticalAlignment()) {
545: case JRAlignment.VERTICAL_ALIGN_TOP: {
546: horizontalAlignment = JRAlignment.HORIZONTAL_ALIGN_LEFT;
547: break;
548: }
549: case JRAlignment.VERTICAL_ALIGN_MIDDLE: {
550: horizontalAlignment = JRAlignment.HORIZONTAL_ALIGN_CENTER;
551: break;
552: }
553: case JRAlignment.VERTICAL_ALIGN_BOTTOM: {
554: horizontalAlignment = JRAlignment.HORIZONTAL_ALIGN_RIGHT;
555: break;
556: }
557: default: {
558: horizontalAlignment = JRAlignment.HORIZONTAL_ALIGN_LEFT;
559: }
560: }
561:
562: break;
563: }
564: case JRTextElement.ROTATION_RIGHT: {
565: switch (textElement.getHorizontalAlignment()) {
566: case JRAlignment.HORIZONTAL_ALIGN_LEFT: {
567: verticalAlignment = JRAlignment.VERTICAL_ALIGN_TOP;
568: break;
569: }
570: case JRAlignment.HORIZONTAL_ALIGN_CENTER: {
571: verticalAlignment = JRAlignment.VERTICAL_ALIGN_MIDDLE;
572: break;
573: }
574: case JRAlignment.HORIZONTAL_ALIGN_RIGHT: {
575: verticalAlignment = JRAlignment.VERTICAL_ALIGN_BOTTOM;
576: break;
577: }
578: case JRAlignment.HORIZONTAL_ALIGN_JUSTIFIED: {
579: verticalAlignment = JRAlignment.VERTICAL_ALIGN_JUSTIFIED;
580: break;
581: }
582: default: {
583: verticalAlignment = JRAlignment.VERTICAL_ALIGN_TOP;
584: }
585: }
586:
587: switch (textElement.getVerticalAlignment()) {
588: case JRAlignment.VERTICAL_ALIGN_TOP: {
589: horizontalAlignment = JRAlignment.HORIZONTAL_ALIGN_RIGHT;
590: break;
591: }
592: case JRAlignment.VERTICAL_ALIGN_MIDDLE: {
593: horizontalAlignment = JRAlignment.HORIZONTAL_ALIGN_CENTER;
594: break;
595: }
596: case JRAlignment.VERTICAL_ALIGN_BOTTOM: {
597: horizontalAlignment = JRAlignment.HORIZONTAL_ALIGN_LEFT;
598: break;
599: }
600: default: {
601: horizontalAlignment = JRAlignment.HORIZONTAL_ALIGN_RIGHT;
602: }
603: }
604:
605: break;
606: }
607: case JRTextElement.ROTATION_UPSIDE_DOWN:
608: case JRTextElement.ROTATION_NONE:
609: default: {
610: horizontalAlignment = textElement.getHorizontalAlignment();
611: verticalAlignment = textElement.getVerticalAlignment();
612: }
613: }
614:
615: return new TextAlignHolder(horizontalAlignment,
616: verticalAlignment, rotation);
617: }
618:
619: /**
620: *
621: */
622: private String getSheetName(String sheetName) {
623: currentSheetName = sheetName;
624:
625: // sheet names must be unique
626: if (!sheetNamesMap.containsKey(sheetName)) {
627: // first time this sheet name is found;
628: sheetNamesMap.put(sheetName, new Integer(1));
629: return sheetName;
630: }
631:
632: int currentIndex = ((Integer) sheetNamesMap.get(sheetName))
633: .intValue() + 1;
634: sheetNamesMap.put(sheetName, new Integer(currentIndex));
635:
636: return sheetName + " " + currentIndex;
637: }
638:
639: protected abstract ExporterNature getNature();
640:
641: protected abstract void openWorkbook(OutputStream os)
642: throws JRException;
643:
644: protected abstract void createSheet(String name);
645:
646: protected abstract void closeWorkbook(OutputStream os)
647: throws JRException;
648:
649: protected abstract void setColumnWidth(short index, short width);
650:
651: protected abstract void setRowHeight(int rowIndex, int lastRowHeight)
652: throws JRException;
653:
654: protected abstract void setCell(int colIndex, int rowIndex);
655:
656: protected abstract void addBlankCell(JRExporterGridCell gridCell,
657: int colIndex, int rowIndex) throws JRException;
658:
659: protected abstract void exportText(JRPrintText text,
660: JRExporterGridCell cell, int colIndex, int rowIndex)
661: throws JRException;
662:
663: protected abstract void exportImage(JRPrintImage image,
664: JRExporterGridCell cell, int colIndex, int rowIndex)
665: throws JRException;
666:
667: protected abstract void exportRectangle(JRPrintElement element,
668: JRExporterGridCell cell, int colIndex, int rowIndex)
669: throws JRException;
670:
671: protected abstract void exportLine(JRPrintLine line,
672: JRExporterGridCell cell, int colIndex, int rowIndex)
673: throws JRException;
674:
675: protected abstract void exportFrame(JRPrintFrame frame,
676: JRExporterGridCell cell, int colIndex, int rowIndex)
677: throws JRException;
678: }
|