package net.sf.jasperreports.engine.fill; import java.io.File; import java.io.InputStream; import java.net.URL; import java.sql.Connection; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import net.sf.jasperreports.engine.JRCommonElement; import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.JRDatasetParameter; import net.sf.jasperreports.engine.JRElement; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRExpression; import net.sf.jasperreports.engine.JRExpressionCollector; import net.sf.jasperreports.engine.JRPrintElement; import net.sf.jasperreports.engine.JRPrintPage; import net.sf.jasperreports.engine.JRPrintRectangle; import net.sf.jasperreports.engine.JRRewindableDataSource; import net.sf.jasperreports.engine.JRRuntimeException; import net.sf.jasperreports.engine.JRStyle; import net.sf.jasperreports.engine.JRSubreport; import net.sf.jasperreports.engine.JRSubreportParameter; import net.sf.jasperreports.engine.JRSubreportReturnValue; import net.sf.jasperreports.engine.JRVariable; import net.sf.jasperreports.engine.JRVisitor; import net.sf.jasperreports.engine.JasperCompileManager; import net.sf.jasperreports.engine.JasperReport; import net.sf.jasperreports.engine.design.JRDesignSubreportReturnValue; import net.sf.jasperreports.engine.util.JRLoader; import net.sf.jasperreports.engine.util.JRProperties; import net.sf.jasperreports.engine.util.JRSingletonCache; import net.sf.jasperreports.engine.util.JRStyleResolver; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class JRFillSubreport extends JRFillElement implements JRSubreport { private static final Log log = LogFactory.getLog(JRFillSubreport.class); private static final JRSingletonCache runnerFactoryCache = new JRSingletonCache(JRSubreportRunnerFactory.class); private Map parameterValues = null; private JRSubreportParameter[] parameters = null; private Connection connection = null; private JRDataSource dataSource = null; private JasperReport jasperReport = null; private Map loadedEvaluators = null; private JRFillSubreportReturnValue[] returnValues = null; protected JRBaseFiller subreportFiller = null; private JRPrintPage printPage = null; private JRSubreportRunner runner; private Set checkedReports; protected JRFillSubreport(JRBaseFiller filler, JRSubreport subreport, JRFillObjectFactory factory) { super(filler, (JRElement)subreport, factory); this.parameters = subreport.getParameters(); JRSubreportReturnValue[] subrepReturnValues = subreport.getReturnValues(); if (subrepReturnValues != null) { List returnValuesList = new ArrayList(subrepReturnValues.length * 2); this.returnValues = new JRFillSubreportReturnValue[subrepReturnValues.length]; for (int i = 0; i < subrepReturnValues.length; i++) addReturnValue(subrepReturnValues[i], returnValuesList, factory); this.returnValues = new JRFillSubreportReturnValue[returnValuesList.size()]; returnValuesList.toArray((Object[])this.returnValues); } this.loadedEvaluators = new HashMap(); this.checkedReports = new HashSet(); } public byte getMode() { return JRStyleResolver.getMode((JRCommonElement)this, (byte)2); } public boolean isUsingCache() { return ((JRSubreport)this.parent).isUsingCache(); } public void setUsingCache(boolean isUsingCache) {} public JRExpression getParametersMapExpression() { return ((JRSubreport)this.parent).getParametersMapExpression(); } public JRSubreportParameter[] getParameters() { return this.parameters; } public JRExpression getConnectionExpression() { return ((JRSubreport)this.parent).getConnectionExpression(); } public JRExpression getDataSourceExpression() { return ((JRSubreport)this.parent).getDataSourceExpression(); } public JRExpression getExpression() { return ((JRSubreport)this.parent).getExpression(); } protected JRTemplateRectangle getJRTemplateRectangle() { JRStyle style = getStyle(); JRTemplateRectangle template = (JRTemplateRectangle)getTemplate(style); if (template == null) { template = new JRTemplateRectangle(this.band.getOrigin(), this.filler.getJasperPrint().getDefaultStyleProvider(), this); transferProperties(template); registerTemplate(style, template); } return template; } protected Collection getPrintElements() { Collection printElements = null; if (this.printPage != null) printElements = this.printPage.getElements(); return printElements; } protected void evaluate(byte evaluation) throws JRException { reset(); evaluatePrintWhenExpression(evaluation); evaluateProperties(evaluation); if (isPrintWhenExpressionNull() || (!isPrintWhenExpressionNull() && isPrintWhenTrue())) evaluateSubreport(evaluation); } protected void evaluateSubreport(byte evaluation) throws JRException { JRExpression expression = getExpression(); Object source = evaluateExpression(expression, evaluation); if (source != null) { JREvaluator evaluator = null; if (isUsingCache() && this.filler.fillContext.hasLoadedSubreport(source)) { this.jasperReport = this.filler.fillContext.getLoadedSubreport(source); evaluator = (JREvaluator)this.loadedEvaluators.get(this.jasperReport); if (evaluator == null) { evaluator = JasperCompileManager.loadEvaluator(this.jasperReport); this.loadedEvaluators.put(this.jasperReport, evaluator); } } else { Class expressionClass = expression.getValueClass(); if (expressionClass.equals(JasperReport.class)) { this.jasperReport = (JasperReport)source; } else if (expressionClass.equals(InputStream.class)) { this.jasperReport = (JasperReport)JRLoader.loadObject((InputStream)source); } else if (expressionClass.equals(URL.class)) { this.jasperReport = (JasperReport)JRLoader.loadObject((URL)source); } else if (expressionClass.equals(File.class)) { this.jasperReport = (JasperReport)JRLoader.loadObject((File)source); } else if (expressionClass.equals(String.class)) { this.jasperReport = (JasperReport)JRLoader.loadObjectFromLocation((String)source, this.filler.reportClassLoader, this.filler.urlHandlerFactory, this.filler.fileResolver); } if (this.jasperReport != null) evaluator = JasperCompileManager.loadEvaluator(this.jasperReport); if (isUsingCache()) { this.filler.fillContext.registerLoadedSubreport(source, this.jasperReport); this.loadedEvaluators.put(this.jasperReport, evaluator); } } if (this.jasperReport != null) { expression = getConnectionExpression(); this.connection = (Connection)evaluateExpression(expression, evaluation); expression = getDataSourceExpression(); this.dataSource = (JRDataSource)evaluateExpression(expression, evaluation); this.parameterValues = getParameterValues(this.filler, getParametersMapExpression(), (JRDatasetParameter[])getParameters(), evaluation, false, (this.jasperReport.getResourceBundle() != null), (this.jasperReport.getFormatFactoryClass() != null)); if (this.subreportFiller != null) this.filler.unregisterSubfiller(this.subreportFiller); initSubreportFiller(evaluator); checkReturnValues(); saveReturnVariables(); } } } protected void initSubreportFiller(JREvaluator evaluator) throws JRException { if (log.isDebugEnabled()) log.debug("Fill " + this.filler.fillerId + ": creating subreport filler"); switch (this.jasperReport.getPrintOrder()) { case 2: this.subreportFiller = new JRHorizontalFiller(this.jasperReport, evaluator, this.filler); break; case 1: this.subreportFiller = new JRVerticalFiller(this.jasperReport, evaluator, this.filler); break; default: throw new JRRuntimeException("Unkown print order " + this.jasperReport.getPrintOrder() + "."); } this.runner = getRunnerFactory().createSubreportRunner(this, this.subreportFiller); this.subreportFiller.setSubreportRunner(this.runner); } protected void saveReturnVariables() { if (this.returnValues != null) for (int i = 0; i < this.returnValues.length; i++) { String varName = this.returnValues[i].getToVariable(); this.band.saveVariable(varName); } } public static Map getParameterValues(JRBaseFiller filler, JRExpression parametersMapExpression, JRDatasetParameter[] subreportParameters, byte evaluation, boolean ignoreNullExpressions, boolean removeResourceBundle, boolean removeFormatFactory) throws JRException { Map parameterValues = null; if (parametersMapExpression != null) parameterValues = (Map)filler.evaluateExpression(parametersMapExpression, evaluation); if (parameterValues != null) { if (removeResourceBundle) parameterValues.remove("REPORT_RESOURCE_BUNDLE"); if (removeFormatFactory) parameterValues.remove("REPORT_FORMAT_FACTORY"); parameterValues.remove("REPORT_CONNECTION"); parameterValues.remove("REPORT_MAX_COUNT"); parameterValues.remove("REPORT_DATA_SOURCE"); parameterValues.remove("REPORT_SCRIPTLET"); parameterValues.remove("REPORT_VIRTUALIZER"); parameterValues.remove("IS_IGNORE_PAGINATION"); parameterValues.remove("REPORT_PARAMETERS_MAP"); } if (parameterValues == null) parameterValues = new HashMap(); if (subreportParameters != null && subreportParameters.length > 0) { Object parameterValue = null; for (int i = 0; i < subreportParameters.length; i++) { JRExpression expression = subreportParameters[i].getExpression(); if (expression != null || !ignoreNullExpressions) { parameterValue = filler.evaluateExpression(expression, evaluation); if (parameterValue == null) { parameterValues.remove(subreportParameters[i].getName()); } else { parameterValues.put(subreportParameters[i].getName(), parameterValue); } } } } if (!parameterValues.containsKey("REPORT_LOCALE")) parameterValues.put("REPORT_LOCALE", filler.getLocale()); if (!parameterValues.containsKey("REPORT_TIME_ZONE")) parameterValues.put("REPORT_TIME_ZONE", filler.getTimeZone()); if (!parameterValues.containsKey("REPORT_FORMAT_FACTORY") && !removeFormatFactory) parameterValues.put("REPORT_FORMAT_FACTORY", filler.getFormatFactory()); if (!parameterValues.containsKey("REPORT_CLASS_LOADER") && filler.reportClassLoader != null) parameterValues.put("REPORT_CLASS_LOADER", filler.reportClassLoader); if (!parameterValues.containsKey("REPORT_URL_HANDLER_FACTORY") && filler.urlHandlerFactory != null) parameterValues.put("REPORT_URL_HANDLER_FACTORY", filler.urlHandlerFactory); if (!parameterValues.containsKey("REPORT_FILE_RESOLVER") && filler.fileResolver != null) parameterValues.put("REPORT_FILE_RESOLVER", filler.fileResolver); return parameterValues; } protected void fillSubreport() throws JRException { if (getConnectionExpression() != null) { this.subreportFiller.fill(this.parameterValues, this.connection); } else if (getDataSourceExpression() != null) { this.subreportFiller.fill(this.parameterValues, this.dataSource); } else { this.subreportFiller.fill(this.parameterValues); } } protected boolean prepare(int availableStretchHeight, boolean isOverflow) throws JRException { boolean willOverflow = false; super.prepare(availableStretchHeight, isOverflow); if (this.subreportFiller == null) setToPrint(false); if (!isToPrint()) return willOverflow; if (availableStretchHeight < getRelativeY() - getY() - getBandBottomY()) { setToPrint(false); return true; } boolean filling = this.runner.isFilling(); boolean toPrint = (!isOverflow || isPrintWhenDetailOverflows() || !isAlreadyPrinted()); boolean reprinted = (isOverflow && isPrintWhenDetailOverflows()); if (!filling && toPrint && reprinted) rewind(); int availableHeight = getHeight() + availableStretchHeight - getRelativeY() + getY() + getBandBottomY(); this.subreportFiller.setPageHeight(availableHeight); synchronized (this.subreportFiller) { JRSubreportRunResult result; if (filling) { if (log.isDebugEnabled()) log.debug("Fill " + this.filler.fillerId + ": resuming " + this.subreportFiller.fillerId); result = this.runner.resume(); } else if (toPrint) { setReprinted(reprinted); if (log.isDebugEnabled()) log.debug("Fill " + this.filler.fillerId + ": starting " + this.subreportFiller.fillerId); result = this.runner.start(); } else { this.printPage = null; setStretchHeight(getHeight()); setToPrint(false); return willOverflow; } if (result.getException() != null) { Throwable error = result.getException(); if (log.isErrorEnabled()) log.error("Fill " + this.filler.fillerId + ": exception", error); if (error instanceof RuntimeException) throw (RuntimeException)error; throw new JRRuntimeException(error); } if (result.hasFinished()) { if (log.isDebugEnabled()) log.debug("Fill " + this.filler.fillerId + ": subreport " + this.subreportFiller.fillerId + " finished"); copyValues(); } else if (log.isDebugEnabled()) { log.debug("Fill " + this.filler.fillerId + ": subreport " + this.subreportFiller.fillerId + " to continue"); } this.printPage = this.subreportFiller.getCurrentPage(); setStretchHeight(result.hasFinished() ? this.subreportFiller.getCurrentPageStretchHeight() : availableHeight); willOverflow = !result.hasFinished(); if (!willOverflow) this.runner.reset(); } Collection printElements = getPrintElements(); if ((printElements == null || printElements.size() == 0) && isRemoveLineWhenBlank()) setToPrint(false); return willOverflow; } public void rewind() throws JRException { if (this.subreportFiller == null) return; if (log.isDebugEnabled()) log.debug("Fill " + this.filler.fillerId + ": cancelling " + this.subreportFiller.fillerId); this.subreportFiller.setInterrupted(true); synchronized (this.subreportFiller) { this.runner.cancel(); this.runner.reset(); } this.filler.unregisterSubfiller(this.subreportFiller); initSubreportFiller((JREvaluator)null); if (getConnectionExpression() == null && this.dataSource != null) if (this.dataSource instanceof JRRewindableDataSource) { ((JRRewindableDataSource)this.dataSource).moveFirst(); } else { throw new JRException("The subreport is placed on a non-splitting band, but it does not have a rewindable data source."); } } protected JRPrintElement fill() { JRPrintRectangle printRectangle = new JRTemplatePrintRectangle(getJRTemplateRectangle()); printRectangle.setX(getX()); printRectangle.setY(getRelativeY()); printRectangle.setWidth(getWidth()); printRectangle.setHeight(getStretchHeight()); return (JRPrintElement)printRectangle; } public void collectExpressions(JRExpressionCollector collector) { collector.collect(this); } public void visit(JRVisitor visitor) { visitor.visitSubreport(this); } private JRFillSubreportReturnValue addReturnValue(JRSubreportReturnValue parentReturnValue, List returnValueList, JRFillObjectFactory factory) { JRSubreportReturnValue jRSubreportReturnValue1, varianceVal, countVal, sumVal; JRFillSubreportReturnValue returnValue = factory.getSubreportReturnValue(parentReturnValue); byte calculation = returnValue.getCalculation(); switch (calculation) { case 3: case 7: jRSubreportReturnValue1 = createHelperReturnValue(parentReturnValue, "_COUNT", (byte)1); addReturnValue(jRSubreportReturnValue1, returnValueList, factory); sumVal = createHelperReturnValue(parentReturnValue, "_SUM", (byte)2); addReturnValue(sumVal, returnValueList, factory); this.filler.addVariableCalculationReq(returnValue.getToVariable(), calculation); break; case 6: varianceVal = createHelperReturnValue(parentReturnValue, "_VARIANCE", (byte)7); addReturnValue(varianceVal, returnValueList, factory); this.filler.addVariableCalculationReq(returnValue.getToVariable(), calculation); break; case 10: countVal = createDistinctCountHelperReturnValue(parentReturnValue); addReturnValue(countVal, returnValueList, factory); this.filler.addVariableCalculationReq(returnValue.getToVariable(), calculation); break; } returnValueList.add(returnValue); return returnValue; } protected JRSubreportReturnValue createHelperReturnValue(JRSubreportReturnValue returnValue, String nameSuffix, byte calculation) { JRDesignSubreportReturnValue helper = new JRDesignSubreportReturnValue(); helper.setToVariable(returnValue.getToVariable() + nameSuffix); helper.setSubreportVariable(returnValue.getSubreportVariable()); helper.setCalculation(calculation); helper.setIncrementerFactoryClassName(helper.getIncrementerFactoryClassName()); return (JRSubreportReturnValue)helper; } protected JRSubreportReturnValue createDistinctCountHelperReturnValue(JRSubreportReturnValue returnValue) { JRDesignSubreportReturnValue helper = new JRDesignSubreportReturnValue(); helper.setToVariable(returnValue.getToVariable() + "_DISTINCT_COUNT"); helper.setSubreportVariable(returnValue.getSubreportVariable()); helper.setCalculation((byte)0); helper.setIncrementerFactoryClassName(helper.getIncrementerFactoryClassName()); return (JRSubreportReturnValue)helper; } public JRSubreportReturnValue[] getReturnValues() { return (JRSubreportReturnValue[])this.returnValues; } public boolean usesForReturnValue(String variableName) { boolean used = false; if (this.returnValues != null) for (int j = 0; j < this.returnValues.length; j++) { JRSubreportReturnValue returnValue = this.returnValues[j]; if (returnValue.getToVariable().equals(variableName)) { used = true; break; } } return used; } protected void copyValues() { if (this.returnValues != null && this.returnValues.length > 0) for (int i = 0; i < this.returnValues.length; i++) copyValue(this.returnValues[i]); } protected void copyValue(JRFillSubreportReturnValue returnValue) { try { JRFillVariable variable = this.filler.getVariable(returnValue.getToVariable()); Object value = this.subreportFiller.getVariableValue(returnValue.getSubreportVariable()); Object newValue = returnValue.getIncrementer().increment(variable, value, AbstractValueProvider.getCurrentValueProvider()); variable.setOldValue(newValue); variable.setValue(newValue); variable.setIncrementedValue(newValue); } catch (JRException e) { throw new JRRuntimeException(e); } } private void checkReturnValues() throws JRException { if (this.returnValues != null && this.returnValues.length > 0 && !this.checkedReports.contains(this.jasperReport)) { for (int i = 0; i < this.returnValues.length; i++) { JRSubreportReturnValue returnValue = this.returnValues[i]; String subreportVariableName = returnValue.getSubreportVariable(); JRVariable subrepVariable = this.subreportFiller.getVariable(subreportVariableName); if (subrepVariable == null) throw new JRException("Subreport variable " + subreportVariableName + " not found."); JRVariable variable = this.filler.getVariable(returnValue.getToVariable()); if (returnValue.getCalculation() == 1 || returnValue.getCalculation() == 10) { if (!Number.class.isAssignableFrom(variable.getValueClass())) throw new JRException("Variable " + returnValue.getToVariable() + " must have a numeric type."); } else if (!variable.getValueClass().isAssignableFrom(subrepVariable.getValueClass()) && (!Number.class.isAssignableFrom(variable.getValueClass()) || !Number.class.isAssignableFrom(subrepVariable.getValueClass()))) { throw new JRException("Variable " + returnValue.getToVariable() + " is not assignable from subreport variable " + subreportVariableName); } } if (isUsingCache()) this.checkedReports.add(this.jasperReport); } } protected void resolveElement(JRPrintElement element, byte evaluation) {} public Boolean isOwnUsingCache() { return ((JRSubreport)this.parent).isOwnUsingCache(); } public void setUsingCache(Boolean isUsingCache) {} public JRFillCloneable createClone(JRFillCloneFactory factory) { return null; } protected static JRSubreportRunnerFactory getRunnerFactory() throws JRException { String factoryClassName = JRProperties.getProperty("net.sf.jasperreports.subreport.runner.factory"); if (factoryClassName == null) throw new JRException("Property \"net.sf.jasperreports.subreport.runner.factory\" must be set"); return (JRSubreportRunnerFactory)runnerFactoryCache.getCachedInstance(factoryClassName); } }