/*
* 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));
}
}