/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ro.nextreports.engine.exporter; import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.Serializable; import java.sql.SQLException; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.HashSet; import java.util.List; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import javax.swing.SwingConstants; import javax.swing.event.EventListenerList; import javax.imageio.ImageIO; import com.itextpdf.text.pdf.Barcode; import com.itextpdf.text.pdf.Barcode128; import com.itextpdf.text.pdf.Barcode39; import com.itextpdf.text.pdf.BarcodeCodabar; import com.itextpdf.text.pdf.BarcodeDatamatrix; import com.itextpdf.text.pdf.BarcodeEAN; import com.itextpdf.text.pdf.BarcodeInter25; import com.itextpdf.text.pdf.BarcodePDF417; import com.itextpdf.text.pdf.BarcodeQRCode; import org.apache.commons.jexl2.JexlEngine; import org.apache.commons.jexl2.Expression; import org.apache.commons.jexl2.JexlException; import org.apache.commons.jexl2.JexlContext; import org.apache.commons.jexl2.MapContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import ro.nextreports.engine.EngineProperties; import ro.nextreports.engine.FunctionCache; import ro.nextreports.engine.GroupCache; import ro.nextreports.engine.Report; import ro.nextreports.engine.ReportGroup; import ro.nextreports.engine.ReportLayout; import ro.nextreports.engine.band.Band; import ro.nextreports.engine.band.BandElement; import ro.nextreports.engine.band.BarcodeBandElement; import ro.nextreports.engine.band.Border; import ro.nextreports.engine.band.ChartBandElement; import ro.nextreports.engine.band.ColumnBandElement; import ro.nextreports.engine.band.ExpressionBandElement; import ro.nextreports.engine.band.ExpressionBean; import ro.nextreports.engine.band.FieldBandElement; import ro.nextreports.engine.band.FunctionBandElement; import ro.nextreports.engine.band.HyperlinkBandElement; import ro.nextreports.engine.band.ImageBandElement; import ro.nextreports.engine.band.ImageColumnBandElement; import ro.nextreports.engine.band.Padding; import ro.nextreports.engine.band.ParameterBandElement; import ro.nextreports.engine.band.ReportBandElement; import ro.nextreports.engine.band.RowElement; import ro.nextreports.engine.band.VariableBandElement; import ro.nextreports.engine.chart.Chart; import ro.nextreports.engine.chart.ChartRunner; import ro.nextreports.engine.condition.BandElementCondition; import ro.nextreports.engine.condition.BandElementConditionProperty; import ro.nextreports.engine.condition.FormattingConditions; import ro.nextreports.engine.condition.RowFormattingConditions; import ro.nextreports.engine.condition.exception.ConditionalException; import ro.nextreports.engine.exporter.event.ExporterEvent; import ro.nextreports.engine.exporter.event.ExporterEventListener; import ro.nextreports.engine.exporter.event.ExporterObject; import ro.nextreports.engine.exporter.exception.NoDataFoundException; import ro.nextreports.engine.exporter.util.StyleFormatConstants; import ro.nextreports.engine.exporter.util.function.FunctionFactory; import ro.nextreports.engine.exporter.util.function.FunctionUtil; import ro.nextreports.engine.exporter.util.function.GFunction; import ro.nextreports.engine.exporter.util.variable.EmptyDataVariable; import ro.nextreports.engine.exporter.util.variable.GroupRowVariable; import ro.nextreports.engine.exporter.util.variable.PageNoVariable; import ro.nextreports.engine.exporter.util.variable.RowVariable; import ro.nextreports.engine.exporter.util.variable.TotalPageNoVariable; import ro.nextreports.engine.exporter.util.variable.Variable; import ro.nextreports.engine.exporter.util.variable.VariableFactory; import ro.nextreports.engine.i18n.I18nLanguage; import ro.nextreports.engine.i18n.I18nUtil; import ro.nextreports.engine.queryexec.IdName; import ro.nextreports.engine.queryexec.Query; import ro.nextreports.engine.queryexec.QueryException; import ro.nextreports.engine.queryexec.QueryExecutor; import ro.nextreports.engine.queryexec.QueryParameter; import ro.nextreports.engine.queryexec.QueryResult; import ro.nextreports.engine.util.PrefixSuffix; import ro.nextreports.engine.util.QueryUtil; import ro.nextreports.engine.util.ReportUtil; import ro.nextreports.engine.util.StringUtil; /** * Created by IntelliJ IDEA. * User: mihai.panaitescu * Date: Dec 4, 2008 * Time: 9:53:57 AM */ public abstract class ResultExporter { protected ExporterBean bean; protected boolean showNrCrt = false; public static final int PORTRAIT = 0; public static final int LANDSCAPE = 1; // special types of reports public static final int DEFAULT_TYPE = 0; public static final int ALARM_TYPE = 1; public static final int TABLE_TYPE = 2; public static final int INDICATOR_TYPE = 3; public static final int DISPLAY_TYPE = 4; // A4 width in inches public static final float A4_WIDTH = 8.27f; // A4 height in inches public static final float A4_HEIGHT = 11.69f; public static final int A4_PORTRAIT_PIXELS = (int) (A4_WIDTH * getDPI()); public static final int A4_LANDSCAPE_PIXELS = (int) (A4_HEIGHT * getDPI()); public static final int DEFAULT_PADDING_PIXELS = (int) (0.5f * getDPI()); public static final int FLUSH_ROWS = 7500; private Map<Long, Map<String, Object>> styleMap; private String title = ""; protected final int BORDER_THIN_VALUE = 1; protected final int BORDER_MEDIUM_VALUE = 2; protected final int BORDER_THICK_VALUE = 3; private boolean stopExport = false; protected boolean isDetail; protected List<GroupCache> groupCache = new ArrayList<GroupCache>(); // functions for entire column (without a group), shown only in footer protected List<FunctionCache> footerFunctionCache = new ArrayList<FunctionCache>(); // informs if a new row started for current cell protected boolean newRow = false; // counts how many rows has the previous band row (max rowSpan) protected int newRowCount = 1; protected int exporterRow = 0; protected int pageRow = 0; protected int headerRow = 0; protected int pageNo = 0; protected int totalPageNo = 0; protected VariableBandElement totalPageNoVbe; private boolean startNewPage = true; protected int resultSetRow = 0; private boolean start = false; // empty data private boolean isEmpty = false; private boolean printRowsForEmptyData = false; // how many times the first group appears // the number of rows for a band is kept in the outter group : // Example : G1 G2 Detail // number of rows for Detail band is kept in G2 group cache // number of rows for G2 band is kept in G1 group cache // number of rows for G1 band is kept in reportGroupRow protected int reportGroupRow = 1; // we can have more than one barcode inside a report, their images must have unique names for HTML exporter private int barcodeIndex = 1; private int NO_VALUES = 30; private Object[] previousRow; private List<ExpressionBean> expressions; protected EventListenerList listenerList = new EventListenerList(); private ExporterObject exporterObject = new ExporterObject(0, 0); protected static String IMAGE_NOT_FOUND = "<image not found>"; protected static String IMAGE_NOT_LOADED = "<image not loaded>"; public static String SPACE_REPLACEMENT = "_"; private String currentBandName; private Map<Integer, RowElement> rowMap = null; // matrix with true values only if formatting conditions are met for a layout grid cell protected boolean[][] modifiedStyle; private static Log LOG = LogFactory.getLog(ResultExporter.class); private JexlEngine jexl = new JexlEngine(); private String imageChartPath; protected ByteArrayOutputStream subreportStream; protected List<Alert> alerts; protected Map<String, Object> templatesValues = new LinkedHashMap<String, Object>(); // for every group keep the current value (this map is used to compute a key for templatesValues) private Map<String, String> groupTemplateKeys = new LinkedHashMap<String, String>(); private Map<String, Object> groupValues = new LinkedHashMap<String, Object>(); // types of what we are printing public static final int PRINT_DOCUMENT = 0; public static final int PRINT_PAGE_HEADER = 1; public static final int PRINT_PAGE_FOOTER = 2; public ResultExporter(ExporterBean bean) { this.bean = bean; this.expressions = ReportUtil.getExpressions(bean.getReportLayout()); createGroupCache(); modifiedStyle = new boolean[bean.getReportLayout().getRowCount()][bean.getReportLayout().getColumnCount()]; totalPageNoVbe = getTotalPageNoVbe(bean.getReportLayout()); alerts = bean.getAlerts(); if (alerts == null) { alerts = new ArrayList<Alert>(); } } public String getDocumentTitle() { return title; } public void setDocumentTitle(String title) { this.title = title; } public String getImageChartPath() { return imageChartPath; } public void setImageChartPath(String imageChartPath) { this.imageChartPath = imageChartPath; } // export the document // header page band and footer page band are written in PDF and RTF exporters public boolean export() throws QueryException, NoDataFoundException { start = true; testForData(); if (needsFirstCrossing() && !(this instanceof FirstCrossingExporter)) { FirstCrossingExporter fe = new FirstCrossingExporter(bean); fe.export(); // get template values from FirstCrossing templatesValues = fe.getTemplatesValues(); groupTemplateKeys = fe.getGroupTemplateKeys(); } initExport(); printHeaderBand(); if (!printContentBands()) { return false; } printFooterBand(); finishExport(); if ((bean.getResult() != null) && (!(this instanceof FirstCrossingExporter)) ) { bean.getResult().close(); } if (this instanceof FirstCrossingExporter) { // after FirstCrossing go to the beginning of the result set try { bean.getResult().getResultSet().beforeFirst(); } catch (SQLException ex) { LOG.error(ex.getMessage(), ex); } } return true; } public OutputStream getOut() { return bean.getOut(); } public void setOut(FileOutputStream out) { bean.setOut(out); } public QueryResult getResult() { return bean.getResult(); } public void setResult(QueryResult result) { bean.setResult(result); } public boolean isStopExport() { return stopExport; } public void setStopExport(boolean stopExport) { this.stopExport = stopExport; } public Map<Long, Map<String, Object>> getStyleMap() { if (styleMap == null) { this.styleMap = new HashMap<Long, Map<String, Object>>(); } return styleMap; } public void setStyleMap(Map<Long, Map<String, Object>> styleMap) { this.styleMap = styleMap; } public ReportLayout getReportLayout() { return bean.getReportLayout(); } public void setReportLayout(ReportLayout reportLayout) { bean.setReportLayout(reportLayout); totalPageNoVbe = getTotalPageNoVbe(bean.getReportLayout()); } protected Map<String, Object> buildCellStyleMap(BandElement bandElement) { Map<String, Object> format = new HashMap<String, Object>(); if (bandElement == null) { return format; } buildCellFont(format, bandElement.getFont()); format.put(StyleFormatConstants.FONT_COLOR, bandElement.getForeground()); format.put(StyleFormatConstants.BACKGROUND_COLOR, bandElement.getBackground()); buildCellHAllign(format, bandElement.getHorizontalAlign()); buildCellVAllign(format, bandElement.getVerticalAlign()); Padding padding = new Padding(0, 0, 0, 0); if (bandElement.getPadding() != null) { padding = bandElement.getPadding(); } buildCellPadding(format, padding); // format.put(StyleFormatConstants.BORDER, new Float(1)); // Border border = new Border(0, 0, 0, 0); if (bandElement.getBorder() != null) { border = bandElement.getBorder(); } buildCellBorder(format, border); if (bandElement instanceof FieldBandElement) { FieldBandElement fbe = (FieldBandElement) bandElement; if (fbe.getPattern() != null) { format.put(StyleFormatConstants.PATTERN, fbe.getPattern()); } } if (bandElement instanceof HyperlinkBandElement) { HyperlinkBandElement hbe = (HyperlinkBandElement) bandElement; if (hbe.getUrl() != null) { format.put(StyleFormatConstants.URL, hbe.getUrl()); } } return format; } protected Map<String, Object> buildCellStyleMap(BandElement bandElement, Object value, int gridRow, int gridColumn, int colSpan) { return buildCellStyleMap(bandElement, value, gridRow, gridColumn, colSpan, true); } protected Map<String, Object> buildCellStyleMap(BandElement bandElement, Object value, int gridRow, int gridColumn, int colSpan, boolean overwriteCellRenderCond) { Map<String, Object> format = new HashMap<String, Object>(); if (bandElement == null) { return format; } format = buildCellStyleMap(bandElement); // overwrite with row render conditions RowElement rowEl = getRowElement(getReportLayout(), gridRow); if (rowEl != null) { RowFormattingConditions rfc = rowEl.getFormattingConditions(); if ((rfc != null) && (rfc.getConditions().size() > 0)) { try { Serializable rowEval = (Serializable) evaluateExpression("", rfc.getExpressionText(), currentBandName, null, null); RowFormattingConditions renderConditions = rowEl.getFormattingConditions(); putFormattingConditions(format, renderConditions, gridRow, gridColumn, rowEval, colSpan, true); } catch (QueryException e) { e.printStackTrace(); LOG.error(e.getMessage(), e); } } } // overwrite with cell render conditions if (overwriteCellRenderCond) { FormattingConditions renderConditions = bandElement.getFormattingConditions(); if (renderConditions != null) { String cellExpressionText = renderConditions.getCellExpressionText(); if (cellExpressionText == null) { putFormattingConditions(format, renderConditions, gridRow, gridColumn, (Serializable) value, colSpan, false); } else { try { Serializable expEval = (Serializable) evaluateExpression("", cellExpressionText, getBand(getReportLayout(), gridRow).getName(), null, null); putFormattingConditions(format, renderConditions, gridRow, gridColumn, expEval, colSpan, true); } catch (QueryException e) { e.printStackTrace(); LOG.error(e.getMessage(), e); } } } } return format; } private void putFormattingConditions(Map<String, Object> format, FormattingConditions renderConditions, int gridRow, int gridColumn, Serializable leftOperand, int colSpan, boolean rowLevel) { if ((renderConditions != null) && (leftOperand != null)) { try { for (BandElementCondition bec : renderConditions.getConditions()) { bec.getExpression().setLeftOperand(leftOperand); if (bec.getExpression().evaluate()) { modifiedStyle[gridRow][gridColumn] = true; if (bec.getProperty() == BandElementConditionProperty.BACKGROUND_PROPERTY) { format.put(StyleFormatConstants.BACKGROUND_COLOR, bec.getPropertyValue()); } else if (bec.getProperty() == BandElementConditionProperty.FOREGROUND_PROPERTY) { format.put(StyleFormatConstants.FONT_COLOR, bec.getPropertyValue()); } else if (bec.getProperty() == BandElementConditionProperty.BORDER_PROPERTY) { Border b = (Border) bec.getPropertyValue(); if (rowLevel) { Border rBorder = b.clone(); // left border must be only for first column in row // right border must be only for last column in row if (gridColumn == 0) { rBorder.setRight(0); } else if (gridColumn+colSpan-1 == bean.getReportLayout().getColumnCount()-1) { rBorder.setLeft(0); } else { rBorder.setLeft(0); rBorder.setRight(0); } buildCellBorder(format, rBorder); } else { buildCellBorder(format, b); } } else if (bec.getProperty() == BandElementConditionProperty.FONT_PROPERTY) { Font f = (Font) bec.getPropertyValue(); buildCellFont(format, f); } } } } catch (ConditionalException ex) { ex.printStackTrace(); LOG.error(ex.getMessage(), ex); } } } private void buildCellBorder(Map<String, Object> format, Border border) { if (border.getLeft() > 0) { format.put(StyleFormatConstants.BORDER_LEFT, new Float(border.getLeft())); } if (border.getRight() > 0) { format.put(StyleFormatConstants.BORDER_RIGHT, new Float(border.getRight())); } if (border.getTop() > 0) { format.put(StyleFormatConstants.BORDER_TOP, new Float(border.getTop())); } if (border.getBottom() > 0) { format.put(StyleFormatConstants.BORDER_BOTTOM, new Float(border.getBottom())); } format.put(StyleFormatConstants.BORDER_LEFT_COLOR, border.getLeftColor()); format.put(StyleFormatConstants.BORDER_RIGHT_COLOR, border.getRightColor()); format.put(StyleFormatConstants.BORDER_TOP_COLOR, border.getTopColor()); format.put(StyleFormatConstants.BORDER_BOTTOM_COLOR, border.getBottomColor()); } private void buildCellFont(Map<String, Object> format, Font font) { format.put(StyleFormatConstants.FONT_FAMILY_KEY, font.getFamily()); format.put(StyleFormatConstants.FONT_NAME_KEY, font.getName()); format.put(StyleFormatConstants.FONT_SIZE, new Float(font.getSize())); if (Font.PLAIN == font.getStyle()) { format.put(StyleFormatConstants.FONT_STYLE_KEY, StyleFormatConstants.FONT_STYLE_NORMAL); } if (Font.BOLD == font.getStyle()) { format.put(StyleFormatConstants.FONT_STYLE_KEY, StyleFormatConstants.FONT_STYLE_BOLD); } if (Font.ITALIC == font.getStyle()) { format.put(StyleFormatConstants.FONT_STYLE_KEY, StyleFormatConstants.FONT_STYLE_ITALIC); } if ((Font.BOLD | Font.ITALIC) == font.getStyle()) { format.put(StyleFormatConstants.FONT_STYLE_KEY, StyleFormatConstants.FONT_STYLE_BOLDITALIC); } } private void buildCellPadding(Map<String, Object> format, Padding padding) { format.put(StyleFormatConstants.PADDING_LEFT, new Float(padding.getLeft())); format.put(StyleFormatConstants.PADDING_RIGHT, new Float(padding.getRight())); format.put(StyleFormatConstants.PADDING_TOP, new Float(padding.getTop())); format.put(StyleFormatConstants.PADDING_BOTTOM, new Float(padding.getBottom())); } private void buildCellHAllign(Map<String, Object> format, int hAllign) { if (SwingConstants.CENTER == hAllign) { format.put(StyleFormatConstants.HORIZONTAL_ALIGN_KEY, StyleFormatConstants.HORIZONTAL_ALIGN_CENTER); } if (SwingConstants.LEFT == hAllign) { format.put(StyleFormatConstants.HORIZONTAL_ALIGN_KEY, StyleFormatConstants.HORIZONTAL_ALIGN_LEFT); } if (SwingConstants.RIGHT == hAllign) { format.put(StyleFormatConstants.HORIZONTAL_ALIGN_KEY, StyleFormatConstants.HORIZONTAL_ALIGN_RIGHT); } } private void buildCellVAllign(Map<String, Object> format, int vAllign) { if (SwingConstants.CENTER == vAllign) { format.put(StyleFormatConstants.VERTICAL_ALIGN_KEY, StyleFormatConstants.VERTICAL_ALIGN_MIDDLE); } if (SwingConstants.TOP == vAllign) { format.put(StyleFormatConstants.VERTICAL_ALIGN_KEY, StyleFormatConstants.VERTICAL_ALIGN_TOP); } if (SwingConstants.BOTTOM == vAllign) { format.put(StyleFormatConstants.VERTICAL_ALIGN_KEY, StyleFormatConstants.VERTICAL_ALIGN_BOTTOM); } } protected boolean hasRenderConditions(BandElement be, Object value) { if (be == null) { return false; } FormattingConditions rc = be.getFormattingConditions(); return ((value != null) && (rc != null) && (rc.getConditions().size() > 0)); } protected boolean hasRowRenderConditions(BandElement be, int gridRow, Object value) { if (be == null) { return false; } RowElement row = getRowElement(getReportLayout(), gridRow); if (row != null) { RowFormattingConditions rfc = row.getFormattingConditions(); return ((value != null) && (rfc != null) && (rfc.getConditions().size() > 0)); } else { return false; } } // Report Layout contains all cells // If two or more cells are merged, the information is kept inside one cell // and the other merged cells are null band elements. protected Set<CellElement> getIgnoredCellElements(Band band) { Set<CellElement> result = new HashSet<CellElement>(); int rows = band.getRowCount(); int cols = band.getColumnCount(); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { BandElement element = band.getElementAt(i, j); if (element == null) { continue; } int rowSpan = element.getRowSpan(); int colSpan = element.getColSpan(); if ((rowSpan > 1) && (colSpan > 1)) { for (int k = 0; k < rowSpan; k++) { for (int m = 0; m < colSpan; m++) { if ((k != 0) || (m != 0)) { result.add(new CellElement(i + k, j + m)); } } } } else if (rowSpan > 1) { for (int k = 1; k < rowSpan; k++) { result.add(new CellElement(i + k, j)); } } else if (colSpan > 1) { for (int k = 1; k < colSpan; k++) { result.add(new CellElement(i, j + k)); } } } } return result; } // @todo was used for pdf and rtf export (previous 2.1.7 iText version) // because there was no support for row span protected Set<CellElement> getIgnoredCellElementsForColSpan(Band band) { Set<CellElement> result = new HashSet<CellElement>(); int rows = band.getRowCount(); int cols = band.getColumnCount(); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { BandElement element = band.getElementAt(i, j); if (element == null) { continue; } //int rowSpan = element.getRowSpan(); int colSpan = element.getColSpan(); if (colSpan > 1) { for (int k = 1; k < colSpan; k++) { result.add(new CellElement(i, j + k)); } } } } return result; } protected int getColumnWidth(int column, int colSpan) { int width = bean.getReportLayout().getColumnsWidth().get(column); for (int i = column + 1; i < column + colSpan; i++) { width += bean.getReportLayout().getColumnsWidth().get(i); } return width; } private boolean findIgnoredCellElement(Set<CellElement> ignored, int i, int j) { return ignored.contains(new CellElement(i, j)); } private void createGroupCache() { List<ReportGroup> groups = bean.getReportLayout().getGroups(); if (groups == null) { groups = new ArrayList<ReportGroup>(); } Collections.sort(groups, new Comparator<ReportGroup>() { public int compare(ReportGroup o1, ReportGroup o2) { return o1.getName().compareTo(o2.getName()); } }); for (ReportGroup reportGroup : groups) { GroupCache gc = new GroupCache(); gc.setGroup(reportGroup); gc.setStart(true); gc.setHgBand(bean.getReportLayout().getBand(ReportLayout.GROUP_HEADER_BAND_NAME_PREFIX + reportGroup.getName())); Band fgBand = bean.getReportLayout().getBand(ReportLayout.GROUP_FOOTER_BAND_NAME_PREFIX + reportGroup.getName()); gc.setFgBand(fgBand); List<FunctionCache> functionCache = new ArrayList<FunctionCache>(); for (int i = 0; i < fgBand.getRowCount(); i++) { for (int j = 0; j < fgBand.getColumnCount(); j++) { BandElement be = fgBand.getElementAt(i, j); if (be instanceof FunctionBandElement) { FunctionBandElement fbe = (FunctionBandElement) be; FunctionCache fc = new FunctionCache(); GFunction gFunction = FunctionFactory.getFunction(fbe.getFunction()); fc.setFunction(gFunction); fc.setFunctionColumn(fbe.getColumn()); fc.setExpression(fbe.isExpression()); functionCache.add(fc); } } } gc.setFuncCache(functionCache); groupCache.add(gc); } // footer function cache Band fBand = bean.getReportLayout().getBand(ReportLayout.FOOTER_BAND_NAME); for (int i = 0; i < fBand.getRowCount(); i++) { for (int j = 0; j < fBand.getColumnCount(); j++) { BandElement be = fBand.getElementAt(i, j); if (be instanceof FunctionBandElement) { FunctionBandElement fbe = (FunctionBandElement) be; FunctionCache fc = new FunctionCache(); GFunction gFunction = FunctionFactory.getFunction(fbe.getFunction()); fc.setFunction(gFunction); fc.setFunctionColumn(fbe.getColumn()); fc.setExpression(fbe.isExpression()); footerFunctionCache.add(fc); } } } } private void resetFunctions(GroupCache gc) { for (FunctionCache fc : gc.getFuncCache()) { GFunction gFunction = fc.getFunction(); gFunction.reset(); } } protected String getPattern(BandElement be) { if (be instanceof FieldBandElement) { FieldBandElement fbe = (FieldBandElement) be; if (fbe.getPattern() != null) { return fbe.getPattern(); } } return null; } protected String getUrl(BandElement be) { if (be instanceof HyperlinkBandElement) { HyperlinkBandElement hbe = (HyperlinkBandElement) be; if (hbe.getUrl() != null) { return hbe.getUrl(); } } return null; } // for ResultSet TYPE-FORWRD_ONLY (like Csv, SQLite) getResult().isEmpty does not work, so // we also test in printContentBands to throw NoDataFoundException private void testForData() throws QueryException, NoDataFoundException { // for procedure call we do not know the row count (is -1) if (this.getOut() == null || this.getResult() == null || getResult().getColumnCount() <= 0 || getResult().getRowCount() == 0 || getResult().isEmpty() ) { if (!bean.getReportLayout().isShowEmptyData()) { throw new NoDataFoundException(); } } } protected PrintStream createPrintStream() throws QueryException { PrintStream p; try { if (bean.isSubreport()) { subreportStream = new ByteArrayOutputStream(); p = new PrintStream(subreportStream, false, "UTF-8"); } else { p = new PrintStream(getOut(), false, "UTF-8"); } return p; } catch (UnsupportedEncodingException e) { throw new QueryException(e); } } private String getStringValue(VariableBandElement bandElement, String bandName) { String pattern = getPattern(bandElement); Variable var = VariableFactory.getVariable(bandElement.getVariable()); Object value = getValue(var, bandName); return StringUtil.getValueAsString(value, pattern); } private Object getValue(Variable var, String bandName) { Map<String, Object> parameters = new HashMap<String, Object>(); if (Variable.ROW_VARIABLE.equals(var.getName())) { parameters.put(RowVariable.ROW_PARAM, resultSetRow + 1); } else if (Variable.GROUP_ROW_VARIABLE.equals(var.getName())) { // if no groups : GROUP_ROW_VARIABLE is the same with ROW_VARIABLE (groupCache.size() == 0) // if groups : we get the outter group for the band // if variable is in group footer , we must decrement groupRow by one // if variable is in footer band , we put 1 int groupRow = reportGroupRow; if (groupCache.size() == 0) { groupRow = resultSetRow + 1; } else { GroupCache gc = getOutterGroupCache(bandName); if (gc != null) { groupRow = gc.getGroupRow(); } if (bandName.startsWith(ReportLayout.GROUP_FOOTER_BAND_NAME_PREFIX)) { groupRow--; } else if (bandName.equals(ReportLayout.FOOTER_BAND_NAME)) { groupRow = 1; } } parameters.put(GroupRowVariable.GROUP_ROW_PARAM, groupRow); } else if (Variable.PAGE_NO_VARIABLE.equals(var.getName())) { parameters.put(PageNoVariable.PAGE_NO_PARAM, getPageNo()); } else if (Variable.TOTAL_PAGE_NO_VARIABLE.equals(var.getName())) { // must be interpreted inside PdfExporter! parameters.put(TotalPageNoVariable.TOTAL_PAGE_NO_PARAM, getTotalPageNo()); } else if (Variable.REPORT_NAME_VARIABLE.equals(var.getName())) { parameters.put(Variable.REPORT_NAME_VARIABLE, bean.getFileName()); } else if (Variable.EMPTY_DATA_VARIABLE.equals(var.getName())) { parameters.put(EmptyDataVariable.EMPTY_DATA_PARAM, isEmpty); } return var.getCurrentValue(parameters); } private String getStringValue(ParameterBandElement bandElement) { String name = bandElement.getParameter(); QueryParameter qp = bean.getParametersBean().getParams().get(name); Object object = bean.getParametersBean().getParamValues().get(name); // a hidden parameter not used in query can be insert to layout, and after that it is modified in not-hidden // but it remains in the layout if (qp == null) { return ""; } if (QueryParameter.MULTIPLE_SELECTION.equals(qp.getSelection())) { Object[] values = (Object[]) object; if ((values == null) || (values.length == 0)) { return getNullElement(); } else { StringBuilder sb = new StringBuilder(); for (int i = 0, size = values.length; i < size; i++) { sb.append(StringUtil.getValueAsString(values[i], bandElement.getPattern())); if (i < size - 1) { sb.append(" ; "); } if (i == NO_VALUES - 1) { if (i < size - 1) { sb.append(" ..."); } break; } } return sb.toString(); } } else { return StringUtil.getValueAsString(object, bandElement.getPattern(), null, qp.getValueClassName()); } } private boolean printContentBands() throws QueryException, NoDataFoundException { int cols = getResult().getColumnCount(); List<String> expNames = ReportUtil.getExpressionsNames(bean.getReportLayout()); int expNo = expNames.size(); previousRow = new Object[cols + expNo]; int k = 0; isEmpty = true; while (getResult().hasNext()) { isEmpty = false; if (Thread.currentThread().isInterrupted() || isStopExport()) { close(); setStopExport(false); return false; } k++; if (k == EngineProperties.getRecordsYield()) { k = 0; try { Thread.sleep(EngineProperties.getMillisYield()); } catch (InterruptedException e) { close(); setStopExport(false); return false; } } flush(); if (bean.isRawPrint()) { printRawRecord(); } else { if (resultSetRow > 0) { printFooterGroupBands(); } printHeaderGroupBands(); printDetailBand(); } resultSetRow++; exporterObject.setRecord(resultSetRow); exporterObject.setRecordCount(bean.getResult().getRowCount()); fireExporterEvent(new ExporterEvent(exporterObject)); afterRowExport(); for (int i = 0; i < cols; i++) { previousRow[i] = getResult().nextValue(i); } for (int i = cols; i < cols+expNo; i++) { previousRow[i] = evaluateExpression(expressions.get(i-cols), null); } } if (isEmpty) { if (!bean.getReportLayout().isShowEmptyData()) { throw new NoDataFoundException(); } else { printDetailBandForEmptyData(); } } // footer for last groups for (int i = groupCache.size() - 1; i >= 0; i--) { GroupCache gc = groupCache.get(i); if (gc.footerHasRows()) { printFooterGroupBand(gc, true); } } return true; } private void printHeaderGroupBands() throws QueryException { for (GroupCache gc : groupCache) { if (gc.isStart()) { groupStarted(gc); } } } private void groupStarted(GroupCache gc) throws QueryException { groupValues.put("G"+ gc.getGroup().getName(), getResult().nextValue(gc.getGroup().getColumn())); resetFunctions(gc); // init functions with first values for (FunctionCache fc : gc.getFuncCache()) { String column = fc.getFunctionColumn(); Object value; if (fc.isExpression()) { value = evaluateExpression(getExpressionBandElement(fc.getFunctionColumn()), currentBandName, gc); } else { value = getResult().nextValue(column); } fc.getFunction().compute(value); } if (gc.headerHasRows()) { printHeaderGroupBand(gc); } gc.setStart(false); // if we start a group we increment group row count and also outter group row count GroupCache outter = getOutterGroupCache(gc); if (outter == null) { reportGroupRow++; } else { outter.incrementGroupRow(); } gc.incrementGroupRow(); } private boolean isGroupFinished(int groupIndex) throws QueryException { GroupCache gc = groupCache.get(groupIndex); String column = gc.getGroup().getColumn(); Object value; if (resultSetRow == 0) { value = getResult().nextValue(column); } else { //value = getResult().getValueAt(currentRow - 1, column); value = previousRow[getResult().getColumnIndex(column)]; } Object newValue = getResult().nextValue(column); boolean equals = FunctionUtil.parameterEquals(value, newValue); return !equals; } private boolean isPreviousGroupFinished(int groupIndex) throws QueryException { for (int i = groupIndex; i >= 0; i--) { if (isGroupFinished(i)) { return true; } } return false; } private void printFooterGroupBands() throws QueryException { for (int i = groupCache.size() - 1; i >= 0; i--) { GroupCache gc = groupCache.get(i); if (!isGroupFinished(i)) { // test to see if some previous group is finished // then this group is also finished if (i > 0) { if (isPreviousGroupFinished(i - 1)) { groupFinished(gc); continue; } } for (FunctionCache fc : gc.getFuncCache()) { Object nv; if (fc.isExpression()) { nv = evaluateExpression(getExpressionBandElement(fc.getFunctionColumn()), currentBandName, gc); } else { nv = getResult().nextValue(fc.getFunctionColumn()); } fc.getFunction().compute(nv); } // increment group row for the current group if (gc.getGroup().equals(getGroupCache(currentBandName).getGroup())) { gc.incrementGroupRow(); } } else { groupFinished(gc); } } } private void groupFinished(GroupCache gc) throws QueryException { if (gc.footerHasRows()) { printFooterGroupBand(gc); } gc.setStart(true); if (gc.getGroup().isNewPageAfter()) { createNewPage(); } // group finished : decrement group row gc.resetGroupRow(); } protected void printHeaderBand() throws QueryException { printBand(null, getReportLayout().getHeaderBand(), false); } private void printFooterBand() throws QueryException { printBand(null, getReportLayout().getFooterBand(), false); } protected void printPageHeaderBand() throws QueryException { printBand(null, getReportLayout().getPageHeaderBand(), false); } protected void printPageFooterBand() throws QueryException { printBand(null, getReportLayout().getPageFooterBand(), false); } // print those rows in detail band defined for empty data with hide expression ($EMPTY_DATA_VARIABLE == false) private void printDetailBandForEmptyData() throws QueryException { printRowsForEmptyData = true; printBand(null, null, false); printRowsForEmptyData = false; } private void printDetailBand() throws QueryException { printBand(null, null, false); } private void printHeaderGroupBand(GroupCache gc) throws QueryException { printBand(gc, null, false); } private void printFooterGroupBand(GroupCache gc) throws QueryException { printBand(gc, null, true, true); } private void printFooterGroupBand(GroupCache gc, boolean usePrevious) throws QueryException { printBand(gc, null, true, usePrevious); } private void printBand(GroupCache gc, Band staticBand, boolean hasFunction) throws QueryException { printBand(gc, staticBand, hasFunction, false); } // Static Band : header -> ColumnBandElement and FunctionBandElement are ignored // footer -> ColumnBandElement is ignored // Detail Band & Header Group Band : FunctionBandElement is ignored // Footer Group Band : nothing is ignored // // print static band (staticBand != null) : header / footer // print detail band (gc=null, hasFunction=false) // print header group band (gc!= null & hasFunction=false) or // footer group band (gc!= null & hasFunction=true) private void printBand(GroupCache gc, Band staticBand, boolean hasFunction, boolean usePrevious) throws QueryException { Band band; List<FunctionCache> fCache = null; isDetail = false; boolean isPageHeaderFooter = false; if (gc == null) { if (staticBand != null) { band = staticBand; if (ReportLayout.PAGE_HEADER_BAND_NAME.equals(band.getName()) || ReportLayout.PAGE_FOOTER_BAND_NAME.equals(band.getName())) { isPageHeaderFooter = true; } } else { isDetail = true; band = getReportLayout().getDetailBand(); } } else { fCache = gc.getFuncCache(); if (hasFunction) { band = gc.getFgBand(); } else { band = gc.getHgBand(); } } currentBandName = band.getName(); int rows = band.getRowCount(); int cols = band.getColumnCount(); Set<CellElement> ignored = getIgnoredCells(band); int lastRow = -1; for (int i = 0; i < rows; i++) { // hide when expression // a hidden cell is considered to be rendered with null value (but value is taken // into account in functions) // if all cells from a row are hidden , we consider the entire row hidden (hideAll) // and no cell from that row is rendered boolean[] hide = new boolean[cols]; boolean hideAll = false; int count = 0; boolean rowWithHideExpression = false; boolean hideExpressionForEmptyData = false; for (int j = 0; j < cols; j++) { BandElement bandElement = band.getElementAt(i, j); if ((bandElement != null) && (bandElement.getHideWhenExpression() != null)) { rowWithHideExpression = true; if (hideExpressionForEmptyData(bandElement.getHideWhenExpression())) { hideExpressionForEmptyData = true; } } } if (rowWithHideExpression) { for (int j = 0; j < cols; j++) { BandElement bandElement = band.getElementAt(i, j); if ((bandElement != null) && (bandElement.getHideWhenExpression() != null)) { String expression = bandElement.getHideWhenExpression(); Boolean result = (Boolean) evaluateExpression("", expression, currentBandName, null, gc); hide[j] = result; } else { // bandElement can be null in older version of reports (previous to 4.1) hide[j] = (bandElement == null) || "".equals(bandElement.getText()); } if (hide[j]) { count++; } } if (count == cols) { hideAll = true; } } if (printRowsForEmptyData && !hideExpressionForEmptyData) { continue; } for (int j = 0; j < cols; j++) { if (findIgnoredCellElement(ignored, i, j)) { //System.out.println("*** header ignored i="+i + " j="+j); continue; } // newRow is computed relative to cells that are renedered through exportCell // ignored cells are not taken into account if (i > lastRow) { newRow = true; } else { newRow = false; } BandElement bandElement = band.getElementAt(i, j); if (bandElement != null) { newRowCount = Math.max(newRowCount, bandElement.getRowSpan()); } if (newRow) { int gridRow = getReportLayout().getGridRow(band.getName(), i); RowElement re = getRowElement(getReportLayout(), gridRow); // if new page is put for the first row in the layout, we should not create a new page if (re.isStartOnNewPage() && !start ) { if (!startNewPage) { // if header on every page, must not use header rows inside pageRow count if (getReportLayout().isHeaderOnEveryPage()) { if (pageRow - headerRow > 0) { createNewPage(); } } else { createNewPage(); } } else { // we create new page excepting only for first group or in case there are no header rows if ( (getReportLayout().getHeaderBand().getRowCount() == 0) || !(ReportLayout.GROUP_HEADER_BAND_NAME_PREFIX + "1").equals(band.getName())) { createNewPage(); } startNewPage = false; } } } // subreports with parameters can be used only inside detail band // we must update values for subreport parameters // parameter name used in subreport must be the column alias from parent report ! // // !!! we may have a subreport with parameters inside another subreport, so we always try yo add parameters from subreports if (isDetail && ((bandElement instanceof ReportBandElement) || (bandElement instanceof ChartBandElement))) { Map<String, QueryParameter> params = bean.getParametersBean().getSubreportParams(); if (params.size() == 0) { // first time we have to look for subreports and add parameters of subreports that are not yet found in master report List<Report> subreports = ReportUtil.getDetailSubreports(bean.getReportLayout()); for (Report subreport : subreports) { bean.getParametersBean().addNotFoundSubreportParameters(subreport.getParameters()); } // similar for charts List<Chart> charts = ReportUtil.getDetailCharts(bean.getReportLayout()); for (Chart chart : charts) { bean.getParametersBean().addNotFoundSubreportParameters(chart.getReport().getParameters()); } } for (QueryParameter qp : params.values()) { try { Object pValue = getResult().nextValue(qp.getName()); bean.getParametersBean().setParameterValue(qp.getName(), pValue); } catch (QueryException ex) { // if parameter is in third level (report->subreport->subreport:param) it won't be found in first level report if (!bean.isSubreport()) { throw new QueryException("Invalid column for parameter: " + qp.getName(), ex); } } } } Object value = getBandElementValue(fCache, gc, staticBand, hasFunction, usePrevious, bandElement); // hide when expression if (!hideAll && hide[j]) { value = null; } int rowSpan = 1, colSpan = 1; if (bandElement != null) { rowSpan = bandElement.getRowSpan(); colSpan = bandElement.getColSpan(); } int gridRow = getReportLayout().getGridRow(band.getName(), i); boolean isImage = bandElement instanceof ImageBandElement; if (!hideAll) { exportCell(band.getName(), bandElement, value, gridRow, i, j, cols, rowSpan, colSpan, isImage); } lastRow = i; // after exportCell where we may use newRow and newRowCount variables // we need to reset newRowCount if (newRow) { newRowCount = 1; } } // page header and page footer do not count for row computation if (!isPageHeaderFooter) { exporterRow++; if (!hideAll) { if (ReportLayout.HEADER_BAND_NAME.equals(band.getName())) { headerRow++; } pageRow++; } start = false; } } } private Object getBandElementValue(List<FunctionCache> fCache, GroupCache gc, Band staticBand, boolean hasFunction, boolean usePrevious, BandElement bandElement) throws QueryException { Object value = null; String column = null; if (bandElement instanceof ColumnBandElement) { if (staticBand == null) { column = ((ColumnBandElement) bandElement).getColumn(); if (usePrevious) { value = previousRow[getResult().getColumnIndex(column)]; } else { if (bandElement instanceof ImageColumnBandElement) { value = getResult().nextBlobValue(column); } else { value = getResult().nextValue(column); } } // here compute the footer functions for (FunctionCache fc : footerFunctionCache) { if (!fc.isExpression() && fc.getFunctionColumn().equals(column)) { fc.getFunction().compute(value); } } // overwrite repeated value if ((gc == null) && !hasFunction) { if ((value != null) && (bandElement != null) && bandElement.isRepeatedValue() && value.equals(previousRow[getResult().getColumnIndex(column)])) { value = null; } } } else { value = getStringValue(bandElement, null); } } else if (bandElement instanceof ExpressionBandElement) { if (staticBand == null) { if (usePrevious) { value = previousRow[getExpressionBandElementIndex((ExpressionBandElement) bandElement)]; } else { value = evaluateExpression((ExpressionBandElement) bandElement, currentBandName, gc); } // here compute the footer functions for (FunctionCache fc : footerFunctionCache) { if (fc.isExpression() && fc.getFunctionColumn().equals( ((ExpressionBandElement) bandElement).getExpressionName() )) { fc.getFunction().compute(value); } } // overwrite repeated value if ((gc == null) && !hasFunction) { if ((value != null) && (bandElement != null) && bandElement.isRepeatedValue() && value.equals(previousRow[getExpressionBandElementIndex((ExpressionBandElement) bandElement)])) { value = null; } } } else { // expression does not contain columns , just variables, parameters and literals value = evaluateExpression((ExpressionBandElement) bandElement, currentBandName, gc); } } else if (bandElement instanceof FunctionBandElement) { FunctionBandElement fbe = (FunctionBandElement) bandElement; //String functionTemplate = getFunctionTemplate(gc, fbe); // functions in footer if (staticBand != null) { for (FunctionCache fc : footerFunctionCache) { if (fc.getFunction().getName().equals(fbe.getFunction()) && fc.getFunctionColumn().equals(fbe.getColumn())) { value = fc.getFunction().getComputedValue(); if (needsFirstCrossing()) { if (this instanceof FirstCrossingExporter) { if (ReportUtil.foundFunctionInHeader(bean.getReportLayout())) { templatesValues.put(getFunctionTemplate(gc, fbe, true), value); } } else { value = getFunctionTemplate(gc, fbe, false); if (templatesValues.containsKey(value)) { value = templatesValues.get(value); } } } break; } } // functions in group } else if (hasFunction) { for (FunctionCache fc : fCache) { if (fc.getFunction().getName().equals(fbe.getFunction()) && fc.getFunctionColumn().equals(fbe.getColumn())) { value = fc.getFunction().getComputedValue(); if (this instanceof FirstCrossingExporter) { if (ReportUtil.foundFunctionInGroupHeader(bean.getReportLayout(), gc.getGroup().getName())) { templatesValues.put(getFunctionTemplate(gc, fbe, true), value); } } break; } } } else { // for FunctionBandElement in header // at first crossing we just save the templateValues // at second crossing we have the values in map and we print them if (this instanceof FirstCrossingExporter) { value = getFunctionTemplate(gc, fbe, true); } else { value = getFunctionTemplate(gc, fbe, false); if (templatesValues.containsKey(value)) { value = templatesValues.get(value); } } } } else if (bandElement instanceof ImageBandElement) { if (bandElement instanceof ChartBandElement) { // generate chart image generateChartImage((ChartBandElement)bandElement); } else if (bandElement instanceof BarcodeBandElement) { // generate barcode image generateBarcodeImage((BarcodeBandElement)bandElement); } value = ((ImageBandElement) bandElement).getImage(); } else if (bandElement instanceof VariableBandElement) { value = getStringValue((VariableBandElement) bandElement, currentBandName); } else if (bandElement instanceof ParameterBandElement) { value = getStringValue((ParameterBandElement) bandElement); } else { value = getStringValue(bandElement, null); } return value; } protected String getBandElementValueAsString(BandElement bandElement) { String pattern = getPattern(bandElement); if (bandElement instanceof ExpressionBandElement) { Object value = null; try { value = evaluateExpression((ExpressionBandElement)bandElement, currentBandName, null); } catch (QueryException e) { LOG.error(e.getMessage(), e); } I18nLanguage lang = I18nUtil.getLanguageByName(bean.getReportLayout(), bean.getLanguage()); return StringUtil.getValueAsString(value, pattern, lang); } else if (bandElement instanceof VariableBandElement) { return getStringValue((VariableBandElement)bandElement, currentBandName); } else { return getStringValue(bandElement, pattern); } } protected Object evaluateExpression(ExpressionBean bean, GroupCache gc) throws QueryException { return evaluateExpression(bean.getBandElement().getExpressionName(), bean.getBandElement().getExpression(), bean.getBandName(), bean.getBandElement().getPattern(), gc); } protected Object evaluateExpression(ExpressionBandElement bandElement, String bandName, GroupCache gc) throws QueryException { return evaluateExpression(bandElement.getExpressionName(), bandElement.getExpression(), bandName, bandElement.getPattern(), gc); } // private Object evaluateExpression(String expressionName, String expression, String bandName, String pattern) throws QueryException { // return evaluateExpression(expressionName, expression, bandName, pattern, null); // } private Object evaluateExpression(String expressionName, String expression, String bandName, String pattern, GroupCache gc) throws QueryException { Object value = null; Expression e = jexl.createExpression(expression); // create context with all variables, parameters and columns // make sure to replace spaces in column names (as in designer expression evaluator) JexlContext checkContext = new MapContext(); for (Variable var : VariableFactory.getVariables()) { if (((this instanceof RtfExporter) || (this instanceof XlsExporter) || (this instanceof XlsxExporter)) && Variable.PAGE_NO_VARIABLE.equals(var.getName())) { // RtfPageNumber must be added in RtfExporter -> let the variable as it is checkContext.set("$V_" + var.getName(), "$V_" + var.getName()); } else if ( (this instanceof PdfExporter) && Variable.TOTAL_PAGE_NO_VARIABLE.equals(var.getName()) ) { // compute total page no inside PdfExporter checkContext.set("$V_" + var.getName(), "$V_" + var.getName()); } else { checkContext.set("$V_" + var.getName(), getValue(var, bandName)); } } for (String paramName : bean.getParametersBean().getParamValues().keySet()) { Object obj = bean.getParametersBean().getParamValues().get(paramName); if (obj instanceof IdName) { obj = ((IdName)obj).toString(); } checkContext.set("$P_" + paramName, obj); } // expresions outside detail or group bands do not contain columns if (expression.contains("$C") ) { for (int k = 0, size = getResult().getColumnCount(); k < size; k++) { String columnName = getResult().getColumnName(k); String col = columnName.replaceAll("\\s", SPACE_REPLACEMENT); checkContext.set("$C_" + col, getResult().nextValue(columnName)); } } // ony expressions in footers or headers can contain functions if (expression.contains("$F")) { for (String f : bean.getReportLayout().getFunctions()) { FunctionCache fc = findFunctionCache(f, bandName); Double fv = new Double(0); if (fc != null) { fv = (Double)fc.getFunction().getComputedValue(); } checkContext.set("$F_" + f,fv); } // expressions in headers if (needsFirstCrossing() && !(this instanceof FirstCrossingExporter)) { for (FunctionBandElement fbe : ReportUtil.getFunctionsFromExpression(expression)) { value = getFunctionTemplate(gc, fbe, false); String template = (String)value; if (templatesValues.containsKey(value)) { value = templatesValues.get(value); } checkContext.set("$F_" + fbe.getFunction()+"_" + fbe.getColumn(),value); } } } try { value = e.evaluate(checkContext); if (value instanceof String) { I18nLanguage lang = I18nUtil.getLanguageByName(bean.getReportLayout(), bean.getLanguage()); value = StringUtil.getI18nString((String)value, lang); } } catch (JexlException ex) { ex.printStackTrace(); LOG.error(ex.getMessage(), ex); } return value; } private FunctionCache findFunctionCache(String fexp, String bandName) { if (bandName.startsWith(ReportLayout.GROUP_FOOTER_BAND_NAME_PREFIX)) { for (GroupCache gc : groupCache) { if (gc.getFgBand().getName().equals(bandName)) { FunctionCache fc = findFunctionCache(gc.getFuncCache(), fexp, bandName); if (fc != null) { return fc; } } } // document footer } else { FunctionCache fc = findFunctionCache(footerFunctionCache, fexp, bandName); if (fc != null) { return fc; } } return null; } private FunctionCache findFunctionCache(List<FunctionCache> list, String fexp, String bandName) { for (FunctionCache fc : list) { String exp = fc.getFunction().getName() + "_" + fc.getFunctionColumn(); if (exp.equals(fexp)) { return fc; } } return null; } private String getStringValue(BandElement bandElement, String pattern) { if ((bandElement == null) || (bandElement.getText().trim().length() == 0)) { return getNullElement(); } I18nLanguage lang = I18nUtil.getLanguageByName(bean.getReportLayout(), bean.getLanguage()); return StringUtil.getValueAsString(StringUtil.getI18nString(bandElement.getText(), lang), pattern); } protected abstract String getNullElement(); protected abstract Set<CellElement> getIgnoredCells(Band band); protected abstract void exportCell(String bandName, BandElement bandElement, Object value, int gridRow, int row, int column, int cols, int rowSpan, int colSpan, boolean isImage); protected abstract void afterRowExport(); protected abstract void close(); protected abstract void flush(); protected abstract void flushNow(); protected abstract void initExport() throws QueryException; protected abstract void finishExport(); // if java.awt.hedless=true => use 96 protected static int getDPI() { return GraphicsEnvironment.isHeadless() ? 96 : Toolkit.getDefaultToolkit().getScreenResolution(); } // This methods allows classes to register for ExporterEvents public void addExporterEventListener(ExporterEventListener listener) { listenerList.add(ExporterEventListener.class, listener); } // This methods allows classes to unregister for ExporterEvents public void removeExporterEventListener(ExporterEventListener listener) { listenerList.remove(ExporterEventListener.class, listener); } // This private class is used to fire ExporterEvents void fireExporterEvent(ExporterEvent evt) { Object[] listeners = listenerList.getListenerList(); // Each listener occupies two elements - the first is the listener class // and the second is the listener instance for (int i = 0; i < listeners.length; i += 2) { if (listeners[i] == ExporterEventListener.class) { ((ExporterEventListener) listeners[i + 1]).notify(evt); } } } protected byte[] getImage(String image) throws IOException { //System.out.println("%%% LOAD IMAGE="+image); InputStream is = getClass().getResourceAsStream("/" + image); if (is == null) { LOG.error("Image '" + image + "' not found in classpath."); throw new IOException("Image '" + image + "' not found."); } //System.out.println("%%% Image loaded."); ByteArrayOutputStream img_bytes = new ByteArrayOutputStream(); int b; try { while ((b = is.read()) != -1) { img_bytes.write(b); } } finally { is.close(); } return img_bytes.toByteArray(); } protected int[] getRealImageSize(String image) { InputStream is = getClass().getResourceAsStream("/" + image); int[] result = new int[2]; try { BufferedImage img = ImageIO.read(is); result[0] = img.getWidth(); result[1] = img.getHeight(); } catch (IOException ex) { ex.printStackTrace(); LOG.error(ex.getMessage(), ex); } return result; } protected int[] getRealImageSize(byte[] image) { InputStream is = new ByteArrayInputStream(image); int[] result = new int[2]; try { BufferedImage img = ImageIO.read(is); result[0] = img.getWidth(); result[1] = img.getHeight(); } catch (IOException ex) { ex.printStackTrace(); LOG.error(ex.getMessage(), ex); } return result; } protected byte[] getScaledImage(String image, int width, int height) throws IOException { InputStream is = getClass().getResourceAsStream("/" + image); if (is == null) { throw new IOException("Image '" + image + "' not found."); } return getScaledImage(is, image, width, height); } protected byte[] getScaledImage(byte[] image, int width, int height) throws IOException { InputStream is = new ByteArrayInputStream(image); return getScaledImage(is, " from db ", width, height); } protected byte[] getScaledImage(InputStream is, String name, int width, int height) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { BufferedImage img = ImageIO.read(is); if ((img.getWidth() == width) && (img.getHeight() == height)) { // original width and height ImageIO.write(img, "png", baos); } else { int type = img.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : img.getType(); BufferedImage scaledImg = new BufferedImage(width, height, type); Graphics2D gScaledImg = scaledImg.createGraphics(); gScaledImg.setComposite(AlphaComposite.Src); gScaledImg.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); gScaledImg.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); gScaledImg.drawImage(img, 0, 0, width, height, null); ImageIO.write(scaledImg, "png", baos); } } catch (IOException ex) { ex.printStackTrace(); LOG.error(ex.getMessage(), ex); throw new IOException("Image '" + name + "' could not be scaled."); } finally { is.close(); } return baos.toByteArray(); } protected byte[] getImage(String image, Integer width, Integer height) throws IOException { byte[] imageBytes; if ((width == null) || (width.intValue() == 0) || (height == null) || (height.intValue() == 0)) { imageBytes = getImage(image); } else { imageBytes = getScaledImage(image, width, height); } return imageBytes; } protected byte[] getImage(byte[] image, Integer width, Integer height) throws IOException { byte[] imageBytes; if ((width == null) || (width.intValue() == 0) || (height == null) || (height.intValue() == 0)) { imageBytes = image; } else { imageBytes = getScaledImage(image, width, height); } return imageBytes; } protected void printRawRecord() throws QueryException { } private void createNewPage() { pageRow = 0; headerRow = 0; newPage(); } protected void newPage() { } // used only by PDF exporter // for RTF we must interpret PAGE_NO variable in header page band protected int getPageNo() { return pageNo + 1; } protected int getTotalPageNo() { return totalPageNo; } protected int getHeaderRows() { return bean.getReportLayout().getBand(ReportLayout.HEADER_BAND_NAME).getRowCount(); } protected int getDetailRows() { return bean.getReportLayout().getBand(ReportLayout.DETAIL_BAND_NAME).getRowCount(); } protected int getFooterRows() { return bean.getReportLayout().getBand(ReportLayout.FOOTER_BAND_NAME).getRowCount(); } protected int getRowsCount() { int recordRows = getResult().getRowCount(); if (recordRows == -1) { throw new UnsupportedOperationException("Must use true value for computeCount inside QueryExecutor in order to get rows count!"); } return getHeaderRows() + getDetailRows() * recordRows + getFooterRows(); } private ExpressionBandElement getExpressionBandElement(String name) { for (ExpressionBean bean : expressions) { ExpressionBandElement ebe = bean.getBandElement(); if (ebe.getExpressionName().equals(name)) { return ebe; } } return null; } private int getExpressionBandElementIndex(ExpressionBandElement bandElement) { for (int i=0, size=expressions.size(); i<size; i++) { ExpressionBandElement ebe = expressions.get(i).getBandElement(); if (bandElement.getExpressionName().equals(ebe.getExpressionName())) { return i + getResult().getColumnCount(); } } return -1; } private int getGroupIndex(String bandName) { int groupIndex = -1; if (bandName.startsWith(ReportLayout.GROUP_HEADER_BAND_NAME_PREFIX)) { groupIndex = Integer.parseInt(bandName.substring(ReportLayout.GROUP_HEADER_BAND_NAME_PREFIX.length())); } else if (bandName.startsWith(ReportLayout.GROUP_FOOTER_BAND_NAME_PREFIX)) { groupIndex = Integer.parseInt(bandName.substring(ReportLayout.GROUP_FOOTER_BAND_NAME_PREFIX.length())); } else if (ReportLayout.DETAIL_BAND_NAME.equals(bandName)) { if (groupCache.size() > 0) { groupIndex = groupCache.size(); } } return groupIndex; } private GroupCache getGroupCache(String bandName) { int groupIndex = getGroupIndex(bandName); GroupCache group = null; if (groupIndex != -1) { group = groupCache.get(groupIndex-1); } return group; } private GroupCache getOutterGroupCache(String bandName) { int groupIndex = getGroupIndex(bandName); if (ReportUtil.isGroupBand(bandName)) { if (groupIndex > 1) { groupIndex--; } else if (groupIndex == 1) { groupIndex = -1; } } GroupCache group = null; if (groupIndex != -1) { group = groupCache.get(groupIndex-1); } return group; } private GroupCache getOutterGroupCache(GroupCache gc) { for (int i=0, size=groupCache.size(); i<size; i++) { GroupCache outerGc = groupCache.get(i); if (gc.getGroup().equals(outerGc.getGroup())) { if (i > 0) { return groupCache.get(i-1); } } } return null; } private void generateChartImage(ChartBandElement bandElement) { if (bean.getConnection() == null) { return; } String image = null; Chart chart = bandElement.getChart(); ChartRunner runner = new ChartRunner(); runner.setFormat(ChartRunner.IMAGE_FORMAT); runner.setChart(chart); runner.setConnection(bean.getConnection()); runner.setQueryTimeout(bean.getQueryTimeout()); // put subreport parameters bean.getParametersBean().addSubreportParameters(chart.getReport().getParameters()); // update values for subchart parameters // must do it before call QueryExecutor (in its constructor we test for values existence) for (QueryParameter qp : bean.getParametersBean().getParams().values()) { try { if (!bean.getParametersBean().getParamValues().containsKey(qp.getName()) && !isSpecialParam(qp)) { Object pValue = getResult().nextValue(qp.getName()); bean.getParametersBean().setParameterValue(qp.getName(), pValue); } } catch (QueryException ex) { // nothing to do // we just give a chance to complete the values // if fails a new chance will be given in printBand method } } runner.setParameterValues(bean.getParametersBean().getParamValues()); String localPath = imageChartPath; if (localPath == null) { localPath = bean.getImageChartPath(); } runner.setImagePath(localPath); I18nLanguage lang = I18nUtil.getLanguageByName(bean.getReportLayout(), bean.getLanguage()); if (lang != null) { runner.setLanguage(lang.getName()); } int width = (bandElement.getWidth() == null) ? 0 : bandElement.getWidth(); int height = (bandElement.getHeight() == null) ? 0 : bandElement.getHeight(); runner.setImageWidth(width); runner.setImageHeight(height); try { runner.run(); image = runner.getChartImageName(); bandElement.setImage(image); } catch (Exception e) { // if there is an exception we must reset image name (not generated) on band element // otherwise the last one generated will be used, which is false! bandElement.setImage("not_generated"); e.printStackTrace(); } } private void generateBarcodeImage(BarcodeBandElement bandElement) { if (bean.getConnection() == null) { return; } int width = (bandElement.getWidth() == null) ? 1 : bandElement.getWidth(); int height = (bandElement.getHeight() == null) ? 1 : bandElement.getHeight(); String value = bandElement.getValue(); if (bandElement.isColumn()) { try { value = String.valueOf(getResult().nextValue(value)); } catch (QueryException e) { e.printStackTrace(); } } Image image = null; if (BarcodeBandElement.isEANFamily(bandElement.getBarcodeType())) { BarcodeEAN codeEAN = new BarcodeEAN(); codeEAN.setCodeType(bandElement.getBarcodeType()); codeEAN.setCode(value); image = codeEAN.createAwtImage(Color.BLACK, Color.WHITE); } else { if (bandElement.getBarcodeType() == BarcodeBandElement.PDF417) { BarcodePDF417 barcode417 = new BarcodePDF417(); barcode417.setText(value); image = barcode417.createAwtImage(Color.BLACK, Color.WHITE); } else if (bandElement.getBarcodeType() == BarcodeBandElement.DATAMATRIX) { BarcodeDatamatrix datamatrix = new BarcodeDatamatrix(); try { datamatrix.generate(value); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } image = datamatrix.createAwtImage(Color.BLACK, Color.WHITE); } else if (bandElement.getBarcodeType() == BarcodeBandElement.QRCODE) { BarcodeQRCode qrcode = new BarcodeQRCode(value, width, height, null); image = qrcode.createAwtImage(Color.BLACK, Color.WHITE); } else { Barcode barcode = null; if (bandElement.getBarcodeType() == BarcodeBandElement.CODE128) { barcode = new Barcode128(); } else if (bandElement.getBarcodeType() == BarcodeBandElement.CODE128_RAW) { barcode = new Barcode128(); barcode.setCodeType(bandElement.getBarcodeType()); } else if (bandElement.getBarcodeType() == BarcodeBandElement.INTER25) { barcode = new BarcodeInter25(); } else if (bandElement.getBarcodeType() == BarcodeBandElement.CODE39) { barcode = new Barcode39(); } else if (bandElement.getBarcodeType() == BarcodeBandElement.CODE39EXT) { barcode = new Barcode39(); barcode.setStartStopText(false); barcode.setExtended(true); } else if (bandElement.getBarcodeType() == BarcodeBandElement.CODABAR) { barcode = new BarcodeCodabar(); } barcode.setCode(value); image = barcode.createAwtImage(Color.BLACK, Color.WHITE); } } String imageName = saveBarcode(bandElement, toBufferedImage(image), "png"); bandElement.setImage(imageName); } private BufferedImage toBufferedImage(Image src) { int w = src.getWidth(null); int h = src.getHeight(null); int type = BufferedImage.TYPE_INT_RGB; BufferedImage dest = new BufferedImage(w, h, type); Graphics2D g2 = dest.createGraphics(); g2.drawImage(src, 0, 0, null); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.dispose(); return dest; } private String saveBarcode(BarcodeBandElement bandElement, BufferedImage image, String ext) { String fileName = "barcode_" + bandElement.getBarcodeType() + "_" + barcodeIndex + "." + ext; barcodeIndex++; File file = new File(imageChartPath + File.separator + fileName); try { ImageIO.write(image, ext, file); // ignore returned boolean } catch(IOException e) { e.printStackTrace(); } return fileName; } public int getPoints(int pixels) { // 1 inch = 72 points return (int) ( (pixels * 72f) / getDPI() ); } // for exporters who need to replace PAGE_NO variable (RTF, XLS) protected PrefixSuffix interpretPageNo(BandElement bandElement) { String exp = ""; try { String pattern = ""; if (bandElement instanceof FieldBandElement) { pattern = ((FieldBandElement)bandElement).getPattern(); } exp = StringUtil.getValueAsString( evaluateExpression(((ExpressionBandElement) bandElement), "", null), pattern); String name = "$V_" + Variable.PAGE_NO_VARIABLE; return StringUtil.parse(exp, name); } catch (QueryException ex) { ex.printStackTrace(); } return null; } private RowElement getRowElement(ReportLayout layout, int gridRow) { if (rowMap == null) { rowMap = new HashMap<Integer, RowElement>(); List<Band> bands = layout.getBands(); int currentRow = 0; for (Band band : bands) { int rows = band.getRowCount(); for (int i = 0; i < rows; i++) { int gr = layout.getGridRow(band.getName(), i); rowMap.put(gr, band.getElements().get(i)); } } } return rowMap.get(gridRow); } protected ExporterBean getSubreportExporterBean(Report subreport) throws Exception { return getSubreportExporterBean(subreport, false); } private boolean isSpecialParam(QueryParameter param) { boolean hasDefaultSource = (param.getDefaultSource() != null) && !"".equals(param.getDefaultSource().trim()); boolean hasSource = (param.getSource() != null) && !"".equals(param.getSource().trim()); return hasDefaultSource || hasSource || param.isHidden(); } protected ExporterBean getSubreportExporterBean(Report subreport, boolean rowCount) throws Exception { String sql = ReportUtil.getSql(subreport); Query query = new Query(sql); // put subreport parameters bean.getParametersBean().addSubreportParameters(subreport.getParameters()); // for reports inside ForReportBandElement we must overwrite parameter values // see ReportUtil.getForReportLayout where generated parameters are set bean.getParametersBean().overwriteSubreportParametersValues(subreport.getGeneratedParamValues()); // update values for subreport parameters // must do it before call QueryExecutor (in its constructor we test for values existence) for (QueryParameter qp : bean.getParametersBean().getParams().values()) { try { if (!bean.getParametersBean().getParamValues().containsKey(qp.getName()) && !isSpecialParam(qp)) { Object pValue = getResult().nextValue(qp.getName()); bean.getParametersBean().setParameterValue(qp.getName(), pValue); } } catch (QueryException ex) { // nothing to do // we just give a chance to complete the values // if fails a new chance will be given in printBand method } } QueryExecutor executor = null; try { executor = new QueryExecutor(query, bean.getParametersBean().getParams(), bean.getParametersBean() .getParamValues(), bean.getConnection(), rowCount); executor.setMaxRows(0); executor.setTimeout(bean.getQueryTimeout()); QueryResult queryResult = executor.execute(); boolean isProcedure = QueryUtil.isProcedureCall(sql); ExporterBean eb = new ExporterBean(bean.getConnection(), bean.getQueryTimeout(), queryResult, bean.getOut(), subreport.getLayout(), bean.getParametersBean(), subreport.getBaseName(), false, isProcedure); eb.setImageChartPath(imageChartPath); eb.setSubreport(true); return eb; } finally { if (executor != null) { executor.closeCursors(); } } } private VariableBandElement getTotalPageNoVbe(ReportLayout layout) { List<Band> bands = layout.getBands(); for (Band band : bands) { for (int i = 0, rows = band.getRowCount(); i < rows; i++) { List<BandElement> elements = band.getRow(i); for (BandElement be : elements) { if ((be instanceof VariableBandElement) && (VariableFactory.getVariable(((VariableBandElement) be).getVariable()) instanceof TotalPageNoVariable)) { return (VariableBandElement) be; } } } } return null; } protected boolean isAlert(Alert alert, Object value) { return (alert != null) && alert.isActive(value); } private void alert(Alert alert, Object value, String message) { if (alert != null) { alert.run(value, message); } } protected void executeAlert(final Alert alert, final Object value, final String message) { Runnable r = new Runnable() { @Override public void run() { alert(alert, value, message); } }; new Thread(r).start(); } // string template used by functions in header and group header bands private String getFunctionTemplate(GroupCache gc, FunctionBandElement fbe, boolean previous) throws QueryException { StringBuilder templateKey = new StringBuilder(); if (gc == null) { // function in Header templateKey.append("F_"). append(fbe.getFunction()).append("_"). append(fbe.getColumn()); return templateKey.toString(); } // function in group header String groupColumn = gc.getGroup().getColumn(); Object groupValue; if (previous) { if (resultSetRow == 0) { groupValue = getResult().nextValue(groupColumn); } else { groupValue = previousRow[getResult().getColumnIndex(groupColumn)]; } } else { groupValue = getResult().nextValue(groupColumn); } // keep the current value of the group groupTemplateKeys.put("G"+ gc.getGroup().getName(), "G"+ gc.getGroup().getName() + "_" + previousRow[getResult().getColumnIndex(groupColumn)]); templateKey.append("G").append(gc.getGroup().getName()).append("_F_"). append(fbe.getFunction()).append("_"). append(fbe.getColumn()).append("_"). append(groupValue); int group = Integer.parseInt(gc.getGroup().getName()); StringBuilder result = new StringBuilder(); for (int i=1; i<group; i++) { result.append(groupTemplateKeys.get("G"+ i)).append("_"); } result.append(templateKey.toString()); return result.toString(); } public Map<String, Object> getTemplatesValues() { return templatesValues; } public Map<String, String> getGroupTemplateKeys() { return groupTemplateKeys; } // if we need to compute something before we start the print process private boolean needsFirstCrossing() { return ReportUtil.foundFunctionInHeader(bean.getReportLayout()) || ReportUtil.foundFunctionInAnyGroupHeader(bean.getReportLayout()); //return true; } private Band getBand(ReportLayout layout, int gridRow) { List<Band> bands = layout.getBands(); int currentRow = 0; for (Band band : bands) { int rows = band.getRowCount(); currentRow += rows; if (gridRow < currentRow) { return band; } } return null; } protected String getSystemDefaultLanguage() { Locale locale = Locale.getDefault(); return locale.getLanguage() + "_" + locale.getCountry(); } protected I18nLanguage getReportLanguage() { if (bean.getLanguage() == null) { return I18nUtil.getDefaultLanguage(bean.getReportLayout()); } else { return I18nUtil.getLanguageByName(bean.getReportLayout(), bean.getLanguage()); } } // group is G1, G2 ,... protected String getCurrentValueForGroup(String group) { Object obj = groupValues.get(group); if (obj == null) { return ""; } return obj.toString(); } private boolean hideExpressionForEmptyData(String expression) { if (expression == null) { return false; } String exp = "$V_EMPTY_DATA==false"; String e = expression.replaceAll("\\s+",""); return (e.equals(exp)); } }