/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core;
import java.awt.print.PageFormat;
import org.pentaho.reporting.engine.classic.core.event.ReportModelEvent;
import org.pentaho.reporting.engine.classic.core.filter.types.bands.SubReportType;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import org.pentaho.reporting.engine.classic.core.designtime.Change;
import org.pentaho.reporting.engine.classic.core.util.InstanceID;
import org.pentaho.reporting.libraries.base.util.LinkedMap;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
/**
* A subreport element. A subreport can be attached to a root-level band and will be printed afterwards. Subreports have
* their own tablemodel (queried with the sub-reports's defined query and the master report's data-factory).
* <p/>
* A sub-report that has been added to a root-level band will always be printed below the root-level band.
* <p/>
* Sub-reports can have import and export parameters. The parameter mapping can be defined freely, so a subreport is not
* required to use the same column names as the parent report.
* <p/>
* If a global import or export is defined (by adding the parameter mapping "*" => "*") the other defined parameter
* mappings will be ignored.
*
* @author Thomas Morgner
*/
public class SubReport extends AbstractReportDefinition
{
/**
* A mapping of export parameters.
*/
private LinkedMap exportParameters;
/**
* A mapping of import parameters.
*/
private LinkedMap inputParameters;
private DataFactory dataFactory;
/**
* Creates a new subreport instance.
*/
public SubReport()
{
setElementType(new SubReportType());
exportParameters = new LinkedMap();
inputParameters = new LinkedMap();
}
public SubReport(InstanceID id)
{
super(id);
setElementType(new SubReportType());
exportParameters = new LinkedMap();
inputParameters = new LinkedMap();
}
/**
* Returns the page definition assigned to the report definition. The page definition defines the report area and how
* the report is subdivided by the child pages.
*
* @return null, as subreports have no page-definition at all.
*/
public PageDefinition getPageDefinition()
{
ReportElement parent = getParentSection();
while (parent != null)
{
if (parent instanceof MasterReport)
{
final MasterReport masterReport = (MasterReport) parent;
return masterReport.getPageDefinition();
}
parent = parent.getParentSection();
}
return new SimplePageDefinition(new PageFormat());
}
/**
* Clones the report.
*
* @return the clone.
* @throws CloneNotSupportedException this should never happen.
*/
public Element derive() throws CloneNotSupportedException
{
final SubReport o = (SubReport) super.derive();
o.exportParameters = (LinkedMap) exportParameters.clone();
o.inputParameters = (LinkedMap) inputParameters.clone();
if (dataFactory != null)
{
o.dataFactory = dataFactory.derive();
}
return o;
}
/**
* Clones the report.
*
* @return the clone.
* @throws CloneNotSupportedException this should never happen.
*/
public Object clone() throws CloneNotSupportedException
{
final SubReport o = (SubReport) super.clone();
o.exportParameters = (LinkedMap) exportParameters.clone();
o.inputParameters = (LinkedMap) inputParameters.clone();
if (dataFactory != null)
{
o.dataFactory = dataFactory.derive();
}
return o;
}
/**
* Adds an export-parameter mapping to the subreport. The parameter specified by 'sourceColumn' will be made available
* with the name 'outerName' in the parent report.
*
* @param outerName the name the parameter will get in the master report.
* @param sourceColumn the source-column in the sub-report.
*/
public void addExportParameter(final String outerName, final String sourceColumn)
{
if (outerName == null)
{
throw new NullPointerException();
}
if (sourceColumn == null)
{
throw new NullPointerException();
}
exportParameters.put(outerName, sourceColumn);
notifyNodePropertiesChanged();
}
/**
* Removes the export parameter from the mapping.
*
* @param outerName the name of the parameter as it is known in the master report.
*/
public void removeExportParameter(final String outerName)
{
if (outerName == null)
{
throw new NullPointerException();
}
exportParameters.remove(outerName);
notifyNodePropertiesChanged();
}
/**
* Returns the parameter mappings for the subreport. The parameter mappings define how columns of the sub-report get
* mapped into the master report.
*
* @return the parameter mappings array.
*/
public ParameterMapping[] getExportMappings()
{
final int length = exportParameters.size();
final String[] keys = (String[]) exportParameters.keys(new String[length]);
final ParameterMapping[] mapping = new ParameterMapping[length];
for (int i = 0; i < length; i++)
{
final String name = keys[i];
final String alias = (String) exportParameters.get(name);
mapping[i] = new ParameterMapping(name, alias);
}
return mapping;
}
public void setExportMappings(final ParameterMapping[] mappings)
{
if (mappings == null)
{
throw new NullPointerException();
}
exportParameters.clear();
for (int i = 0; i < mappings.length; i++)
{
final ParameterMapping mapping = mappings[i];
exportParameters.put(mapping.getName(), mapping.getAlias());
}
notifyNodePropertiesChanged();
}
/**
* Adds an input-parameter mapping to the subreport. Input parameters define how columns that exist in the parent
* report get mapped into the subreport.
* <p/>
* Input parameter mapping happens only once, so after the report has been started, changes to the parameters will not
* pass through to the subreport.
*
* @param outerName the name of the parent report's column that provides the data.
* @param sourceColumn the name under which the parameter will be available in the subreport.
*/
public void addInputParameter(final String outerName, final String sourceColumn)
{
if (outerName == null)
{
throw new NullPointerException();
}
if (sourceColumn == null)
{
throw new NullPointerException();
}
inputParameters.put(sourceColumn, outerName);
notifyNodePropertiesChanged();
}
/**
* Removes the input parameter from the parameter mapping.
*
* @param sourceColumn the name of the column of the subreport report that acts as source for the input parameter.
*/
public void removeInputParameter(final String sourceColumn)
{
if (sourceColumn == null)
{
throw new NullPointerException();
}
inputParameters.remove(sourceColumn);
notifyNodePropertiesChanged();
}
public void clearInputParameters()
{
inputParameters.clear();
notifyNodePropertiesChanged();
}
public void clearExportParameters()
{
exportParameters.clear();
notifyNodePropertiesChanged();
}
/**
* Returns the input mappings defined for this subreport.
*
* @return the input mappings, never null.
*/
public ParameterMapping[] getInputMappings()
{
final int length = inputParameters.size();
final String[] keys = (String[]) inputParameters.keys(new String[length]);
final ParameterMapping[] mapping = new ParameterMapping[length];
for (int i = 0; i < length; i++)
{
final String alias = keys[i];
final String name = (String) inputParameters.get(alias);
mapping[i] = new ParameterMapping(name, alias);
}
return mapping;
}
public void setInputMappings(final ParameterMapping[] mappings)
{
if (mappings == null)
{
throw new NullPointerException();
}
inputParameters.clear();
for (int i = 0; i < mappings.length; i++)
{
final ParameterMapping mapping = mappings[i];
inputParameters.put(mapping.getAlias(), mapping.getName());
}
notifyNodePropertiesChanged();
}
/**
* Checks whether a global import is defined. A global import effectly overrides all other imports.
*
* @return true, if there is a global import defined, false otherwise.
*/
public boolean isGlobalImport()
{
return "*".equals(inputParameters.get("*"));
}
/**
* Checks whether a global export is defined. A global export effectly overrides all other export mappings.
*
* @return true, if there is a global export defined, false otherwise.
*/
public boolean isGlobalExport()
{
return "*".equals(exportParameters.get("*"));
}
public DataFactory getDataFactory()
{
return dataFactory;
}
/**
* The (optional) data-factory for the subreport. If no datafactory is defined here, the subreport will use the master
* report's data-factory.
*
* @param dataFactory
*/
public void setDataFactory(final DataFactory dataFactory)
{
final DataFactory old = this.dataFactory;
this.dataFactory = dataFactory;
if (old != null)
{
notifyNodeChildRemoved(old);
}
if (dataFactory != null)
{
notifyNodeChildAdded(dataFactory);
}
}
public Expression getActivationExpression()
{
return getAttributeExpression(AttributeNames.Core.NAMESPACE, AttributeNames.Core.SUBREPORT_ACTIVE);
}
public void setActivationExpression(final Expression activationExpression)
{
setAttributeExpression(AttributeNames.Core.NAMESPACE, AttributeNames.Core.SUBREPORT_ACTIVE, activationExpression);
}
protected void updateChangedFlagInternal(final ReportElement element, final int type, final Object parameter)
{
// also notify all local listeners on all changes.
super.fireModelLayoutChanged(element, type, parameter);
if ((type == ReportModelEvent.NODE_PROPERTIES_CHANGED && element == this &&
parameter instanceof Change))
{
// propagate the change to the parent report. This change may have come from editing the subreport
// in the parent or from editing the subreport properties itself
// Datasource or parameter changes (or any other non-visual change) will not be propagated.
super.updateChangedFlagInternal(element, type, parameter);
}
}
public ResourceManager getResourceManager()
{
Section parent = getParentSection();
while (parent != null)
{
if (parent instanceof MasterReport)
{
final MasterReport masterReport = (MasterReport) parent;
return masterReport.getResourceManager();
}
parent = parent.getParentSection();
}
final ResourceManager manager = new ResourceManager();
manager.registerDefaults();
return manager;
}
}